1 /++ 2 This module is part of d2sqlite3. 3 4 Authors: 5 Nicolas Sicard (biozic) and other contributors at $(LINK https://github.com/biozic/d2sqlite3) 6 7 Copyright: 8 Copyright 2011-17 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 81 this(sqlite3* handle) nothrow 82 { 83 this.handle = handle; 84 } 85 86 ~this() 87 { 88 close(); 89 } 90 91 void close() 92 { 93 if (!handle) 94 return; 95 96 sqlite3_progress_handler(handle, 0, null, null); 97 auto result = sqlite3_close(handle); 98 // Check that destructor was not call by the GC 99 // See https://p0nce.github.io/d-idioms/#GC-proof-resource-class 100 import core.exception : InvalidMemoryOperationError; 101 try 102 { 103 enforce(result == SQLITE_OK, new SqliteException(errmsg(handle), result)); 104 } 105 catch (InvalidMemoryOperationError e) 106 { 107 import core.stdc.stdio : fprintf, stderr; 108 fprintf(stderr, "Error: release of Database resource incorrectly" 109 ~ " depends on destructors called by the GC.\n"); 110 assert(false); // crash 111 } 112 handle = null; 113 ptrFree(updateHook); 114 ptrFree(commitHook); 115 ptrFree(rollbackHook); 116 ptrFree(progressHandler); 117 ptrFree(traceCallback); 118 ptrFree(profileCallback); 119 } 120 } 121 122 RefCounted!(Payload, RefCountedAutoInitialize.no) p; 123 124 void check(int result) 125 { 126 enforce(result == SQLITE_OK, new SqliteException(errmsg(p.handle), result)); 127 } 128 129 public: 130 /++ 131 Opens a database connection. 132 133 Params: 134 path = The path to the database file. In recent versions of SQLite, the path can be 135 an URI with options. 136 137 flags = Options flags. 138 139 See_Also: $(LINK http://www.sqlite.org/c3ref/open.html) to know how to use the flags 140 parameter or to use path as a file URI if the current configuration allows it. 141 +/ 142 this(string path, int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE) 143 { 144 sqlite3* hdl; 145 auto result = sqlite3_open_v2(path.toStringz, &hdl, flags, null); 146 enforce(result == SQLITE_OK, new SqliteException(hdl ? errmsg(hdl) : "Error opening the database", result)); 147 p = Payload(hdl); 148 } 149 150 /++ 151 Explicitly closes the database connection. 152 153 After a call to `close()`, using the database connection or one of its prepared statement 154 is an error. The `Database` object is destroyed and cannot be used any more. 155 +/ 156 void close() 157 { 158 p.close(); 159 destroy(p); 160 } 161 162 /++ 163 Gets the SQLite internal _handle of the database connection. 164 +/ 165 sqlite3* handle() @property nothrow 166 { 167 return p.handle; 168 } 169 170 /++ 171 Gets the path associated with an attached database. 172 173 Params: 174 database = The name of an attached database. 175 176 Returns: The absolute path of the attached database. 177 If there is no attached database, or if database is a temporary or 178 in-memory database, then null is returned. 179 +/ 180 string attachedFilePath(string database = "main") 181 { 182 assert(p.handle); 183 return sqlite3_db_filename(p.handle, database.toStringz).to!string; 184 } 185 186 /++ 187 Gets the read-only status of an attached database. 188 189 Params: 190 database = The name of an attached database. 191 +/ 192 bool isReadOnly(string database = "main") 193 { 194 immutable ret = sqlite3_db_readonly(p.handle, database.toStringz); 195 enforce(ret >= 0, new SqliteException("Database not found: %s".format(database))); 196 return ret == 1; 197 } 198 199 /++ 200 Gets metadata for a specific table column of an attached database. 201 202 Params: 203 table = The name of the table. 204 205 column = The name of the column. 206 207 database = The name of a database attached. If null, then all attached databases 208 are searched for the table using the same algorithm used by the database engine 209 to resolve unqualified table references. 210 +/ 211 TableColumnMetadata tableColumnMetadata(string table, string column, string database = "main") 212 { 213 TableColumnMetadata data; 214 char* pzDataType, pzCollSeq; 215 int notNull, primaryKey, autoIncrement; 216 check(sqlite3_table_column_metadata(p.handle, database.toStringz, table.toStringz, 217 column.toStringz, &pzDataType, &pzCollSeq, ¬Null, &primaryKey, &autoIncrement)); 218 data.declaredTypeName = pzDataType.to!string; 219 data.collationSequenceName = pzCollSeq.to!string; 220 data.isNotNull = cast(bool) notNull; 221 data.isPrimaryKey = cast(bool) primaryKey; 222 data.isAutoIncrement = cast(bool) autoIncrement; 223 return data; 224 } 225 226 /++ 227 Executes a single SQL statement and returns the results directly. 228 229 It's the equivalent of `prepare(sql).execute()`. 230 Or when used with args the equivalent of: 231 --- 232 auto stm = prepare(sql); 233 stm.bindAll(args); 234 stm.execute(); 235 --- 236 237 The results become undefined when the Database goes out of scope and is destroyed. 238 239 Params: 240 sql = The code of the SQL statement. 241 args = Optional arguments to bind to the SQL statement. 242 +/ 243 ResultRange execute(Args...)(string sql, Args args) 244 { 245 auto stm = prepare(sql); 246 static if (Args.length) stm.bindAll(args); 247 return stm.execute(); 248 } 249 /// 250 unittest 251 { 252 auto db = Database(":memory:"); 253 db.execute("CREATE TABLE test (val INTEGER)"); 254 db.execute("INSERT INTO test (val) VALUES (:v)", 1); 255 assert(db.execute("SELECT val FROM test WHERE val=:v", 1).oneValue!int == 1); 256 } 257 258 /++ 259 Runs an SQL script that can contain multiple statements. 260 261 Params: 262 script = The code of the SQL script. 263 264 dg = A delegate to call for each statement to handle the results. The passed 265 ResultRange will be empty if a statement doesn't return rows. If the delegate 266 return false, the execution is aborted. 267 +/ 268 void run(string script, bool delegate(ResultRange) dg = null) 269 { 270 foreach (sql; script.byStatement) 271 { 272 auto stmt = prepare(sql); 273 auto results = stmt.execute(); 274 if (dg && !dg(results)) 275 return; 276 } 277 } 278 /// 279 unittest 280 { 281 auto db = Database(":memory:"); 282 db.run(`CREATE TABLE test1 (val INTEGER); 283 CREATE TABLE test2 (val FLOAT); 284 DROP TABLE test1; 285 DROP TABLE test2;`); 286 } 287 288 /++ 289 Prepares (compiles) a single SQL statement and returngs it, so that it can be bound to 290 values before execution. 291 292 The statement becomes invalid if the Database goes out of scope and is destroyed. 293 +/ 294 Statement prepare(string sql) 295 { 296 return Statement(this, sql); 297 } 298 299 /// Convenience functions equivalent to an SQL statement. 300 void begin() { execute("BEGIN"); } 301 /// Ditto 302 void commit() { execute("COMMIT"); } 303 /// Ditto 304 void rollback() { execute("ROLLBACK"); } 305 306 /++ 307 Returns the rowid of the last INSERT statement. 308 +/ 309 long lastInsertRowid() 310 { 311 assert(p.handle); 312 return sqlite3_last_insert_rowid(p.handle); 313 } 314 315 /++ 316 Gets the number of database rows that were changed, inserted or deleted by the most 317 recently executed SQL statement. 318 +/ 319 int changes() @property nothrow 320 { 321 assert(p.handle); 322 return sqlite3_changes(p.handle); 323 } 324 325 /++ 326 Gets the number of database rows that were changed, inserted or deleted since the 327 database was opened. 328 +/ 329 int totalChanges() @property nothrow 330 { 331 assert(p.handle); 332 return sqlite3_total_changes(p.handle); 333 } 334 335 /++ 336 Gets the SQLite error code of the last operation. 337 +/ 338 int errorCode() @property nothrow 339 { 340 return p.handle ? sqlite3_errcode(p.handle) : 0; 341 } 342 343 /++ 344 Interrupts any pending database operations. 345 346 It's safe to call this function from anouther thread. 347 348 See_also: $(LINK http://www.sqlite.org/c3ref/interrupt.html). 349 +/ 350 void interrupt() 351 { 352 assert(p.handle); 353 sqlite3_interrupt(p.handle); 354 } 355 356 /++ 357 Sets a connection configuration option. 358 359 See_Also: $(LINK http://www.sqlite.org/c3ref/db_config.html). 360 +/ 361 void config(Args...)(int code, Args args) 362 { 363 auto result = sqlite3_db_config(p.handle, code, args); 364 enforce(result == SQLITE_OK, new SqliteException("Database configuration: error %s".format(result))); 365 } 366 367 version (SQLITE_OMIT_LOAD_EXTENSION) {} 368 else 369 { 370 /++ 371 Enables or disables loading extensions. 372 +/ 373 void enableLoadExtensions(bool enable = true) 374 { 375 enforce(sqlite3_enable_load_extension(p.handle, enable) == SQLITE_OK, 376 new SqliteException("Could not enable loading extensions.")); 377 } 378 379 /++ 380 Loads an extension. 381 382 Params: 383 path = The path of the extension file. 384 385 entryPoint = The name of the entry point function. If null is passed, SQLite 386 uses the name of the extension file as the entry point. 387 +/ 388 void loadExtension(string path, string entryPoint = null) 389 { 390 immutable ret = sqlite3_load_extension(p.handle, path.toStringz, entryPoint.toStringz, null); 391 enforce(ret == SQLITE_OK, new SqliteException( 392 "Could not load extension: %s:%s".format(entryPoint, path))); 393 } 394 } 395 396 /++ 397 Creates and registers a new function in the database. 398 399 If a function with the same name and the same arguments already exists, it is replaced 400 by the new one. 401 402 The memory associated with the function will be released when the database connection 403 is closed. 404 405 Params: 406 name = The name that the function will have in the database. 407 408 fun = a delegate or function that implements the function. $(D_PARAM fun) 409 must satisfy the following criteria: 410 $(UL 411 $(LI It must not be variadic.) 412 $(LI Its arguments must all have a type that is compatible with SQLite types: 413 it must be a boolean or numeric type, a string, an array, `null`, 414 or a `Nullable!T` where T is any of the previous types.) 415 $(LI Its return value must also be of a compatible type.) 416 ) 417 or 418 $(UL 419 $(LI It must be a normal or type-safe variadic function where the arguments 420 are of type `ColumnData`. In other terms, the signature of the function must be: 421 `function(ColumnData[] args)` or `function(ColumnData[] args...)`) 422 $(LI Its return value must be a boolean or numeric type, a string, an array, `null`, 423 or a `Nullable!T` where T is any of the previous types.) 424 ) 425 Pass a `null` function pointer to delete the function from the database connection. 426 427 det = Tells SQLite whether the result of the function is deterministic, i.e. if the 428 result is the same when called with the same parameters. Recent versions of SQLite 429 perform optimizations based on this. Set to `Deterministic.no` otherwise. 430 431 See_Also: $(LINK http://www.sqlite.org/c3ref/create_function.html). 432 +/ 433 void createFunction(T)(string name, T fun, Deterministic det = Deterministic.yes) 434 if (isFunctionPointer!T || isDelegate!T) 435 { 436 import std.meta : AliasSeq, staticMap, EraseAll; 437 import std.traits : variadicFunctionStyle, Variadic, ParameterTypeTuple, 438 ParameterDefaultValueTuple, ReturnType, Unqual; 439 440 static assert(variadicFunctionStyle!(fun) == Variadic.no 441 || is(ParameterTypeTuple!fun == AliasSeq!(ColumnData[])), 442 "only type-safe variadic functions with ColumnData arguments are supported"); 443 444 static if (is(ParameterTypeTuple!fun == AliasSeq!(ColumnData[]))) 445 { 446 extern(C) static nothrow 447 void x_func(sqlite3_context* context, int argc, sqlite3_value** argv) 448 { 449 string name; 450 try 451 { 452 import std.array : appender; 453 auto args = appender!(ColumnData[]); 454 455 foreach (i; 0 .. argc) 456 { 457 auto value = argv[i]; 458 immutable type = sqlite3_value_type(value); 459 460 final switch (type) 461 { 462 case SqliteType.INTEGER: 463 args.put(ColumnData(getValue!long(value))); 464 break; 465 466 case SqliteType.FLOAT: 467 args.put(ColumnData(getValue!double(value))); 468 break; 469 470 case SqliteType.TEXT: 471 args.put(ColumnData(getValue!string(value))); 472 break; 473 474 case SqliteType.BLOB: 475 args.put(ColumnData(getValue!Blob(value))); 476 break; 477 478 case SqliteType.NULL: 479 args.put(ColumnData(null)); 480 break; 481 } 482 } 483 484 auto ptr = sqlite3_user_data(context); 485 486 auto wrappedDelegate = delegateUnwrap!T(ptr); 487 auto dlg = wrappedDelegate.dlg; 488 name = wrappedDelegate.name; 489 setResult(context, dlg(args.data)); 490 } 491 catch (Exception e) 492 { 493 sqlite3_result_error(context, "error in function %s(): %s" 494 .nothrowFormat(name, e.msg).toStringz, -1); 495 } 496 } 497 } 498 else 499 { 500 static assert(!is(ReturnType!fun == void), "function must not return void"); 501 502 alias PT = staticMap!(Unqual, ParameterTypeTuple!fun); 503 alias PD = ParameterDefaultValueTuple!fun; 504 505 extern (C) static nothrow 506 void x_func(sqlite3_context* context, int argc, sqlite3_value** argv) 507 { 508 string name; 509 try 510 { 511 // Get the deledate and its name 512 auto ptr = sqlite3_user_data(context); 513 auto wrappedDelegate = delegateUnwrap!T(ptr); 514 auto dlg = wrappedDelegate.dlg; 515 name = wrappedDelegate.name; 516 517 enum maxArgc = PT.length; 518 enum minArgc = PT.length - EraseAll!(void, PD).length; 519 520 if (argc > maxArgc) 521 { 522 auto txt = ("too many arguments in function %s(), expecting at most %s" 523 ).format(name, maxArgc); 524 sqlite3_result_error(context, txt.toStringz, -1); 525 } 526 else if (argc < minArgc) 527 { 528 auto txt = ("too few arguments in function %s(), expecting at least %s" 529 ).format(name, minArgc); 530 sqlite3_result_error(context, txt.toStringz, -1); 531 } 532 else 533 { 534 PT args; 535 foreach (i, type; PT) 536 { 537 if (i < argc) 538 args[i] = getValue!type(argv[i]); 539 else 540 static if (is(typeof(PD[i]))) 541 args[i] = PD[i]; 542 } 543 setResult(context, dlg(args)); 544 } 545 } 546 catch (Exception e) 547 { 548 sqlite3_result_error(context, "error in function %s(): %s" 549 .nothrowFormat(name, e.msg).toStringz, -1); 550 } 551 } 552 } 553 554 assert(name.length, "function has an empty name"); 555 556 if (!fun) 557 createFunction(name, null); 558 559 assert(p.handle); 560 check(sqlite3_create_function_v2(p.handle, name.toStringz, -1, 561 SQLITE_UTF8 | det, delegateWrap(fun, name), &x_func, null, null, &ptrFree)); 562 } 563 /// 564 unittest 565 { 566 string star(int count, string starSymbol = "*") 567 { 568 import std.range : repeat; 569 import std.array : join; 570 571 return starSymbol.repeat(count).join; 572 } 573 574 auto db = Database(":memory:"); 575 db.createFunction("star", &star); 576 assert(db.execute("SELECT star(5)").oneValue!string == "*****"); 577 assert(db.execute("SELECT star(3, '♥')").oneValue!string == "♥♥♥"); 578 } 579 /// 580 unittest 581 { 582 // The implementation of the new function 583 string myList(ColumnData[] args) 584 { 585 import std.array : appender; 586 import std..string : format, join; 587 588 auto app = appender!(string[]); 589 foreach (arg; args) 590 { 591 if (arg.type == SqliteType.TEXT) 592 app.put(`"%s"`.format(arg)); 593 else 594 app.put("%s".format(arg)); 595 } 596 return app.data.join(", "); 597 } 598 599 auto db = Database(":memory:"); 600 db.createFunction("my_list", &myList); 601 auto list = db.execute("SELECT my_list(42, 3.14, 'text', NULL)").oneValue!string; 602 assert(list == `42, 3.14, "text", null`); 603 } 604 605 /// Ditto 606 void createFunction(T)(string name, T fun = null) 607 if (is(T == typeof(null))) 608 { 609 assert(name.length, "function has an empty name"); 610 assert(p.handle); 611 check(sqlite3_create_function_v2(p.handle, name.toStringz, -1, SQLITE_UTF8, 612 null, fun, null, null, null)); 613 } 614 615 /++ 616 Creates and registers a new aggregate function in the database. 617 618 Params: 619 name = The name that the aggregate function will have in the database. 620 621 agg = The struct of type T implementing the aggregate. T must implement 622 at least these two methods: `accumulate()` and `result()`. 623 Each parameter and the returned type of `accumulate()` and `result()` must be 624 a boolean or numeric type, a string, an array, `null`, or a `Nullable!T` 625 where T is any of the previous types. These methods cannot be variadic. 626 627 det = Tells SQLite whether the result of the function is deterministic, i.e. if the 628 result is the same when called with the same parameters. Recent versions of SQLite 629 perform optimizations based on this. Set to `Deterministic.no` otherwise. 630 631 See_Also: $(LINK http://www.sqlite.org/c3ref/create_function.html). 632 +/ 633 void createAggregate(T)(string name, T agg, Deterministic det = Deterministic.yes) 634 { 635 import std.meta : staticMap; 636 import std.traits : isAggregateType, ReturnType, variadicFunctionStyle, Variadic, 637 Unqual, ParameterTypeTuple; 638 import core.stdc.stdlib : malloc; 639 640 static assert(isAggregateType!T, 641 T.stringof ~ " should be an aggregate type"); 642 static assert(is(typeof(T.accumulate) == function), 643 T.stringof ~ " should have a method named accumulate"); 644 static assert(is(typeof(T.result) == function), 645 T.stringof ~ " should have a method named result"); 646 static assert(is(typeof({ 647 alias RT = ReturnType!(T.result); 648 setResult!RT(null, RT.init); 649 })), T.stringof ~ ".result should return an SQLite-compatible type"); 650 static assert(variadicFunctionStyle!(T.accumulate) == Variadic.no, 651 "variadic functions are not supported"); 652 static assert(variadicFunctionStyle!(T.result) == Variadic.no, 653 "variadic functions are not supported"); 654 655 alias PT = staticMap!(Unqual, ParameterTypeTuple!(T.accumulate)); 656 alias RT = ReturnType!(T.result); 657 658 static struct Context 659 { 660 T aggregate; 661 string functionName; 662 } 663 664 extern(C) static nothrow 665 void x_step(sqlite3_context* context, int /* argc */, sqlite3_value** argv) 666 { 667 auto ctx = cast(Context*) sqlite3_user_data(context); 668 if (!ctx) 669 { 670 sqlite3_result_error_nomem(context); 671 return; 672 } 673 674 PT args; 675 try 676 { 677 foreach (i, type; PT) 678 args[i] = getValue!type(argv[i]); 679 680 ctx.aggregate.accumulate(args); 681 } 682 catch (Exception e) 683 { 684 sqlite3_result_error(context, "error in aggregate function %s(): %s" 685 .nothrowFormat(ctx.functionName, e.msg).toStringz, -1); 686 } 687 } 688 689 extern(C) static nothrow 690 void x_final(sqlite3_context* context) 691 { 692 auto ctx = cast(Context*) sqlite3_user_data(context); 693 if (!ctx) 694 { 695 sqlite3_result_error_nomem(context); 696 return; 697 } 698 699 try 700 { 701 setResult(context, ctx.aggregate.result()); 702 } 703 catch (Exception e) 704 { 705 sqlite3_result_error(context, "error in aggregate function %s(): %s" 706 .nothrowFormat(ctx.functionName, e.msg).toStringz, -1); 707 } 708 } 709 710 static if (is(T == class) || is(T == Interface)) 711 assert(agg, "Attempt to create an aggregate function from a null reference"); 712 713 auto ctx = cast(Context*) malloc(Context.sizeof); 714 ctx.aggregate = agg; 715 ctx.functionName = name; 716 717 assert(p.handle); 718 check(sqlite3_create_function_v2(p.handle, name.toStringz, PT.length, SQLITE_UTF8 | det, 719 cast(void*) ctx, null, &x_step, &x_final, &ptrFree)); 720 } 721 /// 722 unittest // Aggregate creation 723 { 724 import std.array : Appender, join; 725 726 // The implementation of the aggregate function 727 struct Joiner 728 { 729 private 730 { 731 Appender!(string[]) stringList; 732 string separator; 733 } 734 735 this(string separator) 736 { 737 this.separator = separator; 738 } 739 740 void accumulate(string word) 741 { 742 stringList.put(word); 743 } 744 745 string result() 746 { 747 return stringList.data.join(separator); 748 } 749 } 750 751 auto db = Database(":memory:"); 752 db.run("CREATE TABLE test (word TEXT); 753 INSERT INTO test VALUES ('My'); 754 INSERT INTO test VALUES ('cat'); 755 INSERT INTO test VALUES ('is'); 756 INSERT INTO test VALUES ('black');"); 757 758 db.createAggregate("dash_join", Joiner("-")); 759 auto text = db.execute("SELECT dash_join(word) FROM test").oneValue!string; 760 assert(text == "My-cat-is-black"); 761 } 762 763 /++ 764 Creates and registers a collation function in the database. 765 766 Params: 767 name = The name that the function will have in the database. 768 769 fun = a delegate or function that implements the collation. The function $(D_PARAM fun) 770 must be `nothrow`` and satisfy these criteria: 771 $(UL 772 $(LI Takes two string arguments (s1 and s2). These two strings are slices of C-style strings 773 that SQLite manages internally, so there is no guarantee that they are still valid 774 when the function returns.) 775 $(LI Returns an integer (ret).) 776 $(LI If s1 is less than s2, ret < 0.) 777 $(LI If s1 is equal to s2, ret == 0.) 778 $(LI If s1 is greater than s2, ret > 0.) 779 $(LI If s1 is equal to s2, then s2 is equal to s1.) 780 $(LI If s1 is equal to s2 and s2 is equal to s3, then s1 is equal to s3.) 781 $(LI If s1 is less than s2, then s2 is greater than s1.) 782 $(LI If s1 is less than s2 and s2 is less than s3, then s1 is less than s3.) 783 ) 784 785 See_Also: $(LINK http://www.sqlite.org/lang_aggfunc.html) 786 +/ 787 void createCollation(T)(string name, T fun) 788 if (isFunctionPointer!T || isDelegate!T) 789 { 790 import std.traits : isImplicitlyConvertible, functionAttributes, FunctionAttribute, 791 ParameterTypeTuple, isSomeString, ReturnType; 792 793 static assert(isImplicitlyConvertible!(typeof(fun("a", "b")), int), 794 "the collation function has a wrong signature"); 795 796 static assert(functionAttributes!(T) & FunctionAttribute.nothrow_, 797 "only nothrow functions are allowed as collations"); 798 799 alias PT = ParameterTypeTuple!fun; 800 static assert(isSomeString!(PT[0]), 801 "the first argument of function " ~ name ~ " should be a string"); 802 static assert(isSomeString!(PT[1]), 803 "the second argument of function " ~ name ~ " should be a string"); 804 static assert(isImplicitlyConvertible!(ReturnType!fun, int), 805 "function " ~ name ~ " should return a value convertible to an int"); 806 807 extern (C) static nothrow 808 int x_compare(void* ptr, int n1, const(void)* str1, int n2, const(void)* str2) 809 { 810 static string slice(const(void)* str, int n) nothrow 811 { 812 // The string data is owned by SQLite, so it should be safe 813 // to take a slice of it. 814 return str ? (cast(immutable) (cast(const(char)*) str)[0 .. n]) : null; 815 } 816 817 return delegateUnwrap!T(ptr).dlg(slice(str1, n1), slice(str2, n2)); 818 } 819 820 assert(p.handle); 821 auto dgw = delegateWrap(fun, name); 822 auto result = sqlite3_create_collation_v2(p.handle, name.toStringz, SQLITE_UTF8, 823 delegateWrap(fun, name), &x_compare, &ptrFree); 824 if (result != SQLITE_OK) 825 { 826 ptrFree(dgw); 827 throw new SqliteException(errmsg(p.handle), result); 828 } 829 } 830 /// 831 unittest // Collation creation 832 { 833 // The implementation of the collation 834 int my_collation(string s1, string s2) nothrow 835 { 836 import std.uni : icmp; 837 import std.exception : assumeWontThrow; 838 839 return assumeWontThrow(icmp(s1, s2)); 840 } 841 842 auto db = Database(":memory:"); 843 db.createCollation("my_coll", &my_collation); 844 db.run("CREATE TABLE test (word TEXT); 845 INSERT INTO test (word) VALUES ('straße'); 846 INSERT INTO test (word) VALUES ('strasses');"); 847 848 auto word = db.execute("SELECT word FROM test ORDER BY word COLLATE my_coll") 849 .oneValue!string; 850 assert(word == "straße"); 851 } 852 853 /++ 854 Registers a delegate of type `UpdateHookDelegate` as the database's update hook. 855 856 Any previously set hook is released. Pass `null` to disable the callback. 857 858 See_Also: $(LINK http://www.sqlite.org/c3ref/commit_hook.html). 859 +/ 860 void setUpdateHook(UpdateHookDelegate updateHook) 861 { 862 extern(C) static nothrow 863 void callback(void* ptr, int type, const(char)* dbName, const(char)* tableName, long rowid) 864 { 865 WrappedDelegate!UpdateHookDelegate* dg; 866 dg = delegateUnwrap!UpdateHookDelegate(ptr); 867 dg.dlg(type, dbName.to!string, tableName.to!string, rowid); 868 } 869 870 ptrFree(p.updateHook); 871 p.updateHook = delegateWrap(updateHook); 872 sqlite3_update_hook(p.handle, &callback, p.updateHook); 873 } 874 875 /++ 876 Registers a delegate of type `CommitHookDelegate` as the database's commit hook. 877 Any previously set hook is released. 878 879 Params: 880 commitHook = A delegate that should return a non-zero value 881 if the operation must be rolled back, or 0 if it can commit. 882 Pass `null` to disable the callback. 883 884 See_Also: $(LINK http://www.sqlite.org/c3ref/commit_hook.html). 885 +/ 886 void setCommitHook(CommitHookDelegate commitHook) 887 { 888 extern(C) static nothrow 889 int callback(void* ptr) 890 { 891 auto dlg = delegateUnwrap!CommitHookDelegate(ptr).dlg; 892 return dlg(); 893 } 894 895 ptrFree(p.commitHook); 896 p.commitHook = delegateWrap(commitHook); 897 sqlite3_commit_hook(p.handle, &callback, p.commitHook); 898 } 899 900 /++ 901 Registers a delegate of type `RoolbackHookDelegate` as the database's rollback hook. 902 903 Any previously set hook is released. 904 Pass `null` to disable the callback. 905 906 See_Also: $(LINK http://www.sqlite.org/c3ref/commit_hook.html). 907 +/ 908 void setRollbackHook(RoolbackHookDelegate rollbackHook) 909 { 910 extern(C) static nothrow 911 void callback(void* ptr) 912 { 913 auto dlg = delegateUnwrap!RoolbackHookDelegate(ptr).dlg; 914 dlg(); 915 } 916 917 ptrFree(p.rollbackHook); 918 p.rollbackHook = delegateWrap(rollbackHook); 919 sqlite3_rollback_hook(p.handle, &callback, p.rollbackHook); 920 } 921 922 /++ 923 Registers a delegate of type `ProgressHandlerDelegate` as the progress handler. 924 925 Any previously set handler is released. 926 Pass `null` to disable the callback. 927 928 Params: 929 pace = The approximate number of virtual machine instructions that are 930 evaluated between successive invocations of the handler. 931 932 progressHandler = A delegate that should return 0 if the operation can continue 933 or another value if it must be aborted. 934 935 See_Also: $(LINK http://www.sqlite.org/c3ref/progress_handler.html). 936 +/ 937 void setProgressHandler(int pace, ProgressHandlerDelegate progressHandler) 938 { 939 extern(C) static nothrow 940 int callback(void* ptr) 941 { 942 auto dlg = delegateUnwrap!ProgressHandlerDelegate(ptr).dlg; 943 return dlg(); 944 } 945 946 ptrFree(p.progressHandler); 947 p.progressHandler = delegateWrap(progressHandler); 948 sqlite3_progress_handler(p.handle, pace, &callback, p.progressHandler); 949 } 950 951 /++ 952 Registers a delegate of type `TraceCallbackDelegate` as the trace callback. 953 954 Any previously set trace callback is released. 955 Pass `null` to disable the callback. 956 957 The string parameter that is passed to the callback is the SQL text of the statement being 958 executed. 959 960 See_Also: $(LINK http://www.sqlite.org/c3ref/profile.html). 961 +/ 962 void setTraceCallback(TraceCallbackDelegate traceCallback) 963 { 964 extern(C) static nothrow 965 void callback(void* ptr, const(char)* str) 966 { 967 auto dlg = delegateUnwrap!TraceCallbackDelegate(ptr).dlg; 968 dlg(str.to!string); 969 } 970 971 ptrFree(p.traceCallback); 972 p.traceCallback = delegateWrap(traceCallback); 973 sqlite3_trace(p.handle, &callback, p.traceCallback); 974 } 975 976 /++ 977 Registers a delegate of type `ProfileCallbackDelegate` as the profile callback. 978 979 Any previously set profile callback is released. 980 Pass `null` to disable the callback. 981 982 The string parameter that is passed to the callback is the SQL text of the statement being 983 executed. The time unit is defined in SQLite's documentation as nanoseconds (subject to change, 984 as the functionality is experimental). 985 986 See_Also: $(LINK http://www.sqlite.org/c3ref/profile.html). 987 +/ 988 void setProfileCallback(ProfileCallbackDelegate profileCallback) 989 { 990 extern(C) static nothrow 991 void callback(void* ptr, const(char)* str, sqlite3_uint64 time) 992 { 993 auto dlg = delegateUnwrap!ProfileCallbackDelegate(ptr).dlg; 994 dlg(str.to!string, time); 995 } 996 997 ptrFree(p.profileCallback); 998 p.profileCallback = delegateWrap(profileCallback); 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, or 0 if this _code is not known. 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 { 1340 this.sql = sql; 1341 this.code = code; 1342 super(msg, file, line, next); 1343 } 1344 1345 this(string msg, int code, string sql = null, 1346 string file = __FILE__, size_t line = __LINE__, Throwable next = null) 1347 { 1348 this("error %d: %s".format(code, msg), sql, code, file, line, next); 1349 } 1350 1351 this(string msg, string sql = null, 1352 string file = __FILE__, size_t line = __LINE__, Throwable next = null) 1353 { 1354 this(msg, sql, 0, file, line, next); 1355 } 1356 }