1 ///
2 module dpq.querybuilder;
3 
4 import dpq.value;
5 import dpq.query;
6 import dpq.connection;
7 import dpq.attributes;
8 import dpq.column;
9 
10 import std.typecons;
11 import std..string;
12 
13 import std.algorithm : map, sum;
14 import std.conv : to;
15 
16 version (unittest) import std.stdio;
17 
18 enum Order : string
19 {
20 	none = "",
21 	asc = "ASC",
22 	desc = "DESC"
23 };
24 
25 private enum QueryType
26 {
27 	select = "SELECT",
28 	update = "UPDATE",
29 	insert = "INSERT",
30 	delete_ = "DELETE"
31 }
32 
33 /**
34 	A filter builder struct meant for internal usage.
35 
36 	Simplifies building a combination of AND/OR filters and makes the code more
37 	readable.
38  */
39 private struct FilterBuilder
40 {
41 	private string[][] _filters;
42 
43 	ref FilterBuilder and(string filter)
44 	{
45 		if (_filters.length == 0)
46 			_filters.length++;
47 
48 		_filters[$ - 1] ~= '(' ~ filter ~ ')';
49 
50 		return this;
51 	}
52 
53 	ref FilterBuilder or()
54 	{
55 		_filters ~= [];
56 
57 		return this;
58 	}
59 
60 	/// Returns the actual number of lowest-level filters
61 	long length()
62 	{
63 		return _filters.map!(f => f.length).sum;
64 	}
65 
66 
67 	string toString()
68 	{
69 		// Join inner filters by AND, outer by OR
70 		return _filters.map!(innerFilter =>
71 				innerFilter.join(" AND ")
72 				).join(" OR ");
73 	}
74 }
75 
76 /**
77 	Provides a nice way of writing queries in D, as well as some handy shortcuts
78 	to working with D structures in the DB.
79 
80 	Most method names are synonimous with the same keyword in SQL, but their order
81 	does not matter.
82 	
83 	All of the methods can be chained.
84 
85 	Examples:
86 	---------------------
87 	auto qb = QueryBuilder()
88 			.select("id")
89 			.from!User
90 			.where("posts > {posts}") // placeholders can be used 
91 			.order("posts", Order.desc)
92 			.limit(5);
93 
94 	// Placeholders will be replaced ONLY if they are specified.
95 	// No need to escape anything, as it sent with execParams
96 	qb["posts"] = 50;
97 	---------------------
98  */
99 struct QueryBuilder
100 {
101 	private
102 	{
103 		// Columns to select
104 		string[] _columns;
105 
106 		// Table to select from
107 		string _table;
108 
109 		// A list of filters, lowest-level representing AND, and OR between those
110 		FilterBuilder _filters;
111 
112 		// List of ORDER BY columns and list of orders (ASC/DESC)
113 		string[] _orderBy;
114 		Order[] _orders;
115 
116 		// Limit and offset values, using -1 is null value (not set)
117 		Nullable!(int, -1) _limit = -1;
118 		Nullable!(int, -1) _offset = -1;
119 
120 		// Params to be used in the filters
121 		Value[string] _params;
122 
123 		// Params to be be used as positional
124 		Value[] _indexParams;
125 
126 		// Columns to list in RETURNING
127 		string[] _returning;
128 
129 		// UPDATE's SET
130 		string[] _set;
131 
132 		// Current index for positional params, needed because we allow mixing
133 		// placeholders and positional params
134 		int _paramIndex = 0;
135 
136 		// Type of the query, (SELECT, UPDATE, ...)
137 		QueryType _type;
138 
139 		Connection* _connection;
140 	}
141 
142 	@property QueryBuilder dup()
143 	{
144 		// It's a struct, I'll copy anyway
145 		return this;
146 	}
147 
148 	private string escapeIdentifier(string identifier)
149 	{
150 		if (_connection != null)
151 			return _connection.escapeIdentifier(identifier);
152 
153 		// This could potentionally be dangerous, I don't like it.
154 		return `"%s"`.format(identifier);
155 	}
156 
157 	/**
158 		Constructs a new QueryBuilder with the Connection, so we can directly
159 		run queries with it.
160 	 */
161 	this(ref Connection connection)
162 	{
163 		_connection = &connection;
164 	}
165 
166 	/**
167 		Remembers the given key value pair, replacing the placeholders in the query
168 		before running it.
169 
170 		If the same key is set multiple times, the last value will apply.
171 	 */
172 	void opIndexAssign(T)(T val, string key)
173 	{
174 		_params[key] = val;
175 	}
176 
177 	unittest
178 	{
179 		writeln(" * QueryBuilder");
180 		writeln("\t * opIndexAssign");
181 
182 		QueryBuilder qb;
183 		qb["foo"] = 123;
184 		qb["bar"] = "456";
185 
186 		assert(qb._params["foo"] == Value(123));
187 		assert(qb._params["bar"] == Value("456"));
188 	}
189 
190 	/**
191 		Sets the builder's type to SELECT, a variadic array of column names to select
192 	 */
193 	ref QueryBuilder select(string[] cols...)
194 	{
195 		_columns = cols;
196 		_type = QueryType.select;
197 		return this;
198 	}
199 
200 	/**
201 		Same as above, except it accepts a variadic array of Column type. Mostly
202 		used internally.
203 	 */
204 	ref QueryBuilder select(Column[] cols...)
205 	{
206 		_type = QueryType.select;
207 		_columns = [];
208 		foreach(col; cols)
209 		{
210 			if (col.column != col.asName)
211 				_columns ~= "%s AS %s".format(col.column, col.asName);
212 			else
213 				_columns ~= col.column;
214 		}
215 
216 		return this;
217 	}
218 
219 	/**
220 		Selects all the given relation's properties
221 
222 		Examples:
223 		-----------------
224 		struct User { @PK @serial int id; }
225 		auto qb = QueryBuilder()
226 				.select!User
227 				.from!User
228 				.where( ... );
229 		-----------------
230 
231 	 */
232 	ref QueryBuilder select(T)()
233 	{
234 		return select(AttributeList!T);
235 	}
236 
237 	unittest
238 	{
239 		writeln("\t * select");
240 
241 		QueryBuilder qb;
242 		qb.select("foo", "bar", "baz");
243 		assert(qb._columns == ["foo", "bar", "baz"]);
244 
245 		Column[] cs = [Column("foo", "foo_test"), Column("bar")];
246 		qb.select(cs);
247 		assert(qb._columns == ["foo AS foo_test", "bar"]);
248 	}
249 
250 	/**
251 		Sets the builder's FROM value to the given string.
252 	 */
253 	ref QueryBuilder from(string from)
254 	{
255 		assert(
256 				_type == QueryType.select || _type == QueryType.delete_,
257 				"QueryBuilder.from() can only be used for SELECT or DELETE queries.");
258 
259 		_table = from;
260 		return this;
261 	}
262 
263 	/**
264 		Same as above, but instead of accepting a string parameter, it instead
265 		accepts a type as a template parameter, then sets the value to that
266 		type's relation name. Preferred over the above version.
267 	 */
268 	ref QueryBuilder from(T)()
269 	{
270 		return from(relationName!T);
271 	}
272 
273 	unittest
274 	{
275 		writeln("\t\t * from");
276 		QueryBuilder qb;
277 		
278 		qb.from("sometable");
279 		assert(qb._table == "sometable");
280 
281 		struct Test {}
282 		qb.from!Test;
283 		assert(qb._table == "test");
284 	}
285 
286 	/**
287 		Generates a placeholder that should be unique every time.
288 
289 		This is required because we might filter by the same column twice (e.g. 
290 		where(["id": 1])).or.where(["id": 2]), in which case the second value for
291 		ID would overwrite the first one.
292 	 */
293 	private string safePlaceholder(string key)
294 	{
295 		/*
296 			Because we only really need to be unique within this specific QB, just
297 			a simple static counter is good enough. It could be put on the QB
298 			instance instead, but this works just as well no need to complicate for
299 			now.
300 		 */
301 		static int count = 0;
302 		return "%s_%d".format(key, ++count);
303 	}
304 
305 	/**
306 		Adds new filter(s). Param placeholders are used, with the same names as
307 		the AA keys. Calling this multiple times will AND the filters.
308 
309 		Internally, a value placeholder will be used for each of the values, with
310 		the same name as the column itself. Be careful not to overwrite these
311 		before running the query.
312 	 */
313 	ref QueryBuilder where(T)(T[string] filters)
314 	{
315 		foreach (key, value; filters)
316 		{
317 			auto placeholder = safePlaceholder(key);
318 			_filters.and("%s = {%s}".format(escapeIdentifier(key), placeholder));
319 			_params[placeholder] = value;
320 		}
321 
322 		return this;
323 	}
324 
325 	/**
326 		Adds a new custom filter.
327 
328 		Useful for filters that are not simple equality comparisons, or usage psql
329 		functions. Nothing is escaped, make sure you properly escape the reserved
330 		keywords if they are used as identifier names.
331 
332 		Placeholders can be used with this, and even
333 		positional params, since the order is predictable. Read addParam for
334 		more information about that.
335 	 */
336 	ref QueryBuilder where(T...)(string filter, T params)
337 	{
338 		_filters.and("%s".format(filter));
339 
340 		foreach (param; params)
341 			addParam(param);
342 
343 		return this;
344 	}
345 
346 	/// Alias and to where, to allow stuff like User.where( ... ).and( ... )
347 	alias and = where;
348 
349 	/**
350 		Once called, all additional parameters will be placed into their own group,
351 		OR placed between each group of ANDs
352 
353 		Examples:
354 		--------------------
355 		auto qb = QueryBuilder()
356 			.select!User
357 			.from!User
358 			.where(["id	": 1])
359 			.or
360 			.where(["id": 2]);
361 
362 			// Which will produce a filter like "... WHERE (id = $1) OR (id = $2)"
363 		--------------------
364 	 */
365 	@property ref QueryBuilder or()
366 	{
367 		_filters.or();
368 
369 		return this;
370 	}
371 
372 	unittest
373 	{
374 		writeln("\t\t * where");
375 
376 		auto qb = QueryBuilder();
377 
378 		qb.where(["something": "asd"]);
379 		assert(qb._filters.length == 1);
380 
381 		qb.where(["two": 2, "three": 3]);
382 		assert(qb._filters.length == 3);
383 	}
384 
385 	/**
386 		Sets the ORDER part of the query. Accepts a column name and an Order value.
387 	 */
388 	ref QueryBuilder order(string col, Order order)
389 	{
390 		assert(_type == QueryType.select, "QueryBuilder.order() can only be used for SELECT queries.");
391 		_orderBy ~= col;
392 		_orders ~= order;
393 		return this;
394 	}
395 
396 	unittest
397 	{
398 		writeln("\t\t * order");
399 
400 		QueryBuilder qb;
401 
402 		qb.order("some_col", Order.asc);
403 
404 		assert(qb._orderBy[0] == "some_col");
405 		assert(qb._orders[0] == Order.asc);
406 
407 		qb.order("some_other_col", Order.desc);
408 
409 		assert(qb._orderBy[1] == "some_other_col");
410 		assert(qb._orders[1] == Order.desc);
411 	}
412 	
413 	/**
414 		Sets the LIMIT in the query. Only for SELECT queries, obviously.
415 	 */
416 	ref QueryBuilder limit(int limit)
417 	{
418 		assert(_type == QueryType.select, "QueryBuilder.limit() can only be used for SELECT queries.");
419 
420 		_limit = limit;
421 		return this;
422 	}
423 
424 	unittest
425 	{
426 		writeln("\t\t * limit");
427 
428 		QueryBuilder qb;
429 		qb.limit(1);
430 		assert(qb._limit == 1);
431 	}
432 
433 	/// OFFSET for queries
434 	ref QueryBuilder offset(int offset)
435 	{
436 		assert(_type == QueryType.select, "QueryBuilder.offset() can only be used for SELECT queries.");
437 		_offset = offset;
438 		return this;
439 	}
440 
441 	unittest
442 	{
443 		writeln("\t\t * offset");
444 
445 		QueryBuilder qb;
446 		qb.offset(1);
447 		assert(qb._offset == 1);
448 	}
449 
450 	// UPDATE methods
451 	ref QueryBuilder update(string table)
452 	{
453 		_table = table;
454 		_type = QueryType.update;
455 		return this;
456 	}
457 	
458 	ref QueryBuilder update(T)()
459 	{
460 		return update(relationName!T);
461 	}
462 
463 	unittest
464 	{
465 		QueryBuilder qb;
466 		qb.update("sometable");
467 
468 		assert(qb._table == "sometable");
469 		assert(qb._type == QueryType.update);
470 
471 		struct Test {}
472 
473 		qb.update!Test;
474 		assert(qb._type == QueryType.update);
475 		assert(qb._table == relationName!Test);
476 	}
477 
478 	ref QueryBuilder set(T)(T[string] params)
479 	{
480 		foreach (col, val; params)
481 			set(col, val);
482 
483 		return this;
484 	}
485 
486 	ref QueryBuilder set(T)(string col, T val)
487 	{
488 		assert(_type == QueryType.update, "QueryBuilder.set() can only be used on UPDATE queries");
489 
490 		_params[col] = val;
491 		_set ~= "%s = {%s}".format(escapeIdentifier(col), col);
492 
493 		return this;
494 	}
495 
496 	ref QueryBuilder set(string set)
497 	{
498 		_set ~= set;
499 
500 		return this;
501 	}
502 
503 	unittest
504 	{
505 		writeln("\t * set");
506 
507 		QueryBuilder qb;
508 		qb.update("foo")
509 			.set("some_col", 1);
510 
511 		assert(qb._params["some_col"] == Value(1));
512 		assert(qb._set.length == 1);
513 		assert(qb._set[0] == "\"some_col\" = {some_col}");
514 
515 		qb.set([
516 				"col1": Value(1),
517 				"col2": Value(2)]);
518 
519 		assert(qb._params.length == 3);
520 		assert(qb._set.length == 3);
521 		assert(qb._set[1] == "\"col1\" = {col1}");
522 		assert(qb._set[2] == "\"col2\" = {col2}");
523 
524 		string str = "asd = $1";
525 		qb.set(str);
526 		assert(qb._params.length == 3);
527 		assert(qb._set.length == 4);
528 		assert(qb._set[3] == str);
529 	}
530 	
531 	// INSERT methods
532 	ref QueryBuilder insert(string table, string[] cols...)
533 	{
534 		_table = table;
535 		_columns = cols;
536 		_type = QueryType.insert;
537 		return this;
538 	}
539 
540 
541 	ref QueryBuilder insert(string table, Column[] cols...)
542 	{
543 		import std.array;
544 		return insert(table, array(cols.map!(c => c.column)));
545 	}
546 
547 	unittest
548 	{
549 		writeln("\t * insert");
550 
551 		QueryBuilder qb;
552 		qb.insert("table", "col1", "col2");
553 
554 		assert(qb._type == QueryType.insert);
555 		assert(qb._table == "table");
556 		assert(qb._columns == ["col1", "col2"]);
557 
558 		Column[] cs = [
559 			Column("some_col", "stupid_as_name"),
560 			Column("qwe")];
561 
562 		qb.insert("table2", cs);
563 		assert(qb._table == "table2");
564 		assert(qb._columns.length == 2);
565 		assert(qb._columns == ["some_col", "qwe"]);
566 	}
567 
568 	ref QueryBuilder values(T...)(T vals)
569 	{
570 		assert(_type == QueryType.insert, "QueryBuilder.values() can only be used on INSERT queries");
571 
572 		foreach (val; vals)
573 			addValue(val);
574 
575 		return this;
576 	}
577 
578 	ref QueryBuilder values(Value[] vals)
579 	{
580 		assert(_type == QueryType.insert, "QueryBuilder.values() can only be used on INSERT queries");
581 
582 		foreach (val; vals)
583 			addValue(val);
584 
585 		return this;
586 	}
587 
588 	unittest
589 	{
590 		writeln("\t * values");
591 
592 		QueryBuilder qb;
593 		qb.insert("table", "col")
594 			.values(1, 2, 3);
595 
596 		assert(qb._type == QueryType.insert);
597 		assert(qb._indexParams.length == 3);
598 		assert(qb._indexParams == [Value(1), Value(2), Value(3)]);
599 
600 		qb.values([Value(4), Value(5)]);
601 		assert(qb._indexParams.length == 5);
602 		assert(qb._indexParams == [Value(1), Value(2), Value(3), Value(4), Value(5)]);
603 	}
604 
605 	ref QueryBuilder remove()
606 	{
607 		_type = QueryType.delete_;
608 		return this;
609 	}
610 
611 	ref QueryBuilder remove(string table)
612 	{
613 		from(table);
614 		return remove();
615 	}
616 
617 	ref QueryBuilder remove(T)()
618 	{
619 		return remove(relationName!T);
620 	}
621 
622 	ref QueryBuilder returning(string[] ret...)
623 	{
624 		foreach (r; ret)
625 			_returning ~= r;
626 
627 		return this;
628 	}
629 
630 	unittest
631 	{
632 		writeln("\t * remove");
633 
634 		struct Test {}
635 		QueryBuilder qb;
636 		qb.remove!Test;
637 
638 		assert(qb._type == QueryType.delete_);
639 		assert(qb._table == relationName!Test);
640 	}
641 
642 	ref QueryBuilder addValue(T)(T val)
643 	{
644 		_indexParams ~= Value(val);
645 		return this;
646 	}
647 
648 	ref QueryBuilder addValues(T, U)(U val)
649 	{
650 		import std.traits;
651 		import dpq.meta;
652 		import dpq.serialisation;
653 
654 		if (isAnyNull(val))
655 			addValue(null);
656 		else
657 		{
658 			foreach (m; serialisableMembers!(NoNullable!T))
659 			{
660 				static if (isPK!(T, m) || hasUDA!(mixin("T." ~ m), IgnoreAttribute))
661 					continue;
662 				else
663 					addValue(__traits(getMember, val, m));
664 			}
665 		}
666 
667 		return this;
668 	}
669 
670 	// Other stuff
671 
672 	private string replaceParams(string str)
673 	{
674 		int index = _paramIndex;
675 
676 		foreach (param, val; _params)
677 			str = str.replace("{" ~ param ~ "}", "$%d".format(++index));
678 
679 		return str;
680 	}
681 
682 	unittest
683 	{
684 		writeln("\t * replaceParams");
685 		QueryBuilder qb;
686 		string str = "SELECT {foo} FROM table WHERE id = {bar} AND name = '{baz}'";
687 		qb["foo"] = "a";
688 		qb["bar"] = "b";
689 
690 		str = qb.replaceParams(str);
691 
692 		// No idea what the order might be
693 		assert(
694 				str == "SELECT $1 FROM table WHERE id = $2 AND name = '{baz}'" ||
695 				str == "SELECT $2 FROM table WHERE id = $1 AND name = '{baz}'");
696 	}
697 
698 	private string selectCommand()
699 	{
700 		string cols;
701 		if (_columns.length == 0)
702 			cols = "*";
703 		else
704 			cols = _columns
705 				//.map!(c => escapeIdentifier(c))
706 				.join(", ");
707 
708 		string table = escapeIdentifier(_table);
709 		string str = "SELECT %s FROM %s".format(cols, table);
710 
711 		if (_filters.length > 0)
712 			str ~= " WHERE " ~ _filters.to!string;
713 
714 		if (_orderBy.length > 0)
715 		{
716 			str ~= " ORDER BY ";
717 			for (int i = 0; i < _orderBy.length; ++i)
718 			{
719 				if (_orders[i] == Order.none)
720 					continue;
721 
722 				str ~= "\"" ~ _orderBy[i] ~ "\" " ~ _orders[i] ~ ", ";
723 			}
724 			str = str[0 .. $ - 2];
725 		}
726 
727 		if (!_limit.isNull)
728 			str ~= " LIMIT %d".format(_limit);
729 
730 		if (!_offset.isNull)
731 			str ~= " OFFSET %d".format(_offset);
732 
733 		return replaceParams(str);
734 	}
735 
736 	unittest
737 	{
738 		writeln("\t * selectCommand");
739 
740 		QueryBuilder qb;
741 		qb.select("col")
742 			.from("table")
743 			.where(["id": 1])
744 			.limit(1)
745 			.offset(1);
746 
747 		string str = qb.command();
748 		assert(str == `SELECT col FROM "table" WHERE ("id" = $1) LIMIT 1 OFFSET 1`, str);
749 	}
750 
751 	private string insertCommand()
752 	{
753 		int index = 0;
754 		
755 		string params = "(";
756 		foreach (i, v; _indexParams)
757 		{
758 			params ~= "$%d".format(i + 1);
759 			if ((i + 1) % _columns.length)
760 				params ~= ", ";
761 			else if ( (i + 1) < _indexParams.length)
762 				params ~= "),(";
763 		}
764 		params ~= ")";
765 		
766 		string str = "INSERT INTO \"%s\" (%s) VALUES %s".format(
767 				_table,
768 				_columns.join(","),
769 				params
770 				);
771 
772 		if (_returning.length > 0)
773 		{
774 			str ~= " RETURNING ";
775 			str ~= _returning.join(", ");
776 		}
777 
778 		return str;
779 	}
780 
781 	unittest
782 	{
783 		writeln("\t * insertCommand");
784 
785 		QueryBuilder qb;
786 		qb.insert("table", "col")
787 			.values(1, 2)
788 			.returning("id");
789 
790 		string str = qb.command();
791 		assert(str == `INSERT INTO "table" (col) VALUES ($1),($2) RETURNING id`);
792 	}
793 
794 	private string updateCommand()
795 	{
796 		string str = "UPDATE \"%s\" SET %s".format(
797 				_table,
798 				_set.join(", "));
799 
800 		if (_filters.length > 0)
801 			str ~= " WHERE " ~ _filters.to!string;
802 
803 		if (_returning.length > 0)
804 		{
805 			str ~= " RETURNING ";
806 			str ~= _returning.join(", ");
807 		}
808 
809 		return replaceParams(str);
810 	}
811 
812 	unittest
813 	{
814 		writeln("\t * updateCommand");
815 
816 		QueryBuilder qb;
817 		qb.update("table")
818 			.set("col", 1)
819 			.where(["foo": 2])
820 			.returning("id");
821 
822 		string str = qb.command();
823 		assert(
824 				str == `UPDATE "table" SET "col" = $1 WHERE ("foo" = $2) RETURNING id` ||
825 				str == `UPDATE "table" SET "col" = $2 WHERE ("foo" = $1) RETURNING id`);
826 	}
827 
828 	private string deleteCommand()
829 	{
830 		string str = "DELETE FROM \"%s\"".format(_table);
831 
832 		if (_filters.length > 0)
833 			str ~= " WHERE " ~ _filters.to!string;
834 
835 		if (_returning.length > 0)
836 		{
837 			str ~= " RETURNING ";
838 			str ~= _returning.join(", ");
839 		}
840 
841 		return replaceParams(str);
842 	}
843 
844 	unittest
845 	{
846 		writeln("\t * deleteCommand");
847 
848 		QueryBuilder qb;
849 		qb.remove("table")
850 			.where(["id": 1])
851 			.returning("id");
852 
853 		string str = qb.command();
854 		assert(str == `DELETE FROM "table" WHERE ("id" = $1) RETURNING id`, str);
855 	}
856 
857 	@property string command()
858 	{
859 		final switch (_type)
860 		{
861 			case QueryType.select:
862 				return selectCommand();
863 			case QueryType.update:
864 				return updateCommand();
865 			case QueryType.insert:
866 				return insertCommand();
867 			case QueryType.delete_:
868 				return deleteCommand();
869 		}
870 	}
871 
872 	@property private Value[] paramsArr()
873 	{
874 		Value[] res = _indexParams;
875 		//foreach (param; _indexParams)
876 		//	res ~= param;
877 
878 		foreach (param, val; _params)
879 			res ~= val;
880 
881 		return res;
882 	}
883 
884 	unittest
885 	{
886 		writeln("\t * paramsArr");
887 
888 		QueryBuilder qb;
889 		qb.addParams("1", "2", "3");
890 		qb["foo"] = 1;
891 		qb["bar"] = 2;
892 
893 		auto ps = qb.paramsArr();
894 		assert(
895 				ps == [Value("1"), Value("2"), Value("3"), Value(1), Value(2)] ||
896 				ps == [Value("1"), Value("2"), Value("3"), Value(2), Value(1)]);
897 	}
898 
899 	void addParam(T)(T val)
900 	{
901 		_indexParams ~= Value(val);
902 		++_paramIndex;
903 	}
904 
905 	unittest
906 	{
907 		writeln("\t * addParam");
908 
909 		QueryBuilder qb;
910 
911 		assert(qb._paramIndex == 0);
912 
913 		qb.addParam(1);
914 		assert(qb._paramIndex == 1);
915 		assert(qb._indexParams.length == 1);
916 		assert(qb._indexParams[0] == Value(1));
917 
918 		qb.addParam(2);
919 		assert(qb._paramIndex == 2);
920 		assert(qb._indexParams.length == 2);
921 		assert(qb._indexParams[1] == Value(2));
922 	}
923 
924 	ref QueryBuilder addParams(T...)(T vals)
925 	{
926 		foreach (val; vals)
927 			addParam(val);
928 
929 		return this;
930 	}
931 	
932 	unittest
933 	{
934 		writeln("\t * addParams");
935 
936 		QueryBuilder qb;
937 		qb.addParams(1, 2, 3);
938 
939 		assert(qb._indexParams.length == 3);
940 		assert(qb._paramIndex == 3);
941 	}
942 
943 	ref QueryBuilder opBinary(string op, T)(T val)
944 			if (op == "<<")
945 	{
946 		return addParam(val);
947 	}
948 
949 
950 	Query query()
951 	{
952 		if (_connection != null)
953 			return Query(*_connection, command, paramsArr);
954 
955 		return Query(command, paramsArr);
956 	}
957 
958 	Query query(ref Connection conn)
959 	{
960 		return Query(conn, command, paramsArr);
961 	}
962 }