1/*
2  +----------------------------------------------------------------------+
3  | PHP Version 5                                                        |
4  +----------------------------------------------------------------------+
5  | Copyright (c) 1997-2015 The PHP Group                                |
6  +----------------------------------------------------------------------+
7  | This source file is subject to version 3.01 of the PHP license,      |
8  | that is bundled with this package in the file LICENSE, and is        |
9  | available through the world-wide-web at the following url:           |
10  | http://www.php.net/license/3_01.txt                                  |
11  | If you did not receive a copy of the PHP license and are unable to   |
12  | obtain it through the world-wide-web, please send a note to          |
13  | license@php.net so we can mail you a copy immediately.               |
14  +----------------------------------------------------------------------+
15  | Authors: Edin Kadribasic <edink@emini.dk>                            |
16  |          Ilia Alshanestsky <ilia@prohost.org>                        |
17  |          Wez Furlong <wez@php.net>                                   |
18  +----------------------------------------------------------------------+
19*/
20
21/* $Id$ */
22
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
26
27#include "php.h"
28#include "php_ini.h"
29#include "ext/standard/info.h"
30#include "main/php_network.h"
31#include "pdo/php_pdo.h"
32#include "pdo/php_pdo_driver.h"
33#include "pdo/php_pdo_error.h"
34#include "ext/standard/file.h"
35
36#undef PACKAGE_BUGREPORT
37#undef PACKAGE_NAME
38#undef PACKAGE_STRING
39#undef PACKAGE_TARNAME
40#undef PACKAGE_VERSION
41#include "pg_config.h" /* needed for PG_VERSION */
42#include "php_pdo_pgsql.h"
43#include "php_pdo_pgsql_int.h"
44#include "zend_exceptions.h"
45
46static char * _pdo_pgsql_trim_message(const char *message, int persistent)
47{
48    register int i = strlen(message)-1;
49    char *tmp;
50
51    if (i>1 && (message[i-1] == '\r' || message[i-1] == '\n') && message[i] == '.') {
52        --i;
53    }
54    while (i>0 && (message[i] == '\r' || message[i] == '\n')) {
55        --i;
56    }
57    ++i;
58    tmp = pemalloc(i + 1, persistent);
59    memcpy(tmp, message, i);
60    tmp[i] = '\0';
61
62    return tmp;
63}
64
65int _pdo_pgsql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const char *sqlstate, const char *msg, const char *file, int line TSRMLS_DC) /* {{{ */
66{
67    pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
68    pdo_error_type *pdo_err = stmt ? &stmt->error_code : &dbh->error_code;
69    pdo_pgsql_error_info *einfo = &H->einfo;
70    char *errmsg = PQerrorMessage(H->server);
71
72    einfo->errcode = errcode;
73    einfo->file = file;
74    einfo->line = line;
75
76    if (einfo->errmsg) {
77        pefree(einfo->errmsg, dbh->is_persistent);
78        einfo->errmsg = NULL;
79    }
80
81    if (sqlstate == NULL || strlen(sqlstate) >= sizeof(pdo_error_type)) {
82        strcpy(*pdo_err, "HY000");
83    }
84    else {
85        strcpy(*pdo_err, sqlstate);
86    }
87
88    if (msg) {
89        einfo->errmsg = estrdup(msg);
90    }
91    else if (errmsg) {
92        einfo->errmsg = _pdo_pgsql_trim_message(errmsg, dbh->is_persistent);
93    }
94
95    if (!dbh->methods) {
96        zend_throw_exception_ex(php_pdo_get_exception(), einfo->errcode TSRMLS_CC, "SQLSTATE[%s] [%d] %s",
97                *pdo_err, einfo->errcode, einfo->errmsg);
98    }
99
100    return errcode;
101}
102/* }}} */
103
104static void _pdo_pgsql_notice(pdo_dbh_t *dbh, const char *message) /* {{{ */
105{
106/*  pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; */
107}
108/* }}} */
109
110static int pdo_pgsql_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info TSRMLS_DC) /* {{{ */
111{
112    pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
113    pdo_pgsql_error_info *einfo = &H->einfo;
114
115    if (einfo->errcode) {
116        add_next_index_long(info, einfo->errcode);
117        add_next_index_string(info, einfo->errmsg, 1);
118    }
119
120    return 1;
121}
122/* }}} */
123
124/* {{{ pdo_pgsql_create_lob_stream */
125static size_t pgsql_lob_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
126{
127    struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;
128    return lo_write(self->conn, self->lfd, (char*)buf, count);
129}
130
131static size_t pgsql_lob_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
132{
133    struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;
134    return lo_read(self->conn, self->lfd, buf, count);
135}
136
137static int pgsql_lob_close(php_stream *stream, int close_handle TSRMLS_DC)
138{
139    struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;
140    pdo_dbh_t *dbh = self->dbh;
141
142    if (close_handle) {
143        lo_close(self->conn, self->lfd);
144    }
145    efree(self);
146    php_pdo_dbh_delref(dbh TSRMLS_CC);
147    return 0;
148}
149
150static int pgsql_lob_flush(php_stream *stream TSRMLS_DC)
151{
152    return 0;
153}
154
155static int pgsql_lob_seek(php_stream *stream, off_t offset, int whence,
156        off_t *newoffset TSRMLS_DC)
157{
158    struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;
159    int pos = lo_lseek(self->conn, self->lfd, offset, whence);
160    *newoffset = pos;
161    return pos >= 0 ? 0 : -1;
162}
163
164php_stream_ops pdo_pgsql_lob_stream_ops = {
165    pgsql_lob_write,
166    pgsql_lob_read,
167    pgsql_lob_close,
168    pgsql_lob_flush,
169    "pdo_pgsql lob stream",
170    pgsql_lob_seek,
171    NULL,
172    NULL,
173    NULL
174};
175
176php_stream *pdo_pgsql_create_lob_stream(pdo_dbh_t *dbh, int lfd, Oid oid TSRMLS_DC)
177{
178    php_stream *stm;
179    struct pdo_pgsql_lob_self *self = ecalloc(1, sizeof(*self));
180    pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
181
182    self->dbh = dbh;
183    self->lfd = lfd;
184    self->oid = oid;
185    self->conn = H->server;
186
187    stm = php_stream_alloc(&pdo_pgsql_lob_stream_ops, self, 0, "r+b");
188
189    if (stm) {
190        php_pdo_dbh_addref(dbh TSRMLS_CC);
191        return stm;
192    }
193
194    efree(self);
195    return NULL;
196}
197/* }}} */
198
199static int pgsql_handle_closer(pdo_dbh_t *dbh TSRMLS_DC) /* {{{ */
200{
201    pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
202    if (H) {
203        if (H->server) {
204            PQfinish(H->server);
205            H->server = NULL;
206        }
207        if (H->einfo.errmsg) {
208            pefree(H->einfo.errmsg, dbh->is_persistent);
209            H->einfo.errmsg = NULL;
210        }
211        pefree(H, dbh->is_persistent);
212        dbh->driver_data = NULL;
213    }
214    return 0;
215}
216/* }}} */
217
218static int pgsql_handle_preparer(pdo_dbh_t *dbh, const char *sql, long sql_len, pdo_stmt_t *stmt, zval *driver_options TSRMLS_DC)
219{
220    pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
221    pdo_pgsql_stmt *S = ecalloc(1, sizeof(pdo_pgsql_stmt));
222    int scrollable;
223    int ret;
224    char *nsql = NULL;
225    int nsql_len = 0;
226    int emulate = 0;
227    int execute_only = 0;
228
229    S->H = H;
230    stmt->driver_data = S;
231    stmt->methods = &pgsql_stmt_methods;
232
233    scrollable = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR,
234        PDO_CURSOR_FWDONLY TSRMLS_CC) == PDO_CURSOR_SCROLL;
235
236    if (scrollable) {
237        if (S->cursor_name) {
238            efree(S->cursor_name);
239        }
240        spprintf(&S->cursor_name, 0, "pdo_crsr_%08x", ++H->stmt_counter);
241        emulate = 1;
242    } else if (driver_options) {
243        if (pdo_attr_lval(driver_options, PDO_PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT, H->disable_native_prepares TSRMLS_CC) == 1) {
244            php_error_docref(NULL TSRMLS_CC, E_DEPRECATED, "PDO::PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT is deprecated, use PDO::ATTR_EMULATE_PREPARES instead");
245            emulate = 1;
246        }
247        if (pdo_attr_lval(driver_options, PDO_ATTR_EMULATE_PREPARES, H->emulate_prepares TSRMLS_CC) == 1) {
248            emulate = 1;
249        }
250        if (pdo_attr_lval(driver_options, PDO_PGSQL_ATTR_DISABLE_PREPARES, H->disable_prepares TSRMLS_CC) == 1) {
251            execute_only = 1;
252        }
253    } else {
254        emulate = H->disable_native_prepares || H->emulate_prepares;
255        execute_only = H->disable_prepares;
256    }
257
258    if (!emulate && PQprotocolVersion(H->server) > 2) {
259        stmt->supports_placeholders = PDO_PLACEHOLDER_NAMED;
260        stmt->named_rewrite_template = "$%d";
261        ret = pdo_parse_params(stmt, (char*)sql, sql_len, &nsql, &nsql_len TSRMLS_CC);
262
263        if (ret == 1) {
264            /* query was re-written */
265            sql = nsql;
266        } else if (ret == -1) {
267            /* couldn't grok it */
268            strcpy(dbh->error_code, stmt->error_code);
269            return 0;
270        }
271
272        if (!execute_only) {
273            /* prepared query: set the query name and defer the
274               actual prepare until the first execute call */
275            spprintf(&S->stmt_name, 0, "pdo_stmt_%08x", ++H->stmt_counter);
276        }
277
278        if (nsql) {
279            S->query = nsql;
280        } else {
281            S->query = estrdup(sql);
282        }
283
284        return 1;
285    }
286
287    stmt->supports_placeholders = PDO_PLACEHOLDER_NONE;
288    return 1;
289}
290
291static long pgsql_handle_doer(pdo_dbh_t *dbh, const char *sql, long sql_len TSRMLS_DC)
292{
293    pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
294    PGresult *res;
295    long ret = 1;
296    ExecStatusType qs;
297
298    if (!(res = PQexec(H->server, sql))) {
299        /* fatal error */
300        pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
301        return -1;
302    }
303    qs = PQresultStatus(res);
304    if (qs != PGRES_COMMAND_OK && qs != PGRES_TUPLES_OK) {
305        pdo_pgsql_error(dbh, qs, pdo_pgsql_sqlstate(res));
306        PQclear(res);
307        return -1;
308    }
309    H->pgoid = PQoidValue(res);
310    ret = (qs == PGRES_COMMAND_OK) ? atol(PQcmdTuples(res)) : 0L;
311    PQclear(res);
312
313    return ret;
314}
315
316static int pgsql_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, int unquotedlen, char **quoted, int *quotedlen, enum pdo_param_type paramtype TSRMLS_DC)
317{
318    unsigned char *escaped;
319    pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
320    size_t tmp_len;
321
322    switch (paramtype) {
323        case PDO_PARAM_LOB:
324            /* escapedlen returned by PQescapeBytea() accounts for trailing 0 */
325            escaped = PQescapeByteaConn(H->server, (unsigned char *)unquoted, (size_t)unquotedlen, &tmp_len);
326            *quotedlen = (int)tmp_len + 1;
327            *quoted = emalloc(*quotedlen + 1);
328            memcpy((*quoted)+1, escaped, *quotedlen-2);
329            (*quoted)[0] = '\'';
330            (*quoted)[*quotedlen-1] = '\'';
331            (*quoted)[*quotedlen] = '\0';
332            PQfreemem(escaped);
333            break;
334        default:
335            *quoted = safe_emalloc(2, unquotedlen, 3);
336            (*quoted)[0] = '\'';
337            *quotedlen = PQescapeStringConn(H->server, *quoted + 1, unquoted, (size_t)unquotedlen, NULL);
338            (*quoted)[*quotedlen + 1] = '\'';
339            (*quoted)[*quotedlen + 2] = '\0';
340            *quotedlen += 2;
341    }
342    return 1;
343}
344
345static char *pdo_pgsql_last_insert_id(pdo_dbh_t *dbh, const char *name, unsigned int *len TSRMLS_DC)
346{
347    pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
348    char *id = NULL;
349
350    if (name == NULL) {
351        if (H->pgoid == InvalidOid) {
352            return NULL;
353        }
354        *len = spprintf(&id, 0, "%ld", (long) H->pgoid);
355    } else {
356        PGresult *res;
357        ExecStatusType status;
358        const char *q[1];
359        q[0] = name;
360        res = PQexecParams(H->server, "SELECT CURRVAL($1)", 1, NULL, q, NULL, NULL, 0);
361        status = PQresultStatus(res);
362
363        if (res && (status == PGRES_TUPLES_OK)) {
364            id = estrdup((char *)PQgetvalue(res, 0, 0));
365            *len = PQgetlength(res, 0, 0);
366        } else {
367            pdo_pgsql_error(dbh, status, pdo_pgsql_sqlstate(res));
368        }
369
370        if (res) {
371            PQclear(res);
372        }
373    }
374    return id;
375}
376
377static int pdo_pgsql_get_attribute(pdo_dbh_t *dbh, long attr, zval *return_value TSRMLS_DC)
378{
379    pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
380
381    switch (attr) {
382        case PDO_ATTR_EMULATE_PREPARES:
383            ZVAL_BOOL(return_value, H->emulate_prepares);
384            break;
385
386        case PDO_PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT:
387            php_error_docref(NULL TSRMLS_CC, E_DEPRECATED, "PDO::PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT is deprecated, use PDO::ATTR_EMULATE_PREPARES instead");
388            ZVAL_BOOL(return_value, H->disable_native_prepares);
389            break;
390
391        case PDO_PGSQL_ATTR_DISABLE_PREPARES:
392            ZVAL_BOOL(return_value, H->disable_prepares);
393            break;
394
395        case PDO_ATTR_CLIENT_VERSION:
396            ZVAL_STRING(return_value, PG_VERSION, 1);
397            break;
398
399        case PDO_ATTR_SERVER_VERSION:
400            if (PQprotocolVersion(H->server) >= 3) { /* PostgreSQL 7.4 or later */
401                ZVAL_STRING(return_value, (char*)PQparameterStatus(H->server, "server_version"), 1);
402            } else /* emulate above via a query */
403            {
404                PGresult *res = PQexec(H->server, "SELECT VERSION()");
405                if (res && PQresultStatus(res) == PGRES_TUPLES_OK) {
406                    ZVAL_STRING(return_value, (char *)PQgetvalue(res, 0, 0), 1);
407                }
408
409                if (res) {
410                    PQclear(res);
411                }
412            }
413            break;
414
415        case PDO_ATTR_CONNECTION_STATUS:
416            switch (PQstatus(H->server)) {
417                case CONNECTION_STARTED:
418                    ZVAL_STRINGL(return_value, "Waiting for connection to be made.", sizeof("Waiting for connection to be made.")-1, 1);
419                    break;
420
421                case CONNECTION_MADE:
422                case CONNECTION_OK:
423                    ZVAL_STRINGL(return_value, "Connection OK; waiting to send.", sizeof("Connection OK; waiting to send.")-1, 1);
424                    break;
425
426                case CONNECTION_AWAITING_RESPONSE:
427                    ZVAL_STRINGL(return_value, "Waiting for a response from the server.", sizeof("Waiting for a response from the server.")-1, 1);
428                    break;
429
430                case CONNECTION_AUTH_OK:
431                    ZVAL_STRINGL(return_value, "Received authentication; waiting for backend start-up to finish.", sizeof("Received authentication; waiting for backend start-up to finish.")-1, 1);
432                    break;
433#ifdef CONNECTION_SSL_STARTUP
434                case CONNECTION_SSL_STARTUP:
435                    ZVAL_STRINGL(return_value, "Negotiating SSL encryption.", sizeof("Negotiating SSL encryption.")-1, 1);
436                    break;
437#endif
438                case CONNECTION_SETENV:
439                    ZVAL_STRINGL(return_value, "Negotiating environment-driven parameter settings.", sizeof("Negotiating environment-driven parameter settings.")-1, 1);
440                    break;
441
442                case CONNECTION_BAD:
443                default:
444                    ZVAL_STRINGL(return_value, "Bad connection.", sizeof("Bad connection.")-1, 1);
445                    break;
446            }
447            break;
448
449        case PDO_ATTR_SERVER_INFO: {
450            int spid = PQbackendPID(H->server);
451            char *tmp;
452            spprintf(&tmp, 0,
453                "PID: %d; Client Encoding: %s; Is Superuser: %s; Session Authorization: %s; Date Style: %s",
454                spid,
455                (char*)PQparameterStatus(H->server, "client_encoding"),
456                (char*)PQparameterStatus(H->server, "is_superuser"),
457                (char*)PQparameterStatus(H->server, "session_authorization"),
458                (char*)PQparameterStatus(H->server, "DateStyle"));
459            ZVAL_STRING(return_value, tmp, 0);
460        }
461            break;
462
463        default:
464            return 0;
465    }
466
467    return 1;
468}
469
470/* {{{ */
471static int pdo_pgsql_check_liveness(pdo_dbh_t *dbh TSRMLS_DC)
472{
473    pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
474    if (PQstatus(H->server) == CONNECTION_BAD) {
475        PQreset(H->server);
476    }
477    return (PQstatus(H->server) == CONNECTION_OK) ? SUCCESS : FAILURE;
478}
479/* }}} */
480
481static int pgsql_handle_in_transaction(pdo_dbh_t *dbh TSRMLS_DC)
482{
483    pdo_pgsql_db_handle *H;
484
485    H = (pdo_pgsql_db_handle *)dbh->driver_data;
486
487    return PQtransactionStatus(H->server) > PQTRANS_IDLE;
488}
489
490static int pdo_pgsql_transaction_cmd(const char *cmd, pdo_dbh_t *dbh TSRMLS_DC)
491{
492    pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
493    PGresult *res;
494    int ret = 1;
495
496    res = PQexec(H->server, cmd);
497
498    if (PQresultStatus(res) != PGRES_COMMAND_OK) {
499        pdo_pgsql_error(dbh, PQresultStatus(res), pdo_pgsql_sqlstate(res));
500        ret = 0;
501    }
502
503    PQclear(res);
504    return ret;
505}
506
507static int pgsql_handle_begin(pdo_dbh_t *dbh TSRMLS_DC)
508{
509    return pdo_pgsql_transaction_cmd("BEGIN", dbh TSRMLS_CC);
510}
511
512static int pgsql_handle_commit(pdo_dbh_t *dbh TSRMLS_DC)
513{
514    int ret = pdo_pgsql_transaction_cmd("COMMIT", dbh TSRMLS_CC);
515
516    /* When deferred constraints are used the commit could
517       fail, and a ROLLBACK implicitly ran. See bug #67462 */
518    if (!ret) {
519        dbh->in_txn = pgsql_handle_in_transaction(dbh TSRMLS_CC);
520    }
521
522    return ret;
523}
524
525static int pgsql_handle_rollback(pdo_dbh_t *dbh TSRMLS_DC)
526{
527    return pdo_pgsql_transaction_cmd("ROLLBACK", dbh TSRMLS_CC);
528}
529
530/* {{{ proto string PDO::pgsqlCopyFromArray(string $table_name , array $rows [, string $delimiter [, string $null_as ] [, string $fields])
531   Returns true if the copy worked fine or false if error */
532static PHP_METHOD(PDO, pgsqlCopyFromArray)
533{
534    pdo_dbh_t *dbh;
535    pdo_pgsql_db_handle *H;
536
537    zval *pg_rows;
538
539    char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;
540    int table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;
541    char *query;
542
543    PGresult *pgsql_result;
544    ExecStatusType status;
545
546    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s/a|sss",
547                    &table_name, &table_name_len, &pg_rows,
548                    &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
549        return;
550    }
551
552    if (!zend_hash_num_elements(Z_ARRVAL_P(pg_rows))) {
553        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot copy from an empty array");
554        RETURN_FALSE;
555    }
556
557    dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
558    PDO_CONSTRUCT_CHECK;
559    PDO_DBH_CLEAR_ERR();
560
561    /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */
562    if (pg_fields) {
563        spprintf(&query, 0, "COPY %s (%s) FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
564    } else {
565        spprintf(&query, 0, "COPY %s FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
566    }
567
568    /* Obtain db Handle */
569    H = (pdo_pgsql_db_handle *)dbh->driver_data;
570
571    while ((pgsql_result = PQgetResult(H->server))) {
572        PQclear(pgsql_result);
573    }
574    pgsql_result = PQexec(H->server, query);
575
576    efree(query);
577    query = NULL;
578
579    if (pgsql_result) {
580        status = PQresultStatus(pgsql_result);
581    } else {
582        status = (ExecStatusType) PQstatus(H->server);
583    }
584
585    if (status == PGRES_COPY_IN && pgsql_result) {
586        int command_failed = 0;
587        int buffer_len = 0;
588        zval **tmp;
589        HashPosition pos;
590
591        PQclear(pgsql_result);
592        zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(pg_rows), &pos);
593        while (zend_hash_get_current_data_ex(Z_ARRVAL_P(pg_rows), (void **) &tmp, &pos) == SUCCESS) {
594            int query_len;
595            convert_to_string_ex(tmp);
596
597            if (buffer_len < Z_STRLEN_PP(tmp)) {
598                buffer_len = Z_STRLEN_PP(tmp);
599                query = erealloc(query, buffer_len + 2); /* room for \n\0 */
600            }
601            memcpy(query, Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));
602            query_len = Z_STRLEN_PP(tmp);
603            if (query[query_len - 1] != '\n') {
604                query[query_len++] = '\n';
605            }
606            query[query_len] = '\0';
607            if (PQputCopyData(H->server, query, query_len) != 1) {
608                            efree(query);
609                            pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
610                PDO_HANDLE_DBH_ERR();
611                            RETURN_FALSE;
612                    }
613            zend_hash_move_forward_ex(Z_ARRVAL_P(pg_rows), &pos);
614                }
615        if (query) {
616            efree(query);
617        }
618
619        if (PQputCopyEnd(H->server, NULL) != 1) {
620            pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
621            PDO_HANDLE_DBH_ERR();
622            RETURN_FALSE;
623        }
624
625        while ((pgsql_result = PQgetResult(H->server))) {
626            if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) {
627                pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));
628                command_failed = 1;
629            }
630            PQclear(pgsql_result);
631        }
632
633        PDO_HANDLE_DBH_ERR();
634        RETURN_BOOL(!command_failed);
635    } else {
636        pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));
637        PQclear(pgsql_result);
638        PDO_HANDLE_DBH_ERR();
639        RETURN_FALSE;
640    }
641}
642/* }}} */
643
644/* {{{ proto string PDO::pgsqlCopyFromFile(string $table_name , string $filename [, string $delimiter [, string $null_as ] [, string $fields])
645   Returns true if the copy worked fine or false if error */
646static PHP_METHOD(PDO, pgsqlCopyFromFile)
647{
648    pdo_dbh_t *dbh;
649    pdo_pgsql_db_handle *H;
650
651    char *table_name, *filename, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;
652    int  table_name_len, filename_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;
653    char *query;
654    PGresult *pgsql_result;
655    ExecStatusType status;
656    php_stream *stream;
657
658    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sp|sss",
659                &table_name, &table_name_len, &filename, &filename_len,
660                &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
661        return;
662    }
663
664    /* Obtain db Handler */
665    dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
666    PDO_CONSTRUCT_CHECK;
667    PDO_DBH_CLEAR_ERR();
668
669    stream = php_stream_open_wrapper_ex(filename, "rb", ENFORCE_SAFE_MODE, NULL, FG(default_context));
670    if (!stream) {
671        pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, "Unable to open the file");
672        PDO_HANDLE_DBH_ERR();
673        RETURN_FALSE;
674    }
675
676    /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */
677    if (pg_fields) {
678        spprintf(&query, 0, "COPY %s (%s) FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
679    } else {
680        spprintf(&query, 0, "COPY %s FROM STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
681    }
682
683    H = (pdo_pgsql_db_handle *)dbh->driver_data;
684
685    while ((pgsql_result = PQgetResult(H->server))) {
686        PQclear(pgsql_result);
687    }
688    pgsql_result = PQexec(H->server, query);
689
690    efree(query);
691
692    if (pgsql_result) {
693        status = PQresultStatus(pgsql_result);
694    } else {
695        status = (ExecStatusType) PQstatus(H->server);
696    }
697
698    if (status == PGRES_COPY_IN && pgsql_result) {
699        char *buf;
700        int command_failed = 0;
701        size_t line_len = 0;
702
703        PQclear(pgsql_result);
704        while ((buf = php_stream_get_line(stream, NULL, 0, &line_len)) != NULL) {
705            if (PQputCopyData(H->server, buf, line_len) != 1) {
706                            efree(buf);
707                            pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
708                php_stream_close(stream);
709                PDO_HANDLE_DBH_ERR();
710                RETURN_FALSE;
711            }
712            efree(buf);
713        }
714        php_stream_close(stream);
715
716        if (PQputCopyEnd(H->server, NULL) != 1) {
717            pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
718            PDO_HANDLE_DBH_ERR();
719            RETURN_FALSE;
720        }
721
722        while ((pgsql_result = PQgetResult(H->server))) {
723            if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) {
724                pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));
725                command_failed = 1;
726            }
727            PQclear(pgsql_result);
728        }
729
730        PDO_HANDLE_DBH_ERR();
731        RETURN_BOOL(!command_failed);
732    } else {
733        php_stream_close(stream);
734        pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));
735        PQclear(pgsql_result);
736        PDO_HANDLE_DBH_ERR();
737        RETURN_FALSE;
738    }
739}
740/* }}} */
741
742
743/* {{{ proto string PDO::pgsqlCopyToFile(string $table_name , $filename, [string $delimiter [, string $null_as [, string $fields]]])
744   Returns true if the copy worked fine or false if error */
745static PHP_METHOD(PDO, pgsqlCopyToFile)
746{
747    pdo_dbh_t *dbh;
748    pdo_pgsql_db_handle *H;
749
750    char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL, *filename = NULL;
751    int table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len, filename_len;
752    char *query;
753
754    PGresult *pgsql_result;
755    ExecStatusType status;
756
757    php_stream *stream;
758
759    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sp|sss",
760                    &table_name, &table_name_len, &filename, &filename_len,
761                    &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
762        return;
763    }
764
765    dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
766    PDO_CONSTRUCT_CHECK;
767    PDO_DBH_CLEAR_ERR();
768
769    H = (pdo_pgsql_db_handle *)dbh->driver_data;
770
771    stream = php_stream_open_wrapper_ex(filename, "wb", ENFORCE_SAFE_MODE, NULL, FG(default_context));
772    if (!stream) {
773        pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, "Unable to open the file for writing");
774        PDO_HANDLE_DBH_ERR();
775        RETURN_FALSE;
776    }
777
778    while ((pgsql_result = PQgetResult(H->server))) {
779        PQclear(pgsql_result);
780    }
781
782    /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */
783    if (pg_fields) {
784        spprintf(&query, 0, "COPY %s (%s) TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
785    } else {
786        spprintf(&query, 0, "COPY %s TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
787    }
788    pgsql_result = PQexec(H->server, query);
789    efree(query);
790
791    if (pgsql_result) {
792        status = PQresultStatus(pgsql_result);
793    } else {
794        status = (ExecStatusType) PQstatus(H->server);
795    }
796
797    if (status == PGRES_COPY_OUT && pgsql_result) {
798        PQclear(pgsql_result);
799        while (1) {
800            char *csv = NULL;
801            int ret = PQgetCopyData(H->server, &csv, 0);
802
803            if (ret == -1) {
804                break; /* done */
805            } else if (ret > 0) {
806                if (php_stream_write(stream, csv, ret) != ret) {
807                    pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, "Unable to write to file");
808                    PQfreemem(csv);
809                    php_stream_close(stream);
810                    PDO_HANDLE_DBH_ERR();
811                    RETURN_FALSE;
812                } else {
813                    PQfreemem(csv);
814                }
815            } else {
816                pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
817                php_stream_close(stream);
818                PDO_HANDLE_DBH_ERR();
819                RETURN_FALSE;
820            }
821        }
822        php_stream_close(stream);
823
824        while ((pgsql_result = PQgetResult(H->server))) {
825            PQclear(pgsql_result);
826        }
827        RETURN_TRUE;
828    } else {
829        php_stream_close(stream);
830        pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));
831        PQclear(pgsql_result);
832        PDO_HANDLE_DBH_ERR();
833        RETURN_FALSE;
834    }
835}
836/* }}} */
837
838/* {{{ proto string PDO::pgsqlCopyToArray(string $table_name , [string $delimiter [, string $null_as [, string $fields]]])
839   Returns true if the copy worked fine or false if error */
840static PHP_METHOD(PDO, pgsqlCopyToArray)
841{
842    pdo_dbh_t *dbh;
843    pdo_pgsql_db_handle *H;
844
845    char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;
846    int table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;
847    char *query;
848
849    PGresult *pgsql_result;
850    ExecStatusType status;
851
852    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|sss",
853        &table_name, &table_name_len,
854        &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
855        return;
856    }
857
858    dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
859    PDO_CONSTRUCT_CHECK;
860    PDO_DBH_CLEAR_ERR();
861
862    H = (pdo_pgsql_db_handle *)dbh->driver_data;
863
864    while ((pgsql_result = PQgetResult(H->server))) {
865        PQclear(pgsql_result);
866    }
867
868    /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */
869    if (pg_fields) {
870        spprintf(&query, 0, "COPY %s (%s) TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
871    } else {
872        spprintf(&query, 0, "COPY %s TO STDIN WITH DELIMITER E'%c' NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
873    }
874    pgsql_result = PQexec(H->server, query);
875    efree(query);
876
877    if (pgsql_result) {
878        status = PQresultStatus(pgsql_result);
879    } else {
880        status = (ExecStatusType) PQstatus(H->server);
881    }
882
883    if (status == PGRES_COPY_OUT && pgsql_result) {
884        PQclear(pgsql_result);
885                array_init(return_value);
886
887        while (1) {
888            char *csv = NULL;
889            int ret = PQgetCopyData(H->server, &csv, 0);
890            if (ret == -1) {
891                break; /* copy done */
892            } else if (ret > 0) {
893                add_next_index_stringl(return_value, csv, ret, 1);
894                PQfreemem(csv);
895            } else {
896                pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
897                PDO_HANDLE_DBH_ERR();
898                RETURN_FALSE;
899            }
900        }
901
902        while ((pgsql_result = PQgetResult(H->server))) {
903            PQclear(pgsql_result);
904        }
905    } else {
906        pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));
907        PQclear(pgsql_result);
908        PDO_HANDLE_DBH_ERR();
909        RETURN_FALSE;
910    }
911}
912/* }}} */
913
914
915/* {{{ proto string PDO::pgsqlLOBCreate()
916   Creates a new large object, returning its identifier.  Must be called inside a transaction. */
917static PHP_METHOD(PDO, pgsqlLOBCreate)
918{
919    pdo_dbh_t *dbh;
920    pdo_pgsql_db_handle *H;
921    Oid lfd;
922
923    dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
924    PDO_CONSTRUCT_CHECK;
925    PDO_DBH_CLEAR_ERR();
926
927    H = (pdo_pgsql_db_handle *)dbh->driver_data;
928    lfd = lo_creat(H->server, INV_READ|INV_WRITE);
929
930    if (lfd != InvalidOid) {
931        char *buf;
932        spprintf(&buf, 0, "%lu", (long) lfd);
933        RETURN_STRING(buf, 0);
934    }
935
936    pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
937    PDO_HANDLE_DBH_ERR();
938    RETURN_FALSE;
939}
940/* }}} */
941
942/* {{{ proto resource PDO::pgsqlLOBOpen(string oid [, string mode = 'rb'])
943   Opens an existing large object stream.  Must be called inside a transaction. */
944static PHP_METHOD(PDO, pgsqlLOBOpen)
945{
946    pdo_dbh_t *dbh;
947    pdo_pgsql_db_handle *H;
948    Oid oid;
949    int lfd;
950    char *oidstr;
951    int oidstrlen;
952    char *modestr = "rb";
953    int modestrlen;
954    int mode = INV_READ;
955    char *end_ptr;
956
957    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s",
958                &oidstr, &oidstrlen, &modestr, &modestrlen)) {
959        RETURN_FALSE;
960    }
961
962    oid = (Oid)strtoul(oidstr, &end_ptr, 10);
963    if (oid == 0 && (errno == ERANGE || errno == EINVAL)) {
964        RETURN_FALSE;
965    }
966
967    if (strpbrk(modestr, "+w")) {
968        mode = INV_READ|INV_WRITE;
969    }
970
971    dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
972    PDO_CONSTRUCT_CHECK;
973    PDO_DBH_CLEAR_ERR();
974
975    H = (pdo_pgsql_db_handle *)dbh->driver_data;
976
977    lfd = lo_open(H->server, oid, mode);
978
979    if (lfd >= 0) {
980        php_stream *stream = pdo_pgsql_create_lob_stream(dbh, lfd, oid TSRMLS_CC);
981        if (stream) {
982            php_stream_to_zval(stream, return_value);
983            return;
984        }
985    } else {
986        pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
987    }
988
989    PDO_HANDLE_DBH_ERR();
990    RETURN_FALSE;
991}
992/* }}} */
993
994/* {{{ proto bool PDO::pgsqlLOBUnlink(string oid)
995   Deletes the large object identified by oid.  Must be called inside a transaction. */
996static PHP_METHOD(PDO, pgsqlLOBUnlink)
997{
998    pdo_dbh_t *dbh;
999    pdo_pgsql_db_handle *H;
1000    Oid oid;
1001    char *oidstr, *end_ptr;
1002    int oidlen;
1003
1004    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s",
1005                &oidstr, &oidlen)) {
1006        RETURN_FALSE;
1007    }
1008
1009    oid = (Oid)strtoul(oidstr, &end_ptr, 10);
1010    if (oid == 0 && (errno == ERANGE || errno == EINVAL)) {
1011        RETURN_FALSE;
1012    }
1013
1014    dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
1015    PDO_CONSTRUCT_CHECK;
1016    PDO_DBH_CLEAR_ERR();
1017
1018    H = (pdo_pgsql_db_handle *)dbh->driver_data;
1019
1020    if (1 == lo_unlink(H->server, oid)) {
1021        RETURN_TRUE;
1022    }
1023
1024    pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
1025    PDO_HANDLE_DBH_ERR();
1026    RETURN_FALSE;
1027}
1028/* }}} */
1029
1030/* {{{ proto mixed PDO::pgsqlGetNotify([ int $result_type = PDO::FETCH_USE_DEFAULT] [, int $ms_timeout = 0 ]])
1031   Get asyncronous notification */
1032static PHP_METHOD(PDO, pgsqlGetNotify)
1033{
1034    pdo_dbh_t *dbh;
1035    pdo_pgsql_db_handle *H;
1036    long result_type = PDO_FETCH_USE_DEFAULT;
1037    long ms_timeout = 0;
1038    PGnotify *pgsql_notify;
1039
1040    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|ll",
1041                &result_type, &ms_timeout)) {
1042        RETURN_FALSE;
1043    }
1044
1045    dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
1046    PDO_CONSTRUCT_CHECK;
1047
1048    if (result_type == PDO_FETCH_USE_DEFAULT) {
1049        result_type = dbh->default_fetch_type;
1050    }
1051
1052    if (result_type != PDO_FETCH_BOTH && result_type != PDO_FETCH_ASSOC && result_type != PDO_FETCH_NUM) {
1053        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid result type");
1054        RETURN_FALSE;
1055    }
1056
1057    if (ms_timeout < 0) {
1058        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid timeout");
1059        RETURN_FALSE;
1060    }
1061
1062    H = (pdo_pgsql_db_handle *)dbh->driver_data;
1063
1064    PQconsumeInput(H->server);
1065    pgsql_notify = PQnotifies(H->server);
1066
1067    if (ms_timeout && !pgsql_notify) {
1068        php_pollfd_for_ms(PQsocket(H->server), PHP_POLLREADABLE, ms_timeout);
1069
1070        PQconsumeInput(H->server);
1071        pgsql_notify = PQnotifies(H->server);
1072    }
1073
1074    if (!pgsql_notify) {
1075        RETURN_FALSE;
1076    }
1077
1078    array_init(return_value);
1079    if (result_type == PDO_FETCH_NUM || result_type == PDO_FETCH_BOTH) {
1080        add_index_string(return_value, 0, pgsql_notify->relname, 1);
1081        add_index_long(return_value, 1, pgsql_notify->be_pid);
1082        if (pgsql_notify->extra && pgsql_notify->extra[0]) {
1083            add_index_string(return_value, 2, pgsql_notify->extra, 1);
1084        }
1085    }
1086    if (result_type == PDO_FETCH_ASSOC || result_type == PDO_FETCH_BOTH) {
1087        add_assoc_string(return_value, "message", pgsql_notify->relname, 1);
1088        add_assoc_long(return_value, "pid", pgsql_notify->be_pid);
1089        if (pgsql_notify->extra && pgsql_notify->extra[0]) {
1090            add_assoc_string(return_value, "payload", pgsql_notify->extra, 1);
1091        }
1092    }
1093
1094    PQfreemem(pgsql_notify);
1095}
1096/* }}} */
1097
1098/* {{{ proto int PDO::pgsqlGetPid()
1099   Get backend(server) pid */
1100static PHP_METHOD(PDO, pgsqlGetPid)
1101{
1102    pdo_dbh_t *dbh;
1103    pdo_pgsql_db_handle *H;
1104
1105    dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
1106    PDO_CONSTRUCT_CHECK;
1107
1108    H = (pdo_pgsql_db_handle *)dbh->driver_data;
1109
1110    RETURN_LONG(PQbackendPID(H->server));
1111}
1112/* }}} */
1113
1114
1115static const zend_function_entry dbh_methods[] = {
1116    PHP_ME(PDO, pgsqlLOBCreate, NULL, ZEND_ACC_PUBLIC)
1117    PHP_ME(PDO, pgsqlLOBOpen, NULL, ZEND_ACC_PUBLIC)
1118    PHP_ME(PDO, pgsqlLOBUnlink, NULL, ZEND_ACC_PUBLIC)
1119    PHP_ME(PDO, pgsqlCopyFromArray, NULL, ZEND_ACC_PUBLIC)
1120    PHP_ME(PDO, pgsqlCopyFromFile, NULL, ZEND_ACC_PUBLIC)
1121    PHP_ME(PDO, pgsqlCopyToArray, NULL, ZEND_ACC_PUBLIC)
1122    PHP_ME(PDO, pgsqlCopyToFile, NULL, ZEND_ACC_PUBLIC)
1123        PHP_ME(PDO, pgsqlGetNotify, NULL, ZEND_ACC_PUBLIC)
1124        PHP_ME(PDO, pgsqlGetPid, NULL, ZEND_ACC_PUBLIC)
1125    PHP_FE_END
1126};
1127
1128static const zend_function_entry *pdo_pgsql_get_driver_methods(pdo_dbh_t *dbh, int kind TSRMLS_DC)
1129{
1130    switch (kind) {
1131        case PDO_DBH_DRIVER_METHOD_KIND_DBH:
1132            return dbh_methods;
1133        default:
1134            return NULL;
1135    }
1136}
1137
1138static int pdo_pgsql_set_attr(pdo_dbh_t *dbh, long attr, zval *val TSRMLS_DC)
1139{
1140    pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
1141
1142    switch (attr) {
1143        case PDO_ATTR_EMULATE_PREPARES:
1144            H->emulate_prepares = Z_LVAL_P(val);
1145            return 1;
1146        case PDO_PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT:
1147            php_error_docref(NULL TSRMLS_CC, E_DEPRECATED, "PDO::PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT is deprecated, use PDO::ATTR_EMULATE_PREPARES instead");
1148            H->disable_native_prepares = Z_LVAL_P(val);
1149            return 1;
1150        case PDO_PGSQL_ATTR_DISABLE_PREPARES:
1151            H->disable_prepares = Z_LVAL_P(val);
1152            return 1;
1153        default:
1154            return 0;
1155    }
1156}
1157
1158static struct pdo_dbh_methods pgsql_methods = {
1159    pgsql_handle_closer,
1160    pgsql_handle_preparer,
1161    pgsql_handle_doer,
1162    pgsql_handle_quoter,
1163    pgsql_handle_begin,
1164    pgsql_handle_commit,
1165    pgsql_handle_rollback,
1166    pdo_pgsql_set_attr,
1167    pdo_pgsql_last_insert_id,
1168    pdo_pgsql_fetch_error_func,
1169    pdo_pgsql_get_attribute,
1170    pdo_pgsql_check_liveness,   /* check_liveness */
1171    pdo_pgsql_get_driver_methods,  /* get_driver_methods */
1172    NULL,
1173    pgsql_handle_in_transaction,
1174};
1175
1176static int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_DC) /* {{{ */
1177{
1178    pdo_pgsql_db_handle *H;
1179    int ret = 0;
1180    char *conn_str, *p, *e;
1181    char *tmp_pass;
1182    long connect_timeout = 30;
1183
1184    H = pecalloc(1, sizeof(pdo_pgsql_db_handle), dbh->is_persistent);
1185    dbh->driver_data = H;
1186
1187    H->einfo.errcode = 0;
1188    H->einfo.errmsg = NULL;
1189
1190    /* PostgreSQL wants params in the connect string to be separated by spaces,
1191     * if the PDO standard semicolons are used, we convert them to spaces
1192     */
1193    e = (char *) dbh->data_source + strlen(dbh->data_source);
1194    p = (char *) dbh->data_source;
1195    while ((p = memchr(p, ';', (e - p)))) {
1196        *p = ' ';
1197    }
1198
1199    if (driver_options) {
1200        connect_timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, 30 TSRMLS_CC);
1201    }
1202
1203    if (dbh->password) {
1204        if (dbh->password[0] != '\'' && dbh->password[strlen(dbh->password) - 1] != '\'') {
1205            char *pwd = dbh->password;
1206            int pos = 1;
1207
1208            tmp_pass = safe_emalloc(2, strlen(dbh->password), 3);
1209            tmp_pass[0] = '\'';
1210
1211            while (*pwd != '\0') {
1212                if (*pwd == '\\' || *pwd == '\'') {
1213                    tmp_pass[pos++] = '\\';
1214                }
1215
1216                tmp_pass[pos++] = *pwd++;
1217            }
1218
1219            tmp_pass[pos++] = '\'';
1220            tmp_pass[pos] = '\0';
1221        } else {
1222            tmp_pass = dbh->password;
1223        }
1224    }
1225
1226    /* support both full connection string & connection string + login and/or password */
1227    if (dbh->username && dbh->password) {
1228        spprintf(&conn_str, 0, "%s user=%s password=%s connect_timeout=%ld", dbh->data_source, dbh->username, tmp_pass, connect_timeout);
1229    } else if (dbh->username) {
1230        spprintf(&conn_str, 0, "%s user=%s connect_timeout=%ld", dbh->data_source, dbh->username, connect_timeout);
1231    } else if (dbh->password) {
1232        spprintf(&conn_str, 0, "%s password=%s connect_timeout=%ld", dbh->data_source, tmp_pass, connect_timeout);
1233    } else {
1234        spprintf(&conn_str, 0, "%s connect_timeout=%ld", (char *) dbh->data_source, connect_timeout);
1235    }
1236
1237    H->server = PQconnectdb(conn_str);
1238    if (dbh->password && tmp_pass != dbh->password) {
1239        efree(tmp_pass);
1240    }
1241
1242    efree(conn_str);
1243
1244    if (PQstatus(H->server) != CONNECTION_OK) {
1245        pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, PHP_PDO_PGSQL_CONNECTION_FAILURE_SQLSTATE);
1246        goto cleanup;
1247    }
1248
1249    PQsetNoticeProcessor(H->server, (void(*)(void*,const char*))_pdo_pgsql_notice, (void *)&dbh);
1250
1251    H->attached = 1;
1252    H->pgoid = -1;
1253
1254    dbh->methods = &pgsql_methods;
1255    dbh->alloc_own_columns = 1;
1256    dbh->max_escaped_char_length = 2;
1257
1258    ret = 1;
1259
1260cleanup:
1261    dbh->methods = &pgsql_methods;
1262    if (!ret) {
1263        pgsql_handle_closer(dbh TSRMLS_CC);
1264    }
1265
1266    return ret;
1267}
1268/* }}} */
1269
1270pdo_driver_t pdo_pgsql_driver = {
1271    PDO_DRIVER_HEADER(pgsql),
1272    pdo_pgsql_handle_factory
1273};
1274
1275/*
1276 * Local variables:
1277 * tab-width: 4
1278 * c-basic-offset: 4
1279 * End:
1280 * vim600: noet sw=4 ts=4 fdm=marker
1281 * vim<600: noet sw=4 ts=4
1282 */
1283