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