1 /**
2  * DDBC - D DataBase Connector - abstraction layer for RDBMS access, with interface similar to JDBC. 
3  * 
4  * Source file ddbc/drivers/pgsqlddbc.d.
5  *
6  * DDBC library attempts to provide implementation independent interface to different databases.
7  * 
8  * Set of supported RDBMSs can be extended by writing Drivers for particular DBs.
9  * Currently it only includes MySQL driver.
10  * 
11  * JDBC documentation can be found here:
12  * $(LINK http://docs.oracle.com/javase/1.5.0/docs/api/java/sql/package-summary.html)$(BR)
13  *
14  * This module contains implementation of PostgreSQL Driver
15  * 
16  *
17  * You can find usage examples in unittest{} sections.
18  *
19  * Copyright: Copyright 2013
20  * License:   $(LINK www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
21  * Author:   Vadim Lopatin
22  */
23 module ddbc.drivers.pgsql;
24 
25 import std.algorithm;
26 import std.conv;
27 import std.datetime;
28 import std.exception;
29 import std.stdio;
30 import std.string;
31 import std.variant;
32 
33 version(USE_PGSQL) {
34     
35     
36     version (Windows) {
37         pragma (lib, "libpq.lib");
38         //pragma (lib, "pq");
39     } else version (linux) {
40         pragma (lib, "pq");
41     } else version (Posix) {
42         pragma (lib, "pq");
43     } else version (darwin) {
44         pragma (lib, "pq");
45     } else {
46         pragma (msg, "You will need to manually link in the LIBPQ library.");
47     } 
48 
49 
50     string bytesToBytea(byte[] bytes) {
51         if (bytes is null)
52             return null;
53         string res;
54         foreach(b; bytes) {
55             if (b == 0)
56                 res ~= "\\0";
57             else if (b == '\r')
58                 res ~= "\\r";
59             else if (b == '\n')
60                 res ~= "\\n";
61             else if (b == '\t')
62                 res ~= "\\t";
63             else if (b == '\\')
64                 res ~= "\\\\";
65             else
66                 res ~= cast(char)b;
67         }
68         return res;
69     }
70 
71     string ubytesToBytea(ubyte[] bytes) {
72         if (bytes is null)
73             return null;
74         string res;
75         foreach(b; bytes) {
76             if (b == 0)
77                 res ~= "\\0";
78             else if (b == '\r')
79                 res ~= "\\r";
80             else if (b == '\n')
81                 res ~= "\\n";
82             else if (b == '\t')
83                 res ~= "\\t";
84             else if (b == '\\')
85                 res ~= "\\\\";
86             else
87                 res ~= cast(char)b;
88         }
89         return res;
90     }
91 
92     byte[] byteaToBytes(string s) {
93         if (s is null)
94             return null;
95         byte[] res;
96         bool lastBackSlash = 0;
97         foreach(ch; s) {
98             if (ch == '\\') {
99                 if (lastBackSlash) {
100                     res ~= '\\';
101                     lastBackSlash = false;
102                 } else {
103                     lastBackSlash = true;
104                 }
105             } else {
106                 if (lastBackSlash) {
107                     if (ch == '0') {
108                         res ~= 0;
109                     } else if (ch == 'r') {
110                         res ~= '\r';
111                     } else if (ch == 'n') {
112                         res ~= '\n';
113                     } else if (ch == 't') {
114                         res ~= '\t';
115                     } else {
116                     }
117                 } else {
118                     res ~= cast(byte)ch;
119                 }
120                 lastBackSlash = false;
121             }
122         }
123         return res;
124     }
125 
126     ubyte[] byteaToUbytes(string s) {
127         if (s is null)
128             return null;
129         ubyte[] res;
130         bool lastBackSlash = 0;
131         foreach(ch; s) {
132             if (ch == '\\') {
133                 if (lastBackSlash) {
134                     res ~= '\\';
135                     lastBackSlash = false;
136                 } else {
137                     lastBackSlash = true;
138                 }
139             } else {
140                 if (lastBackSlash) {
141                     if (ch == '0') {
142                         res ~= 0;
143                     } else if (ch == 'r') {
144                         res ~= '\r';
145                     } else if (ch == 'n') {
146                         res ~= '\n';
147                     } else if (ch == 't') {
148                         res ~= '\t';
149                     } else {
150                     }
151                 } else {
152                     res ~= cast(byte)ch;
153                 }
154                 lastBackSlash = false;
155             }
156         }
157         return res;
158     }
159     
160 
161     // C interface of libpq is taken from https://github.com/adamdruppe/misc-stuff-including-D-programming-language-web-stuff/blob/master/postgres.d
162     
163 
164     extern(C) {
165         struct PGconn {};
166         struct PGresult {};
167         
168         void PQfinish(PGconn*);
169         PGconn* PQconnectdb(const char*);
170         PGconn *PQconnectdbParams(const char **keywords, const char **values, int expand_dbname);
171         
172         int PQstatus(PGconn*); // FIXME check return value
173         
174         const (char*) PQerrorMessage(PGconn*);
175         
176         PGresult* PQexec(PGconn*, const char*);
177         void PQclear(PGresult*);
178         
179         
180         int PQresultStatus(PGresult*); // FIXME check return value
181         char *PQcmdTuples(PGresult *res);
182         
183         int PQnfields(PGresult*); // number of fields in a result
184         const(char*) PQfname(PGresult*, int); // name of field
185         
186         int PQntuples(PGresult*); // number of rows in result
187         const(char*) PQgetvalue(PGresult*, int row, int column);
188         
189         size_t PQescapeString (char *to, const char *from, size_t length);
190         
191         enum int CONNECTION_OK = 0;
192         enum int PGRES_COMMAND_OK = 1;
193         enum int PGRES_TUPLES_OK = 2;
194         enum int PGRES_COPY_OUT = 3;             /* Copy Out data transfer in progress */
195         enum int PGRES_COPY_IN = 4;              /* Copy In data transfer in progress */
196         enum int PGRES_BAD_RESPONSE = 5;         /* an unexpected response was recv'd from the backend */
197         enum int PGRES_NONFATAL_ERROR = 6;       /* notice or warning message */
198         enum int PGRES_FATAL_ERROR = 7;          /* query failed */
199         enum int PGRES_COPY_BOTH = 8;            /* Copy In/Out data transfer in progress */
200         enum int PGRES_SINGLE_TUPLE = 9;          /* single tuple from larger resultset */
201         
202         int PQgetlength(const PGresult *res,
203                         int row_number,
204                         int column_number);
205         int PQgetisnull(const PGresult *res,
206                         int row_number,
207                         int column_number);
208         
209         alias ulong Oid;
210         
211         Oid PQftype(const PGresult *res,
212                     int column_number);
213         Oid PQoidValue(const PGresult *res);
214         
215         int PQfformat(const PGresult *res,
216                       int column_number);
217 
218         Oid PQftable(const PGresult *res,
219                      int column_number);
220 
221         PGresult *PQprepare(PGconn *conn,
222                             const char *stmtName,
223                             const char *query,
224                             int nParams,
225                             const Oid *paramTypes);
226 
227         PGresult *PQexecPrepared(PGconn *conn,
228                                  const char *stmtName,
229                                  int nParams,
230                                  const char * *paramValues,
231                                  const int *paramLengths,
232                                  const int *paramFormats,
233                                  int resultFormat);
234 
235         PGresult *PQexecParams(PGconn *conn,
236                                const char *command,
237                                int nParams,
238                                const Oid *paramTypes,
239                                const char * *paramValues,
240                                const int *paramLengths,
241                                const int *paramFormats,
242                                int resultFormat);
243 
244         char *PQresultErrorMessage(const PGresult *res);
245 
246         const int BOOLOID = 16;
247         const int BYTEAOID = 17;
248         const int CHAROID = 18;
249         const int NAMEOID = 19;
250         const int INT8OID = 20;
251         const int INT2OID = 21;
252         const int INT2VECTOROID = 22;
253         const int INT4OID = 23;
254         const int REGPROCOID = 24;
255         const int TEXTOID = 25;
256         const int OIDOID = 26;
257         const int TIDOID = 27;
258         const int XIDOID = 28;
259         const int CIDOID = 29;
260         const int OIDVECTOROID = 30;
261         const int JSONOID = 114;
262         const int XMLOID = 142;
263         const int PGNODETREEOID = 194;
264         const int POINTOID = 600;
265         const int LSEGOID = 601;
266         const int PATHOID = 602;
267         const int BOXOID = 603;
268         const int POLYGONOID = 604;
269         const int LINEOID = 628;
270         const int FLOAT4OID = 700;
271         const int FLOAT8OID = 701;
272         const int ABSTIMEOID = 702;
273         const int RELTIMEOID = 703;
274         const int TINTERVALOID = 704;
275         const int UNKNOWNOID = 705;
276         const int CIRCLEOID = 718;
277         const int CASHOID = 790;
278         const int MACADDROID = 829;
279         const int INETOID = 869;
280         const int CIDROID = 650;
281         const int INT4ARRAYOID = 1007;
282         const int TEXTARRAYOID = 1009;
283         const int FLOAT4ARRAYOID = 1021;
284         const int ACLITEMOID = 1033;
285         const int CSTRINGARRAYOID = 1263;
286         const int BPCHAROID = 1042;
287         const int VARCHAROID = 1043;
288         const int DATEOID = 1082;
289         const int TIMEOID = 1083;
290         const int TIMESTAMPOID = 1114;
291         const int TIMESTAMPTZOID = 1184;
292         const int INTERVALOID = 1186;
293         const int TIMETZOID = 1266;
294         const int BITOID = 1560;
295         const int VARBITOID = 1562;
296         const int NUMERICOID = 1700;
297         const int REFCURSOROID = 1790;
298         const int REGPROCEDUREOID = 2202;
299         const int REGOPEROID = 2203;
300         const int REGOPERATOROID = 2204;
301         const int REGCLASSOID = 2205;
302         const int REGTYPEOID = 2206;
303         const int REGTYPEARRAYOID = 2211;
304         const int UUIDOID = 2950;
305         const int TSVECTOROID = 3614;
306         const int GTSVECTOROID = 3642;
307         const int TSQUERYOID = 3615;
308         const int REGCONFIGOID = 3734;
309         const int REGDICTIONARYOID = 3769;
310         const int INT4RANGEOID = 3904;
311         const int RECORDOID = 2249;
312         const int RECORDARRAYOID = 2287;
313         const int CSTRINGOID = 2275;
314         const int ANYOID = 2276;
315         const int ANYARRAYOID = 2277;
316         const int VOIDOID = 2278;
317         const int TRIGGEROID = 2279;
318         const int EVTTRIGGEROID = 3838;
319         const int LANGUAGE_HANDLEROID = 2280;
320         const int INTERNALOID = 2281;
321         const int OPAQUEOID = 2282;
322         const int ANYELEMENTOID = 2283;
323         const int ANYNONARRAYOID = 2776;
324         const int ANYENUMOID = 3500;
325         const int FDW_HANDLEROID = 3115;
326         const int ANYRANGEOID = 3831;
327         const int TYPTYPE_BASE = 'b';
328         const int TYPTYPE_COMPOSITE = 'c';
329         const int TYPTYPE_DOMAIN = 'd';
330         const int TYPTYPE_ENUM = 'e';
331         const int TYPTYPE_PSEUDO = 'p';
332         const int TYPTYPE_RANGE = 'r';
333         const int TYPCATEGORY_INVALID = '\0';
334         const int TYPCATEGORY_ARRAY = 'A';
335         const int TYPCATEGORY_BOOLEAN = 'B';
336         const int TYPCATEGORY_COMPOSITE = 'C';
337         const int TYPCATEGORY_DATETIME = 'D';
338         const int TYPCATEGORY_ENUM = 'E';
339         const int TYPCATEGORY_GEOMETRIC = 'G';
340         const int TYPCATEGORY_NETWORK = 'I';
341         const int TYPCATEGORY_NUMERIC = 'N';
342         const int TYPCATEGORY_PSEUDOTYPE = 'P';
343         const int TYPCATEGORY_RANGE = 'R';
344         const int TYPCATEGORY_STRING = 'S';
345         const int TYPCATEGORY_TIMESPAN = 'T';
346         const int TYPCATEGORY_USER = 'U';
347         const int TYPCATEGORY_BITSTRING = 'V';
348         const int TYPCATEGORY_UNKNOWN = 'X';
349 
350     
351     }
352     
353 } else { // version(USE_PGSQL)
354     immutable bool PGSQL_TESTS_ENABLED = false;
355 }
356