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#if HAVE_PG_LO64 && ZEND_ENABLE_ZVAL_LONG64
159    zend_off_t pos = lo_lseek64(self->conn, self->lfd, offset, whence);
160#else
161    zend_off_t pos = lo_lseek(self->conn, self->lfd, offset, whence);
162#endif
163    *newoffset = pos;
164    return pos >= 0 ? 0 : -1;
165}
166
167php_stream_ops pdo_pgsql_lob_stream_ops = {
168    pgsql_lob_write,
169    pgsql_lob_read,
170    pgsql_lob_close,
171    pgsql_lob_flush,
172    "pdo_pgsql lob stream",
173    pgsql_lob_seek,
174    NULL,
175    NULL,
176    NULL
177};
178
179php_stream *pdo_pgsql_create_lob_stream(zval *dbh, int lfd, Oid oid)
180{
181    php_stream *stm;
182    struct pdo_pgsql_lob_self *self = ecalloc(1, sizeof(*self));
183    pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)(Z_PDO_DBH_P(dbh))->driver_data;
184
185    ZVAL_COPY_VALUE(&self->dbh, dbh);
186    self->lfd = lfd;
187    self->oid = oid;
188    self->conn = H->server;
189
190    stm = php_stream_alloc(&pdo_pgsql_lob_stream_ops, self, 0, "r+b");
191
192    if (stm) {
193        Z_ADDREF_P(dbh);
194        return stm;
195    }
196
197    efree(self);
198    return NULL;
199}
200/* }}} */
201
202static int pgsql_handle_closer(pdo_dbh_t *dbh) /* {{{ */
203{
204    pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
205    if (H) {
206        if (H->server) {
207            PQfinish(H->server);
208            H->server = NULL;
209        }
210        if (H->einfo.errmsg) {
211            pefree(H->einfo.errmsg, dbh->is_persistent);
212            H->einfo.errmsg = NULL;
213        }
214        pefree(H, dbh->is_persistent);
215        dbh->driver_data = NULL;
216    }
217    return 0;
218}
219/* }}} */
220
221static int pgsql_handle_preparer(pdo_dbh_t *dbh, const char *sql, size_t sql_len, pdo_stmt_t *stmt, zval *driver_options)
222{
223    pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
224    pdo_pgsql_stmt *S = ecalloc(1, sizeof(pdo_pgsql_stmt));
225    int scrollable;
226    int ret;
227    char *nsql = NULL;
228    size_t nsql_len = 0;
229    int emulate = 0;
230    int execute_only = 0;
231
232    S->H = H;
233    stmt->driver_data = S;
234    stmt->methods = &pgsql_stmt_methods;
235
236    scrollable = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR,
237        PDO_CURSOR_FWDONLY) == PDO_CURSOR_SCROLL;
238
239    if (scrollable) {
240        if (S->cursor_name) {
241            efree(S->cursor_name);
242        }
243        spprintf(&S->cursor_name, 0, "pdo_crsr_%08x", ++H->stmt_counter);
244        emulate = 1;
245    } else if (driver_options) {
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_PREPARES:
390            ZVAL_BOOL(return_value, H->disable_prepares);
391            break;
392
393        case PDO_ATTR_CLIENT_VERSION:
394            ZVAL_STRING(return_value, PG_VERSION);
395            break;
396
397        case PDO_ATTR_SERVER_VERSION:
398            if (PQprotocolVersion(H->server) >= 3) { /* PostgreSQL 7.4 or later */
399                ZVAL_STRING(return_value, (char*)PQparameterStatus(H->server, "server_version"));
400            } else /* emulate above via a query */
401            {
402                PGresult *res = PQexec(H->server, "SELECT VERSION()");
403                if (res && PQresultStatus(res) == PGRES_TUPLES_OK) {
404                    ZVAL_STRING(return_value, (char *)PQgetvalue(res, 0, 0));
405                }
406
407                if (res) {
408                    PQclear(res);
409                }
410            }
411            break;
412
413        case PDO_ATTR_CONNECTION_STATUS:
414            switch (PQstatus(H->server)) {
415                case CONNECTION_STARTED:
416                    ZVAL_STRINGL(return_value, "Waiting for connection to be made.", sizeof("Waiting for connection to be made.")-1);
417                    break;
418
419                case CONNECTION_MADE:
420                case CONNECTION_OK:
421                    ZVAL_STRINGL(return_value, "Connection OK; waiting to send.", sizeof("Connection OK; waiting to send.")-1);
422                    break;
423
424                case CONNECTION_AWAITING_RESPONSE:
425                    ZVAL_STRINGL(return_value, "Waiting for a response from the server.", sizeof("Waiting for a response from the server.")-1);
426                    break;
427
428                case CONNECTION_AUTH_OK:
429                    ZVAL_STRINGL(return_value, "Received authentication; waiting for backend start-up to finish.", sizeof("Received authentication; waiting for backend start-up to finish.")-1);
430                    break;
431#ifdef CONNECTION_SSL_STARTUP
432                case CONNECTION_SSL_STARTUP:
433                    ZVAL_STRINGL(return_value, "Negotiating SSL encryption.", sizeof("Negotiating SSL encryption.")-1);
434                    break;
435#endif
436                case CONNECTION_SETENV:
437                    ZVAL_STRINGL(return_value, "Negotiating environment-driven parameter settings.", sizeof("Negotiating environment-driven parameter settings.")-1);
438                    break;
439
440                case CONNECTION_BAD:
441                default:
442                    ZVAL_STRINGL(return_value, "Bad connection.", sizeof("Bad connection.")-1);
443                    break;
444            }
445            break;
446
447        case PDO_ATTR_SERVER_INFO: {
448            int spid = PQbackendPID(H->server);
449
450
451            zend_string *str_info =
452                strpprintf(0,
453                    "PID: %d; Client Encoding: %s; Is Superuser: %s; Session Authorization: %s; Date Style: %s",
454                    spid,
455                    (char*)PQparameterStatus(H->server, "client_encoding"),
456                    (char*)PQparameterStatus(H->server, "is_superuser"),
457                    (char*)PQparameterStatus(H->server, "session_authorization"),
458                    (char*)PQparameterStatus(H->server, "DateStyle"));
459
460            ZVAL_STR(return_value, str_info);
461        }
462            break;
463
464        default:
465            return 0;
466    }
467
468    return 1;
469}
470
471/* {{{ */
472static int pdo_pgsql_check_liveness(pdo_dbh_t *dbh)
473{
474    pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
475    if (PQstatus(H->server) == CONNECTION_BAD) {
476        PQreset(H->server);
477    }
478    return (PQstatus(H->server) == CONNECTION_OK) ? SUCCESS : FAILURE;
479}
480/* }}} */
481
482static int pgsql_handle_in_transaction(pdo_dbh_t *dbh)
483{
484    pdo_pgsql_db_handle *H;
485
486    H = (pdo_pgsql_db_handle *)dbh->driver_data;
487
488    return PQtransactionStatus(H->server) > PQTRANS_IDLE;
489}
490
491static int pdo_pgsql_transaction_cmd(const char *cmd, pdo_dbh_t *dbh)
492{
493    pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
494    PGresult *res;
495    int ret = 1;
496
497    res = PQexec(H->server, cmd);
498
499    if (PQresultStatus(res) != PGRES_COMMAND_OK) {
500        pdo_pgsql_error(dbh, PQresultStatus(res), pdo_pgsql_sqlstate(res));
501        ret = 0;
502    }
503
504    PQclear(res);
505    return ret;
506}
507
508static int pgsql_handle_begin(pdo_dbh_t *dbh)
509{
510    return pdo_pgsql_transaction_cmd("BEGIN", dbh);
511}
512
513static int pgsql_handle_commit(pdo_dbh_t *dbh)
514{
515    int ret = pdo_pgsql_transaction_cmd("COMMIT", dbh);
516
517    /* When deferred constraints are used the commit could
518       fail, and a ROLLBACK implicitly ran. See bug #67462 */
519    if (!ret) {
520        dbh->in_txn = pgsql_handle_in_transaction(dbh);
521    }
522
523    return ret;
524}
525
526static int pgsql_handle_rollback(pdo_dbh_t *dbh)
527{
528    return pdo_pgsql_transaction_cmd("ROLLBACK", dbh);
529}
530
531/* {{{ proto string PDO::pgsqlCopyFromArray(string $table_name , array $rows [, string $delimiter [, string $null_as ] [, string $fields])
532   Returns true if the copy worked fine or false if error */
533static PHP_METHOD(PDO, pgsqlCopyFromArray)
534{
535    pdo_dbh_t *dbh;
536    pdo_pgsql_db_handle *H;
537
538    zval *pg_rows;
539
540    char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;
541    size_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;
542    char *query;
543
544    PGresult *pgsql_result;
545    ExecStatusType status;
546
547    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s/a|sss",
548                    &table_name, &table_name_len, &pg_rows,
549                    &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
550        return;
551    }
552
553    if (!zend_hash_num_elements(Z_ARRVAL_P(pg_rows))) {
554        php_error_docref(NULL, E_WARNING, "Cannot copy from an empty array");
555        RETURN_FALSE;
556    }
557
558    dbh = Z_PDO_DBH_P(getThis());
559    PDO_CONSTRUCT_CHECK;
560    PDO_DBH_CLEAR_ERR();
561
562    /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */
563    if (pg_fields) {
564        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"));
565    } else {
566        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"));
567    }
568
569    /* Obtain db Handle */
570    H = (pdo_pgsql_db_handle *)dbh->driver_data;
571
572    while ((pgsql_result = PQgetResult(H->server))) {
573        PQclear(pgsql_result);
574    }
575    pgsql_result = PQexec(H->server, query);
576
577    efree(query);
578    query = NULL;
579
580    if (pgsql_result) {
581        status = PQresultStatus(pgsql_result);
582    } else {
583        status = (ExecStatusType) PQstatus(H->server);
584    }
585
586    if (status == PGRES_COPY_IN && pgsql_result) {
587        int command_failed = 0;
588        size_t buffer_len = 0;
589        zval *tmp;
590
591        PQclear(pgsql_result);
592        ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pg_rows), tmp) {
593            size_t query_len;
594            convert_to_string_ex(tmp);
595
596            if (buffer_len < Z_STRLEN_P(tmp)) {
597                buffer_len = Z_STRLEN_P(tmp);
598                query = erealloc(query, buffer_len + 2); /* room for \n\0 */
599            }
600            memcpy(query, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
601            query_len = Z_STRLEN_P(tmp);
602            if (query[query_len - 1] != '\n') {
603                query[query_len++] = '\n';
604            }
605            query[query_len] = '\0';
606            if (PQputCopyData(H->server, query, query_len) != 1) {
607                efree(query);
608                pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
609                PDO_HANDLE_DBH_ERR();
610                RETURN_FALSE;
611            }
612        } ZEND_HASH_FOREACH_END();
613        if (query) {
614            efree(query);
615        }
616
617        if (PQputCopyEnd(H->server, NULL) != 1) {
618            pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
619            PDO_HANDLE_DBH_ERR();
620            RETURN_FALSE;
621        }
622
623        while ((pgsql_result = PQgetResult(H->server))) {
624            if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) {
625                pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));
626                command_failed = 1;
627            }
628            PQclear(pgsql_result);
629        }
630
631        PDO_HANDLE_DBH_ERR();
632        RETURN_BOOL(!command_failed);
633    } else {
634        pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));
635        PQclear(pgsql_result);
636        PDO_HANDLE_DBH_ERR();
637        RETURN_FALSE;
638    }
639}
640/* }}} */
641
642/* {{{ proto string PDO::pgsqlCopyFromFile(string $table_name , string $filename [, string $delimiter [, string $null_as ] [, string $fields])
643   Returns true if the copy worked fine or false if error */
644static PHP_METHOD(PDO, pgsqlCopyFromFile)
645{
646    pdo_dbh_t *dbh;
647    pdo_pgsql_db_handle *H;
648
649    char *table_name, *filename, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;
650    size_t  table_name_len, filename_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;
651    char *query;
652    PGresult *pgsql_result;
653    ExecStatusType status;
654    php_stream *stream;
655
656    if (zend_parse_parameters(ZEND_NUM_ARGS(), "sp|sss",
657                &table_name, &table_name_len, &filename, &filename_len,
658                &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
659        return;
660    }
661
662    /* Obtain db Handler */
663    dbh = Z_PDO_DBH_P(getThis());
664    PDO_CONSTRUCT_CHECK;
665    PDO_DBH_CLEAR_ERR();
666
667    stream = php_stream_open_wrapper_ex(filename, "rb", 0, NULL, FG(default_context));
668    if (!stream) {
669        pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, "Unable to open the file");
670        PDO_HANDLE_DBH_ERR();
671        RETURN_FALSE;
672    }
673
674    /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */
675    if (pg_fields) {
676        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"));
677    } else {
678        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"));
679    }
680
681    H = (pdo_pgsql_db_handle *)dbh->driver_data;
682
683    while ((pgsql_result = PQgetResult(H->server))) {
684        PQclear(pgsql_result);
685    }
686    pgsql_result = PQexec(H->server, query);
687
688    efree(query);
689
690    if (pgsql_result) {
691        status = PQresultStatus(pgsql_result);
692    } else {
693        status = (ExecStatusType) PQstatus(H->server);
694    }
695
696    if (status == PGRES_COPY_IN && pgsql_result) {
697        char *buf;
698        int command_failed = 0;
699        size_t line_len = 0;
700
701        PQclear(pgsql_result);
702        while ((buf = php_stream_get_line(stream, NULL, 0, &line_len)) != NULL) {
703            if (PQputCopyData(H->server, buf, line_len) != 1) {
704                            efree(buf);
705                            pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
706                php_stream_close(stream);
707                PDO_HANDLE_DBH_ERR();
708                RETURN_FALSE;
709            }
710            efree(buf);
711        }
712        php_stream_close(stream);
713
714        if (PQputCopyEnd(H->server, NULL) != 1) {
715            pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
716            PDO_HANDLE_DBH_ERR();
717            RETURN_FALSE;
718        }
719
720        while ((pgsql_result = PQgetResult(H->server))) {
721            if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) {
722                pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));
723                command_failed = 1;
724            }
725            PQclear(pgsql_result);
726        }
727
728        PDO_HANDLE_DBH_ERR();
729        RETURN_BOOL(!command_failed);
730    } else {
731        php_stream_close(stream);
732        pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));
733        PQclear(pgsql_result);
734        PDO_HANDLE_DBH_ERR();
735        RETURN_FALSE;
736    }
737}
738/* }}} */
739
740
741/* {{{ proto string PDO::pgsqlCopyToFile(string $table_name , $filename, [string $delimiter [, string $null_as [, string $fields]]])
742   Returns true if the copy worked fine or false if error */
743static PHP_METHOD(PDO, pgsqlCopyToFile)
744{
745    pdo_dbh_t *dbh;
746    pdo_pgsql_db_handle *H;
747
748    char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL, *filename = NULL;
749    size_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len, filename_len;
750    char *query;
751
752    PGresult *pgsql_result;
753    ExecStatusType status;
754
755    php_stream *stream;
756
757    if (zend_parse_parameters(ZEND_NUM_ARGS(), "sp|sss",
758                    &table_name, &table_name_len, &filename, &filename_len,
759                    &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
760        return;
761    }
762
763    dbh = Z_PDO_DBH_P(getThis());
764    PDO_CONSTRUCT_CHECK;
765    PDO_DBH_CLEAR_ERR();
766
767    H = (pdo_pgsql_db_handle *)dbh->driver_data;
768
769    stream = php_stream_open_wrapper_ex(filename, "wb", 0, NULL, FG(default_context));
770    if (!stream) {
771        pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, "Unable to open the file for writing");
772        PDO_HANDLE_DBH_ERR();
773        RETURN_FALSE;
774    }
775
776    while ((pgsql_result = PQgetResult(H->server))) {
777        PQclear(pgsql_result);
778    }
779
780    /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */
781    if (pg_fields) {
782        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"));
783    } else {
784        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"));
785    }
786    pgsql_result = PQexec(H->server, query);
787    efree(query);
788
789    if (pgsql_result) {
790        status = PQresultStatus(pgsql_result);
791    } else {
792        status = (ExecStatusType) PQstatus(H->server);
793    }
794
795    if (status == PGRES_COPY_OUT && pgsql_result) {
796        PQclear(pgsql_result);
797        while (1) {
798            char *csv = NULL;
799            int ret = PQgetCopyData(H->server, &csv, 0);
800
801            if (ret == -1) {
802                break; /* done */
803            } else if (ret > 0) {
804                if (php_stream_write(stream, csv, ret) != ret) {
805                    pdo_pgsql_error_msg(dbh, PGRES_FATAL_ERROR, "Unable to write to file");
806                    PQfreemem(csv);
807                    php_stream_close(stream);
808                    PDO_HANDLE_DBH_ERR();
809                    RETURN_FALSE;
810                } else {
811                    PQfreemem(csv);
812                }
813            } else {
814                pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
815                php_stream_close(stream);
816                PDO_HANDLE_DBH_ERR();
817                RETURN_FALSE;
818            }
819        }
820        php_stream_close(stream);
821
822        while ((pgsql_result = PQgetResult(H->server))) {
823            PQclear(pgsql_result);
824        }
825        RETURN_TRUE;
826    } else {
827        php_stream_close(stream);
828        pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));
829        PQclear(pgsql_result);
830        PDO_HANDLE_DBH_ERR();
831        RETURN_FALSE;
832    }
833}
834/* }}} */
835
836/* {{{ proto string PDO::pgsqlCopyToArray(string $table_name , [string $delimiter [, string $null_as [, string $fields]]])
837   Returns true if the copy worked fine or false if error */
838static PHP_METHOD(PDO, pgsqlCopyToArray)
839{
840    pdo_dbh_t *dbh;
841    pdo_pgsql_db_handle *H;
842
843    char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;
844    size_t table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;
845    char *query;
846
847    PGresult *pgsql_result;
848    ExecStatusType status;
849
850    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|sss",
851        &table_name, &table_name_len,
852        &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
853        return;
854    }
855
856    dbh = Z_PDO_DBH_P(getThis());
857    PDO_CONSTRUCT_CHECK;
858    PDO_DBH_CLEAR_ERR();
859
860    H = (pdo_pgsql_db_handle *)dbh->driver_data;
861
862    while ((pgsql_result = PQgetResult(H->server))) {
863        PQclear(pgsql_result);
864    }
865
866    /* using pre-9.0 syntax as PDO_pgsql is 7.4+ compatible */
867    if (pg_fields) {
868        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"));
869    } else {
870        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"));
871    }
872    pgsql_result = PQexec(H->server, query);
873    efree(query);
874
875    if (pgsql_result) {
876        status = PQresultStatus(pgsql_result);
877    } else {
878        status = (ExecStatusType) PQstatus(H->server);
879    }
880
881    if (status == PGRES_COPY_OUT && pgsql_result) {
882        PQclear(pgsql_result);
883                array_init(return_value);
884
885        while (1) {
886            char *csv = NULL;
887            int ret = PQgetCopyData(H->server, &csv, 0);
888            if (ret == -1) {
889                break; /* copy done */
890            } else if (ret > 0) {
891                add_next_index_stringl(return_value, csv, ret);
892                PQfreemem(csv);
893            } else {
894                pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
895                PDO_HANDLE_DBH_ERR();
896                RETURN_FALSE;
897            }
898        }
899
900        while ((pgsql_result = PQgetResult(H->server))) {
901            PQclear(pgsql_result);
902        }
903    } else {
904        pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, pdo_pgsql_sqlstate(pgsql_result));
905        PQclear(pgsql_result);
906        PDO_HANDLE_DBH_ERR();
907        RETURN_FALSE;
908    }
909}
910/* }}} */
911
912
913/* {{{ proto string PDO::pgsqlLOBCreate()
914   Creates a new large object, returning its identifier.  Must be called inside a transaction. */
915static PHP_METHOD(PDO, pgsqlLOBCreate)
916{
917    pdo_dbh_t *dbh;
918    pdo_pgsql_db_handle *H;
919    Oid lfd;
920
921    dbh = Z_PDO_DBH_P(getThis());
922    PDO_CONSTRUCT_CHECK;
923    PDO_DBH_CLEAR_ERR();
924
925    H = (pdo_pgsql_db_handle *)dbh->driver_data;
926    lfd = lo_creat(H->server, INV_READ|INV_WRITE);
927
928    if (lfd != InvalidOid) {
929        zend_string *buf = strpprintf(0, ZEND_ULONG_FMT, (zend_long) lfd);
930
931        RETURN_STR(buf);
932    }
933
934    pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
935    PDO_HANDLE_DBH_ERR();
936    RETURN_FALSE;
937}
938/* }}} */
939
940/* {{{ proto resource PDO::pgsqlLOBOpen(string oid [, string mode = 'rb'])
941   Opens an existing large object stream.  Must be called inside a transaction. */
942static PHP_METHOD(PDO, pgsqlLOBOpen)
943{
944    pdo_dbh_t *dbh;
945    pdo_pgsql_db_handle *H;
946    Oid oid;
947    int lfd;
948    char *oidstr;
949    size_t oidstrlen;
950    char *modestr = "rb";
951    size_t modestrlen;
952    int mode = INV_READ;
953    char *end_ptr;
954
955    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "s|s",
956                &oidstr, &oidstrlen, &modestr, &modestrlen)) {
957        RETURN_FALSE;
958    }
959
960    oid = (Oid)strtoul(oidstr, &end_ptr, 10);
961    if (oid == 0 && (errno == ERANGE || errno == EINVAL)) {
962        RETURN_FALSE;
963    }
964
965    if (strpbrk(modestr, "+w")) {
966        mode = INV_READ|INV_WRITE;
967    }
968
969    dbh = Z_PDO_DBH_P(getThis());
970    PDO_CONSTRUCT_CHECK;
971    PDO_DBH_CLEAR_ERR();
972
973    H = (pdo_pgsql_db_handle *)dbh->driver_data;
974
975    lfd = lo_open(H->server, oid, mode);
976
977    if (lfd >= 0) {
978        php_stream *stream = pdo_pgsql_create_lob_stream(getThis(), lfd, oid);
979        if (stream) {
980            php_stream_to_zval(stream, return_value);
981            return;
982        }
983    } else {
984        pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
985    }
986
987    PDO_HANDLE_DBH_ERR();
988    RETURN_FALSE;
989}
990/* }}} */
991
992/* {{{ proto bool PDO::pgsqlLOBUnlink(string oid)
993   Deletes the large object identified by oid.  Must be called inside a transaction. */
994static PHP_METHOD(PDO, pgsqlLOBUnlink)
995{
996    pdo_dbh_t *dbh;
997    pdo_pgsql_db_handle *H;
998    Oid oid;
999    char *oidstr, *end_ptr;
1000    size_t oidlen;
1001
1002    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "s",
1003                &oidstr, &oidlen)) {
1004        RETURN_FALSE;
1005    }
1006
1007    oid = (Oid)strtoul(oidstr, &end_ptr, 10);
1008    if (oid == 0 && (errno == ERANGE || errno == EINVAL)) {
1009        RETURN_FALSE;
1010    }
1011
1012    dbh = Z_PDO_DBH_P(getThis());
1013    PDO_CONSTRUCT_CHECK;
1014    PDO_DBH_CLEAR_ERR();
1015
1016    H = (pdo_pgsql_db_handle *)dbh->driver_data;
1017
1018    if (1 == lo_unlink(H->server, oid)) {
1019        RETURN_TRUE;
1020    }
1021
1022    pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
1023    PDO_HANDLE_DBH_ERR();
1024    RETURN_FALSE;
1025}
1026/* }}} */
1027
1028/* {{{ proto mixed PDO::pgsqlGetNotify([ int $result_type = PDO::FETCH_USE_DEFAULT] [, int $ms_timeout = 0 ]])
1029   Get asyncronous notification */
1030static PHP_METHOD(PDO, pgsqlGetNotify)
1031{
1032    pdo_dbh_t *dbh;
1033    pdo_pgsql_db_handle *H;
1034    zend_long result_type = PDO_FETCH_USE_DEFAULT;
1035    zend_long ms_timeout = 0;
1036    PGnotify *pgsql_notify;
1037
1038    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "|ll",
1039                &result_type, &ms_timeout)) {
1040        RETURN_FALSE;
1041    }
1042
1043    dbh = Z_PDO_DBH_P(getThis());
1044    PDO_CONSTRUCT_CHECK;
1045
1046    if (result_type == PDO_FETCH_USE_DEFAULT) {
1047        result_type = dbh->default_fetch_type;
1048    }
1049
1050    if (result_type != PDO_FETCH_BOTH && result_type != PDO_FETCH_ASSOC && result_type != PDO_FETCH_NUM) {
1051        php_error_docref(NULL, E_WARNING, "Invalid result type");
1052        RETURN_FALSE;
1053    }
1054
1055    if (ms_timeout < 0) {
1056        php_error_docref(NULL, E_WARNING, "Invalid timeout");
1057        RETURN_FALSE;
1058#if ZEND_ENABLE_ZVAL_LONG64
1059    } else if (ms_timeout > INT_MAX) {
1060        php_error_docref(NULL, E_WARNING, "timeout was shrinked to %d", INT_MAX);
1061        ms_timeout = INT_MAX;
1062#endif
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, (int)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 = 0 != Z_LVAL_P(val);
1149            return 1;
1150        case PDO_PGSQL_ATTR_DISABLE_PREPARES:
1151            convert_to_long(val);
1152            H->disable_prepares = 0 != Z_LVAL_P(val);
1153            return 1;
1154        default:
1155            return 0;
1156    }
1157}
1158
1159static struct pdo_dbh_methods pgsql_methods = {
1160    pgsql_handle_closer,
1161    pgsql_handle_preparer,
1162    pgsql_handle_doer,
1163    pgsql_handle_quoter,
1164    pgsql_handle_begin,
1165    pgsql_handle_commit,
1166    pgsql_handle_rollback,
1167    pdo_pgsql_set_attr,
1168    pdo_pgsql_last_insert_id,
1169    pdo_pgsql_fetch_error_func,
1170    pdo_pgsql_get_attribute,
1171    pdo_pgsql_check_liveness,   /* check_liveness */
1172    pdo_pgsql_get_driver_methods,  /* get_driver_methods */
1173    NULL,
1174    pgsql_handle_in_transaction,
1175};
1176
1177static int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */
1178{
1179    pdo_pgsql_db_handle *H;
1180    int ret = 0;
1181    char *conn_str, *p, *e;
1182    char *tmp_pass;
1183    zend_long connect_timeout = 30;
1184
1185    H = pecalloc(1, sizeof(pdo_pgsql_db_handle), dbh->is_persistent);
1186    dbh->driver_data = H;
1187
1188    H->einfo.errcode = 0;
1189    H->einfo.errmsg = NULL;
1190
1191    /* PostgreSQL wants params in the connect string to be separated by spaces,
1192     * if the PDO standard semicolons are used, we convert them to spaces
1193     */
1194    e = (char *) dbh->data_source + strlen(dbh->data_source);
1195    p = (char *) dbh->data_source;
1196    while ((p = memchr(p, ';', (e - p)))) {
1197        *p = ' ';
1198    }
1199
1200    if (driver_options) {
1201        connect_timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, 30);
1202    }
1203
1204    if (dbh->password) {
1205        if (dbh->password[0] != '\'' && dbh->password[strlen(dbh->password) - 1] != '\'') {
1206            char *pwd = dbh->password;
1207            int pos = 1;
1208
1209            tmp_pass = safe_emalloc(2, strlen(dbh->password), 3);
1210            tmp_pass[0] = '\'';
1211
1212            while (*pwd != '\0') {
1213                if (*pwd == '\\' || *pwd == '\'') {
1214                    tmp_pass[pos++] = '\\';
1215                }
1216
1217                tmp_pass[pos++] = *pwd++;
1218            }
1219
1220            tmp_pass[pos++] = '\'';
1221            tmp_pass[pos] = '\0';
1222        } else {
1223            tmp_pass = dbh->password;
1224        }
1225    }
1226
1227    /* support both full connection string & connection string + login and/or password */
1228    if (dbh->username && dbh->password) {
1229        spprintf(&conn_str, 0, "%s user=%s password=%s connect_timeout=%pd", dbh->data_source, dbh->username, tmp_pass, connect_timeout);
1230    } else if (dbh->username) {
1231        spprintf(&conn_str, 0, "%s user=%s connect_timeout=%pd", dbh->data_source, dbh->username, connect_timeout);
1232    } else if (dbh->password) {
1233        spprintf(&conn_str, 0, "%s password=%s connect_timeout=%pd", dbh->data_source, tmp_pass, connect_timeout);
1234    } else {
1235        spprintf(&conn_str, 0, "%s connect_timeout=%pd", (char *) dbh->data_source, connect_timeout);
1236    }
1237
1238    H->server = PQconnectdb(conn_str);
1239    if (dbh->password && tmp_pass != dbh->password) {
1240        efree(tmp_pass);
1241    }
1242
1243    efree(conn_str);
1244
1245    if (PQstatus(H->server) != CONNECTION_OK) {
1246        pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, PHP_PDO_PGSQL_CONNECTION_FAILURE_SQLSTATE);
1247        goto cleanup;
1248    }
1249
1250    PQsetNoticeProcessor(H->server, (void(*)(void*,const char*))_pdo_pgsql_notice, (void *)&dbh);
1251
1252    H->attached = 1;
1253    H->pgoid = -1;
1254
1255    dbh->methods = &pgsql_methods;
1256    dbh->alloc_own_columns = 1;
1257    dbh->max_escaped_char_length = 2;
1258
1259    ret = 1;
1260
1261cleanup:
1262    dbh->methods = &pgsql_methods;
1263    if (!ret) {
1264        pgsql_handle_closer(dbh);
1265    }
1266
1267    return ret;
1268}
1269/* }}} */
1270
1271pdo_driver_t pdo_pgsql_driver = {
1272    PDO_DRIVER_HEADER(pgsql),
1273    pdo_pgsql_handle_factory
1274};
1275
1276/*
1277 * Local variables:
1278 * tab-width: 4
1279 * c-basic-offset: 4
1280 * End:
1281 * vim600: noet sw=4 ts=4 fdm=marker
1282 * vim<600: noet sw=4 ts=4
1283 */
1284