1 /++
2 Managing prepared statements.
3 
4 Authors:
5     Nicolas Sicard (biozic) and other contributors at $(LINK https://github.com/biozic/d2sqlite3)
6 
7 Copyright:
8     Copyright 2011-18 Nicolas Sicard.
9 
10 License:
11     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
12 +/
13 module d2sqlite3.statement;
14 
15 import d2sqlite3.database;
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)
28     version = _UnlockNotify;
29 else version (SqliteFakeUnlockNotify)
30     version = _UnlockNotify;
31 
32 /++
33 A prepared statement.
34 
35 This struct is a reference-counted wrapper around a `sqlite3_stmt*` pointer.
36 Instances of this struct are typically returned by `Database.prepare()`.
37 +/
38 struct Statement {
39     import std.meta : allSatisfy;
40     import std.traits : isIntegral, isSomeChar, isBoolean, isFloatingPoint,
41         isSomeString, isStaticArray, isDynamicArray, isIterable;
42     import std.typecons : RefCounted, RefCountedAutoInitialize;
43 
44 private:
45 
46     /// Returns $(D true) if the value can be directly bound to the statement
47     enum bool isBindable(T) = is(T == typeof(null)) || is(T == void*) || isIntegral!T
48         || isSomeChar!T || isBoolean!T || isFloatingPoint!T || isSomeString!T
49         || isStaticArray!T || isDynamicArray!T || is(T == Nullable!U, U...);
50 
51     struct Payload {
52         Database db;
53         sqlite3_stmt* handle; // null if error or empty statement
54         int paramCount;
55         debug string sql;
56 
57         ~this() nothrow {
58             debug ensureNotInGC!Statement(sql);
59             sqlite3_finalize(handle);
60         }
61     }
62 
63     RefCounted!(Payload, RefCountedAutoInitialize.no) p;
64 
65     void checkResult(int result) {
66         enforce(result == SQLITE_OK, new SqliteException(errmsg(p.handle), result));
67     }
68 
69     version (_UnlockNotify) {
70         auto sqlite3_blocking_prepare_v2(Database db, const char* zSql, int nByte,
71                 sqlite3_stmt** ppStmt, const char** pzTail) {
72             int rc;
73             while (SQLITE_LOCKED == (rc = sqlite3_prepare_v2(db.handle(), zSql,
74                     nByte, ppStmt, pzTail))) {
75                 rc = db.waitForUnlockNotify();
76                 if (rc != SQLITE_OK)
77                     break;
78             }
79             return rc;
80         }
81     }
82 
83 package(d2sqlite3):
84     this(Database db, string sql) {
85         sqlite3_stmt* handle;
86         version (_UnlockNotify) {
87             auto result = sqlite3_blocking_prepare_v2(db, sql.toStringz,
88                     sql.length.to!int, &handle, null);
89         } else {
90             auto result = sqlite3_prepare_v2(db.handle(), sql.toStringz,
91                     sql.length.to!int, &handle, null);
92         }
93         enforce(result == SQLITE_OK, new SqliteException(errmsg(db.handle()), result, sql));
94         p = Payload(db, handle);
95         p.paramCount = sqlite3_bind_parameter_count(p.handle);
96         debug p.sql = sql;
97     }
98 
99     version (_UnlockNotify) {
100         /// Setup and waits for unlock notify using the provided `IUnlockNotifyHandler`
101         auto waitForUnlockNotify() {
102             return p.db.waitForUnlockNotify();
103         }
104     }
105 
106 public:
107     /++
108     Gets the SQLite internal _handle of the statement.
109     +/
110     sqlite3_stmt* handle() @property nothrow {
111         return p.handle;
112     }
113 
114     /++
115     Explicitly finalizes the prepared statement.
116 
117     After a call to `finalize()`, the `Statement` object is destroyed and cannot be used.
118     +/
119     void finalize() {
120         destroy(p);
121     }
122 
123     /++
124     Tells whether the statement is empty (no SQL statement).
125     +/
126     bool empty() @property nothrow {
127         return p.handle is null;
128     }
129     ///
130     unittest {
131         auto db = Database(":memory:");
132         auto statement = db.prepare(" ; ");
133         assert(statement.empty);
134     }
135 
136     /++
137     Binds values to parameters of this statement, using parameter index.
138 
139     Params:
140         index = The index of the parameter (starting from 1).
141 
142         value = The bound _value. The type of value must be compatible with the SQLite
143         types: it must be a boolean or numeric type, a string, an array, null,
144         or a Nullable!T where T is any of the previous types.
145     +/
146     void bind(T)(int index, T value) if (is(T == typeof(null)) || is(T == void*))
147     in {
148         assert(index > 0 && index <= p.paramCount, "parameter index out of range");
149     }
150     body {
151         assert(p.handle);
152         checkResult(sqlite3_bind_null(p.handle, index));
153     }
154 
155     /// ditto
156     void bind(T)(int index, T value)
157             if (isIntegral!T || isSomeChar!T || isBoolean!T)
158     in {
159         assert(index > 0 && index <= p.paramCount, "parameter index out of range");
160     }
161     body {
162         assert(p.handle);
163         checkResult(sqlite3_bind_int64(p.handle, index, value.to!long));
164     }
165 
166     /// ditto
167     void bind(T)(int index, T value) if (isFloatingPoint!T)
168     in {
169         assert(index > 0 && index <= p.paramCount, "parameter index out of range");
170     }
171     body {
172         assert(p.handle);
173         checkResult(sqlite3_bind_double(p.handle, index, value.to!double));
174     }
175 
176     /// ditto
177     void bind(T)(int index, T value) if (isSomeString!T)
178     in {
179         assert(index > 0 && index <= p.paramCount, "parameter index out of range");
180     }
181     body {
182         assert(p.handle);
183         string str = value.to!string;
184         auto ptr = anchorMem(cast(void*) str.ptr);
185         checkResult(sqlite3_bind_text64(p.handle, index,
186                 cast(const(char)*) ptr, str.length, &releaseMem, SQLITE_UTF8));
187     }
188 
189     /// ditto
190     void bind(T)(int index, T value) if (isStaticArray!T)
191     in {
192         assert(index > 0 && index <= p.paramCount, "parameter index out of range");
193     }
194     body {
195         assert(p.handle);
196         checkResult(sqlite3_bind_blob64(p.handle, index, cast(void*) value.ptr,
197                 value.sizeof, SQLITE_TRANSIENT));
198     }
199 
200     /// ditto
201     void bind(T)(int index, T value) if (isDynamicArray!T && !isSomeString!T)
202     in {
203         assert(index > 0 && index <= p.paramCount, "parameter index out of range");
204     }
205     body {
206         assert(p.handle);
207         auto arr = cast(void[]) value;
208         checkResult(sqlite3_bind_blob64(p.handle, index, anchorMem(arr.ptr),
209                 arr.length, &releaseMem));
210     }
211 
212     /// ditto
213     void bind(T)(int index, T value) if (is(T == Nullable!U, U...))
214     in {
215         assert(index > 0 && index <= p.paramCount, "parameter index out of range");
216     }
217     body {
218         if (value.isNull) {
219             assert(p.handle);
220             checkResult(sqlite3_bind_null(p.handle, index));
221         } else
222             bind(index, value.get);
223     }
224 
225     /++
226     Binds values to parameters of this statement, using parameter names.
227 
228     Params:
229         name = The name of the parameter, including the ':', '@' or '$' that introduced it.
230 
231         value = The bound _value. The type of value must be compatible with the SQLite
232         types: it must be a boolean or numeric type, a string, an array, null,
233         or a Nullable!T where T is any of the previous types.
234 
235     Warning:
236         While convenient, this overload of `bind` is less performant, because it has to
237         retrieve the column index with a call to the SQLite function
238         `sqlite3_bind_parameter_index`.
239     +/
240     void bind(T)(string name, T value)
241     in {
242         assert(name.length);
243     }
244     body {
245         assert(p.handle);
246         auto index = sqlite3_bind_parameter_index(p.handle, name.toStringz);
247         assert(index > 0, "no parameter named '%s'".format(name));
248         bind(index, value);
249     }
250 
251     /++
252     Binds all the arguments at once in order.
253     +/
254     void bindAll(Args...)(Args args)
255     in {
256         assert(Args.length == this.parameterCount, "parameter count mismatch");
257     }
258     body {
259         foreach (index, _; Args)
260             bind(index + 1, args[index]);
261     }
262 
263     /++
264     Clears the bindings.
265 
266     This does not reset the statement. Use `Statement.reset()` for this.
267     +/
268     void clearBindings() {
269         assert(p.handle);
270         checkResult(sqlite3_clear_bindings(p.handle));
271     }
272 
273     /++
274     Executes the statement and return a (possibly empty) range of results.
275     +/
276     ResultRange execute() {
277         return ResultRange(this);
278     }
279 
280     /++
281     Resets a this statement before a new execution.
282 
283     Calling this method invalidates any `ResultRange` struct returned by a previous call
284     to `Database.execute()` or `Statement.execute()`.
285 
286     This does not clear the bindings. Use `Statement.clearBindings()` for this.
287     +/
288     void reset() {
289         assert(p.handle);
290         checkResult(sqlite3_reset(p.handle));
291     }
292 
293     /++
294     Binds arguments, executes and resets the statement, in one call.
295 
296     This convenience function is equivalent to:
297     ---
298     bindAll(args);
299     execute();
300     reset();
301     ---
302     +/
303     void inject(Args...)(Args args) if (allSatisfy!(isBindable, Args)) {
304         bindAll(args);
305         execute();
306         reset();
307     }
308 
309     /++
310     Binds the fields of a struct in order, executes and resets the statement, in one call.
311     +/
312     void inject(T)(auto ref const T obj) if (is(T == struct)) {
313         import std.meta : Filter;
314         import std.traits : FieldNameTuple;
315 
316         enum accesible(string F) = __traits(compiles, __traits(getMember, obj, F));
317         enum bindable(string F) = isBindable!(typeof(__traits(getMember, obj, F)));
318 
319         alias FieldNames = Filter!(bindable, Filter!(accesible, FieldNameTuple!T));
320         assert(FieldNames.length == this.parameterCount, "parameter count mismatch");
321         foreach (i, field; FieldNames)
322             bind(i + 1, __traits(getMember, obj, field));
323         execute();
324         reset();
325     }
326 
327     /++
328     Binds iterable values in order, executes and resets the statement, in one call.
329     +/
330     void inject(T)(auto ref T obj) if (!isBindable!T && isIterable!T)
331     in {
332         static if (__traits(compiles, obj.length))
333             assert(obj.length == this.parameterCount, "parameter count mismatch");
334     }
335     body {
336         static if (__traits(compiles, {
337                 foreach (string k, ref v; obj) {
338                 }
339             })) {
340             foreach (string k, ref v; obj)
341                 bind(k, v);
342         } else {
343             int i = 1;
344             foreach (ref v; obj)
345                 bind(i++, v);
346         }
347         execute();
348         reset();
349     }
350 
351     /// Gets the count of bind parameters.
352     int parameterCount() nothrow {
353         assert(p.handle);
354         return p.paramCount;
355     }
356 
357     /++
358     Gets the name of the bind parameter at the given index.
359 
360     Params:
361         index = The index of the parameter (the first parameter has the index 1).
362 
363     Returns: The name of the parameter or null is not found or out of range.
364     +/
365     string parameterName(int index)
366     in {
367         assert(index > 0 && index <= p.paramCount, "parameter index out of range");
368     }
369     body {
370         assert(p.handle);
371         return sqlite3_bind_parameter_name(p.handle, index).to!string;
372     }
373 
374     /++
375     Gets the index of a bind parameter.
376 
377     Returns: The index of the parameter (the first parameter has the index 1)
378     or 0 is not found or out of range.
379     +/
380     int parameterIndex(string name)
381     in {
382         assert(name.length);
383     }
384     body {
385         assert(p.handle);
386         return sqlite3_bind_parameter_index(p.handle, name.toStringz);
387     }
388 }
389 
390 /++
391 Turns $(D_PARAM value) into a _literal that can be used in an SQLite expression.
392 +/
393 string literal(T)(T value) {
394     import std.string : replace;
395     import std.traits : isBoolean, isNumeric, isSomeString, isArray;
396 
397     static if (is(T == typeof(null)))
398         return "NULL";
399     else static if (isBoolean!T)
400         return value ? "1" : "0";
401     else static if (isNumeric!T)
402         return value.to!string();
403     else static if (isSomeString!T)
404         return format("'%s'", value.replace("'", "''"));
405     else static if (isArray!T)
406         return "'X%(%X%)'".format(cast(Blob) value);
407     else
408         static assert(false, "cannot make a literal of a value of type " ~ T.stringof);
409 }
410 ///
411 unittest {
412     assert(null.literal == "NULL");
413     assert(false.literal == "0");
414     assert(true.literal == "1");
415     assert(4.literal == "4");
416     assert(4.1.literal == "4.1");
417     assert("foo".literal == "'foo'");
418     assert("a'b'".literal == "'a''b'''");
419     import std.conv : hexString;
420 
421     auto a = cast(Blob) hexString!"DEADBEEF";
422     assert(a.literal == "'XDEADBEEF'");
423 }