From: Lars Ingebrigtsen Date: Mon, 10 Oct 2022 08:58:33 +0000 (+0200) Subject: Support "insert into ... returning ..." in sqlite-execute X-Git-Tag: emacs-29.0.90~1616^2~674 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=7e7dc74ffbab5eac863657ef719e9f47165708b3;p=emacs.git 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). --- 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