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