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 }