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