Support "insert into ... returning ..." in sqlite-execute

* doc/lispref/text.texi (Database): Mention it.
* src/sqlite.c (Fsqlite_execute): Support syntax like "insert into
... returning ..." (bug#58390).
This commit is contained in:
Lars Ingebrigtsen 2022-10-10 10:58:33 +02:00
parent 7ab6ec364d
commit 7e7dc74ffb
2 changed files with 81 additions and 70 deletions

View file

@ -5321,9 +5321,12 @@ This has exactly the same effect as the previous example, but is more
efficient and safer (because it doesn't involve any string parsing or
interpolation).
@code{sqlite-execute} returns the number of affected rows. For
instance, an @samp{insert} statement will return @samp{1}, whereas an
@samp{update} statement may return zero or a higher number.
@code{sqlite-execute} usually returns the number of affected rows.
For instance, an @samp{insert} statement will typically return
@samp{1}, whereas an @samp{update} statement may return zero or a
higher number. However, when using @acronym{SQL} statements like
@samp{insert into ... returning ...} and the like, the values
specified by @samp{returning ...} will be returned instead.
Strings in SQLite are, by default, stored as @code{utf-8}, and
selecting a text column will decode the string using that charset.

View file

@ -377,73 +377,6 @@ bind_values (sqlite3 *db, sqlite3_stmt *stmt, Lisp_Object values)
return NULL;
}
DEFUN ("sqlite-execute", Fsqlite_execute, Ssqlite_execute, 2, 3, 0,
doc: /* Execute a non-select SQL statement.
If VALUES is non-nil, it should be a vector or a list of values
to bind when executing a statement like
insert into foo values (?, ?, ...)
Value is the number of affected rows. */)
(Lisp_Object db, Lisp_Object query, Lisp_Object values)
{
check_sqlite (db, false);
CHECK_STRING (query);
if (!(NILP (values) || CONSP (values) || VECTORP (values)))
xsignal1 (Qerror, build_string ("VALUES must be a list or a vector"));
sqlite3 *sdb = XSQLITE (db)->db;
Lisp_Object retval = Qnil;
const char *errmsg = NULL;
Lisp_Object encoded = encode_string (query);
sqlite3_stmt *stmt = NULL;
/* We only execute the first statement -- if there's several
(separated by a semicolon), the subsequent statements won't be
done. */
int ret = sqlite3_prepare_v2 (sdb, SSDATA (encoded), -1, &stmt, NULL);
if (ret != SQLITE_OK)
{
if (stmt != NULL)
{
sqlite3_finalize (stmt);
sqlite3_reset (stmt);
}
errmsg = sqlite3_errmsg (sdb);
goto exit;
}
/* Bind ? values. */
if (!NILP (values))
{
const char *err = bind_values (sdb, stmt, values);
if (err != NULL)
{
errmsg = err;
goto exit;
}
}
ret = sqlite3_step (stmt);
sqlite3_finalize (stmt);
if (ret != SQLITE_OK && ret != SQLITE_DONE)
{
errmsg = sqlite3_errmsg (sdb);
goto exit;
}
retval = make_fixnum (sqlite3_changes (sdb));
exit:
if (errmsg != NULL)
xsignal1 (ret == SQLITE_LOCKED || ret == SQLITE_BUSY?
Qsqlite_locked_error: Qerror,
build_string (errmsg));
return retval;
}
static Lisp_Object
row_to_value (sqlite3_stmt *stmt)
{
@ -488,6 +421,81 @@ row_to_value (sqlite3_stmt *stmt)
return Fnreverse (values);
}
DEFUN ("sqlite-execute", Fsqlite_execute, Ssqlite_execute, 2, 3, 0,
doc: /* Execute a non-select SQL statement.
If VALUES is non-nil, it should be a vector or a list of values
to bind when executing a statement like
insert into foo values (?, ?, ...)
Value is the number of affected rows. */)
(Lisp_Object db, Lisp_Object query, Lisp_Object values)
{
check_sqlite (db, false);
CHECK_STRING (query);
if (!(NILP (values) || CONSP (values) || VECTORP (values)))
xsignal1 (Qerror, build_string ("VALUES must be a list or a vector"));
sqlite3 *sdb = XSQLITE (db)->db;
const char *errmsg = NULL;
Lisp_Object encoded = encode_string (query);
sqlite3_stmt *stmt = NULL;
/* We only execute the first statement -- if there's several
(separated by a semicolon), the subsequent statements won't be
done. */
int ret = sqlite3_prepare_v2 (sdb, SSDATA (encoded), -1, &stmt, NULL);
if (ret != SQLITE_OK)
{
if (stmt != NULL)
{
sqlite3_finalize (stmt);
sqlite3_reset (stmt);
}
errmsg = sqlite3_errmsg (sdb);
goto exit;
}
/* Bind ? values. */
if (!NILP (values))
{
const char *err = bind_values (sdb, stmt, values);
if (err != NULL)
{
errmsg = err;
goto exit;
}
}
ret = sqlite3_step (stmt);
if (ret == SQLITE_ROW)
{
Lisp_Object data = Qnil;
do
data = Fcons (row_to_value (stmt), data);
while (sqlite3_step (stmt) == SQLITE_ROW);
sqlite3_finalize (stmt);
return Fnreverse (data);
}
else if (ret == SQLITE_OK || ret == SQLITE_DONE)
{
Lisp_Object rows = make_fixnum (sqlite3_changes (sdb));
sqlite3_finalize (stmt);
return rows;
}
else
errmsg = sqlite3_errmsg (sdb);
exit:
sqlite3_finalize (stmt);
xsignal1 (ret == SQLITE_LOCKED || ret == SQLITE_BUSY?
Qsqlite_locked_error: Qerror,
build_string (errmsg));
}
static Lisp_Object
column_names (sqlite3_stmt *stmt)
{