]>
git.ipfire.org Git - people/ms/pakfire.git/blob - src/libpakfire/db.c
1 /*#############################################################################
3 # Pakfire - The IPFire package management system #
4 # Copyright (C) 2021 Pakfire development team #
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. #
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. #
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/>. #
19 #############################################################################*/
26 #include <pakfire/db.h>
27 #include <pakfire/logging.h>
28 #include <pakfire/pakfire.h>
29 #include <pakfire/private.h>
30 #include <pakfire/types.h>
31 #include <pakfire/util.h>
33 #define DATABASE_PATH PAKFIRE_PRIVATE_DIR "/packages.db"
35 #define CURRENT_SCHEMA 7
36 #define SCHEMA_MIN_SUP 7
48 static void logging_callback(void* data
, int r
, const char* msg
) {
49 Pakfire pakfire
= (Pakfire
)data
;
51 ERROR(pakfire
, "Database Error: %s: %s\n",
52 sqlite3_errstr(r
), msg
);
55 static int pakfire_db_execute(struct pakfire_db
* db
, const char* stmt
) {
58 DEBUG(db
->pakfire
, "Executing database query: %s\n", stmt
);
61 r
= sqlite3_exec(db
->handle
, stmt
, NULL
, NULL
, NULL
);
62 } while (r
== SQLITE_BUSY
);
66 ERROR(db
->pakfire
, "Database query failed: %s\n", sqlite3_errmsg(db
->handle
));
72 static int pakfire_db_begin_transaction(struct pakfire_db
* db
) {
73 return pakfire_db_execute(db
, "BEGIN TRANSACTION");
76 static int pakfire_db_commit(struct pakfire_db
* db
) {
77 return pakfire_db_execute(db
, "COMMIT");
80 static int pakfire_db_rollback(struct pakfire_db
* db
) {
81 return pakfire_db_execute(db
, "ROLLBACK");
85 This function performs any fast optimization and tries to truncate the WAL log file
86 to keep the database as compact as possible on disk.
88 static void pakfire_db_optimize(struct pakfire_db
* db
) {
89 pakfire_db_execute(db
, "PRAGMA optmize");
90 pakfire_db_execute(db
, "PRAGMA wal_checkpoint = TRUNCATE");
93 static void pakfire_db_free(struct pakfire_db
* db
) {
94 DEBUG(db
->pakfire
, "Releasing database at %p\n", db
);
97 // Optimize the database before it is being closed
98 pakfire_db_optimize(db
);
100 // Close database handle
101 int r
= sqlite3_close(db
->handle
);
102 if (r
!= SQLITE_OK
) {
103 ERROR(db
->pakfire
, "Could not close database handle: %s\n",
104 sqlite3_errmsg(db
->handle
));
108 pakfire_unref(db
->pakfire
);
113 static sqlite3_value
* pakfire_db_get(struct pakfire_db
* db
, const char* key
) {
114 sqlite3_stmt
* stmt
= NULL
;
115 sqlite3_value
* val
= NULL
;
118 const char* sql
= "SELECT val FROM settings WHERE key = ?";
120 // Prepare the statement
121 r
= sqlite3_prepare_v2(db
->handle
, sql
, strlen(sql
), &stmt
, NULL
);
122 if (r
!= SQLITE_OK
) {
123 //ERROR(db->pakfire, "Could not prepare SQL statement: %s: %s\n",
124 // sql, sqlite3_errmsg(db->handle));
129 r
= sqlite3_bind_text(stmt
, 1, key
, strlen(key
), NULL
);
130 if (r
!= SQLITE_OK
) {
131 ERROR(db
->pakfire
, "Could not bind key: %s\n", sqlite3_errmsg(db
->handle
));
135 // Execute the statement
137 r
= sqlite3_step(stmt
);
138 } while (r
== SQLITE_BUSY
);
141 val
= sqlite3_column_value(stmt
, 1);
143 ERROR(db
->pakfire
, "Could not read value\n");
147 // Copy value onto the heap
148 val
= sqlite3_value_dup(val
);
152 sqlite3_finalize(stmt
);
157 static int pakfire_db_set_int(struct pakfire_db
* db
, const char* key
, int val
) {
158 sqlite3_stmt
* stmt
= NULL
;
161 const char* sql
= "INSERT INTO settings(key, val) VALUES(?, ?) \
162 ON CONFLICT (key) DO UPDATE SET val = excluded.val";
165 r
= sqlite3_prepare_v2(db
->handle
, sql
, strlen(sql
), &stmt
, NULL
);
166 if (r
!= SQLITE_OK
) {
167 ERROR(db
->pakfire
, "Could not prepare SQL statement: %s: %s\n",
168 sql
, sqlite3_errmsg(db
->handle
));
173 r
= sqlite3_bind_text(stmt
, 1, key
, strlen(key
), NULL
);
174 if (r
!= SQLITE_OK
) {
175 ERROR(db
->pakfire
, "Could not bind key: %s\n", sqlite3_errmsg(db
->handle
));
180 r
= sqlite3_bind_int64(stmt
, 2, val
);
181 if (r
!= SQLITE_OK
) {
182 ERROR(db
->pakfire
, "Could not bind val: %s\n", sqlite3_errmsg(db
->handle
));
186 // Execute the statement
188 r
= sqlite3_step(stmt
);
189 } while (r
== SQLITE_BUSY
);
192 r
= (r
== SQLITE_OK
);
196 sqlite3_finalize(stmt
);
201 static int pakfire_db_get_schema(struct pakfire_db
* db
) {
202 sqlite3_value
* value
= pakfire_db_get(db
, "schema");
206 int schema
= sqlite3_value_int64(value
);
207 sqlite3_value_free(value
);
209 DEBUG(db
->pakfire
, "Database has schema version %d\n", schema
);
214 static int pakfire_db_create_schema(struct pakfire_db
* db
) {
217 // Create settings table
218 r
= pakfire_db_execute(db
, "CREATE TABLE IF NOT EXISTS settings(key TEXT, val TEXT)");
222 // settings: Add a unique index on key
223 r
= pakfire_db_execute(db
, "CREATE UNIQUE INDEX IF NOT EXISTS settings_key ON settings(key)");
227 // Create packages table
228 r
= pakfire_db_execute(db
,
229 "CREATE TABLE IF NOT EXISTS packages("
230 "id INTEGER PRIMARY KEY, "
239 "inst_size INTEGER, "
249 "build_time INTEGER, "
250 "installed INTEGER, "
257 // packages: Create index to find package by name
258 r
= pakfire_db_execute(db
, "CREATE INDEX IF NOT EXISTS packages_name ON packages(name)");
262 // Create dependencies table
263 r
= pakfire_db_execute(db
,
264 "CREATE TABLE IF NOT EXISTS dependencies("
272 // dependencies: Add index over packages
273 r
= pakfire_db_execute(db
, "CREATE INDEX IF NOT EXISTS dependencies_pkg_index ON dependencies(pkg)");
277 // Create files table
278 r
= pakfire_db_execute(db
,
279 "CREATE TABLE IF NOT EXISTS files("
280 "id INTEGER PRIMARY KEY, "
297 // files: Add index over packages
298 r
= pakfire_db_execute(db
, "CREATE INDEX IF NOT EXISTS files_pkg_index ON files(pkg)");
305 static int pakfire_db_migrate_schema(struct pakfire_db
* db
) {
308 while (db
->schema
< CURRENT_SCHEMA
) {
309 // Begin a new transaction
310 r
= pakfire_db_begin_transaction(db
);
314 switch (db
->schema
) {
317 r
= pakfire_db_create_schema(db
);
321 db
->schema
= CURRENT_SCHEMA
;
325 ERROR(db
->pakfire
, "Cannot migrate database from schema %d\n", db
->schema
);
329 // Update the schema version
330 r
= pakfire_db_set_int(db
, "schema", CURRENT_SCHEMA
);
335 r
= pakfire_db_commit(db
);
343 pakfire_db_rollback(db
);
348 static int pakfire_db_setup(struct pakfire_db
* db
) {
352 sqlite3_config(SQLITE_CONFIG_LOG
, logging_callback
, db
->pakfire
);
354 // Make LIKE case-sensitive
355 pakfire_db_execute(db
, "PRAGMA case_sensitive_like = ON");
357 // Fetch the current schema
358 db
->schema
= pakfire_db_get_schema(db
);
360 // Check if the schema is recent enough
361 if (db
->schema
> 0 && db
->schema
< SCHEMA_MIN_SUP
) {
362 ERROR(db
->pakfire
, "Database schema %d is not supported by this version of Pakfire\n",
367 // Done when not in read-write mode
368 if (db
->mode
!= PAKFIRE_DB_READWRITE
)
371 // Disable secure delete
372 pakfire_db_execute(db
, "PRAGMA secure_delete = OFF");
374 // Set database journal to WAL
375 r
= pakfire_db_execute(db
, "PRAGMA journal_mode = WAL");
376 if (r
!= SQLITE_OK
) {
377 ERROR(db
->pakfire
, "Could not set journal mode to WAL: %s\n",
378 sqlite3_errmsg(db
->handle
));
382 // Disable autocheckpoint
383 r
= sqlite3_wal_autocheckpoint(db
->handle
, 0);
384 if (r
!= SQLITE_OK
) {
385 ERROR(db
->pakfire
, "Could not disable autocheckpoint: %s\n",
386 sqlite3_errmsg(db
->handle
));
390 // Create or migrate schema
391 r
= pakfire_db_migrate_schema(db
);
398 PAKFIRE_EXPORT
int pakfire_db_open(struct pakfire_db
** db
, Pakfire pakfire
, int flags
) {
401 struct pakfire_db
* o
= pakfire_calloc(1, sizeof(*o
));
405 DEBUG(pakfire
, "Allocated database at %p\n", o
);
407 o
->pakfire
= pakfire_ref(pakfire
);
410 int sqlite3_flags
= 0;
412 // Store mode & forward it to sqlite3
413 if (flags
& PAKFIRE_DB_READWRITE
) {
414 o
->mode
= PAKFIRE_DB_READWRITE
;
415 sqlite3_flags
|= SQLITE_OPEN_READWRITE
| SQLITE_OPEN_CREATE
;
417 o
->mode
= PAKFIRE_DB_READONLY
;
418 sqlite3_flags
|= SQLITE_OPEN_READONLY
;
422 char* path
= pakfire_make_path(o
->pakfire
, DATABASE_PATH
);
426 // Try to open the sqlite3 database file
427 r
= sqlite3_open_v2(path
, &o
->handle
, sqlite3_flags
, NULL
);
428 if (r
!= SQLITE_OK
) {
429 ERROR(pakfire
, "Could not open database %s: %s\n",
430 path
, sqlite3_errmsg(o
->handle
));
436 // Setup the database
437 r
= pakfire_db_setup(o
);
454 PAKFIRE_EXPORT
struct pakfire_db
* pakfire_db_ref(struct pakfire_db
* db
) {
460 PAKFIRE_EXPORT
struct pakfire_db
* pakfire_db_unref(struct pakfire_db
* db
) {
469 PAKFIRE_EXPORT
int pakfire_db_add_package(struct pakfire_db
* db
, PakfirePackage pkg
) {
473 PAKFIRE_EXPORT
int pakfire_db_remove_package(struct pakfire_db
* db
, PakfirePackage pkg
) {