/* * Copyright (C) 2007 Tildeslash Ltd. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation. * * There are special exceptions to the terms and conditions of the GPL * as it is applied to this software. View the full text of the exception * in the file EXCEPTIONS accompanying this software distribution. * * 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 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 */ #include "Config.h" #include #include #include "URL.h" #include "Util.h" #include "ResultSet.h" #include "StringBuffer.h" #include "PreparedStatement.h" #include "SQLiteResultSet.h" #include "SQLitePreparedStatement.h" #include "ConnectionStrategy.h" #include "SQLiteConnection.h" /** * Implementation of the Connection/Strategy interface for SQLite * * @version \$Id: SQLiteConnection.c,v 1.26 2007/01/02 10:29:51 hauk Exp $ * @file */ /* ----------------------------------------------------------- Definitions */ const struct conop sqlite3conops= { "sqlite", SQLiteConnection_new, SQLiteConnection_free, SQLiteConnection_setQueryTimeout, SQLiteConnection_setMaxRows, SQLiteConnection_ping, SQLiteConnection_beginTransaction, SQLiteConnection_commit, SQLiteConnection_rollback, SQLiteConnection_lastRowId, SQLiteConnection_rowsChanged, SQLiteConnection_execute, SQLiteConnection_executeQuery, SQLiteConnection_prepareStatement, SQLiteConnection_getLastError }; #define T IConnection_T struct T { URL_T url; sqlite3 *db; int maxRows; int timeout; int lastError; }; extern const struct rsetop sqlite3rsetops; extern const struct prepop sqlite3prepops; /* ------------------------------------------------------------ Prototypes */ static int setProperties(T C, char **error); static void executeSQL(T C, const char *sql); static sqlite3 *doConnect(URL_T url, char **error); static void prepareSQL(T C, const char *sql, int size, sqlite3_stmt **stmt); /* ----------------------------------------------------- Protected methods */ #ifdef PACKAGE_PROTECTED #pragma GCC visibility push(hidden) #endif T SQLiteConnection_new(URL_T url, char **error) { T C; sqlite3 *db; assert(url); assert(error); if((db= doConnect(url, error))==NULL) return NULL; NEW(C); C->db= db; C->url= url; sqlite3_busy_timeout(C->db, SQL_DEFAULT_TIMEOUT); if(!setProperties(C, error)) { SQLiteConnection_free(&C); return NULL; } return C; } void SQLiteConnection_free(T *C) { assert(C && *C); while(sqlite3_close((*C)->db)==SQLITE_BUSY && Util_usleep(1000)); FREE(*C); } void SQLiteConnection_setQueryTimeout(T C, int ms) { assert(C); C->timeout= ms; sqlite3_busy_timeout(C->db, C->timeout); } void SQLiteConnection_setMaxRows(T C, int max) { assert(C); C->maxRows= max; } int SQLiteConnection_ping(T C) { assert(C); executeSQL(C, "select 1;"); return (C->lastError==SQLITE_OK); } int SQLiteConnection_beginTransaction(T C) { assert(C); executeSQL(C, "BEGIN TRANSACTION;"); return (C->lastError == SQLITE_OK); } int SQLiteConnection_commit(T C) { assert(C); executeSQL(C, "COMMIT TRANSACTION;"); return (C->lastError == SQLITE_OK); } int SQLiteConnection_rollback(T C) { assert(C); executeSQL(C, "ROLLBACK TRANSACTION;"); return (C->lastError == SQLITE_OK); } long long int SQLiteConnection_lastRowId(T C) { assert(C); return sqlite3_last_insert_rowid(C->db); } long long int SQLiteConnection_rowsChanged(T C) { assert(C); return (long long int)sqlite3_changes(C->db); } int SQLiteConnection_execute(T C, const char *sql, va_list ap) { char *SQL; assert(C); SQL= sqlite3_vmprintf(sql, ap); executeSQL(C, SQL); sqlite3_free(SQL); return (C->lastError==SQLITE_OK); } ResultSet_T SQLiteConnection_executeQuery(T C, const char *sql, va_list ap) { char *SQL; sqlite3_stmt *stmt; assert(C); SQL= sqlite3_vmprintf(sql, ap); prepareSQL(C, SQL, -1, &stmt); sqlite3_free(SQL); if(C->lastError==SQLITE_OK) return ResultSet_new(SQLiteResultSet_new(stmt, C->maxRows, FALSE), (Rop_T)&sqlite3rsetops); return NULL; } PreparedStatement_T SQLiteConnection_prepareStatement(T C, const char *sql) { sqlite3_stmt *stmt; assert(C); prepareSQL(C, sql, -1, &stmt); if(C->lastError==SQLITE_OK) return PreparedStatement_new(SQLitePreparedStatement_new(stmt, C->maxRows), (Pop_T)&sqlite3prepops); return NULL; } const char *SQLiteConnection_getLastError(T C) { assert(C); return sqlite3_errmsg(C->db); } #ifdef PACKAGE_PROTECTED #pragma GCC visibility pop #endif /* ------------------------------------------------------- Private methods */ static sqlite3 *doConnect(URL_T url, char **error) { sqlite3 *db; const char *path= URL_getPath(url); if(Util_startsWith(path, "/:")) { if(IS(path, "/:memory:")==FALSE) { *error= Util_getString("unknown database '%s', did" " you mean '/:memory:'?", path); return NULL; } path++; } if(SQLITE_OK != sqlite3_open(path, &db)) { *error= Util_getString("cannot open database '%s' -- %s", path, sqlite3_errmsg(db)); sqlite3_close(db); return NULL; } return db; } static int setProperties(T C, char **error) { int i; StringBuffer_T sb; const char **properties= URL_getParameterNames(C->url); if(properties==NULL) return TRUE; sb= StringBuffer_new(""); for(i= 0; properties[i]; i++) StringBuffer_append(sb, "PRAGMA %s = %s; ", properties[i], URL_getParameter(C->url, properties[i])); executeSQL(C, StringBuffer_toString(sb)); StringBuffer_free(&sb); if(C->lastError!=SQLITE_OK) { *error= Util_getString("unable to set database pragmas -- %s", sqlite3_errmsg(C->db)); return FALSE; } return TRUE; } static void executeSQL(T C, const char *sql) { int t= 4; long timeout= C->timeout*1000*0.25; do { C->lastError= sqlite3_exec(C->db, sql, NULL, NULL, NULL); } while(C->lastError==SQLITE_BUSY && t-- && Util_usleep(timeout)); } static void prepareSQL(T C, const char *sql, int size, sqlite3_stmt **stmt) { int t= 4; long timeout; const char *tail; timeout= C->timeout*1000*0.25; do { C->lastError= sqlite3_prepare(C->db, sql, size, stmt, &tail); } while(C->lastError==SQLITE_BUSY && t-- && Util_usleep(timeout)); }