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