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
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) {
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_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
pgsql_stmt_fetch(pdo_stmt_t * stmt,enum pdo_fetch_orientation ori,long offset TSRMLS_DC)382 static 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
pgsql_stmt_describe(pdo_stmt_t * stmt,int colno TSRMLS_DC)430 static 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
pgsql_stmt_get_col(pdo_stmt_t * stmt,int colno,char ** ptr,unsigned long * len,int * caller_frees TSRMLS_DC)492 static 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
pgsql_stmt_get_column_meta(pdo_stmt_t * stmt,long colno,zval * return_value TSRMLS_DC)571 static 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);
609 done:
610 PQclear(res);
611 return 1;
612 }
613
pdo_pgsql_stmt_cursor_closer(pdo_stmt_t * stmt TSRMLS_DC)614 static int pdo_pgsql_stmt_cursor_closer(pdo_stmt_t *stmt TSRMLS_DC)
615 {
616 return 1;
617 }
618
619 struct 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