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