]> git.proxmox.com Git - pve-cluster.git/blame - data/src/database.c
pmxcfs: db: tell query planner that prepared statement are long living
[pve-cluster.git] / data / src / database.c
CommitLineData
fe000966 1/*
84c98315 2 Copyright (C) 2010 - 2020 Proxmox Server Solutions GmbH
fe000966
DM
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Affero General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Affero General Public License for more details.
13
14 You should have received a copy of the GNU Affero General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17 Author: Dietmar Maurer <dietmar@proxmox.com>
18
19*/
20
21#define G_LOG_DOMAIN "database"
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif /* HAVE_CONFIG_H */
26
27#include <stdlib.h>
e5a5a3ea 28#include <inttypes.h>
fe000966
DM
29#include <stdio.h>
30#include <string.h>
31#include <unistd.h>
32#include <glib.h>
33#include <dirent.h>
34#include <sys/stat.h>
35#include <errno.h>
36
37#include <sqlite3.h>
38
39#include "cfs-utils.h"
40#include "status.h"
41#include "memdb.h"
42
43struct db_backend {
44 sqlite3 *db;
45 sqlite3_stmt *stmt_insert_entry;
46 sqlite3_stmt *stmt_update_entry;
47 sqlite3_stmt *stmt_replace_entry;
48 sqlite3_stmt *stmt_delete_entry;
49 sqlite3_stmt *stmt_begin;
50 sqlite3_stmt *stmt_commit;
51 sqlite3_stmt *stmt_rollback;
52 sqlite3_stmt *stmt_load_all;
53};
54
55#define VERSIONFILENAME "__version__"
56
57/* colume type "INTEGER PRIMARY KEY" is a special case, because sqlite
58 * usese the internal ROWID. So only real interger are allowed, and
59 * there is no need to add an additionl check
60 */
61static const char *sql_create_db =
62 "CREATE TABLE IF NOT EXISTS tree ("
63 " inode INTEGER PRIMARY KEY NOT NULL,"
64 " parent INTEGER NOT NULL CHECK(typeof(parent)=='integer'),"
65 " version INTEGER NOT NULL CHECK(typeof(version)=='integer'),"
66 " writer INTEGER NOT NULL CHECK(typeof(writer)=='integer'),"
67 " mtime INTEGER NOT NULL CHECK(typeof(mtime)=='integer'),"
68 " type INTEGER NOT NULL CHECK(typeof(type)=='integer'),"
69 " name TEXT NOT NULL,"
70 " data BLOB);";
71
72static const char *sql_load_all =
73 "SELECT inode, parent, version, writer, mtime, type, name, data FROM tree;";
74
75static char *sql_insert_entry =
76 "INSERT INTO tree ("
77 "inode, parent, version, writer, mtime, type, name, data) "
78 "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8);";
79
80static char *sql_update_entry =
81 "UPDATE tree SET parent = ?2, version = ?3, writer = ?4, mtime = ?5, "
82 "type = ?6, name = ?7, data = ?8 WHERE inode = ?1;";
83
84static char *sql_replace_entry =
85 "REPLACE INTO tree (inode, parent, version, writer, mtime, type, "
86 "name, data) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8);";
87
88static char *sql_delete_entry =
89 "DELETE FROM tree WHERE inode = ?1;";
90
91static char *sql_begin = "BEGIN TRANSACTION;";
92static char *sql_commit = "COMMIT TRANSACTION;";
93static char *sql_rollback = "ROLLBACK TRANSACTION;";
94
95static sqlite3 *bdb_create(
96 const char *filename)
97{
98 int rc;
99 sqlite3 *db = NULL;
100
101 int flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE;
102 rc = sqlite3_open_v2(filename, &db, flags, NULL);
103 if (rc != SQLITE_OK) {
104 cfs_critical("splite3_open_v2 failed: %d\n", rc);
105 sqlite3_close(db);
106 return NULL;
107 }
108
109 if (chmod(filename, 0600) == -1) {
110 cfs_critical("chmod failed: %s", strerror(errno));
111 return NULL;
112 }
113
114 /* use WAL mode - to allow concurrent reads */
115 rc = sqlite3_exec(db, "PRAGMA journal_mode=WAL;", NULL, NULL, NULL);
116 if (rc != SQLITE_OK) {
117 cfs_critical("unable to set WAL mode: %s\n", sqlite3_errmsg(db));
118 sqlite3_close(db);
119 return NULL;
120 }
121
122 /* NORMAL is good enough when using WAL */
123 rc = sqlite3_exec(db, "PRAGMA synchronous=NORMAL", NULL, NULL, NULL);
124 if (rc != SQLITE_OK) {
125 cfs_critical("unable to set synchronous mode: %s\n", sqlite3_errmsg(db));
126 sqlite3_close(db);
127 return NULL;
128 }
129
130 sqlite3_busy_timeout(db, 10000); /* 10 seconds */
131
132 rc = sqlite3_exec(db, sql_create_db, NULL, NULL, NULL);
133 if (rc != SQLITE_OK) {
134 cfs_critical("init database failed: %s\n", sqlite3_errmsg(db));
135 sqlite3_close(db);
136 return NULL;
137 }
138
139 return db;
140}
141
142static int backend_write_inode(
143 sqlite3 *db,
144 sqlite3_stmt *stmt,
145 guint64 inode,
146 guint64 parent,
147 guint64 version,
148 guint32 writer,
149 guint32 mtime,
150 guint32 size,
151 char type,
152 char *name,
153 gpointer value)
154{
155 int rc;
156
42f0a0a5
TL
157 cfs_debug(
158 "enter backend_write_inode %016" PRIX64 " '%s', size %"PRIu32"",
159 inode,
160 name,
161 size
162 );
fe000966
DM
163
164 if ((rc = sqlite3_bind_int64(stmt, 1, inode)) != SQLITE_OK) {
165 cfs_critical("sqlite3_bind failed: %s\n", sqlite3_errmsg(db));
166 return rc;
167 }
168 if ((rc = sqlite3_bind_int64(stmt, 2, parent)) != SQLITE_OK) {
169 cfs_critical("sqlite3_bind failed: %s\n", sqlite3_errmsg(db));
170 return rc;
171 }
172 if ((rc = sqlite3_bind_int64(stmt, 3, version)) != SQLITE_OK) {
173 cfs_critical("sqlite3_bind failed: %s\n", sqlite3_errmsg(db));
174 return rc;
175 }
176 if ((rc = sqlite3_bind_int64(stmt, 4, writer)) != SQLITE_OK) {
177 cfs_critical("sqlite3_bind failed: %s\n", sqlite3_errmsg(db));
178 return rc;
179 }
180 if ((rc = sqlite3_bind_int64(stmt, 5, mtime)) != SQLITE_OK) {
181 cfs_critical("sqlite3_bind failed: %s\n", sqlite3_errmsg(db));
182 return rc;
183 }
184 if ((rc = sqlite3_bind_int64(stmt, 6, type)) != SQLITE_OK) {
185 cfs_critical("sqlite3_bind failed: %s\n", sqlite3_errmsg(db));
186 return rc;
187 }
c44bb3d6 188 if ((rc = sqlite3_bind_text(stmt, 7, name, -1, SQLITE_STATIC)) != SQLITE_OK) {
fe000966
DM
189 cfs_critical("sqlite3_bind failed: %s\n", sqlite3_errmsg(db));
190 return rc;
191 }
c44bb3d6 192 if ((rc = sqlite3_bind_blob(stmt, 8, value, size, SQLITE_STATIC)) != SQLITE_OK) {
fe000966
DM
193 cfs_critical("sqlite3_bind failed: %s\n", sqlite3_errmsg(db));
194 return rc;
195 }
196
197 if ((rc = sqlite3_step(stmt)) != SQLITE_DONE) {
198 cfs_critical("sqlite3_step failed: %s\n", sqlite3_errmsg(db));
199 sqlite3_reset(stmt);
200 return rc;
201 }
202
203 sqlite3_reset(stmt);
204
205 return SQLITE_OK;
206}
207
208static int bdb_backend_delete_inode(
209 db_backend_t *bdb,
210 guint64 inode)
211{
212 int rc;
213
214 cfs_debug("enter dbd_backend_delete_inode");
215
216 sqlite3_stmt *stmt = bdb->stmt_delete_entry;
217
218 if ((rc = sqlite3_bind_int64(stmt, 1, inode)) != SQLITE_OK) {
219 cfs_critical("delete_inode/sqlite3_bind failed: %s\n", sqlite3_errmsg(bdb->db));
220 return rc;
221 }
222
223 if ((rc = sqlite3_step(stmt)) != SQLITE_DONE) {
224 cfs_critical("delete_inode failed: %s\n", sqlite3_errmsg(bdb->db));
225 sqlite3_reset(stmt);
226 return rc;
227 }
228
229 sqlite3_reset(stmt);
230
231 return SQLITE_OK;
232}
233
234int bdb_backend_write(
235 db_backend_t *bdb,
236 guint64 inode,
237 guint64 parent,
238 guint64 version,
239 guint32 writer,
240 guint32 mtime,
241 guint32 size,
242 char type,
243 char *name,
244 gpointer value,
245 guint64 delete_inode)
246{
247 g_return_val_if_fail(bdb != NULL, SQLITE_PERM);
248 g_return_val_if_fail(inode == 0 || (name != NULL && name[0]), SQLITE_PERM);
249 g_return_val_if_fail(type == DT_REG || type == DT_DIR, SQLITE_PERM);
250 int rc;
251
252 gboolean need_txn = (inode != 0 || delete_inode != 0);
253
254 if (need_txn) {
255 rc = sqlite3_step(bdb->stmt_begin);
256 sqlite3_reset(bdb->stmt_begin);
257 if (rc != SQLITE_DONE) {
258 cfs_critical("begin transaction failed: %s\n", sqlite3_errmsg(bdb->db));
259 return rc;
260 }
261 }
262
263 if (delete_inode != 0) {
264 if ((rc = bdb_backend_delete_inode(bdb, delete_inode)) != SQLITE_OK)
265 goto rollback;
266 }
267
268 if (inode != 0) {
269
270 sqlite3_stmt *stmt = (inode > version) ?
271 bdb->stmt_insert_entry : bdb->stmt_replace_entry;
272
273 rc = backend_write_inode(bdb->db, stmt, inode, parent, version,
274 writer, mtime, size, type, name, value);
275 if (rc != SQLITE_OK)
276 goto rollback;
277
278 if (sqlite3_changes(bdb->db) != 1) {
e5a5a3ea 279 cfs_critical("no such inode %016" PRIX64, inode);
fe000966
DM
280 goto rollback;
281 }
282 }
283
284 rc = backend_write_inode(bdb->db, bdb->stmt_replace_entry, 0, 0, version,
285 writer, mtime, 0, DT_REG, VERSIONFILENAME, NULL);
286
287 if (rc != SQLITE_OK)
288 goto rollback;
289
290
291 if (need_txn) {
292 rc = sqlite3_step(bdb->stmt_commit);
293 sqlite3_reset(bdb->stmt_commit);
294 if (rc != SQLITE_DONE) {
295 cfs_critical("commit transaction failed: %s\n", sqlite3_errmsg(bdb->db));
296 goto rollback;
297 }
298 }
299
300 return SQLITE_OK;
301
302rollback:
303
304 if (!need_txn)
305 return rc;
306
307 int rbrc = sqlite3_step(bdb->stmt_rollback);
308 sqlite3_reset(bdb->stmt_rollback);
309 if (rbrc != SQLITE_DONE) {
310 cfs_critical("rollback transaction failed: %s\n", sqlite3_errmsg(bdb->db));
311 return rc;
312 }
313
314 return rc;
315}
316
317static gboolean bdb_backend_load_index(
318 db_backend_t *bdb,
319 memdb_tree_entry_t *root,
320 GHashTable *index)
321{
322 g_return_val_if_fail(bdb != NULL, FALSE);
323 g_return_val_if_fail(root != NULL, FALSE);
324 g_return_val_if_fail(index != NULL, FALSE);
325 g_return_val_if_fail(root->version == 0, FALSE);
326 g_return_val_if_fail(g_hash_table_size(index) == 1, FALSE);
327
fe000966
DM
328 sqlite3_stmt *stmt = bdb->stmt_load_all;
329
42f0a0a5 330 int rc;
fe000966
DM
331 while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
332
333 memdb_tree_entry_t *te;
334
335 guint64 inode = sqlite3_column_int64(stmt, 0);
336 const char *name = (const char *)sqlite3_column_text(stmt, 6);
337 int namelen = sqlite3_column_bytes(stmt, 6);
338 if (name == NULL || namelen == 0) {
e5a5a3ea 339 cfs_critical("inode has no name (inode = %016" PRIX64 ")", inode);
fe000966
DM
340 goto fail;
341 }
342 te = g_malloc0(sizeof(memdb_tree_entry_t) + namelen + 1);
343 strcpy(te->name, name);
344
345 te->inode = inode;
346 te->parent = sqlite3_column_int64(stmt, 1);
347 te->version = sqlite3_column_int64(stmt, 2);
348 te->writer = sqlite3_column_int64(stmt, 3) & 0x0ffffffff;
349 te->mtime = sqlite3_column_int64(stmt, 4) & 0x0ffffffff;
350 te->type = sqlite3_column_int64(stmt, 5) & 255;
351
352 gconstpointer value = sqlite3_column_blob(stmt, 7);
353
354 int size = sqlite3_column_bytes(stmt, 7);
355 te->size = size;
356
357 if (te->type == DT_REG) {
358 if (size > 0)
359 te->data.value = g_memdup(value, size);
360 } else if (te->type == DT_DIR) {
361 if (size) {
e5a5a3ea 362 cfs_critical("directory inode contains data (inode = %016" PRIX64 ")",
fe000966
DM
363 te->inode);
364 g_free(te);
365 goto fail;
366 }
367 te->data.entries = NULL;
368 } else {
e5a5a3ea 369 cfs_critical("inode has unknown type (inode = %016" PRIX64 ", type = %d)",
fe000966
DM
370 te->inode, te->type);
371 g_free(te);
372 goto fail;
373 }
374
e5a5a3ea 375 cfs_debug("name %s (inode = %016" PRIX64 ", parent = %016" PRIX64 ")",
fe000966
DM
376 te->name, te->inode, te->parent);
377
378 if (te->inode == 0) {
73f041fd 379 if (!strcmp(te->name, VERSIONFILENAME)) {
fe000966
DM
380 root->version = te->version;
381 root->writer = te->writer;
382 root->mtime = te->mtime;
383 memdb_tree_entry_free(te);
384 } else {
385 cfs_critical("root inode has unexpected name '%s'", te->name);
386 memdb_tree_entry_free(te);
387 goto fail;
388 }
389 } else {
390 memdb_tree_entry_t *pte;
391
4c34134e 392 if (!(pte = g_hash_table_lookup(index, &te->parent))) {
fe000966
DM
393 /* allocate placeholder (type == 0)
394 * this is simply replaced if we find a real inode later
395 */
396 pte = g_malloc0(sizeof(memdb_tree_entry_t));
397 pte->inode = te->parent;
398 pte->data.entries = g_hash_table_new(g_str_hash, g_str_equal);
399 g_hash_table_replace(index, &pte->inode, pte);
400
4c34134e 401 } else if (!(pte->type == DT_DIR || pte->type == 0)) {
fe000966 402 cfs_critical("parent is not a directory "
e5a5a3ea 403 "(inode = %016" PRIX64 ", parent = %016" PRIX64 ", name = '%s')",
fe000966
DM
404 te->inode, te->parent, te->name);
405 memdb_tree_entry_free(te);
406 goto fail;
407 }
408
409 if (te->type == DT_DIR) {
4c34134e
DM
410 memdb_tree_entry_t *tmpte;
411 /* test if there is a placeholder entry */
412 if ((tmpte = g_hash_table_lookup(index, &te->inode))) {
413 if (tmpte->type != 0) {
414 cfs_critical("found strange placeholder for "
e5a5a3ea 415 "(inode = %016" PRIX64 ", parent = %016" PRIX64 ", name = '%s', type = '%d')",
4c34134e
DM
416 te->inode, te->parent, te->name, tmpte->type);
417 memdb_tree_entry_free(te);
418 goto fail;
419 }
420 /* copy entries from placeholder */
421 te->data.entries = tmpte->data.entries;
422 tmpte->data.entries = NULL;
423 } else {
424 te->data.entries = g_hash_table_new(g_str_hash, g_str_equal);
425 }
fe000966 426 }
42f0a0a5
TL
427
428 memdb_tree_entry_t *existing;
429 if ((existing = g_hash_table_lookup(pte->data.entries, te->name))) {
430 cfs_critical(
431 "found entry with duplicate name '%s' - "
432 "A:(inode = 0x%016"PRIX64", parent = 0x%016"PRIX64", v./mtime = 0x%"PRIX64"/0x%"PRIi32")"
433 " vs. "
434 "B:(inode = 0x%016"PRIX64", parent = 0x%016"PRIX64", v./mtime = 0x%"PRIX64"/0x%"PRIi32")",
435 te->name,
436 existing->inode, existing->parent, existing->version, existing->mtime,
437 te->inode, te->parent, te->version, te->mtime
438 );
fe000966
DM
439 goto fail;
440 }
441
442 g_hash_table_replace(pte->data.entries, te->name, te);
443 g_hash_table_replace(index, &te->inode, te);
444 }
445 }
446 if (rc != SQLITE_DONE) {
447 cfs_critical("select returned error: %s", sqlite3_errmsg(bdb->db));
448 goto fail;
449 }
450
4c34134e 451 /* check if all inodes have parents (there must be no placeholders) */
fe000966
DM
452 GHashTableIter iter;
453 gpointer key, value;
454 g_hash_table_iter_init (&iter, index);
455 while (g_hash_table_iter_next (&iter, &key, &value)) {
456 memdb_tree_entry_t *te = (memdb_tree_entry_t *)value;
457 if (te->type == 0) {
e5a5a3ea 458 cfs_critical("missing directory inode (inode = %016" PRIX64 ")", te->inode);
fe000966
DM
459 goto fail;
460 }
461 }
462
463 sqlite3_reset(stmt);
464
465 return TRUE;
466
467fail:
468 sqlite3_reset(stmt);
469
470 cfs_critical("DB load failed");
471
472 return FALSE;
473}
474
475gboolean bdb_backend_commit_update(
476 memdb_t *memdb,
477 memdb_index_t *master,
478 memdb_index_t *slave,
479 GList *inodes)
480{
481 g_return_val_if_fail(memdb != NULL, FALSE);
482 g_return_val_if_fail(memdb->bdb != NULL, FALSE);
483 g_return_val_if_fail(master != NULL, FALSE);
484 g_return_val_if_fail(slave != NULL, FALSE);
485
486 cfs_debug("enter bdb_backend_commit_update");
487
488 memdb_tree_entry_t *root = NULL;
489 GHashTable *index = NULL;
490
491 db_backend_t *bdb = (db_backend_t *)memdb->bdb;
492 gboolean result = FALSE;
493
494 int rc;
495
496 rc = sqlite3_step(bdb->stmt_begin);
497 sqlite3_reset(bdb->stmt_begin);
498 if (rc != SQLITE_DONE) {
499 cfs_critical("begin transaction failed: %s\n", sqlite3_errmsg(bdb->db));
500 return rc;
501 }
502
89fde9ac 503 g_mutex_lock (&memdb->mutex);
fe000966
DM
504
505 /* first, delete anything not found in master index) */
506
507 int i = 0;
508 int j = 0;
509
510 for (i = 0; i < master->size; i++) {
511 guint64 inode = master->entries[i].inode;
512 guint64 slave_inode;
513 while (j < slave->size && (slave_inode = slave->entries[j].inode) <= inode) {
514
515 if (slave_inode < inode) {
516 if (bdb_backend_delete_inode(bdb, slave_inode) != SQLITE_OK)
517 goto abort;
518
e5a5a3ea 519 cfs_debug("deleted inode %016" PRIX64, slave_inode);
fe000966
DM
520 }
521 j++;
522 }
523 if (j >= slave->size)
524 break;
525 }
526
527 while (j < slave->size) {
528 guint64 slave_inode = slave->entries[j].inode;
529
530 if (bdb_backend_delete_inode(bdb, slave_inode) != SQLITE_OK)
531 goto abort;
42f0a0a5 532
e5a5a3ea 533 cfs_debug("deleted inode %016" PRIX64, slave_inode);
fe000966
DM
534
535 j++;
536 }
537
538 /* now add all updates */
539
540 GList *l = inodes;
541 while (l) {
542 memdb_tree_entry_t *te = (memdb_tree_entry_t *)l->data;
543
544 tree_entry_debug(te);
545
546 if (backend_write_inode(
547 bdb->db, bdb->stmt_replace_entry, te->inode, te->parent, te->version,
548 te->writer, te->mtime, te->size, te->type,
549 te->inode ? te->name : VERSIONFILENAME, te->data.value) != SQLITE_OK) {
550 goto abort;
551 }
552
553 l = g_list_next(l);
554 }
555
556 /* now try to reload */
557 root = memdb_tree_entry_new("");
558 root->data.entries = g_hash_table_new(g_str_hash, g_str_equal);
559 root->type = DT_DIR;
560
561 index = g_hash_table_new_full(g_int64_hash, g_int64_equal, NULL,
562 (GDestroyNotify)memdb_tree_entry_free);
563
564 g_hash_table_replace(index, &root->inode, root);
565
566 if (!bdb_backend_load_index(bdb, root, index))
567 goto abort;
568
569 if (!memdb->root->version) {
570 cfs_critical("new index has version 0 - internal error");
571 goto abort;
572 }
573
574 memdb_index_t *new_idx = memdb_encode_index(index, root);
575 if (!new_idx) {
576 cfs_critical("cant encode new index - internal error");
577 goto abort;
578 }
579
580 int idx_equal = (new_idx->bytes == master->bytes &&
581 (memcmp(master, new_idx, new_idx->bytes) == 0));
582
583 g_free (new_idx);
584
585 if (!idx_equal) {
586 cfs_critical("new index does not match master index - internal error");
587 goto abort;
588 }
589
590 rc = sqlite3_step(bdb->stmt_commit);
591 sqlite3_reset(bdb->stmt_commit);
592 if (rc != SQLITE_DONE) {
593 cfs_critical("commit transaction failed: %s\n", sqlite3_errmsg(bdb->db));
594 goto abort;
595 }
596
597 g_hash_table_destroy(memdb->index);
598 memdb->index = index;
599 memdb->root = root;
600 index = NULL;
601 root = NULL;
602
603 record_memdb_reload();
604
605 if (!memdb_recreate_vmlist(memdb)) {
606 cfs_critical("memdb_recreate_vmlist failed");
607 memdb->errors = 1;
608 result = FALSE;
609 goto ret;
610 }
611
612 memdb_update_locks(memdb);
613
614 result = TRUE;
615
616ret:
89fde9ac 617 g_mutex_unlock (&memdb->mutex);
fe000966
DM
618
619 if (index)
620 g_hash_table_destroy(index);
621
622 cfs_debug("leave bdb_backend_commit_update (%d)", result);
623
624 return result;
625
626abort:
627
628 memdb->errors = 1;
629
630 rc = sqlite3_step(bdb->stmt_rollback);
631 sqlite3_reset(bdb->stmt_rollback);
632 if (rc != SQLITE_DONE)
633 cfs_critical("rollback transaction failed: %s\n", sqlite3_errmsg(bdb->db));
634
635 result = FALSE;
636
637 goto ret;
638}
639
640void bdb_backend_close(db_backend_t *bdb)
641{
642 g_return_if_fail(bdb != NULL);
643
644 sqlite3_finalize(bdb->stmt_insert_entry);
645 sqlite3_finalize(bdb->stmt_replace_entry);
646 sqlite3_finalize(bdb->stmt_update_entry);
647 sqlite3_finalize(bdb->stmt_delete_entry);
648 sqlite3_finalize(bdb->stmt_begin);
649 sqlite3_finalize(bdb->stmt_commit);
650 sqlite3_finalize(bdb->stmt_rollback);
651 sqlite3_finalize(bdb->stmt_load_all);
652
653 int rc;
654 if ((rc = sqlite3_close(bdb->db)) != SQLITE_OK) {
655 cfs_critical("sqlite3_close failed: %d\n", rc);
656 }
657
658 sqlite3_shutdown();
659
660 g_free(bdb);
661}
662
663db_backend_t *bdb_backend_open(
664 const char *filename,
665 memdb_tree_entry_t *root,
666 GHashTable *index)
667{
668 g_return_val_if_fail(filename != NULL, NULL);
669 g_return_val_if_fail(root != NULL, NULL);
670 g_return_val_if_fail(index != NULL, NULL);
671
672 db_backend_t *bdb = g_new0(db_backend_t, 1);
673 g_return_val_if_fail(bdb != NULL, NULL);
674
675 int rc;
676
677 sqlite3_initialize();
678
679 if (!(bdb->db = bdb_create(filename)))
680 goto fail;
681
49426115
TL
682 // tell the query planner that the prepared statement will be retained for a long time and
683 // probably reused many times
684 const unsigned int flags = SQLITE_PREPARE_PERSISTENT;
685
686 rc = sqlite3_prepare_v3(bdb->db, sql_insert_entry, -1, flags, &bdb->stmt_insert_entry, NULL);
fe000966
DM
687 if (rc != SQLITE_OK) {
688 cfs_critical("sqlite3_prepare 'sql_insert_entry' failed: %s\n",
689 sqlite3_errmsg(bdb->db));
690 goto fail;
691 }
49426115 692 rc = sqlite3_prepare_v3(bdb->db, sql_update_entry, -1, flags, &bdb->stmt_update_entry, NULL);
fe000966
DM
693 if (rc != SQLITE_OK) {
694 cfs_critical("sqlite3_prepare 'sql_update_entry' failed: %s\n",
695 sqlite3_errmsg(bdb->db));
696 goto fail;
697 }
49426115 698 rc = sqlite3_prepare_v3(bdb->db, sql_replace_entry, -1, flags, &bdb->stmt_replace_entry, NULL);
fe000966
DM
699 if (rc != SQLITE_OK) {
700 cfs_critical("sqlite3_prepare 'sql_replace_entry' failed: %s\n",
701 sqlite3_errmsg(bdb->db));
702 goto fail;
703 }
49426115 704 rc = sqlite3_prepare_v3(bdb->db, sql_delete_entry, -1, flags, &bdb->stmt_delete_entry, NULL);
fe000966
DM
705 if (rc != SQLITE_OK) {
706 cfs_critical("sqlite3_prepare 'sql_delete_entry' failed: %s\n",
707 sqlite3_errmsg(bdb->db));
708 goto fail;
709 }
49426115 710 rc = sqlite3_prepare_v3(bdb->db, sql_begin, -1, flags, &bdb->stmt_begin, NULL);
fe000966
DM
711 if (rc != SQLITE_OK) {
712 cfs_critical("sqlite3_prepare 'sql_begin' failed: %s\n",
713 sqlite3_errmsg(bdb->db));
714 goto fail;
715 }
49426115 716 rc = sqlite3_prepare_v3(bdb->db, sql_commit, -1, flags, &bdb->stmt_commit, NULL);
fe000966
DM
717 if (rc != SQLITE_OK) {
718 cfs_critical("sqlite3_prepare 'sql_commit' failed: %s\n",
719 sqlite3_errmsg(bdb->db));
720 goto fail;
721 }
49426115 722 rc = sqlite3_prepare_v3(bdb->db, sql_rollback, -1, flags, &bdb->stmt_rollback, NULL);
fe000966
DM
723 if (rc != SQLITE_OK) {
724 cfs_critical("sqlite3_prepare 'sql_rollback' failed: %s\n",
725 sqlite3_errmsg(bdb->db));
726 goto fail;
727 }
49426115 728 rc = sqlite3_prepare_v3(bdb->db, sql_load_all, -1, flags, &bdb->stmt_load_all, NULL);
fe000966
DM
729 if (rc != SQLITE_OK) {
730 cfs_critical("sqlite3_prepare 'sql_load_all' failed: %s\n",
731 sqlite3_errmsg(bdb->db));
732 goto fail;
733 }
734
735 if (!bdb_backend_load_index(bdb, root, index))
736 goto fail;
737
738 if (!root->version) {
739 root->version++;
740
741 guint32 mtime = time(NULL);
742
743 if (bdb_backend_write(bdb, 0, 0, root->version, 0, mtime,
744 0, DT_REG, NULL, NULL, 0) != SQLITE_OK)
745 goto fail;
746 }
747
748 return bdb;
749
750fail:
fe000966 751 bdb_backend_close(bdb);
42f0a0a5 752
fe000966
DM
753 return NULL;
754}