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