00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031 #include "config.h"
00032
00033 #include <errno.h>
00034 #include <glib.h>
00035 #include <limits.h>
00036 #include <libgen.h>
00037 #include <sqlite3.h>
00038 #include <stdlib.h>
00039 #include <string.h>
00040 #include <sys/types.h>
00041 #include <sys/stat.h>
00042 #include <sys/wait.h>
00043 #include <unistd.h>
00044
00045 #include "ermetadb_log.h"
00046 #include "ermetadb.h"
00047 #include "ermetadb_error.h"
00048 #include "ermetadb_private.h"
00049 #include "sqlite3_wrapper.h"
00050
00051
00052
00053
00054
00055 #define DATABASE_VERSION_MAJOR 0
00056 #define DATABASE_VERSION_MINOR 6
00057
00058 #define MAX_COLUMNS 17
00059 static const struct
00060 {
00061 const char *table_name;
00062 const char *column_names[MAX_COLUMNS+1];
00063 } global_database_tables[]
00064 =
00065 {
00066
00067 {
00068 "database_properties",
00069 {
00070 "version_major",
00071 "version_minor",
00072 NULL
00073 }
00074 },
00075 {
00076 "file_metadata",
00077 {
00078 "file_id",
00079 "filename",
00080 "directory_path",
00081 "sort_priority",
00082 "is_directory",
00083 "is_template",
00084 "file_type",
00085 "file_size",
00086 "file_time_modified",
00087 "file_time_lastread",
00088 "file_time_added",
00089 "title",
00090 "author",
00091 "number_of_pages",
00092 "current_page",
00093 "tag",
00094 NULL
00095 }
00096 },
00097 {
00098 "thumbnails",
00099 {
00100 "file_id",
00101 "thumb_data_mini",
00102 "thumb_data_small",
00103 "thumb_data_medium",
00104 "thumb_data_large",
00105 NULL
00106 }
00107 },
00108 {
00109 NULL,
00110 {}
00111 }
00112 };
00113
00114
00115
00116
00117
00118
00119 static gboolean update_version_number(sqlite3 *db, int major, int minor)
00120 {
00121 char query[255];
00122 sprintf(query, "UPDATE database_properties SET version_major=%d, version_minor=%d;",
00123 major, minor);
00124 int rc = sql3_execute_query(db, query, NULL, NULL);
00125 if (rc != ER_OK) {
00126 ERRORPRINTF("cannot update version number to %d.%d", major, minor);
00127 }
00128 return (rc == ER_OK);
00129 }
00130
00131
00132 static void convert_database_01_06(erMetadb thiz, int *minor)
00133 {
00134 WARNPRINTF("Trying to convert db version 0.1 to 0.6");
00135
00136
00137
00138
00139
00140 const char* sql = "CREATE TABLE temp (\n"
00141 " file_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n"
00142 " filename VARCHAR(250) NOT NULL\n"
00143 ");";
00144 if (sql3_execute_query(thiz->database, sql, NULL, NULL) != ER_OK)
00145 {
00146 ERRORPRINTF("cannot create temp table to convert version 0.1->0.6, dir %s", thiz->directory);
00147 return;
00148 }
00149
00150 sql = "INSERT INTO temp SELECT file_id, filename FROM file_metadata;\n"
00151 "DROP TABLE file_metadata;";
00152 if (sql3_execute_query(thiz->database, sql, NULL, NULL) != ER_OK)
00153 {
00154 ERRORPRINTF("cannot fill temp to convert version 0.1->0.6, dir %s", thiz->directory);
00155 return;
00156 }
00157
00158 sql = "ALTER TABLE temp RENAME TO file_metadata;\n"
00159 "DROP TABLE thumbnails;";
00160 if (sql3_execute_query(thiz->database, sql, NULL, NULL) != ER_OK)
00161 {
00162 ERRORPRINTF("cannot convert version 0.1->0.6, dir %s", thiz->directory);
00163 return;
00164 }
00165
00166
00167 sql = "SELECT annotation_id, start_anchor from annotations;";
00168 metadata_table *result = NULL;
00169 if (sql3_execute_query(thiz->database, sql, NULL, &result) != ER_OK)
00170 {
00171 ERRORPRINTF("cannot get annotations to convert version 0.1->0.6, dir %s", thiz->directory);
00172 return;
00173 }
00174
00175 static const char* begin = "pdf:/page:";
00176 unsigned int begin_len = strlen(begin);
00177 static const char* new_anchor = "adobe:|type:0|pf_page";
00178
00179 if (result) {
00180 const metadata_cell *cell = (const metadata_cell*) result->cell_data->data;
00181 unsigned int row;
00182 for (row=0; row<result->n_rows; row++) {
00183 int id = cell->value.v_int64;
00184 cell++;
00185 const char* anchor = cell->value.v_text->str;
00186 cell++;
00187 if (strncmp(begin, anchor, begin_len) == 0 && strlen(anchor) > begin_len)
00188 {
00189 int page = atoi(anchor + begin_len);
00190 if (page > 0) page--;
00191 char query[256];
00192 sprintf(query, "UPDATE annotations set start_anchor='%s:%d.0000' WHERE annotation_id=%d;",
00193 new_anchor, page, id);
00194 if (sql3_execute_query(thiz->database, query, NULL, NULL) != ER_OK) {
00195 ERRORPRINTF("cannot update annotations");
00196 }
00197 }
00198
00199 }
00200 metadata_table_free(result);
00201 }
00202
00203
00204 sql = "SELECT file_id, key, value FROM application_data;";
00205 result = NULL;
00206 if (sql3_execute_query(thiz->database, sql, NULL, &result) != ER_OK)
00207 {
00208 ERRORPRINTF("cannot get application data to convert version 0.1->0.6, dir %s", thiz->directory);
00209 return;
00210 }
00211
00212
00213 if (result) {
00214 const metadata_cell *cell = (const metadata_cell*) result->cell_data->data;
00215 unsigned int row;
00216 for (row=0; row<result->n_rows; row++) {
00217 int id = cell->value.v_int64;
00218 cell++;
00219 const char* key = cell->value.v_text->str;
00220 cell++;
00221 const char* value = cell->value.v_text->str;
00222 cell++;
00223 static const char* lastread_key = "uds_last_read_location";
00224 if (strcmp(lastread_key, key) == 0) {
00225 if (strncmp(begin, value, begin_len) == 0 && strlen(value) > begin_len)
00226 {
00227 int page = atoi(value + begin_len);
00228 if (page > 0) page--;
00229 char query[256];
00230 sprintf(query, "UPDATE application_data set value='%s:%d.0000' WHERE file_id=%d AND key='%s';",
00231 new_anchor, page, id, lastread_key);
00232 if (sql3_execute_query(thiz->database, query, NULL, NULL) != ER_OK) {
00233 ERRORPRINTF("cannot update application_data");
00234 }
00235 }
00236 }
00237 }
00238 metadata_table_free(result);
00239 }
00240
00241 if (update_version_number(thiz->database, 0, 6)) {
00242 LOGPRINTF("version updated to 0.6");
00243 *minor = 6;
00244 }
00245 }
00246
00247
00248 static int add_file_ids(sqlite3* db, GSList **list, const char* table_name)
00249 {
00250 char sql[255];
00251 sprintf(sql, "SELECT DISTINCT file_id FROM %s;", table_name);
00252 metadata_table *result = NULL;
00253 int rc = sql3_execute_query(db, sql, NULL, &result);
00254 if (rc != ER_OK) {
00255 ERRORPRINTF("cannot read file_id's for %s", table_name);
00256 return rc;
00257 }
00258 int n_rows = metadata_table_n_rows(result);
00259 int row;
00260 for (row=0; row<n_rows; row++) {
00261 const metadata_cell *cell = metadata_table_get_cell(result, row);
00262 if (cell->type != METADATA_INT64) {
00263 ERRORPRINTF("invalid data type for file_id [%d]", cell->type);
00264 } else {
00265 int id = cell->value.v_int64;
00266 GSList *found = g_slist_find(*list, (gpointer)id);
00267 if (found == NULL) {
00268 *list = g_slist_append(*list, (gpointer)id);
00269 }
00270 }
00271 }
00272 metadata_table_free(result);
00273 return ER_OK;
00274 }
00275
00276
00277
00278 static const char* get_filename_from_results(const metadata_table *table, int id)
00279 {
00280 g_assert(table->n_columns == 2);
00281 if (table->n_rows == 0) return NULL;
00282
00283 const metadata_cell *cell = (const metadata_cell*) table->cell_data->data;
00284 unsigned int row;
00285 for (row=0; row<table->n_rows; row++) {
00286 g_assert_cmpint(cell->type, ==, METADATA_INT64);
00287
00288 int id2 = cell->value.v_int64;
00289 if (id == id2) {
00290 cell++;
00291 g_assert_cmpint(cell->type, ==, METADATA_TEXT);
00292 return cell->value.v_text->str;
00293 }
00294 cell += table->n_columns;
00295 }
00296 return NULL;
00297 }
00298
00299
00300
00301
00302
00303 static void convert_database_05_06(erMetadb thiz, int *minor)
00304 {
00305 WARNPRINTF("Trying to convert %s db version 0.5 to 0.6", (thiz->is_global ? "global" : "local"));
00306 GSList *id_list = NULL;
00307
00308 if (thiz->is_global)
00309 {
00310
00311 goto ok;
00312 }
00313
00314
00315 const char *sql = "CREATE TABLE file_metadata (file_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, filename VARCHAR(250) NOT NULL);";
00316 if (sql3_execute_query(thiz->database, sql, NULL, NULL) != ER_OK)
00317 {
00318 ERRORPRINTF("Failed to create table 'file_metadata'");
00319 return;
00320 }
00321
00322
00323 int rc1 = add_file_ids(thiz->database, &id_list, "application_data");
00324 int rc2 = add_file_ids(thiz->database, &id_list, "annotations");
00325 if (rc1 != ER_OK || rc2 != ER_OK) goto err;
00326
00327 if (id_list == NULL) goto ok;
00328
00329
00330
00331 const char* global_path = "/media/mmcblk0p1";
00332 erMetadb global_db = ermetadb_global_open(global_path, TRUE);
00333 if (global_db == NULL) {
00334 ERRORPRINTF("cannot open global db for lookups");
00335 goto err;
00336 }
00337
00338
00339 char query[512];
00340 sprintf(query, "SELECT file_id, filename FROM file_metadata WHERE directory_path = '%s';", thiz->directory);
00341 metadata_table *global_results = NULL;
00342 int rc = sql3_execute_query(global_db->database, query, NULL, &global_results);
00343 ermetadb_close(global_db);
00344 if (rc != ER_OK || global_results == NULL) {
00345 ERRORPRINTF("cannot get file_id's from global");
00346 goto err;
00347 }
00348
00349
00350 GSList *iter = id_list;
00351 while (iter) {
00352 int id = (int)iter->data;
00353 const char* filename = get_filename_from_results(global_results, id);
00354
00355 if (filename == NULL) {
00356 WARNPRINTF("cannot find filename for id %d, removing data", id);
00357 local_delete_all_data_for_id(thiz, id);
00358 goto next;
00359 }
00360
00361
00362 char *sql2 = sqlite3_mprintf("INSERT INTO file_metadata (filename, file_id) VALUES (%Q,%d);", filename, id);
00363 rc = sql3_execute_query(thiz->database, sql2, NULL, NULL);
00364 sqlite3_free(sql2);
00365 if (rc != ER_OK) {
00366 ERRORPRINTF("cannot insert <'%s', %d> into database", filename, id);
00367 }
00368 next:
00369 iter = g_slist_next(iter);
00370 }
00371 metadata_table_free(global_results);
00372 ok:
00373
00374 if (update_version_number(thiz->database, 0, 6)) {
00375 LOGPRINTF("version updated to 0.6");
00376 *minor = 6;
00377 }
00378 err:
00379 if (id_list) g_slist_free(id_list);
00380 }
00381
00382
00383 static int convert_database(erMetadb thiz, const int v_major, const int v_minor)
00384 {
00385 g_assert(thiz->database);
00386
00387 int ret = ER_OK;
00388 int major = v_major;
00389 int minor = v_minor;
00390
00391 TRACE("entry: db [%p] version [%d].[%d] is_global=%d", db, v_major, v_minor, is_global);
00392
00393 if (major == 0 && minor == 1)
00394 {
00395 convert_database_01_06(thiz, &minor);
00396 }
00397
00398 if (major == 0 && minor == 5)
00399 {
00400 convert_database_05_06(thiz, &minor);
00401 }
00402
00403
00404 if ( major != DATABASE_VERSION_MAJOR
00405 || minor != DATABASE_VERSION_MINOR )
00406 {
00407 ERRORPRINTF( "cannot convert database version [%d.%d] to %d.%d",
00408 v_major,
00409 v_minor,
00410 DATABASE_VERSION_MAJOR,
00411 DATABASE_VERSION_MINOR );
00412 ret = ERMETADB_DATABASE_STRUCTURE;
00413 }
00414
00415 return ret;
00416 }
00417
00418
00419
00420 static int check_database_version(erMetadb thiz)
00421 {
00422 TRACE("entry: db [%p] global=%d", thiz->database, thiz->is_global);
00423 g_assert(thiz->database);
00424
00425 int ret = ER_OK;
00426
00427 const char* sql = "SELECT version_major, version_minor FROM database_properties;";
00428 metadata_table *result = NULL;
00429 int rc = sql3_execute_query(thiz->database, sql, NULL, &result);
00430 if (rc != ER_OK)
00431 {
00432 ERRORPRINTF("cannot get version from database");
00433 ret = rc;
00434 goto out;
00435 }
00436
00437
00438 const metadata_cell *cell = metadata_table_get_cell(result, 0);
00439 if (cell->type != METADATA_INT64)
00440 {
00441 ERRORPRINTF("invalid data type database version major [%d]", cell->type);
00442 ret = ERMETADB_DATABASE_STRUCTURE;
00443 goto out;
00444 }
00445 int v_major = cell->value.v_int64;
00446
00447 cell = metadata_table_get_cell(result, 1);
00448 if (cell->type != METADATA_INT64)
00449 {
00450 ERRORPRINTF("invalid data type database version minor [%d]", cell->type);
00451 ret = ERMETADB_DATABASE_STRUCTURE;
00452 goto out;
00453 }
00454 int v_minor = cell->value.v_int64;
00455
00456
00457 if (v_major > DATABASE_VERSION_MAJOR)
00458 {
00459 ERRORPRINTF("incompatible database version [%d.%d]", v_major, v_minor);
00460 ret = ERMETADB_DATABASE_STRUCTURE;
00461 goto out;
00462 }
00463 if (v_major != DATABASE_VERSION_MAJOR || v_minor != DATABASE_VERSION_MINOR )
00464 {
00465 ret = convert_database(thiz, v_major, v_minor);
00466 }
00467
00468 out:
00469 metadata_table_free(result);
00470 return ret;
00471 }
00472
00473
00474
00475
00476
00477 static int check_global_database_tables(erMetadb thiz)
00478 {
00479 TRACE("entry: thiz [%p]", thiz);
00480 g_assert(thiz->database);
00481 g_assert(thiz->is_global);
00482
00483
00484 metadata_table *result = NULL;
00485 const char *sql = "SELECT name FROM sqlite_master WHERE type='table';";
00486 int ret = sql3_execute_query(thiz->database, sql, NULL, &result);
00487 if (ret != ER_OK) goto out;
00488
00489 int n_tables = metadata_table_n_rows(result);
00490 int col = metadata_table_find_column(result, "name");
00491 if (col < 0)
00492 {
00493 ERRORPRINTF("No 'names' column in result");
00494 ret = ERMETADB_FAIL;
00495 goto out;
00496 }
00497
00498
00499 int i;
00500 for ( i = 0 ; global_database_tables[i].table_name ; i++)
00501 {
00502 const char *name = global_database_tables[i].table_name;
00503
00504 gboolean found = FALSE;
00505 int row;
00506 for (row = 0 ; row < n_tables && !found ; row++)
00507 {
00508 GString *string = metadata_table_get_string(result, metadata_table_cell_index(result, row, col));
00509 if (string)
00510 {
00511 if ( string->str && strcmp(string->str, name) == 0 )
00512 {
00513 found = TRUE;
00514 }
00515 g_string_free(string, TRUE);
00516 }
00517 }
00518 if (!found)
00519 {
00520 ERRORPRINTF("table [%s] not present", name);
00521 ret = ERMETADB_DATABASE_STRUCTURE;
00522 }
00523 }
00524 out:
00525 metadata_table_free(result);
00526 return ret;
00527 }
00528
00529
00530
00531
00532
00533 static int check_global_database_columns(erMetadb thiz)
00534 {
00535 TRACE("entry: thiz [%p]", thiz);
00536 g_assert(thiz->database);
00537 g_assert(thiz->is_global);
00538
00539
00540 int ret = ER_OK;
00541 int tbl;
00542 for ( tbl = 0 ; global_database_tables[tbl].table_name && ret == ER_OK ; tbl++ )
00543 {
00544 const char* table_name = global_database_tables[tbl].table_name;
00545 int n_columns = 0;
00546 int col;
00547 metadata_table *result = NULL;
00548
00549
00550 GString *sql = g_string_new("");
00551 g_string_printf( sql, "PRAGMA table_info(%s);", table_name );
00552 ret = sql3_execute_query(thiz->database, sql->str, NULL, &result);
00553 g_string_free(sql, TRUE);
00554 if (ret == ER_OK)
00555 {
00556 n_columns = metadata_table_n_rows(result);
00557 col = metadata_table_find_column(result, "name");
00558 if (col < 0)
00559 {
00560 ERRORPRINTF("No 'names' column in result");
00561 ret = ERMETADB_FAIL;
00562 }
00563 }
00564
00565
00566 if (ret == ER_OK)
00567 {
00568
00569 int i;
00570 for ( i = 0 ; global_database_tables[tbl].column_names[i] ; i++ )
00571 {
00572 const char* name = global_database_tables[tbl].column_names[i];
00573
00574 gboolean found = FALSE;
00575 int row;
00576 for ( row = 0 ; row < n_columns && !found ; row++)
00577 {
00578 GString *string = metadata_table_get_string(result, metadata_table_cell_index(result, row, col));
00579 if (string)
00580 {
00581 if ( string->str && strcmp(string->str, name) == 0 )
00582 {
00583 found = TRUE;
00584 }
00585 g_string_free(string, TRUE);
00586 }
00587 }
00588 if (!found)
00589 {
00590 ERRORPRINTF("column [%s] not present in table [%s]", name, global_database_tables[tbl].table_name);
00591 ret = ERMETADB_DATABASE_STRUCTURE;
00592 }
00593 }
00594 }
00595 metadata_table_free(result);
00596 }
00597
00598 return ret;
00599 }
00600
00601
00602
00603
00604
00605
00606 static int check_global_database_consistency(erMetadb thiz)
00607 {
00608 TRACE("entry: thiz [%p]", thiz);
00609
00610 int rc = check_database_version(thiz);
00611 if (rc != ER_OK)
00612 {
00613 ERRORPRINTF("checking database version failed with code [%d]", rc);
00614 return rc;
00615 }
00616
00617 rc = check_global_database_tables(thiz);
00618 if (rc != ER_OK)
00619 {
00620 ERRORPRINTF("checking global tables failed with code [%d]", rc);
00621 return rc;
00622 }
00623
00624 rc = check_global_database_columns(thiz);
00625 if (rc != ER_OK)
00626 {
00627 ERRORPRINTF("checking database columns failed with code [%d]", rc);
00628 return rc;
00629 }
00630
00631 return ER_OK;
00632 }
00633
00634
00635 static int check_local_database_consistency(erMetadb thiz)
00636 {
00637 TRACE("entry: thiz [%p]", thiz);
00638
00639 int rc = check_database_version(thiz);
00640 if (rc != ER_OK)
00641 {
00642 ERRORPRINTF("checking database version failed with code [%d]", rc);
00643 return rc;
00644 }
00645
00646 return ER_OK;
00647 }
00648
00649
00650 static int open_sqlite_database(const gchar *filename, gboolean do_open_readonly, sqlite3 **database)
00651 {
00652 g_assert(filename);
00653 g_assert(database && *database == NULL);
00654
00655
00656 sqlite3 *db = NULL;
00657 int rc = sqlite3_open_v2( filename,
00658 &db,
00659 do_open_readonly ? SQLITE_OPEN_READONLY : SQLITE_OPEN_READWRITE,
00660 NULL );
00661 int ret = ER_OK;
00662 if (rc != SQLITE_OK)
00663 {
00664 ERRORPRINTF("sqlite open error [%d] [%s] on [%s]", rc, sqlite3_errmsg(db), filename);
00665 ret = ER_OPEN_ERROR;
00666 sqlite3_close(db);
00667 db = NULL;
00668 }
00669
00670
00671 if (ret == ER_OK)
00672 {
00673 rc = sqlite3_create_collation( db,
00674 SQLITE_COLLATION_STRCASECMP,
00675 SQLITE_UTF8,
00676 NULL,
00677 sql3_strcase_compare );
00678 if (rc != SQLITE_OK)
00679 {
00680 ERRORPRINTF("sqlite collation install error [%d] [%s]", rc, sqlite3_errmsg(db));
00681 }
00682 }
00683
00684 if (ret == ER_OK)
00685 {
00686 *database = db;
00687 }
00688 TRACE("leave: ret [%d]", ret);
00689 return ret;
00690 }
00691
00692
00693 static int check_database(erMetadb thiz)
00694 {
00695 if (thiz->is_global)
00696 return check_global_database_consistency(thiz);
00697 else
00698 return check_local_database_consistency(thiz);
00699 }
00700
00701
00702 static int open_sql_database(erMetadb thiz, const char* filename)
00703 {
00704
00705 struct stat statbuf;
00706 int rc = stat(thiz->directory, &statbuf);
00707 if (rc != 0 || !S_ISDIR(statbuf.st_mode))
00708 {
00709
00710 LOGPRINTF("folder doesn't exist or is not folder [%s]", thiz->directory);
00711 return ER_NOT_FOUND;
00712 }
00713
00714
00715 char fullname[PATH_MAX];
00716 sprintf(fullname, "%s/%s", thiz->directory, filename);
00717
00718
00719 struct stat statbuf2;
00720 rc = stat(fullname, &statbuf2);
00721 if (rc != 0 || !S_ISREG(statbuf2.st_mode))
00722 {
00723
00724 LOGPRINTF("database not present or is not regular file [%s]", fullname);
00725 return ER_NOT_FOUND;
00726 }
00727
00728 sqlite3* db = NULL;
00729 rc = open_sqlite_database(fullname, thiz->readonly, &db);
00730 if (rc != ER_OK)
00731 {
00732 ERRORPRINTF("open sqlite failed, rc [%d] filename [%s] readonly [%d]",
00733 rc, fullname, thiz->readonly);
00734 return rc;
00735 }
00736 thiz->database = db;
00737
00738 int ret = ER_OK;
00739 if (thiz->readonly)
00740 {
00741 ret = sql3_begin_transaction_readonly(thiz);
00742 }
00743 else
00744 {
00745 ret = sql3_begin_transaction(thiz);
00746 }
00747 ret = check_database(thiz);
00748 ret = sql3_commit_or_rollback(thiz, ret);
00749 if (ret != ER_OK) goto error;
00750
00751
00752
00753 const char *sql = "PRAGMA synchronous = OFF;";
00754 metadata_table *result = NULL;
00755 ret = sql3_execute_query(thiz->database, sql, NULL, &result);
00756 metadata_table_free(result);
00757
00758 if (ret != ER_OK) goto error;
00759 return ER_OK;
00760 error:
00761 sqlite3_close(thiz->database);
00762 thiz->database = NULL;
00763 return ret;
00764 }
00765
00766
00767 static int create_db(erMetadb thiz, const char* filename)
00768 {
00769 WARNPRINTF("(re-)generating database in %s", thiz->directory);
00770 int res = ER_OK;
00771 if (thiz->is_global) {
00772 res = ermetadb_global_create_database(thiz->directory);
00773 } else {
00774 res = ermetadb_local_create_database(thiz->directory, filename);
00775 }
00776 if (res != ER_OK) {
00777 ERRORPRINTF("error creating database %s/%s", thiz->directory, filename);
00778 }
00779 return res;
00780 }
00781
00782
00783 static erMetadb database_open (const gchar *folder,
00784 const gchar *filename,
00785 gboolean global,
00786 gboolean readonly,
00787 gboolean create)
00788 {
00789 g_assert(folder && folder[0]);
00790 g_assert(filename && filename[0]);
00791
00792 erMetadb thiz = g_new0(struct erMetadb_t, 1);
00793
00794 thiz->database = NULL;
00795 thiz->directory = g_strdup(folder);
00796 thiz->is_global = global;
00797 thiz->readonly = readonly;
00798 thiz->transaction_nesting = 0;
00799 thiz->is_transaction_readonly = FALSE;
00800 thiz->do_force_rollback = FALSE;
00801
00802 int res = open_sql_database(thiz, filename);
00803 if (res != ER_OK) {
00804
00805 if (global && !readonly) res = create_db(thiz, filename);
00806 if (!global && create) res = create_db(thiz, filename);
00807
00808 if (res == ER_OK) {
00809 res = open_sql_database(thiz, filename);
00810 }
00811 }
00812
00813 if (res != ER_OK) {
00814 g_assert(thiz->database == NULL);
00815 ermetadb_close(thiz);
00816 return NULL;
00817 }
00818 g_assert(thiz->database);
00819 return thiz;
00820
00821 }
00822
00823
00824 erMetadb ermetadb_global_open (const gchar* folder, gboolean readonly)
00825 {
00826 LOGPRINTF("folder [%s]", folder);
00827 return database_open(folder, ERMETADB_GLOBAL_DATABASE_FILE, TRUE, readonly, FALSE);
00828 }
00829
00830
00831 erMetadb ermetadb_local_open (const gchar* folder, gboolean create)
00832 {
00833 LOGPRINTF("folder [%s] create [%d]", folder, create);
00834 return database_open(folder, ERMETADB_LOCAL_DATABASE_FILE, FALSE, FALSE, create);
00835 }
00836
00837
00838 erMetadb ermetadb_local_open_custom (const gchar* folder, const gchar* filename)
00839 {
00840 LOGPRINTF("folder [%s] file [%s]", folder, filename);
00841 return database_open(folder, filename, FALSE, FALSE, FALSE);
00842 }
00843
00844
00845 const gchar* ermetadb_get_dir (erMetadb thiz)
00846 {
00847 return thiz->directory;
00848 }
00849
00850
00851 void ermetadb_close (erMetadb thiz)
00852 {
00853 LOGPRINTF("entry");
00854
00855 if (thiz == NULL) return;
00856
00857 if (thiz->database) {
00858
00859 if (thiz->transaction_nesting != 0)
00860 {
00861 thiz->transaction_nesting = 1;
00862 sql3_commit_or_rollback(thiz, ER_FAIL);
00863 }
00864
00865
00866 int rc = sqlite3_close(thiz->database);
00867 if (rc != SQLITE_OK)
00868 {
00869 ERRORPRINTF("sqlite close error [%d] [%s]", rc, sqlite3_errmsg(thiz->database));
00870 }
00871 thiz->database = NULL;
00872 }
00873 g_free(thiz->directory);
00874 g_free(thiz);
00875 }
00876
00877
00878
00879 static int create_database(const char *source_name, const char *target_dir, const char *target_name)
00880 {
00881
00882 gchar *file_source = g_strdup_printf("%s/%s", SYSCONFDIR, source_name);
00883 gchar *file_target = g_strdup_printf("%s/%s", target_dir, target_name);
00884 gchar *file_target_old = g_strdup_printf("%s.old", file_target);
00885
00886
00887 unlink(file_target_old);
00888
00889 rename(file_target, file_target_old);
00890
00891
00892 unsigned int argc = 0;
00893 const char *argv[10];
00894 argv[argc++] = "cp";
00895 argv[argc++] = "-f";
00896 argv[argc++] = file_source;
00897 argv[argc++] = file_target;
00898 argv[argc] = NULL;
00899 g_assert(argc < sizeof(argv)/sizeof(argv[0]));
00900 gint stat;
00901 GError *err = NULL;
00902 gboolean ok = g_spawn_sync( NULL,
00903 (char**)argv,
00904 NULL,
00905 G_SPAWN_SEARCH_PATH,
00906 NULL,
00907 NULL,
00908 NULL,
00909 NULL,
00910 &stat,
00911 &err );
00912 int ret = ER_OK;
00913 if ( !ok )
00914 {
00915
00916 ERRORPRINTF("g_spawn_sync error [%s] on copy [%s] [%s]", err->message, file_source, file_target);
00917 g_clear_error(&err);
00918 }
00919 else
00920 {
00921
00922 if ( !WIFEXITED(stat) )
00923 {
00924
00925 ERRORPRINTF("g_spawn_sync ok but child not exited, stat [0x%04X]", stat);
00926 ret = ER_FAIL;
00927 }
00928 else
00929 {
00930
00931 int rc = WEXITSTATUS(stat);
00932 if (rc != 0)
00933 {
00934
00935 ERRORPRINTF( "Failed to copy [%s] [%s], error [%d] [%s]",
00936 file_source,
00937 file_target,
00938 rc,
00939 strerror(rc) );
00940 switch (rc)
00941 {
00942 case EROFS:
00943 case EPERM:
00944 ret = ER_READONLY;
00945 break;
00946 default:
00947 ret = ER_FAIL;
00948 }
00949 }
00950 }
00951 }
00952 g_free(file_target_old);
00953 g_free(file_target );
00954 g_free(file_source );
00955 return ret;
00956 }
00957
00958
00959 int ermetadb_global_create_database(const gchar *folder)
00960 {
00961 LOGPRINTF("folder [%s]", folder);
00962
00963 g_assert(folder && folder[0]);
00964
00965 return create_database(ERMETADB_GLOBAL_DATABASE_FILE, folder, ERMETADB_GLOBAL_DATABASE_FILE);
00966 }
00967
00968
00969 int ermetadb_local_create_database(const gchar *folder, const gchar* filename)
00970 {
00971 LOGPRINTF("folder [%s] file [%s]", folder, filename);
00972
00973 g_assert(folder && folder[0]);
00974 g_assert(filename && filename[0]);
00975
00976 return create_database(ERMETADB_LOCAL_DATABASE_FILE, folder, filename);
00977 }
00978
00979
00980 const char* const * ermetadb_private_get_global_column_names(const char* table)
00981 {
00982 LOGPRINTF("entry: table [%s]", table);
00983 g_assert(table);
00984
00985 int i;
00986 for (i = 0 ; global_database_tables[i].table_name; i++)
00987 {
00988 if ( strcmp(global_database_tables[i].table_name, table) == 0 )
00989 {
00990 return global_database_tables[i].column_names;
00991 }
00992 }
00993
00994 return NULL;
00995 }
00996
00997
00998 int ermetadb_begin_transaction (erMetadb thiz)
00999 {
01000 LOGPRINTF("entry nesting [%d]", thiz->transaction_nesting);
01001
01002 g_assert(thiz);
01003 g_assert(thiz->database);
01004 g_return_val_if_fail( thiz->transaction_nesting == 0, ER_FORBIDDEN );
01005
01006 return sql3_begin_transaction(thiz);
01007 }
01008
01009
01010 int ermetadb_begin_transaction_readonly (erMetadb thiz)
01011 {
01012 LOGPRINTF("entry nesting [%d]", thiz->transaction_nesting);
01013
01014 g_assert(thiz);
01015 g_assert(thiz->database);
01016 g_return_val_if_fail( thiz->transaction_nesting == 0, ER_FORBIDDEN );
01017
01018 return sql3_begin_transaction_readonly(thiz);
01019 }
01020
01021
01022 int ermetadb_end_transaction (erMetadb thiz)
01023 {
01024 LOGPRINTF("entry nesting [%d]", thiz->transaction_nesting);
01025
01026 g_assert(thiz);
01027 g_assert(thiz->database);
01028
01029 if (thiz->transaction_nesting != 1)
01030 {
01031
01032 WARNPRINTF("entry with transaction_nesting [%d]", thiz->transaction_nesting);
01033 thiz->transaction_nesting = 1;
01034 thiz->do_force_rollback = TRUE;
01035 }
01036
01037 return sql3_commit_or_rollback(thiz, ER_OK);
01038 }
01039