Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
metadata_repository.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2026 Aurélien PIERRE.
4
5 darktable is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 darktable is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with darktable. If not, see <http://www.gnu.org/licenses/>.
17*/
18
20
21#include "database/database.h"
22#include "database/sql_debug.h"
23#include "system/macros.h"
24#include "system/mem_alloc.h"
25
26#include <sqlite3.h>
27
28GList *dt_metadata_repository_get_values(const int32_t imgid, const int keyid)
29{
30 /* Prepared per call: a cached statement here would be shared across the GUI thread and
31 * worker jobs with no lock -- two threads stepping one statement interleave both row
32 * cursors, and the lazy first-use prepare is itself a race. */
34
35 if(imgid < 0)
36 {
37 // clang-format off
39 "SELECT value FROM main.meta_data WHERE id IN "
40 "(SELECT imgid FROM main.selected_images) AND key = ?1 ORDER BY value",
41 -1, &stmt, NULL);
42 // clang-format on
43 if(IS_NULL_PTR(stmt)) return NULL;
45 }
46 else // single image under mouse cursor
47 {
48 // clang-format off
50 "SELECT value FROM main.meta_data WHERE id = ?1 AND key = ?2 ORDER BY value",
51 -1, &stmt, NULL);
52 // clang-format on
53 if(IS_NULL_PTR(stmt)) return NULL;
56 }
57
58 GList *result = NULL;
60 {
61 const char *value = (const char *)sqlite3_column_text(stmt, 0);
62 result = g_list_prepend(result, g_strdup(value ? value : "")); // to avoid NULL value
63 }
65
66 return g_list_reverse(result); // list was built in reverse order, so un-reverse it
67}
68
70{
72 // clang-format off
74 "SELECT key, value FROM main.meta_data WHERE id=?1", -1, &stmt, NULL);
75 // clang-format on
77
78 GList *metadata = NULL;
80 {
81 const gchar *value = (const char *)sqlite3_column_text(stmt, 1);
82 gchar *ckey = g_strdup_printf("%d", sqlite3_column_int(stmt, 0));
83 gchar *cvalue = g_strdup(value ? value : ""); // to avoid NULL value
84 metadata = g_list_append(metadata, (gpointer)ckey);
85 metadata = g_list_append(metadata, (gpointer)cvalue);
86 }
88 return metadata;
89}
90
91void dt_metadata_repository_remove(const int32_t imgid, const char *keyid_list)
92{
93 if(imgid <= 0 || IS_NULL_PTR(keyid_list)) return;
94
96 // clang-format off
97 gchar *query = g_strdup_printf("DELETE FROM main.meta_data WHERE id = %d AND key IN (%s)",
98 imgid, keyid_list);
99 // clang-format on
103 dt_free(query);
104}
105
106void dt_metadata_repository_add(const dt_metadata_row_t *rows, const size_t count)
107{
108 if(IS_NULL_PTR(rows) || count == 0) return;
109
110 /* One statement for the whole batch, as before. Built rather than bound because the
111 * number of rows is not known until here, and a prepared statement's placeholder count
112 * is fixed. */
113 GString *values = g_string_new(NULL);
114 for(size_t i = 0; i < count; i++)
115 {
116 char *escaped = sqlite3_mprintf("%q", rows[i].value ? rows[i].value : "");
117 g_string_append_printf(values, "%s(%d,%d,'%s')", (i > 0) ? "," : "",
118 rows[i].imgid, rows[i].keyid, escaped);
120 }
121
123 // clang-format off
124 gchar *query = g_strdup_printf("INSERT INTO main.meta_data (id, key, value) VALUES %s", values->str);
125 // clang-format on
129 dt_free(query);
130 g_string_free(values, TRUE);
131}
132
134{
136 // clang-format off
138 "SELECT id FROM main.meta_data WHERE value=?1", -1, &stmt, NULL);
139 // clang-format on
141
142 int32_t imgid = -1;
144 imgid = sqlite3_column_int(stmt, 0);
146 return imgid;
147}
148
150 void *user_data)
151{
152 if(!cb) return;
153
155 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get_sqlite3_global(), "SELECT key, value FROM main.meta_data WHERE id = ?1",
156 -1, &stmt, NULL);
158 while(sqlite3_step(stmt) == SQLITE_ROW)
159 cb(user_data, sqlite3_column_int(stmt, 0), (const char *)sqlite3_column_text(stmt, 1));
161}
162
164{
165 if(!cb) return;
166
168 // clang-format off
170 "SELECT m.key, m.value, COUNT(m.id) AS ct"
171 " FROM main.meta_data AS m"
172 " JOIN main.selected_images AS s ON s.imgid = m.id"
173 " GROUP BY m.key, m.value ORDER BY m.value",
174 -1, &stmt, NULL);
175 // clang-format on
176 if(!stmt) return;
177
178 while(sqlite3_step(stmt) == SQLITE_ROW)
179 cb(user_data, sqlite3_column_int(stmt, 0), (const char *)sqlite3_column_text(stmt, 1),
180 (uint32_t)sqlite3_column_int(stmt, 2));
182}
183
185{
186 /* Nothing cached any more: every statement in this file is prepared and finalised per
187 * call. Kept because the connection's close order calls every repository's cleanup. */
188}
189
190// clang-format off
191// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
192// vim: shiftwidth=2 expandtab tabstop=2 cindent
193// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
194// clang-format on
#define TRUE
Definition ashift_lsd.c:162
sqlite3 * dt_database_get_sqlite3_global(void)
Definition database.c:3753
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:65
#define dt_free(ptr)
Definition mem_alloc.h:97
GList * dt_metadata_repository_get_values(const int32_t imgid, const int keyid)
Values stored under keyid, ordered by value.
void dt_metadata_repository_remove(const int32_t imgid, const char *keyid_list)
Delete the rows of imgid whose key is in keyid_list, a comma-separated list of decimal key ids....
GList * dt_metadata_repository_get_all(const int32_t imgid)
Every (key, value) pair of imgid, as a flat list.
void dt_metadata_repository_foreach_selected(dt_metadata_repository_selected_cb cb, void *user_data)
Every distinct (key, value) pair across main.selected_images, ordered by value.
void dt_metadata_repository_cleanup(void)
Finalise the prepared statements. See dt_colorlabel_repository_cleanup().
void dt_metadata_repository_foreach(const int32_t imgid, dt_metadata_repository_row_cb cb, void *user_data)
Every metadata row of imgid, in row order.
int32_t dt_metadata_repository_find_image_by_value(const char *value)
The first image carrying value under any key, or -1.
void dt_metadata_repository_add(const dt_metadata_row_t *rows, const size_t count)
Insert count rows as a single multi-VALUES statement.
main.meta_data: the free-text image metadata (title, description, creator, publisher,...
void(* dt_metadata_repository_selected_cb)(void *user_data, const int keyid, const char *value, const uint32_t count)
One distinct (key, value) across the selection, and how many selected images carry it.
void(* dt_metadata_repository_row_cb)(void *user_data, const int keyid, const char *value)
One main.meta_data row: its key id and its value.
static const dt_aligned_pixel_simd_t value
Definition simd.h:144
Checked wrappers around the sqlite3_* calls: trace the statement, assert the return code,...
#define DT_DEBUG_SQLITE3_PREPARE_V2(a, b, c, d, e)
Definition sql_debug.h:137
#define DT_DEBUG_SQLITE3_BIND_TEXT(a, b, c, d, e)
Definition sql_debug.h:148
#define DT_DEBUG_SQLITE3_BIND_INT(a, b, c)
Definition sql_debug.h:145