2d2e09778c5d1713384e5b04932f2c15c691e7d6
[lms] / lightmediascanner / src / lib / lightmediascanner_db_image.c
1 #include <lightmediascanner_db.h>
2 #include "lightmediascanner_db_private.h"
3 #include <stdlib.h>
4 #include <stdio.h>
5
6 struct lms_db_image {
7     sqlite3 *db;
8     sqlite3_stmt *insert;
9     unsigned int _references;
10     unsigned int _is_started:1;
11 };
12
13 static struct lms_db_cache _cache = {0, NULL};
14
15 static int
16 _db_table_updater_images_0(sqlite3 *db, const char *table, unsigned int current_version, int is_last_run) {
17     char *errmsg;
18     int r, ret;
19
20     errmsg = NULL;
21     r = sqlite3_exec(db,
22                      "CREATE TABLE IF NOT EXISTS images ("
23                      "id INTEGER PRIMARY KEY, "
24                      "title TEXT, "
25                      "artist TEXT, "
26                      "date INTEGER NOT NULL, "
27                      "width INTEGER NOT NULL, "
28                      "height INTEGER NOT NULL, "
29                      "orientation INTEGER NOT NULL, "
30                      "gps_lat REAL DEFAULT 0.0, "
31                      "gps_long REAL DEFAULT 0.0, "
32                      "gps_alt REAL DEFAULT 0.0"
33                      ")",
34                      NULL, NULL, &errmsg);
35     if (r != SQLITE_OK) {
36         fprintf(stderr, "ERROR: could not create 'images' table: %s\n", errmsg);
37         sqlite3_free(errmsg);
38         return -1;
39     }
40
41     r = sqlite3_exec(db,
42                      "CREATE INDEX IF NOT EXISTS images_date_idx ON images ("
43                      "date"
44                      ")",
45                      NULL, NULL, &errmsg);
46     if (r != SQLITE_OK) {
47         fprintf(stderr, "ERROR: could not create 'images_date_idx' index: %s\n",
48                 errmsg);
49         sqlite3_free(errmsg);
50         return -2;
51     }
52
53     ret = lms_db_create_trigger_if_not_exists(db,
54         "delete_images_on_files_deleted "
55         "DELETE ON files FOR EACH ROW BEGIN "
56         " DELETE FROM images WHERE id = OLD.id; END;");
57     if (ret != 0)
58         goto done;
59
60     ret = lms_db_create_trigger_if_not_exists(db,
61         "delete_files_on_images_deleted "
62         "DELETE ON images FOR EACH ROW BEGIN "
63         " DELETE FROM files WHERE id = OLD.id; END;");
64
65   done:
66     return ret;
67 }
68
69 static lms_db_table_updater_t _db_table_updater_images[] = {
70     _db_table_updater_images_0
71 };
72
73
74 static int
75 _db_create_table_if_required(sqlite3 *db)
76 {
77     return lms_db_table_update_if_required(db, "images",
78          LMS_ARRAY_SIZE(_db_table_updater_images),
79          _db_table_updater_images);
80 }
81
82 /**
83  * Create image DB access tool.
84  *
85  * Creates or get a reference to tools to access 'images' table in an
86  * optimized and easy way.
87  *
88  * This is usually called from plugin's @b setup() callback with the @p db
89  * got from @c ctxt.
90  *
91  * @param db database connection.
92  *
93  * @return DB access tool handle.
94  * @ingroup LMS_Plugins
95  */
96 lms_db_image_t *
97 lms_db_image_new(sqlite3 *db)
98 {
99     lms_db_image_t *ldi;
100     void *p;
101
102     if (lms_db_cache_get(&_cache, db, &p) == 0) {
103         ldi = p;
104         ldi->_references++;
105         return ldi;
106     }
107
108     if (!db)
109         return NULL;
110
111     if (_db_create_table_if_required(db) != 0) {
112         fprintf(stderr, "ERROR: could not create table.\n");
113         return NULL;
114     }
115
116     ldi = calloc(1, sizeof(lms_db_image_t));
117     ldi->_references = 1;
118     ldi->db = db;
119
120     if (lms_db_cache_add(&_cache, db, ldi) != 0) {
121         lms_db_image_free(ldi);
122         return NULL;
123     }
124
125     return ldi;
126 }
127
128 /**
129  * Start image DB access tool.
130  *
131  * Compile SQL statements and other initialization functions.
132  *
133  * This is usually called from plugin's @b start() callback.
134  *
135  * @param ldi handle returned by lms_db_image_new().
136  *
137  * @return On success 0 is returned.
138  * @ingroup LMS_Plugins
139  */
140 int
141 lms_db_image_start(lms_db_image_t *ldi)
142 {
143     if (!ldi)
144         return -1;
145     if (ldi->_is_started)
146         return 0;
147
148     ldi->insert = lms_db_compile_stmt(ldi->db,
149         "INSERT OR REPLACE INTO images ("
150         "id, title, artist, date, width, height, orientation, "
151         "gps_lat, gps_long, gps_alt) VALUES ("
152         "?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
153     if (!ldi->insert)
154         return -2;
155
156     ldi->_is_started = 1;
157     return 0;
158 }
159
160 /**
161  * Free image DB access tool.
162  *
163  * Unreference and possible free resources allocated to access tool.
164  *
165  * This is usually called from plugin's @b finish() callback.
166  *
167  * @param ldi handle returned by lms_db_image_new().
168  *
169  * @return On success 0 is returned.
170  * @ingroup LMS_Plugins
171  */
172 int
173 lms_db_image_free(lms_db_image_t *ldi)
174 {
175     int r;
176
177     if (!ldi)
178         return -1;
179     if (ldi->_references == 0) {
180         fprintf(stderr, "ERROR: over-called lms_db_image_free(%p)\n", ldi);
181         return -1;
182     }
183
184     ldi->_references--;
185     if (ldi->_references > 0)
186         return 0;
187
188     if (ldi->insert)
189         lms_db_finalize_stmt(ldi->insert, "insert");
190
191     r = lms_db_cache_del(&_cache, ldi->db, ldi);
192     free(ldi);
193
194     return r;
195 }
196
197 static int
198 _db_insert(lms_db_image_t *ldi, const struct lms_image_info *info)
199 {
200     sqlite3_stmt *stmt;
201     int r, ret;
202
203     stmt = ldi->insert;
204
205     ret = lms_db_bind_int64(stmt, 1, info->id);
206     if (ret != 0)
207         goto done;
208
209     ret = lms_db_bind_text(stmt, 2, info->title.str, info->title.len);
210     if (ret != 0)
211         goto done;
212
213     ret = lms_db_bind_text(stmt, 3, info->artist.str, info->artist.len);
214     if (ret != 0)
215         goto done;
216
217     ret = lms_db_bind_int(stmt, 4, info->date);
218     if (ret != 0)
219         goto done;
220
221     ret = lms_db_bind_int(stmt, 5, info->width);
222     if (ret != 0)
223         goto done;
224
225     ret = lms_db_bind_int(stmt, 6, info->height);
226     if (ret != 0)
227         goto done;
228
229     ret = lms_db_bind_int(stmt, 7, info->orientation);
230     if (ret != 0)
231         goto done;
232
233     ret = lms_db_bind_double(stmt, 8, info->gps.latitude);
234     if (ret != 0)
235         goto done;
236
237     ret = lms_db_bind_double(stmt, 9, info->gps.longitude);
238     if (ret != 0)
239         goto done;
240
241     ret = lms_db_bind_double(stmt, 10, info->gps.altitude);
242     if (ret != 0)
243         goto done;
244
245     r = sqlite3_step(stmt);
246     if (r != SQLITE_DONE) {
247         fprintf(stderr, "ERROR: could not insert image info: %s\n",
248                 sqlite3_errmsg(ldi->db));
249         ret = -11;
250         goto done;
251     }
252
253     ret = 0;
254
255   done:
256     lms_db_reset_stmt(stmt);
257
258     return ret;
259 }
260
261 /**
262  * Add image file to DB.
263  *
264  * This is usually called from plugin's @b parse() callback.
265  *
266  * @param ldi handle returned by lms_db_image_new().
267  * @param info image information to store.
268  *
269  * @return On success 0 is returned.
270  * @ingroup LMS_Plugins
271  */
272 int
273 lms_db_image_add(lms_db_image_t *ldi, struct lms_image_info *info)
274 {
275     if (!ldi)
276         return -1;
277     if (!info)
278         return -2;
279     if (info->id < 1)
280         return -3;
281
282     return _db_insert(ldi, info);
283 }