dt_database_t *dt_database_init(char *alternative) { /* migrate default database location to new default */ _database_migrate_to_xdg_structure(); /* delete old mipmaps files */ _database_delete_mipmaps_files(); /* lets construct the db filename */ gchar * dbname = NULL; gchar dbfilename[DT_MAX_PATH_LEN] = {0}; gchar datadir[DT_MAX_PATH_LEN] = {0}; dt_loc_get_user_config_dir(datadir, DT_MAX_PATH_LEN); if ( alternative == NULL ) { dbname = dt_conf_get_string ("database"); if(!dbname) snprintf(dbfilename, DT_MAX_PATH_LEN, "%s/library.db", datadir); else if(dbname[0] != '/') snprintf(dbfilename, DT_MAX_PATH_LEN, "%s/%s", datadir, dbname); else snprintf(dbfilename, DT_MAX_PATH_LEN, "%s", dbname); } else { snprintf(dbfilename, DT_MAX_PATH_LEN, "%s", alternative); GFile *galternative = g_file_new_for_path(alternative); dbname = g_file_get_basename (galternative); g_object_unref(galternative); } /* create database */ dt_database_t *db = (dt_database_t *)g_malloc(sizeof(dt_database_t)); memset(db,0,sizeof(dt_database_t)); db->dbfilename = g_strdup(dbfilename); db->is_new_database = FALSE; db->lock_acquired = FALSE; /* having more than one instance of darktable using the same database is a bad idea */ /* try to get a lock for the database */ #ifdef __WIN32__ db->lock_acquired = TRUE; #else mode_t old_mode; int fd, lock_tries = 0; if(!strcmp(dbfilename, ":memory:")) { db->lock_acquired = TRUE; } else { db->lockfile = g_strconcat(dbfilename, ".lock", NULL); lock_again: lock_tries++; old_mode = umask(0); fd = open(db->lockfile, O_RDWR | O_CREAT | O_EXCL, 0666); umask(old_mode); if(fd >= 0) // the lockfile was successfully created - write our PID into it { gchar *pid = g_strdup_printf("%d", getpid()); if(write(fd, pid, strlen(pid)+1) > -1) db->lock_acquired = TRUE; close(fd); } else // the lockfile already exists - see if it's a stale one left over from a crashed instance { char buf[64]; memset(buf, 0, sizeof(buf)); fd = open(db->lockfile, O_RDWR | O_CREAT, 0666); if(fd >= 0) { if(read(fd, buf, sizeof(buf) - 1) > -1) { int other_pid = atoi(buf); if((kill(other_pid, 0) == -1) && errno == ESRCH) { // the other process seems to no longer exist. unlink the .lock file and try again unlink(db->lockfile); if(lock_tries < 5) goto lock_again; } } close(fd); } } } #endif if(!db->lock_acquired) { fprintf(stderr, "[init] database is locked, probably another process is already using it\n"); g_free(dbname); return db; } /* test if databasefile is available */ if(!g_file_test(dbfilename, G_FILE_TEST_IS_REGULAR)) db->is_new_database = TRUE; /* opening / creating database */ if(sqlite3_open(db->dbfilename, &db->handle)) { fprintf(stderr, "[init] could not find database "); if(dbname) fprintf(stderr, "`%s'!\n", dbname); else fprintf(stderr, "\n"); fprintf(stderr, "[init] maybe your %s/darktablerc is corrupt?\n",datadir); dt_loc_get_datadir(dbfilename, 512); fprintf(stderr, "[init] try `cp %s/darktablerc %s/darktablerc'\n", dbfilename,datadir); sqlite3_close(db->handle); g_free(dbname); g_free(db->lockfile); g_free(db); return NULL; } /* attach a memory database to db connection for use with temporary tables used during instance life time, which is discarded on exit. */ sqlite3_exec(db->handle, "attach database ':memory:' as memory",NULL,NULL,NULL); sqlite3_exec(db->handle, "PRAGMA synchronous = OFF", NULL, NULL, NULL); sqlite3_exec(db->handle, "PRAGMA journal_mode = MEMORY", NULL, NULL, NULL); sqlite3_exec(db->handle, "PRAGMA page_size = 32768", NULL, NULL, NULL); /* now that we got a functional database that is locked for us we can make sure that the schema is set up */ // does the db contain the new 'db_info' table? sqlite3_stmt *stmt; int rc = sqlite3_prepare_v2(db->handle, "select value from db_info where key = 'version'", -1, &stmt, NULL); if(rc == SQLITE_OK && sqlite3_step(stmt) == SQLITE_ROW) { // compare the version of the db with what is current for this executable const int db_version = sqlite3_column_int(stmt, 0); sqlite3_finalize(stmt); if(db_version < CURRENT_DATABASE_VERSION) { // older: upgrade if(!_upgrade_schema(db, db_version)) { // we couldn't upgrade the db for some reason. bail out. fprintf(stderr, "[init] database `%s' couldn't be upgraded from version %d to %d. aborting\n", dbname, db_version, CURRENT_DATABASE_VERSION); dt_database_destroy(db); db = NULL; goto error; } } else if(db_version > CURRENT_DATABASE_VERSION) { // newer: bail out. it's better than what we did before: delete everything fprintf(stderr, "[init] database version of `%s' is too new for this build of darktable. aborting\n", dbname); dt_database_destroy(db); db = NULL; goto error; } // else: the current version, do nothing } else { // does it contain the legacy 'settings' table? sqlite3_finalize(stmt); rc = sqlite3_prepare_v2(db->handle, "select settings from settings", -1, &stmt, NULL); if(rc == SQLITE_OK && sqlite3_step(stmt) == SQLITE_ROW) { // the old blob had the version as an int in the first place const void *set = sqlite3_column_blob(stmt, 0); const int db_version = *(int*)set; sqlite3_finalize(stmt); if(!_migrate_schema(db, db_version)) // bring the legacy layout to the first one known to our upgrade path ... { // we couldn't migrate the db for some reason. bail out. fprintf(stderr, "[init] database `%s' couldn't be migrated from the legacy version %d. aborting\n", dbname, db_version); dt_database_destroy(db); db = NULL; goto error; } if(!_upgrade_schema(db, 1)) // ... and upgrade it { // we couldn't upgrade the db for some reason. bail out. fprintf(stderr, "[init] database `%s' couldn't be upgraded from version 1 to %d. aborting\n", dbname, CURRENT_DATABASE_VERSION); dt_database_destroy(db); db = NULL; goto error; } } else { sqlite3_finalize(stmt); _create_schema(db); // a brand new db it seems } } // create the in-memory tables // temporary stuff for some ops, need this for some reason with newer sqlite3: DT_DEBUG_SQLITE3_EXEC(db->handle, "CREATE TABLE memory.color_labels_temp (imgid INTEGER PRIMARY KEY)", NULL, NULL, NULL); DT_DEBUG_SQLITE3_EXEC(db->handle, "CREATE TABLE memory.collected_images (rowid INTEGER PRIMARY KEY AUTOINCREMENT, imgid INTEGER)", NULL, NULL, NULL); DT_DEBUG_SQLITE3_EXEC(db->handle, "CREATE TABLE memory.tmp_selection (imgid INTEGER)", NULL, NULL, NULL); DT_DEBUG_SQLITE3_EXEC(db->handle, "CREATE TABLE memory.tagq (tmpid INTEGER PRIMARY KEY, id INTEGER)", NULL, NULL, NULL); DT_DEBUG_SQLITE3_EXEC(db->handle, "CREATE TABLE memory.taglist " "(tmpid INTEGER PRIMARY KEY, id INTEGER UNIQUE ON CONFLICT REPLACE, " "count INTEGER)", NULL, NULL, NULL); DT_DEBUG_SQLITE3_EXEC(db->handle, "CREATE TABLE memory.history (imgid INTEGER, num INTEGER, module INTEGER, " "operation VARCHAR(256) UNIQUE ON CONFLICT REPLACE, op_params BLOB, enabled INTEGER, " "blendop_params BLOB, blendop_version INTEGER, multi_priority INTEGER, multi_name VARCHAR(256))", NULL, NULL, NULL); DT_DEBUG_SQLITE3_EXEC(db->handle, "CREATE TABLE MEMORY.style_items (styleid INTEGER, num INTEGER, module INTEGER, " "operation VARCHAR(256), op_params BLOB, enabled INTEGER, " "blendop_params BLOB, blendop_version INTEGER, multi_priority INTEGER, multi_name VARCHAR(256))", NULL, NULL, NULL); // create a table legacy_presets with all the presets from pre-auto-apply-cleanup darktable. dt_legacy_presets_create(db); // drop table settings -- we don't want old versions of dt to drop our tables sqlite3_exec(db->handle, "drop table settings", NULL, NULL, NULL); error: g_free(dbname); return db; }
dt_database_t *dt_database_init(char *alternative) { /* migrate default database location to new default */ _database_migrate_to_xdg_structure(); /* delete old mipmaps files */ _database_delete_mipmaps_files(); /* lets construct the db filename */ gchar * dbname = NULL; gchar dbfilename[1024] = {0}; gchar datadir[1024] = {0}; dt_loc_get_user_config_dir(datadir, 1024); if ( alternative == NULL ) { dbname = dt_conf_get_string ("database"); if(!dbname) snprintf(dbfilename, 1024, "%s/library.db", datadir); else if(dbname[0] != '/') snprintf(dbfilename, 1024, "%s/%s", datadir, dbname); else snprintf(dbfilename, 1024, "%s", dbname); } else { snprintf(dbfilename, 1024, "%s", alternative); dbname = g_file_get_basename (g_file_new_for_path(alternative)); } /* create database */ dt_database_t *db = (dt_database_t *)g_malloc(sizeof(dt_database_t)); memset(db,0,sizeof(dt_database_t)); db->dbfilename = g_strdup(dbfilename); db->is_new_database = FALSE; /* test if databasefile is available */ if(!g_file_test(dbfilename, G_FILE_TEST_IS_REGULAR)) db->is_new_database = TRUE; /* opening / creating database */ if(sqlite3_open(db->dbfilename, &db->handle)) { fprintf(stderr, "[init] could not find database "); if(dbname) fprintf(stderr, "`%s'!\n", dbname); else fprintf(stderr, "\n"); fprintf(stderr, "[init] maybe your %s/darktablerc is corrupt?\n",datadir); dt_loc_get_datadir(dbfilename, 512); fprintf(stderr, "[init] try `cp %s/darktablerc %s/darktablerc'\n", dbfilename,datadir); g_free(dbname); g_free(db); return NULL; } /* attach a memory database to db connection for use with temporary tables used during instance life time, which is discarded on exit. */ sqlite3_exec(db->handle, "attach database ':memory:' as memory",NULL,NULL,NULL); sqlite3_exec(db->handle, "PRAGMA synchronous = OFF", NULL, NULL, NULL); sqlite3_exec(db->handle, "PRAGMA journal_mode = MEMORY", NULL, NULL, NULL); sqlite3_exec(db->handle, "PRAGMA page_size = 32768", NULL, NULL, NULL); g_free(dbname); return db; }