1 module utils.db.sqlite; 2 3 import 4 std.conv, 5 std.meta, 6 std.array, 7 std..string, 8 std.traits, 9 std.typecons, 10 std.exception, 11 std.algorithm, 12 13 etc.c.sqlite3, 14 utils.except; 15 16 17 final class SQLite 18 { 19 this(string name) 20 { 21 sqlite3_open(name.toStringz, &_db) == SQLITE_OK || throwError(lastError); 22 } 23 24 ~this() 25 { 26 _stmts.byValue.each!(a => remove(a)); 27 sqlite3_close(_db); 28 } 29 30 void backup(SQLite dest) 31 { 32 auto bk = sqlite3_backup_init(dest._db, `main`, _db, `main`); 33 bk || throwError(dest.lastError); 34 35 scope(exit) 36 { 37 sqlite3_backup_finish(bk); 38 } 39 40 auto rc = sqlite3_backup_step(bk, -1); 41 rc == SQLITE_DONE || throwError!`error backuping db: %s`(rc); 42 } 43 44 package: 45 void process(sqlite3_stmt* stmt) 46 { 47 execute(stmt); 48 sqlite3_reset(stmt); 49 } 50 51 auto process(A...)(sqlite3_stmt* stmt) 52 { 53 auto self = this; // TODO: DMD BUG 54 55 struct S 56 { 57 this(this) @disable; 58 59 ~this() 60 { 61 sqlite3_reset(stmt); 62 } 63 64 const empty() 65 { 66 return !_hasRow; 67 } 68 69 void popFront() 70 { 71 assert(!empty); 72 _hasRow = self.execute(stmt); 73 } 74 75 auto array() 76 { 77 ReturnType!front[] res; 78 79 for(; _hasRow; popFront) 80 { 81 res ~= front; 82 } 83 84 return res; 85 } 86 87 auto front() 88 { 89 assert(_hasRow); 90 91 Tuple!A r; 92 93 foreach(i, ref v; r) 94 { 95 alias T = A[i]; 96 97 v = sqlite3_column_text(stmt, i).fromStringz.to!T; 98 } 99 100 static if(A.length > 1) 101 { 102 return r; 103 } 104 else 105 { 106 return r[0]; 107 } 108 } 109 110 private: 111 bool _hasRow; 112 } 113 114 return S(execute(stmt)); 115 } 116 117 auto prepare(string sql) 118 { 119 auto stmt = _stmts.get(sql, null); 120 121 if(!stmt) 122 { 123 sqlite3_prepare_v2(_db, sql.toStringz, cast(int)sql.length, &stmt, null) == SQLITE_OK || throwError(lastError); 124 _stmts[sql] = stmt; 125 } 126 127 return stmt; 128 } 129 130 void bind(A...)(sqlite3_stmt* stmt, A args) 131 { 132 foreach(uint i, v; args) 133 { 134 int res; 135 136 alias T = typeof(v); 137 auto idx = i + 1; 138 139 static if(is(T == typeof(null))) 140 { 141 res = sqlite3_bind_null(stmt, idx); 142 } 143 else static if(isFloatingPoint!T) 144 { 145 res = sqlite3_bind_double(stmt, idx, v); 146 } 147 else static if(isIntegral!T) 148 { 149 res = sqlite3_bind_int64(stmt, idx, v); 150 } 151 else static if(isSomeString!T) 152 { 153 res = sqlite3_bind_text(stmt, idx, v.toStringz, cast(int)v.length, SQLITE_TRANSIENT); 154 } 155 else 156 { 157 static assert(false); 158 } 159 160 res == SQLITE_OK || throwError(lastError); 161 } 162 } 163 164 auto lastId(sqlite3_stmt*) 165 { 166 return sqlite3_last_insert_rowid(_db); 167 } 168 169 auto affected(sqlite3_stmt*) 170 { 171 return sqlite3_changes(_db); 172 } 173 174 private: 175 void remove(sqlite3_stmt* stmt) 176 { 177 sqlite3_finalize(stmt); 178 } 179 180 bool execute(sqlite3_stmt* stmt) 181 { 182 auto res = sqlite3_step(stmt); 183 res == SQLITE_ROW || res == SQLITE_DONE || throwError(lastError); 184 return res == SQLITE_ROW; 185 } 186 187 auto lastError() 188 { 189 return sqlite3_errmsg(_db).fromStringz; 190 } 191 192 sqlite3* _db; 193 sqlite3_stmt*[string] _stmts; 194 }