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