summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Ingebrigtsen <larsi@gnus.org>2022-10-10 10:58:33 +0200
committerLars Ingebrigtsen <larsi@gnus.org>2022-10-10 10:58:33 +0200
commit7e7dc74ffbab5eac863657ef719e9f47165708b3 (patch)
tree8445727c13f2ffb2c3ff52a840eb9986649da78c
parent7ab6ec364d56c292de2d6294e9424824866691b3 (diff)
downloademacs-7e7dc74ffbab5eac863657ef719e9f47165708b3.tar.gz
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).
-rw-r--r--doc/lispref/text.texi9
-rw-r--r--src/sqlite.c120
2 files changed, 70 insertions, 59 deletions
diff --git a/doc/lispref/text.texi b/doc/lispref/text.texi
index 8b859042ad0..e0768721d94 100644
--- a/doc/lispref/text.texi
+++ b/doc/lispref/text.texi
@@ -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.
diff --git a/src/sqlite.c b/src/sqlite.c
index 7af3760eb4c..65b1dc492f6 100644
--- a/src/sqlite.c
+++ b/src/sqlite.c
@@ -377,6 +377,50 @@ bind_values (sqlite3 *db, sqlite3_stmt *stmt, Lisp_Object values)
return NULL;
}
+static Lisp_Object
+row_to_value (sqlite3_stmt *stmt)
+{
+ int len = sqlite3_column_count (stmt);
+ Lisp_Object values = Qnil;
+
+ for (int i = 0; i < len; ++i)
+ {
+ Lisp_Object v = Qnil;
+
+ switch (sqlite3_column_type (stmt, i))
+ {
+ case SQLITE_INTEGER:
+ v = make_int (sqlite3_column_int64 (stmt, i));
+ break;
+
+ case SQLITE_FLOAT:
+ v = make_float (sqlite3_column_double (stmt, i));
+ break;
+
+ case SQLITE_BLOB:
+ v = make_unibyte_string (sqlite3_column_blob (stmt, i),
+ sqlite3_column_bytes (stmt, i));
+ break;
+
+ case SQLITE_NULL:
+ v = Qnil;
+ break;
+
+ case SQLITE_TEXT:
+ v =
+ code_convert_string_norecord
+ (make_unibyte_string ((const char *)sqlite3_column_text (stmt, i),
+ sqlite3_column_bytes (stmt, i)),
+ Qutf_8, false);
+ break;
+ }
+
+ values = Fcons (v, 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
@@ -393,7 +437,6 @@ Value is the number of affected rows. */)
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;
@@ -426,66 +469,31 @@ Value is the number of affected rows. */)
}
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)
-{
- int len = sqlite3_column_count (stmt);
- Lisp_Object values = Qnil;
-
- for (int i = 0; i < len; ++i)
+ if (ret == SQLITE_ROW)
{
- Lisp_Object v = Qnil;
-
- switch (sqlite3_column_type (stmt, i))
- {
- case SQLITE_INTEGER:
- v = make_int (sqlite3_column_int64 (stmt, i));
- break;
-
- case SQLITE_FLOAT:
- v = make_float (sqlite3_column_double (stmt, i));
- break;
+ Lisp_Object data = Qnil;
+ do
+ data = Fcons (row_to_value (stmt), data);
+ while (sqlite3_step (stmt) == SQLITE_ROW);
- case SQLITE_BLOB:
- v = make_unibyte_string (sqlite3_column_blob (stmt, i),
- sqlite3_column_bytes (stmt, i));
- break;
-
- case SQLITE_NULL:
- v = Qnil;
- break;
-
- case SQLITE_TEXT:
- v =
- code_convert_string_norecord
- (make_unibyte_string ((const char *)sqlite3_column_text (stmt, i),
- sqlite3_column_bytes (stmt, i)),
- Qutf_8, false);
- break;
- }
-
- values = Fcons (v, values);
+ 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);
- return Fnreverse (values);
+ exit:
+ sqlite3_finalize (stmt);
+ xsignal1 (ret == SQLITE_LOCKED || ret == SQLITE_BUSY?
+ Qsqlite_locked_error: Qerror,
+ build_string (errmsg));
}
static Lisp_Object