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 500static int pgsql_handle_in_transaction(pdo_dbh_t *dbh TSRMLS_DC) 501{ 502 pdo_pgsql_db_handle *H; 503 504 H = (pdo_pgsql_db_handle *)dbh->driver_data; 505 506 return PQtransactionStatus(H->server); 507} 508 509/* {{{ proto string PDO::pgsqlCopyFromArray(string $table_name , array $rows [, string $delimiter [, string $null_as ] [, string $fields]) 510 Returns true if the copy worked fine or false if error */ 511static PHP_METHOD(PDO, pgsqlCopyFromArray) 512{ 513 pdo_dbh_t *dbh; 514 pdo_pgsql_db_handle *H; 515 516 zval *pg_rows; 517 518 char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL; 519 int table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len; 520 char *query; 521 522 PGresult *pgsql_result; 523 ExecStatusType status; 524 525 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s/a|sss", 526 &table_name, &table_name_len, &pg_rows, 527 &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) { 528 return; 529 } 530 531 if (!zend_hash_num_elements(Z_ARRVAL_P(pg_rows))) { 532 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot copy from an empty array"); 533 RETURN_FALSE; 534 } 535 536 dbh = zend_object_store_get_object(getThis() TSRMLS_CC); 537 PDO_CONSTRUCT_CHECK; 538 539 if (pg_fields) { 540 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")); 541 } else { 542 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")); 543 } 544 545 /* Obtain db Handle */ 546 H = (pdo_pgsql_db_handle *)dbh->driver_data; 547 548 while ((pgsql_result = PQgetResult(H->server))) { 549 PQclear(pgsql_result); 550 } 551 pgsql_result = PQexec(H->server, query); 552 553 efree(query); 554 query = NULL; 555 556 if (pgsql_result) { 557 status = PQresultStatus(pgsql_result); 558 } else { 559 status = (ExecStatusType) PQstatus(H->server); 560 } 561 562 if (status == PGRES_COPY_IN && pgsql_result) { 563 int command_failed = 0; 564 int buffer_len = 0; 565 zval **tmp; 566 HashPosition pos; 567 568 PQclear(pgsql_result); 569 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(pg_rows), &pos); 570 while (zend_hash_get_current_data_ex(Z_ARRVAL_P(pg_rows), (void **) &tmp, &pos) == SUCCESS) { 571 int query_len; 572 convert_to_string_ex(tmp); 573 574 if (buffer_len < Z_STRLEN_PP(tmp)) { 575 buffer_len = Z_STRLEN_PP(tmp); 576 query = erealloc(query, buffer_len + 2); /* room for \n\0 */ 577 } 578 memcpy(query, Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp)); 579 query_len = Z_STRLEN_PP(tmp); 580 if (query[query_len - 1] != '\n') { 581 query[query_len++] = '\n'; 582 } 583 query[query_len] = '\0'; 584 if (PQputCopyData(H->server, query, query_len) != 1) { 585 efree(query); 586 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "copy failed"); 587 RETURN_FALSE; 588 } 589 zend_hash_move_forward_ex(Z_ARRVAL_P(pg_rows), &pos); 590 } 591 if (query) { 592 efree(query); 593 } 594 595 if (PQputCopyEnd(H->server, NULL) != 1) { 596 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "putcopyend failed"); 597 RETURN_FALSE; 598 } 599 600 while ((pgsql_result = PQgetResult(H->server))) { 601 if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) { 602 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed"); 603 command_failed = 1; 604 } 605 PQclear(pgsql_result); 606 } 607 608 RETURN_BOOL(!command_failed); 609 } else { 610 PQclear(pgsql_result); 611 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed"); 612 RETURN_FALSE; 613 } 614} 615/* }}} */ 616 617/* {{{ proto string PDO::pgsqlCopyFromFile(string $table_name , string $filename [, string $delimiter [, string $null_as ] [, string $fields]) 618 Returns true if the copy worked fine or false if error */ 619static PHP_METHOD(PDO, pgsqlCopyFromFile) 620{ 621 pdo_dbh_t *dbh; 622 pdo_pgsql_db_handle *H; 623 624 char *table_name, *filename, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL; 625 int table_name_len, filename_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len; 626 char *query; 627 PGresult *pgsql_result; 628 ExecStatusType status; 629 php_stream *stream; 630 631 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sp|sss", 632 &table_name, &table_name_len, &filename, &filename_len, 633 &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) { 634 return; 635 } 636 637 /* Obtain db Handler */ 638 dbh = zend_object_store_get_object(getThis() TSRMLS_CC); 639 PDO_CONSTRUCT_CHECK; 640 641 stream = php_stream_open_wrapper_ex(filename, "rb", ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL, FG(default_context)); 642 if (!stream) { 643 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Unable to open the file"); 644 RETURN_FALSE; 645 } 646 647 if (pg_fields) { 648 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")); 649 } else { 650 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")); 651 } 652 653 H = (pdo_pgsql_db_handle *)dbh->driver_data; 654 655 while ((pgsql_result = PQgetResult(H->server))) { 656 PQclear(pgsql_result); 657 } 658 pgsql_result = PQexec(H->server, query); 659 660 efree(query); 661 662 if (pgsql_result) { 663 status = PQresultStatus(pgsql_result); 664 } else { 665 status = (ExecStatusType) PQstatus(H->server); 666 } 667 668 if (status == PGRES_COPY_IN && pgsql_result) { 669 char *buf; 670 int command_failed = 0; 671 size_t line_len = 0; 672 673 PQclear(pgsql_result); 674 while ((buf = php_stream_get_line(stream, NULL, 0, &line_len)) != NULL) { 675 if (PQputCopyData(H->server, buf, line_len) != 1) { 676 efree(buf); 677 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "copy failed"); 678 php_stream_close(stream); 679 RETURN_FALSE; 680 } 681 efree(buf); 682 } 683 php_stream_close(stream); 684 685 if (PQputCopyEnd(H->server, NULL) != 1) { 686 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "putcopyend failed"); 687 RETURN_FALSE; 688 } 689 690 while ((pgsql_result = PQgetResult(H->server))) { 691 if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) { 692 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed"); 693 command_failed = 1; 694 } 695 PQclear(pgsql_result); 696 } 697 698 RETURN_BOOL(!command_failed); 699 } else { 700 PQclear(pgsql_result); 701 php_stream_close(stream); 702 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed"); 703 RETURN_FALSE; 704 } 705} 706/* }}} */ 707 708 709/* {{{ proto string PDO::pgsqlCopyToFile(string $table_name , $filename, [string $delimiter [, string $null_as [, string $fields]]]) 710 Returns true if the copy worked fine or false if error */ 711static PHP_METHOD(PDO, pgsqlCopyToFile) 712{ 713 pdo_dbh_t *dbh; 714 pdo_pgsql_db_handle *H; 715 716 char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL, *filename = NULL; 717 int table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len, filename_len; 718 char *query; 719 720 PGresult *pgsql_result; 721 ExecStatusType status; 722 723 php_stream *stream; 724 725 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sp|sss", 726 &table_name, &table_name_len, &filename, &filename_len, 727 &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) { 728 return; 729 } 730 731 dbh = zend_object_store_get_object(getThis() TSRMLS_CC); 732 PDO_CONSTRUCT_CHECK; 733 734 H = (pdo_pgsql_db_handle *)dbh->driver_data; 735 736 stream = php_stream_open_wrapper_ex(filename, "wb", ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL, FG(default_context)); 737 if (!stream) { 738 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Unable to open the file for writing"); 739 RETURN_FALSE; 740 } 741 742 while ((pgsql_result = PQgetResult(H->server))) { 743 PQclear(pgsql_result); 744 } 745 746 if (pg_fields) { 747 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")); 748 } else { 749 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")); 750 } 751 pgsql_result = PQexec(H->server, query); 752 efree(query); 753 754 if (pgsql_result) { 755 status = PQresultStatus(pgsql_result); 756 } else { 757 status = (ExecStatusType) PQstatus(H->server); 758 } 759 760 if (status == PGRES_COPY_OUT && pgsql_result) { 761 PQclear(pgsql_result); 762 while (1) { 763 char *csv = NULL; 764 int ret = PQgetCopyData(H->server, &csv, 0); 765 766 if (ret == -1) { 767 break; /* done */ 768 } else if (ret > 0) { 769 if (php_stream_write(stream, csv, ret) != ret) { 770 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Unable to write to file"); 771 PQfreemem(csv); 772 php_stream_close(stream); 773 RETURN_FALSE; 774 } else { 775 PQfreemem(csv); 776 } 777 } else { 778 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed: getline failed"); 779 php_stream_close(stream); 780 RETURN_FALSE; 781 } 782 } 783 php_stream_close(stream); 784 785 while ((pgsql_result = PQgetResult(H->server))) { 786 PQclear(pgsql_result); 787 } 788 RETURN_TRUE; 789 } else { 790 php_stream_close(stream); 791 PQclear(pgsql_result); 792 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed"); 793 RETURN_FALSE; 794 } 795} 796/* }}} */ 797 798/* {{{ proto string PDO::pgsqlCopyToArray(string $table_name , [string $delimiter [, string $null_as [, string $fields]]]) 799 Returns true if the copy worked fine or false if error */ 800static PHP_METHOD(PDO, pgsqlCopyToArray) 801{ 802 pdo_dbh_t *dbh; 803 pdo_pgsql_db_handle *H; 804 805 char *table_name, *pg_delim = NULL, *pg_null_as = NULL, *pg_fields = NULL; 806 int table_name_len, pg_delim_len = 0, pg_null_as_len = 0, pg_fields_len; 807 char *query; 808 809 PGresult *pgsql_result; 810 ExecStatusType status; 811 812 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|sss", 813 &table_name, &table_name_len, 814 &pg_delim, &pg_delim_len, &pg_null_as, &pg_null_as_len, &pg_fields, &pg_fields_len) == FAILURE) { 815 return; 816 } 817 818 dbh = zend_object_store_get_object(getThis() TSRMLS_CC); 819 PDO_CONSTRUCT_CHECK; 820 821 H = (pdo_pgsql_db_handle *)dbh->driver_data; 822 823 while ((pgsql_result = PQgetResult(H->server))) { 824 PQclear(pgsql_result); 825 } 826 827 if (pg_fields) { 828 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")); 829 } else { 830 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")); 831 } 832 pgsql_result = PQexec(H->server, query); 833 efree(query); 834 835 if (pgsql_result) { 836 status = PQresultStatus(pgsql_result); 837 } else { 838 status = (ExecStatusType) PQstatus(H->server); 839 } 840 841 if (status == PGRES_COPY_OUT && pgsql_result) { 842 PQclear(pgsql_result); 843 array_init(return_value); 844 845 while (1) { 846 char *csv = NULL; 847 int ret = PQgetCopyData(H->server, &csv, 0); 848 if (ret == -1) { 849 break; /* copy done */ 850 } else if (ret > 0) { 851 add_next_index_stringl(return_value, csv, ret, 1); 852 PQfreemem(csv); 853 } else { 854 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed: getline failed"); 855 RETURN_FALSE; 856 } 857 } 858 859 while ((pgsql_result = PQgetResult(H->server))) { 860 PQclear(pgsql_result); 861 } 862 } else { 863 PQclear(pgsql_result); 864 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "Copy command failed"); 865 RETURN_FALSE; 866 } 867} 868/* }}} */ 869 870 871/* {{{ proto string PDO::pgsqlLOBCreate() 872 Creates a new large object, returning its identifier. Must be called inside a transaction. */ 873static PHP_METHOD(PDO, pgsqlLOBCreate) 874{ 875 pdo_dbh_t *dbh; 876 pdo_pgsql_db_handle *H; 877 Oid lfd; 878 879 dbh = zend_object_store_get_object(getThis() TSRMLS_CC); 880 PDO_CONSTRUCT_CHECK; 881 882 H = (pdo_pgsql_db_handle *)dbh->driver_data; 883 lfd = lo_creat(H->server, INV_READ|INV_WRITE); 884 885 if (lfd != InvalidOid) { 886 char *buf; 887 spprintf(&buf, 0, "%lu", (long) lfd); 888 RETURN_STRING(buf, 0); 889 } 890 891 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "HY000"); 892 RETURN_FALSE; 893} 894/* }}} */ 895 896/* {{{ proto resource PDO::pgsqlLOBOpen(string oid [, string mode = 'rb']) 897 Opens an existing large object stream. Must be called inside a transaction. */ 898static PHP_METHOD(PDO, pgsqlLOBOpen) 899{ 900 pdo_dbh_t *dbh; 901 pdo_pgsql_db_handle *H; 902 Oid oid; 903 int lfd; 904 char *oidstr; 905 int oidstrlen; 906 char *modestr = "rb"; 907 int modestrlen; 908 int mode = INV_READ; 909 char *end_ptr; 910 911 if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", 912 &oidstr, &oidstrlen, &modestr, &modestrlen)) { 913 RETURN_FALSE; 914 } 915 916 oid = (Oid)strtoul(oidstr, &end_ptr, 10); 917 if (oid == 0 && (errno == ERANGE || errno == EINVAL)) { 918 RETURN_FALSE; 919 } 920 921 if (strpbrk(modestr, "+w")) { 922 mode = INV_READ|INV_WRITE; 923 } 924 925 dbh = zend_object_store_get_object(getThis() TSRMLS_CC); 926 PDO_CONSTRUCT_CHECK; 927 928 H = (pdo_pgsql_db_handle *)dbh->driver_data; 929 930 lfd = lo_open(H->server, oid, mode); 931 932 if (lfd >= 0) { 933 php_stream *stream = pdo_pgsql_create_lob_stream(dbh, lfd, oid TSRMLS_CC); 934 if (stream) { 935 php_stream_to_zval(stream, return_value); 936 return; 937 } 938 } else { 939 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "HY000"); 940 } 941 RETURN_FALSE; 942} 943/* }}} */ 944 945/* {{{ proto bool PDO::pgsqlLOBUnlink(string oid) 946 Deletes the large object identified by oid. Must be called inside a transaction. */ 947static PHP_METHOD(PDO, pgsqlLOBUnlink) 948{ 949 pdo_dbh_t *dbh; 950 pdo_pgsql_db_handle *H; 951 Oid oid; 952 char *oidstr, *end_ptr; 953 int oidlen; 954 955 if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", 956 &oidstr, &oidlen)) { 957 RETURN_FALSE; 958 } 959 960 oid = (Oid)strtoul(oidstr, &end_ptr, 10); 961 if (oid == 0 && (errno == ERANGE || errno == EINVAL)) { 962 RETURN_FALSE; 963 } 964 965 dbh = zend_object_store_get_object(getThis() TSRMLS_CC); 966 PDO_CONSTRUCT_CHECK; 967 968 H = (pdo_pgsql_db_handle *)dbh->driver_data; 969 970 if (1 == lo_unlink(H->server, oid)) { 971 RETURN_TRUE; 972 } 973 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, "HY000"); 974 RETURN_FALSE; 975} 976/* }}} */ 977 978 979static const zend_function_entry dbh_methods[] = { 980 PHP_ME(PDO, pgsqlLOBCreate, NULL, ZEND_ACC_PUBLIC) 981 PHP_ME(PDO, pgsqlLOBOpen, NULL, ZEND_ACC_PUBLIC) 982 PHP_ME(PDO, pgsqlLOBUnlink, NULL, ZEND_ACC_PUBLIC) 983 PHP_ME(PDO, pgsqlCopyFromArray, NULL, ZEND_ACC_PUBLIC) 984 PHP_ME(PDO, pgsqlCopyFromFile, NULL, ZEND_ACC_PUBLIC) 985 PHP_ME(PDO, pgsqlCopyToArray, NULL, ZEND_ACC_PUBLIC) 986 PHP_ME(PDO, pgsqlCopyToFile, NULL, ZEND_ACC_PUBLIC) 987 PHP_FE_END 988}; 989 990static const zend_function_entry *pdo_pgsql_get_driver_methods(pdo_dbh_t *dbh, int kind TSRMLS_DC) 991{ 992 switch (kind) { 993 case PDO_DBH_DRIVER_METHOD_KIND_DBH: 994 return dbh_methods; 995 default: 996 return NULL; 997 } 998} 999 1000static int pdo_pgsql_set_attr(pdo_dbh_t *dbh, long attr, zval *val TSRMLS_DC) 1001{ 1002 pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; 1003 1004 switch (attr) { 1005#if HAVE_PQPREPARE 1006 case PDO_ATTR_EMULATE_PREPARES: 1007 H->emulate_prepares = Z_LVAL_P(val); 1008 return 1; 1009 case PDO_PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT: 1010 H->disable_native_prepares = Z_LVAL_P(val); 1011 return 1; 1012#endif 1013 1014 default: 1015 return 0; 1016 } 1017} 1018 1019static struct pdo_dbh_methods pgsql_methods = { 1020 pgsql_handle_closer, 1021 pgsql_handle_preparer, 1022 pgsql_handle_doer, 1023 pgsql_handle_quoter, 1024 pgsql_handle_begin, 1025 pgsql_handle_commit, 1026 pgsql_handle_rollback, 1027 pdo_pgsql_set_attr, 1028 pdo_pgsql_last_insert_id, 1029 pdo_pgsql_fetch_error_func, 1030 pdo_pgsql_get_attribute, 1031 pdo_pgsql_check_liveness, /* check_liveness */ 1032 pdo_pgsql_get_driver_methods, /* get_driver_methods */ 1033 NULL, 1034 pgsql_handle_in_transaction, 1035}; 1036 1037static int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_DC) /* {{{ */ 1038{ 1039 pdo_pgsql_db_handle *H; 1040 int ret = 0; 1041 char *conn_str, *p, *e; 1042 long connect_timeout = 30; 1043 1044 H = pecalloc(1, sizeof(pdo_pgsql_db_handle), dbh->is_persistent); 1045 dbh->driver_data = H; 1046 1047 H->einfo.errcode = 0; 1048 H->einfo.errmsg = NULL; 1049 1050 /* PostgreSQL wants params in the connect string to be separated by spaces, 1051 * if the PDO standard semicolons are used, we convert them to spaces 1052 */ 1053 e = (char *) dbh->data_source + strlen(dbh->data_source); 1054 p = (char *) dbh->data_source; 1055 while ((p = memchr(p, ';', (e - p)))) { 1056 *p = ' '; 1057 } 1058 1059 if (driver_options) { 1060 connect_timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, 30 TSRMLS_CC); 1061 } 1062 1063 /* support both full connection string & connection string + login and/or password */ 1064 if (dbh->username && dbh->password) { 1065 spprintf(&conn_str, 0, "%s user=%s password=%s connect_timeout=%ld", dbh->data_source, dbh->username, dbh->password, connect_timeout); 1066 } else if (dbh->username) { 1067 spprintf(&conn_str, 0, "%s user=%s connect_timeout=%ld", dbh->data_source, dbh->username, connect_timeout); 1068 } else if (dbh->password) { 1069 spprintf(&conn_str, 0, "%s password=%s connect_timeout=%ld", dbh->data_source, dbh->password, connect_timeout); 1070 } else { 1071 spprintf(&conn_str, 0, "%s connect_timeout=%ld", (char *) dbh->data_source, connect_timeout); 1072 } 1073 1074 H->server = PQconnectdb(conn_str); 1075 1076 efree(conn_str); 1077 1078 if (PQstatus(H->server) != CONNECTION_OK) { 1079 pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, PHP_PDO_PGSQL_CONNECTION_FAILURE_SQLSTATE); 1080 goto cleanup; 1081 } 1082 1083 PQsetNoticeProcessor(H->server, (void(*)(void*,const char*))_pdo_pgsql_notice, (void *)&dbh); 1084 1085 H->attached = 1; 1086 H->pgoid = -1; 1087 1088 dbh->methods = &pgsql_methods; 1089 dbh->alloc_own_columns = 1; 1090 dbh->max_escaped_char_length = 2; 1091 1092 ret = 1; 1093 1094cleanup: 1095 dbh->methods = &pgsql_methods; 1096 if (!ret) { 1097 pgsql_handle_closer(dbh TSRMLS_CC); 1098 } 1099 1100 return ret; 1101} 1102/* }}} */ 1103 1104pdo_driver_t pdo_pgsql_driver = { 1105 PDO_DRIVER_HEADER(pgsql), 1106 pdo_pgsql_handle_factory 1107}; 1108 1109/* 1110 * Local variables: 1111 * tab-width: 4 1112 * c-basic-offset: 4 1113 * End: 1114 * vim600: noet sw=4 ts=4 fdm=marker 1115 * vim<600: noet sw=4 ts=4 1116 */ 1117