From 3d38d1d1345aa65c4018b42e6c648606e32216f8 Mon Sep 17 00:00:00 2001 From: Lars Ingebrigtsen Date: Sat, 11 Dec 2021 04:55:57 +0100 Subject: Add sqlite3 support to Emacs * configure.ac: Add check for the sqlite library. * doc/lispref/text.texi (Database): Document it. * lisp/sqlite.el: New file. * lisp/term/w32-win.el (dynamic-library-alist): Add a mapping. * src/Makefile.in (SQLITE3_LIBS): Add the libraries. * src/alloc.c (union emacs_align_type): Add a Lisp_Sqlite struct. * src/data.c (Ftype_of): Add sqlite. * src/emacs.c (main): Load the syms. * src/lisp.h (DEFINE_GDB_SYMBOL_BEGIN): Add PVEC_SQLITE. (GCALIGNED_STRUCT): New struct to keep data for sqlite database objects and statement objects. (SQLITEP, SQLITE, CHECK_SQLITE, XSQLITE): New macros for accessing the objects. * src/pdumper.c (dump_vectorlike): Update hash. (dump_vectorlike): Don't dump it. * src/print.c (print_vectorlike): Add a printer for the sqlite object. * src/sqlite.c: New file. * test/src/sqlite-tests.el: Add tests. --- src/sqlite.c | 708 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 708 insertions(+) create mode 100644 src/sqlite.c (limited to 'src/sqlite.c') diff --git a/src/sqlite.c b/src/sqlite.c new file mode 100644 index 00000000000..b1843bc573a --- /dev/null +++ b/src/sqlite.c @@ -0,0 +1,708 @@ +/* +Copyright (C) 2021 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at +your option) any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs. If not, see . + +This file is based on the emacs-sqlite3 package written by Syohei +YOSHIDA , which can be found at: + + https://github.com/syohex/emacs-sqlite3 +*/ + +#include +#include "lisp.h" +#include "coding.h" + +#ifdef HAVE_SQLITE3 + +#include + +#ifdef WINDOWSNT + +# include +# include "w32common.h" +# include "w32.h" + +DEF_DLL_FN (SQLITE_API int, sqlite3_finalize, (sqlite3_stmt*)); +DEF_DLL_FN (SQLITE_API int, sqlite3_close, (sqlite3*)); +DEF_DLL_FN (SQLITE_API int, sqlite3_open_v2, + (const char*, sqlite3**, int, const char*)); +DEF_DLL_FN (SQLITE_API int, sqlite3_reset, (sqlite3_stmt*)); +DEF_DLL_FN (SQLITE_API int, sqlite3_bind_text, + (sqlite3_stmt*, int, const char*, int, void(*)(void*))); +DEF_DLL_FN (SQLITE_API int, sqlite3_bind_int64, + (sqlite3_stmt*, int, sqlite3_int64)); +DEF_DLL_FN (SQLITE_API int, sqlite3_bind_double, (sqlite3_stmt*, int, double)); +DEF_DLL_FN (SQLITE_API int, sqlite3_bind_null, (sqlite3_stmt*, int)); +DEF_DLL_FN (SQLITE_API int, sqlite3_bind_int, (sqlite3_stmt*, int, int)); +DEF_DLL_FN (SQLITE_API const char*, sqlite3_errmsg, (sqlite3*)); +DEF_DLL_FN (SQLITE_API int, sqlite3_step, (sqlite3_stmt*)); +DEF_DLL_FN (SQLITE_API int, sqlite3_changes, (sqlite3*)); +DEF_DLL_FN (SQLITE_API int, sqlite3_column_count, (sqlite3_stmt*)); +DEF_DLL_FN (SQLITE_API int, sqlite3_column_type, (sqlite3_stmt*, int)); +DEF_DLL_FN (SQLITE_API sqlite3_int64, sqlite3_column_int64, + (sqlite3_stmt*, int)); +DEF_DLL_FN (SQLITE_API double, sqlite3_column_double, (sqlite3_stmt*, int)); +DEF_DLL_FN (SQLITE_API const void*, sqlite3_column_blob, + (sqlite3_stmt*, int)); +DEF_DLL_FN (SQLITE_API int, sqlite3_column_bytes, (sqlite3_stmt*, int)); +DEF_DLL_FN (SQLITE_API const unsigned char*, sqlite3_column_text, + (sqlite3_stmt*, int)); +DEF_DLL_FN (SQLITE_API const char*, sqlite3_column_name, (sqlite3_stmt*, int)); +DEF_DLL_FN (SQLITE_API int, sqlite3_exec, + (sqlite3*, const char*, int (*callback)(void*,int,char**,char**), + void*, char**)); +DEF_DLL_FN (SQLITE_API int, sqlite3_load_extension, + (sqlite3*, const char*, const char*, char**)); +DEF_DLL_FN (SQLITE_API int, sqlite3_prepare_v2, + (sqlite3*, const char*, int, sqlite3_stmt**, const char**)); + +# undef sqlite3_finalize +# undef sqlite3_close +# undef sqlite3_open_v2 +# undef sqlite3_reset +# undef sqlite3_bind_text +# undef sqlite3_bind_int64 +# undef sqlite3_bind_double +# undef sqlite3_bind_null +# undef sqlite3_bind_int +# undef sqlite3_errmsg +# undef sqlite3_step +# undef sqlite3_changes +# undef sqlite3_column_count +# undef sqlite3_column_type +# undef sqlite3_column_int64 +# undef sqlite3_column_double +# undef sqlite3_column_blob +# undef sqlite3_column_bytes +# undef sqlite3_column_text +# undef sqlite3_column_name +# undef sqlite3_exec +# undef sqlite3_load_extension +# undef sqlite3_prepare_v2 + +# define sqlite3_finalize fn_sqlite3_finalize +# define sqlite3_close fn_sqlite3_close +# define sqlite3_open_v2 fn_sqlite3_open_v2 +# define sqlite3_reset fn_sqlite3_reset +# define sqlite3_bind_text fn_sqlite3_bind_text +# define sqlite3_bind_int64 fn_sqlite3_bind_int64 +# define sqlite3_bind_double fn_sqlite3_bind_double +# define sqlite3_bind_null fn_sqlite3_bind_null +# define sqlite3_bind_int fn_sqlite3_bind_int +# define sqlite3_errmsg fn_sqlite3_errmsg +# define sqlite3_step fn_sqlite3_step +# define sqlite3_changes fn_sqlite3_changes +# define sqlite3_column_count fn_sqlite3_column_count +# define sqlite3_column_type fn_sqlite3_column_type +# define sqlite3_column_int64 fn_sqlite3_column_int64 +# define sqlite3_column_double fn_sqlite3_column_double +# define sqlite3_column_blob fn_sqlite3_column_blob +# define sqlite3_column_bytes fn_sqlite3_column_bytes +# define sqlite3_column_text fn_sqlite3_column_text +# define sqlite3_column_name fn_sqlite3_column_name +# define sqlite3_exec fn_sqlite3_exec +# define sqlite3_load_extension fn_sqlite3_load_extension +# define sqlite3_prepare_v2 fn_sqlite3_prepare_v2 + +static bool +load_dll_functions (HMODULE library) +{ + LOAD_DLL_FN (library, sqlite3_finalize); + LOAD_DLL_FN (library, sqlite3_close); + LOAD_DLL_FN (library, sqlite3_open_v2); + LOAD_DLL_FN (library, sqlite3_reset); + LOAD_DLL_FN (library, sqlite3_bind_text); + LOAD_DLL_FN (library, sqlite3_bind_int64); + LOAD_DLL_FN (library, sqlite3_bind_double); + LOAD_DLL_FN (library, sqlite3_bind_null); + LOAD_DLL_FN (library, sqlite3_bind_int); + LOAD_DLL_FN (library, sqlite3_errmsg); + LOAD_DLL_FN (library, sqlite3_step); + LOAD_DLL_FN (library, sqlite3_changes); + LOAD_DLL_FN (library, sqlite3_column_count); + LOAD_DLL_FN (library, sqlite3_column_type); + LOAD_DLL_FN (library, sqlite3_column_int64); + LOAD_DLL_FN (library, sqlite3_column_double); + LOAD_DLL_FN (library, sqlite3_column_blob); + LOAD_DLL_FN (library, sqlite3_column_bytes); + LOAD_DLL_FN (library, sqlite3_column_text); + LOAD_DLL_FN (library, sqlite3_column_name); + LOAD_DLL_FN (library, sqlite3_exec); + LOAD_DLL_FN (library, sqlite3_load_extension); + LOAD_DLL_FN (library, sqlite3_prepare_v2); + return true; +} + +static bool +sqlite_loaded_p (void) +{ + Lisp_Object found = Fassq (Qsqlite3, Vlibrary_cache); + + return CONSP (found) && EQ (XCDR (found), Qt); +} +#endif /* WINDOWSNT */ + +static bool +init_sqlite_functions (void) +{ +#ifdef WINDOWSNT + if (sqlite_loaded_p ()) + return true; + else + { + HMODULE library; + + if (!(library = w32_delayed_load (Qsqlite3))) + { + message1 ("sqlite3 library not found"); + return false; + } + + if (! load_dll_functions (library)) + goto bad_library; + + Vlibrary_cache = Fcons (Fcons (Qsqlite3, Qt), Vlibrary_cache); + return true; + } + + bad_library: + Vlibrary_cache = Fcons (Fcons (Qsqlite3, Qnil), Vlibrary_cache); + + return false; +#else /* !WINDOWSNT */ + return true; +#endif /* !WINDOWSNT */ +} + + +static void +sqlite_free (void *arg) +{ + struct Lisp_Sqlite *ptr = (struct Lisp_Sqlite *)arg; + if (ptr->is_statement) + sqlite3_finalize (ptr->stmt); + else if (ptr->db) + sqlite3_close (ptr->db); + xfree (ptr->name); + xfree (ptr); +} + +static Lisp_Object +encode_string (Lisp_Object string) +{ + if (STRING_MULTIBYTE (string)) + return encode_string_utf_8 (string, Qnil, 0, Qt, Qt); + else + return string; +} + +static Lisp_Object +make_sqlite (bool is_statement, void *db, void *stmt, char *name) +{ + struct Lisp_Sqlite *ptr + = ALLOCATE_PLAIN_PSEUDOVECTOR (struct Lisp_Sqlite, PVEC_SQLITE); + ptr->is_statement = is_statement; + ptr->finalizer = sqlite_free; + ptr->db = db; + ptr->name = name; + ptr->stmt = stmt; + ptr->eof = false; + return make_lisp_ptr (ptr, Lisp_Vectorlike); +} + +static void +check_sqlite (Lisp_Object db, bool is_statement) +{ + init_sqlite_functions (); + CHECK_SQLITE (db); + if (is_statement && !XSQLITE (db)->is_statement) + xsignal1 (Qerror, build_string ("Invalid set object")); + else if (!is_statement && XSQLITE (db)->is_statement) + xsignal1 (Qerror, build_string ("Invalid database object")); + if (!is_statement && !XSQLITE (db)->db) + xsignal1 (Qerror, build_string ("Database closed")); + else if (is_statement && !XSQLITE (db)->db) + xsignal1 (Qerror, build_string ("Statement closed")); +} + +static int db_count = 0; + +DEFUN ("sqlite-open", Fsqlite_open, Ssqlite_open, 0, 1, 0, + doc: /* Open FILE as an sqlite database. +If FILE is nil, an in-memory database will be opened instead. */) + (Lisp_Object file) +{ + char *name; + init_sqlite_functions (); + + if (!NILP (file)) + { + CHECK_STRING (file); + name = xstrdup (SSDATA (Fexpand_file_name (file, Qnil))); + } + else + /* In-memory database. These have to have different names to + refer to different databases. */ + name = xstrdup (SSDATA (CALLN (Fformat, build_string (":memory:%d"), + make_int (++db_count)))); + + sqlite3 *sdb; + int ret = sqlite3_open_v2 (name, + &sdb, + SQLITE_OPEN_FULLMUTEX + | SQLITE_OPEN_READWRITE + | SQLITE_OPEN_CREATE + | (NILP (file) ? SQLITE_OPEN_MEMORY : 0) +#ifdef SQLITE_OPEN_URI + | SQLITE_OPEN_URI +#endif + | 0, NULL); + + if (ret != SQLITE_OK) + return Qnil; + + return make_sqlite (false, sdb, NULL, name); +} + +DEFUN ("sqlite-close", Fsqlite_close, Ssqlite_close, 1, 1, 0, + doc: /* Close the database DB. */) + (Lisp_Object db) +{ + check_sqlite (db, false); + sqlite3_close (XSQLITE (db)->db); + XSQLITE (db)->db = NULL; + return Qnil; +} + +/* Bind values in a statement like + "insert into foo values (?, ?, ?)". */ +static const char * +bind_values (sqlite3 *db, sqlite3_stmt *stmt, Lisp_Object values) +{ + sqlite3_reset (stmt); + int len; + if (VECTORP (values)) + len = ASIZE (values); + else + len = list_length (values); + + for (int i = 0; i < len; ++i) + { + int ret = SQLITE_MISMATCH; + Lisp_Object value; + if (VECTORP (values)) + value = AREF (values, i); + else + { + value = XCAR (values); + values = XCDR (values); + } + Lisp_Object type = Ftype_of (value); + + if (EQ (type, Qstring)) + { + Lisp_Object encoded = encode_string (value); + ret = sqlite3_bind_text (stmt, i + 1, + SSDATA (encoded), SBYTES (encoded), + NULL); + } + else if (EQ (type, Qinteger)) + { + if (BIGNUMP (value)) + ret = sqlite3_bind_int64 (stmt, i + 1, bignum_to_intmax (value)); + else + ret = sqlite3_bind_int64 (stmt, i + 1, XFIXNUM (value)); + } + else if (EQ (type, Qfloat)) + ret = sqlite3_bind_double (stmt, i + 1, XFLOAT_DATA (value)); + else if (NILP (value)) + ret = sqlite3_bind_null (stmt, i + 1); + else if (EQ (value, Qt)) + ret = sqlite3_bind_int (stmt, i + 1, 1); + else if (EQ (value, Qfalse)) + ret = sqlite3_bind_int (stmt, i + 1, 0); + else + return "invalid argument"; + + if (ret != SQLITE_OK) + return sqlite3_errmsg (db); + } + + 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 list of values to bind when +executing a statement like + + insert into foo values (?, ?, ...) + +The number of affected rows is returned. */) + (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 (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) + { + 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 = + code_convert_string_norecord + (make_string (sqlite3_column_blob (stmt, i), + sqlite3_column_bytes (stmt, i)), + Qutf_8, false); + break; + + case SQLITE_NULL: + v = Qnil; + break; + + case SQLITE_TEXT: + v = + code_convert_string_norecord + (make_string ((const char*)sqlite3_column_text (stmt, i), + sqlite3_column_bytes (stmt, i)), + Qutf_8, false); + break; + } + + values = Fcons (v, values); + } + + return Fnreverse (values); +} + +static Lisp_Object +column_names (sqlite3_stmt *stmt) +{ + Lisp_Object columns = Qnil; + int count = sqlite3_column_count (stmt); + for (int i = 0; i < count; ++i) + columns = Fcons (build_string (sqlite3_column_name (stmt, i)), columns); + + return Fnreverse (columns); +} + +DEFUN ("sqlite-select", Fsqlite_select, Ssqlite_select, 2, 4, 0, + doc: /* Select data from the database DB that matches QUERY. +If VALUES is non-nil, they are values that will be interpolated into a +parametrised statement. + +By default, the return value is a list where the first element is a +list of column names, and the rest of the elements are the matching data. + +RETURN-TYPE can be either nil (which means that the matching data +should be returned as a list of rows), or `full' (the same, but the +first element in the return list will be the column names), or `set', +which means that we return a set object that can be queried with +`sqlite-next' and other functions to get the data. */) + (Lisp_Object db, Lisp_Object query, Lisp_Object values, + Lisp_Object return_type) +{ + 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; + int ret = sqlite3_prepare_v2 (sdb, SSDATA (encoded), SBYTES (encoded), + &stmt, NULL); + if (ret != SQLITE_OK) + { + if (stmt) + sqlite3_finalize (stmt); + + goto exit; + } + + /* Query with parameters. */ + if (!NILP (values)) + { + const char *err = bind_values (sdb, stmt, values); + if (err != NULL) + { + sqlite3_finalize (stmt); + errmsg = err; + goto exit; + } + } + + /* Return a handle to get the data. */ + if (EQ (return_type, Qset)) + { + retval = make_sqlite (true, db, stmt, XSQLITE (db)->name); + goto exit; + } + + /* Return the data directly. */ + Lisp_Object data = Qnil; + while ((ret = sqlite3_step (stmt)) == SQLITE_ROW) + data = Fcons (row_to_value (stmt), data); + + if (EQ (return_type, Qfull)) + retval = Fcons (column_names (stmt), Fnreverse (data)); + else + retval = Fnreverse (data); + sqlite3_finalize (stmt); + + exit: + if (errmsg != NULL) + xsignal1 (Qerror, build_string (errmsg)); + + return retval; +} + +static Lisp_Object +sqlite_exec (sqlite3 *sdb, const char *query) +{ + int ret = sqlite3_exec (sdb, query, NULL, NULL, NULL); + if (ret != SQLITE_OK) + return Qnil; + + return Qt; +} + +DEFUN ("sqlite-transaction", Fsqlite_transaction, Ssqlite_transaction, 1, 1, 0, + doc: /* Start a transaction in DB. */) + (Lisp_Object db) +{ + check_sqlite (db, false); + return sqlite_exec (XSQLITE (db)->db, "begin"); +} + +DEFUN ("sqlite-commit", Fsqlite_commit, Ssqlite_commit, 1, 1, 0, + doc: /* Commit a transaction in DB. */) + (Lisp_Object db) +{ + check_sqlite (db, false); + return sqlite_exec (XSQLITE (db)->db, "commit"); +} + +DEFUN ("sqlite-rollback", Fsqlite_rollback, Ssqlite_rollback, 1, 1, 0, + doc: /* Roll back a transaction in DB. */) + (Lisp_Object db) +{ + check_sqlite (db, false); + return sqlite_exec (XSQLITE (db)->db, "rollback"); +} + +DEFUN ("sqlite-load-extension", Fsqlite_load_extension, + Ssqlite_load_extension, 2, 2, 0, + doc: /* Load a an SQlite module into DB. +MODULE should be the file name of an SQlite module .so file. */) + (Lisp_Object db, Lisp_Object module) +{ + check_sqlite (db, false); + CHECK_STRING (module); + + sqlite3 *sdb = XSQLITE (db)->db; + int result = sqlite3_load_extension (sdb, + SSDATA (Fexpand_file_name (module, Qnil)), + NULL, NULL); + if (result == SQLITE_OK) + return Qt; + return Qnil; +} + +DEFUN ("sqlite-next", Fsqlite_next, Ssqlite_next, 1, 1, 0, + doc: /* Return the next result set from SET. */) + (Lisp_Object set) +{ + check_sqlite (set, true); + + int ret = sqlite3_step (XSQLITE (set)->stmt); + if (ret != SQLITE_ROW && ret != SQLITE_OK && ret != SQLITE_DONE) + xsignal1 (Qerror, build_string (sqlite3_errmsg (XSQLITE (set)->db))); + + if (ret == SQLITE_DONE) + { + XSQLITE (set)->eof = true; + return Qnil; + } + + return row_to_value (XSQLITE (set)->stmt); +} + +DEFUN ("sqlite-columns", Fsqlite_columns, Ssqlite_columns, 1, 1, 0, + doc: /* Return the column names of SET. */) + (Lisp_Object set) +{ + check_sqlite (set, true); + return column_names (XSQLITE (set)->stmt); +} + +DEFUN ("sqlite-more-p", Fsqlite_more_p, Ssqlite_more_p, 1, 1, 0, + doc: /* Say whether there's any further results in SET. */) + (Lisp_Object set) +{ + check_sqlite (set, true); + + if (XSQLITE (set)->eof) + return Qnil; + else + return Qt; +} + +DEFUN ("sqlite-finalize", Fsqlite_finalize, Ssqlite_finalize, 1, 1, 0, + doc: /* Mark this SET as being finished. +This will free the resources held by SET. */) + (Lisp_Object set) +{ + check_sqlite (set, true); + sqlite3_finalize (XSQLITE (set)->stmt); + return Qt; +} + +#endif /* HAVE_SQLITE3 */ + +DEFUN ("sqlitep", Fsqlitep, Ssqlitep, 1, 1, 0, + doc: /* Say whether OBJECT is an SQlite object. */) + (Lisp_Object object) +{ +#ifdef HAVE_SQLITE3 + return SQLITE (object)? Qt: Qnil; +#else + return Qnil; +#endif +} + +DEFUN ("sqlite-available-p", Fsqlite_available_p, Ssqlite_available_p, 0, 0, 0, + doc: /* Return t if sqlite3 support is available in this instance of Emacs.*/) + (void) +{ +#ifdef HAVE_SQLITE3 +# ifdef WINDOWSNT + Lisp_Object found = Fassq (Qsqlite3, Vlibrary_cache); + if (CONSP (found)) + return XCDR (found); + else + { + Lisp_Object status; + status = init_sqlite_functions () ? Qt : Qnil; + Vlibrary_cache = Fcons (Fcons (Qsqlite3, status), Vlibrary_cache); + return status; + } +# else + return Qt; +#endif +#else + return Qnil; +#endif +} + +void +syms_of_sqlite (void) +{ +#ifdef HAVE_SQLITE3 + defsubr (&Ssqlite_open); + defsubr (&Ssqlite_close); + defsubr (&Ssqlite_execute); + defsubr (&Ssqlite_select); + defsubr (&Ssqlite_transaction); + defsubr (&Ssqlite_commit); + defsubr (&Ssqlite_rollback); + defsubr (&Ssqlite_load_extension); + defsubr (&Ssqlite_next); + defsubr (&Ssqlite_columns); + defsubr (&Ssqlite_more_p); + defsubr (&Ssqlite_finalize); + DEFSYM (Qset, "set"); + DEFSYM (Qfull, "full"); +#endif + defsubr (&Ssqlitep); + DEFSYM (Qsqlitep, "sqlitep"); + defsubr (&Ssqlite_available_p); + DEFSYM (Qfalse, "false"); + DEFSYM (Qsqlite, "sqlite"); + DEFSYM (Qsqlite3, "sqlite3"); +} -- cgit v1.2.1 From 385f2faf347b18eb4624f97020a49ae7e3f315e2 Mon Sep 17 00:00:00 2001 From: Lars Ingebrigtsen Date: Sat, 11 Dec 2021 06:26:37 +0100 Subject: Fix some sqlite doc string typos * src/sqlite.c (Fsqlite_load_extension, Fsqlite_more_p): Fix typos in doc strings. --- src/sqlite.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/sqlite.c') diff --git a/src/sqlite.c b/src/sqlite.c index b1843bc573a..50989434ffa 100644 --- a/src/sqlite.c +++ b/src/sqlite.c @@ -578,7 +578,7 @@ DEFUN ("sqlite-rollback", Fsqlite_rollback, Ssqlite_rollback, 1, 1, 0, DEFUN ("sqlite-load-extension", Fsqlite_load_extension, Ssqlite_load_extension, 2, 2, 0, - doc: /* Load a an SQlite module into DB. + doc: /* Load an SQlite module into DB. MODULE should be the file name of an SQlite module .so file. */) (Lisp_Object db, Lisp_Object module) { @@ -622,7 +622,7 @@ DEFUN ("sqlite-columns", Fsqlite_columns, Ssqlite_columns, 1, 1, 0, } DEFUN ("sqlite-more-p", Fsqlite_more_p, Ssqlite_more_p, 1, 1, 0, - doc: /* Say whether there's any further results in SET. */) + doc: /* Say whether there are any further results in SET. */) (Lisp_Object set) { check_sqlite (set, true); -- cgit v1.2.1 From ebf59d1a28b49b391b8025d7017bacf853c01aa2 Mon Sep 17 00:00:00 2001 From: Lars Ingebrigtsen Date: Sat, 11 Dec 2021 06:40:01 +0100 Subject: Check whether the sqlite supports sqlite3_load_extension * configure.ac: Check for sqlite3_load_extension, which is apparently missing in some versions. * src/sqlite.c: Add guards. (Fsqlite_load_extension): Ifdef out on systems that doesn't have it. --- src/sqlite.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'src/sqlite.c') diff --git a/src/sqlite.c b/src/sqlite.c index 50989434ffa..42a7a3a0268 100644 --- a/src/sqlite.c +++ b/src/sqlite.c @@ -65,11 +65,16 @@ DEF_DLL_FN (SQLITE_API const char*, sqlite3_column_name, (sqlite3_stmt*, int)); DEF_DLL_FN (SQLITE_API int, sqlite3_exec, (sqlite3*, const char*, int (*callback)(void*,int,char**,char**), void*, char**)); -DEF_DLL_FN (SQLITE_API int, sqlite3_load_extension, - (sqlite3*, const char*, const char*, char**)); DEF_DLL_FN (SQLITE_API int, sqlite3_prepare_v2, (sqlite3*, const char*, int, sqlite3_stmt**, const char**)); +# ifdef HAVE_SQLITE3_LOAD_EXTENSION +DEF_DLL_FN (SQLITE_API int, sqlite3_load_extension, + (sqlite3*, const char*, const char*, char**)); +# undef sqlite3_load_extension +# define sqlite3_load_extension fn_sqlite3_load_extension +# endif + # undef sqlite3_finalize # undef sqlite3_close # undef sqlite3_open_v2 @@ -91,7 +96,6 @@ DEF_DLL_FN (SQLITE_API int, sqlite3_prepare_v2, # undef sqlite3_column_text # undef sqlite3_column_name # undef sqlite3_exec -# undef sqlite3_load_extension # undef sqlite3_prepare_v2 # define sqlite3_finalize fn_sqlite3_finalize @@ -115,7 +119,6 @@ DEF_DLL_FN (SQLITE_API int, sqlite3_prepare_v2, # define sqlite3_column_text fn_sqlite3_column_text # define sqlite3_column_name fn_sqlite3_column_name # define sqlite3_exec fn_sqlite3_exec -# define sqlite3_load_extension fn_sqlite3_load_extension # define sqlite3_prepare_v2 fn_sqlite3_prepare_v2 static bool @@ -142,7 +145,9 @@ load_dll_functions (HMODULE library) LOAD_DLL_FN (library, sqlite3_column_text); LOAD_DLL_FN (library, sqlite3_column_name); LOAD_DLL_FN (library, sqlite3_exec); +# ifdef HAVE_SQLITE3_LOAD_EXTENSION LOAD_DLL_FN (library, sqlite3_load_extension); +# endif LOAD_DLL_FN (library, sqlite3_prepare_v2); return true; } @@ -576,6 +581,7 @@ DEFUN ("sqlite-rollback", Fsqlite_rollback, Ssqlite_rollback, 1, 1, 0, return sqlite_exec (XSQLITE (db)->db, "rollback"); } +#ifdef HAVE_SQLITE3_LOAD_EXTENSION DEFUN ("sqlite-load-extension", Fsqlite_load_extension, Ssqlite_load_extension, 2, 2, 0, doc: /* Load an SQlite module into DB. @@ -593,6 +599,7 @@ MODULE should be the file name of an SQlite module .so file. */) return Qt; return Qnil; } +#endif /* HAVE_SQLITE3_LOAD_EXTENSION */ DEFUN ("sqlite-next", Fsqlite_next, Ssqlite_next, 1, 1, 0, doc: /* Return the next result set from SET. */) @@ -691,7 +698,9 @@ syms_of_sqlite (void) defsubr (&Ssqlite_transaction); defsubr (&Ssqlite_commit); defsubr (&Ssqlite_rollback); +#ifdef HAVE_SQLITE3_LOAD_EXTENSION defsubr (&Ssqlite_load_extension); +#endif defsubr (&Ssqlite_next); defsubr (&Ssqlite_columns); defsubr (&Ssqlite_more_p); -- cgit v1.2.1 From 17569c94954dc1d9d47155a8ca987d8ff4855180 Mon Sep 17 00:00:00 2001 From: Lars Ingebrigtsen Date: Sat, 11 Dec 2021 07:47:34 +0100 Subject: Fix Fsqlite_finalize book-keeping * src/sqlite.c (Fsqlite_finalize): Mark the object as dead. --- src/sqlite.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/sqlite.c') diff --git a/src/sqlite.c b/src/sqlite.c index 42a7a3a0268..c1f3e7b599f 100644 --- a/src/sqlite.c +++ b/src/sqlite.c @@ -290,7 +290,7 @@ DEFUN ("sqlite-close", Fsqlite_close, Ssqlite_close, 1, 1, 0, check_sqlite (db, false); sqlite3_close (XSQLITE (db)->db); XSQLITE (db)->db = NULL; - return Qnil; + return Qt; } /* Bind values in a statement like @@ -647,6 +647,7 @@ This will free the resources held by SET. */) { check_sqlite (set, true); sqlite3_finalize (XSQLITE (set)->stmt); + XSQLITE (set)->db = NULL; return Qt; } -- cgit v1.2.1 From 6c81683a2791a1a08e4abe9b670f47b2b4037eff Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Sat, 11 Dec 2021 10:51:09 +0200 Subject: Fix a typo in sqlite.c * src/sqlite.c (Fsqlite_select): Fix a typo in arguments to make_sqlite. --- src/sqlite.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/sqlite.c') diff --git a/src/sqlite.c b/src/sqlite.c index c1f3e7b599f..aea79406aa6 100644 --- a/src/sqlite.c +++ b/src/sqlite.c @@ -525,7 +525,7 @@ which means that we return a set object that can be queried with /* Return a handle to get the data. */ if (EQ (return_type, Qset)) { - retval = make_sqlite (true, db, stmt, XSQLITE (db)->name); + retval = make_sqlite (true, sdb, stmt, XSQLITE (db)->name); goto exit; } -- cgit v1.2.1 From 628306c299923551cdc8cf09c874744ae7b74216 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Sat, 11 Dec 2021 11:26:04 +0200 Subject: Minor cleanups of sqlite3 code on MS-Windows * src/sqlite.c (sqlite_loaded_p): Function deleted: not used anymore. (init_sqlite_functions) [WINDOWSNT]: Use a static 'bool' variable to indicate if sqlite3 DLL was successfully loaded. (Fsqlite_available_p) [WINDOWSNT]: Just call 'init_sqlite_functions' if Vlibrary_cache doesn't mention 'sqlite3'. --- src/sqlite.c | 48 ++++++++++++++++-------------------------------- 1 file changed, 16 insertions(+), 32 deletions(-) (limited to 'src/sqlite.c') diff --git a/src/sqlite.c b/src/sqlite.c index aea79406aa6..47829cbdf7f 100644 --- a/src/sqlite.c +++ b/src/sqlite.c @@ -151,43 +151,32 @@ load_dll_functions (HMODULE library) LOAD_DLL_FN (library, sqlite3_prepare_v2); return true; } - -static bool -sqlite_loaded_p (void) -{ - Lisp_Object found = Fassq (Qsqlite3, Vlibrary_cache); - - return CONSP (found) && EQ (XCDR (found), Qt); -} #endif /* WINDOWSNT */ static bool init_sqlite_functions (void) { #ifdef WINDOWSNT - if (sqlite_loaded_p ()) - return true; - else + static bool sqlite3_initialized; + + if (!sqlite3_initialized) { - HMODULE library; + HMODULE library = w32_delayed_load (Qsqlite3); - if (!(library = w32_delayed_load (Qsqlite3))) + if (!library) + message1 ("sqlite3 library was not found"); + else if (load_dll_functions (library)) { - message1 ("sqlite3 library not found"); - return false; + sqlite3_initialized = true; + Vlibrary_cache = Fcons (Fcons (Qsqlite3, Qt), Vlibrary_cache); + } + else + { + message1 ("sqlite3 library was found, but could not be loaded successfully"); + Vlibrary_cache = Fcons (Fcons (Qsqlite3, Qnil), Vlibrary_cache); } - - if (! load_dll_functions (library)) - goto bad_library; - - Vlibrary_cache = Fcons (Fcons (Qsqlite3, Qt), Vlibrary_cache); - return true; } - - bad_library: - Vlibrary_cache = Fcons (Fcons (Qsqlite3, Qnil), Vlibrary_cache); - - return false; + return sqlite3_initialized; #else /* !WINDOWSNT */ return true; #endif /* !WINDOWSNT */ @@ -674,12 +663,7 @@ DEFUN ("sqlite-available-p", Fsqlite_available_p, Ssqlite_available_p, 0, 0, 0, if (CONSP (found)) return XCDR (found); else - { - Lisp_Object status; - status = init_sqlite_functions () ? Qt : Qnil; - Vlibrary_cache = Fcons (Fcons (Qsqlite3, status), Vlibrary_cache); - return status; - } + return init_sqlite_functions () ? Qt : Qnil; # else return Qt; #endif -- cgit v1.2.1 From 4cdc59f33a0759a238184e67d59ea8e5143b7cc2 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Sat, 11 Dec 2021 11:54:44 +0200 Subject: Minor cleanups in sqlite.c * src/sqlite.c (Fsqlite_open): Signal an error if 'init_sqlite_functions' fails. Encode FILE using UTF-8. (Fsqlite_close, Fsqlite_execute, Fsqlite_select) (Fsqlite_load_extension): Doc fixes. (Fsqlite_load_extension): Encode MODULE using UTF-8. --- src/sqlite.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) (limited to 'src/sqlite.c') diff --git a/src/sqlite.c b/src/sqlite.c index 47829cbdf7f..87cebb16860 100644 --- a/src/sqlite.c +++ b/src/sqlite.c @@ -241,12 +241,14 @@ If FILE is nil, an in-memory database will be opened instead. */) (Lisp_Object file) { char *name; - init_sqlite_functions (); + if (!init_sqlite_functions ()) + xsignal1 (Qerror, build_string ("sqlite support is not available")); if (!NILP (file)) { CHECK_STRING (file); - name = xstrdup (SSDATA (Fexpand_file_name (file, Qnil))); + file = encode_string (Fexpand_file_name (file, Qnil)); + name = xstrdup (SSDATA (file)); } else /* In-memory database. These have to have different names to @@ -273,7 +275,7 @@ If FILE is nil, an in-memory database will be opened instead. */) } DEFUN ("sqlite-close", Fsqlite_close, Ssqlite_close, 1, 1, 0, - doc: /* Close the database DB. */) + doc: /* Close the sqlite database DB. */) (Lisp_Object db) { check_sqlite (db, false); @@ -341,12 +343,12 @@ bind_values (sqlite3 *db, sqlite3_stmt *stmt, Lisp_Object 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 list of values to bind when -executing a statement like +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 (?, ?, ...) -The number of affected rows is returned. */) +Value is the number of affected rows. */) (Lisp_Object db, Lisp_Object query, Lisp_Object values) { check_sqlite (db, false); @@ -463,8 +465,8 @@ column_names (sqlite3_stmt *stmt) DEFUN ("sqlite-select", Fsqlite_select, Ssqlite_select, 2, 4, 0, doc: /* Select data from the database DB that matches QUERY. -If VALUES is non-nil, they are values that will be interpolated into a -parametrised statement. +If VALUES is non-nil, it should be a list or a vector specifying the +values that will be interpolated into a parameterized statement. By default, the return value is a list where the first element is a list of column names, and the rest of the elements are the matching data. @@ -573,16 +575,18 @@ DEFUN ("sqlite-rollback", Fsqlite_rollback, Ssqlite_rollback, 1, 1, 0, #ifdef HAVE_SQLITE3_LOAD_EXTENSION DEFUN ("sqlite-load-extension", Fsqlite_load_extension, Ssqlite_load_extension, 2, 2, 0, - doc: /* Load an SQlite module into DB. -MODULE should be the file name of an SQlite module .so file. */) + doc: /* Load an SQlite MODULE into DB. +MODULE should be the name of an SQlite module's file, a +shared library in the system-dependent format and having a +system-dependent file-name extension. */) (Lisp_Object db, Lisp_Object module) { check_sqlite (db, false); CHECK_STRING (module); + Lisp_Object module_encoded = encode_string (Fexpand_file_name (module, Qnil)); sqlite3 *sdb = XSQLITE (db)->db; - int result = sqlite3_load_extension (sdb, - SSDATA (Fexpand_file_name (module, Qnil)), + int result = sqlite3_load_extension (sdb, SSDATA (module_encoded), NULL, NULL); if (result == SQLITE_OK) return Qt; -- cgit v1.2.1 From facddfc8036fa81dd2ad3a0ec2227ef8790f28eb Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Sat, 11 Dec 2021 12:05:54 +0200 Subject: * src/sqlite.c (row_to_value): Call 'make_unibyte_string'. --- src/sqlite.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/sqlite.c') diff --git a/src/sqlite.c b/src/sqlite.c index 87cebb16860..d92dcf723c9 100644 --- a/src/sqlite.c +++ b/src/sqlite.c @@ -428,8 +428,8 @@ row_to_value (sqlite3_stmt *stmt) case SQLITE_BLOB: v = code_convert_string_norecord - (make_string (sqlite3_column_blob (stmt, i), - sqlite3_column_bytes (stmt, i)), + (make_unibyte_string (sqlite3_column_blob (stmt, i), + sqlite3_column_bytes (stmt, i)), Qutf_8, false); break; @@ -440,8 +440,8 @@ row_to_value (sqlite3_stmt *stmt) case SQLITE_TEXT: v = code_convert_string_norecord - (make_string ((const char*)sqlite3_column_text (stmt, i), - sqlite3_column_bytes (stmt, i)), + (make_unibyte_string ((const char *)sqlite3_column_text (stmt, i), + sqlite3_column_bytes (stmt, i)), Qutf_8, false); break; } -- cgit v1.2.1 From c86b86f9a9ee3c42aed9ede794e8c3e19ce35ec5 Mon Sep 17 00:00:00 2001 From: Lars Ingebrigtsen Date: Mon, 13 Dec 2021 05:38:29 +0100 Subject: Introduce a new sqlite-locked-error * src/sqlite.c (Fsqlite_execute): Use it. (syms_of_sqlite): Introduce a new error for locked databases so that we can catch that condition on higher levels. --- src/sqlite.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'src/sqlite.c') diff --git a/src/sqlite.c b/src/sqlite.c index d92dcf723c9..38e939cd84a 100644 --- a/src/sqlite.c +++ b/src/sqlite.c @@ -400,7 +400,9 @@ Value is the number of affected rows. */) exit: if (errmsg != NULL) - xsignal1 (Qerror, build_string (errmsg)); + xsignal1 (ret == SQLITE_LOCKED || ret == SQLITE_BUSY? + Qsqlite_locked_error: Qerror, + build_string (errmsg)); return retval; } @@ -698,8 +700,15 @@ syms_of_sqlite (void) DEFSYM (Qfull, "full"); #endif defsubr (&Ssqlitep); - DEFSYM (Qsqlitep, "sqlitep"); defsubr (&Ssqlite_available_p); + + DEFSYM (Qsqlite_locked_error, "sqlite-locked-error"); + Fput (Qsqlite_locked_error, Qerror_conditions, + Fpurecopy (list2 (Qsqlite_locked_error, Qerror))); + Fput (Qsqlite_locked_error, Qerror_message, + build_pure_c_string ("Database locked")); + + DEFSYM (Qsqlitep, "sqlitep"); DEFSYM (Qfalse, "false"); DEFSYM (Qsqlite, "sqlite"); DEFSYM (Qsqlite3, "sqlite3"); -- cgit v1.2.1 From 9ce0fe5ef4d6e9c3dcd69237e0b5bb3fd46ee7da Mon Sep 17 00:00:00 2001 From: Lars Ingebrigtsen Date: Mon, 13 Dec 2021 06:08:09 +0100 Subject: Add a new `sqlite-pragma' command * doc/lispref/text.texi (Database): Document it. * src/sqlite.c (Fsqlite_pragma): Add a separate command for pragmas. These can be done via sqlite-execute, but it's less confusing to have them in a separate command. --- src/sqlite.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'src/sqlite.c') diff --git a/src/sqlite.c b/src/sqlite.c index 38e939cd84a..4968ce3f690 100644 --- a/src/sqlite.c +++ b/src/sqlite.c @@ -574,6 +574,17 @@ DEFUN ("sqlite-rollback", Fsqlite_rollback, Ssqlite_rollback, 1, 1, 0, return sqlite_exec (XSQLITE (db)->db, "rollback"); } +DEFUN ("sqlite-pragma", Fsqlite_pragma, Ssqlite_pragma, 2, 2, 0, + doc: /* Execute PRAGMA in DB. */) + (Lisp_Object db, Lisp_Object pragma) +{ + check_sqlite (db, false); + CHECK_STRING (pragma); + + return sqlite_exec (XSQLITE (db)->db, + SSDATA (concat2 (build_string ("PRAGMA "), pragma))); +} + #ifdef HAVE_SQLITE3_LOAD_EXTENSION DEFUN ("sqlite-load-extension", Fsqlite_load_extension, Ssqlite_load_extension, 2, 2, 0, @@ -689,6 +700,7 @@ syms_of_sqlite (void) defsubr (&Ssqlite_transaction); defsubr (&Ssqlite_commit); defsubr (&Ssqlite_rollback); + defsubr (&Ssqlite_pragma); #ifdef HAVE_SQLITE3_LOAD_EXTENSION defsubr (&Ssqlite_load_extension); #endif -- cgit v1.2.1 From 57efc5d1bb60f6abb4766387b708e20532638135 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Mon, 13 Dec 2021 15:35:31 +0200 Subject: * src/sqlite.c (Fsqlite_open, Fsqlite_load_extension): Use ENCODE_FILE. --- src/sqlite.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/sqlite.c') diff --git a/src/sqlite.c b/src/sqlite.c index 4968ce3f690..248ad478d57 100644 --- a/src/sqlite.c +++ b/src/sqlite.c @@ -247,7 +247,7 @@ If FILE is nil, an in-memory database will be opened instead. */) if (!NILP (file)) { CHECK_STRING (file); - file = encode_string (Fexpand_file_name (file, Qnil)); + file = ENCODE_FILE (Fexpand_file_name (file, Qnil)); name = xstrdup (SSDATA (file)); } else @@ -596,7 +596,7 @@ system-dependent file-name extension. */) { check_sqlite (db, false); CHECK_STRING (module); - Lisp_Object module_encoded = encode_string (Fexpand_file_name (module, Qnil)); + Lisp_Object module_encoded = ENCODE_FILE (Fexpand_file_name (module, Qnil)); sqlite3 *sdb = XSQLITE (db)->db; int result = sqlite3_load_extension (sdb, SSDATA (module_encoded), -- cgit v1.2.1 From 8c0f9be0d1ace6437d4c604b9af79b7b0006dec4 Mon Sep 17 00:00:00 2001 From: Lars Ingebrigtsen Date: Tue, 14 Dec 2021 09:29:06 +0100 Subject: Only allow SQLite extensions from an allowlist * src/sqlite.c (Fsqlite_load_extension): Only allow extensions from an allowlist. --- src/sqlite.c | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) (limited to 'src/sqlite.c') diff --git a/src/sqlite.c b/src/sqlite.c index 248ad478d57..428b84b21e7 100644 --- a/src/sqlite.c +++ b/src/sqlite.c @@ -591,16 +591,42 @@ DEFUN ("sqlite-load-extension", Fsqlite_load_extension, doc: /* Load an SQlite MODULE into DB. MODULE should be the name of an SQlite module's file, a shared library in the system-dependent format and having a -system-dependent file-name extension. */) +system-dependent file-name extension. + +Only modules on Emacs' list of allowed modules can be loaded. */) (Lisp_Object db, Lisp_Object module) { check_sqlite (db, false); CHECK_STRING (module); - Lisp_Object module_encoded = ENCODE_FILE (Fexpand_file_name (module, Qnil)); - sqlite3 *sdb = XSQLITE (db)->db; - int result = sqlite3_load_extension (sdb, SSDATA (module_encoded), - NULL, NULL); + /* Add names of useful and free modules here. */ + const char *allowlist[3] = { "pcre", "csvtable", NULL }; + char *name = SSDATA (Ffile_name_nondirectory (module)); + /* Possibly skip past a common prefix. */ + const char *prefix = "libsqlite3_mod_"; + if (!strncmp (name, prefix, strlen (prefix))) + name += strlen (prefix); + + bool do_allow = false; + for (const char **allow = allowlist; *allow; allow++) + { + if (strlen (*allow) < strlen (name) + && !strncmp (*allow, name, strlen (*allow)) + && (!strcmp (name + strlen (*allow), ".so") + || !strcmp (name + strlen (*allow), ".DLL"))) + { + do_allow = true; + break; + } + } + + if (!do_allow) + xsignal (Qerror, build_string ("Module name not on allowlist")); + + int result = sqlite3_load_extension + (XSQLITE (db)->db, + SSDATA (ENCODE_FILE (Fexpand_file_name (module, Qnil))), + NULL, NULL); if (result == SQLITE_OK) return Qt; return Qnil; -- cgit v1.2.1 From 823b6b8d260e6e5bc0c428c9b3d92b6822624761 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Sat, 1 Jan 2022 07:07:15 -0500 Subject: ; Add 2022 to copyright years. --- src/sqlite.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/sqlite.c') diff --git a/src/sqlite.c b/src/sqlite.c index 428b84b21e7..649cb382948 100644 --- a/src/sqlite.c +++ b/src/sqlite.c @@ -1,5 +1,5 @@ /* -Copyright (C) 2021 Free Software Foundation, Inc. +Copyright (C) 2021-2022 Free Software Foundation, Inc. This file is part of GNU Emacs. -- cgit v1.2.1