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