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 }