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 debug string filename; 81 82 this(sqlite3* handle) 83 { 84 this.handle = handle; 85 } 86 87 ~this() 88 { 89 debug ensureNotInGC!Database(filename); 90 close(); 91 } 92 93 void close() 94 { 95 if (!handle) 96 return; 97 98 sqlite3_progress_handler(handle, 0, null, null); 99 auto result = sqlite3_close(handle); 100 //enforce(result == SQLITE_OK, new SqliteException(errmsg(handle), result)); 101 handle = null; 102 ptrFree(updateHook); 103 ptrFree(commitHook); 104 ptrFree(rollbackHook); 105 ptrFree(progressHandler); 106 ptrFree(traceCallback); 107 ptrFree(profileCallback); 108 } 109 } 110 111 RefCounted!(Payload, RefCountedAutoInitialize.no) p; 112 113 void check(int result) 114 { 115 enforce(result == SQLITE_OK, new SqliteException(errmsg(p.handle), result)); 116 } 117 118 public: 119 /++ 120 Opens a database connection. 121 122 Params: 123 path = The path to the database file. In recent versions of SQLite, the path can be 124 an URI with options. 125 126 flags = Options flags. 127 128 See_Also: $(LINK http://www.sqlite.org/c3ref/open.html) to know how to use the flags 129 parameter or to use path as a file URI if the current configuration allows it. 130 +/ 131 this(string path, int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE) 132 { 133 sqlite3* hdl; 134 auto result = sqlite3_open_v2(path.toStringz, &hdl, flags, null); 135 enforce(result == SQLITE_OK, new SqliteException(hdl ? errmsg(hdl) : "Error opening the database", result)); 136 p = Payload(hdl); 137 debug p.filename = path; 138 } 139 140 /++ 141 Explicitly closes the database connection. 142 143 After a call to `close()`, using the database connection or one of its prepared statement 144 is an error. The `Database` object is destroyed and cannot be used any more. 145 +/ 146 void close() 147 { 148 p.close(); 149 destroy(p); 150 } 151 152 /++ 153 Gets the SQLite internal _handle of the database connection. 154 +/ 155 sqlite3* handle() @property nothrow 156 { 157 return p.handle; 158 } 159 160 /++ 161 Gets the path associated with an attached database. 162 163 Params: 164 database = The name of an attached database. 165 166 Returns: The absolute path of the attached database. 167 If there is no attached database, or if database is a temporary or 168 in-memory database, then null is returned. 169 +/ 170 string attachedFilePath(string database = "main") 171 { 172 assert(p.handle); 173 return sqlite3_db_filename(p.handle, database.toStringz).to!string; 174 } 175 176 /++ 177 Gets the read-only status of an attached database. 178 179 Params: 180 database = The name of an attached database. 181 +/ 182 bool isReadOnly(string database = "main") 183 { 184 immutable ret = sqlite3_db_readonly(p.handle, database.toStringz); 185 enforce(ret >= 0, new SqliteException("Database not found: %s".format(database))); 186 return ret == 1; 187 } 188 189 /++ 190 Gets metadata for a specific table column of an attached database. 191 192 Params: 193 table = The name of the table. 194 195 column = The name of the column. 196 197 database = The name of a database attached. If null, then all attached databases 198 are searched for the table using the same algorithm used by the database engine 199 to resolve unqualified table references. 200 +/ 201 TableColumnMetadata tableColumnMetadata(string table, string column, string database = "main") 202 { 203 TableColumnMetadata data; 204 char* pzDataType, pzCollSeq; 205 int notNull, primaryKey, autoIncrement; 206 check(sqlite3_table_column_metadata(p.handle, database.toStringz, table.toStringz, 207 column.toStringz, &pzDataType, &pzCollSeq, ¬Null, &primaryKey, &autoIncrement)); 208 data.declaredTypeName = pzDataType.to!string; 209 data.collationSequenceName = pzCollSeq.to!string; 210 data.isNotNull = cast(bool) notNull; 211 data.isPrimaryKey = cast(bool) primaryKey; 212 data.isAutoIncrement = cast(bool) autoIncrement; 213 return data; 214 } 215 216 /++ 217 Executes a single SQL statement and returns the results directly. 218 219 It's the equivalent of `prepare(sql).execute()`. 220 Or when used with args the equivalent of: 221 --- 222 auto stm = prepare(sql); 223 stm.bindAll(args); 224 stm.execute(); 225 --- 226 227 The results become undefined when the Database goes out of scope and is destroyed. 228 229 Params: 230 sql = The code of the SQL statement. 231 args = Optional arguments to bind to the SQL statement. 232 +/ 233 ResultRange execute(Args...)(string sql, Args args) 234 { 235 auto stm = prepare(sql); 236 static if (Args.length) stm.bindAll(args); 237 return stm.execute(); 238 } 239 /// 240 unittest 241 { 242 auto db = Database(":memory:"); 243 db.execute("CREATE TABLE test (val INTEGER)"); 244 db.execute("INSERT INTO test (val) VALUES (:v)", 1); 245 assert(db.execute("SELECT val FROM test WHERE val=:v", 1).oneValue!int == 1); 246 } 247 248 /++ 249 Runs an SQL script that can contain multiple statements. 250 251 Params: 252 script = The code of the SQL script. 253 254 dg = A delegate to call for each statement to handle the results. The passed 255 ResultRange will be empty if a statement doesn't return rows. If the delegate 256 return false, the execution is aborted. 257 +/ 258 void run(string script, bool delegate(ResultRange) dg = null) 259 { 260 foreach (sql; script.byStatement) 261 { 262 auto stmt = prepare(sql); 263 auto results = stmt.execute(); 264 if (dg && !dg(results)) 265 return; 266 } 267 } 268 /// 269 unittest 270 { 271 auto db = Database(":memory:"); 272 db.run(`CREATE TABLE test1 (val INTEGER); 273 CREATE TABLE test2 (val FLOAT); 274 DROP TABLE test1; 275 DROP TABLE test2;`); 276 } 277 278 /++ 279 Prepares (compiles) a single SQL statement and returngs it, so that it can be bound to 280 values before execution. 281 282 The statement becomes invalid if the Database goes out of scope and is destroyed. 283 +/ 284 Statement prepare(string sql) 285 { 286 return Statement(this, sql); 287 } 288 289 /// Convenience functions equivalent to an SQL statement. 290 void begin() { execute("BEGIN"); } 291 /// Ditto 292 void commit() { execute("COMMIT"); } 293 /// Ditto 294 void rollback() { execute("ROLLBACK"); } 295 296 /++ 297 Returns the rowid of the last INSERT statement. 298 +/ 299 long lastInsertRowid() 300 { 301 assert(p.handle); 302 return sqlite3_last_insert_rowid(p.handle); 303 } 304 305 /++ 306 Gets the number of database rows that were changed, inserted or deleted by the most 307 recently executed SQL statement. 308 +/ 309 int changes() @property nothrow 310 { 311 assert(p.handle); 312 return sqlite3_changes(p.handle); 313 } 314 315 /++ 316 Gets the number of database rows that were changed, inserted or deleted since the 317 database was opened. 318 +/ 319 int totalChanges() @property nothrow 320 { 321 assert(p.handle); 322 return sqlite3_total_changes(p.handle); 323 } 324 325 /++ 326 Gets the SQLite error code of the last operation. 327 +/ 328 int errorCode() @property nothrow 329 { 330 return p.handle ? sqlite3_errcode(p.handle) : 0; 331 } 332 333 /++ 334 Interrupts any pending database operations. 335 336 It's safe to call this function from anouther thread. 337 338 See_also: $(LINK http://www.sqlite.org/c3ref/interrupt.html). 339 +/ 340 void interrupt() 341 { 342 assert(p.handle); 343 sqlite3_interrupt(p.handle); 344 } 345 346 /++ 347 Sets a connection configuration option. 348 349 See_Also: $(LINK http://www.sqlite.org/c3ref/db_config.html). 350 +/ 351 void config(Args...)(int code, Args args) 352 { 353 auto result = sqlite3_db_config(p.handle, code, args); 354 enforce(result == SQLITE_OK, new SqliteException("Database configuration: error %s".format(result))); 355 } 356 357 /++ 358 Enables or disables loading extensions. 359 +/ 360 void enableLoadExtensions(bool enable = true) 361 { 362 enforce(sqlite3_enable_load_extension(p.handle, enable) == SQLITE_OK, 363 new SqliteException("Could not enable loading extensions.")); 364 } 365 366 /++ 367 Loads an extension. 368 369 Params: 370 path = The path of the extension file. 371 372 entryPoint = The name of the entry point function. If null is passed, SQLite 373 uses the name of the extension file as the entry point. 374 +/ 375 void loadExtension(string path, string entryPoint = null) 376 { 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 sqlite3_update_hook(p.handle, &callback, p.updateHook); 859 } 860 861 /++ 862 Registers a delegate of type `CommitHookDelegate` as the database's commit hook. 863 Any previously set hook is released. 864 865 Params: 866 commitHook = A delegate that should return a non-zero value 867 if the operation must be rolled back, or 0 if it can commit. 868 Pass `null` to disable the callback. 869 870 See_Also: $(LINK http://www.sqlite.org/c3ref/commit_hook.html). 871 +/ 872 void setCommitHook(CommitHookDelegate commitHook) 873 { 874 extern(C) static nothrow 875 int callback(void* ptr) 876 { 877 auto dlg = delegateUnwrap!CommitHookDelegate(ptr).dlg; 878 return dlg(); 879 } 880 881 ptrFree(p.commitHook); 882 p.commitHook = delegateWrap(commitHook); 883 sqlite3_commit_hook(p.handle, &callback, p.commitHook); 884 } 885 886 /++ 887 Registers a delegate of type `RoolbackHookDelegate` as the database's rollback hook. 888 889 Any previously set hook is released. 890 Pass `null` to disable the callback. 891 892 See_Also: $(LINK http://www.sqlite.org/c3ref/commit_hook.html). 893 +/ 894 void setRollbackHook(RoolbackHookDelegate rollbackHook) 895 { 896 extern(C) static nothrow 897 void callback(void* ptr) 898 { 899 auto dlg = delegateUnwrap!RoolbackHookDelegate(ptr).dlg; 900 dlg(); 901 } 902 903 ptrFree(p.rollbackHook); 904 p.rollbackHook = delegateWrap(rollbackHook); 905 sqlite3_rollback_hook(p.handle, &callback, p.rollbackHook); 906 } 907 908 /++ 909 Registers a delegate of type `ProgressHandlerDelegate` as the progress handler. 910 911 Any previously set handler is released. 912 Pass `null` to disable the callback. 913 914 Params: 915 pace = The approximate number of virtual machine instructions that are 916 evaluated between successive invocations of the handler. 917 918 progressHandler = A delegate that should return 0 if the operation can continue 919 or another value if it must be aborted. 920 921 See_Also: $(LINK http://www.sqlite.org/c3ref/progress_handler.html). 922 +/ 923 void setProgressHandler(int pace, ProgressHandlerDelegate progressHandler) 924 { 925 extern(C) static nothrow 926 int callback(void* ptr) 927 { 928 auto dlg = delegateUnwrap!ProgressHandlerDelegate(ptr).dlg; 929 return dlg(); 930 } 931 932 ptrFree(p.progressHandler); 933 p.progressHandler = delegateWrap(progressHandler); 934 sqlite3_progress_handler(p.handle, pace, &callback, p.progressHandler); 935 } 936 937 /++ 938 Registers a delegate of type `TraceCallbackDelegate` as the trace callback. 939 940 Any previously set trace callback is released. 941 Pass `null` to disable the callback. 942 943 The string parameter that is passed to the callback is the SQL text of the statement being 944 executed. 945 946 See_Also: $(LINK http://www.sqlite.org/c3ref/profile.html). 947 +/ 948 void setTraceCallback(TraceCallbackDelegate traceCallback) 949 { 950 extern(C) static nothrow 951 void callback(void* ptr, const(char)* str) 952 { 953 auto dlg = delegateUnwrap!TraceCallbackDelegate(ptr).dlg; 954 dlg(str.to!string); 955 } 956 957 ptrFree(p.traceCallback); 958 p.traceCallback = delegateWrap(traceCallback); 959 sqlite3_trace(p.handle, &callback, p.traceCallback); 960 } 961 962 /++ 963 Registers a delegate of type `ProfileCallbackDelegate` as the profile callback. 964 965 Any previously set profile callback is released. 966 Pass `null` to disable the callback. 967 968 The string parameter that is passed to the callback is the SQL text of the statement being 969 executed. The time unit is defined in SQLite's documentation as nanoseconds (subject to change, 970 as the functionality is experimental). 971 972 See_Also: $(LINK http://www.sqlite.org/c3ref/profile.html). 973 +/ 974 void setProfileCallback(ProfileCallbackDelegate profileCallback) 975 { 976 extern(C) static nothrow 977 void callback(void* ptr, const(char)* str, sqlite3_uint64 time) 978 { 979 auto dlg = delegateUnwrap!ProfileCallbackDelegate(ptr).dlg; 980 dlg(str.to!string, time); 981 } 982 983 ptrFree(p.profileCallback); 984 p.profileCallback = delegateWrap(profileCallback); 985 sqlite3_profile(p.handle, &callback, p.profileCallback); 986 } 987 988 version (_UnlockNotify) 989 { 990 /++ 991 Registers a `IUnlockNotifyHandler` used to handle database locks. 992 993 When running in shared-cache mode, a database operation may fail with an SQLITE_LOCKED error if 994 the required locks on the shared-cache or individual tables within the shared-cache cannot be obtained. 995 See SQLite Shared-Cache Mode for a description of shared-cache locking. 996 This API may be used to register a callback that SQLite will invoke when the connection currently 997 holding the required lock relinquishes it. 998 This API can be used only if the SQLite library was compiled with the `SQLITE_ENABLE_UNLOCK_NOTIFY` 999 C-preprocessor symbol defined. 1000 1001 See_Also: $(LINK http://sqlite.org/c3ref/unlock_notify.html). 1002 1003 Parameters: 1004 unlockNotifyHandler - custom handler used to control the unlocking mechanism 1005 +/ 1006 void setUnlockNotifyHandler(IUnlockNotifyHandler unlockNotifyHandler) 1007 { 1008 p.unlockNotifyHandler = unlockNotifyHandler; 1009 } 1010 1011 /// Setup and waits for unlock notify using the provided `IUnlockNotifyHandler` 1012 package (d2sqlite3) auto waitForUnlockNotify() 1013 { 1014 if (p.unlockNotifyHandler is null) return SQLITE_LOCKED; 1015 1016 version (SqliteEnableUnlockNotify) 1017 { 1018 extern(C) static nothrow 1019 void callback(void** ntfPtr, int nPtr) 1020 { 1021 for (int i=0; i<nPtr; i++) 1022 { 1023 auto handler = cast(IUnlockNotifyHandler*)ntfPtr[i]; 1024 handler.emit(SQLITE_OK); 1025 } 1026 } 1027 1028 int rc = sqlite3_unlock_notify(p.handle, &callback, &p.unlockNotifyHandler); 1029 assert(rc==SQLITE_LOCKED || rc==SQLITE_OK); 1030 1031 /+ The call to sqlite3_unlock_notify() always returns either SQLITE_LOCKED or SQLITE_OK. 1032 1033 If SQLITE_LOCKED was returned, then the system is deadlocked. In this case this function 1034 needs to return SQLITE_LOCKED to the caller so that the current transaction can be rolled 1035 back. Otherwise, block until the unlock-notify callback is invoked, then return SQLITE_OK. 1036 +/ 1037 if(rc == SQLITE_OK) 1038 { 1039 p.unlockNotifyHandler.wait(); 1040 scope (exit) p.unlockNotifyHandler.reset(); 1041 return p.unlockNotifyHandler.result; 1042 } 1043 return rc; 1044 } 1045 else 1046 { 1047 p.unlockNotifyHandler.waitOne(); 1048 auto res = p.unlockNotifyHandler.result; 1049 if (res != SQLITE_OK) p.unlockNotifyHandler.reset(); 1050 return res; 1051 } 1052 } 1053 } 1054 } 1055 1056 /// Delegate types 1057 alias UpdateHookDelegate = void delegate(int type, string dbName, string tableName, long rowid) nothrow; 1058 /// ditto 1059 alias CommitHookDelegate = int delegate() nothrow; 1060 /// ditto 1061 alias RoolbackHookDelegate = void delegate() nothrow; 1062 /// ditto 1063 alias ProgressHandlerDelegate = int delegate() nothrow; 1064 /// ditto 1065 alias TraceCallbackDelegate = void delegate(string sql) nothrow; 1066 /// ditto 1067 alias ProfileCallbackDelegate = void delegate(string sql, ulong time) nothrow; 1068 1069 /// Information about a table column. 1070 struct TableColumnMetadata 1071 { 1072 string declaredTypeName; /// 1073 string collationSequenceName; /// 1074 bool isNotNull; /// 1075 bool isPrimaryKey; /// 1076 bool isAutoIncrement; /// 1077 } 1078 1079 version (_UnlockNotify) 1080 { 1081 /++ 1082 UnlockNotifyHandler interface to be used for custom implementations of UnlockNotify pattern with SQLite. 1083 1084 Note: 1085 For the C API sqlite3_unlock_notify to be used, this library must be compiled with 1086 `-version=SqliteEnableUnlockNotify`. 1087 Otherwise only emulated solution is provided, that is based on retries for the defined amount of time. 1088 1089 Implementation must be able to handle situation when emit is called sooner than the wait itself. 1090 1091 See_Also: $(LINK http://sqlite.org/c3ref/unlock_notify.html). 1092 See_Also: $(LINK http://www.sqlite.org/unlock_notify.html). 1093 +/ 1094 interface IUnlockNotifyHandler 1095 { 1096 version (SqliteEnableUnlockNotify) 1097 { 1098 /// Blocks until emit is called 1099 void wait(); 1100 1101 /++ 1102 Unlocks the handler. 1103 This is called from registered callback from SQLite. 1104 1105 Params: 1106 state - Value to set as a handler result. It can be SQLITE_LOCKED or SQLITE_OK. 1107 +/ 1108 void emit(int state) nothrow; 1109 } 1110 else 1111 { 1112 /++ 1113 This is used as an alternative when SQLite is not compiled with SQLITE_ENABLE_UNLOCK_NOTIFY, and 1114 when the library is built with `-version=SqliteFakeUnlockNotify`. 1115 Using this, the handler tries to wait out the SQLITE_LOCKED state for some time. 1116 Implementation have to block for some amount of time and check if total amount is not greater than some constant afterwards. 1117 If there is still some time to try again, the handler must set the result to SQLITE_OK or to SQLITE_LOCKED otherwise. 1118 +/ 1119 void waitOne(); 1120 } 1121 1122 /// Resets the handler for the next use 1123 void reset(); 1124 1125 /// Result after wait is finished 1126 @property int result() const; 1127 } 1128 1129 version (SqliteEnableUnlockNotify) 1130 { 1131 /++ 1132 UnlockNotifyHandler used when SQLite is compiled with SQLITE_ENABLE_UNLOCK_NOTIFY, and 1133 when the library is built with `-version=SqliteEnableUnlockNotify`. 1134 It is implemented using the standard `core.sync` package. 1135 1136 Use setUnlockNotifyHandler method to handle the database lock. 1137 1138 See_Also: $(LINK http://sqlite.org/c3ref/unlock_notify.html). 1139 See_Also: $(LINK http://www.sqlite.org/unlock_notify.html). 1140 +/ 1141 final class UnlockNotifyHandler : IUnlockNotifyHandler 1142 { 1143 import core.sync.condition : Condition; 1144 import core.sync.mutex : Mutex; 1145 1146 private 1147 { 1148 __gshared Mutex mtx; 1149 __gshared Condition cond; 1150 __gshared int res; 1151 __gshared bool fired; 1152 } 1153 1154 /// Constructor 1155 this() 1156 { 1157 mtx = new Mutex(); 1158 cond = new Condition(mtx); 1159 } 1160 1161 /// Blocks until emit is called 1162 void wait() 1163 { 1164 synchronized (mtx) 1165 { 1166 if (!fired) cond.wait(); 1167 } 1168 } 1169 1170 /// Unlocks the handler, state is one of SQLITE_LOCKED or SQLITE_OK 1171 void emit(int res) nothrow 1172 in { assert(res == SQLITE_LOCKED || res == SQLITE_OK); } 1173 body 1174 { 1175 try 1176 { 1177 synchronized (mtx) 1178 { 1179 this.res = res; 1180 fired = true; 1181 cond.notify(); 1182 } 1183 } 1184 catch (Exception) {} 1185 } 1186 1187 /// Resets the handler for the next use 1188 void reset() 1189 { 1190 res = SQLITE_LOCKED; 1191 fired = false; 1192 } 1193 1194 /// Result after wait is finished 1195 @property int result() const 1196 out (result) { assert(result == SQLITE_OK || result == SQLITE_LOCKED); } 1197 body { return res; } 1198 } 1199 } 1200 else 1201 { 1202 /++ 1203 UnlockNotifyHandler that can be used when SQLite is not compiled with SQLITE_ENABLE_UNLOCK_NOTIFY, 1204 and when the library is built with `-version=SqliteFakeUnlockNotify`.. 1205 It retries the statement execution for the provided amount of time before the SQLITE_LOCKED is returned. 1206 1207 Use setUnlockNotifyHandler method to handle the database lock. 1208 1209 See_Also: $(LINK http://sqlite.org/c3ref/unlock_notify.html). 1210 See_Also: $(LINK http://www.sqlite.org/unlock_notify.html). 1211 +/ 1212 final class UnlockNotifyHandler : IUnlockNotifyHandler 1213 { 1214 import core.time : Duration, msecs; 1215 import std.datetime.stopwatch : StopWatch; 1216 1217 private 1218 { 1219 int res; 1220 Duration maxDuration; 1221 StopWatch sw; 1222 } 1223 1224 /// Constructor 1225 this(Duration max = 1000.msecs) 1226 in { assert(max > Duration.zero); } 1227 body 1228 { 1229 maxDuration = max; 1230 } 1231 1232 /// Blocks for some time to retry the statement 1233 void waitOne() 1234 { 1235 import core.thread : Thread; 1236 import std.random : uniform; 1237 1238 if (!sw.running) sw.start; 1239 1240 Thread.sleep(uniform(50, 100).msecs); 1241 1242 if (sw.peek > maxDuration) 1243 { 1244 sw.stop; 1245 res = SQLITE_LOCKED; 1246 } 1247 else res = SQLITE_OK; 1248 } 1249 1250 /// Resets the handler for the next use 1251 void reset() 1252 { 1253 res = SQLITE_LOCKED; 1254 sw.reset(); 1255 } 1256 1257 /// Result after wait is finished 1258 @property int result() const 1259 out (result) { assert(result == SQLITE_OK || result == SQLITE_LOCKED); } 1260 body 1261 { 1262 return res; 1263 } 1264 } 1265 } 1266 1267 unittest 1268 { 1269 import core.time : Duration, msecs; 1270 1271 /++ 1272 Tests the unlock notify facility. 1273 Params: 1274 delay - time to wait in the transaction to block the other one 1275 expected - expected result (can be used to test timeout when fake unlock notify is used) 1276 +/ 1277 void testUnlockNotify(Duration delay = 500.msecs, int expected = 3) 1278 { 1279 import core.thread : Thread; 1280 import core.time : msecs, seconds; 1281 import std.concurrency : spawn; 1282 1283 static void test(int n, Duration delay) 1284 { 1285 auto db = Database("file::memory:?cache=shared", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_URI | SQLITE_OPEN_MEMORY); 1286 db.setUnlockNotifyHandler = new UnlockNotifyHandler(); 1287 db.execute("BEGIN IMMEDIATE"); 1288 Thread.sleep(delay); 1289 db.execute("INSERT INTO foo (bar) VALUES (?)", n); 1290 db.commit(); 1291 } 1292 1293 auto db = Database("file::memory:?cache=shared", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_URI | SQLITE_OPEN_MEMORY); 1294 db.execute(`CREATE TABLE foo (bar INTEGER);`); 1295 1296 spawn(&test, 1, delay); 1297 Thread.sleep(100.msecs); 1298 spawn(&test, 2, delay); 1299 Thread.sleep(2*delay + 100.msecs); 1300 assert(db.execute("SELECT sum(bar) FROM foo").oneValue!int == expected, format!"%s != %s"(db.execute("SELECT sum(bar) FROM foo").oneValue!int, expected)); 1301 } 1302 1303 testUnlockNotify(); 1304 version (SqliteFakeUnlockNotify) testUnlockNotify(1500.msecs, 1); //timeout test 1305 } 1306 } 1307 1308 /++ 1309 Exception thrown when SQLite functions return an error. 1310 +/ 1311 class SqliteException : Exception 1312 { 1313 /++ 1314 The _code of the error that raised the exception, or 0 if this _code is not known. 1315 +/ 1316 int code; 1317 1318 /++ 1319 The SQL code that raised the exception, if applicable. 1320 +/ 1321 string sql; 1322 1323 private this(string msg, string sql, int code, 1324 string file = __FILE__, size_t line = __LINE__, Throwable next = null) 1325 { 1326 this.sql = sql; 1327 this.code = code; 1328 super(msg, file, line, next); 1329 } 1330 1331 package(d2sqlite3): 1332 this(string msg, int code, string sql = null, 1333 string file = __FILE__, size_t line = __LINE__, Throwable next = null) 1334 { 1335 this("error %d: %s".format(code, msg), sql, code, file, line, next); 1336 } 1337 1338 this(string msg, string sql = null, 1339 string file = __FILE__, size_t line = __LINE__, Throwable next = null) 1340 { 1341 this(msg, sql, 0, file, line, next); 1342 } 1343 }