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