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 moduled2sqlite3.database;
14 15 importd2sqlite3.statement;
16 importd2sqlite3.results;
17 importd2sqlite3.sqlite3;
18 importd2sqlite3.internal.memory;
19 importd2sqlite3.internal.util;
20 21 importstd.conv : to;
22 importstd.exception : enforce;
23 importstd..string : format, toStringz;
24 importstd.typecons : Nullable;
25 26 /// Set _UnlockNotify version if compiled with SqliteEnableUnlockNotify or SqliteFakeUnlockNotify27 version (SqliteEnableUnlockNotify) version = _UnlockNotify;
28 elseversion (SqliteFakeUnlockNotify) version = _UnlockNotify;
29 30 /// Type for the internal representation of blobs31 aliasBlob = immutable(ubyte)[];
32 33 /// SQLite type codes34 enumSqliteType35 {
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 enumDeterministic47 {
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 = 057 }
58 59 /++
60 An database connection.
61 62 This struct is a reference-counted wrapper around a `sqlite3*` pointer.
63 +/64 structDatabase65 {
66 importstd.traits : isFunctionPointer, isDelegate;
67 importstd.typecons : RefCounted, RefCountedAutoInitialize;
68 69 private:
70 structPayload71 {
72 sqlite3* handle;
73 void* updateHook;
74 void* commitHook;
75 void* rollbackHook;
76 void* progressHandler;
77 void* traceCallback;
78 void* profileCallback;
79 version (_UnlockNotify) IUnlockNotifyHandlerunlockNotifyHandler;
80 debugstringfilename;
81 82 this(sqlite3* handle)
83 {
84 this.handle = handle;
85 }
86 87 ~this()
88 {
89 debugensureNotInGC!Database(filename);
90 close();
91 }
92 93 voidclose()
94 {
95 if (!handle)
96 return;
97 98 sqlite3_progress_handler(handle, 0, null, null);
99 autoresult = 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 voidcheck(intresult)
114 {
115 enforce(result == SQLITE_OK, newSqliteException(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(stringpath, intflags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE)
132 {
133 sqlite3* hdl;
134 autoresult = sqlite3_open_v2(path.toStringz, &hdl, flags, null);
135 enforce(result == SQLITE_OK, newSqliteException(hdl ? errmsg(hdl) : "Error opening the database", result));
136 p = Payload(hdl);
137 debugp.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 voidclose()
147 {
148 p.close();
149 destroy(p);
150 }
151 152 /++
153 Gets the SQLite internal _handle of the database connection.
154 +/155 sqlite3* handle() @propertynothrow156 {
157 returnp.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 stringattachedFilePath(stringdatabase = "main")
171 {
172 assert(p.handle);
173 returnsqlite3_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 boolisReadOnly(stringdatabase = "main")
183 {
184 immutableret = sqlite3_db_readonly(p.handle, database.toStringz);
185 enforce(ret >= 0, newSqliteException("Database not found: %s".format(database)));
186 returnret == 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 TableColumnMetadatatableColumnMetadata(stringtable, stringcolumn, stringdatabase = "main")
202 {
203 TableColumnMetadatadata;
204 char* pzDataType, pzCollSeq;
205 intnotNull, 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 returndata;
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 ResultRangeexecute(Args...)(stringsql, Argsargs)
234 {
235 autostm = prepare(sql);
236 staticif (Args.length) stm.bindAll(args);
237 returnstm.execute();
238 }
239 ///240 unittest241 {
242 autodb = 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 voidrun(stringscript, booldelegate(ResultRange) dg = null)
259 {
260 foreach (sql; script.byStatement)
261 {
262 autostmt = prepare(sql);
263 autoresults = stmt.execute();
264 if (dg && !dg(results))
265 return;
266 }
267 }
268 ///269 unittest270 {
271 autodb = 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 Statementprepare(stringsql)
285 {
286 returnStatement(this, sql);
287 }
288 289 /// Convenience functions equivalent to an SQL statement.290 voidbegin() { execute("BEGIN"); }
291 /// Ditto292 voidcommit() { execute("COMMIT"); }
293 /// Ditto294 voidrollback() { execute("ROLLBACK"); }
295 296 /++
297 Returns the rowid of the last INSERT statement.
298 +/299 longlastInsertRowid()
300 {
301 assert(p.handle);
302 returnsqlite3_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 intchanges() @propertynothrow310 {
311 assert(p.handle);
312 returnsqlite3_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 inttotalChanges() @propertynothrow320 {
321 assert(p.handle);
322 returnsqlite3_total_changes(p.handle);
323 }
324 325 /++
326 Gets the SQLite error code of the last operation.
327 +/328 interrorCode() @propertynothrow329 {
330 returnp.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 voidinterrupt()
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 voidconfig(Args...)(intcode, Argsargs)
352 {
353 autoresult = sqlite3_db_config(p.handle, code, args);
354 enforce(result == SQLITE_OK, newSqliteException("Database configuration: error %s".format(result)));
355 }
356 357 /++
358 Enables or disables loading extensions.
359 +/360 voidenableLoadExtensions(boolenable = true)
361 {
362 enforce(sqlite3_enable_load_extension(p.handle, enable) == SQLITE_OK,
363 newSqliteException("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 voidloadExtension(stringpath, stringentryPoint = null)
376 {
377 immutableret = sqlite3_load_extension(p.handle, path.toStringz, entryPoint.toStringz, null);
378 enforce(ret == SQLITE_OK, newSqliteException(
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 voidcreateFunction(T)(stringname, Tfun, Deterministicdet = Deterministic.yes)
420 if (isFunctionPointer!T || isDelegate!T)
421 {
422 importstd.meta : AliasSeq, staticMap, EraseAll;
423 importstd.traits : variadicFunctionStyle, Variadic, ParameterTypeTuple,
424 ParameterDefaultValueTuple, ReturnType, Unqual;
425 426 staticassert(variadicFunctionStyle!(fun) == Variadic.no427 || is(ParameterTypeTuple!fun == AliasSeq!(ColumnData[])),
428 "only type-safe variadic functions with ColumnData arguments are supported");
429 430 staticif (is(ParameterTypeTuple!fun == AliasSeq!(ColumnData[])))
431 {
432 extern(C) staticnothrow433 voidx_func(sqlite3_context* context, intargc, sqlite3_value** argv)
434 {
435 stringname;
436 try437 {
438 importstd.array : appender;
439 autoargs = appender!(ColumnData[]);
440 441 foreach (i; 0 .. argc)
442 {
443 autovalue = argv[i];
444 immutabletype = sqlite3_value_type(value);
445 446 finalswitch (type)
447 {
448 caseSqliteType.INTEGER:
449 args.put(ColumnData(getValue!long(value)));
450 break;
451 452 caseSqliteType.FLOAT:
453 args.put(ColumnData(getValue!double(value)));
454 break;
455 456 caseSqliteType.TEXT:
457 args.put(ColumnData(getValue!string(value)));
458 break;
459 460 caseSqliteType.BLOB:
461 args.put(ColumnData(getValue!Blob(value)));
462 break;
463 464 caseSqliteType.NULL:
465 args.put(ColumnData(null));
466 break;
467 }
468 }
469 470 autoptr = sqlite3_user_data(context);
471 472 autowrappedDelegate = delegateUnwrap!T(ptr);
473 autodlg = wrappedDelegate.dlg;
474 name = wrappedDelegate.name;
475 setResult(context, dlg(args.data));
476 }
477 catch (Exceptione)
478 {
479 sqlite3_result_error(context, "error in function %s(): %s"480 .nothrowFormat(name, e.msg).toStringz, -1);
481 }
482 }
483 }
484 else485 {
486 staticassert(!is(ReturnType!fun == void), "function must not return void");
487 488 aliasPT = staticMap!(Unqual, ParameterTypeTuple!fun);
489 aliasPD = ParameterDefaultValueTuple!fun;
490 491 extern (C) staticnothrow492 voidx_func(sqlite3_context* context, intargc, sqlite3_value** argv)
493 {
494 stringname;
495 try496 {
497 // Get the deledate and its name498 autoptr = sqlite3_user_data(context);
499 autowrappedDelegate = delegateUnwrap!T(ptr);
500 autodlg = wrappedDelegate.dlg;
501 name = wrappedDelegate.name;
502 503 enummaxArgc = PT.length;
504 enumminArgc = PT.length - EraseAll!(void, PD).length;
505 506 if (argc > maxArgc)
507 {
508 autotxt = ("too many arguments in function %s(), expecting at most %s"509 ).format(name, maxArgc);
510 sqlite3_result_error(context, txt.toStringz, -1);
511 }
512 elseif (argc < minArgc)
513 {
514 autotxt = ("too few arguments in function %s(), expecting at least %s"515 ).format(name, minArgc);
516 sqlite3_result_error(context, txt.toStringz, -1);
517 }
518 else519 {
520 PTargs;
521 foreach (i, type; PT)
522 {
523 if (i < argc)
524 args[i] = getValue!type(argv[i]);
525 else526 staticif (is(typeof(PD[i])))
527 args[i] = PD[i];
528 }
529 setResult(context, dlg(args));
530 }
531 }
532 catch (Exceptione)
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 unittest551 {
552 stringstar(intcount, stringstarSymbol = "*")
553 {
554 importstd.range : repeat;
555 importstd.array : join;
556 557 returnstarSymbol.repeat(count).join;
558 }
559 560 autodb = 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 unittest567 {
568 // The implementation of the new function569 stringmyList(ColumnData[] args)
570 {
571 importstd.array : appender;
572 importstd..string : format, join;
573 574 autoapp = appender!(string[]);
575 foreach (arg; args)
576 {
577 if (arg.type == SqliteType.TEXT)
578 app.put(`"%s"`.format(arg));
579 else580 app.put("%s".format(arg));
581 }
582 returnapp.data.join(", ");
583 }
584 585 autodb = Database(":memory:");
586 db.createFunction("my_list", &myList);
587 autolist = db.execute("SELECT my_list(42, 3.14, 'text', NULL)").oneValue!string;
588 assert(list == `42, 3.14, "text", null`);
589 }
590 591 /// Ditto592 voidcreateFunction(T)(stringname, Tfun = 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 voidcreateAggregate(T)(stringname, Tagg, Deterministicdet = Deterministic.yes)
620 {
621 importstd.meta : staticMap;
622 importstd.traits : isAggregateType, ReturnType, variadicFunctionStyle, Variadic,
623 Unqual, ParameterTypeTuple;
624 importcore.stdc.stdlib : malloc;
625 626 staticassert(isAggregateType!T,
627 T.stringof ~ " should be an aggregate type");
628 staticassert(is(typeof(T.accumulate) == function),
629 T.stringof ~ " should have a method named accumulate");
630 staticassert(is(typeof(T.result) == function),
631 T.stringof ~ " should have a method named result");
632 staticassert(is(typeof({
633 aliasRT = ReturnType!(T.result);
634 setResult!RT(null, RT.init);
635 })), T.stringof ~ ".result should return an SQLite-compatible type");
636 staticassert(variadicFunctionStyle!(T.accumulate) == Variadic.no,
637 "variadic functions are not supported");
638 staticassert(variadicFunctionStyle!(T.result) == Variadic.no,
639 "variadic functions are not supported");
640 641 aliasPT = staticMap!(Unqual, ParameterTypeTuple!(T.accumulate));
642 aliasRT = ReturnType!(T.result);
643 644 staticstructContext645 {
646 Taggregate;
647 stringfunctionName;
648 }
649 650 extern(C) staticnothrow651 voidx_step(sqlite3_context* context, int/* argc */, sqlite3_value** argv)
652 {
653 autoctx = cast(Context*) sqlite3_user_data(context);
654 if (!ctx)
655 {
656 sqlite3_result_error_nomem(context);
657 return;
658 }
659 660 PTargs;
661 try662 {
663 foreach (i, type; PT)
664 args[i] = getValue!type(argv[i]);
665 666 ctx.aggregate.accumulate(args);
667 }
668 catch (Exceptione)
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) staticnothrow676 voidx_final(sqlite3_context* context)
677 {
678 autoctx = cast(Context*) sqlite3_user_data(context);
679 if (!ctx)
680 {
681 sqlite3_result_error_nomem(context);
682 return;
683 }
684 685 try686 {
687 setResult(context, ctx.aggregate.result());
688 }
689 catch (Exceptione)
690 {
691 sqlite3_result_error(context, "error in aggregate function %s(): %s"692 .nothrowFormat(ctx.functionName, e.msg).toStringz, -1);
693 }
694 }
695 696 staticif (is(T == class) || is(T == Interface))
697 assert(agg, "Attempt to create an aggregate function from a null reference");
698 699 autoctx = 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 creation709 {
710 importstd.array : Appender, join;
711 712 // The implementation of the aggregate function713 structJoiner714 {
715 private716 {
717 Appender!(string[]) stringList;
718 stringseparator;
719 }
720 721 this(stringseparator)
722 {
723 this.separator = separator;
724 }
725 726 voidaccumulate(stringword)
727 {
728 stringList.put(word);
729 }
730 731 stringresult()
732 {
733 returnstringList.data.join(separator);
734 }
735 }
736 737 autodb = 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 autotext = 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 voidcreateCollation(T)(stringname, Tfun)
774 if (isFunctionPointer!T || isDelegate!T)
775 {
776 importstd.traits : isImplicitlyConvertible, functionAttributes, FunctionAttribute,
777 ParameterTypeTuple, isSomeString, ReturnType;
778 779 staticassert(isImplicitlyConvertible!(typeof(fun("a", "b")), int),
780 "the collation function has a wrong signature");
781 782 staticassert(functionAttributes!(T) & FunctionAttribute.nothrow_,
783 "only nothrow functions are allowed as collations");
784 785 aliasPT = ParameterTypeTuple!fun;
786 staticassert(isSomeString!(PT[0]),
787 "the first argument of function " ~ name ~ " should be a string");
788 staticassert(isSomeString!(PT[1]),
789 "the second argument of function " ~ name ~ " should be a string");
790 staticassert(isImplicitlyConvertible!(ReturnType!fun, int),
791 "function " ~ name ~ " should return a value convertible to an int");
792 793 extern (C) staticnothrow794 intx_compare(void* ptr, intn1, const(void)* str1, intn2, const(void)* str2)
795 {
796 staticstringslice(const(void)* str, intn) nothrow797 {
798 // The string data is owned by SQLite, so it should be safe799 // to take a slice of it.800 returnstr ? (cast(immutable) (cast(const(char)*) str)[0 .. n]) : null;
801 }
802 803 returndelegateUnwrap!T(ptr).dlg(slice(str1, n1), slice(str2, n2));
804 }
805 806 assert(p.handle);
807 autodgw = delegateWrap(fun, name);
808 autoresult = 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 thrownewSqliteException(errmsg(p.handle), result);
814 }
815 }
816 ///817 unittest// Collation creation818 {
819 // The implementation of the collation820 intmy_collation(strings1, strings2) nothrow821 {
822 importstd.uni : icmp;
823 importstd.exception : assumeWontThrow;
824 825 returnassumeWontThrow(icmp(s1, s2));
826 }
827 828 autodb = 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 autoword = 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 voidsetUpdateHook(UpdateHookDelegateupdateHook)
847 {
848 extern(C) staticnothrow849 voidcallback(void* ptr, inttype, const(char)* dbName, const(char)* tableName, longrowid)
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 voidsetCommitHook(CommitHookDelegatecommitHook)
873 {
874 extern(C) staticnothrow875 intcallback(void* ptr)
876 {
877 autodlg = delegateUnwrap!CommitHookDelegate(ptr).dlg;
878 returndlg();
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 voidsetRollbackHook(RoolbackHookDelegaterollbackHook)
895 {
896 extern(C) staticnothrow897 voidcallback(void* ptr)
898 {
899 autodlg = 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 voidsetProgressHandler(intpace, ProgressHandlerDelegateprogressHandler)
924 {
925 extern(C) staticnothrow926 intcallback(void* ptr)
927 {
928 autodlg = delegateUnwrap!ProgressHandlerDelegate(ptr).dlg;
929 returndlg();
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 voidsetTraceCallback(TraceCallbackDelegatetraceCallback)
949 {
950 extern(C) staticnothrow951 voidcallback(void* ptr, const(char)* str)
952 {
953 autodlg = 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 voidsetProfileCallback(ProfileCallbackDelegateprofileCallback)
975 {
976 extern(C) staticnothrow977 voidcallback(void* ptr, const(char)* str, sqlite3_uint64time)
978 {
979 autodlg = 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 voidsetUnlockNotifyHandler(IUnlockNotifyHandlerunlockNotifyHandler)
1007 {
1008 p.unlockNotifyHandler = unlockNotifyHandler;
1009 }
1010 1011 /// Setup and waits for unlock notify using the provided `IUnlockNotifyHandler`1012 package (d2sqlite3) autowaitForUnlockNotify()
1013 {
1014 if (p.unlockNotifyHandlerisnull) returnSQLITE_LOCKED;
1015 1016 version (SqliteEnableUnlockNotify)
1017 {
1018 extern(C) staticnothrow1019 voidcallback(void** ntfPtr, intnPtr)
1020 {
1021 for (inti=0; i<nPtr; i++)
1022 {
1023 autohandler = cast(IUnlockNotifyHandler*)ntfPtr[i];
1024 handler.emit(SQLITE_OK);
1025 }
1026 }
1027 1028 intrc = 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 returnp.unlockNotifyHandler.result;
1042 }
1043 returnrc;
1044 }
1045 else1046 {
1047 p.unlockNotifyHandler.waitOne();
1048 autores = p.unlockNotifyHandler.result;
1049 if (res != SQLITE_OK) p.unlockNotifyHandler.reset();
1050 returnres;
1051 }
1052 }
1053 }
1054 }
1055 1056 /// Delegate types1057 aliasUpdateHookDelegate = voiddelegate(inttype, stringdbName, stringtableName, longrowid) nothrow;
1058 /// ditto1059 aliasCommitHookDelegate = intdelegate() nothrow;
1060 /// ditto1061 aliasRoolbackHookDelegate = voiddelegate() nothrow;
1062 /// ditto1063 aliasProgressHandlerDelegate = intdelegate() nothrow;
1064 /// ditto1065 aliasTraceCallbackDelegate = voiddelegate(stringsql) nothrow;
1066 /// ditto1067 aliasProfileCallbackDelegate = voiddelegate(stringsql, ulongtime) nothrow;
1068 1069 /// Information about a table column.1070 structTableColumnMetadata1071 {
1072 stringdeclaredTypeName; ///1073 stringcollationSequenceName; ///1074 boolisNotNull; ///1075 boolisPrimaryKey; ///1076 boolisAutoIncrement; ///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 interfaceIUnlockNotifyHandler1095 {
1096 version (SqliteEnableUnlockNotify)
1097 {
1098 /// Blocks until emit is called1099 voidwait();
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 voidemit(intstate) nothrow;
1109 }
1110 else1111 {
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 voidwaitOne();
1120 }
1121 1122 /// Resets the handler for the next use1123 voidreset();
1124 1125 /// Result after wait is finished1126 @propertyintresult() 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 finalclassUnlockNotifyHandler : IUnlockNotifyHandler1142 {
1143 importcore.sync.condition : Condition;
1144 importcore.sync.mutex : Mutex;
1145 1146 private1147 {
1148 __gsharedMutexmtx;
1149 __gsharedConditioncond;
1150 __gsharedintres;
1151 __gsharedboolfired;
1152 }
1153 1154 /// Constructor1155 this()
1156 {
1157 mtx = newMutex();
1158 cond = newCondition(mtx);
1159 }
1160 1161 /// Blocks until emit is called1162 voidwait()
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_OK1171 voidemit(intres) nothrow1172 in { assert(res == SQLITE_LOCKED || res == SQLITE_OK); }
1173 body1174 {
1175 try1176 {
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 use1188 voidreset()
1189 {
1190 res = SQLITE_LOCKED;
1191 fired = false;
1192 }
1193 1194 /// Result after wait is finished1195 @propertyintresult() const1196 out (result) { assert(result == SQLITE_OK || result == SQLITE_LOCKED); }
1197 body { returnres; }
1198 }
1199 }
1200 else1201 {
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 finalclassUnlockNotifyHandler : IUnlockNotifyHandler1213 {
1214 importcore.time : Duration, msecs;
1215 importstd.datetime.stopwatch : StopWatch;
1216 1217 private1218 {
1219 intres;
1220 DurationmaxDuration;
1221 StopWatchsw;
1222 }
1223 1224 /// Constructor1225 this(Durationmax = 1000.msecs)
1226 in { assert(max > Duration.zero); }
1227 body1228 {
1229 maxDuration = max;
1230 }
1231 1232 /// Blocks for some time to retry the statement1233 voidwaitOne()
1234 {
1235 importcore.thread : Thread;
1236 importstd.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 elseres = SQLITE_OK;
1248 }
1249 1250 /// Resets the handler for the next use1251 voidreset()
1252 {
1253 res = SQLITE_LOCKED;
1254 sw.reset();
1255 }
1256 1257 /// Result after wait is finished1258 @propertyintresult() const1259 out (result) { assert(result == SQLITE_OK || result == SQLITE_LOCKED); }
1260 body1261 {
1262 returnres;
1263 }
1264 }
1265 }
1266 1267 unittest1268 {
1269 importcore.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 voidtestUnlockNotify(Durationdelay = 500.msecs, intexpected = 3)
1278 {
1279 importcore.thread : Thread;
1280 importcore.time : msecs, seconds;
1281 importstd.concurrency : spawn;
1282 1283 staticvoidtest(intn, Durationdelay)
1284 {
1285 autodb = Database("file::memory:?cache=shared", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_URI | SQLITE_OPEN_MEMORY);
1286 db.setUnlockNotifyHandler = newUnlockNotifyHandler();
1287 db.execute("BEGIN IMMEDIATE");
1288 Thread.sleep(delay);
1289 db.execute("INSERT INTO foo (bar) VALUES (?)", n);
1290 db.commit();
1291 }
1292 1293 autodb = 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 test1305 }
1306 }
1307 1308 /++
1309 Exception thrown when SQLite functions return an error.
1310 +/1311 classSqliteException : Exception1312 {
1313 /++
1314 The _code of the error that raised the exception, or 0 if this _code is not known.
1315 +/1316 intcode;
1317 1318 /++
1319 The SQL code that raised the exception, if applicable.
1320 +/1321 stringsql;
1322 1323 privatethis(stringmsg, stringsql, intcode,
1324 stringfile = __FILE__, size_tline = __LINE__, Throwablenext = null)
1325 {
1326 this.sql = sql;
1327 this.code = code;
1328 super(msg, file, line, next);
1329 }
1330 1331 package(d2sqlite3):
1332 this(stringmsg, intcode, stringsql = null,
1333 stringfile = __FILE__, size_tline = __LINE__, Throwablenext = null)
1334 {
1335 this("error %d: %s".format(code, msg), sql, code, file, line, next);
1336 }
1337 1338 this(stringmsg, stringsql = null,
1339 stringfile = __FILE__, size_tline = __LINE__, Throwablenext = null)
1340 {
1341 this(msg, sql, 0, file, line, next);
1342 }
1343 }