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