1393 lines
36 KiB
C
1393 lines
36 KiB
C
|
|
/*
|
|
* 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 "rpmlib.h"
|
|
#include "rpmmacro.h"
|
|
#include "rpmurl.h" /* XXX urlPath proto */
|
|
|
|
#include "rpmdb_internal.h"
|
|
#include "rpmdb.h"
|
|
|
|
#include <sqlite3.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 unsigned int endian = 0x11223344;
|
|
|
|
static char * sqlCwd = NULL;
|
|
static int sqlInRoot = 0;
|
|
|
|
static void enterChroot(dbiIndex dbi)
|
|
{
|
|
int xx;
|
|
char * currDir = NULL;
|
|
|
|
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);
|
|
|
|
{
|
|
int currDirLen = 0;
|
|
|
|
do {
|
|
currDirLen += 128;
|
|
currDir = xrealloc(currDir, currDirLen);
|
|
memset(currDir, 0, currDirLen);
|
|
} while (getcwd(currDir, currDirLen) == NULL && errno == ERANGE);
|
|
}
|
|
|
|
sqlCwd = currDir;
|
|
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_WARNING, "reset %d\n", xx);
|
|
xx = sqlite3_finalize(scp->pStmt);
|
|
if (xx) rpmlog(RPMLOG_WARNING, "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:
|
|
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", getcwd(NULL,0));
|
|
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[BUFSIZ];
|
|
int rc = 0;
|
|
|
|
/* Check if the table exists... */
|
|
sprintf(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);
|
|
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);
|
|
sprintf(cmd, "CREATE TABLE '%s' (key %s, value %s)",
|
|
dbi->dbi_subfile, keytype, valtype);
|
|
rc = sqlite3_exec(sqldb->db, cmd, NULL, NULL, (char **)&scp->pzErrmsg);
|
|
if (rc)
|
|
goto exit;
|
|
|
|
sprintf(cmd, "CREATE TABLE 'db_info' (endian TEXT)");
|
|
rc = sqlite3_exec(sqldb->db, cmd, NULL, NULL, (char **)&scp->pzErrmsg);
|
|
if (rc)
|
|
goto exit;
|
|
|
|
sprintf(cmd, "INSERT INTO 'db_info' values('%d')", ((union _dbswap *)&endian)->uc[0]);
|
|
rc = sqlite3_exec(sqldb->db, cmd, NULL, NULL, (char **)&scp->pzErrmsg);
|
|
if (rc)
|
|
goto exit;
|
|
}
|
|
|
|
if (dbi->dbi_no_fsync) {
|
|
int xx;
|
|
sprintf(cmd, "PRAGMA synchronous = OFF;");
|
|
xx = sqlite3_exec(sqldb->db, cmd, NULL, NULL, (char **)&scp->pzErrmsg);
|
|
}
|
|
|
|
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 struct _dbiVec sqlitevec;
|
|
|
|
const char * urlfn = NULL;
|
|
const char * root;
|
|
const char * home;
|
|
const char * dbhome;
|
|
const char * dbfile;
|
|
const 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;
|
|
|
|
/*
|
|
* Either the root or directory components may be a URL. Concatenate,
|
|
* convert the URL to a path, and add the name of the file.
|
|
*/
|
|
urlfn = rpmGenPath(NULL, home, NULL);
|
|
(void) urlPath(urlfn, &dbhome);
|
|
|
|
/*
|
|
* 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_DEBUG, "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);
|
|
}
|
|
|
|
urlfn = _free(urlfn);
|
|
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_WARNING, "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_WARNING, "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_WARNING, "cdel(%s) bind data %s (%d)\n", dbi->dbi_subfile, sqlite3_errmsg(sqldb->db), rc);
|
|
|
|
rc = sql_step(dbi, scp);
|
|
if (rc) rpmlog(RPMLOG_WARNING, "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_WARNING, "cget(%s) sequential prepare %s (%d)\n", dbi->dbi_subfile, sqlite3_errmsg(sqldb->db), rc);
|
|
|
|
rc = sql_step(dbi, scp);
|
|
if (rc) rpmlog(RPMLOG_WARNING, "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_WARNING, "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_WARNING, "cget(%s) key bind %s (%d)\n", dbi->dbi_subfile, sqlite3_errmsg(sqldb->db), rc);
|
|
|
|
rc = sql_step(dbi, scp);
|
|
if (rc) rpmlog(RPMLOG_WARNING, "cget(%s) sql_step rc %d\n", dbi->dbi_subfile, rc);
|
|
|
|
rc = sqlite3_reset(scp->pStmt);
|
|
if (rc) rpmlog(RPMLOG_WARNING, "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_WARNING, "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_WARNING, "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_WARNING, "cput(%s) data bind %s (%d)\n", dbi->dbi_subfile, sqlite3_errmsg(sqldb->db), rc);
|
|
|
|
rc = sql_step(dbi, scp);
|
|
if (rc) rpmlog(RPMLOG_WARNING, "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 endian.\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 */
|
|
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
|
|
};
|
|
|