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