1 module dpq.connection;
2 
3 //import derelict.pq.pq;
4 import libpq.libpq;
5 
6 import dpq.exception;
7 import dpq.result;
8 import dpq.value;
9 import dpq.attributes;
10 import dpq.querybuilder;
11 import dpq.meta;
12 import dpq.prepared;
13 import dpq.smartptr;
14 
15 import std.string;
16 //import derelict.pq.pq;
17 import libpq.libpq;
18 import std.conv : to;
19 import std.traits;
20 import std.typecons;
21 
22 
23 version(unittest)
24 {
25 	import std.stdio;
26 	Connection c;
27 }
28 
29 /**
30 	Represents the PostgreSQL connection and allows executing queries on it.
31 
32 	Examples:
33 	-------------
34 	auto conn = Connection("host=localhost dbname=testdb user=testuser");
35 	//conn.exec ...
36 	-------------
37 */
38 struct Connection
39 {
40 	alias ConnectionPtr = SmartPointer!(PGconn*, PQfinish);
41 
42 	private ConnectionPtr _connection;
43 	private PreparedStatement[string] _prepared;
44 
45 	/**
46 		Connection constructor
47 
48 		Params:
49 			connString = connection string
50 
51 		See Also:
52 			http://www.postgresql.org/docs/9.3/static/libpq-connect.html#LIBPQ-CONNSTRING
53 	*/
54 	this(string connString)
55 	{
56 		char* err;
57 		auto opts = PQconninfoParse(cast(char*)connString.toStringz, &err);
58 
59 		if (err != null)
60 			throw new DPQException(err.fromStringz.to!string);
61 
62 		_connection = new ConnectionPtr(PQconnectdb(connString.toStringz));
63 
64 		if (status != CONNECTION_OK)
65 			throw new DPQException(errorMessage);
66 
67 		_dpqLastConnection = &this;
68 	}
69 
70 	unittest
71 	{
72 		c = Connection("host=127.0.0.1 dbname=test user=test");
73 		writeln(" * Database connection with connection string");
74 		assert(c.status == CONNECTION_OK);
75 	}
76 
77 	/** 
78 		Close the connection manually 
79 	*/
80 	void close()
81 	{
82 		_connection.clear();
83 	}
84 
85 	@property const(ConnStatusType) status()
86 	{
87 		return PQstatus(_connection);
88 	}
89 
90 	/** Returns the name of the database currently selected */
91 	@property const(string) db()
92 	{
93 		return PQdb(_connection).to!string;
94 	}
95 
96 	/** Returns the name of the current user */
97 	@property const(string) user()
98 	{
99 		return PQuser(_connection).to!string;
100 	}
101 
102 	/// ditto, but password
103 	@property const(string) password()
104 	{
105 		return PQpass(_connection).to!string;
106 	}
107 
108 	/// ditto, but host
109 	@property const(string) host()
110 	{
111 		return PQhost(_connection).to!string;
112 	}
113 
114 	/// ditto, but port
115 	@property const(ushort) port()
116 	{
117 		return PQport(_connection).fromStringz.to!ushort;
118 	}
119 
120 	/** // FIXME: BROKEN ATM
121 		Executes the given string directly
122 
123 		Throws on fatal query errors like bad syntax
124 
125 		Examples:
126 		----------------
127 		Connection conn; // An established connection
128 
129 		conn.exec("CREATE TABLE IF NOT EXISTS test_table");
130 		----------------
131 	*/
132 	Result exec(string command)
133 	{
134 		PGresult* res = PQexec(_connection, cast(const char*)command.toStringz);
135 		return Result(res);
136 	}
137 
138 	/*
139 	unittest
140 	{
141 		auto res = c.exec("SELECT 1::INT4 AS int4, 2::INT8 AS some_long");
142 		writeln(" * exec for selecting INT4 and INT8");
143 		assert(res.rows == 1);
144 		assert(res.columns == 2);
145 
146 		auto r = res[0];
147 		assert(r[0].as!int == 1);
148 		assert(r[0].as!long == 2);
149 
150 		writeln(" * Row opIndex(int) and opIndex(string) equality ");
151 		assert(r[0] == r["int4"]);
152 		assert(r[1] == r["some_long"]);
153 
154 	}
155 	*/
156 
157 	/// ditto, async
158 	bool send(string command)
159 	{
160 		return PQsendQuery(_connection, cast(const char*)command.toStringz) == 1;
161 	}
162 
163 	/**
164 		Executes the given string with given params
165 
166 		Params should be given as $1, $2, ... $n in the actual command.
167 		All params are sent in a binary format and should not be escaped.
168 		If a param's type cannot be inferred, this method will throw an exception,
169 		in this case, either specify the type using the :: (cast) notation or
170 		make sure the type can be inferred by PostgreSQL in your query.
171 
172 		Examples:
173 		----------------
174 		Connection conn; // An established connection
175 
176 		conn.execParams("SELECT $1::string, $2::int, $3::double");
177 		----------------
178 
179 		See also:
180 			http://www.postgresql.org/docs/9.3/static/libpq-exec.html
181 	*/
182 	Result execParams(T...)(string command, T params)
183 	{
184 		Value[] values;
185 		foreach(param; params)
186 			values ~= Value(param);
187 
188 		return execParams(command, values);
189 	}
190 
191 
192 	void sendParams(T...)(string command, T params)
193 	{
194 		Value[] values;
195 		foreach(param; params)
196 			values ~= Value(param);
197 
198 		execParams(command, values, true);
199 	}
200 
201 	unittest
202 	{
203 		auto res = c.execParams("SELECT 1::INT4 AS int4, 2::INT8 AS some_long", []);
204 		writeln("\t * execParams");
205 		writeln("\t\t * Rows and cols");
206 
207 		assert(res.rows == 1);
208 		assert(res.columns == 2);
209 
210 		writeln("\t\t * Static values");
211 		auto r = res[0];
212 		assert(r[0].as!int == 1);
213 		assert(r[1].as!long == 2);
214 
215 		writeln("\t\t * opIndex(int) and opIndex(string) equality");
216 		assert(r[0] == r["int4"]);
217 		assert(r[1] == r["some_long"]);
218 
219 		int int4 = 1;
220 		long int8 = 2;
221 		string str = "foo bar baz";
222 		float float4 = 3.14;
223 		double float8 = 3.1415;
224 
225 		writeln("\t\t * Passed values");
226 		res = c.execParams(
227 				"SELECT $1::INT4, $2::INT8, $3::TEXT, $4::FLOAT4, $5::FLOAT8",
228 				int4,
229 				int8,
230 				str,
231 				float4,
232 				float8);
233 
234 		assert(res.rows == 1);
235 		r = res[0];
236 
237 		assert(r[0].as!int == int4);
238 		assert(r[1].as!long == int8);
239 		assert(r[2].as!string == str);
240 		assert(r[3].as!float == float4);
241 		assert(r[4].as!double == float8);
242 	}
243 
244 	/// ditto, but taking an array of params, instead of variadic template
245 	Result execParams(string command, Value[] params, bool async = false)
246 	{
247 		const char* cStr = cast(const char*) command.toStringz;
248 
249 		auto pTypes = params.paramTypes;
250 		auto pValues = params.paramValues;
251 		auto pLengths = params.paramLengths;
252 		auto pFormats = params.paramFormats;
253 
254 		if (async)
255 		{
256 			PQsendQueryParams(
257 				_connection,
258 				cStr,
259 				params.length.to!int,
260 				pTypes.ptr,
261 				cast(const(char*)*)pValues.ptr,
262 				pLengths.ptr,
263 				pFormats.ptr,
264 				1);
265 
266 			return Result(null);
267 		}
268 		else
269 			return Result(PQexecParams(
270 					_connection, 
271 					cStr, 
272 					params.length.to!int,
273 					pTypes.ptr, 
274 					cast(const(char*)*)pValues.ptr,
275 					pLengths.ptr,
276 					pFormats.ptr,
277 					1));
278 	}
279 
280 	/// ditto, async
281 	void sendParams(string command, Value[] params)
282 	{
283 		execParams(command, params, true);
284 	}
285 
286 	/**
287 		Returns the last error message
288 
289 		Examples:
290 		--------------------
291 		Connection conn; // An established connection
292 
293 		writeln(conn.errorMessage);
294 		--------------------
295 		
296 	 */
297 	@property string errorMessage()
298 	{
299 		return PQerrorMessage(_connection).to!string;
300 	}
301 
302 	unittest
303 	{
304 		writeln("\t * errorMessage");
305 		try
306 		{
307 			c.execParams("SELECT_BADSYNTAX $1::INT4", 1);
308 		}
309 		catch {}
310 
311 		assert(c.errorMessage.length != 0);
312 
313 	}
314 
315 	/**
316 		Generates and runs the DDL from the given structures
317 
318 		Attributes from dpq.attributes should be used to define
319 		primary keys, indexes, and relationships.
320 
321 		A custom type can be specified with the @type attribute.
322 
323 		Examples:
324 		-----------------------
325 		Connection conn; // An established connection
326 		struct User 
327 		{
328 			@serial8 @PKey long id;
329 			string username;
330 			byte[] passwordHash;
331 		};
332 
333 		struct Article { ... };
334 
335 		conn.ensureSchema!(User, Article);
336 		-----------------------
337 	*/
338 	void ensureSchema(T...)(bool createType = false)
339 	{
340 		import std.stdio;
341 		string[] additional;
342 		string[] relations;
343 
344 		foreach (type; T)
345 		{
346 			enum name = relationName!(type);
347 			relations ~= name;
348 
349 			string str;
350 			if (createType)
351 				str = "CREATE TYPE \"" ~ name ~ "\" AS (%s)";
352 			else
353 				str = "CREATE TABLE IF NOT EXISTS \"" ~ name ~ "\" (%s)";
354 
355 			string cols;
356 			foreach(m; serialisableMembers!type)
357 			{
358 				string colName = attributeName!(mixin("type." ~ m));
359 				cols ~= "\"" ~ colName ~ "\"";
360 
361 				// HACK: typeof a @property seems to be failing hard
362 				static if (is(FunctionTypeOf!(mixin("type." ~ m)) == function))
363 					alias t = typeof(mixin("type()." ~ m));
364 				else
365 					alias t = typeof(mixin("type." ~ m));
366 
367 				cols ~= " ";
368 
369 				// Basic data types
370 				static if (hasUDA!(mixin("type." ~ m), PGTypeAttribute))
371 					cols ~= getUDAs!(mixin("type." ~ m), PGTypeAttribute)[0].type;
372 				else
373 				{
374 					alias tu = Unqual!t;
375 					static if (ShouldRecurse!(mixin("type." ~ m)))
376 					{
377 						ensureSchema!(NoNullable!tu)(true);
378 						cols ~= '"' ~ relationName!(NoNullable!tu) ~ '"';
379 					}
380 					else
381 						cols ~= SQLType!tu;
382 				}
383 				
384 				// Primary key
385 				static if (hasUDA!(mixin("type." ~ m), PrimaryKeyAttribute))
386 				{
387 					if (!createType)
388 						cols ~= " PRIMARY KEY";
389 				}
390 				// Index
391 				else static if (hasUDA!(mixin("type." ~ m), IndexAttribute))
392 				{
393 					enum uda = getUDAs!(mixin("type." ~ m), IndexAttribute)[0];
394 					additional ~= "CREATE%sINDEX \"%s\" ON \"%s\" (\"%s\")".format(
395 							uda.unique ? " UNIQUE " : " ",
396 							"%s_%s_index".format(name, colName),
397 							name,
398 							colName);
399 
400 					// DEBUG
401 				}
402 				// Foreign key
403 				else static if (hasUDA!(mixin("type." ~ m), ForeignKeyAttribute))
404 				{
405 					enum uda = getUDAs!(mixin("type." ~ m), ForeignKeyAttribute)[0];
406 					additional ~= 
407 						"ALTER TABLE \"%s\" ADD CONSTRAINT \"%s\" FOREIGN KEY (\"%s\") REFERENCES \"%s\" (\"%s\")".format(
408 								name,
409 								"%s_%s_fk_%s".format(name, colName, uda.relation),
410 								colName,
411 								uda.relation,
412 								uda.pkey);
413 
414 					// Create an index on the FK too
415 					additional ~= "CREATE INDEX \"%s\" ON \"%s\" (\"%s\")".format(
416 							"%s_%s_fk_index".format(name, colName),
417 							name,
418 							colName);
419 
420 				}
421 
422 				cols ~= ", ";
423 			}
424 
425 			cols = cols[0 .. $ - 2];
426 			str = str.format(cols);
427 			if (createType)
428 			{
429 				try
430 				{
431 					exec(str);
432 				}
433 				catch {} // Do nothing, type already exists
434 			}
435 			else
436 				exec(str);
437 		}
438 		foreach (cmd; additional)
439 		{
440 			try
441 			{
442 				exec(cmd);
443 			}
444 			catch {} // Horrible, I know, but this just means the constraint/index already exists
445 		}
446 
447 		// After we're done creating all the types, a good idea would be to get OIDs for all of them
448 		if (relations.length > 0)
449 		{
450 			auto res = execParams(
451 					"SELECT typname, oid FROM pg_type WHERE typname IN ('%s')".format(
452 						relations.join("','")));
453 
454 			foreach (r; res)
455 				_dpqCustomOIDs[r[0].as!string] = r[1].as!Oid;
456 		}
457 
458 		
459 	}
460 
461 	unittest
462 	{
463 		// Probably needs more thorough testing, let's assume right now
464 		// everything is correct if the creating was successful.
465 
466 		writeln("\t * ensureSchema");
467 		struct Inner
468 		{
469 			string innerStr;
470 			int innerInt;
471 		}
472 
473 		struct TestTable1
474 		{
475 			@serial8 @PK long id;
476 			string str;
477 			int n;
478 			@embed Inner inner;
479 		}
480 
481 		c.ensureSchema!TestTable1;
482 		
483 		auto res = c.execParams(
484 				"SELECT COUNT(*) FROM pg_catalog.pg_tables WHERE tablename = $1",
485 				relationName!TestTable1);
486 
487 		assert(res.rows == 1);
488 		assert(res[0][0].as!long == 1);
489 
490 		c.exec("DROP TABLE " ~ relationName!TestTable1);
491 		c.exec("DROP TYPE \"" ~ relationName!Inner ~ "\" CASCADE");
492 	}
493 
494 	/**
495 		Returns the requested structure or a Nullable null value if no rows are returned
496 
497 		This method queries for the given structure by its primary key. If no
498 		primary key can be found, a compile-time error will be generated.
499 
500 		Examples:
501 		----------------------
502 		Connection conn; // An established connection
503 		struct User
504 		{
505 			@serial @PKey int id;
506 			...
507 		};
508 
509 		auto user = conn.findOne!User(1); // will search by the id attribute
510 		----------------------
511 	*/
512 	Nullable!T findOne(T, U)(U id)
513 	{
514 		return findOneBy!T(primaryKeyAttributeName!T, id);
515 	}
516 
517 	unittest
518 	{
519 		writeln("\t * findOne(T)(U id), findOneBy, findOne");
520 		struct Testy
521 		{
522 			@serial @PK int id;
523 			string foo;
524 			int bar;
525 			long baz;
526 			int[] intArr;
527 			//string[] stringArr; // TODO: string[]
528 		}
529 
530 		c.ensureSchema!Testy;
531 
532 		writeln("\t\t * Null result");
533 		auto shouldBeNull = c.findOne!Testy(0);
534 		assert(shouldBeNull.isNull);
535 
536 		c.exec("INSERT INTO " ~ relationName!Testy ~ " (id, foo, bar, baz, " ~ attributeName!(Testy.intArr) ~ ") "~
537 				"VALUES (1, 'somestr', 2, 3, '{1,2,3}')");
538 
539 		writeln("\t\t * Valid result");
540 		Testy t = c.findOne!Testy(1);
541 		assert(t.id == 1, `t.id == 1` );
542 		assert(t.foo == "somestr", `t.foo == "somestr"`);
543 		assert(t.bar == 2, `t.bar == 2`);
544 		assert(t.baz == 3, `t.baz == 3`);
545 		assert(t.intArr == [1,2,3], `t.intArr == [1,2,3]`);
546 		//assert(t.stringArr == ["asd", "qwe"]);
547 
548 		writeln("\t\t * findOne with custom filter");
549 		Testy t2 = c.findOne!Testy("id = $1", 1);
550 		assert(t == t2);
551 
552 		c.exec("DROP TABLE " ~ relationName!Testy);
553 	}
554 
555 	/**
556 		Returns the requestes structure, searches by the given column name
557 		with the given value
558 		If not rows are returned, a Nullable null value is returned
559 
560 		Examples:
561 		----------------------
562 		Connection conn; // An established connection
563 		struct User
564 		{
565 			@serial @PKey int id;
566 			...
567 		};
568 
569 		auto user = conn.findOneBy!User("id", 1); // will search by "id"
570 		----------------------
571 	*/
572 	Nullable!T findOneBy(T, U)(string col, U val)
573 	{
574 		import std.stdio;
575 
576 		auto members = AttributeList!T;
577 
578 		QueryBuilder qb;
579 		qb.select(members)
580 			.from(relationName!T)
581 			.where( col ~ " = {col_" ~ col ~ "}")
582 			.limit(1);
583 
584 		qb["col_" ~ col] = val;
585 
586 		auto q = qb.query(this);
587 
588 		auto r = q.run();
589 		if (r.rows == 0)
590 			return Nullable!T.init;
591 
592 		//return T();
593 		
594 		auto res = deserialise!T(r[0]);
595 		return Nullable!T(res);
596 	}
597 	
598 	/**
599 		Returns the requested structure, searches by the specified filter
600 		with given params
601 
602 		The filter is not further escaped, so programmer needs to make sure
603 		not to properly escape or enclose reserved keywords (like user -> "user")
604 		so PostgreSQL can understand them.
605 
606 		If not rows are returned, a Nullable null value is returned
607 
608 		Examples:
609 		----------------------
610 		Connection conn; // An established connection
611 		struct User
612 		{
613 			@serial @PKey int id;
614 			string username;
615 			int posts;
616 		};
617 
618 		auto user = conn.findOne!User("username = $1 OR posts > $2", "foo", 42);
619 		if (!user.isNull)
620 		{
621 			... // do something
622 		}
623 		----------------------
624 	*/
625 	Nullable!T findOne(T, U...)(string filter, U vals)
626 	{
627 		QueryBuilder qb;
628 		qb.select(AttributeList!T)
629 			.from(relationName!T)
630 			.where(filter)
631 			.limit(1);
632 
633 		auto q = qb.query(this);
634 		auto r = q.run(vals);
635 
636 		if (r.rows == 0)
637 			return Nullable!T.init;
638 
639 		auto res = deserialise!T(r[0]);
640 		return Nullable!T(res);
641 	}
642 
643 	/**
644 		Returns an array of the specified type, filtered with the given filter and
645 		params
646 
647 		If no rows are returned by PostgreSQL, an empty array is returned.
648 
649 		Examples:
650 		----------------------
651 		Connection conn; // An established connection
652 		struct User
653 		{
654 			@serial @PKey int id;
655 			string username;
656 			int posts;
657 		};
658 
659 		auto users = conn.find!User("username = $1 OR posts > $2", "foo", 42);
660 		foreach (u; users)
661 		{
662 			... // do something
663 		}
664 		----------------------
665 	*/
666 	T[] find(T, U...)(string filter = "", U vals = U.init)
667 	{
668 		QueryBuilder qb;
669 		qb.select(AttributeList!T)
670 			.from(relationName!T)
671 			.where(filter);
672 
673 		auto q = qb.query(this);
674 
675 		T[] res;
676 		foreach (r; q.run(vals))
677 			res ~= deserialise!T(r);
678 
679 		return res;
680 	}
681 
682 	unittest
683 	{
684 		writeln("\t * find");
685 
686 		@relation("find_test")
687 		struct Test
688 		{
689 			@serial @PK int id;
690 			@attr("my_n") int n;
691 		}
692 
693 		c.ensureSchema!Test;
694 
695 		Test t;
696 		t.n = 1;
697 
698 		c.insert(t);
699 		c.insert(t);
700 		++t.n;
701 		c.insert(t);
702 		c.insert(t);
703 		c.insert(t);
704 
705 		Test[] ts = c.find!Test("my_n = $1", 1);
706 		assert(ts.length == 2);
707 		ts = c.find!Test("my_n > 0");
708 		assert(ts.length == 5);
709 		ts = c.find!Test("false");
710 		assert(ts.length == 0);
711 
712 		c.exec("DROP TABLE find_test");
713 	}
714 
715 	int update(T, U...)(string filter, string update, U vals)
716 	{
717 		QueryBuilder qb;
718 		qb.update(relationName!T)
719 			.set(update)
720 			.where(filter);
721 
722 		auto r = qb.query(this).run(vals);
723 		return r.rows;
724 	}
725 
726 	unittest
727 	{
728 		writeln("\t * update");
729 
730 		@relation("update_test")
731 		struct Test
732 		{
733 			@serial @PK int id;
734 			int n;
735 		}
736 
737 		c.ensureSchema!Test;
738 
739 		Test t;
740 		t.n = 5;
741 		c.insert(t);
742 
743 		int nUpdates = c.update!Test("n = $1", "n = $2", 5, 123);
744 		assert(nUpdates == 1, `nUpdates == 1`);
745 
746 		t = c.findOneBy!Test("n", 123);
747 		assert(t.n == 123, `t.n == 123`);
748 
749 		writeln("\t\t * async");
750 		c.updateAsync!Test("n = $1", "n = $2", 123, 6);
751 		auto r = c.nextResult();
752 
753 		assert(r.rows == 1);
754 		assert(!c.findOneBy!Test("n", 6).isNull);
755 
756 		c.exec("DROP TABLE update_test");
757 	}
758 
759 	void updateAsync(T, U...)(string filter, string update, U vals)
760 	{
761 		QueryBuilder qb;
762 		qb.update(relationName!T)
763 			.set(update)
764 			.where(filter);
765 
766 		qb.query(this).runAsync(vals);
767 	}
768 
769 	int update(T, U)(U id, Value[string] updates, bool async = false)
770 	{
771 		QueryBuilder qb;
772 
773 		qb.update(relationName!T)
774 			.set(updates)
775 			.where(primaryKeyAttributeName!T, id);
776 
777 		auto q = qb.query(this);
778 
779 		if (async)
780 		{
781 			q.runAsync();
782 			return -1;
783 		}
784 
785 		auto r = q.run();
786 		return r.rows;
787 	}
788 
789 	unittest
790 	{
791 		writeln("\t * update with AA updates");
792 
793 		@relation("update_aa_test")
794 		struct Test
795 		{
796 			@serial @PK int id;
797 			int n;
798 		}
799 		c.ensureSchema!Test;
800 
801 		Test t;
802 		t.n = 1;
803 		c.insert(t);
804 
805 		int rows = c.update!Test(1, ["n": Value(2)]);
806 		assert(rows == 1, `r.rows == 1`);
807 
808 		c.exec("DROP TABLE update_aa_test");
809 	}
810 
811 	void updateAsync(T, U)(U id, Value[string] updates)
812 	{
813 		update!T(id, updates, true);
814 	}
815 
816 	int update(T, U)(U id, T updates, bool async = false)
817 	{
818 		import dpq.attributes;
819 
820 		QueryBuilder qb;
821 
822 		qb.update(relationName!T)
823 			.where(primaryKeyAttributeName!T, id);
824 
825 		foreach (m; serialisableMembers!T)
826 			qb.set(attributeName!(mixin("T." ~ m)), __traits(getMember, updates, m));
827 
828 		auto q = qb.query(this);
829 		if (async)
830 		{
831 			qb.query(this).runAsync();
832 			return -1;
833 		}
834 
835 		auto r = q.run();
836 		return r.rows;
837 	}
838 
839 	unittest
840 	{
841 		writeln("\t * update with object");
842 		
843 		@relation("update_object_test")
844 		struct Test
845 		{
846 			@serial @PK int id;
847 			int n;
848 		}
849 		c.ensureSchema!Test;
850 
851 		Test t;
852 		t.n = 1;
853 		t.id = 1; // assumptions <3
854 
855 		c.insert(t);
856 
857 		t.n = 2;
858 		c.update!Test(1, t);
859 		
860 		t = c.findOne!Test(1);
861 		assert(t.n == 2);
862 
863 		t.n = 3;
864 		c.updateAsync!Test(1, t);
865 		auto r = c.nextResult();
866 
867 		writeln("\t\t * async");
868 		assert(r.rows == 1);
869 
870 		c.exec("DROP TABLE update_object_test");
871 	}
872 
873 	void updateAsync(T, U)(U id, T updates)
874 	{
875 		update!T(id, updates, true);
876 	}
877 
878 
879 	private void addVals(T, U)(ref QueryBuilder qb, U val)
880 	{
881 		if (isAnyNull(val))
882 			qb.addValue(null);
883 		else
884 		{
885 			foreach (m; serialisableMembers!(NoNullable!T))
886 			{
887 				static if (isPK!(T, m) || hasUDA!(mixin("T." ~ m), IgnoreAttribute))
888 					continue;
889 				else
890 					qb.addValue(__traits(getMember, val, m));
891 			}
892 		}
893 	}
894 
895 	Result insertR(T)(T val, string ret = "")
896 	{
897 		QueryBuilder qb;
898 		qb.insert(relationName!T, AttributeList!(T, true, true));
899 		if (ret.length > 0)
900 			qb.returning(ret);
901 
902 		addVals!T(qb, val);
903 
904 		return qb.query(this).run();
905 	}
906 
907 	bool insert(T)(T val, bool async = false)
908 	{
909 		QueryBuilder qb;
910 		qb.insert(relationName!T, AttributeList!(T, true, true));
911 
912 		addVals!T(qb, val);
913 
914 		if (async)
915 			return qb.query(this).runAsync();
916 
917 		auto r = qb.query(this).run();
918 		return r.rows > 0;
919 	}
920 
921 	unittest
922 	{
923 		writeln("\t * insert");
924 
925 		@relation("insert_test_inner")
926 		struct Inner
927 		{
928 			int bar;
929 		}
930 
931 		@relation("insert_test")
932 		struct Test
933 		{
934 			int n;
935 			Nullable!int n2;
936 			@embed Inner foo;
937 		}
938 		c.ensureSchema!Test;
939 
940 		Test t;
941 		t.n = 1;
942 		t.n2 = 2;
943 		t.foo.bar = 2;
944 		
945 		auto r = c.insert(t);
946 		assert(r == true);
947 
948 		auto r2 = c.insertR(t, "n");
949 		assert(r2.rows == 1);
950 		assert(r2[0][0].as!int == t.n);
951 
952 		Test t2 = c.findOneBy!Test("n", 1);
953 		assert(t2 == t, t.to!string ~ " != " ~ t2.to!string);
954 
955 		writeln("\t\t * async");
956 		t.n = 123;
957 		t.n2.nullify;
958 		c.insertAsync(t);
959 		
960 		auto res = c.nextResult();
961 		assert(res.rows == 1);
962 		t2 = c.findOneBy!Test("n", 123);
963 		assert(t.n2.isNull);
964 		assert(t2.n2.isNull);
965 
966 		c.exec("DROP TABLE insert_test");
967 		c.exec("DROP TYPE \"%s\" CASCADE".format(relationName!Inner));
968 	}
969 
970 	void insertAsync(T)(T val)
971 	{
972 		insert(val, true);
973 	}
974 
975 	int remove(T, U)(U id)
976 	{
977 		QueryBuilder qb;
978 		qb.remove!T
979 			.where(primaryKeyAttributeName!T, id);
980 
981 		return qb.query(this).run().rows;
982 	}
983 
984 	bool removeAsync(T, U)(U id)
985 	{
986 		QueryBuilder qb;
987 		qb.remove!T
988 			.where(primaryKeyAttributeName!T, id);
989 
990 		return qb.query(this).runAsync() == 1;
991 	}
992 
993 
994 	int remove(T, U...)(string filter, U vals)
995 	{
996 		QueryBuilder qb;
997 		qb.remove!T
998 			.where(filter);
999 
1000 		foreach (v; vals)
1001 			qb.addValue(v);
1002 
1003 		return qb.query(this).run().rows;
1004 	}
1005 
1006 	bool removeAsync(T, U...)(string filter, U vals)
1007 	{
1008 		QueryBuilder qb;
1009 		qb.remove!T
1010 			.where(filter);
1011 
1012 		foreach (v; vals)
1013 			qb.addValue(v);
1014 
1015 		return qb.query(this).runAsync() == 1;
1016 	}
1017 
1018 	unittest
1019 	{
1020 		@relation("remove_test")
1021 		struct Test
1022 		{
1023 			@serial @PK int id;
1024 			int n;
1025 		}
1026 		c.ensureSchema!Test;
1027 
1028 		foreach (i; 0 .. 10)
1029 			c.insert(Test(0, i));
1030 
1031 		writeln("\t * remove(id)");
1032 		int n = c.remove!Test(1);
1033 		assert(n == 1, `n == 1`);
1034 
1035 
1036 		writeln("\t\t * async");
1037 		c.removeAsync!Test(2);
1038 		auto r = c.nextResult();
1039 		assert(r.rows == 1, `r.rows == 1`);
1040 
1041 		writeln("\t * remove(filter, vals...)");
1042 		n = c.remove!Test("id IN($1,$2,$3,$4,$5)", 3, 4, 5, 6, 7);
1043 		assert(n == 5);
1044 
1045 		writeln("\t\t * async");
1046 		c.removeAsync!Test("id >= $1", 7);
1047 		r = c.nextResult();
1048 		assert(r.rows == 3);
1049 
1050 		c.exec("DROP TABLE remove_test");
1051 	}
1052 
1053 	long count(T, U...)(string filter = "", U vals = U.init)
1054 	{
1055 		import dpq.query;
1056 		auto q = Query(this);
1057 		string str = `SELECT COUNT(*) FROM "%s"`.format(relationName!T);
1058 
1059 		if (filter.length > 0)
1060 			str ~= " WHERE " ~ filter;
1061 
1062 		q = str;
1063 		auto r = q.run(vals);
1064 
1065 		return r[0][0].as!long;
1066 	}
1067 
1068 	unittest
1069 	{
1070 		writeln("\t * count");
1071 
1072 		@relation("test_count")
1073 		struct Test
1074 		{
1075 			@serial @PK int id;
1076 			int n;
1077 		}
1078 
1079 		c.ensureSchema!Test;
1080 
1081 		Test t;
1082 		c.insert(t);
1083 
1084 		assert(c.count!Test == 1, `count == 1`);
1085 		c.insert(t);
1086 		assert(c.count!Test == 2, `count == 2`);
1087 
1088 		c.exec("DROP TABLE test_count");
1089 	}
1090 
1091 	bool isBusy()
1092 	{
1093 		return PQisBusy(_connection) == 1;
1094 	}
1095 
1096 	unittest
1097 	{
1098 		writeln("\t * isBusy");
1099 
1100 		assert(c.isBusy() == false);
1101 
1102 		c.send("SELECT 1::INT");
1103 
1104 		assert(c.isBusy() == true);
1105 
1106 		c.nextResult();
1107 		assert(c.isBusy() == false);
1108 	}
1109 
1110 
1111 	/**
1112 		 Blocks until a result is read, then returns it
1113 
1114 		 If no more results remain, a null result will be returned
1115 
1116 		 Make sure to call this until a null is returned.
1117 	*/
1118 	Result nextResult()
1119 	{
1120 		import core.thread;
1121 
1122 		/*
1123 		do
1124 		{
1125 			PQconsumeInput(_connection);
1126 			//Thread.sleep(dur!"msecs"(1)); // What's a reasonable amount of time to wait?
1127 		}
1128 		while (isBusy());
1129 		*/
1130 
1131 		PGresult* res = PQgetResult(_connection);
1132 		return Result(res);
1133 	}
1134 
1135 	Result[] allResults()
1136 	{
1137 		Result[] res;
1138 		
1139 		PGresult* r;
1140 		while ((r = PQgetResult(_connection)) != null)
1141 			res ~= Result(r);
1142 
1143 		return res;
1144 	}
1145 	
1146 	Result lastResult()
1147 	{
1148 		Result res;
1149 
1150 		PGresult* r;
1151 		while ((r = PQgetResult(_connection)) != null)
1152 			res = Result(r);
1153 
1154 		return res;
1155 	}
1156 
1157 	unittest
1158 	{
1159 		writeln("\t * nextResult");
1160 		auto x = c.nextResult();
1161 		assert(x.isNull);
1162 
1163 		int int1 = 1;
1164 		int int2 = 2;
1165 
1166 		c.sendParams("SELECT $1", int1);
1167 
1168 		// In every way the same as lastResult
1169 		Result r, t;
1170 		while(!(t = c.nextResult()).isNull)
1171 			r = t;
1172 
1173 		assert(r.rows == 1);
1174 		assert(r.columns == 1);
1175 		assert(r[0][0].as!int == int1);
1176 
1177 		writeln("\t * lastResult");
1178 		c.sendParams("SELECT $1", int2);
1179 		r = c.lastResult();
1180 
1181 		assert(r.rows == 1);
1182 		assert(r.columns == 1);
1183 		assert(r[0][0].as!int == int2);
1184 	}
1185 
1186 	Result prepare(T...)(string name, string command, T paramTypes)
1187 	{
1188 		Oid[] oids;
1189 		foreach (pType; paramTypes)
1190 			oids ~= pType;
1191 
1192 		char* cName = cast(char*) name.toStringz;
1193 		char* cComm = cast(char*) command.toStringz;
1194 
1195 		auto p = PreparedStatement(this, name, command, oids);
1196 		_prepared[name] = p;
1197 
1198 		return Result(PQprepare(
1199 					_connection,
1200 					cName,
1201 					cComm,
1202 					oids.length.to!int,
1203 					oids.ptr));
1204 	}
1205 
1206 	Result execPrepared(string name, Value[] params...)
1207 	{
1208 		char* cStr = cast(char*) name.toStringz;
1209 
1210 		return Result(PQexecPrepared(
1211 					_connection,
1212 					cStr,
1213 					params.length.to!int,
1214 					cast(char**) params.paramValues.ptr,
1215 					params.paramLengths.ptr,
1216 					params.paramFormats.ptr,
1217 					1));
1218 	}
1219 
1220 	Result execPrepared(T...)(string name, T params)
1221 	{
1222 		Value[] vals;
1223 		foreach (p; params)
1224 			vals ~= Value(p);
1225 
1226 		return execPrepared(name, vals);
1227 	}
1228 
1229 	bool sendPrepared(string name, Value[] params...)
1230 	{
1231 		char* cStr = cast(char*) name.toStringz;
1232 
1233 		return PQsendQueryPrepared(
1234 				_connection,
1235 				cStr,
1236 				params.length.to!int,
1237 				cast(char**) params.paramValues.ptr,
1238 				params.paramLengths.ptr,
1239 				params.paramFormats.ptr,
1240 				1) == 1;
1241 	}
1242 
1243 	bool sendPrepared(T...)(string name, T params)
1244 	{
1245 		Value[] vals;
1246 		foreach (p; params)
1247 			vals ~= Value(p);
1248 
1249 		return sendPrepared(name, vals);
1250 	}
1251 
1252 	unittest
1253 	{
1254 		writeln("\t * prepare");
1255 		// The result of this isn't really all that useful, but as long as it 
1256 		// throws on errors, it kinda is
1257 		c.prepare("prepare_test", "SELECT $1", Type.INT4);
1258 
1259 		writeln("\t * execPrepared");
1260 		auto r = c.execPrepared("prepare_test", 1);
1261 		assert(r.rows == 1);
1262 		assert(r[0][0].as!int == 1);
1263 
1264 		writeln("\t\t * sendPrepared");
1265 		bool s = c.sendPrepared("prepare_test", 1);
1266 		assert(s);
1267 
1268 		r = c.lastResult();
1269 		assert(r.rows == 1);
1270 		assert(r[0][0].as!int == 1);
1271 	}
1272 	
1273 	ref PreparedStatement prepared(string name)
1274 	{
1275 		return _prepared[name];
1276 	}
1277 
1278 	ref PreparedStatement opIndex(string name)
1279 	{
1280 		return prepared(name);
1281 	}
1282 }
1283 
1284 
1285 /**
1286 	Deserialises the given Row to the requested type
1287 
1288 	Params:
1289 		T  = (template) type to deserialise into
1290 		r  = Row to deserialise
1291 */
1292 T deserialise(T)(Row r, string prefix = "")
1293 {
1294 	T res;
1295 	foreach (m; serialisableMembers!T)
1296 	{
1297 		enum n = attributeName!(mixin("T." ~ m));
1298 		alias mType = Unqual!(typeof(mixin("T." ~ m)));
1299 
1300 		try
1301 		{
1302 			auto x = r[prefix ~ n].as!mType;
1303 			if (!x.isNull)
1304 				__traits(getMember, res, m) = cast(TypedefType!(mType)) x;
1305 		}
1306 		catch (DPQException e) 
1307 		{
1308 			if (!isInstanceOf!(Nullable, mType))
1309 				throw e;
1310 		}
1311 	}
1312 	return res;
1313 }
1314 
1315 /// Hold the last created connection, not to be used outside the library
1316 package Connection* _dpqLastConnection;
1317 
1318 /// Holds a list of all the OIDs of our custom types, indexed by their relationName
1319 package Oid[string] _dpqCustomOIDs;
1320