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