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:
parent
7ab6ec364d
commit
7e7dc74ffb
2 changed files with 81 additions and 70 deletions
|
@ -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.
|
||||
|
|
142
src/sqlite.c
142
src/sqlite.c
|
@ -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)
|
||||
{
|
||||
|
|
Loading…
Add table
Reference in a new issue