1 /++
2 D2SQLite3 provides a thin and convenient wrapper around the SQLite C API.
3 
4 Features:
5 $(UL
6     $(LI Use reference-counted structs (`Database`, `Statement`) instead of SQLite objects
7     pointers.)
8     $(LI Run multistatement SQL code with `Database.run()`.)
9     $(LI Use built-in integral types, floating point types, `string`, `immutable(ubyte)[]` and
10     `Nullable` types directly: conversions to and from SQLite types is automatic and GC-safe.)
11     $(LI Bind multiple values to a prepare statement with `Statement.bindAll()` or
12     `Statement.inject()`. It's also possible to bind the fields of a struct automatically with
13     `Statement.inject()`.)
14     $(LI Handle the results of a query as a range of `Row`s, and the columns of a row
15     as a range of `ColumnData` (equivalent of a `Variant` fit for SQLite types).)
16     $(LI Access the data in a result row directly, by index or by name,
17     with the `Row.peek!T()` methods.)
18     $(LI Make a struct out of the data of a row with `Row.as!T()`.)
19     $(LI Register D functions as SQLite callbacks, with `Database.setUpdateHook()` $(I et al).)
20     $(LI Create new SQLite functions, aggregates or collations out of D functions or delegate,
21     with automatic type converions, with `Database.createFunction()` $(I et al).)
22     $(LI Store all the rows and columns resulting from a query at once with the `cached` function
23     (sometimes useful even if not memory-friendly...).)
24     $(LI Use an unlock notification when two or more connections access the same database in
25     shared-cache mode, either using SQLite's dedicated API (sqlite_unlock_notify) or using an
26     emulated equivalent.)
27 )
28 
29 Modules:
30 $(UL
31     $(LI `d2sqlite3.database`: database connections and library global data.)
32     $(LI `d2sqlite3.statement`: prepared statements creation and parameter binding.)
33     $(LI `d2sqlite3.results`: query results.)
34     $(LI `d2sqlite3.sqlite3`: autogenerated C API bindings.)
35 )
36 
37 Authors:
38     Nicolas Sicard (biozic) and other contributors at $(LINK https://github.com/biozic/d2sqlite3)
39 
40 Copyright:
41     Copyright 2011-17 Nicolas Sicard.
42 
43 License:
44     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
45 +/
46 module d2sqlite3;
47 
48 public import d2sqlite3.database;
49 public import d2sqlite3.statement;
50 public import d2sqlite3.results;
51 public import d2sqlite3.sqlite3;
52 
53 import std.exception : enforce;
54 import std..string : format;
55 
56 ///
57 unittest // Documentation example
58 {
59     // Note: exception handling is left aside for clarity.
60     import d2sqlite3;
61     import std.typecons : Nullable;
62 
63     // Open a database in memory.
64     auto db = Database(":memory:");
65 
66     // Create a table
67     db.run("DROP TABLE IF EXISTS person;
68             CREATE TABLE person (
69               id    INTEGER PRIMARY KEY,
70               name  TEXT NOT NULL,
71               score FLOAT
72             )");
73 
74     // Prepare an INSERT statement
75     Statement statement = db.prepare(
76         "INSERT INTO person (name, score)
77          VALUES (:name, :score)"
78     );
79 
80     // Bind values one by one (by parameter name or index)
81     statement.bind(":name", "John");
82     statement.bind(2, 77.5);
83     statement.execute();
84     statement.reset(); // Need to reset the statement after execution.
85 
86     // Bind muliple values at the same time
87     statement.bindAll("John", null);
88     statement.execute();
89     statement.reset();
90 
91     // Bind, execute and reset in one call
92     statement.inject("Clara", 88.1);
93 
94     // Count the changes
95     assert(db.totalChanges == 3);
96 
97     // Count the Johns in the table.
98     auto count = db.execute("SELECT count(*) FROM person WHERE name == 'John'")
99                    .oneValue!long;
100     assert(count == 2);
101 
102     // Read the data from the table lazily
103     ResultRange results = db.execute("SELECT * FROM person");
104     foreach (Row row; results)
105     {
106         // Retrieve "id", which is the column at index 0, and contains an int,
107         // e.g. using the peek function (best performance).
108         auto id = row.peek!long(0);
109 
110         // Retrieve "name", e.g. using opIndex(string), which returns a ColumnData.
111         auto name = row["name"].as!string;
112 
113         // Retrieve "score", which is at index 2, e.g. using the peek function,
114         // using a Nullable type
115         auto score = row.peek!(Nullable!double)(2);
116         if (!score.isNull)
117         {
118             // ...
119         }
120     }
121 }
122 
123 /++
124 Gets the library's version string (e.g. "3.8.7"), version number (e.g. 3_008_007)
125 or source ID.
126 
127 These values are returned by the linked SQLite C library. They can be checked against
128 the values of the enums defined by the `d2sqlite3` package (`SQLITE_VERSION`,
129 `SQLITE_VERSION_NUMBER` and `SQLITE_SOURCE_ID`).
130 
131 See_Also: $(LINK http://www.sqlite.org/c3ref/libversion.html).
132 +/
133 string versionString()
134 {
135     import std.conv : to;
136     return sqlite3_libversion().to!string;
137 }
138 
139 /// Ditto
140 int versionNumber() nothrow
141 {
142     return sqlite3_libversion_number();
143 }
144 
145 /// Ditto
146 string sourceID()
147 {
148     import std.conv : to;
149     return sqlite3_sourceid().to!string;
150 }
151 
152 /++
153 Tells whether SQLite was compiled with the thread-safe options.
154 
155 See_also: $(LINK http://www.sqlite.org/c3ref/threadsafe.html).
156 +/
157 bool threadSafe() nothrow
158 {
159     return cast(bool) sqlite3_threadsafe();
160 }
161 
162 /++
163 Manually initializes (or shuts down) SQLite.
164 
165 SQLite initializes itself automatically on the first request execution, so this
166 usually wouldn't be called. Use for instance before a call to config().
167 +/
168 void initialize()
169 {
170     auto result = sqlite3_initialize();
171     enforce(result == SQLITE_OK, new SqliteException("Initialization: error %s".format(result)));
172 }
173 /// Ditto
174 void shutdown()
175 {
176     auto result = sqlite3_shutdown();
177     enforce(result == SQLITE_OK, new SqliteException("Shutdown: error %s".format(result)));
178 }
179 
180 /++
181 Sets a configuration option.
182 
183 Use before initialization, e.g. before the first
184 call to initialize and before execution of the first statement.
185 
186 See_Also: $(LINK http://www.sqlite.org/c3ref/config.html).
187 +/
188 void config(Args...)(int code, Args args)
189 {
190     auto result = sqlite3_config(code, args);
191     enforce(result == SQLITE_OK, new SqliteException("Configuration: error %s".format(result)));
192 }
193 
194 /++
195 Tests if an SQLite compile option is set
196 
197 See_Also: $(LINK http://sqlite.org/c3ref/compileoption_get.html).
198 +/
199 bool isCompiledWith(string option)
200 {
201     import std..string : toStringz;
202     return cast(bool) sqlite3_compileoption_used(option.toStringz);
203 }
204 ///
205 version (SqliteEnableUnlockNotify)
206 unittest
207 {
208     assert(isCompiledWith("SQLITE_ENABLE_UNLOCK_NOTIFY"));
209     assert(!isCompiledWith("SQLITE_UNKNOWN_COMPILE_OPTION"));
210 }