include_HEADERS = \
lightmediascanner.h \
lightmediascanner_plugin.h \
- lightmediascanner_utils.h
+ lightmediascanner_utils.h \
+ lightmediascanner_db.h
+noinst_HEADERS = lightmediascanner_db_private.h
lib_LTLIBRARIES = liblightmediascanner.la
liblightmediascanner_la_SOURCES = \
lightmediascanner.c \
- lightmediascanner_utils.c
+ lightmediascanner_utils.c \
+ lightmediascanner_db_common.c \
+ lightmediascanner_db_image.c
+
liblightmediascanner_la_LIBADD = -ldl @SQLITE3_LIBS@
liblightmediascanner_la_LDFLAGS = -version-info @version_info@
#include "lightmediascanner.h"
#include "lightmediascanner_plugin.h"
+#include "lightmediascanner_db_private.h"
#define PATH_SIZE PATH_MAX
#define DEFAULT_SLAVE_TIMEOUT 1000
return 0;
}
-static sqlite3_stmt *
-_db_compile_stmt(sqlite3 *db, const char *sql)
-{
- sqlite3_stmt *stmt;
-
- if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK)
- fprintf(stderr, "ERROR: could not prepare \"%s\": %s\n", sql,
- sqlite3_errmsg(db));
-
- return stmt;
-}
-
static int
_db_compile_all_stmts(struct db *db)
{
- db->transaction_begin = _db_compile_stmt(db->handle,
+ db->transaction_begin = lms_db_compile_stmt(db->handle,
"BEGIN TRANSACTION");
if (!db->transaction_begin)
return -1;
- db->transaction_commit = _db_compile_stmt(db->handle,
+ db->transaction_commit = lms_db_compile_stmt(db->handle,
"COMMIT");
if (!db->transaction_commit)
return -2;
- db->transaction_rollback = _db_compile_stmt(db->handle,
+ db->transaction_rollback = lms_db_compile_stmt(db->handle,
"ROLLBACK");
if (!db->transaction_rollback)
return -3;
- db->get_file_info = _db_compile_stmt(db->handle,
+ db->get_file_info = lms_db_compile_stmt(db->handle,
"SELECT id, mtime, valid FROM files WHERE path = ?");
if (!db->get_file_info)
return -4;
- db->insert_file_info = _db_compile_stmt(db->handle,
+ db->insert_file_info = lms_db_compile_stmt(db->handle,
"INSERT INTO files (path, mtime, valid) VALUES(?, ?, ?)");
if (!db->insert_file_info)
return -5;
- db->update_file_info = _db_compile_stmt(db->handle,
+ db->update_file_info = lms_db_compile_stmt(db->handle,
"UPDATE files SET mtime = ?, valid = ? WHERE id = ?");
if (!db->update_file_info)
return -6;
}
static int
-_db_finalize_stmt(sqlite3_stmt *stmt, const char *name)
-{
- int r;
-
- r = sqlite3_finalize(stmt);
- if (r != SQLITE_OK) {
- fprintf(stderr, "ERROR: could not finalize %s statement: #%d\n",
- name, r);
- return -1;
- }
-
- return 0;
-}
-
-static int
_db_close(struct db *db)
{
if (db->transaction_begin)
- _db_finalize_stmt(db->transaction_begin, "transaction_begin");
+ lms_db_finalize_stmt(db->transaction_begin, "transaction_begin");
if (db->transaction_commit)
- _db_finalize_stmt(db->transaction_commit, "transaction_commit");
+ lms_db_finalize_stmt(db->transaction_commit, "transaction_commit");
if (db->transaction_rollback)
- _db_finalize_stmt(db->transaction_rollback, "transaction_rollback");
+ lms_db_finalize_stmt(db->transaction_rollback, "transaction_rollback");
if (db->get_file_info)
- _db_finalize_stmt(db->get_file_info, "get_file_info");
+ lms_db_finalize_stmt(db->get_file_info, "get_file_info");
if (db->insert_file_info)
- _db_finalize_stmt(db->insert_file_info, "insert_file_info");
+ lms_db_finalize_stmt(db->insert_file_info, "insert_file_info");
if (db->update_file_info)
- _db_finalize_stmt(db->update_file_info, "update_file_info");
+ lms_db_finalize_stmt(db->update_file_info, "update_file_info");
if (sqlite3_close(db->handle) != SQLITE_OK) {
fprintf(stderr, "ERROR: clould not close DB: %s\n",
}
static int
-_db_reset_stmt(sqlite3_stmt *stmt)
-{
- int r, ret;
-
- ret = r = sqlite3_reset(stmt);
- if (r != SQLITE_OK)
- fprintf(stderr, "ERROR: could not reset SQL statement: #%d\n", r);
-
- r = sqlite3_clear_bindings(stmt);
- ret += r;
- if (r != SQLITE_OK)
- fprintf(stderr, "ERROR: could not clear SQL: #%d\n", r);
-
- return ret;
-}
-
-static int
_db_get_file_info(struct db *db, struct lms_file_info *finfo)
{
sqlite3_stmt *stmt;
stmt = db->get_file_info;
- r = sqlite3_bind_text(stmt, 1, finfo->path, finfo->path_len, SQLITE_STATIC);
- if (r != SQLITE_OK) {
- fprintf(stderr, "ERROR: could not bind SQL value 1: %s\n",
- sqlite3_errmsg(db->handle));
- ret = -1;
+ ret = lms_db_bind_text(stmt, 1, finfo->path, finfo->path_len);
+ if (ret != 0)
goto done;
- }
r = sqlite3_step(stmt);
if (r == SQLITE_DONE) {
ret = 0;
done:
- _db_reset_stmt(stmt);
+ lms_db_reset_stmt(stmt);
return ret;
}
stmt = db->update_file_info;
- r = sqlite3_bind_int(stmt, 1, finfo->mtime);
- if (r != SQLITE_OK) {
- fprintf(stderr, "ERROR: could not bind SQL value 1: %s\n",
- sqlite3_errmsg(db->handle));
- ret = -1;
+ ret = lms_db_bind_int(stmt, 1, finfo->mtime);
+ if (ret != 0)
goto done;
- }
- r = sqlite3_bind_int(stmt, 2, finfo->is_valid);
- if (r != SQLITE_OK) {
- fprintf(stderr, "ERROR: could not bind SQL value 2: %s\n",
- sqlite3_errmsg(db->handle));
- ret = -2;
+ ret = lms_db_bind_int(stmt, 2, finfo->is_valid);
+ if (ret != 0)
goto done;
- }
- r = sqlite3_bind_int64(stmt, 3, finfo->id);
- if (r != SQLITE_OK) {
- fprintf(stderr, "ERROR: could not bind SQL value 3: %s\n",
- sqlite3_errmsg(db->handle));
- ret = -3;
+ ret = lms_db_bind_int(stmt, 3, finfo->id);
+ if (ret != 0)
goto done;
- }
r = sqlite3_step(stmt);
if (r != SQLITE_DONE) {
ret = 0;
done:
- _db_reset_stmt(stmt);
+ lms_db_reset_stmt(stmt);
return ret;
}
stmt = db->insert_file_info;
- r = sqlite3_bind_text(stmt, 1, finfo->path, finfo->path_len, SQLITE_STATIC);
- if (r != SQLITE_OK) {
- fprintf(stderr, "ERROR: could not bind SQL value 1: %s\n",
- sqlite3_errmsg(db->handle));
- ret = -1;
+ ret = lms_db_bind_text(stmt, 1, finfo->path, finfo->path_len);
+ if (ret != 0)
goto done;
- }
- r = sqlite3_bind_int(stmt, 2, finfo->mtime);
- if (r != SQLITE_OK) {
- fprintf(stderr, "ERROR: could not bind SQL value 2: %s\n",
- sqlite3_errmsg(db->handle));
- ret = -2;
+ ret = lms_db_bind_int(stmt, 2, finfo->mtime);
+ if (ret != 0)
goto done;
- }
- r = sqlite3_bind_int(stmt, 3, finfo->is_valid);
- if (r != SQLITE_OK) {
- fprintf(stderr, "ERROR: could not bind SQL value 3: %s\n",
- sqlite3_errmsg(db->handle));
- ret = -3;
+ ret = lms_db_bind_int(stmt, 3, finfo->is_valid);
+ if (ret != 0)
goto done;
- }
r = sqlite3_step(stmt);
if (r != SQLITE_DONE) {
ret = 0;
done:
- _db_reset_stmt(stmt);
+ lms_db_reset_stmt(stmt);
return ret;
}
--- /dev/null
+/**
+ * Copyright (C) 2007 by INdT
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @author Gustavo Sverzut Barbieri <gustavo.barbieri@openbossa.org>
+ */
+
+#ifndef _LIGHTMEDIASCANNER_DB_H_
+#define _LIGHTMEDIASCANNER_DB_H_ 1
+
+#ifdef API
+#undef API
+#endif
+
+#ifdef __GNUC__
+# if __GNUC__ >= 4
+# define API __attribute__ ((visibility("default")))
+# else
+# define API
+# endif
+# if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+# define GNUC_NON_NULL(...) __attribute__((nonnull(__VA_ARGS__)))
+# else
+# define GNUC_NON_NULL(...)
+# endif
+#else
+# define API
+# define GNUC_NON_NULL(...)
+#endif
+
+#include <lightmediascanner_plugin.h>
+#include <lightmediascanner_utils.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /* Image Records */
+ struct lms_gps_info {
+ double latitude;
+ double longitude;
+ double altitude;
+ };
+
+ struct lms_image_info {
+ int64_t id;
+ struct lms_string_size title;
+ struct lms_string_size artist;
+ unsigned int date;
+ unsigned short width;
+ unsigned short height;
+ unsigned short orientation;
+ struct lms_gps_info gps;
+ };
+
+ typedef struct lms_db_image lms_db_image_t;
+
+ API lms_db_image_t *lms_db_image_new(sqlite3 *db) GNUC_NON_NULL(1);
+ API int lms_db_image_free(lms_db_image_t *lms_db_image) GNUC_NON_NULL(1);
+ API int lms_db_image_add(lms_db_image_t *lms_db_image, struct lms_image_info *info) GNUC_NON_NULL(1, 2);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _LIGHTMEDIASCANNER_DB_H_ */
--- /dev/null
+#include "lightmediascanner_db_private.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+sqlite3_stmt *
+lms_db_compile_stmt(sqlite3 *db, const char *sql)
+{
+ sqlite3_stmt *stmt;
+
+ if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK)
+ fprintf(stderr, "ERROR: could not prepare \"%s\": %s\n", sql,
+ sqlite3_errmsg(db));
+
+ return stmt;
+}
+
+int
+lms_db_finalize_stmt(sqlite3_stmt *stmt, const char *name)
+{
+ int r;
+
+ r = sqlite3_finalize(stmt);
+ if (r != SQLITE_OK) {
+ fprintf(stderr, "ERROR: could not finalize %s statement: #%d\n",
+ name, r);
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+lms_db_reset_stmt(sqlite3_stmt *stmt)
+{
+ int r, ret;
+
+ ret = r = sqlite3_reset(stmt);
+ if (r != SQLITE_OK)
+ fprintf(stderr, "ERROR: could not reset SQL statement: #%d\n", r);
+
+ r = sqlite3_clear_bindings(stmt);
+ ret += r;
+ if (r != SQLITE_OK)
+ fprintf(stderr, "ERROR: could not clear SQL: #%d\n", r);
+
+ return ret;
+}
+
+int
+lms_db_bind_text(sqlite3_stmt *stmt, int col, const char *text, int len)
+{
+ int r;
+
+ if (text)
+ r = sqlite3_bind_text(stmt, col, text, len, SQLITE_STATIC);
+ else
+ r = sqlite3_bind_null(stmt, col);
+
+ if (r == SQLITE_OK)
+ return 0;
+ else {
+ sqlite3 *db;
+ const char *err;
+
+ db = sqlite3_db_handle(stmt);
+ err = sqlite3_errmsg(db);
+ fprintf(stderr, "ERROR: could not bind SQL value %d: %s\n", col, err);
+ return -col;
+ }
+}
+
+int
+lms_db_bind_int64(sqlite3_stmt *stmt, int col, int64_t value)
+{
+ int r;
+
+ r = sqlite3_bind_int64(stmt, col, value);
+ if (r == SQLITE_OK)
+ return 0;
+ else {
+ sqlite3 *db;
+ const char *err;
+
+ db = sqlite3_db_handle(stmt);
+ err = sqlite3_errmsg(db);
+ fprintf(stderr, "ERROR: could not bind SQL value %d: %s\n", col, err);
+ return -col;
+ }
+}
+
+int
+lms_db_bind_int(sqlite3_stmt *stmt, int col, int value)
+{
+ int r;
+
+ r = sqlite3_bind_int(stmt, col, value);
+ if (r == SQLITE_OK)
+ return 0;
+ else {
+ sqlite3 *db;
+ const char *err;
+
+ db = sqlite3_db_handle(stmt);
+ err = sqlite3_errmsg(db);
+ fprintf(stderr, "ERROR: could not bind SQL value %d: %s\n", col, err);
+ return -col;
+ }
+}
+
+int
+lms_db_bind_double(sqlite3_stmt *stmt, int col, double value)
+{
+ int r;
+
+ r = sqlite3_bind_double(stmt, col, value);
+ if (r == SQLITE_OK)
+ return 0;
+ else {
+ sqlite3 *db;
+ const char *err;
+
+ db = sqlite3_db_handle(stmt);
+ err = sqlite3_errmsg(db);
+ fprintf(stderr, "ERROR: could not bind SQL value %d: %s\n", col, err);
+ return -col;
+ }
+}
--- /dev/null
+#include <lightmediascanner_db.h>
+#include "lightmediascanner_db_private.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+struct lms_db_image {
+ sqlite3 *db;
+ sqlite3_stmt *insert;
+ int _references;
+};
+
+lms_db_image_t *_singleton = NULL;
+
+static int
+_db_create_table_if_required(sqlite3 *db)
+{
+ char *errmsg;
+ int r;
+
+ errmsg = NULL;
+ r = sqlite3_exec(db,
+ "CREATE TABLE IF NOT EXISTS images ("
+ "id INTEGER PRIMARY KEY, "
+ "title TEXT, "
+ "artist TEXT, "
+ "date INTEGER NOT NULL, "
+ "width INTEGER NOT NULL, "
+ "height INTEGER NOT NULL, "
+ "orientation INTEGER NOT NULL, "
+ "thumb_width INTEGER NOT NULL, "
+ "thumb_height INTEGER NOT NULL, "
+ "gps_lat REAL DEFAULT 0.0, "
+ "gps_long REAL DEFAULT 0.0, "
+ "gps_alt REAL DEFAULT 0.0"
+ ")",
+ NULL, NULL, &errmsg);
+ if (r != SQLITE_OK) {
+ fprintf(stderr, "ERROR: could not create 'images' table: %s\n", errmsg);
+ sqlite3_free(errmsg);
+ return -1;
+ }
+
+ r = sqlite3_exec(db,
+ "CREATE INDEX IF NOT EXISTS images_date_idx ON images ("
+ "date"
+ ")",
+ NULL, NULL, &errmsg);
+ if (r != SQLITE_OK) {
+ fprintf(stderr, "ERROR: could not create 'images_date_idx' index: %s\n",
+ errmsg);
+ sqlite3_free(errmsg);
+ return -2;
+ }
+
+ r = sqlite3_exec(db,
+ "CREATE TRIGGER IF NOT EXISTS"
+ " delete_images_on_files_deleted "
+ "DELETE ON files "
+ "FOR EACH ROW BEGIN "
+ " DELETE FROM images WHERE id = OLD.id;"
+ "END;",
+ NULL, NULL, &errmsg);
+ if (r != SQLITE_OK) {
+ fprintf(stderr, "ERROR: could not create trigger to delete images on "
+ "files deletion: %s\n",
+ errmsg);
+ sqlite3_free(errmsg);
+ return -2;
+ }
+
+ r = sqlite3_exec(db,
+ "CREATE TRIGGER IF NOT EXISTS"
+ " delete_files_on_images_deleted "
+ "DELETE ON images "
+ "FOR EACH ROW BEGIN "
+ " DELETE FROM files WHERE id = OLD.id;"
+ "END;",
+ NULL, NULL, &errmsg);
+ if (r != SQLITE_OK) {
+ fprintf(stderr, "ERROR: could not create trigger to delete files on "
+ "images deletion: %s\n",
+ errmsg);
+ sqlite3_free(errmsg);
+ return -2;
+ }
+
+ return 0;
+}
+
+static int
+_db_compile_all_stmts(lms_db_image_t *ldi)
+{
+ ldi->insert = lms_db_compile_stmt(ldi->db,
+ "INSERT OR REPLACE INTO images ("
+ "id, title, artist, date, width, height, orientation, "
+ "thumb_width, thumb_height, gps_lat, gps_long, gps_alt) VALUES ("
+ "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
+ if (!ldi->insert)
+ return -1;
+
+ return 0;
+}
+
+lms_db_image_t *
+lms_db_image_new(sqlite3 *db)
+{
+ lms_db_image_t *ldi;
+
+ if (_singleton) {
+ _singleton->_references++;
+ return _singleton;
+ }
+
+ if (!db)
+ return NULL;
+
+ if (_db_create_table_if_required(db) != 0) {
+ fprintf(stderr, "ERROR: could not create table.\n");
+ return NULL;
+ }
+
+ ldi = calloc(1, sizeof(lms_db_image_t));
+ ldi->_references = 1;
+ ldi->db = db;
+
+ if (_db_compile_all_stmts(ldi) != 0) {
+ fprintf(stderr, "ERROR: could not compile image statements.\n");
+ lms_db_image_free(ldi);
+ return NULL;
+ }
+
+ return ldi;
+}
+
+int
+lms_db_image_free(lms_db_image_t *ldi)
+{
+ if (!ldi)
+ return -1;
+
+ ldi->_references--;
+ if (ldi->_references > 0)
+ return 0;
+
+ if (ldi->insert)
+ lms_db_finalize_stmt(ldi->insert, "insert");
+
+ free(ldi);
+ _singleton = NULL;
+
+ return 0;
+}
+
+int
+_db_insert(lms_db_image_t *ldi, const struct lms_image_info *info)
+{
+ sqlite3_stmt *stmt;
+ int r, ret;
+ unsigned long tw, th;
+
+ if (info->height < info->width) {
+ tw = 128;
+ th = (info->height * 128) / info->width;
+ if (th == 0)
+ th = 1;
+ } else if (info->height == info->width)
+ tw = th = 128;
+ else {
+ th = 128;
+ tw = (info->width * 128) / info->height;
+ if (tw == 0)
+ tw = 1;
+ }
+
+ stmt = ldi->insert;
+
+ ret = lms_db_bind_int64(stmt, 1, info->id);
+ if (ret != 0)
+ goto done;
+
+ ret = lms_db_bind_text(stmt, 2, info->title.str, info->title.len);
+ if (ret != 0)
+ goto done;
+
+ ret = lms_db_bind_text(stmt, 3, info->artist.str, info->artist.len);
+ if (ret != 0)
+ goto done;
+
+ ret = lms_db_bind_int(stmt, 4, info->date);
+ if (ret != 0)
+ goto done;
+
+ ret = lms_db_bind_int(stmt, 5, info->width);
+ if (ret != 0)
+ goto done;
+
+ ret = lms_db_bind_int(stmt, 6, info->height);
+ if (ret != 0)
+ goto done;
+
+ ret = lms_db_bind_int(stmt, 7, info->orientation);
+ if (ret != 0)
+ goto done;
+
+ ret = lms_db_bind_int(stmt, 8, tw);
+ if (ret != 0)
+ goto done;
+
+ ret = lms_db_bind_int(stmt, 9, th);
+ if (ret != 0)
+ goto done;
+
+ ret = lms_db_bind_double(stmt, 10, info->gps.latitude);
+ if (ret != 0)
+ goto done;
+
+ ret = lms_db_bind_double(stmt, 11, info->gps.longitude);
+ if (ret != 0)
+ goto done;
+
+ ret = lms_db_bind_double(stmt, 12, info->gps.altitude);
+ if (ret != 0)
+ goto done;
+
+ r = sqlite3_step(stmt);
+ if (r != SQLITE_DONE) {
+ fprintf(stderr, "ERROR: could not insert image info: %s\n",
+ sqlite3_errmsg(ldi->db));
+ ret = -13;
+ goto done;
+ }
+
+ ret = 0;
+
+ done:
+ lms_db_reset_stmt(stmt);
+
+ return ret;
+}
+
+int
+lms_db_image_add(lms_db_image_t *ldi, struct lms_image_info *info)
+{
+ if (!ldi)
+ return -1;
+ if (!info)
+ return -2;
+ if (info->id < 1)
+ return -3;
+
+ return _db_insert(ldi, info);
+}
--- /dev/null
+/**
+ * Copyright (C) 2007 by INdT
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * @author Gustavo Sverzut Barbieri <gustavo.barbieri@openbossa.org>
+ */
+
+#ifndef _LIGHTMEDIASCANNER_DB_PRIVATE_H_
+#define _LIGHTMEDIASCANNER_DB_PRIVATE_H_ 1
+
+#ifdef API
+#undef API
+#endif
+
+#ifdef __GNUC__
+# if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+# define GNUC_NON_NULL(...) __attribute__((nonnull(__VA_ARGS__)))
+# else
+# define GNUC_NON_NULL(...)
+# endif
+#else
+# define GNUC_NON_NULL(...)
+#endif
+
+#include <sqlite3.h>
+#include <sys/types.h>
+
+sqlite3_stmt *lms_db_compile_stmt(sqlite3 *db, const char *sql) GNUC_NON_NULL(1, 2);
+int lms_db_finalize_stmt(sqlite3_stmt *stmt, const char *name) GNUC_NON_NULL(1, 2);
+int lms_db_reset_stmt(sqlite3_stmt *stmt) GNUC_NON_NULL(1);
+int lms_db_bind_text(sqlite3_stmt *stmt, int col, const char *text, int len) GNUC_NON_NULL(1);
+int lms_db_bind_int64(sqlite3_stmt *stmt, int col, int64_t value) GNUC_NON_NULL(1);
+int lms_db_bind_int(sqlite3_stmt *stmt, int col, int value) GNUC_NON_NULL(1);
+int lms_db_bind_double(sqlite3_stmt *stmt, int col, double value) GNUC_NON_NULL(1);
+
+#endif /* _LIGHTMEDIASCANNER_DB_PRIVATE_H_ */