Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add four new timestamps columns in library.db #4324

Merged
merged 1 commit into from Apr 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
37 changes: 33 additions & 4 deletions src/common/database.c
Expand Up @@ -44,7 +44,7 @@

// whenever _create_*_schema() gets changed you HAVE to bump this version and add an update path to
// _upgrade_*_schema_step()!
#define CURRENT_DATABASE_VERSION_LIBRARY 27
#define CURRENT_DATABASE_VERSION_LIBRARY 28
#define CURRENT_DATABASE_VERSION_DATA 6

typedef struct dt_database_t
Expand Down Expand Up @@ -1534,7 +1534,7 @@ static int _upgrade_library_schema_step(dt_database_t *db, int version)
{
TRY_EXEC("ALTER TABLE main.images ADD COLUMN exposure_bias REAL",
"[init] can't add `exposure_bias' column to images table in database\n");

new_version = 26;
}
else if(version == 26)
Expand Down Expand Up @@ -1568,6 +1568,33 @@ static int _upgrade_library_schema_step(dt_database_t *db, int version)
sqlite3_exec(db->handle, "COMMIT", NULL, NULL, NULL);
new_version = 27;
}
else if(version == 27)
{
sqlite3_exec(db->handle, "BEGIN TRANSACTION", NULL, NULL, NULL);

TRY_EXEC("ALTER TABLE main.images ADD COLUMN import_timestamp INTEGER DEFAULT -1",
"[init] can't add `import_timestamp' column to images table in database\n");
TRY_EXEC("ALTER TABLE main.images ADD COLUMN change_timestamp INTEGER DEFAULT -1",
"[init] can't add `change_timestamp' column to images table in database\n");
TRY_EXEC("ALTER TABLE main.images ADD COLUMN export_timestamp INTEGER DEFAULT -1",
"[init] can't add `export_timestamp' column to images table in database\n");
TRY_EXEC("ALTER TABLE main.images ADD COLUMN print_timestamp INTEGER DEFAULT -1",
"[init] can't add `print_timestamp' column to images table in database\n");

TRY_EXEC("UPDATE main.images SET import_timestamp = (SELECT access_timestamp "
"FROM main.film_rolls WHERE film_rolls.id = images.film_id)",
"[init] can't populate import_timestamp column from film_rolls.access_timestamp.\n");

TRY_EXEC("UPDATE main.images SET change_timestamp = images.write_timestamp "
"WHERE images.write_timestamp IS NOT NULL "
"AND images.id = (SELECT imgid FROM tagged_images "
"JOIN data.tags ON tags.id = tagged_images.tagid "
"WHERE data.tags.name = 'darktable|changed')",
"[init] can't populate change_timestamp column from images.write_timestamp.\n");

sqlite3_exec(db->handle, "COMMIT", NULL, NULL, NULL);
new_version = 28;
}
else
new_version = version; // should be the fallback so that calling code sees that we are in an infinite loop

Expand Down Expand Up @@ -1834,8 +1861,10 @@ static void _create_library_schema(dt_database_t *db)
"caption VARCHAR, description VARCHAR, license VARCHAR, sha1sum CHAR(40), "
"orientation INTEGER, histogram BLOB, lightmap BLOB, longitude REAL, "
"latitude REAL, altitude REAL, color_matrix BLOB, colorspace INTEGER, version INTEGER, "
"max_version INTEGER, write_timestamp INTEGER, history_end INTEGER, position INTEGER, aspect_ratio REAL,"
"exposure_bias REAL)",
"max_version INTEGER, write_timestamp INTEGER, history_end INTEGER, position INTEGER, aspect_ratio REAL, "
"exposure_bias REAL, "
"import_timestamp INTEGER DEFAULT -1, change_timestamp INTEGER DEFAULT -1, "
"export_timestamp INTEGER DEFAULT -1, print_timestamp INTEGER DEFAULT -1)",
NULL, NULL, NULL);
sqlite3_exec(db->handle, "CREATE INDEX main.images_group_id_index ON images (group_id)", NULL, NULL, NULL);
sqlite3_exec(db->handle, "CREATE INDEX main.images_film_id_index ON images (film_id)", NULL, NULL, NULL);
Expand Down
85 changes: 82 additions & 3 deletions src/common/exif.cc
Expand Up @@ -259,6 +259,7 @@ class Lock
}

static void _exif_import_tags(dt_image_t *img, Exiv2::XmpData::iterator &pos);
static gboolean read_xmp_timestamps(Exiv2::XmpData &xmpData, const int imgid);

// this array should contain all XmpBag and XmpSeq keys used by dt
const char *dt_xmp_keys[]
Expand All @@ -273,7 +274,9 @@ const char *dt_xmp_keys[]
"Xmp.darktable.masks_history", "Xmp.darktable.mask_num", "Xmp.darktable.mask_points",
"Xmp.darktable.mask_version", "Xmp.darktable.mask", "Xmp.darktable.mask_nb", "Xmp.darktable.mask_src",
"Xmp.darktable.history_basic_hash", "Xmp.darktable.history_auto_hash", "Xmp.darktable.history_current_hash",
"Xmp.acdsee.notes","Xmp.darktable.version_name",
"Xmp.darktable.import_timestamp", "Xmp.darktable.change_timestamp",
"Xmp.darktable.export_timestamp", "Xmp.darktable.print_timestamp",
"Xmp.acdsee.notes", "Xmp.darktable.version_name",
"Xmp.dc.creator", "Xmp.dc.publisher", "Xmp.dc.title", "Xmp.dc.description", "Xmp.dc.rights",
"Xmp.xmpMM.DerivedFrom" };

Expand Down Expand Up @@ -2702,7 +2705,7 @@ int dt_exif_xmp_read(dt_image_t *img, const char *filename, const int history_on
}

int32_t preset_applied = 0;

if((pos = xmpData.findKey(Exiv2::XmpKey("Xmp.darktable.auto_presets_applied"))) != xmpData.end())
{
preset_applied = pos->toLong();
Expand Down Expand Up @@ -3018,7 +3021,10 @@ int dt_exif_xmp_read(dt_image_t *img, const char *filename, const int history_on
goto end;
}

end:
end:

all_ok = read_xmp_timestamps(xmpData, img->id);

sqlite3_finalize(stmt);

// set or clear bit in image struct. ONLY set if the Xmp.darktable.auto_presets_applied was 1
Expand Down Expand Up @@ -3231,6 +3237,76 @@ static void dt_set_xmp_dt_history(Exiv2::XmpData &xmpData, const int imgid, int
xmpData["Xmp.darktable.history_end"] = history_end;
}

// add timestamps to XmpData.
static void set_xmp_timestamps(Exiv2::XmpData &xmpData, const int imgid)
{
sqlite3_stmt *stmt;

DT_DEBUG_SQLITE3_PREPARE_V2(
dt_database_get(darktable.db),
"SELECT id, import_timestamp, change_timestamp, export_timestamp, print_timestamp"
" FROM main.images"
" WHERE id = ?1",
-1, &stmt, NULL);
DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid);

if(sqlite3_step(stmt) == SQLITE_ROW)
{
xmpData["Xmp.darktable.import_timestamp"] = sqlite3_column_int(stmt, 1);
xmpData["Xmp.darktable.change_timestamp"] = sqlite3_column_int(stmt, 2);
xmpData["Xmp.darktable.export_timestamp"] = sqlite3_column_int(stmt, 3);
xmpData["Xmp.darktable.print_timestamp"] = sqlite3_column_int(stmt, 4);
}
else
{
xmpData["Xmp.darktable.import_timestamp"] = -1;
xmpData["Xmp.darktable.change_timestamp"] = -1;
xmpData["Xmp.darktable.export_timestamp"] = -1;
xmpData["Xmp.darktable.print_timestamp"] = -1;
}
sqlite3_finalize(stmt);
}

// read timestamps from XmpData
gboolean read_xmp_timestamps(Exiv2::XmpData &xmpData, const int imgid)
{
gboolean all_ok = TRUE;
sqlite3_stmt *stmt;
Exiv2::XmpData::iterator pos;

const char *timestamps[] = { "change_timestamp", "export_timestamp", "print_timestamp" };
// Do not read for import_ts. It must be updated at each import.

const int nb_timestamps = sizeof(timestamps) / sizeof(char *);
int i;
char xmpkey[1024];
char query[1024];
char values[1024];
char tmp[64];

for (i = 0 ; i < nb_timestamps ; i++)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If would be nice to create a single SQL statement instead of 3 (we already are stressing a lot the db), I'm thinking of something like:

UPDATE main.images SET %s WHERE id = %d

Where %s will be replaced by:

xxx_timestamp = val1, yyy_timestamp = val2

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

{
snprintf(xmpkey, sizeof(xmpkey), "Xmp.darktable.%s", timestamps[i]);
if((pos = xmpData.findKey(Exiv2::XmpKey(xmpkey))) != xmpData.end())
{
snprintf(tmp, sizeof(tmp), " %s = %ld,", timestamps[i], pos->toLong());
g_strlcat(values, tmp, sizeof(values));
}
}
values[strlen(values) - 1] = '\0'; /* remove last comma */
snprintf(query, sizeof(query), "UPDATE main.images SET %s WHERE id = %d", values, imgid);
DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), query, -1, &stmt, NULL);
if(sqlite3_step(stmt) != SQLITE_DONE)
{
fprintf(stderr, "[exif] error writing timestamps entry for image %d\n", imgid);
fprintf(stderr, "[exif] %s\n", sqlite3_errmsg(dt_database_get(darktable.db)));
all_ok = FALSE;
}
sqlite3_finalize(stmt);

return all_ok;
}

static void dt_remove_xmp_exif_geotag(Exiv2::XmpData &xmpData)
{
static const char *keys[] =
Expand Down Expand Up @@ -3380,6 +3456,9 @@ static void _exif_xmp_read_data(Exiv2::XmpData &xmpData, const int imgid)
// The original file name
if(filename) xmpData["Xmp.xmpMM.DerivedFrom"] = filename;

// timestamps
set_xmp_timestamps(xmpData, imgid);

// GPS data
dt_set_xmp_exif_geotag(xmpData, longitude, latitude, altitude);

Expand Down
5 changes: 5 additions & 0 deletions src/common/history.c
Expand Up @@ -121,6 +121,9 @@ void dt_history_delete_on_image_ext(int32_t imgid, gboolean undo)
dt_tag_detach_by_string("darktable|style%", imgid, FALSE, FALSE);
dt_tag_detach_by_string("darktable|changed", imgid, FALSE, FALSE);

/* unset change timestamp */
dt_image_cache_unset_change_timestamp(darktable.image_cache, imgid);

// signal that the mipmap need to be updated
dt_control_signal_raise(darktable.signals, DT_SIGNAL_DEVELOP_MIPMAP_UPDATED, imgid);

Expand Down Expand Up @@ -756,6 +759,8 @@ int dt_history_copy_and_paste_on_image(int32_t imgid, int32_t dest_imgid, gboole
guint tagid = 0;
dt_tag_new("darktable|changed", &tagid);
dt_tag_attach(tagid, dest_imgid, FALSE, FALSE);
/* set change_timestamp */
dt_image_cache_set_change_timestamp(darktable.image_cache, dest_imgid);

/* if current image in develop reload history */
if(dt_dev_is_current_image(darktable.develop, dest_imgid))
Expand Down
15 changes: 11 additions & 4 deletions src/common/image.c
Expand Up @@ -778,14 +778,14 @@ int32_t dt_image_duplicate_with_version(const int32_t imgid, const int32_t newve
" raw_auto_bright_threshold, raw_black, raw_maximum,"
" caption, description, license, sha1sum, orientation, histogram, lightmap,"
" longitude, latitude, altitude, color_matrix, colorspace, version, max_version, history_end,"
" position, aspect_ratio, exposure_bias)"
" position, aspect_ratio, exposure_bias, import_timestamp)"
" SELECT NULL, group_id, film_id, width, height, filename, maker, model, lens,"
" exposure, aperture, iso, focal_length, focus_distance, datetime_taken,"
" flags, width, height, crop, raw_parameters, raw_denoise_threshold,"
" raw_auto_bright_threshold, raw_black, raw_maximum,"
" caption, description, license, sha1sum, orientation, histogram, lightmap,"
" longitude, latitude, altitude, color_matrix, colorspace, NULL, NULL, 0, ?1,"
" aspect_ratio, exposure_bias"
" aspect_ratio, exposure_bias, import_timestamp"
" FROM main.images WHERE id = ?2",
-1, &stmt, NULL);
DT_DEBUG_SQLITE3_BIND_INT64(stmt, 1, new_image_position);
Expand Down Expand Up @@ -861,6 +861,9 @@ int32_t dt_image_duplicate_with_version(const int32_t imgid, const int32_t newve
dt_tag_detach_by_string("darktable|changed", newid, FALSE, FALSE);
dt_tag_detach_by_string("darktable|exported", newid, FALSE, FALSE);

/* unset change timestamp */
dt_image_cache_unset_change_timestamp(darktable.image_cache, imgid);

// set version of new entry and max_version of all involved duplicates (with same film_id and filename)
const int32_t version = (newversion != -1) ? newversion : max_version + 1;
max_version = (newversion != -1) ? MAX(max_version, newversion) : max_version + 1;
Expand Down Expand Up @@ -1174,14 +1177,15 @@ static uint32_t dt_image_import_internal(const int32_t film_id, const char *file
DT_DEBUG_SQLITE3_PREPARE_V2
(dt_database_get(darktable.db),
"INSERT INTO main.images (id, film_id, filename, caption, description, license, sha1sum, flags, version, "
" max_version, history_end, position)"
" SELECT NULL, ?1, ?2, '', '', '', '', ?3, 0, 0, 0, (IFNULL(MAX(position),0) & (4294967295 << 32)) + (1 << 32) "
" max_version, history_end, position, import_timestamp)"
" SELECT NULL, ?1, ?2, '', '', '', '', ?3, 0, 0, 0, (IFNULL(MAX(position),0) & (4294967295 << 32)) + (1 << 32), ?4 "
" FROM images",
-1, &stmt, NULL);

DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, film_id);
DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, imgfname, -1, SQLITE_TRANSIENT);
DT_DEBUG_SQLITE3_BIND_INT(stmt, 3, flags);
DT_DEBUG_SQLITE3_BIND_INT(stmt, 4, time(0));

rc = sqlite3_step(stmt);
if(rc != SQLITE_DONE) fprintf(stderr, "sqlite3 error %d\n", rc);
Expand Down Expand Up @@ -1371,6 +1375,9 @@ void dt_image_init(dt_image_t *img)
img->aspect_ratio = 0.f;
img->crop_x = img->crop_y = img->crop_width = img->crop_height = 0;
img->orientation = ORIENTATION_NULL;

img->import_timestamp = img->change_timestamp = img->export_timestamp = img->print_timestamp = -1;

img->legacy_flip.legacy = 0;
img->legacy_flip.user_flip = 0;

Expand Down
4 changes: 4 additions & 0 deletions src/common/image.h
Expand Up @@ -179,6 +179,10 @@ typedef struct dt_image_t

// used by library
int32_t num, flags, film_id, id, group_id, version;

//timestamps
time_t import_timestamp, change_timestamp, export_timestamp, print_timestamp;

dt_image_loader_t loader;

dt_iop_buffer_dsc_t buf_dsc;
Expand Down
68 changes: 64 additions & 4 deletions src/common/image_cache.c
Expand Up @@ -41,7 +41,9 @@ void dt_image_cache_allocate(void *data, dt_cache_entry_t *entry)
"SELECT id, group_id, film_id, width, height, filename, maker, model, lens, exposure, "
"aperture, iso, focal_length, datetime_taken, flags, crop, orientation, focus_distance, "
"raw_parameters, longitude, latitude, altitude, color_matrix, colorspace, version, raw_black, "
"raw_maximum, aspect_ratio, exposure_bias FROM main.images WHERE id = ?1",
"raw_maximum, aspect_ratio, exposure_bias, "
"import_timestamp, change_timestamp, export_timestamp, print_timestamp "
"FROM main.images WHERE id = ?1",
-1, &stmt, NULL);
DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, entry->key);
if(sqlite3_step(stmt) == SQLITE_ROW)
Expand Down Expand Up @@ -110,6 +112,10 @@ void dt_image_cache_allocate(void *data, dt_cache_entry_t *entry)
img->exif_exposure_bias = sqlite3_column_double(stmt, 28);
else
img->exif_exposure_bias = NAN;
img->import_timestamp = sqlite3_column_int(stmt, 29);
img->change_timestamp = sqlite3_column_int(stmt, 30);
img->export_timestamp = sqlite3_column_int(stmt, 31);
img->print_timestamp = sqlite3_column_int(stmt, 32);

// buffer size? colorspace?
if(img->flags & DT_IMAGE_LDR)
Expand Down Expand Up @@ -235,6 +241,7 @@ void dt_image_cache_write_release(dt_image_cache_t *cache, dt_image_t *img, dt_i
img->aspect_ratio = (float )img->height / (float )img->width;
}
if(img->id <= 0) return;

sqlite3_stmt *stmt;
DT_DEBUG_SQLITE3_PREPARE_V2(
dt_database_get(darktable.db),
Expand All @@ -243,7 +250,9 @@ void dt_image_cache_write_release(dt_image_cache_t *cache, dt_image_t *img, dt_i
"focus_distance = ?11, film_id = ?12, datetime_taken = ?13, flags = ?14, "
"crop = ?15, orientation = ?16, raw_parameters = ?17, group_id = ?18, longitude = ?19, "
"latitude = ?20, altitude = ?21, color_matrix = ?22, colorspace = ?23, raw_black = ?24, "
"raw_maximum = ?25, aspect_ratio = ROUND(?26,1), exposure_bias = ?28 WHERE id = ?27",
"raw_maximum = ?25, aspect_ratio = ROUND(?26,1), exposure_bias = ?27, "
"change_timestamp = ?28, change_timestamp = ?29, export_timestamp = ?30, print_timestamp = ?31 "
"WHERE id = ?32",
-1, &stmt, NULL);
DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, img->width);
DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, img->height);
Expand Down Expand Up @@ -272,8 +281,12 @@ void dt_image_cache_write_release(dt_image_cache_t *cache, dt_image_t *img, dt_i
DT_DEBUG_SQLITE3_BIND_INT(stmt, 24, img->raw_black_level);
DT_DEBUG_SQLITE3_BIND_INT(stmt, 25, img->raw_white_point);
DT_DEBUG_SQLITE3_BIND_DOUBLE(stmt, 26, img->aspect_ratio);
DT_DEBUG_SQLITE3_BIND_INT(stmt, 27, img->id);
DT_DEBUG_SQLITE3_BIND_DOUBLE(stmt, 28, img->exif_exposure_bias);
DT_DEBUG_SQLITE3_BIND_DOUBLE(stmt, 27, img->exif_exposure_bias);
DT_DEBUG_SQLITE3_BIND_INT(stmt, 28, img->import_timestamp);
DT_DEBUG_SQLITE3_BIND_INT(stmt, 29, img->change_timestamp);
DT_DEBUG_SQLITE3_BIND_INT(stmt, 30, img->export_timestamp);
DT_DEBUG_SQLITE3_BIND_INT(stmt, 31, img->print_timestamp);
DT_DEBUG_SQLITE3_BIND_INT(stmt, 32, img->id);
const int rc = sqlite3_step(stmt);
if(rc != SQLITE_DONE) fprintf(stderr, "[image_cache_write_release] sqlite3 error %d\n", rc);
sqlite3_finalize(stmt);
Expand All @@ -295,7 +308,54 @@ void dt_image_cache_remove(dt_image_cache_t *cache, const uint32_t imgid)
dt_cache_remove(&cache->cache, imgid);
}

/* set timestamps */
void dt_image_cache_set_change_timestamp(dt_image_cache_t *cache, const uint32_t imgid)
{
if(imgid <= 0) return;
dt_cache_entry_t *entry = dt_cache_get(&cache->cache, imgid, DT_IMAGE_CACHE_SAFE);
if(!entry) return;
ASAN_UNPOISON_MEMORY_REGION(entry->data, sizeof(dt_image_t));
dt_image_t *img = (dt_image_t *)entry->data;
img->cache_entry = entry;
img->change_timestamp = time(0);
dt_image_cache_write_release(cache, img, DT_IMAGE_CACHE_SAFE);
}

void dt_image_cache_unset_change_timestamp(dt_image_cache_t *cache, const uint32_t imgid)
{
if(imgid <= 0) return;
dt_cache_entry_t *entry = dt_cache_get(&cache->cache, imgid, DT_IMAGE_CACHE_SAFE);
if(!entry) return;
ASAN_UNPOISON_MEMORY_REGION(entry->data, sizeof(dt_image_t));
dt_image_t *img = (dt_image_t *)entry->data;
img->cache_entry = entry;
img->change_timestamp = -1;
dt_image_cache_write_release(cache, img, DT_IMAGE_CACHE_SAFE);
}

void dt_image_cache_set_export_timestamp(dt_image_cache_t *cache, const uint32_t imgid)
{
if(imgid <= 0) return;
dt_cache_entry_t *entry = dt_cache_get(&cache->cache, imgid, DT_IMAGE_CACHE_SAFE);
if(!entry) return;
ASAN_UNPOISON_MEMORY_REGION(entry->data, sizeof(dt_image_t));
dt_image_t *img = (dt_image_t *)entry->data;
img->cache_entry = entry;
img->export_timestamp = time(0);
dt_image_cache_write_release(cache, img, DT_IMAGE_CACHE_SAFE);
}

void dt_image_cache_set_print_timestamp(dt_image_cache_t *cache, const uint32_t imgid)
{
if(imgid <= 0) return;
dt_cache_entry_t *entry = dt_cache_get(&cache->cache, imgid, DT_IMAGE_CACHE_SAFE);
if(!entry) return;
ASAN_UNPOISON_MEMORY_REGION(entry->data, sizeof(dt_image_t));
dt_image_t *img = (dt_image_t *)entry->data;
img->cache_entry = entry;
img->print_timestamp = time(0);
dt_image_cache_write_release(cache, img, DT_IMAGE_CACHE_SAFE);
}

// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
// vim: shiftwidth=2 expandtab tabstop=2 cindent
Expand Down