1 #include "system.h"
2 
3 #include <sqlite3.h>
4 #include <fcntl.h>
5 
6 #include <rpm/rpmlog.h>
7 #include <rpm/rpmfileutil.h>
8 #include <rpm/rpmmacro.h>
9 #include "lib/rpmdb_internal.h"
10 
11 #include "debug.h"
12 
13 static const int sleep_ms = 50;
14 
15 struct dbiCursor_s {
16     sqlite3 *sdb;
17     sqlite3_stmt *stmt;
18     const char *fmt;
19     int flags;
20     rpmTagVal tag;
21     int ctype;
22     struct dbiCursor_s *subc;
23 
24     const void *key;
25     unsigned int keylen;
26 };
27 
28 static int sqlexec(sqlite3 *sdb, const char *fmt, ...);
29 
rpm_match3(sqlite3_context * sctx,int argc,sqlite3_value ** argv)30 static void rpm_match3(sqlite3_context *sctx, int argc, sqlite3_value **argv)
31 {
32     int match = 0;
33     if (argc == 3) {
34 	int b1len = sqlite3_value_bytes(argv[0]);
35 	int b2len = sqlite3_value_bytes(argv[1]);
36 	int n = sqlite3_value_int(argv[2]);
37 	if (b1len >= n && b2len >= n) {
38 	    const char *b1 = sqlite3_value_blob(argv[0]);
39 	    const char *b2 = sqlite3_value_blob(argv[1]);
40 	    match = (memcmp(b1, b2, n) == 0);
41 	}
42     }
43     sqlite3_result_int(sctx, match);
44 }
45 
errCb(void * data,int err,const char * msg)46 static void errCb(void *data, int err, const char *msg)
47 {
48     rpmdb rdb = data;
49     rpmlog(RPMLOG_WARNING, "%s: %s: %s\n",
50 		rdb->db_descr, sqlite3_errstr(err), msg);
51 }
52 
dbiCursorReset(dbiCursor dbc)53 static int dbiCursorReset(dbiCursor dbc)
54 {
55     if (dbc->stmt) {
56 	sqlite3_reset(dbc->stmt);
57 	sqlite3_clear_bindings(dbc->stmt);
58     }
59     return 0;
60 }
61 
dbiCursorResult(dbiCursor dbc)62 static int dbiCursorResult(dbiCursor dbc)
63 {
64     int rc = sqlite3_errcode(dbc->sdb);
65     int err = (rc != SQLITE_OK && rc != SQLITE_DONE && rc != SQLITE_ROW);
66     if (err) {
67 	rpmlog(RPMLOG_ERR, "%s: %d: %s\n", sqlite3_sql(dbc->stmt),
68 		sqlite3_errcode(dbc->sdb), sqlite3_errmsg(dbc->sdb));
69     }
70     return err ? RPMRC_FAIL : RPMRC_OK;
71 }
72 
dbiCursorPrep(dbiCursor dbc,const char * fmt,...)73 static int dbiCursorPrep(dbiCursor dbc, const char *fmt, ...)
74 {
75     if (dbc->stmt == NULL) {
76 	char *cmd = NULL;
77 	va_list ap;
78 
79 	va_start(ap, fmt);
80 	cmd = sqlite3_vmprintf(fmt, ap);
81 	va_end(ap);
82 
83 	sqlite3_prepare_v2(dbc->sdb, cmd, -1, &dbc->stmt, NULL);
84 	sqlite3_free(cmd);
85     } else {
86 	dbiCursorReset(dbc);
87     }
88 
89     return dbiCursorResult(dbc);
90 }
91 
dbiCursorBindPkg(dbiCursor dbc,unsigned int hnum,void * blob,unsigned int bloblen)92 static int dbiCursorBindPkg(dbiCursor dbc, unsigned int hnum,
93 				void *blob, unsigned int bloblen)
94 {
95     int rc = 0;
96 
97     if (hnum)
98 	rc = sqlite3_bind_int(dbc->stmt, 1, hnum);
99     else
100 	rc = sqlite3_bind_null(dbc->stmt, 1);
101 
102     if (blob) {
103 	if (!rc)
104 	    rc = sqlite3_bind_blob(dbc->stmt, 2, blob, bloblen, NULL);
105     }
106     return dbiCursorResult(dbc);
107 }
108 
dbiCursorBindIdx(dbiCursor dbc,const void * key,int keylen,dbiIndexItem rec)109 static int dbiCursorBindIdx(dbiCursor dbc, const void *key, int keylen,
110 				dbiIndexItem rec)
111 {
112     int rc;
113     if (dbc->ctype == SQLITE_TEXT) {
114 	rc = sqlite3_bind_text(dbc->stmt, 1, key, keylen, NULL);
115     } else {
116 	rc = sqlite3_bind_blob(dbc->stmt, 1, key, keylen, NULL);
117     }
118 
119     if (rec) {
120 	if (!rc)
121 	    rc = sqlite3_bind_int(dbc->stmt, 2, rec->hdrNum);
122 	if (!rc)
123 	    rc = sqlite3_bind_int(dbc->stmt, 3, rec->tagNum);
124     }
125 
126     return dbiCursorResult(dbc);
127 }
128 
sqlite_init(rpmdb rdb,const char * dbhome)129 static int sqlite_init(rpmdb rdb, const char * dbhome)
130 {
131     int rc = 0;
132     char *dbfile = NULL;
133 
134     if (rdb->db_dbenv == NULL) {
135 	dbfile = rpmGenPath(dbhome, rdb->db_ops->path, NULL);
136 	sqlite3 *sdb = NULL;
137 	int xx, flags = 0;
138 	int retry_open = 1;
139 	if ((rdb->db_mode & O_ACCMODE) == O_RDONLY)
140 	    flags |= SQLITE_OPEN_READONLY;
141 	else
142 	    flags |= (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
143 
144 	while (retry_open--) {
145 	    xx = sqlite3_open_v2(dbfile, &sdb, flags, NULL);
146 	    /* Attempt to create if missing, discarding OPEN_READONLY (!) */
147 	    if (xx == SQLITE_CANTOPEN && (flags & SQLITE_OPEN_READONLY)) {
148 		/* Sqlite allocates resources even on failure to open (!) */
149 		sqlite3_close(sdb);
150 		flags &= ~SQLITE_OPEN_READONLY;
151 		flags |= (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
152 		retry_open++;
153 	    }
154 	}
155 
156 	if (xx != SQLITE_OK) {
157 	    rpmlog(RPMLOG_ERR, _("Unable to open sqlite database %s: %s\n"),
158 		    dbfile, sqlite3_errstr(xx));
159 	    rc = 1;
160 	    goto exit;
161 	}
162 
163 	sqlite3_create_function(sdb, "match", 3,
164 				(SQLITE_UTF8|SQLITE_DETERMINISTIC),
165 				NULL, rpm_match3, NULL, NULL);
166 
167 	sqlite3_busy_timeout(sdb, sleep_ms);
168 	sqlite3_config(SQLITE_CONFIG_LOG, errCb, rdb);
169 
170 	sqlexec(sdb, "PRAGMA secure_delete = OFF");
171 	sqlexec(sdb, "PRAGMA case_sensitive_like = ON");
172 
173 	if (sqlite3_db_readonly(sdb, NULL) == 0) {
174 	    if (sqlexec(sdb, "PRAGMA journal_mode = WAL") == 0) {
175 		int one = 1;
176 		/* Annoying but necessary to support non-privileged readers */
177 		sqlite3_file_control(sdb, NULL, SQLITE_FCNTL_PERSIST_WAL, &one);
178 
179 		if (!rpmExpandNumeric("%{?_flush_io}"))
180 		    sqlexec(sdb, "PRAGMA wal_autocheckpoint = 0");
181 	    }
182 	}
183 
184 	rdb->db_dbenv = sdb;
185     }
186     rdb->db_opens++;
187 
188 exit:
189     free(dbfile);
190     return rc;
191 }
192 
sqlite_fini(rpmdb rdb)193 static int sqlite_fini(rpmdb rdb)
194 {
195     int rc = 0;
196     if (rdb) {
197 	sqlite3 *sdb = rdb->db_dbenv;
198 	if (rdb->db_opens > 1) {
199 	    rdb->db_opens--;
200 	} else {
201 	    if (sqlite3_db_readonly(sdb, NULL) == 0) {
202 		sqlexec(sdb, "PRAGMA optimize");
203 		sqlexec(sdb, "PRAGMA wal_checkpoint = TRUNCATE");
204 	    }
205 	    rdb->db_dbenv = NULL;
206 	    int xx = sqlite3_close(sdb);
207 	    rc = (xx != SQLITE_OK);
208 	}
209     }
210 
211     return rc;
212 }
213 
sqlexec(sqlite3 * sdb,const char * fmt,...)214 static int sqlexec(sqlite3 *sdb, const char *fmt, ...)
215 {
216     int rc = 0;
217     char *cmd = NULL;
218     char *err = NULL;
219     va_list ap;
220 
221     va_start(ap, fmt);
222     cmd = sqlite3_vmprintf(fmt, ap);
223     va_end(ap);
224 
225     /* sqlite3_exec() doesn't seeem to honor sqlite3_busy_timeout() */
226     while ((rc = sqlite3_exec(sdb, cmd, NULL, NULL, &err)) == SQLITE_BUSY) {
227 	usleep(sleep_ms);
228     }
229 
230     if (rc)
231 	rpmlog(RPMLOG_ERR, "sqlite failure: %s: %s\n", cmd, err);
232     else
233 	rpmlog(RPMLOG_DEBUG, "%s: %d\n", cmd, rc);
234 
235     sqlite3_free(cmd);
236 
237     return rc ? RPMRC_FAIL : RPMRC_OK;
238 }
239 
dbiExists(dbiIndex dbi)240 static int dbiExists(dbiIndex dbi)
241 {
242     const char *col = (dbi->dbi_type == DBI_PRIMARY) ? "hnum" : "key";
243     const char *tbl = dbi->dbi_file;
244     int rc = sqlite3_table_column_metadata(dbi->dbi_db, NULL, tbl, col,
245 					   NULL, NULL, NULL, NULL, NULL);
246     return (rc == 0);
247 }
248 
init_table(dbiIndex dbi,rpmTagVal tag)249 static int init_table(dbiIndex dbi, rpmTagVal tag)
250 {
251     int rc = 0;
252 
253     if (dbiExists(dbi))
254 	return 0;
255 
256     if (dbi->dbi_type == DBI_PRIMARY) {
257 	rc = sqlexec(dbi->dbi_db,
258 			"CREATE TABLE IF NOT EXISTS '%q' ("
259 			    "hnum INTEGER PRIMARY KEY AUTOINCREMENT,"
260 			    "blob BLOB NOT NULL"
261 			")",
262 			dbi->dbi_file);
263     } else {
264 	const char *keytype = (rpmTagGetClass(tag) == RPM_STRING_CLASS) ?
265 				"TEXT" : "BLOB";
266 	rc = sqlexec(dbi->dbi_db,
267 			"CREATE TABLE IF NOT EXISTS '%q' ("
268 			    "key '%q' NOT NULL, "
269 			    "hnum INTEGER NOT NULL, "
270 			    "idx INTEGER NOT NULL, "
271 			    "FOREIGN KEY (hnum) REFERENCES 'Packages'(hnum)"
272 			")",
273 			dbi->dbi_file, keytype);
274     }
275     if (!rc)
276 	dbi->dbi_flags |= DBI_CREATED;
277 
278     return rc;
279 }
280 
create_index(sqlite3 * sdb,const char * table,const char * col)281 static int create_index(sqlite3 *sdb, const char *table, const char *col)
282 {
283     return sqlexec(sdb,
284 		"CREATE INDEX IF NOT EXISTS '%s_%s_idx' ON '%q'(%s ASC)",
285 		table, col, table, col);
286 }
287 
init_index(dbiIndex dbi,rpmTagVal tag)288 static int init_index(dbiIndex dbi, rpmTagVal tag)
289 {
290     int rc = 0;
291 
292     /* Can't create on readonly database, but things will still work */
293     if (sqlite3_db_readonly(dbi->dbi_db, NULL) == 1)
294 	return 0;
295 
296     if (dbi->dbi_type == DBI_SECONDARY) {
297 	int string = (rpmTagGetClass(tag) == RPM_STRING_CLASS);
298 	int array = (rpmTagGetReturnType(tag) == RPM_ARRAY_RETURN_TYPE);
299 	if (!rc && string)
300 	    rc = create_index(dbi->dbi_db, dbi->dbi_file, "key");
301 	if (!rc && array)
302 	    rc = create_index(dbi->dbi_db, dbi->dbi_file, "hnum");
303     }
304     return rc;
305 }
306 
sqlite_Open(rpmdb rdb,rpmDbiTagVal rpmtag,dbiIndex * dbip,int flags)307 static int sqlite_Open(rpmdb rdb, rpmDbiTagVal rpmtag, dbiIndex * dbip, int flags)
308 {
309     int rc = sqlite_init(rdb, rpmdbHome(rdb));
310 
311     if (!rc) {
312 	dbiIndex dbi = dbiNew(rdb, rpmtag);
313 	dbi->dbi_db = rdb->db_dbenv;
314 
315 	rc = init_table(dbi, rpmtag);
316 
317 	if (!rc && !(rdb->db_flags & RPMDB_FLAG_REBUILD))
318 	    rc = init_index(dbi, rpmtag);
319 
320 	if (!rc && dbip)
321 	    *dbip = dbi;
322 	else
323 	    dbiFree(dbi);
324     }
325 
326     return rc;
327 }
328 
sqlite_Close(dbiIndex dbi,unsigned int flags)329 static int sqlite_Close(dbiIndex dbi, unsigned int flags)
330 {
331     rpmdb rdb = dbi->dbi_rpmdb;
332     int rc = 0;
333     if (rdb->db_flags & RPMDB_FLAG_REBUILD)
334 	rc = init_index(dbi, rpmTagGetValue(dbi->dbi_file));
335     sqlite_fini(dbi->dbi_rpmdb);
336     dbiFree(dbi);
337     return rc;
338 }
339 
sqlite_Verify(dbiIndex dbi,unsigned int flags)340 static int sqlite_Verify(dbiIndex dbi, unsigned int flags)
341 {
342     int errors = -1;
343     int key_errors = -1;
344     sqlite3_stmt *s = NULL;
345     const char *cmd = "PRAGMA integrity_check";
346 
347     if (dbi->dbi_type == DBI_SECONDARY)
348 	return RPMRC_OK;
349 
350     if (sqlite3_prepare_v2(dbi->dbi_db, cmd, -1, &s, NULL) == SQLITE_OK) {
351 	errors = 0;
352 	while (sqlite3_step(s) == SQLITE_ROW) {
353 	    const char *txt = (const char *)sqlite3_column_text(s, 0);
354 	    if (!rstreq(txt, "ok")) {
355 		errors++;
356 		rpmlog(RPMLOG_ERR, "verify: %s\n", txt);
357 	    }
358 	}
359 	sqlite3_finalize(s);
360     } else {
361 	rpmlog(RPMLOG_ERR, "%s: %s\n", cmd, sqlite3_errmsg(dbi->dbi_db));
362     }
363 
364     /* No point checking higher-level errors if low-level errors exist */
365     if (errors)
366 	goto exit;
367 
368     cmd = "PRAGMA foreign_key_check";
369     if (sqlite3_prepare_v2(dbi->dbi_db, cmd, -1, &s, NULL) == SQLITE_OK) {
370 	key_errors = 0;
371 	while (sqlite3_step(s) == SQLITE_ROW) {
372 	    key_errors++;
373 	    rpmlog(RPMLOG_ERR, "verify key: %s[%lld]\n",
374 				sqlite3_column_text(s, 0),
375 				sqlite3_column_int64(s, 1));
376 	}
377 	sqlite3_finalize(s);
378     } else {
379 	rpmlog(RPMLOG_ERR, "%s: %s\n", cmd, sqlite3_errmsg(dbi->dbi_db));
380     }
381 
382 exit:
383 
384     return (errors == 0 && key_errors == 0) ? RPMRC_OK : RPMRC_FAIL;
385 }
386 
sqlite_SetFSync(rpmdb rdb,int enable)387 static void sqlite_SetFSync(rpmdb rdb, int enable)
388 {
389     sqlexec(rdb->db_dbenv,
390 	    "PRAGMA synchronous = %s", enable ? "FULL" : "OFF");
391 }
392 
sqlite_Ctrl(rpmdb rdb,dbCtrlOp ctrl)393 static int sqlite_Ctrl(rpmdb rdb, dbCtrlOp ctrl)
394 {
395     int rc = 0;
396 
397     switch (ctrl) {
398     case DB_CTRL_LOCK_RW:
399 	rc = sqlexec(rdb->db_dbenv, "SAVEPOINT 'rwlock'");
400 	break;
401     case DB_CTRL_UNLOCK_RW:
402 	rc = sqlexec(rdb->db_dbenv, "RELEASE 'rwlock'");
403 	break;
404     default:
405 	break;
406     }
407 
408     return rc;
409 }
410 
sqlite_CursorInit(dbiIndex dbi,unsigned int flags)411 static dbiCursor sqlite_CursorInit(dbiIndex dbi, unsigned int flags)
412 {
413     dbiCursor dbc = xcalloc(1, sizeof(*dbc));
414     dbc->sdb = dbi->dbi_db;
415     dbc->flags = flags;
416     dbc->tag = rpmTagGetValue(dbi->dbi_file);
417     if (rpmTagGetClass(dbc->tag) == RPM_STRING_CLASS) {
418 	dbc->ctype = SQLITE_TEXT;
419     } else {
420 	dbc->ctype = SQLITE_BLOB;
421     }
422     if (dbc->flags & DBC_WRITE)
423 	sqlexec(dbc->sdb, "SAVEPOINT '%s'", dbi->dbi_file);
424     return dbc;
425 }
426 
sqlite_CursorFree(dbiIndex dbi,dbiCursor dbc)427 static dbiCursor sqlite_CursorFree(dbiIndex dbi, dbiCursor dbc)
428 {
429     if (dbc) {
430 	sqlite3_finalize(dbc->stmt);
431 	if (dbc->subc)
432 	    dbiCursorFree(dbi, dbc->subc);
433 	if (dbc->flags & DBC_WRITE)
434 	    sqlexec(dbc->sdb, "RELEASE '%s'", dbi->dbi_file);
435 	free(dbc);
436     }
437     return NULL;
438 }
439 
sqlite_pkgdbPut(dbiIndex dbi,dbiCursor dbc,unsigned int * hdrNum,unsigned char * hdrBlob,unsigned int hdrLen)440 static rpmRC sqlite_pkgdbPut(dbiIndex dbi, dbiCursor dbc,  unsigned int *hdrNum, unsigned char *hdrBlob, unsigned int hdrLen)
441 {
442     int rc = 0;
443     dbiCursor dbwc = NULL;
444 
445     /* Avoid trashing existing query cursor on header rewrite */
446     if (hdrNum && *hdrNum) {
447 	dbwc = dbiCursorInit(dbi, DBC_WRITE);
448 	dbc = dbwc;
449     }
450 
451     if (!rc) {
452 	rc = dbiCursorPrep(dbc, "INSERT OR REPLACE INTO '%q' VALUES(?, ?)",
453 			    dbi->dbi_file);
454     }
455 
456     if (!rc)
457 	rc = dbiCursorBindPkg(dbc, *hdrNum, hdrBlob, hdrLen);
458 
459     if (!rc)
460 	while ((rc = sqlite3_step(dbc->stmt)) == SQLITE_ROW) {};
461 
462     /* XXX rowid is a 64bit integer and could overflow hdrnum */
463     if (rc == SQLITE_DONE && *hdrNum == 0)
464 	*hdrNum = sqlite3_last_insert_rowid(dbc->sdb);
465 
466     rc = dbiCursorResult(dbc);
467 
468     if (dbwc)
469 	dbiCursorFree(dbi, dbwc);
470 
471     return rc;
472 }
473 
sqlite_pkgdbDel(dbiIndex dbi,dbiCursor dbc,unsigned int hdrNum)474 static rpmRC sqlite_pkgdbDel(dbiIndex dbi, dbiCursor dbc,  unsigned int hdrNum)
475 {
476     int rc = dbiCursorPrep(dbc, "DELETE FROM '%q' WHERE hnum=?;",
477 			    dbi->dbi_file);
478 
479     if (!rc)
480 	rc = dbiCursorBindPkg(dbc, hdrNum, NULL, 0);
481 
482     if (!rc)
483 	while ((rc = sqlite3_step(dbc->stmt)) == SQLITE_ROW) {};
484 
485     return dbiCursorResult(dbc);
486 }
487 
sqlite_stepPkg(dbiCursor dbc,unsigned char ** hdrBlob,unsigned int * hdrLen)488 static rpmRC sqlite_stepPkg(dbiCursor dbc, unsigned char **hdrBlob, unsigned int *hdrLen)
489 {
490     int rc = sqlite3_step(dbc->stmt);
491 
492     if (rc == SQLITE_ROW) {
493 	if (hdrLen)
494 	    *hdrLen = sqlite3_column_bytes(dbc->stmt, 1);
495 	if (hdrBlob)
496 	    *hdrBlob = (void *) sqlite3_column_blob(dbc->stmt, 1);
497     }
498     return rc;
499 }
500 
sqlite_pkgdbByKey(dbiIndex dbi,dbiCursor dbc,unsigned int hdrNum,unsigned char ** hdrBlob,unsigned int * hdrLen)501 static rpmRC sqlite_pkgdbByKey(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum, unsigned char **hdrBlob, unsigned int *hdrLen)
502 {
503     int rc = dbiCursorPrep(dbc, "SELECT hnum, blob FROM '%q' WHERE hnum=?",
504 				dbi->dbi_file);
505 
506     if (!rc)
507 	rc = dbiCursorBindPkg(dbc, hdrNum, NULL, 0);
508 
509     if (!rc)
510 	rc = sqlite_stepPkg(dbc, hdrBlob, hdrLen);
511 
512     return dbiCursorResult(dbc);
513 }
514 
sqlite_pkgdbIter(dbiIndex dbi,dbiCursor dbc,unsigned char ** hdrBlob,unsigned int * hdrLen)515 static rpmRC sqlite_pkgdbIter(dbiIndex dbi, dbiCursor dbc,
516 				unsigned char **hdrBlob, unsigned int *hdrLen)
517 {
518     int rc = RPMRC_OK;
519     if (dbc->stmt == NULL) {
520 	rc = dbiCursorPrep(dbc, "SELECT hnum, blob FROM '%q'", dbi->dbi_file);
521     }
522 
523     if (!rc)
524 	rc = sqlite_stepPkg(dbc, hdrBlob, hdrLen);
525 
526     if (rc == SQLITE_DONE) {
527 	rc = RPMRC_NOTFOUND;
528     } else {
529 	rc = dbiCursorResult(dbc);
530     }
531 
532     return rc;
533 }
534 
sqlite_pkgdbGet(dbiIndex dbi,dbiCursor dbc,unsigned int hdrNum,unsigned char ** hdrBlob,unsigned int * hdrLen)535 static rpmRC sqlite_pkgdbGet(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum, unsigned char **hdrBlob, unsigned int *hdrLen)
536 {
537     int rc;
538 
539     if (hdrNum) {
540 	rc = sqlite_pkgdbByKey(dbi, dbc, hdrNum, hdrBlob, hdrLen);
541     } else {
542 	rc = sqlite_pkgdbIter(dbi, dbc, hdrBlob, hdrLen);
543     }
544 
545     return rc;
546 }
547 
sqlite_pkgdbKey(dbiIndex dbi,dbiCursor dbc)548 static unsigned int sqlite_pkgdbKey(dbiIndex dbi, dbiCursor dbc)
549 {
550     return sqlite3_column_int(dbc->stmt, 0);
551 }
552 
sqlite_idxdbByKey(dbiIndex dbi,dbiCursor dbc,const char * keyp,size_t keylen,int searchType,dbiIndexSet * set)553 static rpmRC sqlite_idxdbByKey(dbiIndex dbi, dbiCursor dbc,
554 			    const char *keyp, size_t keylen, int searchType,
555 			    dbiIndexSet *set)
556 {
557     int rc = RPMRC_NOTFOUND;
558 
559     if (searchType == DBC_PREFIX_SEARCH) {
560 	rc = dbiCursorPrep(dbc, "SELECT hnum, idx FROM '%q' "
561 				"WHERE MATCH(key,'%q',%d) "
562 				"ORDER BY key",
563 				dbi->dbi_file, keyp, keylen);
564     } else {
565 	rc = dbiCursorPrep(dbc, "SELECT hnum, idx FROM '%q' WHERE key=?",
566 			dbi->dbi_file);
567 	if (!rc)
568 	    rc = dbiCursorBindIdx(dbc, keyp, keylen, NULL);
569     }
570 
571 
572     if (!rc) {
573 	while ((rc = sqlite3_step(dbc->stmt)) == SQLITE_ROW) {
574 	    unsigned int hnum = sqlite3_column_int(dbc->stmt, 0);
575 	    unsigned int tnum = sqlite3_column_int(dbc->stmt, 1);
576 
577 	    if (*set == NULL)
578 		*set = dbiIndexSetNew(5);
579 	    dbiIndexSetAppendOne(*set, hnum, tnum, 0);
580 	}
581     }
582 
583     if (rc == SQLITE_DONE) {
584 	rc = (*set) ? RPMRC_OK : RPMRC_NOTFOUND;
585     } else {
586 	rc = dbiCursorResult(dbc);
587     }
588 
589     return rc;
590 }
591 
sqlite_idxdbIter(dbiIndex dbi,dbiCursor dbc,dbiIndexSet * set)592 static rpmRC sqlite_idxdbIter(dbiIndex dbi, dbiCursor dbc, dbiIndexSet *set)
593 {
594     int rc = RPMRC_OK;
595 
596     if (dbc->stmt == NULL) {
597 	rc = dbiCursorPrep(dbc, "SELECT DISTINCT key FROM '%q' ORDER BY key",
598 				dbi->dbi_file);
599 	if (set)
600 	    dbc->subc = dbiCursorInit(dbi, 0);
601     }
602 
603     if (!rc)
604 	rc = sqlite3_step(dbc->stmt);
605 
606     if (rc == SQLITE_ROW) {
607 	if (dbc->ctype == SQLITE_TEXT) {
608 	    dbc->key = sqlite3_column_text(dbc->stmt, 0);
609 	} else {
610 	    dbc->key = sqlite3_column_blob(dbc->stmt, 0);
611 	}
612 	dbc->keylen = sqlite3_column_bytes(dbc->stmt, 0);
613 	if (dbc->subc) {
614 	    rc = sqlite_idxdbByKey(dbi, dbc->subc, dbc->key, dbc->keylen,
615 				    DBC_NORMAL_SEARCH, set);
616 	} else {
617 	    rc = RPMRC_OK;
618 	}
619     } else if (rc == SQLITE_DONE) {
620 	rc = RPMRC_NOTFOUND;
621     } else {
622 	rc = dbiCursorResult(dbc);
623     }
624 
625     return rc;
626 }
627 
sqlite_idxdbGet(dbiIndex dbi,dbiCursor dbc,const char * keyp,size_t keylen,dbiIndexSet * set,int searchType)628 static rpmRC sqlite_idxdbGet(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, dbiIndexSet *set, int searchType)
629 {
630     int rc;
631     if (keyp) {
632 	rc = sqlite_idxdbByKey(dbi, dbc, keyp, keylen, searchType, set);
633     } else {
634 	rc = sqlite_idxdbIter(dbi, dbc, set);
635     }
636 
637     return rc;
638 }
639 
sqlite_idxdbPutOne(dbiIndex dbi,dbiCursor dbc,const char * keyp,size_t keylen,dbiIndexItem rec)640 static rpmRC sqlite_idxdbPutOne(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, dbiIndexItem rec)
641 {
642     int rc = dbiCursorPrep(dbc, "INSERT INTO '%q' VALUES(?, ?, ?)",
643 			dbi->dbi_file);
644 
645     if (!rc)
646 	rc = dbiCursorBindIdx(dbc, keyp, keylen, rec);
647 
648     if (!rc)
649 	while ((rc = sqlite3_step(dbc->stmt)) == SQLITE_ROW) {};
650 
651     return dbiCursorResult(dbc);
652 }
653 
sqlite_idxdbPut(dbiIndex dbi,rpmTagVal rpmtag,unsigned int hdrNum,Header h)654 static rpmRC sqlite_idxdbPut(dbiIndex dbi, rpmTagVal rpmtag, unsigned int hdrNum, Header h)
655 {
656     return tag2index(dbi, rpmtag, hdrNum, h, sqlite_idxdbPutOne);
657 }
658 
sqlite_idxdbDel(dbiIndex dbi,rpmTagVal rpmtag,unsigned int hdrNum,Header h)659 static rpmRC sqlite_idxdbDel(dbiIndex dbi, rpmTagVal rpmtag, unsigned int hdrNum, Header h)
660 {
661     dbiCursor dbc = dbiCursorInit(dbi, DBC_WRITE);
662     int rc = dbiCursorPrep(dbc, "DELETE FROM '%q' WHERE hnum=?", dbi->dbi_file);
663 
664     if (!rc)
665 	rc = dbiCursorBindPkg(dbc, hdrNum, NULL, 0);
666 
667     if (!rc)
668 	while ((rc = sqlite3_step(dbc->stmt)) == SQLITE_ROW) {};
669 
670     rc = dbiCursorResult(dbc);
671     dbiCursorFree(dbi, dbc);
672     return rc;
673 }
674 
sqlite_idxdbKey(dbiIndex dbi,dbiCursor dbc,unsigned int * keylen)675 static const void * sqlite_idxdbKey(dbiIndex dbi, dbiCursor dbc, unsigned int *keylen)
676 {
677     const void *key = NULL;
678     if (dbc) {
679 	key = dbc->key;
680 	if (key && keylen)
681 	    *keylen = dbc->keylen;
682     }
683     return key;
684 }
685 
686 
687 
688 struct rpmdbOps_s sqlite_dbops = {
689     .name	= "sqlite",
690     .path	= "rpmdb.sqlite",
691 
692     .open	= sqlite_Open,
693     .close	= sqlite_Close,
694     .verify	= sqlite_Verify,
695     .setFSync	= sqlite_SetFSync,
696     .ctrl	= sqlite_Ctrl,
697 
698     .cursorInit	= sqlite_CursorInit,
699     .cursorFree	= sqlite_CursorFree,
700 
701     .pkgdbPut	= sqlite_pkgdbPut,
702     .pkgdbDel	= sqlite_pkgdbDel,
703     .pkgdbGet	= sqlite_pkgdbGet,
704     .pkgdbKey	= sqlite_pkgdbKey,
705 
706     .idxdbGet	= sqlite_idxdbGet,
707     .idxdbPut	= sqlite_idxdbPut,
708     .idxdbDel	= sqlite_idxdbDel,
709     .idxdbKey	= sqlite_idxdbKey
710 };
711 
712