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, ¶m, &len) == FAILURE) {
909 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", ¶m, &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