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
|
efficient and safer (because it doesn't involve any string parsing or
|
||||||
interpolation).
|
interpolation).
|
||||||
|
|
||||||
@code{sqlite-execute} returns the number of affected rows. For
|
@code{sqlite-execute} usually returns the number of affected rows.
|
||||||
instance, an @samp{insert} statement will return @samp{1}, whereas an
|
For instance, an @samp{insert} statement will typically return
|
||||||
@samp{update} statement may return zero or a higher number.
|
@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
|
Strings in SQLite are, by default, stored as @code{utf-8}, and
|
||||||
selecting a text column will decode the string using that charset.
|
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;
|
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
|
static Lisp_Object
|
||||||
row_to_value (sqlite3_stmt *stmt)
|
row_to_value (sqlite3_stmt *stmt)
|
||||||
{
|
{
|
||||||
|
@ -488,6 +421,81 @@ row_to_value (sqlite3_stmt *stmt)
|
||||||
return Fnreverse (values);
|
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
|
static Lisp_Object
|
||||||
column_names (sqlite3_stmt *stmt)
|
column_names (sqlite3_stmt *stmt)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Reference in a new issue