1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Authors: Zeev Suraski <zeev@php.net> |
14 | Jouni Ahto <jouni.ahto@exdec.fi> |
15 | Yasuo Ohgaki <yohgaki@php.net> |
16 | Youichi Iwakiri <yiwakiri@st.rim.or.jp> (pg_copy_*) |
17 | Chris Kings-Lynne <chriskl@php.net> (v3 protocol) |
18 +----------------------------------------------------------------------+
19 */
20
21 #include <stdlib.h>
22
23 #define PHP_PGSQL_PRIVATE 1
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #define SMART_STR_PREALLOC 512
30
31 #include "php.h"
32 #include "php_ini.h"
33 #include "ext/standard/php_standard.h"
34 #include "zend_smart_str.h"
35 #include "ext/pcre/php_pcre.h"
36 #ifdef PHP_WIN32
37 # include "win32/time.h"
38 #endif
39 #include "php_pgsql.h"
40 #include "php_globals.h"
41 #include "zend_exceptions.h"
42
43 #ifdef HAVE_PGSQL
44
45 #ifndef InvalidOid
46 #define InvalidOid ((Oid) 0)
47 #endif
48
49 #define PGSQL_ASSOC 1<<0
50 #define PGSQL_NUM 1<<1
51 #define PGSQL_BOTH (PGSQL_ASSOC|PGSQL_NUM)
52
53 #define PGSQL_NOTICE_LAST 1 /* Get the last notice */
54 #define PGSQL_NOTICE_ALL 2 /* Get all notices */
55 #define PGSQL_NOTICE_CLEAR 3 /* Remove notices */
56
57 #define PGSQL_STATUS_LONG 1
58 #define PGSQL_STATUS_STRING 2
59
60 #define PGSQL_MAX_LENGTH_OF_LONG 30
61 #define PGSQL_MAX_LENGTH_OF_DOUBLE 60
62
63 char pgsql_libpq_version[16];
64
65 #include "pgsql_arginfo.h"
66
67 #if ZEND_LONG_MAX < UINT_MAX
68 #define PGSQL_RETURN_OID(oid) do { \
69 if (oid > ZEND_LONG_MAX) { \
70 RETURN_STR(zend_ulong_to_str(oid)); \
71 } \
72 RETURN_LONG((zend_long)oid); \
73 } while(0)
74 #else
75 #define PGSQL_RETURN_OID(oid) RETURN_LONG((zend_long)oid)
76 #endif
77
78 #define CHECK_DEFAULT_LINK(x) \
79 if ((x) == NULL) { \
80 zend_throw_error(NULL, "No PostgreSQL connection opened yet"); \
81 RETURN_THROWS(); \
82 }
83
84 /* This is a bit hacky as the macro usage is "link = FETCH_DEFAULT_LINK();" */
85 #define FETCH_DEFAULT_LINK() \
86 (PGG(default_link) ? pgsql_link_from_obj(PGG(default_link)) : NULL); \
87 php_error_docref(NULL, E_DEPRECATED, "Automatic fetching of PostgreSQL connection is deprecated")
88
89 /* Used only when creating a connection */
90 #define FETCH_DEFAULT_LINK_NO_WARNING() \
91 (PGG(default_link) ? pgsql_link_from_obj(PGG(default_link)) : NULL)
92
93 #define CHECK_PGSQL_LINK(link_handle) \
94 if (link_handle->conn == NULL) { \
95 zend_throw_error(NULL, "PostgreSQL connection has already been closed"); \
96 RETURN_THROWS(); \
97 }
98
99 #define CHECK_PGSQL_RESULT(result_handle) \
100 if (result_handle->result == NULL) { \
101 zend_throw_error(NULL, "PostgreSQL result has already been closed"); \
102 RETURN_THROWS(); \
103 }
104
105 #define CHECK_PGSQL_LOB(lob) \
106 if (lob->conn == NULL) { \
107 zend_throw_error(NULL, "PostgreSQL large object has already been closed"); \
108 RETURN_THROWS(); \
109 }
110
111 #ifndef HAVE_PQFREEMEM
112 #define PQfreemem free
113 #endif
114
115 #if PG_VERSION_NUM < 120000
116 #define PQERRORS_SQLSTATE 0
117 #endif
118
119 ZEND_DECLARE_MODULE_GLOBALS(pgsql)
120 static PHP_GINIT_FUNCTION(pgsql);
121
122 /* {{{ pgsql_module_entry */
123 zend_module_entry pgsql_module_entry = {
124 STANDARD_MODULE_HEADER,
125 "pgsql",
126 ext_functions,
127 PHP_MINIT(pgsql),
128 PHP_MSHUTDOWN(pgsql),
129 PHP_RINIT(pgsql),
130 PHP_RSHUTDOWN(pgsql),
131 PHP_MINFO(pgsql),
132 PHP_PGSQL_VERSION,
133 PHP_MODULE_GLOBALS(pgsql),
134 PHP_GINIT(pgsql),
135 NULL,
136 NULL,
137 STANDARD_MODULE_PROPERTIES_EX
138 };
139 /* }}} */
140
141 #ifdef COMPILE_DL_PGSQL
142 #ifdef ZTS
143 ZEND_TSRMLS_CACHE_DEFINE()
144 #endif
145 ZEND_GET_MODULE(pgsql)
146 #endif
147
148 static int le_plink;
149
150 static zend_class_entry *pgsql_link_ce, *pgsql_result_ce, *pgsql_lob_ce;
151 static zend_object_handlers pgsql_link_object_handlers, pgsql_result_object_handlers, pgsql_lob_object_handlers;
152
pgsql_link_from_obj(zend_object * obj)153 static inline pgsql_link_handle *pgsql_link_from_obj(zend_object *obj) {
154 return (pgsql_link_handle *)((char *)(obj) - XtOffsetOf(pgsql_link_handle, std));
155 }
156
157 #define Z_PGSQL_LINK_P(zv) pgsql_link_from_obj(Z_OBJ_P(zv))
158
pgsql_link_create_object(zend_class_entry * class_type)159 static zend_object *pgsql_link_create_object(zend_class_entry *class_type) {
160 pgsql_link_handle *intern = zend_object_alloc(sizeof(pgsql_link_handle), class_type);
161
162 zend_object_std_init(&intern->std, class_type);
163 object_properties_init(&intern->std, class_type);
164
165 return &intern->std;
166 }
167
pgsql_link_get_constructor(zend_object * object)168 static zend_function *pgsql_link_get_constructor(zend_object *object) {
169 zend_throw_error(NULL, "Cannot directly construct PgSql\\Connection, use pg_connect() or pg_pconnect() instead");
170 return NULL;
171 }
172
pgsql_link_free(pgsql_link_handle * link)173 static void pgsql_link_free(pgsql_link_handle *link)
174 {
175 PGresult *res;
176
177 while ((res = PQgetResult(link->conn))) {
178 PQclear(res);
179 }
180 if (!link->persistent) {
181 PQuntrace(link->conn);
182 PQfinish(link->conn);
183 }
184 PGG(num_links)--;
185
186 zend_hash_del(&PGG(connections), link->hash);
187
188 link->conn = NULL;
189 zend_string_release(link->hash);
190
191 if (link->notices) {
192 zend_hash_destroy(link->notices);
193 FREE_HASHTABLE(link->notices);
194 link->notices = NULL;
195 }
196 }
197
pgsql_link_free_obj(zend_object * obj)198 static void pgsql_link_free_obj(zend_object *obj)
199 {
200 pgsql_link_handle *link = pgsql_link_from_obj(obj);
201
202 if (link->conn) {
203 pgsql_link_free(link);
204 }
205
206 zend_object_std_dtor(&link->std);
207 }
208
pgsql_result_from_obj(zend_object * obj)209 static inline pgsql_result_handle *pgsql_result_from_obj(zend_object *obj) {
210 return (pgsql_result_handle *)((char *)(obj) - XtOffsetOf(pgsql_result_handle, std));
211 }
212
213 #define Z_PGSQL_RESULT_P(zv) pgsql_result_from_obj(Z_OBJ_P(zv))
214
pgsql_result_create_object(zend_class_entry * class_type)215 static zend_object *pgsql_result_create_object(zend_class_entry *class_type) {
216 pgsql_result_handle *intern = zend_object_alloc(sizeof(pgsql_result_handle), class_type);
217
218 zend_object_std_init(&intern->std, class_type);
219 object_properties_init(&intern->std, class_type);
220
221 return &intern->std;
222 }
223
pgsql_result_get_constructor(zend_object * object)224 static zend_function *pgsql_result_get_constructor(zend_object *object) {
225 zend_throw_error(NULL, "Cannot directly construct PgSql\\Result, use a dedicated function instead");
226 return NULL;
227 }
228
pgsql_result_free(pgsql_result_handle * pg_result)229 static void pgsql_result_free(pgsql_result_handle *pg_result)
230 {
231 PQclear(pg_result->result);
232 pg_result->result = NULL;
233 }
234
pgsql_result_free_obj(zend_object * obj)235 static void pgsql_result_free_obj(zend_object *obj)
236 {
237 pgsql_result_handle *pg_result = pgsql_result_from_obj(obj);
238
239 if (pg_result->result) {
240 pgsql_result_free(pg_result);
241 }
242
243 zend_object_std_dtor(&pg_result->std);
244 }
245
pgsql_lob_from_obj(zend_object * obj)246 static inline pgLofp *pgsql_lob_from_obj(zend_object *obj) {
247 return (pgLofp *)((char *)(obj) - XtOffsetOf(pgLofp, std));
248 }
249
250 #define Z_PGSQL_LOB_P(zv) pgsql_lob_from_obj(Z_OBJ_P(zv))
251
pgsql_lob_create_object(zend_class_entry * class_type)252 static zend_object *pgsql_lob_create_object(zend_class_entry *class_type) {
253 pgLofp *intern = zend_object_alloc(sizeof(pgLofp), class_type);
254
255 zend_object_std_init(&intern->std, class_type);
256 object_properties_init(&intern->std, class_type);
257
258 return &intern->std;
259 }
260
pgsql_lob_get_constructor(zend_object * object)261 static zend_function *pgsql_lob_get_constructor(zend_object *object) {
262 zend_throw_error(NULL, "Cannot directly construct PgSql\\Lob, use pg_lo_open() instead");
263 return NULL;
264 }
265
pgsql_lob_free_obj(zend_object * obj)266 static void pgsql_lob_free_obj(zend_object *obj)
267 {
268 pgLofp *lofp = pgsql_lob_from_obj(obj);
269
270 zend_object_std_dtor(&lofp->std);
271 }
272
273 /* Compatibility definitions */
274
275 #ifndef HAVE_PGSQL_WITH_MULTIBYTE_SUPPORT
276 #define pg_encoding_to_char(x) "SQL_ASCII"
277 #endif
278
_php_pgsql_trim_message(const char * message)279 static zend_string *_php_pgsql_trim_message(const char *message)
280 {
281 size_t i = strlen(message);
282
283 if (i>2 && (message[i-2] == '\r' || message[i-2] == '\n') && message[i-1] == '.') {
284 --i;
285 }
286 while (i>1 && (message[i-1] == '\r' || message[i-1] == '\n')) {
287 --i;
288 }
289 return zend_string_init(message, i, 0);
290 }
291
292 #define PHP_PQ_ERROR(text, pgsql) { \
293 zend_string *msgbuf = _php_pgsql_trim_message(PQerrorMessage(pgsql)); \
294 php_error_docref(NULL, E_WARNING, text, ZSTR_VAL(msgbuf)); \
295 zend_string_release(msgbuf); \
296 } \
297
php_pgsql_set_default_link(zend_object * obj)298 static void php_pgsql_set_default_link(zend_object *obj)
299 {
300 GC_ADDREF(obj);
301
302 if (PGG(default_link) != NULL) {
303 zend_object_release(PGG(default_link));
304 }
305
306 PGG(default_link) = obj;
307 }
308
_close_pgsql_plink(zend_resource * rsrc)309 static void _close_pgsql_plink(zend_resource *rsrc)
310 {
311 PGconn *link = (PGconn *)rsrc->ptr;
312 PGresult *res;
313
314 while ((res = PQgetResult(link))) {
315 PQclear(res);
316 }
317 PQfinish(link);
318 PGG(num_persistent)--;
319 PGG(num_links)--;
320 rsrc->ptr = NULL;
321 }
322
_php_pgsql_notice_handler(void * l,const char * message)323 static void _php_pgsql_notice_handler(void *l, const char *message)
324 {
325 if (PGG(ignore_notices)) {
326 return;
327 }
328
329 zval tmp;
330 pgsql_link_handle *link = (pgsql_link_handle *) l;
331
332 if (!link->notices) {
333 link->notices = zend_new_array(1);
334 }
335
336 zend_string *trimmed_message = _php_pgsql_trim_message(message);
337 if (PGG(log_notices)) {
338 php_error_docref(NULL, E_NOTICE, "%s", ZSTR_VAL(trimmed_message));
339 }
340
341 ZVAL_STR(&tmp, trimmed_message);
342 zend_hash_next_index_insert(link->notices, &tmp);
343 }
344
_rollback_transactions(zval * el)345 static int _rollback_transactions(zval *el)
346 {
347 PGconn *link;
348 PGresult *res;
349 zend_resource *rsrc = Z_RES_P(el);
350
351 if (rsrc->type != le_plink) {
352 return ZEND_HASH_APPLY_KEEP;
353 }
354
355 link = (PGconn *) rsrc->ptr;
356
357 if (PQsetnonblocking(link, 0)) {
358 php_error_docref("ref.pgsql", E_NOTICE, "Cannot set connection to blocking mode");
359 return -1;
360 }
361
362 while ((res = PQgetResult(link))) {
363 PQclear(res);
364 }
365 if (PQtransactionStatus(link) != PQTRANS_IDLE) {
366 int orig = PGG(ignore_notices);
367 PGG(ignore_notices) = 1;
368 res = PQexec(link,"ROLLBACK;");
369 PQclear(res);
370 PGG(ignore_notices) = orig;
371 }
372
373 return ZEND_HASH_APPLY_KEEP;
374 }
375
release_string(zval * zv)376 static void release_string(zval *zv)
377 {
378 zend_string_release((zend_string *) Z_PTR_P(zv));
379 }
380
_php_pgsql_identifier_is_escaped(const char * identifier,size_t len)381 static bool _php_pgsql_identifier_is_escaped(const char *identifier, size_t len) /* {{{ */
382 {
383 /* Handle edge case. Cannot be a escaped string */
384 if (len <= 2) {
385 return false;
386 }
387 /* Detect double quotes */
388 if (identifier[0] == '"' && identifier[len-1] == '"') {
389 size_t i;
390
391 /* Detect wrong format of " inside of escaped string */
392 for (i = 1; i < len-1; i++) {
393 if (identifier[i] == '"' && (identifier[++i] != '"' || i == len-1)) {
394 return false;
395 }
396 }
397 } else {
398 return false;
399 }
400 /* Escaped properly */
401 return true;
402 }
403
404 /* {{{ PHP_INI */
405 PHP_INI_BEGIN()
406 STD_PHP_INI_BOOLEAN( "pgsql.allow_persistent", "1", PHP_INI_SYSTEM, OnUpdateBool, allow_persistent, zend_pgsql_globals, pgsql_globals)
407 STD_PHP_INI_ENTRY_EX("pgsql.max_persistent", "-1", PHP_INI_SYSTEM, OnUpdateLong, max_persistent, zend_pgsql_globals, pgsql_globals, display_link_numbers)
408 STD_PHP_INI_ENTRY_EX("pgsql.max_links", "-1", PHP_INI_SYSTEM, OnUpdateLong, max_links, zend_pgsql_globals, pgsql_globals, display_link_numbers)
409 STD_PHP_INI_BOOLEAN( "pgsql.auto_reset_persistent", "0", PHP_INI_SYSTEM, OnUpdateLong, auto_reset_persistent, zend_pgsql_globals, pgsql_globals)
410 STD_PHP_INI_BOOLEAN( "pgsql.ignore_notice", "0", PHP_INI_ALL, OnUpdateBool, ignore_notices, zend_pgsql_globals, pgsql_globals)
411 STD_PHP_INI_BOOLEAN( "pgsql.log_notice", "0", PHP_INI_ALL, OnUpdateBool, log_notices, zend_pgsql_globals, pgsql_globals)
PHP_INI_END()412 PHP_INI_END()
413
414 static PHP_GINIT_FUNCTION(pgsql)
415 {
416 #if defined(COMPILE_DL_PGSQL) && defined(ZTS)
417 ZEND_TSRMLS_CACHE_UPDATE();
418 #endif
419 memset(pgsql_globals, 0, sizeof(zend_pgsql_globals));
420 zend_hash_init(&pgsql_globals->connections, 0, NULL, NULL, 1);
421 }
422
php_libpq_version(char * buf,size_t len)423 static void php_libpq_version(char *buf, size_t len)
424 {
425 int version = PQlibVersion();
426 int major = version / 10000;
427 if (major >= 10) {
428 int minor = version % 10000;
429 snprintf(buf, len, "%d.%d", major, minor);
430 } else {
431 int minor = version / 100 % 100;
432 int revision = version % 100;
433 snprintf(buf, len, "%d.%d.%d", major, minor, revision);
434 }
435 }
436
PHP_MINIT_FUNCTION(pgsql)437 PHP_MINIT_FUNCTION(pgsql)
438 {
439 REGISTER_INI_ENTRIES();
440
441 le_plink = zend_register_list_destructors_ex(NULL, _close_pgsql_plink, "pgsql link persistent", module_number);
442
443 pgsql_link_ce = register_class_PgSql_Connection();
444 pgsql_link_ce->create_object = pgsql_link_create_object;
445 pgsql_link_ce->default_object_handlers = &pgsql_link_object_handlers;
446
447 memcpy(&pgsql_link_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
448 pgsql_link_object_handlers.offset = XtOffsetOf(pgsql_link_handle, std);
449 pgsql_link_object_handlers.free_obj = pgsql_link_free_obj;
450 pgsql_link_object_handlers.get_constructor = pgsql_link_get_constructor;
451 pgsql_link_object_handlers.clone_obj = NULL;
452 pgsql_link_object_handlers.compare = zend_objects_not_comparable;
453
454 pgsql_result_ce = register_class_PgSql_Result();
455 pgsql_result_ce->create_object = pgsql_result_create_object;
456 pgsql_result_ce->default_object_handlers = &pgsql_result_object_handlers;
457
458 memcpy(&pgsql_result_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
459 pgsql_result_object_handlers.offset = XtOffsetOf(pgsql_result_handle, std);
460 pgsql_result_object_handlers.free_obj = pgsql_result_free_obj;
461 pgsql_result_object_handlers.get_constructor = pgsql_result_get_constructor;
462 pgsql_result_object_handlers.clone_obj = NULL;
463 pgsql_result_object_handlers.compare = zend_objects_not_comparable;
464
465 pgsql_lob_ce = register_class_PgSql_Lob();
466 pgsql_lob_ce->create_object = pgsql_lob_create_object;
467 pgsql_lob_ce->default_object_handlers = &pgsql_lob_object_handlers;
468
469 memcpy(&pgsql_lob_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
470 pgsql_lob_object_handlers.offset = XtOffsetOf(pgLofp, std);
471 pgsql_lob_object_handlers.free_obj = pgsql_lob_free_obj;
472 pgsql_lob_object_handlers.get_constructor = pgsql_lob_get_constructor;
473 pgsql_lob_object_handlers.clone_obj = NULL;
474 pgsql_lob_object_handlers.compare = zend_objects_not_comparable;
475
476 /* libpq version */
477 php_libpq_version(pgsql_libpq_version, sizeof(pgsql_libpq_version));
478
479 register_pgsql_symbols(module_number);
480
481 return SUCCESS;
482 }
483
PHP_MSHUTDOWN_FUNCTION(pgsql)484 PHP_MSHUTDOWN_FUNCTION(pgsql)
485 {
486 UNREGISTER_INI_ENTRIES();
487 zend_hash_destroy(&PGG(connections));
488
489 return SUCCESS;
490 }
491
PHP_RINIT_FUNCTION(pgsql)492 PHP_RINIT_FUNCTION(pgsql)
493 {
494 PGG(default_link) = NULL;
495 PGG(num_links) = PGG(num_persistent);
496 zend_hash_init(&PGG(field_oids), 0, NULL, release_string, 0);
497 zend_hash_init(&PGG(table_oids), 0, NULL, release_string, 0);
498 return SUCCESS;
499 }
500
PHP_RSHUTDOWN_FUNCTION(pgsql)501 PHP_RSHUTDOWN_FUNCTION(pgsql)
502 {
503 if (PGG(default_link)) {
504 zend_object_release(PGG(default_link));
505 PGG(default_link) = NULL;
506 }
507
508 zend_hash_destroy(&PGG(field_oids));
509 zend_hash_destroy(&PGG(table_oids));
510 /* clean up persistent connection */
511 zend_hash_apply(&EG(persistent_list), (apply_func_t) _rollback_transactions);
512 return SUCCESS;
513 }
514
PHP_MINFO_FUNCTION(pgsql)515 PHP_MINFO_FUNCTION(pgsql)
516 {
517 char buf[256];
518
519 php_info_print_table_start();
520 php_info_print_table_row(2, "PostgreSQL Support", "enabled");
521 php_info_print_table_row(2, "PostgreSQL (libpq) Version", pgsql_libpq_version);
522 #ifdef HAVE_PGSQL_WITH_MULTIBYTE_SUPPORT
523 php_info_print_table_row(2, "Multibyte character support", "enabled");
524 #else
525 php_info_print_table_row(2, "Multibyte character support", "disabled");
526 #endif
527 snprintf(buf, sizeof(buf), ZEND_LONG_FMT, PGG(num_persistent));
528 php_info_print_table_row(2, "Active Persistent Links", buf);
529 snprintf(buf, sizeof(buf), ZEND_LONG_FMT, PGG(num_links));
530 php_info_print_table_row(2, "Active Links", buf);
531 php_info_print_table_end();
532
533 DISPLAY_INI_ENTRIES();
534 }
535
php_pgsql_do_connect(INTERNAL_FUNCTION_PARAMETERS,int persistent)536 static void php_pgsql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
537 {
538 char *connstring;
539 size_t connstring_len;
540 pgsql_link_handle *link;
541 PGconn *pgsql;
542 smart_str str = {0};
543 zend_long connect_type = 0;
544 PGresult *pg_result;
545
546 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &connstring, &connstring_len, &connect_type) == FAILURE) {
547 RETURN_THROWS();
548 }
549
550 smart_str_appends(&str, "pgsql");
551 smart_str_appendl(&str, connstring, connstring_len);
552 smart_str_appendc(&str, '_');
553 /* make sure that the PGSQL_CONNECT_FORCE_NEW bit is not part of the hash so that subsequent
554 * connections can re-use this connection. See bug #39979. */
555 smart_str_append_long(&str, connect_type & ~PGSQL_CONNECT_FORCE_NEW);
556 smart_str_0(&str);
557
558 if (persistent && PGG(allow_persistent)) {
559 zend_resource *le;
560
561 /* try to find if we already have this link in our persistent list */
562 if ((le = zend_hash_find_ptr(&EG(persistent_list), str.s)) == NULL) { /* we don't */
563 if (PGG(max_links) != -1 && PGG(num_links) >= PGG(max_links)) {
564 php_error_docref(NULL, E_WARNING,
565 "Cannot create new link. Too many open links (" ZEND_LONG_FMT ")", PGG(num_links));
566 goto err;
567 }
568 if (PGG(max_persistent) != -1 && PGG(num_persistent) >= PGG(max_persistent)) {
569 php_error_docref(NULL, E_WARNING,
570 "Cannot create new link. Too many open persistent links (" ZEND_LONG_FMT ")", PGG(num_persistent));
571 goto err;
572 }
573
574 /* create the link */
575 pgsql = PQconnectdb(connstring);
576 if (pgsql == NULL || PQstatus(pgsql) == CONNECTION_BAD) {
577 PHP_PQ_ERROR("Unable to connect to PostgreSQL server: %s", pgsql)
578 if (pgsql) {
579 PQfinish(pgsql);
580 }
581 goto err;
582 }
583
584 /* hash it up */
585 if (zend_register_persistent_resource(ZSTR_VAL(str.s), ZSTR_LEN(str.s), pgsql, le_plink) == NULL) {
586 goto err;
587 }
588 PGG(num_links)++;
589 PGG(num_persistent)++;
590 } else { /* we do */
591 if (le->type != le_plink) {
592 goto err;
593 }
594 /* ensure that the link did not die */
595 if (PGG(auto_reset_persistent) & 1) {
596 /* need to send & get something from backend to
597 make sure we catch CONNECTION_BAD every time */
598 PGresult *pg_result;
599 pg_result = PQexec(le->ptr, "select 1");
600 PQclear(pg_result);
601 }
602 if (PQstatus(le->ptr) == CONNECTION_BAD) { /* the link died */
603 if (le->ptr == NULL) {
604 le->ptr = PQconnectdb(connstring);
605 }
606 else {
607 PQreset(le->ptr);
608 }
609 if (le->ptr == NULL || PQstatus(le->ptr) == CONNECTION_BAD) {
610 php_error_docref(NULL, E_WARNING,"PostgreSQL connection lost, unable to reconnect");
611 zend_hash_del(&EG(persistent_list), str.s);
612 goto err;
613 }
614 }
615 pgsql = (PGconn *) le->ptr;
616 /* consider to use php_version_compare() here */
617 if (zend_strtod(PQparameterStatus(pgsql, "server_version"), NULL) >= 7.2) {
618 pg_result = PQexec(pgsql, "RESET ALL;");
619 PQclear(pg_result);
620 }
621 }
622
623 object_init_ex(return_value, pgsql_link_ce);
624 link = Z_PGSQL_LINK_P(return_value);
625 link->conn = pgsql;
626 link->hash = zend_string_copy(str.s);
627 link->notices = NULL;
628 link->persistent = 1;
629 } else { /* Non persistent connection */
630 zval *index_ptr;
631
632 /* first we check the hash for the hashed_details key. If it exists,
633 * it should point us to the right offset where the actual pgsql link sits.
634 * if it doesn't, open a new pgsql link, add it to the resource list,
635 * and add a pointer to it with hashed_details as the key.
636 */
637 if (!(connect_type & PGSQL_CONNECT_FORCE_NEW)
638 && (index_ptr = zend_hash_find(&PGG(connections), str.s)) != NULL) {
639 php_pgsql_set_default_link(Z_OBJ_P(index_ptr));
640 ZVAL_COPY(return_value, index_ptr);
641
642 goto cleanup;
643 }
644 if (PGG(max_links) != -1 && PGG(num_links) >= PGG(max_links)) {
645 php_error_docref(NULL, E_WARNING, "Cannot create new link. Too many open links (" ZEND_LONG_FMT ")", PGG(num_links));
646 goto err;
647 }
648
649 /* Non-blocking connect */
650 if (connect_type & PGSQL_CONNECT_ASYNC) {
651 pgsql = PQconnectStart(connstring);
652 if (pgsql==NULL || PQstatus(pgsql)==CONNECTION_BAD) {
653 PHP_PQ_ERROR("Unable to connect to PostgreSQL server: %s", pgsql);
654 if (pgsql) {
655 PQfinish(pgsql);
656 }
657 goto err;
658 }
659 } else {
660 pgsql = PQconnectdb(connstring);
661 if (pgsql==NULL || PQstatus(pgsql)==CONNECTION_BAD) {
662 PHP_PQ_ERROR("Unable to connect to PostgreSQL server: %s", pgsql);
663 if (pgsql) {
664 PQfinish(pgsql);
665 }
666 goto err;
667 }
668 }
669
670 object_init_ex(return_value, pgsql_link_ce);
671 link = Z_PGSQL_LINK_P(return_value);
672 link->conn = pgsql;
673 link->hash = zend_string_copy(str.s);
674 link->notices = NULL;
675 link->persistent = 0;
676
677 /* add it to the hash */
678 zend_hash_update(&PGG(connections), str.s, return_value);
679
680 /* Keep track of link => hash mapping, so we can remove the hash entry from connections
681 * when the connection is closed. This uses the address of the connection rather than the
682 * zend_resource, because the resource destructor is passed a stack copy of the resource
683 * structure. */
684
685 PGG(num_links)++;
686 }
687 /* set notice processor */
688 if (! PGG(ignore_notices) && Z_TYPE_P(return_value) == IS_OBJECT) {
689 PQsetNoticeProcessor(pgsql, _php_pgsql_notice_handler, link);
690 }
691 php_pgsql_set_default_link(Z_OBJ_P(return_value));
692
693 cleanup:
694 smart_str_free(&str);
695 return;
696
697 err:
698 smart_str_free(&str);
699 RETURN_FALSE;
700 }
701 /* }}} */
702
703 /* {{{ Open a PostgreSQL connection */
PHP_FUNCTION(pg_connect)704 PHP_FUNCTION(pg_connect)
705 {
706 php_pgsql_do_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU,0);
707 }
708 /* }}} */
709
710 /* {{{ Poll the status of an in-progress async PostgreSQL connection attempt*/
PHP_FUNCTION(pg_connect_poll)711 PHP_FUNCTION(pg_connect_poll)
712 {
713 zval *pgsql_link;
714 pgsql_link_handle *pgsql_handle;
715 PGconn *pgsql;
716 int ret;
717
718 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pgsql_link, pgsql_link_ce) == FAILURE) {
719 RETURN_THROWS();
720 }
721
722 pgsql_handle = Z_PGSQL_LINK_P(pgsql_link);
723 CHECK_PGSQL_LINK(pgsql_handle);
724 pgsql = pgsql_handle->conn;
725
726 ret = PQconnectPoll(pgsql);
727
728 RETURN_LONG(ret);
729 }
730 /* }}} */
731
732 /* {{{ Open a persistent PostgreSQL connection */
PHP_FUNCTION(pg_pconnect)733 PHP_FUNCTION(pg_pconnect)
734 {
735 php_pgsql_do_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU,1);
736 }
737 /* }}} */
738
739 /* {{{ Close a PostgreSQL connection */
PHP_FUNCTION(pg_close)740 PHP_FUNCTION(pg_close)
741 {
742 zval *pgsql_link = NULL;
743 pgsql_link_handle *link;
744
745 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|O!", &pgsql_link, pgsql_link_ce) == FAILURE) {
746 RETURN_THROWS();
747 }
748
749 if (!pgsql_link) {
750 link = FETCH_DEFAULT_LINK();
751 CHECK_DEFAULT_LINK(link);
752 zend_object_release(PGG(default_link));
753 PGG(default_link) = NULL;
754 RETURN_TRUE;
755 }
756
757 link = Z_PGSQL_LINK_P(pgsql_link);
758 CHECK_PGSQL_LINK(link);
759
760 if (link == FETCH_DEFAULT_LINK_NO_WARNING()) {
761 GC_DELREF(PGG(default_link));
762 PGG(default_link) = NULL;
763 }
764 pgsql_link_free(link);
765
766 RETURN_TRUE;
767 }
768 /* }}} */
769
770 #define PHP_PG_DBNAME 1
771 #define PHP_PG_ERROR_MESSAGE 2
772 #define PHP_PG_OPTIONS 3
773 #define PHP_PG_PORT 4
774 #define PHP_PG_TTY 5
775 #define PHP_PG_HOST 6
776 #define PHP_PG_VERSION 7
777
778 /* php_pgsql_get_link_info */
php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAMETERS,int entry_type)779 static void php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAMETERS, int entry_type)
780 {
781 pgsql_link_handle *link;
782 zval *pgsql_link = NULL;
783 PGconn *pgsql;
784 char *result;
785
786 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|O!", &pgsql_link, pgsql_link_ce) == FAILURE) {
787 RETURN_THROWS();
788 }
789
790 if (!pgsql_link) {
791 link = FETCH_DEFAULT_LINK();
792 CHECK_DEFAULT_LINK(link);
793 } else {
794 link = Z_PGSQL_LINK_P(pgsql_link);
795 CHECK_PGSQL_LINK(link);
796 }
797
798 pgsql = link->conn;
799
800 switch(entry_type) {
801 case PHP_PG_DBNAME:
802 result = PQdb(pgsql);
803 break;
804 case PHP_PG_ERROR_MESSAGE:
805 RETURN_STR(_php_pgsql_trim_message(PQerrorMessage(pgsql)));
806 case PHP_PG_OPTIONS:
807 result = PQoptions(pgsql);
808 break;
809 case PHP_PG_PORT:
810 result = PQport(pgsql);
811 break;
812 case PHP_PG_TTY:
813 result = PQtty(pgsql);
814 break;
815 case PHP_PG_HOST:
816 result = PQhost(pgsql);
817 break;
818 case PHP_PG_VERSION: {
819 array_init(return_value);
820 char *tmp;
821 add_assoc_string(return_value, "client", pgsql_libpq_version);
822 add_assoc_long(return_value, "protocol", PQprotocolVersion(pgsql));
823 add_assoc_string(return_value, "server", (char*)PQparameterStatus(pgsql, "server_version"));
824
825 #define PHP_PQ_COPY_PARAM(_x) tmp = (char*)PQparameterStatus(pgsql, _x); \
826 if(tmp) add_assoc_string(return_value, _x, tmp); \
827 else add_assoc_null(return_value, _x);
828
829 PHP_PQ_COPY_PARAM("server_encoding");
830 PHP_PQ_COPY_PARAM("client_encoding");
831 PHP_PQ_COPY_PARAM("is_superuser");
832 PHP_PQ_COPY_PARAM("session_authorization");
833 PHP_PQ_COPY_PARAM("DateStyle");
834 PHP_PQ_COPY_PARAM("IntervalStyle");
835 PHP_PQ_COPY_PARAM("TimeZone");
836 PHP_PQ_COPY_PARAM("integer_datetimes");
837 PHP_PQ_COPY_PARAM("standard_conforming_strings");
838 PHP_PQ_COPY_PARAM("application_name");
839 return;
840 }
841 EMPTY_SWITCH_DEFAULT_CASE()
842 }
843 if (result) {
844 RETURN_STRING(result);
845 } else {
846 RETURN_EMPTY_STRING();
847 }
848 }
849
850 /* Get the database name */
PHP_FUNCTION(pg_dbname)851 PHP_FUNCTION(pg_dbname)
852 {
853 php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_DBNAME);
854 }
855
856 /* Get the error message string */
PHP_FUNCTION(pg_last_error)857 PHP_FUNCTION(pg_last_error)
858 {
859 php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_ERROR_MESSAGE);
860 }
861
862 /* Get the options associated with the connection */
PHP_FUNCTION(pg_options)863 PHP_FUNCTION(pg_options)
864 {
865 php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_OPTIONS);
866 }
867
868 /* Return the port number associated with the connection */
PHP_FUNCTION(pg_port)869 PHP_FUNCTION(pg_port)
870 {
871 php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_PORT);
872 }
873
874 /* Return the tty name associated with the connection */
PHP_FUNCTION(pg_tty)875 PHP_FUNCTION(pg_tty)
876 {
877 php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_TTY);
878 }
879
880 /* Returns the host name associated with the connection */
PHP_FUNCTION(pg_host)881 PHP_FUNCTION(pg_host)
882 {
883 php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_HOST);
884 }
885
886 /* Returns an array with client, protocol and server version (when available) */
PHP_FUNCTION(pg_version)887 PHP_FUNCTION(pg_version)
888 {
889 php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_VERSION);
890 }
891
892 /* Returns the value of a server parameter */
PHP_FUNCTION(pg_parameter_status)893 PHP_FUNCTION(pg_parameter_status)
894 {
895 zval *pgsql_link = NULL;
896 pgsql_link_handle *link;
897 PGconn *pgsql;
898 char *param;
899 size_t len;
900
901 if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "Os", &pgsql_link, pgsql_link_ce, ¶m, &len) == FAILURE) {
902 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", ¶m, &len) == FAILURE) {
903 RETURN_THROWS();
904 }
905
906 link = FETCH_DEFAULT_LINK();
907 CHECK_DEFAULT_LINK(link);
908 } else {
909 link = Z_PGSQL_LINK_P(pgsql_link);
910 CHECK_PGSQL_LINK(link);
911 }
912
913 pgsql = link->conn;
914
915 param = (char*)PQparameterStatus(pgsql, param);
916 if (param) {
917 RETURN_STRING(param);
918 } else {
919 RETURN_FALSE;
920 }
921 }
922
923 /* Ping database. If connection is bad, try to reconnect. */
PHP_FUNCTION(pg_ping)924 PHP_FUNCTION(pg_ping)
925 {
926 zval *pgsql_link = NULL;
927 PGconn *pgsql;
928 PGresult *res;
929 pgsql_link_handle *link;
930
931 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|O!", &pgsql_link, pgsql_link_ce) == FAILURE) {
932 RETURN_THROWS();
933 }
934
935 if (pgsql_link == NULL) {
936 link = FETCH_DEFAULT_LINK();
937 CHECK_DEFAULT_LINK(link);
938 } else {
939 link = Z_PGSQL_LINK_P(pgsql_link);
940 CHECK_PGSQL_LINK(link);
941 }
942
943 pgsql = link->conn;
944
945 /* ping connection */
946 res = PQexec(pgsql, "SELECT 1;");
947 PQclear(res);
948
949 /* check status. */
950 if (PQstatus(pgsql) == CONNECTION_OK)
951 RETURN_TRUE;
952
953 /* reset connection if it's broken */
954 PQreset(pgsql);
955 if (PQstatus(pgsql) == CONNECTION_OK) {
956 RETURN_TRUE;
957 }
958 RETURN_FALSE;
959 }
960
961 /* Execute a query */
PHP_FUNCTION(pg_query)962 PHP_FUNCTION(pg_query)
963 {
964 zval *pgsql_link = NULL;
965 char *query;
966 size_t query_len;
967 int leftover = 0;
968 pgsql_link_handle *link;
969 PGconn *pgsql;
970 PGresult *pgsql_result;
971 ExecStatusType status;
972
973 if (ZEND_NUM_ARGS() == 1) {
974 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &query, &query_len) == FAILURE) {
975 RETURN_THROWS();
976 }
977 link = FETCH_DEFAULT_LINK();
978 CHECK_DEFAULT_LINK(link);
979 } else {
980 if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os", &pgsql_link, pgsql_link_ce, &query, &query_len) == FAILURE) {
981 RETURN_THROWS();
982 }
983 link = Z_PGSQL_LINK_P(pgsql_link);
984 CHECK_PGSQL_LINK(link);
985 }
986
987 pgsql = link->conn;
988
989 if (PQsetnonblocking(pgsql, 0)) {
990 php_error_docref(NULL, E_NOTICE,"Cannot set connection to blocking mode");
991 RETURN_FALSE;
992 }
993 while ((pgsql_result = PQgetResult(pgsql))) {
994 PQclear(pgsql_result);
995 leftover = 1;
996 }
997 if (leftover) {
998 php_error_docref(NULL, E_NOTICE, "Found results on this connection. Use pg_get_result() to get these results first");
999 }
1000 pgsql_result = PQexec(pgsql, query);
1001 if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
1002 PQclear(pgsql_result);
1003 PQreset(pgsql);
1004 pgsql_result = PQexec(pgsql, query);
1005 }
1006
1007 if (pgsql_result) {
1008 status = PQresultStatus(pgsql_result);
1009 } else {
1010 status = (ExecStatusType) PQstatus(pgsql);
1011 }
1012
1013 switch (status) {
1014 case PGRES_EMPTY_QUERY:
1015 case PGRES_BAD_RESPONSE:
1016 case PGRES_NONFATAL_ERROR:
1017 case PGRES_FATAL_ERROR:
1018 PHP_PQ_ERROR("Query failed: %s", pgsql);
1019 PQclear(pgsql_result);
1020 RETURN_FALSE;
1021 break;
1022 case PGRES_COMMAND_OK: /* successful command that did not return rows */
1023 default:
1024 if (pgsql_result) {
1025 object_init_ex(return_value, pgsql_result_ce);
1026 pgsql_result_handle *pg_result = Z_PGSQL_RESULT_P(return_value);
1027 pg_result->conn = pgsql;
1028 pg_result->result = pgsql_result;
1029 pg_result->row = 0;
1030 } else {
1031 PQclear(pgsql_result);
1032 RETURN_FALSE;
1033 }
1034 break;
1035 }
1036 }
1037
_php_pgsql_free_params(char ** params,int num_params)1038 static void _php_pgsql_free_params(char **params, int num_params)
1039 {
1040 if (num_params > 0) {
1041 int i;
1042 for (i = 0; i < num_params; i++) {
1043 if (params[i]) {
1044 efree(params[i]);
1045 }
1046 }
1047 efree(params);
1048 }
1049 }
1050
1051 /* Execute a query */
PHP_FUNCTION(pg_query_params)1052 PHP_FUNCTION(pg_query_params)
1053 {
1054 zval *pgsql_link = NULL;
1055 zval *pv_param_arr, *tmp;
1056 char *query;
1057 size_t query_len;
1058 int leftover = 0;
1059 int num_params = 0;
1060 char **params = NULL;
1061 pgsql_link_handle *link;
1062 PGconn *pgsql;
1063 PGresult *pgsql_result;
1064 ExecStatusType status;
1065 pgsql_result_handle *pg_result;
1066
1067 if (ZEND_NUM_ARGS() == 2) {
1068 if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa", &query, &query_len, &pv_param_arr) == FAILURE) {
1069 RETURN_THROWS();
1070 }
1071 link = FETCH_DEFAULT_LINK();
1072 CHECK_DEFAULT_LINK(link);
1073 } else {
1074 if (zend_parse_parameters(ZEND_NUM_ARGS(), "Osa", &pgsql_link, pgsql_link_ce, &query, &query_len, &pv_param_arr) == FAILURE) {
1075 RETURN_THROWS();
1076 }
1077 link = Z_PGSQL_LINK_P(pgsql_link);
1078 CHECK_PGSQL_LINK(link);
1079 }
1080
1081 pgsql = link->conn;
1082
1083 if (PQsetnonblocking(pgsql, 0)) {
1084 php_error_docref(NULL, E_NOTICE,"Cannot set connection to blocking mode");
1085 RETURN_FALSE;
1086 }
1087 while ((pgsql_result = PQgetResult(pgsql))) {
1088 PQclear(pgsql_result);
1089 leftover = 1;
1090 }
1091 if (leftover) {
1092 php_error_docref(NULL, E_NOTICE, "Found results on this connection. Use pg_get_result() to get these results first");
1093 }
1094
1095 num_params = zend_hash_num_elements(Z_ARRVAL_P(pv_param_arr));
1096 if (num_params > 0) {
1097 int i = 0;
1098 params = (char **)safe_emalloc(sizeof(char *), num_params, 0);
1099
1100 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pv_param_arr), tmp) {
1101 ZVAL_DEREF(tmp);
1102 if (Z_TYPE_P(tmp) == IS_NULL) {
1103 params[i] = NULL;
1104 } else {
1105 zend_string *param_str = zval_try_get_string(tmp);
1106 if (!param_str) {
1107 _php_pgsql_free_params(params, i);
1108 RETURN_THROWS();
1109 }
1110 params[i] = estrndup(ZSTR_VAL(param_str), ZSTR_LEN(param_str));
1111 zend_string_release(param_str);
1112 }
1113 i++;
1114 } ZEND_HASH_FOREACH_END();
1115 }
1116
1117 pgsql_result = PQexecParams(pgsql, query, num_params,
1118 NULL, (const char * const *)params, NULL, NULL, 0);
1119 if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
1120 PQclear(pgsql_result);
1121 PQreset(pgsql);
1122 pgsql_result = PQexecParams(pgsql, query, num_params,
1123 NULL, (const char * const *)params, NULL, NULL, 0);
1124 }
1125
1126 if (pgsql_result) {
1127 status = PQresultStatus(pgsql_result);
1128 } else {
1129 status = (ExecStatusType) PQstatus(pgsql);
1130 }
1131
1132 _php_pgsql_free_params(params, num_params);
1133
1134 switch (status) {
1135 case PGRES_EMPTY_QUERY:
1136 case PGRES_BAD_RESPONSE:
1137 case PGRES_NONFATAL_ERROR:
1138 case PGRES_FATAL_ERROR:
1139 PHP_PQ_ERROR("Query failed: %s", pgsql);
1140 PQclear(pgsql_result);
1141 RETURN_FALSE;
1142 break;
1143 case PGRES_COMMAND_OK: /* successful command that did not return rows */
1144 default:
1145 if (pgsql_result) {
1146 object_init_ex(return_value, pgsql_result_ce);
1147 pg_result = Z_PGSQL_RESULT_P(return_value);
1148 pg_result->conn = pgsql;
1149 pg_result->result = pgsql_result;
1150 pg_result->row = 0;
1151 } else {
1152 PQclear(pgsql_result);
1153 RETURN_FALSE;
1154 }
1155 break;
1156 }
1157 }
1158
1159 /* Prepare a query for future execution */
PHP_FUNCTION(pg_prepare)1160 PHP_FUNCTION(pg_prepare)
1161 {
1162 zval *pgsql_link = NULL;
1163 char *query, *stmtname;
1164 size_t query_len, stmtname_len;
1165 int leftover = 0;
1166 PGconn *pgsql;
1167 pgsql_link_handle *link;
1168 PGresult *pgsql_result;
1169 ExecStatusType status;
1170 pgsql_result_handle *pg_result;
1171
1172 if (ZEND_NUM_ARGS() == 2) {
1173 if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &stmtname, &stmtname_len, &query, &query_len) == FAILURE) {
1174 RETURN_THROWS();
1175 }
1176 link = FETCH_DEFAULT_LINK();
1177 CHECK_DEFAULT_LINK(link);
1178 } else {
1179 if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oss", &pgsql_link, pgsql_link_ce, &stmtname, &stmtname_len, &query, &query_len) == FAILURE) {
1180 RETURN_THROWS();
1181 }
1182 link = Z_PGSQL_LINK_P(pgsql_link);
1183 CHECK_PGSQL_LINK(link);
1184 }
1185
1186 pgsql = link->conn;
1187
1188 if (PQsetnonblocking(pgsql, 0)) {
1189 php_error_docref(NULL, E_NOTICE,"Cannot set connection to blocking mode");
1190 RETURN_FALSE;
1191 }
1192 while ((pgsql_result = PQgetResult(pgsql))) {
1193 PQclear(pgsql_result);
1194 leftover = 1;
1195 }
1196 if (leftover) {
1197 php_error_docref(NULL, E_NOTICE, "Found results on this connection. Use pg_get_result() to get these results first");
1198 }
1199 pgsql_result = PQprepare(pgsql, stmtname, query, 0, NULL);
1200 if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
1201 PQclear(pgsql_result);
1202 PQreset(pgsql);
1203 pgsql_result = PQprepare(pgsql, stmtname, query, 0, NULL);
1204 }
1205
1206 if (pgsql_result) {
1207 status = PQresultStatus(pgsql_result);
1208 } else {
1209 status = (ExecStatusType) PQstatus(pgsql);
1210 }
1211
1212 switch (status) {
1213 case PGRES_EMPTY_QUERY:
1214 case PGRES_BAD_RESPONSE:
1215 case PGRES_NONFATAL_ERROR:
1216 case PGRES_FATAL_ERROR:
1217 PHP_PQ_ERROR("Query failed: %s", pgsql);
1218 PQclear(pgsql_result);
1219 RETURN_FALSE;
1220 break;
1221 case PGRES_COMMAND_OK: /* successful command that did not return rows */
1222 default:
1223 if (pgsql_result) {
1224 object_init_ex(return_value, pgsql_result_ce);
1225 pg_result = Z_PGSQL_RESULT_P(return_value);
1226 pg_result->conn = pgsql;
1227 pg_result->result = pgsql_result;
1228 pg_result->row = 0;
1229 } else {
1230 PQclear(pgsql_result);
1231 RETURN_FALSE;
1232 }
1233 break;
1234 }
1235 }
1236
1237 /* Execute a prepared query */
PHP_FUNCTION(pg_execute)1238 PHP_FUNCTION(pg_execute)
1239 {
1240 zval *pgsql_link = NULL;
1241 zval *pv_param_arr, *tmp;
1242 char *stmtname;
1243 size_t stmtname_len;
1244 int leftover = 0;
1245 int num_params = 0;
1246 char **params = NULL;
1247 PGconn *pgsql;
1248 pgsql_link_handle *link;
1249 PGresult *pgsql_result;
1250 ExecStatusType status;
1251 pgsql_result_handle *pg_result;
1252
1253 if (ZEND_NUM_ARGS() == 2) {
1254 if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa", &stmtname, &stmtname_len, &pv_param_arr)==FAILURE) {
1255 RETURN_THROWS();
1256 }
1257 link = FETCH_DEFAULT_LINK();
1258 CHECK_DEFAULT_LINK(link);
1259 } else {
1260 if (zend_parse_parameters(ZEND_NUM_ARGS(), "Osa", &pgsql_link, pgsql_link_ce, &stmtname, &stmtname_len, &pv_param_arr) == FAILURE) {
1261 RETURN_THROWS();
1262 }
1263 link = Z_PGSQL_LINK_P(pgsql_link);
1264 CHECK_PGSQL_LINK(link);
1265 }
1266
1267 pgsql = link->conn;
1268
1269 if (PQsetnonblocking(pgsql, 0)) {
1270 php_error_docref(NULL, E_NOTICE,"Cannot set connection to blocking mode");
1271 RETURN_FALSE;
1272 }
1273 while ((pgsql_result = PQgetResult(pgsql))) {
1274 PQclear(pgsql_result);
1275 leftover = 1;
1276 }
1277 if (leftover) {
1278 php_error_docref(NULL, E_NOTICE, "Found results on this connection. Use pg_get_result() to get these results first");
1279 }
1280
1281 num_params = zend_hash_num_elements(Z_ARRVAL_P(pv_param_arr));
1282 if (num_params > 0) {
1283 int i = 0;
1284 params = (char **)safe_emalloc(sizeof(char *), num_params, 0);
1285
1286 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pv_param_arr), tmp) {
1287 ZVAL_DEREF(tmp);
1288 if (Z_TYPE_P(tmp) == IS_NULL) {
1289 params[i] = NULL;
1290 } else {
1291 zend_string *tmp_str;
1292 zend_string *str = zval_get_tmp_string(tmp, &tmp_str);
1293
1294 params[i] = estrndup(ZSTR_VAL(str), ZSTR_LEN(str));
1295 zend_tmp_string_release(tmp_str);
1296 }
1297
1298 i++;
1299 } ZEND_HASH_FOREACH_END();
1300 }
1301
1302 pgsql_result = PQexecPrepared(pgsql, stmtname, num_params,
1303 (const char * const *)params, NULL, NULL, 0);
1304 if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
1305 PQclear(pgsql_result);
1306 PQreset(pgsql);
1307 pgsql_result = PQexecPrepared(pgsql, stmtname, num_params,
1308 (const char * const *)params, NULL, NULL, 0);
1309 }
1310
1311 if (pgsql_result) {
1312 status = PQresultStatus(pgsql_result);
1313 } else {
1314 status = (ExecStatusType) PQstatus(pgsql);
1315 }
1316
1317 _php_pgsql_free_params(params, num_params);
1318
1319 switch (status) {
1320 case PGRES_EMPTY_QUERY:
1321 case PGRES_BAD_RESPONSE:
1322 case PGRES_NONFATAL_ERROR:
1323 case PGRES_FATAL_ERROR:
1324 PHP_PQ_ERROR("Query failed: %s", pgsql);
1325 PQclear(pgsql_result);
1326 RETURN_FALSE;
1327 break;
1328 case PGRES_COMMAND_OK: /* successful command that did not return rows */
1329 default:
1330 if (pgsql_result) {
1331 object_init_ex(return_value, pgsql_result_ce);
1332 pg_result = Z_PGSQL_RESULT_P(return_value);
1333 pg_result->conn = pgsql;
1334 pg_result->result = pgsql_result;
1335 pg_result->row = 0;
1336 } else {
1337 PQclear(pgsql_result);
1338 RETURN_FALSE;
1339 }
1340 break;
1341 }
1342 }
1343
1344 #define PHP_PG_NUM_ROWS 1
1345 #define PHP_PG_NUM_FIELDS 2
1346 #define PHP_PG_CMD_TUPLES 3
1347
1348 /* php_pgsql_get_result_info */
php_pgsql_get_result_info(INTERNAL_FUNCTION_PARAMETERS,int entry_type)1349 static void php_pgsql_get_result_info(INTERNAL_FUNCTION_PARAMETERS, int entry_type)
1350 {
1351 zval *result;
1352 PGresult *pgsql_result;
1353 pgsql_result_handle *pg_result;
1354
1355 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &result, pgsql_result_ce) == FAILURE) {
1356 RETURN_THROWS();
1357 }
1358
1359 pg_result = Z_PGSQL_RESULT_P(result);
1360 CHECK_PGSQL_RESULT(pg_result);
1361 pgsql_result = pg_result->result;
1362
1363 switch (entry_type) {
1364 case PHP_PG_NUM_ROWS:
1365 RETVAL_LONG(PQntuples(pgsql_result));
1366 break;
1367 case PHP_PG_NUM_FIELDS:
1368 RETVAL_LONG(PQnfields(pgsql_result));
1369 break;
1370 case PHP_PG_CMD_TUPLES:
1371 RETVAL_LONG(atoi(PQcmdTuples(pgsql_result)));
1372 break;
1373 EMPTY_SWITCH_DEFAULT_CASE()
1374 }
1375 }
1376
1377 /* Return the number of rows in the result */
PHP_FUNCTION(pg_num_rows)1378 PHP_FUNCTION(pg_num_rows)
1379 {
1380 php_pgsql_get_result_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_NUM_ROWS);
1381 }
1382
1383 /* Return the number of fields in the result */
PHP_FUNCTION(pg_num_fields)1384 PHP_FUNCTION(pg_num_fields)
1385 {
1386 php_pgsql_get_result_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_NUM_FIELDS);
1387 }
1388
1389 /* Returns the number of affected tuples */
PHP_FUNCTION(pg_affected_rows)1390 PHP_FUNCTION(pg_affected_rows)
1391 {
1392 php_pgsql_get_result_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_CMD_TUPLES);
1393 }
1394
1395 /* Returns the last notice set by the backend */
PHP_FUNCTION(pg_last_notice)1396 PHP_FUNCTION(pg_last_notice)
1397 {
1398 zval *pgsql_link = NULL;
1399 zval *notice;
1400 HashTable *notices;
1401 pgsql_link_handle *link;
1402 zend_long option = PGSQL_NOTICE_LAST;
1403
1404 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|l", &pgsql_link, pgsql_link_ce, &option) == FAILURE) {
1405 RETURN_THROWS();
1406 }
1407
1408 link = Z_PGSQL_LINK_P(pgsql_link);
1409 CHECK_PGSQL_LINK(link);
1410
1411 notices = link->notices;
1412 switch (option) {
1413 case PGSQL_NOTICE_LAST:
1414 if (notices) {
1415 zend_hash_internal_pointer_end(notices);
1416 if ((notice = zend_hash_get_current_data(notices)) == NULL) {
1417 RETURN_EMPTY_STRING();
1418 }
1419 RETURN_COPY(notice);
1420 } else {
1421 RETURN_EMPTY_STRING();
1422 }
1423 break;
1424 case PGSQL_NOTICE_ALL:
1425 if (notices) {
1426 RETURN_ARR(zend_array_dup(notices));
1427 } else {
1428 array_init(return_value);
1429 return;
1430 }
1431 break;
1432 case PGSQL_NOTICE_CLEAR:
1433 if (notices) {
1434 zend_hash_clean(notices);
1435 }
1436 RETURN_TRUE;
1437 break;
1438 default:
1439 zend_argument_value_error(2, "must be one of PGSQL_NOTICE_LAST, PGSQL_NOTICE_ALL, or PGSQL_NOTICE_CLEAR");
1440 RETURN_THROWS();
1441 }
1442 ZEND_UNREACHABLE();
1443 }
1444
is_valid_oid_string(zend_string * oid,Oid * return_oid)1445 static inline bool is_valid_oid_string(zend_string *oid, Oid *return_oid)
1446 {
1447 char *end_ptr;
1448 *return_oid = (Oid) strtoul(ZSTR_VAL(oid), &end_ptr, 10);
1449 return ZSTR_VAL(oid) + ZSTR_LEN(oid) == end_ptr;
1450 }
1451
get_field_name(PGconn * pgsql,Oid oid)1452 static zend_string *get_field_name(PGconn *pgsql, Oid oid)
1453 {
1454 zend_string *ret = zend_hash_index_find_ptr(&PGG(field_oids), oid);
1455 if (ret) {
1456 zend_string_addref(ret);
1457 return ret;
1458 }
1459
1460 PGresult *result = PQexec(pgsql, "select oid,typname from pg_type");
1461 if (!result || PQresultStatus(result) != PGRES_TUPLES_OK) {
1462 if (result) {
1463 PQclear(result);
1464 }
1465 return ZSTR_EMPTY_ALLOC();
1466 }
1467
1468 int num_rows = PQntuples(result);
1469 int oid_offset = PQfnumber(result,"oid");
1470 int name_offset = PQfnumber(result,"typname");
1471 for (int i = 0; i < num_rows; i++) {
1472 char *tmp_oid_str = PQgetvalue(result, i, oid_offset);
1473 if (!tmp_oid_str) {
1474 continue;
1475 }
1476
1477 char *tmp_name = PQgetvalue(result, i, name_offset);
1478 if (!tmp_name) {
1479 continue;
1480 }
1481
1482 char *end_ptr;
1483 Oid tmp_oid = strtoul(tmp_oid_str, &end_ptr, 10);
1484
1485 zend_string *name = zend_string_init(tmp_name, strlen(tmp_name), 0);
1486 zend_hash_index_update_ptr(&PGG(field_oids), tmp_oid, name);
1487 if (!ret && tmp_oid == oid) {
1488 ret = zend_string_copy(name);
1489 }
1490 }
1491
1492 PQclear(result);
1493 return ret;
1494 }
1495
1496 /* Returns the name of the table field belongs to, or table's oid if oid_only is true */
PHP_FUNCTION(pg_field_table)1497 PHP_FUNCTION(pg_field_table)
1498 {
1499 zval *result;
1500 pgsql_result_handle *pg_result;
1501 zend_long fnum = -1;
1502 bool return_oid = 0;
1503
1504 if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol|b", &result, pgsql_result_ce, &fnum, &return_oid) == FAILURE) {
1505 RETURN_THROWS();
1506 }
1507
1508 pg_result = Z_PGSQL_RESULT_P(result);
1509 CHECK_PGSQL_RESULT(pg_result);
1510
1511 if (fnum < 0) {
1512 zend_argument_value_error(2, "must be greater than or equal to 0");
1513 RETURN_THROWS();
1514 }
1515
1516 if (fnum >= PQnfields(pg_result->result)) {
1517 zend_argument_value_error(2, "must be less than the number of fields for this result set");
1518 RETURN_THROWS();
1519 }
1520
1521 Oid oid = PQftable(pg_result->result, (int)fnum);
1522 if (InvalidOid == oid) {
1523 RETURN_FALSE;
1524 }
1525
1526 if (return_oid) {
1527 PGSQL_RETURN_OID(oid);
1528 }
1529
1530 zend_string *field_table = zend_hash_index_find_ptr(&PGG(table_oids), oid);
1531 if (field_table) {
1532 RETURN_STR_COPY(field_table);
1533 }
1534
1535 /* Not found, lookup by querying PostgreSQL system tables */
1536 smart_str querystr = {0};
1537 smart_str_appends(&querystr, "select relname from pg_class where oid=");
1538 smart_str_append_unsigned(&querystr, oid);
1539 smart_str_0(&querystr);
1540
1541 PGresult *tmp_res = PQexec(pg_result->conn, ZSTR_VAL(querystr.s));
1542 smart_str_free(&querystr);
1543 if (!tmp_res || PQresultStatus(tmp_res) != PGRES_TUPLES_OK) {
1544 if (tmp_res) {
1545 PQclear(tmp_res);
1546 }
1547 RETURN_FALSE;
1548 }
1549
1550 char *table_name = PQgetvalue(tmp_res, 0, 0);
1551 if (!table_name) {
1552 PQclear(tmp_res);
1553 RETURN_FALSE;
1554 }
1555
1556 field_table = zend_string_init(table_name, strlen(table_name), 0);
1557 zend_hash_index_update_ptr(&PGG(table_oids), oid, field_table);
1558
1559 PQclear(tmp_res);
1560 RETURN_STR_COPY(field_table);
1561 }
1562 /* }}} */
1563
1564 #define PHP_PG_FIELD_NAME 1
1565 #define PHP_PG_FIELD_SIZE 2
1566 #define PHP_PG_FIELD_TYPE 3
1567 #define PHP_PG_FIELD_TYPE_OID 4
1568
1569 /* {{{ php_pgsql_get_field_info */
php_pgsql_get_field_info(INTERNAL_FUNCTION_PARAMETERS,int entry_type)1570 static void php_pgsql_get_field_info(INTERNAL_FUNCTION_PARAMETERS, int entry_type)
1571 {
1572 zval *result;
1573 zend_long field;
1574 PGresult *pgsql_result;
1575 pgsql_result_handle *pg_result;
1576 Oid oid;
1577
1578 if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &result, pgsql_result_ce, &field) == FAILURE) {
1579 RETURN_THROWS();
1580 }
1581
1582 pg_result = Z_PGSQL_RESULT_P(result);
1583 CHECK_PGSQL_RESULT(pg_result);
1584
1585 if (field < 0) {
1586 zend_argument_value_error(2, "must be greater than or equal to 0");
1587 RETURN_THROWS();
1588 }
1589
1590 pgsql_result = pg_result->result;
1591
1592 if (field >= PQnfields(pgsql_result)) {
1593 zend_argument_value_error(2, "must be less than the number of fields for this result set");
1594 RETURN_THROWS();
1595 }
1596
1597 switch (entry_type) {
1598 case PHP_PG_FIELD_NAME:
1599 RETURN_STRING(PQfname(pgsql_result, (int)field));
1600 break;
1601 case PHP_PG_FIELD_SIZE:
1602 RETURN_LONG(PQfsize(pgsql_result, (int)field));
1603 break;
1604 case PHP_PG_FIELD_TYPE:
1605 RETURN_STR(get_field_name(pg_result->conn, PQftype(pgsql_result, (int)field)));
1606 break;
1607 case PHP_PG_FIELD_TYPE_OID:
1608
1609 oid = PQftype(pgsql_result, (int)field);
1610 PGSQL_RETURN_OID(oid);
1611 break;
1612 EMPTY_SWITCH_DEFAULT_CASE()
1613 }
1614 }
1615 /* }}} */
1616
1617 /* {{{ Returns the name of the field */
PHP_FUNCTION(pg_field_name)1618 PHP_FUNCTION(pg_field_name)
1619 {
1620 php_pgsql_get_field_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_FIELD_NAME);
1621 }
1622 /* }}} */
1623
1624 /* {{{ Returns the internal size of the field */
PHP_FUNCTION(pg_field_size)1625 PHP_FUNCTION(pg_field_size)
1626 {
1627 php_pgsql_get_field_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_FIELD_SIZE);
1628 }
1629 /* }}} */
1630
1631 /* {{{ Returns the type name for the given field */
PHP_FUNCTION(pg_field_type)1632 PHP_FUNCTION(pg_field_type)
1633 {
1634 php_pgsql_get_field_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_FIELD_TYPE);
1635 }
1636 /* }}} */
1637
1638 /* {{{ Returns the type oid for the given field */
PHP_FUNCTION(pg_field_type_oid)1639 PHP_FUNCTION(pg_field_type_oid)
1640 {
1641 php_pgsql_get_field_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_FIELD_TYPE_OID);
1642 }
1643 /* }}} */
1644
1645 /* {{{ Returns the field number of the named field */
PHP_FUNCTION(pg_field_num)1646 PHP_FUNCTION(pg_field_num)
1647 {
1648 zval *result;
1649 char *field;
1650 size_t field_len;
1651 PGresult *pgsql_result;
1652 pgsql_result_handle *pg_result;
1653
1654 if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os", &result, pgsql_result_ce, &field, &field_len) == FAILURE) {
1655 RETURN_THROWS();
1656 }
1657
1658 pg_result = Z_PGSQL_RESULT_P(result);
1659 CHECK_PGSQL_RESULT(pg_result);
1660 pgsql_result = pg_result->result;
1661
1662 RETURN_LONG(PQfnumber(pgsql_result, field));
1663 }
1664 /* }}} */
1665
field_arg_to_offset(PGresult * result,zend_string * field_name,zend_long field_offset,int arg_num)1666 static zend_long field_arg_to_offset(
1667 PGresult *result, zend_string *field_name, zend_long field_offset, int arg_num) {
1668 if (field_name) {
1669 field_offset = PQfnumber(result, ZSTR_VAL(field_name));
1670 if (field_offset < 0) {
1671 /* Avoid displaying the argument name, as the signature is overloaded and the name
1672 * might not line up. */
1673 zend_value_error("Argument #%d must be a field name from this result set", arg_num);
1674 return -1;
1675 }
1676 } else {
1677 if (field_offset < 0) {
1678 zend_value_error("Argument #%d must be greater than or equal to 0", arg_num);
1679 return -1;
1680 }
1681 if (field_offset >= PQnfields(result)) {
1682 zend_value_error("Argument #%d must be less than the number of fields for this result set", arg_num);
1683 return -1;
1684 }
1685 }
1686 return field_offset;
1687 }
1688
1689 /* {{{ Returns values from a result identifier */
PHP_FUNCTION(pg_fetch_result)1690 PHP_FUNCTION(pg_fetch_result)
1691 {
1692 zval *result;
1693 zend_string *field_name;
1694 zend_long row, field_offset = 0;
1695 bool row_is_null = false;
1696 PGresult *pgsql_result;
1697 pgsql_result_handle *pg_result;
1698 int pgsql_row;
1699
1700 if (ZEND_NUM_ARGS() == 2) {
1701 if (zend_string_equals_literal(EX(func)->common.function_name, "pg_fetch_result")) {
1702 zend_error(E_DEPRECATED, "Calling pg_fetch_result() with 2 arguments is deprecated, "
1703 "use the 3-parameter signature with a null $row parameter instead");
1704 if (UNEXPECTED(EG(exception))) {
1705 RETURN_THROWS();
1706 }
1707 }
1708
1709 ZEND_PARSE_PARAMETERS_START(2, 2)
1710 Z_PARAM_OBJECT_OF_CLASS(result, pgsql_result_ce)
1711 Z_PARAM_STR_OR_LONG(field_name, field_offset)
1712 ZEND_PARSE_PARAMETERS_END();
1713 } else {
1714 ZEND_PARSE_PARAMETERS_START(3, 3)
1715 Z_PARAM_OBJECT_OF_CLASS(result, pgsql_result_ce)
1716 if (zend_string_equals_literal(EG(current_execute_data)->func->common.function_name, "pg_result")) {
1717 Z_PARAM_LONG(row)
1718 } else {
1719 Z_PARAM_LONG_OR_NULL(row, row_is_null)
1720 }
1721 Z_PARAM_STR_OR_LONG(field_name, field_offset)
1722 ZEND_PARSE_PARAMETERS_END();
1723 }
1724
1725 pg_result = Z_PGSQL_RESULT_P(result);
1726 CHECK_PGSQL_RESULT(pg_result);
1727 pgsql_result = pg_result->result;
1728
1729 if (ZEND_NUM_ARGS() == 2 || row_is_null) {
1730 if (pg_result->row < 0) {
1731 pg_result->row = 0;
1732 }
1733 pgsql_row = pg_result->row;
1734 if (pgsql_row >= PQntuples(pgsql_result)) {
1735 RETURN_FALSE;
1736 }
1737 pg_result->row++;
1738 } else {
1739 if (row < 0) {
1740 zend_argument_value_error(2, "must be greater than or equal to 0");
1741 RETURN_THROWS();
1742 }
1743 if (row >= PQntuples(pgsql_result)) {
1744 php_error_docref(NULL, E_WARNING, "Unable to jump to row " ZEND_LONG_FMT " on PostgreSQL result index " ZEND_LONG_FMT,
1745 row, Z_LVAL_P(result));
1746 RETURN_FALSE;
1747 }
1748 pgsql_row = (int)row;
1749 }
1750
1751 field_offset = field_arg_to_offset(pgsql_result, field_name, field_offset, ZEND_NUM_ARGS());
1752 if (field_offset < 0) {
1753 RETURN_THROWS();
1754 }
1755
1756 if (PQgetisnull(pgsql_result, pgsql_row, field_offset)) {
1757 RETVAL_NULL();
1758 } else {
1759 RETVAL_STRINGL(PQgetvalue(pgsql_result, pgsql_row, field_offset),
1760 PQgetlength(pgsql_result, pgsql_row, field_offset));
1761 }
1762 }
1763 /* }}} */
1764
1765 /* {{{ void php_pgsql_fetch_hash */
php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS,zend_long result_type,int into_object)1766 static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, zend_long result_type, int into_object)
1767 {
1768 zval *result;
1769 PGresult *pgsql_result;
1770 pgsql_result_handle *pg_result;
1771 int i, num_fields, pgsql_row;
1772 zend_long row;
1773 bool row_is_null = 1;
1774 char *field_name;
1775 HashTable *ctor_params = NULL;
1776 zend_class_entry *ce = NULL;
1777
1778 if (into_object) {
1779 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|l!Ch", &result, pgsql_result_ce, &row, &row_is_null, &ce, &ctor_params) == FAILURE) {
1780 RETURN_THROWS();
1781 }
1782 if (!ce) {
1783 ce = zend_standard_class_def;
1784 }
1785 result_type = PGSQL_ASSOC;
1786 } else {
1787 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|l!l", &result, pgsql_result_ce, &row, &row_is_null, &result_type) == FAILURE) {
1788 RETURN_THROWS();
1789 }
1790 }
1791
1792 if (!row_is_null && row < 0) {
1793 zend_argument_value_error(2, "must be greater than or equal to 0");
1794 RETURN_THROWS();
1795 }
1796
1797 if (!(result_type & PGSQL_BOTH)) {
1798 zend_argument_value_error(3, "must be one of PGSQL_ASSOC, PGSQL_NUM, or PGSQL_BOTH");
1799 RETURN_THROWS();
1800 }
1801
1802 pg_result = Z_PGSQL_RESULT_P(result);
1803 CHECK_PGSQL_RESULT(pg_result);
1804 pgsql_result = pg_result->result;
1805
1806 if (!row_is_null) {
1807 if (row >= PQntuples(pgsql_result)) {
1808 php_error_docref(NULL, E_WARNING, "Unable to jump to row " ZEND_LONG_FMT " on PostgreSQL result index " ZEND_LONG_FMT,
1809 row, Z_LVAL_P(result));
1810 RETURN_FALSE;
1811 }
1812 pgsql_row = (int)row;
1813 pg_result->row = pgsql_row;
1814 } else {
1815 /* If 2nd param is NULL, use internal row counter to access next row */
1816 pgsql_row = pg_result->row;
1817 if (pgsql_row < 0 || pgsql_row >= PQntuples(pgsql_result)) {
1818 RETURN_FALSE;
1819 }
1820 pg_result->row++;
1821 }
1822
1823 array_init(return_value);
1824 for (i = 0, num_fields = PQnfields(pgsql_result); i < num_fields; i++) {
1825 if (PQgetisnull(pgsql_result, pgsql_row, i)) {
1826 if (result_type & PGSQL_NUM) {
1827 add_index_null(return_value, i);
1828 }
1829 if (result_type & PGSQL_ASSOC) {
1830 field_name = PQfname(pgsql_result, i);
1831 add_assoc_null(return_value, field_name);
1832 }
1833 } else {
1834 char *element = PQgetvalue(pgsql_result, pgsql_row, i);
1835 if (element) {
1836 const size_t element_len = strlen(element);
1837
1838 if (result_type & PGSQL_NUM) {
1839 add_index_stringl(return_value, i, element, element_len);
1840 }
1841
1842 if (result_type & PGSQL_ASSOC) {
1843 field_name = PQfname(pgsql_result, i);
1844 add_assoc_stringl(return_value, field_name, element, element_len);
1845 }
1846 }
1847 }
1848 }
1849
1850 if (into_object) {
1851 zval dataset;
1852
1853 ZVAL_COPY_VALUE(&dataset, return_value);
1854 object_init_ex(return_value, ce);
1855 if (!ce->default_properties_count && !ce->__set) {
1856 Z_OBJ_P(return_value)->properties = Z_ARR(dataset);
1857 } else {
1858 zend_merge_properties(return_value, Z_ARRVAL(dataset));
1859 zval_ptr_dtor(&dataset);
1860 }
1861
1862 if (ce->constructor) {
1863 zend_call_known_function(ce->constructor, Z_OBJ_P(return_value), Z_OBJCE_P(return_value),
1864 /* retval */ NULL, /* argc */ 0, /* params */ NULL, ctor_params);
1865 } else if (ctor_params && zend_hash_num_elements(ctor_params) > 0) {
1866 zend_argument_value_error(3,
1867 "must be empty when the specified class (%s) does not have a constructor",
1868 ZSTR_VAL(ce->name)
1869 );
1870 }
1871 }
1872 }
1873 /* }}} */
1874
1875 /* {{{ Get a row as an enumerated array */
PHP_FUNCTION(pg_fetch_row)1876 PHP_FUNCTION(pg_fetch_row)
1877 {
1878 php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_NUM, 0);
1879 }
1880 /* }}} */
1881
1882 /* {{{ Fetch a row as an assoc array */
PHP_FUNCTION(pg_fetch_assoc)1883 PHP_FUNCTION(pg_fetch_assoc)
1884 {
1885 /* pg_fetch_assoc() is added from PHP 4.3.0. It should raise error, when
1886 there is 3rd parameter */
1887 if (ZEND_NUM_ARGS() > 2)
1888 WRONG_PARAM_COUNT;
1889 php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_ASSOC, 0);
1890 }
1891 /* }}} */
1892
1893 /* {{{ Fetch a row as an array */
PHP_FUNCTION(pg_fetch_array)1894 PHP_FUNCTION(pg_fetch_array)
1895 {
1896 php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_BOTH, 0);
1897 }
1898 /* }}} */
1899
1900 /* {{{ Fetch a row as an object */
PHP_FUNCTION(pg_fetch_object)1901 PHP_FUNCTION(pg_fetch_object)
1902 {
1903 /* pg_fetch_object() allowed result_type used to be. 3rd parameter
1904 must be allowed for compatibility */
1905 php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_ASSOC, 1);
1906 }
1907 /* }}} */
1908
1909 /* {{{ Fetch all rows into array */
PHP_FUNCTION(pg_fetch_all)1910 PHP_FUNCTION(pg_fetch_all)
1911 {
1912 zval *result;
1913 zend_long result_type = PGSQL_ASSOC;
1914 PGresult *pgsql_result;
1915 pgsql_result_handle *pg_result;
1916
1917 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|l", &result, pgsql_result_ce, &result_type) == FAILURE) {
1918 RETURN_THROWS();
1919 }
1920
1921 if (!(result_type & PGSQL_BOTH)) {
1922 zend_argument_value_error(2, "must be one of PGSQL_ASSOC, PGSQL_NUM, or PGSQL_BOTH");
1923 RETURN_THROWS();
1924 }
1925
1926 pg_result = Z_PGSQL_RESULT_P(result);
1927 CHECK_PGSQL_RESULT(pg_result);
1928 pgsql_result = pg_result->result;
1929
1930 array_init(return_value);
1931 php_pgsql_result2array(pgsql_result, return_value, result_type);
1932 }
1933 /* }}} */
1934
1935 /* {{{ Fetch all rows into array */
PHP_FUNCTION(pg_fetch_all_columns)1936 PHP_FUNCTION(pg_fetch_all_columns)
1937 {
1938 zval *result;
1939 PGresult *pgsql_result;
1940 pgsql_result_handle *pg_result;
1941 zend_long colno=0;
1942 int pg_numrows, pg_row;
1943 size_t num_fields;
1944
1945 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|l", &result, pgsql_result_ce, &colno) == FAILURE) {
1946 RETURN_THROWS();
1947 }
1948
1949 pg_result = Z_PGSQL_RESULT_P(result);
1950 CHECK_PGSQL_RESULT(pg_result);
1951
1952 if (colno < 0) {
1953 zend_argument_value_error(2, "must be greater than or equal to 0");
1954 RETURN_THROWS();
1955 }
1956
1957 pgsql_result = pg_result->result;
1958
1959 num_fields = PQnfields(pgsql_result);
1960 if (colno >= (zend_long)num_fields) {
1961 zend_argument_value_error(2, "must be less than the number of fields for this result set");
1962 RETURN_THROWS();
1963 }
1964
1965 array_init(return_value);
1966
1967 if ((pg_numrows = PQntuples(pgsql_result)) <= 0) {
1968 return;
1969 }
1970
1971 for (pg_row = 0; pg_row < pg_numrows; pg_row++) {
1972 if (PQgetisnull(pgsql_result, pg_row, (int)colno)) {
1973 add_next_index_null(return_value);
1974 } else {
1975 add_next_index_string(return_value, PQgetvalue(pgsql_result, pg_row, (int)colno));
1976 }
1977 }
1978 }
1979 /* }}} */
1980
1981 /* {{{ Set internal row offset */
PHP_FUNCTION(pg_result_seek)1982 PHP_FUNCTION(pg_result_seek)
1983 {
1984 zval *result;
1985 zend_long row;
1986 pgsql_result_handle *pg_result;
1987
1988 if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &result, pgsql_result_ce, &row) == FAILURE) {
1989 RETURN_THROWS();
1990 }
1991
1992 pg_result = Z_PGSQL_RESULT_P(result);
1993 CHECK_PGSQL_RESULT(pg_result);
1994
1995 if (row < 0 || row >= PQntuples(pg_result->result)) {
1996 RETURN_FALSE;
1997 }
1998
1999 /* seek to offset */
2000 pg_result->row = (int)row;
2001 RETURN_TRUE;
2002 }
2003 /* }}} */
2004
2005 #define PHP_PG_DATA_LENGTH 1
2006 #define PHP_PG_DATA_ISNULL 2
2007
2008 /* {{{ php_pgsql_data_info */
php_pgsql_data_info(INTERNAL_FUNCTION_PARAMETERS,int entry_type,bool nullable_row)2009 static void php_pgsql_data_info(INTERNAL_FUNCTION_PARAMETERS, int entry_type, bool nullable_row)
2010 {
2011 zval *result;
2012 zend_string *field_name;
2013 zend_long row, field_offset = 0;
2014 bool row_is_null = false;
2015 PGresult *pgsql_result;
2016 pgsql_result_handle *pg_result;
2017 int pgsql_row;
2018
2019 if (ZEND_NUM_ARGS() == 2) {
2020 if (nullable_row) {
2021 zend_error(E_DEPRECATED, "Calling %s() with 2 arguments is deprecated, "
2022 "use the 3-parameter signature with a null $row parameter instead",
2023 ZSTR_VAL(EX(func)->common.function_name));
2024 if (UNEXPECTED(EG(exception))) {
2025 RETURN_THROWS();
2026 }
2027 }
2028
2029 ZEND_PARSE_PARAMETERS_START(2, 2)
2030 Z_PARAM_OBJECT_OF_CLASS(result, pgsql_result_ce)
2031 Z_PARAM_STR_OR_LONG(field_name, field_offset)
2032 ZEND_PARSE_PARAMETERS_END();
2033 } else {
2034 ZEND_PARSE_PARAMETERS_START(3, 3)
2035 Z_PARAM_OBJECT_OF_CLASS(result, pgsql_result_ce)
2036 if (nullable_row) {
2037 Z_PARAM_LONG_OR_NULL(row, row_is_null)
2038 } else {
2039 Z_PARAM_LONG(row)
2040 }
2041 Z_PARAM_STR_OR_LONG(field_name, field_offset)
2042 ZEND_PARSE_PARAMETERS_END();
2043 }
2044
2045 pg_result = Z_PGSQL_RESULT_P(result);
2046 CHECK_PGSQL_RESULT(pg_result);
2047 pgsql_result = pg_result->result;
2048
2049 if (ZEND_NUM_ARGS() == 2 || row_is_null) {
2050 if (pg_result->row < 0) {
2051 pg_result->row = 0;
2052 }
2053 pgsql_row = pg_result->row;
2054 if (pgsql_row < 0 || pgsql_row >= PQntuples(pgsql_result)) {
2055 RETURN_FALSE;
2056 }
2057 } else {
2058 if (row < 0) {
2059 zend_argument_value_error(2, "must be greater than or equal to 0");
2060 RETURN_THROWS();
2061 }
2062 if (row >= PQntuples(pgsql_result)) {
2063 php_error_docref(NULL, E_WARNING, "Unable to jump to row " ZEND_LONG_FMT " on PostgreSQL result index " ZEND_LONG_FMT,
2064 row, Z_LVAL_P(result));
2065 RETURN_FALSE;
2066 }
2067 pgsql_row = (int)row;
2068 }
2069
2070 field_offset = field_arg_to_offset(pgsql_result, field_name, field_offset, ZEND_NUM_ARGS());
2071 if (field_offset < 0) {
2072 RETURN_THROWS();
2073 }
2074
2075 switch (entry_type) {
2076 case PHP_PG_DATA_LENGTH:
2077 RETVAL_LONG(PQgetlength(pgsql_result, pgsql_row, field_offset));
2078 break;
2079 case PHP_PG_DATA_ISNULL:
2080 RETVAL_LONG(PQgetisnull(pgsql_result, pgsql_row, field_offset));
2081 break;
2082 EMPTY_SWITCH_DEFAULT_CASE()
2083 }
2084 }
2085 /* }}} */
2086
2087 /* {{{ Returns the printed length */
PHP_FUNCTION(pg_field_prtlen)2088 PHP_FUNCTION(pg_field_prtlen)
2089 {
2090 php_pgsql_data_info(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_PG_DATA_LENGTH, true);
2091 }
2092 /* }}} */
2093
2094 /* {{{ Returns the printed length */
PHP_FUNCTION(pg_fieldprtlen)2095 PHP_FUNCTION(pg_fieldprtlen)
2096 {
2097 php_pgsql_data_info(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_PG_DATA_LENGTH, false);
2098 }
2099 /* }}} */
2100
2101 /* {{{ Test if a field is NULL */
PHP_FUNCTION(pg_field_is_null)2102 PHP_FUNCTION(pg_field_is_null)
2103 {
2104 php_pgsql_data_info(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_PG_DATA_ISNULL, true);
2105 }
2106 /* }}} */
2107
2108 /* {{{ Test if a field is NULL */
PHP_FUNCTION(pg_fieldisnull)2109 PHP_FUNCTION(pg_fieldisnull)
2110 {
2111 php_pgsql_data_info(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_PG_DATA_ISNULL, false);
2112 }
2113 /* }}} */
2114
2115 /* {{{ Free result memory */
PHP_FUNCTION(pg_free_result)2116 PHP_FUNCTION(pg_free_result)
2117 {
2118 zval *result;
2119 pgsql_result_handle *pg_result;
2120
2121 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &result, pgsql_result_ce) == FAILURE) {
2122 RETURN_THROWS();
2123 }
2124
2125 pg_result = Z_PGSQL_RESULT_P(result);
2126 CHECK_PGSQL_RESULT(pg_result);
2127
2128 pgsql_result_free(pg_result);
2129 RETURN_TRUE;
2130 }
2131 /* }}} */
2132
2133 /* {{{ Returns the last object identifier */
PHP_FUNCTION(pg_last_oid)2134 PHP_FUNCTION(pg_last_oid)
2135 {
2136 zval *result;
2137 PGresult *pgsql_result;
2138 pgsql_result_handle *pg_result;
2139 Oid oid;
2140
2141 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &result, pgsql_result_ce) == FAILURE) {
2142 RETURN_THROWS();
2143 }
2144
2145 pg_result = Z_PGSQL_RESULT_P(result);
2146 CHECK_PGSQL_RESULT(pg_result);
2147 pgsql_result = pg_result->result;
2148
2149 oid = PQoidValue(pgsql_result);
2150 if (oid == InvalidOid) {
2151 RETURN_FALSE;
2152 }
2153 PGSQL_RETURN_OID(oid);
2154 }
2155 /* }}} */
2156
2157 /* {{{ Enable tracing a PostgreSQL connection */
PHP_FUNCTION(pg_trace)2158 PHP_FUNCTION(pg_trace)
2159 {
2160 char *z_filename, *mode = "w";
2161 size_t z_filename_len, mode_len;
2162 zend_long trace_mode = 0;
2163 zval *pgsql_link = NULL;
2164 PGconn *pgsql;
2165 FILE *fp = NULL;
2166 php_stream *stream;
2167 pgsql_link_handle *link;
2168
2169 if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|sO!l", &z_filename, &z_filename_len, &mode, &mode_len, &pgsql_link, pgsql_link_ce, &trace_mode) == FAILURE) {
2170 RETURN_THROWS();
2171 }
2172
2173 if (!pgsql_link) {
2174 link = FETCH_DEFAULT_LINK();
2175 CHECK_DEFAULT_LINK(link);
2176 } else {
2177 link = Z_PGSQL_LINK_P(pgsql_link);
2178 CHECK_PGSQL_LINK(link);
2179 }
2180
2181 pgsql = link->conn;
2182
2183 stream = php_stream_open_wrapper(z_filename, mode, REPORT_ERRORS, NULL);
2184
2185 if (!stream) {
2186 RETURN_FALSE;
2187 }
2188
2189 if (FAILURE == php_stream_cast(stream, PHP_STREAM_AS_STDIO, (void**)&fp, REPORT_ERRORS)) {
2190 php_stream_close(stream);
2191 RETURN_FALSE;
2192 }
2193 php_stream_auto_cleanup(stream);
2194 PQtrace(pgsql, fp);
2195 if (trace_mode > 0) {
2196 #ifdef PQTRACE_REGRESS_MODE
2197 if (!(trace_mode & (PQTRACE_SUPPRESS_TIMESTAMPS|PQTRACE_REGRESS_MODE))) {
2198 zend_argument_value_error(4, "must be PGSQL_TRACE_SUPPRESS_TIMESTAMPS and/or PGSQL_TRACE_REGRESS_MODE");
2199 RETURN_THROWS();
2200 } else {
2201 PQsetTraceFlags(pgsql, trace_mode);
2202 }
2203 #else
2204 zend_argument_value_error(4, "cannot set as trace is unsupported");
2205 RETURN_THROWS();
2206 #endif
2207 }
2208 RETURN_TRUE;
2209 }
2210 /* }}} */
2211
2212 /* {{{ Disable tracing of a PostgreSQL connection */
PHP_FUNCTION(pg_untrace)2213 PHP_FUNCTION(pg_untrace)
2214 {
2215 zval *pgsql_link = NULL;
2216 PGconn *pgsql;
2217 pgsql_link_handle *link;
2218
2219 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|O!", &pgsql_link, pgsql_link_ce) == FAILURE) {
2220 RETURN_THROWS();
2221 }
2222
2223 if (pgsql_link == NULL) {
2224 link = FETCH_DEFAULT_LINK();
2225 CHECK_DEFAULT_LINK(link);
2226 } else {
2227 link = Z_PGSQL_LINK_P(pgsql_link);
2228 CHECK_PGSQL_LINK(link);
2229 }
2230
2231 pgsql = link->conn;
2232
2233 PQuntrace(pgsql);
2234 RETURN_TRUE;
2235 }
2236 /* }}} */
2237
2238 /* {{{ Create a large object */
PHP_FUNCTION(pg_lo_create)2239 PHP_FUNCTION(pg_lo_create)
2240 {
2241 zval *pgsql_link = NULL, *oid = NULL;
2242 PGconn *pgsql;
2243 Oid pgsql_oid, wanted_oid = InvalidOid;
2244 pgsql_link_handle *link;
2245
2246 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|zz", &pgsql_link, &oid) == FAILURE) {
2247 RETURN_THROWS();
2248 }
2249
2250 /* Overloaded method uses default link if arg 1 is not an object, set oid pointer */
2251 if ((ZEND_NUM_ARGS() == 1) && (Z_TYPE_P(pgsql_link) != IS_OBJECT)) {
2252 oid = pgsql_link;
2253 pgsql_link = NULL;
2254 }
2255
2256 if (pgsql_link == NULL) {
2257 link = FETCH_DEFAULT_LINK();
2258 CHECK_DEFAULT_LINK(link);
2259 } else if ((Z_TYPE_P(pgsql_link) == IS_OBJECT && instanceof_function(Z_OBJCE_P(pgsql_link), pgsql_link_ce))) {
2260 link = Z_PGSQL_LINK_P(pgsql_link);
2261 CHECK_PGSQL_LINK(link);
2262 } else {
2263 zend_argument_type_error(1, "must be of type PgSql\\Connection when the connection is provided");
2264 RETURN_THROWS();
2265 }
2266
2267 pgsql = link->conn;
2268
2269 if (oid) {
2270 switch (Z_TYPE_P(oid)) {
2271 case IS_STRING:
2272 {
2273 if (!is_valid_oid_string(Z_STR_P(oid), &wanted_oid)) {
2274 /* wrong integer format */
2275 zend_value_error("Invalid OID value passed");
2276 RETURN_THROWS();
2277 }
2278 }
2279 break;
2280 case IS_LONG:
2281 if (Z_LVAL_P(oid) < (zend_long)InvalidOid) {
2282 zend_value_error("Invalid OID value passed");
2283 RETURN_THROWS();
2284 }
2285 wanted_oid = (Oid)Z_LVAL_P(oid);
2286 break;
2287 default:
2288 zend_type_error("OID value must be of type string|int, %s given", zend_zval_value_name(oid));
2289 RETURN_THROWS();
2290 }
2291 if ((pgsql_oid = lo_create(pgsql, wanted_oid)) == InvalidOid) {
2292 php_error_docref(NULL, E_WARNING, "Unable to create PostgreSQL large object");
2293 RETURN_FALSE;
2294 }
2295
2296 PGSQL_RETURN_OID(pgsql_oid);
2297 }
2298
2299 if ((pgsql_oid = lo_creat(pgsql, INV_READ|INV_WRITE)) == InvalidOid) {
2300 php_error_docref(NULL, E_WARNING, "Unable to create PostgreSQL large object");
2301 RETURN_FALSE;
2302 }
2303
2304 PGSQL_RETURN_OID(pgsql_oid);
2305 }
2306 /* }}} */
2307
2308 /* {{{ Delete a large object */
PHP_FUNCTION(pg_lo_unlink)2309 PHP_FUNCTION(pg_lo_unlink)
2310 {
2311 zval *pgsql_link = NULL;
2312 zend_long oid_long;
2313 zend_string *oid_string;
2314 PGconn *pgsql;
2315 Oid oid;
2316 pgsql_link_handle *link;
2317
2318 /* accept string type since Oid type is unsigned int */
2319 if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "OS", &pgsql_link, pgsql_link_ce, &oid_string) == SUCCESS) {
2320 if (!is_valid_oid_string(oid_string, &oid)) {
2321 /* wrong integer format */
2322 zend_value_error("Invalid OID value passed");
2323 RETURN_THROWS();
2324 }
2325 link = Z_PGSQL_LINK_P(pgsql_link);
2326 CHECK_PGSQL_LINK(link);
2327 }
2328 else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(),
2329 "Ol", &pgsql_link, pgsql_link_ce, &oid_long) == SUCCESS) {
2330 if (oid_long <= (zend_long)InvalidOid) {
2331 zend_value_error("Invalid OID value passed");
2332 RETURN_THROWS();
2333 }
2334 oid = (Oid)oid_long;
2335 link = Z_PGSQL_LINK_P(pgsql_link);
2336 CHECK_PGSQL_LINK(link);
2337 }
2338 else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "S", &oid_string) == SUCCESS) {
2339 if (!is_valid_oid_string(oid_string, &oid)) {
2340 /* wrong integer format */
2341 zend_value_error("Invalid OID value passed");
2342 RETURN_THROWS();
2343 }
2344 link = FETCH_DEFAULT_LINK();
2345 CHECK_DEFAULT_LINK(link);
2346 }
2347 else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(),
2348 "l", &oid_long) == SUCCESS) {
2349 if (oid_long <= (zend_long)InvalidOid) {
2350 zend_value_error("Invalid OID value passed");
2351 RETURN_THROWS();
2352 }
2353 oid = (Oid)oid_long;
2354 link = FETCH_DEFAULT_LINK();
2355 CHECK_DEFAULT_LINK(link);
2356 }
2357 else {
2358 zend_argument_count_error("Requires 1 or 2 arguments, %d given", ZEND_NUM_ARGS());
2359 RETURN_THROWS();
2360 }
2361
2362 pgsql = link->conn;
2363
2364 if (lo_unlink(pgsql, oid) == -1) {
2365 php_error_docref(NULL, E_WARNING, "Unable to delete PostgreSQL large object %u", oid);
2366 RETURN_FALSE;
2367 }
2368 RETURN_TRUE;
2369 }
2370 /* }}} */
2371
2372 /* {{{ Open a large object and return fd */
PHP_FUNCTION(pg_lo_open)2373 PHP_FUNCTION(pg_lo_open)
2374 {
2375 zval *pgsql_link = NULL;
2376 zend_long oid_long;
2377 zend_string *oid_string;
2378 zend_string *mode;
2379 PGconn *pgsql;
2380 Oid oid;
2381 int pgsql_mode=0, pgsql_lofd;
2382 bool create = false;
2383 pgLofp *pgsql_lofp;
2384 pgsql_link_handle *link;
2385
2386 /* accept string type since Oid is unsigned int */
2387 if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(),
2388 "OSS", &pgsql_link, pgsql_link_ce, &oid_string, &mode) == SUCCESS) {
2389 if (!is_valid_oid_string(oid_string, &oid)) {
2390 /* wrong integer format */
2391 zend_value_error("Invalid OID value passed");
2392 RETURN_THROWS();
2393 }
2394 link = Z_PGSQL_LINK_P(pgsql_link);
2395 CHECK_PGSQL_LINK(link);
2396 }
2397 else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(),
2398 "OlS", &pgsql_link, pgsql_link_ce, &oid_long, &mode) == SUCCESS) {
2399 if (oid_long <= (zend_long)InvalidOid) {
2400 zend_value_error("Invalid OID value passed");
2401 RETURN_THROWS();
2402 }
2403 oid = (Oid)oid_long;
2404 link = Z_PGSQL_LINK_P(pgsql_link);
2405 CHECK_PGSQL_LINK(link);
2406 }
2407 else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(),
2408 "SS", &oid_string, &mode) == SUCCESS) {
2409 if (!is_valid_oid_string(oid_string, &oid)) {
2410 /* wrong integer format */
2411 zend_value_error("Invalid OID value passed");
2412 RETURN_THROWS();
2413 }
2414 link = FETCH_DEFAULT_LINK();
2415 CHECK_DEFAULT_LINK(link);
2416 }
2417 else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(),
2418 "lS", &oid_long, &mode) == SUCCESS) {
2419 if (oid_long <= (zend_long)InvalidOid) {
2420 zend_value_error("Invalid OID value passed");
2421 RETURN_THROWS();
2422 }
2423 oid = (Oid)oid_long;
2424 link = FETCH_DEFAULT_LINK();
2425 CHECK_DEFAULT_LINK(link);
2426 }
2427 else {
2428 zend_argument_count_error("Requires 1 or 2 arguments, %d given", ZEND_NUM_ARGS());
2429 RETURN_THROWS();
2430 }
2431
2432 pgsql = link->conn;
2433
2434 /* r/w/+ is little bit more PHP-like than INV_READ/INV_WRITE and a lot of
2435 faster to type. Unfortunately, doesn't behave the same way as fopen()...
2436 (Jouni)
2437 */
2438 if (zend_string_equals_literal(mode, "r")) {
2439 pgsql_mode |= INV_READ;
2440 } else if (zend_string_equals_literal(mode, "w")) {
2441 pgsql_mode |= INV_WRITE;
2442 create = true;
2443 } else if (zend_string_equals_literal(mode, "r+")) {
2444 pgsql_mode |= INV_READ;
2445 pgsql_mode |= INV_WRITE;
2446 } else if (zend_string_equals_literal(mode, "w+")) {
2447 pgsql_mode |= INV_READ;
2448 pgsql_mode |= INV_WRITE;
2449 create = true;
2450 } else {
2451 zend_value_error("Mode must be one of 'r', 'r+', 'w', or 'w+'");
2452 RETURN_THROWS();
2453 }
2454
2455 if ((pgsql_lofd = lo_open(pgsql, oid, pgsql_mode)) == -1) {
2456 if (create) {
2457 if ((oid = lo_creat(pgsql, INV_READ|INV_WRITE)) == 0) {
2458 php_error_docref(NULL, E_WARNING, "Unable to create PostgreSQL large object");
2459 RETURN_FALSE;
2460 } else {
2461 if ((pgsql_lofd = lo_open(pgsql, oid, pgsql_mode)) == -1) {
2462 if (lo_unlink(pgsql, oid) == -1) {
2463 php_error_docref(NULL, E_WARNING, "Something is really messed up! Your database is badly corrupted in a way NOT related to PHP");
2464 } else {
2465 php_error_docref(NULL, E_WARNING, "Unable to open PostgreSQL large object");
2466 }
2467
2468 RETURN_FALSE;
2469 }
2470 }
2471 } else {
2472 php_error_docref(NULL, E_WARNING, "Unable to open PostgreSQL large object");
2473 RETURN_FALSE;
2474 }
2475 }
2476
2477 object_init_ex(return_value, pgsql_lob_ce);
2478 pgsql_lofp = Z_PGSQL_LOB_P(return_value);
2479 pgsql_lofp->conn = pgsql;
2480 pgsql_lofp->lofd = pgsql_lofd;
2481 }
2482 /* }}} */
2483
2484 /* {{{ Close a large object */
PHP_FUNCTION(pg_lo_close)2485 PHP_FUNCTION(pg_lo_close)
2486 {
2487 zval *pgsql_lofp;
2488 pgLofp *pgsql;
2489
2490 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pgsql_lofp, pgsql_lob_ce) == FAILURE) {
2491 RETURN_THROWS();
2492 }
2493
2494 pgsql = Z_PGSQL_LOB_P(pgsql_lofp);
2495 CHECK_PGSQL_LOB(pgsql);
2496
2497 if (lo_close((PGconn *)pgsql->conn, pgsql->lofd) < 0) {
2498 php_error_docref(NULL, E_WARNING, "Unable to close PostgreSQL large object descriptor %d", pgsql->lofd);
2499 RETVAL_FALSE;
2500 } else {
2501 RETVAL_TRUE;
2502 }
2503
2504 return;
2505 }
2506 /* }}} */
2507
2508 #define PGSQL_LO_READ_BUF_SIZE 8192
2509
2510 /* {{{ Read a large object */
PHP_FUNCTION(pg_lo_read)2511 PHP_FUNCTION(pg_lo_read)
2512 {
2513 zval *pgsql_id;
2514 zend_long buffer_length = PGSQL_LO_READ_BUF_SIZE;
2515 int nbytes;
2516 zend_string *buf;
2517 pgLofp *pgsql;
2518
2519 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|l", &pgsql_id, pgsql_lob_ce, &buffer_length) == FAILURE) {
2520 RETURN_THROWS();
2521 }
2522
2523 pgsql = Z_PGSQL_LOB_P(pgsql_id);
2524 CHECK_PGSQL_LOB(pgsql);
2525
2526 if (buffer_length < 0) {
2527 zend_argument_value_error(2, "must be greater or equal than 0");
2528 RETURN_THROWS();
2529 }
2530
2531 buf = zend_string_alloc(buffer_length, 0);
2532 if ((nbytes = lo_read((PGconn *)pgsql->conn, pgsql->lofd, ZSTR_VAL(buf), ZSTR_LEN(buf)))<0) {
2533 zend_string_efree(buf);
2534 RETURN_FALSE;
2535 }
2536
2537 ZSTR_VAL(buf)[nbytes] = '\0';
2538 buf = zend_string_truncate(buf, nbytes, 0);
2539 RETURN_NEW_STR(buf);
2540 }
2541 /* }}} */
2542
2543 /* {{{ Write a large object */
PHP_FUNCTION(pg_lo_write)2544 PHP_FUNCTION(pg_lo_write)
2545 {
2546 zval *pgsql_id;
2547 zend_string *str;
2548 zend_long z_len;
2549 bool z_len_is_null = 1;
2550 size_t nbytes;
2551 size_t len;
2552 pgLofp *pgsql;
2553
2554 if (zend_parse_parameters(ZEND_NUM_ARGS(), "OS|l!", &pgsql_id, pgsql_lob_ce, &str, &z_len, &z_len_is_null) == FAILURE) {
2555 RETURN_THROWS();
2556 }
2557
2558 if (!z_len_is_null) {
2559 if (z_len < 0) {
2560 zend_argument_value_error(3, "must be greater than or equal to 0");
2561 RETURN_THROWS();
2562 }
2563 if (z_len > (zend_long)ZSTR_LEN(str)) {
2564 zend_argument_value_error(3, "must be less than or equal to the length of argument #2 ($buf)");
2565 RETURN_THROWS();
2566 }
2567 len = z_len;
2568 }
2569 else {
2570 len = ZSTR_LEN(str);
2571 }
2572
2573 pgsql = Z_PGSQL_LOB_P(pgsql_id);
2574 CHECK_PGSQL_LOB(pgsql);
2575
2576 if ((nbytes = lo_write((PGconn *)pgsql->conn, pgsql->lofd, ZSTR_VAL(str), len)) == (size_t)-1) {
2577 RETURN_FALSE;
2578 }
2579
2580 RETURN_LONG(nbytes);
2581 }
2582 /* }}} */
2583
2584 /* {{{ Read a large object and send straight to browser */
PHP_FUNCTION(pg_lo_read_all)2585 PHP_FUNCTION(pg_lo_read_all)
2586 {
2587 zval *pgsql_id;
2588 int tbytes;
2589 volatile int nbytes;
2590 char buf[PGSQL_LO_READ_BUF_SIZE];
2591 pgLofp *pgsql;
2592
2593 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pgsql_id, pgsql_lob_ce) == FAILURE) {
2594 RETURN_THROWS();
2595 }
2596
2597 pgsql = Z_PGSQL_LOB_P(pgsql_id);
2598 CHECK_PGSQL_LOB(pgsql);
2599
2600 tbytes = 0;
2601 while ((nbytes = lo_read((PGconn *)pgsql->conn, pgsql->lofd, buf, PGSQL_LO_READ_BUF_SIZE))>0) {
2602 PHPWRITE(buf, nbytes);
2603 tbytes += nbytes;
2604 }
2605 RETURN_LONG(tbytes);
2606 }
2607 /* }}} */
2608
2609 /* {{{ Import large object direct from filesystem */
PHP_FUNCTION(pg_lo_import)2610 PHP_FUNCTION(pg_lo_import)
2611 {
2612 zval *pgsql_link = NULL, *oid = NULL;
2613 zend_string *file_in;
2614 PGconn *pgsql;
2615 Oid returned_oid;
2616 pgsql_link_handle *link;
2617
2618 if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(),
2619 "OP|z", &pgsql_link, pgsql_link_ce, &file_in, &oid) == SUCCESS) {
2620 link = Z_PGSQL_LINK_P(pgsql_link);
2621 CHECK_PGSQL_LINK(link);
2622 }
2623 else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(),
2624 "P|z", &file_in, &oid) == SUCCESS) {
2625 link = FETCH_DEFAULT_LINK();
2626 CHECK_DEFAULT_LINK(link);
2627 }
2628 else {
2629 WRONG_PARAM_COUNT;
2630 }
2631
2632 if (php_check_open_basedir(ZSTR_VAL(file_in))) {
2633 RETURN_FALSE;
2634 }
2635
2636 pgsql = link->conn;
2637
2638 if (oid) {
2639 Oid wanted_oid;
2640 switch (Z_TYPE_P(oid)) {
2641 case IS_STRING:
2642 {
2643 if (!is_valid_oid_string(Z_STR_P(oid), &wanted_oid)) {
2644 /* wrong integer format */
2645 zend_value_error("Invalid OID value passed");
2646 RETURN_THROWS();
2647 }
2648 }
2649 break;
2650 case IS_LONG:
2651 if (Z_LVAL_P(oid) < (zend_long)InvalidOid) {
2652 zend_value_error("Invalid OID value passed");
2653 RETURN_THROWS();
2654 }
2655 wanted_oid = (Oid)Z_LVAL_P(oid);
2656 break;
2657 default:
2658 zend_type_error("OID value must be of type string|int, %s given", zend_zval_value_name(oid));
2659 RETURN_THROWS();
2660 }
2661
2662 returned_oid = lo_import_with_oid(pgsql, ZSTR_VAL(file_in), wanted_oid);
2663
2664 if (returned_oid == InvalidOid) {
2665 RETURN_FALSE;
2666 }
2667
2668 PGSQL_RETURN_OID(returned_oid);
2669 }
2670
2671 returned_oid = lo_import(pgsql, ZSTR_VAL(file_in));
2672
2673 if (returned_oid == InvalidOid) {
2674 RETURN_FALSE;
2675 }
2676 PGSQL_RETURN_OID(returned_oid);
2677 }
2678 /* }}} */
2679
2680 /* {{{ Export large object direct to filesystem */
PHP_FUNCTION(pg_lo_export)2681 PHP_FUNCTION(pg_lo_export)
2682 {
2683 zval *pgsql_link = NULL;
2684 zend_string *oid_string;
2685 zend_string *file_out;
2686 zend_long oid_long;
2687 Oid oid;
2688 PGconn *pgsql;
2689 pgsql_link_handle *link;
2690
2691 /* allow string to handle large OID value correctly */
2692 if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(),
2693 "OlP", &pgsql_link, pgsql_link_ce, &oid_long, &file_out) == SUCCESS) {
2694 if (oid_long <= (zend_long)InvalidOid) {
2695 zend_value_error("Invalid OID value passed");
2696 RETURN_THROWS();
2697 }
2698 oid = (Oid)oid_long;
2699 link = Z_PGSQL_LINK_P(pgsql_link);
2700 CHECK_PGSQL_LINK(link);
2701 }
2702 else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(),
2703 "OSP", &pgsql_link, pgsql_link_ce, &oid_string, &file_out) == SUCCESS) {
2704 if (!is_valid_oid_string(oid_string, &oid)) {
2705 /* wrong integer format */
2706 zend_value_error("Invalid OID value passed");
2707 RETURN_THROWS();
2708 }
2709 link = Z_PGSQL_LINK_P(pgsql_link);
2710 CHECK_PGSQL_LINK(link);
2711 }
2712 else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(),
2713 "lP", &oid_long, &file_out) == SUCCESS) {
2714 if (oid_long <= (zend_long)InvalidOid) {
2715 zend_value_error("Invalid OID value passed");
2716 RETURN_THROWS();
2717 }
2718 oid = (Oid)oid_long;
2719 link = FETCH_DEFAULT_LINK();
2720 CHECK_DEFAULT_LINK(link);
2721 }
2722 else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(),
2723 "SP", &oid_string, &file_out) == SUCCESS) {
2724 if (!is_valid_oid_string(oid_string, &oid)) {
2725 /* wrong integer format */
2726 zend_value_error("Invalid OID value passed");
2727 RETURN_THROWS();
2728 }
2729 link = FETCH_DEFAULT_LINK();
2730 CHECK_DEFAULT_LINK(link);
2731 }
2732 else {
2733 zend_argument_count_error("Requires 2 or 3 arguments, %d given", ZEND_NUM_ARGS());
2734 RETURN_THROWS();
2735 }
2736
2737 if (php_check_open_basedir(ZSTR_VAL(file_out))) {
2738 RETURN_FALSE;
2739 }
2740
2741 pgsql = link->conn;
2742
2743 if (lo_export(pgsql, oid, ZSTR_VAL(file_out)) == -1) {
2744 RETURN_FALSE;
2745 }
2746 RETURN_TRUE;
2747 }
2748 /* }}} */
2749
2750 /* {{{ Seeks position of large object */
PHP_FUNCTION(pg_lo_seek)2751 PHP_FUNCTION(pg_lo_seek)
2752 {
2753 zval *pgsql_id = NULL;
2754 zend_long result, offset = 0, whence = SEEK_CUR;
2755 pgLofp *pgsql;
2756
2757 if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol|l", &pgsql_id, pgsql_lob_ce, &offset, &whence) == FAILURE) {
2758 RETURN_THROWS();
2759 }
2760 if (whence != SEEK_SET && whence != SEEK_CUR && whence != SEEK_END) {
2761 zend_argument_value_error(3, "must be one of PGSQL_SEEK_SET, PGSQL_SEEK_CUR, or PGSQL_SEEK_END");
2762 RETURN_THROWS();
2763 }
2764
2765 pgsql = Z_PGSQL_LOB_P(pgsql_id);
2766 CHECK_PGSQL_LOB(pgsql);
2767
2768 #ifdef HAVE_PG_LO64
2769 if (PQserverVersion((PGconn *)pgsql->conn) >= 90300) {
2770 result = lo_lseek64((PGconn *)pgsql->conn, pgsql->lofd, offset, (int)whence);
2771 } else {
2772 result = lo_lseek((PGconn *)pgsql->conn, pgsql->lofd, (int)offset, (int)whence);
2773 }
2774 #else
2775 result = lo_lseek((PGconn *)pgsql->conn, pgsql->lofd, offset, whence);
2776 #endif
2777 if (result > -1) {
2778 RETURN_TRUE;
2779 } else {
2780 RETURN_FALSE;
2781 }
2782 }
2783 /* }}} */
2784
2785 /* {{{ Returns current position of large object */
PHP_FUNCTION(pg_lo_tell)2786 PHP_FUNCTION(pg_lo_tell)
2787 {
2788 zval *pgsql_id = NULL;
2789 zend_long offset = 0;
2790 pgLofp *pgsql;
2791
2792 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pgsql_id, pgsql_lob_ce) == FAILURE) {
2793 RETURN_THROWS();
2794 }
2795
2796 pgsql = Z_PGSQL_LOB_P(pgsql_id);
2797 CHECK_PGSQL_LOB(pgsql);
2798
2799 #ifdef VE_PG_LO64
2800 if (PQserverVersion((PGconn *)pgsql->conn) >= 90300) {
2801 offset = lo_tell64((PGconn *)pgsql->conn, pgsql->lofd);
2802 } else {
2803 offset = lo_tell((PGconn *)pgsql->conn, pgsql->lofd);
2804 }
2805 #else
2806 offset = lo_tell((PGconn *)pgsql->conn, pgsql->lofd);
2807 #endif
2808 RETURN_LONG(offset);
2809 }
2810 /* }}} */
2811
2812 /* {{{ Truncate large object to size */
PHP_FUNCTION(pg_lo_truncate)2813 PHP_FUNCTION(pg_lo_truncate)
2814 {
2815 zval *pgsql_id = NULL;
2816 size_t size;
2817 pgLofp *pgsql;
2818 int result;
2819
2820 if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &pgsql_id, pgsql_lob_ce, &size) == FAILURE) {
2821 RETURN_THROWS();
2822 }
2823
2824 pgsql = Z_PGSQL_LOB_P(pgsql_id);
2825 CHECK_PGSQL_LOB(pgsql);
2826
2827 #ifdef VE_PG_LO64
2828 if (PQserverVersion((PGconn *)pgsql->conn) >= 90300) {
2829 result = lo_truncate64((PGconn *)pgsql->conn, pgsql->lofd, size);
2830 } else {
2831 result = lo_truncate((PGconn *)pgsql->conn, pgsql->lofd, size);
2832 }
2833 #else
2834 result = lo_truncate((PGconn *)pgsql->conn, pgsql->lofd, size);
2835 #endif
2836 if (!result) {
2837 RETURN_TRUE;
2838 } else {
2839 RETURN_FALSE;
2840 }
2841 }
2842 /* }}} */
2843
2844 /* {{{ Set error verbosity */
PHP_FUNCTION(pg_set_error_verbosity)2845 PHP_FUNCTION(pg_set_error_verbosity)
2846 {
2847 zval *pgsql_link = NULL;
2848 zend_long verbosity;
2849 PGconn *pgsql;
2850 pgsql_link_handle *link;
2851
2852 if (ZEND_NUM_ARGS() == 1) {
2853 if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &verbosity) == FAILURE) {
2854 RETURN_THROWS();
2855 }
2856 link = FETCH_DEFAULT_LINK();
2857 CHECK_DEFAULT_LINK(link);
2858 } else {
2859 if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &pgsql_link, pgsql_link_ce, &verbosity) == FAILURE) {
2860 RETURN_THROWS();
2861 }
2862 link = Z_PGSQL_LINK_P(pgsql_link);
2863 CHECK_PGSQL_LINK(link);
2864 }
2865
2866 pgsql = link->conn;
2867
2868 if (verbosity & (PQERRORS_TERSE|PQERRORS_DEFAULT|PQERRORS_VERBOSE|PQERRORS_SQLSTATE)) {
2869 RETURN_LONG(PQsetErrorVerbosity(pgsql, verbosity));
2870 } else {
2871 RETURN_FALSE;
2872 }
2873 }
2874 /* }}} */
2875
2876 #ifdef HAVE_PG_CONTEXT_VISIBILITY
PHP_FUNCTION(pg_set_error_context_visibility)2877 PHP_FUNCTION(pg_set_error_context_visibility)
2878 {
2879 zval *pgsql_link = NULL;
2880 zend_long visibility;
2881 PGconn *pgsql;
2882 pgsql_link_handle *link;
2883
2884 if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &pgsql_link, pgsql_link_ce, &visibility) == FAILURE) {
2885 RETURN_THROWS();
2886 }
2887 link = Z_PGSQL_LINK_P(pgsql_link);
2888 CHECK_PGSQL_LINK(link);
2889
2890 pgsql = link->conn;
2891
2892 if (visibility == PQSHOW_CONTEXT_NEVER || visibility & (PQSHOW_CONTEXT_ERRORS|PQSHOW_CONTEXT_ALWAYS)) {
2893 RETURN_LONG(PQsetErrorContextVisibility(pgsql, visibility));
2894 } else {
2895 zend_argument_value_error(2, "must be one of PGSQL_SHOW_CONTEXT_NEVER, PGSQL_SHOW_CONTEXT_ERRORS or PGSQL_SHOW_CONTEXT_ALWAYS");
2896 RETURN_THROWS();
2897 }
2898 }
2899 #endif
2900
2901 /* {{{ Set client encoding */
PHP_FUNCTION(pg_set_client_encoding)2902 PHP_FUNCTION(pg_set_client_encoding)
2903 {
2904 char *encoding;
2905 size_t encoding_len;
2906 zval *pgsql_link = NULL;
2907 PGconn *pgsql;
2908 pgsql_link_handle *link;
2909
2910 if (ZEND_NUM_ARGS() == 1) {
2911 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &encoding, &encoding_len) == FAILURE) {
2912 RETURN_THROWS();
2913 }
2914 link = FETCH_DEFAULT_LINK();
2915 CHECK_DEFAULT_LINK(link);
2916 } else {
2917 if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os", &pgsql_link, pgsql_link_ce, &encoding, &encoding_len) == FAILURE) {
2918 RETURN_THROWS();
2919 }
2920 link = Z_PGSQL_LINK_P(pgsql_link);
2921 CHECK_PGSQL_LINK(link);
2922 }
2923
2924 pgsql = link->conn;
2925
2926 RETURN_LONG(PQsetClientEncoding(pgsql, encoding));
2927 }
2928 /* }}} */
2929
2930 /* {{{ Get the current client encoding */
PHP_FUNCTION(pg_client_encoding)2931 PHP_FUNCTION(pg_client_encoding)
2932 {
2933 zval *pgsql_link = NULL;
2934 PGconn *pgsql;
2935 pgsql_link_handle *link;
2936
2937 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|O!", &pgsql_link, pgsql_link_ce) == FAILURE) {
2938 RETURN_THROWS();
2939 }
2940
2941 if (pgsql_link == NULL) {
2942 link = FETCH_DEFAULT_LINK();
2943 CHECK_DEFAULT_LINK(link);
2944 } else {
2945 link = Z_PGSQL_LINK_P(pgsql_link);
2946 CHECK_PGSQL_LINK(link);
2947 }
2948
2949 pgsql = link->conn;
2950
2951 /* Just do the same as found in PostgreSQL sources... */
2952
2953 RETURN_STRING((char *) pg_encoding_to_char(PQclientEncoding(pgsql)));
2954 }
2955 /* }}} */
2956
2957 /* {{{ Sync with backend. Completes the Copy command */
PHP_FUNCTION(pg_end_copy)2958 PHP_FUNCTION(pg_end_copy)
2959 {
2960 zval *pgsql_link = NULL;
2961 PGconn *pgsql;
2962 int result = 0;
2963 pgsql_link_handle *link;
2964
2965 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|O!", &pgsql_link, pgsql_link_ce) == FAILURE) {
2966 RETURN_THROWS();
2967 }
2968
2969 if (pgsql_link == NULL) {
2970 link = FETCH_DEFAULT_LINK();
2971 CHECK_DEFAULT_LINK(link);
2972 } else {
2973 link = Z_PGSQL_LINK_P(pgsql_link);
2974 CHECK_PGSQL_LINK(link);
2975 }
2976
2977 pgsql = link->conn;
2978
2979 result = PQendcopy(pgsql);
2980
2981 if (result!=0) {
2982 PHP_PQ_ERROR("Query failed: %s", pgsql);
2983 RETURN_FALSE;
2984 }
2985 RETURN_TRUE;
2986 }
2987 /* }}} */
2988
2989 /* {{{ Send null-terminated string to backend server*/
PHP_FUNCTION(pg_put_line)2990 PHP_FUNCTION(pg_put_line)
2991 {
2992 char *query;
2993 size_t query_len;
2994 zval *pgsql_link = NULL;
2995 PGconn *pgsql;
2996 pgsql_link_handle *link;
2997 int result = 0;
2998
2999 if (ZEND_NUM_ARGS() == 1) {
3000 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &query, &query_len) == FAILURE) {
3001 RETURN_THROWS();
3002 }
3003 link = FETCH_DEFAULT_LINK();
3004 CHECK_DEFAULT_LINK(link);
3005 } else {
3006 if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os", &pgsql_link, pgsql_link_ce, &query, &query_len) == FAILURE) {
3007 RETURN_THROWS();
3008 }
3009 link = Z_PGSQL_LINK_P(pgsql_link);
3010 CHECK_PGSQL_LINK(link);
3011 }
3012
3013 pgsql = link->conn;
3014
3015 result = PQputline(pgsql, query);
3016 if (result==EOF) {
3017 PHP_PQ_ERROR("Query failed: %s", pgsql);
3018 RETURN_FALSE;
3019 }
3020 RETURN_TRUE;
3021 }
3022 /* }}} */
3023
3024 /* {{{ Copy table to array */
PHP_FUNCTION(pg_copy_to)3025 PHP_FUNCTION(pg_copy_to)
3026 {
3027 zval *pgsql_link;
3028 pgsql_link_handle *link;
3029 zend_string *table_name;
3030 zend_string *pg_delimiter = NULL;
3031 char *pg_null_as = NULL;
3032 size_t pg_null_as_len = 0;
3033 bool free_pg_null = false;
3034 char *query;
3035 PGconn *pgsql;
3036 PGresult *pgsql_result;
3037 ExecStatusType status;
3038 char *csv = (char *)NULL;
3039
3040 if (zend_parse_parameters(ZEND_NUM_ARGS(), "OP|Ss", &pgsql_link, pgsql_link_ce,
3041 &table_name, &pg_delimiter, &pg_null_as, &pg_null_as_len) == FAILURE
3042 ) {
3043 RETURN_THROWS();
3044 }
3045
3046 link = Z_PGSQL_LINK_P(pgsql_link);
3047 CHECK_PGSQL_LINK(link);
3048 pgsql = link->conn;
3049
3050 if (!pg_delimiter) {
3051 pg_delimiter = ZSTR_CHAR('\t');
3052 } else if (ZSTR_LEN(pg_delimiter) != 1) {
3053 zend_argument_value_error(3, "must be one character");
3054 RETURN_THROWS();
3055 }
3056 if (!pg_null_as) {
3057 pg_null_as = estrdup("\\\\N");
3058 free_pg_null = true;
3059 }
3060
3061 spprintf(&query, 0, "COPY %s TO STDOUT DELIMITER E'%c' NULL AS E'%s'", ZSTR_VAL(table_name), *ZSTR_VAL(pg_delimiter), pg_null_as);
3062
3063 while ((pgsql_result = PQgetResult(pgsql))) {
3064 PQclear(pgsql_result);
3065 }
3066 pgsql_result = PQexec(pgsql, query);
3067 if (free_pg_null) {
3068 efree(pg_null_as);
3069 }
3070 efree(query);
3071
3072 if (pgsql_result) {
3073 status = PQresultStatus(pgsql_result);
3074 } else {
3075 status = (ExecStatusType) PQstatus(pgsql);
3076 }
3077
3078 switch (status) {
3079 case PGRES_COPY_OUT:
3080 if (pgsql_result) {
3081 int copydone = 0;
3082
3083 PQclear(pgsql_result);
3084 array_init(return_value);
3085 while (!copydone)
3086 {
3087 int ret = PQgetCopyData(pgsql, &csv, 0);
3088 switch (ret) {
3089 case -1:
3090 copydone = 1;
3091 break;
3092 case 0:
3093 case -2:
3094 PHP_PQ_ERROR("getline failed: %s", pgsql);
3095 RETURN_FALSE;
3096 break;
3097 default:
3098 add_next_index_string(return_value, csv);
3099 PQfreemem(csv);
3100 break;
3101 }
3102 }
3103 while ((pgsql_result = PQgetResult(pgsql))) {
3104 PQclear(pgsql_result);
3105 }
3106 } else {
3107 PQclear(pgsql_result);
3108 RETURN_FALSE;
3109 }
3110 break;
3111 default:
3112 PQclear(pgsql_result);
3113 PHP_PQ_ERROR("Copy command failed: %s", pgsql);
3114 RETURN_FALSE;
3115 break;
3116 }
3117 }
3118 /* }}} */
3119
3120 /* {{{ Copy table from array */
PHP_FUNCTION(pg_copy_from)3121 PHP_FUNCTION(pg_copy_from)
3122 {
3123 zval *pgsql_link = NULL, *pg_rows;
3124 pgsql_link_handle *link;
3125 zval *value;
3126 zend_string *table_name;
3127 zend_string *pg_delimiter = NULL;
3128 char *pg_null_as = NULL;
3129 size_t pg_null_as_len;
3130 bool pg_null_as_free = false;
3131 char *query;
3132 PGconn *pgsql;
3133 PGresult *pgsql_result;
3134 ExecStatusType status;
3135
3136 if (zend_parse_parameters(ZEND_NUM_ARGS(), "OPa|Ss", &pgsql_link, pgsql_link_ce,
3137 &table_name, &pg_rows, &pg_delimiter, &pg_null_as, &pg_null_as_len) == FAILURE
3138 ) {
3139 RETURN_THROWS();
3140 }
3141
3142 link = Z_PGSQL_LINK_P(pgsql_link);
3143 CHECK_PGSQL_LINK(link);
3144 pgsql = link->conn;
3145
3146 if (!pg_delimiter) {
3147 pg_delimiter = ZSTR_CHAR('\t');
3148 } else if (ZSTR_LEN(pg_delimiter) != 1) {
3149 zend_argument_value_error(4, "must be one character");
3150 RETURN_THROWS();
3151 }
3152 if (!pg_null_as) {
3153 pg_null_as = estrdup("\\\\N");
3154 pg_null_as_free = true;
3155 }
3156
3157 spprintf(&query, 0, "COPY %s FROM STDIN DELIMITER E'%c' NULL AS E'%s'", ZSTR_VAL(table_name), *ZSTR_VAL(pg_delimiter), pg_null_as);
3158 while ((pgsql_result = PQgetResult(pgsql))) {
3159 PQclear(pgsql_result);
3160 }
3161 pgsql_result = PQexec(pgsql, query);
3162
3163 if (pg_null_as_free) {
3164 efree(pg_null_as);
3165 }
3166 efree(query);
3167
3168 if (pgsql_result) {
3169 status = PQresultStatus(pgsql_result);
3170 } else {
3171 status = (ExecStatusType) PQstatus(pgsql);
3172 }
3173
3174 switch (status) {
3175 case PGRES_COPY_IN:
3176 if (pgsql_result) {
3177 int command_failed = 0;
3178 PQclear(pgsql_result);
3179 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pg_rows), value) {
3180 zend_string *tmp = zval_try_get_string(value);
3181 if (UNEXPECTED(!tmp)) {
3182 return;
3183 }
3184 query = (char *)emalloc(ZSTR_LEN(tmp) + 2);
3185 strlcpy(query, ZSTR_VAL(tmp), ZSTR_LEN(tmp) + 2);
3186 if (ZSTR_LEN(tmp) > 0 && *(query + ZSTR_LEN(tmp) - 1) != '\n') {
3187 strlcat(query, "\n", ZSTR_LEN(tmp) + 2);
3188 }
3189 if (PQputCopyData(pgsql, query, (int)strlen(query)) != 1) {
3190 efree(query);
3191 zend_string_release(tmp);
3192 PHP_PQ_ERROR("copy failed: %s", pgsql);
3193 RETURN_FALSE;
3194 }
3195 efree(query);
3196 zend_string_release(tmp);
3197 } ZEND_HASH_FOREACH_END();
3198
3199 if (PQputCopyEnd(pgsql, NULL) != 1) {
3200 PHP_PQ_ERROR("putcopyend failed: %s", pgsql);
3201 RETURN_FALSE;
3202 }
3203 while ((pgsql_result = PQgetResult(pgsql))) {
3204 if (PGRES_COMMAND_OK != PQresultStatus(pgsql_result)) {
3205 PHP_PQ_ERROR("Copy command failed: %s", pgsql);
3206 command_failed = 1;
3207 }
3208 PQclear(pgsql_result);
3209 }
3210 if (command_failed) {
3211 RETURN_FALSE;
3212 }
3213 } else {
3214 PQclear(pgsql_result);
3215 RETURN_FALSE;
3216 }
3217 RETURN_TRUE;
3218 break;
3219 default:
3220 PQclear(pgsql_result);
3221 PHP_PQ_ERROR("Copy command failed: %s", pgsql);
3222 RETURN_FALSE;
3223 break;
3224 }
3225 }
3226 /* }}} */
3227
3228 /* {{{ Escape string for text/char type */
PHP_FUNCTION(pg_escape_string)3229 PHP_FUNCTION(pg_escape_string)
3230 {
3231 zend_string *from = NULL, *to = NULL;
3232 zval *pgsql_link;
3233 pgsql_link_handle *link;
3234 PGconn *pgsql;
3235
3236 switch (ZEND_NUM_ARGS()) {
3237 case 1:
3238 if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &from) == FAILURE) {
3239 RETURN_THROWS();
3240 }
3241 link = FETCH_DEFAULT_LINK();
3242 break;
3243 default:
3244 if (zend_parse_parameters(ZEND_NUM_ARGS(), "OS", &pgsql_link, pgsql_link_ce, &from) == FAILURE) {
3245 RETURN_THROWS();
3246 }
3247 link = Z_PGSQL_LINK_P(pgsql_link);
3248 CHECK_PGSQL_LINK(link);
3249 break;
3250 }
3251
3252 to = zend_string_safe_alloc(ZSTR_LEN(from), 2, 0, 0);
3253 if (link) {
3254 pgsql = link->conn;
3255 ZSTR_LEN(to) = PQescapeStringConn(pgsql, ZSTR_VAL(to), ZSTR_VAL(from), ZSTR_LEN(from), NULL);
3256 } else
3257 {
3258 ZSTR_LEN(to) = PQescapeString(ZSTR_VAL(to), ZSTR_VAL(from), ZSTR_LEN(from));
3259 }
3260
3261 to = zend_string_truncate(to, ZSTR_LEN(to), 0);
3262 RETURN_NEW_STR(to);
3263 }
3264 /* }}} */
3265
3266 /* {{{ Escape binary for bytea type */
PHP_FUNCTION(pg_escape_bytea)3267 PHP_FUNCTION(pg_escape_bytea)
3268 {
3269 zend_string *from;
3270 char *to = NULL;
3271 size_t to_len;
3272 PGconn *pgsql;
3273 zval *pgsql_link;
3274 pgsql_link_handle *link;
3275
3276 switch (ZEND_NUM_ARGS()) {
3277 case 1:
3278 if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &from) == FAILURE) {
3279 RETURN_THROWS();
3280 }
3281 link = FETCH_DEFAULT_LINK();
3282 break;
3283 default:
3284 if (zend_parse_parameters(ZEND_NUM_ARGS(), "OS", &pgsql_link, pgsql_link_ce, &from) == FAILURE) {
3285 RETURN_THROWS();
3286 }
3287 link = Z_PGSQL_LINK_P(pgsql_link);
3288 CHECK_PGSQL_LINK(link);
3289 break;
3290 }
3291
3292 if (link) {
3293 pgsql = link->conn;
3294 to = (char *)PQescapeByteaConn(pgsql, (unsigned char *)ZSTR_VAL(from), ZSTR_LEN(from), &to_len);
3295 } else {
3296 to = (char *)PQescapeBytea((unsigned char *)ZSTR_VAL(from), ZSTR_LEN(from), &to_len);
3297 }
3298
3299 RETVAL_STRINGL(to, to_len-1); /* to_len includes additional '\0' */
3300 PQfreemem(to);
3301 }
3302 /* }}} */
3303
3304 /* {{{ Unescape binary for bytea type */
PHP_FUNCTION(pg_unescape_bytea)3305 PHP_FUNCTION(pg_unescape_bytea)
3306 {
3307 char *from, *tmp;
3308 size_t to_len;
3309 size_t from_len;
3310 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &from, &from_len) == FAILURE) {
3311 RETURN_THROWS();
3312 }
3313
3314 tmp = (char *)PQunescapeBytea((unsigned char*)from, &to_len);
3315 if (!tmp) {
3316 zend_error_noreturn(E_ERROR, "Out of memory");
3317 return;
3318 }
3319
3320 RETVAL_STRINGL(tmp, to_len);
3321 PQfreemem(tmp);
3322 }
3323 /* }}} */
3324
php_pgsql_escape_internal(INTERNAL_FUNCTION_PARAMETERS,int escape_literal)3325 static void php_pgsql_escape_internal(INTERNAL_FUNCTION_PARAMETERS, int escape_literal) /* {{{ */ {
3326 zend_string *from = NULL;
3327 zval *pgsql_link = NULL;
3328 PGconn *pgsql;
3329 char *tmp;
3330 pgsql_link_handle *link;
3331
3332 switch (ZEND_NUM_ARGS()) {
3333 case 1:
3334 if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &from) == FAILURE) {
3335 RETURN_THROWS();
3336 }
3337 link = FETCH_DEFAULT_LINK();
3338 CHECK_DEFAULT_LINK(link);
3339 break;
3340
3341 default:
3342 if (zend_parse_parameters(ZEND_NUM_ARGS(), "OS", &pgsql_link, pgsql_link_ce, &from) == FAILURE) {
3343 RETURN_THROWS();
3344 }
3345 link = Z_PGSQL_LINK_P(pgsql_link);
3346 CHECK_PGSQL_LINK(link);
3347 break;
3348 }
3349
3350 pgsql = link->conn;
3351
3352 if (escape_literal) {
3353 tmp = PQescapeLiteral(pgsql, ZSTR_VAL(from), ZSTR_LEN(from));
3354 } else {
3355 tmp = PQescapeIdentifier(pgsql, ZSTR_VAL(from), ZSTR_LEN(from));
3356 }
3357 if (!tmp) {
3358 php_error_docref(NULL, E_WARNING, "Failed to escape");
3359 RETURN_FALSE;
3360 }
3361
3362 RETVAL_STRING(tmp);
3363 PQfreemem(tmp);
3364 }
3365 /* }}} */
3366
3367 /* {{{ Escape parameter as string literal (i.e. parameter) */
PHP_FUNCTION(pg_escape_literal)3368 PHP_FUNCTION(pg_escape_literal)
3369 {
3370 php_pgsql_escape_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
3371 }
3372 /* }}} */
3373
3374 /* {{{ Escape identifier (i.e. table name, field name) */
PHP_FUNCTION(pg_escape_identifier)3375 PHP_FUNCTION(pg_escape_identifier)
3376 {
3377 php_pgsql_escape_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
3378 }
3379 /* }}} */
3380
3381 /* {{{ Get error message associated with result */
PHP_FUNCTION(pg_result_error)3382 PHP_FUNCTION(pg_result_error)
3383 {
3384 zval *result;
3385 PGresult *pgsql_result;
3386 pgsql_result_handle *pg_result;
3387 char *err = NULL;
3388
3389 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &result, pgsql_result_ce) == FAILURE) {
3390 RETURN_THROWS();
3391 }
3392
3393 pg_result = Z_PGSQL_RESULT_P(result);
3394 pgsql_result = pg_result->result;
3395 if (!pgsql_result) {
3396 RETURN_FALSE;
3397 }
3398
3399 err = PQresultErrorMessage(pgsql_result);
3400 RETURN_STRING(err);
3401 }
3402 /* }}} */
3403
3404 /* {{{ Get error message field associated with result */
PHP_FUNCTION(pg_result_error_field)3405 PHP_FUNCTION(pg_result_error_field)
3406 {
3407 zval *result;
3408 zend_long fieldcode;
3409 PGresult *pgsql_result;
3410 pgsql_result_handle *pg_result;
3411 char *field = NULL;
3412
3413 if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &result, pgsql_result_ce, &fieldcode) == FAILURE) {
3414 RETURN_THROWS();
3415 }
3416
3417 pg_result = Z_PGSQL_RESULT_P(result);
3418 pgsql_result = pg_result->result;
3419 if (!pgsql_result) {
3420 RETURN_FALSE;
3421 }
3422
3423 if (fieldcode & (PG_DIAG_SEVERITY|PG_DIAG_SQLSTATE|PG_DIAG_MESSAGE_PRIMARY|PG_DIAG_MESSAGE_DETAIL
3424 |PG_DIAG_MESSAGE_HINT|PG_DIAG_STATEMENT_POSITION
3425 #ifdef PG_DIAG_INTERNAL_POSITION
3426 |PG_DIAG_INTERNAL_POSITION
3427 #endif
3428 #ifdef PG_DIAG_INTERNAL_QUERY
3429 |PG_DIAG_INTERNAL_QUERY
3430 #endif
3431 |PG_DIAG_CONTEXT|PG_DIAG_SOURCE_FILE|PG_DIAG_SOURCE_LINE
3432 |PG_DIAG_SOURCE_FUNCTION)) {
3433 field = PQresultErrorField(pgsql_result, (int)fieldcode);
3434 if (field == NULL) {
3435 RETURN_NULL();
3436 } else {
3437 RETURN_STRING(field);
3438 }
3439 } else {
3440 RETURN_FALSE;
3441 }
3442 }
3443 /* }}} */
3444
3445 /* {{{ Get connection status */
PHP_FUNCTION(pg_connection_status)3446 PHP_FUNCTION(pg_connection_status)
3447 {
3448 zval *pgsql_link = NULL;
3449 pgsql_link_handle *link;
3450 PGconn *pgsql;
3451
3452 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pgsql_link, pgsql_link_ce) == FAILURE) {
3453 RETURN_THROWS();
3454 }
3455
3456 link = Z_PGSQL_LINK_P(pgsql_link);
3457 CHECK_PGSQL_LINK(link);
3458 pgsql = link->conn;
3459
3460 RETURN_LONG(PQstatus(pgsql));
3461 }
3462
3463 /* }}} */
3464
3465 /* {{{ Get transaction status */
PHP_FUNCTION(pg_transaction_status)3466 PHP_FUNCTION(pg_transaction_status)
3467 {
3468 zval *pgsql_link = NULL;
3469 pgsql_link_handle *link;
3470 PGconn *pgsql;
3471
3472 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pgsql_link, pgsql_link_ce) == FAILURE) {
3473 RETURN_THROWS();
3474 }
3475
3476 link = Z_PGSQL_LINK_P(pgsql_link);
3477 CHECK_PGSQL_LINK(link);
3478 pgsql = link->conn;
3479
3480 RETURN_LONG(PQtransactionStatus(pgsql));
3481 }
3482
3483 /* }}} */
3484
3485 /* {{{ Reset connection (reconnect) */
PHP_FUNCTION(pg_connection_reset)3486 PHP_FUNCTION(pg_connection_reset)
3487 {
3488 zval *pgsql_link;
3489 pgsql_link_handle *link;
3490 PGconn *pgsql;
3491
3492 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pgsql_link, pgsql_link_ce) == FAILURE) {
3493 RETURN_THROWS();
3494 }
3495
3496 link = Z_PGSQL_LINK_P(pgsql_link);
3497 CHECK_PGSQL_LINK(link);
3498 pgsql = link->conn;
3499
3500 PQreset(pgsql);
3501 if (PQstatus(pgsql) == CONNECTION_BAD) {
3502 RETURN_FALSE;
3503 }
3504 RETURN_TRUE;
3505 }
3506 /* }}} */
3507
3508 #define PHP_PG_ASYNC_IS_BUSY 1
3509 #define PHP_PG_ASYNC_REQUEST_CANCEL 2
3510
3511 /* {{{ php_pgsql_flush_query */
php_pgsql_flush_query(PGconn * pgsql)3512 static int php_pgsql_flush_query(PGconn *pgsql)
3513 {
3514 PGresult *res;
3515 int leftover = 0;
3516
3517 if (PQsetnonblocking(pgsql, 1)) {
3518 php_error_docref(NULL, E_NOTICE,"Cannot set connection to nonblocking mode");
3519 return -1;
3520 }
3521 while ((res = PQgetResult(pgsql))) {
3522 PQclear(res);
3523 leftover++;
3524 }
3525 PQsetnonblocking(pgsql, 0);
3526 return leftover;
3527 }
3528 /* }}} */
3529
3530 /* {{{ php_pgsql_do_async */
php_pgsql_do_async(INTERNAL_FUNCTION_PARAMETERS,int entry_type)3531 static void php_pgsql_do_async(INTERNAL_FUNCTION_PARAMETERS, int entry_type)
3532 {
3533 zval *pgsql_link;
3534 pgsql_link_handle *link;
3535 PGconn *pgsql;
3536 PGresult *pgsql_result;
3537
3538 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pgsql_link, pgsql_link_ce) == FAILURE) {
3539 RETURN_THROWS();
3540 }
3541
3542 link = Z_PGSQL_LINK_P(pgsql_link);
3543 CHECK_PGSQL_LINK(link);
3544 pgsql = link->conn;
3545
3546 if (PQsetnonblocking(pgsql, 1)) {
3547 php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode");
3548 RETURN_FALSE;
3549 }
3550 switch(entry_type) {
3551 case PHP_PG_ASYNC_IS_BUSY:
3552 PQconsumeInput(pgsql);
3553 RETVAL_LONG(PQisBusy(pgsql));
3554 break;
3555 case PHP_PG_ASYNC_REQUEST_CANCEL: {
3556 PGcancel *c;
3557 char err[256];
3558 int rc;
3559
3560 c = PQgetCancel(pgsql);
3561 RETVAL_LONG((rc = PQcancel(c, err, sizeof(err))));
3562 if (rc < 0) {
3563 zend_error(E_WARNING, "cannot cancel the query: %s", err);
3564 }
3565 while ((pgsql_result = PQgetResult(pgsql))) {
3566 PQclear(pgsql_result);
3567 }
3568 PQfreeCancel(c);
3569 break;
3570 }
3571 EMPTY_SWITCH_DEFAULT_CASE()
3572 }
3573 if (PQsetnonblocking(pgsql, 0)) {
3574 php_error_docref(NULL, E_NOTICE, "Cannot set connection to blocking mode");
3575 }
3576 convert_to_boolean(return_value);
3577 }
3578 /* }}} */
3579
3580 /* {{{ Cancel request */
PHP_FUNCTION(pg_cancel_query)3581 PHP_FUNCTION(pg_cancel_query)
3582 {
3583 php_pgsql_do_async(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_PG_ASYNC_REQUEST_CANCEL);
3584 }
3585 /* }}} */
3586
3587 /* {{{ Get connection is busy or not */
PHP_FUNCTION(pg_connection_busy)3588 PHP_FUNCTION(pg_connection_busy)
3589 {
3590 php_pgsql_do_async(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_PG_ASYNC_IS_BUSY);
3591 }
3592 /* }}} */
3593
_php_pgsql_link_has_results(PGconn * pgsql)3594 static bool _php_pgsql_link_has_results(PGconn *pgsql) /* {{{ */
3595 {
3596 PGresult *result;
3597 while ((result = PQgetResult(pgsql))) {
3598 PQclear(result);
3599 return true;
3600 }
3601 return false;
3602 }
3603 /* }}} */
3604
3605 /* {{{ Send asynchronous query */
PHP_FUNCTION(pg_send_query)3606 PHP_FUNCTION(pg_send_query)
3607 {
3608 zval *pgsql_link;
3609 pgsql_link_handle *link;
3610 char *query;
3611 size_t len;
3612 PGconn *pgsql;
3613 int is_non_blocking;
3614 int ret;
3615
3616 if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os", &pgsql_link, pgsql_link_ce, &query, &len) == FAILURE) {
3617 RETURN_THROWS();
3618 }
3619
3620 link = Z_PGSQL_LINK_P(pgsql_link);
3621 CHECK_PGSQL_LINK(link);
3622 pgsql = link->conn;
3623
3624 is_non_blocking = PQisnonblocking(pgsql);
3625
3626 if (is_non_blocking == 0 && PQsetnonblocking(pgsql, 1) == -1) {
3627 php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode");
3628 RETURN_FALSE;
3629 }
3630
3631 if (_php_pgsql_link_has_results(pgsql)) {
3632 php_error_docref(NULL, E_NOTICE,
3633 "There are results on this connection. Call pg_get_result() until it returns FALSE");
3634 }
3635
3636 if (is_non_blocking) {
3637 if (!PQsendQuery(pgsql, query)) {
3638 RETURN_FALSE;
3639 }
3640 ret = PQflush(pgsql);
3641 } else {
3642 if (!PQsendQuery(pgsql, query)) {
3643 if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
3644 PQreset(pgsql);
3645 }
3646 if (!PQsendQuery(pgsql, query)) {
3647 RETURN_FALSE;
3648 }
3649 }
3650
3651 /* Wait to finish sending buffer */
3652 while ((ret = PQflush(pgsql))) {
3653 if (ret == -1) {
3654 php_error_docref(NULL, E_NOTICE, "Could not empty PostgreSQL send buffer");
3655 break;
3656 }
3657 usleep(10000);
3658 }
3659
3660 if (PQsetnonblocking(pgsql, 0)) {
3661 php_error_docref(NULL, E_NOTICE, "Cannot set connection to blocking mode");
3662 }
3663 }
3664
3665 if (ret == 0) {
3666 RETURN_TRUE;
3667 } else if (ret == -1) {
3668 RETURN_FALSE;
3669 } else {
3670 RETURN_LONG(0);
3671 }
3672 }
3673 /* }}} */
3674
3675 /* {{{ Send asynchronous parameterized query */
PHP_FUNCTION(pg_send_query_params)3676 PHP_FUNCTION(pg_send_query_params)
3677 {
3678 zval *pgsql_link, *pv_param_arr, *tmp;
3679 pgsql_link_handle *link;
3680 int num_params = 0;
3681 char **params = NULL;
3682 char *query;
3683 size_t query_len;
3684 PGconn *pgsql;
3685 int is_non_blocking;
3686 int ret;
3687
3688 if (zend_parse_parameters(ZEND_NUM_ARGS(), "Osa", &pgsql_link, pgsql_link_ce, &query, &query_len, &pv_param_arr) == FAILURE) {
3689 RETURN_THROWS();
3690 }
3691
3692 link = Z_PGSQL_LINK_P(pgsql_link);
3693 CHECK_PGSQL_LINK(link);
3694 pgsql = link->conn;
3695
3696 is_non_blocking = PQisnonblocking(pgsql);
3697
3698 if (is_non_blocking == 0 && PQsetnonblocking(pgsql, 1) == -1) {
3699 php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode");
3700 RETURN_FALSE;
3701 }
3702
3703 if (_php_pgsql_link_has_results(pgsql)) {
3704 php_error_docref(NULL, E_NOTICE,
3705 "There are results on this connection. Call pg_get_result() until it returns FALSE");
3706 }
3707
3708 num_params = zend_hash_num_elements(Z_ARRVAL_P(pv_param_arr));
3709 if (num_params > 0) {
3710 int i = 0;
3711 params = (char **)safe_emalloc(sizeof(char *), num_params, 0);
3712
3713 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pv_param_arr), tmp) {
3714 ZVAL_DEREF(tmp);
3715 if (Z_TYPE_P(tmp) == IS_NULL) {
3716 params[i] = NULL;
3717 } else {
3718 zend_string *tmp_str;
3719 zend_string *str = zval_get_tmp_string(tmp, &tmp_str);
3720
3721 params[i] = estrndup(ZSTR_VAL(str), ZSTR_LEN(str));
3722 zend_tmp_string_release(tmp_str);
3723 }
3724
3725 i++;
3726 } ZEND_HASH_FOREACH_END();
3727 }
3728
3729 if (PQsendQueryParams(pgsql, query, num_params, NULL, (const char * const *)params, NULL, NULL, 0)) {
3730 _php_pgsql_free_params(params, num_params);
3731 } else if (is_non_blocking) {
3732 _php_pgsql_free_params(params, num_params);
3733 RETURN_FALSE;
3734 } else {
3735 if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
3736 PQreset(pgsql);
3737 }
3738 if (!PQsendQueryParams(pgsql, query, num_params, NULL, (const char * const *)params, NULL, NULL, 0)) {
3739 _php_pgsql_free_params(params, num_params);
3740 RETURN_FALSE;
3741 }
3742 }
3743
3744 if (is_non_blocking) {
3745 ret = PQflush(pgsql);
3746 } else {
3747 /* Wait to finish sending buffer */
3748 while ((ret = PQflush(pgsql))) {
3749 if (ret == -1) {
3750 php_error_docref(NULL, E_NOTICE, "Could not empty PostgreSQL send buffer");
3751 break;
3752 }
3753 usleep(10000);
3754 }
3755
3756 if (PQsetnonblocking(pgsql, 0) != 0) {
3757 php_error_docref(NULL, E_NOTICE, "Cannot set connection to blocking mode");
3758 }
3759 }
3760
3761 if (ret == 0) {
3762 RETURN_TRUE;
3763 } else if (ret == -1) {
3764 RETURN_FALSE;
3765 } else {
3766 RETURN_LONG(0);
3767 }
3768 }
3769 /* }}} */
3770
3771 /* {{{ Asynchronously prepare a query for future execution */
PHP_FUNCTION(pg_send_prepare)3772 PHP_FUNCTION(pg_send_prepare)
3773 {
3774 zval *pgsql_link;
3775 pgsql_link_handle *link;
3776 char *query, *stmtname;
3777 size_t stmtname_len, query_len;
3778 PGconn *pgsql;
3779 int is_non_blocking;
3780 int ret;
3781
3782 if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oss", &pgsql_link, pgsql_link_ce, &stmtname, &stmtname_len, &query, &query_len) == FAILURE) {
3783 RETURN_THROWS();
3784 }
3785
3786 link = Z_PGSQL_LINK_P(pgsql_link);
3787 CHECK_PGSQL_LINK(link);
3788 pgsql = link->conn;
3789
3790 is_non_blocking = PQisnonblocking(pgsql);
3791
3792 if (is_non_blocking == 0 && PQsetnonblocking(pgsql, 1) == -1) {
3793 php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode");
3794 RETURN_FALSE;
3795 }
3796
3797 if (_php_pgsql_link_has_results(pgsql)) {
3798 php_error_docref(NULL, E_NOTICE,
3799 "There are results on this connection. Call pg_get_result() until it returns FALSE");
3800 }
3801
3802 if (!PQsendPrepare(pgsql, stmtname, query, 0, NULL)) {
3803 if (is_non_blocking) {
3804 RETURN_FALSE;
3805 } else {
3806 if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
3807 PQreset(pgsql);
3808 }
3809 if (!PQsendPrepare(pgsql, stmtname, query, 0, NULL)) {
3810 RETURN_FALSE;
3811 }
3812 }
3813 }
3814
3815 if (is_non_blocking) {
3816 ret = PQflush(pgsql);
3817 } else {
3818 /* Wait to finish sending buffer */
3819 while ((ret = PQflush(pgsql))) {
3820 if (ret == -1) {
3821 php_error_docref(NULL, E_NOTICE, "Could not empty PostgreSQL send buffer");
3822 break;
3823 }
3824 usleep(10000);
3825 }
3826 if (PQsetnonblocking(pgsql, 0) != 0) {
3827 php_error_docref(NULL, E_NOTICE, "Cannot set connection to blocking mode");
3828 }
3829 }
3830
3831 if (ret == 0) {
3832 RETURN_TRUE;
3833 } else if (ret == -1) {
3834 RETURN_FALSE;
3835 } else {
3836 RETURN_LONG(0);
3837 }
3838 }
3839 /* }}} */
3840
3841 /* {{{ Executes prevriously prepared stmtname asynchronously */
PHP_FUNCTION(pg_send_execute)3842 PHP_FUNCTION(pg_send_execute)
3843 {
3844 zval *pgsql_link;
3845 pgsql_link_handle *link;
3846 zval *pv_param_arr, *tmp;
3847 int num_params = 0;
3848 char **params = NULL;
3849 char *stmtname;
3850 size_t stmtname_len;
3851 PGconn *pgsql;
3852 int is_non_blocking;
3853 int ret;
3854
3855 if (zend_parse_parameters(ZEND_NUM_ARGS(), "Osa", &pgsql_link, pgsql_link_ce, &stmtname, &stmtname_len, &pv_param_arr) == FAILURE) {
3856 RETURN_THROWS();
3857 }
3858
3859 link = Z_PGSQL_LINK_P(pgsql_link);
3860 CHECK_PGSQL_LINK(link);
3861 pgsql = link->conn;
3862
3863 is_non_blocking = PQisnonblocking(pgsql);
3864
3865 if (is_non_blocking == 0 && PQsetnonblocking(pgsql, 1) == -1) {
3866 php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode");
3867 RETURN_FALSE;
3868 }
3869
3870 if (_php_pgsql_link_has_results(pgsql)) {
3871 php_error_docref(NULL, E_NOTICE,
3872 "There are results on this connection. Call pg_get_result() until it returns FALSE");
3873 }
3874
3875 num_params = zend_hash_num_elements(Z_ARRVAL_P(pv_param_arr));
3876 if (num_params > 0) {
3877 int i = 0;
3878 params = (char **)safe_emalloc(sizeof(char *), num_params, 0);
3879
3880 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pv_param_arr), tmp) {
3881 ZVAL_DEREF(tmp);
3882 if (Z_TYPE_P(tmp) == IS_NULL) {
3883 params[i] = NULL;
3884 } else {
3885 zend_string *tmp_str = zval_try_get_string(tmp);
3886 if (UNEXPECTED(!tmp_str)) {
3887 _php_pgsql_free_params(params, i);
3888 return;
3889 }
3890 params[i] = estrndup(ZSTR_VAL(tmp_str), ZSTR_LEN(tmp_str));
3891 zend_string_release(tmp_str);
3892 }
3893
3894 i++;
3895 } ZEND_HASH_FOREACH_END();
3896 }
3897
3898 if (PQsendQueryPrepared(pgsql, stmtname, num_params, (const char * const *)params, NULL, NULL, 0)) {
3899 _php_pgsql_free_params(params, num_params);
3900 } else if (is_non_blocking) {
3901 _php_pgsql_free_params(params, num_params);
3902 RETURN_FALSE;
3903 } else {
3904 if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) {
3905 PQreset(pgsql);
3906 }
3907 if (!PQsendQueryPrepared(pgsql, stmtname, num_params, (const char * const *)params, NULL, NULL, 0)) {
3908 _php_pgsql_free_params(params, num_params);
3909 RETURN_FALSE;
3910 }
3911 }
3912
3913 if (is_non_blocking) {
3914 ret = PQflush(pgsql);
3915 } else {
3916 /* Wait to finish sending buffer */
3917 while ((ret = PQflush(pgsql))) {
3918 if (ret == -1) {
3919 php_error_docref(NULL, E_NOTICE, "Could not empty PostgreSQL send buffer");
3920 break;
3921 }
3922 usleep(10000);
3923 }
3924 if (PQsetnonblocking(pgsql, 0) != 0) {
3925 php_error_docref(NULL, E_NOTICE, "Cannot set connection to blocking mode");
3926 }
3927 }
3928
3929 if (ret == 0) {
3930 RETURN_TRUE;
3931 } else if (ret == -1) {
3932 RETURN_FALSE;
3933 } else {
3934 RETURN_LONG(0);
3935 }
3936 }
3937 /* }}} */
3938
3939 /* {{{ Get asynchronous query result */
PHP_FUNCTION(pg_get_result)3940 PHP_FUNCTION(pg_get_result)
3941 {
3942 zval *pgsql_link;
3943 pgsql_link_handle *link;
3944 PGconn *pgsql;
3945 PGresult *pgsql_result;
3946 pgsql_result_handle *pg_result;
3947
3948 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pgsql_link, pgsql_link_ce) == FAILURE) {
3949 RETURN_THROWS();
3950 }
3951
3952 link = Z_PGSQL_LINK_P(pgsql_link);
3953 CHECK_PGSQL_LINK(link);
3954 pgsql = link->conn;
3955
3956 pgsql_result = PQgetResult(pgsql);
3957 if (!pgsql_result) {
3958 /* no result */
3959 RETURN_FALSE;
3960 }
3961
3962 object_init_ex(return_value, pgsql_result_ce);
3963 pg_result = Z_PGSQL_RESULT_P(return_value);
3964 pg_result->conn = pgsql;
3965 pg_result->result = pgsql_result;
3966 pg_result->row = 0;
3967 }
3968 /* }}} */
3969
3970 /* {{{ Get status of query result */
PHP_FUNCTION(pg_result_status)3971 PHP_FUNCTION(pg_result_status)
3972 {
3973 zval *result;
3974 zend_long result_type = PGSQL_STATUS_LONG;
3975 ExecStatusType status;
3976 PGresult *pgsql_result;
3977 pgsql_result_handle *pg_result;
3978
3979 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|l", &result, pgsql_result_ce, &result_type) == FAILURE) {
3980 RETURN_THROWS();
3981 }
3982
3983 pg_result = Z_PGSQL_RESULT_P(result);
3984 CHECK_PGSQL_RESULT(pg_result);
3985 pgsql_result = pg_result->result;
3986
3987 if (result_type == PGSQL_STATUS_LONG) {
3988 status = PQresultStatus(pgsql_result);
3989 RETURN_LONG((int)status);
3990 }
3991 else if (result_type == PGSQL_STATUS_STRING) {
3992 RETURN_STRING(PQcmdStatus(pgsql_result));
3993 } else {
3994 zend_argument_value_error(2, "must be either PGSQL_STATUS_LONG or PGSQL_STATUS_STRING");
3995 RETURN_THROWS();
3996 }
3997 }
3998 /* }}} */
3999
4000 /* {{{ Get asynchronous notification */
PHP_FUNCTION(pg_get_notify)4001 PHP_FUNCTION(pg_get_notify)
4002 {
4003 zval *pgsql_link;
4004 pgsql_link_handle *link;
4005 zend_long result_type = PGSQL_ASSOC;
4006 PGconn *pgsql;
4007 PGnotify *pgsql_notify;
4008
4009 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|l", &pgsql_link, pgsql_link_ce, &result_type) == FAILURE) {
4010 RETURN_THROWS();
4011 }
4012
4013 link = Z_PGSQL_LINK_P(pgsql_link);
4014 CHECK_PGSQL_LINK(link);
4015 pgsql = link->conn;
4016
4017 if (!(result_type & PGSQL_BOTH)) {
4018 zend_argument_value_error(2, "must be one of PGSQL_ASSOC, PGSQL_NUM, or PGSQL_BOTH");
4019 RETURN_THROWS();
4020 }
4021
4022 PQconsumeInput(pgsql);
4023 pgsql_notify = PQnotifies(pgsql);
4024 if (!pgsql_notify) {
4025 /* no notify message */
4026 RETURN_FALSE;
4027 }
4028 array_init(return_value);
4029 if (result_type & PGSQL_NUM) {
4030 add_index_string(return_value, 0, pgsql_notify->relname);
4031 add_index_long(return_value, 1, pgsql_notify->be_pid);
4032 /* consider to use php_version_compare() here */
4033 if (zend_strtod(PQparameterStatus(pgsql, "server_version"), NULL) >= 9.0) {
4034 add_index_string(return_value, 2, pgsql_notify->extra);
4035 }
4036 }
4037 if (result_type & PGSQL_ASSOC) {
4038 add_assoc_string(return_value, "message", pgsql_notify->relname);
4039 add_assoc_long(return_value, "pid", pgsql_notify->be_pid);
4040 /* consider to use php_version_compare() here */
4041 if (zend_strtod(PQparameterStatus(pgsql, "server_version"), NULL) >= 9.0) {
4042 add_assoc_string(return_value, "payload", pgsql_notify->extra);
4043 }
4044 }
4045 PQfreemem(pgsql_notify);
4046 }
4047 /* }}} */
4048
4049 /* {{{ Get backend(server) pid */
PHP_FUNCTION(pg_get_pid)4050 PHP_FUNCTION(pg_get_pid)
4051 {
4052 zval *pgsql_link;
4053 pgsql_link_handle *link;
4054 PGconn *pgsql;
4055
4056 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pgsql_link, pgsql_link_ce) == FAILURE) {
4057 RETURN_THROWS();
4058 }
4059
4060 link = Z_PGSQL_LINK_P(pgsql_link);
4061 CHECK_PGSQL_LINK(link);
4062 pgsql = link->conn;
4063
4064 RETURN_LONG(PQbackendPID(pgsql));
4065 }
4066 /* }}} */
4067
php_pgsql_fd_write(php_stream * stream,const char * buf,size_t count)4068 static ssize_t php_pgsql_fd_write(php_stream *stream, const char *buf, size_t count) /* {{{ */
4069 {
4070 return -1;
4071 }
4072 /* }}} */
4073
php_pgsql_fd_read(php_stream * stream,char * buf,size_t count)4074 static ssize_t php_pgsql_fd_read(php_stream *stream, char *buf, size_t count) /* {{{ */
4075 {
4076 return -1;
4077 }
4078 /* }}} */
4079
php_pgsql_fd_close(php_stream * stream,int close_handle)4080 static int php_pgsql_fd_close(php_stream *stream, int close_handle) /* {{{ */
4081 {
4082 return EOF;
4083 }
4084 /* }}} */
4085
php_pgsql_fd_flush(php_stream * stream)4086 static int php_pgsql_fd_flush(php_stream *stream) /* {{{ */
4087 {
4088 return FAILURE;
4089 }
4090 /* }}} */
4091
php_pgsql_fd_set_option(php_stream * stream,int option,int value,void * ptrparam)4092 static int php_pgsql_fd_set_option(php_stream *stream, int option, int value, void *ptrparam) /* {{{ */
4093 {
4094 PGconn *pgsql = (PGconn *) stream->abstract;
4095 switch (option) {
4096 case PHP_STREAM_OPTION_BLOCKING:
4097 return PQsetnonblocking(pgsql, value);
4098 default:
4099 return FAILURE;
4100 }
4101 }
4102 /* }}} */
4103
php_pgsql_fd_cast(php_stream * stream,int cast_as,void ** ret)4104 static int php_pgsql_fd_cast(php_stream *stream, int cast_as, void **ret) /* {{{ */
4105 {
4106 PGconn *pgsql = (PGconn *) stream->abstract;
4107
4108 switch (cast_as) {
4109 case PHP_STREAM_AS_FD_FOR_SELECT:
4110 case PHP_STREAM_AS_FD:
4111 case PHP_STREAM_AS_SOCKETD: {
4112 int fd_number = PQsocket(pgsql);
4113 if (fd_number == -1) {
4114 return FAILURE;
4115 }
4116
4117 if (ret) {
4118 *(php_socket_t *)ret = fd_number;
4119 }
4120 }
4121 return SUCCESS;
4122 ZEND_FALLTHROUGH;
4123 default:
4124 return FAILURE;
4125 }
4126 }
4127 /* }}} */
4128
4129 /* {{{ Get a read-only handle to the socket underlying the pgsql connection */
PHP_FUNCTION(pg_socket)4130 PHP_FUNCTION(pg_socket)
4131 {
4132 zval *pgsql_link;
4133 pgsql_link_handle *link;
4134 php_stream *stream;
4135 PGconn *pgsql;
4136
4137 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pgsql_link, pgsql_link_ce) == FAILURE) {
4138 RETURN_THROWS();
4139 }
4140
4141 link = Z_PGSQL_LINK_P(pgsql_link);
4142 CHECK_PGSQL_LINK(link);
4143 pgsql = link->conn;
4144
4145 stream = php_stream_alloc(&php_stream_pgsql_fd_ops, pgsql, NULL, "r");
4146
4147 if (stream) {
4148 php_stream_to_zval(stream, return_value);
4149 return;
4150 }
4151
4152 RETURN_FALSE;
4153 }
4154 /* }}} */
4155
4156 /* {{{ Reads input on the connection */
PHP_FUNCTION(pg_consume_input)4157 PHP_FUNCTION(pg_consume_input)
4158 {
4159 zval *pgsql_link;
4160 pgsql_link_handle *link;
4161 PGconn *pgsql;
4162
4163 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pgsql_link, pgsql_link_ce) == FAILURE) {
4164 RETURN_THROWS();
4165 }
4166
4167 link = Z_PGSQL_LINK_P(pgsql_link);
4168 CHECK_PGSQL_LINK(link);
4169 pgsql = link->conn;
4170
4171 RETURN_BOOL(PQconsumeInput(pgsql));
4172 }
4173 /* }}} */
4174
4175 /* {{{ Flush outbound query data on the connection */
PHP_FUNCTION(pg_flush)4176 PHP_FUNCTION(pg_flush)
4177 {
4178 zval *pgsql_link;
4179 pgsql_link_handle *link;
4180 PGconn *pgsql;
4181 int ret;
4182 int is_non_blocking;
4183
4184 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pgsql_link, pgsql_link_ce) == FAILURE) {
4185 RETURN_THROWS();
4186 }
4187
4188 link = Z_PGSQL_LINK_P(pgsql_link);
4189 CHECK_PGSQL_LINK(link);
4190 pgsql = link->conn;
4191
4192 is_non_blocking = PQisnonblocking(pgsql);
4193
4194 if (is_non_blocking == 0 && PQsetnonblocking(pgsql, 1) == -1) {
4195 php_error_docref(NULL, E_NOTICE, "Cannot set connection to nonblocking mode");
4196 RETURN_FALSE;
4197 }
4198
4199 ret = PQflush(pgsql);
4200
4201 if (is_non_blocking == 0 && PQsetnonblocking(pgsql, 0) == -1) {
4202 php_error_docref(NULL, E_NOTICE, "Failed resetting connection to blocking mode");
4203 }
4204
4205 switch (ret) {
4206 case 0: RETURN_TRUE; break;
4207 case 1: RETURN_LONG(0); break;
4208 default: RETURN_FALSE;
4209 }
4210 }
4211 /* }}} */
4212
4213 /* {{{ php_pgsql_meta_data
4214 * table_name must not be empty
4215 * TODO: Add meta_data cache for better performance
4216 */
php_pgsql_meta_data(PGconn * pg_link,const zend_string * table_name,zval * meta,bool extended)4217 PHP_PGSQL_API zend_result php_pgsql_meta_data(PGconn *pg_link, const zend_string *table_name, zval *meta, bool extended)
4218 {
4219 PGresult *pg_result;
4220 char *src, *tmp_name, *tmp_name2 = NULL;
4221 char *escaped;
4222 smart_str querystr = {0};
4223 size_t new_len;
4224 int i, num_rows;
4225 zval elem;
4226
4227 ZEND_ASSERT(ZSTR_LEN(table_name) != 0);
4228
4229 src = estrdup(ZSTR_VAL(table_name));
4230 tmp_name = php_strtok_r(src, ".", &tmp_name2);
4231 if (!tmp_name) {
4232 efree(src);
4233 zend_argument_value_error(2, "must be specified (%s)", ZSTR_VAL(table_name));
4234 return FAILURE;
4235 }
4236 if (!tmp_name2 || !*tmp_name2) {
4237 /* Default schema */
4238 tmp_name2 = tmp_name;
4239 tmp_name = "public";
4240 }
4241
4242 if (extended) {
4243 smart_str_appends(&querystr,
4244 "SELECT a.attname, a.attnum, t.typname, a.attlen, a.attnotNULL, a.atthasdef, a.attndims, t.typtype, "
4245 "d.description "
4246 "FROM pg_class as c "
4247 " JOIN pg_attribute a ON (a.attrelid = c.oid) "
4248 " JOIN pg_type t ON (a.atttypid = t.oid) "
4249 " JOIN pg_namespace n ON (c.relnamespace = n.oid) "
4250 " LEFT JOIN pg_description d ON (d.objoid=a.attrelid AND d.objsubid=a.attnum AND c.oid=d.objoid) "
4251 "WHERE a.attnum > 0 AND c.relname = '");
4252 } else {
4253 smart_str_appends(&querystr,
4254 "SELECT a.attname, a.attnum, t.typname, a.attlen, a.attnotnull, a.atthasdef, a.attndims, t.typtype "
4255 "FROM pg_class as c "
4256 " JOIN pg_attribute a ON (a.attrelid = c.oid) "
4257 " JOIN pg_type t ON (a.atttypid = t.oid) "
4258 " JOIN pg_namespace n ON (c.relnamespace = n.oid) "
4259 "WHERE a.attnum > 0 AND c.relname = '");
4260 }
4261 escaped = (char *)safe_emalloc(strlen(tmp_name2), 2, 1);
4262 new_len = PQescapeStringConn(pg_link, escaped, tmp_name2, strlen(tmp_name2), NULL);
4263 if (new_len) {
4264 smart_str_appendl(&querystr, escaped, new_len);
4265 }
4266 efree(escaped);
4267
4268 smart_str_appends(&querystr, "' AND n.nspname = '");
4269 escaped = (char *)safe_emalloc(strlen(tmp_name), 2, 1);
4270 new_len = PQescapeStringConn(pg_link, escaped, tmp_name, strlen(tmp_name), NULL);
4271 if (new_len) {
4272 smart_str_appendl(&querystr, escaped, new_len);
4273 }
4274 efree(escaped);
4275
4276 smart_str_appends(&querystr, "' ORDER BY a.attnum;");
4277 smart_str_0(&querystr);
4278 efree(src);
4279
4280 pg_result = PQexec(pg_link, ZSTR_VAL(querystr.s));
4281 if (PQresultStatus(pg_result) != PGRES_TUPLES_OK || (num_rows = PQntuples(pg_result)) == 0) {
4282 php_error_docref(NULL, E_WARNING, "Table '%s' doesn't exists", ZSTR_VAL(table_name));
4283 smart_str_free(&querystr);
4284 PQclear(pg_result);
4285 return FAILURE;
4286 }
4287 smart_str_free(&querystr);
4288
4289 for (i = 0; i < num_rows; i++) {
4290 char *name;
4291 array_init(&elem);
4292 /* pg_attribute.attnum */
4293 add_assoc_long_ex(&elem, "num", sizeof("num") - 1, atoi(PQgetvalue(pg_result, i, 1)));
4294 /* pg_type.typname */
4295 add_assoc_string_ex(&elem, "type", sizeof("type") - 1, PQgetvalue(pg_result, i, 2));
4296 /* pg_attribute.attlen */
4297 add_assoc_long_ex(&elem, "len", sizeof("len") - 1, atoi(PQgetvalue(pg_result,i,3)));
4298 /* pg_attribute.attnonull */
4299 add_assoc_bool_ex(&elem, "not null", sizeof("not null") - 1, !strcmp(PQgetvalue(pg_result, i, 4), "t"));
4300 /* pg_attribute.atthasdef */
4301 add_assoc_bool_ex(&elem, "has default", sizeof("has default") - 1, !strcmp(PQgetvalue(pg_result,i,5), "t"));
4302 /* pg_attribute.attndims */
4303 add_assoc_long_ex(&elem, "array dims", sizeof("array dims") - 1, atoi(PQgetvalue(pg_result, i, 6)));
4304 /* pg_type.typtype */
4305 add_assoc_bool_ex(&elem, "is enum", sizeof("is enum") - 1, !strcmp(PQgetvalue(pg_result, i, 7), "e"));
4306 if (extended) {
4307 /* pg_type.typtype */
4308 add_assoc_bool_ex(&elem, "is base", sizeof("is base") - 1, !strcmp(PQgetvalue(pg_result, i, 7), "b"));
4309 add_assoc_bool_ex(&elem, "is composite", sizeof("is composite") - 1, !strcmp(PQgetvalue(pg_result, i, 7), "c"));
4310 add_assoc_bool_ex(&elem, "is pseudo", sizeof("is pseudo") - 1, !strcmp(PQgetvalue(pg_result, i, 7), "p"));
4311 /* pg_description.description */
4312 add_assoc_string_ex(&elem, "description", sizeof("description") - 1, PQgetvalue(pg_result, i, 8));
4313 }
4314 /* pg_attribute.attname */
4315 name = PQgetvalue(pg_result,i,0);
4316 add_assoc_zval(meta, name, &elem);
4317 }
4318 PQclear(pg_result);
4319
4320 return SUCCESS;
4321 }
4322
4323 /* }}} */
4324
4325 /* {{{ Get meta_data */
PHP_FUNCTION(pg_meta_data)4326 PHP_FUNCTION(pg_meta_data)
4327 {
4328 zval *pgsql_link;
4329 pgsql_link_handle *link;
4330 zend_string *table_name;
4331 bool extended=0;
4332 PGconn *pgsql;
4333
4334 if (zend_parse_parameters(ZEND_NUM_ARGS(), "OP|b",
4335 &pgsql_link, pgsql_link_ce, &table_name, &extended) == FAILURE
4336 ) {
4337 RETURN_THROWS();
4338 }
4339
4340 link = Z_PGSQL_LINK_P(pgsql_link);
4341 CHECK_PGSQL_LINK(link);
4342 pgsql = link->conn;
4343
4344 /* php_pgsql_meta_data() asserts that table_name is not empty */
4345 if (ZSTR_LEN(table_name) == 0) {
4346 zend_argument_value_error(2, "cannot be empty");
4347 RETURN_THROWS();
4348 }
4349
4350 array_init(return_value);
4351 if (php_pgsql_meta_data(pgsql, table_name, return_value, extended) == FAILURE) {
4352 zend_array_destroy(Z_ARR_P(return_value)); /* destroy array */
4353 RETURN_FALSE;
4354 }
4355 }
4356 /* }}} */
4357
4358 /* {{{ php_pgsql_get_data_type */
php_pgsql_get_data_type(const zend_string * type_name)4359 static php_pgsql_data_type php_pgsql_get_data_type(const zend_string *type_name)
4360 {
4361 /* This is stupid way to do. I'll fix it when I decide how to support
4362 user defined types. (Yasuo) */
4363 /* boolean */
4364 if (zend_string_equals(type_name, ZSTR_KNOWN(ZEND_STR_BOOL)) ||zend_string_equals(type_name, ZSTR_KNOWN(ZEND_STR_BOOLEAN)))
4365 return PG_BOOL;
4366 /* object id */
4367 if (zend_string_equals_literal(type_name, "oid"))
4368 return PG_OID;
4369 /* integer */
4370 if (zend_string_equals_literal(type_name, "int2") || zend_string_equals_literal(type_name, "smallint"))
4371 return PG_INT2;
4372 if (zend_string_equals_literal(type_name, "int4") || zend_string_equals_literal(type_name, "integer"))
4373 return PG_INT4;
4374 if (zend_string_equals_literal(type_name, "int8") || zend_string_equals_literal(type_name, "bigint"))
4375 return PG_INT8;
4376 /* real and other */
4377 if (zend_string_equals_literal(type_name, "float4") || zend_string_equals_literal(type_name, "real"))
4378 return PG_FLOAT4;
4379 if (zend_string_equals_literal(type_name, "float8") || zend_string_equals_literal(type_name, "double precision"))
4380 return PG_FLOAT8;
4381 if (zend_string_equals_literal(type_name, "numeric"))
4382 return PG_NUMERIC;
4383 if (zend_string_equals_literal(type_name, "money"))
4384 return PG_MONEY;
4385 /* character */
4386 if (zend_string_equals_literal(type_name, "text"))
4387 return PG_TEXT;
4388 if (zend_string_equals_literal(type_name, "bpchar") || zend_string_equals_literal(type_name, "character"))
4389 return PG_CHAR;
4390 if (zend_string_equals_literal(type_name, "varchar") || zend_string_equals_literal(type_name, "character varying"))
4391 return PG_VARCHAR;
4392 /* time and interval */
4393 if (zend_string_equals_literal(type_name, "abstime"))
4394 return PG_UNIX_TIME;
4395 if (zend_string_equals_literal(type_name, "reltime"))
4396 return PG_UNIX_TIME_INTERVAL;
4397 if (zend_string_equals_literal(type_name, "tinterval"))
4398 return PG_UNIX_TIME_INTERVAL;
4399 if (zend_string_equals_literal(type_name, "date"))
4400 return PG_DATE;
4401 if (zend_string_equals_literal(type_name, "time"))
4402 return PG_TIME;
4403 if (zend_string_equals_literal(type_name, "time with time zone") || zend_string_equals_literal(type_name, "timetz"))
4404 return PG_TIME_WITH_TIMEZONE;
4405 if (zend_string_equals_literal(type_name, "timestamp without time zone") || zend_string_equals_literal(type_name, "timestamp"))
4406 return PG_TIMESTAMP;
4407 if (zend_string_equals_literal(type_name, "timestamp with time zone") || zend_string_equals_literal(type_name, "timestamptz"))
4408 return PG_TIMESTAMP_WITH_TIMEZONE;
4409 if (zend_string_equals_literal(type_name, "interval"))
4410 return PG_INTERVAL;
4411 /* binary */
4412 if (zend_string_equals_literal(type_name, "bytea"))
4413 return PG_BYTEA;
4414 /* network */
4415 if (zend_string_equals_literal(type_name, "cidr"))
4416 return PG_CIDR;
4417 if (zend_string_equals_literal(type_name, "inet"))
4418 return PG_INET;
4419 if (zend_string_equals_literal(type_name, "macaddr"))
4420 return PG_MACADDR;
4421 /* bit */
4422 if (zend_string_equals_literal(type_name, "bit"))
4423 return PG_BIT;
4424 if (zend_string_equals_literal(type_name, "bit varying"))
4425 return PG_VARBIT;
4426 /* geometric */
4427 if (zend_string_equals_literal(type_name, "line"))
4428 return PG_LINE;
4429 if (zend_string_equals_literal(type_name, "lseg"))
4430 return PG_LSEG;
4431 if (zend_string_equals_literal(type_name, "box"))
4432 return PG_BOX;
4433 if (zend_string_equals_literal(type_name, "path"))
4434 return PG_PATH;
4435 if (zend_string_equals_literal(type_name, "point"))
4436 return PG_POINT;
4437 if (zend_string_equals_literal(type_name, "polygon"))
4438 return PG_POLYGON;
4439 if (zend_string_equals_literal(type_name, "circle"))
4440 return PG_CIRCLE;
4441
4442 return PG_UNKNOWN;
4443 }
4444 /* }}} */
4445
4446 /* {{{ php_pgsql_convert_match
4447 * test field value with regular expression specified.
4448 */
php_pgsql_convert_match(const zend_string * str,const char * regex,size_t regex_len,int icase)4449 static int php_pgsql_convert_match(const zend_string *str, const char *regex , size_t regex_len, int icase)
4450 {
4451 pcre2_code *re;
4452 PCRE2_SIZE err_offset;
4453 int res, errnumber;
4454 uint32_t options = PCRE2_NO_AUTO_CAPTURE;
4455 size_t i;
4456 pcre2_match_data *match_data;
4457
4458 /* Check invalid chars for POSIX regex */
4459 for (i = 0; i < ZSTR_LEN(str); i++) {
4460 if (ZSTR_VAL(str)[i] == '\n' ||
4461 ZSTR_VAL(str)[i] == '\r' ||
4462 ZSTR_VAL(str)[i] == '\0' ) {
4463 return FAILURE;
4464 }
4465 }
4466
4467 if (icase) {
4468 options |= PCRE2_CASELESS;
4469 }
4470
4471 re = pcre2_compile((PCRE2_SPTR)regex, regex_len, options, &errnumber, &err_offset, php_pcre_cctx());
4472 if (NULL == re) {
4473 PCRE2_UCHAR err_msg[128];
4474 pcre2_get_error_message(errnumber, err_msg, sizeof(err_msg));
4475 php_error_docref(NULL, E_WARNING, "Cannot compile regex: '%s'", err_msg);
4476 return FAILURE;
4477 }
4478
4479 match_data = php_pcre_create_match_data(0, re);
4480 if (NULL == match_data) {
4481 pcre2_code_free(re);
4482 php_error_docref(NULL, E_WARNING, "Cannot allocate match data");
4483 return FAILURE;
4484 }
4485 res = pcre2_match(re, (PCRE2_SPTR)ZSTR_VAL(str), ZSTR_LEN(str), 0, 0, match_data, php_pcre_mctx());
4486 php_pcre_free_match_data(match_data);
4487 pcre2_code_free(re);
4488
4489 if (res == PCRE2_ERROR_NOMATCH) {
4490 return FAILURE;
4491 } else if (res < 0) {
4492 php_error_docref(NULL, E_WARNING, "Cannot exec regex");
4493 return FAILURE;
4494 }
4495 return SUCCESS;
4496 }
4497
4498 /* }}} */
4499
4500 /* {{{ php_pgsql_add_quote
4501 * add quotes around string.
4502 */
php_pgsql_add_quotes(zend_string * src)4503 static zend_string *php_pgsql_add_quotes(zend_string *src)
4504 {
4505 return zend_string_concat3("E'", strlen("E'"), ZSTR_VAL(src), ZSTR_LEN(src), "'", strlen("'"));
4506 }
4507 /* }}} */
4508
4509 /* Raise E_NOTICE to E_WARNING or Error? */
4510 #define PGSQL_CONV_CHECK_IGNORE() \
4511 if (!err && Z_TYPE(new_val) == IS_STRING && zend_string_equals_literal(Z_STR(new_val), "NULL")) { \
4512 /* if new_value is string "NULL" and field has default value, remove element to use default value */ \
4513 if (!(opt & PGSQL_CONV_IGNORE_DEFAULT) && Z_TYPE_P(has_default) == IS_TRUE) { \
4514 zval_ptr_dtor(&new_val); \
4515 skip_field = 1; \
4516 } \
4517 /* raise error if it's not null and cannot be ignored */ \
4518 else if (!(opt & PGSQL_CONV_IGNORE_NOT_NULL) && Z_TYPE_P(not_null) == IS_TRUE) { \
4519 php_error_docref(NULL, E_NOTICE, "Detected NULL for 'NOT NULL' field '%s'", ZSTR_VAL(field)); \
4520 err = 1; \
4521 } \
4522 }
4523
4524 /* {{{ php_pgsql_convert
4525 * check and convert array values (fieldname=>value pair) for sql
4526 */
php_pgsql_convert(PGconn * pg_link,const zend_string * table_name,const zval * values,zval * result,zend_ulong opt)4527 PHP_PGSQL_API zend_result php_pgsql_convert(PGconn *pg_link, const zend_string *table_name, const zval *values, zval *result, zend_ulong opt)
4528 {
4529 zend_string *field = NULL;
4530 zval meta, *def, *type, *not_null, *has_default, *is_enum, *val, new_val;
4531 int err = 0, skip_field;
4532 php_pgsql_data_type data_type;
4533
4534 ZEND_ASSERT(pg_link != NULL);
4535 ZEND_ASSERT(Z_TYPE_P(values) == IS_ARRAY);
4536 ZEND_ASSERT(Z_TYPE_P(result) == IS_ARRAY);
4537 ZEND_ASSERT(!(opt & ~PGSQL_CONV_OPTS));
4538 ZEND_ASSERT(table_name);
4539 /* Table name cannot be empty for php_pgsql_meta_data() */
4540 ZEND_ASSERT(ZSTR_LEN(table_name) != 0);
4541
4542 array_init(&meta);
4543 /* table_name is escaped by php_pgsql_meta_data */
4544 if (php_pgsql_meta_data(pg_link, table_name, &meta, 0) == FAILURE) {
4545 zval_ptr_dtor(&meta);
4546 return FAILURE;
4547 }
4548
4549 ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(values), field, val) {
4550 skip_field = 0;
4551 ZVAL_DEREF(val);
4552 ZVAL_NULL(&new_val);
4553
4554 /* TODO: Check when meta data can be broken and see if can use assertions instead */
4555
4556 if (!err && field == NULL) {
4557 zend_value_error("Array of values must be an associative array with string keys");
4558 err = 1;
4559 }
4560
4561 if (!err && (def = zend_hash_find(Z_ARRVAL(meta), field)) == NULL) {
4562 php_error_docref(NULL, E_NOTICE, "Invalid field name (%s) in values", ZSTR_VAL(field));
4563 err = 1;
4564 }
4565 if (!err && (type = zend_hash_str_find(Z_ARRVAL_P(def), "type", sizeof("type") - 1)) == NULL) {
4566 php_error_docref(NULL, E_NOTICE, "Detected broken meta data. Missing 'type'");
4567 err = 1;
4568 }
4569 if (!err && (not_null = zend_hash_str_find(Z_ARRVAL_P(def), "not null", sizeof("not null") - 1)) == NULL) {
4570 php_error_docref(NULL, E_NOTICE, "Detected broken meta data. Missing 'not null'");
4571 err = 1;
4572 }
4573 if (!err && (has_default = zend_hash_str_find(Z_ARRVAL_P(def), "has default", sizeof("has default") - 1)) == NULL) {
4574 php_error_docref(NULL, E_NOTICE, "Detected broken meta data. Missing 'has default'");
4575 err = 1;
4576 }
4577 if (!err && (is_enum = zend_hash_str_find(Z_ARRVAL_P(def), "is enum", sizeof("is enum") - 1)) == NULL) {
4578 php_error_docref(NULL, E_NOTICE, "Detected broken meta data. Missing 'is enum'");
4579 err = 1;
4580 }
4581 if (!err && (Z_TYPE_P(val) == IS_ARRAY || Z_TYPE_P(val) == IS_OBJECT || Z_TYPE_P(val) == IS_RESOURCE)) {
4582 zend_type_error("Values must be of type string|int|float|bool|null, %s given", zend_zval_value_name(val));
4583 err = 1;
4584 }
4585 if (err) {
4586 break; /* break out for() */
4587 }
4588
4589 convert_to_boolean(is_enum);
4590 if (Z_TYPE_P(is_enum) == IS_TRUE) {
4591 /* enums need to be treated like strings */
4592 data_type = PG_TEXT;
4593 } else {
4594 data_type = php_pgsql_get_data_type(Z_STR_P(type));
4595 }
4596
4597 switch(data_type)
4598 {
4599 case PG_BOOL:
4600 switch (Z_TYPE_P(val)) {
4601 case IS_STRING:
4602 if (Z_STRLEN_P(val) == 0) {
4603 ZVAL_STR(&new_val, ZSTR_KNOWN(ZEND_STR_NULL));
4604 }
4605 else {
4606 if (zend_string_equals_literal(Z_STR_P(val), "t") || zend_string_equals_literal(Z_STR_P(val), "T") ||
4607 zend_string_equals_literal(Z_STR_P(val), "y") || zend_string_equals_literal(Z_STR_P(val), "Y") ||
4608 zend_string_equals_literal(Z_STR_P(val), "true") || zend_string_equals_literal(Z_STR_P(val), "True") ||
4609 zend_string_equals_literal(Z_STR_P(val), "yes") || zend_string_equals_literal(Z_STR_P(val), "Yes") ||
4610 zend_string_equals_literal(Z_STR_P(val), "1")) {
4611 ZVAL_STRINGL(&new_val, "'t'", sizeof("'t'")-1);
4612 }
4613 else if (zend_string_equals_literal(Z_STR_P(val), "f") || zend_string_equals_literal(Z_STR_P(val), "F") ||
4614 zend_string_equals_literal(Z_STR_P(val), "n") || zend_string_equals_literal(Z_STR_P(val), "N") ||
4615 zend_string_equals_literal(Z_STR_P(val), "false") || zend_string_equals_literal(Z_STR_P(val), "False") ||
4616 zend_string_equals_literal(Z_STR_P(val), "no") || zend_string_equals_literal(Z_STR_P(val), "No") ||
4617 zend_string_equals_literal(Z_STR_P(val), "0")) {
4618 ZVAL_STRINGL(&new_val, "'f'", sizeof("'f'")-1);
4619 }
4620 else {
4621 zend_value_error("%s(): Field \"%s\" must be of type bool, invalid PostgreSQL string boolean value \"%s\" given", get_active_function_name(), ZSTR_VAL(field), Z_STRVAL_P(val));
4622 err = 1;
4623 }
4624 }
4625 break;
4626
4627 case IS_LONG:
4628 if (Z_LVAL_P(val)) {
4629 ZVAL_STRINGL(&new_val, "'t'", sizeof("'t'")-1);
4630 }
4631 else {
4632 ZVAL_STRINGL(&new_val, "'f'", sizeof("'f'")-1);
4633 }
4634 break;
4635
4636 case IS_TRUE:
4637 ZVAL_STRINGL(&new_val, "'t'", sizeof("'t'")-1);
4638 break;
4639
4640 case IS_FALSE:
4641 ZVAL_STRINGL(&new_val, "'f'", sizeof("'f'")-1);
4642 break;
4643
4644 case IS_NULL:
4645 ZVAL_STR(&new_val, ZSTR_KNOWN(ZEND_STR_NULL));
4646 break;
4647
4648 default:
4649 err = 1;
4650 }
4651 PGSQL_CONV_CHECK_IGNORE();
4652 if (err) {
4653 zend_type_error("%s(): Field \"%s\" must be of type string|null|int|bool, %s given", get_active_function_name(), ZSTR_VAL(field), Z_STRVAL_P(type));
4654 }
4655 break;
4656
4657 case PG_OID:
4658 case PG_INT2:
4659 case PG_INT4:
4660 case PG_INT8:
4661 switch (Z_TYPE_P(val)) {
4662 case IS_STRING:
4663 if (Z_STRLEN_P(val) == 0) {
4664 ZVAL_STR(&new_val, ZSTR_KNOWN(ZEND_STR_NULL));
4665 }
4666 else {
4667 /* FIXME: better regex must be used */
4668 #define REGEX0 "^([+-]{0,1}[0-9]+)$"
4669 if (php_pgsql_convert_match(Z_STR_P(val), REGEX0, sizeof(REGEX0)-1, 0) == FAILURE) {
4670 err = 1;
4671 }
4672 else {
4673 ZVAL_STRINGL(&new_val, Z_STRVAL_P(val), Z_STRLEN_P(val));
4674 }
4675 #undef REGEX0
4676 }
4677 break;
4678
4679 case IS_DOUBLE:
4680 ZVAL_DOUBLE(&new_val, Z_DVAL_P(val));
4681 convert_to_long(&new_val);
4682 break;
4683
4684 case IS_LONG:
4685 ZVAL_LONG(&new_val, Z_LVAL_P(val));
4686 break;
4687
4688 case IS_NULL:
4689 ZVAL_STR(&new_val, ZSTR_KNOWN(ZEND_STR_NULL));
4690 break;
4691
4692 default:
4693 err = 1;
4694 }
4695 PGSQL_CONV_CHECK_IGNORE();
4696 if (err) {
4697 zend_type_error("%s(): Field \"%s\" must be of type int|null, %s given", get_active_function_name(), ZSTR_VAL(field), Z_STRVAL_P(type));
4698 }
4699 break;
4700
4701 case PG_NUMERIC:
4702 case PG_MONEY:
4703 case PG_FLOAT4:
4704 case PG_FLOAT8:
4705 switch (Z_TYPE_P(val)) {
4706 case IS_STRING:
4707 if (Z_STRLEN_P(val) == 0) {
4708 ZVAL_STR(&new_val, ZSTR_KNOWN(ZEND_STR_NULL));
4709 }
4710 else {
4711 #define REGEX0 "^[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?$"
4712 #define REGEX1 "^[+-]{0,1}(inf)(inity){0,1}$"
4713 /* better regex? */
4714 if (php_pgsql_convert_match(Z_STR_P(val), REGEX0, sizeof(REGEX0)-1, 0) == FAILURE) {
4715 if (php_pgsql_convert_match(Z_STR_P(val), REGEX1, sizeof(REGEX1)-1, 1) == FAILURE) {
4716 err = 1;
4717 } else {
4718 ZVAL_STR(&new_val, php_pgsql_add_quotes(Z_STR_P(val)));
4719 }
4720 }
4721 else {
4722 ZVAL_STRING(&new_val, Z_STRVAL_P(val));
4723 }
4724 #undef REGEX0
4725 #undef REGEX1
4726 }
4727 break;
4728
4729 case IS_LONG:
4730 ZVAL_LONG(&new_val, Z_LVAL_P(val));
4731 break;
4732
4733 case IS_DOUBLE:
4734 ZVAL_DOUBLE(&new_val, Z_DVAL_P(val));
4735 break;
4736
4737 case IS_NULL:
4738 ZVAL_STR(&new_val, ZSTR_KNOWN(ZEND_STR_NULL));
4739 break;
4740
4741 default:
4742 err = 1;
4743 }
4744 PGSQL_CONV_CHECK_IGNORE();
4745 if (err) {
4746 zend_type_error("%s(): Field \"%s\" must be of type %s|int|null, %s given", get_active_function_name(), (data_type == PG_MONEY ? "money" : "float"), ZSTR_VAL(field), Z_STRVAL_P(type));
4747 }
4748 break;
4749
4750 /* Exotic types are handled as string also.
4751 Please feel free to add more valitions. Invalid query fails
4752 at execution anyway. */
4753 case PG_TEXT:
4754 case PG_CHAR:
4755 case PG_VARCHAR:
4756 /* bit */
4757 case PG_BIT:
4758 case PG_VARBIT:
4759 /* geometric */
4760 case PG_LINE:
4761 case PG_LSEG:
4762 case PG_POINT:
4763 case PG_BOX:
4764 case PG_PATH:
4765 case PG_POLYGON:
4766 case PG_CIRCLE:
4767 /* unknown. JSON, Array etc */
4768 case PG_UNKNOWN:
4769 switch (Z_TYPE_P(val)) {
4770 case IS_STRING:
4771 if (Z_STRLEN_P(val) == 0) {
4772 if (opt & PGSQL_CONV_FORCE_NULL) {
4773 ZVAL_STR(&new_val, ZSTR_KNOWN(ZEND_STR_NULL));
4774 } else {
4775 ZVAL_STRINGL(&new_val, "''", sizeof("''")-1);
4776 }
4777 }
4778 else {
4779 zend_string *str;
4780 /* PostgreSQL ignores \0 */
4781 str = zend_string_alloc(Z_STRLEN_P(val) * 2, 0);
4782 /* better to use PGSQLescapeLiteral since PGescapeStringConn does not handle special \ */
4783 ZSTR_LEN(str) = PQescapeStringConn(pg_link, ZSTR_VAL(str), Z_STRVAL_P(val), Z_STRLEN_P(val), NULL);
4784 ZVAL_STR(&new_val, php_pgsql_add_quotes(str));
4785 zend_string_release_ex(str, false);
4786 }
4787 break;
4788
4789 case IS_LONG:
4790 ZVAL_STR(&new_val, zend_long_to_str(Z_LVAL_P(val)));
4791 break;
4792
4793 case IS_DOUBLE:
4794 ZVAL_DOUBLE(&new_val, Z_DVAL_P(val));
4795 convert_to_string(&new_val);
4796 break;
4797
4798 case IS_NULL:
4799 ZVAL_STR(&new_val, ZSTR_KNOWN(ZEND_STR_NULL));
4800 break;
4801
4802 default:
4803 err = 1;
4804 }
4805 PGSQL_CONV_CHECK_IGNORE();
4806 if (err) {
4807 zend_type_error("%s(): Field \"%s\" must be of type string|null, %s given", get_active_function_name(), ZSTR_VAL(field), Z_STRVAL_P(type));
4808 }
4809 break;
4810
4811 case PG_UNIX_TIME:
4812 case PG_UNIX_TIME_INTERVAL:
4813 /* these are the actallay a integer */
4814 switch (Z_TYPE_P(val)) {
4815 case IS_STRING:
4816 if (Z_STRLEN_P(val) == 0) {
4817 ZVAL_STR(&new_val, ZSTR_KNOWN(ZEND_STR_NULL));
4818 }
4819 else {
4820 /* better regex? */
4821 if (php_pgsql_convert_match(Z_STR_P(val), "^[0-9]+$", sizeof("^[0-9]+$")-1, 0) == FAILURE) {
4822 err = 1;
4823 }
4824 else {
4825 ZVAL_STRINGL(&new_val, Z_STRVAL_P(val), Z_STRLEN_P(val));
4826 convert_to_long(&new_val);
4827 }
4828 }
4829 break;
4830
4831 case IS_DOUBLE:
4832 ZVAL_DOUBLE(&new_val, Z_DVAL_P(val));
4833 convert_to_long(&new_val);
4834 break;
4835
4836 case IS_LONG:
4837 ZVAL_LONG(&new_val, Z_LVAL_P(val));
4838 break;
4839
4840 case IS_NULL:
4841 ZVAL_STR(&new_val, ZSTR_KNOWN(ZEND_STR_NULL));
4842 break;
4843
4844 default:
4845 err = 1;
4846 }
4847 PGSQL_CONV_CHECK_IGNORE();
4848 if (err) {
4849 zend_type_error("%s(): Field \"%s\" must be of type int|null, %s given", get_active_function_name(), ZSTR_VAL(field), zend_zval_value_name(val));
4850 }
4851 break;
4852
4853 case PG_CIDR:
4854 case PG_INET:
4855 switch (Z_TYPE_P(val)) {
4856 case IS_STRING:
4857 if (Z_STRLEN_P(val) == 0) {
4858 ZVAL_STR(&new_val, ZSTR_KNOWN(ZEND_STR_NULL));
4859 }
4860 else {
4861 #define REGEX0 "^((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])(\\/[0-9]{1,3})?$"
4862 #define REGEX1 "^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))(\\/[0-9]{1,3})?$"
4863 /* The inet type holds an IPv4 or IPv6 host address, and optionally its subnet, all in one field. See more in the doc.
4864 The regex might still be not perfect, but catches the most of IP variants. We might decide to remove the regex
4865 at all though and let the server side to handle it.*/
4866 if (php_pgsql_convert_match(Z_STR_P(val), REGEX0, sizeof(REGEX0)-1, 0) == FAILURE
4867 && php_pgsql_convert_match(Z_STR_P(val), REGEX1, sizeof(REGEX1)-1, 0) == FAILURE) {
4868 err = 2;
4869 }
4870 else {
4871 ZVAL_STR(&new_val, php_pgsql_add_quotes(Z_STR_P(val)));
4872 }
4873 #undef REGEX0
4874 #undef REGEX1
4875 }
4876 break;
4877
4878 case IS_NULL:
4879 ZVAL_STR(&new_val, ZSTR_KNOWN(ZEND_STR_NULL));
4880 break;
4881
4882 default:
4883 err = 1;
4884 }
4885 PGSQL_CONV_CHECK_IGNORE();
4886 if (err) {
4887 if (err == 2) {
4888 zend_value_error("%s(): Field \"%s\" must be a valid IPv4 or IPv6 address string, \"%s\" given", get_active_function_name(), ZSTR_VAL(field), Z_STRVAL_P(val));
4889 } else {
4890 zend_type_error("%s(): Field \"%s\" must be of type string|null, given %s", get_active_function_name(), ZSTR_VAL(field), zend_zval_value_name(val));
4891 }
4892 }
4893 break;
4894
4895 case PG_TIME_WITH_TIMEZONE:
4896 case PG_TIMESTAMP:
4897 case PG_TIMESTAMP_WITH_TIMEZONE:
4898 switch(Z_TYPE_P(val)) {
4899 case IS_STRING:
4900 if (Z_STRLEN_P(val) == 0) {
4901 ZVAL_STR(&new_val, ZSTR_KNOWN(ZEND_STR_NULL));
4902 } else if (zend_string_equals_literal_ci(Z_STR_P(val), "now()")) {
4903 ZVAL_STRINGL(&new_val, "NOW()", sizeof("NOW()")-1);
4904 } else {
4905 #define REGEX0 "^([0-9]{4}[/-][0-9]{1,2}[/-][0-9]{1,2})(([ \\t]+|T)(([0-9]{1,2}:[0-9]{1,2}){1}(:[0-9]{1,2}){0,1}(\\.[0-9]+){0,1}([ \\t]*([+-][0-9]{1,4}(:[0-9]{1,2}){0,1}|[-a-zA-Z_/+]{1,50})){0,1})){0,1}$"
4906 /* better regex? */
4907 if (php_pgsql_convert_match(Z_STR_P(val), REGEX0, sizeof(REGEX0)-1, 1) == FAILURE) {
4908 err = 1;
4909 } else {
4910 ZVAL_STR(&new_val, php_pgsql_add_quotes(Z_STR_P(val)));
4911 }
4912 #undef REGEX0
4913 }
4914 break;
4915
4916 case IS_NULL:
4917 ZVAL_STR(&new_val, ZSTR_KNOWN(ZEND_STR_NULL));
4918 break;
4919
4920 default:
4921 err = 1;
4922 }
4923 PGSQL_CONV_CHECK_IGNORE();
4924 if (err) {
4925 zend_type_error("%s(): Field \"%s\" must be of type string|null, %s given", get_active_function_name(), ZSTR_VAL(field), Z_STRVAL_P(type));
4926 }
4927 break;
4928
4929 case PG_DATE:
4930 switch(Z_TYPE_P(val)) {
4931 case IS_STRING:
4932 if (Z_STRLEN_P(val) == 0) {
4933 ZVAL_STR(&new_val, ZSTR_KNOWN(ZEND_STR_NULL));
4934 }
4935 else {
4936 #define REGEX0 "^([0-9]{4}[/-][0-9]{1,2}[/-][0-9]{1,2})$"
4937 /* FIXME: better regex must be used */
4938 if (php_pgsql_convert_match(Z_STR_P(val), REGEX0, sizeof(REGEX0)-1, 1) == FAILURE) {
4939 err = 1;
4940 }
4941 else {
4942 ZVAL_STR(&new_val, php_pgsql_add_quotes(Z_STR_P(val)));
4943 }
4944 #undef REGEX0
4945 }
4946 break;
4947
4948 case IS_NULL:
4949 ZVAL_STR(&new_val, ZSTR_KNOWN(ZEND_STR_NULL));
4950 break;
4951
4952 default:
4953 err = 1;
4954 }
4955 PGSQL_CONV_CHECK_IGNORE();
4956 if (err) {
4957 zend_type_error("%s(): Field \"%s\" must be of type string|null, %s given", get_active_function_name(), ZSTR_VAL(field), Z_STRVAL_P(type));
4958 }
4959 break;
4960
4961 case PG_TIME:
4962 switch(Z_TYPE_P(val)) {
4963 case IS_STRING:
4964 if (Z_STRLEN_P(val) == 0) {
4965 ZVAL_STR(&new_val, ZSTR_KNOWN(ZEND_STR_NULL));
4966 }
4967 else {
4968 #define REGEX0 "^(([0-9]{1,2}:[0-9]{1,2}){1}(:[0-9]{1,2}){0,1}){0,1}$"
4969 /* FIXME: better regex must be used */
4970 if (php_pgsql_convert_match(Z_STR_P(val), REGEX0, sizeof(REGEX0)-1, 1) == FAILURE) {
4971 err = 1;
4972 }
4973 else {
4974 ZVAL_STR(&new_val, php_pgsql_add_quotes(Z_STR_P(val)));
4975 }
4976 #undef REGEX0
4977 }
4978 break;
4979
4980 case IS_NULL:
4981 ZVAL_STR(&new_val, ZSTR_KNOWN(ZEND_STR_NULL));
4982 break;
4983
4984 default:
4985 err = 1;
4986 }
4987 PGSQL_CONV_CHECK_IGNORE();
4988 if (err) {
4989 zend_type_error("%s(): Field \"%s\" must be of type string|null, %s given", get_active_function_name(), ZSTR_VAL(field), Z_STRVAL_P(type));
4990 }
4991 break;
4992
4993 case PG_INTERVAL:
4994 switch(Z_TYPE_P(val)) {
4995 case IS_STRING:
4996 if (Z_STRLEN_P(val) == 0) {
4997 ZVAL_STR(&new_val, ZSTR_KNOWN(ZEND_STR_NULL));
4998 }
4999 else {
5000
5001 /* From the Postgres docs:
5002
5003 interval values can be written with the following syntax:
5004 [@] quantity unit [quantity unit...] [direction]
5005
5006 Where: quantity is a number (possibly signed); unit is second, minute, hour,
5007 day, week, month, year, decade, century, millennium, or abbreviations or
5008 plurals of these units [note not *all* abbreviations] ; direction can be
5009 ago or empty. The at sign (@) is optional noise.
5010
5011 ...
5012
5013 Quantities of days, hours, minutes, and seconds can be specified without explicit
5014 unit markings. For example, '1 12:59:10' is read the same as '1 day 12 hours 59 min 10
5015 sec'.
5016 */
5017 #define REGEX0 \
5018 "^(@?[ \\t]+)?(" \
5019 /* Textual time units and their abbreviations: */ \
5020 "(([-+]?[ \\t]+)?" \
5021 "[0-9]+(\\.[0-9]*)?[ \\t]*" \
5022 "(millenniums|millennia|millennium|mil|mils|" \
5023 "centuries|century|cent|c|" \
5024 "decades|decade|dec|decs|" \
5025 "years|year|y|" \
5026 "months|month|mon|" \
5027 "weeks|week|w|" \
5028 "days|day|d|" \
5029 "hours|hour|hr|hrs|h|" \
5030 "minutes|minute|mins|min|m|" \
5031 "seconds|second|secs|sec|s))+|" \
5032 /* Textual time units plus (dd)* hh[:mm[:ss]] */ \
5033 "((([-+]?[ \\t]+)?" \
5034 "[0-9]+(\\.[0-9]*)?[ \\t]*" \
5035 "(millenniums|millennia|millennium|mil|mils|" \
5036 "centuries|century|cent|c|" \
5037 "decades|decade|dec|decs|" \
5038 "years|year|y|" \
5039 "months|month|mon|" \
5040 "weeks|week|w|" \
5041 "days|day|d))+" \
5042 "([-+]?[ \\t]+" \
5043 "([0-9]+[ \\t]+)+" /* dd */ \
5044 "(([0-9]{1,2}:){0,2}[0-9]{0,2})" /* hh:[mm:[ss]] */ \
5045 ")?))" \
5046 "([ \\t]+ago)?$"
5047
5048 if (php_pgsql_convert_match(Z_STR_P(val), REGEX0, sizeof(REGEX0)-1, 1) == FAILURE) {
5049 err = 1;
5050 }
5051 else {
5052 ZVAL_STR(&new_val, php_pgsql_add_quotes(Z_STR_P(val)));
5053 }
5054 #undef REGEX0
5055 }
5056 break;
5057
5058 case IS_NULL:
5059 ZVAL_STR(&new_val, ZSTR_KNOWN(ZEND_STR_NULL));
5060 break;
5061
5062 default:
5063 err = 1;
5064 }
5065 PGSQL_CONV_CHECK_IGNORE();
5066 if (err) {
5067 zend_type_error("%s(): Field \"%s\" must be of type string|null, %s given", get_active_function_name(), ZSTR_VAL(field), Z_STRVAL_P(type));
5068 }
5069 break;
5070 case PG_BYTEA:
5071 switch (Z_TYPE_P(val)) {
5072 case IS_STRING:
5073 if (Z_STRLEN_P(val) == 0) {
5074 ZVAL_STR(&new_val, ZSTR_KNOWN(ZEND_STR_NULL));
5075 }
5076 else {
5077 unsigned char *tmp;
5078 size_t to_len;
5079 zend_string *tmp_zstr;
5080
5081 tmp = PQescapeByteaConn(pg_link, (unsigned char *)Z_STRVAL_P(val), Z_STRLEN_P(val), &to_len);
5082 tmp_zstr = zend_string_init((char *)tmp, to_len - 1, false); /* PQescapeBytea's to_len includes additional '\0' */
5083 PQfreemem(tmp);
5084
5085 ZVAL_STR(&new_val, php_pgsql_add_quotes(tmp_zstr));
5086 zend_string_release_ex(tmp_zstr, false);
5087 }
5088 break;
5089
5090 case IS_LONG:
5091 ZVAL_STR(&new_val, zend_long_to_str(Z_LVAL_P(val)));
5092 break;
5093
5094 case IS_DOUBLE:
5095 ZVAL_DOUBLE(&new_val, Z_DVAL_P(val));
5096 convert_to_string(&new_val);
5097 break;
5098
5099 case IS_NULL:
5100 ZVAL_STR(&new_val, ZSTR_KNOWN(ZEND_STR_NULL));
5101 break;
5102
5103 default:
5104 err = 1;
5105 }
5106 PGSQL_CONV_CHECK_IGNORE();
5107 if (err) {
5108 zend_type_error("%s(): Field \"%s\" must be of type string|null, %s given", get_active_function_name(), ZSTR_VAL(field), Z_STRVAL_P(type));
5109 }
5110 break;
5111
5112 case PG_MACADDR:
5113 switch(Z_TYPE_P(val)) {
5114 case IS_STRING:
5115 if (Z_STRLEN_P(val) == 0) {
5116 ZVAL_STR(&new_val, ZSTR_KNOWN(ZEND_STR_NULL));
5117 }
5118 else {
5119 #define REGEX0 "^([0-9a-f]{2,2}:){5,5}[0-9a-f]{2,2}$"
5120 if (php_pgsql_convert_match(Z_STR_P(val), REGEX0, sizeof(REGEX0)-1, 1) == FAILURE) {
5121 err = 1;
5122 }
5123 else {
5124 ZVAL_STR(&new_val, php_pgsql_add_quotes(Z_STR_P(val)));
5125 }
5126 #undef REGEX0
5127 }
5128 break;
5129
5130 case IS_NULL:
5131 ZVAL_STR(&new_val, ZSTR_KNOWN(ZEND_STR_NULL));
5132 break;
5133
5134 default:
5135 err = 1;
5136 }
5137 PGSQL_CONV_CHECK_IGNORE();
5138 if (err) {
5139 zend_type_error("%s(): Field \"%s\" must be of type string|null, %s given", get_active_function_name(), ZSTR_VAL(field), Z_STRVAL_P(type));
5140 }
5141 break;
5142
5143 default:
5144 /* should not happen */
5145 php_error_docref(NULL, E_NOTICE, "Unknown or system data type '%s' for '%s'. Report error", Z_STRVAL_P(type), ZSTR_VAL(field));
5146 err = 1;
5147 break;
5148 } /* switch */
5149
5150 if (err) {
5151 zval_ptr_dtor(&new_val);
5152 break; /* break out for() */
5153 }
5154 /* If field is NULL and HAS DEFAULT, should be skipped */
5155 if (!skip_field) {
5156 if (_php_pgsql_identifier_is_escaped(ZSTR_VAL(field), ZSTR_LEN(field))) {
5157 zend_hash_update(Z_ARRVAL_P(result), field, &new_val);
5158 } else {
5159 char *escaped = PQescapeIdentifier(pg_link, ZSTR_VAL(field), ZSTR_LEN(field));
5160 add_assoc_zval(result, escaped, &new_val);
5161 PQfreemem(escaped);
5162 }
5163 }
5164 } ZEND_HASH_FOREACH_END(); /* for */
5165
5166 zval_ptr_dtor(&meta);
5167
5168 if (err) {
5169 /* shouldn't destroy & free zval here */
5170 return FAILURE;
5171 }
5172 return SUCCESS;
5173 }
5174 /* }}} */
5175
5176 /* {{{ Check and convert values for PostgreSQL SQL statement */
PHP_FUNCTION(pg_convert)5177 PHP_FUNCTION(pg_convert)
5178 {
5179 zval *pgsql_link, *values;
5180 pgsql_link_handle *link;
5181 zend_string *table_name;
5182 zend_ulong option = 0;
5183 PGconn *pg_link;
5184
5185 if (zend_parse_parameters(ZEND_NUM_ARGS(), "OPa|l", &pgsql_link, pgsql_link_ce, &table_name, &values, &option) == FAILURE) {
5186 RETURN_THROWS();
5187 }
5188
5189 if (ZSTR_LEN(table_name) == 0) {
5190 zend_argument_value_error(2, "cannot be empty");
5191 RETURN_THROWS();
5192 }
5193
5194 if (option & ~PGSQL_CONV_OPTS) {
5195 zend_argument_value_error(4, "must be a valid bit mask of PGSQL_CONV_IGNORE_DEFAULT, "
5196 "PGSQL_CONV_FORCE_NULL, and PGSQL_CONV_IGNORE_NOT_NULL");
5197 RETURN_THROWS();
5198 }
5199
5200 link = Z_PGSQL_LINK_P(pgsql_link);
5201 CHECK_PGSQL_LINK(link);
5202 pg_link = link->conn;
5203
5204 if (php_pgsql_flush_query(pg_link)) {
5205 php_error_docref(NULL, E_NOTICE, "Detected unhandled result(s) in connection");
5206 }
5207 array_init(return_value);
5208 if (php_pgsql_convert(pg_link, table_name, values, return_value, option) == FAILURE) {
5209 zend_array_destroy(Z_ARR_P(return_value));
5210 RETURN_FALSE;
5211 }
5212 }
5213 /* }}} */
5214
do_exec(smart_str * querystr,ExecStatusType expect,PGconn * pg_link,zend_ulong opt)5215 static bool do_exec(smart_str *querystr, ExecStatusType expect, PGconn *pg_link, zend_ulong opt) /* {{{ */
5216 {
5217 if (opt & PGSQL_DML_ASYNC) {
5218 if (PQsendQuery(pg_link, ZSTR_VAL(querystr->s))) {
5219 return true;
5220 }
5221 } else {
5222 PGresult *pg_result;
5223
5224 pg_result = PQexec(pg_link, ZSTR_VAL(querystr->s));
5225 if (PQresultStatus(pg_result) == expect) {
5226 PQclear(pg_result);
5227 return true;
5228 } else {
5229 php_error_docref(NULL, E_WARNING, "%s", PQresultErrorMessage(pg_result));
5230 PQclear(pg_result);
5231 }
5232 }
5233
5234 return false;
5235 }
5236 /* }}} */
5237
build_tablename(smart_str * querystr,PGconn * pg_link,const zend_string * table)5238 static inline void build_tablename(smart_str *querystr, PGconn *pg_link, const zend_string *table) /* {{{ */
5239 {
5240 /* schema.table should be "schema"."table" */
5241 const char *dot = memchr(ZSTR_VAL(table), '.', ZSTR_LEN(table));
5242 size_t len = dot ? dot - ZSTR_VAL(table) : ZSTR_LEN(table);
5243
5244 if (_php_pgsql_identifier_is_escaped(ZSTR_VAL(table), len)) {
5245 smart_str_appendl(querystr, ZSTR_VAL(table), len);
5246 } else {
5247 char *escaped = PQescapeIdentifier(pg_link, ZSTR_VAL(table), len);
5248 smart_str_appends(querystr, escaped);
5249 PQfreemem(escaped);
5250 }
5251 if (dot) {
5252 const char *after_dot = dot + 1;
5253 len = ZSTR_LEN(table) - len - 1;
5254 /* "schema"."table" format */
5255 if (_php_pgsql_identifier_is_escaped(after_dot, len)) {
5256 smart_str_appendc(querystr, '.');
5257 smart_str_appendl(querystr, after_dot, len);
5258 } else {
5259 char *escaped = PQescapeIdentifier(pg_link, after_dot, len);
5260 smart_str_appendc(querystr, '.');
5261 smart_str_appends(querystr, escaped);
5262 PQfreemem(escaped);
5263 }
5264 }
5265 }
5266 /* }}} */
5267
5268 /* {{{ php_pgsql_insert */
php_pgsql_insert(PGconn * pg_link,const zend_string * table,zval * var_array,zend_ulong opt,zend_string ** sql)5269 PHP_PGSQL_API zend_result php_pgsql_insert(PGconn *pg_link, const zend_string *table, zval *var_array, zend_ulong opt, zend_string **sql)
5270 {
5271 zval *val, converted;
5272 char buf[256];
5273 char *tmp;
5274 smart_str querystr = {0};
5275 zend_result ret = FAILURE;
5276 zend_string *fld;
5277
5278 ZEND_ASSERT(pg_link != NULL);
5279 ZEND_ASSERT(table != NULL);
5280 ZEND_ASSERT(Z_TYPE_P(var_array) == IS_ARRAY);
5281
5282 ZVAL_UNDEF(&converted);
5283 if (zend_hash_num_elements(Z_ARRVAL_P(var_array)) == 0) {
5284 smart_str_appends(&querystr, "INSERT INTO ");
5285 build_tablename(&querystr, pg_link, table);
5286 smart_str_appends(&querystr, " DEFAULT VALUES");
5287
5288 goto no_values;
5289 }
5290
5291 /* convert input array if needed */
5292 if (!(opt & (PGSQL_DML_NO_CONV|PGSQL_DML_ESCAPE))) {
5293 array_init(&converted);
5294 if (php_pgsql_convert(pg_link, table, var_array, &converted, (opt & PGSQL_CONV_OPTS)) == FAILURE) {
5295 goto cleanup;
5296 }
5297 var_array = &converted;
5298 }
5299
5300 smart_str_appends(&querystr, "INSERT INTO ");
5301 build_tablename(&querystr, pg_link, table);
5302 smart_str_appends(&querystr, " (");
5303
5304 ZEND_HASH_FOREACH_STR_KEY(Z_ARRVAL_P(var_array), fld) {
5305 if (fld == NULL) {
5306 zend_value_error("Array of values must be an associative array with string keys");
5307 goto cleanup;
5308 }
5309 if (opt & PGSQL_DML_ESCAPE) {
5310 tmp = PQescapeIdentifier(pg_link, ZSTR_VAL(fld), ZSTR_LEN(fld) + 1);
5311 smart_str_appends(&querystr, tmp);
5312 PQfreemem(tmp);
5313 } else {
5314 smart_str_append(&querystr, fld);
5315 }
5316 smart_str_appendc(&querystr, ',');
5317 } ZEND_HASH_FOREACH_END();
5318 ZSTR_LEN(querystr.s)--;
5319 smart_str_appends(&querystr, ") VALUES (");
5320
5321 /* make values string */
5322 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(var_array), val) {
5323 /* we can avoid the key_type check here, because we tested it in the other loop */
5324 switch (Z_TYPE_P(val)) {
5325 case IS_STRING:
5326 if (opt & PGSQL_DML_ESCAPE) {
5327 size_t new_len;
5328 char *tmp;
5329 tmp = (char *)safe_emalloc(Z_STRLEN_P(val), 2, 1);
5330 new_len = PQescapeStringConn(pg_link, tmp, Z_STRVAL_P(val), Z_STRLEN_P(val), NULL);
5331 smart_str_appendc(&querystr, '\'');
5332 smart_str_appendl(&querystr, tmp, new_len);
5333 smart_str_appendc(&querystr, '\'');
5334 efree(tmp);
5335 } else {
5336 smart_str_append(&querystr, Z_STR_P(val));
5337 }
5338 break;
5339 case IS_LONG:
5340 smart_str_append_long(&querystr, Z_LVAL_P(val));
5341 break;
5342 case IS_DOUBLE:
5343 smart_str_appendl(&querystr, buf, snprintf(buf, sizeof(buf), "%F", Z_DVAL_P(val)));
5344 break;
5345 case IS_NULL:
5346 smart_str_appendl(&querystr, "NULL", sizeof("NULL")-1);
5347 break;
5348 default:
5349 zend_type_error("Value must be of type string|int|float|null, %s given", zend_zval_value_name(val));
5350 goto cleanup;
5351 }
5352 smart_str_appendc(&querystr, ',');
5353 } ZEND_HASH_FOREACH_END();
5354 /* Remove the trailing "," */
5355 ZSTR_LEN(querystr.s)--;
5356 smart_str_appends(&querystr, ");");
5357
5358 no_values:
5359
5360 smart_str_0(&querystr);
5361
5362 if ((opt & (PGSQL_DML_EXEC|PGSQL_DML_ASYNC)) &&
5363 do_exec(&querystr, PGRES_COMMAND_OK, pg_link, (opt & PGSQL_CONV_OPTS))) {
5364 ret = SUCCESS;
5365 }
5366 else if (opt & PGSQL_DML_STRING) {
5367 ret = SUCCESS;
5368 }
5369
5370 cleanup:
5371 zval_ptr_dtor(&converted);
5372 if (ret == SUCCESS && (opt & PGSQL_DML_STRING)) {
5373 *sql = querystr.s;
5374 }
5375 else {
5376 smart_str_free(&querystr);
5377 }
5378 return ret;
5379 }
5380 /* }}} */
5381
5382 /* {{{ Insert values (filed=>value) to table */
PHP_FUNCTION(pg_insert)5383 PHP_FUNCTION(pg_insert)
5384 {
5385 zval *pgsql_link, *values;
5386 pgsql_link_handle *link;
5387 zend_string *table;
5388 zend_ulong option = PGSQL_DML_EXEC, return_sql;
5389 PGconn *pg_link;
5390 PGresult *pg_result;
5391 ExecStatusType status;
5392 zend_string *sql = NULL;
5393
5394 if (zend_parse_parameters(ZEND_NUM_ARGS(), "OPa|l",
5395 &pgsql_link, pgsql_link_ce, &table, &values, &option) == FAILURE
5396 ) {
5397 RETURN_THROWS();
5398 }
5399
5400 if (ZSTR_LEN(table) == 0) {
5401 zend_argument_value_error(2, "cannot be empty");
5402 RETURN_THROWS();
5403 }
5404
5405 if (option & ~(PGSQL_CONV_OPTS|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_ASYNC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)) {
5406 zend_argument_value_error(4, "must be a valid bit mask of PGSQL_CONV_FORCE_NULL, PGSQL_DML_NO_CONV, "
5407 "PGSQL_DML_ESCAPE, PGSQL_DML_EXEC, PGSQL_DML_ASYNC, and PGSQL_DML_STRING");
5408 RETURN_THROWS();
5409 }
5410
5411 link = Z_PGSQL_LINK_P(pgsql_link);
5412 CHECK_PGSQL_LINK(link);
5413 pg_link = link->conn;
5414
5415 if (php_pgsql_flush_query(pg_link)) {
5416 php_error_docref(NULL, E_NOTICE, "Detected unhandled result(s) in connection");
5417 }
5418 return_sql = option & PGSQL_DML_STRING;
5419 if (option & PGSQL_DML_EXEC) {
5420 /* return object when executed */
5421 option = option & ~PGSQL_DML_EXEC;
5422 if (php_pgsql_insert(pg_link, table, values, option|PGSQL_DML_STRING, &sql) == FAILURE) {
5423 RETURN_FALSE;
5424 }
5425 pg_result = PQexec(pg_link, ZSTR_VAL(sql));
5426 if ((PGG(auto_reset_persistent) & 2) && PQstatus(pg_link) != CONNECTION_OK) {
5427 PQclear(pg_result);
5428 PQreset(pg_link);
5429 pg_result = PQexec(pg_link, ZSTR_VAL(sql));
5430 }
5431 efree(sql);
5432
5433 if (pg_result) {
5434 status = PQresultStatus(pg_result);
5435 } else {
5436 status = (ExecStatusType) PQstatus(pg_link);
5437 }
5438
5439 switch (status) {
5440 case PGRES_EMPTY_QUERY:
5441 case PGRES_BAD_RESPONSE:
5442 case PGRES_NONFATAL_ERROR:
5443 case PGRES_FATAL_ERROR:
5444 PHP_PQ_ERROR("Query failed: %s", pg_link);
5445 PQclear(pg_result);
5446 RETURN_FALSE;
5447 break;
5448 case PGRES_COMMAND_OK: /* successful command that did not return rows */
5449 default:
5450 if (pg_result) {
5451 object_init_ex(return_value, pgsql_result_ce);
5452 pgsql_result_handle *pg_res = Z_PGSQL_RESULT_P(return_value);
5453 pg_res->conn = pg_link;
5454 pg_res->result = pg_result;
5455 pg_res->row = 0;
5456 return;
5457 } else {
5458 PQclear(pg_result);
5459 RETURN_FALSE;
5460 }
5461 break;
5462 }
5463 } else if (php_pgsql_insert(pg_link, table, values, option, &sql) == FAILURE) {
5464 RETURN_FALSE;
5465 }
5466 if (return_sql) {
5467 RETURN_STR(sql);
5468 return;
5469 }
5470 RETURN_TRUE;
5471 }
5472 /* }}} */
5473
build_assignment_string(PGconn * pg_link,smart_str * querystr,HashTable * ht,int where_cond,const char * pad,int pad_len,zend_ulong opt)5474 static inline int build_assignment_string(PGconn *pg_link, smart_str *querystr, HashTable *ht, int where_cond, const char *pad, int pad_len, zend_ulong opt) /* {{{ */
5475 {
5476 zend_string *fld;
5477 zval *val;
5478
5479 ZEND_HASH_FOREACH_STR_KEY_VAL(ht, fld, val) {
5480 if (fld == NULL) {
5481 zend_value_error("Array of values must be an associative array with string keys");
5482 return -1;
5483 }
5484 if (opt & PGSQL_DML_ESCAPE) {
5485 char *tmp = PQescapeIdentifier(pg_link, ZSTR_VAL(fld), ZSTR_LEN(fld) + 1);
5486 smart_str_appends(querystr, tmp);
5487 PQfreemem(tmp);
5488 } else {
5489 smart_str_append(querystr, fld);
5490 }
5491 if (where_cond && (Z_TYPE_P(val) == IS_TRUE || Z_TYPE_P(val) == IS_FALSE ||
5492 (Z_TYPE_P(val) == IS_STRING && zend_string_equals_literal(Z_STR_P(val), "NULL")))) {
5493 smart_str_appends(querystr, " IS ");
5494 } else {
5495 smart_str_appendc(querystr, '=');
5496 }
5497
5498 switch (Z_TYPE_P(val)) {
5499 case IS_STRING:
5500 if (opt & PGSQL_DML_ESCAPE) {
5501 char *tmp = (char *)safe_emalloc(Z_STRLEN_P(val), 2, 1);
5502 size_t new_len = PQescapeStringConn(pg_link, tmp, Z_STRVAL_P(val), Z_STRLEN_P(val), NULL);
5503 smart_str_appendc(querystr, '\'');
5504 smart_str_appendl(querystr, tmp, new_len);
5505 smart_str_appendc(querystr, '\'');
5506 efree(tmp);
5507 } else {
5508 smart_str_append(querystr, Z_STR_P(val));
5509 }
5510 break;
5511 case IS_LONG:
5512 smart_str_append_long(querystr, Z_LVAL_P(val));
5513 break;
5514 case IS_DOUBLE: {
5515 char buf[256];
5516 smart_str_appendl(querystr, buf, MIN(snprintf(buf, sizeof(buf), "%F", Z_DVAL_P(val)), sizeof(buf) - 1));
5517 }
5518 break;
5519 case IS_NULL:
5520 smart_str_appendl(querystr, "NULL", sizeof("NULL")-1);
5521 break;
5522 default:
5523 zend_type_error("Value must be of type string|int|float|null, %s given", zend_zval_value_name(val));
5524 return -1;
5525 }
5526 smart_str_appendl(querystr, pad, pad_len);
5527 } ZEND_HASH_FOREACH_END();
5528 if (querystr->s) {
5529 ZSTR_LEN(querystr->s) -= pad_len;
5530 }
5531
5532 return 0;
5533 }
5534 /* }}} */
5535
5536 /* {{{ php_pgsql_update */
php_pgsql_update(PGconn * pg_link,const zend_string * table,zval * var_array,zval * ids_array,zend_ulong opt,zend_string ** sql)5537 PHP_PGSQL_API zend_result php_pgsql_update(PGconn *pg_link, const zend_string *table, zval *var_array, zval *ids_array, zend_ulong opt, zend_string **sql)
5538 {
5539 zval var_converted, ids_converted;
5540 smart_str querystr = {0};
5541 zend_result ret = FAILURE;
5542
5543 ZEND_ASSERT(pg_link != NULL);
5544 ZEND_ASSERT(table != NULL);
5545 ZEND_ASSERT(Z_TYPE_P(var_array) == IS_ARRAY);
5546 ZEND_ASSERT(Z_TYPE_P(ids_array) == IS_ARRAY);
5547 ZEND_ASSERT(!(opt & ~(PGSQL_CONV_OPTS|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)));
5548
5549 if (zend_hash_num_elements(Z_ARRVAL_P(var_array)) == 0
5550 || zend_hash_num_elements(Z_ARRVAL_P(ids_array)) == 0) {
5551 return FAILURE;
5552 }
5553
5554 ZVAL_UNDEF(&var_converted);
5555 ZVAL_UNDEF(&ids_converted);
5556 if (!(opt & (PGSQL_DML_NO_CONV|PGSQL_DML_ESCAPE))) {
5557 array_init(&var_converted);
5558 if (php_pgsql_convert(pg_link, table, var_array, &var_converted, (opt & PGSQL_CONV_OPTS)) == FAILURE) {
5559 goto cleanup;
5560 }
5561 var_array = &var_converted;
5562 array_init(&ids_converted);
5563 if (php_pgsql_convert(pg_link, table, ids_array, &ids_converted, (opt & PGSQL_CONV_OPTS)) == FAILURE) {
5564 goto cleanup;
5565 }
5566 ids_array = &ids_converted;
5567 }
5568
5569 smart_str_appends(&querystr, "UPDATE ");
5570 build_tablename(&querystr, pg_link, table);
5571 smart_str_appends(&querystr, " SET ");
5572
5573 if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(var_array), 0, ",", 1, opt))
5574 goto cleanup;
5575
5576 smart_str_appends(&querystr, " WHERE ");
5577
5578 if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(ids_array), 1, " AND ", sizeof(" AND ")-1, opt))
5579 goto cleanup;
5580
5581 smart_str_appendc(&querystr, ';');
5582 smart_str_0(&querystr);
5583
5584 if ((opt & PGSQL_DML_EXEC) && do_exec(&querystr, PGRES_COMMAND_OK, pg_link, opt)) {
5585 ret = SUCCESS;
5586 } else if (opt & PGSQL_DML_STRING) {
5587 ret = SUCCESS;
5588 }
5589
5590 cleanup:
5591 zval_ptr_dtor(&var_converted);
5592 zval_ptr_dtor(&ids_converted);
5593 if (ret == SUCCESS && (opt & PGSQL_DML_STRING)) {
5594 *sql = querystr.s;
5595 }
5596 else {
5597 smart_str_free(&querystr);
5598 }
5599 return ret;
5600 }
5601 /* }}} */
5602
5603 /* {{{ Update table using values (field=>value) and ids (id=>value) */
PHP_FUNCTION(pg_update)5604 PHP_FUNCTION(pg_update)
5605 {
5606 zval *pgsql_link, *values, *ids;
5607 pgsql_link_handle *link;
5608 zend_string *table;
5609 zend_ulong option = PGSQL_DML_EXEC;
5610 PGconn *pg_link;
5611 zend_string *sql = NULL;
5612
5613 if (zend_parse_parameters(ZEND_NUM_ARGS(), "OPaa|l",
5614 &pgsql_link, pgsql_link_ce, &table, &values, &ids, &option) == FAILURE
5615 ) {
5616 RETURN_THROWS();
5617 }
5618
5619 if (ZSTR_LEN(table) == 0) {
5620 zend_argument_value_error(2, "cannot be empty");
5621 RETURN_THROWS();
5622 }
5623
5624 if (option & ~(PGSQL_CONV_OPTS|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)) {
5625 zend_argument_value_error(5, "must be a valid bit mask of PGSQL_CONV_FORCE_NULL, PGSQL_DML_NO_CONV, "
5626 "PGSQL_DML_ESCAPE, PGSQL_DML_EXEC, PGSQL_DML_ASYNC, and PGSQL_DML_STRING");
5627 RETURN_THROWS();
5628 }
5629
5630 link = Z_PGSQL_LINK_P(pgsql_link);
5631 CHECK_PGSQL_LINK(link);
5632 pg_link = link->conn;
5633
5634 if (php_pgsql_flush_query(pg_link)) {
5635 php_error_docref(NULL, E_NOTICE, "Detected unhandled result(s) in connection");
5636 }
5637 if (php_pgsql_update(pg_link, table, values, ids, option, &sql) == FAILURE) {
5638 RETURN_FALSE;
5639 }
5640 if (option & PGSQL_DML_STRING) {
5641 RETURN_STR(sql);
5642 }
5643 RETURN_TRUE;
5644 }
5645 /* }}} */
5646
5647 /* {{{ php_pgsql_delete */
php_pgsql_delete(PGconn * pg_link,const zend_string * table,zval * ids_array,zend_ulong opt,zend_string ** sql)5648 PHP_PGSQL_API zend_result php_pgsql_delete(PGconn *pg_link, const zend_string *table, zval *ids_array, zend_ulong opt, zend_string **sql)
5649 {
5650 zval ids_converted;
5651 smart_str querystr = {0};
5652 zend_result ret = FAILURE;
5653
5654 ZEND_ASSERT(pg_link != NULL);
5655 ZEND_ASSERT(table != NULL);
5656 ZEND_ASSERT(Z_TYPE_P(ids_array) == IS_ARRAY);
5657 ZEND_ASSERT(!(opt & ~(PGSQL_CONV_FORCE_NULL|PGSQL_DML_EXEC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)));
5658
5659 if (zend_hash_num_elements(Z_ARRVAL_P(ids_array)) == 0) {
5660 return FAILURE;
5661 }
5662
5663 ZVAL_UNDEF(&ids_converted);
5664 if (!(opt & (PGSQL_DML_NO_CONV|PGSQL_DML_ESCAPE))) {
5665 array_init(&ids_converted);
5666 if (php_pgsql_convert(pg_link, table, ids_array, &ids_converted, (opt & PGSQL_CONV_OPTS)) == FAILURE) {
5667 goto cleanup;
5668 }
5669 ids_array = &ids_converted;
5670 }
5671
5672 smart_str_appends(&querystr, "DELETE FROM ");
5673 build_tablename(&querystr, pg_link, table);
5674 smart_str_appends(&querystr, " WHERE ");
5675
5676 if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(ids_array), 1, " AND ", sizeof(" AND ")-1, opt))
5677 goto cleanup;
5678
5679 smart_str_appendc(&querystr, ';');
5680 smart_str_0(&querystr);
5681
5682 if ((opt & PGSQL_DML_EXEC) && do_exec(&querystr, PGRES_COMMAND_OK, pg_link, opt)) {
5683 ret = SUCCESS;
5684 } else if (opt & PGSQL_DML_STRING) {
5685 ret = SUCCESS;
5686 }
5687
5688 cleanup:
5689 zval_ptr_dtor(&ids_converted);
5690 if (ret == SUCCESS && (opt & PGSQL_DML_STRING)) {
5691 *sql = querystr.s;
5692 }
5693 else {
5694 smart_str_free(&querystr);
5695 }
5696 return ret;
5697 }
5698 /* }}} */
5699
5700 /* {{{ Delete records has ids (id=>value) */
PHP_FUNCTION(pg_delete)5701 PHP_FUNCTION(pg_delete)
5702 {
5703 zval *pgsql_link, *ids;
5704 pgsql_link_handle *link;
5705 zend_string *table;
5706 zend_ulong option = PGSQL_DML_EXEC;
5707 PGconn *pg_link;
5708 zend_string *sql;
5709
5710 if (zend_parse_parameters(ZEND_NUM_ARGS(), "OPa|l",
5711 &pgsql_link, pgsql_link_ce, &table, &ids, &option
5712 ) == FAILURE) {
5713 RETURN_THROWS();
5714 }
5715
5716 if (ZSTR_LEN(table) == 0) {
5717 zend_argument_value_error(2, "cannot be empty");
5718 RETURN_THROWS();
5719 }
5720
5721 if (option & ~(PGSQL_CONV_FORCE_NULL|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)) {
5722 zend_argument_value_error(4, "must be a valid bit mask of PGSQL_CONV_FORCE_NULL, PGSQL_DML_NO_CONV, "
5723 "PGSQL_DML_ESCAPE, PGSQL_DML_EXEC, PGSQL_DML_ASYNC, and PGSQL_DML_STRING");
5724 RETURN_THROWS();
5725 }
5726
5727 link = Z_PGSQL_LINK_P(pgsql_link);
5728 CHECK_PGSQL_LINK(link);
5729 pg_link = link->conn;
5730
5731 if (php_pgsql_flush_query(pg_link)) {
5732 php_error_docref(NULL, E_NOTICE, "Detected unhandled result(s) in connection");
5733 }
5734 if (php_pgsql_delete(pg_link, table, ids, option, &sql) == FAILURE) {
5735 RETURN_FALSE;
5736 }
5737 if (option & PGSQL_DML_STRING) {
5738 RETURN_STR(sql);
5739 }
5740 RETURN_TRUE;
5741 }
5742 /* }}} */
5743
5744 /* {{{ php_pgsql_result2array */
php_pgsql_result2array(PGresult * pg_result,zval * ret_array,long result_type)5745 PHP_PGSQL_API void php_pgsql_result2array(PGresult *pg_result, zval *ret_array, long result_type)
5746 {
5747 zval row;
5748 char *field_name;
5749 size_t num_fields;
5750 int pg_numrows, pg_row;
5751 uint32_t i;
5752
5753 ZEND_ASSERT(Z_TYPE_P(ret_array) == IS_ARRAY);
5754
5755 pg_numrows = PQntuples(pg_result);
5756 for (pg_row = 0; pg_row < pg_numrows; pg_row++) {
5757 array_init(&row);
5758 for (i = 0, num_fields = PQnfields(pg_result); i < num_fields; i++) {
5759 field_name = PQfname(pg_result, i);
5760 if (PQgetisnull(pg_result, pg_row, i)) {
5761 if (result_type & PGSQL_ASSOC) {
5762 add_assoc_null(&row, field_name);
5763 }
5764 if (result_type & PGSQL_NUM) {
5765 add_next_index_null(&row);
5766 }
5767 } else {
5768 char *element = PQgetvalue(pg_result, pg_row, i);
5769 if (element) {
5770 const size_t element_len = strlen(element);
5771 if (result_type & PGSQL_ASSOC) {
5772 add_assoc_stringl(&row, field_name, element, element_len);
5773 }
5774 if (result_type & PGSQL_NUM) {
5775 add_next_index_stringl(&row, element, element_len);
5776 }
5777 }
5778 }
5779 }
5780 add_index_zval(ret_array, pg_row, &row);
5781 }
5782 }
5783 /* }}} */
5784
5785 /* {{{ php_pgsql_select */
php_pgsql_select(PGconn * pg_link,const zend_string * table,zval * ids_array,zval * ret_array,zend_ulong opt,long result_type,zend_string ** sql)5786 PHP_PGSQL_API zend_result php_pgsql_select(PGconn *pg_link, const zend_string *table, zval *ids_array, zval *ret_array, zend_ulong opt, long result_type, zend_string **sql)
5787 {
5788 zval ids_converted;
5789 smart_str querystr = {0};
5790 zend_result ret = FAILURE;
5791 PGresult *pg_result;
5792
5793 ZEND_ASSERT(pg_link != NULL);
5794 ZEND_ASSERT(table != NULL);
5795 if (ids_array) {
5796 ZEND_ASSERT(Z_TYPE_P(ids_array) == IS_ARRAY);
5797 }
5798 ZEND_ASSERT(Z_TYPE_P(ret_array) == IS_ARRAY);
5799 ZEND_ASSERT(!(opt & ~(PGSQL_CONV_OPTS|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_ASYNC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)));
5800
5801 zend_bool is_valid_ids_array = ids_array && zend_hash_num_elements(Z_ARRVAL_P(ids_array)) != 0;
5802
5803 if (is_valid_ids_array) {
5804 ZVAL_UNDEF(&ids_converted);
5805 if (!(opt & (PGSQL_DML_NO_CONV|PGSQL_DML_ESCAPE))) {
5806 array_init(&ids_converted);
5807 if (php_pgsql_convert(pg_link, table, ids_array, &ids_converted, (opt & PGSQL_CONV_OPTS)) == FAILURE) {
5808 goto cleanup;
5809 }
5810 ids_array = &ids_converted;
5811 }
5812 }
5813
5814 smart_str_appends(&querystr, "SELECT * FROM ");
5815 build_tablename(&querystr, pg_link, table);
5816
5817 if (is_valid_ids_array) {
5818 smart_str_appends(&querystr, " WHERE ");
5819 if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(ids_array), 1, " AND ", sizeof(" AND ")-1, opt)) {
5820 goto cleanup;
5821 }
5822 }
5823
5824 smart_str_appendc(&querystr, ';');
5825 smart_str_0(&querystr);
5826
5827 pg_result = PQexec(pg_link, ZSTR_VAL(querystr.s));
5828 if (PQresultStatus(pg_result) == PGRES_TUPLES_OK) {
5829 php_pgsql_result2array(pg_result, ret_array, result_type);
5830 ret = SUCCESS;
5831 } else {
5832 php_error_docref(NULL, E_NOTICE, "Failed to execute '%s'", ZSTR_VAL(querystr.s));
5833 }
5834 PQclear(pg_result);
5835
5836 cleanup:
5837 if (is_valid_ids_array) {
5838 zval_ptr_dtor(&ids_converted);
5839 }
5840 if (ret == SUCCESS && (opt & PGSQL_DML_STRING)) {
5841 *sql = querystr.s;
5842 }
5843 else {
5844 smart_str_free(&querystr);
5845 }
5846 return ret;
5847 }
5848 /* }}} */
5849
5850 /* {{{ Select records that has ids (id=>value) */
PHP_FUNCTION(pg_select)5851 PHP_FUNCTION(pg_select)
5852 {
5853 zval *pgsql_link;
5854 zval *ids = NULL;
5855 pgsql_link_handle *link;
5856 zend_string *table;
5857 zend_ulong option = PGSQL_DML_EXEC;
5858 zend_long result_type = PGSQL_ASSOC;
5859 PGconn *pg_link;
5860 zend_string *sql = NULL;
5861
5862 /* TODO Document result_type param on php.net (apparently it was added in PHP 7.1) */
5863 if (zend_parse_parameters(ZEND_NUM_ARGS(), "OP|all",
5864 &pgsql_link, pgsql_link_ce, &table, &ids, &option, &result_type
5865 ) == FAILURE) {
5866 RETURN_THROWS();
5867 }
5868
5869 if (ZSTR_LEN(table) == 0) {
5870 zend_argument_value_error(2, "cannot be empty");
5871 RETURN_THROWS();
5872 }
5873
5874 if (option & ~(PGSQL_CONV_FORCE_NULL|PGSQL_DML_NO_CONV|PGSQL_DML_EXEC|PGSQL_DML_ASYNC|PGSQL_DML_STRING|PGSQL_DML_ESCAPE)) {
5875 zend_argument_value_error(4, "must be a valid bit mask of PGSQL_CONV_FORCE_NULL, PGSQL_DML_NO_CONV, "
5876 "PGSQL_DML_ESCAPE, PGSQL_DML_EXEC, PGSQL_DML_ASYNC, and PGSQL_DML_STRING");
5877 RETURN_THROWS();
5878 }
5879 if (!(result_type & PGSQL_BOTH)) {
5880 zend_argument_value_error(5, "must be one of PGSQL_ASSOC, PGSQL_NUM, or PGSQL_BOTH");
5881 RETURN_THROWS();
5882 }
5883
5884 link = Z_PGSQL_LINK_P(pgsql_link);
5885 CHECK_PGSQL_LINK(link);
5886 pg_link = link->conn;
5887
5888 if (php_pgsql_flush_query(pg_link)) {
5889 php_error_docref(NULL, E_NOTICE, "Detected unhandled result(s) in connection");
5890 }
5891 array_init(return_value);
5892 if (php_pgsql_select(pg_link, table, ids, return_value, option, result_type, &sql) == FAILURE) {
5893 zval_ptr_dtor(return_value);
5894 RETURN_FALSE;
5895 }
5896 if (option & PGSQL_DML_STRING) {
5897 zval_ptr_dtor(return_value);
5898 RETURN_STR(sql);
5899 }
5900 return;
5901 }
5902 /* }}} */
5903
5904 #endif
5905