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