1 /++ 2 Managing SQLite3 database connections. 3 4 Authors: 5 Nicolas Sicard (biozic) and other contributors at $(LINK https://github.com/biozic/d2sqlite3) 6 7 Copyright: 8 Copyright 2011-18 Nicolas Sicard. 9 10 License: 11 $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0). 12 +/ 13 module d2sqlite3.database; 14 15 import d2sqlite3.statement; 16 import d2sqlite3.results; 17 import d2sqlite3.sqlite3; 18 import d2sqlite3.internal.memory; 19 import d2sqlite3.internal.util; 20 21 import std.conv : text, to; 22 import std.exception : enforce; 23 import std.string : format, fromStringz, toStringz; 24 import std.typecons : Nullable; 25 26 import core.stdc.stdlib : free; 27 28 /// Set _UnlockNotify version if compiled with SqliteEnableUnlockNotify or SqliteFakeUnlockNotify 29 version (SqliteEnableUnlockNotify) 30 version = _UnlockNotify; 31 else version (SqliteFakeUnlockNotify) 32 version = _UnlockNotify; 33 34 /// Type for the internal representation of blobs 35 alias Blob = immutable(ubyte)[]; 36 37 /// SQLite type codes 38 enum SqliteType { 39 INTEGER = SQLITE_INTEGER, /// 40 FLOAT = SQLITE_FLOAT, /// 41 TEXT = SQLITE3_TEXT, /// 42 BLOB = SQLITE_BLOB, /// 43 NULL = SQLITE_NULL /// 44 } 45 46 /++ 47 A caracteristic of user-defined functions or aggregates. 48 +/ 49 enum Deterministic { 50 /++ 51 The returned value is the same if the function is called with the same parameters. 52 +/ 53 yes = 0x800, 54 55 /++ 56 The returned value can vary even if the function is called with the same parameters. 57 +/ 58 no = 0 59 } 60 61 /++ 62 An database connection. 63 64 This struct is a reference-counted wrapper around a `sqlite3*` pointer. 65 +/ 66 struct Database { 67 import std.traits : isFunctionPointer, isDelegate; 68 import std.typecons : RefCounted, RefCountedAutoInitialize; 69 70 private: 71 struct Payload { 72 sqlite3* handle; 73 void* updateHook; 74 void* commitHook; 75 void* rollbackHook; 76 void* progressHandler; 77 void* traceCallback; 78 void* profileCallback; 79 version (_UnlockNotify) IUnlockNotifyHandler unlockNotifyHandler; 80 debug string filename; 81 82 this(sqlite3* handle) nothrow { 83 this.handle = handle; 84 } 85 86 ~this() nothrow { 87 debug ensureNotInGC!Database(filename); 88 free(updateHook); 89 free(commitHook); 90 free(rollbackHook); 91 free(progressHandler); 92 free(traceCallback); 93 free(profileCallback); 94 95 if (!handle) 96 return; 97 sqlite3_progress_handler(handle, 0, null, null); 98 sqlite3_close(handle); 99 } 100 } 101 102 RefCounted!(Payload, RefCountedAutoInitialize.no) p; 103 104 void check(int result) { 105 enforce(result == SQLITE_OK, new SqliteException(errmsg(p.handle), result)); 106 } 107 108 public: 109 /++ 110 Opens a database connection. 111 112 Params: 113 path = The path to the database file. In recent versions of SQLite, the path can be 114 an URI with options. 115 116 flags = Options flags. 117 118 See_Also: $(LINK http://www.sqlite.org/c3ref/open.html) to know how to use the flags 119 parameter or to use path as a file URI if the current configuration allows it. 120 +/ 121 this(string path, int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE) { 122 sqlite3* hdl; 123 auto result = sqlite3_open_v2(path.toStringz, &hdl, flags, null); 124 enforce(result == SQLITE_OK, new SqliteException(hdl 125 ? errmsg(hdl) : "Error opening the database", result)); 126 p = Payload(hdl); 127 debug p.filename = path; 128 } 129 130 /++ 131 Explicitly closes the database connection. 132 133 After a successful call to `close()`, using the database connection or one of its prepared 134 statement is an error. The `Database` object is destroyed and cannot be used any more. 135 +/ 136 void close() { 137 auto result = sqlite3_close(p.handle); 138 enforce(result == SQLITE_OK, new SqliteException(errmsg(p.handle), result)); 139 p.handle = null; 140 destroy(p); 141 } 142 143 /++ 144 Gets the SQLite internal _handle of the database connection. 145 +/ 146 sqlite3* handle() @property nothrow { 147 return p.handle; 148 } 149 150 /++ 151 Gets the path associated with an attached database. 152 153 Params: 154 database = The name of an attached database. 155 156 Returns: The absolute path of the attached database. 157 If there is no attached database, or if database is a temporary or 158 in-memory database, then null is returned. 159 +/ 160 string attachedFilePath(string database = "main") { 161 assert(p.handle); 162 return sqlite3_db_filename(p.handle, database.toStringz).to!string; 163 } 164 165 /++ 166 Gets the read-only status of an attached database. 167 168 Params: 169 database = The name of an attached database. 170 +/ 171 bool isReadOnly(string database = "main") { 172 assert(p.handle); 173 immutable ret = sqlite3_db_readonly(p.handle, database.toStringz); 174 enforce(ret >= 0, new SqliteException("Database not found: %s".format(database), ret)); 175 return ret == 1; 176 } 177 178 /++ 179 Gets metadata for a specific table column of an attached database. 180 181 Params: 182 table = The name of the table. 183 184 column = The name of the column. 185 186 database = The name of a database attached. If null, then all attached databases 187 are searched for the table using the same algorithm used by the database engine 188 to resolve unqualified table references. 189 +/ 190 TableColumnMetadata tableColumnMetadata(string table, string column, string database = "main") { 191 TableColumnMetadata data; 192 char* pzDataType, pzCollSeq; 193 int notNull, primaryKey, autoIncrement; 194 assert(p.handle); 195 check(sqlite3_table_column_metadata(p.handle, database.toStringz, table.toStringz, 196 column.toStringz, &pzDataType, &pzCollSeq, ¬Null, 197 &primaryKey, &autoIncrement)); 198 data.declaredTypeName = pzDataType.to!string; 199 data.collationSequenceName = pzCollSeq.to!string; 200 data.isNotNull = cast(bool) notNull; 201 data.isPrimaryKey = cast(bool) primaryKey; 202 data.isAutoIncrement = cast(bool) autoIncrement; 203 return data; 204 } 205 206 /++ 207 Executes a single SQL statement and returns the results directly. 208 209 It's the equivalent of `prepare(sql).execute()`. 210 Or when used with args the equivalent of: 211 --- 212 auto stm = prepare(sql); 213 stm.bindAll(args); 214 stm.execute(); 215 --- 216 217 The results become undefined when the Database goes out of scope and is destroyed. 218 219 Params: 220 sql = The code of the SQL statement. 221 args = Optional arguments to bind to the SQL statement. 222 +/ 223 ResultRange execute(Args...)(string sql, Args args) { 224 auto stm = prepare(sql); 225 static if (Args.length) 226 stm.bindAll(args); 227 return stm.execute(); 228 } 229 /// 230 unittest { 231 auto db = Database(":memory:"); 232 db.execute("CREATE TABLE test (val INTEGER)"); 233 db.execute("INSERT INTO test (val) VALUES (:v)", 1); 234 assert(db.execute("SELECT val FROM test WHERE val=:v", 1).oneValue!int == 1); 235 } 236 237 /++ 238 Runs an SQL script that can contain multiple statements. 239 240 Params: 241 script = The code of the SQL script. 242 243 dg = A delegate to call for each statement to handle the results. The passed 244 ResultRange will be empty if a statement doesn't return rows. If the delegate 245 return false, the execution is aborted. 246 +/ 247 void run(string script, bool delegate(ResultRange) dg = null) { 248 foreach (sql; script.byStatement) { 249 auto stmt = prepare(sql); 250 auto results = stmt.execute(); 251 if (dg && !dg(results)) 252 return; 253 } 254 } 255 /// 256 unittest { 257 auto db = Database(":memory:"); 258 db.run(`CREATE TABLE test1 (val INTEGER); 259 CREATE TABLE test2 (val FLOAT); 260 DROP TABLE test1; 261 DROP TABLE test2;`); 262 } 263 264 /++ 265 Prepares (compiles) a single SQL statement and returns it, so that it can be bound to 266 values before execution. 267 268 The statement becomes invalid if the Database goes out of scope and is destroyed. 269 +/ 270 Statement prepare(string sql) { 271 return Statement(this, sql); 272 } 273 274 /// Convenience functions equivalent to an SQL statement. 275 void begin() { 276 execute("BEGIN"); 277 } 278 /// Ditto 279 void commit() { 280 execute("COMMIT"); 281 } 282 /// Ditto 283 void rollback() { 284 execute("ROLLBACK"); 285 } 286 287 /++ 288 Returns the rowid of the last INSERT statement. 289 +/ 290 long lastInsertRowid() { 291 assert(p.handle); 292 return sqlite3_last_insert_rowid(p.handle); 293 } 294 295 /++ 296 Gets the number of database rows that were changed, inserted or deleted by the most 297 recently executed SQL statement. 298 +/ 299 int changes() @property nothrow { 300 assert(p.handle); 301 return sqlite3_changes(p.handle); 302 } 303 304 /++ 305 Gets the number of database rows that were changed, inserted or deleted since the 306 database was opened. 307 +/ 308 int totalChanges() @property nothrow { 309 assert(p.handle); 310 return sqlite3_total_changes(p.handle); 311 } 312 313 /++ 314 Gets the SQLite error code of the last operation. 315 +/ 316 int errorCode() @property nothrow { 317 return p.handle ? sqlite3_errcode(p.handle) : 0; 318 } 319 320 /++ 321 Interrupts any pending database operations. 322 323 It's safe to call this function from anouther thread. 324 325 See_also: $(LINK http://www.sqlite.org/c3ref/interrupt.html). 326 +/ 327 void interrupt() { 328 assert(p.handle); 329 sqlite3_interrupt(p.handle); 330 } 331 332 /++ 333 Sets a connection configuration option. 334 335 See_Also: $(LINK http://www.sqlite.org/c3ref/db_config.html). 336 +/ 337 void config(Args...)(int code, Args args) { 338 assert(p.handle); 339 auto result = sqlite3_db_config(p.handle, code, args); 340 enforce(result == SQLITE_OK, 341 new SqliteException("Database configuration: error %s".format(result), result)); 342 } 343 344 /++ 345 Enables or disables loading extensions. 346 +/ 347 void enableLoadExtensions(bool enable = true) { 348 assert(p.handle); 349 immutable ret = sqlite3_enable_load_extension(p.handle, enable); 350 enforce(ret == SQLITE_OK, new SqliteException("Could not enable loading extensions.", ret)); 351 } 352 353 /++ 354 Loads an extension. 355 356 Params: 357 path = The path of the extension file. 358 359 entryPoint = The name of the entry point function. If null is passed, SQLite 360 uses the name of the extension file as the entry point. 361 +/ 362 void loadExtension(string path, string entryPoint = null) { 363 assert(p.handle); 364 char* p_err; 365 scope (failure) 366 sqlite3_free(p_err); 367 368 immutable ret = sqlite3_load_extension(p.handle, path.toStringz, 369 entryPoint.toStringz, &p_err); 370 enforce(ret == SQLITE_OK, new SqliteException("Could not load extension: %s:%s (%s)".format(entryPoint, 371 path, p_err !is null ? fromStringz(p_err) : "No additional info"), ret)); 372 } 373 374 /++ 375 Creates and registers a new function in the database. 376 377 If a function with the same name and the same arguments already exists, it is replaced 378 by the new one. 379 380 The memory associated with the function will be released when the database connection 381 is closed. 382 383 Params: 384 name = The name that the function will have in the database. 385 386 fun = a delegate or function that implements the function. $(D_PARAM fun) 387 must satisfy the following criteria: 388 $(UL 389 $(LI It must not be variadic.) 390 $(LI Its arguments must all have a type that is compatible with SQLite types: 391 it must be a boolean or numeric type, a string, an array, `null`, 392 or a `Nullable!T` where T is any of the previous types.) 393 $(LI Its return value must also be of a compatible type.) 394 ) 395 or 396 $(UL 397 $(LI It must be a normal or type-safe variadic function where the arguments 398 are of type `ColumnData`. In other terms, the signature of the function must be: 399 `function(ColumnData[] args)` or `function(ColumnData[] args...)`) 400 $(LI Its return value must be a boolean or numeric type, a string, an array, `null`, 401 or a `Nullable!T` where T is any of the previous types.) 402 ) 403 Pass a `null` function pointer to delete the function from the database connection. 404 405 det = Tells SQLite whether the result of the function is deterministic, i.e. if the 406 result is the same when called with the same parameters. Recent versions of SQLite 407 perform optimizations based on this. Set to `Deterministic.no` otherwise. 408 409 See_Also: $(LINK http://www.sqlite.org/c3ref/create_function.html). 410 +/ 411 void createFunction(T)(string name, T fun, Deterministic det = Deterministic.yes) 412 if (isFunctionPointer!T || isDelegate!T) { 413 import std.meta : AliasSeq, staticMap, EraseAll; 414 import std.traits : variadicFunctionStyle, Variadic, ParameterTypeTuple, 415 ParameterDefaultValueTuple, ReturnType, Unqual; 416 417 static assert(variadicFunctionStyle!(fun) == Variadic.no 418 || is(ParameterTypeTuple!fun == AliasSeq!(ColumnData[])), 419 "only type-safe variadic functions with ColumnData arguments are supported"); 420 421 static if (is(ParameterTypeTuple!fun == AliasSeq!(ColumnData[]))) { 422 extern (C) static nothrow void x_func(sqlite3_context* context, 423 int argc, sqlite3_value** argv) { 424 string name; 425 try { 426 import std.array : appender; 427 428 auto args = appender!(ColumnData[]); 429 430 foreach (i; 0 .. argc) { 431 auto value = argv[i]; 432 immutable type = sqlite3_value_type(value); 433 434 final switch (type) { 435 case SqliteType.INTEGER: 436 args.put(ColumnData(getValue!long(value))); 437 break; 438 439 case SqliteType.FLOAT: 440 args.put(ColumnData(getValue!double(value))); 441 break; 442 443 case SqliteType.TEXT: 444 args.put(ColumnData(getValue!string(value))); 445 break; 446 447 case SqliteType.BLOB: 448 args.put(ColumnData(getValue!Blob(value))); 449 break; 450 451 case SqliteType.NULL: 452 args.put(ColumnData(null)); 453 break; 454 } 455 } 456 457 auto ptr = sqlite3_user_data(context); 458 459 auto wrappedDelegate = delegateUnwrap!T(ptr); 460 auto dlg = wrappedDelegate.dlg; 461 name = wrappedDelegate.name; 462 setResult(context, dlg(args.data)); 463 } catch (Exception e) { 464 sqlite3_result_error(context, 465 "error in function %s(): %s".nothrowFormat(name, e.msg).toStringz, -1); 466 } 467 } 468 } else { 469 static assert(!is(ReturnType!fun == void), "function must not return void"); 470 471 alias PT = staticMap!(Unqual, ParameterTypeTuple!fun); 472 alias PD = ParameterDefaultValueTuple!fun; 473 474 extern (C) static nothrow void x_func(sqlite3_context* context, 475 int argc, sqlite3_value** argv) { 476 string name; 477 try { 478 // Get the deledate and its name 479 auto ptr = sqlite3_user_data(context); 480 auto wrappedDelegate = delegateUnwrap!T(ptr); 481 auto dlg = wrappedDelegate.dlg; 482 name = wrappedDelegate.name; 483 484 enum maxArgc = PT.length; 485 enum minArgc = PT.length - EraseAll!(void, PD).length; 486 487 if (argc > maxArgc) { 488 auto txt = ("too many arguments in function %s(), expecting at most %s").format(name, 489 maxArgc); 490 sqlite3_result_error(context, txt.toStringz, -1); 491 } else if (argc < minArgc) { 492 auto txt = ("too few arguments in function %s(), expecting at least %s").format(name, 493 minArgc); 494 sqlite3_result_error(context, txt.toStringz, -1); 495 } else { 496 PT args; 497 foreach (i, type; PT) { 498 if (i < argc) 499 args[i] = getValue!type(argv[i]); 500 else static if (is(typeof(PD[i]))) 501 args[i] = PD[i]; 502 } 503 setResult(context, dlg(args)); 504 } 505 } catch (Exception e) { 506 sqlite3_result_error(context, 507 "error in function %s(): %s".nothrowFormat(name, e.msg).toStringz, -1); 508 } 509 } 510 } 511 512 assert(name.length, "function has an empty name"); 513 514 if (!fun) 515 createFunction(name, null); 516 517 assert(p.handle); 518 check(sqlite3_create_function_v2(p.handle, name.toStringz, -1, 519 SQLITE_UTF8 | det, delegateWrap(fun, name), &x_func, null, null, &free)); 520 } 521 /// 522 unittest { 523 string star(int count, string starSymbol = "*") { 524 import std.range : repeat; 525 import std.array : join; 526 527 return starSymbol.repeat(count).join; 528 } 529 530 auto db = Database(":memory:"); 531 db.createFunction("star", &star); 532 assert(db.execute("SELECT star(5)").oneValue!string == "*****"); 533 assert(db.execute("SELECT star(3, '♥')").oneValue!string == "♥♥♥"); 534 } 535 /// 536 unittest { 537 // The implementation of the new function 538 string myList(ColumnData[] args) { 539 import std.array : appender; 540 import std.string : format, join; 541 542 auto app = appender!(string[]); 543 foreach (arg; args) { 544 if (arg.type == SqliteType.TEXT) 545 app.put(`"%s"`.format(arg)); 546 else 547 app.put("%s".format(arg)); 548 } 549 return app.data.join(", "); 550 } 551 552 auto db = Database(":memory:"); 553 db.createFunction("my_list", &myList); 554 auto list = db.execute("SELECT my_list(42, 3.14, 'text', NULL)").oneValue!string; 555 assert(list == `42, 3.14, "text", null`); 556 } 557 558 /// Ditto 559 void createFunction(T)(string name, T fun = null) if (is(T == typeof(null))) { 560 assert(name.length, "function has an empty name"); 561 assert(p.handle); 562 check(sqlite3_create_function_v2(p.handle, name.toStringz, -1, 563 SQLITE_UTF8, null, fun, null, null, null)); 564 } 565 566 /++ 567 Creates and registers a new aggregate function in the database. 568 569 Params: 570 name = The name that the aggregate function will have in the database. 571 572 agg = The struct of type T implementing the aggregate. T must implement 573 at least these two methods: `accumulate()` and `result()`. 574 Each parameter and the returned type of `accumulate()` and `result()` must be 575 a boolean or numeric type, a string, an array, `null`, or a `Nullable!T` 576 where T is any of the previous types. These methods cannot be variadic. 577 578 det = Tells SQLite whether the result of the function is deterministic, i.e. if the 579 result is the same when called with the same parameters. Recent versions of SQLite 580 perform optimizations based on this. Set to `Deterministic.no` otherwise. 581 582 See_Also: $(LINK http://www.sqlite.org/c3ref/create_function.html). 583 +/ 584 void createAggregate(T)(string name, T agg, Deterministic det = Deterministic.yes) { 585 import std.meta : staticMap; 586 import std.traits : isAggregateType, ReturnType, variadicFunctionStyle, 587 Variadic, Unqual, ParameterTypeTuple; 588 import core.stdc.stdlib : malloc; 589 590 static assert(isAggregateType!T, T.stringof ~ " should be an aggregate type"); 591 static assert(is(typeof(T.accumulate) == function), 592 T.stringof ~ " should have a method named accumulate"); 593 static assert(is(typeof(T.result) == function), 594 T.stringof ~ " should have a method named result"); 595 static assert(is(typeof({ 596 alias RT = ReturnType!(T.result); 597 setResult!RT(null, RT.init); 598 })), T.stringof ~ ".result should return an SQLite-compatible type"); 599 static assert(variadicFunctionStyle!(T.accumulate) == Variadic.no, 600 "variadic functions are not supported"); 601 static assert(variadicFunctionStyle!(T.result) == Variadic.no, 602 "variadic functions are not supported"); 603 604 alias PT = staticMap!(Unqual, ParameterTypeTuple!(T.accumulate)); 605 alias RT = ReturnType!(T.result); 606 607 static struct Context { 608 T aggregate; 609 string functionName; 610 } 611 612 extern (C) static nothrow void x_step(sqlite3_context* context, int /* argc */ , 613 sqlite3_value** argv) { 614 auto ctx = cast(Context*) sqlite3_user_data(context); 615 if (!ctx) { 616 sqlite3_result_error_nomem(context); 617 return; 618 } 619 620 PT args; 621 try { 622 foreach (i, type; PT) 623 args[i] = getValue!type(argv[i]); 624 625 ctx.aggregate.accumulate(args); 626 } catch (Exception e) { 627 sqlite3_result_error(context, 628 "error in aggregate function %s(): %s".nothrowFormat(ctx.functionName, 629 e.msg).toStringz, -1); 630 } 631 } 632 633 extern (C) static nothrow void x_final(sqlite3_context* context) { 634 auto ctx = cast(Context*) sqlite3_user_data(context); 635 if (!ctx) { 636 sqlite3_result_error_nomem(context); 637 return; 638 } 639 640 try { 641 setResult(context, ctx.aggregate.result()); 642 } catch (Exception e) { 643 sqlite3_result_error(context, 644 "error in aggregate function %s(): %s".nothrowFormat(ctx.functionName, 645 e.msg).toStringz, -1); 646 } 647 } 648 649 static if (is(T == class) || is(T == Interface)) 650 assert(agg, "Attempt to create an aggregate function from a null reference"); 651 652 auto ctx = cast(Context*) malloc(Context.sizeof); 653 ctx.aggregate = agg; 654 ctx.functionName = name; 655 656 assert(p.handle); 657 check(sqlite3_create_function_v2(p.handle, name.toStringz, PT.length, 658 SQLITE_UTF8 | det, cast(void*) ctx, null, &x_step, &x_final, &free)); 659 } 660 /// 661 unittest // Aggregate creation 662 { 663 import std.array : Appender, join; 664 665 // The implementation of the aggregate function 666 struct Joiner { 667 private { 668 Appender!(string[]) stringList; 669 string separator; 670 } 671 672 this(string separator) { 673 this.separator = separator; 674 } 675 676 void accumulate(string word) { 677 stringList.put(word); 678 } 679 680 string result() { 681 return stringList.data.join(separator); 682 } 683 } 684 685 auto db = Database(":memory:"); 686 db.run("CREATE TABLE test (word TEXT); 687 INSERT INTO test VALUES ('My'); 688 INSERT INTO test VALUES ('cat'); 689 INSERT INTO test VALUES ('is'); 690 INSERT INTO test VALUES ('black');"); 691 692 db.createAggregate("dash_join", Joiner("-")); 693 auto text = db.execute("SELECT dash_join(word) FROM test").oneValue!string; 694 assert(text == "My-cat-is-black"); 695 } 696 697 /++ 698 Creates and registers a collation function in the database. 699 700 Params: 701 name = The name that the function will have in the database. 702 703 fun = a delegate or function that implements the collation. The function $(D_PARAM fun) 704 must be `nothrow`` and satisfy these criteria: 705 $(UL 706 $(LI Takes two string arguments (s1 and s2). These two strings are slices of C-style strings 707 that SQLite manages internally, so there is no guarantee that they are still valid 708 when the function returns.) 709 $(LI Returns an integer (ret).) 710 $(LI If s1 is less than s2, ret < 0.) 711 $(LI If s1 is equal to s2, ret == 0.) 712 $(LI If s1 is greater than s2, ret > 0.) 713 $(LI If s1 is equal to s2, then s2 is equal to s1.) 714 $(LI If s1 is equal to s2 and s2 is equal to s3, then s1 is equal to s3.) 715 $(LI If s1 is less than s2, then s2 is greater than s1.) 716 $(LI If s1 is less than s2 and s2 is less than s3, then s1 is less than s3.) 717 ) 718 719 See_Also: $(LINK http://www.sqlite.org/lang_aggfunc.html) 720 +/ 721 void createCollation(T)(string name, T fun) 722 if (isFunctionPointer!T || isDelegate!T) { 723 import std.traits : isImplicitlyConvertible, functionAttributes, 724 FunctionAttribute, ParameterTypeTuple, isSomeString, ReturnType; 725 726 static assert(isImplicitlyConvertible!(typeof(fun("a", "b")), int), 727 "the collation function has a wrong signature"); 728 729 static assert(functionAttributes!(T) & FunctionAttribute.nothrow_, 730 "only nothrow functions are allowed as collations"); 731 732 alias PT = ParameterTypeTuple!fun; 733 static assert(isSomeString!(PT[0]), 734 "the first argument of function " ~ name ~ " should be a string"); 735 static assert(isSomeString!(PT[1]), 736 "the second argument of function " ~ name ~ " should be a string"); 737 static assert(isImplicitlyConvertible!(ReturnType!fun, int), 738 "function " ~ name ~ " should return a value convertible to an int"); 739 740 extern (C) static nothrow int x_compare(void* ptr, int n1, 741 const(void)* str1, int n2, const(void)* str2) { 742 static string slice(const(void)* str, int n) nothrow { 743 // The string data is owned by SQLite, so it should be safe 744 // to take a slice of it. 745 return str ? (cast(immutable)(cast(const(char)*) str)[0 .. n]) : null; 746 } 747 748 return delegateUnwrap!T(ptr).dlg(slice(str1, n1), slice(str2, n2)); 749 } 750 751 assert(p.handle); 752 auto dgw = delegateWrap(fun, name); 753 auto result = sqlite3_create_collation_v2(p.handle, name.toStringz, 754 SQLITE_UTF8, delegateWrap(fun, name), &x_compare, &free); 755 if (result != SQLITE_OK) { 756 free(dgw); 757 throw new SqliteException(errmsg(p.handle), result); 758 } 759 } 760 /// 761 unittest // Collation creation 762 { 763 // The implementation of the collation 764 int my_collation(string s1, string s2) nothrow { 765 import std.uni : icmp; 766 import std.exception : assumeWontThrow; 767 768 return assumeWontThrow(icmp(s1, s2)); 769 } 770 771 auto db = Database(":memory:"); 772 db.createCollation("my_coll", &my_collation); 773 db.run("CREATE TABLE test (word TEXT); 774 INSERT INTO test (word) VALUES ('straße'); 775 INSERT INTO test (word) VALUES ('strasses');"); 776 777 auto word = db.execute("SELECT word FROM test ORDER BY word COLLATE my_coll") 778 .oneValue!string; 779 assert(word == "straße"); 780 } 781 782 /++ 783 Registers a delegate of type `UpdateHookDelegate` as the database's update hook. 784 785 Any previously set hook is released. Pass `null` to disable the callback. 786 787 See_Also: $(LINK http://www.sqlite.org/c3ref/commit_hook.html). 788 +/ 789 void setUpdateHook(UpdateHookDelegate updateHook) { 790 extern (C) static nothrow void callback(void* ptr, int type, 791 const(char)* dbName, const(char)* tableName, long rowid) { 792 WrappedDelegate!UpdateHookDelegate* dg; 793 dg = delegateUnwrap!UpdateHookDelegate(ptr); 794 dg.dlg(type, dbName.to!string, tableName.to!string, rowid); 795 } 796 797 free(p.updateHook); 798 p.updateHook = delegateWrap(updateHook); 799 assert(p.handle); 800 sqlite3_update_hook(p.handle, &callback, p.updateHook); 801 } 802 803 /++ 804 Registers a delegate of type `CommitHookDelegate` as the database's commit hook. 805 Any previously set hook is released. 806 807 Params: 808 commitHook = A delegate that should return a non-zero value 809 if the operation must be rolled back, or 0 if it can commit. 810 Pass `null` to disable the callback. 811 812 See_Also: $(LINK http://www.sqlite.org/c3ref/commit_hook.html). 813 +/ 814 void setCommitHook(CommitHookDelegate commitHook) { 815 extern (C) static nothrow int callback(void* ptr) { 816 auto dlg = delegateUnwrap!CommitHookDelegate(ptr).dlg; 817 return dlg(); 818 } 819 820 free(p.commitHook); 821 p.commitHook = delegateWrap(commitHook); 822 assert(p.handle); 823 sqlite3_commit_hook(p.handle, &callback, p.commitHook); 824 } 825 826 /++ 827 Registers a delegate of type `RoolbackHookDelegate` as the database's rollback hook. 828 829 Any previously set hook is released. 830 Pass `null` to disable the callback. 831 832 See_Also: $(LINK http://www.sqlite.org/c3ref/commit_hook.html). 833 +/ 834 void setRollbackHook(RoolbackHookDelegate rollbackHook) { 835 extern (C) static nothrow void callback(void* ptr) { 836 auto dlg = delegateUnwrap!RoolbackHookDelegate(ptr).dlg; 837 dlg(); 838 } 839 840 free(p.rollbackHook); 841 p.rollbackHook = delegateWrap(rollbackHook); 842 assert(p.handle); 843 sqlite3_rollback_hook(p.handle, &callback, p.rollbackHook); 844 } 845 846 /++ 847 Registers a delegate of type `ProgressHandlerDelegate` as the progress handler. 848 849 Any previously set handler is released. 850 Pass `null` to disable the callback. 851 852 Params: 853 pace = The approximate number of virtual machine instructions that are 854 evaluated between successive invocations of the handler. 855 856 progressHandler = A delegate that should return 0 if the operation can continue 857 or another value if it must be aborted. 858 859 See_Also: $(LINK http://www.sqlite.org/c3ref/progress_handler.html). 860 +/ 861 void setProgressHandler(int pace, ProgressHandlerDelegate progressHandler) { 862 extern (C) static nothrow int callback(void* ptr) { 863 auto dlg = delegateUnwrap!ProgressHandlerDelegate(ptr).dlg; 864 return dlg(); 865 } 866 867 free(p.progressHandler); 868 p.progressHandler = delegateWrap(progressHandler); 869 assert(p.handle); 870 sqlite3_progress_handler(p.handle, pace, &callback, p.progressHandler); 871 } 872 873 /++ 874 Registers a delegate of type `TraceCallbackDelegate` as the trace callback. 875 876 Any previously set profile or trace callback is released. 877 Pass `null` to disable the callback. 878 879 The string parameter that is passed to the callback is the SQL text of the statement being 880 executed. 881 882 See_Also: $(LINK http://www.sqlite.org/c3ref/profile.html). 883 +/ 884 void setTraceCallback(TraceCallbackDelegate traceCallback) { 885 extern (C) static nothrow void callback(void* ptr, const(char)* str) { 886 auto dlg = delegateUnwrap!TraceCallbackDelegate(ptr).dlg; 887 dlg(str.to!string); 888 } 889 890 free(p.traceCallback); 891 p.traceCallback = delegateWrap(traceCallback); 892 assert(p.handle); 893 sqlite3_trace(p.handle, &callback, p.traceCallback); 894 } 895 896 /++ 897 Registers a delegate of type `ProfileCallbackDelegate` as the profile callback. 898 899 Any previously set profile or trace callback is released. 900 Pass `null` to disable the callback. 901 902 The string parameter that is passed to the callback is the SQL text of the statement being 903 executed. The time unit is defined in SQLite's documentation as nanoseconds (subject to change, 904 as the functionality is experimental). 905 906 See_Also: $(LINK http://www.sqlite.org/c3ref/profile.html). 907 +/ 908 void setProfileCallback(ProfileCallbackDelegate profileCallback) { 909 extern (C) static nothrow void callback(void* ptr, const(char)* str, sqlite3_uint64 time) { 910 auto dlg = delegateUnwrap!ProfileCallbackDelegate(ptr).dlg; 911 dlg(str.to!string, time); 912 } 913 914 free(p.profileCallback); 915 p.profileCallback = delegateWrap(profileCallback); 916 assert(p.handle); 917 sqlite3_profile(p.handle, &callback, p.profileCallback); 918 } 919 920 version (_UnlockNotify) { 921 /++ 922 Registers a `IUnlockNotifyHandler` used to handle database locks. 923 924 When running in shared-cache mode, a database operation may fail with an SQLITE_LOCKED error if 925 the required locks on the shared-cache or individual tables within the shared-cache cannot be obtained. 926 See SQLite Shared-Cache Mode for a description of shared-cache locking. 927 This API may be used to register a callback that SQLite will invoke when the connection currently 928 holding the required lock relinquishes it. 929 This API can be used only if the SQLite library was compiled with the `SQLITE_ENABLE_UNLOCK_NOTIFY` 930 C-preprocessor symbol defined. 931 932 See_Also: $(LINK http://sqlite.org/c3ref/unlock_notify.html). 933 934 Parameters: 935 unlockNotifyHandler - custom handler used to control the unlocking mechanism 936 +/ 937 void setUnlockNotifyHandler(IUnlockNotifyHandler unlockNotifyHandler) { 938 p.unlockNotifyHandler = unlockNotifyHandler; 939 } 940 941 /// Setup and waits for unlock notify using the provided `IUnlockNotifyHandler` 942 package(d2sqlite3) auto waitForUnlockNotify() { 943 if (p.unlockNotifyHandler is null) 944 return SQLITE_LOCKED; 945 946 version (SqliteEnableUnlockNotify) { 947 extern (C) static nothrow void callback(void** ntfPtr, int nPtr) { 948 for (int i = 0; i < nPtr; i++) { 949 auto handler = cast(IUnlockNotifyHandler*) ntfPtr[i]; 950 handler.emit(SQLITE_OK); 951 } 952 } 953 954 int rc = sqlite3_unlock_notify(p.handle, &callback, &p.unlockNotifyHandler); 955 assert(rc == SQLITE_LOCKED || rc == SQLITE_OK); 956 957 /+ The call to sqlite3_unlock_notify() always returns either SQLITE_LOCKED or SQLITE_OK. 958 959 If SQLITE_LOCKED was returned, then the system is deadlocked. In this case this function 960 needs to return SQLITE_LOCKED to the caller so that the current transaction can be rolled 961 back. Otherwise, block until the unlock-notify callback is invoked, then return SQLITE_OK. 962 +/ 963 if (rc == SQLITE_OK) { 964 p.unlockNotifyHandler.wait(); 965 scope (exit) 966 p.unlockNotifyHandler.reset(); 967 return p.unlockNotifyHandler.result; 968 } 969 return rc; 970 } else { 971 p.unlockNotifyHandler.waitOne(); 972 auto res = p.unlockNotifyHandler.result; 973 if (res != SQLITE_OK) 974 p.unlockNotifyHandler.reset(); 975 return res; 976 } 977 } 978 } 979 } 980 981 /// Delegate types 982 alias UpdateHookDelegate = void delegate(int type, string dbName, string tableName, long rowid) nothrow; 983 /// ditto 984 alias CommitHookDelegate = int delegate() nothrow; 985 /// ditto 986 alias RoolbackHookDelegate = void delegate() nothrow; 987 /// ditto 988 alias ProgressHandlerDelegate = int delegate() nothrow; 989 /// ditto 990 alias TraceCallbackDelegate = void delegate(string sql) nothrow; 991 /// ditto 992 alias ProfileCallbackDelegate = void delegate(string sql, ulong time) nothrow; 993 994 /// Information about a table column. 995 struct TableColumnMetadata { 996 string declaredTypeName; /// 997 string collationSequenceName; /// 998 bool isNotNull; /// 999 bool isPrimaryKey; /// 1000 bool isAutoIncrement; /// 1001 } 1002 1003 version (_UnlockNotify) { 1004 /++ 1005 UnlockNotifyHandler interface to be used for custom implementations of UnlockNotify pattern with SQLite. 1006 1007 Note: 1008 For the C API sqlite3_unlock_notify to be used, this library must be compiled with 1009 `-version=SqliteEnableUnlockNotify`. 1010 Otherwise only emulated solution is provided, that is based on retries for the defined amount of time. 1011 1012 Implementation must be able to handle situation when emit is called sooner than the wait itself. 1013 1014 See_Also: $(LINK http://sqlite.org/c3ref/unlock_notify.html). 1015 See_Also: $(LINK http://www.sqlite.org/unlock_notify.html). 1016 +/ 1017 interface IUnlockNotifyHandler { 1018 version (SqliteEnableUnlockNotify) { 1019 /// Blocks until emit is called 1020 void wait(); 1021 1022 /++ 1023 Unlocks the handler. 1024 This is called from registered callback from SQLite. 1025 1026 Params: 1027 state = Value to set as a handler result. It can be SQLITE_LOCKED or SQLITE_OK. 1028 +/ 1029 void emit(int state) nothrow; 1030 } else { 1031 /++ 1032 This is used as an alternative when SQLite is not compiled with SQLITE_ENABLE_UNLOCK_NOTIFY, and 1033 when the library is built with `-version=SqliteFakeUnlockNotify`. 1034 Using this, the handler tries to wait out the SQLITE_LOCKED state for some time. 1035 Implementation have to block for some amount of time and check if total amount is not greater than some constant afterwards. 1036 If there is still some time to try again, the handler must set the result to SQLITE_OK or to SQLITE_LOCKED otherwise. 1037 +/ 1038 void waitOne(); 1039 } 1040 1041 /// Resets the handler for the next use 1042 void reset(); 1043 1044 /// Result after wait is finished 1045 @property int result() const; 1046 } 1047 1048 version (SqliteEnableUnlockNotify) { 1049 /++ 1050 UnlockNotifyHandler used when SQLite is compiled with SQLITE_ENABLE_UNLOCK_NOTIFY, and 1051 when the library is built with `-version=SqliteEnableUnlockNotify`. 1052 It is implemented using the standard `core.sync` package. 1053 1054 Use setUnlockNotifyHandler method to handle the database lock. 1055 1056 See_Also: $(LINK http://sqlite.org/c3ref/unlock_notify.html). 1057 See_Also: $(LINK http://www.sqlite.org/unlock_notify.html). 1058 +/ 1059 final class UnlockNotifyHandler : IUnlockNotifyHandler { 1060 import core.sync.condition : Condition; 1061 import core.sync.mutex : Mutex; 1062 1063 private { 1064 __gshared Mutex mtx; 1065 __gshared Condition cond; 1066 __gshared int res; 1067 __gshared bool fired; 1068 } 1069 1070 /// Constructor 1071 this() { 1072 mtx = new Mutex(); 1073 cond = new Condition(mtx); 1074 } 1075 1076 /// Blocks until emit is called 1077 void wait() { 1078 synchronized (mtx) { 1079 if (!fired) 1080 cond.wait(); 1081 } 1082 } 1083 1084 /// Unlocks the handler, state is one of SQLITE_LOCKED or SQLITE_OK 1085 void emit(int res) nothrow 1086 in { 1087 assert(res == SQLITE_LOCKED || res == SQLITE_OK); 1088 } 1089 body { 1090 try { 1091 synchronized (mtx) { 1092 this.res = res; 1093 fired = true; 1094 cond.notify(); 1095 } 1096 } catch (Exception) { 1097 } 1098 } 1099 1100 /// Resets the handler for the next use 1101 void reset() { 1102 res = SQLITE_LOCKED; 1103 fired = false; 1104 } 1105 1106 /// Result after wait is finished 1107 @property int result() const 1108 out (result) { 1109 assert(result == SQLITE_OK || result == SQLITE_LOCKED); 1110 } 1111 body { 1112 return res; 1113 } 1114 } 1115 } else { 1116 /++ 1117 UnlockNotifyHandler that can be used when SQLite is not compiled with SQLITE_ENABLE_UNLOCK_NOTIFY, 1118 and when the library is built with `-version=SqliteFakeUnlockNotify`.. 1119 It retries the statement execution for the provided amount of time before the SQLITE_LOCKED is returned. 1120 1121 Use setUnlockNotifyHandler method to handle the database lock. 1122 1123 See_Also: $(LINK http://sqlite.org/c3ref/unlock_notify.html). 1124 See_Also: $(LINK http://www.sqlite.org/unlock_notify.html). 1125 +/ 1126 final class UnlockNotifyHandler : IUnlockNotifyHandler { 1127 import core.time : Duration, msecs; 1128 import std.datetime.stopwatch : StopWatch; 1129 1130 private { 1131 int res; 1132 Duration maxDuration; 1133 StopWatch sw; 1134 } 1135 1136 /// Constructor 1137 this(Duration max = 1000.msecs) 1138 in { 1139 assert(max > Duration.zero); 1140 } 1141 body { 1142 maxDuration = max; 1143 } 1144 1145 /// Blocks for some time to retry the statement 1146 void waitOne() { 1147 import core.thread : Thread; 1148 import std.random : uniform; 1149 1150 if (!sw.running) 1151 sw.start; 1152 1153 Thread.sleep(uniform(50, 100).msecs); 1154 1155 if (sw.peek > maxDuration) { 1156 sw.stop; 1157 res = SQLITE_LOCKED; 1158 } else 1159 res = SQLITE_OK; 1160 } 1161 1162 /// Resets the handler for the next use 1163 void reset() { 1164 res = SQLITE_LOCKED; 1165 sw.reset(); 1166 } 1167 1168 /// Result after wait is finished 1169 @property int result() const 1170 out (result) { 1171 assert(result == SQLITE_OK || result == SQLITE_LOCKED); 1172 } 1173 body { 1174 return res; 1175 } 1176 } 1177 } 1178 1179 unittest { 1180 import core.time : Duration, msecs; 1181 1182 /++ 1183 Tests the unlock notify facility. 1184 Params: 1185 delay - time to wait in the transaction to block the other one 1186 expected - expected result (can be used to test timeout when fake unlock notify is used) 1187 +/ 1188 void testUnlockNotify(Duration delay = 500.msecs, int expected = 3) { 1189 import core.thread : Thread; 1190 import core.time : msecs, seconds; 1191 import std.concurrency : spawn; 1192 1193 static void test(int n, Duration delay) { 1194 auto db = Database("file::memory:?cache=shared", 1195 SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE 1196 | SQLITE_OPEN_URI | SQLITE_OPEN_MEMORY); 1197 db.setUnlockNotifyHandler = new UnlockNotifyHandler(); 1198 db.execute("BEGIN IMMEDIATE"); 1199 Thread.sleep(delay); 1200 db.execute("INSERT INTO foo (bar) VALUES (?)", n); 1201 db.commit(); 1202 } 1203 1204 auto db = Database("file::memory:?cache=shared", 1205 SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_URI 1206 | SQLITE_OPEN_MEMORY); 1207 db.execute(`CREATE TABLE foo (bar INTEGER);`); 1208 1209 spawn(&test, 1, delay); 1210 Thread.sleep(100.msecs); 1211 spawn(&test, 2, delay); 1212 Thread.sleep(2 * delay + 100.msecs); 1213 assert(db.execute("SELECT sum(bar) FROM foo").oneValue!int == expected, 1214 format!"%s != %s"(db.execute("SELECT sum(bar) FROM foo") 1215 .oneValue!int, expected)); 1216 } 1217 1218 testUnlockNotify(); 1219 version (SqliteFakeUnlockNotify) 1220 testUnlockNotify(1500.msecs, 1); //timeout test 1221 } 1222 } 1223 1224 /++ 1225 Exception thrown when SQLite functions return an error. 1226 +/ 1227 class SqliteException : Exception { 1228 /++ 1229 The _code of the error that raised the exception 1230 +/ 1231 int code; 1232 1233 /++ 1234 The SQL code that raised the exception, if applicable. 1235 +/ 1236 string sql; 1237 1238 private this(string msg, string sql, int code, string file = __FILE__, 1239 size_t line = __LINE__, Throwable next = null) @safe pure nothrow @nogc { 1240 this.sql = sql; 1241 this.code = code; 1242 super(msg, file, line, next); 1243 } 1244 1245 package(d2sqlite3): 1246 this(string msg, int code, string sql = null, string file = __FILE__, 1247 size_t line = __LINE__, Throwable next = null) @safe pure nothrow { 1248 this(text("error ", code, ": ", msg), sql, code, file, line, next); 1249 } 1250 }