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