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