image/video db: new fields added
[platform/upstream/lightmediascanner.git] / src / lib / lightmediascanner_db_image.c
1 /**
2  * Copyright (C) 2008-2011 by ProFUSION embedded systems
3  * Copyright (C) 2007 by INdT
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public License
7  * as published by the Free Software Foundation; either version 2.1 of
8  * the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18  * 02110-1301 USA
19  *
20  * @author Gustavo Sverzut Barbieri <barbieri@profusion.mobi>
21  */
22
23 #include <lightmediascanner_db.h>
24 #include "lightmediascanner_db_private.h"
25 #include <stdlib.h>
26 #include <stdio.h>
27
28 struct lms_db_image {
29     sqlite3 *db;
30     sqlite3_stmt *insert;
31     unsigned int _references;
32     unsigned int _is_started:1;
33 };
34
35 static struct lms_db_cache _cache = { };
36
37 static int
38 _db_table_updater_images_0(sqlite3 *db, const char *table, unsigned int current_version, int is_last_run) {
39     char *errmsg;
40     int r, ret;
41
42     errmsg = NULL;
43     r = sqlite3_exec(db,
44                      "CREATE TABLE IF NOT EXISTS images ("
45                      "id INTEGER PRIMARY KEY, "
46                      "title TEXT, "
47                      "artist TEXT, "
48                      "date INTEGER NOT NULL, "
49                      "width INTEGER NOT NULL, "
50                      "height INTEGER NOT NULL, "
51                      "orientation INTEGER NOT NULL, "
52                      "gps_lat REAL DEFAULT 0.0, "
53                      "gps_long REAL DEFAULT 0.0, "
54                      "gps_alt REAL DEFAULT 0.0"
55                      ")",
56                      NULL, NULL, &errmsg);
57     if (r != SQLITE_OK) {
58         fprintf(stderr, "ERROR: could not create 'images' table: %s\n", errmsg);
59         sqlite3_free(errmsg);
60         return -1;
61     }
62
63     r = sqlite3_exec(db,
64                      "CREATE INDEX IF NOT EXISTS images_date_idx ON images ("
65                      "date"
66                      ")",
67                      NULL, NULL, &errmsg);
68     if (r != SQLITE_OK) {
69         fprintf(stderr, "ERROR: could not create 'images_date_idx' index: %s\n",
70                 errmsg);
71         sqlite3_free(errmsg);
72         return -2;
73     }
74
75     ret = lms_db_create_trigger_if_not_exists(db,
76         "delete_images_on_files_deleted "
77         "DELETE ON files FOR EACH ROW BEGIN "
78         " DELETE FROM images WHERE id = OLD.id; END;");
79     if (ret != 0)
80         goto done;
81
82     ret = lms_db_create_trigger_if_not_exists(db,
83         "delete_files_on_images_deleted "
84         "DELETE ON images FOR EACH ROW BEGIN "
85         " DELETE FROM files WHERE id = OLD.id; END;");
86
87   done:
88     return ret;
89 }
90
91 static int
92 _db_table_updater_images_1(sqlite3 *db, const char *table, unsigned int current_version, int is_last_run)
93 {
94     int ret;
95     char *err;
96
97     ret = sqlite3_exec(
98         db, "BEGIN TRANSACTION;"
99         "ALTER TABLE images ADD COLUMN dlna_profile TEXT DEFAULT NULL;"
100         "ALTER TABLE images ADD COLUMN dlna_mime TEXT DEFAULT NULL;"
101         "COMMIT;",
102         NULL, NULL, &err);
103     if (ret != SQLITE_OK) {
104         fprintf(stderr, "ERROR: could add columns to images table: %s\n", err);
105         sqlite3_free(err);
106     }
107
108     return ret;
109 }
110
111 static int
112 _db_table_updater_images_2(sqlite3 *db, const char *table, unsigned int current_version, int is_last_run)
113 {
114     int ret;
115     char *err;
116
117     ret = sqlite3_exec(
118         db, "BEGIN TRANSACTION;"
119         "ALTER TABLE images ADD COLUMN container TEXT DEFAULT NULL;"
120         "COMMIT;",
121         NULL, NULL, &err);
122     if (ret != SQLITE_OK) {
123         fprintf(stderr, "ERROR: could add columns to images table: %s\n", err);
124         sqlite3_free(err);
125     }
126
127     return ret;
128 }
129
130 static lms_db_table_updater_t _db_table_updater_images[] = {
131     _db_table_updater_images_0,
132     _db_table_updater_images_1,
133     _db_table_updater_images_2
134 };
135
136
137 static int
138 _db_create_table_if_required(sqlite3 *db)
139 {
140     return lms_db_table_update_if_required(db, "images",
141          LMS_ARRAY_SIZE(_db_table_updater_images),
142          _db_table_updater_images);
143 }
144
145 /**
146  * Create image DB access tool.
147  *
148  * Creates or get a reference to tools to access 'images' table in an
149  * optimized and easy way.
150  *
151  * This is usually called from plugin's @b setup() callback with the @p db
152  * got from @c ctxt.
153  *
154  * @param db database connection.
155  *
156  * @return DB access tool handle.
157  * @ingroup LMS_Plugins
158  */
159 lms_db_image_t *
160 lms_db_image_new(sqlite3 *db)
161 {
162     lms_db_image_t *ldi;
163     void *p;
164
165     if (lms_db_cache_get(&_cache, db, &p) == 0) {
166         ldi = p;
167         ldi->_references++;
168         return ldi;
169     }
170
171     if (!db)
172         return NULL;
173
174     if (_db_create_table_if_required(db) != 0) {
175         fprintf(stderr, "ERROR: could not create table.\n");
176         return NULL;
177     }
178
179     ldi = calloc(1, sizeof(lms_db_image_t));
180     ldi->_references = 1;
181     ldi->db = db;
182
183     if (lms_db_cache_add(&_cache, db, ldi) != 0) {
184         lms_db_image_free(ldi);
185         return NULL;
186     }
187
188     return ldi;
189 }
190
191 /**
192  * Start image DB access tool.
193  *
194  * Compile SQL statements and other initialization functions.
195  *
196  * This is usually called from plugin's @b start() callback.
197  *
198  * @param ldi handle returned by lms_db_image_new().
199  *
200  * @return On success 0 is returned.
201  * @ingroup LMS_Plugins
202  */
203 int
204 lms_db_image_start(lms_db_image_t *ldi)
205 {
206     if (!ldi)
207         return -1;
208     if (ldi->_is_started)
209         return 0;
210
211     ldi->insert = lms_db_compile_stmt(ldi->db,
212         "INSERT OR REPLACE INTO images ("
213         "id, title, artist, date, width, height, orientation, "
214         "gps_lat, gps_long, gps_alt, dlna_profile, dlna_mime, container) "
215          "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
216     if (!ldi->insert)
217         return -2;
218
219     ldi->_is_started = 1;
220     return 0;
221 }
222
223 /**
224  * Free image DB access tool.
225  *
226  * Unreference and possible free resources allocated to access tool.
227  *
228  * This is usually called from plugin's @b finish() callback.
229  *
230  * @param ldi handle returned by lms_db_image_new().
231  *
232  * @return On success 0 is returned.
233  * @ingroup LMS_Plugins
234  */
235 int
236 lms_db_image_free(lms_db_image_t *ldi)
237 {
238     int r;
239
240     if (!ldi)
241         return -1;
242     if (ldi->_references == 0) {
243         fprintf(stderr, "ERROR: over-called lms_db_image_free(%p)\n", ldi);
244         return -1;
245     }
246
247     ldi->_references--;
248     if (ldi->_references > 0)
249         return 0;
250
251     if (ldi->insert)
252         lms_db_finalize_stmt(ldi->insert, "insert");
253
254     r = lms_db_cache_del(&_cache, ldi->db, ldi);
255     free(ldi);
256
257     return r;
258 }
259
260 static int
261 _db_insert(lms_db_image_t *ldi, const struct lms_image_info *info)
262 {
263     sqlite3_stmt *stmt;
264     int r, ret;
265
266     stmt = ldi->insert;
267
268     ret = lms_db_bind_int64(stmt, 1, info->id);
269     if (ret != 0)
270         goto done;
271
272     ret = lms_db_bind_text(stmt, 2, info->title.str, info->title.len);
273     if (ret != 0)
274         goto done;
275
276     ret = lms_db_bind_text(stmt, 3, info->artist.str, info->artist.len);
277     if (ret != 0)
278         goto done;
279
280     ret = lms_db_bind_int(stmt, 4, info->date);
281     if (ret != 0)
282         goto done;
283
284     ret = lms_db_bind_int(stmt, 5, info->width);
285     if (ret != 0)
286         goto done;
287
288     ret = lms_db_bind_int(stmt, 6, info->height);
289     if (ret != 0)
290         goto done;
291
292     ret = lms_db_bind_int(stmt, 7, info->orientation);
293     if (ret != 0)
294         goto done;
295
296     ret = lms_db_bind_double(stmt, 8, info->gps.latitude);
297     if (ret != 0)
298         goto done;
299
300     ret = lms_db_bind_double(stmt, 9, info->gps.longitude);
301     if (ret != 0)
302         goto done;
303
304     ret = lms_db_bind_double(stmt, 10, info->gps.altitude);
305     if (ret != 0)
306         goto done;
307
308     ret = lms_db_bind_text(stmt, 11, info->dlna_profile.str,
309                            info->dlna_profile.len);
310     if (ret != 0)
311         goto done;
312
313     ret = lms_db_bind_text(stmt, 12, info->dlna_mime.str, info->dlna_mime.len);
314     if (ret != 0)
315         goto done;
316
317     ret = lms_db_bind_text(stmt, 13, info->container.str, info->container.len);
318     if (ret != 0)
319         goto done;
320
321     r = sqlite3_step(stmt);
322     if (r != SQLITE_DONE) {
323         fprintf(stderr, "ERROR: could not insert image info: %s\n",
324                 sqlite3_errmsg(ldi->db));
325         ret = -11;
326         goto done;
327     }
328
329     ret = 0;
330
331   done:
332     lms_db_reset_stmt(stmt);
333
334     return ret;
335 }
336
337 /**
338  * Add image file to DB.
339  *
340  * This is usually called from plugin's @b parse() callback.
341  *
342  * @param ldi handle returned by lms_db_image_new().
343  * @param info image information to store.
344  *
345  * @return On success 0 is returned.
346  * @ingroup LMS_Plugins
347  */
348 int
349 lms_db_image_add(lms_db_image_t *ldi, struct lms_image_info *info)
350 {
351     if (!ldi)
352         return -1;
353     if (!info)
354         return -2;
355     if (info->id < 1)
356         return -3;
357
358     return _db_insert(ldi, info);
359 }