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