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