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 "php_pdo_pgsql.h" 33#include "php_pdo_pgsql_int.h" 34#if HAVE_NETINET_IN_H 35#include <netinet/in.h> 36#endif 37 38/* from postgresql/src/include/catalog/pg_type.h */ 39#define BOOLOID 16 40#define BYTEAOID 17 41#define INT8OID 20 42#define INT2OID 21 43#define INT4OID 23 44#define TEXTOID 25 45#define OIDOID 26 46 47static int pgsql_stmt_dtor(pdo_stmt_t *stmt TSRMLS_DC) 48{ 49 pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; 50 51 if (S->result) { 52 /* free the resource */ 53 PQclear(S->result); 54 S->result = NULL; 55 } 56 57#if HAVE_PQPREPARE 58 if (S->stmt_name) { 59 pdo_pgsql_db_handle *H = S->H; 60 char *q = NULL; 61 PGresult *res; 62 63 if (S->is_prepared) { 64 spprintf(&q, 0, "DEALLOCATE %s", S->stmt_name); 65 res = PQexec(H->server, q); 66 efree(q); 67 if (res) { 68 PQclear(res); 69 } 70 } 71 efree(S->stmt_name); 72 S->stmt_name = NULL; 73 } 74 if (S->param_lengths) { 75 efree(S->param_lengths); 76 S->param_lengths = NULL; 77 } 78 if (S->param_values) { 79 efree(S->param_values); 80 S->param_values = NULL; 81 } 82 if (S->param_formats) { 83 efree(S->param_formats); 84 S->param_formats = NULL; 85 } 86 if (S->param_types) { 87 efree(S->param_types); 88 S->param_types = NULL; 89 } 90 if (S->query) { 91 efree(S->query); 92 S->query = NULL; 93 } 94#endif 95 96 if (S->cursor_name) { 97 pdo_pgsql_db_handle *H = S->H; 98 char *q = NULL; 99 PGresult *res; 100 101 spprintf(&q, 0, "CLOSE %s", S->cursor_name); 102 res = PQexec(H->server, q); 103 efree(q); 104 if (res) PQclear(res); 105 efree(S->cursor_name); 106 S->cursor_name = NULL; 107 } 108 109 if(S->cols) { 110 efree(S->cols); 111 S->cols = NULL; 112 } 113 efree(S); 114 stmt->driver_data = NULL; 115 return 1; 116} 117 118static int pgsql_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC) 119{ 120 pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; 121 pdo_pgsql_db_handle *H = S->H; 122 ExecStatusType status; 123 124 /* ensure that we free any previous unfetched results */ 125 if(S->result) { 126 PQclear(S->result); 127 S->result = NULL; 128 } 129 130 S->current_row = 0; 131 132 if (S->cursor_name) { 133 char *q = NULL; 134 135 if (S->is_prepared) { 136 spprintf(&q, 0, "CLOSE %s", S->cursor_name); 137 S->result = PQexec(H->server, q); 138 efree(q); 139 } 140 141 spprintf(&q, 0, "DECLARE %s SCROLL CURSOR WITH HOLD FOR %s", S->cursor_name, stmt->active_query_string); 142 S->result = PQexec(H->server, q); 143 efree(q); 144 145 /* check if declare failed */ 146 status = PQresultStatus(S->result); 147 if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) { 148 pdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result)); 149 return 0; 150 } 151 152 /* the cursor was declared correctly */ 153 S->is_prepared = 1; 154 155 /* fetch to be able to get the number of tuples later, but don't advance the cursor pointer */ 156 spprintf(&q, 0, "FETCH FORWARD 0 FROM %s", S->cursor_name); 157 S->result = PQexec(H->server, q); 158 efree(q); 159 } else 160#if HAVE_PQPREPARE 161 if (S->stmt_name) { 162 /* using a prepared statement */ 163 164 if (!S->is_prepared) { 165stmt_retry: 166 /* we deferred the prepare until now, because we didn't 167 * know anything about the parameter types; now we do */ 168 S->result = PQprepare(H->server, S->stmt_name, S->query, 169 stmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0, 170 S->param_types); 171 status = PQresultStatus(S->result); 172 switch (status) { 173 case PGRES_COMMAND_OK: 174 case PGRES_TUPLES_OK: 175 /* it worked */ 176 S->is_prepared = 1; 177 PQclear(S->result); 178 break; 179 default: { 180 char *sqlstate = pdo_pgsql_sqlstate(S->result); 181 /* 42P05 means that the prepared statement already existed. this can happen if you use 182 * a connection pooling software line pgpool which doesn't close the db-connection once 183 * php disconnects. if php dies (no chance to run RSHUTDOWN) during execution it has no 184 * chance to DEALLOCATE the prepared statements it has created. so, if we hit a 42P05 we 185 * deallocate it and retry ONCE (thies 2005.12.15) 186 */ 187 if (sqlstate && !strcmp(sqlstate, "42P05")) { 188 char buf[100]; /* stmt_name == "pdo_crsr_%08x" */ 189 PGresult *res; 190 snprintf(buf, sizeof(buf), "DEALLOCATE %s", S->stmt_name); 191 res = PQexec(H->server, buf); 192 if (res) { 193 PQclear(res); 194 } 195 goto stmt_retry; 196 } else { 197 pdo_pgsql_error_stmt(stmt, status, sqlstate); 198 return 0; 199 } 200 } 201 } 202 } 203 S->result = PQexecPrepared(H->server, S->stmt_name, 204 stmt->bound_params ? 205 zend_hash_num_elements(stmt->bound_params) : 206 0, 207 (const char**)S->param_values, 208 S->param_lengths, 209 S->param_formats, 210 0); 211 } else 212#endif 213 { 214 S->result = PQexec(H->server, stmt->active_query_string); 215 } 216 status = PQresultStatus(S->result); 217 218 if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) { 219 pdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result)); 220 return 0; 221 } 222 223 if (!stmt->executed && !stmt->column_count) { 224 stmt->column_count = (int) PQnfields(S->result); 225 S->cols = ecalloc(stmt->column_count, sizeof(pdo_pgsql_column)); 226 } 227 228 if (status == PGRES_COMMAND_OK) { 229 stmt->row_count = (long)atoi(PQcmdTuples(S->result)); 230 H->pgoid = PQoidValue(S->result); 231 } else { 232 stmt->row_count = (long)PQntuples(S->result); 233 } 234 235 return 1; 236} 237 238static int pgsql_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, 239 enum pdo_param_event event_type TSRMLS_DC) 240{ 241#if HAVE_PQPREPARE 242 pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; 243 244 if (S->stmt_name && param->is_param) { 245 switch (event_type) { 246 case PDO_PARAM_EVT_FREE: 247 if (param->driver_data) { 248 efree(param->driver_data); 249 } 250 break; 251 252 case PDO_PARAM_EVT_NORMALIZE: 253 /* decode name from $1, $2 into 0, 1 etc. */ 254 if (param->name) { 255 if (param->name[0] == '$') { 256 param->paramno = atoi(param->name + 1); 257 } else { 258 /* resolve parameter name to rewritten name */ 259 char *nameptr; 260 if (stmt->bound_param_map && SUCCESS == zend_hash_find(stmt->bound_param_map, 261 param->name, param->namelen + 1, (void**)&nameptr)) { 262 param->paramno = atoi(nameptr + 1) - 1; 263 } else { 264 pdo_raise_impl_error(stmt->dbh, stmt, "HY093", param->name TSRMLS_CC); 265 return 0; 266 } 267 } 268 } 269 break; 270 271 case PDO_PARAM_EVT_ALLOC: 272 case PDO_PARAM_EVT_EXEC_POST: 273 case PDO_PARAM_EVT_FETCH_PRE: 274 case PDO_PARAM_EVT_FETCH_POST: 275 /* work is handled by EVT_NORMALIZE */ 276 return 1; 277 278 case PDO_PARAM_EVT_EXEC_PRE: 279 if (!stmt->bound_param_map) { 280 return 0; 281 } 282 if (!S->param_values) { 283 S->param_values = ecalloc( 284 zend_hash_num_elements(stmt->bound_param_map), 285 sizeof(char*)); 286 S->param_lengths = ecalloc( 287 zend_hash_num_elements(stmt->bound_param_map), 288 sizeof(int)); 289 S->param_formats = ecalloc( 290 zend_hash_num_elements(stmt->bound_param_map), 291 sizeof(int)); 292 S->param_types = ecalloc( 293 zend_hash_num_elements(stmt->bound_param_map), 294 sizeof(Oid)); 295 } 296 if (param->paramno >= 0) { 297 if (param->paramno > zend_hash_num_elements(stmt->bound_param_map)) { 298 pdo_pgsql_error_stmt(stmt, PGRES_FATAL_ERROR, "HY105"); 299 return 0; 300 } 301 302 if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && 303 Z_TYPE_P(param->parameter) == IS_RESOURCE) { 304 php_stream *stm; 305 php_stream_from_zval_no_verify(stm, ¶m->parameter); 306 if (stm) { 307 if (php_stream_is(stm, &pdo_pgsql_lob_stream_ops)) { 308 struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stm->abstract; 309 pdo_pgsql_bound_param *P = param->driver_data; 310 311 if (P == NULL) { 312 P = ecalloc(1, sizeof(*P)); 313 param->driver_data = P; 314 } 315 P->oid = htonl(self->oid); 316 S->param_values[param->paramno] = (char*)&P->oid; 317 S->param_lengths[param->paramno] = sizeof(P->oid); 318 S->param_formats[param->paramno] = 1; 319 S->param_types[param->paramno] = OIDOID; 320 return 1; 321 } else { 322 int len; 323 324 SEPARATE_ZVAL_IF_NOT_REF(¶m->parameter); 325 Z_TYPE_P(param->parameter) = IS_STRING; 326 327 if ((len = php_stream_copy_to_mem(stm, &Z_STRVAL_P(param->parameter), PHP_STREAM_COPY_ALL, 0)) > 0) { 328 Z_STRLEN_P(param->parameter) = len; 329 } else { 330 ZVAL_EMPTY_STRING(param->parameter); 331 } 332 } 333 } else { 334 /* expected a stream resource */ 335 pdo_pgsql_error_stmt(stmt, PGRES_FATAL_ERROR, "HY105"); 336 return 0; 337 } 338 } 339 340 if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL || 341 Z_TYPE_P(param->parameter) == IS_NULL) { 342 S->param_values[param->paramno] = NULL; 343 S->param_lengths[param->paramno] = 0; 344 } else if (Z_TYPE_P(param->parameter) == IS_BOOL) { 345 S->param_values[param->paramno] = Z_BVAL_P(param->parameter) ? "t" : "f"; 346 S->param_lengths[param->paramno] = 1; 347 S->param_formats[param->paramno] = 0; 348 } else { 349 SEPARATE_ZVAL_IF_NOT_REF(¶m->parameter); 350 convert_to_string(param->parameter); 351 S->param_values[param->paramno] = Z_STRVAL_P(param->parameter); 352 S->param_lengths[param->paramno] = Z_STRLEN_P(param->parameter); 353 S->param_formats[param->paramno] = 0; 354 } 355 356 if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { 357 S->param_types[param->paramno] = 0; 358 S->param_formats[param->paramno] = 1; 359 } else { 360 S->param_types[param->paramno] = 0; 361 } 362 } 363 break; 364 } 365 } else { 366#endif 367 if (param->is_param) { 368 /* We need to manually convert to a pg native boolean value */ 369 if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_BOOL && 370 ((param->param_type & PDO_PARAM_INPUT_OUTPUT) != PDO_PARAM_INPUT_OUTPUT)) { 371 SEPARATE_ZVAL(¶m->parameter); 372 param->param_type = PDO_PARAM_STR; 373 ZVAL_STRINGL(param->parameter, Z_BVAL_P(param->parameter) ? "t" : "f", 1, 1); 374 } 375 } 376#if HAVE_PQPREPARE 377 } 378#endif 379 return 1; 380} 381 382static int pgsql_stmt_fetch(pdo_stmt_t *stmt, 383 enum pdo_fetch_orientation ori, long offset TSRMLS_DC) 384{ 385 pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; 386 387 if (S->cursor_name) { 388 char *ori_str = NULL; 389 char *q = NULL; 390 ExecStatusType status; 391 392 switch (ori) { 393 case PDO_FETCH_ORI_NEXT: spprintf(&ori_str, 0, "NEXT"); break; 394 case PDO_FETCH_ORI_PRIOR: spprintf(&ori_str, 0, "BACKWARD"); break; 395 case PDO_FETCH_ORI_FIRST: spprintf(&ori_str, 0, "FIRST"); break; 396 case PDO_FETCH_ORI_LAST: spprintf(&ori_str, 0, "LAST"); break; 397 case PDO_FETCH_ORI_ABS: spprintf(&ori_str, 0, "ABSOLUTE %ld", offset); break; 398 case PDO_FETCH_ORI_REL: spprintf(&ori_str, 0, "RELATIVE %ld", offset); break; 399 default: 400 return 0; 401 } 402 403 spprintf(&q, 0, "FETCH %s FROM %s", ori_str, S->cursor_name); 404 efree(ori_str); 405 S->result = PQexec(S->H->server, q); 406 efree(q); 407 status = PQresultStatus(S->result); 408 409 if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) { 410 pdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result)); 411 return 0; 412 } 413 414 if (PQntuples(S->result)) { 415 S->current_row = 1; 416 return 1; 417 } else { 418 return 0; 419 } 420 } else { 421 if (S->current_row < stmt->row_count) { 422 S->current_row++; 423 return 1; 424 } else { 425 return 0; 426 } 427 } 428} 429 430static int pgsql_stmt_describe(pdo_stmt_t *stmt, int colno TSRMLS_DC) 431{ 432 pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; 433 struct pdo_column_data *cols = stmt->columns; 434 struct pdo_bound_param_data *param; 435 436 if (!S->result) { 437 return 0; 438 } 439 440 cols[colno].name = estrdup(PQfname(S->result, colno)); 441 cols[colno].namelen = strlen(cols[colno].name); 442 cols[colno].maxlen = PQfsize(S->result, colno); 443 cols[colno].precision = PQfmod(S->result, colno); 444 S->cols[colno].pgsql_type = PQftype(S->result, colno); 445 446 switch(S->cols[colno].pgsql_type) { 447 448 case BOOLOID: 449 cols[colno].param_type = PDO_PARAM_BOOL; 450 break; 451 452 case OIDOID: 453 /* did the user bind the column as a LOB ? */ 454 if (stmt->bound_columns && ( 455 SUCCESS == zend_hash_index_find(stmt->bound_columns, 456 colno, (void**)¶m) || 457 SUCCESS == zend_hash_find(stmt->bound_columns, 458 cols[colno].name, cols[colno].namelen, 459 (void**)¶m))) { 460 if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { 461 cols[colno].param_type = PDO_PARAM_LOB; 462 break; 463 } 464 } 465 cols[colno].param_type = PDO_PARAM_INT; 466 break; 467 468 case INT2OID: 469 case INT4OID: 470 cols[colno].param_type = PDO_PARAM_INT; 471 break; 472 473 case INT8OID: 474 if (sizeof(long)>=8) { 475 cols[colno].param_type = PDO_PARAM_INT; 476 } else { 477 cols[colno].param_type = PDO_PARAM_STR; 478 } 479 break; 480 481 case BYTEAOID: 482 cols[colno].param_type = PDO_PARAM_LOB; 483 break; 484 485 default: 486 cols[colno].param_type = PDO_PARAM_STR; 487 } 488 489 return 1; 490} 491 492static int pgsql_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, unsigned long *len, int *caller_frees TSRMLS_DC) 493{ 494 pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; 495 struct pdo_column_data *cols = stmt->columns; 496 size_t tmp_len; 497 498 if (!S->result) { 499 return 0; 500 } 501 502 /* We have already increased count by 1 in pgsql_stmt_fetch() */ 503 if (PQgetisnull(S->result, S->current_row - 1, colno)) { /* Check if we got NULL */ 504 *ptr = NULL; 505 *len = 0; 506 } else { 507 *ptr = PQgetvalue(S->result, S->current_row - 1, colno); 508 *len = PQgetlength(S->result, S->current_row - 1, colno); 509 510 switch(cols[colno].param_type) { 511 512 case PDO_PARAM_INT: 513 S->cols[colno].intval = atol(*ptr); 514 *ptr = (char *) &(S->cols[colno].intval); 515 *len = sizeof(long); 516 break; 517 518 case PDO_PARAM_BOOL: 519 S->cols[colno].boolval = **ptr == 't' ? 1: 0; 520 *ptr = (char *) &(S->cols[colno].boolval); 521 *len = sizeof(zend_bool); 522 break; 523 524 case PDO_PARAM_LOB: 525 if (S->cols[colno].pgsql_type == OIDOID) { 526 /* ooo, a real large object */ 527 char *end_ptr; 528 Oid oid = (Oid)strtoul(*ptr, &end_ptr, 10); 529 int loid = lo_open(S->H->server, oid, INV_READ); 530 if (loid >= 0) { 531 *ptr = (char*)pdo_pgsql_create_lob_stream(stmt->dbh, loid, oid TSRMLS_CC); 532 *len = 0; 533 return *ptr ? 1 : 0; 534 } 535 *ptr = NULL; 536 *len = 0; 537 return 0; 538 } else { 539 char *tmp_ptr = PQunescapeBytea(*ptr, &tmp_len); 540 if (!tmp_ptr) { 541 /* PQunescapeBytea returned an error */ 542 *len = 0; 543 return 0; 544 } 545 if (!tmp_len) { 546 /* Empty string, return as empty stream */ 547 *ptr = (char *)php_stream_memory_open(TEMP_STREAM_READONLY, "", 0); 548 PQfreemem(tmp_ptr); 549 *len = 0; 550 } else { 551 *ptr = estrndup(tmp_ptr, tmp_len); 552 PQfreemem(tmp_ptr); 553 *len = tmp_len; 554 *caller_frees = 1; 555 } 556 } 557 break; 558 case PDO_PARAM_NULL: 559 case PDO_PARAM_STR: 560 case PDO_PARAM_STMT: 561 case PDO_PARAM_INPUT_OUTPUT: 562 case PDO_PARAM_ZVAL: 563 default: 564 break; 565 } 566 } 567 568 return 1; 569} 570 571static int pgsql_stmt_get_column_meta(pdo_stmt_t *stmt, long colno, zval *return_value TSRMLS_DC) 572{ 573 pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; 574 PGresult *res; 575 char *q=NULL; 576 ExecStatusType status; 577 578 if (!S->result) { 579 return FAILURE; 580 } 581 582 if (colno >= stmt->column_count) { 583 return FAILURE; 584 } 585 586 array_init(return_value); 587 add_assoc_long(return_value, "pgsql:oid", S->cols[colno].pgsql_type); 588 589 /* Fetch metadata from Postgres system catalogue */ 590 spprintf(&q, 0, "SELECT TYPNAME FROM PG_TYPE WHERE OID=%d", S->cols[colno].pgsql_type); 591 res = PQexec(S->H->server, q); 592 efree(q); 593 594 status = PQresultStatus(res); 595 596 if (status != PGRES_TUPLES_OK) { 597 /* Failed to get system catalogue, but return success 598 * with the data we have collected so far 599 */ 600 goto done; 601 } 602 603 /* We want exactly one row returned */ 604 if (1 != PQntuples(res)) { 605 goto done; 606 } 607 608 add_assoc_string(return_value, "native_type", PQgetvalue(res, 0, 0), 1); 609done: 610 PQclear(res); 611 return 1; 612} 613 614static int pdo_pgsql_stmt_cursor_closer(pdo_stmt_t *stmt TSRMLS_DC) 615{ 616 return 1; 617} 618 619struct pdo_stmt_methods pgsql_stmt_methods = { 620 pgsql_stmt_dtor, 621 pgsql_stmt_execute, 622 pgsql_stmt_fetch, 623 pgsql_stmt_describe, 624 pgsql_stmt_get_col, 625 pgsql_stmt_param_hook, 626 NULL, /* set_attr */ 627 NULL, /* get_attr */ 628 pgsql_stmt_get_column_meta, 629 NULL, /* next_rowset */ 630 pdo_pgsql_stmt_cursor_closer 631}; 632 633/* 634 * Local variables: 635 * tab-width: 4 636 * c-basic-offset: 4 637 * End: 638 * vim600: noet sw=4 ts=4 fdm=marker 639 * vim<600: noet sw=4 ts=4 640 */ 641