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