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