summaryrefslogtreecommitdiff
path: root/lib/backend
diff options
context:
space:
mode:
Diffstat (limited to 'lib/backend')
-rw-r--r--lib/backend/db3.c1184
-rw-r--r--lib/backend/dbconfig.c473
-rw-r--r--lib/backend/sqlite.c1386
3 files changed, 3043 insertions, 0 deletions
diff --git a/lib/backend/db3.c b/lib/backend/db3.c
new file mode 100644
index 000000000..e1983de41
--- /dev/null
+++ b/lib/backend/db3.c
@@ -0,0 +1,1184 @@
+/** \ingroup db3
+ * \file rpmdb/db3.c
+ */
+
+static int _debug = 1; /* XXX if < 0 debugging, > 0 unusual error returns */
+
+#include "system.h"
+
+#if defined(HAVE_FTOK) && defined(HAVE_SYS_IPC_H)
+#include <sys/ipc.h>
+#endif
+
+#include <rpm/rpmtag.h>
+#include <rpm/rpmmacro.h>
+#include <rpm/rpmfileutil.h> /* rpmioMkPath */
+#include <rpm/rpmlog.h>
+
+#include "rpmdb/rpmdb_internal.h"
+
+#include "debug.h"
+
+#if !defined(DB_CLIENT) /* XXX db-4.2.42 retrofit */
+#define DB_CLIENT DB_RPCCLIENT
+#endif
+
+
+/** \ingroup dbi
+ * Hash database statistics.
+ */
+struct dbiHStats_s {
+ unsigned int hash_magic; /*!< hash database magic number. */
+ unsigned int hash_version; /*!< version of the hash database. */
+ unsigned int hash_nkeys; /*!< no. of unique keys in the database. */
+ unsigned int hash_ndata; /*!< no. of key/data pairs in the database. */
+ unsigned int hash_pagesize; /*!< db page (and bucket) size, in bytes. */
+ unsigned int hash_nelem; /*!< estimated size of the hash table. */
+ unsigned int hash_ffactor; /*!< no. of items per bucket. */
+ unsigned int hash_buckets; /*!< no. of hash buckets. */
+ unsigned int hash_free; /*!< no. of pages on the free list. */
+ unsigned int hash_bfree; /*!< no. of bytes free on bucket pages. */
+ unsigned int hash_bigpages; /*!< no. of big key/data pages. */
+ unsigned int hash_big_bfree;/*!< no. of bytes free on big item pages. */
+ unsigned int hash_overflows;/*!< no. of overflow pages. */
+ unsigned int hash_ovfl_free;/*!< no. of bytes free on overflow pages. */
+ unsigned int hash_dup; /*!< no. of duplicate pages. */
+ unsigned int hash_dup_free; /*!< no. bytes free on duplicate pages. */
+};
+
+/** \ingroup dbi
+ * B-tree database statistics.
+ */
+struct dbiBStats_s {
+ unsigned int bt_magic; /*!< btree database magic. */
+ unsigned int bt_version; /*!< version of the btree database. */
+ unsigned int bt_nkeys; /*!< no. of unique keys in the database. */
+ unsigned int bt_ndata; /*!< no. of key/data pairs in the database. */
+ unsigned int bt_pagesize; /*!< database page size, in bytes. */
+ unsigned int bt_minkey; /*!< minimum keys per page. */
+ unsigned int bt_re_len; /*!< length of fixed-length records. */
+ unsigned int bt_re_pad; /*!< padding byte for fixed-length records. */
+ unsigned int bt_levels; /*!< no. of levels in the database. */
+ unsigned int bt_int_pg; /*!< no. of database internal pages. */
+ unsigned int bt_leaf_pg; /*!< no. of database leaf pages. */
+ unsigned int bt_dup_pg; /*!< no. of database duplicate pages. */
+ unsigned int bt_over_pg; /*!< no. of database overflow pages. */
+ unsigned int bt_free; /*!< no. of pages on the free list. */
+ unsigned int bt_int_pgfree; /*!< no. of bytes free in internal pages. */
+ unsigned int bt_leaf_pgfree;/*!< no. of bytes free in leaf pages. */
+ unsigned int bt_dup_pgfree; /*!< no. of bytes free in duplicate pages. */
+ unsigned int bt_over_pgfree;/*!< no. of bytes free in overflow pages. */
+};
+
+#ifdef NOTNOW
+static const char * bfstring(unsigned int x, const char * xbf)
+{
+ const char * s = xbf;
+ static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+ static char buf[256];
+ char * t, * te;
+ unsigned radix;
+ unsigned c, i, k;
+
+ radix = (s != NULL ? *s++ : 16);
+
+ if (radix <= 1 || radix >= 32)
+ radix = 16;
+
+ t = buf;
+ switch (radix) {
+ case 8: *t++ = '0'; break;
+ case 16: *t++ = '0'; *t++ = 'x'; break;
+ }
+
+ i = 0;
+ k = x;
+ do { i++; k /= radix; } while (k);
+
+ te = t + i;
+
+ k = x;
+ do { --i; t[i] = digits[k % radix]; k /= radix; } while (k);
+
+ t = te;
+ i = '<';
+ if (s != NULL)
+ while ((c = *s++) != '\0') {
+ if (c > ' ') continue;
+
+ k = (1 << (c - 1));
+ if (!(x & k)) continue;
+
+ if (t == te) *t++ = '=';
+
+ *t++ = i;
+ i = ',';
+ while (*s > ' ')
+ *t++ = *s++;
+ }
+ if (t > te) *t++ = '>';
+ *t = '\0';
+ return buf;
+}
+
+static const char * dbtFlags =
+ "\20\1APPMALLOC\2ISSET\3MALLOC\4PARTIAL\5REALLOC\6USERMEM\7DUPOK";
+
+static const char * dbenvOpenFlags =
+ "\20\1CREATE\2NO_EXCEPTIONS\3FORCE\4NOMMAP\5RDONLY\6RECOVER\7THREAD\10TXN_NOSYNC\11USE_ENVIRON\12USE_ENVIRON_ROOT\13CDB\14LOCK\15LOG\16MPOOL\17TXN\20JOINENV\21LOCKDOWN\22PRIVATE\23RECOVER_FATAL\24SYSTEM_MEM";
+
+static const char * dbOpenFlags =
+ "\20\1CREATE\2NO_EXCEPTIONS\3FORCE\4NOMMAP\5RDONLY\6RECOVER\7THREAD\10TXN_NOSYNC\11USE_ENVIRON\12USE_ENVIRON_ROOT\13EXCL\14FCNTL_LOCKING\15RDWRMASTER\16TRUNCATE\17EXTENT\20APPLY_LOGREG";
+
+static const char * dbenvSetFlags =
+ "\20\1CREATE\2NO_EXCEPTIONS\3FORCE\4NOMMAP\5RDONLY\6RECOVER\7THREAD\10TXN_NOSYNC\11USE_ENVIRON\12USE_ENVIRON_ROOT\13CDB_ALLDB\14NOLOCKING\15NOPANIC\16PANIC_ENV\17REGION_INIT\20YIELDCPU";
+
+static const char * dbSetFlags =
+ "\20\1DUP\2DUPSORT\3RECNUM\4RENUMBER\5REVSPLITOFF\6SNAPSHOT";
+
+static const char * dbiModeFlags =
+ "\20\1WRONLY\2RDWR\7CREAT\10EXCL\11NOCTTY\12TRUNC\13APPEND\14NONBLOCK\15SYNC\16ASYNC\17DIRECT\20LARGEFILE\21DIRECTORY\22NOFOLLOW";
+#endif /* NOTNOW */
+
+
+static int cvtdberr(dbiIndex dbi, const char * msg, int error, int printit)
+{
+ int rc = error;
+
+ if (printit && rc) {
+ if (msg)
+ rpmlog(RPMLOG_ERR, _("db%d error(%d) from %s: %s\n"),
+ dbi->dbi_api, rc, msg, db_strerror(error));
+ else
+ rpmlog(RPMLOG_ERR, _("db%d error(%d): %s\n"),
+ dbi->dbi_api, rc, db_strerror(error));
+ }
+
+ return rc;
+}
+
+static int db_fini(dbiIndex dbi, const char * dbhome,
+ const char * dbfile,
+ const char * dbsubfile)
+{
+ rpmdb rpmdb = dbi->dbi_rpmdb;
+ DB_ENV * dbenv = rpmdb->db_dbenv;
+ int rc;
+
+ if (dbenv == NULL)
+ return 0;
+
+ rc = dbenv->close(dbenv, 0);
+ rc = cvtdberr(dbi, "dbenv->close", rc, _debug);
+
+ if (dbfile)
+ rpmlog(RPMLOG_DEBUG, "closed db environment %s/%s\n",
+ dbhome, dbfile);
+
+ if (rpmdb->db_remove_env) {
+ int xx;
+
+ xx = db_env_create(&dbenv, 0);
+ xx = cvtdberr(dbi, "db_env_create", xx, _debug);
+ xx = dbenv->remove(dbenv, dbhome, 0);
+ xx = cvtdberr(dbi, "dbenv->remove", xx, _debug);
+
+ if (dbfile)
+ rpmlog(RPMLOG_DEBUG, "removed db environment %s/%s\n",
+ dbhome, dbfile);
+
+ }
+ return rc;
+}
+
+static int db3_fsync_disable(int fd)
+{
+ return 0;
+}
+
+#if (DB_VERSION_MAJOR >= 4 && DB_VERSION_MINOR >= 5)
+/*
+ * dbenv->failchk() callback method for determining is the given pid/tid
+ * is alive. We only care about pid's though.
+ */
+static int db3isalive(DB_ENV *dbenv, pid_t pid, db_threadid_t tid, uint32_t flags)
+{
+ int alive = 0;
+
+ if (pid == getpid()) {
+ alive = 1;
+ } else if (kill(pid, 0) == 0) {
+ alive = 1;
+ /* only existing processes can fail with EPERM */
+ } else if (errno == EPERM) {
+ alive = 1;
+ }
+
+ return alive;
+}
+#endif
+
+static int db_init(dbiIndex dbi, const char * dbhome,
+ const char * dbfile,
+ const char * dbsubfile,
+ DB_ENV ** dbenvp)
+{
+ rpmdb rpmdb = dbi->dbi_rpmdb;
+ DB_ENV *dbenv = NULL;
+ int eflags;
+ int rc;
+
+ if (dbenvp == NULL)
+ return 1;
+
+ /* XXX HACK */
+ if (rpmdb->db_errfile == NULL)
+ rpmdb->db_errfile = stderr;
+
+ eflags = (dbi->dbi_oeflags | dbi->dbi_eflags);
+ if (eflags & DB_JOINENV) eflags &= DB_JOINENV;
+
+ if (dbfile) {
+ char *dbiflags = prDbiOpenFlags(eflags, 1);
+ rpmlog(RPMLOG_DEBUG, "opening db environment %s/%s %s\n",
+ dbhome, dbfile, dbiflags);
+ free(dbiflags);
+ }
+
+ /* XXX Can't do RPC w/o host. */
+ if (dbi->dbi_host == NULL)
+ dbi->dbi_ecflags &= ~DB_CLIENT;
+
+ /* XXX Set a default shm_key. */
+ if ((dbi->dbi_eflags & DB_SYSTEM_MEM) && dbi->dbi_shmkey == 0) {
+#if defined(HAVE_FTOK)
+ dbi->dbi_shmkey = ftok(dbhome, 0);
+#else
+ dbi->dbi_shmkey = 0x44631380;
+#endif
+ }
+
+ rc = db_env_create(&dbenv, dbi->dbi_ecflags);
+ rc = cvtdberr(dbi, "db_env_create", rc, _debug);
+ if (dbenv == NULL || rc)
+ goto errxit;
+
+ { int xx;
+
+ /* 4.1: dbenv->set_app_dispatch(???) */
+ /* 4.1: dbenv->set_alloc(???) */
+ /* 4.1: dbenv->set_data_dir(???) */
+ /* 4.1: dbenv->set_encrypt(???) */
+
+ dbenv->set_errcall(dbenv, (void *) rpmdb->db_errcall);
+ dbenv->set_errfile(dbenv, rpmdb->db_errfile);
+ dbenv->set_errpfx(dbenv, rpmdb->db_errpfx);
+
+ /* 4.1: dbenv->set_feedback(???) */
+ /* 4.1: dbenv->set_flags(???) */
+
+ /* dbenv->set_paniccall(???) */
+
+#if (DB_VERSION_MAJOR >= 4 && DB_VERSION_MINOR >= 5)
+ /*
+ * These enable automatic stale lock removal.
+ * thread_count 8 is some kind of "magic minimum" value...
+ */
+ dbenv->set_thread_count(dbenv, 8);
+ dbenv->set_isalive(dbenv, db3isalive);
+#endif
+
+ if ((dbi->dbi_ecflags & DB_CLIENT) && dbi->dbi_host) {
+ const char * home;
+ int retry = 0;
+
+ if ((home = strrchr(dbhome, '/')) != NULL)
+ dbhome = ++home;
+
+ while (retry++ < 5) {
+ xx = dbenv->set_rpc_server(dbenv, NULL, dbi->dbi_host,
+ dbi->dbi_cl_timeout, dbi->dbi_sv_timeout, 0);
+ xx = cvtdberr(dbi, "dbenv->set_server", xx, _debug);
+ if (!xx)
+ break;
+ (void) sleep(15);
+ }
+ } else {
+#if !(DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3)
+ xx = dbenv->set_verbose(dbenv, DB_VERB_CHKPOINT,
+ (dbi->dbi_verbose & DB_VERB_CHKPOINT));
+#endif
+ xx = dbenv->set_verbose(dbenv, DB_VERB_DEADLOCK,
+ (dbi->dbi_verbose & DB_VERB_DEADLOCK));
+ xx = dbenv->set_verbose(dbenv, DB_VERB_RECOVERY,
+ (dbi->dbi_verbose & DB_VERB_RECOVERY));
+ xx = dbenv->set_verbose(dbenv, DB_VERB_WAITSFOR,
+ (dbi->dbi_verbose & DB_VERB_WAITSFOR));
+
+ if (dbi->dbi_mmapsize) {
+ xx = dbenv->set_mp_mmapsize(dbenv, dbi->dbi_mmapsize);
+ xx = cvtdberr(dbi, "dbenv->set_mp_mmapsize", xx, _debug);
+ }
+ if (dbi->dbi_tmpdir) {
+ const char * root;
+ char * tmpdir;
+
+ root = (dbi->dbi_root ? dbi->dbi_root : rpmdb->db_root);
+ if ((root[0] == '/' && root[1] == '\0') || rpmdb->db_chrootDone)
+ root = NULL;
+ tmpdir = rpmGenPath(root, dbi->dbi_tmpdir, NULL);
+ xx = dbenv->set_tmp_dir(dbenv, tmpdir);
+ xx = cvtdberr(dbi, "dbenv->set_tmp_dir", xx, _debug);
+ tmpdir = _free(tmpdir);
+ }
+ }
+
+ /* dbenv->set_lk_conflicts(???) */
+ /* dbenv->set_lk_detect(???) */
+ /* 4.1: dbenv->set_lk_max_lockers(???) */
+ /* 4.1: dbenv->set_lk_max_locks(???) */
+ /* 4.1: dbenv->set_lk_max_objects(???) */
+
+ /* 4.1: dbenv->set_lg_bsize(???) */
+ /* 4.1: dbenv->set_lg_dir(???) */
+ /* 4.1: dbenv->set_lg_max(???) */
+ /* 4.1: dbenv->set_lg_regionmax(???) */
+
+ if (dbi->dbi_cachesize) {
+ xx = dbenv->set_cachesize(dbenv, 0, dbi->dbi_cachesize, 0);
+ xx = cvtdberr(dbi, "dbenv->set_cachesize", xx, _debug);
+ }
+
+ /* 4.1 dbenv->set_timeout(???) */
+ /* dbenv->set_tx_max(???) */
+ /* 4.1: dbenv->set_tx_timestamp(???) */
+ /* dbenv->set_tx_recover(???) */
+
+ /* dbenv->set_rep_transport(???) */
+ /* dbenv->set_rep_limit(???) */
+
+ if (dbi->dbi_no_fsync) {
+ xx = db_env_set_func_fsync(db3_fsync_disable);
+ xx = cvtdberr(dbi, "db_env_set_func_fsync", xx, _debug);
+ }
+
+ if (dbi->dbi_shmkey) {
+ xx = dbenv->set_shm_key(dbenv, dbi->dbi_shmkey);
+ xx = cvtdberr(dbi, "dbenv->set_shm_key", xx, _debug);
+ }
+ }
+
+ rc = (dbenv->open)(dbenv, dbhome, eflags, dbi->dbi_perms);
+ rc = cvtdberr(dbi, "dbenv->open", rc, _debug);
+ if (rc)
+ goto errxit;
+
+#if (DB_VERSION_MAJOR >= 4 && DB_VERSION_MINOR >= 5)
+ /* stale lock removal */
+ rc = dbenv->failchk(dbenv, 0);
+#endif
+
+ *dbenvp = dbenv;
+
+ return 0;
+
+errxit:
+ if (dbenv) {
+ int xx;
+ xx = dbenv->close(dbenv, 0);
+ xx = cvtdberr(dbi, "dbenv->close", xx, _debug);
+ }
+ return rc;
+}
+
+static int db3sync(dbiIndex dbi, unsigned int flags)
+{
+ DB * db = dbi->dbi_db;
+ int rc = 0;
+ int _printit;
+
+ if (db != NULL)
+ rc = db->sync(db, flags);
+ /* XXX DB_INCOMPLETE is returned occaisionally with multiple access. */
+ _printit = _debug;
+ rc = cvtdberr(dbi, "db->sync", rc, _printit);
+ return rc;
+}
+
+static int db3cdup(dbiIndex dbi, DBC * dbcursor, DBC ** dbcp,
+ unsigned int flags)
+{
+ int rc;
+
+ if (dbcp) *dbcp = NULL;
+ rc = dbcursor->c_dup(dbcursor, dbcp, flags);
+ rc = cvtdberr(dbi, "dbcursor->c_dup", rc, _debug);
+ /* FIX: *dbcp can be NULL */
+ return rc;
+}
+
+static int db3cclose(dbiIndex dbi, DBC * dbcursor,
+ unsigned int flags)
+{
+ int rc = -2;
+
+ /* XXX db3copen error pathways come through here. */
+ if (dbcursor != NULL) {
+ rc = dbcursor->c_close(dbcursor);
+ rc = cvtdberr(dbi, "dbcursor->c_close", rc, _debug);
+ }
+ return rc;
+}
+
+static int db3copen(dbiIndex dbi, DB_TXN * txnid,
+ DBC ** dbcp, unsigned int dbiflags)
+{
+ DB * db = dbi->dbi_db;
+ DBC * dbcursor = NULL;
+ int flags;
+ int rc;
+
+ /* XXX DB_WRITECURSOR cannot be used with sunrpc dbenv. */
+ assert(db != NULL);
+ if ((dbiflags & DB_WRITECURSOR) &&
+ (dbi->dbi_eflags & DB_INIT_CDB) && !(dbi->dbi_oflags & DB_RDONLY))
+ {
+ flags = DB_WRITECURSOR;
+ } else
+ flags = 0;
+
+ rc = db->cursor(db, txnid, &dbcursor, flags);
+ rc = cvtdberr(dbi, "db->cursor", rc, _debug);
+
+ if (dbcp)
+ *dbcp = dbcursor;
+ else
+ (void) db3cclose(dbi, dbcursor, 0);
+
+ return rc;
+}
+
+static int db3cput(dbiIndex dbi, DBC * dbcursor, DBT * key, DBT * data,
+ unsigned int flags)
+{
+ DB * db = dbi->dbi_db;
+ int rc;
+
+ assert(db != NULL);
+ if (dbcursor == NULL) {
+ rc = db->put(db, dbi->dbi_txnid, key, data, 0);
+ rc = cvtdberr(dbi, "db->put", rc, _debug);
+ } else {
+ rc = dbcursor->c_put(dbcursor, key, data, DB_KEYLAST);
+ rc = cvtdberr(dbi, "dbcursor->c_put", rc, _debug);
+ }
+
+ return rc;
+}
+
+static int db3cdel(dbiIndex dbi, DBC * dbcursor, DBT * key, DBT * data,
+ unsigned int flags)
+{
+ DB * db = dbi->dbi_db;
+ int rc;
+
+ assert(db != NULL);
+ if (dbcursor == NULL) {
+ rc = db->del(db, dbi->dbi_txnid, key, flags);
+ rc = cvtdberr(dbi, "db->del", rc, _debug);
+ } else {
+ int _printit;
+
+ /* XXX TODO: insure that cursor is positioned with duplicates */
+ rc = dbcursor->c_get(dbcursor, key, data, DB_SET);
+ /* XXX DB_NOTFOUND can be returned */
+ _printit = (rc == DB_NOTFOUND ? 0 : _debug);
+ rc = cvtdberr(dbi, "dbcursor->c_get", rc, _printit);
+
+ if (rc == 0) {
+ rc = dbcursor->c_del(dbcursor, flags);
+ rc = cvtdberr(dbi, "dbcursor->c_del", rc, _debug);
+ }
+ }
+
+ return rc;
+}
+
+static int db3cget(dbiIndex dbi, DBC * dbcursor, DBT * key, DBT * data,
+ unsigned int flags)
+{
+ DB * db = dbi->dbi_db;
+ int _printit;
+ int rc;
+
+ assert(db != NULL);
+ if (dbcursor == NULL) {
+ /* XXX duplicates require cursors. */
+ rc = db->get(db, dbi->dbi_txnid, key, data, 0);
+ /* XXX DB_NOTFOUND can be returned */
+ _printit = (rc == DB_NOTFOUND ? 0 : _debug);
+ rc = cvtdberr(dbi, "db->get", rc, _printit);
+ } else {
+ /* XXX db3 does DB_FIRST on uninitialized cursor */
+ rc = dbcursor->c_get(dbcursor, key, data, flags);
+ /* XXX DB_NOTFOUND can be returned */
+ _printit = (rc == DB_NOTFOUND ? 0 : _debug);
+ rc = cvtdberr(dbi, "dbcursor->c_get", rc, _printit);
+ }
+
+ return rc;
+}
+
+static int db3cpget(dbiIndex dbi, DBC * dbcursor, DBT * key, DBT * pkey,
+ DBT * data, unsigned int flags)
+{
+ DB * db = dbi->dbi_db;
+ int _printit;
+ int rc;
+
+ assert(db != NULL);
+ assert(dbcursor != NULL);
+
+ /* XXX db3 does DB_FIRST on uninitialized cursor */
+ rc = dbcursor->c_pget(dbcursor, key, pkey, data, flags);
+ /* XXX DB_NOTFOUND can be returned */
+ _printit = (rc == DB_NOTFOUND ? 0 : _debug);
+ rc = cvtdberr(dbi, "dbcursor->c_pget", rc, _printit);
+
+ return rc;
+}
+
+static int db3ccount(dbiIndex dbi, DBC * dbcursor,
+ unsigned int * countp,
+ unsigned int flags)
+{
+ db_recno_t count = 0;
+ int rc = 0;
+
+ flags = 0;
+ rc = dbcursor->c_count(dbcursor, &count, flags);
+ rc = cvtdberr(dbi, "dbcursor->c_count", rc, _debug);
+ if (rc) return rc;
+ if (countp) *countp = count;
+
+ return rc;
+}
+
+static int db3byteswapped(dbiIndex dbi)
+{
+ DB * db = dbi->dbi_db;
+ int rc = 0;
+
+ if (db != NULL) {
+ int isswapped = 0;
+ rc = db->get_byteswapped(db, &isswapped);
+ if (rc == 0)
+ rc = isswapped;
+ }
+
+ return rc;
+}
+
+static int db3stat(dbiIndex dbi, unsigned int flags)
+{
+ DB * db = dbi->dbi_db;
+#if (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3)
+ DB_TXN * txnid = NULL;
+#endif
+ int rc = 0;
+
+ assert(db != NULL);
+#if defined(DB_FAST_STAT)
+ if (flags)
+ flags = DB_FAST_STAT;
+ else
+#endif
+ flags = 0;
+ dbi->dbi_stats = _free(dbi->dbi_stats);
+#if (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3)
+ rc = db->stat(db, txnid, &dbi->dbi_stats, flags);
+#else
+ rc = db->stat(db, &dbi->dbi_stats, flags);
+#endif
+ rc = cvtdberr(dbi, "db->stat", rc, _debug);
+ return rc;
+}
+
+static int db3associate(dbiIndex dbi, dbiIndex dbisecondary,
+ int (*callback)(DB *, const DBT *, const DBT *, DBT *),
+ unsigned int flags)
+{
+ DB * db = dbi->dbi_db;
+ DB * secondary = dbisecondary->dbi_db;
+ int rc;
+
+ DB_TXN * txnid = NULL;
+
+assert(db != NULL);
+ rc = db->associate(db, txnid, secondary, callback, flags);
+ rc = cvtdberr(dbi, "db->associate", rc, _debug);
+ return rc;
+}
+
+static int db3join(dbiIndex dbi, DBC ** curslist, DBC ** dbcp,
+ unsigned int flags)
+{
+ DB * db = dbi->dbi_db;
+ int rc;
+
+assert(db != NULL);
+ rc = db->join(db, curslist, dbcp, flags);
+ rc = cvtdberr(dbi, "db->join", rc, _debug);
+ return rc;
+}
+
+static int db3close(dbiIndex dbi, unsigned int flags)
+{
+ rpmdb rpmdb = dbi->dbi_rpmdb;
+ const char * root;
+ const char * home;
+ char * dbhome;
+ const char * dbfile;
+ const char * dbsubfile;
+ DB * db = dbi->dbi_db;
+ int _printit;
+ int rc = 0, xx;
+
+ flags = 0; /* XXX unused */
+
+ /*
+ * Get the prefix/root component and directory path.
+ */
+ root = (dbi->dbi_root ? dbi->dbi_root : rpmdb->db_root);
+ if ((root[0] == '/' && root[1] == '\0') || rpmdb->db_chrootDone)
+ root = NULL;
+ home = (dbi->dbi_home ? dbi->dbi_home : rpmdb->db_home);
+
+ dbhome = rpmGenPath(root, home, NULL);
+ if (dbi->dbi_temporary) {
+ dbfile = NULL;
+ dbsubfile = NULL;
+ } else {
+#ifdef HACK /* XXX necessary to support dbsubfile */
+ dbfile = (dbi->dbi_file ? dbi->dbi_file : db3basename);
+ dbsubfile = (dbi->dbi_subfile ? dbi->dbi_subfile : rpmTagGetName(dbi->dbi_rpmtag));
+#else
+ dbfile = (dbi->dbi_file ? dbi->dbi_file : rpmTagGetName(dbi->dbi_rpmtag));
+ dbsubfile = NULL;
+#endif
+ }
+
+ if (db) {
+ rc = db->close(db, 0);
+ /* XXX ignore not found error messages. */
+ _printit = (rc == ENOENT ? 0 : _debug);
+ rc = cvtdberr(dbi, "db->close", rc, _printit);
+ db = dbi->dbi_db = NULL;
+
+ rpmlog(RPMLOG_DEBUG, "closed db index %s/%s\n",
+ dbhome, (dbfile ? dbfile : rpmTagGetName(dbi->dbi_rpmtag)));
+
+ }
+
+ if (rpmdb->db_dbenv != NULL && dbi->dbi_use_dbenv) {
+ if (rpmdb->db_opens == 1) {
+ xx = db_fini(dbi, (dbhome ? dbhome : ""), dbfile, dbsubfile);
+ rpmdb->db_dbenv = NULL;
+ }
+ rpmdb->db_opens--;
+ }
+
+ if (dbi->dbi_verify_on_close && !dbi->dbi_temporary) {
+ DB_ENV * dbenv = NULL;
+
+ rc = db_env_create(&dbenv, 0);
+ rc = cvtdberr(dbi, "db_env_create", rc, _debug);
+ if (rc || dbenv == NULL) goto exit;
+
+ dbenv->set_errcall(dbenv, (void *) rpmdb->db_errcall);
+ dbenv->set_errfile(dbenv, rpmdb->db_errfile);
+ dbenv->set_errpfx(dbenv, rpmdb->db_errpfx);
+ /* dbenv->set_paniccall(???) */
+#if !(DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3)
+ xx = dbenv->set_verbose(dbenv, DB_VERB_CHKPOINT,
+ (dbi->dbi_verbose & DB_VERB_CHKPOINT));
+#endif
+ xx = dbenv->set_verbose(dbenv, DB_VERB_DEADLOCK,
+ (dbi->dbi_verbose & DB_VERB_DEADLOCK));
+ xx = dbenv->set_verbose(dbenv, DB_VERB_RECOVERY,
+ (dbi->dbi_verbose & DB_VERB_RECOVERY));
+ xx = dbenv->set_verbose(dbenv, DB_VERB_WAITSFOR,
+ (dbi->dbi_verbose & DB_VERB_WAITSFOR));
+
+ if (dbi->dbi_tmpdir) {
+ char * tmpdir = rpmGenPath(root, dbi->dbi_tmpdir, NULL);
+ rc = dbenv->set_tmp_dir(dbenv, tmpdir);
+ rc = cvtdberr(dbi, "dbenv->set_tmp_dir", rc, _debug);
+ tmpdir = _free(tmpdir);
+ if (rc) goto exit;
+ }
+
+ rc = (dbenv->open)(dbenv, dbhome,
+ DB_CREATE | DB_INIT_MPOOL | DB_PRIVATE | DB_USE_ENVIRON, 0);
+ rc = cvtdberr(dbi, "dbenv->open", rc, _debug);
+ if (rc) goto exit;
+
+ rc = db_create(&db, dbenv, 0);
+ rc = cvtdberr(dbi, "db_create", rc, _debug);
+
+ if (db != NULL) {
+ char * dbf = rpmGetPath(dbhome, "/", dbfile, NULL);
+
+ rc = db->verify(db, dbf, NULL, NULL, flags);
+ rc = cvtdberr(dbi, "db->verify", rc, _debug);
+
+ rpmlog(RPMLOG_DEBUG, "verified db index %s/%s\n",
+ (dbhome ? dbhome : ""),
+ (dbfile ? dbfile : rpmTagGetName(dbi->dbi_rpmtag)));
+
+ /*
+ * The DB handle may not be accessed again after
+ * DB->verify is called, regardless of its return.
+ */
+ db = NULL;
+ dbf = _free(dbf);
+ }
+ xx = dbenv->close(dbenv, 0);
+ xx = cvtdberr(dbi, "dbenv->close", xx, _debug);
+ if (rc == 0 && xx) rc = xx;
+ }
+
+exit:
+ dbi->dbi_db = NULL;
+
+ free(dbhome);
+
+ dbi = db3Free(dbi);
+
+ return rc;
+}
+
+static int db3open(rpmdb rpmdb, rpmTag rpmtag, dbiIndex * dbip)
+{
+ extern const struct _dbiVec db3vec;
+ const char * root;
+ const char * home;
+ char * dbhome;
+ const char * dbfile;
+ const char * dbsubfile;
+ dbiIndex dbi = NULL;
+ int rc = 0;
+ int xx;
+
+ DB * db = NULL;
+ DB_ENV * dbenv = NULL;
+ DB_TXN * txnid = NULL;
+ uint32_t oflags;
+ int _printit;
+
+ if (dbip)
+ *dbip = NULL;
+
+ /*
+ * Parse db configuration parameters.
+ */
+ if ((dbi = db3New(rpmdb, rpmtag)) == NULL)
+ return 1;
+ dbi->dbi_api = DB_VERSION_MAJOR;
+
+ /*
+ * Get the prefix/root component and directory path.
+ */
+ root = (dbi->dbi_root ? dbi->dbi_root : rpmdb->db_root);
+ if ((root[0] == '/' && root[1] == '\0') || rpmdb->db_chrootDone)
+ root = NULL;
+ home = (dbi->dbi_home ? dbi->dbi_home : rpmdb->db_home);
+
+ dbhome = rpmGenPath(root, home, NULL);
+ if (dbi->dbi_temporary) {
+ dbfile = NULL;
+ dbsubfile = NULL;
+ } else {
+#ifdef HACK /* XXX necessary to support dbsubfile */
+ dbfile = (dbi->dbi_file ? dbi->dbi_file : db3basename);
+ dbsubfile = (dbi->dbi_subfile ? dbi->dbi_subfile : rpmTagGetName(dbi->dbi_rpmtag));
+#else
+ dbfile = (dbi->dbi_file ? dbi->dbi_file : rpmTagGetName(dbi->dbi_rpmtag));
+ dbsubfile = NULL;
+#endif
+ }
+
+ oflags = (dbi->dbi_oeflags | dbi->dbi_oflags);
+ oflags &= ~DB_TRUNCATE; /* XXX this is dangerous */
+
+#if 0 /* XXX rpmdb: illegal flag combination specified to DB->open */
+ if ( dbi->dbi_mode & O_EXCL) oflags |= DB_EXCL;
+#endif
+
+ /*
+ * Map open mode flags onto configured database/environment flags.
+ */
+ if (dbi->dbi_temporary) {
+ oflags |= DB_CREATE;
+ dbi->dbi_oeflags |= DB_CREATE;
+ oflags &= ~DB_RDONLY;
+ dbi->dbi_oflags &= ~DB_RDONLY;
+ } else {
+ if (!(dbi->dbi_mode & (O_RDWR|O_WRONLY))) oflags |= DB_RDONLY;
+ if (dbi->dbi_mode & O_CREAT) {
+ oflags |= DB_CREATE;
+ dbi->dbi_oeflags |= DB_CREATE;
+ }
+#ifdef DANGEROUS
+ if ( dbi->dbi_mode & O_TRUNC) oflags |= DB_TRUNCATE;
+#endif
+ }
+
+ /*
+ * Create the /var/lib/rpm directory if it doesn't exist (root only).
+ */
+ (void) rpmioMkpath(dbhome, 0755, getuid(), getgid());
+
+ /*
+ * Avoid incompatible DB_CREATE/DB_RDONLY flags on DBENV->open.
+ */
+ if (dbi->dbi_use_dbenv) {
+
+ if (access(dbhome, W_OK) == -1) {
+
+ /* dbhome is unwritable, don't attempt DB_CREATE on DB->open ... */
+ oflags &= ~DB_CREATE;
+
+ /* ... but DBENV->open might still need DB_CREATE ... */
+ if (dbi->dbi_eflags & DB_PRIVATE) {
+ dbi->dbi_eflags &= ~DB_JOINENV;
+ } else {
+ dbi->dbi_eflags |= DB_JOINENV;
+ dbi->dbi_oeflags &= ~DB_CREATE;
+ dbi->dbi_oeflags &= ~DB_THREAD;
+ /* ... but, unless DB_PRIVATE is used, skip DBENV. */
+ dbi->dbi_use_dbenv = 0;
+ }
+
+ /* ... DB_RDONLY maps dbhome perms across files ... */
+ if (dbi->dbi_temporary) {
+ oflags |= DB_CREATE;
+ dbi->dbi_oeflags |= DB_CREATE;
+ oflags &= ~DB_RDONLY;
+ dbi->dbi_oflags &= ~DB_RDONLY;
+ } else {
+ oflags |= DB_RDONLY;
+ /* ... and DB_WRITECURSOR won't be needed ... */
+ dbi->dbi_oflags |= DB_RDONLY;
+ }
+
+ } else { /* dbhome is writable, check for persistent dbenv. */
+ char * dbf = rpmGetPath(dbhome, "/__db.001", NULL);
+
+ if (access(dbf, F_OK) == -1) {
+ /* ... non-existent (or unwritable) DBENV, will create ... */
+ dbi->dbi_oeflags |= DB_CREATE;
+ dbi->dbi_eflags &= ~DB_JOINENV;
+ } else {
+ /* ... pre-existent (or bogus) DBENV, will join ... */
+ if (dbi->dbi_eflags & DB_PRIVATE) {
+ dbi->dbi_eflags &= ~DB_JOINENV;
+ } else {
+ dbi->dbi_eflags |= DB_JOINENV;
+ dbi->dbi_oeflags &= ~DB_CREATE;
+ dbi->dbi_oeflags &= ~DB_THREAD;
+ }
+ }
+ dbf = _free(dbf);
+ }
+ }
+
+ /*
+ * Avoid incompatible DB_CREATE/DB_RDONLY flags on DB->open.
+ */
+ if ((oflags & DB_CREATE) && (oflags & DB_RDONLY)) {
+ /* dbhome is writable, and DB->open flags may conflict. */
+ const char * dbfn = (dbfile ? dbfile : rpmTagGetName(dbi->dbi_rpmtag));
+ char * dbf = rpmGetPath(dbhome, "/", dbfn, NULL);
+
+ if (access(dbf, F_OK) == -1) {
+ /* File does not exist, DB->open might create ... */
+ oflags &= ~DB_RDONLY;
+ } else {
+ /* File exists, DB->open need not create ... */
+ oflags &= ~DB_CREATE;
+ }
+
+ /* Only writers need DB_WRITECURSOR ... */
+ if (!(oflags & DB_RDONLY) && access(dbf, W_OK) == 0) {
+ dbi->dbi_oflags &= ~DB_RDONLY;
+ } else {
+ dbi->dbi_oflags |= DB_RDONLY;
+ }
+ dbf = _free(dbf);
+ }
+
+ /*
+ * Turn off verify-on-close if opening read-only.
+ */
+ if (oflags & DB_RDONLY)
+ dbi->dbi_verify_on_close = 0;
+
+ if (dbi->dbi_use_dbenv) {
+ if (rpmdb->db_dbenv == NULL) {
+ rc = db_init(dbi, dbhome, dbfile, dbsubfile, &dbenv);
+ if (rc == 0) {
+ rpmdb->db_dbenv = dbenv;
+ rpmdb->db_opens = 1;
+ }
+ } else {
+ dbenv = rpmdb->db_dbenv;
+ rpmdb->db_opens++;
+ }
+ }
+
+ { char *dbiflags = prDbiOpenFlags(oflags, 0);
+ rpmlog(RPMLOG_DEBUG, "opening db index %s/%s %s mode=0x%x\n",
+ dbhome, (dbfile ? dbfile : rpmTagGetName(dbi->dbi_rpmtag)),
+ dbiflags, dbi->dbi_mode);
+ free(dbiflags);
+ }
+
+ if (rc == 0) {
+ static int _lockdbfd = 0;
+
+ rc = db_create(&db, dbenv, dbi->dbi_cflags);
+ rc = cvtdberr(dbi, "db_create", rc, _debug);
+ if (rc == 0 && db != NULL) {
+
+ if (rc == 0 &&
+ rpmdb->db_malloc && rpmdb->db_realloc && rpmdb->db_free)
+ {
+ rc = db->set_alloc(db,
+ rpmdb->db_malloc, rpmdb->db_realloc, rpmdb->db_free);
+ rc = cvtdberr(dbi, "db->set_alloc", rc, _debug);
+ }
+
+/* 4.1: db->set_cache_priority(???) */
+ if (rc == 0 && !dbi->dbi_use_dbenv && dbi->dbi_cachesize) {
+ rc = db->set_cachesize(db, 0, dbi->dbi_cachesize, 0);
+ rc = cvtdberr(dbi, "db->set_cachesize", rc, _debug);
+ }
+/* 4.1: db->set_encrypt(???) */
+/* 4.1: db->set_errcall(dbenv, rpmdb->db_errcall); */
+/* 4.1: db->set_errfile(dbenv, rpmdb->db_errfile); */
+/* 4.1: db->set_errpfx(dbenv, rpmdb->db_errpfx); */
+ /* 4.1: db->set_feedback(???) */
+
+ if (rc == 0 && dbi->dbi_lorder) {
+ rc = db->set_lorder(db, dbi->dbi_lorder);
+ rc = cvtdberr(dbi, "db->set_lorder", rc, _debug);
+ }
+ if (rc == 0 && dbi->dbi_pagesize) {
+ rc = db->set_pagesize(db, dbi->dbi_pagesize);
+ rc = cvtdberr(dbi, "db->set_pagesize", rc, _debug);
+ }
+ /* 4.1: db->set_paniccall(???) */
+ if (rc == 0 && oflags & DB_CREATE) {
+ switch(dbi->dbi_type) {
+ default:
+ case DB_HASH:
+ if (dbi->dbi_h_ffactor) {
+ rc = db->set_h_ffactor(db, dbi->dbi_h_ffactor);
+ rc = cvtdberr(dbi, "db->set_h_ffactor", rc, _debug);
+ if (rc) break;
+ }
+ if (dbi->dbi_h_nelem) {
+ rc = db->set_h_nelem(db, dbi->dbi_h_nelem);
+ rc = cvtdberr(dbi, "db->set_h_nelem", rc, _debug);
+ if (rc) break;
+ }
+ if (dbi->dbi_h_flags) {
+ rc = db->set_flags(db, dbi->dbi_h_flags);
+ rc = cvtdberr(dbi, "db->set_h_flags", rc, _debug);
+ if (rc) break;
+ }
+ if (dbi->dbi_h_hash_fcn) {
+ rc = db->set_h_hash(db, dbi->dbi_h_hash_fcn);
+ rc = cvtdberr(dbi, "db->set_h_hash", rc, _debug);
+ if (rc) break;
+ }
+ if (dbi->dbi_h_dup_compare_fcn) {
+ rc = db->set_dup_compare(db, dbi->dbi_h_dup_compare_fcn);
+ rc = cvtdberr(dbi, "db->set_dup_compare", rc, _debug);
+ if (rc) break;
+ }
+ break;
+ case DB_BTREE:
+/* 4.1: db->set_append_recno(???) */
+ if (dbi->dbi_bt_flags) {
+ rc = db->set_flags(db, dbi->dbi_bt_flags);
+ rc = cvtdberr(dbi, "db->set_bt_flags", rc, _debug);
+ if (rc) break;
+ }
+ if (dbi->dbi_bt_minkey) {
+ rc = db->set_bt_minkey(db, dbi->dbi_bt_minkey);
+ rc = cvtdberr(dbi, "db->set_bt_minkey", rc, _debug);
+ if (rc) break;
+ }
+ if (dbi->dbi_bt_compare_fcn) {
+ rc = db->set_bt_compare(db, dbi->dbi_bt_compare_fcn);
+ rc = cvtdberr(dbi, "db->set_bt_compare", rc, _debug);
+ if (rc) break;
+ }
+ if (dbi->dbi_bt_dup_compare_fcn) {
+ rc = db->set_dup_compare(db, dbi->dbi_bt_dup_compare_fcn);
+ rc = cvtdberr(dbi, "db->set_dup_compare", rc, _debug);
+ if (rc) break;
+ }
+ if (dbi->dbi_bt_prefix_fcn) {
+ rc = db->set_bt_prefix(db, dbi->dbi_bt_prefix_fcn);
+ rc = cvtdberr(dbi, "db->set_bt_prefix", rc, _debug);
+ if (rc) break;
+ }
+ break;
+ case DB_RECNO:
+ if (dbi->dbi_re_delim) {
+/* 4.1: db->set_append_recno(???) */
+ rc = db->set_re_delim(db, dbi->dbi_re_delim);
+ rc = cvtdberr(dbi, "db->set_re_selim", rc, _debug);
+ if (rc) break;
+ }
+ if (dbi->dbi_re_len) {
+ rc = db->set_re_len(db, dbi->dbi_re_len);
+ rc = cvtdberr(dbi, "db->set_re_len", rc, _debug);
+ if (rc) break;
+ }
+ if (dbi->dbi_re_pad) {
+ rc = db->set_re_pad(db, dbi->dbi_re_pad);
+ rc = cvtdberr(dbi, "db->set_re_pad", rc, _debug);
+ if (rc) break;
+ }
+ if (dbi->dbi_re_source) {
+ rc = db->set_re_source(db, dbi->dbi_re_source);
+ rc = cvtdberr(dbi, "db->set_re_source", rc, _debug);
+ if (rc) break;
+ }
+ break;
+ case DB_QUEUE:
+ if (dbi->dbi_q_extentsize) {
+ rc = db->set_q_extentsize(db, dbi->dbi_q_extentsize);
+ rc = cvtdberr(dbi, "db->set_q_extentsize", rc, _debug);
+ if (rc) break;
+ }
+ break;
+ }
+ }
+
+ if (rc == 0) {
+ char * fullpath;
+ const char * dbpath;
+ fullpath = rpmGetPath(dbhome, "/", dbfile ? dbfile : "", NULL);
+
+#ifdef HACK /* XXX necessary to support dbsubfile */
+ dbpath = (!dbi->dbi_use_dbenv && !dbi->dbi_temporary)
+ ? fullpath : dbfile;
+#else
+ dbpath = (!dbi->dbi_temporary)
+ ? fullpath : dbfile;
+#endif
+
+ rc = (db->open)(db, txnid, dbpath, dbsubfile,
+ dbi->dbi_type, oflags, dbi->dbi_perms);
+
+ if (rc == 0 && dbi->dbi_type == DB_UNKNOWN) {
+ DBTYPE dbi_type = DB_UNKNOWN;
+ xx = db->get_type(db, &dbi_type);
+ if (xx == 0)
+ dbi->dbi_type = dbi_type;
+ }
+ free(fullpath);
+ }
+
+ /* XXX return rc == errno without printing */
+ _printit = (rc > 0 ? 0 : _debug);
+ xx = cvtdberr(dbi, "db->open", rc, _printit);
+
+ dbi->dbi_txnid = NULL;
+
+ /*
+ * Lock a file using fcntl(2). Traditionally this is Packages,
+ * the file used to store metadata of installed header(s),
+ * as Packages is always opened, and should be opened first,
+ * for any rpmdb access.
+ *
+ * If no DBENV is used, then access is protected with a
+ * shared/exclusive locking scheme, as always.
+ *
+ * With a DBENV, the fcntl(2) lock is necessary only to keep
+ * the riff-raff from playing where they don't belong, as
+ * the DBENV should provide it's own locking scheme. So try to
+ * acquire a lock, but permit failures, as some other
+ * DBENV player may already have acquired the lock.
+ *
+ * With NPTL posix mutexes, revert to fcntl lock on non-functioning
+ * glibc/kernel combinations.
+ */
+ if (rc == 0 && dbi->dbi_lockdbfd &&
+ !((dbi->dbi_ecflags & DB_CLIENT) && dbi->dbi_host) &&
+ (!dbi->dbi_use_dbenv || _lockdbfd++ == 0))
+ {
+ int fdno = -1;
+
+ if (!(db->fd(db, &fdno) == 0 && fdno >= 0)) {
+ rc = 1;
+ } else {
+ struct flock l;
+ memset(&l, 0, sizeof(l));
+ l.l_whence = 0;
+ l.l_start = 0;
+ l.l_len = 0;
+ l.l_type = (dbi->dbi_mode & (O_RDWR|O_WRONLY))
+ ? F_WRLCK : F_RDLCK;
+ l.l_pid = 0;
+
+ rc = fcntl(fdno, F_SETLK, (void *) &l);
+ if (rc) {
+ /* Warning iff using non-private CDB locking. */
+ rc = ((dbi->dbi_use_dbenv &&
+ (dbi->dbi_eflags & DB_INIT_CDB) &&
+ !(dbi->dbi_eflags & DB_PRIVATE))
+ ? 0 : 1);
+ rpmlog( (rc ? RPMLOG_ERR : RPMLOG_WARNING),
+ _("cannot get %s lock on %s/%s\n"),
+ ((dbi->dbi_mode & (O_RDWR|O_WRONLY))
+ ? _("exclusive") : _("shared")),
+ dbhome, (dbfile ? dbfile : ""));
+ } else if (dbfile) {
+ rpmlog(RPMLOG_DEBUG,
+ "locked db index %s/%s\n",
+ dbhome, dbfile);
+ }
+ }
+ }
+ }
+ }
+
+ dbi->dbi_db = db;
+
+ if (rc == 0 && dbi->dbi_db != NULL && dbip != NULL) {
+ dbi->dbi_vec = &db3vec;
+ *dbip = dbi;
+ } else {
+ dbi->dbi_verify_on_close = 0;
+ (void) db3close(dbi, 0);
+ }
+
+ free(dbhome);
+
+ return rc;
+}
+
+/** \ingroup db3
+ */
+const struct _dbiVec db3vec = {
+ DB_VERSION_MAJOR, DB_VERSION_MINOR, DB_VERSION_PATCH,
+ db3open, db3close, db3sync, db3associate, db3join,
+ db3copen, db3cclose, db3cdup, db3cdel, db3cget, db3cpget, db3cput, db3ccount,
+ db3byteswapped, db3stat
+};
diff --git a/lib/backend/dbconfig.c b/lib/backend/dbconfig.c
new file mode 100644
index 000000000..fc75af3bf
--- /dev/null
+++ b/lib/backend/dbconfig.c
@@ -0,0 +1,473 @@
+/** \ingroup rpmdb
+ * \file rpmdb/dbconfig.c
+ */
+
+#include "system.h"
+
+#include <popt.h>
+
+#include <rpm/rpmtag.h>
+#include <rpm/rpmmacro.h>
+#include <rpm/rpmstring.h>
+#include <rpm/rpmlog.h>
+#include <rpm/argv.h>
+#include "rpmdb/rpmdb_internal.h"
+#include "debug.h"
+
+
+#if (DB_VERSION_MAJOR == 3) || (DB_VERSION_MAJOR == 4)
+#define __USE_DB3 1
+
+struct _dbiIndex db3dbi;
+
+static int dbi_use_cursors;
+
+static int dbi_tear_down;
+
+/** \ingroup db3
+ */
+struct poptOption rdbOptions[] = {
+ /* XXX DB_CXX_NO_EXCEPTIONS */
+#if defined(DB_CLIENT)
+ { "client", 0,POPT_BIT_SET, &db3dbi.dbi_ecflags, DB_CLIENT,
+ NULL, NULL },
+#endif
+#if defined(DB_RPCCLIENT)
+ { "client", 0,POPT_BIT_SET, &db3dbi.dbi_ecflags, DB_RPCCLIENT,
+ NULL, NULL },
+ { "rpcclient", 0,POPT_BIT_SET, &db3dbi.dbi_ecflags, DB_RPCCLIENT,
+ NULL, NULL },
+#endif
+
+ { "xa_create", 0,POPT_BIT_SET, &db3dbi.dbi_cflags, DB_XA_CREATE,
+ NULL, NULL },
+
+ { "create", 0,POPT_BIT_SET, &db3dbi.dbi_oeflags, DB_CREATE,
+ NULL, NULL },
+ { "thread", 0,POPT_BIT_SET, &db3dbi.dbi_oeflags, DB_THREAD,
+ NULL, NULL },
+
+ { "force", 0,POPT_BIT_SET, &db3dbi.dbi_eflags, DB_FORCE,
+ NULL, NULL },
+ { "cdb", 0,POPT_BIT_SET, &db3dbi.dbi_eflags, DB_INIT_CDB,
+ NULL, NULL },
+ { "lock", 0,POPT_BIT_SET, &db3dbi.dbi_eflags, DB_INIT_LOCK,
+ NULL, NULL },
+ { "log", 0,POPT_BIT_SET, &db3dbi.dbi_eflags, DB_INIT_LOG,
+ NULL, NULL },
+ { "mpool", 0,POPT_BIT_SET, &db3dbi.dbi_eflags, DB_INIT_MPOOL,
+ NULL, NULL },
+ { "txn", 0,POPT_BIT_SET, &db3dbi.dbi_eflags, DB_INIT_TXN,
+ NULL, NULL },
+ { "joinenv", 0,POPT_BIT_SET, &db3dbi.dbi_eflags, DB_JOINENV,
+ NULL, NULL },
+ { "recover", 0,POPT_BIT_SET, &db3dbi.dbi_eflags, DB_RECOVER,
+ NULL, NULL },
+ { "recover_fatal", 0,POPT_BIT_SET, &db3dbi.dbi_eflags, DB_RECOVER_FATAL,
+ NULL, NULL },
+ { "shared", 0,POPT_BIT_SET, &db3dbi.dbi_eflags, DB_SYSTEM_MEM,
+ NULL, NULL },
+ { "txn_nosync", 0,POPT_BIT_SET, &db3dbi.dbi_eflags, DB_TXN_NOSYNC,
+ NULL, NULL },
+ { "use_environ_root", 0,POPT_BIT_SET, &db3dbi.dbi_eflags, DB_USE_ENVIRON_ROOT,
+ NULL, NULL },
+ { "use_environ", 0,POPT_BIT_SET, &db3dbi.dbi_eflags, DB_USE_ENVIRON,
+ NULL, NULL },
+ { "lockdown", 0,POPT_BIT_SET, &db3dbi.dbi_eflags, DB_LOCKDOWN,
+ NULL, NULL },
+ { "private", 0,POPT_BIT_SET, &db3dbi.dbi_eflags, DB_PRIVATE,
+ NULL, NULL },
+
+ { "txn_sync", 0,POPT_BIT_SET, &db3dbi.dbi_tflags, DB_TXN_SYNC,
+ NULL, NULL },
+ { "txn_nowait",0,POPT_BIT_SET, &db3dbi.dbi_tflags, DB_TXN_NOWAIT,
+ NULL, NULL },
+
+ { "excl", 0,POPT_BIT_SET, &db3dbi.dbi_oflags, DB_EXCL,
+ NULL, NULL },
+ { "nommap", 0,POPT_BIT_SET, &db3dbi.dbi_oflags, DB_NOMMAP,
+ NULL, NULL },
+ { "rdonly", 0,POPT_BIT_SET, &db3dbi.dbi_oflags, DB_RDONLY,
+ NULL, NULL },
+ { "truncate", 0,POPT_BIT_SET, &db3dbi.dbi_oflags, DB_TRUNCATE,
+ NULL, NULL },
+ { "fcntl_locking",0,POPT_BIT_SET, &db3dbi.dbi_oflags, DB_FCNTL_LOCKING,
+ NULL, NULL },
+
+ { "btree", 0,POPT_ARG_VAL, &db3dbi.dbi_type, DB_BTREE,
+ NULL, NULL },
+ { "hash", 0,POPT_ARG_VAL, &db3dbi.dbi_type, DB_HASH,
+ NULL, NULL },
+ { "recno", 0,POPT_ARG_VAL, &db3dbi.dbi_type, DB_RECNO,
+ NULL, NULL },
+ { "queue", 0,POPT_ARG_VAL, &db3dbi.dbi_type, DB_QUEUE,
+ NULL, NULL },
+ { "unknown", 0,POPT_ARG_VAL, &db3dbi.dbi_type, DB_UNKNOWN,
+ NULL, NULL },
+
+ { "root", 0,POPT_ARG_STRING, &db3dbi.dbi_root, 0,
+ NULL, NULL },
+ { "home", 0,POPT_ARG_STRING, &db3dbi.dbi_home, 0,
+ NULL, NULL },
+ { "file", 0,POPT_ARG_STRING, &db3dbi.dbi_file, 0,
+ NULL, NULL },
+ { "subfile", 0,POPT_ARG_STRING, &db3dbi.dbi_subfile, 0,
+ NULL, NULL },
+ { "mode", 0,POPT_ARG_INT, &db3dbi.dbi_mode, 0,
+ NULL, NULL },
+ { "perms", 0,POPT_ARG_INT, &db3dbi.dbi_perms, 0,
+ NULL, NULL },
+ { "shmkey", 0,POPT_ARG_LONG, &db3dbi.dbi_shmkey, 0,
+ NULL, NULL },
+ { "tmpdir", 0,POPT_ARG_STRING, &db3dbi.dbi_tmpdir, 0,
+ NULL, NULL },
+
+ { "host", 0,POPT_ARG_STRING, &db3dbi.dbi_host, 0,
+ NULL, NULL },
+ { "server", 0,POPT_ARG_STRING, &db3dbi.dbi_host, 0,
+ NULL, NULL },
+ { "cl_timeout", 0,POPT_ARG_LONG, &db3dbi.dbi_cl_timeout, 0,
+ NULL, NULL },
+ { "sv_timeout", 0,POPT_ARG_LONG, &db3dbi.dbi_sv_timeout, 0,
+ NULL, NULL },
+
+ { "verify", 0,POPT_ARG_NONE, &db3dbi.dbi_verify_on_close, 0,
+ NULL, NULL },
+ { "teardown", 0,POPT_ARG_NONE, &dbi_tear_down, 0,
+ NULL, NULL },
+ { "usecursors",0,POPT_ARG_NONE, &dbi_use_cursors, 0,
+ NULL, NULL },
+ { "usedbenv", 0,POPT_ARG_NONE, &db3dbi.dbi_use_dbenv, 0,
+ NULL, NULL },
+ { "nofsync", 0,POPT_ARG_NONE, &db3dbi.dbi_no_fsync, 0,
+ NULL, NULL },
+ { "nodbsync", 0,POPT_ARG_NONE, &db3dbi.dbi_no_dbsync, 0,
+ NULL, NULL },
+ { "lockdbfd", 0,POPT_ARG_NONE, &db3dbi.dbi_lockdbfd, 0,
+ NULL, NULL },
+ { "temporary", 0,POPT_ARG_NONE, &db3dbi.dbi_temporary, 0,
+ NULL, NULL },
+ { "debug", 0,POPT_ARG_NONE, &db3dbi.dbi_debug, 0,
+ NULL, NULL },
+
+ { "cachesize", 0,POPT_ARG_INT, &db3dbi.dbi_cachesize, 0,
+ NULL, NULL },
+ { "errpfx", 0,POPT_ARG_STRING, &db3dbi.dbi_errpfx, 0,
+ NULL, NULL },
+ { "region_init", 0,POPT_ARG_VAL, &db3dbi.dbi_region_init, 1,
+ NULL, NULL },
+ { "tas_spins", 0,POPT_ARG_INT, &db3dbi.dbi_tas_spins, 0,
+ NULL, NULL },
+
+#if defined(DB_VERB_CHKPOINT)
+ { "chkpoint", 0,POPT_BIT_SET, &db3dbi.dbi_verbose, DB_VERB_CHKPOINT,
+ NULL, NULL },
+#endif
+ { "deadlock", 0,POPT_BIT_SET, &db3dbi.dbi_verbose, DB_VERB_DEADLOCK,
+ NULL, NULL },
+ { "recovery", 0,POPT_BIT_SET, &db3dbi.dbi_verbose, DB_VERB_RECOVERY,
+ NULL, NULL },
+ { "waitsfor", 0,POPT_BIT_SET, &db3dbi.dbi_verbose, DB_VERB_WAITSFOR,
+ NULL, NULL },
+ { "verbose", 0,POPT_ARG_VAL, &db3dbi.dbi_verbose, -1,
+ NULL, NULL },
+
+ { "lk_oldest", 0,POPT_ARG_VAL, &db3dbi.dbi_lk_detect, DB_LOCK_OLDEST,
+ NULL, NULL },
+ { "lk_random", 0,POPT_ARG_VAL, &db3dbi.dbi_lk_detect, DB_LOCK_RANDOM,
+ NULL, NULL },
+ { "lk_youngest",0, POPT_ARG_VAL, &db3dbi.dbi_lk_detect, DB_LOCK_YOUNGEST,
+ NULL, NULL },
+/* XXX lk_conflicts matrix */
+ { "lk_max", 0,POPT_ARG_INT, &db3dbi.dbi_lk_max, 0,
+ NULL, NULL },
+
+ { "lg_bsize", 0,POPT_ARG_INT, &db3dbi.dbi_lg_bsize, 0,
+ NULL, NULL },
+ { "lg_max", 0,POPT_ARG_INT, &db3dbi.dbi_lg_max, 0,
+ NULL, NULL },
+
+/* XXX tx_recover */
+ { "tx_max", 0,POPT_ARG_INT, &db3dbi.dbi_tx_max, 0,
+ NULL, NULL },
+
+ { "lorder", 0,POPT_ARG_INT, &db3dbi.dbi_lorder, 0,
+ NULL, NULL },
+
+ { "mmapsize", 0,POPT_ARG_INT, &db3dbi.dbi_mmapsize, 0,
+ NULL, NULL },
+ { "mp_mmapsize", 0,POPT_ARG_INT, &db3dbi.dbi_mmapsize, 0,
+ NULL, NULL },
+ { "mp_size", 0,POPT_ARG_INT, &db3dbi.dbi_cachesize, 0,
+ NULL, NULL },
+ { "pagesize", 0,POPT_ARG_INT, &db3dbi.dbi_pagesize, 0,
+ NULL, NULL },
+
+/* XXX bt_minkey */
+/* XXX bt_compare */
+/* XXX bt_dup_compare */
+/* XXX bt_prefix */
+ { "bt_dup", 0,POPT_BIT_SET, &db3dbi.dbi_bt_flags, DB_DUP,
+ NULL, NULL },
+ { "bt_dupsort",0,POPT_BIT_SET, &db3dbi.dbi_bt_flags, DB_DUPSORT,
+ NULL, NULL },
+ { "bt_recnum", 0,POPT_BIT_SET, &db3dbi.dbi_bt_flags, DB_RECNUM,
+ NULL, NULL },
+ { "bt_revsplitoff", 0,POPT_BIT_SET, &db3dbi.dbi_bt_flags, DB_REVSPLITOFF,
+ NULL, NULL },
+
+ { "h_dup", 0,POPT_BIT_SET, &db3dbi.dbi_h_flags, DB_DUP,
+ NULL, NULL },
+ { "h_dupsort", 0,POPT_BIT_SET, &db3dbi.dbi_h_flags, DB_DUPSORT,
+ NULL, NULL },
+ { "h_ffactor", 0,POPT_ARG_INT, &db3dbi.dbi_h_ffactor, 0,
+ NULL, NULL },
+ { "h_nelem", 0,POPT_ARG_INT, &db3dbi.dbi_h_nelem, 0,
+ NULL, NULL },
+
+ { "re_renumber", 0,POPT_BIT_SET, &db3dbi.dbi_re_flags, DB_RENUMBER,
+ NULL, NULL },
+ { "re_snapshot",0,POPT_BIT_SET, &db3dbi.dbi_re_flags, DB_SNAPSHOT,
+ NULL, NULL },
+ { "re_delim", 0,POPT_ARG_INT, &db3dbi.dbi_re_delim, 0,
+ NULL, NULL },
+ { "re_len", 0,POPT_ARG_INT, &db3dbi.dbi_re_len, 0,
+ NULL, NULL },
+ { "re_pad", 0,POPT_ARG_INT, &db3dbi.dbi_re_pad, 0,
+ NULL, NULL },
+ { "re_source", 0,POPT_ARG_STRING, &db3dbi.dbi_re_source, 0,
+ NULL, NULL },
+
+ { "q_extentsize", 0,POPT_ARG_INT, &db3dbi.dbi_q_extentsize, 0,
+ NULL, NULL },
+
+ POPT_TABLEEND
+};
+
+dbiIndex db3Free(dbiIndex dbi)
+{
+ if (dbi) {
+ dbi->dbi_root = _free(dbi->dbi_root);
+ dbi->dbi_home = _free(dbi->dbi_home);
+ dbi->dbi_file = _free(dbi->dbi_file);
+ dbi->dbi_subfile = _free(dbi->dbi_subfile);
+ dbi->dbi_tmpdir = _free(dbi->dbi_tmpdir);
+ dbi->dbi_host = _free(dbi->dbi_host);
+ dbi->dbi_errpfx = _free(dbi->dbi_errpfx);
+ dbi->dbi_re_source = _free(dbi->dbi_re_source);
+ dbi->dbi_stats = _free(dbi->dbi_stats);
+ dbi = _free(dbi);
+ }
+ return dbi;
+}
+
+/** @todo Set a reasonable "last gasp" default db config. */
+static const char * const db3_config_default =
+ "db3:hash:mpool:cdb:usecursors:verbose:mp_mmapsize=8Mb:cachesize=512Kb:pagesize=512:perms=0644";
+
+dbiIndex db3New(rpmdb rpmdb, rpmTag rpmtag)
+{
+ dbiIndex dbi = xcalloc(1, sizeof(*dbi));
+ char *dbOpts;
+
+ dbOpts = rpmExpand("%{_dbi_config_", rpmTagGetName(rpmtag), "}", NULL);
+
+ if (!(dbOpts && *dbOpts && *dbOpts != '%')) {
+ dbOpts = _free(dbOpts);
+ dbOpts = rpmExpand("%{_dbi_config}", NULL);
+ if (!(dbOpts && *dbOpts && *dbOpts != '%')) {
+ dbOpts = rpmExpand(db3_config_default, NULL);
+ }
+ }
+
+ /* Parse the options for the database element(s). */
+ if (dbOpts && *dbOpts && *dbOpts != '%') {
+ char *o, *oe;
+ char *p, *pe;
+
+ memset(&db3dbi, 0, sizeof(db3dbi));
+/*=========*/
+ for (o = dbOpts; o && *o; o = oe) {
+ struct poptOption *opt;
+ const char * tok;
+ unsigned int argInfo;
+
+ /* Skip leading white space. */
+ while (*o && risspace(*o))
+ o++;
+
+ /* Find and terminate next key=value pair. Save next start point. */
+ for (oe = o; oe && *oe; oe++) {
+ if (risspace(*oe))
+ break;
+ if (oe[0] == ':' && !(oe[1] == '/' && oe[2] == '/'))
+ break;
+ }
+ if (oe && *oe)
+ *oe++ = '\0';
+ if (*o == '\0')
+ continue;
+
+ /* Separate key from value, save value start (if any). */
+ for (pe = o; pe && *pe && *pe != '='; pe++)
+ {};
+ p = (pe ? *pe++ = '\0', pe : NULL);
+
+ /* Skip over negation at start of token. */
+ for (tok = o; *tok == '!'; tok++)
+ {};
+
+ /* Find key in option table. */
+ for (opt = rdbOptions; opt->longName != NULL; opt++) {
+ if (strcmp(tok, opt->longName))
+ continue;
+ break;
+ }
+ if (opt->longName == NULL) {
+ rpmlog(RPMLOG_ERR,
+ _("unrecognized db option: \"%s\" ignored.\n"), o);
+ continue;
+ }
+
+ /* Toggle the flags for negated tokens, if necessary. */
+ argInfo = opt->argInfo;
+ if (argInfo == POPT_BIT_SET && *o == '!' && ((tok - o) % 2))
+ argInfo = POPT_BIT_CLR;
+
+ /* Save value in template as appropriate. */
+ switch (argInfo & POPT_ARG_MASK) {
+
+ case POPT_ARG_NONE:
+ (void) poptSaveInt((int *)opt->arg, argInfo, 1L);
+ break;
+ case POPT_ARG_VAL:
+ (void) poptSaveInt((int *)opt->arg, argInfo, (long)opt->val);
+ break;
+ case POPT_ARG_STRING:
+ { char ** t = opt->arg;
+ if (t) {
+/* FIX: opt->arg annotation in popt.h */
+ *t = _free(*t);
+ *t = xstrdup( (p ? p : "") );
+ }
+ } break;
+
+ case POPT_ARG_INT:
+ case POPT_ARG_LONG:
+ { long aLong = strtol(p, &pe, 0);
+ if (pe) {
+ if (!rstrncasecmp(pe, "Mb", 2))
+ aLong *= 1024 * 1024;
+ else if (!rstrncasecmp(pe, "Kb", 2))
+ aLong *= 1024;
+ else if (*pe != '\0') {
+ rpmlog(RPMLOG_ERR,
+ _("%s has invalid numeric value, skipped\n"),
+ opt->longName);
+ continue;
+ }
+ }
+
+ if ((argInfo & POPT_ARG_MASK) == POPT_ARG_LONG) {
+ if (aLong == LONG_MIN || aLong == LONG_MAX) {
+ rpmlog(RPMLOG_ERR,
+ _("%s has too large or too small long value, skipped\n"),
+ opt->longName);
+ continue;
+ }
+ (void) poptSaveLong((long *)opt->arg, argInfo, aLong);
+ break;
+ } else {
+ if (aLong > INT_MAX || aLong < INT_MIN) {
+ rpmlog(RPMLOG_ERR,
+ _("%s has too large or too small integer value, skipped\n"),
+ opt->longName);
+ continue;
+ }
+ (void) poptSaveInt((int *)opt->arg, argInfo, aLong);
+ }
+ } break;
+ default:
+ break;
+ }
+ }
+/*=========*/
+ }
+
+ dbOpts = _free(dbOpts);
+
+ *dbi = db3dbi; /* structure assignment */
+ memset(&db3dbi, 0, sizeof(db3dbi));
+
+ if (!(dbi->dbi_perms & 0600))
+ dbi->dbi_perms = 0644;
+ dbi->dbi_mode = rpmdb->db_mode;
+ /* FIX: figger rpmdb/dbi refcounts */
+ dbi->dbi_rpmdb = rpmdb;
+ dbi->dbi_rpmtag = rpmtag;
+
+ /*
+ * Inverted lists have join length of 2, primary data has join length of 1.
+ */
+ switch (rpmtag) {
+ case RPMDBI_PACKAGES:
+ case RPMDBI_DEPENDS:
+ dbi->dbi_jlen = 1 * sizeof(int32_t);
+ break;
+ default:
+ dbi->dbi_jlen = 2 * sizeof(int32_t);
+ break;
+ }
+
+ dbi->dbi_byteswapped = -1; /* -1 unknown, 0 native order, 1 alien order */
+
+ if (!dbi->dbi_use_dbenv) { /* db3 dbenv is always used now. */
+ dbi->dbi_use_dbenv = 1;
+ dbi->dbi_eflags |= (DB_INIT_MPOOL|DB_JOINENV);
+ dbi->dbi_mmapsize = 16 * 1024 * 1024;
+ dbi->dbi_cachesize = 1 * 1024 * 1024;
+ }
+
+ if ((dbi->dbi_bt_flags | dbi->dbi_h_flags) & DB_DUP)
+ dbi->dbi_permit_dups = 1;
+
+ /* FIX: *(rdbOptions->arg) reachable */
+ return dbi;
+}
+
+char * prDbiOpenFlags(int dbflags, int print_dbenv_flags)
+{
+ ARGV_t flags = NULL;
+ struct poptOption *opt;
+ char *buf;
+
+ for (opt = rdbOptions; opt->longName != NULL; opt++) {
+ if (opt->argInfo != POPT_BIT_SET)
+ continue;
+ if (print_dbenv_flags) {
+ if (!(opt->arg == &db3dbi.dbi_oeflags ||
+ opt->arg == &db3dbi.dbi_eflags))
+ continue;
+ } else {
+ if (!(opt->arg == &db3dbi.dbi_oeflags ||
+ opt->arg == &db3dbi.dbi_oflags))
+ continue;
+ }
+ if ((dbflags & opt->val) != opt->val)
+ continue;
+ argvAdd(&flags, opt->longName);
+ dbflags &= ~opt->val;
+ }
+ if (dbflags) {
+ char *df = NULL;
+ rasprintf(&df, "0x%x", (unsigned)dbflags);
+ argvAdd(&flags, df);
+ free(df);
+ }
+ buf = argvJoin(flags, ":");
+ argvFree(flags);
+
+ return buf;
+}
+
+#endif
diff --git a/lib/backend/sqlite.c b/lib/backend/sqlite.c
new file mode 100644
index 000000000..e290a5b25
--- /dev/null
+++ b/lib/backend/sqlite.c
@@ -0,0 +1,1386 @@
+
+/*
+ * sqlite.c
+ * sqlite interface for rpmdb
+ *
+ * Author: Mark Hatle <mhatle@mvista.com> or <fray@kernel.crashing.org>
+ * Copyright (c) 2004 MontaVista Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * or GNU Library General Public License, at your option,
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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
+ * and GNU Library Public License along with this program;
+ * if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "system.h"
+
+#include <sqlite3.h>
+
+#include <rpm/rpmtag.h>
+#include <rpm/rpmlog.h>
+#include <rpm/rpmmacro.h>
+#include <rpm/rpmfileutil.h> /* rpmioMkpath */
+#include <rpm/rpmstring.h>
+#include <rpm/rpmdb.h>
+
+#include "rpmdb/rpmdb_internal.h"
+
+#include "debug.h"
+
+
+static int _debug = 0;
+
+/* Define the things normally in a header... */
+struct _sql_db_s; typedef struct _sql_db_s SQL_DB;
+struct _sql_dbcursor_s; typedef struct _sql_dbcursor_s *SCP_t;
+
+struct _sql_db_s {
+ sqlite3 * db; /* Database pointer */
+ int transaction; /* Do we have a transaction open? */
+};
+
+struct _sql_dbcursor_s {
+ DB *dbp;
+
+ char * cmd; /* SQL command string */
+ sqlite3_stmt *pStmt; /* SQL byte code */
+ const char * pzErrmsg; /* SQL error msg */
+
+ /* Table -- result of query */
+ char ** av; /* item ptrs */
+ int * avlen; /* item sizes */
+ int nalloc;
+ int ac; /* no. of items */
+ int rx; /* Which row are we on? 1, 2, 3 ... */
+ int nr; /* no. of rows */
+ int nc; /* no. of columns */
+
+ int all; /* sequential iteration cursor */
+ DBT ** keys; /* array of package keys */
+ int nkeys;
+
+ int count;
+
+ void * lkey; /* Last key returned */
+ void * ldata; /* Last data returned */
+
+ int used;
+};
+
+union _dbswap {
+ unsigned int ui;
+ unsigned char uc[4];
+};
+
+#define _DBSWAP(_a) \
+ { unsigned char _b, *_c = (_a).uc; \
+ _b = _c[3]; _c[3] = _c[0]; _c[0] = _b; \
+ _b = _c[2]; _c[2] = _c[1]; _c[1] = _b; \
+ }
+
+static const unsigned int endian = 0x11223344;
+
+static char * sqlCwd = NULL;
+static int sqlInRoot = 0;
+
+static void enterChroot(dbiIndex dbi)
+{
+ int xx;
+
+ if ((dbi->dbi_root[0] == '/' && dbi->dbi_root[1] == '\0') || dbi->dbi_rpmdb->db_chrootDone || sqlInRoot)
+ /* Nothing to do, was not already in chroot */
+ return;
+
+if (_debug)
+fprintf(stderr, "sql:chroot(%s)\n", dbi->dbi_root);
+
+ sqlCwd = rpmGetCwd();
+ xx = chdir("/");
+ xx = chroot(dbi->dbi_root);
+assert(xx == 0);
+ sqlInRoot=1;
+}
+
+static void leaveChroot(dbiIndex dbi)
+{
+ int xx;
+
+ if ((dbi->dbi_root[0] == '/' && dbi->dbi_root[1] == '\0') || dbi->dbi_rpmdb->db_chrootDone || !sqlInRoot)
+ /* Nothing to do, not in chroot */
+ return;
+
+if (_debug)
+fprintf(stderr, "sql:chroot(.)\n");
+
+ xx = chroot(".");
+assert(xx == 0);
+ xx = chdir(sqlCwd);
+ sqlCwd = _free(sqlCwd);
+
+ sqlInRoot=0;
+}
+
+static void dbg_scp(void *ptr)
+{
+ SCP_t scp = ptr;
+
+if (_debug)
+fprintf(stderr, "\tscp %p [%d:%d] av %p avlen %p nr [%d:%d] nc %d all %d\n", scp, scp->ac, scp->nalloc, scp->av, scp->avlen, scp->rx, scp->nr, scp->nc, scp->all);
+
+}
+
+static void dbg_keyval(const char * msg, dbiIndex dbi, DBC * dbcursor,
+ DBT * key, DBT * data, unsigned int flags)
+{
+
+if (!_debug) return;
+
+ fprintf(stderr, "%s on %s (%p,%p,%p,0x%x)", msg, dbi->dbi_subfile, dbcursor, key, data, flags);
+
+ /* XXX FIXME: ptr alignment is fubar here. */
+ if (key != NULL && key->data != NULL) {
+ fprintf(stderr, " key 0x%x[%d]", *(unsigned int *)key->data, key->size);
+ if (dbi->dbi_rpmtag == RPMTAG_NAME)
+ fprintf(stderr, " \"%s\"", (const char *)key->data);
+ }
+ if (data != NULL && data->data != NULL)
+ fprintf(stderr, " data 0x%x[%d]", *(unsigned int *)data->data, data->size);
+
+ fprintf(stderr, "\n");
+ dbg_scp(dbcursor);
+}
+
+
+static SCP_t scpResetKeys(SCP_t scp)
+{
+ int ix;
+
+if (_debug)
+fprintf(stderr, "*** %s(%p)\n", __FUNCTION__, scp);
+dbg_scp(scp);
+
+ for ( ix =0 ; ix < scp->nkeys ; ix++ ) {
+ scp->keys[ix]->data = _free(scp->keys[ix]->data);
+ scp->keys[ix] = _free(scp->keys[ix]);
+ }
+ scp->keys = _free(scp->keys);
+ scp->nkeys = 0;
+
+ return scp;
+}
+
+
+static SCP_t scpResetAv(SCP_t scp)
+{
+ int xx;
+
+if (_debug)
+fprintf(stderr, "*** %s(%p)\n", __FUNCTION__, scp);
+dbg_scp(scp);
+
+ if (scp->av) {
+ if (scp->nalloc <= 0) {
+ /* Clean up SCP_t used by sqlite3_get_table(). */
+ sqlite3_free_table(scp->av);
+ scp->av = NULL;
+ scp->nalloc = 0;
+ } else {
+ /* Clean up SCP_t used by sql_step(). */
+ for (xx = 0; xx < scp->ac; xx++)
+ scp->av[xx] = _free(scp->av[xx]);
+ if (scp->av != NULL)
+ memset(scp->av, 0, scp->nalloc * sizeof(*scp->av));
+ if (scp->avlen != NULL)
+ memset(scp->avlen, 0, scp->nalloc * sizeof(*scp->avlen));
+ scp->av = _free(scp->av);
+ scp->avlen = _free(scp->avlen);
+ scp->nalloc = 0;
+ }
+ } else
+ scp->nalloc = 0;
+ scp->ac = 0;
+ scp->nr = 0;
+ scp->nc = 0;
+
+ return scp;
+}
+
+
+static SCP_t scpReset(SCP_t scp)
+{
+ int xx;
+
+if (_debug)
+fprintf(stderr, "*** %s(%p)\n", __FUNCTION__, scp);
+dbg_scp(scp);
+
+ if (scp->cmd) {
+ sqlite3_free(scp->cmd);
+ scp->cmd = NULL;
+ }
+ if (scp->pStmt) {
+ xx = sqlite3_reset(scp->pStmt);
+ if (xx) rpmlog(RPMLOG_DEBUG, "reset %d\n", xx);
+ xx = sqlite3_finalize(scp->pStmt);
+ if (xx) rpmlog(RPMLOG_DEBUG, "finalize %d\n", xx);
+ scp->pStmt = NULL;
+ }
+
+ scp = scpResetAv(scp);
+
+ scp->rx = 0;
+ return scp;
+}
+
+static SCP_t scpFree(SCP_t scp)
+{
+ scp = scpReset(scp);
+ scp = scpResetKeys(scp);
+ scp->av = _free(scp->av);
+ scp->avlen = _free(scp->avlen);
+
+if (_debug)
+fprintf(stderr, "*** %s(%p)\n", __FUNCTION__, scp);
+ scp = _free(scp);
+ return NULL;
+}
+
+static SCP_t scpNew(DB * dbp)
+{
+ SCP_t scp = xcalloc(1, sizeof(*scp));
+ scp->dbp = dbp;
+
+ scp->used = 0;
+
+ scp->lkey = NULL;
+ scp->ldata = NULL;
+
+if (_debug)
+fprintf(stderr, "*** %s(%p)\n", __FUNCTION__, scp);
+ return scp;
+}
+
+static int sql_step(dbiIndex dbi, SCP_t scp)
+{
+ const char * cname;
+ const char * vtype;
+ size_t nb;
+ int loop;
+ int need;
+ int rc;
+ int i;
+
+ scp->nc = sqlite3_column_count(scp->pStmt);
+
+ if (scp->nr == 0 && scp->av != NULL)
+ need = 2 * scp->nc;
+ else
+ need = scp->nc;
+
+ /* XXX scp->nc = need = scp->nalloc = 0 case forces + 1 here */
+ if (!scp->ac && !need && !scp->nalloc)
+ need++;
+
+ if (scp->ac + need >= scp->nalloc) {
+ /* XXX +4 is bogus, was +1 */
+ scp->nalloc = 2 * scp->nalloc + need + 4;
+ scp->av = xrealloc(scp->av, scp->nalloc * sizeof(*scp->av));
+ scp->avlen = xrealloc(scp->avlen, scp->nalloc * sizeof(*scp->avlen));
+ }
+
+ if (scp->nr == 0) {
+ for (i = 0; i < scp->nc; i++) {
+ scp->av[scp->ac] = xstrdup(sqlite3_column_name(scp->pStmt, i));
+ if (scp->avlen) scp->avlen[scp->ac] = strlen(scp->av[scp->ac]) + 1;
+ scp->ac++;
+assert(scp->ac <= scp->nalloc);
+ }
+ }
+
+ loop = 1;
+ while (loop) {
+ rc = sqlite3_step(scp->pStmt);
+ switch (rc) {
+ case SQLITE_DONE:
+if (_debug)
+fprintf(stderr, "sqlite3_step: DONE scp %p [%d:%d] av %p avlen %p\n", scp, scp->ac, scp->nalloc, scp->av, scp->avlen);
+ loop = 0;
+ break;
+ case SQLITE_ROW:
+ if (scp->av != NULL)
+ for (i = 0; i < scp->nc; i++) {
+ /* Expand the row array for new elements */
+ if (scp->ac + need >= scp->nalloc) {
+ /* XXX +4 is bogus, was +1 */
+ scp->nalloc = 2 * scp->nalloc + need + 4;
+ scp->av = xrealloc(scp->av, scp->nalloc * sizeof(*scp->av));
+ scp->avlen = xrealloc(scp->avlen, scp->nalloc * sizeof(*scp->avlen));
+ }
+
+ cname = sqlite3_column_name(scp->pStmt, i);
+ vtype = sqlite3_column_decltype(scp->pStmt, i);
+ nb = 0;
+
+ if (!strcmp(vtype, "blob")) {
+ const void * v = sqlite3_column_blob(scp->pStmt, i);
+ nb = sqlite3_column_bytes(scp->pStmt, i);
+if (_debug)
+fprintf(stderr, "\t%d %s %s %p[%zd]\n", i, cname, vtype, v, nb);
+ if (nb > 0) {
+ void * t = xmalloc(nb);
+ scp->av[scp->ac] = memcpy(t, v, nb);
+ scp->avlen[scp->ac] = nb;
+ scp->ac++;
+ }
+ } else
+ if (!strcmp(vtype, "double")) {
+ double v = sqlite3_column_double(scp->pStmt, i);
+ nb = sizeof(v);
+if (_debug)
+fprintf(stderr, "\t%d %s %s %g\n", i, cname, vtype, v);
+ if (nb > 0) {
+ scp->av[scp->ac] = memcpy(xmalloc(nb), &v, nb);
+ scp->avlen[scp->ac] = nb;
+assert(dbiByteSwapped(dbi) == 0); /* Byte swap?! */
+ scp->ac++;
+ }
+ } else
+ if (!strcmp(vtype, "int")) {
+ int32_t v = sqlite3_column_int(scp->pStmt, i);
+ nb = sizeof(v);
+if (_debug)
+fprintf(stderr, "\t%d %s %s %d\n", i, cname, vtype, v);
+ if (nb > 0) {
+ scp->av[scp->ac] = memcpy(xmalloc(nb), &v, nb);
+ scp->avlen[scp->ac] = nb;
+if (dbiByteSwapped(dbi) == 1)
+{
+ union _dbswap dbswap;
+ memcpy(&dbswap.ui, scp->av[scp->ac], sizeof(dbswap.ui));
+ _DBSWAP(dbswap);
+ memcpy(scp->av[scp->ac], &dbswap.ui, sizeof(dbswap.ui));
+}
+ scp->ac++;
+ }
+ } else
+ if (!strcmp(vtype, "int64")) {
+ int64_t v = sqlite3_column_int64(scp->pStmt, i);
+ nb = sizeof(v);
+if (_debug)
+fprintf(stderr, "\t%d %s %s %ld\n", i, cname, vtype, (long)v);
+ if (nb > 0) {
+ scp->av[scp->ac] = memcpy(xmalloc(nb), &v, nb);
+ scp->avlen[scp->ac] = nb;
+assert(dbiByteSwapped(dbi) == 0); /* Byte swap?! */
+ scp->ac++;
+ }
+ } else
+ if (!strcmp(vtype, "text")) {
+ const char * v = (const char *) sqlite3_column_text(scp->pStmt, i);
+ nb = strlen(v) + 1;
+if (_debug)
+fprintf(stderr, "\t%d %s %s \"%s\"\n", i, cname, vtype, v);
+ if (nb > 0) {
+ scp->av[scp->ac] = memcpy(xmalloc(nb), v, nb);
+ scp->avlen[scp->ac] = nb;
+ scp->ac++;
+ }
+ }
+assert(scp->ac <= scp->nalloc);
+ }
+ scp->nr++;
+ break;
+ case SQLITE_BUSY:
+ fprintf(stderr, "sqlite3_step: BUSY %d\n", rc);
+ break;
+ case SQLITE_ERROR: {
+ char *cwd = rpmGetCwd();
+ fprintf(stderr, "sqlite3_step: ERROR %d -- %s\n", rc, scp->cmd);
+ fprintf(stderr, " %s (%d)\n",
+ sqlite3_errmsg(((SQL_DB*)dbi->dbi_db)->db), sqlite3_errcode(((SQL_DB*)dbi->dbi_db)->db));
+
+ fprintf(stderr, " cwd '%s'\n", cwd);
+ free(cwd);
+ loop = 0;
+ }
+ break;
+ case SQLITE_MISUSE:
+ fprintf(stderr, "sqlite3_step: MISUSE %d\n", rc);
+ loop = 0;
+ break;
+ default:
+ fprintf(stderr, "sqlite3_step: rc %d\n", rc);
+ loop = 0;
+ break;
+ }
+ }
+
+ if (rc == SQLITE_DONE)
+ rc = SQLITE_OK;
+
+ return rc;
+}
+
+static int sql_bind_key(dbiIndex dbi, SCP_t scp, int pos, DBT * key)
+{
+ int rc = 0;
+
+ union _dbswap dbswap;
+
+assert(key->data != NULL);
+ switch (dbi->dbi_rpmtag) {
+ case RPMDBI_PACKAGES:
+ { unsigned int hnum;
+assert(key->size == sizeof(int32_t));
+ memcpy(&hnum, key->data, sizeof(hnum));
+
+if (dbiByteSwapped(dbi) == 1)
+{
+ memcpy(&dbswap.ui, &hnum, sizeof(dbswap.ui));
+ _DBSWAP(dbswap);
+ memcpy(&hnum, &dbswap.ui, sizeof(dbswap.ui));
+}
+ rc = sqlite3_bind_int(scp->pStmt, pos, hnum);
+ } break;
+ default:
+ switch (rpmTagGetType(dbi->dbi_rpmtag)) {
+ case RPM_NULL_TYPE:
+ case RPM_BIN_TYPE:
+ rc = sqlite3_bind_blob(scp->pStmt, pos, key->data, key->size, SQLITE_STATIC);
+ break;
+ case RPM_CHAR_TYPE:
+ case RPM_INT8_TYPE:
+ { unsigned char i;
+assert(key->size == sizeof(unsigned char));
+assert(dbiByteSwapped(dbi) == 0); /* Byte swap?! */
+ memcpy(&i, key->data, sizeof(i));
+ rc = sqlite3_bind_int(scp->pStmt, pos, i);
+ } break;
+ case RPM_INT16_TYPE:
+ { unsigned short i;
+assert(key->size == sizeof(int16_t));
+assert(dbiByteSwapped(dbi) == 0); /* Byte swap?! */
+ memcpy(&i, key->data, sizeof(i));
+ rc = sqlite3_bind_int(scp->pStmt, pos, i);
+ } break;
+ case RPM_INT32_TYPE:
+/* case RPM_INT64_TYPE: */
+ default:
+ { unsigned int i;
+assert(key->size == sizeof(int32_t));
+ memcpy(&i, key->data, sizeof(i));
+
+if (dbiByteSwapped(dbi) == 1)
+{
+ memcpy(&dbswap.ui, &i, sizeof(dbswap.ui));
+ _DBSWAP(dbswap);
+ memcpy(&i, &dbswap.ui, sizeof(dbswap.ui));
+}
+ rc = sqlite3_bind_int(scp->pStmt, pos, i);
+ } break;
+ case RPM_STRING_TYPE:
+ case RPM_STRING_ARRAY_TYPE:
+ case RPM_I18NSTRING_TYPE:
+ rc = sqlite3_bind_text(scp->pStmt, pos, key->data, key->size, SQLITE_STATIC);
+ break;
+ }
+ }
+
+ return rc;
+}
+
+static int sql_bind_data(dbiIndex dbi, SCP_t scp, int pos, DBT * data)
+{
+ int rc;
+
+assert(data->data != NULL);
+ rc = sqlite3_bind_blob(scp->pStmt, pos, data->data, data->size, SQLITE_STATIC);
+
+ return rc;
+}
+
+/*===================================================================*/
+/*
+ * Transaction support
+ */
+
+static int sql_startTransaction(dbiIndex dbi)
+{
+ SQL_DB * sqldb = (SQL_DB *) dbi->dbi_db;
+ int rc = 0;
+
+ /* XXX: Transaction Support */
+ if (!sqldb->transaction) {
+ char * pzErrmsg;
+ rc = sqlite3_exec(sqldb->db, "BEGIN TRANSACTION;", NULL, NULL, &pzErrmsg);
+
+if (_debug)
+fprintf(stderr, "Begin %s SQL transaction %s (%d)\n",
+ dbi->dbi_subfile, pzErrmsg, rc);
+
+ if (rc == 0)
+ sqldb->transaction = 1;
+ }
+
+ return rc;
+}
+
+static int sql_endTransaction(dbiIndex dbi)
+{
+ SQL_DB * sqldb = (SQL_DB *) dbi->dbi_db;
+ int rc=0;
+
+ /* XXX: Transaction Support */
+ if (sqldb->transaction) {
+ char * pzErrmsg;
+ rc = sqlite3_exec(sqldb->db, "END TRANSACTION;", NULL, NULL, &pzErrmsg);
+
+if (_debug)
+fprintf(stderr, "End %s SQL transaction %s (%d)\n",
+ dbi->dbi_subfile, pzErrmsg, rc);
+
+ if (rc == 0)
+ sqldb->transaction = 0;
+ }
+
+ return rc;
+}
+
+static int sql_commitTransaction(dbiIndex dbi, int flag)
+{
+ SQL_DB * sqldb = (SQL_DB *) dbi->dbi_db;
+ int rc = 0;
+
+ /* XXX: Transactions */
+ if ( sqldb->transaction ) {
+ char * pzErrmsg;
+ rc = sqlite3_exec(sqldb->db, "COMMIT;", NULL, NULL, &pzErrmsg);
+
+if (_debug)
+fprintf(stderr, "Commit %s SQL transaction(s) %s (%d)\n",
+ dbi->dbi_subfile, pzErrmsg, rc);
+
+ sqldb->transaction=0;
+
+ /* Start a new transaction if we were in the middle of one */
+ if ( flag == 0 )
+ rc = sql_startTransaction(dbi);
+ }
+
+ return rc;
+}
+
+static int sql_busy_handler(void * dbi_void, int time)
+{
+ dbiIndex dbi = (dbiIndex) dbi_void;
+
+ rpmlog(RPMLOG_WARNING, _("Unable to get lock on db %s, retrying... (%d)\n"),
+ dbi->dbi_file, time);
+
+ (void) sleep(1);
+
+ return 1;
+}
+
+/*===================================================================*/
+
+/**
+ * Verify the DB is setup.. if not initialize it
+ *
+ * Create the table.. create the db_info
+ */
+static int sql_initDB(dbiIndex dbi)
+{
+ SQL_DB * sqldb = (SQL_DB *) dbi->dbi_db;
+ SCP_t scp = scpNew(dbi->dbi_db);
+ char *cmd = NULL;
+ int rc = 0;
+
+ /* Check if the table exists... */
+ rasprintf(&cmd,
+ "SELECT name FROM 'sqlite_master' WHERE type='table' and name='%s';",
+ dbi->dbi_subfile);
+ rc = sqlite3_get_table(sqldb->db, cmd,
+ &scp->av, &scp->nr, &scp->nc, (char **)&scp->pzErrmsg);
+ cmd = _free(cmd);
+ if (rc)
+ goto exit;
+
+ if (scp->nr < 1) {
+ const char * valtype = "blob";
+ const char * keytype;
+
+ switch (dbi->dbi_rpmtag) {
+ case RPMDBI_PACKAGES:
+ keytype = "int UNIQUE PRIMARY KEY";
+ valtype = "blob";
+ break;
+ default:
+ switch (rpmTagGetType(dbi->dbi_rpmtag)) {
+ case RPM_NULL_TYPE:
+ case RPM_BIN_TYPE:
+ default:
+ keytype = "blob UNIQUE";
+ break;
+ case RPM_CHAR_TYPE:
+ case RPM_INT8_TYPE:
+ case RPM_INT16_TYPE:
+ case RPM_INT32_TYPE:
+/* case RPM_INT64_TYPE: */
+ keytype = "int UNIQUE";
+ break;
+ case RPM_STRING_TYPE:
+ case RPM_STRING_ARRAY_TYPE:
+ case RPM_I18NSTRING_TYPE:
+ keytype = "text UNIQUE";
+ break;
+ }
+ }
+if (_debug)
+fprintf(stderr, "\t%s(%d) type(%d) keytype %s\n", rpmTagGetName(dbi->dbi_rpmtag), dbi->dbi_rpmtag, rpmTagGetType(dbi->dbi_rpmtag), keytype);
+ rasprintf(&cmd, "CREATE TABLE '%s' (key %s, value %s)",
+ dbi->dbi_subfile, keytype, valtype);
+ rc = sqlite3_exec(sqldb->db, cmd, NULL, NULL, (char **)&scp->pzErrmsg);
+ cmd = _free(cmd);
+ if (rc)
+ goto exit;
+
+ rasprintf(&cmd, "CREATE TABLE 'db_info' (endian TEXT)");
+ rc = sqlite3_exec(sqldb->db, cmd, NULL, NULL, (char **)&scp->pzErrmsg);
+ cmd = _free(cmd);
+ if (rc)
+ goto exit;
+
+ rasprintf(&cmd, "INSERT INTO 'db_info' values('%d')", ((union _dbswap *)&endian)->uc[0]);
+ rc = sqlite3_exec(sqldb->db, cmd, NULL, NULL, (char **)&scp->pzErrmsg);
+ _free(cmd);
+ if (rc)
+ goto exit;
+ }
+
+ if (dbi->dbi_no_fsync) {
+ int xx;
+ rasprintf(&cmd, "PRAGMA synchronous = OFF;");
+ xx = sqlite3_exec(sqldb->db, cmd, NULL, NULL, (char **)&scp->pzErrmsg);
+ cmd = _free(cmd);
+ }
+
+exit:
+ if (rc)
+ rpmlog(RPMLOG_WARNING, _("Unable to initDB %s (%d)\n"),
+ scp->pzErrmsg, rc);
+
+ scp = scpFree(scp);
+
+ return rc;
+}
+
+/**
+ * Close database cursor.
+ * @param dbi index database handle
+ * @param dbcursor database cursor
+ * @param flags (unused)
+ * @return 0 on success
+ */
+static int sql_cclose (dbiIndex dbi, DBC * dbcursor,
+ unsigned int flags)
+{
+ SCP_t scp = (SCP_t)dbcursor;
+ int rc;
+
+if (_debug)
+fprintf(stderr, "==> %s(%p)\n", __FUNCTION__, scp);
+
+ if (scp->lkey)
+ scp->lkey = _free(scp->lkey);
+
+ if (scp->ldata)
+ scp->ldata = _free(scp->ldata);
+
+enterChroot(dbi);
+
+ if (flags == DB_WRITECURSOR)
+ rc = sql_commitTransaction(dbi, 1);
+ else
+ rc = sql_endTransaction(dbi);
+
+ scp = scpFree(scp);
+
+leaveChroot(dbi);
+
+ return rc;
+}
+
+/**
+ * Close index database, and destroy database handle.
+ * @param dbi index database handle
+ * @param flags (unused)
+ * @return 0 on success
+ */
+static int sql_close(dbiIndex dbi, unsigned int flags)
+{
+ SQL_DB * sqldb = (SQL_DB *) dbi->dbi_db;
+ int rc = 0;
+
+ if (sqldb) {
+enterChroot(dbi);
+
+ /* Commit, don't open a new one */
+ rc = sql_commitTransaction(dbi, 1);
+
+ (void) sqlite3_close(sqldb->db);
+
+ rpmlog(RPMLOG_DEBUG, "closed sql db %s\n",
+ dbi->dbi_subfile);
+
+ dbi->dbi_stats = _free(dbi->dbi_stats);
+ dbi->dbi_file = _free(dbi->dbi_file);
+ dbi->dbi_db = _free(dbi->dbi_db);
+
+leaveChroot(dbi);
+ }
+
+ dbi = _free(dbi);
+
+ return rc;
+}
+
+/**
+ * Return handle for an index database.
+ * @param rpmdb rpm database
+ * @param rpmtag rpm tag
+ * @param dbip
+ * @return 0 on success
+ */
+static int sql_open(rpmdb rpmdb, rpmTag rpmtag, dbiIndex * dbip)
+{
+ extern const struct _dbiVec sqlitevec;
+
+ char * root;
+ char * home;
+ char * dbhome;
+ const char * dbfile;
+ char * dbfname;
+ const char * sql_errcode;
+ dbiIndex dbi;
+ SQL_DB * sqldb;
+ size_t len;
+ int rc = 0;
+ int xx;
+
+ if (dbip)
+ *dbip = NULL;
+
+ /*
+ * Parse db configuration parameters.
+ */
+ if ((dbi = db3New(rpmdb, rpmtag)) == NULL)
+ return 1;
+
+ /*
+ * Get the prefix/root component and directory path
+ */
+ root = rpmdb->db_root;
+ home = rpmdb->db_home;
+
+ dbi->dbi_root = root;
+ dbi->dbi_home = home;
+
+ dbfile = rpmTagGetName(dbi->dbi_rpmtag);
+
+enterChroot(dbi);
+
+ /*
+ * Make a copy of the tagName result..
+ * use this for the filename and table name
+ */
+ {
+ char * t;
+ len = strlen(dbfile);
+ t = xcalloc(len + 1, sizeof(*t));
+ (void) stpcpy( t, dbfile );
+ dbi->dbi_file = t;
+/* WRONG */
+ dbi->dbi_subfile = t;
+ }
+
+ dbi->dbi_mode=O_RDWR;
+
+ dbhome = rpmGenPath(NULL, home, NULL);
+
+ /*
+ * Create the /var/lib/rpm directory if it doesn't exist (root only).
+ */
+ (void) rpmioMkpath(dbhome, 0755, getuid(), getgid());
+
+ dbfname = rpmGenPath(dbhome, dbi->dbi_file, NULL);
+
+ rpmlog(RPMLOG_DEBUG, "opening sql db %s (%s) mode=0x%x\n",
+ dbfname, dbi->dbi_subfile, dbi->dbi_mode);
+
+ /* Open the Database */
+ sqldb = xcalloc(1,sizeof(*sqldb));
+
+ sql_errcode = NULL;
+ xx = sqlite3_open(dbfname, &sqldb->db);
+ if (xx != SQLITE_OK)
+ sql_errcode = sqlite3_errmsg(sqldb->db);
+
+ if (sqldb->db)
+ (void) sqlite3_busy_handler(sqldb->db, &sql_busy_handler, dbi);
+
+ sqldb->transaction = 0; /* Initialize no current transactions */
+
+ dbi->dbi_db = (DB *)sqldb;
+
+ if (sql_errcode != NULL) {
+ rpmlog(RPMLOG_ERR, _("Unable to open database: %s\n"), sql_errcode);
+ rc = EINVAL;
+ }
+
+ /* initialize table */
+ if (rc == 0)
+ rc = sql_initDB(dbi);
+
+ if (rc == 0 && dbi->dbi_db != NULL && dbip != NULL) {
+ dbi->dbi_vec = &sqlitevec;
+ *dbip = dbi;
+ } else {
+ (void) sql_close(dbi, 0);
+ }
+
+ free(dbhome);
+ dbfname = _free(dbfname);
+
+leaveChroot(dbi);
+
+ return rc;
+}
+
+/**
+ * Flush pending operations to disk.
+ * @param dbi index database handle
+ * @param flags (unused)
+ * @return 0 on success
+ */
+static int sql_sync (dbiIndex dbi, unsigned int flags)
+{
+ int rc = 0;
+
+enterChroot(dbi);
+ rc = sql_commitTransaction(dbi, 0);
+leaveChroot(dbi);
+
+ return rc;
+}
+
+/**
+ * Open database cursor.
+ * @param dbi index database handle
+ * @param txnid database transaction handle
+ * @retval dbcp address of new database cursor
+ * @param flags DB_WRITECURSOR or 0
+ * @return 0 on success
+ */
+static int sql_copen (dbiIndex dbi, DB_TXN * txnid,
+ DBC ** dbcp, unsigned int flags)
+{
+ SCP_t scp = scpNew(dbi->dbi_db);
+ DBC * dbcursor = (DBC *)scp;
+ int rc = 0;
+
+if (_debug)
+fprintf(stderr, "==> %s(%s) tag %d type %d scp %p\n", __FUNCTION__, rpmTagGetName(dbi->dbi_rpmtag), dbi->dbi_rpmtag, rpmTagGetType(dbi->dbi_rpmtag), scp);
+
+enterChroot(dbi);
+
+ /* If we're going to write, start a transaction (lock the DB) */
+ if (flags == DB_WRITECURSOR)
+ rc = sql_startTransaction(dbi);
+
+ if (dbcp)
+ *dbcp = dbcursor;
+ else
+ (void) sql_cclose(dbi, dbcursor, 0);
+
+leaveChroot(dbi);
+
+ return rc;
+}
+
+/**
+ * Delete (key,data) pair(s) using db->del or dbcursor->c_del.
+ * @param dbi index database handle
+ * @param dbcursor database cursor (NULL will use db->del)
+ * @param key delete key value/length/flags
+ * @param data delete data value/length/flags
+ * @param flags (unused)
+ * @return 0 on success
+ */
+static int sql_cdel (dbiIndex dbi, DBC * dbcursor, DBT * key,
+ DBT * data, unsigned int flags)
+{
+ SQL_DB * sqldb = (SQL_DB *) dbi->dbi_db;
+ SCP_t scp = scpNew(dbi->dbi_db);
+ int rc = 0;
+
+dbg_keyval(__FUNCTION__, dbi, dbcursor, key, data, flags);
+enterChroot(dbi);
+
+ scp->cmd = sqlite3_mprintf("DELETE FROM '%q' WHERE key=? AND value=?;",
+ dbi->dbi_subfile);
+
+ rc = sqlite3_prepare(sqldb->db, scp->cmd, strlen(scp->cmd), &scp->pStmt, &scp->pzErrmsg);
+ if (rc) rpmlog(RPMLOG_DEBUG, "cdel(%s) prepare %s (%d)\n", dbi->dbi_subfile, sqlite3_errmsg(sqldb->db), rc);
+ rc = sql_bind_key(dbi, scp, 1, key);
+ if (rc) rpmlog(RPMLOG_DEBUG, "cdel(%s) bind key %s (%d)\n", dbi->dbi_subfile, sqlite3_errmsg(sqldb->db), rc);
+ rc = sql_bind_data(dbi, scp, 2, data);
+ if (rc) rpmlog(RPMLOG_DEBUG, "cdel(%s) bind data %s (%d)\n", dbi->dbi_subfile, sqlite3_errmsg(sqldb->db), rc);
+
+ rc = sql_step(dbi, scp);
+ if (rc) rpmlog(RPMLOG_DEBUG, "cdel(%s) sql_step rc %d\n", dbi->dbi_subfile, rc);
+
+ scp = scpFree(scp);
+
+leaveChroot(dbi);
+
+ return rc;
+}
+
+/**
+ * Retrieve (key,data) pair using db->get or dbcursor->c_get.
+ * @param dbi index database handle
+ * @param dbcursor database cursor (NULL will use db->get)
+ * @param key retrieve key value/length/flags
+ * @param data retrieve data value/length/flags
+ * @param flags (unused)
+ * @return 0 on success
+ */
+static int sql_cget (dbiIndex dbi, DBC * dbcursor, DBT * key,
+ DBT * data, unsigned int flags)
+{
+ SQL_DB * sqldb = (SQL_DB *) dbi->dbi_db;
+ SCP_t scp = (SCP_t)dbcursor;
+ int rc = 0;
+ int ix;
+
+dbg_keyval(__FUNCTION__, dbi, dbcursor, key, data, flags);
+
+enterChroot(dbi);
+
+ /*
+ * First determine if this is a new scan or existing scan
+ */
+
+if (_debug)
+fprintf(stderr, "\tcget(%s) scp %p rc %d flags %d av %p\n",
+ dbi->dbi_subfile, scp, rc, flags, scp->av);
+ if ( flags == DB_SET || scp->used == 0 ) {
+ scp->used = 1; /* Signal this scp as now in use... */
+ scp = scpReset(scp); /* Free av and avlen, reset counters.*/
+
+/* XXX: Should we also reset the key table here? Can you re-use a cursor? */
+
+ /*
+ * If we're scanning everything, load the iterator key table
+ */
+ if ( key->size == 0) {
+ scp->all = 1;
+
+/*
+ * The only condition not dealt with is if there are multiple identical keys. This can lead
+ * to later iteration confusion. (It may return the same value for the multiple keys.)
+ */
+
+/* Only RPMDBI_PACKAGES is supposed to be iterating, and this is guarenteed to be unique */
+assert(dbi->dbi_rpmtag == RPMDBI_PACKAGES);
+
+ switch (dbi->dbi_rpmtag) {
+ case RPMDBI_PACKAGES:
+ scp->cmd = sqlite3_mprintf("SELECT key FROM '%q' ORDER BY key;",
+ dbi->dbi_subfile);
+ break;
+ default:
+ scp->cmd = sqlite3_mprintf("SELECT key FROM '%q';",
+ dbi->dbi_subfile);
+ break;
+ }
+ rc = sqlite3_prepare(sqldb->db, scp->cmd, strlen(scp->cmd), &scp->pStmt, &scp->pzErrmsg);
+ if (rc) rpmlog(RPMLOG_DEBUG, "cget(%s) sequential prepare %s (%d)\n", dbi->dbi_subfile, sqlite3_errmsg(sqldb->db), rc);
+
+ rc = sql_step(dbi, scp);
+ if (rc) rpmlog(RPMLOG_DEBUG, "cget(%s) sequential sql_step rc %d\n", dbi->dbi_subfile, rc);
+
+ scp = scpResetKeys(scp);
+ scp->nkeys = scp->nr;
+ scp->keys = xcalloc(scp->nkeys, sizeof(*scp->keys));
+ for (ix = 0; ix < scp->nkeys; ix++) {
+ scp->keys[ix] = xmalloc(sizeof(DBT));
+ scp->keys[ix]->size = scp->avlen[ix+1];
+ scp->keys[ix]->data = xmalloc(scp->keys[ix]->size);
+ memcpy(scp->keys[ix]->data, scp->av[ix+1], scp->avlen[ix+1]);
+ }
+ } else {
+ /*
+ * We're only scanning ONE element
+ */
+ scp = scpResetKeys(scp);
+ scp->nkeys = 1;
+ scp->keys = xcalloc(scp->nkeys, sizeof(*scp->keys));
+ scp->keys[0] = xmalloc(sizeof(DBT));
+ scp->keys[0]->size = key->size;
+ scp->keys[0]->data = xmalloc(scp->keys[0]->size);
+ memcpy(scp->keys[0]->data, key->data, key->size);
+ }
+
+ scp = scpReset(scp); /* reset */
+
+ /* Prepare SQL statement to retrieve the value for the current key */
+ scp->cmd = sqlite3_mprintf("SELECT value FROM '%q' WHERE key=?;", dbi->dbi_subfile);
+ rc = sqlite3_prepare(sqldb->db, scp->cmd, strlen(scp->cmd), &scp->pStmt, &scp->pzErrmsg);
+
+ if (rc) rpmlog(RPMLOG_DEBUG, "cget(%s) prepare %s (%d)\n", dbi->dbi_subfile, sqlite3_errmsg(sqldb->db), rc);
+ }
+
+ scp = scpResetAv(scp); /* Free av and avlen, reset counters.*/
+
+ /* Now continue with a normal retrive based on key */
+ if ((scp->rx + 1) > scp->nkeys )
+ rc = DB_NOTFOUND; /* At the end of the list */
+
+ if (rc != 0)
+ goto exit;
+
+ /* Bind key to prepared statement */
+ rc = sql_bind_key(dbi, scp, 1, scp->keys[scp->rx]);
+ if (rc) rpmlog(RPMLOG_DEBUG, "cget(%s) key bind %s (%d)\n", dbi->dbi_subfile, sqlite3_errmsg(sqldb->db), rc);
+
+ rc = sql_step(dbi, scp);
+ if (rc) rpmlog(RPMLOG_DEBUG, "cget(%s) sql_step rc %d\n", dbi->dbi_subfile, rc);
+
+ rc = sqlite3_reset(scp->pStmt);
+ if (rc) rpmlog(RPMLOG_DEBUG, "reset %d\n", rc);
+
+/* 1 key should return 0 or 1 row/value */
+assert(scp->nr < 2);
+
+ if (scp->nr == 0 && scp->all == 0)
+ rc = DB_NOTFOUND; /* No data for that key found! */
+
+ if (rc != 0)
+ goto exit;
+
+ /* If we're looking at the whole db, return the key */
+ if (scp->all) {
+
+/* To get this far there has to be _1_ key returned! (protect against dup keys) */
+assert(scp->nr == 1);
+
+ if ( scp->lkey ) {
+ scp->lkey = _free(scp->lkey);
+ }
+
+ key->size = scp->keys[scp->rx]->size;
+ key->data = xmalloc(key->size);
+ if (! (key->flags & DB_DBT_MALLOC))
+ scp->lkey = key->data;
+
+ (void) memcpy(key->data, scp->keys[scp->rx]->data, key->size);
+ }
+
+ /* Construct and return the data element (element 0 is "value", 1 is _THE_ value)*/
+ switch (dbi->dbi_rpmtag) {
+ default:
+ if ( scp->ldata ) {
+ scp->ldata = _free(scp->ldata);
+ }
+
+ data->size = scp->avlen[1];
+ data->data = xmalloc(data->size);
+ if (! (data->flags & DB_DBT_MALLOC) )
+ scp->ldata = data->data;
+
+ (void) memcpy(data->data, scp->av[1], data->size);
+ }
+
+ scp->rx++;
+
+ /* XXX FIXME: ptr alignment is fubar here. */
+if (_debug)
+fprintf(stderr, "\tcget(%s) found key 0x%x (%d)\n", dbi->dbi_subfile,
+ key->data == NULL ? 0 : *(unsigned int *)key->data, key->size);
+if (_debug)
+fprintf(stderr, "\tcget(%s) found data 0x%x (%d)\n", dbi->dbi_subfile,
+ key->data == NULL ? 0 : *(unsigned int *)data->data, data->size);
+
+exit:
+ if (rc == DB_NOTFOUND) {
+if (_debug)
+fprintf(stderr, "\tcget(%s) not found\n", dbi->dbi_subfile);
+ }
+
+leaveChroot(dbi);
+
+ return rc;
+}
+
+/**
+ * Store (key,data) pair using db->put or dbcursor->c_put.
+ * @param dbi index database handle
+ * @param dbcursor database cursor (NULL will use db->put)
+ * @param key store key value/length/flags
+ * @param data store data value/length/flags
+ * @param flags (unused)
+ * @return 0 on success
+ */
+static int sql_cput (dbiIndex dbi, DBC * dbcursor, DBT * key,
+ DBT * data, unsigned int flags)
+{
+ SQL_DB * sqldb = (SQL_DB *) dbi->dbi_db;
+ SCP_t scp = scpNew(dbi->dbi_db);
+ int rc = 0;
+
+dbg_keyval(__FUNCTION__, dbi, dbcursor, key, data, flags);
+
+enterChroot(dbi);
+
+ switch (dbi->dbi_rpmtag) {
+ default:
+ scp->cmd = sqlite3_mprintf("INSERT OR REPLACE INTO '%q' VALUES(?, ?);",
+ dbi->dbi_subfile);
+ rc = sqlite3_prepare(sqldb->db, scp->cmd, strlen(scp->cmd), &scp->pStmt, &scp->pzErrmsg);
+ if (rc) rpmlog(RPMLOG_DEBUG, "cput(%s) prepare %s (%d)\n",dbi->dbi_subfile, sqlite3_errmsg(sqldb->db), rc);
+ rc = sql_bind_key(dbi, scp, 1, key);
+ if (rc) rpmlog(RPMLOG_DEBUG, "cput(%s) key bind %s (%d)\n", dbi->dbi_subfile, sqlite3_errmsg(sqldb->db), rc);
+ rc = sql_bind_data(dbi, scp, 2, data);
+ if (rc) rpmlog(RPMLOG_DEBUG, "cput(%s) data bind %s (%d)\n", dbi->dbi_subfile, sqlite3_errmsg(sqldb->db), rc);
+
+ rc = sql_step(dbi, scp);
+ if (rc) rpmlog(RPMLOG_DEBUG, "cput(%s) sql_step rc %d\n", dbi->dbi_subfile, rc);
+
+ break;
+ }
+
+ scp = scpFree(scp);
+
+leaveChroot(dbi);
+
+ return rc;
+}
+
+/**
+ * Is database byte swapped?
+ * @param dbi index database handle
+ * @return 0 no
+ */
+static int sql_byteswapped (dbiIndex dbi)
+{
+ SQL_DB * sqldb = (SQL_DB *) dbi->dbi_db;
+ SCP_t scp = scpNew(dbi->dbi_db);
+ int sql_rc, rc = 0;
+ union _dbswap db_endian;
+
+enterChroot(dbi);
+
+ sql_rc = sqlite3_get_table(sqldb->db, "SELECT endian FROM 'db_info';",
+ &scp->av, &scp->nr, &scp->nc, (char **)&scp->pzErrmsg);
+
+ if (sql_rc == 0 && scp->nr > 0) {
+assert(scp->av != NULL);
+ db_endian.uc[0] = strtol(scp->av[1], NULL, 10);
+
+ if ( db_endian.uc[0] == ((union _dbswap *)&endian)->uc[0] )
+ rc = 0; /* Native endian */
+ else
+ rc = 1; /* swapped */
+
+ } else {
+ if ( sql_rc ) {
+ rpmlog(RPMLOG_DEBUG, "db_info failed %s (%d)\n",
+ scp->pzErrmsg, sql_rc);
+ }
+ rpmlog(RPMLOG_WARNING, _("Unable to determine DB endianess.\n"));
+ }
+
+ scp = scpFree(scp);
+
+leaveChroot(dbi);
+
+ return rc;
+}
+
+/**************************************************
+ *
+ * All of the following are not implemented!
+ * they are not used by the rest of the system
+ *
+ **************************************************/
+
+/**
+ * Associate secondary database with primary.
+ * @param dbi index database handle
+ * @param dbisecondary secondary index database handle
+ * @param callback create secondary key from primary (NULL if DB_RDONLY)
+ * @param flags DB_CREATE or 0
+ * @return 0 on success
+ */
+static int sql_associate (dbiIndex dbi, dbiIndex dbisecondary,
+ int (*callback) (DB *, const DBT *, const DBT *, DBT *),
+ unsigned int flags)
+{
+if (_debug)
+fprintf(stderr, "*** %s:\n", __FUNCTION__);
+ return EINVAL;
+}
+
+/**
+ * Return join cursor for list of cursors.
+ * @param dbi index database handle
+ * @param curslist NULL terminated list of database cursors
+ * @retval dbcp address of join database cursor
+ * @param flags DB_JOIN_NOSORT or 0
+ * @return 0 on success
+ */
+static int sql_join (dbiIndex dbi, DBC ** curslist, DBC ** dbcp,
+ unsigned int flags)
+{
+if (_debug)
+fprintf(stderr, "*** %s:\n", __FUNCTION__);
+ return EINVAL;
+}
+
+/**
+ * Duplicate a database cursor.
+ * @param dbi index database handle
+ * @param dbcursor database cursor
+ * @retval dbcp address of new database cursor
+ * @param flags DB_POSITION for same position, 0 for uninitialized
+ * @return 0 on success
+ */
+static int sql_cdup (dbiIndex dbi, DBC * dbcursor, DBC ** dbcp,
+ unsigned int flags)
+{
+if (_debug)
+fprintf(stderr, "*** %s:\n", __FUNCTION__);
+ return EINVAL;
+}
+
+/**
+ * Retrieve (key,data) pair using dbcursor->c_pget.
+ * @param dbi index database handle
+ * @param dbcursor database cursor
+ * @param key secondary retrieve key value/length/flags
+ * @param pkey primary retrieve key value/length/flags
+ * @param data primary retrieve data value/length/flags
+ * @param flags DB_NEXT, DB_SET, or 0
+ * @return 0 on success
+ */
+static int sql_cpget (dbiIndex dbi, DBC * dbcursor,
+ DBT * key, DBT * pkey, DBT * data, unsigned int flags)
+{
+if (_debug)
+fprintf(stderr, "*** %s:\n", __FUNCTION__);
+ return EINVAL;
+}
+
+/**
+ * Retrieve count of (possible) duplicate items using dbcursor->c_count.
+ * @param dbi index database handle
+ * @param dbcursor database cursor
+ * @param countp address of count
+ * @param flags (unused)
+ * @return 0 on success
+ */
+static int sql_ccount (dbiIndex dbi, DBC * dbcursor,
+ unsigned int * countp,
+ unsigned int flags)
+{
+if (_debug)
+fprintf(stderr, "*** %s:\n", __FUNCTION__);
+ return EINVAL;
+}
+
+/** \ingroup dbi
+ * Save statistics in database handle.
+ * @param dbi index database handle
+ * @param flags retrieve statistics that don't require traversal?
+ * @return 0 on success
+ */
+static int sql_stat (dbiIndex dbi, unsigned int flags)
+{
+ SQL_DB * sqldb = (SQL_DB *) dbi->dbi_db;
+ SCP_t scp = scpNew(dbi->dbi_db);
+ int rc = 0;
+ long nkeys = -1;
+
+enterChroot(dbi);
+
+ dbi->dbi_stats = _free(dbi->dbi_stats);
+
+ dbi->dbi_stats = xcalloc(1, sizeof(DB_HASH_STAT));
+
+ scp->cmd = sqlite3_mprintf("SELECT COUNT('key') FROM '%q';", dbi->dbi_subfile);
+ rc = sqlite3_get_table(sqldb->db, scp->cmd,
+ &scp->av, &scp->nr, &scp->nc, (char **)&scp->pzErrmsg);
+
+ if ( rc == 0 && scp->nr > 0) {
+assert(scp->av != NULL);
+ nkeys = strtol(scp->av[1], NULL, 10);
+
+ rpmlog(RPMLOG_DEBUG, " stat on %s nkeys %ld\n",
+ dbi->dbi_subfile, nkeys);
+ } else {
+ if ( rc ) {
+ rpmlog(RPMLOG_DEBUG, "stat failed %s (%d)\n",
+ scp->pzErrmsg, rc);
+ }
+ }
+
+ if (nkeys < 0)
+ nkeys = 4096; /* Good high value */
+
+ ((DB_HASH_STAT *)(dbi->dbi_stats))->hash_nkeys = nkeys;
+
+ scp = scpFree(scp);
+
+leaveChroot(dbi);
+
+ return rc;
+}
+
+/* Major, minor, patch version of DB.. we're not using db.. so set to 0 */
+/* open, close, sync, associate, join */
+/* cursor_open, cursor_close, cursor_dup, cursor_delete, cursor_get, */
+/* cursor_pget?, cursor_put, cursor_count */
+/* db_bytewapped, stat */
+const struct _dbiVec sqlitevec = {
+ 0, 0, 0,
+ sql_open,
+ sql_close,
+ sql_sync,
+ sql_associate,
+ sql_join,
+ sql_copen,
+ sql_cclose,
+ sql_cdup,
+ sql_cdel,
+ sql_cget,
+ sql_cpget,
+ sql_cput,
+ sql_ccount,
+ sql_byteswapped,
+ sql_stat
+};
+