1 /** 2 * SQLite wrapper 3 * 4 * Copyright: 5 * (C) 2012 Jack Lloyd 6 * (C) 2014-2015 Etienne Cimon 7 * 8 * License: 9 * Botan is released under the Simplified BSD License (see LICENSE.md) 10 */ 11 module botan.utils.sqlite3.sqlite3; 12 13 import botan.constants; 14 static if (BOTAN_HAS_SQLITE): 15 16 import std.exception; 17 import etc.c.sqlite3; 18 import botan.utils.types; 19 import botan.utils.types; 20 import std..string : toStringz, fromStringz; 21 import std.datetime; 22 23 class sqlite3_database 24 { 25 public: 26 this(in string db_filename) 27 { 28 int rc = sqlite3_open(db_filename.toStringz, &m_db); 29 if (!rc) 30 sqlite3_busy_timeout(m_db, 500); 31 if (rc) 32 { 33 const string err_msg = fromStringz(sqlite3_errmsg(m_db)).to!string; 34 sqlite3_close(m_db); 35 m_db = null; 36 throw new Exception("sqlite3_open failed - " ~ err_msg); 37 } 38 } 39 this(in string file); 40 41 ~this() 42 { 43 if (m_db) 44 sqlite3_close(m_db); 45 m_db = null; 46 } 47 48 size_t rowCount(in string table_name) 49 { 50 sqlite3_statement stmt = sqlite3_statement(this, "select count(*) from " ~ table_name); 51 52 if (stmt.step()) 53 return stmt.getSizeT(0); 54 else 55 throw new Exception("Querying size of table " ~ table_name ~ " failed"); 56 } 57 58 void createTable(in string table_schema) 59 { 60 char* errmsg = null; 61 int rc; 62 int iter; 63 import core.thread; 64 do { 65 rc = sqlite3_exec(m_db, table_schema.toStringz, null, null, &errmsg); 66 if (rc != SQLITE_OK) { 67 Thread.sleep(30.msecs); 68 continue; 69 } 70 } while (++iter < 5); 71 72 if (rc != SQLITE_OK) { 73 import std.conv : to; 74 const string err_msg = fromStringz(errmsg).to!string; 75 sqlite3_free(errmsg); 76 sqlite3_close(m_db); 77 m_db = null; 78 throw new Exception("sqlite3_exec for table failed - " ~ err_msg); 79 } 80 } 81 private: 82 sqlite3* m_db; 83 } 84 85 struct sqlite3_statement 86 { 87 public: 88 this(sqlite3_database db, in string base_sql) 89 { 90 int rc = sqlite3_prepare_v2(db.m_db, base_sql.toStringz, -1, &m_stmt, null); 91 92 if (rc != SQLITE_OK) 93 throw new Exception("sqlite3_prepare failed " ~ base_sql ~ ", code " ~ to!string(rc)); 94 } 95 96 void bind(int column, in string val) 97 { 98 int rc = sqlite3_bind_text(m_stmt, column, val.toStringz, -1, SQLITE_TRANSIENT); 99 if (rc != SQLITE_OK) 100 throw new Exception("sqlite3_bind_text failed, code " ~ to!string(rc)); 101 } 102 103 void bind(int column, int val) 104 { 105 int rc = sqlite3_bind_int(m_stmt, column, val); 106 if (rc != SQLITE_OK) 107 throw new Exception("sqlite3_bind_int failed, code " ~ to!string(rc)); 108 } 109 110 void bind(int column, SysTime time) 111 { 112 const int timeval = cast(int)time.toUnixTime(); 113 bind(column, timeval); 114 } 115 116 void bind()(int column, auto const ref Vector!ubyte val) 117 { 118 int rc = sqlite3_bind_blob(m_stmt, column, cast(void*)val.ptr, cast(int)val.length, SQLITE_TRANSIENT); 119 if (rc != SQLITE_OK) 120 throw new Exception("sqlite3_bind_text failed, code " ~ to!string(rc)); 121 } 122 123 Pair!(const(ubyte)*, size_t) getBlob(int column) 124 { 125 assert(sqlite3_column_type(m_stmt, 0) == SQLITE_BLOB, 126 "Return value is a blob"); 127 128 const void* session_blob = sqlite3_column_blob(m_stmt, column); 129 const int session_blob_size = sqlite3_column_bytes(m_stmt, column); 130 131 assert(session_blob_size >= 0, "Blob size is non-negative"); 132 133 return makePair(cast(const(ubyte)*)(session_blob), 134 cast(size_t)(session_blob_size)); 135 } 136 137 size_t getSizeT(int column) 138 { 139 assert(sqlite3_column_type(m_stmt, column) == SQLITE_INTEGER, 140 "Return count is an integer"); 141 142 const int sessions_int = sqlite3_column_int(m_stmt, column); 143 144 assert(sessions_int >= 0, "Expected size_t is non-negative"); 145 146 return cast(size_t)(sessions_int); 147 } 148 149 void spin() 150 { 151 while (step()) {} 152 } 153 154 bool step() 155 { 156 return (sqlite3_step(m_stmt) == SQLITE_ROW); 157 } 158 159 sqlite3_stmt* stmt() { return m_stmt; } 160 161 ~this() 162 { 163 sqlite3_finalize(m_stmt); 164 } 165 private: 166 sqlite3_stmt* m_stmt; 167 }