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