Annotation of embedaddon/strongswan/src/libstrongswan/plugins/sqlite/sqlite_database.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Copyright (C) 2013 Tobias Brunner
! 3: * Copyright (C) 2007 Martin Willi
! 4: * HSR Hochschule fuer Technik Rapperswil
! 5: *
! 6: * This program is free software; you can redistribute it and/or modify it
! 7: * under the terms of the GNU General Public License as published by the
! 8: * Free Software Foundation; either version 2 of the License, or (at your
! 9: * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
! 10: *
! 11: * This program is distributed in the hope that it will be useful, but
! 12: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
! 13: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
! 14: * for more details.
! 15: */
! 16:
! 17: #include "sqlite_database.h"
! 18:
! 19: #include <sqlite3.h>
! 20: #include <unistd.h>
! 21: #include <library.h>
! 22: #include <utils/debug.h>
! 23: #include <threading/mutex.h>
! 24: #include <threading/thread_value.h>
! 25:
! 26: typedef struct private_sqlite_database_t private_sqlite_database_t;
! 27:
! 28: /**
! 29: * private data of sqlite_database
! 30: */
! 31: struct private_sqlite_database_t {
! 32:
! 33: /**
! 34: * public functions
! 35: */
! 36: sqlite_database_t public;
! 37:
! 38: /**
! 39: * sqlite database connection
! 40: */
! 41: sqlite3 *db;
! 42:
! 43: /**
! 44: * thread-specific transaction, as transaction_t
! 45: */
! 46: thread_value_t *transaction;
! 47:
! 48: /**
! 49: * mutex used to lock execute(), if necessary
! 50: */
! 51: mutex_t *mutex;
! 52: };
! 53:
! 54: /**
! 55: * Database transaction
! 56: */
! 57: typedef struct {
! 58:
! 59: /**
! 60: * Refcounter if transaction() is called multiple times
! 61: */
! 62: refcount_t refs;
! 63:
! 64: /**
! 65: * TRUE if transaction was rolled back
! 66: */
! 67: bool rollback;
! 68:
! 69: } transaction_t;
! 70:
! 71: /**
! 72: * Check if the SQLite library is thread safe
! 73: */
! 74: static bool is_threadsafe()
! 75: {
! 76: #if SQLITE_VERSION_NUMBER >= 3005000
! 77: return sqlite3_threadsafe() > 0;
! 78: #endif
! 79: /* sqlite connections prior to 3.5 may be used by a single thread only */
! 80: return FALSE;
! 81: }
! 82:
! 83: /**
! 84: * Create and run a sqlite stmt using a sql string and args
! 85: */
! 86: static sqlite3_stmt* run(private_sqlite_database_t *this, char *sql,
! 87: va_list *args)
! 88: {
! 89: sqlite3_stmt *stmt = NULL;
! 90: int params, i, res = SQLITE_OK;
! 91:
! 92: #ifdef HAVE_SQLITE3_PREPARE_V2
! 93: if (sqlite3_prepare_v2(this->db, sql, -1, &stmt, NULL) == SQLITE_OK)
! 94: #else
! 95: if (sqlite3_prepare(this->db, sql, -1, &stmt, NULL) == SQLITE_OK)
! 96: #endif
! 97: {
! 98: params = sqlite3_bind_parameter_count(stmt);
! 99: for (i = 1; i <= params; i++)
! 100: {
! 101: switch (va_arg(*args, db_type_t))
! 102: {
! 103: case DB_INT:
! 104: {
! 105: res = sqlite3_bind_int(stmt, i, va_arg(*args, int));
! 106: break;
! 107: }
! 108: case DB_UINT:
! 109: {
! 110: res = sqlite3_bind_int64(stmt, i, va_arg(*args, u_int));
! 111: break;
! 112: }
! 113: case DB_TEXT:
! 114: {
! 115: const char *text = va_arg(*args, const char*);
! 116: res = sqlite3_bind_text(stmt, i, text, -1,
! 117: SQLITE_TRANSIENT);
! 118: break;
! 119: }
! 120: case DB_BLOB:
! 121: {
! 122: chunk_t c = va_arg(*args, chunk_t);
! 123: res = sqlite3_bind_blob(stmt, i, c.ptr, c.len,
! 124: SQLITE_TRANSIENT);
! 125: break;
! 126: }
! 127: case DB_DOUBLE:
! 128: {
! 129: res = sqlite3_bind_double(stmt, i, va_arg(*args, double));
! 130: break;
! 131: }
! 132: case DB_NULL:
! 133: {
! 134: res = sqlite3_bind_null(stmt, i);
! 135: break;
! 136: }
! 137: default:
! 138: {
! 139: res = SQLITE_MISUSE;
! 140: break;
! 141: }
! 142: }
! 143: if (res != SQLITE_OK)
! 144: {
! 145: break;
! 146: }
! 147: }
! 148: }
! 149: else
! 150: {
! 151: DBG1(DBG_LIB, "preparing sqlite statement failed: %s",
! 152: sqlite3_errmsg(this->db));
! 153: }
! 154: if (res != SQLITE_OK)
! 155: {
! 156: DBG1(DBG_LIB, "binding sqlite statement failed: %s",
! 157: sqlite3_errmsg(this->db));
! 158: sqlite3_finalize(stmt);
! 159: return NULL;
! 160: }
! 161: return stmt;
! 162: }
! 163:
! 164: typedef struct {
! 165: /** implements enumerator_t */
! 166: enumerator_t public;
! 167: /** associated sqlite statement */
! 168: sqlite3_stmt *stmt;
! 169: /** number of result columns */
! 170: int count;
! 171: /** column types */
! 172: db_type_t *columns;
! 173: /** back reference to parent */
! 174: private_sqlite_database_t *database;
! 175: } sqlite_enumerator_t;
! 176:
! 177: METHOD(enumerator_t, sqlite_enumerator_destroy, void,
! 178: sqlite_enumerator_t *this)
! 179: {
! 180: sqlite3_finalize(this->stmt);
! 181: if (!is_threadsafe())
! 182: {
! 183: this->database->mutex->unlock(this->database->mutex);
! 184: }
! 185: free(this->columns);
! 186: free(this);
! 187: }
! 188:
! 189: METHOD(enumerator_t, sqlite_enumerator_enumerate, bool,
! 190: sqlite_enumerator_t *this, va_list args)
! 191: {
! 192: int i;
! 193:
! 194: switch (sqlite3_step(this->stmt))
! 195: {
! 196: case SQLITE_ROW:
! 197: break;
! 198: default:
! 199: DBG1(DBG_LIB, "stepping sqlite statement failed: %s",
! 200: sqlite3_errmsg(this->database->db));
! 201: /* fall */
! 202: case SQLITE_DONE:
! 203: return FALSE;
! 204: }
! 205:
! 206: for (i = 0; i < this->count; i++)
! 207: {
! 208: switch (this->columns[i])
! 209: {
! 210: case DB_INT:
! 211: {
! 212: int *value = va_arg(args, int*);
! 213: *value = sqlite3_column_int(this->stmt, i);
! 214: break;
! 215: }
! 216: case DB_UINT:
! 217: {
! 218: u_int *value = va_arg(args, u_int*);
! 219: *value = (u_int)sqlite3_column_int64(this->stmt, i);
! 220: break;
! 221: }
! 222: case DB_TEXT:
! 223: {
! 224: const unsigned char **value = va_arg(args, const unsigned char**);
! 225: *value = sqlite3_column_text(this->stmt, i);
! 226: break;
! 227: }
! 228: case DB_BLOB:
! 229: {
! 230: chunk_t *chunk = va_arg(args, chunk_t*);
! 231: chunk->len = sqlite3_column_bytes(this->stmt, i);
! 232: chunk->ptr = (u_char*)sqlite3_column_blob(this->stmt, i);
! 233: break;
! 234: }
! 235: case DB_DOUBLE:
! 236: {
! 237: double *value = va_arg(args, double*);
! 238: *value = sqlite3_column_double(this->stmt, i);
! 239: break;
! 240: }
! 241: default:
! 242: DBG1(DBG_LIB, "invalid result type supplied");
! 243: return FALSE;
! 244: }
! 245: }
! 246: return TRUE;
! 247: }
! 248:
! 249: METHOD(database_t, query, enumerator_t*,
! 250: private_sqlite_database_t *this, char *sql, ...)
! 251: {
! 252: sqlite3_stmt *stmt;
! 253: va_list args;
! 254: sqlite_enumerator_t *enumerator = NULL;
! 255: int i;
! 256:
! 257: if (!is_threadsafe())
! 258: {
! 259: this->mutex->lock(this->mutex);
! 260: }
! 261:
! 262: va_start(args, sql);
! 263: stmt = run(this, sql, &args);
! 264: if (stmt)
! 265: {
! 266: INIT(enumerator,
! 267: .public = {
! 268: .enumerate = enumerator_enumerate_default,
! 269: .venumerate = _sqlite_enumerator_enumerate,
! 270: .destroy = _sqlite_enumerator_destroy,
! 271: },
! 272: .stmt = stmt,
! 273: .count = sqlite3_column_count(stmt),
! 274: .database = this,
! 275: );
! 276: enumerator->columns = malloc(sizeof(db_type_t) * enumerator->count);
! 277: for (i = 0; i < enumerator->count; i++)
! 278: {
! 279: enumerator->columns[i] = va_arg(args, db_type_t);
! 280: }
! 281: }
! 282: va_end(args);
! 283: return (enumerator_t*)enumerator;
! 284: }
! 285:
! 286: METHOD(database_t, execute, int,
! 287: private_sqlite_database_t *this, int *rowid, char *sql, ...)
! 288: {
! 289: sqlite3_stmt *stmt;
! 290: int affected = -1;
! 291: va_list args;
! 292:
! 293: /* we need a lock to get our rowid/changes correctly */
! 294: this->mutex->lock(this->mutex);
! 295: va_start(args, sql);
! 296: stmt = run(this, sql, &args);
! 297: va_end(args);
! 298: if (stmt)
! 299: {
! 300: if (sqlite3_step(stmt) == SQLITE_DONE)
! 301: {
! 302: if (rowid)
! 303: {
! 304: *rowid = sqlite3_last_insert_rowid(this->db);
! 305: }
! 306: affected = sqlite3_changes(this->db);
! 307: }
! 308: else
! 309: {
! 310: DBG1(DBG_LIB, "sqlite execute failed: %s",
! 311: sqlite3_errmsg(this->db));
! 312: }
! 313: sqlite3_finalize(stmt);
! 314: }
! 315: this->mutex->unlock(this->mutex);
! 316: return affected;
! 317: }
! 318:
! 319: METHOD(database_t, transaction, bool,
! 320: private_sqlite_database_t *this, bool serializable)
! 321: {
! 322: transaction_t *trans;
! 323: char *cmd = serializable ? "BEGIN EXCLUSIVE TRANSACTION"
! 324: : "BEGIN TRANSACTION";
! 325:
! 326: trans = this->transaction->get(this->transaction);
! 327: if (trans)
! 328: {
! 329: ref_get(&trans->refs);
! 330: return TRUE;
! 331: }
! 332: if (execute(this, NULL, cmd) == -1)
! 333: {
! 334: return FALSE;
! 335: }
! 336: INIT(trans,
! 337: .refs = 1,
! 338: );
! 339: this->transaction->set(this->transaction, trans);
! 340: return TRUE;
! 341: }
! 342:
! 343: /**
! 344: * Finalize a transaction depending on the reference count and if it should be
! 345: * rolled back.
! 346: */
! 347: static bool finalize_transaction(private_sqlite_database_t *this,
! 348: bool rollback)
! 349: {
! 350: transaction_t *trans;
! 351: char *command = "COMMIT TRANSACTION";
! 352: bool success;
! 353:
! 354: trans = this->transaction->get(this->transaction);
! 355: if (!trans)
! 356: {
! 357: DBG1(DBG_LIB, "no database transaction found");
! 358: return FALSE;
! 359: }
! 360:
! 361: if (ref_put(&trans->refs))
! 362: {
! 363: if (trans->rollback)
! 364: {
! 365: command = "ROLLBACK TRANSACTION";
! 366: }
! 367: success = execute(this, NULL, command) != -1;
! 368:
! 369: this->transaction->set(this->transaction, NULL);
! 370: free(trans);
! 371: return success;
! 372: }
! 373: else
! 374: { /* set flag, can't be unset */
! 375: trans->rollback |= rollback;
! 376: }
! 377: return TRUE;
! 378: }
! 379:
! 380: METHOD(database_t, commit_, bool,
! 381: private_sqlite_database_t *this)
! 382: {
! 383: return finalize_transaction(this, FALSE);
! 384: }
! 385:
! 386: METHOD(database_t, rollback, bool,
! 387: private_sqlite_database_t *this)
! 388: {
! 389: return finalize_transaction(this, TRUE);
! 390: }
! 391:
! 392: METHOD(database_t, get_driver, db_driver_t,
! 393: private_sqlite_database_t *this)
! 394: {
! 395: return DB_SQLITE;
! 396: }
! 397:
! 398: /**
! 399: * Busy handler implementation
! 400: */
! 401: static int busy_handler(private_sqlite_database_t *this, int count)
! 402: {
! 403: /* add a backoff time, quadratically increasing with every try */
! 404: usleep(count * count * 1000);
! 405: /* always retry */
! 406: return 1;
! 407: }
! 408:
! 409: METHOD(database_t, destroy, void,
! 410: private_sqlite_database_t *this)
! 411: {
! 412: if (sqlite3_close(this->db) == SQLITE_BUSY)
! 413: {
! 414: DBG1(DBG_LIB, "sqlite close failed because database is busy");
! 415: }
! 416: this->transaction->destroy(this->transaction);
! 417: this->mutex->destroy(this->mutex);
! 418: free(this);
! 419: }
! 420:
! 421: /*
! 422: * see header file
! 423: */
! 424: sqlite_database_t *sqlite_database_create(char *uri)
! 425: {
! 426: char *file;
! 427: private_sqlite_database_t *this;
! 428:
! 429: /**
! 430: * parse sqlite:///path/to/file.db uri
! 431: */
! 432: if (!strpfx(uri, "sqlite://"))
! 433: {
! 434: return NULL;
! 435: }
! 436: file = uri + 9;
! 437:
! 438: INIT(this,
! 439: .public = {
! 440: .db = {
! 441: .query = _query,
! 442: .execute = _execute,
! 443: .transaction = _transaction,
! 444: .commit = _commit_,
! 445: .rollback = _rollback,
! 446: .get_driver = _get_driver,
! 447: .destroy = _destroy,
! 448: },
! 449: },
! 450: .mutex = mutex_create(MUTEX_TYPE_RECURSIVE),
! 451: .transaction = thread_value_create(NULL),
! 452: );
! 453:
! 454: if (sqlite3_open(file, &this->db) != SQLITE_OK)
! 455: {
! 456: DBG1(DBG_LIB, "opening SQLite database '%s' failed: %s",
! 457: file, sqlite3_errmsg(this->db));
! 458: destroy(this);
! 459: return NULL;
! 460: }
! 461:
! 462: sqlite3_busy_handler(this->db, (void*)busy_handler, this);
! 463:
! 464: return &this->public;
! 465: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>