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