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