]>
Commit | Line | Data |
---|---|---|
33d55ab4 MT |
1 | /*############################################################################# |
2 | # # | |
3 | # Pakfire - The IPFire package management system # | |
4 | # Copyright (C) 2021 Pakfire development team # | |
5 | # # | |
6 | # This program is free software: you can redistribute it and/or modify # | |
7 | # it under the terms of the GNU General Public License as published by # | |
8 | # the Free Software Foundation, either version 3 of the License, or # | |
9 | # (at your option) any later version. # | |
10 | # # | |
11 | # This program is distributed in the hope that it will be useful, # | |
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of # | |
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # | |
14 | # GNU General Public License for more details. # | |
15 | # # | |
16 | # You should have received a copy of the GNU General Public License # | |
17 | # along with this program. If not, see <http://www.gnu.org/licenses/>. # | |
18 | # # | |
19 | #############################################################################*/ | |
20 | ||
21 | #include <errno.h> | |
22 | #include <stdlib.h> | |
23 | ||
26affd69 MT |
24 | #include <sqlite3.h> |
25 | ||
f5c77233 | 26 | #include <pakfire/archive.h> |
33d55ab4 | 27 | #include <pakfire/db.h> |
f5c77233 | 28 | #include <pakfire/file.h> |
33d55ab4 | 29 | #include <pakfire/logging.h> |
e49b93d1 | 30 | #include <pakfire/package.h> |
18bf891d MT |
31 | #include <pakfire/pakfire.h> |
32 | #include <pakfire/private.h> | |
e49b93d1 | 33 | #include <pakfire/repo.h> |
33d55ab4 MT |
34 | #include <pakfire/types.h> |
35 | #include <pakfire/util.h> | |
36 | ||
0cb487ff MT |
37 | #define DATABASE_PATH PAKFIRE_PRIVATE_DIR "/packages.db" |
38 | ||
5ba550fe | 39 | #define CURRENT_SCHEMA 8 |
c745fb2d MT |
40 | #define SCHEMA_MIN_SUP 7 |
41 | ||
33d55ab4 MT |
42 | struct pakfire_db { |
43 | Pakfire pakfire; | |
44 | int nrefs; | |
77a4b3a3 MT |
45 | |
46 | int mode; | |
26affd69 MT |
47 | |
48 | sqlite3* handle; | |
c745fb2d | 49 | int schema; |
33d55ab4 MT |
50 | }; |
51 | ||
9c5938ea MT |
52 | static void logging_callback(void* data, int r, const char* msg) { |
53 | Pakfire pakfire = (Pakfire)data; | |
54 | ||
55 | ERROR(pakfire, "Database Error: %s: %s\n", | |
56 | sqlite3_errstr(r), msg); | |
57 | } | |
58 | ||
0076c50d MT |
59 | static int pakfire_db_execute(struct pakfire_db* db, const char* stmt) { |
60 | int r; | |
c745fb2d MT |
61 | |
62 | DEBUG(db->pakfire, "Executing database query: %s\n", stmt); | |
0076c50d MT |
63 | |
64 | do { | |
c745fb2d | 65 | r = sqlite3_exec(db->handle, stmt, NULL, NULL, NULL); |
0076c50d MT |
66 | } while (r == SQLITE_BUSY); |
67 | ||
c745fb2d MT |
68 | // Log any errors |
69 | if (r) { | |
70 | ERROR(db->pakfire, "Database query failed: %s\n", sqlite3_errmsg(db->handle)); | |
71 | } | |
72 | ||
25753290 MT |
73 | return r; |
74 | } | |
75 | ||
c745fb2d MT |
76 | static int pakfire_db_begin_transaction(struct pakfire_db* db) { |
77 | return pakfire_db_execute(db, "BEGIN TRANSACTION"); | |
78 | } | |
79 | ||
80 | static int pakfire_db_commit(struct pakfire_db* db) { | |
81 | return pakfire_db_execute(db, "COMMIT"); | |
82 | } | |
83 | ||
84 | static int pakfire_db_rollback(struct pakfire_db* db) { | |
85 | return pakfire_db_execute(db, "ROLLBACK"); | |
86 | } | |
87 | ||
25753290 MT |
88 | /* |
89 | This function performs any fast optimization and tries to truncate the WAL log file | |
90 | to keep the database as compact as possible on disk. | |
91 | */ | |
92 | static void pakfire_db_optimize(struct pakfire_db* db) { | |
44d392ef | 93 | pakfire_db_execute(db, "PRAGMA optimize"); |
25753290 | 94 | pakfire_db_execute(db, "PRAGMA wal_checkpoint = TRUNCATE"); |
0076c50d MT |
95 | } |
96 | ||
26affd69 MT |
97 | static void pakfire_db_free(struct pakfire_db* db) { |
98 | DEBUG(db->pakfire, "Releasing database at %p\n", db); | |
99 | ||
26affd69 | 100 | if (db->handle) { |
25753290 MT |
101 | // Optimize the database before it is being closed |
102 | pakfire_db_optimize(db); | |
103 | ||
104 | // Close database handle | |
26affd69 MT |
105 | int r = sqlite3_close(db->handle); |
106 | if (r != SQLITE_OK) { | |
107 | ERROR(db->pakfire, "Could not close database handle: %s\n", | |
108 | sqlite3_errmsg(db->handle)); | |
109 | } | |
110 | } | |
111 | ||
112 | pakfire_unref(db->pakfire); | |
113 | ||
114 | pakfire_free(db); | |
115 | } | |
116 | ||
c745fb2d MT |
117 | static sqlite3_value* pakfire_db_get(struct pakfire_db* db, const char* key) { |
118 | sqlite3_stmt* stmt = NULL; | |
119 | sqlite3_value* val = NULL; | |
120 | int r; | |
121 | ||
122 | const char* sql = "SELECT val FROM settings WHERE key = ?"; | |
123 | ||
124 | // Prepare the statement | |
125 | r = sqlite3_prepare_v2(db->handle, sql, strlen(sql), &stmt, NULL); | |
126 | if (r != SQLITE_OK) { | |
127 | //ERROR(db->pakfire, "Could not prepare SQL statement: %s: %s\n", | |
128 | // sql, sqlite3_errmsg(db->handle)); | |
129 | return NULL; | |
130 | } | |
131 | ||
132 | // Bind key | |
133 | r = sqlite3_bind_text(stmt, 1, key, strlen(key), NULL); | |
134 | if (r != SQLITE_OK) { | |
135 | ERROR(db->pakfire, "Could not bind key: %s\n", sqlite3_errmsg(db->handle)); | |
136 | goto ERROR; | |
137 | } | |
138 | ||
139 | // Execute the statement | |
140 | do { | |
141 | r = sqlite3_step(stmt); | |
142 | } while (r == SQLITE_BUSY); | |
143 | ||
f168dd0c MT |
144 | // We should have read a row |
145 | if (r != SQLITE_ROW) | |
146 | goto ERROR; | |
147 | ||
c745fb2d | 148 | // Read value |
f168dd0c | 149 | val = sqlite3_column_value(stmt, 0); |
c745fb2d MT |
150 | if (!val) { |
151 | ERROR(db->pakfire, "Could not read value\n"); | |
152 | goto ERROR; | |
153 | } | |
154 | ||
155 | // Copy value onto the heap | |
156 | val = sqlite3_value_dup(val); | |
157 | ||
158 | ERROR: | |
159 | if (stmt) | |
160 | sqlite3_finalize(stmt); | |
161 | ||
162 | return val; | |
163 | } | |
164 | ||
165 | static int pakfire_db_set_int(struct pakfire_db* db, const char* key, int val) { | |
166 | sqlite3_stmt* stmt = NULL; | |
167 | int r; | |
168 | ||
169 | const char* sql = "INSERT INTO settings(key, val) VALUES(?, ?) \ | |
170 | ON CONFLICT (key) DO UPDATE SET val = excluded.val"; | |
171 | ||
172 | // Prepare statement | |
173 | r = sqlite3_prepare_v2(db->handle, sql, strlen(sql), &stmt, NULL); | |
174 | if (r != SQLITE_OK) { | |
175 | ERROR(db->pakfire, "Could not prepare SQL statement: %s: %s\n", | |
176 | sql, sqlite3_errmsg(db->handle)); | |
177 | return 1; | |
178 | } | |
179 | ||
180 | // Bind key | |
181 | r = sqlite3_bind_text(stmt, 1, key, strlen(key), NULL); | |
182 | if (r != SQLITE_OK) { | |
183 | ERROR(db->pakfire, "Could not bind key: %s\n", sqlite3_errmsg(db->handle)); | |
184 | goto ERROR; | |
185 | } | |
186 | ||
187 | // Bind val | |
188 | r = sqlite3_bind_int64(stmt, 2, val); | |
189 | if (r != SQLITE_OK) { | |
190 | ERROR(db->pakfire, "Could not bind val: %s\n", sqlite3_errmsg(db->handle)); | |
191 | goto ERROR; | |
192 | } | |
193 | ||
194 | // Execute the statement | |
195 | do { | |
196 | r = sqlite3_step(stmt); | |
197 | } while (r == SQLITE_BUSY); | |
198 | ||
199 | // Set return code | |
200 | r = (r == SQLITE_OK); | |
201 | ||
202 | ERROR: | |
203 | if (stmt) | |
204 | sqlite3_finalize(stmt); | |
205 | ||
206 | return r; | |
207 | } | |
208 | ||
209 | static int pakfire_db_get_schema(struct pakfire_db* db) { | |
210 | sqlite3_value* value = pakfire_db_get(db, "schema"); | |
211 | if (!value) | |
f168dd0c | 212 | return -1; |
c745fb2d MT |
213 | |
214 | int schema = sqlite3_value_int64(value); | |
215 | sqlite3_value_free(value); | |
216 | ||
217 | DEBUG(db->pakfire, "Database has schema version %d\n", schema); | |
218 | ||
219 | return schema; | |
220 | } | |
221 | ||
222 | static int pakfire_db_create_schema(struct pakfire_db* db) { | |
223 | int r; | |
224 | ||
225 | // Create settings table | |
226 | r = pakfire_db_execute(db, "CREATE TABLE IF NOT EXISTS settings(key TEXT, val TEXT)"); | |
227 | if (r) | |
228 | return 1; | |
229 | ||
230 | // settings: Add a unique index on key | |
231 | r = pakfire_db_execute(db, "CREATE UNIQUE INDEX IF NOT EXISTS settings_key ON settings(key)"); | |
232 | if (r) | |
233 | return 1; | |
234 | ||
704b3993 MT |
235 | // Create packages table |
236 | r = pakfire_db_execute(db, | |
237 | "CREATE TABLE IF NOT EXISTS packages(" | |
238 | "id INTEGER PRIMARY KEY, " | |
239 | "name TEXT, " | |
240 | "epoch INTEGER, " | |
241 | "version TEXT, " | |
242 | "release TEXT, " | |
243 | "arch TEXT, " | |
244 | "groups TEXT, " | |
245 | "filename TEXT, " | |
246 | "size INTEGER, " | |
247 | "inst_size INTEGER, " | |
248 | "hash1 TEXT, " | |
249 | "license TEXT, " | |
250 | "summary TEXT, " | |
251 | "description TEXT, " | |
252 | "uuid TEXT, " | |
253 | "vendor TEXT, " | |
704b3993 | 254 | "build_host TEXT, " |
704b3993 MT |
255 | "build_time INTEGER, " |
256 | "installed INTEGER, " | |
257 | "reason TEXT, " | |
258 | "repository TEXT" | |
259 | ")"); | |
260 | if (r) | |
261 | return 1; | |
262 | ||
263 | // packages: Create index to find package by name | |
264 | r = pakfire_db_execute(db, "CREATE INDEX IF NOT EXISTS packages_name ON packages(name)"); | |
265 | if (r) | |
266 | return 1; | |
267 | ||
2359ca14 MT |
268 | // Create dependencies table |
269 | r = pakfire_db_execute(db, | |
270 | "CREATE TABLE IF NOT EXISTS dependencies(" | |
271 | "pkg INTEGER, " | |
272 | "type TEXT, " | |
46257c5f MT |
273 | "dependency TEXT, " |
274 | "FOREIGN KEY (pkg) REFERENCES packages(id)" | |
2359ca14 MT |
275 | ")"); |
276 | if (r) | |
277 | return r; | |
278 | ||
279 | // dependencies: Add index over packages | |
280 | r = pakfire_db_execute(db, "CREATE INDEX IF NOT EXISTS dependencies_pkg_index ON dependencies(pkg)"); | |
281 | if (r) | |
282 | return r; | |
283 | ||
68036506 MT |
284 | // Create files table |
285 | r = pakfire_db_execute(db, | |
286 | "CREATE TABLE IF NOT EXISTS files(" | |
287 | "id INTEGER PRIMARY KEY, " | |
288 | "name TEXT, " | |
289 | "pkg INTEGER, " | |
290 | "size INTEGER, " | |
291 | "type INTEGER, " | |
292 | "config INTEGER, " | |
293 | "datafile INTEGER, " | |
294 | "mode INTEGER, " | |
295 | "user TEXT, " | |
296 | "'group' TEXT, " | |
297 | "hash1 TEXT, " | |
298 | "mtime INTEGER, " | |
46257c5f MT |
299 | "capabilities TEXT, " |
300 | "FOREIGN KEY (pkg) REFERENCES packages(id)" | |
68036506 MT |
301 | ")"); |
302 | if (r) | |
303 | return 1; | |
304 | ||
305 | // files: Add index over packages | |
306 | r = pakfire_db_execute(db, "CREATE INDEX IF NOT EXISTS files_pkg_index ON files(pkg)"); | |
307 | if (r) | |
308 | return 1; | |
309 | ||
d83414aa MT |
310 | // Create scriptlets table |
311 | r = pakfire_db_execute(db, | |
312 | "CREATE TABLE IF NOT EXISTS scriptlets(" | |
313 | "id INTEGER PRIMARY KEY, " | |
314 | "pkg INTEGER, " | |
315 | "action TEXT, " | |
46257c5f MT |
316 | "scriptlet TEXT, " |
317 | "FOREIGN KEY (pkg) REFERENCES packages(id)" | |
d83414aa MT |
318 | ")"); |
319 | if (r) | |
320 | return 1; | |
321 | ||
322 | // scriptlets: Add index over packages | |
323 | r = pakfire_db_execute(db, "CREATE INDEX IF NOT EXISTS scriptlets_pkg_index ON scriptlets(pkg)"); | |
324 | if (r) | |
325 | return 1; | |
326 | ||
c745fb2d MT |
327 | return 0; |
328 | } | |
329 | ||
46257c5f | 330 | static int pakfire_db_migrate_to_schema_8(struct pakfire_db* db) { |
5ba550fe MT |
331 | // packages: Drop build_id column |
332 | ||
46257c5f MT |
333 | // Add foreign keys |
334 | // TODO sqlite doesn't support adding foreign keys to existing tables and so we would | |
335 | // need to recreate the whole table and rename it afterwards. Annoying. | |
336 | ||
337 | return 0; | |
338 | } | |
339 | ||
c745fb2d MT |
340 | static int pakfire_db_migrate_schema(struct pakfire_db* db) { |
341 | int r; | |
342 | ||
343 | while (db->schema < CURRENT_SCHEMA) { | |
344 | // Begin a new transaction | |
345 | r = pakfire_db_begin_transaction(db); | |
346 | if (r) | |
347 | goto ROLLBACK; | |
348 | ||
349 | switch (db->schema) { | |
350 | // No schema exists | |
f168dd0c | 351 | case -1: |
c745fb2d MT |
352 | r = pakfire_db_create_schema(db); |
353 | if (r) | |
354 | goto ROLLBACK; | |
355 | ||
356 | db->schema = CURRENT_SCHEMA; | |
357 | break; | |
358 | ||
46257c5f MT |
359 | case 7: |
360 | r = pakfire_db_migrate_to_schema_8(db); | |
361 | if (r) | |
362 | goto ROLLBACK; | |
363 | ||
364 | db->schema++; | |
365 | break; | |
366 | ||
c745fb2d MT |
367 | default: |
368 | ERROR(db->pakfire, "Cannot migrate database from schema %d\n", db->schema); | |
369 | goto ROLLBACK; | |
370 | } | |
371 | ||
372 | // Update the schema version | |
373 | r = pakfire_db_set_int(db, "schema", CURRENT_SCHEMA); | |
374 | if (r) | |
375 | goto ROLLBACK; | |
376 | ||
377 | // All done, commit! | |
378 | r = pakfire_db_commit(db); | |
379 | if (r) | |
380 | goto ROLLBACK; | |
381 | } | |
382 | ||
383 | return 0; | |
384 | ||
385 | ROLLBACK: | |
386 | pakfire_db_rollback(db); | |
387 | ||
388 | return 1; | |
389 | } | |
390 | ||
9c5938ea | 391 | static int pakfire_db_setup(struct pakfire_db* db) { |
25753290 MT |
392 | int r; |
393 | ||
9c5938ea MT |
394 | // Setup logging |
395 | sqlite3_config(SQLITE_CONFIG_LOG, logging_callback, db->pakfire); | |
396 | ||
46257c5f MT |
397 | // Enable foreign keys |
398 | pakfire_db_execute(db, "PRAGMA foreign_keys = ON"); | |
399 | ||
0076c50d MT |
400 | // Make LIKE case-sensitive |
401 | pakfire_db_execute(db, "PRAGMA case_sensitive_like = ON"); | |
402 | ||
c745fb2d MT |
403 | // Fetch the current schema |
404 | db->schema = pakfire_db_get_schema(db); | |
405 | ||
406 | // Check if the schema is recent enough | |
407 | if (db->schema > 0 && db->schema < SCHEMA_MIN_SUP) { | |
408 | ERROR(db->pakfire, "Database schema %d is not supported by this version of Pakfire\n", | |
409 | db->schema); | |
410 | return 1; | |
411 | } | |
412 | ||
9c5938ea MT |
413 | // Done when not in read-write mode |
414 | if (db->mode != PAKFIRE_DB_READWRITE) | |
415 | return 0; | |
416 | ||
0076c50d MT |
417 | // Disable secure delete |
418 | pakfire_db_execute(db, "PRAGMA secure_delete = OFF"); | |
419 | ||
25753290 MT |
420 | // Set database journal to WAL |
421 | r = pakfire_db_execute(db, "PRAGMA journal_mode = WAL"); | |
422 | if (r != SQLITE_OK) { | |
423 | ERROR(db->pakfire, "Could not set journal mode to WAL: %s\n", | |
424 | sqlite3_errmsg(db->handle)); | |
425 | return 1; | |
426 | } | |
427 | ||
428 | // Disable autocheckpoint | |
429 | r = sqlite3_wal_autocheckpoint(db->handle, 0); | |
430 | if (r != SQLITE_OK) { | |
431 | ERROR(db->pakfire, "Could not disable autocheckpoint: %s\n", | |
432 | sqlite3_errmsg(db->handle)); | |
433 | return 1; | |
434 | } | |
435 | ||
c745fb2d MT |
436 | // Create or migrate schema |
437 | r = pakfire_db_migrate_schema(db); | |
438 | if (r) | |
439 | return r; | |
9c5938ea MT |
440 | |
441 | return 0; | |
442 | } | |
443 | ||
77a4b3a3 | 444 | PAKFIRE_EXPORT int pakfire_db_open(struct pakfire_db** db, Pakfire pakfire, int flags) { |
26affd69 MT |
445 | int r = 1; |
446 | ||
33d55ab4 MT |
447 | struct pakfire_db* o = pakfire_calloc(1, sizeof(*o)); |
448 | if (!o) | |
449 | return -ENOMEM; | |
450 | ||
451 | DEBUG(pakfire, "Allocated database at %p\n", o); | |
452 | ||
453 | o->pakfire = pakfire_ref(pakfire); | |
454 | o->nrefs = 1; | |
455 | ||
26affd69 MT |
456 | int sqlite3_flags = 0; |
457 | ||
458 | // Store mode & forward it to sqlite3 | |
459 | if (flags & PAKFIRE_DB_READWRITE) { | |
77a4b3a3 | 460 | o->mode = PAKFIRE_DB_READWRITE; |
26affd69 MT |
461 | sqlite3_flags |= SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; |
462 | } else { | |
77a4b3a3 | 463 | o->mode = PAKFIRE_DB_READONLY; |
26affd69 MT |
464 | sqlite3_flags |= SQLITE_OPEN_READONLY; |
465 | } | |
466 | ||
467 | // Make the filename | |
468 | char* path = pakfire_make_path(o->pakfire, DATABASE_PATH); | |
469 | if (!path) | |
470 | goto END; | |
471 | ||
472 | // Try to open the sqlite3 database file | |
473 | r = sqlite3_open_v2(path, &o->handle, sqlite3_flags, NULL); | |
474 | if (r != SQLITE_OK) { | |
475 | ERROR(pakfire, "Could not open database %s: %s\n", | |
476 | path, sqlite3_errmsg(o->handle)); | |
477 | ||
478 | r = 1; | |
479 | goto END; | |
480 | } | |
77a4b3a3 | 481 | |
9c5938ea MT |
482 | // Setup the database |
483 | r = pakfire_db_setup(o); | |
484 | if (r) | |
485 | goto END; | |
486 | ||
33d55ab4 | 487 | *db = o; |
26affd69 MT |
488 | r = 0; |
489 | ||
490 | END: | |
491 | if (r) | |
492 | pakfire_db_free(o); | |
33d55ab4 | 493 | |
26affd69 MT |
494 | if (path) |
495 | free(path); | |
496 | ||
497 | return r; | |
33d55ab4 MT |
498 | } |
499 | ||
18bf891d | 500 | PAKFIRE_EXPORT struct pakfire_db* pakfire_db_ref(struct pakfire_db* db) { |
33d55ab4 MT |
501 | db->nrefs++; |
502 | ||
503 | return db; | |
504 | } | |
505 | ||
18bf891d | 506 | PAKFIRE_EXPORT struct pakfire_db* pakfire_db_unref(struct pakfire_db* db) { |
33d55ab4 MT |
507 | if (--db->nrefs > 0) |
508 | return db; | |
509 | ||
510 | pakfire_db_free(db); | |
511 | ||
512 | return NULL; | |
513 | } | |
eafbe2ce | 514 | |
a1571786 MT |
515 | static unsigned long pakfire_db_integrity_check(struct pakfire_db* db) { |
516 | sqlite3_stmt* stmt = NULL; | |
517 | int r; | |
518 | ||
519 | r = sqlite3_prepare_v2(db->handle, "PRAGMA integrity_check", -1, &stmt, NULL); | |
520 | if (r) { | |
521 | ERROR(db->pakfire, "Could not prepare integrity check: %s\n", | |
522 | sqlite3_errmsg(db->handle)); | |
523 | return 1; | |
524 | } | |
525 | ||
526 | // Count any errors | |
527 | unsigned long errors = 0; | |
528 | ||
529 | while (1) { | |
530 | do { | |
531 | r = sqlite3_step(stmt); | |
532 | } while (r == SQLITE_BUSY); | |
533 | ||
534 | if (r == SQLITE_ROW) { | |
535 | const char* error = (const char*)sqlite3_column_text(stmt, 0); | |
536 | ||
537 | // If the message is "ok", the database has passed the check | |
538 | if (strcmp(error, "ok") == 0) | |
539 | continue; | |
540 | ||
541 | // Increment error counter | |
542 | errors++; | |
543 | ||
544 | // Log the message | |
545 | ERROR(db->pakfire, "%s\n", error); | |
546 | ||
547 | // Break on anything else | |
548 | } else | |
549 | break; | |
550 | } | |
551 | ||
552 | sqlite3_finalize(stmt); | |
553 | ||
554 | if (errors) | |
555 | ERROR(db->pakfire, "Database integrity check failed\n"); | |
556 | else | |
557 | INFO(db->pakfire, "Database integrity check passed\n"); | |
558 | ||
559 | return errors; | |
560 | } | |
561 | ||
562 | static unsigned long pakfire_db_foreign_key_check(struct pakfire_db* db) { | |
563 | sqlite3_stmt* stmt = NULL; | |
564 | int r; | |
565 | ||
566 | r = sqlite3_prepare_v2(db->handle, "PRAGMA foreign_key_check", -1, &stmt, NULL); | |
567 | if (r) { | |
568 | ERROR(db->pakfire, "Could not prepare foreign key check: %s\n", | |
569 | sqlite3_errmsg(db->handle)); | |
570 | return 1; | |
571 | } | |
572 | ||
573 | // Count any errors | |
574 | unsigned long errors = 0; | |
575 | ||
576 | while (1) { | |
577 | do { | |
578 | r = sqlite3_step(stmt); | |
579 | } while (r == SQLITE_BUSY); | |
580 | ||
581 | if (r == SQLITE_ROW) { | |
582 | const unsigned char* table = sqlite3_column_text(stmt, 0); | |
583 | unsigned long rowid = sqlite3_column_int64(stmt, 1); | |
584 | const unsigned char* foreign_table = sqlite3_column_text(stmt, 2); | |
585 | unsigned long foreign_rowid = sqlite3_column_int64(stmt, 3); | |
586 | ||
587 | // Increment error counter | |
588 | errors++; | |
589 | ||
590 | // Log the message | |
591 | ERROR(db->pakfire, "Foreign key violation found in %s, row %lu: " | |
592 | "%lu does not exist in table %s\n", table, rowid, foreign_rowid, foreign_table); | |
593 | ||
594 | // Break on anything else | |
595 | } else | |
596 | break; | |
597 | } | |
598 | ||
599 | sqlite3_finalize(stmt); | |
600 | ||
601 | if (errors) | |
602 | ERROR(db->pakfire, "Foreign key check failed\n"); | |
603 | else | |
604 | INFO(db->pakfire, "Foreign key check passed\n"); | |
605 | ||
606 | return errors; | |
607 | } | |
608 | ||
609 | /* | |
610 | This function performs an integrity check of the database | |
611 | */ | |
612 | PAKFIRE_EXPORT int pakfire_db_check(struct pakfire_db* db) { | |
613 | int r; | |
614 | ||
615 | // Perform integrity check | |
616 | r = pakfire_db_integrity_check(db); | |
617 | if (r) | |
618 | return 1; | |
619 | ||
620 | // Perform foreign key check | |
621 | r = pakfire_db_foreign_key_check(db); | |
622 | if (r) | |
623 | return 1; | |
624 | ||
625 | return 0; | |
626 | } | |
627 | ||
f5c77233 MT |
628 | static int pakfire_db_add_files(struct pakfire_db* db, unsigned long id, PakfireArchive archive) { |
629 | sqlite3_stmt* stmt = NULL; | |
dccd04a4 | 630 | int r = 1; |
f5c77233 MT |
631 | |
632 | // Get the filelist from the archive | |
5e9463ec MT |
633 | PakfireFilelist filelist = pakfire_archive_get_filelist(archive); |
634 | if (!filelist) { | |
f5c77233 MT |
635 | ERROR(db->pakfire, "Could not fetch filelist from archive\n"); |
636 | return 1; | |
637 | } | |
638 | ||
5e9463ec MT |
639 | // Nothing to do if the list is empty |
640 | if (pakfire_filelist_is_empty(filelist)) | |
641 | goto END; | |
642 | ||
f5c77233 MT |
643 | const char* sql = "INSERT INTO files(pkg, name, size, type, config, datafile, mode, " |
644 | "user, 'group', hash1, mtime, capabilities) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; | |
645 | ||
646 | // Prepare the statement | |
647 | r = sqlite3_prepare_v2(db->handle, sql, -1, &stmt, NULL); | |
648 | if (r) { | |
649 | ERROR(db->pakfire, "Could not prepare SQL statement: %s: %s\n", | |
650 | sql, sqlite3_errmsg(db->handle)); | |
651 | goto END; | |
652 | } | |
653 | ||
5e9463ec MT |
654 | for (unsigned int i = 0; i < pakfire_filelist_size(filelist); i++) { |
655 | PakfireFile file = pakfire_filelist_get(filelist, i); | |
656 | ||
f5c77233 MT |
657 | // Bind package ID |
658 | r = sqlite3_bind_int64(stmt, 1, id); | |
659 | if (r) { | |
660 | ERROR(db->pakfire, "Could not bind id: %s\n", sqlite3_errmsg(db->handle)); | |
5e9463ec | 661 | pakfire_file_unref(file); |
f5c77233 MT |
662 | goto END; |
663 | } | |
664 | ||
665 | // Bind name | |
666 | const char* name = pakfire_file_get_name(file); | |
667 | ||
668 | r = sqlite3_bind_text(stmt, 2, name, -1, NULL); | |
669 | if (r) { | |
670 | ERROR(db->pakfire, "Could not bind name: %s\n", sqlite3_errmsg(db->handle)); | |
5e9463ec | 671 | pakfire_file_unref(file); |
f5c77233 MT |
672 | goto END; |
673 | } | |
674 | ||
675 | // Bind size | |
676 | size_t size = pakfire_file_get_size(file); | |
677 | ||
678 | r = sqlite3_bind_int64(stmt, 3, size); | |
679 | if (r) { | |
680 | ERROR(db->pakfire, "Could not bind size: %s\n", sqlite3_errmsg(db->handle)); | |
5e9463ec | 681 | pakfire_file_unref(file); |
f5c77233 MT |
682 | goto END; |
683 | } | |
684 | ||
685 | // Bind type - XXX this is char which isn't very helpful | |
686 | //char type = pakfire_file_get_type(file); | |
687 | ||
688 | r = sqlite3_bind_null(stmt, 4); | |
689 | if (r) { | |
690 | ERROR(db->pakfire, "Could not bind type: %s\n", sqlite3_errmsg(db->handle)); | |
5e9463ec | 691 | pakfire_file_unref(file); |
f5c77233 MT |
692 | goto END; |
693 | } | |
694 | ||
695 | // Bind config - XXX TODO | |
696 | r = sqlite3_bind_null(stmt, 5); | |
697 | if (r) { | |
698 | ERROR(db->pakfire, "Could not bind config: %s\n", sqlite3_errmsg(db->handle)); | |
5e9463ec | 699 | pakfire_file_unref(file); |
f5c77233 MT |
700 | goto END; |
701 | } | |
702 | ||
703 | // Bind datafile - XXX TODO | |
704 | r = sqlite3_bind_null(stmt, 6); | |
705 | if (r) { | |
706 | ERROR(db->pakfire, "Could not bind datafile: %s\n", sqlite3_errmsg(db->handle)); | |
5e9463ec | 707 | pakfire_file_unref(file); |
f5c77233 MT |
708 | goto END; |
709 | } | |
710 | ||
711 | // Bind mode | |
712 | mode_t mode = pakfire_file_get_mode(file); | |
713 | ||
714 | r = sqlite3_bind_int64(stmt, 7, mode); | |
715 | if (r) { | |
716 | ERROR(db->pakfire, "Could not bind mode: %s\n", sqlite3_errmsg(db->handle)); | |
5e9463ec | 717 | pakfire_file_unref(file); |
f5c77233 MT |
718 | goto END; |
719 | } | |
720 | ||
721 | // Bind user | |
722 | const char* user = pakfire_file_get_user(file); | |
723 | ||
724 | r = sqlite3_bind_text(stmt, 8, user, -1, NULL); | |
725 | if (r) { | |
726 | ERROR(db->pakfire, "Could not bind user: %s\n", sqlite3_errmsg(db->handle)); | |
5e9463ec | 727 | pakfire_file_unref(file); |
f5c77233 MT |
728 | goto END; |
729 | } | |
730 | ||
731 | // Bind group | |
732 | const char* group = pakfire_file_get_group(file); | |
733 | ||
734 | r = sqlite3_bind_text(stmt, 9, group, -1, NULL); | |
735 | if (r) { | |
736 | ERROR(db->pakfire, "Could not bind group: %s\n", sqlite3_errmsg(db->handle)); | |
5e9463ec | 737 | pakfire_file_unref(file); |
f5c77233 MT |
738 | goto END; |
739 | } | |
740 | ||
741 | // Bind hash1 | |
742 | const char* chksum = pakfire_file_get_chksum(file); | |
743 | ||
744 | r = sqlite3_bind_text(stmt, 10, chksum, -1, NULL); | |
745 | if (r) { | |
746 | ERROR(db->pakfire, "Could not bind hash1: %s\n", sqlite3_errmsg(db->handle)); | |
5e9463ec | 747 | pakfire_file_unref(file); |
f5c77233 MT |
748 | goto END; |
749 | } | |
750 | ||
751 | // Bind mtime | |
752 | time_t mtime = pakfire_file_get_time(file); | |
753 | ||
754 | r = sqlite3_bind_int64(stmt, 11, mtime); | |
755 | if (r) { | |
756 | ERROR(db->pakfire, "Could not bind mtime: %s\n", sqlite3_errmsg(db->handle)); | |
5e9463ec | 757 | pakfire_file_unref(file); |
f5c77233 MT |
758 | goto END; |
759 | } | |
760 | ||
761 | // Bind capabilities - XXX TODO | |
762 | r = sqlite3_bind_null(stmt, 12); | |
763 | if (r) { | |
764 | ERROR(db->pakfire, "Could not bind capabilities: %s\n", sqlite3_errmsg(db->handle)); | |
5e9463ec | 765 | pakfire_file_unref(file); |
f5c77233 MT |
766 | goto END; |
767 | } | |
768 | ||
769 | // Execute query | |
770 | do { | |
771 | r = sqlite3_step(stmt); | |
772 | } while (r == SQLITE_BUSY); | |
773 | ||
774 | // Move on to next file | |
5e9463ec | 775 | pakfire_file_unref(file); |
f5c77233 MT |
776 | |
777 | // Reset bound values | |
778 | sqlite3_reset(stmt); | |
779 | } | |
780 | ||
781 | // All okay | |
782 | r = 0; | |
783 | ||
784 | END: | |
785 | if (stmt) | |
786 | sqlite3_finalize(stmt); | |
787 | ||
5e9463ec MT |
788 | pakfire_filelist_unref(filelist); |
789 | ||
f5c77233 MT |
790 | return r; |
791 | } | |
792 | ||
793 | PAKFIRE_EXPORT int pakfire_db_add_package(struct pakfire_db* db, | |
794 | PakfirePackage pkg, PakfireArchive archive) { | |
e49b93d1 MT |
795 | sqlite3_stmt* stmt = NULL; |
796 | int r; | |
797 | ||
798 | // Begin a new transaction | |
799 | r = pakfire_db_begin_transaction(db); | |
800 | if (r) | |
801 | goto ROLLBACK; | |
802 | ||
803 | const char* sql = "INSERT INTO packages(name, epoch, version, release, arch, groups, " | |
804 | "filename, size, inst_size, hash1, license, summary, description, uuid, vendor, " | |
1ca6a13d MT |
805 | "build_host, build_time, installed, repository, reason) VALUES(?, ?, " |
806 | "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, ?, ?)"; | |
e49b93d1 MT |
807 | |
808 | // Prepare the statement | |
809 | r = sqlite3_prepare_v2(db->handle, sql, strlen(sql), &stmt, NULL); | |
810 | if (r != SQLITE_OK) { | |
811 | ERROR(db->pakfire, "Could not prepare SQL statement: %s: %s\n", | |
812 | sql, sqlite3_errmsg(db->handle)); | |
813 | goto ROLLBACK; | |
814 | } | |
815 | ||
816 | // Bind name | |
817 | const char* name = pakfire_package_get_name(pkg); | |
818 | ||
819 | r = sqlite3_bind_text(stmt, 1, name, -1, NULL); | |
820 | if (r) { | |
821 | ERROR(db->pakfire, "Could not bind name: %s\n", sqlite3_errmsg(db->handle)); | |
822 | goto ROLLBACK; | |
823 | } | |
824 | ||
825 | // Bind epoch | |
826 | unsigned long epoch = pakfire_package_get_epoch(pkg); | |
827 | ||
828 | r = sqlite3_bind_int64(stmt, 2, epoch); | |
829 | if (r) { | |
830 | ERROR(db->pakfire, "Could not bind epoch: %s\n", sqlite3_errmsg(db->handle)); | |
831 | goto ROLLBACK; | |
832 | } | |
833 | ||
834 | // Bind version | |
835 | const char* version = pakfire_package_get_version(pkg); | |
836 | ||
837 | r = sqlite3_bind_text(stmt, 3, version, -1, NULL); | |
838 | if (r) { | |
839 | ERROR(db->pakfire, "Could not bind version: %s\n", sqlite3_errmsg(db->handle)); | |
840 | goto ROLLBACK; | |
841 | } | |
842 | ||
843 | // Bind release | |
844 | const char* release = pakfire_package_get_release(pkg); | |
845 | ||
846 | r = sqlite3_bind_text(stmt, 4, release, -1, NULL); | |
847 | if (r) { | |
848 | ERROR(db->pakfire, "Could not bind release: %s\n", sqlite3_errmsg(db->handle)); | |
849 | goto ROLLBACK; | |
850 | } | |
851 | ||
852 | // Bind arch | |
853 | const char* arch = pakfire_package_get_arch(pkg); | |
854 | ||
855 | r = sqlite3_bind_text(stmt, 5, arch, -1, NULL); | |
856 | if (r) { | |
857 | ERROR(db->pakfire, "Could not bind arch: %s\n", sqlite3_errmsg(db->handle)); | |
858 | goto ROLLBACK; | |
859 | } | |
860 | ||
861 | // Bind groups | |
862 | const char* groups = pakfire_package_get_groups(pkg); | |
863 | ||
864 | r = sqlite3_bind_text(stmt, 6, groups, -1, NULL); | |
865 | if (r) { | |
866 | ERROR(db->pakfire, "Could not bind groups: %s\n", sqlite3_errmsg(db->handle)); | |
867 | goto ROLLBACK; | |
868 | } | |
869 | ||
870 | // Bind filename | |
871 | const char* filename = pakfire_package_get_filename(pkg); | |
872 | ||
873 | r = sqlite3_bind_text(stmt, 7, filename, -1, NULL); | |
874 | if (r) { | |
875 | ERROR(db->pakfire, "Could not bind filename: %s\n", sqlite3_errmsg(db->handle)); | |
876 | goto ROLLBACK; | |
877 | } | |
878 | ||
879 | // Bind size | |
880 | unsigned long long size = pakfire_package_get_downloadsize(pkg); | |
881 | ||
882 | r = sqlite3_bind_int64(stmt, 8, size); | |
883 | if (r) { | |
884 | ERROR(db->pakfire, "Could not bind size: %s\n", sqlite3_errmsg(db->handle)); | |
885 | goto ROLLBACK; | |
886 | } | |
887 | ||
888 | // Bind installed size | |
889 | unsigned long long inst_size = pakfire_package_get_installsize(pkg); | |
890 | ||
891 | r = sqlite3_bind_int64(stmt, 9, inst_size); | |
892 | if (r) { | |
893 | ERROR(db->pakfire, "Could not bind inst_size: %s\n", sqlite3_errmsg(db->handle)); | |
894 | goto ROLLBACK; | |
895 | } | |
896 | ||
897 | // Bind hash1 | |
898 | const char* hash1 = pakfire_package_get_checksum(pkg); | |
899 | ||
900 | r = sqlite3_bind_text(stmt, 10, hash1, -1, NULL); | |
901 | if (r) { | |
902 | ERROR(db->pakfire, "Could not bind hash1: %s\n", sqlite3_errmsg(db->handle)); | |
903 | goto ROLLBACK; | |
904 | } | |
905 | ||
906 | // Bind license | |
907 | const char* license = pakfire_package_get_license(pkg); | |
908 | ||
909 | r = sqlite3_bind_text(stmt, 11, license, -1, NULL); | |
910 | if (r) { | |
911 | ERROR(db->pakfire, "Could not bind license: %s\n", sqlite3_errmsg(db->handle)); | |
912 | goto ROLLBACK; | |
913 | } | |
914 | ||
915 | // Bind summary | |
916 | const char* summary = pakfire_package_get_summary(pkg); | |
917 | ||
918 | r = sqlite3_bind_text(stmt, 12, summary, -1, NULL); | |
919 | if (r) { | |
920 | ERROR(db->pakfire, "Could not bind summary: %s\n", sqlite3_errmsg(db->handle)); | |
921 | goto ROLLBACK; | |
922 | } | |
923 | ||
924 | // Bind description | |
925 | const char* description = pakfire_package_get_description(pkg); | |
926 | ||
927 | r = sqlite3_bind_text(stmt, 13, description, -1, NULL); | |
928 | if (r) { | |
929 | ERROR(db->pakfire, "Could not bind description: %s\n", sqlite3_errmsg(db->handle)); | |
930 | goto ROLLBACK; | |
931 | } | |
932 | ||
933 | // Bind uuid | |
934 | const char* uuid = pakfire_package_get_uuid(pkg); | |
935 | ||
936 | r = sqlite3_bind_text(stmt, 14, uuid, -1, NULL); | |
937 | if (r) { | |
938 | ERROR(db->pakfire, "Could not bind uuid: %s\n", sqlite3_errmsg(db->handle)); | |
939 | goto ROLLBACK; | |
940 | } | |
941 | ||
942 | // Bind vendor | |
943 | const char* vendor = pakfire_package_get_vendor(pkg); | |
944 | ||
945 | r = sqlite3_bind_text(stmt, 14, vendor, -1, NULL); | |
946 | if (r) { | |
947 | ERROR(db->pakfire, "Could not bind vendor: %s\n", sqlite3_errmsg(db->handle)); | |
948 | goto ROLLBACK; | |
949 | } | |
950 | ||
e49b93d1 MT |
951 | // Bind build_host |
952 | const char* buildhost = pakfire_package_get_buildhost(pkg); | |
953 | ||
954 | r = sqlite3_bind_text(stmt, 16, buildhost, -1, NULL); | |
955 | if (r) { | |
956 | ERROR(db->pakfire, "Could not bind build_host: %s\n", sqlite3_errmsg(db->handle)); | |
957 | goto ROLLBACK; | |
958 | } | |
959 | ||
960 | // Bind build_time | |
961 | unsigned long long build_time = pakfire_package_get_buildtime(pkg); | |
962 | ||
963 | r = sqlite3_bind_int64(stmt, 17, build_time); | |
964 | if (r) { | |
965 | ERROR(db->pakfire, "Could not bind build_time: %s\n", sqlite3_errmsg(db->handle)); | |
966 | goto ROLLBACK; | |
967 | } | |
968 | ||
969 | // Bind repository name | |
970 | PakfireRepo repo = pakfire_package_get_repo(pkg); | |
971 | if (repo) { | |
972 | const char* repo_name = pakfire_repo_get_name(repo); | |
973 | pakfire_repo_unref(repo); | |
974 | ||
975 | r = sqlite3_bind_text(stmt, 18, repo_name, -1, NULL); | |
976 | if (r) | |
977 | goto ROLLBACK; | |
978 | ||
979 | // No repository? | |
980 | } else { | |
981 | r = sqlite3_bind_null(stmt, 18); | |
982 | if (r) | |
983 | goto ROLLBACK; | |
984 | } | |
985 | ||
986 | // XXX TODO Bind reason | |
987 | r = sqlite3_bind_null(stmt, 19); | |
988 | if (r) | |
989 | goto ROLLBACK; | |
990 | ||
991 | // Run query | |
992 | do { | |
993 | r = sqlite3_step(stmt); | |
994 | } while (r == SQLITE_BUSY); | |
995 | ||
996 | if (r != SQLITE_DONE) { | |
997 | ERROR(db->pakfire, "Could not add package to database: %s\n", | |
998 | sqlite3_errmsg(db->handle)); | |
999 | goto ROLLBACK; | |
1000 | } | |
1001 | ||
f5c77233 MT |
1002 | // Save package ID |
1003 | unsigned long packages_id = sqlite3_last_insert_rowid(db->handle); | |
1004 | ||
e49b93d1 | 1005 | // This is done |
f5c77233 MT |
1006 | r = sqlite3_finalize(stmt); |
1007 | if (r == SQLITE_OK) | |
1008 | stmt = NULL; | |
1009 | ||
1010 | // Add files | |
1011 | r = pakfire_db_add_files(db, packages_id, archive); | |
1012 | if (r) | |
1013 | goto ROLLBACK; | |
e49b93d1 MT |
1014 | |
1015 | // All done, commit! | |
1016 | r = pakfire_db_commit(db); | |
1017 | if (r) | |
1018 | goto ROLLBACK; | |
1019 | ||
1020 | return 0; | |
1021 | ||
1022 | ROLLBACK: | |
f5c77233 MT |
1023 | if (stmt) |
1024 | sqlite3_finalize(stmt); | |
e49b93d1 MT |
1025 | |
1026 | pakfire_db_rollback(db); | |
1027 | ||
1028 | return 1; | |
eafbe2ce MT |
1029 | } |
1030 | ||
18bf891d | 1031 | PAKFIRE_EXPORT int pakfire_db_remove_package(struct pakfire_db* db, PakfirePackage pkg) { |
eafbe2ce MT |
1032 | return 0; // TODO |
1033 | } |