1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2015 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
pgsql_stmt_dtor(pdo_stmt_t * stmt TSRMLS_DC)47 static 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
pgsql_stmt_execute(pdo_stmt_t * stmt TSRMLS_DC)118 static 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) {
165 stmt_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 || S->cols == NULL)) {
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
pgsql_stmt_param_hook(pdo_stmt_t * stmt,struct pdo_bound_param_data * param,enum pdo_param_event event_type TSRMLS_DC)238 static 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_params)) {
298 pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "parameter was not defined" TSRMLS_CC);
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 convert_to_boolean(param->parameter);
374 ZVAL_STRINGL(param->parameter, Z_BVAL_P(param->parameter) ? "t" : "f", 1, 1);
375 }
376 }
377 #if HAVE_PQPREPARE
378 }
379 #endif
380 return 1;
381 }
382
pgsql_stmt_fetch(pdo_stmt_t * stmt,enum pdo_fetch_orientation ori,long offset TSRMLS_DC)383 static int pgsql_stmt_fetch(pdo_stmt_t *stmt,
384 enum pdo_fetch_orientation ori, long offset TSRMLS_DC)
385 {
386 pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;
387
388 if (S->cursor_name) {
389 char *ori_str = NULL;
390 char *q = NULL;
391 ExecStatusType status;
392
393 switch (ori) {
394 case PDO_FETCH_ORI_NEXT: spprintf(&ori_str, 0, "NEXT"); break;
395 case PDO_FETCH_ORI_PRIOR: spprintf(&ori_str, 0, "BACKWARD"); break;
396 case PDO_FETCH_ORI_FIRST: spprintf(&ori_str, 0, "FIRST"); break;
397 case PDO_FETCH_ORI_LAST: spprintf(&ori_str, 0, "LAST"); break;
398 case PDO_FETCH_ORI_ABS: spprintf(&ori_str, 0, "ABSOLUTE %ld", offset); break;
399 case PDO_FETCH_ORI_REL: spprintf(&ori_str, 0, "RELATIVE %ld", offset); break;
400 default:
401 return 0;
402 }
403
404 spprintf(&q, 0, "FETCH %s FROM %s", ori_str, S->cursor_name);
405 efree(ori_str);
406 S->result = PQexec(S->H->server, q);
407 efree(q);
408 status = PQresultStatus(S->result);
409
410 if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {
411 pdo_pgsql_error_stmt(stmt, status, pdo_pgsql_sqlstate(S->result));
412 return 0;
413 }
414
415 if (PQntuples(S->result)) {
416 S->current_row = 1;
417 return 1;
418 } else {
419 return 0;
420 }
421 } else {
422 if (S->current_row < stmt->row_count) {
423 S->current_row++;
424 return 1;
425 } else {
426 return 0;
427 }
428 }
429 }
430
pgsql_stmt_describe(pdo_stmt_t * stmt,int colno TSRMLS_DC)431 static int pgsql_stmt_describe(pdo_stmt_t *stmt, int colno TSRMLS_DC)
432 {
433 pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;
434 struct pdo_column_data *cols = stmt->columns;
435 struct pdo_bound_param_data *param;
436
437 if (!S->result) {
438 return 0;
439 }
440
441 cols[colno].name = estrdup(PQfname(S->result, colno));
442 cols[colno].namelen = strlen(cols[colno].name);
443 cols[colno].maxlen = PQfsize(S->result, colno);
444 cols[colno].precision = PQfmod(S->result, colno);
445 S->cols[colno].pgsql_type = PQftype(S->result, colno);
446
447 switch(S->cols[colno].pgsql_type) {
448
449 case BOOLOID:
450 cols[colno].param_type = PDO_PARAM_BOOL;
451 break;
452
453 case OIDOID:
454 /* did the user bind the column as a LOB ? */
455 if (stmt->bound_columns && (
456 SUCCESS == zend_hash_index_find(stmt->bound_columns,
457 colno, (void**)¶m) ||
458 SUCCESS == zend_hash_find(stmt->bound_columns,
459 cols[colno].name, cols[colno].namelen,
460 (void**)¶m))) {
461 if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) {
462 cols[colno].param_type = PDO_PARAM_LOB;
463 break;
464 }
465 }
466 cols[colno].param_type = PDO_PARAM_INT;
467 break;
468
469 case INT2OID:
470 case INT4OID:
471 cols[colno].param_type = PDO_PARAM_INT;
472 break;
473
474 case INT8OID:
475 if (sizeof(long)>=8) {
476 cols[colno].param_type = PDO_PARAM_INT;
477 } else {
478 cols[colno].param_type = PDO_PARAM_STR;
479 }
480 break;
481
482 case BYTEAOID:
483 cols[colno].param_type = PDO_PARAM_LOB;
484 break;
485
486 default:
487 cols[colno].param_type = PDO_PARAM_STR;
488 }
489
490 return 1;
491 }
492
pgsql_stmt_get_col(pdo_stmt_t * stmt,int colno,char ** ptr,unsigned long * len,int * caller_frees TSRMLS_DC)493 static int pgsql_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, unsigned long *len, int *caller_frees TSRMLS_DC)
494 {
495 pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;
496 struct pdo_column_data *cols = stmt->columns;
497 size_t tmp_len;
498
499 if (!S->result) {
500 return 0;
501 }
502
503 /* We have already increased count by 1 in pgsql_stmt_fetch() */
504 if (PQgetisnull(S->result, S->current_row - 1, colno)) { /* Check if we got NULL */
505 *ptr = NULL;
506 *len = 0;
507 } else {
508 *ptr = PQgetvalue(S->result, S->current_row - 1, colno);
509 *len = PQgetlength(S->result, S->current_row - 1, colno);
510
511 switch(cols[colno].param_type) {
512
513 case PDO_PARAM_INT:
514 S->cols[colno].intval = atol(*ptr);
515 *ptr = (char *) &(S->cols[colno].intval);
516 *len = sizeof(long);
517 break;
518
519 case PDO_PARAM_BOOL:
520 S->cols[colno].boolval = **ptr == 't' ? 1: 0;
521 *ptr = (char *) &(S->cols[colno].boolval);
522 *len = sizeof(zend_bool);
523 break;
524
525 case PDO_PARAM_LOB:
526 if (S->cols[colno].pgsql_type == OIDOID) {
527 /* ooo, a real large object */
528 char *end_ptr;
529 Oid oid = (Oid)strtoul(*ptr, &end_ptr, 10);
530 int loid = lo_open(S->H->server, oid, INV_READ);
531 if (loid >= 0) {
532 *ptr = (char*)pdo_pgsql_create_lob_stream(stmt->dbh, loid, oid TSRMLS_CC);
533 *len = 0;
534 return *ptr ? 1 : 0;
535 }
536 *ptr = NULL;
537 *len = 0;
538 return 0;
539 } else {
540 char *tmp_ptr = (char *)PQunescapeBytea((unsigned char *)*ptr, &tmp_len);
541 if (!tmp_ptr) {
542 /* PQunescapeBytea returned an error */
543 *len = 0;
544 return 0;
545 }
546 if (!tmp_len) {
547 /* Empty string, return as empty stream */
548 *ptr = (char *)php_stream_memory_open(TEMP_STREAM_READONLY, "", 0);
549 PQfreemem(tmp_ptr);
550 *len = 0;
551 } else {
552 *ptr = estrndup(tmp_ptr, tmp_len);
553 PQfreemem(tmp_ptr);
554 *len = tmp_len;
555 *caller_frees = 1;
556 }
557 }
558 break;
559 case PDO_PARAM_NULL:
560 case PDO_PARAM_STR:
561 case PDO_PARAM_STMT:
562 case PDO_PARAM_INPUT_OUTPUT:
563 case PDO_PARAM_ZVAL:
564 default:
565 break;
566 }
567 }
568
569 return 1;
570 }
571
pgsql_stmt_get_column_meta(pdo_stmt_t * stmt,long colno,zval * return_value TSRMLS_DC)572 static int pgsql_stmt_get_column_meta(pdo_stmt_t *stmt, long colno, zval *return_value TSRMLS_DC)
573 {
574 pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;
575 PGresult *res;
576 char *q=NULL;
577 ExecStatusType status;
578
579 if (!S->result) {
580 return FAILURE;
581 }
582
583 if (colno >= stmt->column_count) {
584 return FAILURE;
585 }
586
587 array_init(return_value);
588 add_assoc_long(return_value, "pgsql:oid", S->cols[colno].pgsql_type);
589
590 /* Fetch metadata from Postgres system catalogue */
591 spprintf(&q, 0, "SELECT TYPNAME FROM PG_TYPE WHERE OID=%u", S->cols[colno].pgsql_type);
592 res = PQexec(S->H->server, q);
593 efree(q);
594
595 status = PQresultStatus(res);
596
597 if (status != PGRES_TUPLES_OK) {
598 /* Failed to get system catalogue, but return success
599 * with the data we have collected so far
600 */
601 goto done;
602 }
603
604 /* We want exactly one row returned */
605 if (1 != PQntuples(res)) {
606 goto done;
607 }
608
609 add_assoc_string(return_value, "native_type", PQgetvalue(res, 0, 0), 1);
610 done:
611 PQclear(res);
612 return 1;
613 }
614
pdo_pgsql_stmt_cursor_closer(pdo_stmt_t * stmt TSRMLS_DC)615 static int pdo_pgsql_stmt_cursor_closer(pdo_stmt_t *stmt TSRMLS_DC)
616 {
617 pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data;
618
619 if (S->cols != NULL){
620 efree(S->cols);
621 S->cols = NULL;
622 }
623 return 1;
624 }
625
626 struct pdo_stmt_methods pgsql_stmt_methods = {
627 pgsql_stmt_dtor,
628 pgsql_stmt_execute,
629 pgsql_stmt_fetch,
630 pgsql_stmt_describe,
631 pgsql_stmt_get_col,
632 pgsql_stmt_param_hook,
633 NULL, /* set_attr */
634 NULL, /* get_attr */
635 pgsql_stmt_get_column_meta,
636 NULL, /* next_rowset */
637 pdo_pgsql_stmt_cursor_closer
638 };
639
640 /*
641 * Local variables:
642 * tab-width: 4
643 * c-basic-offset: 4
644 * End:
645 * vim600: noet sw=4 ts=4 fdm=marker
646 * vim<600: noet sw=4 ts=4
647 */
648