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