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