Back to index

php5  5.3.10
pgsql_driver.c
Go to the documentation of this file.
00001 /*
00002   +----------------------------------------------------------------------+
00003   | PHP Version 5                                                        |
00004   +----------------------------------------------------------------------+
00005   | Copyright (c) 1997-2012 The PHP Group                                |
00006   +----------------------------------------------------------------------+
00007   | This source file is subject to version 3.01 of the PHP license,      |
00008   | that is bundled with this package in the file LICENSE, and is        |
00009   | available through the world-wide-web at the following url:           |
00010   | http://www.php.net/license/3_01.txt                                  |
00011   | If you did not receive a copy of the PHP license and are unable to   |
00012   | obtain it through the world-wide-web, please send a note to          |
00013   | license@php.net so we can mail you a copy immediately.               |
00014   +----------------------------------------------------------------------+
00015   | Authors: Edin Kadribasic <edink@emini.dk>                            |
00016   |          Ilia Alshanestsky <ilia@prohost.org>                        |
00017   |          Wez Furlong <wez@php.net>                                   |
00018   +----------------------------------------------------------------------+
00019 */
00020 
00021 /* $Id: pgsql_driver.c 321634 2012-01-01 13:15:04Z felipe $ */
00022 
00023 #ifdef HAVE_CONFIG_H
00024 #include "config.h"
00025 #endif
00026 
00027 #include "php.h"
00028 #include "php_ini.h"
00029 #include "ext/standard/info.h"
00030 #include "pdo/php_pdo.h"
00031 #include "pdo/php_pdo_driver.h"
00032 #include "ext/standard/file.h"
00033 
00034 #undef PACKAGE_BUGREPORT
00035 #undef PACKAGE_NAME
00036 #undef PACKAGE_STRING
00037 #undef PACKAGE_TARNAME
00038 #undef PACKAGE_VERSION
00039 #include "pg_config.h" /* needed for PG_VERSION */
00040 #include "php_pdo_pgsql.h"
00041 #include "php_pdo_pgsql_int.h"
00042 #include "zend_exceptions.h"
00043 
00044 static char * _pdo_pgsql_trim_message(const char *message, int persistent)
00045 {
00046        register int i = strlen(message)-1;
00047        char *tmp;
00048 
00049        if (i>1 && (message[i-1] == '\r' || message[i-1] == '\n') && message[i] == '.') {
00050               --i;
00051        }
00052        while (i>0 && (message[i] == '\r' || message[i] == '\n')) {
00053               --i;
00054        }
00055        ++i;
00056        tmp = pemalloc(i + 1, persistent);
00057        memcpy(tmp, message, i);
00058        tmp[i] = '\0';
00059        
00060        return tmp;
00061 }
00062 
00063 int _pdo_pgsql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const char *sqlstate, const char *file, int line TSRMLS_DC) /* {{{ */
00064 {
00065        pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
00066        pdo_error_type *pdo_err = stmt ? &stmt->error_code : &dbh->error_code;
00067        pdo_pgsql_error_info *einfo = &H->einfo;
00068        char *errmsg = PQerrorMessage(H->server);
00069 
00070        einfo->errcode = errcode;
00071        einfo->file = file;
00072        einfo->line = line;
00073 
00074        if (einfo->errmsg) {
00075               pefree(einfo->errmsg, dbh->is_persistent);
00076               einfo->errmsg = NULL;
00077        }
00078 
00079        if (sqlstate == NULL) {
00080               strcpy(*pdo_err, "HY000");
00081        }
00082        else {
00083               strcpy(*pdo_err, sqlstate);
00084        }
00085 
00086        if (errmsg) {
00087               einfo->errmsg = _pdo_pgsql_trim_message(errmsg, dbh->is_persistent);
00088        }
00089 
00090        if (!dbh->methods) {
00091               zend_throw_exception_ex(php_pdo_get_exception(), einfo->errcode TSRMLS_CC, "SQLSTATE[%s] [%d] %s",
00092                             *pdo_err, einfo->errcode, einfo->errmsg);
00093        }
00094        
00095        return errcode;
00096 }
00097 /* }}} */
00098 
00099 static void _pdo_pgsql_notice(pdo_dbh_t *dbh, const char *message) /* {{{ */
00100 {
00101 /*     pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; */
00102 }
00103 /* }}} */
00104 
00105 static int pdo_pgsql_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info TSRMLS_DC) /* {{{ */
00106 {
00107        pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
00108        pdo_pgsql_error_info *einfo = &H->einfo;
00109 
00110        if (einfo->errcode) {
00111               add_next_index_long(info, einfo->errcode);
00112               add_next_index_string(info, einfo->errmsg, 1);
00113        }
00114 
00115        return 1;
00116 }
00117 /* }}} */
00118 
00119 /* {{{ pdo_pgsql_create_lob_stream */
00120 static size_t pgsql_lob_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
00121 {
00122        struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;
00123        return lo_write(self->conn, self->lfd, (char*)buf, count);
00124 }
00125 
00126 static size_t pgsql_lob_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
00127 {
00128        struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;
00129        return lo_read(self->conn, self->lfd, buf, count);
00130 }
00131 
00132 static int pgsql_lob_close(php_stream *stream, int close_handle TSRMLS_DC)
00133 {
00134        struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;
00135        pdo_dbh_t *dbh = self->dbh;
00136 
00137        if (close_handle) {
00138               lo_close(self->conn, self->lfd);
00139        }
00140        efree(self);
00141        php_pdo_dbh_delref(dbh TSRMLS_CC);
00142        return 0;
00143 }
00144 
00145 static int pgsql_lob_flush(php_stream *stream TSRMLS_DC)
00146 {
00147        return 0;
00148 }
00149 
00150 static int pgsql_lob_seek(php_stream *stream, off_t offset, int whence,
00151               off_t *newoffset TSRMLS_DC)
00152 {
00153        struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract;
00154        int pos = lo_lseek(self->conn, self->lfd, offset, whence);
00155        *newoffset = pos;
00156        return pos >= 0 ? 0 : -1;
00157 }
00158 
00159 php_stream_ops pdo_pgsql_lob_stream_ops = {
00160        pgsql_lob_write,
00161        pgsql_lob_read,
00162        pgsql_lob_close,
00163        pgsql_lob_flush,
00164        "pdo_pgsql lob stream",
00165        pgsql_lob_seek,
00166        NULL,
00167        NULL,
00168        NULL
00169 };
00170 
00171 php_stream *pdo_pgsql_create_lob_stream(pdo_dbh_t *dbh, int lfd, Oid oid TSRMLS_DC)
00172 {
00173        php_stream *stm;
00174        struct pdo_pgsql_lob_self *self = ecalloc(1, sizeof(*self));
00175        pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
00176 
00177        self->dbh = dbh;
00178        self->lfd = lfd;
00179        self->oid = oid;
00180        self->conn = H->server;
00181 
00182        stm = php_stream_alloc(&pdo_pgsql_lob_stream_ops, self, 0, "r+b");
00183 
00184        if (stm) {
00185               php_pdo_dbh_addref(dbh TSRMLS_CC);
00186               return stm;
00187        }
00188 
00189        efree(self);
00190        return NULL;
00191 }
00192 /* }}} */
00193 
00194 static int pgsql_handle_closer(pdo_dbh_t *dbh TSRMLS_DC) /* {{{ */
00195 {
00196        pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
00197        if (H) {
00198               if (H->server) {
00199                      PQfinish(H->server);
00200                      H->server = NULL;
00201               }
00202               if (H->einfo.errmsg) {
00203                      pefree(H->einfo.errmsg, dbh->is_persistent);
00204                      H->einfo.errmsg = NULL;
00205               }
00206               pefree(H, dbh->is_persistent);
00207               dbh->driver_data = NULL;
00208        }
00209        return 0;
00210 }
00211 /* }}} */
00212 
00213 static int pgsql_handle_preparer(pdo_dbh_t *dbh, const char *sql, long sql_len, pdo_stmt_t *stmt, zval *driver_options TSRMLS_DC)
00214 {
00215        pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
00216        pdo_pgsql_stmt *S = ecalloc(1, sizeof(pdo_pgsql_stmt));
00217        int scrollable;
00218 #if HAVE_PQPREPARE
00219        int ret;
00220        char *nsql = NULL;
00221        int nsql_len = 0;
00222        int emulate = 0;
00223 #endif
00224 
00225        S->H = H;
00226        stmt->driver_data = S;
00227        stmt->methods = &pgsql_stmt_methods;
00228 
00229        scrollable = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR,
00230               PDO_CURSOR_FWDONLY TSRMLS_CC) == PDO_CURSOR_SCROLL;
00231 
00232        if (scrollable) {
00233               if (S->cursor_name) {
00234                      efree(S->cursor_name);
00235               }
00236               spprintf(&S->cursor_name, 0, "pdo_crsr_%08x", ++H->stmt_counter);
00237 #if HAVE_PQPREPARE
00238               emulate = 1;
00239 #endif
00240        }
00241 
00242 #if HAVE_PQPREPARE
00243        else if (driver_options) {
00244               if (pdo_attr_lval(driver_options, PDO_PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT, H->disable_native_prepares TSRMLS_CC) == 1 ||
00245                      pdo_attr_lval(driver_options, PDO_ATTR_EMULATE_PREPARES, H->emulate_prepares TSRMLS_CC) == 1) {
00246                      emulate = 1;
00247               }
00248        } else {
00249               emulate = H->disable_native_prepares || H->emulate_prepares;
00250        }
00251 
00252        if (!emulate && PQprotocolVersion(H->server) > 2) {
00253               stmt->supports_placeholders = PDO_PLACEHOLDER_NAMED;
00254               stmt->named_rewrite_template = "$%d";
00255               ret = pdo_parse_params(stmt, (char*)sql, sql_len, &nsql, &nsql_len TSRMLS_CC);
00256 
00257               if (ret == 1) {
00258                      /* query was re-written */
00259                      sql = nsql;
00260               } else if (ret == -1) {
00261                      /* couldn't grok it */
00262                      strcpy(dbh->error_code, stmt->error_code);
00263                      return 0;
00264               }
00265 
00266               spprintf(&S->stmt_name, 0, "pdo_stmt_%08x", ++H->stmt_counter);
00267               /* that's all for now; we'll defer the actual prepare until the first execute call */
00268        
00269               if (nsql) {
00270                      S->query = nsql;
00271               } else {
00272                      S->query = estrdup(sql);
00273               }
00274 
00275               return 1;
00276        }
00277 #endif
00278 
00279        stmt->supports_placeholders = PDO_PLACEHOLDER_NONE;
00280        return 1;
00281 }
00282 
00283 static long pgsql_handle_doer(pdo_dbh_t *dbh, const char *sql, long sql_len TSRMLS_DC)
00284 {
00285        pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
00286        PGresult *res;
00287        long ret = 1;
00288        ExecStatusType qs;
00289        
00290        if (!(res = PQexec(H->server, sql))) {
00291               /* fatal error */
00292               pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL);
00293               return -1;
00294        }
00295        qs = PQresultStatus(res);
00296        if (qs != PGRES_COMMAND_OK && qs != PGRES_TUPLES_OK) {
00297               pdo_pgsql_error(dbh, qs, pdo_pgsql_sqlstate(res));
00298               PQclear(res);
00299               return -1;
00300        }
00301        H->pgoid = PQoidValue(res);
00302        ret = atol(PQcmdTuples(res));
00303        PQclear(res);
00304 
00305        return ret;
00306 }
00307 
00308 static int pgsql_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, int unquotedlen, char **quoted, int *quotedlen, enum pdo_param_type paramtype TSRMLS_DC)
00309 {
00310        unsigned char *escaped;
00311        pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
00312        size_t tmp_len;
00313        
00314        switch (paramtype) {
00315               case PDO_PARAM_LOB:
00316                      /* escapedlen returned by PQescapeBytea() accounts for trailing 0 */
00317 #ifdef HAVE_PQESCAPE_BYTEA_CONN
00318                      escaped = PQescapeByteaConn(H->server, unquoted, unquotedlen, &tmp_len);
00319 #else
00320                      escaped = PQescapeBytea(unquoted, unquotedlen, &tmp_len);
00321 #endif
00322                      *quotedlen = (int)tmp_len + 1;
00323                      *quoted = emalloc(*quotedlen + 1);
00324                      memcpy((*quoted)+1, escaped, *quotedlen-2);
00325                      (*quoted)[0] = '\'';
00326                      (*quoted)[*quotedlen-1] = '\'';
00327                      (*quoted)[*quotedlen] = '\0';
00328                      PQfreemem(escaped);
00329                      break;
00330               default:
00331                      *quoted = safe_emalloc(2, unquotedlen, 3);
00332                      (*quoted)[0] = '\'';
00333 #ifndef HAVE_PQESCAPE_CONN
00334                      *quotedlen = PQescapeString(*quoted + 1, unquoted, unquotedlen);
00335 #else
00336                      *quotedlen = PQescapeStringConn(H->server, *quoted + 1, unquoted, unquotedlen, NULL);
00337 #endif
00338                      (*quoted)[*quotedlen + 1] = '\'';
00339                      (*quoted)[*quotedlen + 2] = '\0';
00340                      *quotedlen += 2;
00341        }
00342        return 1;
00343 }
00344 
00345 static char *pdo_pgsql_last_insert_id(pdo_dbh_t *dbh, const char *name, unsigned int *len TSRMLS_DC)
00346 {
00347        pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
00348        char *id = NULL;
00349 
00350        if (name == NULL) {
00351               if (H->pgoid == InvalidOid) {
00352                      return NULL;
00353               }
00354               *len = spprintf(&id, 0, "%ld", (long) H->pgoid);
00355        } else {
00356               PGresult *res;
00357               ExecStatusType status;
00358               const char *q[1];
00359               q[0] = name;
00360               res = PQexecParams(H->server, "SELECT CURRVAL($1)", 1, NULL, q, NULL, NULL, 0);
00361               status = PQresultStatus(res);
00362 
00363               if (res && (status == PGRES_TUPLES_OK)) {
00364                      id = estrdup((char *)PQgetvalue(res, 0, 0));
00365                      *len = PQgetlength(res, 0, 0);
00366               } else {
00367                      pdo_pgsql_error(dbh, status, pdo_pgsql_sqlstate(res));
00368               }
00369 
00370               if (res) {
00371                      PQclear(res);
00372               }
00373        }
00374        return id;
00375 }
00376 
00377 static int pdo_pgsql_get_attribute(pdo_dbh_t *dbh, long attr, zval *return_value TSRMLS_DC)
00378 {
00379        pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
00380 
00381        switch (attr) {
00382               case PDO_ATTR_CLIENT_VERSION:
00383                      ZVAL_STRING(return_value, PG_VERSION, 1);
00384                      break;
00385 
00386               case PDO_ATTR_SERVER_VERSION:
00387                      if (PQprotocolVersion(H->server) >= 3) { /* PostgreSQL 7.4 or later */
00388                             ZVAL_STRING(return_value, (char*)PQparameterStatus(H->server, "server_version"), 1);
00389                      } else /* emulate above via a query */
00390                      {
00391                             PGresult *res = PQexec(H->server, "SELECT VERSION()");
00392                             if (res && PQresultStatus(res) == PGRES_TUPLES_OK) {
00393                                    ZVAL_STRING(return_value, (char *)PQgetvalue(res, 0, 0), 1);
00394                             }
00395 
00396                             if (res) {
00397                                    PQclear(res);
00398                             }
00399                      }
00400                      break;
00401 
00402               case PDO_ATTR_CONNECTION_STATUS:
00403                      switch (PQstatus(H->server)) {
00404                             case CONNECTION_STARTED:
00405                                    ZVAL_STRINGL(return_value, "Waiting for connection to be made.", sizeof("Waiting for connection to be made.")-1, 1);
00406                                    break;
00407 
00408                             case CONNECTION_MADE:
00409                             case CONNECTION_OK:
00410                                    ZVAL_STRINGL(return_value, "Connection OK; waiting to send.", sizeof("Connection OK; waiting to send.")-1, 1);
00411                                    break;
00412 
00413                             case CONNECTION_AWAITING_RESPONSE:
00414                                    ZVAL_STRINGL(return_value, "Waiting for a response from the server.", sizeof("Waiting for a response from the server.")-1, 1);
00415                                    break;
00416 
00417                             case CONNECTION_AUTH_OK:
00418                                    ZVAL_STRINGL(return_value, "Received authentication; waiting for backend start-up to finish.", sizeof("Received authentication; waiting for backend start-up to finish.")-1, 1);
00419                                    break;
00420 #ifdef CONNECTION_SSL_STARTUP
00421                             case CONNECTION_SSL_STARTUP:
00422                                    ZVAL_STRINGL(return_value, "Negotiating SSL encryption.", sizeof("Negotiating SSL encryption.")-1, 1);
00423                                    break;
00424 #endif
00425                             case CONNECTION_SETENV:
00426                                    ZVAL_STRINGL(return_value, "Negotiating environment-driven parameter settings.", sizeof("Negotiating environment-driven parameter settings.")-1, 1);
00427                                    break;
00428 
00429                             case CONNECTION_BAD:
00430                             default:
00431                                    ZVAL_STRINGL(return_value, "Bad connection.", sizeof("Bad connection.")-1, 1);
00432                                    break;
00433                      }
00434                      break;
00435 
00436               case PDO_ATTR_SERVER_INFO: {
00437                      int spid = PQbackendPID(H->server);
00438                      char *tmp;
00439                      spprintf(&tmp, 0, 
00440                             "PID: %d; Client Encoding: %s; Is Superuser: %s; Session Authorization: %s; Date Style: %s", 
00441                             spid,
00442                             (char*)PQparameterStatus(H->server, "client_encoding"),
00443                             (char*)PQparameterStatus(H->server, "is_superuser"),
00444                             (char*)PQparameterStatus(H->server, "session_authorization"),
00445                             (char*)PQparameterStatus(H->server, "DateStyle"));
00446                      ZVAL_STRING(return_value, tmp, 0);
00447               }
00448                      break;
00449 
00450               default:
00451                      return 0;     
00452        }
00453 
00454        return 1;
00455 }
00456 
00457 /* {{{ */
00458 static int pdo_pgsql_check_liveness(pdo_dbh_t *dbh TSRMLS_DC)
00459 {
00460        pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
00461        if (PQstatus(H->server) == CONNECTION_BAD) {
00462               PQreset(H->server);
00463        }
00464        return (PQstatus(H->server) == CONNECTION_OK) ? SUCCESS : FAILURE;
00465 }
00466 /* }}} */
00467 
00468 static int pdo_pgsql_transaction_cmd(const char *cmd, pdo_dbh_t *dbh TSRMLS_DC)
00469 {
00470        pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
00471        PGresult *res;
00472        int ret = 1;
00473 
00474        res = PQexec(H->server, cmd);
00475 
00476        if (PQresultStatus(res) != PGRES_COMMAND_OK) {
00477               pdo_pgsql_error(dbh, PQresultStatus(res), pdo_pgsql_sqlstate(res));
00478               ret = 0;
00479        }
00480 
00481        PQclear(res);
00482        return ret;
00483 }
00484 
00485 static int pgsql_handle_begin(pdo_dbh_t *dbh TSRMLS_DC)
00486 {
00487        return pdo_pgsql_transaction_cmd("BEGIN", dbh TSRMLS_CC);
00488 }
00489 
00490 static int pgsql_handle_commit(pdo_dbh_t *dbh TSRMLS_DC)
00491 {
00492        return pdo_pgsql_transaction_cmd("COMMIT", dbh TSRMLS_CC);
00493 }
00494 
00495 static int pgsql_handle_rollback(pdo_dbh_t *dbh TSRMLS_DC)
00496 {
00497        return pdo_pgsql_transaction_cmd("ROLLBACK", dbh TSRMLS_CC);
00498 }
00499 
00500 /* {{{ proto string PDO::pgsqlCopyFromArray(string $table_name , array $rows [, string $delimiter [, string $null_as ] [, string $fields])
00501    Returns true if the copy worked fine or false if error */
00502 static PHP_METHOD(PDO, pgsqlCopyFromArray)
00503 {
00504        pdo_dbh_t *dbh;
00505        pdo_pgsql_db_handle *H;
00506 
00507        zval *pg_rows;
00508 
00509        char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;
00510        int table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;
00511        char *query;
00512 
00513        PGresult *pgsql_result;
00514        ExecStatusType status;
00515 
00516        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s/a|sss",
00517                                    &table_name, &table_name_len, &pg_rows,
00518                                    &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
00519               return;
00520        }
00521 
00522        if (!zend_hash_num_elements(Z_ARRVAL_P(pg_rows))) {
00523               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot copy from an empty array");
00524               RETURN_FALSE;
00525        }
00526 
00527        dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
00528        PDO_CONSTRUCT_CHECK;
00529 
00530        if (pg_fields) {
00531               spprintf(&query, 0, "COPY %s (%s) FROM STDIN DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
00532        } else {
00533               spprintf(&query, 0, "COPY %s FROM STDIN DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
00534        }
00535 
00536        /* Obtain db Handle */
00537        H = (pdo_pgsql_db_handle *)dbh->driver_data;
00538 
00539        while ((pgsql_result = PQgetResult(H->server))) {
00540               PQclear(pgsql_result);
00541        }
00542        pgsql_result = PQexec(H->server, query);
00543 
00544        efree(query);
00545        query = NULL;
00546 
00547        if (pgsql_result) {
00548               status = PQresultStatus(pgsql_result);
00549        } else {
00550               status = (ExecStatusType) PQstatus(H->server);
00551        }
00552 
00553        if (status == PGRES_COPY_IN && pgsql_result) {
00554               int command_failed = 0;
00555               int buffer_len = 0;
00556               zval **tmp;
00557               HashPosition pos;
00558 
00559               PQclear(pgsql_result);
00560               zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(pg_rows), &pos);
00561               while (zend_hash_get_current_data_ex(Z_ARRVAL_P(pg_rows), (void **) &tmp, &pos) == SUCCESS) {
00562                      int query_len;
00563                      convert_to_string_ex(tmp);
00564               
00565                      if (buffer_len < Z_STRLEN_PP(tmp)) {
00566                             buffer_len = Z_STRLEN_PP(tmp);
00567                             query = erealloc(query, buffer_len + 2); /* room for \n\0 */
00568                      }
00569                      memcpy(query, Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));
00570                      query_len = Z_STRLEN_PP(tmp);
00571                      if (query[query_len - 1] != '\n') {
00572                             query[query_len++] = '\n';
00573                      }
00574                      query[query_len] = '\0';
00575                      if (PQputCopyData(H->server, query, query_len) != 1) {
00576                             efree(query);
00577                                pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "copy failed");
00578                               RETURN_FALSE;
00579                      }
00580                      zend_hash_move_forward_ex(Z_ARRVAL_P(pg_rows), &pos);
00581                 }
00582               if (query) {
00583                      efree(query);
00584               }
00585 
00586               if (PQputCopyEnd(H->server, NULL) != 1) {
00587                      pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "putcopyend failed");
00588                      RETURN_FALSE;
00589               }
00590 
00591               while ((pgsql_result = PQgetResult(H->server))) {
00592                      if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) {
00593                             pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed");
00594                             command_failed = 1;
00595                      }
00596                      PQclear(pgsql_result);
00597               }
00598 
00599               RETURN_BOOL(!command_failed);
00600        } else {
00601               PQclear(pgsql_result);
00602               pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed");
00603               RETURN_FALSE;
00604        }
00605 }
00606 /* }}} */
00607 
00608 /* {{{ proto string PDO::pgsqlCopyFromFile(string $table_name , string $filename [, string $delimiter [, string $null_as ] [, string $fields])
00609    Returns true if the copy worked fine or false if error */
00610 static PHP_METHOD(PDO, pgsqlCopyFromFile)
00611 {
00612        pdo_dbh_t *dbh;
00613        pdo_pgsql_db_handle *H;
00614 
00615        char *table_name, *filename, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;
00616        int  table_name_len, filename_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;
00617        char *query;
00618        PGresult *pgsql_result;
00619        ExecStatusType status;
00620        php_stream *stream;
00621 
00622        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|sss",
00623                             &table_name, &table_name_len, &filename, &filename_len,
00624                             &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
00625               return;
00626        }
00627 
00628        /* Obtain db Handler */
00629        dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
00630        PDO_CONSTRUCT_CHECK;
00631 
00632        stream = php_stream_open_wrapper_ex(filename, "rb", ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL, FG(default_context));
00633        if (!stream) {
00634               pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Unable to open the file");
00635               RETURN_FALSE;
00636        }
00637 
00638        if (pg_fields) {
00639               spprintf(&query, 0, "COPY %s (%s) FROM STDIN DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
00640        } else {
00641               spprintf(&query, 0, "COPY %s FROM STDIN DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
00642        }
00643 
00644        H = (pdo_pgsql_db_handle *)dbh->driver_data;
00645 
00646        while ((pgsql_result = PQgetResult(H->server))) {
00647               PQclear(pgsql_result);
00648        }
00649        pgsql_result = PQexec(H->server, query);
00650 
00651        efree(query);
00652 
00653        if (pgsql_result) {
00654               status = PQresultStatus(pgsql_result);
00655        } else {
00656               status = (ExecStatusType) PQstatus(H->server);
00657        }
00658 
00659        if (status == PGRES_COPY_IN && pgsql_result) {
00660               char *buf;
00661               int command_failed = 0;
00662               size_t line_len = 0;
00663 
00664               PQclear(pgsql_result);
00665               while ((buf = php_stream_get_line(stream, NULL, 0, &line_len)) != NULL) {
00666                      if (PQputCopyData(H->server, buf, line_len) != 1) {
00667                                efree(buf);
00668                               pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "copy failed");
00669                             php_stream_close(stream);
00670                             RETURN_FALSE;
00671                      }
00672                      efree(buf);
00673               }
00674               php_stream_close(stream);
00675 
00676               if (PQputCopyEnd(H->server, NULL) != 1) {
00677                      pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "putcopyend failed");
00678                      RETURN_FALSE;
00679               }
00680 
00681               while ((pgsql_result = PQgetResult(H->server))) {
00682                      if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) {
00683                             pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed");
00684                             command_failed = 1;
00685                      }
00686                      PQclear(pgsql_result);
00687               }
00688 
00689               RETURN_BOOL(!command_failed);
00690        } else {
00691               PQclear(pgsql_result);
00692               php_stream_close(stream);
00693               pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed");
00694               RETURN_FALSE;
00695        }
00696 }
00697 /* }}} */
00698 
00699 
00700 /* {{{ proto string PDO::pgsqlCopyToFile(string $table_name , $filename, [string $delimiter [, string $null_as [, string $fields]]])
00701    Returns true if the copy worked fine or false if error */
00702 static PHP_METHOD(PDO, pgsqlCopyToFile)
00703 {
00704        pdo_dbh_t *dbh;
00705        pdo_pgsql_db_handle *H;
00706 
00707        char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL, *filename = NULL;
00708        int table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len, filename_len;
00709        char *query;
00710 
00711        PGresult *pgsql_result;
00712        ExecStatusType status;
00713 
00714        php_stream *stream;
00715 
00716        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|sss",
00717                                    &table_name, &table_name_len, &filename, &filename_len,
00718                                    &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
00719               return;
00720        }
00721 
00722        dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
00723        PDO_CONSTRUCT_CHECK;
00724 
00725        H = (pdo_pgsql_db_handle *)dbh->driver_data;
00726 
00727        stream = php_stream_open_wrapper_ex(filename, "wb", ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL, FG(default_context));
00728        if (!stream) {
00729               pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Unable to open the file for writing");
00730               RETURN_FALSE;
00731        }
00732 
00733        while ((pgsql_result = PQgetResult(H->server))) {
00734               PQclear(pgsql_result);
00735        }
00736 
00737        if (pg_fields) {
00738               spprintf(&query, 0, "COPY %s (%s) TO STDIN DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
00739        } else {
00740               spprintf(&query, 0, "COPY %s TO STDIN DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
00741        }
00742        pgsql_result = PQexec(H->server, query);
00743        efree(query);
00744 
00745        if (pgsql_result) {
00746               status = PQresultStatus(pgsql_result);
00747        } else {
00748               status = (ExecStatusType) PQstatus(H->server);
00749        }
00750 
00751        if (status == PGRES_COPY_OUT && pgsql_result) {
00752               PQclear(pgsql_result);
00753               while (1) {
00754                      char *csv = NULL;
00755                      int ret = PQgetCopyData(H->server, &csv, 0);
00756 
00757                      if (ret == -1) {
00758                             break; /* done */
00759                      } else if (ret > 0) {
00760                             if (php_stream_write(stream, csv, ret) != ret) {
00761                                    pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Unable to write to file");
00762                                    PQfreemem(csv);
00763                                    php_stream_close(stream);
00764                                    RETURN_FALSE;
00765                             } else {
00766                                    PQfreemem(csv);
00767                             }
00768                      } else {
00769                             pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed: getline failed");
00770                             php_stream_close(stream);
00771                             RETURN_FALSE;
00772                      }
00773               }
00774               php_stream_close(stream);
00775 
00776               while ((pgsql_result = PQgetResult(H->server))) {
00777                      PQclear(pgsql_result);
00778               }
00779               RETURN_TRUE;
00780        } else {
00781               php_stream_close(stream);
00782               PQclear(pgsql_result);
00783               pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed");
00784               RETURN_FALSE;
00785        }
00786 }
00787 /* }}} */
00788 
00789 /* {{{ proto string PDO::pgsqlCopyToArray(string $table_name , [string $delimiter [, string $null_as [, string $fields]]])
00790    Returns true if the copy worked fine or false if error */
00791 static PHP_METHOD(PDO, pgsqlCopyToArray)
00792 {
00793        pdo_dbh_t *dbh;
00794        pdo_pgsql_db_handle *H;
00795 
00796        char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL;
00797        int table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len;
00798        char *query;
00799 
00800        PGresult *pgsql_result;
00801        ExecStatusType status;
00802 
00803        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|sss",
00804               &table_name, &table_name_len,
00805               &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) {
00806               return;
00807        }
00808 
00809        dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
00810        PDO_CONSTRUCT_CHECK;
00811 
00812        H = (pdo_pgsql_db_handle *)dbh->driver_data;
00813 
00814        while ((pgsql_result = PQgetResult(H->server))) {
00815               PQclear(pgsql_result);
00816        }
00817 
00818        if (pg_fields) {
00819               spprintf(&query, 0, "COPY %s (%s) TO STDIN DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, pg_fields, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
00820        } else {
00821               spprintf(&query, 0, "COPY %s TO STDIN DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N"));
00822        }
00823        pgsql_result = PQexec(H->server, query);
00824        efree(query);
00825 
00826        if (pgsql_result) {
00827               status = PQresultStatus(pgsql_result);
00828        } else {
00829               status = (ExecStatusType) PQstatus(H->server);
00830        }
00831 
00832        if (status == PGRES_COPY_OUT && pgsql_result) {
00833               PQclear(pgsql_result);
00834                 array_init(return_value);
00835 
00836               while (1) {
00837                      char *csv = NULL;
00838                      int ret = PQgetCopyData(H->server, &csv, 0);
00839                      if (ret == -1) {
00840                             break; /* copy done */
00841                      } else if (ret > 0) { 
00842                             add_next_index_stringl(return_value, csv, ret, 1);
00843                             PQfreemem(csv);
00844                      } else {
00845                             pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed: getline failed");
00846                             RETURN_FALSE;
00847                      }
00848               }
00849 
00850               while ((pgsql_result = PQgetResult(H->server))) {
00851                      PQclear(pgsql_result);
00852               }
00853        } else {
00854               PQclear(pgsql_result);
00855               pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed");
00856               RETURN_FALSE;
00857        }
00858 }
00859 /* }}} */
00860 
00861 
00862 /* {{{ proto string PDO::pgsqlLOBCreate()
00863    Creates a new large object, returning its identifier.  Must be called inside a transaction. */
00864 static PHP_METHOD(PDO, pgsqlLOBCreate)
00865 {
00866        pdo_dbh_t *dbh;
00867        pdo_pgsql_db_handle *H;
00868        Oid lfd;
00869 
00870        dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
00871        PDO_CONSTRUCT_CHECK;
00872 
00873        H = (pdo_pgsql_db_handle *)dbh->driver_data;
00874        lfd = lo_creat(H->server, INV_READ|INV_WRITE);
00875 
00876        if (lfd != InvalidOid) {
00877               char *buf;
00878               spprintf(&buf, 0, "%lu", (long) lfd);
00879               RETURN_STRING(buf, 0);
00880        }
00881        
00882        pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "HY000");
00883        RETURN_FALSE;
00884 }
00885 /* }}} */
00886 
00887 /* {{{ proto resource PDO::pgsqlLOBOpen(string oid [, string mode = 'rb'])
00888    Opens an existing large object stream.  Must be called inside a transaction. */
00889 static PHP_METHOD(PDO, pgsqlLOBOpen)
00890 {
00891        pdo_dbh_t *dbh;
00892        pdo_pgsql_db_handle *H;
00893        Oid oid;
00894        int lfd;
00895        char *oidstr;
00896        int oidstrlen;
00897        char *modestr = "rb";
00898        int modestrlen;
00899        int mode = INV_READ;
00900        char *end_ptr;
00901 
00902        if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s",
00903                             &oidstr, &oidstrlen, &modestr, &modestrlen)) {
00904               RETURN_FALSE;
00905        }
00906 
00907        oid = (Oid)strtoul(oidstr, &end_ptr, 10);
00908        if (oid == 0 && (errno == ERANGE || errno == EINVAL)) {
00909               RETURN_FALSE;
00910        }
00911 
00912        if (strpbrk(modestr, "+w")) {
00913               mode = INV_READ|INV_WRITE;
00914        }
00915        
00916        dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
00917        PDO_CONSTRUCT_CHECK;
00918 
00919        H = (pdo_pgsql_db_handle *)dbh->driver_data;
00920 
00921        lfd = lo_open(H->server, oid, mode);
00922 
00923        if (lfd >= 0) {
00924               php_stream *stream = pdo_pgsql_create_lob_stream(dbh, lfd, oid TSRMLS_CC);
00925               if (stream) {
00926                      php_stream_to_zval(stream, return_value);
00927                      return;
00928               }
00929        } else {
00930               pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "HY000");
00931        }
00932        RETURN_FALSE;
00933 }
00934 /* }}} */
00935 
00936 /* {{{ proto bool PDO::pgsqlLOBUnlink(string oid)
00937    Deletes the large object identified by oid.  Must be called inside a transaction. */
00938 static PHP_METHOD(PDO, pgsqlLOBUnlink)
00939 {
00940        pdo_dbh_t *dbh;
00941        pdo_pgsql_db_handle *H;
00942        Oid oid;
00943        char *oidstr, *end_ptr;
00944        int oidlen;
00945 
00946        if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s",
00947                             &oidstr, &oidlen)) {
00948               RETURN_FALSE;
00949        }
00950 
00951        oid = (Oid)strtoul(oidstr, &end_ptr, 10);
00952        if (oid == 0 && (errno == ERANGE || errno == EINVAL)) {
00953               RETURN_FALSE;
00954        }
00955 
00956        dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
00957        PDO_CONSTRUCT_CHECK;
00958 
00959        H = (pdo_pgsql_db_handle *)dbh->driver_data;
00960        
00961        if (1 == lo_unlink(H->server, oid)) {
00962               RETURN_TRUE;
00963        }
00964        pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "HY000");
00965        RETURN_FALSE;
00966 }
00967 /* }}} */
00968 
00969 
00970 static const zend_function_entry dbh_methods[] = {
00971        PHP_ME(PDO, pgsqlLOBCreate, NULL, ZEND_ACC_PUBLIC)
00972        PHP_ME(PDO, pgsqlLOBOpen, NULL, ZEND_ACC_PUBLIC)
00973        PHP_ME(PDO, pgsqlLOBUnlink, NULL, ZEND_ACC_PUBLIC)
00974        PHP_ME(PDO, pgsqlCopyFromArray, NULL, ZEND_ACC_PUBLIC)
00975        PHP_ME(PDO, pgsqlCopyFromFile, NULL, ZEND_ACC_PUBLIC)
00976        PHP_ME(PDO, pgsqlCopyToArray, NULL, ZEND_ACC_PUBLIC)
00977        PHP_ME(PDO, pgsqlCopyToFile, NULL, ZEND_ACC_PUBLIC)
00978        PHP_FE_END
00979 };
00980 
00981 static const zend_function_entry *pdo_pgsql_get_driver_methods(pdo_dbh_t *dbh, int kind TSRMLS_DC)
00982 {
00983        switch (kind) {
00984               case PDO_DBH_DRIVER_METHOD_KIND_DBH:
00985                      return dbh_methods;
00986               default:
00987                      return NULL;
00988        }
00989 }
00990 
00991 static int pdo_pgsql_set_attr(pdo_dbh_t *dbh, long attr, zval *val TSRMLS_DC)
00992 {
00993        pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data;
00994 
00995        switch (attr) {
00996 #if HAVE_PQPREPARE
00997               case PDO_ATTR_EMULATE_PREPARES:
00998                      H->emulate_prepares = Z_LVAL_P(val);
00999                      return 1;
01000               case PDO_PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT:
01001                      H->disable_native_prepares = Z_LVAL_P(val);
01002                      return 1;
01003 #endif
01004 
01005               default:
01006                      return 0;
01007        }
01008 }
01009 
01010 static struct pdo_dbh_methods pgsql_methods = {
01011        pgsql_handle_closer,
01012        pgsql_handle_preparer,
01013        pgsql_handle_doer,
01014        pgsql_handle_quoter,
01015        pgsql_handle_begin,
01016        pgsql_handle_commit,
01017        pgsql_handle_rollback,
01018        pdo_pgsql_set_attr,
01019        pdo_pgsql_last_insert_id,
01020        pdo_pgsql_fetch_error_func,
01021        pdo_pgsql_get_attribute,
01022        pdo_pgsql_check_liveness,   /* check_liveness */
01023        pdo_pgsql_get_driver_methods,  /* get_driver_methods */
01024        NULL,
01025 };
01026 
01027 static int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_DC) /* {{{ */
01028 {
01029        pdo_pgsql_db_handle *H;
01030        int ret = 0;
01031        char *conn_str, *p, *e;
01032        long connect_timeout = 30;
01033 
01034        H = pecalloc(1, sizeof(pdo_pgsql_db_handle), dbh->is_persistent);
01035        dbh->driver_data = H;
01036 
01037        H->einfo.errcode = 0;
01038        H->einfo.errmsg = NULL;
01039        
01040        /* PostgreSQL wants params in the connect string to be separated by spaces,
01041         * if the PDO standard semicolons are used, we convert them to spaces
01042         */
01043        e = (char *) dbh->data_source + strlen(dbh->data_source);
01044        p = (char *) dbh->data_source;
01045        while ((p = memchr(p, ';', (e - p)))) {
01046               *p = ' ';
01047        }
01048 
01049        if (driver_options) {
01050               connect_timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, 30 TSRMLS_CC);
01051        }
01052 
01053        /* support both full connection string & connection string + login and/or password */
01054        if (dbh->username && dbh->password) {
01055               spprintf(&conn_str, 0, "%s user=%s password=%s connect_timeout=%ld", dbh->data_source, dbh->username, dbh->password, connect_timeout);
01056        } else if (dbh->username) {
01057               spprintf(&conn_str, 0, "%s user=%s connect_timeout=%ld", dbh->data_source, dbh->username, connect_timeout);
01058        } else if (dbh->password) {
01059               spprintf(&conn_str, 0, "%s password=%s connect_timeout=%ld", dbh->data_source, dbh->password, connect_timeout);
01060        } else {
01061               spprintf(&conn_str, 0, "%s connect_timeout=%ld", (char *) dbh->data_source, connect_timeout);
01062        }
01063 
01064        H->server = PQconnectdb(conn_str);
01065 
01066        efree(conn_str);
01067 
01068        if (PQstatus(H->server) != CONNECTION_OK) {
01069               pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, PHP_PDO_PGSQL_CONNECTION_FAILURE_SQLSTATE);
01070               goto cleanup;
01071        }
01072 
01073        PQsetNoticeProcessor(H->server, (void(*)(void*,const char*))_pdo_pgsql_notice, (void *)&dbh);
01074 
01075        H->attached = 1;
01076        H->pgoid = -1;
01077 
01078        dbh->methods = &pgsql_methods;
01079        dbh->alloc_own_columns = 1;
01080        dbh->max_escaped_char_length = 2;
01081 
01082        ret = 1;
01083        
01084 cleanup:
01085        dbh->methods = &pgsql_methods;
01086        if (!ret) {
01087               pgsql_handle_closer(dbh TSRMLS_CC);
01088        }
01089 
01090        return ret;
01091 }
01092 /* }}} */
01093 
01094 pdo_driver_t pdo_pgsql_driver = {
01095        PDO_DRIVER_HEADER(pgsql),
01096        pdo_pgsql_handle_factory
01097 };
01098 
01099 /*
01100  * Local variables:
01101  * tab-width: 4
01102  * c-basic-offset: 4
01103  * End:
01104  * vim600: noet sw=4 ts=4 fdm=marker
01105  * vim<600: noet sw=4 ts=4
01106  */