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