2
* libdbi - database independent abstraction layer for C.
3
* Copyright (C) 2001-2002, David Parker and Mark Tobenkin.
4
* http://libdbi.sourceforge.net
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.
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.
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
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
24
* $Id: dbd_pgsql.c,v 1.38 2004/01/04 00:43:20 mhoenicka Exp $
31
#define _GNU_SOURCE /* we need asprintf */
34
long long atoll(const char *str);
38
long long strtoll(const char *nptr, char **endptr, int base);
46
#include <dbi/dbi-dev.h>
50
#include "dbd_pgsql.h"
52
static const dbi_info_t driver_info = {
54
"PostgreSQL database support (using libpq)",
55
"David A. Parker <david@neongoat.com>",
56
"http://libdbi-drivers.sourceforge.net",
57
"dbd_pgsql v" VERSION,
61
static const char *custom_functions[] = {NULL}; // TODO
62
static const char *reserved_words[] = PGSQL_RESERVED_WORDS;
64
/* encoding strings, array is terminated by empty string */
65
static const char pgsql_encoding_hash[][16] = {
67
"SQL_ASCII", "US-ASCII",
71
"LATIN1", "ISO-8859-1",
72
"LATIN2", "ISO-8859-2",
73
"LATIN3", "ISO-8859-3",
74
"LATIN4", "ISO-8859-4",
75
"LATIN5", "ISO-8859-9",
76
"LATIN6", "ISO-8859-10",
77
"LATIN7", "ISO-8859-13",
78
"LATIN8", "ISO-8859-14",
79
"LATIN9", "ISO-8859-15",
80
"LATIN10", "ISO-8859-16",
81
"ISO-8859-5", "ISO-8859-5",
82
"ISO-8859-6", "ISO-8859-6",
83
"ISO-8859-7", "ISO-8859-7",
84
"ISO-8859-8", "ISO-8859-8",
86
"WIN", "windows-1251",
91
/* forward declarations of internal functions */
92
void _translate_postgresql_type(unsigned int oid, unsigned short *type, unsigned int *attribs);
93
void _get_field_info(dbi_result_t *result);
94
void _get_row_data(dbi_result_t *result, dbi_row_t *row, unsigned long long rowidx);
96
/* this function is available through the PostgreSQL client library, but it
97
is not declared in any of their headers. I hope this won't break anything */
98
char *pg_encoding_to_char(int encoding_id);
101
/* real code starts here */
102
void dbd_register_driver(const dbi_info_t **_driver_info, const char ***_custom_functions, const char ***_reserved_words) {
103
/* this is the first function called after the driver module is loaded into memory */
104
*_driver_info = &driver_info;
105
*_custom_functions = custom_functions;
106
*_reserved_words = reserved_words;
109
int dbd_initialize(dbi_driver_t *driver) {
110
/* perform any database-specific server initialization.
111
* this is called right after dbd_register_driver().
112
* return -1 on error, 0 on success. if -1 is returned, the driver will not
113
* be added to the list of available drivers. */
118
int dbd_connect(dbi_conn_t *conn) {
119
const char *host = dbi_conn_get_option(conn, "host");
120
const char *username = dbi_conn_get_option(conn, "username");
121
const char *password = dbi_conn_get_option(conn, "password");
122
const char *dbname = dbi_conn_get_option(conn, "dbname");
123
int port = dbi_conn_get_option_numeric(conn, "port");
125
/* pgsql specific options */
126
const char *options = dbi_conn_get_option(conn, "pgsql_options");
127
const char *tty = dbi_conn_get_option(conn, "pgsql_tty");
132
char *conninfo_kludge;
134
if (port > 0) asprintf(&port_str, "%d", port);
135
else port_str = NULL;
137
/* YUCK YUCK YUCK YUCK YUCK. stupid libpq. */
138
if (host && port_str) asprintf(&conninfo_kludge, "host='%s' port='%s'", host, port_str);
139
else if (host) asprintf(&conninfo_kludge, "host='%s'", host);
140
else if (port_str) asprintf(&conninfo_kludge, "port='%s'", port_str);
141
else conninfo_kludge = NULL;
143
if (port_str) free(port_str);
145
asprintf(&conninfo, "%s dbname='%s' user='%s' password='%s' options='%s' tty='%s'",
146
conninfo_kludge ? conninfo_kludge : "", /* if we pass a NULL directly to the %s it will show up as "(null)" */
147
dbname ? dbname : "",
148
username ? username : "",
149
password ? password : "",
150
options ? options : "",
153
if (conninfo_kludge) free(conninfo_kludge);
155
pgconn = PQconnectdb(conninfo);
156
if (conninfo) free(conninfo);
157
if (!pgconn) return -1;
159
if (PQstatus(pgconn) == CONNECTION_BAD) {
160
conn->connection = (void *)pgconn; // still need this set so _error_handler can grab information
161
_error_handler(conn, DBI_ERROR_DBD);
163
conn->connection = NULL; // pgconn no longer valid
167
conn->connection = (void *)pgconn;
168
if (dbname) conn->current_db = strdup(dbname);
174
int dbd_disconnect(dbi_conn_t *conn) {
175
if (conn->connection) PQfinish((PGconn *)conn->connection);
179
int dbd_fetch_row(dbi_result_t *result, unsigned long long rownum) {
180
dbi_row_t *row = NULL;
182
if (result->result_state == NOTHING_RETURNED) return -1;
184
if (result->result_state == ROWS_RETURNED) {
186
row = _dbd_row_allocate(result->numfields);
187
_get_row_data(result, row, rownum);
188
_dbd_row_finalize(result, row, rownum);
191
return 1; /* 0 on error, 1 on successful fetchrow */
194
int dbd_free_query(dbi_result_t *result) {
195
PQclear((PGresult *)result->result_handle);
199
int dbd_goto_row(dbi_result_t *result, unsigned long long row) {
200
/* libpq doesn't have to do anything, the row index is specified when
205
int dbd_get_socket(dbi_conn_t *conn)
207
PGconn *pgconn = (PGconn*) conn->connection;
209
if(!pgconn) return -1;
211
return PQsocket(pgconn);
214
const char *dbd_get_encoding(dbi_conn_t *conn){
217
PGconn *pgconn = (PGconn*) conn->connection;
219
if(!pgconn) return NULL;
221
/* this is somewhat murky as the pg_encoding_to_char()
222
function is not declared properly by the PostgreSQL client
223
library headers. This may indicate that it is not supposed
224
to be exported or that it may disappear without a trace
225
eventually. If it breaks, use a query "SHOW CLIENT_ENCODING"
227
my_enc = pg_encoding_to_char(PQclientEncoding(pgconn));
235
/* loop over all even entries in hash and compare to my_enc */
236
while (*pgsql_encoding_hash[i]) {
237
if (!strcmp(pgsql_encoding_hash[i], my_enc)) {
238
/* return corresponding odd entry */
239
return pgsql_encoding_hash[i+1];
244
/* don't know how to translate, return original string */
249
dbi_result_t *dbd_list_dbs(dbi_conn_t *conn, const char *pattern) {
253
if (pattern == NULL) {
254
return dbd_query(conn, "SELECT datname FROM pg_database");
257
asprintf(&sql_cmd, "SELECT datname FROM pg_database WHERE datname LIKE '%s'", pattern);
258
res = dbd_query(conn, sql_cmd);
264
dbi_result_t *dbd_list_tables(dbi_conn_t *conn, const char *db, const char *pattern) {
269
if (pattern == NULL) {
270
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);
273
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);
277
int dbd_quote_string(dbi_driver_t *driver, const char *orig, char *dest) {
278
/* foo's -> 'foo\'s' */
282
len = PQescapeString(dest+1, orig, strlen(orig));
288
dbi_result_t *dbd_query(dbi_conn_t *conn, const char *statement) {
289
/* allocate a new dbi_result_t and fill its applicable members:
291
* result_handle, numrows_matched, and numrows_changed.
292
* everything else will be filled in by DBI */
294
dbi_result_t *result;
298
res = PQexec((PGconn *)conn->connection, statement);
299
if (res) resstatus = PQresultStatus(res);
300
if (!res || ((resstatus != PGRES_COMMAND_OK) && (resstatus != PGRES_TUPLES_OK))) {
305
result = _dbd_result_create(conn, (void *)res, PQntuples(res), atol(PQcmdTuples(res)));
306
_dbd_result_set_numfields(result, PQnfields((PGresult *)result->result_handle));
307
_get_field_info(result);
312
dbi_result_t *dbd_query_null(dbi_conn_t *conn, const unsigned char *statement, unsigned long st_length) {
316
char *dbd_select_db(dbi_conn_t *conn, const char *db) {
317
/* postgresql doesn't support switching databases without reconnecting */
322
if (conn->connection) {
323
PQfinish((PGconn *)conn->connection);
324
conn->connection = NULL;
327
dbi_conn_set_option(conn, "dbname", db);
328
if (dbd_connect(conn)) {
335
int dbd_geterror(dbi_conn_t *conn, int *errno, char **errstr) {
336
/* put error number into errno, error string into errstr
337
* return 0 if error, 1 if errno filled, 2 if errstr filled, 3 if both errno and errstr filled */
340
*errstr = strdup(PQerrorMessage((PGconn *)conn->connection));
345
unsigned long long dbd_get_seq_last(dbi_conn_t *conn, const char *sequence) {
346
unsigned long long seq_last = 0;
349
dbi_result_t *result;
351
asprintf(&sql_cmd, "SELECT currval('%s')", sequence);
352
if (!sql_cmd) return 0;
353
result = dbd_query(conn, sql_cmd);
357
rawdata = PQgetvalue((PGresult *)result->result_handle, 0, 0);
359
seq_last = atoll(rawdata);
361
dbi_result_free((dbi_result)result);
367
unsigned long long dbd_get_seq_next(dbi_conn_t *conn, const char *sequence) {
368
unsigned long long seq_next = 0;
371
dbi_result_t *result;
373
asprintf(&sql_cmd, "SELECT nextval('%s')", sequence);
374
if (!sql_cmd) return 0;
375
result = dbd_query(conn, sql_cmd);
379
rawdata = PQgetvalue((PGresult *)result->result_handle, 0, 0);
381
seq_next = atoll(rawdata);
383
dbi_result_free((dbi_result)result);
389
int dbd_ping(dbi_conn_t *conn) {
390
PGconn *pgsql = (PGconn *)conn->connection;
392
PQexec(pgsql, "SELECT 1");
394
if (PQstatus(pgsql) == CONNECTION_OK) {
398
PQreset(pgsql); // attempt a reconnection
400
if (PQstatus(pgsql) == CONNECTION_OK) {
407
/* CORE POSTGRESQL DATA FETCHING STUFF */
409
void _translate_postgresql_type(unsigned int oid, unsigned short *type, unsigned int *attribs) {
410
unsigned int _type = 0;
411
unsigned int _attribs = 0;
415
_type = DBI_TYPE_INTEGER;
416
_attribs |= DBI_INTEGER_SIZE1;
419
_type = DBI_TYPE_INTEGER;
420
_attribs |= DBI_INTEGER_SIZE2;
423
_type = DBI_TYPE_INTEGER;
424
_attribs |= DBI_INTEGER_SIZE4;
427
_type = DBI_TYPE_INTEGER;
428
_attribs |= DBI_INTEGER_SIZE8;
431
_type = DBI_TYPE_INTEGER;
432
_attribs |= DBI_INTEGER_SIZE8;
433
_attribs |= DBI_INTEGER_UNSIGNED;
437
_type = DBI_TYPE_DECIMAL;
438
_attribs |= DBI_DECIMAL_SIZE4;
441
_type = DBI_TYPE_DECIMAL;
442
_attribs |= DBI_DECIMAL_SIZE8;
445
_type = DBI_TYPE_DATETIME;
446
_attribs |= DBI_DATETIME_DATE;
449
_type = DBI_TYPE_DATETIME;
450
_attribs |= DBI_DATETIME_TIME;
452
case PG_TYPE_DATETIME:
453
case PG_TYPE_TIMESTAMP:
454
_type = DBI_TYPE_DATETIME;
455
_attribs |= DBI_DATETIME_DATE;
456
_attribs |= DBI_DATETIME_TIME;
464
case PG_TYPE_VARCHAR:
465
_type = DBI_TYPE_STRING;
469
_type = DBI_TYPE_BINARY;
473
_type = DBI_TYPE_STRING;
481
void _get_field_info(dbi_result_t *result) {
482
unsigned int idx = 0;
483
unsigned int pgOID = 0;
485
unsigned short fieldtype;
486
unsigned int fieldattribs;
488
while (idx < result->numfields) {
489
pgOID = PQftype((PGresult *)result->result_handle, idx);
490
fieldname = PQfname((PGresult *)result->result_handle, idx);
491
_translate_postgresql_type(pgOID, &fieldtype, &fieldattribs);
492
_dbd_result_add_field(result, idx, fieldname, fieldtype, fieldattribs);
497
void _get_row_data(dbi_result_t *result, dbi_row_t *row, unsigned long long rowidx) {
500
unsigned long long strsize = 0;
501
unsigned long sizeattrib;
504
while (curfield < result->numfields) {
505
raw = PQgetvalue((PGresult *)result->result_handle, rowidx, curfield);
506
strsize = (unsigned long long) PQfmod((PGresult *)result->result_handle, curfield);
507
data = &row->field_values[curfield];
509
row->field_sizes[curfield] = 0;
510
/* will be set to strlen later on for strings */
512
if (PQgetisnull((PGresult *)result->result_handle, rowidx, curfield) == 1) {
517
switch (result->field_types[curfield]) {
518
case DBI_TYPE_INTEGER:
519
sizeattrib = _isolate_attrib(result->field_attribs[curfield], DBI_INTEGER_SIZE1, DBI_INTEGER_SIZE8);
520
switch (sizeattrib) {
521
case DBI_INTEGER_SIZE1:
522
data->d_char = (char) atol(raw); break;
523
case DBI_INTEGER_SIZE2:
524
data->d_short = (short) atol(raw); break;
525
case DBI_INTEGER_SIZE3:
526
case DBI_INTEGER_SIZE4:
527
data->d_long = (long) atol(raw); break;
528
case DBI_INTEGER_SIZE8:
529
data->d_longlong = (long long) atoll(raw); break; /* hah, wonder if that'll work */
534
case DBI_TYPE_DECIMAL:
535
sizeattrib = _isolate_attrib(result->field_attribs[curfield], DBI_DECIMAL_SIZE4, DBI_DECIMAL_SIZE8);
536
switch (sizeattrib) {
537
case DBI_DECIMAL_SIZE4:
538
data->d_float = (float) strtod(raw, NULL); break;
539
case DBI_DECIMAL_SIZE8:
540
data->d_double = (double) strtod(raw, NULL); break;
545
case DBI_TYPE_STRING:
546
data->d_string = strdup(raw);
547
row->field_sizes[curfield] = strsize;
549
case DBI_TYPE_BINARY:
550
row->field_sizes[curfield] = strsize;
551
data->d_string = malloc(strsize);
552
memcpy(data->d_string, raw, strsize);
555
case DBI_TYPE_DATETIME:
556
sizeattrib = _isolate_attrib(result->field_attribs[curfield], DBI_DATETIME_DATE, DBI_DATETIME_TIME);
557
data->d_datetime = _dbd_parse_datetime(raw, sizeattrib);