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