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