1 /++
2 Use a DB via plain SQL statements.
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 
11 module mysql.commands;
12 
13 import std.conv;
14 import std.exception;
15 import std.range;
16 import std.typecons;
17 import std.variant;
18 
19 import mysql.connection;
20 import mysql.exceptions;
21 import mysql.prepared;
22 import mysql.protocol.comms;
23 import mysql.protocol.constants;
24 import mysql.protocol.extra_types;
25 import mysql.protocol.packets;
26 import mysql.result;
27 
28 /// This feature is not yet implemented. It currently has no effect.
29 /+
30 A struct to represent specializations of returned statement columns.
31 
32 If you are executing a query that will include result columns that are large objects,
33 it may be expedient to deal with the data as it is received rather than first buffering
34 it to some sort of byte array. These two variables allow for this. If both are provided
35 then the corresponding column will be fed to the stipulated delegate in chunks of
36 `chunkSize`, with the possible exception of the last chunk, which may be smaller.
37 The bool argument `finished` will be set to true when the last chunk is set.
38 
39 Be aware when specifying types for column specializations that for some reason the
40 field descriptions returned for a resultset have all of the types TINYTEXT, MEDIUMTEXT,
41 TEXT, LONGTEXT, TINYBLOB, MEDIUMBLOB, BLOB, and LONGBLOB lumped as type 0xfc
42 contrary to what it says in the protocol documentation.
43 +/
44 struct ColumnSpecialization
45 {
46 	size_t  cIndex;    // parameter number 0 - number of params-1
47 	ushort  type;
48 	uint    chunkSize; /// In bytes
49 	void delegate(const(ubyte)[] chunk, bool finished) chunkDelegate;
50 }
51 ///ditto
52 alias CSN = ColumnSpecialization;
53 
54 /++
55 Execute an SQL command or prepared statement, such as INSERT/UPDATE/CREATE/etc.
56 
57 This method is intended for commands such as which do not produce a result set
58 (otherwise, use one of the `query` functions instead.) If the SQL command does
59 produces a result set (such as SELECT), `mysql.exceptions.MYXResultRecieved`
60 will be thrown.
61 
62 If `args` is supplied, the sql string will automatically be used as a prepared
63 statement. Prepared statements are automatically cached by mysql-native,
64 so there's no performance penalty for using this multiple times for the
65 same statement instead of manually preparing a statement.
66 
67 If `args` and `prepared` are both provided, `args` will be used,
68 and any arguments that are already set in the prepared statement
69 will automatically be replaced with `args` (note, just like calling
70 `mysql.prepared.Prepared.setArgs`, this will also remove all
71 `mysql.prepared.ParameterSpecialization` that may have been applied).
72 
73 Only use the `const(char[]) sql` overload that doesn't take `args`
74 when you are not going to be using the same
75 command repeatedly and you are CERTAIN all the data you're sending is properly
76 escaped. Otherwise, consider using overload that takes a `Prepared`.
77 
78 If you need to use any `mysql.prepared.ParameterSpecialization`, use
79 `mysql.connection.prepare` to manually create a `mysql.prepared.Prepared`,
80 and set your parameter specializations using `mysql.prepared.Prepared.setArg`
81 or `mysql.prepared.Prepared.setArgs`.
82 
83 Type_Mappings: $(TYPE_MAPPINGS)
84 
85 Params:
86 conn = An open `mysql.connection.Connection` to the database.
87 sql = The SQL command to be run.
88 prepared = The prepared statement to be run.
89 
90 Returns: The number of rows affected.
91 
92 Example:
93 ---
94 auto myInt = 7;
95 auto rowsAffected = myConnection.exec("INSERT INTO `myTable` (`a`) VALUES (?)", myInt);
96 ---
97 +/
98 ulong exec(Connection conn, const(char[]) sql)
99 {
100 	return execImpl(conn, ExecQueryImplInfo(false, sql));
101 }
102 ///ditto
103 ulong exec(T...)(Connection conn, const(char[]) sql, T args)
104 	if(T.length > 0 && !is(T[0] == Variant[]))
105 {
106 	auto prepared = conn.prepare(sql);
107 	prepared.setArgs(args);
108 	return exec(conn, prepared);
109 }
110 ///ditto
111 ulong exec(Connection conn, const(char[]) sql, Variant[] args)
112 {
113 	auto prepared = conn.prepare(sql);
114 	prepared.setArgs(args);
115 	return exec(conn, prepared);
116 }
117 
118 ///ditto
119 ulong exec(Connection conn, ref Prepared prepared)
120 {
121 	auto preparedInfo = conn.registerIfNeeded(prepared.sql);
122 	auto ra = execImpl(conn, prepared.getExecQueryImplInfo(preparedInfo.statementId));
123 	prepared._lastInsertID = conn.lastInsertID;
124 	return ra;
125 }
126 ///ditto
127 ulong exec(T...)(Connection conn, ref Prepared prepared, T args)
128 	if(T.length > 0 && !is(T[0] == Variant[]))
129 {
130 	prepared.setArgs(args);
131 	return exec(conn, prepared);
132 }
133 ///ditto
134 ulong exec(Connection conn, ref Prepared prepared, Variant[] args)
135 {
136 	prepared.setArgs(args);
137 	return exec(conn, prepared);
138 }
139 
140 ///ditto
141 ulong exec(Connection conn, ref BackwardCompatPrepared prepared)
142 {
143 	auto p = prepared.prepared;
144 	auto result = exec(conn, p);
145 	prepared._prepared = p;
146 	return result;
147 }
148 
149 /// Common implementation for `exec` overloads
150 package ulong execImpl(Connection conn, ExecQueryImplInfo info)
151 {
152 	ulong rowsAffected;
153 	bool receivedResultSet = execQueryImpl(conn, info, rowsAffected);
154 	if(receivedResultSet)
155 	{
156 		conn.purgeResult();
157 		throw new MYXResultRecieved();
158 	}
159 
160 	return rowsAffected;
161 }
162 
163 /++
164 Execute an SQL SELECT command or prepared statement.
165 
166 This returns an input range of `mysql.result.Row`, so if you need random access
167 to the `mysql.result.Row` elements, simply call
168 $(LINK2 https://dlang.org/phobos/std_array.html#array, `std.array.array()`)
169 on the result.
170 
171 If the SQL command does not produce a result set (such as INSERT/CREATE/etc),
172 then `mysql.exceptions.MYXNoResultRecieved` will be thrown. Use
173 `exec` instead for such commands.
174 
175 If `args` is supplied, the sql string will automatically be used as a prepared
176 statement. Prepared statements are automatically cached by mysql-native,
177 so there's no performance penalty for using this multiple times for the
178 same statement instead of manually preparing a statement.
179 
180 If `args` and `prepared` are both provided, `args` will be used,
181 and any arguments that are already set in the prepared statement
182 will automatically be replaced with `args` (note, just like calling
183 `mysql.prepared.Prepared.setArgs`, this will also remove all
184 `mysql.prepared.ParameterSpecialization` that may have been applied).
185 
186 Only use the `const(char[]) sql` overload that doesn't take `args`
187 when you are not going to be using the same
188 command repeatedly and you are CERTAIN all the data you're sending is properly
189 escaped. Otherwise, consider using overload that takes a `Prepared`.
190 
191 If you need to use any `mysql.prepared.ParameterSpecialization`, use
192 `mysql.connection.prepare` to manually create a `mysql.prepared.Prepared`,
193 and set your parameter specializations using `mysql.prepared.Prepared.setArg`
194 or `mysql.prepared.Prepared.setArgs`.
195 
196 Type_Mappings: $(TYPE_MAPPINGS)
197 
198 Params:
199 conn = An open `mysql.connection.Connection` to the database.
200 sql = The SQL command to be run.
201 prepared = The prepared statement to be run.
202 csa = Not yet implemented.
203 
204 Returns: A (possibly empty) `mysql.result.ResultRange`.
205 
206 Example:
207 ---
208 ResultRange oneAtATime = myConnection.query("SELECT * from `myTable`");
209 Row[]       allAtOnce  = myConnection.query("SELECT * from `myTable`").array;
210 
211 auto myInt = 7;
212 ResultRange rows = myConnection.query("SELECT * FROM `myTable` WHERE `a` = ?", myInt);
213 ---
214 +/
215 /+
216 Future text:
217 If there are long data items among the expected result columns you can use
218 the `csa` param to specify that they are to be subject to chunked transfer via a
219 delegate.
220 
221 csa = An optional array of `ColumnSpecialization` structs. If you need to
222 use this with a prepared statement, please use `mysql.prepared.Prepared.columnSpecials`.
223 +/
224 ResultRange query(Connection conn, const(char[]) sql, ColumnSpecialization[] csa = null)
225 {
226 	return queryImpl(csa, conn, ExecQueryImplInfo(false, sql));
227 }
228 ///ditto
229 ResultRange query(T...)(Connection conn, const(char[]) sql, T args)
230 	if(T.length > 0 && !is(T[0] == Variant[]) && !is(T[0] == ColumnSpecialization) && !is(T[0] == ColumnSpecialization[]))
231 {
232 	auto prepared = conn.prepare(sql);
233 	prepared.setArgs(args);
234 	return query(conn, prepared);
235 }
236 ///ditto
237 ResultRange query(Connection conn, const(char[]) sql, Variant[] args)
238 {
239 	auto prepared = conn.prepare(sql);
240 	prepared.setArgs(args);
241 	return query(conn, prepared);
242 }
243 
244 ///ditto
245 ResultRange query(Connection conn, ref Prepared prepared)
246 {
247 	auto preparedInfo = conn.registerIfNeeded(prepared.sql);
248 	auto result = queryImpl(prepared.columnSpecials, conn, prepared.getExecQueryImplInfo(preparedInfo.statementId));
249 	prepared._lastInsertID = conn.lastInsertID; // Conceivably, this might be needed when multi-statements are enabled.
250 	return result;
251 }
252 ///ditto
253 ResultRange query(T...)(Connection conn, ref Prepared prepared, T args)
254 	if(T.length > 0 && !is(T[0] == Variant[]) && !is(T[0] == ColumnSpecialization) && !is(T[0] == ColumnSpecialization[]))
255 {
256 	prepared.setArgs(args);
257 	return query(conn, prepared);
258 }
259 ///ditto
260 ResultRange query(Connection conn, ref Prepared prepared, Variant[] args)
261 {
262 	prepared.setArgs(args);
263 	return query(conn, prepared);
264 }
265 
266 ///ditto
267 ResultRange query(Connection conn, ref BackwardCompatPrepared prepared)
268 {
269 	auto p = prepared.prepared;
270 	auto result = query(conn, p);
271 	prepared._prepared = p;
272 	return result;
273 }
274 
275 /// Common implementation for `query` overloads
276 package ResultRange queryImpl(ColumnSpecialization[] csa,
277 	Connection conn, ExecQueryImplInfo info)
278 {
279 	ulong ra;
280 	enforce!MYXNoResultRecieved(execQueryImpl(conn, info, ra));
281 
282 	conn._rsh = ResultSetHeaders(conn, conn._fieldCount);
283 	if(csa !is null)
284 		conn._rsh.addSpecializations(csa);
285 
286 	conn._headersPending = false;
287 	return ResultRange(conn, conn._rsh, conn._rsh.fieldNames);
288 }
289 
290 /++
291 Execute an SQL SELECT command or prepared statement where you only want the
292 first `mysql.result.Row`, if any.
293 
294 If the SQL command does not produce a result set (such as INSERT/CREATE/etc),
295 then `mysql.exceptions.MYXNoResultRecieved` will be thrown. Use
296 `exec` instead for such commands.
297 
298 If `args` is supplied, the sql string will automatically be used as a prepared
299 statement. Prepared statements are automatically cached by mysql-native,
300 so there's no performance penalty for using this multiple times for the
301 same statement instead of manually preparing a statement.
302 
303 If `args` and `prepared` are both provided, `args` will be used,
304 and any arguments that are already set in the prepared statement
305 will automatically be replaced with `args` (note, just like calling
306 `mysql.prepared.Prepared.setArgs`, this will also remove all
307 `mysql.prepared.ParameterSpecialization` that may have been applied).
308 
309 Only use the `const(char[]) sql` overload that doesn't take `args`
310 when you are not going to be using the same
311 command repeatedly and you are CERTAIN all the data you're sending is properly
312 escaped. Otherwise, consider using overload that takes a `Prepared`.
313 
314 If you need to use any `mysql.prepared.ParameterSpecialization`, use
315 `mysql.connection.prepare` to manually create a `mysql.prepared.Prepared`,
316 and set your parameter specializations using `mysql.prepared.Prepared.setArg`
317 or `mysql.prepared.Prepared.setArgs`.
318 
319 Type_Mappings: $(TYPE_MAPPINGS)
320 
321 Params:
322 conn = An open `mysql.connection.Connection` to the database.
323 sql = The SQL command to be run.
324 prepared = The prepared statement to be run.
325 csa = Not yet implemented.
326 
327 Returns: `Nullable!(mysql.result.Row)`: This will be null (check via `Nullable.isNull`) if the
328 query resulted in an empty result set.
329 
330 Example:
331 ---
332 auto myInt = 7;
333 Nullable!Row row = myConnection.queryRow("SELECT * FROM `myTable` WHERE `a` = ?", myInt);
334 ---
335 +/
336 /+
337 Future text:
338 If there are long data items among the expected result columns you can use
339 the `csa` param to specify that they are to be subject to chunked transfer via a
340 delegate.
341 
342 csa = An optional array of `ColumnSpecialization` structs. If you need to
343 use this with a prepared statement, please use `mysql.prepared.Prepared.columnSpecials`.
344 +/
345 /+
346 Future text:
347 If there are long data items among the expected result columns you can use
348 the `csa` param to specify that they are to be subject to chunked transfer via a
349 delegate.
350 
351 csa = An optional array of `ColumnSpecialization` structs. If you need to
352 use this with a prepared statement, please use `mysql.prepared.Prepared.columnSpecials`.
353 +/
354 Nullable!Row queryRow(Connection conn, const(char[]) sql, ColumnSpecialization[] csa = null)
355 {
356 	return queryRowImpl(csa, conn, ExecQueryImplInfo(false, sql));
357 }
358 ///ditto
359 Nullable!Row queryRow(T...)(Connection conn, const(char[]) sql, T args)
360 	if(T.length > 0 && !is(T[0] == Variant[]) && !is(T[0] == ColumnSpecialization) && !is(T[0] == ColumnSpecialization[]))
361 {
362 	auto prepared = conn.prepare(sql);
363 	prepared.setArgs(args);
364 	return queryRow(conn, prepared);
365 }
366 ///ditto
367 Nullable!Row queryRow(Connection conn, const(char[]) sql, Variant[] args)
368 {
369 	auto prepared = conn.prepare(sql);
370 	prepared.setArgs(args);
371 	return queryRow(conn, prepared);
372 }
373 
374 ///ditto
375 Nullable!Row queryRow(Connection conn, ref Prepared prepared)
376 {
377 	auto preparedInfo = conn.registerIfNeeded(prepared.sql);
378 	auto result = queryRowImpl(prepared.columnSpecials, conn, prepared.getExecQueryImplInfo(preparedInfo.statementId));
379 	prepared._lastInsertID = conn.lastInsertID; // Conceivably, this might be needed when multi-statements are enabled.
380 	return result;
381 }
382 ///ditto
383 Nullable!Row queryRow(T...)(Connection conn, ref Prepared prepared, T args)
384 	if(T.length > 0 && !is(T[0] == Variant[]) && !is(T[0] == ColumnSpecialization) && !is(T[0] == ColumnSpecialization[]))
385 {
386 	prepared.setArgs(args);
387 	return queryRow(conn, prepared);
388 }
389 ///ditto
390 Nullable!Row queryRow(Connection conn, ref Prepared prepared, Variant[] args)
391 {
392 	prepared.setArgs(args);
393 	return queryRow(conn, prepared);
394 }
395 
396 ///ditto
397 Nullable!Row queryRow(Connection conn, ref BackwardCompatPrepared prepared)
398 {
399 	auto p = prepared.prepared;
400 	auto result = queryRow(conn, p);
401 	prepared._prepared = p;
402 	return result;
403 }
404 
405 /// Common implementation for `querySet` overloads.
406 package Nullable!Row queryRowImpl(ColumnSpecialization[] csa, Connection conn,
407 	ExecQueryImplInfo info)
408 {
409 	auto results = queryImpl(csa, conn, info);
410 	if(results.empty)
411 		return Nullable!Row();
412 	else
413 	{
414 		auto row = results.front;
415 		results.close();
416 		return Nullable!Row(row);
417 	}
418 }
419 
420 /++
421 Execute an SQL SELECT command or prepared statement where you only want the
422 first `mysql.result.Row`, and place result values into a set of D variables.
423 
424 This method will throw if any column type is incompatible with the corresponding D variable.
425 
426 Unlike the other query functions, queryRowTuple will throw
427 `mysql.exceptions.MYX` if the result set is empty
428 (and thus the reference variables passed in cannot be filled).
429 
430 If the SQL command does not produce a result set (such as INSERT/CREATE/etc),
431 then `mysql.exceptions.MYXNoResultRecieved` will be thrown. Use
432 `exec` instead for such commands.
433 
434 Only use the `const(char[]) sql` overload when you are not going to be using the same
435 command repeatedly and you are CERTAIN all the data you're sending is properly
436 escaped. Otherwise, consider using overload that takes a `Prepared`.
437 
438 Type_Mappings: $(TYPE_MAPPINGS)
439 
440 Params:
441 conn = An open `mysql.connection.Connection` to the database.
442 sql = The SQL command to be run.
443 prepared = The prepared statement to be run.
444 args = The variables, taken by reference, to receive the values.
445 +/
446 void queryRowTuple(T...)(Connection conn, const(char[]) sql, ref T args)
447 {
448 	return queryRowTupleImpl(conn, ExecQueryImplInfo(false, sql), args);
449 }
450 
451 ///ditto
452 void queryRowTuple(T...)(Connection conn, ref Prepared prepared, ref T args)
453 {
454 	auto preparedInfo = conn.registerIfNeeded(prepared.sql);
455 	queryRowTupleImpl(conn, prepared.getExecQueryImplInfo(preparedInfo.statementId), args);
456 	prepared._lastInsertID = conn.lastInsertID; // Conceivably, this might be needed when multi-statements are enabled.
457 }
458 
459 ///ditto
460 void queryRowTuple(T...)(Connection conn, ref BackwardCompatPrepared prepared, ref T args)
461 {
462 	auto p = prepared.prepared;
463 	queryRowTuple(conn, p, args);
464 	prepared._prepared = p;
465 }
466 
467 /// Common implementation for `queryRowTuple` overloads.
468 package void queryRowTupleImpl(T...)(Connection conn, ExecQueryImplInfo info, ref T args)
469 {
470 	ulong ra;
471 	enforce!MYXNoResultRecieved(execQueryImpl(conn, info, ra));
472 
473 	Row rr = conn.getNextRow();
474 	/+if (!rr._valid)   // The result set was empty - not a crime.
475 		return;+/
476 	enforce!MYX(rr._values.length == args.length, "Result column count does not match the target tuple.");
477 	foreach (size_t i, dummy; args)
478 	{
479 		enforce!MYX(typeid(args[i]).toString() == rr._values[i].type.toString(),
480 			"Tuple "~to!string(i)~" type and column type are not compatible.");
481 		args[i] = rr._values[i].get!(typeof(args[i]));
482 	}
483 	// If there were more rows, flush them away
484 	// Question: Should I check in purgeResult and throw if there were - it's very inefficient to
485 	// allow sloppy SQL that does not ensure just one row!
486 	conn.purgeResult();
487 }
488 
489 /++
490 Execute an SQL SELECT command or prepared statement and return a single value:
491 the first column of the first row received.
492 
493 If the query did not produce any rows, or the rows it produced have zero columns,
494 this will return `Nullable!Variant()`, ie, null. Test for this with `result.isNull`.
495 
496 If the query DID produce a result, but the value actually received is NULL,
497 then `result.isNull` will be FALSE, and `result.get` will produce a Variant
498 which CONTAINS null. Check for this with `result.get.type == typeid(typeof(null))`.
499 
500 If the SQL command does not produce a result set (such as INSERT/CREATE/etc),
501 then `mysql.exceptions.MYXNoResultRecieved` will be thrown. Use
502 `exec` instead for such commands.
503 
504 If `args` is supplied, the sql string will automatically be used as a prepared
505 statement. Prepared statements are automatically cached by mysql-native,
506 so there's no performance penalty for using this multiple times for the
507 same statement instead of manually preparing a statement.
508 
509 If `args` and `prepared` are both provided, `args` will be used,
510 and any arguments that are already set in the prepared statement
511 will automatically be replaced with `args` (note, just like calling
512 `mysql.prepared.Prepared.setArgs`, this will also remove all
513 `mysql.prepared.ParameterSpecialization` that may have been applied).
514 
515 Only use the `const(char[]) sql` overload that doesn't take `args`
516 when you are not going to be using the same
517 command repeatedly and you are CERTAIN all the data you're sending is properly
518 escaped. Otherwise, consider using overload that takes a `Prepared`.
519 
520 If you need to use any `mysql.prepared.ParameterSpecialization`, use
521 `mysql.connection.prepare` to manually create a `mysql.prepared.Prepared`,
522 and set your parameter specializations using `mysql.prepared.Prepared.setArg`
523 or `mysql.prepared.Prepared.setArgs`.
524 
525 Type_Mappings: $(TYPE_MAPPINGS)
526 
527 Params:
528 conn = An open `mysql.connection.Connection` to the database.
529 sql = The SQL command to be run.
530 prepared = The prepared statement to be run.
531 csa = Not yet implemented.
532 
533 Returns: `Nullable!Variant`: This will be null (check via `Nullable.isNull`) if the
534 query resulted in an empty result set.
535 
536 Example:
537 ---
538 auto myInt = 7;
539 Nullable!Variant value = myConnection.queryRow("SELECT * FROM `myTable` WHERE `a` = ?", myInt);
540 ---
541 +/
542 /+
543 Future text:
544 If there are long data items among the expected result columns you can use
545 the `csa` param to specify that they are to be subject to chunked transfer via a
546 delegate.
547 
548 csa = An optional array of `ColumnSpecialization` structs. If you need to
549 use this with a prepared statement, please use `mysql.prepared.Prepared.columnSpecials`.
550 +/
551 /+
552 Future text:
553 If there are long data items among the expected result columns you can use
554 the `csa` param to specify that they are to be subject to chunked transfer via a
555 delegate.
556 
557 csa = An optional array of `ColumnSpecialization` structs. If you need to
558 use this with a prepared statement, please use `mysql.prepared.Prepared.columnSpecials`.
559 +/
560 Nullable!Variant queryValue(Connection conn, const(char[]) sql, ColumnSpecialization[] csa = null)
561 {
562 	return queryValueImpl(csa, conn, ExecQueryImplInfo(false, sql));
563 }
564 ///ditto
565 Nullable!Variant queryValue(T...)(Connection conn, const(char[]) sql, T args)
566 	if(T.length > 0 && !is(T[0] == Variant[]) && !is(T[0] == ColumnSpecialization) && !is(T[0] == ColumnSpecialization[]))
567 {
568 	auto prepared = conn.prepare(sql);
569 	prepared.setArgs(args);
570 	return queryValue(conn, prepared);
571 }
572 ///ditto
573 Nullable!Variant queryValue(Connection conn, const(char[]) sql, Variant[] args)
574 {
575 	auto prepared = conn.prepare(sql);
576 	prepared.setArgs(args);
577 	return queryValue(conn, prepared);
578 }
579 
580 ///ditto
581 Nullable!Variant queryValue(Connection conn, ref Prepared prepared)
582 {
583 	auto preparedInfo = conn.registerIfNeeded(prepared.sql);
584 	auto result = queryValueImpl(prepared.columnSpecials, conn, prepared.getExecQueryImplInfo(preparedInfo.statementId));
585 	prepared._lastInsertID = conn.lastInsertID; // Conceivably, this might be needed when multi-statements are enabled.
586 	return result;
587 }
588 ///ditto
589 Nullable!Variant queryValue(T...)(Connection conn, ref Prepared prepared, T args)
590 	if(T.length > 0 && !is(T[0] == Variant[]) && !is(T[0] == ColumnSpecialization) && !is(T[0] == ColumnSpecialization[]))
591 {
592 	prepared.setArgs(args);
593 	return queryValue(conn, prepared);
594 }
595 ///ditto
596 Nullable!Variant queryValue(Connection conn, ref Prepared prepared, Variant[] args)
597 {
598 	prepared.setArgs(args);
599 	return queryValue(conn, prepared);
600 }
601 
602 ///ditto
603 Nullable!Variant queryValue(Connection conn, ref BackwardCompatPrepared prepared)
604 {
605 	auto p = prepared.prepared;
606 	auto result = queryValue(conn, p);
607 	prepared._prepared = p;
608 	return result;
609 }
610 
611 /// Common implementation for `queryValue` overloads.
612 package Nullable!Variant queryValueImpl(ColumnSpecialization[] csa, Connection conn,
613 	ExecQueryImplInfo info)
614 {
615 	auto results = queryImpl(csa, conn, info);
616 	if(results.empty)
617 		return Nullable!Variant();
618 	else
619 	{
620 		auto row = results.front;
621 		results.close();
622 		
623 		if(row.length == 0)
624 			return Nullable!Variant();
625 		else
626 			return Nullable!Variant(row[0]);
627 	}
628 }
629