/* * libopensync - A synchronization framework * Copyright (C) 2006 Daniel Gollub * * This library 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.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "opensync.h" #include "opensync_internals.h" #include "opensync_db.h" #include "opensync_db_internals.h" /* static void _osync_db_trace(void *data, const char *query) { osync_trace(TRACE_INTERNAL, "osync_db query executed: %s", query); } */ OSyncDB *osync_db_new(OSyncError **error) { osync_trace(TRACE_ENTRY, "%s(%p)", __func__, error); OSyncDB *db = osync_try_malloc0(sizeof(OSyncDB), error); if (!db) { osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); return NULL; } osync_trace(TRACE_EXIT, "%s: %p", __func__, db); return db; } /** * @brief Open a database * * @param db Pointer to database struct * @param dbfile Full file path to database file * @param error Pointer to a error struct * @return If database opened successfully then TRUE else FALSE */ osync_bool osync_db_open(OSyncDB *db, const char *dbfile, OSyncError **error) { osync_trace(TRACE_ENTRY, "%s(%p, %s, %p)", __func__, db, dbfile, error); osync_assert(db); osync_assert(dbfile); if (sqlite3_open(dbfile, &(db->sqlite3db)) != SQLITE_OK) { osync_error_set(error, OSYNC_ERROR_GENERIC, "Cannot open database: %s", sqlite3_errmsg(db->sqlite3db)); osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, sqlite3_errmsg(db->sqlite3db)); return FALSE; } osync_trace(TRACE_EXIT, "%s", __func__); return TRUE; } /** * @brief Close a sqlite3 database file * * @param db Pointer to database struct * @param error Pointer to a error struct * @return If database closed successfully then TRUE else FALSE */ osync_bool osync_db_close(OSyncDB *db, OSyncError **error) { osync_trace(TRACE_ENTRY, "%s(%p, %p)", __func__, db, error); osync_assert(db); int rc = sqlite3_close(db->sqlite3db); if (rc) { osync_error_set(error, OSYNC_ERROR_GENERIC, "Cannot close database: %s", sqlite3_errmsg(db->sqlite3db)); osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, sqlite3_errmsg(db->sqlite3db)); return FALSE; } osync_trace(TRACE_EXIT, "%s", __func__); return TRUE; } /** * @brief Counts result of a database query * * @param db Pointer to database struct * @param query SQL database query * @param error Pointer to a error struct * @return Returns number of query result or -1 on error */ int osync_db_count(OSyncDB *db, const char *query, OSyncError **error) { osync_trace(TRACE_ENTRY, "%s(%p, %s, %p)", __func__, db, query, error); osync_assert(db); osync_assert(query); int num; char **result = NULL; char *errmsg = NULL; if (sqlite3_get_table(db->sqlite3db, query, &result, &num, NULL, &errmsg) != SQLITE_OK) { osync_error_set(error, OSYNC_ERROR_GENERIC, "Unable count result of query: %s", errmsg); sqlite3_free_table(result); g_free(errmsg); osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); return -1; } sqlite3_free_table(result); osync_trace(TRACE_EXIT, "%s: %i", __func__, num); return num; } /** * @brief Executes a simple SQL query * * @param db Pointer to database struct * @param query SQL database query * @param error Pointer to a error struct * @return Returns TRUE on success otherwise FALSE */ osync_bool osync_db_query(OSyncDB *db, const char *query, OSyncError **error) { osync_trace(TRACE_ENTRY, "%s(%p, %s, %p)", __func__, db, query, error); osync_assert(db); osync_assert(query); char *errmsg = NULL; if (sqlite3_exec(db->sqlite3db, query, NULL, NULL, &errmsg) != SQLITE_OK) { osync_error_set(error, OSYNC_ERROR_GENERIC, "Unable to execute simple query: %s", errmsg); osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, errmsg); g_free(errmsg); return FALSE; } osync_trace(TRACE_EXIT, "%s", __func__); return TRUE; } /** * @brief Exectues a SQL query and fill all requested data in a GList. * Check error with osync_error_is_set(). * * @param db Pointer to database struct * @param query SQL database query * @param error Pointer to a error struct * @return Returns pointer to GList which contains the each result as another GList ptr. Freeing is recommend with osync_db_free_list() */ GList *osync_db_query_table(OSyncDB *db, const char *query, OSyncError **error) { osync_trace(TRACE_ENTRY, "%s(%p, %s, %p)", __func__, db, query, error); osync_assert(db); osync_assert(query); GList *table = NULL; int i, j, column_count = 0; int numrows = 0, numcolumns = 0; char **result = NULL; char *errmsg = NULL; if (sqlite3_get_table(db->sqlite3db, query, &result, &numrows, &numcolumns, &errmsg) != SQLITE_OK) { osync_error_set(error, OSYNC_ERROR_GENERIC, "Unable to query table: %s", errmsg); g_free(errmsg); osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); return NULL; } /* First row contains only names of columns. So we skip this! We start reading the array with a offset (number of columns) */ column_count = numcolumns; for (j=0; j < numrows; j++) { GList *row = NULL; for (i=0; i < numcolumns; i++) row = g_list_append(row, g_strdup(result[column_count++])); table = g_list_append(table, row); } sqlite3_free_table(result); osync_trace(TRACE_EXIT, "%s: %p", __func__, table); return table; } /** * @brief Frees the full result of osync_db_query_table(). * * @param list Result GList pointer of osync_db_query_table() */ void osync_db_free_list(GList *list) { osync_trace(TRACE_ENTRY, "%s(%p)", __func__, list); GList *row; for (row = list; row; row = row->next) { g_list_foreach((GList *) row->data, (GFunc) g_free, NULL); g_list_free((GList *) row->data); } g_list_free(list); osync_trace(TRACE_EXIT, "%s", __func__); } /** * @brief Execute a SQL query for a single string (char *). Do only use this * function when you expect a single return. The function will return * NULL if there are more then one results! * * @param db Pointer to database struct * @param query SQL database query * @param error Pointer to a error struct * @return Returns result string from database. NULL on more then one result or on error (the caller is responsible for freeing) */ char *osync_db_query_single_string(OSyncDB *db, const char *query, OSyncError **error) { osync_trace(TRACE_ENTRY, "%s(%p, %s, %p)", __func__, db, query, error); osync_assert(db); osync_assert(query); char *result = NULL; sqlite3_stmt *ppStmt = NULL; if (sqlite3_prepare(db->sqlite3db, query, -1, &ppStmt, NULL) != SQLITE_OK) { osync_error_set(error, OSYNC_ERROR_GENERIC, "Query Error: %s", sqlite3_errmsg(db->sqlite3db)); goto error; } if (sqlite3_step(ppStmt) != SQLITE_ROW) { sqlite3_finalize(ppStmt); osync_trace(TRACE_EXIT, "%s: no result of query", __func__); return NULL; } result = g_strdup((const char *)sqlite3_column_text(ppStmt, 0)); if (sqlite3_step(ppStmt) == SQLITE_ROW) { osync_error_set(error, OSYNC_ERROR_GENERIC, "Returned more than one result! This function only handle a single string!"); goto error; } sqlite3_finalize(ppStmt); osync_trace(TRACE_EXIT, "%s: %s", __func__, result); return result; error: g_free(result); sqlite3_finalize(ppStmt); osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); return NULL; } /** * @brief Execute a SQL query for a single integer. Do only use this * function when you expect a single return. The function will return * NULL if there are more then one results! * * @param db Pointer to database struct * @param query SQL database query * @param error Pointer to a error struct * @return Returns result integer from database. On more then one result or on error -1 */ int osync_db_query_single_int(OSyncDB *db, const char *query, OSyncError **error) { osync_trace(TRACE_ENTRY, "%s(%p, %s, %p)", __func__, db, query, error); osync_assert(db); osync_assert(query); int result = 0; sqlite3_stmt *ppStmt = NULL; if (sqlite3_prepare(db->sqlite3db, query, -1, &ppStmt, NULL) != SQLITE_OK) { osync_error_set(error, OSYNC_ERROR_GENERIC, "Query Error: %s", sqlite3_errmsg(db->sqlite3db)); goto error; } if (sqlite3_step(ppStmt) != SQLITE_ROW) { sqlite3_finalize(ppStmt); osync_trace(TRACE_EXIT, "%s: no result of query", __func__); return -1; } result = sqlite3_column_int(ppStmt, 0); if (sqlite3_step(ppStmt) == SQLITE_ROW) { osync_error_set(error, OSYNC_ERROR_GENERIC, "Returned more than one result! This function only handle a single integer!"); goto error; } sqlite3_finalize(ppStmt); osync_trace(TRACE_EXIT, "%s: %i", __func__, result); return result; error: sqlite3_finalize(ppStmt); osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); return -1; } /** * @brief Full reset of a sqlite3 table. * * @param db Pointer to database struct * @param tablename Name of table which gets reseted * @param error Pointer to a error struct * @return TRUE on success otherwise FALSE */ osync_bool osync_db_reset(OSyncDB *db, const char *tablename, OSyncError **error) { osync_trace(TRACE_ENTRY, "%s(%p, %s, %p)", __func__, db, tablename, error); osync_assert(db); osync_assert(tablename); char *query = g_strdup_printf("DELETE FROM %s", tablename); if (!osync_db_query(db, query, error)) goto error; g_free(query); osync_trace(TRACE_EXIT, "%s", __func__); return TRUE; error: g_free(query); osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); return FALSE; } osync_bool osync_db_reset_full(OSyncDB *db, OSyncError **error) { osync_trace(TRACE_ENTRY, "%s(%p, %p)", __func__, db, error); osync_assert(db); sqlite3_stmt *ppStmt = NULL; char *query = g_strdup("SELECT name FROM (SELECT * FROM sqlite_master) WHERE type='table'"); if (sqlite3_prepare(db->sqlite3db, query, -1, &ppStmt, NULL) != SQLITE_OK) { osync_error_set(error, OSYNC_ERROR_GENERIC, "Query Error: %s", sqlite3_errmsg(db->sqlite3db)); goto error; } while (sqlite3_step(ppStmt) == SQLITE_ROW) { const char *table = (const char *) sqlite3_column_text(ppStmt, 0); if (!osync_db_reset(db, table, error)) goto error; } sqlite3_finalize(ppStmt); osync_trace(TRACE_EXIT, "%s: TRUE", __func__); return TRUE; error: sqlite3_finalize(ppStmt); osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); return FALSE; } osync_bool osync_db_reset_full_by_path(const char *path, OSyncError **error) { osync_trace(TRACE_ENTRY, "%s(%s, %p)", __func__, path, error); osync_assert(path); OSyncDB *db = NULL; if (!osync_db_open(db, path, error)) goto error; if (!osync_db_reset_full(db, error)) goto error; osync_trace(TRACE_EXIT, "%s: TRUE", __func__); return TRUE; error: osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); return FALSE; } /** * @brief Checks if requested table exists in database * * @param db Pointer to database struct * @param tablename Name of table which gets reseted * @param error Pointer to a error struct * @return If the table exist 1 else 0. On error -1. */ int osync_db_exists(OSyncDB *db, const char *tablename, OSyncError **error) { osync_trace(TRACE_ENTRY, "%s(%p, %s, %p)", __func__, db, tablename, error); osync_assert(db); osync_assert(tablename); sqlite3_stmt *ppStmt = NULL; char *query = g_strdup_printf("SELECT name FROM (SELECT * FROM sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) WHERE type='table' AND name='%s'", tablename); if (sqlite3_prepare(db->sqlite3db, query, -1, &ppStmt, NULL) != SQLITE_OK) { sqlite3_finalize(ppStmt); g_free(query); osync_error_set(error, OSYNC_ERROR_GENERIC, "Query Error: %s", sqlite3_errmsg(db->sqlite3db)); osync_trace(TRACE_EXIT_ERROR, "Database query error: %s", sqlite3_errmsg(db->sqlite3db)); return -1; } if (sqlite3_step(ppStmt) != SQLITE_ROW) { sqlite3_finalize(ppStmt); g_free(query); osync_trace(TRACE_EXIT, "%s: table \"%s\" doesn't exist.", __func__, tablename); return 0; } sqlite3_finalize(ppStmt); g_free(query); osync_trace(TRACE_EXIT, "%s: table \"%s\" exists.", __func__, tablename); return 1; } /** * @brief Insert a data (blob) in database. * * @param db Pointer to database struct * @param query SQL query to bind the data blob * @param data Pointer to the data * @param size The size of the data which should be binded * @param error Pointer to a error struct * @return TRUE on success otherwise FALSE */ osync_bool osync_db_bind_blob(OSyncDB *db, const char *query, const char *data, unsigned int size, OSyncError **error) { osync_trace(TRACE_ENTRY, "%s(%p, %s, %s, %u, %p)", __func__, db, query, data, size, error); osync_assert(db); osync_assert(query); osync_assert(data); osync_assert(size); sqlite3_stmt *sqlite_stmt = NULL; int rc = sqlite3_prepare(db->sqlite3db, query, -1, &sqlite_stmt, NULL); if(rc != SQLITE_OK) goto error_msg; /* TODO Handle the case of multiple data blobs.... index = 1 will break with multiple blobs. */ rc = sqlite3_bind_blob(sqlite_stmt, 1, data, size, SQLITE_TRANSIENT); if(rc != SQLITE_OK) goto error_msg; rc = sqlite3_step(sqlite_stmt); if (rc != SQLITE_DONE) { if(rc != SQLITE_ERROR) goto error_msg; osync_error_set(error, OSYNC_ERROR_PARAMETER, "Unable to insert data! %s", sqlite3_errmsg(db->sqlite3db)); goto error; } sqlite3_reset(sqlite_stmt); sqlite3_finalize(sqlite_stmt); osync_trace(TRACE_EXIT, "%s", __func__); return TRUE; error_msg: osync_error_set(error, OSYNC_ERROR_GENERIC, "Unable to insert data: %s", sqlite3_errmsg(db->sqlite3db)); error: if(sqlite_stmt) { sqlite3_reset(sqlite_stmt); sqlite3_finalize(sqlite_stmt); } osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); return FALSE; } /** * @brief Get a data (blob) from the database. * * @param db Pointer to database struct * @param query SQL query to get the data blob * @param data Pointer where to store the data * @param size Pointer to store the size of data * @param error Pointer to a error struct * @return 1 on success, 0 no result, -1 on error. */ int osync_db_get_blob(OSyncDB *db, const char *query, char **data, unsigned int *size, OSyncError **error) { osync_trace(TRACE_ENTRY, "%s(%p, %s, %p, %p, %p)", __func__, db, query, data, size, error); osync_assert(db); osync_assert(query); osync_assert(data); osync_assert(size); sqlite3_stmt *sqlite_stmt = NULL; int rc = sqlite3_prepare(db->sqlite3db, query, -1, &sqlite_stmt, NULL); if(rc != SQLITE_OK) goto error_msg; rc = sqlite3_step(sqlite_stmt); if(rc != SQLITE_ROW) { sqlite3_reset(sqlite_stmt); sqlite3_finalize(sqlite_stmt); osync_trace(TRACE_EXIT, "%s: no result!", __func__); return 0; } const char *tmp = sqlite3_column_blob(sqlite_stmt, 0); *size = sqlite3_column_bytes(sqlite_stmt, 0); if (*size == 0) { sqlite3_reset(sqlite_stmt); sqlite3_finalize(sqlite_stmt); osync_trace(TRACE_EXIT, "%s: no data!", __func__); return 0; } *data = osync_try_malloc0(*size, error); if(!*data) goto error; memcpy(*data, tmp, *size); if (sqlite3_step(sqlite_stmt) == SQLITE_ROW) { osync_error_set(error, OSYNC_ERROR_GENERIC, "Returned more than one result for a uid"); goto error; } sqlite3_reset(sqlite_stmt); sqlite3_finalize(sqlite_stmt); osync_trace(TRACE_EXIT, "%s", __func__); return 1; error_msg: osync_error_set(error, OSYNC_ERROR_GENERIC, "Unable to get data: %s", sqlite3_errmsg(db->sqlite3db)); error: if(sqlite_stmt) { sqlite3_reset(sqlite_stmt); sqlite3_finalize(sqlite_stmt); } osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); return -1; } long long int osync_db_last_rowid(OSyncDB *db) { osync_assert(db); return sqlite3_last_insert_rowid(db->sqlite3db); }