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