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