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