dcavalca / rpms / rpm

Forked from rpms/rpm a year ago
Clone
James Antill ee2eaf
From 34790c335fe6e5e1099c9320d7b3134398104120 Mon Sep 17 00:00:00 2001
James Antill ee2eaf
Message-Id: <34790c335fe6e5e1099c9320d7b3134398104120.1624429665.git.pmatilai@redhat.com>
James Antill ee2eaf
From: Panu Matilainen <pmatilai@redhat.com>
James Antill ee2eaf
Date: Wed, 23 Jun 2021 08:24:44 +0300
James Antill ee2eaf
Subject: [PATCH] Add read-only support for sqlite
James Antill ee2eaf
James Antill ee2eaf
Based on latest upstream sqlite backend version, chainsaw write support
James Antill ee2eaf
out and adjust for the infra differences (which there are more than a
James Antill ee2eaf
few) and add an error message instead.
James Antill ee2eaf
---
James Antill ee2eaf
 configure.ac         |  23 ++
James Antill ee2eaf
 lib/Makefile.am      |   6 +
James Antill ee2eaf
 lib/backend/dbi.c    |  14 +
James Antill ee2eaf
 lib/backend/dbi.h    |   5 +
James Antill ee2eaf
 lib/backend/sqlite.c | 659 +++++++++++++++++++++++++++++++++++++++++++
James Antill ee2eaf
 macros.in            |   1 +
James Antill ee2eaf
 6 files changed, 708 insertions(+)
James Antill ee2eaf
 create mode 100644 lib/backend/sqlite.c
James Antill ee2eaf
James Antill ee2eaf
diff --git a/configure.ac b/configure.ac
James Antill ee2eaf
index 3fcb3ff20..e04aced68 100644
James Antill ee2eaf
--- a/configure.ac
James Antill ee2eaf
+++ b/configure.ac
James Antill ee2eaf
@@ -589,6 +589,29 @@ AS_IF([test "$enable_ndb" = yes],[
James Antill ee2eaf
 ])
James Antill ee2eaf
 AM_CONDITIONAL([NDB], [test "$enable_ndb" = yes])
James Antill ee2eaf
 
James Antill ee2eaf
+# Check for SQLITE support
James Antill ee2eaf
+AC_ARG_ENABLE([sqlite],
James Antill ee2eaf
+              [AS_HELP_STRING([--enable-sqlite=@<:@yes/no/auto@:>@)],
James Antill ee2eaf
+                              [build with sqlite rpm database format support (default=yes)])],
James Antill ee2eaf
+              [enable_sqlite="$enableval"],
James Antill ee2eaf
+              [enable_sqlite=yes])
James Antill ee2eaf
+
James Antill ee2eaf
+AS_IF([test "x$enable_sqlite" != "xno"], [
James Antill ee2eaf
+  PKG_CHECK_MODULES([SQLITE], [sqlite3 >= 3.22.0], [have_sqlite=yes], [have_sqlite=no])
James Antill ee2eaf
+  AS_IF([test "$enable_sqlite" = "yes"], [
James Antill ee2eaf
+    if test "$have_sqlite" = "no"; then
James Antill ee2eaf
+      AC_MSG_ERROR([--enable-sqlite specified, but not available])
James Antill ee2eaf
+    fi
James Antill ee2eaf
+  ])
James Antill ee2eaf
+])
James Antill ee2eaf
+
James Antill ee2eaf
+if test "x$have_sqlite" = "xyes"; then
James Antill ee2eaf
+  AC_DEFINE([WITH_SQLITE], [1], [Define if SQLITE is available])
James Antill ee2eaf
+  SQLITE_REQUIRES=sqlite
James Antill ee2eaf
+  AC_SUBST(SQLITE_REQUIRES)
James Antill ee2eaf
+fi
James Antill ee2eaf
+AM_CONDITIONAL([SQLITE], [test "x$have_sqlite" = "xyes"])
James Antill ee2eaf
+
James Antill ee2eaf
 #=================
James Antill ee2eaf
 # Check for LMDB support
James Antill ee2eaf
 AC_ARG_ENABLE([lmdb],
James Antill ee2eaf
diff --git a/lib/Makefile.am b/lib/Makefile.am
James Antill ee2eaf
index baf3238ee..8a9fe77bd 100644
James Antill ee2eaf
--- a/lib/Makefile.am
James Antill ee2eaf
+++ b/lib/Makefile.am
James Antill ee2eaf
@@ -76,6 +76,12 @@ librpm_la_SOURCES += \
James Antill ee2eaf
 	backend/ndb/rpmxdb.h
James Antill ee2eaf
 endif
James Antill ee2eaf
 
James Antill ee2eaf
+if SQLITE
James Antill ee2eaf
+AM_CPPFLAGS += $(SQLITE_CFLAGS)
James Antill ee2eaf
+librpm_la_LIBADD += $(SQLITE_LIBS)
James Antill ee2eaf
+librpm_la_SOURCES += backend/sqlite.c
James Antill ee2eaf
+endif
James Antill ee2eaf
+
James Antill ee2eaf
 if LMDB
James Antill ee2eaf
 AM_CPPFLAGS += $(LMDB_CFLAGS)
James Antill ee2eaf
 librpm_la_LIBADD += $(LMDB_LIBS)
James Antill ee2eaf
diff --git a/lib/backend/dbi.c b/lib/backend/dbi.c
James Antill ee2eaf
index e99a5f2b2..dc3587f58 100644
James Antill ee2eaf
--- a/lib/backend/dbi.c
James Antill ee2eaf
+++ b/lib/backend/dbi.c
James Antill ee2eaf
@@ -48,6 +48,11 @@ dbDetectBackend(rpmdb rdb)
James Antill ee2eaf
     if (!strcmp(db_backend, "ndb")) {
James Antill ee2eaf
 	rdb->db_ops = &ndb_dbops;
James Antill ee2eaf
     } else
James Antill ee2eaf
+#endif
James Antill ee2eaf
+#ifdef WITH_SQLITE
James Antill ee2eaf
+    if (!strcmp(db_backend, "sqlite")) {
James Antill ee2eaf
+	rdb->db_ops = &sqlite_dbops;
James Antill ee2eaf
+    } else
James Antill ee2eaf
 #endif
James Antill ee2eaf
     {
James Antill ee2eaf
 	rdb->db_ops = &db3_dbops;
James Antill ee2eaf
@@ -75,6 +80,15 @@ dbDetectBackend(rpmdb rdb)
James Antill ee2eaf
     free(path);
James Antill ee2eaf
 #endif
James Antill ee2eaf
 
James Antill ee2eaf
+#ifdef WITH_SQLITE
James Antill ee2eaf
+    path = rstrscat(NULL, dbhome, "/rpmdb.sqlite", NULL);
James Antill ee2eaf
+    if (access(path, F_OK) == 0 && rdb->db_ops != &sqlite_dbops) {
James Antill ee2eaf
+	rdb->db_ops = &sqlite_dbops;
James Antill ee2eaf
+	rpmlog(RPMLOG_WARNING, _("Found SQLITE rpmdb.sqlite database while attempting %s backend: using sqlite backend.\n"), db_backend);
James Antill ee2eaf
+    }
James Antill ee2eaf
+    free(path);
James Antill ee2eaf
+#endif
James Antill ee2eaf
+
James Antill ee2eaf
     path = rstrscat(NULL, dbhome, "/Packages", NULL);
James Antill ee2eaf
     if (access(path, F_OK) == 0 && rdb->db_ops != &db3_dbops) {
James Antill ee2eaf
 	rdb->db_ops = &db3_dbops;
James Antill ee2eaf
diff --git a/lib/backend/dbi.h b/lib/backend/dbi.h
James Antill ee2eaf
index 02f49c8fd..ff2b4f974 100644
James Antill ee2eaf
--- a/lib/backend/dbi.h
James Antill ee2eaf
+++ b/lib/backend/dbi.h
James Antill ee2eaf
@@ -275,6 +275,11 @@ RPM_GNUC_INTERNAL
James Antill ee2eaf
 extern struct rpmdbOps_s lmdb_dbops;
James Antill ee2eaf
 #endif
James Antill ee2eaf
 
James Antill ee2eaf
+#if defined(WITH_SQLITE)
James Antill ee2eaf
+RPM_GNUC_INTERNAL
James Antill ee2eaf
+extern struct rpmdbOps_s sqlite_dbops;
James Antill ee2eaf
+#endif
James Antill ee2eaf
+
James Antill ee2eaf
 #ifdef __cplusplus
James Antill ee2eaf
 }
James Antill ee2eaf
 #endif
James Antill ee2eaf
diff --git a/lib/backend/sqlite.c b/lib/backend/sqlite.c
James Antill ee2eaf
new file mode 100644
James Antill ee2eaf
index 000000000..3caeba5f0
James Antill ee2eaf
--- /dev/null
James Antill ee2eaf
+++ b/lib/backend/sqlite.c
James Antill ee2eaf
@@ -0,0 +1,659 @@
James Antill ee2eaf
+#include "system.h"
James Antill ee2eaf
+
James Antill ee2eaf
+#include <sqlite3.h>
James Antill ee2eaf
+#include <fcntl.h>
James Antill ee2eaf
+
James Antill ee2eaf
+#include <rpm/rpmlog.h>
James Antill ee2eaf
+#include <rpm/rpmfileutil.h>
James Antill ee2eaf
+#include <rpm/rpmmacro.h>
James Antill ee2eaf
+#include "lib/rpmdb_internal.h"
James Antill ee2eaf
+
James Antill ee2eaf
+#include "debug.h"
James Antill ee2eaf
+
James Antill ee2eaf
+static const int sleep_ms = 50;
James Antill ee2eaf
+
James Antill ee2eaf
+struct dbiCursor_s {
James Antill ee2eaf
+    sqlite3 *sdb;
James Antill ee2eaf
+    sqlite3_stmt *stmt;
James Antill ee2eaf
+    const char *fmt;
James Antill ee2eaf
+    int flags;
James Antill ee2eaf
+    rpmTagVal tag;
James Antill ee2eaf
+    int ctype;
James Antill ee2eaf
+    struct dbiCursor_s *subc;
James Antill ee2eaf
+
James Antill ee2eaf
+    const void *key;
James Antill ee2eaf
+    unsigned int keylen;
James Antill ee2eaf
+};
James Antill ee2eaf
+
James Antill ee2eaf
+static int sqlexec(sqlite3 *sdb, const char *fmt, ...);
James Antill ee2eaf
+
James Antill ee2eaf
+static void rpm_match3(sqlite3_context *sctx, int argc, sqlite3_value **argv)
James Antill ee2eaf
+{
James Antill ee2eaf
+    int match = 0;
James Antill ee2eaf
+    if (argc == 3) {
James Antill ee2eaf
+	int b1len = sqlite3_value_bytes(argv[0]);
James Antill ee2eaf
+	int b2len = sqlite3_value_bytes(argv[1]);
James Antill ee2eaf
+	int n = sqlite3_value_int(argv[2]);
James Antill ee2eaf
+	if (b1len >= n && b2len >= n) {
James Antill ee2eaf
+	    const char *b1 = sqlite3_value_blob(argv[0]);
James Antill ee2eaf
+	    const char *b2 = sqlite3_value_blob(argv[1]);
James Antill ee2eaf
+	    match = (memcmp(b1, b2, n) == 0);
James Antill ee2eaf
+	}
James Antill ee2eaf
+    }
James Antill ee2eaf
+    sqlite3_result_int(sctx, match);
James Antill ee2eaf
+}
James Antill ee2eaf
+
James Antill ee2eaf
+static void errCb(void *data, int err, const char *msg)
James Antill ee2eaf
+{
James Antill ee2eaf
+    rpmdb rdb = data;
James Antill ee2eaf
+    rpmlog(RPMLOG_WARNING, "%s: %s: %s\n",
James Antill ee2eaf
+		rdb->db_descr, sqlite3_errstr(err), msg);
James Antill ee2eaf
+}
James Antill ee2eaf
+
James Antill ee2eaf
+static int dbiCursorReset(dbiCursor dbc)
James Antill ee2eaf
+{
James Antill ee2eaf
+    if (dbc->stmt) {
James Antill ee2eaf
+	sqlite3_reset(dbc->stmt);
James Antill ee2eaf
+	sqlite3_clear_bindings(dbc->stmt);
James Antill ee2eaf
+    }
James Antill ee2eaf
+    return 0;
James Antill ee2eaf
+}
James Antill ee2eaf
+
James Antill ee2eaf
+static int dbiCursorResult(dbiCursor dbc)
James Antill ee2eaf
+{
James Antill ee2eaf
+    int rc = sqlite3_errcode(dbc->sdb);
James Antill ee2eaf
+    int err = (rc != SQLITE_OK && rc != SQLITE_DONE && rc != SQLITE_ROW);
James Antill ee2eaf
+    if (err) {
James Antill ee2eaf
+	rpmlog(RPMLOG_ERR, "%s: %d: %s\n", sqlite3_sql(dbc->stmt),
James Antill ee2eaf
+		sqlite3_errcode(dbc->sdb), sqlite3_errmsg(dbc->sdb));
James Antill ee2eaf
+    }
James Antill ee2eaf
+    return err ? RPMRC_FAIL : RPMRC_OK;
James Antill ee2eaf
+}
James Antill ee2eaf
+
James Antill ee2eaf
+static int dbiCursorPrep(dbiCursor dbc, const char *fmt, ...)
James Antill ee2eaf
+{
James Antill ee2eaf
+    if (dbc->stmt == NULL) {
James Antill ee2eaf
+	char *cmd = NULL;
James Antill ee2eaf
+	va_list ap;
James Antill ee2eaf
+
James Antill ee2eaf
+	va_start(ap, fmt); 
James Antill ee2eaf
+	cmd = sqlite3_vmprintf(fmt, ap);
James Antill ee2eaf
+	va_end(ap);
James Antill ee2eaf
+
James Antill ee2eaf
+	sqlite3_prepare_v2(dbc->sdb, cmd, -1, &dbc->stmt, NULL);
James Antill ee2eaf
+	sqlite3_free(cmd);
James Antill ee2eaf
+    } else {
James Antill ee2eaf
+	dbiCursorReset(dbc);
James Antill ee2eaf
+    }
James Antill ee2eaf
+
James Antill ee2eaf
+    return dbiCursorResult(dbc);
James Antill ee2eaf
+}
James Antill ee2eaf
+
James Antill ee2eaf
+static int dbiCursorBindPkg(dbiCursor dbc, unsigned int hnum,
James Antill ee2eaf
+				void *blob, unsigned int bloblen)
James Antill ee2eaf
+{
James Antill ee2eaf
+    int rc = 0;
James Antill ee2eaf
+
James Antill ee2eaf
+    if (hnum)
James Antill ee2eaf
+	rc = sqlite3_bind_int(dbc->stmt, 1, hnum);
James Antill ee2eaf
+    else
James Antill ee2eaf
+	rc = sqlite3_bind_null(dbc->stmt, 1);
James Antill ee2eaf
+
James Antill ee2eaf
+    if (blob) {
James Antill ee2eaf
+	if (!rc)
James Antill ee2eaf
+	    rc = sqlite3_bind_blob(dbc->stmt, 2, blob, bloblen, NULL);
James Antill ee2eaf
+    }
James Antill ee2eaf
+    return dbiCursorResult(dbc);
James Antill ee2eaf
+}
James Antill ee2eaf
+
James Antill ee2eaf
+static int dbiCursorBindIdx(dbiCursor dbc, const void *key, int keylen,
James Antill ee2eaf
+				dbiIndexItem rec)
James Antill ee2eaf
+{
James Antill ee2eaf
+    int rc;
James Antill ee2eaf
+    if (dbc->ctype == SQLITE_TEXT) {
James Antill ee2eaf
+	rc = sqlite3_bind_text(dbc->stmt, 1, key, keylen, NULL);
James Antill ee2eaf
+    } else {
James Antill ee2eaf
+	rc = sqlite3_bind_blob(dbc->stmt, 1, key, keylen, NULL);
James Antill ee2eaf
+    }
James Antill ee2eaf
+
James Antill ee2eaf
+    if (rec) {
James Antill ee2eaf
+	if (!rc)
James Antill ee2eaf
+	    rc = sqlite3_bind_int(dbc->stmt, 2, rec->hdrNum);
James Antill ee2eaf
+	if (!rc)
James Antill ee2eaf
+	    rc = sqlite3_bind_int(dbc->stmt, 3, rec->tagNum);
James Antill ee2eaf
+    }
James Antill ee2eaf
+
James Antill ee2eaf
+    return dbiCursorResult(dbc);
James Antill ee2eaf
+}
James Antill ee2eaf
+
James Antill ee2eaf
+static int sqlite_init(rpmdb rdb, const char * dbhome)
James Antill ee2eaf
+{
James Antill ee2eaf
+    int rc = 0;
James Antill ee2eaf
+    char *dbfile = NULL;
James Antill ee2eaf
+
James Antill ee2eaf
+    if (rdb->db_dbenv == NULL) {
James Antill ee2eaf
+	dbfile = rpmGenPath(dbhome, "rpmdb.sqlite", NULL);
James Antill ee2eaf
+	sqlite3 *sdb = NULL;
James Antill ee2eaf
+	int xx, flags = 0;
James Antill ee2eaf
+	int retry_open = 1;
James Antill ee2eaf
+
James Antill ee2eaf
+	free(rdb->db_descr);
James Antill ee2eaf
+	rdb->db_descr = xstrdup("sqlite");
James Antill ee2eaf
+
James Antill ee2eaf
+	if ((rdb->db_mode & O_ACCMODE) == O_RDONLY)
James Antill ee2eaf
+	    flags |= SQLITE_OPEN_READONLY;
James Antill ee2eaf
+	else {
James Antill ee2eaf
+	    rpmlog(RPMLOG_ERR,
James Antill ee2eaf
+		_("unable to open sqlite database %s for writing, sqlite support is read-only\n"), dbfile);
James Antill ee2eaf
+	    rc = RPMRC_FAIL;
James Antill ee2eaf
+	    goto exit;
James Antill ee2eaf
+	}
James Antill ee2eaf
+
James Antill ee2eaf
+	while (retry_open--) {
James Antill ee2eaf
+	    xx = sqlite3_open_v2(dbfile, &sdb, flags, NULL);
James Antill ee2eaf
+	    /* Attempt to create if missing, discarding OPEN_READONLY (!) */
James Antill ee2eaf
+	    if (xx == SQLITE_CANTOPEN && (flags & SQLITE_OPEN_READONLY)) {
James Antill ee2eaf
+		/* Sqlite allocates resources even on failure to open (!) */
James Antill ee2eaf
+		sqlite3_close(sdb);
James Antill ee2eaf
+		flags &= ~SQLITE_OPEN_READONLY;
James Antill ee2eaf
+		flags |= (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
James Antill ee2eaf
+		retry_open++;
James Antill ee2eaf
+	    }
James Antill ee2eaf
+	}
James Antill ee2eaf
+
James Antill ee2eaf
+	if (xx != SQLITE_OK) {
James Antill ee2eaf
+	    rpmlog(RPMLOG_ERR, _("Unable to open sqlite database %s: %s\n"),
James Antill ee2eaf
+		    dbfile, sqlite3_errstr(xx));
James Antill ee2eaf
+	    rc = 1;
James Antill ee2eaf
+	    goto exit;
James Antill ee2eaf
+	}
James Antill ee2eaf
+
James Antill ee2eaf
+	sqlite3_create_function(sdb, "match", 3,
James Antill ee2eaf
+				(SQLITE_UTF8|SQLITE_DETERMINISTIC),
James Antill ee2eaf
+				NULL, rpm_match3, NULL, NULL);
James Antill ee2eaf
+
James Antill ee2eaf
+	sqlite3_busy_timeout(sdb, sleep_ms);
James Antill ee2eaf
+	sqlite3_config(SQLITE_CONFIG_LOG, errCb, rdb);
James Antill ee2eaf
+
James Antill ee2eaf
+	sqlexec(sdb, "PRAGMA secure_delete = OFF");
James Antill ee2eaf
+	sqlexec(sdb, "PRAGMA case_sensitive_like = ON");
James Antill ee2eaf
+
James Antill ee2eaf
+	if (sqlite3_db_readonly(sdb, NULL) == 0) {
James Antill ee2eaf
+	    if (sqlexec(sdb, "PRAGMA journal_mode = WAL") == 0) {
James Antill ee2eaf
+		int one = 1;
James Antill ee2eaf
+		/* Annoying but necessary to support non-privileged readers */
James Antill ee2eaf
+		sqlite3_file_control(sdb, NULL, SQLITE_FCNTL_PERSIST_WAL, &one);
James Antill ee2eaf
+
James Antill ee2eaf
+		if (!rpmExpandNumeric("%{?_flush_io}"))
James Antill ee2eaf
+		    sqlexec(sdb, "PRAGMA wal_autocheckpoint = 0");
James Antill ee2eaf
+	    }
James Antill ee2eaf
+	}
James Antill ee2eaf
+
James Antill ee2eaf
+	rdb->db_dbenv = sdb;
James Antill ee2eaf
+    }
James Antill ee2eaf
+    rdb->db_opens++;
James Antill ee2eaf
+
James Antill ee2eaf
+exit:
James Antill ee2eaf
+    free(dbfile);
James Antill ee2eaf
+    return rc;
James Antill ee2eaf
+}
James Antill ee2eaf
+
James Antill ee2eaf
+static int sqlite_fini(rpmdb rdb)
James Antill ee2eaf
+{
James Antill ee2eaf
+    int rc = 0;
James Antill ee2eaf
+    if (rdb) {
James Antill ee2eaf
+	sqlite3 *sdb = rdb->db_dbenv;
James Antill ee2eaf
+	if (rdb->db_opens > 1) {
James Antill ee2eaf
+	    rdb->db_opens--;
James Antill ee2eaf
+	} else {
James Antill ee2eaf
+	    if (sqlite3_db_readonly(sdb, NULL) == 0) {
James Antill ee2eaf
+		sqlexec(sdb, "PRAGMA optimize");
James Antill ee2eaf
+		sqlexec(sdb, "PRAGMA wal_checkpoint = TRUNCATE");
James Antill ee2eaf
+	    }
James Antill ee2eaf
+	    rdb->db_dbenv = NULL;
James Antill ee2eaf
+	    int xx = sqlite3_close(sdb);
James Antill ee2eaf
+	    rc = (xx != SQLITE_OK);
James Antill ee2eaf
+	}
James Antill ee2eaf
+    }
James Antill ee2eaf
+
James Antill ee2eaf
+    return rc;
James Antill ee2eaf
+}
James Antill ee2eaf
+
James Antill ee2eaf
+static int sqlexec(sqlite3 *sdb, const char *fmt, ...)
James Antill ee2eaf
+{
James Antill ee2eaf
+    int rc = 0;
James Antill ee2eaf
+    char *cmd = NULL;
James Antill ee2eaf
+    char *err = NULL;
James Antill ee2eaf
+    va_list ap;
James Antill ee2eaf
+
James Antill ee2eaf
+    va_start(ap, fmt);
James Antill ee2eaf
+    cmd = sqlite3_vmprintf(fmt, ap);
James Antill ee2eaf
+    va_end(ap);
James Antill ee2eaf
+
James Antill ee2eaf
+    /* sqlite3_exec() doesn't seeem to honor sqlite3_busy_timeout() */
James Antill ee2eaf
+    while ((rc = sqlite3_exec(sdb, cmd, NULL, NULL, &err)) == SQLITE_BUSY) {
James Antill ee2eaf
+	usleep(sleep_ms);
James Antill ee2eaf
+    }
James Antill ee2eaf
+
James Antill ee2eaf
+    if (rc)
James Antill ee2eaf
+	rpmlog(RPMLOG_ERR, "sqlite failure: %s: %s\n", cmd, err);
James Antill ee2eaf
+    else
James Antill ee2eaf
+	rpmlog(RPMLOG_DEBUG, "%s: %d\n", cmd, rc);
James Antill ee2eaf
+
James Antill ee2eaf
+    sqlite3_free(cmd);
James Antill ee2eaf
+
James Antill ee2eaf
+    return rc ? RPMRC_FAIL : RPMRC_OK;
James Antill ee2eaf
+}
James Antill ee2eaf
+
James Antill ee2eaf
+static int dbiExists(dbiIndex dbi)
James Antill ee2eaf
+{
James Antill ee2eaf
+    const char *col = (dbi->dbi_type == DBI_PRIMARY) ? "hnum" : "key";
James Antill ee2eaf
+    const char *tbl = dbi->dbi_file;
James Antill ee2eaf
+    int rc = sqlite3_table_column_metadata(dbi->dbi_db, NULL, tbl, col,
James Antill ee2eaf
+					   NULL, NULL, NULL, NULL, NULL);
James Antill ee2eaf
+    return (rc == 0);
James Antill ee2eaf
+}
James Antill ee2eaf
+
James Antill ee2eaf
+static int init_table(dbiIndex dbi, rpmTagVal tag)
James Antill ee2eaf
+{
James Antill ee2eaf
+    int rc = 0;
James Antill ee2eaf
+
James Antill ee2eaf
+    if (dbiExists(dbi))
James Antill ee2eaf
+	return 0;
James Antill ee2eaf
+
James Antill ee2eaf
+    if (dbi->dbi_type == DBI_PRIMARY) {
James Antill ee2eaf
+	rc = sqlexec(dbi->dbi_db,
James Antill ee2eaf
+			"CREATE TABLE IF NOT EXISTS '%q' ("
James Antill ee2eaf
+			    "hnum INTEGER PRIMARY KEY AUTOINCREMENT,"
James Antill ee2eaf
+			    "blob BLOB NOT NULL"
James Antill ee2eaf
+			")",
James Antill ee2eaf
+			dbi->dbi_file);
James Antill ee2eaf
+    } else {
James Antill ee2eaf
+	const char *keytype = (rpmTagGetClass(tag) == RPM_STRING_CLASS) ?
James Antill ee2eaf
+				"TEXT" : "BLOB";
James Antill ee2eaf
+	rc = sqlexec(dbi->dbi_db,
James Antill ee2eaf
+			"CREATE TABLE IF NOT EXISTS '%q' ("
James Antill ee2eaf
+			    "key '%q' NOT NULL, "
James Antill ee2eaf
+			    "hnum INTEGER NOT NULL, "
James Antill ee2eaf
+			    "idx INTEGER NOT NULL, "
James Antill ee2eaf
+			    "FOREIGN KEY (hnum) REFERENCES 'Packages'(hnum)"
James Antill ee2eaf
+			")",
James Antill ee2eaf
+			dbi->dbi_file, keytype);
James Antill ee2eaf
+    }
James Antill ee2eaf
+    if (!rc)
James Antill ee2eaf
+	dbi->dbi_flags |= DBI_CREATED;
James Antill ee2eaf
+
James Antill ee2eaf
+    return rc;
James Antill ee2eaf
+}
James Antill ee2eaf
+
James Antill ee2eaf
+static int create_index(sqlite3 *sdb, const char *table, const char *col)
James Antill ee2eaf
+{
James Antill ee2eaf
+    return sqlexec(sdb,
James Antill ee2eaf
+		"CREATE INDEX IF NOT EXISTS '%s_%s_idx' ON '%q'(%s ASC)",
James Antill ee2eaf
+		table, col, table, col);
James Antill ee2eaf
+}
James Antill ee2eaf
+
James Antill ee2eaf
+static int init_index(dbiIndex dbi, rpmTagVal tag)
James Antill ee2eaf
+{
James Antill ee2eaf
+    int rc = 0;
James Antill ee2eaf
+
James Antill ee2eaf
+    /* Can't create on readonly database, but things will still work */
James Antill ee2eaf
+    if (sqlite3_db_readonly(dbi->dbi_db, NULL) == 1)
James Antill ee2eaf
+	return 0;
James Antill ee2eaf
+
James Antill ee2eaf
+    if (dbi->dbi_type == DBI_SECONDARY) {
James Antill ee2eaf
+	int string = (rpmTagGetClass(tag) == RPM_STRING_CLASS);
James Antill ee2eaf
+	int array = (rpmTagGetReturnType(tag) == RPM_ARRAY_RETURN_TYPE);
James Antill ee2eaf
+	if (!rc && string)
James Antill ee2eaf
+	    rc = create_index(dbi->dbi_db, dbi->dbi_file, "key");
James Antill ee2eaf
+	if (!rc && array)
James Antill ee2eaf
+	    rc = create_index(dbi->dbi_db, dbi->dbi_file, "hnum");
James Antill ee2eaf
+    }
James Antill ee2eaf
+    return rc;
James Antill ee2eaf
+}
James Antill ee2eaf
+
James Antill ee2eaf
+static int sqlite_Open(rpmdb rdb, rpmDbiTagVal rpmtag, dbiIndex * dbip, int flags)
James Antill ee2eaf
+{
James Antill ee2eaf
+    int rc = sqlite_init(rdb, rpmdbHome(rdb));
James Antill ee2eaf
+
James Antill ee2eaf
+    if (!rc) {
James Antill ee2eaf
+	dbiIndex dbi = dbiNew(rdb, rpmtag);
James Antill ee2eaf
+	dbi->dbi_db = rdb->db_dbenv;
James Antill ee2eaf
+
James Antill ee2eaf
+	rc = init_table(dbi, rpmtag);
James Antill ee2eaf
+
James Antill ee2eaf
+	if (!rc && !(rdb->db_flags & RPMDB_FLAG_REBUILD))
James Antill ee2eaf
+	    rc = init_index(dbi, rpmtag);
James Antill ee2eaf
+
James Antill ee2eaf
+	if (!rc && dbip)
James Antill ee2eaf
+	    *dbip = dbi;
James Antill ee2eaf
+	else
James Antill ee2eaf
+	    dbiFree(dbi);
James Antill ee2eaf
+    }
James Antill ee2eaf
+
James Antill ee2eaf
+    return rc;
James Antill ee2eaf
+}
James Antill ee2eaf
+
James Antill ee2eaf
+static int sqlite_Close(dbiIndex dbi, unsigned int flags)
James Antill ee2eaf
+{
James Antill ee2eaf
+    rpmdb rdb = dbi->dbi_rpmdb;
James Antill ee2eaf
+    int rc = 0;
James Antill ee2eaf
+    if (rdb->db_flags & RPMDB_FLAG_REBUILD)
James Antill ee2eaf
+	rc = init_index(dbi, rpmTagGetValue(dbi->dbi_file));
James Antill ee2eaf
+    sqlite_fini(dbi->dbi_rpmdb);
James Antill ee2eaf
+    dbiFree(dbi);
James Antill ee2eaf
+    return rc;
James Antill ee2eaf
+}
James Antill ee2eaf
+
James Antill ee2eaf
+static int sqlite_Verify(dbiIndex dbi, unsigned int flags)
James Antill ee2eaf
+{
James Antill ee2eaf
+    int errors = -1;
James Antill ee2eaf
+    int key_errors = -1;
James Antill ee2eaf
+    sqlite3_stmt *s = NULL;
James Antill ee2eaf
+    const char *cmd = "PRAGMA integrity_check";
James Antill ee2eaf
+
James Antill ee2eaf
+    if (dbi->dbi_type == DBI_SECONDARY)
James Antill ee2eaf
+	return RPMRC_OK;
James Antill ee2eaf
+
James Antill ee2eaf
+    if (sqlite3_prepare_v2(dbi->dbi_db, cmd, -1, &s, NULL) == SQLITE_OK) {
James Antill ee2eaf
+	errors = 0;
James Antill ee2eaf
+	while (sqlite3_step(s) == SQLITE_ROW) {
James Antill ee2eaf
+	    const char *txt = (const char *)sqlite3_column_text(s, 0);
James Antill ee2eaf
+	    if (!rstreq(txt, "ok")) {
James Antill ee2eaf
+		errors++;
James Antill ee2eaf
+		rpmlog(RPMLOG_ERR, "verify: %s\n", txt);
James Antill ee2eaf
+	    }
James Antill ee2eaf
+	}
James Antill ee2eaf
+	sqlite3_finalize(s);
James Antill ee2eaf
+    } else {
James Antill ee2eaf
+	rpmlog(RPMLOG_ERR, "%s: %s\n", cmd, sqlite3_errmsg(dbi->dbi_db));
James Antill ee2eaf
+    }
James Antill ee2eaf
+
James Antill ee2eaf
+    /* No point checking higher-level errors if low-level errors exist */
James Antill ee2eaf
+    if (errors)
James Antill ee2eaf
+	goto exit;
James Antill ee2eaf
+
James Antill ee2eaf
+    cmd = "PRAGMA foreign_key_check";
James Antill ee2eaf
+    if (sqlite3_prepare_v2(dbi->dbi_db, cmd, -1, &s, NULL) == SQLITE_OK) {
James Antill ee2eaf
+	key_errors = 0;
James Antill ee2eaf
+	while (sqlite3_step(s) == SQLITE_ROW) {
James Antill ee2eaf
+	    key_errors++;
James Antill ee2eaf
+	    rpmlog(RPMLOG_ERR, "verify key: %s[%lld]\n",
James Antill ee2eaf
+				sqlite3_column_text(s, 0),
James Antill ee2eaf
+				sqlite3_column_int64(s, 1));
James Antill ee2eaf
+	}
James Antill ee2eaf
+	sqlite3_finalize(s);
James Antill ee2eaf
+    } else {
James Antill ee2eaf
+	rpmlog(RPMLOG_ERR, "%s: %s\n", cmd, sqlite3_errmsg(dbi->dbi_db));
James Antill ee2eaf
+    }
James Antill ee2eaf
+
James Antill ee2eaf
+exit:
James Antill ee2eaf
+
James Antill ee2eaf
+    return (errors == 0 && key_errors == 0) ? RPMRC_OK : RPMRC_FAIL;
James Antill ee2eaf
+}
James Antill ee2eaf
+
James Antill ee2eaf
+static void sqlite_SetFSync(rpmdb rdb, int enable)
James Antill ee2eaf
+{
James Antill ee2eaf
+    if (rdb->db_dbenv) {
James Antill ee2eaf
+	sqlexec(rdb->db_dbenv,
James Antill ee2eaf
+	    "PRAGMA synchronous = %s", enable ? "FULL" : "OFF");
James Antill ee2eaf
+    }
James Antill ee2eaf
+}
James Antill ee2eaf
+
James Antill ee2eaf
+static int sqlite_Ctrl(rpmdb rdb, dbCtrlOp ctrl)
James Antill ee2eaf
+{
James Antill ee2eaf
+    int rc = 0;
James Antill ee2eaf
+
James Antill ee2eaf
+    switch (ctrl) {
James Antill ee2eaf
+    case DB_CTRL_LOCK_RW:
James Antill ee2eaf
+	rc = sqlexec(rdb->db_dbenv, "SAVEPOINT 'rwlock'");
James Antill ee2eaf
+	break;
James Antill ee2eaf
+    case DB_CTRL_UNLOCK_RW:
James Antill ee2eaf
+	rc = sqlexec(rdb->db_dbenv, "RELEASE 'rwlock'");
James Antill ee2eaf
+	break;
James Antill ee2eaf
+    default:
James Antill ee2eaf
+	break;
James Antill ee2eaf
+    }
James Antill ee2eaf
+
James Antill ee2eaf
+    return rc;
James Antill ee2eaf
+}
James Antill ee2eaf
+
James Antill ee2eaf
+static dbiCursor sqlite_CursorInit(dbiIndex dbi, unsigned int flags)
James Antill ee2eaf
+{
James Antill ee2eaf
+    dbiCursor dbc = xcalloc(1, sizeof(*dbc));
James Antill ee2eaf
+    dbc->sdb = dbi->dbi_db;
James Antill ee2eaf
+    dbc->flags = flags;
James Antill ee2eaf
+    dbc->tag = rpmTagGetValue(dbi->dbi_file);
James Antill ee2eaf
+    if (rpmTagGetClass(dbc->tag) == RPM_STRING_CLASS) {
James Antill ee2eaf
+	dbc->ctype = SQLITE_TEXT;
James Antill ee2eaf
+    } else {
James Antill ee2eaf
+	dbc->ctype = SQLITE_BLOB;
James Antill ee2eaf
+    }
James Antill ee2eaf
+    if (dbc->flags & DBC_WRITE)
James Antill ee2eaf
+	sqlexec(dbc->sdb, "SAVEPOINT '%s'", dbi->dbi_file);
James Antill ee2eaf
+    return dbc;
James Antill ee2eaf
+}
James Antill ee2eaf
+
James Antill ee2eaf
+static dbiCursor sqlite_CursorFree(dbiIndex dbi, dbiCursor dbc)
James Antill ee2eaf
+{
James Antill ee2eaf
+    if (dbc) {
James Antill ee2eaf
+	sqlite3_finalize(dbc->stmt);
James Antill ee2eaf
+	if (dbc->subc)
James Antill ee2eaf
+	    dbiCursorFree(dbi, dbc->subc);
James Antill ee2eaf
+	if (dbc->flags & DBC_WRITE)
James Antill ee2eaf
+	    sqlexec(dbc->sdb, "RELEASE '%s'", dbi->dbi_file);
James Antill ee2eaf
+	free(dbc);
James Antill ee2eaf
+    }
James Antill ee2eaf
+    return NULL;
James Antill ee2eaf
+}
James Antill ee2eaf
+
James Antill ee2eaf
+static rpmRC sqlite_pkgdbNew(dbiIndex dbi, dbiCursor dbc, unsigned int *hdrNum)
James Antill ee2eaf
+{
James Antill ee2eaf
+    return RPMRC_FAIL;
James Antill ee2eaf
+}
James Antill ee2eaf
+
James Antill ee2eaf
+static rpmRC sqlite_pkgdbPut(dbiIndex dbi, dbiCursor dbc,  unsigned int hdrNum, unsigned char *hdrBlob, unsigned int hdrLen)
James Antill ee2eaf
+{
James Antill ee2eaf
+    return RPMRC_FAIL;
James Antill ee2eaf
+}
James Antill ee2eaf
+
James Antill ee2eaf
+static rpmRC sqlite_pkgdbDel(dbiIndex dbi, dbiCursor dbc,  unsigned int hdrNum)
James Antill ee2eaf
+{
James Antill ee2eaf
+    return RPMRC_FAIL;
James Antill ee2eaf
+}
James Antill ee2eaf
+
James Antill ee2eaf
+static rpmRC sqlite_stepPkg(dbiCursor dbc, unsigned char **hdrBlob, unsigned int *hdrLen)
James Antill ee2eaf
+{
James Antill ee2eaf
+    int rc = sqlite3_step(dbc->stmt);
James Antill ee2eaf
+
James Antill ee2eaf
+    if (rc == SQLITE_ROW) {
James Antill ee2eaf
+	if (hdrLen)
James Antill ee2eaf
+	    *hdrLen = sqlite3_column_bytes(dbc->stmt, 1);
James Antill ee2eaf
+	if (hdrBlob)
James Antill ee2eaf
+	    *hdrBlob = (void *) sqlite3_column_blob(dbc->stmt, 1);
James Antill ee2eaf
+    }
James Antill ee2eaf
+    return rc;
James Antill ee2eaf
+}
James Antill ee2eaf
+
James Antill ee2eaf
+static rpmRC sqlite_pkgdbByKey(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum, unsigned char **hdrBlob, unsigned int *hdrLen)
James Antill ee2eaf
+{
James Antill ee2eaf
+    int rc = dbiCursorPrep(dbc, "SELECT hnum, blob FROM '%q' WHERE hnum=?",
James Antill ee2eaf
+				dbi->dbi_file);
James Antill ee2eaf
+
James Antill ee2eaf
+    if (!rc)
James Antill ee2eaf
+	rc = dbiCursorBindPkg(dbc, hdrNum, NULL, 0);
James Antill ee2eaf
+
James Antill ee2eaf
+    if (!rc)
James Antill ee2eaf
+	rc = sqlite_stepPkg(dbc, hdrBlob, hdrLen);
James Antill ee2eaf
+
James Antill ee2eaf
+    return dbiCursorResult(dbc);
James Antill ee2eaf
+}
James Antill ee2eaf
+
James Antill ee2eaf
+static rpmRC sqlite_pkgdbIter(dbiIndex dbi, dbiCursor dbc,
James Antill ee2eaf
+				unsigned char **hdrBlob, unsigned int *hdrLen)
James Antill ee2eaf
+{
James Antill ee2eaf
+    int rc = RPMRC_OK;
James Antill ee2eaf
+    if (dbc->stmt == NULL) {
James Antill ee2eaf
+	rc = dbiCursorPrep(dbc, "SELECT hnum, blob FROM '%q'", dbi->dbi_file);
James Antill ee2eaf
+    }
James Antill ee2eaf
+
James Antill ee2eaf
+    if (!rc)
James Antill ee2eaf
+	rc = sqlite_stepPkg(dbc, hdrBlob, hdrLen);
James Antill ee2eaf
+
James Antill ee2eaf
+    if (rc == SQLITE_DONE) {
James Antill ee2eaf
+	rc = RPMRC_NOTFOUND;
James Antill ee2eaf
+    } else {
James Antill ee2eaf
+	rc = dbiCursorResult(dbc);
James Antill ee2eaf
+    }
James Antill ee2eaf
+
James Antill ee2eaf
+    return rc;
James Antill ee2eaf
+}
James Antill ee2eaf
+
James Antill ee2eaf
+static rpmRC sqlite_pkgdbGet(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum, unsigned char **hdrBlob, unsigned int *hdrLen)
James Antill ee2eaf
+{
James Antill ee2eaf
+    int rc;
James Antill ee2eaf
+
James Antill ee2eaf
+    if (hdrNum) {
James Antill ee2eaf
+	rc = sqlite_pkgdbByKey(dbi, dbc, hdrNum, hdrBlob, hdrLen);
James Antill ee2eaf
+    } else {
James Antill ee2eaf
+	rc = sqlite_pkgdbIter(dbi, dbc, hdrBlob, hdrLen);
James Antill ee2eaf
+    }
James Antill ee2eaf
+
James Antill ee2eaf
+    return rc;
James Antill ee2eaf
+}
James Antill ee2eaf
+
James Antill ee2eaf
+static unsigned int sqlite_pkgdbKey(dbiIndex dbi, dbiCursor dbc)
James Antill ee2eaf
+{
James Antill ee2eaf
+    return sqlite3_column_int(dbc->stmt, 0);
James Antill ee2eaf
+}
James Antill ee2eaf
+
James Antill ee2eaf
+static rpmRC sqlite_idxdbByKey(dbiIndex dbi, dbiCursor dbc,
James Antill ee2eaf
+			    const char *keyp, size_t keylen, int searchType,
James Antill ee2eaf
+			    dbiIndexSet *set)
James Antill ee2eaf
+{
James Antill ee2eaf
+    int rc = RPMRC_NOTFOUND;
James Antill ee2eaf
+
James Antill ee2eaf
+    if (searchType == DBC_PREFIX_SEARCH) {
James Antill ee2eaf
+	rc = dbiCursorPrep(dbc, "SELECT hnum, idx FROM '%q' "
James Antill ee2eaf
+				"WHERE MATCH(key,'%q',%d) "
James Antill ee2eaf
+				"ORDER BY key",
James Antill ee2eaf
+				dbi->dbi_file, keyp, keylen);
James Antill ee2eaf
+    } else {
James Antill ee2eaf
+	rc = dbiCursorPrep(dbc, "SELECT hnum, idx FROM '%q' WHERE key=?",
James Antill ee2eaf
+			dbi->dbi_file);
James Antill ee2eaf
+	if (!rc)
James Antill ee2eaf
+	    rc = dbiCursorBindIdx(dbc, keyp, keylen, NULL);
James Antill ee2eaf
+    }
James Antill ee2eaf
+
James Antill ee2eaf
+
James Antill ee2eaf
+    if (!rc) {
James Antill ee2eaf
+	while ((rc = sqlite3_step(dbc->stmt)) == SQLITE_ROW) {
James Antill ee2eaf
+	    unsigned int hnum = sqlite3_column_int(dbc->stmt, 0);
James Antill ee2eaf
+	    unsigned int tnum = sqlite3_column_int(dbc->stmt, 1);
James Antill ee2eaf
+
James Antill ee2eaf
+	    if (*set == NULL)
James Antill ee2eaf
+		*set = dbiIndexSetNew(5);
James Antill ee2eaf
+	    dbiIndexSetAppendOne(*set, hnum, tnum, 0);
James Antill ee2eaf
+	}
James Antill ee2eaf
+    }
James Antill ee2eaf
+
James Antill ee2eaf
+    if (rc == SQLITE_DONE) {
James Antill ee2eaf
+	rc = (*set) ? RPMRC_OK : RPMRC_NOTFOUND;
James Antill ee2eaf
+    } else {
James Antill ee2eaf
+	rc = dbiCursorResult(dbc);
James Antill ee2eaf
+    }
James Antill ee2eaf
+
James Antill ee2eaf
+    return rc;
James Antill ee2eaf
+}
James Antill ee2eaf
+
James Antill ee2eaf
+static rpmRC sqlite_idxdbIter(dbiIndex dbi, dbiCursor dbc, dbiIndexSet *set)
James Antill ee2eaf
+{
James Antill ee2eaf
+    int rc = RPMRC_OK;
James Antill ee2eaf
+
James Antill ee2eaf
+    if (dbc->stmt == NULL) {
James Antill ee2eaf
+	rc = dbiCursorPrep(dbc, "SELECT DISTINCT key FROM '%q' ORDER BY key",
James Antill ee2eaf
+				dbi->dbi_file);
James Antill ee2eaf
+	if (set)
James Antill ee2eaf
+	    dbc->subc = dbiCursorInit(dbi, 0);
James Antill ee2eaf
+    }
James Antill ee2eaf
+
James Antill ee2eaf
+    if (!rc)
James Antill ee2eaf
+	rc = sqlite3_step(dbc->stmt);
James Antill ee2eaf
+
James Antill ee2eaf
+    if (rc == SQLITE_ROW) {
James Antill ee2eaf
+	if (dbc->ctype == SQLITE_TEXT) {
James Antill ee2eaf
+	    dbc->key = sqlite3_column_text(dbc->stmt, 0);
James Antill ee2eaf
+	} else {
James Antill ee2eaf
+	    dbc->key = sqlite3_column_blob(dbc->stmt, 0);
James Antill ee2eaf
+	}
James Antill ee2eaf
+	dbc->keylen = sqlite3_column_bytes(dbc->stmt, 0);
James Antill ee2eaf
+	if (dbc->subc) {
James Antill ee2eaf
+	    rc = sqlite_idxdbByKey(dbi, dbc->subc, dbc->key, dbc->keylen,
James Antill ee2eaf
+				    DBC_NORMAL_SEARCH, set);
James Antill ee2eaf
+	} else {
James Antill ee2eaf
+	    rc = RPMRC_OK;
James Antill ee2eaf
+	}
James Antill ee2eaf
+    } else if (rc == SQLITE_DONE) {
James Antill ee2eaf
+	rc = RPMRC_NOTFOUND;
James Antill ee2eaf
+    } else {
James Antill ee2eaf
+	rc = dbiCursorResult(dbc);
James Antill ee2eaf
+    }
James Antill ee2eaf
+
James Antill ee2eaf
+    return rc;
James Antill ee2eaf
+}
James Antill ee2eaf
+
James Antill ee2eaf
+static rpmRC sqlite_idxdbGet(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, dbiIndexSet *set, int searchType)
James Antill ee2eaf
+{
James Antill ee2eaf
+    int rc;
James Antill ee2eaf
+    if (keyp) {
James Antill ee2eaf
+	rc = sqlite_idxdbByKey(dbi, dbc, keyp, keylen, searchType, set);
James Antill ee2eaf
+    } else {
James Antill ee2eaf
+	rc = sqlite_idxdbIter(dbi, dbc, set);
James Antill ee2eaf
+    }
James Antill ee2eaf
+
James Antill ee2eaf
+    return rc;
James Antill ee2eaf
+}
James Antill ee2eaf
+
James Antill ee2eaf
+static rpmRC sqlite_idxdbPut(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, dbiIndexItem rec)
James Antill ee2eaf
+{
James Antill ee2eaf
+    return RPMRC_FAIL;
James Antill ee2eaf
+}
James Antill ee2eaf
+
James Antill ee2eaf
+static rpmRC sqlite_idxdbDel(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, dbiIndexItem rec)
James Antill ee2eaf
+{
James Antill ee2eaf
+    return RPMRC_FAIL;
James Antill ee2eaf
+}
James Antill ee2eaf
+
James Antill ee2eaf
+static const void * sqlite_idxdbKey(dbiIndex dbi, dbiCursor dbc, unsigned int *keylen)
James Antill ee2eaf
+{
James Antill ee2eaf
+    const void *key = NULL;
James Antill ee2eaf
+    if (dbc) {
James Antill ee2eaf
+	key = dbc->key;
James Antill ee2eaf
+	if (key && keylen)
James Antill ee2eaf
+	    *keylen = dbc->keylen;
James Antill ee2eaf
+    }
James Antill ee2eaf
+    return key;
James Antill ee2eaf
+}
James Antill ee2eaf
+
James Antill ee2eaf
+struct rpmdbOps_s sqlite_dbops = {
James Antill ee2eaf
+    .open	= sqlite_Open,
James Antill ee2eaf
+    .close	= sqlite_Close,
James Antill ee2eaf
+    .verify	= sqlite_Verify,
James Antill ee2eaf
+    .setFSync	= sqlite_SetFSync,
James Antill ee2eaf
+    .ctrl	= sqlite_Ctrl,
James Antill ee2eaf
+
James Antill ee2eaf
+    .cursorInit	= sqlite_CursorInit,
James Antill ee2eaf
+    .cursorFree	= sqlite_CursorFree,
James Antill ee2eaf
+
James Antill ee2eaf
+    .pkgdbPut	= sqlite_pkgdbPut,
James Antill ee2eaf
+    .pkgdbDel	= sqlite_pkgdbDel,
James Antill ee2eaf
+    .pkgdbGet	= sqlite_pkgdbGet,
James Antill ee2eaf
+    .pkgdbKey	= sqlite_pkgdbKey,
James Antill ee2eaf
+    .pkgdbNew	= sqlite_pkgdbNew,
James Antill ee2eaf
+
James Antill ee2eaf
+    .idxdbGet	= sqlite_idxdbGet,
James Antill ee2eaf
+    .idxdbPut	= sqlite_idxdbPut,
James Antill ee2eaf
+    .idxdbDel	= sqlite_idxdbDel,
James Antill ee2eaf
+    .idxdbKey	= sqlite_idxdbKey
James Antill ee2eaf
+};
James Antill ee2eaf
+
James Antill ee2eaf
diff --git a/macros.in b/macros.in
James Antill ee2eaf
index a6069ee4d..9ad3d60ef 100644
James Antill ee2eaf
--- a/macros.in
James Antill ee2eaf
+++ b/macros.in
James Antill ee2eaf
@@ -620,6 +620,7 @@ package or when debugging this package.\
James Antill ee2eaf
 # bdb Berkeley DB
James Antill ee2eaf
 # lmdb Lightning Memory-mapped Database
James Antill ee2eaf
 # ndb new data base format
James Antill ee2eaf
+# sqlite Sqlite database (read-only in this version!)
James Antill ee2eaf
 #
James Antill ee2eaf
 %_db_backend	      bdb
James Antill ee2eaf
 
James Antill ee2eaf
-- 
James Antill ee2eaf
2.31.1
James Antill ee2eaf