1/*
2  +----------------------------------------------------------------------+
3  | PHP Version 7                                                        |
4  +----------------------------------------------------------------------+
5  | Copyright (c) 1997-2014 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);
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
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 TSRMLS_DC)
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 TSRMLS_DC)
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 TSRMLS_DC)
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 TSRMLS_DC) /* {{{ */
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, zend_long sql_len, pdo_stmt_t *stmt, zval *driver_options TSRMLS_DC)
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    int 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 TSRMLS_CC) == 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 TSRMLS_CC) == 1) {
243            php_error_docref(NULL TSRMLS_CC, 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 TSRMLS_CC) == 1) {
247            emulate = 1;
248        }
249        if (pdo_attr_lval(driver_options, PDO_PGSQL_ATTR_DISABLE_PREPARES, H->disable_prepares TSRMLS_CC) == 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 TSRMLS_CC);
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, zend_long sql_len TSRMLS_DC)
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, int unquotedlen, char **quoted, int *quotedlen, enum pdo_param_type paramtype TSRMLS_DC)
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, (size_t)unquotedlen, &tmp_len);
329            *quotedlen = (int)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, (size_t)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, unsigned int *len TSRMLS_DC)
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 TSRMLS_DC)
381{
382    pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
383
384    switch (attr) {
385        case PDO_ATTR_CLIENT_VERSION:
386            ZVAL_STRING(return_value, PG_VERSION);
387            break;
388
389        case PDO_ATTR_SERVER_VERSION:
390            if (PQprotocolVersion(H->server) >= 3) { /* PostgreSQL 7.4 or later */
391                ZVAL_STRING(return_value, (char*)PQparameterStatus(H->server, "server_version"));
392            } else /* emulate above via a query */
393            {
394                PGresult *res = PQexec(H->server, "SELECT VERSION()");
395                if (res && PQresultStatus(res) == PGRES_TUPLES_OK) {
396                    ZVAL_STRING(return_value, (char *)PQgetvalue(res, 0, 0));
397                }
398
399                if (res) {
400                    PQclear(res);
401                }
402            }
403            break;
404
405        case PDO_ATTR_CONNECTION_STATUS:
406            switch (PQstatus(H->server)) {
407                case CONNECTION_STARTED:
408                    ZVAL_STRINGL(return_value, "Waiting for connection to be made.", sizeof("Waiting for connection to be made.")-1);
409                    break;
410
411                case CONNECTION_MADE:
412                case CONNECTION_OK:
413                    ZVAL_STRINGL(return_value, "Connection OK; waiting to send.", sizeof("Connection OK; waiting to send.")-1);
414                    break;
415
416                case CONNECTION_AWAITING_RESPONSE:
417                    ZVAL_STRINGL(return_value, "Waiting for a response from the server.", sizeof("Waiting for a response from the server.")-1);
418                    break;
419
420                case CONNECTION_AUTH_OK:
421                    ZVAL_STRINGL(return_value, "Received authentication; waiting for backend start-up to finish.", sizeof("Received authentication; waiting for backend start-up to finish.")-1);
422                    break;
423#ifdef CONNECTION_SSL_STARTUP
424                case CONNECTION_SSL_STARTUP:
425                    ZVAL_STRINGL(return_value, "Negotiating SSL encryption.", sizeof("Negotiating SSL encryption.")-1);
426                    break;
427#endif
428                case CONNECTION_SETENV:
429                    ZVAL_STRINGL(return_value, "Negotiating environment-driven parameter settings.", sizeof("Negotiating environment-driven parameter settings.")-1);
430                    break;
431
432                case CONNECTION_BAD:
433                default:
434                    ZVAL_STRINGL(return_value, "Bad connection.", sizeof("Bad connection.")-1);
435                    break;
436            }
437            break;
438
439        case PDO_ATTR_SERVER_INFO: {
440            int spid = PQbackendPID(H->server);
441
442
443            zend_string *str_info =
444                strpprintf(0,
445                    "PID: %d; Client Encoding: %s; Is Superuser: %s; Session Authorization: %s; Date Style: %s",
446                    spid,
447                    (char*)PQparameterStatus(H->server, "client_encoding"),
448                    (char*)PQparameterStatus(H->server, "is_superuser"),
449                    (char*)PQparameterStatus(H->server, "session_authorization"),
450                    (char*)PQparameterStatus(H->server, "DateStyle"));
451
452            ZVAL_STR(return_value, str_info);
453        }
454            break;
455
456        default:
457            return 0;
458    }
459
460    return 1;
461}
462
463/* {{{ */
464static int pdo_pgsql_check_liveness(pdo_dbh_t *dbh TSRMLS_DC)
465{
466    pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
467    if (PQstatus(H->server) == CONNECTION_BAD) {
468        PQreset(H->server);
469    }
470    return (PQstatus(H->server) == CONNECTION_OK) ? SUCCESS : FAILURE;
471}
472/* }}} */
473
474static int pdo_pgsql_transaction_cmd(const char *cmd, pdo_dbh_t *dbh TSRMLS_DC)
475{
476    pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
477    PGresult *res;
478    int ret = 1;
479
480    res = PQexec(H->server, cmd);
481
482    if (PQresultStatus(res) != PGRES_COMMAND_OK) {
483        pdo_pgsql_error(dbh, PQresultStatus(res), pdo_pgsql_sqlstate(res));
484        ret = 0;
485    }
486
487    PQclear(res);
488    return ret;
489}
490
491static int pgsql_handle_begin(pdo_dbh_t *dbh TSRMLS_DC)
492{
493    return pdo_pgsql_transaction_cmd("BEGIN", dbh TSRMLS_CC);
494}
495
496static int pgsql_handle_commit(pdo_dbh_t *dbh TSRMLS_DC)
497{
498    return pdo_pgsql_transaction_cmd("COMMIT", dbh TSRMLS_CC);
499}
500
501static int pgsql_handle_rollback(pdo_dbh_t *dbh TSRMLS_DC)
502{
503    return pdo_pgsql_transaction_cmd("ROLLBACK", dbh TSRMLS_CC);
504}
505
506static int pgsql_handle_in_transaction(pdo_dbh_t *dbh TSRMLS_DC)
507{
508    pdo_pgsql_db_handle *H;
509
510    H = (pdo_pgsql_db_handle *)dbh->driver_data;
511
512    return PQtransactionStatus(H->server);
513}
514
515/* {{{ proto string PDO::pgsqlCopyFromArray(string $table_name , array $rows [, string $delimiter [, string $null_as ] [, string $fields])
516   Returns true if the copy worked fine or false if error */
517static PHP_METHOD(PDO, pgsqlCopyFromArray)
518{
519    pdo_dbh_t *dbh;
520    pdo_pgsql_db_handle *H;
521
522    zval *pg_rows;
523
524    char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;
525    size_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;
526    char *query;
527
528    PGresult *pgsql_result;
529    ExecStatusType status;
530
531    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s/a|sss",
532                    &table_name, &table_name_len, &pg_rows,
533                    &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
534        return;
535    }
536
537    if (!zend_hash_num_elements(Z_ARRVAL_P(pg_rows))) {
538        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot copy from an empty array");
539        RETURN_FALSE;
540    }
541
542    dbh = Z_PDO_DBH_P(getThis());
543    PDO_CONSTRUCT_CHECK;
544    PDO_DBH_CLEAR_ERR();
545
546    /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */
547    if (pg_fields) {
548        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"));
549    } else {
550        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"));
551    }
552
553    /* Obtain db Handle */
554    H = (pdo_pgsql_db_handle *)dbh->driver_data;
555
556    while ((pgsql_result = PQgetResult(H->server))) {
557        PQclear(pgsql_result);
558    }
559    pgsql_result = PQexec(H->server, query);
560
561    efree(query);
562    query = NULL;
563
564    if (pgsql_result) {
565        status = PQresultStatus(pgsql_result);
566    } else {
567        status = (ExecStatusType) PQstatus(H->server);
568    }
569
570    if (status == PGRES_COPY_IN && pgsql_result) {
571        int command_failed = 0;
572        int buffer_len = 0;
573        zval *tmp;
574
575        PQclear(pgsql_result);
576        ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pg_rows), tmp) {
577            int query_len;
578            convert_to_string_ex(tmp);
579
580            if (buffer_len < Z_STRLEN_P(tmp)) {
581                buffer_len = Z_STRLEN_P(tmp);
582                query = erealloc(query, buffer_len + 2); /* room for \n\0 */
583            }
584            memcpy(query, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
585            query_len = Z_STRLEN_P(tmp);
586            if (query[query_len - 1] != '\n') {
587                query[query_len++] = '\n';
588            }
589            query[query_len] = '\0';
590            if (PQputCopyData(H->server, query, query_len) != 1) {
591                efree(query);
592                pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
593                PDO_HANDLE_DBH_ERR();
594                RETURN_FALSE;
595            }
596        } ZEND_HASH_FOREACH_END();
597        if (query) {
598            efree(query);
599        }
600
601        if (PQputCopyEnd(H->server, NULL) != 1) {
602            pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
603            PDO_HANDLE_DBH_ERR();
604            RETURN_FALSE;
605        }
606
607        while ((pgsql_result = PQgetResult(H->server))) {
608            if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) {
609                pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));
610                command_failed = 1;
611            }
612            PQclear(pgsql_result);
613        }
614
615        PDO_HANDLE_DBH_ERR();
616        RETURN_BOOL(!command_failed);
617    } else {
618        pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));
619        PQclear(pgsql_result);
620        PDO_HANDLE_DBH_ERR();
621        RETURN_FALSE;
622    }
623}
624/* }}} */
625
626/* {{{ proto string PDO::pgsqlCopyFromFile(string $table_name , string $filename [, string $delimiter [, string $null_as ] [, string $fields])
627   Returns true if the copy worked fine or false if error */
628static PHP_METHOD(PDO, pgsqlCopyFromFile)
629{
630    pdo_dbh_t *dbh;
631    pdo_pgsql_db_handle *H;
632
633    char *table_name, *filename, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;
634    size_t  table_name_len, filename_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;
635    char *query;
636    PGresult *pgsql_result;
637    ExecStatusType status;
638    php_stream *stream;
639
640    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sp|sss",
641                &table_name, &table_name_len, &filename, &filename_len,
642                &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
643        return;
644    }
645
646    /* Obtain db Handler */
647    dbh = Z_PDO_DBH_P(getThis());
648    PDO_CONSTRUCT_CHECK;
649    PDO_DBH_CLEAR_ERR();
650
651    stream = php_stream_open_wrapper_ex(filename, "rb", ENFORCE_SAFE_MODE, NULL, FG(default_context));
652    if (!stream) {
653        pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, "Unable to open the file");
654        PDO_HANDLE_DBH_ERR();
655        RETURN_FALSE;
656    }
657
658    /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */
659    if (pg_fields) {
660        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"));
661    } else {
662        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"));
663    }
664
665    H = (pdo_pgsql_db_handle *)dbh->driver_data;
666
667    while ((pgsql_result = PQgetResult(H->server))) {
668        PQclear(pgsql_result);
669    }
670    pgsql_result = PQexec(H->server, query);
671
672    efree(query);
673
674    if (pgsql_result) {
675        status = PQresultStatus(pgsql_result);
676    } else {
677        status = (ExecStatusType) PQstatus(H->server);
678    }
679
680    if (status == PGRES_COPY_IN && pgsql_result) {
681        char *buf;
682        int command_failed = 0;
683        size_t line_len = 0;
684
685        PQclear(pgsql_result);
686        while ((buf = php_stream_get_line(stream, NULL, 0, &line_len)) != NULL) {
687            if (PQputCopyData(H->server, buf, line_len) != 1) {
688                            efree(buf);
689                            pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
690                php_stream_close(stream);
691                PDO_HANDLE_DBH_ERR();
692                RETURN_FALSE;
693            }
694            efree(buf);
695        }
696        php_stream_close(stream);
697
698        if (PQputCopyEnd(H->server, NULL) != 1) {
699            pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
700            PDO_HANDLE_DBH_ERR();
701            RETURN_FALSE;
702        }
703
704        while ((pgsql_result = PQgetResult(H->server))) {
705            if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) {
706                pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));
707                command_failed = 1;
708            }
709            PQclear(pgsql_result);
710        }
711
712        PDO_HANDLE_DBH_ERR();
713        RETURN_BOOL(!command_failed);
714    } else {
715        php_stream_close(stream);
716        pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));
717        PQclear(pgsql_result);
718        PDO_HANDLE_DBH_ERR();
719        RETURN_FALSE;
720    }
721}
722/* }}} */
723
724
725/* {{{ proto string PDO::pgsqlCopyToFile(string $table_name , $filename, [string $delimiter [, string $null_as [, string $fields]]])
726   Returns true if the copy worked fine or false if error */
727static PHP_METHOD(PDO, pgsqlCopyToFile)
728{
729    pdo_dbh_t *dbh;
730    pdo_pgsql_db_handle *H;
731
732    char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL, *filename = NULL;
733    size_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len, filename_len;
734    char *query;
735
736    PGresult *pgsql_result;
737    ExecStatusType status;
738
739    php_stream *stream;
740
741    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sp|sss",
742                    &table_name, &table_name_len, &filename, &filename_len,
743                    &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
744        return;
745    }
746
747    dbh = Z_PDO_DBH_P(getThis());
748    PDO_CONSTRUCT_CHECK;
749    PDO_DBH_CLEAR_ERR();
750
751    H = (pdo_pgsql_db_handle *)dbh->driver_data;
752
753    stream = php_stream_open_wrapper_ex(filename, "wb", ENFORCE_SAFE_MODE, NULL, FG(default_context));
754    if (!stream) {
755        pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, "Unable to open the file for writing");
756        PDO_HANDLE_DBH_ERR();
757        RETURN_FALSE;
758    }
759
760    while ((pgsql_result = PQgetResult(H->server))) {
761        PQclear(pgsql_result);
762    }
763
764    /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */
765    if (pg_fields) {
766        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"));
767    } else {
768        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"));
769    }
770    pgsql_result = PQexec(H->server, query);
771    efree(query);
772
773    if (pgsql_result) {
774        status = PQresultStatus(pgsql_result);
775    } else {
776        status = (ExecStatusType) PQstatus(H->server);
777    }
778
779    if (status == PGRES_COPY_OUT && pgsql_result) {
780        PQclear(pgsql_result);
781        while (1) {
782            char *csv = NULL;
783            int ret = PQgetCopyData(H->server, &csv, 0);
784
785            if (ret == -1) {
786                break; /* done */
787            } else if (ret > 0) {
788                if (php_stream_write(stream, csv, ret) != ret) {
789                    pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, "Unable to write to file");
790                    PQfreemem(csv);
791                    php_stream_close(stream);
792                    PDO_HANDLE_DBH_ERR();
793                    RETURN_FALSE;
794                } else {
795                    PQfreemem(csv);
796                }
797            } else {
798                pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
799                php_stream_close(stream);
800                PDO_HANDLE_DBH_ERR();
801                RETURN_FALSE;
802            }
803        }
804        php_stream_close(stream);
805
806        while ((pgsql_result = PQgetResult(H->server))) {
807            PQclear(pgsql_result);
808        }
809        RETURN_TRUE;
810    } else {
811        php_stream_close(stream);
812        pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));
813        PQclear(pgsql_result);
814        PDO_HANDLE_DBH_ERR();
815        RETURN_FALSE;
816    }
817}
818/* }}} */
819
820/* {{{ proto string PDO::pgsqlCopyToArray(string $table_name , [string $delimiter [, string $null_as [, string $fields]]])
821   Returns true if the copy worked fine or false if error */
822static PHP_METHOD(PDO, pgsqlCopyToArray)
823{
824    pdo_dbh_t *dbh;
825    pdo_pgsql_db_handle *H;
826
827    char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;
828    size_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;
829    char *query;
830
831    PGresult *pgsql_result;
832    ExecStatusType status;
833
834    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|sss",
835        &table_name, &table_name_len,
836        &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
837        return;
838    }
839
840    dbh = Z_PDO_DBH_P(getThis());
841    PDO_CONSTRUCT_CHECK;
842    PDO_DBH_CLEAR_ERR();
843
844    H = (pdo_pgsql_db_handle *)dbh->driver_data;
845
846    while ((pgsql_result = PQgetResult(H->server))) {
847        PQclear(pgsql_result);
848    }
849
850    /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */
851    if (pg_fields) {
852        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"));
853    } else {
854        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"));
855    }
856    pgsql_result = PQexec(H->server, query);
857    efree(query);
858
859    if (pgsql_result) {
860        status = PQresultStatus(pgsql_result);
861    } else {
862        status = (ExecStatusType) PQstatus(H->server);
863    }
864
865    if (status == PGRES_COPY_OUT && pgsql_result) {
866        PQclear(pgsql_result);
867                array_init(return_value);
868
869        while (1) {
870            char *csv = NULL;
871            int ret = PQgetCopyData(H->server, &csv, 0);
872            if (ret == -1) {
873                break; /* copy done */
874            } else if (ret > 0) {
875                add_next_index_stringl(return_value, csv, ret);
876                PQfreemem(csv);
877            } else {
878                pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
879                PDO_HANDLE_DBH_ERR();
880                RETURN_FALSE;
881            }
882        }
883
884        while ((pgsql_result = PQgetResult(H->server))) {
885            PQclear(pgsql_result);
886        }
887    } else {
888        pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));
889        PQclear(pgsql_result);
890        PDO_HANDLE_DBH_ERR();
891        RETURN_FALSE;
892    }
893}
894/* }}} */
895
896
897/* {{{ proto string PDO::pgsqlLOBCreate()
898   Creates a new large object, returning its identifier.  Must be called inside a transaction. */
899static PHP_METHOD(PDO, pgsqlLOBCreate)
900{
901    pdo_dbh_t *dbh;
902    pdo_pgsql_db_handle *H;
903    Oid lfd;
904
905    dbh = Z_PDO_DBH_P(getThis());
906    PDO_CONSTRUCT_CHECK;
907    PDO_DBH_CLEAR_ERR();
908
909    H = (pdo_pgsql_db_handle *)dbh->driver_data;
910    lfd = lo_creat(H->server, INV_READ|INV_WRITE);
911
912    if (lfd != InvalidOid) {
913        zend_string *buf = strpprintf(0, ZEND_ULONG_FMT, (zend_long) lfd);
914
915        RETURN_STR(buf);
916    }
917
918    pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
919    PDO_HANDLE_DBH_ERR();
920    RETURN_FALSE;
921}
922/* }}} */
923
924/* {{{ proto resource PDO::pgsqlLOBOpen(string oid [, string mode = 'rb'])
925   Opens an existing large object stream.  Must be called inside a transaction. */
926static PHP_METHOD(PDO, pgsqlLOBOpen)
927{
928    pdo_dbh_t *dbh;
929    pdo_pgsql_db_handle *H;
930    Oid oid;
931    int lfd;
932    char *oidstr;
933    size_t oidstrlen;
934    char *modestr = "rb";
935    size_t modestrlen;
936    int mode = INV_READ;
937    char *end_ptr;
938
939    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s",
940                &oidstr, &oidstrlen, &modestr, &modestrlen)) {
941        RETURN_FALSE;
942    }
943
944    oid = (Oid)strtoul(oidstr, &end_ptr, 10);
945    if (oid == 0 && (errno == ERANGE || errno == EINVAL)) {
946        RETURN_FALSE;
947    }
948
949    if (strpbrk(modestr, "+w")) {
950        mode = INV_READ|INV_WRITE;
951    }
952
953    dbh = Z_PDO_DBH_P(getThis());
954    PDO_CONSTRUCT_CHECK;
955    PDO_DBH_CLEAR_ERR();
956
957    H = (pdo_pgsql_db_handle *)dbh->driver_data;
958
959    lfd = lo_open(H->server, oid, mode);
960
961    if (lfd >= 0) {
962        php_stream *stream = pdo_pgsql_create_lob_stream(getThis(), lfd, oid TSRMLS_CC);
963        if (stream) {
964            php_stream_to_zval(stream, return_value);
965            return;
966        }
967    } else {
968        pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
969    }
970
971    PDO_HANDLE_DBH_ERR();
972    RETURN_FALSE;
973}
974/* }}} */
975
976/* {{{ proto bool PDO::pgsqlLOBUnlink(string oid)
977   Deletes the large object identified by oid.  Must be called inside a transaction. */
978static PHP_METHOD(PDO, pgsqlLOBUnlink)
979{
980    pdo_dbh_t *dbh;
981    pdo_pgsql_db_handle *H;
982    Oid oid;
983    char *oidstr, *end_ptr;
984    size_t oidlen;
985
986    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s",
987                &oidstr, &oidlen)) {
988        RETURN_FALSE;
989    }
990
991    oid = (Oid)strtoul(oidstr, &end_ptr, 10);
992    if (oid == 0 && (errno == ERANGE || errno == EINVAL)) {
993        RETURN_FALSE;
994    }
995
996    dbh = Z_PDO_DBH_P(getThis());
997    PDO_CONSTRUCT_CHECK;
998    PDO_DBH_CLEAR_ERR();
999
1000    H = (pdo_pgsql_db_handle *)dbh->driver_data;
1001
1002    if (1 == lo_unlink(H->server, oid)) {
1003        RETURN_TRUE;
1004    }
1005
1006    pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
1007    PDO_HANDLE_DBH_ERR();
1008    RETURN_FALSE;
1009}
1010/* }}} */
1011
1012/* {{{ proto mixed PDO::pgsqlGetNotify([ int $result_type = PDO::FETCH_USE_DEFAULT] [, int $ms_timeout = 0 ]])
1013   Get asyncronous notification */
1014static PHP_METHOD(PDO, pgsqlGetNotify)
1015{
1016    pdo_dbh_t *dbh;
1017    pdo_pgsql_db_handle *H;
1018    zend_long result_type = PDO_FETCH_USE_DEFAULT;
1019    zend_long ms_timeout = 0;
1020    PGnotify *pgsql_notify;
1021
1022    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|ll",
1023                &result_type, &ms_timeout)) {
1024        RETURN_FALSE;
1025    }
1026
1027    dbh = Z_PDO_DBH_P(getThis());
1028    PDO_CONSTRUCT_CHECK;
1029
1030    if (result_type == PDO_FETCH_USE_DEFAULT) {
1031        result_type = dbh->default_fetch_type;
1032    }
1033
1034    if (result_type != PDO_FETCH_BOTH && result_type != PDO_FETCH_ASSOC && result_type != PDO_FETCH_NUM) {
1035        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid result type");
1036        RETURN_FALSE;
1037    }
1038
1039    if (ms_timeout < 0) {
1040        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid timeout");
1041        RETURN_FALSE;
1042    }
1043
1044    H = (pdo_pgsql_db_handle *)dbh->driver_data;
1045
1046    PQconsumeInput(H->server);
1047    pgsql_notify = PQnotifies(H->server);
1048
1049    if (ms_timeout && !pgsql_notify) {
1050        php_pollfd_for_ms(PQsocket(H->server), PHP_POLLREADABLE, ms_timeout);
1051
1052        PQconsumeInput(H->server);
1053        pgsql_notify = PQnotifies(H->server);
1054    }
1055
1056    if (!pgsql_notify) {
1057        RETURN_FALSE;
1058    }
1059
1060    array_init(return_value);
1061    if (result_type == PDO_FETCH_NUM || result_type == PDO_FETCH_BOTH) {
1062        add_index_string(return_value, 0, pgsql_notify->relname);
1063        add_index_long(return_value, 1, pgsql_notify->be_pid);
1064        if (pgsql_notify->extra && pgsql_notify->extra[0]) {
1065            add_index_string(return_value, 2, pgsql_notify->extra);
1066        }
1067    }
1068    if (result_type == PDO_FETCH_ASSOC || result_type == PDO_FETCH_BOTH) {
1069        add_assoc_string(return_value, "message", pgsql_notify->relname);
1070        add_assoc_long(return_value, "pid", pgsql_notify->be_pid);
1071        if (pgsql_notify->extra && pgsql_notify->extra[0]) {
1072            add_assoc_string(return_value, "payload", pgsql_notify->extra);
1073        }
1074    }
1075
1076    PQfreemem(pgsql_notify);
1077}
1078/* }}} */
1079
1080/* {{{ proto int PDO::pgsqlGetPid()
1081   Get backend(server) pid */
1082static PHP_METHOD(PDO, pgsqlGetPid)
1083{
1084    pdo_dbh_t *dbh;
1085    pdo_pgsql_db_handle *H;
1086
1087    dbh = Z_PDO_DBH_P(getThis());
1088    PDO_CONSTRUCT_CHECK;
1089
1090    H = (pdo_pgsql_db_handle *)dbh->driver_data;
1091
1092    RETURN_LONG(PQbackendPID(H->server));
1093}
1094/* }}} */
1095
1096
1097static const zend_function_entry dbh_methods[] = {
1098    PHP_ME(PDO, pgsqlLOBCreate, NULL, ZEND_ACC_PUBLIC)
1099    PHP_ME(PDO, pgsqlLOBOpen, NULL, ZEND_ACC_PUBLIC)
1100    PHP_ME(PDO, pgsqlLOBUnlink, NULL, ZEND_ACC_PUBLIC)
1101    PHP_ME(PDO, pgsqlCopyFromArray, NULL, ZEND_ACC_PUBLIC)
1102    PHP_ME(PDO, pgsqlCopyFromFile, NULL, ZEND_ACC_PUBLIC)
1103    PHP_ME(PDO, pgsqlCopyToArray, NULL, ZEND_ACC_PUBLIC)
1104    PHP_ME(PDO, pgsqlCopyToFile, NULL, ZEND_ACC_PUBLIC)
1105    PHP_ME(PDO, pgsqlGetNotify, NULL, ZEND_ACC_PUBLIC)
1106    PHP_ME(PDO, pgsqlGetPid, NULL, ZEND_ACC_PUBLIC)
1107    PHP_FE_END
1108};
1109
1110static const zend_function_entry *pdo_pgsql_get_driver_methods(pdo_dbh_t *dbh, int kind TSRMLS_DC)
1111{
1112    switch (kind) {
1113        case PDO_DBH_DRIVER_METHOD_KIND_DBH:
1114            return dbh_methods;
1115        default:
1116            return NULL;
1117    }
1118}
1119
1120static int pdo_pgsql_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val TSRMLS_DC)
1121{
1122    pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
1123
1124    switch (attr) {
1125        case PDO_ATTR_EMULATE_PREPARES:
1126            convert_to_long(val);
1127            H->emulate_prepares = Z_LVAL_P(val);
1128            return 1;
1129        case PDO_PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT:
1130            convert_to_long(val);
1131            php_error_docref(NULL TSRMLS_CC, E_DEPRECATED, "PDO::PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT is deprecated, use PDO::ATTR_EMULATE_PREPARES instead");
1132            H->disable_native_prepares = Z_LVAL_P(val);
1133            return 1;
1134        case PDO_PGSQL_ATTR_DISABLE_PREPARES:
1135            convert_to_long(val);
1136            H->disable_prepares = Z_LVAL_P(val);
1137            return 1;
1138        default:
1139            return 0;
1140    }
1141}
1142
1143static struct pdo_dbh_methods pgsql_methods = {
1144    pgsql_handle_closer,
1145    pgsql_handle_preparer,
1146    pgsql_handle_doer,
1147    pgsql_handle_quoter,
1148    pgsql_handle_begin,
1149    pgsql_handle_commit,
1150    pgsql_handle_rollback,
1151    pdo_pgsql_set_attr,
1152    pdo_pgsql_last_insert_id,
1153    pdo_pgsql_fetch_error_func,
1154    pdo_pgsql_get_attribute,
1155    pdo_pgsql_check_liveness,   /* check_liveness */
1156    pdo_pgsql_get_driver_methods,  /* get_driver_methods */
1157    NULL,
1158    pgsql_handle_in_transaction,
1159};
1160
1161static int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_DC) /* {{{ */
1162{
1163    pdo_pgsql_db_handle *H;
1164    int ret = 0;
1165    char *conn_str, *p, *e;
1166    char *tmp_pass;
1167    zend_long connect_timeout = 30;
1168
1169    H = pecalloc(1, sizeof(pdo_pgsql_db_handle), dbh->is_persistent);
1170    dbh->driver_data = H;
1171
1172    H->einfo.errcode = 0;
1173    H->einfo.errmsg = NULL;
1174
1175    /* PostgreSQL wants params in the connect string to be separated by spaces,
1176     * if the PDO standard semicolons are used, we convert them to spaces
1177     */
1178    e = (char *) dbh->data_source + strlen(dbh->data_source);
1179    p = (char *) dbh->data_source;
1180    while ((p = memchr(p, ';', (e - p)))) {
1181        *p = ' ';
1182    }
1183
1184    if (driver_options) {
1185        connect_timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, 30 TSRMLS_CC);
1186    }
1187
1188    if (dbh->password) {
1189        if (dbh->password[0] != '\'' && dbh->password[strlen(dbh->password) - 1] != '\'') {
1190            char *pwd = dbh->password;
1191            int pos = 1;
1192
1193            tmp_pass = safe_emalloc(2, strlen(dbh->password), 3);
1194            tmp_pass[0] = '\'';
1195
1196            while (*pwd != '\0') {
1197                if (*pwd == '\\' || *pwd == '\'') {
1198                    tmp_pass[pos++] = '\\';
1199                }
1200
1201                tmp_pass[pos++] = *pwd++;
1202            }
1203
1204            tmp_pass[pos++] = '\'';
1205            tmp_pass[pos] = '\0';
1206        } else {
1207            tmp_pass = dbh->password;
1208        }
1209    }
1210
1211    /* support both full connection string & connection string + login and/or password */
1212    if (dbh->username && dbh->password) {
1213        spprintf(&conn_str, 0, "%s user=%s password=%s connect_timeout=%pd", dbh->data_source, dbh->username, tmp_pass, connect_timeout);
1214    } else if (dbh->username) {
1215        spprintf(&conn_str, 0, "%s user=%s connect_timeout=%pd", dbh->data_source, dbh->username, connect_timeout);
1216    } else if (dbh->password) {
1217        spprintf(&conn_str, 0, "%s password=%s connect_timeout=%pd", dbh->data_source, tmp_pass, connect_timeout);
1218    } else {
1219        spprintf(&conn_str, 0, "%s connect_timeout=%pd", (char *) dbh->data_source, connect_timeout);
1220    }
1221
1222    H->server = PQconnectdb(conn_str);
1223    if (dbh->password && tmp_pass != dbh->password) {
1224        efree(tmp_pass);
1225    }
1226
1227    efree(conn_str);
1228
1229    if (PQstatus(H->server) != CONNECTION_OK) {
1230        pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, PHP_PDO_PGSQL_CONNECTION_FAILURE_SQLSTATE);
1231        goto cleanup;
1232    }
1233
1234    PQsetNoticeProcessor(H->server, (void(*)(void*,const char*))_pdo_pgsql_notice, (void *)&dbh);
1235
1236    H->attached = 1;
1237    H->pgoid = -1;
1238
1239    dbh->methods = &pgsql_methods;
1240    dbh->alloc_own_columns = 1;
1241    dbh->max_escaped_char_length = 2;
1242
1243    ret = 1;
1244
1245cleanup:
1246    dbh->methods = &pgsql_methods;
1247    if (!ret) {
1248        pgsql_handle_closer(dbh TSRMLS_CC);
1249    }
1250
1251    return ret;
1252}
1253/* }}} */
1254
1255pdo_driver_t pdo_pgsql_driver = {
1256    PDO_DRIVER_HEADER(pgsql),
1257    pdo_pgsql_handle_factory
1258};
1259
1260/*
1261 * Local variables:
1262 * tab-width: 4
1263 * c-basic-offset: 4
1264 * End:
1265 * vim600: noet sw=4 ts=4 fdm=marker
1266 * vim<600: noet sw=4 ts=4
1267 */
1268