1 ///
2 module dpq.connection;
3 
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 import dpq.serialisation;
15 
16 import dpq.serialisers.array;
17 import dpq.serialisers.composite;
18 
19 import std.string;
20 import libpq.libpq;
21 import std.conv : to;
22 import std.traits;
23 import std.typecons;
24 
25 version (unittest)
26 {
27    import std.stdio;
28 
29    Connection c;
30 }
31 
32 /**
33    Represents the PostgreSQL connection and allows executing queries on it.
34 
35    Examples:
36    -------------
37    auto conn = Connection("host=localhost dbname=testdb user=testuser");
38    //conn.exec ...
39    -------------
40 */
41 struct Connection
42 {
43    private alias ConnectionPtr = SmartPointer!(PGconn*, PQfinish);
44 
45    private ConnectionPtr _connection;
46    private PreparedStatement[string] _prepared;
47 
48    /**
49       Connection constructor
50 
51       Params:
52          connString = connection string
53 
54       See Also:
55          http://www.postgresql.org/docs/9.3/static/libpq-connect.html#LIBPQ-CONNSTRING
56    */
57    this(string connString)
58    {
59       char* err;
60       auto opts = PQconninfoParse(cast(char*)connString.toStringz, &err);
61 
62       if (err != null)
63          throw new DPQException(err.fromStringz.to!string);
64 
65       _connection = new ConnectionPtr(PQconnectdb(connString.toStringz));
66 
67       if (status != CONNECTION_OK)
68          throw new DPQException(errorMessage);
69 
70       _dpqLastConnection = &this;
71    }
72 
73    unittest
74    {
75       writeln(" * Try to connect host=127.0.0.1 dbname=test user=test");
76       c = Connection("host=127.0.0.1 dbname=test user=test");
77       writeln(" * Database connection with connection string");
78       assert(c.status == CONNECTION_OK);
79    }
80 
81    /**
82       Close the connection manually
83    */
84    void close()
85    {
86       _connection.clear();
87    }
88 
89    @property const(ConnStatusType) status()
90    {
91       return PQstatus(_connection);
92    }
93 
94    /** Returns the name of the database currently selected */
95    @property const(string) db()
96    {
97       return PQdb(_connection).to!string;
98    }
99 
100    /** Returns the name of the current user */
101    @property const(string) user()
102    {
103       return PQuser(_connection).to!string;
104    }
105 
106    /// ditto, but password
107    @property const(string) password()
108    {
109       return PQpass(_connection).to!string;
110    }
111 
112    /// ditto, but host
113    @property const(string) host()
114    {
115       return PQhost(_connection).to!string;
116    }
117 
118    /// ditto, but port
119    @property const(ushort) port()
120    {
121       return PQport(_connection).fromStringz.to!ushort;
122    }
123 
124    /**
125       Executes the given string directly
126 
127       Throws on fatal query errors like bad syntax.
128       WARNING: Only returns textual values!!!
129 
130       Examples:
131       ----------------
132       Connection conn; // An established connection
133 
134       conn.exec("CREATE TABLE IF NOT EXISTS test_table");
135       ----------------
136    */
137    Result exec(string command)
138    {
139       PGresult* res = PQexec(_connection, cast(const char*)command.toStringz);
140       return Result(res);
141    }
142 
143    unittest
144    {
145       auto res = c.exec("SELECT 1::INT4 AS int4, 2::INT8 AS some_long");
146       writeln(" * exec for selecting INT4 and INT8");
147       assert(res.rows == 1);
148       assert(res.columns == 2);
149 
150       auto r = res[0];
151       assert(r[0].as!string == "1");
152       assert(r[1].as!string == "2");
153 
154       writeln(" * Row opIndex(int) and opIndex(string) equality ");
155       assert(r[0] == r["int4"]);
156       assert(r[1] == r["some_long"]);
157 
158    }
159 
160    /// ditto, async
161    bool send(string command)
162    {
163       return PQsendQuery(_connection, cast(const char*)command.toStringz) == 1;
164    }
165 
166    /**
167       Executes the given string with given params
168 
169       Params should be given as $1, $2, ... $n in the actual command.
170       All params are sent in a binary format and should not be escaped.
171       If a param's type cannot be inferred, this method will throw an exception,
172       in this case, either specify the type using the :: (cast) notation or
173       make sure the type can be inferred by PostgreSQL in your query.
174 
175       Examples:
176       ----------------
177       Connection conn; // An established connection
178 
179       conn.execParams("SELECT $1::string, $2::int, $3::double");
180       ----------------
181 
182       See also:
183          http://www.postgresql.org/docs/9.3/static/libpq-exec.html
184    */
185    Result execParams(T...)(string command, T params)
186    {
187       Value[] values;
188       foreach (param; params)
189          values ~= Value(param);
190 
191       return execParams(command, values);
192    }
193 
194    /// ditty, but async
195    void sendParams(T...)(string command, T params)
196    {
197       Value[] values;
198       foreach (param; params)
199          values ~= Value(param);
200 
201       execParams(command, values, true);
202    }
203 
204    /// ditto, but taking an array of params, instead of variadic template
205    Result execParams(string command, Value[] params, bool async = false)
206    {
207       const char* cStr = cast(const char*)command.toStringz;
208 
209       auto pTypes = params.paramTypes;
210       auto pValues = params.paramValues;
211       auto pLengths = params.paramLengths;
212       auto pFormats = params.paramFormats;
213 
214       if (async)
215       {
216          PQsendQueryParams(_connection, cStr, params.length.to!int, pTypes.ptr, cast(const(char*)*)pValues.ptr,
217                pLengths.ptr, pFormats.ptr, 1);
218 
219          return Result(null);
220       }
221       else
222          return Result(PQexecParams(_connection, cStr, params.length.to!int, pTypes.ptr,
223                cast(const(char*)*)pValues.ptr, pLengths.ptr, pFormats.ptr, 1));
224    }
225 
226    /// ditto, async
227    void sendParams(string command, Value[] params)
228    {
229       execParams(command, params, true);
230    }
231 
232    unittest
233    {
234       writeln("\t * execParams");
235       writeln("\t\t * Rows and cols");
236 
237       // we're not testing value here, specify types in the query to avoid any oid issues
238       auto res = c.execParams("SELECT 1::INT4 AS int4, 2::INT8 AS some_long", []);
239       assert(res.rows == 1);
240       assert(res.columns == 2);
241 
242       writeln("\t\t * Static values");
243       auto r = res[0];
244       assert(r[0].as!int == 1);
245       assert(r[1].as!long == 2);
246 
247       writeln("\t\t * opIndex(int) and opIndex(string) equality");
248       assert(r[0] == r["int4"]);
249       assert(r[1] == r["some_long"]);
250 
251       int int4 = 1;
252       long int8 = 2;
253       string str = "foo bar baz";
254       float float4 = 3.14;
255       double float8 = 3.1415;
256 
257       writeln("\t\t * Passed values");
258       res = c.execParams("SELECT $1::INT4, $2::INT8, $3::TEXT, $4::FLOAT4, $5::FLOAT8", int4, int8, str, float4, float8);
259 
260       assert(res.rows == 1);
261       r = res[0];
262 
263       // This should probably be tested by the serialisers, not here.
264       assert(r[0].as!int == int4);
265       assert(r[1].as!long == int8);
266       assert(r[2].as!string == str);
267       assert(r[3].as!float == float4);
268       assert(r[4].as!double == float8);
269    }
270 
271    /**
272       Returns the last error message
273 
274       Examples:
275       --------------------
276       Connection conn; // An established connection
277 
278       writeln(conn.errorMessage);
279       --------------------
280     */
281    @property string errorMessage()
282    {
283       return PQerrorMessage(_connection).to!string;
284    }
285 
286    unittest
287    {
288       writeln("\t * errorMessage");
289       try
290       {
291          c.execParams("SELECT_BADSYNTAX $1::INT4", 1);
292       }
293       catch (Throwable)
294       {
295       }
296 
297       assert(c.errorMessage.length != 0);
298    }
299 
300    /**
301       Escapes a string, to be used in a query
302     */
303    string escapeLiteral(string str)
304    {
305       const(char)* cStr = str.toStringz;
306       auto esc = PQescapeLiteral(_connection, cStr, str.length);
307 
308       if (esc == null)
309          throw new DPQException("escapeLiteral failed: " ~ this.errorMessage);
310 
311       str = esc.fromStringz.dup; // make a copy, escaped data must be freed
312       PQfreemem(esc);
313       return str;
314    }
315 
316    /**
317       Escapes an identifier (column, function, table name, ...) to be used in a query.
318     */
319    string escapeIdentifier(string str)
320    {
321       const(char)* cStr = str.toStringz;
322       auto esc = PQescapeIdentifier(_connection, cStr, str.length);
323 
324       if (esc == null)
325          throw new DPQException("escapeIdentifier failed: " ~ this.errorMessage);
326 
327       str = esc.fromStringz.dup; // make a copy, escaped data must be freed
328       PQfreemem(esc);
329       return str;
330    }
331 
332    /**
333       Will create the relation and return the queries (foreign key and index creation)
334       that still need to be ran, usually after ALL the relations are created.
335     */
336    private string[] createRelation(T)()
337    {
338       alias members = serialisableMembers!T;
339 
340       string relName = SerialiserFor!T.nameForType!T;
341       string escRelName = escapeIdentifier(relName);
342 
343       // A list of columns in the table
344       string[] columns;
345       // Queries that must be ran after the table is created
346       string[] additionalQueries;
347 
348       foreach (mName; members)
349       {
350          // Is there a better way to do this?
351          enum member = "T." ~ mName;
352 
353          // If the member is a property, typeof will fail with the wront context error
354          // so we construct it and then check the type
355          static if (is(FunctionTypeOf!(mixin(member)) == function))
356             alias MType = RealType!(typeof(mixin("T()." ~ mName)));
357          else
358             alias MType = RealType!(typeof(mixin(member)));
359 
360          alias serialiser = SerialiserFor!MType;
361 
362          // The attribute's name
363          string attrName = attributeName!(mixin(member));
364          string escAttrName = escapeIdentifier(attrName);
365 
366          // And the type, the user-specified type overwrites anything else
367          static if (hasUDA!(mixin(member), PGTypeAttribute))
368             string attrType = getUDAs!(mixin(member), PGTypeAttribute)[0].type;
369          else
370             string attrType = serialiser.nameForType!MType;
371 
372          string attr = escAttrName ~ " " ~ attrType;
373 
374          // A type must be created before using it.
375          serialiser.ensureExistence!MType(this);
376 
377          // Take care of primary keys
378          static if (hasUDA!(mixin(member), PrimaryKeyAttribute))
379             attr ~= " PRIMARY KEY";
380          // And foreign key
381          else static if (hasUDA!(mixin(member), ForeignKeyAttribute))
382          {
383             enum uda = getUDAs!(mixin(member), ForeignKeyAttribute)[0];
384 
385             // Create the FK
386             additionalQueries ~= `ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY(%s) REFERENCES %s (%s)`.format(escRelName,
387                   escapeIdentifier("%s_%s_fk_%s".format(relName, attrName, uda.relation)), escAttrName,
388                   escapeIdentifier(uda.relation), escapeIdentifier(uda.pkey));
389 
390             // Also create an index on the foreign key
391             additionalQueries ~= "CREATE INDEX %s ON %s (%s)".format(escapeIdentifier("%s_%s_fk_index".format(relName,
392                   attrName)), escRelName, escAttrName);
393          }
394          else static if (hasUDA!(mixin(member), IndexAttribute))
395          {
396             enum uda = getUDAs!(mixin(member), IndexAttribute)[0];
397 
398             additionalQueries ~= "CREATE%sINDEX %s ON %s (%s)".format(uda.unique ? " UNIQUE " : " ",
399                   escapeIdentifier("%s_%s_fk_index".format(relName, attrName)), escRelName, escAttrName);
400          }
401 
402          // Custom suffixes
403          static if (hasUDA!(mixin(member), ColumnSuffixAttribute))
404          {
405             enum suffix = getUDAs!(mixin(member), ColumnSuffixAttribute)[0].suffix;
406             attr ~= " " ~ suffix;
407          }
408 
409          columns ~= attr;
410       }
411 
412       // Create the table
413       exec("CREATE TABLE IF NOT EXISTS %s (%s)".format(escRelName, columns.join(", ")));
414 
415       addOidsFor(relName);
416 
417       return additionalQueries;
418    }
419 
420    package void addOidsFor(string typeName)
421    {
422       auto r = execParams("SELECT $1::regtype::oid, $2::regtype::oid", typeName, typeName ~ "[]");
423       Oid typeOid = r[0][0].as!(int).get;
424       Oid arrOid = r[0][1].as!(int).get;
425 
426       CompositeTypeSerialiser.addCustomOid(typeName, typeOid);
427       ArraySerialiser.addCustomOid(typeOid, arrOid);
428    }
429 
430    /**
431       Generates and runs the DDL from the given structures
432 
433       Attributes from dpq.attributes should be used to define
434       primary keys, indexes, and relationships.
435 
436       A custom type can be specified with the @type attribute.
437 
438       Examples:
439       -----------------------
440       Connection conn; // An established connection
441       struct User
442       {
443          @serial8 @PKey long id;
444          string username;
445          byte[] passwordHash;
446       };
447 
448       struct Article { ... };
449 
450       conn.ensureSchema!(User, Article);
451       -----------------------
452    */
453    void ensureSchema(T...)(bool createType = false)
454    {
455       import std.stdio;
456 
457       string[] additional;
458 
459       foreach (Type; T)
460          additional ~= createRelation!Type;
461 
462       foreach (cmd; additional)
463          try
464          {
465             exec(cmd);
466          }
467          catch
468       {
469       } // Horrible, I know, but this just means the constraint/index already exists
470    }
471 
472    unittest
473    {
474       // Probably needs more thorough testing, let's assume right now
475       // everything is correct if the creating was successful.
476 
477       writeln("\t * ensureSchema");
478       struct Inner
479       {
480          string innerStr;
481          int innerInt;
482       }
483 
484       struct TestTable1
485       {
486          @serial8 @PK long id;
487          string str;
488          int n;
489          Inner inner;
490       }
491 
492       c.ensureSchema!TestTable1;
493 
494       auto res = c.execParams("SELECT COUNT(*) FROM pg_catalog.pg_tables WHERE tablename = $1", relationName!TestTable1);
495 
496       assert(res.rows == 1);
497       assert(res[0][0].as!long == 1);
498 
499       c.exec("DROP TABLE " ~ relationName!TestTable1);
500       c.exec("DROP TYPE \"" ~ relationName!Inner ~ "\" CASCADE");
501    }
502 
503    /**
504       Returns the requested structure or a Nullable null value if no rows are returned
505 
506       This method queries for the given structure by its primary key. If no
507       primary key can be found, a compile-time error will be generated.
508 
509       Examples:
510       ----------------------
511       Connection conn; // An established connection
512       struct User
513       {
514          @serial @PKey int id;
515          ...
516       };
517 
518       auto user = conn.findOne!User(1); // will search by the id attribute
519       ----------------------
520    */
521    Nullable!T findOne(T, U)(U id)
522    {
523       return findOneBy!T(primaryKeyAttributeName!T, id);
524    }
525 
526    unittest
527    {
528       writeln("\t * findOne(T)(U id), findOneBy, findOne");
529       struct Testy
530       {
531          @serial @PK int id;
532          string foo;
533          int bar;
534          long baz;
535          int[] intArr;
536       }
537 
538       c.ensureSchema!Testy;
539 
540       writeln("\t\t * Null result");
541       auto shouldBeNull = c.findOne!Testy(0);
542       assert(shouldBeNull.isNull);
543 
544       c.exec("INSERT INTO %s (id, foo, bar, baz, %s) VALUES (1, 'somestr', 2, 3, '{1,2,3}')".format(relationName!Testy,
545             attributeName!(Testy.intArr)));
546 
547       writeln("\t\t * Valid result");
548       Testy t = c.findOne!Testy(1).get;
549       assert(t.id == 1, `t.id == 1`);
550       assert(t.foo == "somestr", `t.foo == "somestr"`);
551       assert(t.bar == 2, `t.bar == 2`);
552       assert(t.baz == 3, `t.baz == 3`);
553       assert(t.intArr == [1, 2, 3], `t.intArr == [1,2,3]`);
554 
555       writeln("\t\t * findOne with custom filter");
556       Testy t2 = c.findOne!Testy("id = $1", 1).get;
557       assert(t == t2);
558 
559       c.exec("DROP TABLE " ~ relationName!Testy);
560    }
561 
562    /**
563       Returns the requested structure, searches by the given column name
564       with the given value
565       If not rows are returned, a Nullable null value is returned
566 
567       Examples:
568       ----------------------
569       Connection conn; // An established connection
570       struct User
571       {
572          @serial @PKey int id;
573          ...
574       };
575 
576       auto user = conn.findOneBy!User("id", 1); // will search by "id"
577       ----------------------
578    */
579    Nullable!T findOneBy(T, U)(string col, U val)
580    {
581       import std.stdio;
582 
583       auto members = AttributeList!T;
584 
585       QueryBuilder qb;
586       qb.select(members).from(relationName!T).where([col: val]).limit(1);
587 
588       auto q = qb.query(this);
589 
590       auto r = q.run();
591       if (r.rows == 0)
592          return Nullable!T.init;
593 
594       //return T();
595 
596       auto res = deserialise!T(r[0]);
597       return Nullable!T(res);
598    }
599 
600    /**
601       Returns the requested structure, searches by the specified filter
602       with given params
603 
604       The filter is not further escaped, so programmer needs to make sure
605       not to properly escape or enclose reserved keywords (like user -> "user")
606       so PostgreSQL can understand them.
607 
608       If not rows are returned, a Nullable null value is returned
609 
610       Examples:
611       ----------------------
612       Connection conn; // An established connection
613       struct User
614       {
615          @serial @PKey int id;
616          string username;
617          int posts;
618       };
619 
620       auto user = conn.findOne!User("username = $1 OR posts > $2", "foo", 42);
621       if (!user.isNull)
622       {
623          ... // do something
624       }
625       ----------------------
626    */
627    Nullable!T findOne(T, U...)(string filter, U vals)
628    {
629       QueryBuilder qb;
630       qb.select(AttributeList!T).from(relationName!T).where(filter).limit(1);
631 
632       auto q = qb.query(this);
633       auto r = q.run(vals);
634 
635       if (r.rows == 0)
636          return Nullable!T.init;
637 
638       auto res = deserialise!T(r[0]);
639       return Nullable!T(res);
640    }
641 
642    /**
643       Returns an array of the specified type, filtered with the given filter and
644       params
645 
646       If no rows are returned by PostgreSQL, an empty array is returned.
647 
648       Examples:
649       ----------------------
650       Connection conn; // An established connection
651       struct User
652       {
653          @serial @PKey int id;
654          string username;
655          int posts;
656       };
657 
658       auto users = conn.find!User("username = $1 OR posts > $2", "foo", 42);
659       foreach (u; users)
660       {
661          ... // do something
662       }
663       ----------------------
664    */
665    T[] find(T, U...)(string filter = "", U vals = U.init)
666    {
667       QueryBuilder qb;
668       qb.select(AttributeList!T).from(relationName!T).where(filter);
669 
670       auto q = qb.query(this);
671 
672       T[] res;
673       foreach (r; q.run(vals))
674          res ~= deserialise!T(r);
675 
676       return res;
677    }
678 
679    /**
680       Returns an array of the specified type, filtered with the given filter and
681       params with length limited by limit
682 
683       If no rows are returned by PostgreSQL, an empty array is returned.
684       If limit is set to -1 then all filtered rows are returned.
685 
686       Examples:
687       ----------------------
688       Connection conn; // An established connection
689       struct User
690       {
691          @serial @PKey int id;
692          string username;
693          int posts;
694       };
695 
696       auto users = conn.findL!User("username = $1 OR posts > $2", 5, "foo", 42);
697       foreach (u; users)
698       {
699          ... // do something
700       }
701       ----------------------
702    */
703    T[] findL(T, U...)(string filter, int limit, U vals)
704    {
705       QueryBuilder qb;
706       qb.select(AttributeList!T).from(relationName!T).where(filter).limit(limit);
707 
708       auto q = qb.query(this);
709 
710       T[] res;
711       foreach (r; q.run(vals))
712          res ~= deserialise!T(r);
713 
714       return res;
715    }
716 
717    unittest
718    {
719       writeln("\t * find, findL");
720 
721       @relation("find_test")
722       struct Test
723       {
724          @serial @PK int id;
725          @attr("my_n") int n;
726       }
727 
728       c.ensureSchema!Test;
729 
730       Test t;
731       t.n = 1;
732 
733       c.insert(t);
734       c.insert(t);
735       ++t.n;
736       c.insert(t);
737       c.insert(t);
738       c.insert(t);
739 
740       Test[] ts = c.find!Test("my_n = $1", 1);
741       assert(ts.length == 2);
742       ts = c.find!Test("my_n > 0");
743       assert(ts.length == 5);
744       ts = c.find!Test("false");
745       assert(ts.length == 0);
746       ts = c.findL!Test("0 = 0", -1);
747       assert(ts.length == 5);
748       ts = c.findL!Test("my_n = $1", -1, 2);
749       assert(ts.length == 3);
750       ts = c.findL!Test("my_n = $1", 2, 2);
751       assert(ts.length == 2);
752 
753       c.exec("DROP TABLE find_test");
754    }
755 
756    /**
757       Updates records filtered by the filter string, setting the values
758       as specified in the update string. Both should be SQL-syntax
759 
760       Useful when updating just a single or a bunch of values in the table, or
761       when setting the values relatively to their current value.
762 
763       Params:
764          filter = the SQL filter string
765          update = the SQL update string
766          vals   = values to be used in the query
767 
768       Examples:
769       ----------------
770       Connection c; // an established connection
771       struct User { int id; int posts ...}
772       c.update!User("id = $1", "posts = posts + $2", 123, 1);
773       ----------------
774     */
775    int update(T, U...)(string filter, string update, U vals)
776    {
777       QueryBuilder qb;
778       qb.update(relationName!T).set(update).where(filter);
779 
780       auto r = qb.query(this).run(vals);
781       return r.rows;
782    }
783 
784    unittest
785    {
786       writeln("\t * update");
787 
788       @relation("update_test")
789       struct Test
790       {
791          @serial @PK int id;
792          int n;
793       }
794 
795       c.ensureSchema!Test;
796 
797       Test t;
798       t.n = 5;
799       c.insert(t);
800 
801       int nUpdates = c.update!Test("n = $1", "n = $2", 5, 123);
802       assert(nUpdates == 1, `nUpdates == 1`);
803 
804       t = c.findOneBy!Test("n", 123);
805       assert(t.n == 123, `t.n == 123`);
806 
807       writeln("\t\t * async");
808       c.updateAsync!Test("n = $1", "n = $2", 123, 6);
809       auto r = c.nextResult();
810 
811       assert(r.rows == 1);
812       assert(!c.findOneBy!Test("n", 6).isNull);
813 
814       c.exec("DROP TABLE update_test");
815    }
816 
817    /// ditto, async
818    void updateAsync(T, U...)(string filter, string update, U vals)
819    {
820       QueryBuilder qb;
821       qb.update(relationName!T).set(update).where(filter);
822 
823       qb.query(this).runAsync(vals);
824    }
825 
826    /**
827       Similar to above update, but instead of acceptign a filter and and update string,
828       always filters by the PK and updates with absolute values from the updates AA.
829 
830       Params:
831          id      = the value of the relation's PK to filter by
832          updates = an AA, mapping column name to the new value to be set for the column
833          async   = optionally send this query async
834 
835       Examples:
836       ------------------
837          Connection c; // en established connection
838          struct User { @PK int id; int x; string y }
839          c.update!User(1, [
840                "x": Value(2),
841                "y": Value("Hello there")]);
842       ------------------
843     */
844    int update(T, U)(U id, Value[string] updates, bool async = false)
845    {
846       QueryBuilder qb;
847 
848       qb.update(relationName!T).set(updates).where([primaryKeyAttributeName!T: id]);
849 
850       auto q = qb.query(this);
851 
852       if (async)
853       {
854          q.runAsync();
855          return -1;
856       }
857 
858       auto r = q.run();
859       return r.rows;
860    }
861 
862    unittest
863    {
864       writeln("\t * update with AA updates");
865 
866       @relation("update_aa_test")
867       struct Test
868       {
869          @serial @PK int id;
870          int n;
871       }
872 
873       c.ensureSchema!Test;
874 
875       Test t;
876       t.n = 1;
877       c.insert(t);
878 
879       int rows = c.update!Test(1, ["n": Value(2)]);
880       assert(rows == 1, `r.rows == 1`);
881 
882       c.exec("DROP TABLE update_aa_test");
883    }
884 
885    // ditto, but async
886    void updateAsync(T, U)(U id, Value[string] updates)
887    {
888       update!T(id, updates, true);
889    }
890 
891    /**
892       Similar to above, but accepts the whole structure as an update param.
893       Filters by the PK, updates ALL the values in the filtered rows.
894 
895       Params:
896          id = value of the relation's PK to filter by
897          updates = the structure that will provide values for the UPDATE
898          asnyc = whether the query should be sent async
899 
900       Examples:
901       ------------------
902       Connection c; // an established connection
903       struct User { @PK int id; int a; int b; }
904       c.update(1, myUser);
905       ------------------
906     */
907    int update(T, U)(U id, T updates, bool async = false)
908    {
909       import dpq.attributes;
910 
911       QueryBuilder qb;
912 
913       qb.update(relationName!T).where([primaryKeyAttributeName!T: id]);
914 
915       foreach (m; serialisableMembers!T)
916          qb.set(attributeName!(mixin("T." ~ m)), __traits(getMember, updates, m));
917 
918       auto q = qb.query(this);
919       if (async)
920       {
921          qb.query(this).runAsync();
922          return -1;
923       }
924 
925       auto r = q.run();
926       return r.rows;
927    }
928 
929    // ditto, async
930    void updateAsync(T, U)(U id, T updates)
931    {
932       update!T(id, updates, true);
933    }
934 
935    unittest
936    {
937       writeln("\t * update with object");
938 
939       @relation("update_object_test")
940       struct Test
941       {
942          @serial @PK int id;
943          int n;
944       }
945 
946       c.ensureSchema!Test;
947 
948       Test t;
949       t.n = 1;
950       t.id = 1; // assumptions <3
951 
952       c.insert(t);
953 
954       t.n = 2;
955       c.update!Test(1, t);
956 
957       t = c.findOne!Test(1);
958       assert(t.n == 2);
959 
960       t.n = 3;
961       c.updateAsync!Test(1, t);
962       auto r = c.nextResult();
963 
964       writeln("\t\t * async");
965       assert(r.rows == 1);
966 
967       c.exec("DROP TABLE update_object_test");
968    }
969 
970    /**
971       Inserts the given structure, returning whatever columns are specified by the
972       second param as a normal Result.
973 
974       Equivalent to specifying RETURNING at the end of the query.
975 
976       Examples:
977       -------------------
978       Connection c; // an established connection
979       struct Data { @PK int id, int a; int b; }
980       Data myData;
981       auto result = c.insert(myData, "id");
982       -------------------
983     */
984    Result insertR(T)(T val, string ret = "") if (!isArray!T)
985    {
986       QueryBuilder qb;
987       qb.insert(relationName!T, AttributeList!(T, true, true));
988       if (ret.length > 0)
989          qb.returning(ret);
990 
991       qb.addValues!T(val);
992 
993       return qb.query(this).run();
994    }
995 
996    /**
997       Inserts the given array structures in a singl query, returning whatever columns are specified by the
998       second param as a normal Result.
999 
1000       Equivalent to specifying RETURNING at the end of the query.
1001 
1002       Examples:
1003       -------------------
1004       Connection c; // an established connection
1005       struct Data { @PK int id, int a; int b; }
1006       Data[2] myData;
1007       auto result = c.insert(myData, "id");
1008       -------------------
1009     */
1010    Result insertR(T)(T vals, string ret = "") if (isArray!T)
1011    {
1012       alias BT = BaseType!T;
1013 
1014       if (!vals.length)
1015          return Result.init;
1016 
1017       QueryBuilder qb;
1018       qb.insert(relationName!BT, AttributeList!(BT, true, true));
1019       if (ret.length > 0)
1020          qb.returning(ret);
1021 
1022       foreach (val; vals)
1023          qb.addValues!BT(val);
1024 
1025       return qb.query(this).run();
1026    }
1027 
1028    /**
1029       Inserts the given structure to the DB
1030 
1031       Examples:
1032       ---------------
1033       Connection c; // An established connection
1034       struct User {@PK @serial int id; int a }
1035       User myUser;
1036       c.insert(myUser);
1037       ---------------
1038     */
1039    bool insert(T)(T val, bool async = false) if (!isArray!T)
1040    {
1041 
1042       QueryBuilder qb;
1043       qb.insert(relationName!T, AttributeList!(T, true, true));
1044       qb.addValues!T(val);
1045 
1046       if (async)
1047          return qb.query(this).runAsync();
1048 
1049       auto r = qb.query(this).run();
1050       return r.rows > 0;
1051    }
1052 
1053    /**
1054       Inserts the given array of structures to the DB as one query
1055 
1056       Examples:
1057       ---------------
1058       Connection c; // An established connection
1059       struct User {@PK @serial int id; int a }
1060       User[2] myUsers;
1061       c.insert(myUsers);
1062       ---------------
1063     */
1064    int insert(T)(T vals, bool async = false) if (isArray!T)
1065    {
1066       alias BT = BaseType!T;
1067 
1068       QueryBuilder qb;
1069       qb.insert(relationName!BT, AttributeList!(BT, true, true));
1070 
1071       if (!vals.length)
1072          return 0;
1073 
1074       foreach (val; vals)
1075          qb.addValues!BT(val);
1076 
1077       if (async)
1078          return qb.query(this).runAsync();
1079 
1080       auto r = qb.query(this).run();
1081       return r.rows;
1082    }
1083 
1084    unittest
1085    {
1086       writeln("\t * insert");
1087 
1088       @relation("insert_test_inner")
1089       struct Inner
1090       {
1091          int bar;
1092       }
1093 
1094       @relation("insert_test")
1095       struct Test
1096       {
1097          int n;
1098          Nullable!int n2;
1099          Inner foo;
1100       }
1101 
1102       c.ensureSchema!Test;
1103 
1104       Test t;
1105       t.n = 1;
1106       t.n2 = 2;
1107       t.foo.bar = 2;
1108 
1109       auto r = c.insert(t);
1110       assert(r == true);
1111 
1112       auto r2 = c.insertR(t, "n");
1113       assert(r2.rows == 1);
1114       assert(r2[0][0].as!int == t.n);
1115 
1116       Test t2 = c.findOneBy!Test("n", 1).get;
1117       assert(t2 == t, t.to!string ~ " != " ~ t2.to!string);
1118 
1119       Test[] t_arr;
1120       t_arr ~= Test.init;
1121       t_arr[0].n = 1;
1122       t_arr[0].n2 = 2;
1123       t_arr[0].foo.bar = 3;
1124       t_arr ~= Test.init;
1125       t_arr[1].n = 4;
1126       t_arr[1].n2 = 5;
1127       t_arr[1].foo.bar = 6;
1128 
1129       auto r3 = c.insert(t_arr);
1130       assert(r3 == 2);
1131 
1132       auto r4 = c.insertR(t_arr, "n");
1133       assert(r4[0][0].as!int == t_arr[0].n);
1134       assert(r4[1][0].as!int == t_arr[1].n);
1135 
1136       writeln("\t\t * async");
1137       t.n = 123;
1138       t.n2.nullify;
1139       c.insertAsync(t);
1140 
1141       auto res = c.nextResult();
1142       assert(res.rows == 1);
1143       t2 = c.findOneBy!Test("n", 123);
1144       assert(t.n2.isNull);
1145       assert(t2.n2.isNull);
1146 
1147       c.exec("DROP TABLE insert_test");
1148       c.exec("DROP TYPE \"%s\" CASCADE".format(relationName!Inner));
1149    }
1150 
1151    /// ditto, async
1152    void insertAsync(T)(T val)
1153    {
1154       insert(val, true);
1155    }
1156 
1157    /**
1158       Deletes the record in the given table, by its PK
1159 
1160       Examples:
1161       ---------------
1162       Connection c; // An established connection
1163       struct User {@PK @serial int id; int a }
1164       c.remove!User(1);
1165       ---------------
1166     */
1167    int remove(T, U)(U id)
1168    {
1169       QueryBuilder qb;
1170       qb.remove!T.where([primaryKeyAttributeName!T: id]);
1171 
1172       return qb.query(this).run().rows;
1173    }
1174 
1175    // ditto, async
1176    bool removeAsync(T, U)(U id)
1177    {
1178       QueryBuilder qb;
1179       qb.remove!T.where([primaryKeyAttributeName!T: id]);
1180 
1181       return qb.query(this).runAsync() == 1;
1182    }
1183 
1184    /**
1185       Deletes rows in the specified relation, filtered by the given filter string and values
1186 
1187       Examples:
1188       ---------------
1189       Connection c; // An established connection
1190       struct User { @PK @serial int id; int posts }
1191       c.remove!User("id > $1 AND posts == $2", 50, 0);
1192       ---------------
1193     */
1194    int remove(T, U...)(string filter, U vals)
1195    {
1196       QueryBuilder qb;
1197       qb.remove!T.where(filter);
1198 
1199       foreach (v; vals)
1200          qb.addValue(v);
1201 
1202       return qb.query(this).run().rows;
1203    }
1204 
1205    /// ditto, async
1206    bool removeAsync(T, U...)(string filter, U vals)
1207    {
1208       QueryBuilder qb;
1209       qb.remove!T.where(filter);
1210 
1211       foreach (v; vals)
1212          qb.addValue(v);
1213 
1214       return qb.query(this).runAsync() == 1;
1215    }
1216 
1217    unittest
1218    {
1219       @relation("remove_test")
1220       struct Test
1221       {
1222          @serial @PK int id;
1223          int n;
1224       }
1225 
1226       c.ensureSchema!Test;
1227 
1228       foreach (i; 0 .. 10)
1229          c.insert(Test(0, i));
1230 
1231       writeln("\t * remove(id)");
1232       int n = c.remove!Test(1);
1233       assert(n == 1, `n == 1`);
1234 
1235       writeln("\t\t * async");
1236       c.removeAsync!Test(2);
1237       auto r = c.nextResult();
1238       assert(r.rows == 1, `r.rows == 1`);
1239 
1240       writeln("\t * remove(filter, vals...)");
1241       n = c.remove!Test("id IN($1,$2,$3,$4,$5)", 3, 4, 5, 6, 7);
1242       assert(n == 5);
1243 
1244       writeln("\t\t * async");
1245       c.removeAsync!Test("id >= $1", 7);
1246       r = c.nextResult();
1247       assert(r.rows == 3);
1248 
1249       c.exec("DROP TABLE remove_test");
1250    }
1251 
1252    /**
1253       Returns a count of the rows matching the filter in the specified relation.
1254       Filter can be empty or not given to select a count of all the rows in the relation.
1255 
1256       Examples:
1257       ---------------
1258       Connection c; // An established connection
1259       struct User {@PK @serial int id; int a }
1260       long nUsers = c.count!User;
1261       nUsers = c.count!User("id > $1", 123);
1262       ---------------
1263     */
1264    long count(T, U...)(string filter = "", U vals = U.init)
1265    {
1266       import dpq.query;
1267 
1268       auto q = Query(this);
1269       string str = `SELECT COUNT(*) FROM "%s"`.format(relationName!T);
1270 
1271       if (filter.length > 0)
1272          str ~= " WHERE " ~ filter;
1273 
1274       q = str;
1275       auto r = q.run(vals);
1276 
1277       return r[0][0].as!long.get;
1278    }
1279 
1280    unittest
1281    {
1282       writeln("\t * count");
1283 
1284       @relation("test_count")
1285       struct Test
1286       {
1287          @serial @PK int id;
1288          int n;
1289       }
1290 
1291       c.ensureSchema!Test;
1292 
1293       Test t;
1294       c.insert(t);
1295 
1296       assert(c.count!Test == 1, `count == 1`);
1297       c.insert(t);
1298       assert(c.count!Test == 2, `count == 2`);
1299 
1300       c.exec("DROP TABLE test_count");
1301    }
1302 
1303    /**
1304       Equivalent to calling PQisBusy from libpq. Only useful if you're doing async
1305       stuff manually.
1306     */
1307    bool isBusy()
1308    {
1309       return PQisBusy(_connection) == 1;
1310    }
1311 
1312    unittest
1313    {
1314       writeln("\t * isBusy");
1315 
1316       assert(c.isBusy() == false);
1317 
1318       c.send("SELECT 1::INT");
1319 
1320       // This could fail in theory, but in practice ... fat chance.
1321       assert(c.isBusy() == true);
1322 
1323       c.nextResult();
1324       assert(c.isBusy() == false);
1325    }
1326 
1327    /**
1328        Blocks until a result is read, then returns it
1329 
1330        If no more results remain, a null result will be returned
1331 
1332        Make sure to call this until a null is returned or just use allResults.
1333    */
1334    Result nextResult()
1335    {
1336       PGresult* res = PQgetResult(_connection);
1337       return Result(res);
1338    }
1339 
1340    /**
1341       Calls nextResult until the value returned is null, the returns them
1342       as an array.
1343     */
1344    Result[] allResults()
1345    {
1346       Result[] res;
1347 
1348       PGresult* r;
1349       while ((r = PQgetResult(_connection)) != null)
1350          res ~= Result(r);
1351 
1352       return res;
1353    }
1354 
1355    /**
1356       Calls nextResult until null is returned, then retuns only the last non-null result.
1357     */
1358    Result lastResult()
1359    {
1360       Result res;
1361 
1362       PGresult* r;
1363       while ((r = PQgetResult(_connection)) != null)
1364          res = Result(r);
1365 
1366       return res;
1367    }
1368 
1369    unittest
1370    {
1371       writeln("\t * nextResult");
1372       auto x = c.nextResult();
1373       assert(x.isNull);
1374 
1375       int int1 = 1;
1376       int int2 = 2;
1377 
1378       c.sendParams("SELECT $1", int1);
1379 
1380       // In every way the same as lastResult
1381       Result r, t;
1382       while (!(t = c.nextResult()).isNull)
1383          r = t;
1384 
1385       assert(r.rows == 1);
1386       assert(r.columns == 1);
1387       assert(r[0][0].as!int == int1);
1388 
1389       writeln("\t * lastResult");
1390       c.sendParams("SELECT $1", int2);
1391       r = c.lastResult();
1392 
1393       assert(r.rows == 1);
1394       assert(r.columns == 1);
1395       assert(r[0][0].as!int == int2);
1396    }
1397 
1398    Result prepare(T...)(string name, string command, T paramTypes)
1399    {
1400       Oid[] oids;
1401       foreach (pType; paramTypes)
1402          oids ~= pType;
1403 
1404       char* cName = cast(char*)name.toStringz;
1405       char* cComm = cast(char*)command.toStringz;
1406 
1407       auto p = PreparedStatement(this, name, command, oids);
1408       _prepared[name] = p;
1409 
1410       return Result(PQprepare(_connection, cName, cComm, oids.length.to!int, oids.ptr));
1411    }
1412 
1413    Result execPrepared(string name, Value[] params...)
1414    {
1415       char* cStr = cast(char*)name.toStringz;
1416 
1417       return Result(PQexecPrepared(_connection, cStr, params.length.to!int, cast(char**)params.paramValues.ptr,
1418             params.paramLengths.ptr, params.paramFormats.ptr, 1));
1419    }
1420 
1421    Result execPrepared(T...)(string name, T params)
1422    {
1423       Value[] vals;
1424       foreach (p; params)
1425          vals ~= Value(p);
1426 
1427       return execPrepared(name, vals);
1428    }
1429 
1430    bool sendPrepared(string name, Value[] params...)
1431    {
1432       char* cStr = cast(char*)name.toStringz;
1433 
1434       return PQsendQueryPrepared(_connection, cStr, params.length.to!int, cast(char**)params.paramValues.ptr,
1435             params.paramLengths.ptr, params.paramFormats.ptr, 1) == 1;
1436    }
1437 
1438    bool sendPrepared(T...)(string name, T params)
1439    {
1440       Value[] vals;
1441       foreach (p; params)
1442          vals ~= Value(p);
1443 
1444       return sendPrepared(name, vals);
1445    }
1446 
1447    unittest
1448    {
1449       writeln("\t * prepare");
1450       // The result of this isn't really all that useful, but as long as it
1451       // throws on errors, it kinda is
1452       c.prepare("prepare_test", "SELECT $1", Type.INT4);
1453 
1454       writeln("\t * execPrepared");
1455       auto r = c.execPrepared("prepare_test", 1);
1456       assert(r.rows == 1);
1457       assert(r[0][0].as!int == 1);
1458 
1459       writeln("\t\t * sendPrepared");
1460       bool s = c.sendPrepared("prepare_test", 1);
1461       assert(s);
1462 
1463       r = c.lastResult();
1464       assert(r.rows == 1);
1465       assert(r[0][0].as!int == 1);
1466    }
1467 
1468    /**
1469       Begins a transaction block.
1470    */
1471    void begin()
1472    {
1473       import dpq.query;
1474 
1475       auto q = Query(this, "BEGIN");
1476       q.run();
1477    }
1478 
1479    /**
1480       Commits current transaction
1481    */
1482    void commit()
1483    {
1484       import dpq.query;
1485 
1486       auto q = Query(this, "COMMIT");
1487       q.run();
1488    }
1489 
1490    /**
1491       Creates savepoint in the current transaction block.
1492 
1493       Params:
1494          name = name of created savepoint
1495 
1496       Returns: created Savepoint.
1497    */
1498    Savepoint savepoint(string name)
1499    {
1500       import dpq.query;
1501 
1502       Savepoint s = new Savepoint(name);
1503       auto q = Query(this, "SAVEPOINT " ~ s.name);
1504       q.run();
1505 
1506       return s;
1507    }
1508 
1509    /**
1510       Destroys savepoint in the current transaction block.
1511 
1512       Params:
1513          s = savepoint to destroy
1514    */
1515    void releaseSavepoint(Savepoint s)
1516    {
1517       import dpq.query;
1518 
1519       auto q = Query(this, "RELEASE SAVEPOINT " ~ s.name);
1520       q.run();
1521    }
1522 
1523    /**
1524       Rollback the current transaction to savepoint.
1525 
1526       Params:
1527          s = savepoint to rollback to. If savepoint s is null or no savepoint is specified then transaction
1528          will be rolled back to the begining.
1529    */
1530    void rollback(Savepoint s = null)
1531    {
1532       import dpq.query;
1533 
1534       auto q = Query(this);
1535 
1536       if (s is null)
1537          q.command = "ROLLBACK";
1538       else
1539          q.command = "ROLLBACK TO " ~ s.name;
1540 
1541       q.run();
1542    }
1543 
1544    unittest
1545    {
1546       writeln("\t * transaction");
1547 
1548       @relation("transaction_test")
1549       struct Test
1550       {
1551          @serial @PK int id;
1552          string t;
1553       }
1554 
1555       c.ensureSchema!Test;
1556 
1557       Test t;
1558       t.t = "before transaction";
1559       auto r = c.insertR(t, "id");
1560       t.id = r[0][0].as!int.get;
1561 
1562       c.begin();
1563       t.t = "this value is ignored";
1564       c.update(t.id, t);
1565 
1566       auto s1 = c.savepoint("s1");
1567       t.t = "before savepoint s2";
1568       c.update(t.id, t);
1569 
1570       auto s2 = c.savepoint("s2");
1571       t.t = "after savepoint s2";
1572       c.update(t.id, t);
1573 
1574       assert(c.findOne!Test(t.id).get.t == "after savepoint s2");
1575 
1576       c.rollback(s2);
1577       assert(c.findOne!Test(t.id).get.t == "before savepoint s2");
1578 
1579       c.rollback();
1580       assert(c.findOne!Test(t.id).get.t == "before transaction");
1581 
1582       Connection c2 = Connection("host=127.0.0.1 dbname=test user=test");
1583       c.begin();
1584       t.t = "inside transaction";
1585       c.update(t.id, t);
1586 
1587       assert(c.findOne!Test(t.id).get.t == "inside transaction");
1588       assert(c2.findOne!Test(t.id).get.t == "before transaction");
1589 
1590       c.commit();
1591       assert(c.findOne!Test(t.id).get.t == "inside transaction");
1592       assert(c2.findOne!Test(t.id).get.t == "inside transaction");
1593 
1594       c.exec("DROP TABLE transaction_test");
1595       c2.close();
1596    }
1597 
1598    ref PreparedStatement prepared(string name)
1599    {
1600       return _prepared[name];
1601    }
1602 
1603    ref PreparedStatement opIndex(string name)
1604    {
1605       return prepared(name);
1606    }
1607 }
1608 
1609 /**
1610    Deserialises the given Row to the requested type
1611 
1612    Params:
1613       T  = (template) type to deserialise into
1614       r  = Row to deserialise
1615 */
1616 T deserialise(T)(Row r, string prefix = "")
1617 {
1618    static if (is(T == class))
1619       T res = new T();
1620    else
1621       T res;
1622    foreach (m; serialisableMembers!T)
1623    {
1624       enum member = "T." ~ m;
1625       enum n = attributeName!(mixin(member));
1626       alias OType = typeof(mixin(member));
1627       alias MType = RealType!OType;
1628 
1629       try
1630       {
1631          auto x = r[prefix ~ n].as!MType;
1632          if (!x.isNull)
1633             __traits(getMember, res, m) = cast(OType)x.get;
1634       }
1635       catch (DPQException e)
1636       {
1637          if (!isInstanceOf!(Nullable, MType))
1638             throw e;
1639       }
1640    }
1641    return res;
1642 }
1643 
1644 class Savepoint
1645 {
1646    string name;
1647 
1648    this(string name)
1649    {
1650       this.name = name;
1651    }
1652 }
1653 
1654 /// Hold the last created connection, not to be used outside the library
1655 package Connection* _dpqLastConnection;