1 /*
2 * libdbi - database independent abstraction layer for C.
3 * Copyright (C) 2001-2002, David Parker and Mark Tobenkin.
4 * http://libdbi.sourceforge.net
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 * dbd_pgsql.c: PostgreSQL database support (using libpq)
21 * Copyright (C) 2001-2002, David A. Parker <david@neongoat.com>.
22 * http://libdbi.sourceforge.net
23 *
24 * $Id: dbd_pgsql.c,v 1.69 2013/02/08 01:01:31 mhoenicka Exp $
25 */
26
27 #ifdef HAVE_CONFIG_H
28 #include <config.h>
29 #endif
30
31 #define _GNU_SOURCE /* we need asprintf */
32
33 #ifndef HAVE_ATOLL
34 long long atoll(const char *str);
35 #endif
36
37 #ifndef HAVE_STRTOLL
38 long long strtoll(const char *nptr, char **endptr, int base);
39 #endif
40
41 /* In 7.4 PQfreeNotify was deprecated and PQfreemem is used instead. A
42 macro exists in 7.4 for backwards compatibility. */
43 #ifndef PQfreeNotify /* must be earlier than 7.4 */
44 #define PQfreemem PQfreeNotify
45 #endif
46
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <ctype.h> /* for isdigit() */
51
52 #include <dbi/dbi.h>
53 #include <dbi/dbi-dev.h>
54 #include <dbi/dbd.h>
55
56 #include <libpq-fe.h>
57 #include "dbd_pgsql.h"
58
59 static const dbi_info_t driver_info = {
60 "pgsql",
61 "PostgreSQL database support (using libpq)",
62 "David A. Parker <david@neongoat.com>",
63 "http://libdbi-drivers.sourceforge.net",
64 "dbd_pgsql v" VERSION,
65 __DATE__
66 };
67
68 static const char *custom_functions[] = PGSQL_CUSTOM_FUNCTIONS;
69 static const char *reserved_words[] = PGSQL_RESERVED_WORDS;
70
71 /* encoding strings, array is terminated by a pair of empty strings */
72 static const char pgsql_encoding_hash[][16] = {
73 /* PostgreSQL , www.iana.org */
74 "SQL_ASCII", "US-ASCII",
75 "EUC_JP", "EUC-JP",
76 "EUC_KR", "EUC-KR",
77 "UNICODE", "UTF-8",
78 "UTF8", "UTF-8",
79 "LATIN1", "ISO-8859-1",
80 "LATIN2", "ISO-8859-2",
81 "LATIN3", "ISO-8859-3",
82 "LATIN4", "ISO-8859-4",
83 "LATIN5", "ISO-8859-9",
84 "LATIN6", "ISO-8859-10",
85 "LATIN7", "ISO-8859-13",
86 "LATIN8", "ISO-8859-14",
87 "LATIN9", "ISO-8859-15",
88 "LATIN10", "ISO-8859-16",
89 "ISO-8859-5", "ISO-8859-5",
90 "ISO-8859-6", "ISO-8859-6",
91 "ISO-8859-7", "ISO-8859-7",
92 "ISO-8859-8", "ISO-8859-8",
93 "KOI8", "KOI8-R",
94 "WIN", "windows-1251",
95 "ALT", "IBM866",
96 "", ""
97 };
98
99 /* forward declarations of internal functions */
100 void _translate_postgresql_type(unsigned int oid, unsigned short *type, unsigned int *attribs);
101 void _get_field_info(dbi_result_t *result);
102 void _get_row_data(dbi_result_t *result, dbi_row_t *row, unsigned long long rowidx);
103 int _dbd_real_connect(dbi_conn_t *conn, const char *db);
104 char *_unescape_hex_binary(char* raw, size_t in_len, size_t* out_len);
105 int _digit_to_number(const char c);
106
107 /* this function is available through the PostgreSQL client library, but it
108 is not declared in any of their headers. I hope this won't break anything */
109 const char *pg_encoding_to_char(int encoding_id);
110
111
112 /* these are helpers for dbd_real_connect */
113 #define CONNINFO_APPEND_ESCAPED(conninfo, fmt, key, value ) \
114 do { \
115 size_t orig_size = strlen( value ); \
116 char *value_escaped = malloc( 2 * orig_size + 1 ); \
117 _dbd_escape_chars( value_escaped, value, orig_size, "\\'" ); \
118 CONNINFO_APPEND( conninfo, fmt, key, value_escaped ); \
119 free( value_escaped ); \
120 } while(0)
121
122 #define CONNINFO_APPEND(conninfo, fmt, key, value ) \
123 do { \
124 char *tmp = conninfo; \
125 if( conninfo ) { \
126 asprintf( &conninfo, "%s " fmt, tmp, key, value ); \
127 free( tmp ); \
128 } \
129 else \
130 asprintf( &conninfo, fmt, key, value ); \
131 } while(0)
132
133
134 /* base36 decoding to convert the 5 alphanumeric chars of SQLSTATE to a 32bit int */
base36decode(char * base36)135 int base36decode(char *base36) {
136 int len = strlen(base36);
137 int output = 0;
138 int pos = 0;
139
140 for (; pos < len; pos++) {
141 char c = base36[pos];
142 if ( ((c - '0') >= 0) && ((c - '0') <= 9) ) {
143 c = c - '0';
144 } else {
145 c = c - 'A' + 10;
146 }
147 output = 36 * output + c;
148 }
149
150 return output;
151 }
152
153 /* real code starts here */
dbd_register_driver(const dbi_info_t ** _driver_info,const char *** _custom_functions,const char *** _reserved_words)154 void dbd_register_driver(const dbi_info_t **_driver_info, const char ***_custom_functions, const char ***_reserved_words) {
155 /* this is the first function called after the driver module is loaded into memory */
156 *_driver_info = &driver_info;
157 *_custom_functions = custom_functions;
158 *_reserved_words = reserved_words;
159 }
160
dbd_initialize(dbi_driver_t * driver)161 int dbd_initialize(dbi_driver_t *driver) {
162 /* perform any database-specific server initialization.
163 * this is called right after dbd_register_driver().
164 * return -1 on error, 0 on success. if -1 is returned, the driver will not
165 * be added to the list of available drivers. */
166
167 /* this indicates the driver can be safely unloaded when libdbi is
168 shut down. Change the value to '0' (zero) if the driver, or a
169 library it is linked against, installs exit handlers via
170 atexit() */
171 _dbd_register_driver_cap(driver, "safe_dlclose", 1);
172
173 /* this indicates the database engine supports transactions */
174 _dbd_register_driver_cap(driver, "transaction_support", 1);
175
176 /* this indicates the database engine supports savepoints */
177 _dbd_register_driver_cap(driver, "savepoint_support", 1);
178
179 return 0;
180 }
181
dbd_finalize(dbi_driver_t * driver)182 int dbd_finalize(dbi_driver_t *driver) {
183 /* perform any database-specific client library shutdown.
184 * this is called right before dlclose()ing the driver.
185 * return -1 on error, 0 on success. */
186
187 return 0;
188 }
189
dbd_connect(dbi_conn_t * conn)190 int dbd_connect(dbi_conn_t *conn) {
191 return _dbd_real_connect(conn, NULL);
192 }
193
_dbd_real_connect(dbi_conn_t * conn,const char * db)194 int _dbd_real_connect(dbi_conn_t *conn, const char *db) {
195 const char *dbname;
196 const char *encoding = dbi_conn_get_option(conn, "encoding");
197
198 PGconn *pgconn;
199 char *conninfo = NULL;
200
201 const char *optname = NULL;
202 const char *pgopt;
203 const char *optval;
204 int optval_num;
205 int have_port = 0;
206
207 /* PQconnectdb accepts additional options as a string of
208 "key=value" pairs. Assemble that string from the option
209 list */
210 while(( pgopt = optname = dbi_conn_get_option_list( conn, optname ) ))
211 {
212 /* Ignore "encoding" and "dbname"; we'll deal with them later */
213 if ( !strcmp( pgopt, "encoding" ) || !strcmp( pgopt, "dbname" ) ) {
214 continue;
215 }
216
217 /* Map "username" to "user" */
218 else if( !strcmp( pgopt, "username" ) ) {
219 pgopt = "user";
220 }
221
222 else if (!strcmp(pgopt, "timeout")) {
223 pgopt = "connect_timeout";
224 }
225
226 /* Map "pgsql_foo" to "foo" */
227 else if( !strncmp( pgopt, "pgsql_", 6 ) ) {
228 pgopt += 6;
229 }
230
231 /* Accept these non-pgsql_ options but discard all others */
232 else if (strcmp(pgopt, "password")
233 && strcmp(pgopt, "host")
234 && strcmp(pgopt, "port")) {
235 continue;
236 }
237
238 if (!strcmp(pgopt, "port")) {
239 have_port++;
240 }
241
242 optval = dbi_conn_get_option( conn, optname );
243 optval_num = dbi_conn_get_option_numeric( conn, optname );
244
245 if( optval ) {
246 CONNINFO_APPEND_ESCAPED( conninfo, "%s='%s'", pgopt, optval );
247 }
248 else {
249 CONNINFO_APPEND( conninfo, "%s='%d'", pgopt, optval_num );
250 }
251 }
252
253 if (db && *db) {
254 dbname = db;
255 }
256 else {
257 dbname = dbi_conn_get_option(conn, "dbname");
258 }
259
260 if( dbname )
261 CONNINFO_APPEND_ESCAPED( conninfo, "%s='%s'", "dbname", dbname );
262
263 /* if no port was specified, fill in the default PostgreSQL port */
264 if (!have_port) {
265 CONNINFO_APPEND( conninfo, "%s='%d'", "port", 5432 );
266 }
267
268 /* send an empty string instead of NULL if there are no options */
269 pgconn = PQconnectdb(conninfo ? conninfo : "");
270 if (conninfo) free(conninfo);
271 if (!pgconn) return -1;
272
273 if (PQstatus(pgconn) == CONNECTION_BAD) {
274 conn->connection = (void *)pgconn; // still need this set so _error_handler can grab information
275 _dbd_internal_error_handler(conn, NULL, DBI_ERROR_DBD);
276 PQfinish(pgconn);
277 conn->connection = NULL; // pgconn no longer valid
278 return -2;
279 }
280 else {
281 conn->connection = (void *)pgconn;
282 if (dbname) conn->current_db = strdup(dbname);
283 }
284
285 if (encoding && *encoding) {
286 /* set connection encoding */
287 if (strcmp(encoding, "auto")) {
288 if (PQsetClientEncoding(pgconn, dbd_encoding_from_iana(encoding))) {
289 /* printf("could not set client encoding to %s\n", dbd_encoding_from_iana(encoding)); */
290 }
291 }
292 /* else: by default, pgsql uses the database encoding
293 as the client encoding, nothing to do */
294 }
295
296 return 0;
297 }
298
dbd_disconnect(dbi_conn_t * conn)299 int dbd_disconnect(dbi_conn_t *conn) {
300 if (conn->connection) {
301 PQfinish((PGconn *)conn->connection);
302 }
303 return 0;
304 }
305
dbd_fetch_row(dbi_result_t * result,unsigned long long rowidx)306 int dbd_fetch_row(dbi_result_t *result, unsigned long long rowidx) {
307 dbi_row_t *row = NULL;
308
309 if (result->result_state == NOTHING_RETURNED) return 0;
310
311 if (result->result_state == ROWS_RETURNED) {
312 /* get row here */
313 row = _dbd_row_allocate(result->numfields);
314 _get_row_data(result, row, rowidx);
315 _dbd_row_finalize(result, row, rowidx);
316 }
317
318 return 1; /* 0 on error, 1 on successful fetchrow */
319 }
320
dbd_free_query(dbi_result_t * result)321 int dbd_free_query(dbi_result_t *result) {
322 PQclear((PGresult *)result->result_handle);
323 return 0;
324 }
325
dbd_goto_row(dbi_result_t * result,unsigned long long rowidx,unsigned long long currowidx)326 int dbd_goto_row(dbi_result_t *result, unsigned long long rowidx, unsigned long long currowidx) {
327 /* libpq doesn't have to do anything, the row index is specified when
328 * fetching fields */
329 return 1;
330 }
331
dbd_get_socket(dbi_conn_t * conn)332 int dbd_get_socket(dbi_conn_t *conn)
333 {
334 PGconn *pgconn = (PGconn*) conn->connection;
335
336 if(!pgconn) return -1;
337
338 return PQsocket(pgconn);
339 }
340
dbd_get_encoding(dbi_conn_t * conn)341 const char *dbd_get_encoding(dbi_conn_t *conn){
342 const char* my_enc;
343 int n_encoding;
344 const char* encodingopt;
345 char* sql_cmd;
346 dbi_result dbires = NULL;
347 PGconn *pgconn = (PGconn*) conn->connection;
348
349 if(!pgconn) return NULL;
350
351 encodingopt = dbi_conn_get_option(conn, "encoding");
352 if (encodingopt && !strcmp(encodingopt, "auto")) {
353
354 /* this is somewhat murky as the pg_encoding_to_char()
355 function is not declared properly by the PostgreSQL client
356 library headers. This may indicate that it is not supposed
357 to be exported or that it may disappear without a trace
358 eventually. If it breaks, use a query "SHOW CLIENT_ENCODING"
359 instead */
360 my_enc = pg_encoding_to_char(PQclientEncoding(pgconn));
361 /* printf("use PQclientEncoding, auto\n"); */
362 }
363 else if (encodingopt) {
364 my_enc = pg_encoding_to_char(PQclientEncoding(pgconn));
365 /* printf("use PQclientEncoding, %s\n", encodingopt); */
366 }
367 else {
368 asprintf(&sql_cmd, "SELECT encoding FROM pg_database WHERE datname='%s'", conn->current_db);
369
370 dbires = dbi_conn_query(conn, sql_cmd);
371 free(sql_cmd);
372
373 if (dbires && dbi_result_next_row(dbires)) {
374 n_encoding = dbi_result_get_int_idx(dbires, 1);
375 my_enc = pg_encoding_to_char(n_encoding);
376 /* printf("select returned encoding %d<<%s\n", n_encoding, my_enc); */
377 }
378 }
379
380 if (!my_enc) {
381 return NULL;
382 }
383 else {
384 return dbd_encoding_to_iana(my_enc);
385 }
386 }
387
dbd_encoding_to_iana(const char * db_encoding)388 const char* dbd_encoding_to_iana(const char *db_encoding) {
389 int i = 0;
390
391 /* loop over all even entries in hash and compare to penc */
392 while (*pgsql_encoding_hash[i]) {
393 if (!strcmp(pgsql_encoding_hash[i], db_encoding)) {
394 /* return corresponding odd entry */
395 return pgsql_encoding_hash[i+1];
396 }
397 i+=2;
398 }
399
400 /* don't know how to translate, return original encoding */
401 return db_encoding;
402 }
403
dbd_encoding_from_iana(const char * iana_encoding)404 const char* dbd_encoding_from_iana(const char *iana_encoding) {
405 int i = 0;
406
407 /* loop over all odd entries in hash and compare to ienc */
408 while (*pgsql_encoding_hash[i+1]) {
409 if (!strcmp(pgsql_encoding_hash[i+1], iana_encoding)) {
410 /* return corresponding even entry */
411 return pgsql_encoding_hash[i];
412 }
413 i+=2;
414 }
415
416 /* don't know how to translate, return original encoding */
417 return iana_encoding;
418 }
419
dbd_get_engine_version(dbi_conn_t * conn,char * versionstring)420 char *dbd_get_engine_version(dbi_conn_t *conn, char *versionstring) {
421 snprintf(versionstring, VERSIONSTRING_LENGTH, "%d", PQserverVersion((PGconn *)conn->connection));
422 return versionstring;
423 }
424
dbd_list_dbs(dbi_conn_t * conn,const char * pattern)425 dbi_result_t *dbd_list_dbs(dbi_conn_t *conn, const char *pattern) {
426 dbi_result_t *res;
427 char *sql_cmd;
428
429 if (pattern == NULL) {
430 return dbd_query(conn, "SELECT datname FROM pg_database");
431 }
432 else {
433 asprintf(&sql_cmd, "SELECT datname FROM pg_database WHERE datname LIKE '%s'", pattern);
434 res = dbd_query(conn, sql_cmd);
435 free(sql_cmd);
436 return res;
437 }
438 }
439
dbd_list_tables(dbi_conn_t * conn,const char * db,const char * pattern)440 dbi_result_t *dbd_list_tables(dbi_conn_t *conn, const char *db, const char *pattern) {
441 if (db == NULL) {
442 return NULL;
443 }
444
445 if (pattern == NULL) {
446 return (dbi_result_t *)dbi_conn_queryf((dbi_conn)conn, "SELECT relname FROM pg_class WHERE relname !~ '^pg_' AND relkind = 'r' AND relowner = (SELECT datdba FROM pg_database WHERE datname = '%s') ORDER BY relname", db);
447 }
448 else {
449 return (dbi_result_t *)dbi_conn_queryf((dbi_conn)conn, "SELECT relname FROM pg_class WHERE relname !~ '^pg_' AND relname LIKE '%s' AND relkind = 'r' AND relowner = (SELECT datdba FROM pg_database WHERE datname = '%s') ORDER BY relname", pattern, db);
450 }
451 }
452
dbd_quote_string(dbi_driver_t * driver,const char * orig,char * dest)453 size_t dbd_quote_string(dbi_driver_t *driver, const char *orig, char *dest) {
454 /* foo's -> 'foo\'s' */
455 size_t len;
456
457 strcpy(dest, "'");
458 len = PQescapeString(dest+1, orig, strlen(orig));
459 strcat(dest, "'");
460
461 return len+2;
462 }
463
dbd_conn_quote_string(dbi_conn_t * conn,const char * orig,char * dest)464 size_t dbd_conn_quote_string(dbi_conn_t *conn, const char *orig, char *dest) {
465 return dbd_quote_string(conn->driver, orig, dest);
466 }
467
dbd_quote_binary(dbi_conn_t * conn,const unsigned char * orig,size_t from_length,unsigned char ** ptr_dest)468 size_t dbd_quote_binary(dbi_conn_t *conn, const unsigned char* orig, size_t from_length, unsigned char **ptr_dest) {
469 unsigned char *temp = NULL;
470 unsigned char *quoted_temp = NULL;
471 size_t to_length;
472
473 temp = PQescapeByteaConn((PGconn *)conn->connection, orig, from_length, &to_length);
474
475 if (!temp) {
476 return 0;
477 }
478
479 if ((quoted_temp = malloc(to_length+2)) == NULL) {
480 PQfreemem((void *)temp);
481 return 0;
482 }
483
484 strcpy((char *)quoted_temp, "'");
485 strcpy((char *)(quoted_temp+1), (char *)temp);
486 strcat((char *)quoted_temp, "'");
487
488 PQfreemem((void*)temp);
489
490 *ptr_dest = quoted_temp;
491
492 /* to_length already contains one extra byte for the trailing NULL byte */
493 return to_length+1;
494 }
495
dbd_query(dbi_conn_t * conn,const char * statement)496 dbi_result_t *dbd_query(dbi_conn_t *conn, const char *statement) {
497 /* allocate a new dbi_result_t and fill its applicable members:
498 *
499 * result_handle, numrows_matched, and numrows_changed.
500 * everything else will be filled in by DBI */
501
502 dbi_result_t *result;
503 PGresult *res;
504 int resstatus;
505
506 res = PQexec((PGconn *)conn->connection, statement);
507 if (res) resstatus = PQresultStatus(res);
508 if (!res || ((resstatus != PGRES_COMMAND_OK) && (resstatus != PGRES_TUPLES_OK) && (resstatus != PGRES_COPY_OUT) && (resstatus != PGRES_COPY_IN))) {
509 char *base36 = PQresultErrorField(res, PG_DIAG_SQLSTATE);
510 conn->error_number = (! base36) ? 0 : base36decode(base36);
511 PQclear(res);
512 return NULL;
513 }
514
515 conn->error_number = 0;
516
517 result = _dbd_result_create(conn, (void *)res, (unsigned long long)PQntuples(res), (unsigned long long)atoll(PQcmdTuples(res)));
518 _dbd_result_set_numfields(result, (unsigned int)PQnfields((PGresult *)result->result_handle));
519 _get_field_info(result);
520
521 return result;
522 }
523
dbd_query_null(dbi_conn_t * conn,const unsigned char * statement,size_t st_length)524 dbi_result_t *dbd_query_null(dbi_conn_t *conn, const unsigned char *statement, size_t st_length) {
525 return NULL;
526 }
527
dbd_transaction_begin(dbi_conn_t * conn)528 int dbd_transaction_begin(dbi_conn_t *conn) {
529 if (dbd_query(conn, "BEGIN TRANSACTION") == NULL) {
530 return 1;
531 }
532 else {
533 return 0;
534 }
535 }
536
dbd_transaction_commit(dbi_conn_t * conn)537 int dbd_transaction_commit(dbi_conn_t *conn) {
538 if (dbd_query(conn, "COMMIT") == NULL) {
539 return 1;
540 }
541 else {
542 return 0;
543 }
544 }
545
dbd_transaction_rollback(dbi_conn_t * conn)546 int dbd_transaction_rollback(dbi_conn_t *conn) {
547 if (dbd_query(conn, "ROLLBACK") == NULL) {
548 return 1;
549 }
550 else {
551 return 0;
552 }
553 }
554
dbd_savepoint(dbi_conn_t * conn,const char * savepoint)555 int dbd_savepoint(dbi_conn_t *conn, const char *savepoint) {
556 char* query;
557
558 if (!savepoint) {
559 return 1;
560 }
561
562 asprintf(&query, "SAVEPOINT %s", savepoint);
563
564 if (dbd_query(conn, query) == NULL) {
565 free(query);
566 return 1;
567 }
568 else {
569 free(query);
570 return 0;
571 }
572 }
573
dbd_rollback_to_savepoint(dbi_conn_t * conn,const char * savepoint)574 int dbd_rollback_to_savepoint(dbi_conn_t *conn, const char *savepoint) {
575 char* query;
576
577 if (!savepoint) {
578 return 1;
579 }
580
581 asprintf(&query, "ROLLBACK TO SAVEPOINT %s", savepoint);
582
583 if (dbd_query(conn, query) == NULL) {
584 free(query);
585 return 1;
586 }
587 else {
588 free(query);
589 return 0;
590 }
591 }
592
dbd_release_savepoint(dbi_conn_t * conn,const char * savepoint)593 int dbd_release_savepoint(dbi_conn_t *conn, const char *savepoint) {
594 char* query;
595
596 if (!savepoint) {
597 return 1;
598 }
599
600 asprintf(&query, "RELEASE SAVEPOINT %s", savepoint);
601
602 if (dbd_query(conn, query) == NULL) {
603 free(query);
604 return 1;
605 }
606 else {
607 free(query);
608 return 0;
609 }
610 }
611
dbd_select_db(dbi_conn_t * conn,const char * db)612 const char *dbd_select_db(dbi_conn_t *conn, const char *db) {
613 /* postgresql doesn't support switching databases without reconnecting */
614 if (!db || !*db) {
615 return NULL;
616 }
617
618 if (conn->connection) {
619 PQfinish((PGconn *)conn->connection);
620 conn->connection = NULL;
621 }
622
623 if (_dbd_real_connect(conn, db)) {
624 return NULL;
625 }
626
627 return db;
628 }
629
dbd_geterror(dbi_conn_t * conn,int * err_no,char ** errstr)630 int dbd_geterror(dbi_conn_t *conn, int *err_no, char **errstr) {
631 /* put error number into err_no, error string into errstr
632 * return 0 if error, 1 if err_no filled, 2 if errstr filled, 3 if both err_no and errstr filled */
633
634 *err_no = conn->error_number;
635 *errstr = strdup(PQerrorMessage((PGconn *)conn->connection));
636
637 return 3;
638 }
639
dbd_get_seq_last(dbi_conn_t * conn,const char * sequence)640 unsigned long long dbd_get_seq_last(dbi_conn_t *conn, const char *sequence) {
641 unsigned long long seq_last = 0;
642 char *sql_cmd;
643 char *rawdata;
644 dbi_result_t *result;
645
646 asprintf(&sql_cmd, "SELECT currval('%s')", sequence);
647 if (!sql_cmd) return 0;
648 result = dbd_query(conn, sql_cmd);
649 free(sql_cmd);
650
651 if (result) {
652 rawdata = PQgetvalue((PGresult *)result->result_handle, 0, 0);
653 if (rawdata) {
654 seq_last = (unsigned long long)atoll(rawdata);
655 }
656 dbi_result_free((dbi_result)result);
657 }
658
659 return seq_last;
660 }
661
dbd_get_seq_next(dbi_conn_t * conn,const char * sequence)662 unsigned long long dbd_get_seq_next(dbi_conn_t *conn, const char *sequence) {
663 unsigned long long seq_next = 0;
664 char *sql_cmd;
665 char *rawdata;
666 dbi_result_t *result;
667
668 asprintf(&sql_cmd, "SELECT nextval('%s')", sequence);
669 if (!sql_cmd) return 0;
670 result = dbd_query(conn, sql_cmd);
671 free(sql_cmd);
672
673 if (result) {
674 rawdata = PQgetvalue((PGresult *)result->result_handle, 0, 0);
675 if (rawdata) {
676 seq_next = (unsigned long long)atoll(rawdata);
677 }
678 dbi_result_free((dbi_result)result);
679 }
680
681 return seq_next;
682 }
683
dbd_ping(dbi_conn_t * conn)684 int dbd_ping(dbi_conn_t *conn) {
685 PGconn *pgsql = (PGconn *)conn->connection;
686 PGresult *res;
687
688 res = PQexec(pgsql, "SELECT 1");
689 if (res) {
690 PQclear (res);
691 }
692
693 if (PQstatus(pgsql) == CONNECTION_OK) {
694 return 1;
695 }
696
697 PQreset(pgsql); // attempt a reconnection
698
699 if (PQstatus(pgsql) == CONNECTION_OK) {
700 return 1;
701 }
702
703 return 0;
704 }
705
706 /* CORE POSTGRESQL DATA FETCHING STUFF */
707
_translate_postgresql_type(unsigned int oid,unsigned short * type,unsigned int * attribs)708 void _translate_postgresql_type(unsigned int oid, unsigned short *type, unsigned int *attribs) {
709 unsigned int _type = 0;
710 unsigned int _attribs = 0;
711
712 /* fprintf(stderr, "oid went to %d\n", oid); */
713 switch (oid) {
714 case PG_TYPE_CHAR:
715 _type = DBI_TYPE_INTEGER;
716 _attribs |= DBI_INTEGER_SIZE1;
717 break;
718 case PG_TYPE_INT2:
719 _type = DBI_TYPE_INTEGER;
720 _attribs |= DBI_INTEGER_SIZE2;
721 break;
722 case PG_TYPE_INT4:
723 _type = DBI_TYPE_INTEGER;
724 _attribs |= DBI_INTEGER_SIZE4;
725 break;
726 case PG_TYPE_INT8:
727 _type = DBI_TYPE_INTEGER;
728 _attribs |= DBI_INTEGER_SIZE8;
729 break;
730 case PG_TYPE_OID:
731 _type = DBI_TYPE_INTEGER;
732 _attribs |= DBI_INTEGER_SIZE8;
733 _attribs |= DBI_INTEGER_UNSIGNED;
734 break;
735
736 case PG_TYPE_FLOAT4:
737 _type = DBI_TYPE_DECIMAL;
738 _attribs |= DBI_DECIMAL_SIZE4;
739 break;
740 case PG_TYPE_FLOAT8:
741 _type = DBI_TYPE_DECIMAL;
742 _attribs |= DBI_DECIMAL_SIZE8;
743 break;
744
745 case PG_TYPE_DATE:
746 _type = DBI_TYPE_DATETIME;
747 _attribs |= DBI_DATETIME_DATE;
748 break;
749 case PG_TYPE_TIME:
750 case PG_TYPE_TIMETZ:
751 _type = DBI_TYPE_DATETIME;
752 _attribs |= DBI_DATETIME_TIME;
753 break;
754 case PG_TYPE_TIMESTAMP:
755 case PG_TYPE_TIMESTAMPTZ:
756 _type = DBI_TYPE_DATETIME;
757 _attribs |= DBI_DATETIME_DATE;
758 _attribs |= DBI_DATETIME_TIME;
759 break;
760
761 case PG_TYPE_NAME:
762 case PG_TYPE_TEXT:
763 case PG_TYPE_CHAR2:
764 case PG_TYPE_CHAR4:
765 case PG_TYPE_CHAR8:
766 case PG_TYPE_BPCHAR:
767 case PG_TYPE_VARCHAR:
768 _type = DBI_TYPE_STRING;
769 break;
770
771 case PG_TYPE_BYTEA:
772 _type = DBI_TYPE_BINARY;
773 break;
774
775 default:
776 _type = DBI_TYPE_STRING;
777 break;
778 }
779
780 *type = _type;
781 *attribs = _attribs;
782 }
783
_get_field_info(dbi_result_t * result)784 void _get_field_info(dbi_result_t *result) {
785 unsigned int idx = 0;
786 unsigned int pgOID = 0;
787 char *fieldname;
788 unsigned short fieldtype;
789 unsigned int fieldattribs;
790
791 while (idx < result->numfields) {
792 pgOID = PQftype((PGresult *)result->result_handle, idx);
793 fieldname = PQfname((PGresult *)result->result_handle, idx);
794 _translate_postgresql_type(pgOID, &fieldtype, &fieldattribs);
795 _dbd_result_add_field(result, idx, fieldname, fieldtype, fieldattribs);
796 idx++;
797 }
798 }
799
_get_row_data(dbi_result_t * result,dbi_row_t * row,unsigned long long rowidx)800 void _get_row_data(dbi_result_t *result, dbi_row_t *row, unsigned long long rowidx) {
801 unsigned int curfield = 0;
802 char *raw = NULL;
803 size_t strsize = 0;
804 unsigned int sizeattrib;
805 dbi_data_t *data;
806 unsigned char *temp = NULL;
807 size_t unquoted_length;
808
809
810 while (curfield < result->numfields) {
811 raw = PQgetvalue((PGresult *)result->result_handle, rowidx, curfield);
812 data = &row->field_values[curfield];
813
814 row->field_sizes[curfield] = 0;
815 /* will be set to strlen later on for strings */
816
817 if (PQgetisnull((PGresult *)result->result_handle, rowidx, curfield) == 1) {
818 _set_field_flag( row, curfield, DBI_VALUE_NULL, 1);
819 curfield++;
820 continue;
821 }
822
823 switch (result->field_types[curfield]) {
824 case DBI_TYPE_INTEGER:
825 switch (result->field_attribs[curfield] & DBI_INTEGER_SIZEMASK) {
826 case DBI_INTEGER_SIZE1:
827 data->d_char = (char) atol(raw); break;
828 case DBI_INTEGER_SIZE2:
829 data->d_short = (short) atol(raw); break;
830 case DBI_INTEGER_SIZE3:
831 case DBI_INTEGER_SIZE4:
832 data->d_long = (int) atol(raw); break;
833 case DBI_INTEGER_SIZE8:
834 data->d_longlong = (long long) atoll(raw); break; /* hah, wonder if that'll work */
835 default:
836 break;
837 }
838 break;
839 case DBI_TYPE_DECIMAL:
840 switch (result->field_attribs[curfield] & DBI_DECIMAL_SIZEMASK) {
841 case DBI_DECIMAL_SIZE4:
842 data->d_float = (float) strtod(raw, NULL); break;
843 case DBI_DECIMAL_SIZE8:
844 data->d_double = (double) strtod(raw, NULL); break;
845 default:
846 break;
847 }
848 break;
849 case DBI_TYPE_STRING:
850 strsize = (size_t)PQgetlength((PGresult *)result->result_handle, rowidx, curfield);
851 data->d_string = strdup(raw);
852 row->field_sizes[curfield] = strsize;
853 break;
854 case DBI_TYPE_BINARY:
855 strsize = (size_t)PQgetlength((PGresult *)result->result_handle, rowidx, curfield);
856 if (strsize > 2
857 && raw[0] == '\\'
858 && raw[1] == 'x') {
859 /* hex format */
860 /* row->field_sizes[curfield] = strsize; */
861 /* data->d_string = malloc(strsize); */
862 /* memcpy(data->d_string, raw, strsize); */
863 temp = PQunescapeBytea((const unsigned char *)_unescape_hex_binary(raw, strsize, &unquoted_length), &(row->field_sizes[curfield]));
864 if ((data->d_string = malloc(row->field_sizes[curfield])) == NULL) {
865 PQfreemem(temp);
866 break;
867 }
868 memmove(data->d_string, temp, row->field_sizes[curfield]);
869 PQfreemem(temp);
870 }
871 else {
872 temp = PQunescapeBytea((const unsigned char *)raw, &unquoted_length);
873 if ((data->d_string = malloc(unquoted_length)) == NULL) {
874 PQfreemem(temp);
875 break;
876 }
877 memmove(data->d_string, temp, unquoted_length);
878 PQfreemem(temp);
879 row->field_sizes[curfield] = unquoted_length;
880 }
881 break;
882
883 case DBI_TYPE_DATETIME:
884 sizeattrib = result->field_attribs[curfield] & (DBI_DATETIME_DATE|DBI_DATETIME_TIME);
885 data->d_datetime = _dbd_parse_datetime(raw, sizeattrib);
886 break;
887
888 default:
889 break;
890 }
891
892 curfield++;
893 }
894 }
895
896 /* this function reverts the changes done by PQescapeByteaConn to a
897 binary string. libpq does not provide such a function. Returns the
898 result as a malloc'ed string which must be freed by the caller. The
899 output string is in the BYTEA escape format with single backslashes
900 and single single quotes. It must be post-processed by
901 PQunescapeBytea() to obtain true binary data
902 */
_unescape_hex_binary(char * raw,size_t in_len,size_t * out_len)903 char *_unescape_hex_binary(char* raw, size_t in_len, size_t* out_len) {
904 size_t i;
905 int in_pair = 0;
906 int last_nibble = 0;
907 char *outstring;
908 char *end_of_outstring;
909 int have_backslash = 0;
910 int have_singlequote = 0;
911 char tempchar;
912
913 /* algorithm borrowed and modified from:
914 http://pqxx.org/development/libpqxx/browser/trunk/src/binarystring.cxx
915 */
916
917 if ((outstring = malloc(((in_len-2)/2)+1)) == NULL) {
918 return NULL;
919 }
920 end_of_outstring = outstring;
921
922 for (i=2; i<in_len; ++i) {
923 const unsigned char c = raw[i];
924 if (isspace(c)) {
925 if (in_pair) {
926 /* "Escaped binary data is malformed." */
927 }
928 }
929 else if (!isxdigit(c)) {
930 /* "Escaped binary data contains invalid characters." */
931 }
932 else {
933 const int nibble = (isdigit(c) ? _digit_to_number(c) : (10 + tolower(c) - 'a'));
934 if (in_pair) {
935 tempchar = (char)((last_nibble<<4) | nibble);
936 if (tempchar == '\\' && have_backslash) {
937 /* skip second consecutive backslash */
938 have_backslash = 0;
939 }
940 else if (tempchar == '\'' && have_singlequote) {
941 /* skip second consecutive single quote */
942 have_singlequote = 0;
943 }
944 else {
945 if (tempchar == '\\') {
946 have_backslash = 1;
947 }
948 else if (tempchar == '\'') {
949 have_singlequote = 1;
950 }
951 else {
952 have_backslash = 0;
953 have_singlequote = 0;
954 }
955 *end_of_outstring = tempchar;
956 end_of_outstring++;
957 }
958 }
959 else {
960 last_nibble = nibble;
961 }
962 in_pair = !in_pair;
963 }
964 }
965 *end_of_outstring = '\0';
966 *out_len = end_of_outstring-outstring;
967 return outstring;
968 }
969
970 /* converts an ASCII character code to an integer */
_digit_to_number(const char c)971 int _digit_to_number(const char c) {
972 return (int)c - (int)'0';
973 }
974