1 /++ 2 Use a DB via plain SQL statements (unsafe version). 3 4 Commands that are expected to return a result set - queries - have distinctive 5 methods that are enforced. That is it will be an error to call such a method 6 with an SQL command that does not produce a result set. So for commands like 7 SELECT, use the `query` functions. For other commands, like 8 INSERT/UPDATE/CREATE/etc, use `exec`. 9 10 This is the @system version of mysql's command module, and as such uses the @system 11 rows and result ranges, and the `Variant` type. For the `MySQLVal` safe 12 version, please import `mysql.safe.commands`. 13 +/ 14 15 module mysql.unsafe.commands; 16 import SC = mysql.safe.commands; 17 18 import std.conv; 19 import std.exception; 20 import std.range; 21 import std.typecons; 22 import std.variant; 23 24 import mysql.unsafe.connection; 25 import mysql.exceptions; 26 import mysql.unsafe.prepared; 27 import mysql.protocol.comms; 28 import mysql.protocol.constants; 29 import mysql.protocol.extra_types; 30 import mysql.protocol.packets; 31 import mysql.impl.result; 32 import mysql.types; 33 34 alias ColumnSpecialization = SC.ColumnSpecialization; 35 alias CSN = ColumnSpecialization; 36 37 /++ 38 Execute an SQL command or prepared statement, such as INSERT/UPDATE/CREATE/etc. 39 40 This method is intended for commands such as which do not produce a result set 41 (otherwise, use one of the `query` functions instead.) If the SQL command does 42 produces a result set (such as SELECT), `mysql.exceptions.MYXResultRecieved` 43 will be thrown. 44 45 If `args` is supplied, the sql string will automatically be used as a prepared 46 statement. Prepared statements are automatically cached by mysql-native, 47 so there's no performance penalty for using this multiple times for the 48 same statement instead of manually preparing a statement. 49 50 If `args` and `prepared` are both provided, `args` will be used, 51 and any arguments that are already set in the prepared statement 52 will automatically be replaced with `args` (note, just like calling 53 `mysql.prepared.Prepared.setArgs`, this will also remove all 54 `mysql.prepared.ParameterSpecialization` that may have been applied). 55 56 Only use the `const(char[]) sql` overload that doesn't take `args` 57 when you are not going to be using the same 58 command repeatedly and you are CERTAIN all the data you're sending is properly 59 escaped. Otherwise, consider using overload that takes a `Prepared`. 60 61 If you need to use any `mysql.prepared.ParameterSpecialization`, use 62 `mysql.connection.prepare` to manually create a `mysql.prepared.Prepared`, 63 and set your parameter specializations using `mysql.prepared.Prepared.setArg` 64 or `mysql.prepared.Prepared.setArgs`. 65 66 Type_Mappings: $(TYPE_MAPPINGS) 67 68 Params: 69 conn = An open `mysql.connection.Connection` to the database. 70 sql = The SQL command to be run. 71 prepared = The prepared statement to be run. 72 73 Returns: The number of rows affected. 74 75 Example: 76 --- 77 auto myInt = 7; 78 auto rowsAffected = myConnection.exec("INSERT INTO `myTable` (`a`) VALUES (?)", myInt); 79 --- 80 +/ 81 ulong exec(Connection conn, const(char[]) sql, Variant[] args) @system 82 { 83 auto prepared = conn.prepare(sql); 84 prepared.setArgs(args); 85 return exec(conn, prepared); 86 } 87 ///ditto 88 ulong exec(Connection conn, ref Prepared prepared, Variant[] args) @system 89 { 90 prepared.setArgs(args); 91 return exec(conn, prepared); 92 } 93 94 ///ditto 95 ulong exec(Connection conn, ref BackwardCompatPrepared prepared) @system 96 { 97 auto p = prepared.prepared; 98 auto result = exec(conn, p); 99 prepared._prepared = p; 100 return result; 101 } 102 103 ///ditto 104 ulong exec(Connection conn, ref Prepared prepared) @system 105 { 106 return SC.exec(conn, prepared.safeForExec); 107 } 108 109 ///ditto 110 ulong exec(T...)(Connection conn, ref Prepared prepared, T args) 111 if(T.length > 0 && !is(T[0] == Variant[]) && !is(T[0] == MySQLVal[])) 112 { 113 // we are about to set all args, which will clear any parameter specializations. 114 prepared.setArgs(args); 115 return SC.exec(conn, prepared.safe); 116 } 117 118 // Note: this is a wrapper for the safe commands exec functions that do not 119 // involve a Prepared struct directly. 120 ///ditto 121 @safe ulong exec(T...)(Connection conn, const(char[]) sql, T args) 122 if(!is(T[0] == Variant[])) 123 { 124 return SC.exec(conn, sql, args); 125 } 126 127 /++ 128 Execute an SQL SELECT command or prepared statement. 129 130 This returns an input range of `mysql.result.UnsafeRow`, so if you need random access 131 to the `mysql.result.UnsafeRow` elements, simply call 132 $(LINK2 https://dlang.org/phobos/std_array.html#array, `std.array.array()`) 133 on the result. 134 135 If the SQL command does not produce a result set (such as INSERT/CREATE/etc), 136 then `mysql.exceptions.MYXNoResultRecieved` will be thrown. Use 137 `exec` instead for such commands. 138 139 If `args` is supplied, the sql string will automatically be used as a prepared 140 statement. Prepared statements are automatically cached by mysql-native, 141 so there's no performance penalty for using this multiple times for the 142 same statement instead of manually preparing a statement. 143 144 If `args` and `prepared` are both provided, `args` will be used, 145 and any arguments that are already set in the prepared statement 146 will automatically be replaced with `args` (note, just like calling 147 `mysql.prepared.Prepared.setArgs`, this will also remove all 148 `mysql.prepared.ParameterSpecialization` that may have been applied). 149 150 Only use the `const(char[]) sql` overload that doesn't take `args` 151 when you are not going to be using the same 152 command repeatedly and you are CERTAIN all the data you're sending is properly 153 escaped. Otherwise, consider using overload that takes a `Prepared`. 154 155 If you need to use any `mysql.prepared.ParameterSpecialization`, use 156 `mysql.connection.prepare` to manually create a `mysql.prepared.Prepared`, 157 and set your parameter specializations using `mysql.prepared.Prepared.setArg` 158 or `mysql.prepared.Prepared.setArgs`. 159 160 Type_Mappings: $(TYPE_MAPPINGS) 161 162 Params: 163 conn = An open `mysql.connection.Connection` to the database. 164 sql = The SQL command to be run. 165 prepared = The prepared statement to be run. 166 csa = Not yet implemented. 167 168 Returns: A (possibly empty) `mysql.result.UnsafeResultRange`. 169 170 Example: 171 --- 172 UnsafeResultRange oneAtATime = myConnection.query("SELECT * from `myTable`"); 173 UnsafeRow[] allAtOnce = myConnection.query("SELECT * from `myTable`").array; 174 175 auto myInt = 7; 176 UnsafeResultRange rows = myConnection.query("SELECT * FROM `myTable` WHERE `a` = ?", myInt); 177 --- 178 +/ 179 /+ 180 Future text: 181 If there are long data items among the expected result columns you can use 182 the `csa` param to specify that they are to be subject to chunked transfer via a 183 delegate. 184 185 csa = An optional array of `ColumnSpecialization` structs. If you need to 186 use this with a prepared statement, please use `mysql.prepared.Prepared.columnSpecials`. 187 +/ 188 UnsafeResultRange query(Connection conn, const(char[]) sql, ColumnSpecialization[] csa = null) @safe 189 { 190 return SC.query(conn, sql, csa).unsafe; 191 } 192 ///ditto 193 UnsafeResultRange query(T...)(Connection conn, const(char[]) sql, T args) 194 if(T.length > 0 && !is(T[0] == Variant[]) && !is(T[0] == MySQLVal[]) && !is(T[0] == ColumnSpecialization) && !is(T[0] == ColumnSpecialization[])) 195 { 196 return SC.query(conn, sql, args).unsafe; 197 } 198 ///ditto 199 UnsafeResultRange query(Connection conn, const(char[]) sql, Variant[] args) @system 200 { 201 auto prepared = conn.prepare(sql); 202 prepared.setArgs(args); 203 return query(conn, prepared); 204 } 205 ///ditto 206 UnsafeResultRange query(Connection conn, ref Prepared prepared) @system 207 { 208 return SC.query(conn, prepared.safeForExec).unsafe; 209 } 210 ///ditto 211 UnsafeResultRange query(T...)(Connection conn, ref Prepared prepared, T args) 212 if(T.length > 0 && !is(T[0] == Variant[]) && !is(T[0] == MySQLVal[]) && !is(T[0] == ColumnSpecialization) && !is(T[0] == ColumnSpecialization[])) 213 { 214 // this is going to clear any parameter specialization 215 prepared.setArgs(args); 216 return SC.query(conn, prepared.safe, args).unsafe; 217 } 218 ///ditto 219 UnsafeResultRange query(Connection conn, ref Prepared prepared, Variant[] args) @system 220 { 221 prepared.setArgs(args); 222 return query(conn, prepared); 223 } 224 225 ///ditto 226 UnsafeResultRange query(Connection conn, ref BackwardCompatPrepared prepared) @system 227 { 228 auto p = prepared.prepared; 229 auto result = query(conn, p); 230 prepared._prepared = p; 231 return result; 232 } 233 234 /++ 235 Execute an SQL SELECT command or prepared statement where you only want the 236 first `mysql.result.UnsafeRow`, if any. 237 238 If the SQL command does not produce a result set (such as INSERT/CREATE/etc), 239 then `mysql.exceptions.MYXNoResultRecieved` will be thrown. Use 240 `exec` instead for such commands. 241 242 If `args` is supplied, the sql string will automatically be used as a prepared 243 statement. Prepared statements are automatically cached by mysql-native, 244 so there's no performance penalty for using this multiple times for the 245 same statement instead of manually preparing a statement. 246 247 If `args` and `prepared` are both provided, `args` will be used, 248 and any arguments that are already set in the prepared statement 249 will automatically be replaced with `args` (note, just like calling 250 `mysql.prepared.Prepared.setArgs`, this will also remove all 251 `mysql.prepared.ParameterSpecialization` that may have been applied). 252 253 Only use the `const(char[]) sql` overload that doesn't take `args` 254 when you are not going to be using the same 255 command repeatedly and you are CERTAIN all the data you're sending is properly 256 escaped. Otherwise, consider using overload that takes a `Prepared`. 257 258 If you need to use any `mysql.prepared.ParameterSpecialization`, use 259 `mysql.connection.prepare` to manually create a `mysql.prepared.Prepared`, 260 and set your parameter specializations using `mysql.prepared.Prepared.setArg` 261 or `mysql.prepared.Prepared.setArgs`. 262 263 Type_Mappings: $(TYPE_MAPPINGS) 264 265 Params: 266 conn = An open `mysql.connection.Connection` to the database. 267 sql = The SQL command to be run. 268 prepared = The prepared statement to be run. 269 csa = Not yet implemented. 270 271 Returns: `Nullable!(mysql.result.UnsafeRow)`: This will be null (check via `Nullable.isNull`) if the 272 query resulted in an empty result set. 273 274 Example: 275 --- 276 auto myInt = 7; 277 Nullable!UnsafeRow row = myConnection.queryRow("SELECT * FROM `myTable` WHERE `a` = ?", myInt); 278 --- 279 +/ 280 /+ 281 Future text: 282 If there are long data items among the expected result columns you can use 283 the `csa` param to specify that they are to be subject to chunked transfer via a 284 delegate. 285 286 csa = An optional array of `ColumnSpecialization` structs. If you need to 287 use this with a prepared statement, please use `mysql.prepared.Prepared.columnSpecials`. 288 +/ 289 /+ 290 Future text: 291 If there are long data items among the expected result columns you can use 292 the `csa` param to specify that they are to be subject to chunked transfer via a 293 delegate. 294 295 csa = An optional array of `ColumnSpecialization` structs. If you need to 296 use this with a prepared statement, please use `mysql.prepared.Prepared.columnSpecials`. 297 +/ 298 Nullable!UnsafeRow queryRow(Connection conn, const(char[]) sql, ColumnSpecialization[] csa = null) @safe 299 { 300 return SC.queryRow(conn, sql, csa).unsafe; 301 } 302 ///ditto 303 Nullable!UnsafeRow queryRow(T...)(Connection conn, const(char[]) sql, T args) 304 if(T.length > 0 && !is(T[0] == Variant[]) && !is(T[0] == MySQLVal[]) && !is(T[0] == ColumnSpecialization) && !is(T[0] == ColumnSpecialization[])) 305 { 306 return SC.queryRow(conn, sql, args).unsafe; 307 } 308 ///ditto 309 Nullable!UnsafeRow queryRow(Connection conn, const(char[]) sql, Variant[] args) @system 310 { 311 auto prepared = conn.prepare(sql); 312 prepared.setArgs(args); 313 return queryRow(conn, prepared); 314 } 315 ///ditto 316 Nullable!UnsafeRow queryRow(Connection conn, ref Prepared prepared) @system 317 { 318 return SC.queryRow(conn, prepared.safeForExec).unsafe; 319 } 320 ///ditto 321 Nullable!UnsafeRow queryRow(T...)(Connection conn, ref Prepared prepared, T args) @system 322 if(T.length > 0 && !is(T[0] == Variant[]) && !is(T[0] == MySQLVal[]) && !is(T[0] == ColumnSpecialization) && !is(T[0] == ColumnSpecialization[])) 323 { 324 prepared.setArgs(args); 325 return SC.queryRow(conn, prepared.safe, args).unsafe; 326 } 327 ///ditto 328 Nullable!UnsafeRow queryRow(Connection conn, ref Prepared prepared, Variant[] args) @system 329 { 330 prepared.setArgs(args); 331 return queryRow(conn, prepared); 332 } 333 334 ///ditto 335 Nullable!UnsafeRow queryRow(Connection conn, ref BackwardCompatPrepared prepared) @system 336 { 337 auto p = prepared.prepared; 338 auto result = queryRow(conn, p); 339 prepared._prepared = p; 340 return result; 341 } 342 343 /++ 344 Execute an SQL SELECT command or prepared statement where you only want the 345 first `mysql.result.UnsafeRow`, and place result values into a set of D variables. 346 347 This method will throw if any column type is incompatible with the corresponding D variable. 348 349 Unlike the other query functions, queryRowTuple will throw 350 `mysql.exceptions.MYX` if the result set is empty 351 (and thus the reference variables passed in cannot be filled). 352 353 If the SQL command does not produce a result set (such as INSERT/CREATE/etc), 354 then `mysql.exceptions.MYXNoResultRecieved` will be thrown. Use 355 `exec` instead for such commands. 356 357 Only use the `const(char[]) sql` overload when you are not going to be using the same 358 command repeatedly and you are CERTAIN all the data you're sending is properly 359 escaped. Otherwise, consider using overload that takes a `Prepared`. 360 361 Type_Mappings: $(TYPE_MAPPINGS) 362 363 Params: 364 conn = An open `mysql.connection.Connection` to the database. 365 sql = The SQL command to be run. 366 prepared = The prepared statement to be run. 367 args = The variables, taken by reference, to receive the values. 368 +/ 369 void queryRowTuple(T...)(Connection conn, const(char[]) sql, ref T args) 370 { 371 return SC.queryRowTuple(conn, sql, args); 372 } 373 374 ///ditto 375 void queryRowTuple(T...)(Connection conn, ref Prepared prepared, ref T args) 376 { 377 SC.queryRowTuple(conn, prepared.safeForExec, args); 378 } 379 380 ///ditto 381 void queryRowTuple(T...)(Connection conn, ref BackwardCompatPrepared prepared, ref T args) @system 382 { 383 auto p = prepared.prepared; 384 SC.queryRowTuple(conn, p.safeForExec, args); 385 prepared._prepared = p; 386 } 387 388 389 /++ 390 Execute an SQL SELECT command or prepared statement and return a single value: 391 the first column of the first row received. 392 393 If the query did not produce any rows, or the rows it produced have zero columns, 394 this will return `Nullable!Variant()`, ie, null. Test for this with `result.isNull`. 395 396 If the query DID produce a result, but the value actually received is NULL, 397 then `result.isNull` will be FALSE, and `result.get` will produce a Variant 398 which CONTAINS null. Check for this with `result.get.type == typeid(typeof(null))`. 399 400 If the SQL command does not produce a result set (such as INSERT/CREATE/etc), 401 then `mysql.exceptions.MYXNoResultRecieved` will be thrown. Use 402 `exec` instead for such commands. 403 404 If `args` is supplied, the sql string will automatically be used as a prepared 405 statement. Prepared statements are automatically cached by mysql-native, 406 so there's no performance penalty for using this multiple times for the 407 same statement instead of manually preparing a statement. 408 409 If `args` and `prepared` are both provided, `args` will be used, 410 and any arguments that are already set in the prepared statement 411 will automatically be replaced with `args` (note, just like calling 412 `mysql.prepared.Prepared.setArgs`, this will also remove all 413 `mysql.prepared.ParameterSpecialization` that may have been applied). 414 415 Only use the `const(char[]) sql` overload that doesn't take `args` 416 when you are not going to be using the same 417 command repeatedly and you are CERTAIN all the data you're sending is properly 418 escaped. Otherwise, consider using overload that takes a `Prepared`. 419 420 If you need to use any `mysql.prepared.ParameterSpecialization`, use 421 `mysql.connection.prepare` to manually create a `mysql.prepared.Prepared`, 422 and set your parameter specializations using `mysql.prepared.Prepared.setArg` 423 or `mysql.prepared.Prepared.setArgs`. 424 425 Type_Mappings: $(TYPE_MAPPINGS) 426 427 Params: 428 conn = An open `mysql.connection.Connection` to the database. 429 sql = The SQL command to be run. 430 prepared = The prepared statement to be run. 431 csa = Not yet implemented. 432 433 Returns: `Nullable!Variant`: This will be null (check via `Nullable.isNull`) if the 434 query resulted in an empty result set. 435 436 Example: 437 --- 438 auto myInt = 7; 439 Nullable!Variant value = myConnection.queryRow("SELECT * FROM `myTable` WHERE `a` = ?", myInt); 440 --- 441 +/ 442 /+ 443 Future text: 444 If there are long data items among the expected result columns you can use 445 the `csa` param to specify that they are to be subject to chunked transfer via a 446 delegate. 447 448 csa = An optional array of `ColumnSpecialization` structs. If you need to 449 use this with a prepared statement, please use `mysql.prepared.Prepared.columnSpecials`. 450 +/ 451 /+ 452 Future text: 453 If there are long data items among the expected result columns you can use 454 the `csa` param to specify that they are to be subject to chunked transfer via a 455 delegate. 456 457 csa = An optional array of `ColumnSpecialization` structs. If you need to 458 use this with a prepared statement, please use `mysql.prepared.Prepared.columnSpecials`. 459 +/ 460 Nullable!Variant queryValue(Connection conn, const(char[]) sql, ColumnSpecialization[] csa = null) @system 461 { 462 return SC.queryValue(conn, sql, csa).asVariant; 463 } 464 ///ditto 465 Nullable!Variant queryValue(T...)(Connection conn, const(char[]) sql, T args) 466 if(T.length > 0 && !is(T[0] == Variant[]) && !is(T[0] == MySQLVal[]) && !is(T[0] == ColumnSpecialization) && !is(T[0] == ColumnSpecialization[])) 467 { 468 return SC.queryValue(conn, sql, args).asVariant; 469 } 470 ///ditto 471 Nullable!Variant queryValue(Connection conn, const(char[]) sql, Variant[] args) @system 472 { 473 auto prepared = conn.prepare(sql); 474 prepared.setArgs(args); 475 return queryValue(conn, prepared); 476 } 477 ///ditto 478 Nullable!Variant queryValue(Connection conn, ref Prepared prepared) @system 479 { 480 return SC.queryValue(conn, prepared.safeForExec).asVariant; 481 } 482 ///ditto 483 Nullable!Variant queryValue(T...)(Connection conn, ref Prepared prepared, T args) @system 484 if(T.length > 0 && !is(T[0] == Variant[]) && !is(T[0] == MySQLVal[]) && !is(T[0] == ColumnSpecialization) && !is(T[0] == ColumnSpecialization[])) 485 { 486 prepared.setArgs(args); 487 return queryValue(conn, prepared); 488 } 489 ///ditto 490 Nullable!Variant queryValue(Connection conn, ref Prepared prepared, Variant[] args) @system 491 { 492 prepared.setArgs(args); 493 return queryValue(conn, prepared); 494 } 495 ///ditto 496 Nullable!Variant queryValue(Connection conn, ref BackwardCompatPrepared prepared) @system 497 { 498 auto p = prepared.prepared; 499 auto result = queryValue(conn, p); 500 prepared._prepared = p; 501 return result; 502 }