1/* 2 +----------------------------------------------------------------------+ 3 | PHP Version 5 | 4 +----------------------------------------------------------------------+ 5 | Copyright (c) 1997-2013 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 "pdo/php_pdo.h" 31#include "pdo/php_pdo_driver.h" 32#include "ext/standard/file.h" 33 34#undef PACKAGE_BUGREPORT 35#undef PACKAGE_NAME 36#undef PACKAGE_STRING 37#undef PACKAGE_TARNAME 38#undef PACKAGE_VERSION 39#include "pg_config.h" /* needed for PG_VERSION */ 40#include "php_pdo_pgsql.h" 41#include "php_pdo_pgsql_int.h" 42#include "zend_exceptions.h" 43 44static char * _pdo_pgsql_trim_message(const char *message, int persistent) 45{ 46 register int i = strlen(message)-1; 47 char *tmp; 48 49 if (i>1 && (message[i-1] == '\r' || message[i-1] == '\n') && message[i] == '.') { 50 --i; 51 } 52 while (i>0 && (message[i] == '\r' || message[i] == '\n')) { 53 --i; 54 } 55 ++i; 56 tmp = pemalloc(i + 1, persistent); 57 memcpy(tmp, message, i); 58 tmp[i] = '\0'; 59 60 return tmp; 61} 62 63int _pdo_pgsql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const char *sqlstate, const char *file, int line TSRMLS_DC) /* {{{ */ 64{ 65 pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; 66 pdo_error_type *pdo_err = stmt ? &stmt->error_code : &dbh->error_code; 67 pdo_pgsql_error_info *einfo = &H->einfo; 68 char *errmsg = PQerrorMessage(H->server); 69 70 einfo->errcode = errcode; 71 einfo->file = file; 72 einfo->line = line; 73 74 if (einfo->errmsg) { 75 pefree(einfo->errmsg, dbh->is_persistent); 76 einfo->errmsg = NULL; 77 } 78 79 if (sqlstate == NULL) { 80 strcpy(*pdo_err, "HY000"); 81 } 82 else { 83 strcpy(*pdo_err, sqlstate); 84 } 85 86 if (errmsg) { 87 einfo->errmsg = _pdo_pgsql_trim_message(errmsg, dbh->is_persistent); 88 } 89 90 if (!dbh->methods) { 91 zend_throw_exception_ex(php_pdo_get_exception(), einfo->errcode TSRMLS_CC, "SQLSTATE[%s] [%d] %s", 92 *pdo_err, einfo->errcode, einfo->errmsg); 93 } 94 95 return errcode; 96} 97/* }}} */ 98 99static void _pdo_pgsql_notice(pdo_dbh_t *dbh, const char *message) /* {{{ */ 100{ 101/* pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; */ 102} 103/* }}} */ 104 105static int pdo_pgsql_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info TSRMLS_DC) /* {{{ */ 106{ 107 pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; 108 pdo_pgsql_error_info *einfo = &H->einfo; 109 110 if (einfo->errcode) { 111 add_next_index_long(info, einfo->errcode); 112 add_next_index_string(info, einfo->errmsg, 1); 113 } 114 115 return 1; 116} 117/* }}} */ 118 119/* {{{ pdo_pgsql_create_lob_stream */ 120static size_t pgsql_lob_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) 121{ 122 struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract; 123 return lo_write(self->conn, self->lfd, (char*)buf, count); 124} 125 126static size_t pgsql_lob_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) 127{ 128 struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract; 129 return lo_read(self->conn, self->lfd, buf, count); 130} 131 132static int pgsql_lob_close(php_stream *stream, int close_handle TSRMLS_DC) 133{ 134 struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract; 135 pdo_dbh_t *dbh = self->dbh; 136 137 if (close_handle) { 138 lo_close(self->conn, self->lfd); 139 } 140 efree(self); 141 php_pdo_dbh_delref(dbh TSRMLS_CC); 142 return 0; 143} 144 145static int pgsql_lob_flush(php_stream *stream TSRMLS_DC) 146{ 147 return 0; 148} 149 150static int pgsql_lob_seek(php_stream *stream, off_t offset, int whence, 151 off_t *newoffset TSRMLS_DC) 152{ 153 struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract; 154 int pos = lo_lseek(self->conn, self->lfd, offset, whence); 155 *newoffset = pos; 156 return pos >= 0 ? 0 : -1; 157} 158 159php_stream_ops pdo_pgsql_lob_stream_ops = { 160 pgsql_lob_write, 161 pgsql_lob_read, 162 pgsql_lob_close, 163 pgsql_lob_flush, 164 "pdo_pgsql lob stream", 165 pgsql_lob_seek, 166 NULL, 167 NULL, 168 NULL 169}; 170 171php_stream *pdo_pgsql_create_lob_stream(pdo_dbh_t *dbh, int lfd, Oid oid TSRMLS_DC) 172{ 173 php_stream *stm; 174 struct pdo_pgsql_lob_self *self = ecalloc(1, sizeof(*self)); 175 pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; 176 177 self->dbh = dbh; 178 self->lfd = lfd; 179 self->oid = oid; 180 self->conn = H->server; 181 182 stm = php_stream_alloc(&pdo_pgsql_lob_stream_ops, self, 0, "r+b"); 183 184 if (stm) { 185 php_pdo_dbh_addref(dbh TSRMLS_CC); 186 return stm; 187 } 188 189 efree(self); 190 return NULL; 191} 192/* }}} */ 193 194static int pgsql_handle_closer(pdo_dbh_t *dbh TSRMLS_DC) /* {{{ */ 195{ 196 pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; 197 if (H) { 198 if (H->server) { 199 PQfinish(H->server); 200 H->server = NULL; 201 } 202 if (H->einfo.errmsg) { 203 pefree(H->einfo.errmsg, dbh->is_persistent); 204 H->einfo.errmsg = NULL; 205 } 206 pefree(H, dbh->is_persistent); 207 dbh->driver_data = NULL; 208 } 209 return 0; 210} 211/* }}} */ 212 213static int pgsql_handle_preparer(pdo_dbh_t *dbh, const char *sql, long sql_len, pdo_stmt_t *stmt, zval *driver_options TSRMLS_DC) 214{ 215 pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; 216 pdo_pgsql_stmt *S = ecalloc(1, sizeof(pdo_pgsql_stmt)); 217 int scrollable; 218#if HAVE_PQPREPARE 219 int ret; 220 char *nsql = NULL; 221 int nsql_len = 0; 222 int emulate = 0; 223#endif 224 225 S->H = H; 226 stmt->driver_data = S; 227 stmt->methods = &pgsql_stmt_methods; 228 229 scrollable = pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, 230 PDO_CURSOR_FWDONLY TSRMLS_CC) == PDO_CURSOR_SCROLL; 231 232 if (scrollable) { 233 if (S->cursor_name) { 234 efree(S->cursor_name); 235 } 236 spprintf(&S->cursor_name, 0, "pdo_crsr_%08x", ++H->stmt_counter); 237#if HAVE_PQPREPARE 238 emulate = 1; 239#endif 240 } 241 242#if HAVE_PQPREPARE 243 else if (driver_options) { 244 if (pdo_attr_lval(driver_options, PDO_PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT, H->disable_native_prepares TSRMLS_CC) == 1 || 245 pdo_attr_lval(driver_options, PDO_ATTR_EMULATE_PREPARES, H->emulate_prepares TSRMLS_CC) == 1) { 246 emulate = 1; 247 } 248 } else { 249 emulate = H->disable_native_prepares || H->emulate_prepares; 250 } 251 252 if (!emulate && PQprotocolVersion(H->server) > 2) { 253 stmt->supports_placeholders = PDO_PLACEHOLDER_NAMED; 254 stmt->named_rewrite_template = "$%d"; 255 ret = pdo_parse_params(stmt, (char*)sql, sql_len, &nsql, &nsql_len TSRMLS_CC); 256 257 if (ret == 1) { 258 /* query was re-written */ 259 sql = nsql; 260 } else if (ret == -1) { 261 /* couldn't grok it */ 262 strcpy(dbh->error_code, stmt->error_code); 263 return 0; 264 } 265 266 spprintf(&S->stmt_name, 0, "pdo_stmt_%08x", ++H->stmt_counter); 267 /* that's all for now; we'll defer the actual prepare until the first execute call */ 268 269 if (nsql) { 270 S->query = nsql; 271 } else { 272 S->query = estrdup(sql); 273 } 274 275 return 1; 276 } 277#endif 278 279 stmt->supports_placeholders = PDO_PLACEHOLDER_NONE; 280 return 1; 281} 282 283static long pgsql_handle_doer(pdo_dbh_t *dbh, const char *sql, long sql_len TSRMLS_DC) 284{ 285 pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; 286 PGresult *res; 287 long ret = 1; 288 ExecStatusType qs; 289 290 if (!(res = PQexec(H->server, sql))) { 291 /* fatal error */ 292 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); 293 return -1; 294 } 295 qs = PQresultStatus(res); 296 if (qs != PGRES_COMMAND_OK && qs != PGRES_TUPLES_OK) { 297 pdo_pgsql_error(dbh, qs, pdo_pgsql_sqlstate(res)); 298 PQclear(res); 299 return -1; 300 } 301 H->pgoid = PQoidValue(res); 302 ret = (qs == PGRES_COMMAND_OK) ? atol(PQcmdTuples(res)) : 0L; 303 PQclear(res); 304 305 return ret; 306} 307 308static int pgsql_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, int unquotedlen, char **quoted, int *quotedlen, enum pdo_param_type paramtype TSRMLS_DC) 309{ 310 unsigned char *escaped; 311 pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; 312 size_t tmp_len; 313 314 switch (paramtype) { 315 case PDO_PARAM_LOB: 316 /* escapedlen returned by PQescapeBytea() accounts for trailing 0 */ 317#ifdef HAVE_PQESCAPE_BYTEA_CONN 318 escaped = PQescapeByteaConn(H->server, unquoted, unquotedlen, &tmp_len); 319#else 320 escaped = PQescapeBytea(unquoted, unquotedlen, &tmp_len); 321#endif 322 *quotedlen = (int)tmp_len + 1; 323 *quoted = emalloc(*quotedlen + 1); 324 memcpy((*quoted)+1, escaped, *quotedlen-2); 325 (*quoted)[0] = '\''; 326 (*quoted)[*quotedlen-1] = '\''; 327 (*quoted)[*quotedlen] = '\0'; 328 PQfreemem(escaped); 329 break; 330 default: 331 *quoted = safe_emalloc(2, unquotedlen, 3); 332 (*quoted)[0] = '\''; 333#ifndef HAVE_PQESCAPE_CONN 334 *quotedlen = PQescapeString(*quoted + 1, unquoted, unquotedlen); 335#else 336 *quotedlen = PQescapeStringConn(H->server, *quoted + 1, unquoted, unquotedlen, NULL); 337#endif 338 (*quoted)[*quotedlen + 1] = '\''; 339 (*quoted)[*quotedlen + 2] = '\0'; 340 *quotedlen += 2; 341 } 342 return 1; 343} 344 345static char *pdo_pgsql_last_insert_id(pdo_dbh_t *dbh, const char *name, unsigned int *len TSRMLS_DC) 346{ 347 pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; 348 char *id = NULL; 349 350 if (name == NULL) { 351 if (H->pgoid == InvalidOid) { 352 return NULL; 353 } 354 *len = spprintf(&id, 0, "%ld", (long) H->pgoid); 355 } else { 356 PGresult *res; 357 ExecStatusType status; 358 const char *q[1]; 359 q[0] = name; 360 res = PQexecParams(H->server, "SELECT CURRVAL($1)", 1, NULL, q, NULL, NULL, 0); 361 status = PQresultStatus(res); 362 363 if (res && (status == PGRES_TUPLES_OK)) { 364 id = estrdup((char *)PQgetvalue(res, 0, 0)); 365 *len = PQgetlength(res, 0, 0); 366 } else { 367 pdo_pgsql_error(dbh, status, pdo_pgsql_sqlstate(res)); 368 } 369 370 if (res) { 371 PQclear(res); 372 } 373 } 374 return id; 375} 376 377static int pdo_pgsql_get_attribute(pdo_dbh_t *dbh, long attr, zval *return_value TSRMLS_DC) 378{ 379 pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; 380 381 switch (attr) { 382 case PDO_ATTR_CLIENT_VERSION: 383 ZVAL_STRING(return_value, PG_VERSION, 1); 384 break; 385 386 case PDO_ATTR_SERVER_VERSION: 387 if (PQprotocolVersion(H->server) >= 3) { /* PostgreSQL 7.4 or later */ 388 ZVAL_STRING(return_value, (char*)PQparameterStatus(H->server, "server_version"), 1); 389 } else /* emulate above via a query */ 390 { 391 PGresult *res = PQexec(H->server, "SELECT VERSION()"); 392 if (res && PQresultStatus(res) == PGRES_TUPLES_OK) { 393 ZVAL_STRING(return_value, (char *)PQgetvalue(res, 0, 0), 1); 394 } 395 396 if (res) { 397 PQclear(res); 398 } 399 } 400 break; 401 402 case PDO_ATTR_CONNECTION_STATUS: 403 switch (PQstatus(H->server)) { 404 case CONNECTION_STARTED: 405 ZVAL_STRINGL(return_value, "Waiting for connection to be made.", sizeof("Waiting for connection to be made.")-1, 1); 406 break; 407 408 case CONNECTION_MADE: 409 case CONNECTION_OK: 410 ZVAL_STRINGL(return_value, "Connection OK; waiting to send.", sizeof("Connection OK; waiting to send.")-1, 1); 411 break; 412 413 case CONNECTION_AWAITING_RESPONSE: 414 ZVAL_STRINGL(return_value, "Waiting for a response from the server.", sizeof("Waiting for a response from the server.")-1, 1); 415 break; 416 417 case CONNECTION_AUTH_OK: 418 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); 419 break; 420#ifdef CONNECTION_SSL_STARTUP 421 case CONNECTION_SSL_STARTUP: 422 ZVAL_STRINGL(return_value, "Negotiating SSL encryption.", sizeof("Negotiating SSL encryption.")-1, 1); 423 break; 424#endif 425 case CONNECTION_SETENV: 426 ZVAL_STRINGL(return_value, "Negotiating environment-driven parameter settings.", sizeof("Negotiating environment-driven parameter settings.")-1, 1); 427 break; 428 429 case CONNECTION_BAD: 430 default: 431 ZVAL_STRINGL(return_value, "Bad connection.", sizeof("Bad connection.")-1, 1); 432 break; 433 } 434 break; 435 436 case PDO_ATTR_SERVER_INFO: { 437 int spid = PQbackendPID(H->server); 438 char *tmp; 439 spprintf(&tmp, 0, 440 "PID: %d; Client Encoding: %s; Is Superuser: %s; Session Authorization: %s; Date Style: %s", 441 spid, 442 (char*)PQparameterStatus(H->server, "client_encoding"), 443 (char*)PQparameterStatus(H->server, "is_superuser"), 444 (char*)PQparameterStatus(H->server, "session_authorization"), 445 (char*)PQparameterStatus(H->server, "DateStyle")); 446 ZVAL_STRING(return_value, tmp, 0); 447 } 448 break; 449 450 default: 451 return 0; 452 } 453 454 return 1; 455} 456 457/* {{{ */ 458static int pdo_pgsql_check_liveness(pdo_dbh_t *dbh TSRMLS_DC) 459{ 460 pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; 461 if (PQstatus(H->server) == CONNECTION_BAD) { 462 PQreset(H->server); 463 } 464 return (PQstatus(H->server) == CONNECTION_OK) ? SUCCESS : FAILURE; 465} 466/* }}} */ 467 468static int pdo_pgsql_transaction_cmd(const char *cmd, pdo_dbh_t *dbh TSRMLS_DC) 469{ 470 pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; 471 PGresult *res; 472 int ret = 1; 473 474 res = PQexec(H->server, cmd); 475 476 if (PQresultStatus(res) != PGRES_COMMAND_OK) { 477 pdo_pgsql_error(dbh, PQresultStatus(res), pdo_pgsql_sqlstate(res)); 478 ret = 0; 479 } 480 481 PQclear(res); 482 return ret; 483} 484 485static int pgsql_handle_begin(pdo_dbh_t *dbh TSRMLS_DC) 486{ 487 return pdo_pgsql_transaction_cmd("BEGIN", dbh TSRMLS_CC); 488} 489 490static int pgsql_handle_commit(pdo_dbh_t *dbh TSRMLS_DC) 491{ 492 return pdo_pgsql_transaction_cmd("COMMIT", dbh TSRMLS_CC); 493} 494 495static int pgsql_handle_rollback(pdo_dbh_t *dbh TSRMLS_DC) 496{ 497 return pdo_pgsql_transaction_cmd("ROLLBACK", dbh TSRMLS_CC); 498} 499 500/* {{{ proto string PDO::pgsqlCopyFromArray(string $table_name , array $rows [, string $delimiter [, string $null_as ] [, string $fields]) 501 Returns true if the copy worked fine or false if error */ 502static PHP_METHOD(PDO, pgsqlCopyFromArray) 503{ 504 pdo_dbh_t *dbh; 505 pdo_pgsql_db_handle *H; 506 507 zval *pg_rows; 508 509 char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL; 510 int table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len; 511 char *query; 512 513 PGresult *pgsql_result; 514 ExecStatusType status; 515 516 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s/a|sss", 517 &table_name, &table_name_len, &pg_rows, 518 &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) { 519 return; 520 } 521 522 if (!zend_hash_num_elements(Z_ARRVAL_P(pg_rows))) { 523 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot copy from an empty array"); 524 RETURN_FALSE; 525 } 526 527 dbh = zend_object_store_get_object(getThis() TSRMLS_CC); 528 PDO_CONSTRUCT_CHECK; 529 530 if (pg_fields) { 531 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")); 532 } else { 533 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")); 534 } 535 536 /* Obtain db Handle */ 537 H = (pdo_pgsql_db_handle *)dbh->driver_data; 538 539 while ((pgsql_result = PQgetResult(H->server))) { 540 PQclear(pgsql_result); 541 } 542 pgsql_result = PQexec(H->server, query); 543 544 efree(query); 545 query = NULL; 546 547 if (pgsql_result) { 548 status = PQresultStatus(pgsql_result); 549 } else { 550 status = (ExecStatusType) PQstatus(H->server); 551 } 552 553 if (status == PGRES_COPY_IN && pgsql_result) { 554 int command_failed = 0; 555 int buffer_len = 0; 556 zval **tmp; 557 HashPosition pos; 558 559 PQclear(pgsql_result); 560 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(pg_rows), &pos); 561 while (zend_hash_get_current_data_ex(Z_ARRVAL_P(pg_rows), (void **) &tmp, &pos) == SUCCESS) { 562 int query_len; 563 convert_to_string_ex(tmp); 564 565 if (buffer_len < Z_STRLEN_PP(tmp)) { 566 buffer_len = Z_STRLEN_PP(tmp); 567 query = erealloc(query, buffer_len + 2); /* room for \n\0 */ 568 } 569 memcpy(query, Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp)); 570 query_len = Z_STRLEN_PP(tmp); 571 if (query[query_len - 1] != '\n') { 572 query[query_len++] = '\n'; 573 } 574 query[query_len] = '\0'; 575 if (PQputCopyData(H->server, query, query_len) != 1) { 576 efree(query); 577 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "copy failed"); 578 RETURN_FALSE; 579 } 580 zend_hash_move_forward_ex(Z_ARRVAL_P(pg_rows), &pos); 581 } 582 if (query) { 583 efree(query); 584 } 585 586 if (PQputCopyEnd(H->server, NULL) != 1) { 587 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "putcopyend failed"); 588 RETURN_FALSE; 589 } 590 591 while ((pgsql_result = PQgetResult(H->server))) { 592 if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) { 593 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed"); 594 command_failed = 1; 595 } 596 PQclear(pgsql_result); 597 } 598 599 RETURN_BOOL(!command_failed); 600 } else { 601 PQclear(pgsql_result); 602 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed"); 603 RETURN_FALSE; 604 } 605} 606/* }}} */ 607 608/* {{{ proto string PDO::pgsqlCopyFromFile(string $table_name , string $filename [, string $delimiter [, string $null_as ] [, string $fields]) 609 Returns true if the copy worked fine or false if error */ 610static PHP_METHOD(PDO, pgsqlCopyFromFile) 611{ 612 pdo_dbh_t *dbh; 613 pdo_pgsql_db_handle *H; 614 615 char *table_name, *filename, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL; 616 int table_name_len, filename_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len; 617 char *query; 618 PGresult *pgsql_result; 619 ExecStatusType status; 620 php_stream *stream; 621 622 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|sss", 623 &table_name, &table_name_len, &filename, &filename_len, 624 &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) { 625 return; 626 } 627 628 /* Obtain db Handler */ 629 dbh = zend_object_store_get_object(getThis() TSRMLS_CC); 630 PDO_CONSTRUCT_CHECK; 631 632 stream = php_stream_open_wrapper_ex(filename, "rb", ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL, FG(default_context)); 633 if (!stream) { 634 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Unable to open the file"); 635 RETURN_FALSE; 636 } 637 638 if (pg_fields) { 639 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")); 640 } else { 641 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")); 642 } 643 644 H = (pdo_pgsql_db_handle *)dbh->driver_data; 645 646 while ((pgsql_result = PQgetResult(H->server))) { 647 PQclear(pgsql_result); 648 } 649 pgsql_result = PQexec(H->server, query); 650 651 efree(query); 652 653 if (pgsql_result) { 654 status = PQresultStatus(pgsql_result); 655 } else { 656 status = (ExecStatusType) PQstatus(H->server); 657 } 658 659 if (status == PGRES_COPY_IN && pgsql_result) { 660 char *buf; 661 int command_failed = 0; 662 size_t line_len = 0; 663 664 PQclear(pgsql_result); 665 while ((buf = php_stream_get_line(stream, NULL, 0, &line_len)) != NULL) { 666 if (PQputCopyData(H->server, buf, line_len) != 1) { 667 efree(buf); 668 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "copy failed"); 669 php_stream_close(stream); 670 RETURN_FALSE; 671 } 672 efree(buf); 673 } 674 php_stream_close(stream); 675 676 if (PQputCopyEnd(H->server, NULL) != 1) { 677 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "putcopyend failed"); 678 RETURN_FALSE; 679 } 680 681 while ((pgsql_result = PQgetResult(H->server))) { 682 if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) { 683 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed"); 684 command_failed = 1; 685 } 686 PQclear(pgsql_result); 687 } 688 689 RETURN_BOOL(!command_failed); 690 } else { 691 PQclear(pgsql_result); 692 php_stream_close(stream); 693 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed"); 694 RETURN_FALSE; 695 } 696} 697/* }}} */ 698 699 700/* {{{ proto string PDO::pgsqlCopyToFile(string $table_name , $filename, [string $delimiter [, string $null_as [, string $fields]]]) 701 Returns true if the copy worked fine or false if error */ 702static PHP_METHOD(PDO, pgsqlCopyToFile) 703{ 704 pdo_dbh_t *dbh; 705 pdo_pgsql_db_handle *H; 706 707 char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL, *filename = NULL; 708 int table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len, filename_len; 709 char *query; 710 711 PGresult *pgsql_result; 712 ExecStatusType status; 713 714 php_stream *stream; 715 716 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|sss", 717 &table_name, &table_name_len, &filename, &filename_len, 718 &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) { 719 return; 720 } 721 722 dbh = zend_object_store_get_object(getThis() TSRMLS_CC); 723 PDO_CONSTRUCT_CHECK; 724 725 H = (pdo_pgsql_db_handle *)dbh->driver_data; 726 727 stream = php_stream_open_wrapper_ex(filename, "wb", ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL, FG(default_context)); 728 if (!stream) { 729 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Unable to open the file for writing"); 730 RETURN_FALSE; 731 } 732 733 while ((pgsql_result = PQgetResult(H->server))) { 734 PQclear(pgsql_result); 735 } 736 737 if (pg_fields) { 738 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")); 739 } else { 740 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")); 741 } 742 pgsql_result = PQexec(H->server, query); 743 efree(query); 744 745 if (pgsql_result) { 746 status = PQresultStatus(pgsql_result); 747 } else { 748 status = (ExecStatusType) PQstatus(H->server); 749 } 750 751 if (status == PGRES_COPY_OUT && pgsql_result) { 752 PQclear(pgsql_result); 753 while (1) { 754 char *csv = NULL; 755 int ret = PQgetCopyData(H->server, &csv, 0); 756 757 if (ret == -1) { 758 break; /* done */ 759 } else if (ret > 0) { 760 if (php_stream_write(stream, csv, ret) != ret) { 761 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Unable to write to file"); 762 PQfreemem(csv); 763 php_stream_close(stream); 764 RETURN_FALSE; 765 } else { 766 PQfreemem(csv); 767 } 768 } else { 769 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed: getline failed"); 770 php_stream_close(stream); 771 RETURN_FALSE; 772 } 773 } 774 php_stream_close(stream); 775 776 while ((pgsql_result = PQgetResult(H->server))) { 777 PQclear(pgsql_result); 778 } 779 RETURN_TRUE; 780 } else { 781 php_stream_close(stream); 782 PQclear(pgsql_result); 783 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed"); 784 RETURN_FALSE; 785 } 786} 787/* }}} */ 788 789/* {{{ proto string PDO::pgsqlCopyToArray(string $table_name , [string $delimiter [, string $null_as [, string $fields]]]) 790 Returns true if the copy worked fine or false if error */ 791static PHP_METHOD(PDO, pgsqlCopyToArray) 792{ 793 pdo_dbh_t *dbh; 794 pdo_pgsql_db_handle *H; 795 796 char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL; 797 int table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len; 798 char *query; 799 800 PGresult *pgsql_result; 801 ExecStatusType status; 802 803 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|sss", 804 &table_name, &table_name_len, 805 &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) { 806 return; 807 } 808 809 dbh = zend_object_store_get_object(getThis() TSRMLS_CC); 810 PDO_CONSTRUCT_CHECK; 811 812 H = (pdo_pgsql_db_handle *)dbh->driver_data; 813 814 while ((pgsql_result = PQgetResult(H->server))) { 815 PQclear(pgsql_result); 816 } 817 818 if (pg_fields) { 819 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")); 820 } else { 821 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")); 822 } 823 pgsql_result = PQexec(H->server, query); 824 efree(query); 825 826 if (pgsql_result) { 827 status = PQresultStatus(pgsql_result); 828 } else { 829 status = (ExecStatusType) PQstatus(H->server); 830 } 831 832 if (status == PGRES_COPY_OUT && pgsql_result) { 833 PQclear(pgsql_result); 834 array_init(return_value); 835 836 while (1) { 837 char *csv = NULL; 838 int ret = PQgetCopyData(H->server, &csv, 0); 839 if (ret == -1) { 840 break; /* copy done */ 841 } else if (ret > 0) { 842 add_next_index_stringl(return_value, csv, ret, 1); 843 PQfreemem(csv); 844 } else { 845 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed: getline failed"); 846 RETURN_FALSE; 847 } 848 } 849 850 while ((pgsql_result = PQgetResult(H->server))) { 851 PQclear(pgsql_result); 852 } 853 } else { 854 PQclear(pgsql_result); 855 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed"); 856 RETURN_FALSE; 857 } 858} 859/* }}} */ 860 861 862/* {{{ proto string PDO::pgsqlLOBCreate() 863 Creates a new large object, returning its identifier. Must be called inside a transaction. */ 864static PHP_METHOD(PDO, pgsqlLOBCreate) 865{ 866 pdo_dbh_t *dbh; 867 pdo_pgsql_db_handle *H; 868 Oid lfd; 869 870 dbh = zend_object_store_get_object(getThis() TSRMLS_CC); 871 PDO_CONSTRUCT_CHECK; 872 873 H = (pdo_pgsql_db_handle *)dbh->driver_data; 874 lfd = lo_creat(H->server, INV_READ|INV_WRITE); 875 876 if (lfd != InvalidOid) { 877 char *buf; 878 spprintf(&buf, 0, "%lu", (long) lfd); 879 RETURN_STRING(buf, 0); 880 } 881 882 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "HY000"); 883 RETURN_FALSE; 884} 885/* }}} */ 886 887/* {{{ proto resource PDO::pgsqlLOBOpen(string oid [, string mode = 'rb']) 888 Opens an existing large object stream. Must be called inside a transaction. */ 889static PHP_METHOD(PDO, pgsqlLOBOpen) 890{ 891 pdo_dbh_t *dbh; 892 pdo_pgsql_db_handle *H; 893 Oid oid; 894 int lfd; 895 char *oidstr; 896 int oidstrlen; 897 char *modestr = "rb"; 898 int modestrlen; 899 int mode = INV_READ; 900 char *end_ptr; 901 902 if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", 903 &oidstr, &oidstrlen, &modestr, &modestrlen)) { 904 RETURN_FALSE; 905 } 906 907 oid = (Oid)strtoul(oidstr, &end_ptr, 10); 908 if (oid == 0 && (errno == ERANGE || errno == EINVAL)) { 909 RETURN_FALSE; 910 } 911 912 if (strpbrk(modestr, "+w")) { 913 mode = INV_READ|INV_WRITE; 914 } 915 916 dbh = zend_object_store_get_object(getThis() TSRMLS_CC); 917 PDO_CONSTRUCT_CHECK; 918 919 H = (pdo_pgsql_db_handle *)dbh->driver_data; 920 921 lfd = lo_open(H->server, oid, mode); 922 923 if (lfd >= 0) { 924 php_stream *stream = pdo_pgsql_create_lob_stream(dbh, lfd, oid TSRMLS_CC); 925 if (stream) { 926 php_stream_to_zval(stream, return_value); 927 return; 928 } 929 } else { 930 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "HY000"); 931 } 932 RETURN_FALSE; 933} 934/* }}} */ 935 936/* {{{ proto bool PDO::pgsqlLOBUnlink(string oid) 937 Deletes the large object identified by oid. Must be called inside a transaction. */ 938static PHP_METHOD(PDO, pgsqlLOBUnlink) 939{ 940 pdo_dbh_t *dbh; 941 pdo_pgsql_db_handle *H; 942 Oid oid; 943 char *oidstr, *end_ptr; 944 int oidlen; 945 946 if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", 947 &oidstr, &oidlen)) { 948 RETURN_FALSE; 949 } 950 951 oid = (Oid)strtoul(oidstr, &end_ptr, 10); 952 if (oid == 0 && (errno == ERANGE || errno == EINVAL)) { 953 RETURN_FALSE; 954 } 955 956 dbh = zend_object_store_get_object(getThis() TSRMLS_CC); 957 PDO_CONSTRUCT_CHECK; 958 959 H = (pdo_pgsql_db_handle *)dbh->driver_data; 960 961 if (1 == lo_unlink(H->server, oid)) { 962 RETURN_TRUE; 963 } 964 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "HY000"); 965 RETURN_FALSE; 966} 967/* }}} */ 968 969 970static const zend_function_entry dbh_methods[] = { 971 PHP_ME(PDO, pgsqlLOBCreate, NULL, ZEND_ACC_PUBLIC) 972 PHP_ME(PDO, pgsqlLOBOpen, NULL, ZEND_ACC_PUBLIC) 973 PHP_ME(PDO, pgsqlLOBUnlink, NULL, ZEND_ACC_PUBLIC) 974 PHP_ME(PDO, pgsqlCopyFromArray, NULL, ZEND_ACC_PUBLIC) 975 PHP_ME(PDO, pgsqlCopyFromFile, NULL, ZEND_ACC_PUBLIC) 976 PHP_ME(PDO, pgsqlCopyToArray, NULL, ZEND_ACC_PUBLIC) 977 PHP_ME(PDO, pgsqlCopyToFile, NULL, ZEND_ACC_PUBLIC) 978 PHP_FE_END 979}; 980 981static const zend_function_entry *pdo_pgsql_get_driver_methods(pdo_dbh_t *dbh, int kind TSRMLS_DC) 982{ 983 switch (kind) { 984 case PDO_DBH_DRIVER_METHOD_KIND_DBH: 985 return dbh_methods; 986 default: 987 return NULL; 988 } 989} 990 991static int pdo_pgsql_set_attr(pdo_dbh_t *dbh, long attr, zval *val TSRMLS_DC) 992{ 993 pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; 994 995 switch (attr) { 996#if HAVE_PQPREPARE 997 case PDO_ATTR_EMULATE_PREPARES: 998 H->emulate_prepares = Z_LVAL_P(val); 999 return 1; 1000 case PDO_PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT: 1001 H->disable_native_prepares = Z_LVAL_P(val); 1002 return 1; 1003#endif 1004 1005 default: 1006 return 0; 1007 } 1008} 1009 1010static struct pdo_dbh_methods pgsql_methods = { 1011 pgsql_handle_closer, 1012 pgsql_handle_preparer, 1013 pgsql_handle_doer, 1014 pgsql_handle_quoter, 1015 pgsql_handle_begin, 1016 pgsql_handle_commit, 1017 pgsql_handle_rollback, 1018 pdo_pgsql_set_attr, 1019 pdo_pgsql_last_insert_id, 1020 pdo_pgsql_fetch_error_func, 1021 pdo_pgsql_get_attribute, 1022 pdo_pgsql_check_liveness, /* check_liveness */ 1023 pdo_pgsql_get_driver_methods, /* get_driver_methods */ 1024 NULL, 1025}; 1026 1027static int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_DC) /* {{{ */ 1028{ 1029 pdo_pgsql_db_handle *H; 1030 int ret = 0; 1031 char *conn_str, *p, *e; 1032 long connect_timeout = 30; 1033 1034 H = pecalloc(1, sizeof(pdo_pgsql_db_handle), dbh->is_persistent); 1035 dbh->driver_data = H; 1036 1037 H->einfo.errcode = 0; 1038 H->einfo.errmsg = NULL; 1039 1040 /* PostgreSQL wants params in the connect string to be separated by spaces, 1041 * if the PDO standard semicolons are used, we convert them to spaces 1042 */ 1043 e = (char *) dbh->data_source + strlen(dbh->data_source); 1044 p = (char *) dbh->data_source; 1045 while ((p = memchr(p, ';', (e - p)))) { 1046 *p = ' '; 1047 } 1048 1049 if (driver_options) { 1050 connect_timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, 30 TSRMLS_CC); 1051 } 1052 1053 /* support both full connection string & connection string + login and/or password */ 1054 if (dbh->username && dbh->password) { 1055 spprintf(&conn_str, 0, "%s user=%s password=%s connect_timeout=%ld", dbh->data_source, dbh->username, dbh->password, connect_timeout); 1056 } else if (dbh->username) { 1057 spprintf(&conn_str, 0, "%s user=%s connect_timeout=%ld", dbh->data_source, dbh->username, connect_timeout); 1058 } else if (dbh->password) { 1059 spprintf(&conn_str, 0, "%s password=%s connect_timeout=%ld", dbh->data_source, dbh->password, connect_timeout); 1060 } else { 1061 spprintf(&conn_str, 0, "%s connect_timeout=%ld", (char *) dbh->data_source, connect_timeout); 1062 } 1063 1064 H->server = PQconnectdb(conn_str); 1065 1066 efree(conn_str); 1067 1068 if (PQstatus(H->server) != CONNECTION_OK) { 1069 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, PHP_PDO_PGSQL_CONNECTION_FAILURE_SQLSTATE); 1070 goto cleanup; 1071 } 1072 1073 PQsetNoticeProcessor(H->server, (void(*)(void*,const char*))_pdo_pgsql_notice, (void *)&dbh); 1074 1075 H->attached = 1; 1076 H->pgoid = -1; 1077 1078 dbh->methods = &pgsql_methods; 1079 dbh->alloc_own_columns = 1; 1080 dbh->max_escaped_char_length = 2; 1081 1082 ret = 1; 1083 1084cleanup: 1085 dbh->methods = &pgsql_methods; 1086 if (!ret) { 1087 pgsql_handle_closer(dbh TSRMLS_CC); 1088 } 1089 1090 return ret; 1091} 1092/* }}} */ 1093 1094pdo_driver_t pdo_pgsql_driver = { 1095 PDO_DRIVER_HEADER(pgsql), 1096 pdo_pgsql_handle_factory 1097}; 1098 1099/* 1100 * Local variables: 1101 * tab-width: 4 1102 * c-basic-offset: 4 1103 * End: 1104 * vim600: noet sw=4 ts=4 fdm=marker 1105 * vim<600: noet sw=4 ts=4 1106 */ 1107