1 /** 2 * Authors: Jason White, Alexey Khmara 3 * 4 * Description: Wraps the SQLite3 C API in D goodness. 5 * This is an updated version of $(WEB https://github.com/bayun/SQLite3-D). 6 */ 7 module util.sqlite3; 8 9 import etc.c.sqlite3; 10 import std..string, std.exception; 11 12 13 /** 14 * This is thrown if something went wrong in SQLite3. 15 */ 16 class SQLite3Exception : Exception 17 { 18 // SQLite3 error code. 19 // See https://www.sqlite.org/rescode.html 20 int code; 21 22 this(int code, string msg, string file = __FILE__, size_t line = __LINE__) 23 { 24 super(msg, file, line); 25 this.code = code; 26 } 27 28 this(sqlite3 *db, string msg = null, string file = __FILE__, size_t line = __LINE__) 29 { 30 super(fromStringz(sqlite3_errmsg(db)), file, line); 31 this.code = sqlite3_errcode(db); 32 } 33 } 34 35 T sqliteEnforce(T, string file = __FILE__, size_t line = __LINE__) 36 (T value, sqlite3 *db, lazy string msg = null) 37 { 38 // TODO: Use extended result codes instead. 39 if (!value) 40 throw new SQLite3Exception(db, msg, file, line); 41 return value; 42 } 43 44 45 /** 46 * SQLite3 database wrapper. 47 */ 48 class SQLite3 49 { 50 private sqlite3* db; 51 52 /** 53 * Opens the given database file. 54 */ 55 this(string file) 56 { 57 open(file); 58 } 59 60 /** 61 * Takes control of an existing database handle. The database will still be 62 * closed upon destruction. 63 */ 64 this(sqlite3* db) 65 { 66 this.db = db; 67 } 68 69 /** 70 * Closes the database. 71 */ 72 ~this() 73 { 74 close(); 75 } 76 77 /** 78 * Opens or creates a database. 79 */ 80 void open(string file) 81 { 82 close(); 83 84 auto r = sqlite3_open(&(toStringz(file))[0], &db); 85 if (r != SQLITE_OK) { 86 throw new SQLite3Exception(r, "Cannot open database " ~ file); 87 } 88 } 89 90 /** 91 * Takes control of an existing database handle. The database will still be 92 * closed upon destruction. 93 */ 94 void open(sqlite3* db) 95 { 96 close(); 97 this.db = db; 98 } 99 100 /** 101 * Closes the database. 102 */ 103 void close() 104 { 105 if (db) 106 { 107 sqlite3_close(db); 108 db = null; 109 } 110 } 111 112 /** 113 * Convenience functions for beginning, committing, or rolling back a 114 * transaction. 115 */ 116 void begin() 117 { 118 execute("BEGIN"); 119 } 120 121 /// Ditto 122 void commit() 123 { 124 execute("COMMIT"); 125 } 126 127 /// Ditto 128 void rollback() 129 { 130 execute("ROLLBACK"); 131 } 132 133 /** 134 * Returns the internal handle to the SQLite3 database. This should only be 135 * used if this class does not provide the necessary functionality. 136 */ 137 @property sqlite3* handle() { return db; } 138 139 /** 140 * Prepare SQL statement for multiple execution or for parameters binding. 141 * 142 * If $(D args) are given, they are bound before return, so client can 143 * immediately call step() to get rows. 144 */ 145 Statement prepare(T...)(string sql, const auto ref T args) 146 { 147 debug (SQLite) 148 { 149 import std.stdio; 150 writeln("Prepared Statement: ", sql); 151 foreach(arg; args) 152 writefln(" %s: %s", typeof(arg).stringof, arg); 153 } 154 155 auto s = new Statement(sql); 156 if (args.length) s.bind(args); 157 return s; 158 } 159 160 /** 161 * Like $(D prepare), but ignores results and returns the number of changed 162 * rows. 163 */ 164 uint execute(T...)(string sql, const auto ref T args) 165 { 166 auto s = prepare(sql, args); 167 168 s.step(); 169 170 if (s.columns > 0) 171 return this.changes; 172 173 return 0; 174 } 175 176 /** 177 * Returns the ID of the last row that was inserted. 178 */ 179 @property ulong lastInsertId() 180 { 181 return sqlite3_last_insert_rowid(db); 182 } 183 184 /** 185 * Returns the number of rows changed by the last statement. 186 */ 187 @property uint changes() 188 { 189 return cast(uint)sqlite3_changes(db); 190 } 191 192 /** 193 * The database is accessed using statements. 194 * 195 * First, a statement is prepared from a SQL query. Then, values are bound to 196 * the parameters in the statement using $(D bind). Finally, the statement is 197 * executed using $(D step). 198 */ 199 class Statement 200 { 201 private sqlite3_stmt *_stmt; 202 203 /** 204 * Compiles the SQL statement. Values can then be bound to the 205 * parameters of the statement using $(D bind). 206 */ 207 this(string sql) 208 { 209 auto r = sqlite3_prepare_v2( 210 db, toStringz(sql), cast(int)sql.length, &_stmt, null 211 ); 212 213 sqliteEnforce(r == SQLITE_OK, db); 214 } 215 216 ~this() 217 { 218 sqlite3_finalize(_stmt); 219 } 220 221 /** 222 * Returns the internal SQLite3 statement handle. This should only be 223 * used if this class does not provide the necessary functionality. 224 */ 225 @property sqlite3_stmt* handle() { return _stmt; } 226 227 /** 228 * Returns the number of columns in the result set. This number will be 229 * 0 if there is no result set (e.g., INSERT, UPDATE, CREATE TABLE). 230 */ 231 @property uint columns() 232 { 233 return cast(uint)sqlite3_column_count(_stmt); 234 } 235 236 /** 237 * Returns the SQL statement string. 238 */ 239 @property string sql() { return fromStringz(sqlite3_sql(_stmt)); } 240 241 /** 242 * Binds a value to the statement at a particular index. Indices start 243 * at 0. 244 */ 245 void opIndexAssign(T : int)(T v, uint i) 246 { 247 sqliteEnforce(sqlite3_bind_int(_stmt, i+1, v) == SQLITE_OK, db); 248 } 249 250 /// Ditto 251 void opIndexAssign(T : uint)(T v, uint i) 252 { 253 sqliteEnforce(sqlite3_bind_int(_stmt, i+1, v) == SQLITE_OK, db); 254 } 255 256 /// Ditto 257 void opIndexAssign(T : long)(T v, uint i) 258 { 259 sqliteEnforce(sqlite3_bind_int64(_stmt, i+1, v) == SQLITE_OK, db); 260 } 261 262 /// Ditto 263 void opIndexAssign(T : ulong)(T v, uint i) 264 { 265 sqliteEnforce(sqlite3_bind_int64(_stmt, i+1, v) == SQLITE_OK, db); 266 } 267 268 /// Ditto 269 void opIndexAssign(T : double)(T v, uint i) 270 { 271 sqliteEnforce(sqlite3_bind_double(_stmt, i+1, v) == SQLITE_OK, db); 272 } 273 274 /// Ditto 275 void opIndexAssign(T : const(char)[])(T v, uint i) 276 { 277 import std.conv : to; 278 sqliteEnforce(sqlite3_bind_text(_stmt, i+1, toStringz(v), 279 v.length.to!int, SQLITE_TRANSIENT) == SQLITE_OK, db); 280 } 281 282 /// Ditto 283 void opIndexAssign(const(ubyte)[] v, uint i) 284 { 285 import std.conv : to; 286 sqliteEnforce(sqlite3_bind_blob(_stmt, i+1, v.ptr, 287 v.length.to!int, SQLITE_TRANSIENT) == SQLITE_OK, db); 288 } 289 290 /// Ditto 291 void opIndexAssign(T : typeof(null))(T v, uint i) 292 { 293 sqliteEnforce(sqlite3_bind_null(_stmt, i+1) == SQLITE_OK, db); 294 } 295 296 version (none) 297 { 298 // FIXME: These require bind_*64() functions which, at this time, 299 // are not in DMD. 300 void opIndexAssign(T : const(string))(T v, uint i) 301 { 302 sqliteEnforce(sqlite3_bind_text64(_stmt, i+1, toStringz(v), 303 v.length, SQLITE_TRANSIENT, SQLITE_UTF8) == SQLITE_OK, db); 304 } 305 306 void opIndexAssign(T : const(wstring))(T v, uint i) 307 { 308 sqliteEnforce(sqlite3_bind_text64(_stmt, i+1, toStringz(v), 309 v.length * wchar.sizeof, SQLITE_TRANSIENT, SQLITE_UTF16) == SQLITE_OK, db); 310 } 311 312 void opIndexAssign(in ubyte[] v, uint i) 313 { 314 // FIXME: void* has no length property 315 sqliteEnforce(sqlite3_bind_blob64(_stmt, i+1, v, 316 v.length, SQLITE_TRANSIENT) == SQLITE_OK, db); 317 } 318 } 319 320 /** 321 * Gets the index of the bind parameter $(D name). 322 */ 323 uint opIndex(string name) 324 { 325 int pos = sqlite3_bind_parameter_index(_stmt, toStringz(name)); 326 sqliteEnforce(pos <= 0, db, "Invalid bind parameter '"~ name ~"'"); 327 return cast(uint)(pos - 1); 328 } 329 330 /** 331 * Binds a value by name. 332 */ 333 void opIndexAssign(T)(const auto ref T v, string name) 334 { 335 this[this[name]] = v; 336 } 337 338 /** 339 * Bind multiple values to the statement. 340 */ 341 void bind(T...)(const auto ref T args) 342 { 343 foreach (i, arg; args) 344 this[i] = arg; 345 } 346 347 /** 348 * Steps through the results of the statement. Returns true while there 349 * are results or false if there are no more results. 350 * 351 * Throws: $(D SQLite3Exception) if an error occurs. 352 */ 353 bool step() 354 { 355 int r = sqlite3_step(_stmt); 356 if (r == SQLITE_ROW) 357 return true; 358 else if (r == SQLITE_DONE) 359 return false; 360 else 361 throw new SQLite3Exception(db); 362 } 363 364 /** 365 * Gets the value of a column. 366 */ 367 T get(T)(uint i) if (is(T == int) || is(T == uint)) 368 { 369 return cast(T)sqlite3_column_int(_stmt, cast(int)i); 370 } 371 372 /// Ditto 373 T get(T)(uint i) if (is(T == long) || is(T == ulong)) 374 { 375 return cast(T)sqlite3_column_int64(_stmt, cast(int)i); 376 } 377 378 /// Ditto 379 T get(T)(uint i) if (is(T == double)) 380 { 381 return sqlite3_column_double(_stmt, cast(int)i); 382 } 383 384 /// Ditto 385 T get(T)(uint i) if (is(T == char[])) 386 { 387 auto s = sqlite3_column_text(_stmt, cast(int)i); 388 int l = sqlite3_column_bytes(_stmt, cast(int)i); 389 return s[0 .. l].dup; 390 } 391 392 /// Ditto 393 T get(T)(uint i) if (is(T : const(string))) 394 { 395 // We can safely cast here. 396 return cast(T)get!(char[])(i); 397 } 398 399 /// Ditto 400 const(void)[] get(T)(uint i) if (is(T == void[])) 401 { 402 auto v = sqlite3_column_blob(_stmt, cast(int)i); 403 int l = sqlite3_column_bytes(_stmt, cast(int)i); 404 return v[0 .. l]; 405 } 406 407 /** 408 * Gets the values in the row.. 409 */ 410 void getRow(T...)(ref T args) 411 { 412 foreach (i, arg; args) 413 args[i] = get!(typeof(arg))(i); 414 } 415 416 /** 417 * Returns true if the column index has a NULL value. 418 */ 419 bool isNull(uint i) 420 in { assert(i < columns); } 421 body 422 { 423 return sqlite3_column_type(_stmt, cast(int)i) == SQLITE_NULL; 424 } 425 426 /** 427 * Resets the execution of this statement. This must be called after $(D 428 * step) returns false. 429 * 430 * Note: Bindings are not reset too. 431 */ 432 void reset() 433 { 434 sqliteEnforce(sqlite3_reset(_stmt) == SQLITE_OK, db); 435 } 436 437 /** 438 * Sets all bindings to NULL. 439 */ 440 void clear() 441 { 442 sqliteEnforce(sqlite3_clear_bindings(_stmt) == SQLITE_OK, db); 443 } 444 } 445 } 446 447 /** 448 * Range that deserializes and returns row data. 449 */ 450 static struct Rows(alias read) 451 { 452 import std.traits : Unqual, ReturnType; 453 454 alias T = ReturnType!read; 455 456 private 457 { 458 SQLite3.Statement _statement; 459 bool _empty; 460 Unqual!T _data; 461 } 462 463 this(SQLite3.Statement statement) 464 { 465 _statement = statement; 466 467 // Prime the cannons 468 popFront(); 469 } 470 471 bool empty() const pure nothrow 472 { 473 return _empty; 474 } 475 476 void popFront() 477 { 478 if (!_statement.step()) 479 { 480 _empty = true; 481 return; 482 } 483 484 _data = read(_statement); 485 } 486 487 ref T front() pure nothrow 488 { 489 return _data; 490 } 491 } 492 493 /** 494 * Convenience function to return a range to iterate over rows in the 495 * statement. 496 */ 497 @property auto rows(alias read)(SQLite3.Statement s) 498 { 499 return Rows!read(s); 500 } 501 502 // Converts a C-string string to a D-string. 503 private string fromStringz(const(char)* s) 504 { 505 size_t i = 0; 506 while (s[i] != '\0') ++i; 507 return s[0 .. i].idup; 508 }