20182c277fb7996ef0e34b35a4e0c0321be0bdea
[m6w6/ext-pq] / src / php_pq.c
1 /*
2 +--------------------------------------------------------------------+
3 | PECL :: pq |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the conditions mentioned |
7 | in the accompanying LICENSE file are met. |
8 +--------------------------------------------------------------------+
9 | Copyright (c) 2013, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
16
17 #include <php.h>
18 #include <Zend/zend_interfaces.h>
19 #include <ext/standard/info.h>
20 #include <ext/spl/spl_array.h>
21
22 #include <libpq-events.h>
23
24 #include "php_pq.h"
25
26 typedef int STATUS; /* SUCCESS/FAILURE */
27
28 /*
29 ZEND_DECLARE_MODULE_GLOBALS(pq)
30 */
31
32 const zend_function_entry pq_functions[] = {
33 {0}
34 };
35
36 /* {{{ pq_module_entry
37 */
38 zend_module_entry pq_module_entry = {
39 STANDARD_MODULE_HEADER,
40 "pq",
41 pq_functions,
42 PHP_MINIT(pq),
43 PHP_MSHUTDOWN(pq),
44 NULL,/*PHP_RINIT(pq),*/
45 NULL,/*PHP_RSHUTDOWN(pq),*/
46 PHP_MINFO(pq),
47 PHP_PQ_EXT_VERSION,
48 STANDARD_MODULE_PROPERTIES
49 };
50 /* }}} */
51
52 #ifdef COMPILE_DL_PQ
53 ZEND_GET_MODULE(pq)
54 #endif
55
56 /* {{{ PHP_INI
57 */
58 /* Remove comments and fill if you need to have entries in php.ini
59 PHP_INI_BEGIN()
60 STD_PHP_INI_ENTRY("pq.global_value", "42", PHP_INI_ALL, OnUpdateLong, global_value, zend_pq_globals, pq_globals)
61 STD_PHP_INI_ENTRY("pq.global_string", "foobar", PHP_INI_ALL, OnUpdateString, global_string, zend_pq_globals, pq_globals)
62 PHP_INI_END()
63 */
64 /* }}} */
65
66 /* {{{ php_pq_init_globals
67 */
68 /* Uncomment this function if you have INI entries
69 static void php_pq_init_globals(zend_pq_globals *pq_globals)
70 {
71 pq_globals->global_value = 0;
72 pq_globals->global_string = NULL;
73 }
74 */
75 /* }}} */
76
77 static zend_class_entry *php_pqconn_class_entry;
78 static zend_class_entry *php_pqres_class_entry;
79 static zend_class_entry *php_pqstm_class_entry;
80
81 static zend_object_handlers php_pqconn_object_handlers;
82 static zend_object_handlers php_pqres_object_handlers;
83 static zend_object_handlers php_pqstm_object_handlers;
84
85 typedef struct php_pqconn_listener {
86 zend_fcall_info fci;
87 zend_fcall_info_cache fcc;
88 } php_pqconn_listener_t;
89
90 typedef struct php_pqconn_object {
91 zend_object zo;
92 PGconn *conn;
93 int (*poller)(PGconn *);
94 HashTable listeners;
95 unsigned async:1;
96 } php_pqconn_object_t;
97
98 typedef enum php_pqres_fetch {
99 PHP_PQRES_FETCH_ARRAY,
100 PHP_PQRES_FETCH_ASSOC,
101 PHP_PQRES_FETCH_OBJECT
102 } php_pqres_fetch_t;
103
104 typedef struct php_pqres_iterator {
105 zend_object_iterator zi;
106 zval *current_val;
107 unsigned index;
108 php_pqres_fetch_t fetch_type;
109 } php_pqres_iterator_t;
110
111 typedef struct php_pqres_object {
112 zend_object zo;
113 PGresult *res;
114 php_pqres_iterator_t *iter;
115 } php_pqres_object_t;
116
117 typedef struct php_pqstm_object {
118 zend_object zo;
119 char *name;
120 zval *conn;
121 } php_pqstm_object_t;
122
123 static zend_object_iterator_funcs php_pqres_iterator_funcs;
124
125 static zend_object_iterator *php_pqres_iterator_init(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC)
126 {
127 php_pqres_iterator_t *iter;
128 zval *prop, *zfetch_type;
129
130 iter = ecalloc(1, sizeof(*iter));
131 iter->zi.funcs = &php_pqres_iterator_funcs;
132 iter->zi.data = object;
133 Z_ADDREF_P(object);
134
135 zfetch_type = prop = zend_read_property(ce, object, ZEND_STRL("fetchType"), 0 TSRMLS_CC);
136 if (Z_TYPE_P(zfetch_type) != IS_LONG) {
137 convert_to_long_ex(&zfetch_type);
138 }
139 iter->fetch_type = Z_LVAL_P(zfetch_type);
140 if (zfetch_type != prop) {
141 zval_ptr_dtor(&zfetch_type);
142 }
143 if (Z_REFCOUNT_P(prop)) {
144 zval_ptr_dtor(&prop);
145 } else {
146 zval_dtor(prop);
147 FREE_ZVAL(prop);
148 }
149
150 return (zend_object_iterator *) iter;
151 }
152
153 static void php_pqres_iterator_dtor(zend_object_iterator *i TSRMLS_DC)
154 {
155 php_pqres_iterator_t *iter = (php_pqres_iterator_t *) i;
156
157 if (iter->current_val) {
158 zval_ptr_dtor(&iter->current_val);
159 iter->current_val = NULL;
160 }
161 zval_ptr_dtor((zval **) &iter->zi.data);
162 efree(iter);
163 }
164
165 static STATUS php_pqres_iterator_valid(zend_object_iterator *i TSRMLS_DC)
166 {
167 php_pqres_iterator_t *iter = (php_pqres_iterator_t *) i;
168 php_pqres_object_t *obj = zend_object_store_get_object(iter->zi.data TSRMLS_CC);
169
170 if (PQresultStatus(obj->res) != PGRES_TUPLES_OK) {
171 return FAILURE;
172 }
173 if (PQntuples(obj->res) <= iter->index) {
174 return FAILURE;
175 }
176
177 return SUCCESS;
178 }
179
180 static zval *php_pqres_row_to_zval(PGresult *res, unsigned row, php_pqres_fetch_t fetch_type TSRMLS_DC)
181 {
182 zval *data;
183 int c, cols;
184
185 MAKE_STD_ZVAL(data);
186 if (PHP_PQRES_FETCH_OBJECT == fetch_type) {
187 object_init(data);
188 } else {
189 array_init(data);
190 }
191
192 for (c = 0, cols = PQnfields(res); c < cols; ++c) {
193 if (PQgetisnull(res, row, c)) {
194 switch (fetch_type) {
195 case PHP_PQRES_FETCH_OBJECT:
196 add_property_null(data, PQfname(res, c));
197 break;
198
199 case PHP_PQRES_FETCH_ASSOC:
200 add_assoc_null(data, PQfname(res, c));
201 break;
202
203 case PHP_PQRES_FETCH_ARRAY:
204 add_index_null(data, c);
205 break;
206 }
207 } else {
208 char *val = PQgetvalue(res, row, c);
209 int len = PQgetlength(res, row, c);
210
211 switch (fetch_type) {
212 case PHP_PQRES_FETCH_OBJECT:
213 add_property_stringl(data, PQfname(res, c), val, len, 1);
214 break;
215
216 case PHP_PQRES_FETCH_ASSOC:
217 add_assoc_stringl(data, PQfname(res, c), val, len, 1);
218 break;
219
220 case PHP_PQRES_FETCH_ARRAY:
221 add_index_stringl(data, c, val, len ,1);
222 break;
223 }
224 }
225 }
226
227 return data;
228 }
229
230 static void php_pqres_iterator_current(zend_object_iterator *i, zval ***data_ptr TSRMLS_DC)
231 {
232 php_pqres_iterator_t *iter = (php_pqres_iterator_t *) i;
233 php_pqres_object_t *obj = zend_object_store_get_object(iter->zi.data TSRMLS_CC);
234
235 if (iter->current_val) {
236 zval_ptr_dtor(&iter->current_val);
237 }
238 iter->current_val = php_pqres_row_to_zval(obj->res, iter->index, iter->fetch_type TSRMLS_CC);
239 *data_ptr = &iter->current_val;
240 }
241
242 static int php_pqres_iterator_key(zend_object_iterator *i, char **key_str, uint *key_len, ulong *key_num TSRMLS_DC)
243 {
244 php_pqres_iterator_t *iter = (php_pqres_iterator_t *) i;
245
246 *key_num = (ulong) iter->index;
247
248 return HASH_KEY_IS_LONG;
249 }
250
251 static void php_pqres_iterator_next(zend_object_iterator *i TSRMLS_DC)
252 {
253 php_pqres_iterator_t *iter = (php_pqres_iterator_t *) i;
254
255 ++iter->index;
256 }
257
258 static void php_pqres_iterator_rewind(zend_object_iterator *i TSRMLS_DC)
259 {
260 php_pqres_iterator_t *iter = (php_pqres_iterator_t *) i;
261
262 iter->index = 0;
263 }
264
265 static zend_object_iterator_funcs php_pqres_iterator_funcs = {
266 php_pqres_iterator_dtor,
267 /* check for end of iteration (FAILURE or SUCCESS if data is valid) */
268 php_pqres_iterator_valid,
269 /* fetch the item data for the current element */
270 php_pqres_iterator_current,
271 /* fetch the key for the current element (return HASH_KEY_IS_STRING or HASH_KEY_IS_LONG) (optional, may be NULL) */
272 php_pqres_iterator_key,
273 /* step forwards to next element */
274 php_pqres_iterator_next,
275 /* rewind to start of data (optional, may be NULL) */
276 php_pqres_iterator_rewind,
277 /* invalidate current value/key (optional, may be NULL) */
278 NULL
279 };
280
281 static void php_pqconn_object_free(void *o TSRMLS_DC)
282 {
283 php_pqconn_object_t *obj = o;
284
285 if (obj->conn) {
286 PQfinish(obj->conn);
287 obj->conn = NULL;
288 }
289 zend_hash_destroy(&obj->listeners);
290 zend_object_std_dtor((zend_object *) o TSRMLS_CC);
291 efree(obj);
292 }
293
294 static void php_pqres_object_free(void *o TSRMLS_DC)
295 {
296 php_pqres_object_t *obj = o;
297
298 if (obj->res) {
299 PQclear(obj->res);
300 obj->res = NULL;
301 }
302 if (obj->iter) {
303 php_pqres_iterator_dtor((zend_object_iterator *) obj->iter TSRMLS_CC);
304 obj->iter = NULL;
305 }
306 zend_object_std_dtor((zend_object *) o TSRMLS_CC);
307 efree(obj);
308 }
309
310 static void php_pqstm_object_free(void *o TSRMLS_DC)
311 {
312 php_pqstm_object_t *obj = o;
313
314 if (obj->name) {
315 efree(obj->name);
316 obj->name = NULL;
317 }
318 if (obj->conn) {
319 zval_ptr_dtor(&obj->conn);
320 obj->conn = NULL;
321 }
322 zend_object_std_dtor((zend_object *) o TSRMLS_CC);
323 efree(obj);
324 }
325
326 static zend_object_value php_pqconn_create_object_ex(zend_class_entry *ce, PGconn *conn, php_pqconn_object_t **ptr TSRMLS_DC)
327 {
328 zend_object_value ov;
329 php_pqconn_object_t *o;
330
331 o = ecalloc(1, sizeof(*o));
332 zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
333 object_properties_init((zend_object *) o, ce);
334
335 if (ptr) {
336 *ptr = o;
337 }
338
339 if (conn) {
340 o->conn = conn;
341 o->async = !PQisnonblocking(o->conn);
342 }
343
344 zend_hash_init(&o->listeners, 0, NULL, (dtor_func_t) zend_hash_destroy, 0);
345
346 ov.handle = zend_objects_store_put((zend_object *) o, NULL, php_pqconn_object_free, NULL TSRMLS_CC);
347 ov.handlers = &php_pqconn_object_handlers;
348
349 return ov;
350 }
351
352 static zend_object_value php_pqres_create_object_ex(zend_class_entry *ce, PGresult *res, php_pqres_object_t **ptr TSRMLS_DC)
353 {
354 zend_object_value ov;
355 php_pqres_object_t *o;
356
357 o = ecalloc(1, sizeof(*o));
358 zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
359 object_properties_init((zend_object *) o, ce);
360
361 if (ptr) {
362 *ptr = o;
363 }
364
365 if (res) {
366 o->res = res;
367 }
368
369 ov.handle = zend_objects_store_put((zend_object *) o, NULL, php_pqres_object_free, NULL TSRMLS_CC);
370 ov.handlers = &php_pqres_object_handlers;
371
372 return ov;
373 }
374
375 static zend_object_value php_pqstm_create_object_ex(zend_class_entry *ce, zval *conn, const char *name, php_pqstm_object_t **ptr TSRMLS_DC)
376 {
377 zend_object_value ov;
378 php_pqstm_object_t *o;
379
380 o = ecalloc(1, sizeof(*o));
381 zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
382 object_properties_init((zend_object *) o, ce);
383
384 if (ptr) {
385 *ptr = o;
386 }
387
388 if (conn) {
389 Z_ADDREF_P(conn);
390 o->conn = conn;
391 }
392
393 if (name) {
394 o->name = estrdup(name);
395 }
396
397 ov.handle = zend_objects_store_put((zend_object *) o, NULL, php_pqstm_object_free, NULL TSRMLS_CC);
398 ov.handlers = &php_pqstm_object_handlers;
399
400 return ov;
401 }
402
403 static zend_object_value php_pqconn_create_object(zend_class_entry *class_type TSRMLS_DC)
404 {
405 return php_pqconn_create_object_ex(class_type, NULL, NULL TSRMLS_CC);
406 }
407
408 static zend_object_value php_pqres_create_object(zend_class_entry *class_type TSRMLS_DC)
409 {
410 return php_pqres_create_object_ex(class_type, NULL, NULL TSRMLS_CC);
411 }
412
413 static zend_object_value php_pqstm_create_object(zend_class_entry *class_type TSRMLS_DC)
414 {
415 return php_pqstm_create_object_ex(class_type, NULL, NULL, NULL TSRMLS_CC);
416 }
417
418 static HashTable php_pqconn_object_prophandlers;
419 static HashTable php_pqres_object_prophandlers;
420 static HashTable php_pqstm_object_prophandlers;
421
422 typedef void (*php_pq_object_prophandler_func_t)(zval *object, void *o, zval *return_value TSRMLS_DC);
423
424 typedef struct php_pq_object_prophandler {
425 php_pq_object_prophandler_func_t read;
426 php_pq_object_prophandler_func_t write;
427 } php_pq_object_prophandler_t;
428
429 static void php_pqconn_object_read_status(zval *object, void *o, zval *return_value TSRMLS_DC)
430 {
431 php_pqconn_object_t *obj = o;
432
433 RETVAL_LONG(PQstatus(obj->conn));
434 }
435
436 static void php_pqconn_object_read_transaction_status(zval *object, void *o, zval *return_value TSRMLS_DC)
437 {
438 php_pqconn_object_t *obj = o;
439
440 RETVAL_LONG(PQtransactionStatus(obj->conn));
441 }
442
443 static void php_pqconn_object_read_error_message(zval *object, void *o, zval *return_value TSRMLS_DC)
444 {
445 php_pqconn_object_t *obj = o;
446 char *error = PQerrorMessage(obj->conn);
447
448 if (error) {
449 RETVAL_STRING(error, 1);
450 } else {
451 RETVAL_NULL();
452 }
453 }
454
455 static int apply_notify_listener(void *p, void *arg TSRMLS_DC)
456 {
457 php_pqconn_listener_t *listener = p;
458 PGnotify *nfy = arg;
459 zval *zpid, *zchannel, *zmessage;
460
461 MAKE_STD_ZVAL(zpid);
462 ZVAL_LONG(zpid, nfy->be_pid);
463 MAKE_STD_ZVAL(zchannel);
464 ZVAL_STRING(zchannel, nfy->relname, 1);
465 MAKE_STD_ZVAL(zmessage);
466 ZVAL_STRING(zmessage, nfy->extra, 1);
467
468 zend_fcall_info_argn(&listener->fci TSRMLS_CC, 3, &zchannel, &zmessage, &zpid);
469 zend_fcall_info_call(&listener->fci, &listener->fcc, NULL, NULL TSRMLS_CC);
470
471 zval_ptr_dtor(&zchannel);
472 zval_ptr_dtor(&zmessage);
473 zval_ptr_dtor(&zpid);
474
475 return ZEND_HASH_APPLY_KEEP;
476 }
477
478 static int apply_notify_listeners(void *p, void *arg TSRMLS_DC)
479 {
480 HashTable *listeners = p;
481
482 zend_hash_apply_with_argument(listeners, apply_notify_listener, arg TSRMLS_CC);
483
484 return ZEND_HASH_APPLY_KEEP;
485 }
486
487 static void php_pqconn_notify_listeners(zval *this_ptr, php_pqconn_object_t *obj TSRMLS_DC)
488 {
489 PGnotify *nfy;
490
491 if (!obj) {
492 obj = zend_object_store_get_object(getThis() TSRMLS_CC);
493 }
494
495 while ((nfy = PQnotifies(obj->conn))) {
496 zend_hash_apply_with_argument(&obj->listeners, apply_notify_listeners, nfy TSRMLS_CC);
497 PQfreemem(nfy);
498 }
499 }
500
501 /* FIXME: extend to types->nspname->typname */
502 #define PHP_PQ_TYPES_QUERY \
503 "select t.oid, t.* " \
504 "from pg_type t join pg_namespace n on t.typnamespace=n.oid " \
505 "where typisdefined " \
506 "and typrelid=0 " \
507 "and nspname in ('public', 'pg_catalog')"
508 static void php_pqconn_object_read_types(zval *object, void *o, zval *return_value TSRMLS_DC)
509 {
510 php_pqconn_object_t *obj = o;
511 PGresult *res = PQexec(obj->conn, PHP_PQ_TYPES_QUERY);
512
513 php_pqconn_notify_listeners(object, obj TSRMLS_CC);
514
515 /* FIXME: cache that */
516 if (res) {
517 if (PGRES_TUPLES_OK == PQresultStatus(res)) {
518 int r, rows;
519 zval *byoid, *byname;
520
521 MAKE_STD_ZVAL(byoid);
522 MAKE_STD_ZVAL(byname);
523 object_init(byoid);
524 object_init(byname);
525 object_init(return_value);
526 for (r = 0, rows = PQntuples(res); r < rows; ++r) {
527 zval *row = php_pqres_row_to_zval(res, r, PHP_PQRES_FETCH_OBJECT TSRMLS_CC);
528
529 add_property_zval(byoid, PQgetvalue(res, r, 0), row);
530 add_property_zval(byname, PQgetvalue(res, r, 1), row);
531 zval_ptr_dtor(&row);
532 }
533
534 add_property_zval(return_value, "byOid", byoid);
535 add_property_zval(return_value, "byName", byname);
536 zval_ptr_dtor(&byoid);
537 zval_ptr_dtor(&byname);
538 } else {
539 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not fetch types: %s", PQresultErrorMessage(res));
540 }
541 PQclear(res);
542 } else {
543 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not fetch types: %s", PQerrorMessage(obj->conn));
544 }
545 }
546
547 static void php_pqres_object_read_status(zval *object, void *o, zval *return_value TSRMLS_DC)
548 {
549 php_pqres_object_t *obj = o;
550
551 RETVAL_LONG(PQresultStatus(obj->res));
552 }
553
554 static void php_pqres_object_read_error_message(zval *object, void *o, zval *return_value TSRMLS_DC)
555 {
556 php_pqres_object_t *obj = o;
557 char *error = PQresultErrorMessage(obj->res);
558
559 if (error) {
560 RETVAL_STRING(error, 1);
561 } else {
562 RETVAL_NULL();
563 }
564 }
565
566 static void php_pqres_object_read_num_rows(zval *object, void *o, zval *return_value TSRMLS_DC)
567 {
568 php_pqres_object_t *obj = o;
569
570 RETVAL_LONG(PQntuples(obj->res));
571 }
572
573 static void php_pqres_object_read_num_cols(zval *object, void *o, zval *return_value TSRMLS_DC)
574 {
575 php_pqres_object_t *obj = o;
576
577 RETVAL_LONG(PQnfields(obj->res));
578 }
579
580 static void php_pqres_object_read_affected_rows(zval *object, void *o, zval *return_value TSRMLS_DC)
581 {
582 php_pqres_object_t *obj = o;
583
584 RETVAL_LONG(atoi(PQcmdTuples(obj->res)));
585 }
586
587 static void php_pqres_object_read_fetch_type(zval *object, void *o, zval *return_value TSRMLS_DC)
588 {
589 php_pqres_object_t *obj = o;
590
591 if (obj->iter) {
592 RETVAL_LONG(obj->iter->fetch_type);
593 } else {
594 RETVAL_LONG(PHP_PQRES_FETCH_ARRAY);
595 }
596 }
597
598 static void php_pqres_object_write_fetch_type(zval *object, void *o, zval *value TSRMLS_DC)
599 {
600 php_pqres_object_t *obj = o;
601 zval *zfetch_type = value;
602
603 if (Z_TYPE_P(zfetch_type) != IS_LONG) {
604 convert_to_long_ex(&zfetch_type);
605 }
606
607 obj->iter->fetch_type = Z_LVAL_P(zfetch_type);
608
609 if (zfetch_type != value) {
610 zval_ptr_dtor(&zfetch_type);
611 }
612 }
613
614 static void php_pqstm_object_read_name(zval *object, void *o, zval *return_value TSRMLS_DC)
615 {
616 php_pqstm_object_t *obj = o;
617
618 RETVAL_STRING(obj->name, 1);
619 }
620
621 static void php_pqstm_object_read_connection(zval *object, void *o, zval *return_value TSRMLS_DC)
622 {
623 php_pqstm_object_t *obj = o;
624
625 RETVAL_ZVAL(obj->conn, 1, 0);
626 }
627
628 static zval *php_pqconn_object_read_prop(zval *object, zval *member, int type, const zend_literal *key TSRMLS_DC)
629 {
630 php_pqconn_object_t *obj = zend_object_store_get_object(object TSRMLS_CC);
631 php_pq_object_prophandler_t *handler;
632 zval *return_value;
633
634 if (!obj->conn) {
635 zend_error(E_WARNING, "Connection not initialized");
636 } else if ((SUCCESS == zend_hash_find(&php_pqconn_object_prophandlers, Z_STRVAL_P(member), Z_STRLEN_P(member)+1, (void *) &handler)) && handler->read) {
637 if (type == BP_VAR_R) {
638 ALLOC_ZVAL(return_value);
639 Z_SET_REFCOUNT_P(return_value, 0);
640 Z_UNSET_ISREF_P(return_value);
641
642 handler->read(object, obj, return_value TSRMLS_CC);
643 } else {
644 zend_error(E_ERROR, "Cannot access pq\\Connection properties by reference or array key/index");
645 return_value = NULL;
646 }
647 } else {
648 return_value = zend_get_std_object_handlers()->read_property(object, member, type, key TSRMLS_CC);
649 }
650
651 return return_value;
652 }
653
654 static void php_pqconn_object_write_prop(zval *object, zval *member, zval *value, const zend_literal *key TSRMLS_DC)
655 {
656 php_pqconn_object_t *obj = zend_object_store_get_object(object TSRMLS_CC);
657 php_pq_object_prophandler_t *handler;
658
659 if (SUCCESS == zend_hash_find(&php_pqconn_object_prophandlers, Z_STRVAL_P(member), Z_STRLEN_P(member)+1, (void *) &handler)) {
660 if (handler->write) {
661 handler->write(object, obj, value TSRMLS_CC);
662 }
663 } else {
664 zend_get_std_object_handlers()->write_property(object, member, value, key TSRMLS_CC);
665 }
666 }
667
668 static zval *php_pqres_object_read_prop(zval *object, zval *member, int type, const zend_literal *key TSRMLS_DC)
669 {
670 php_pqres_object_t *obj = zend_object_store_get_object(object TSRMLS_CC);
671 php_pq_object_prophandler_t *handler;
672 zval *return_value;
673
674 if (!obj->res) {
675 zend_error(E_WARNING, "Result not initialized");
676 } else if (SUCCESS == zend_hash_find(&php_pqres_object_prophandlers, Z_STRVAL_P(member), Z_STRLEN_P(member)+1, (void *) &handler)) {
677 if (type == BP_VAR_R) {
678 ALLOC_ZVAL(return_value);
679 Z_SET_REFCOUNT_P(return_value, 0);
680 Z_UNSET_ISREF_P(return_value);
681
682 handler->read(object, obj, return_value TSRMLS_CC);
683 } else {
684 zend_error(E_ERROR, "Cannot access pq\\Result properties by reference or array key/index");
685 return_value = NULL;
686 }
687 } else {
688 return_value = zend_get_std_object_handlers()->read_property(object, member, type, key TSRMLS_CC);
689 }
690
691 return return_value;
692 }
693
694 static void php_pqres_object_write_prop(zval *object, zval *member, zval *value, const zend_literal *key TSRMLS_DC)
695 {
696 php_pqres_object_t *obj = zend_object_store_get_object(object TSRMLS_CC);
697 php_pq_object_prophandler_t *handler;
698
699 if (!obj->res) {
700 zend_error(E_WARNING, "Result not initialized");
701 } else if (SUCCESS == zend_hash_find(&php_pqres_object_prophandlers, Z_STRVAL_P(member), Z_STRLEN_P(member)+1, (void *) &handler)) {
702 if (handler->write) {
703 /* ensure obj->iter is initialized, for e.g. write_fetch_type */
704 if (!obj->iter) {
705 obj->iter = (php_pqres_iterator_t *) php_pqres_iterator_init(Z_OBJCE_P(object), object, 0 TSRMLS_CC);
706 obj->iter->zi.funcs->rewind((zend_object_iterator *) obj->iter TSRMLS_CC);
707 }
708 handler->write(object, obj, value TSRMLS_CC);
709 }
710 } else {
711 zend_get_std_object_handlers()->write_property(object, member, value, key TSRMLS_CC);
712 }
713 }
714
715 static zval *php_pqstm_object_read_prop(zval *object, zval *member, int type, const zend_literal *key TSRMLS_DC)
716 {
717 php_pqstm_object_t *obj = zend_object_store_get_object(object TSRMLS_CC);
718 php_pq_object_prophandler_t *handler;
719 zval *return_value;
720
721 if (!obj->conn) {
722 zend_error(E_WARNING, "Statement not initialized");
723 } else if (SUCCESS == zend_hash_find(&php_pqstm_object_prophandlers, Z_STRVAL_P(member), Z_STRLEN_P(member)+1, (void *) &handler)) {
724 if (type == BP_VAR_R) {
725 ALLOC_ZVAL(return_value);
726 Z_SET_REFCOUNT_P(return_value, 0);
727 Z_UNSET_ISREF_P(return_value);
728
729 handler->read(object, obj, return_value TSRMLS_CC);
730 } else {
731 zend_error(E_ERROR, "Cannot access pq\\Statement properties by reference or array key/index");
732 return_value = NULL;
733 }
734 } else {
735 return_value = zend_get_std_object_handlers()->read_property(object, member, type, key TSRMLS_CC);
736 }
737
738 return return_value;
739 }
740
741 static void php_pqstm_object_write_prop(zval *object, zval *member, zval *value, const zend_literal *key TSRMLS_DC)
742 {
743 php_pqstm_object_t *obj = zend_object_store_get_object(object TSRMLS_CC);
744 php_pq_object_prophandler_t *handler;
745
746 if (!obj->conn) {
747 zend_error(E_WARNING, "Result not initialized");
748 } else if (SUCCESS == zend_hash_find(&php_pqstm_object_prophandlers, Z_STRVAL_P(member), Z_STRLEN_P(member)+1, (void *) &handler)) {
749 if (handler->write) {
750 handler->write(object, obj, value TSRMLS_CC);
751 }
752 } else {
753 zend_get_std_object_handlers()->write_property(object, member, value, key TSRMLS_CC);
754 }
755 }
756
757 static STATUS php_pqconn_update_socket(zval *this_ptr, php_pqconn_object_t *obj TSRMLS_DC)
758 {
759 zval *zsocket, zmember;
760 php_stream *stream;
761 STATUS retval;
762 int socket;
763
764 if (!obj) {
765 obj = zend_object_store_get_object(getThis() TSRMLS_CC);
766 }
767
768 INIT_PZVAL(&zmember);
769 ZVAL_STRINGL(&zmember, "socket", sizeof("socket")-1, 0);
770 MAKE_STD_ZVAL(zsocket);
771
772 if ((CONNECTION_BAD != PQstatus(obj->conn))
773 && (-1 < (socket = PQsocket(obj->conn)))
774 && (stream = php_stream_fopen_from_fd(socket, "r+b", NULL))) {
775 php_stream_to_zval(stream, zsocket);
776 retval = SUCCESS;
777 } else {
778 ZVAL_NULL(zsocket);
779 retval = FAILURE;
780 }
781 zend_get_std_object_handlers()->write_property(getThis(), &zmember, zsocket, NULL TSRMLS_CC);
782 zval_ptr_dtor(&zsocket);
783
784 return retval;
785 }
786
787 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_construct, 0, 0, 1)
788 ZEND_ARG_INFO(0, dsn)
789 ZEND_ARG_INFO(0, async)
790 ZEND_END_ARG_INFO();
791 static PHP_METHOD(pqconn, __construct) {
792 zend_error_handling zeh;
793 char *dsn_str;
794 int dsn_len;
795 zend_bool async = 0;
796
797 zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC);
798 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &dsn_str, &dsn_len, &async)) {
799 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
800
801 if (obj->conn) {
802 PQfinish(obj->conn);
803 }
804 if ((obj->async = async)) {
805 obj->conn = PQconnectStart(dsn_str);
806 obj->poller = (int (*)(PGconn*)) PQconnectPoll;
807 } else {
808 obj->conn = PQconnectdb(dsn_str);
809 }
810
811 if (SUCCESS != php_pqconn_update_socket(getThis(), obj TSRMLS_CC)) {
812 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Connection failed: %s", PQerrorMessage(obj->conn));
813 }
814 }
815 zend_restore_error_handling(&zeh TSRMLS_CC);
816 }
817
818 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_reset, 0, 0, 0)
819 ZEND_END_ARG_INFO();
820 static PHP_METHOD(pqconn, reset) {
821 if (SUCCESS == zend_parse_parameters_none()) {
822 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
823
824 if (obj->conn) {
825 if (obj->async) {
826 if (PQresetStart(obj->conn)) {
827 obj->poller = (int (*)(PGconn*)) PQresetPoll;
828 RETURN_TRUE;
829 }
830 } else {
831 PQreset(obj->conn);
832
833 if (CONNECTION_OK == PQstatus(obj->conn)) {
834 RETURN_TRUE;
835 } else {
836 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Connection reset failed: %s", PQerrorMessage(obj->conn));
837 }
838 }
839 } else {
840 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Connection not initialized");
841 }
842 RETURN_FALSE;
843 }
844 }
845
846 static void listener_dtor(void *l) {
847 php_pqconn_listener_t *listener = l;
848
849 zend_fcall_info_args_clear(&listener->fci, 1);
850
851 zval_ptr_dtor(&listener->fci.function_name);
852 if (listener->fci.object_ptr) {
853 zval_ptr_dtor(&listener->fci.object_ptr);
854 }
855 }
856
857 static void php_pqconn_add_listener(php_pqconn_object_t *obj, const char *channel_str, size_t channel_len, php_pqconn_listener_t *listener TSRMLS_DC)
858 {
859 HashTable ht, *existing_listeners;
860
861 Z_ADDREF_P(listener->fci.function_name);
862 if (listener->fci.object_ptr) {
863 Z_ADDREF_P(listener->fci.object_ptr);
864 }
865 if (SUCCESS == zend_hash_find(&obj->listeners, channel_str, channel_len + 1, (void *) &existing_listeners)) {
866 zend_hash_next_index_insert(existing_listeners, (void *) listener, sizeof(*listener), NULL);
867 } else {
868 zend_hash_init(&ht, 1, NULL, (dtor_func_t) listener_dtor, 0);
869 zend_hash_next_index_insert(&ht, (void *) listener, sizeof(*listener), NULL);
870 zend_hash_add(&obj->listeners, channel_str, channel_len + 1, (void *) &ht, sizeof(HashTable), NULL);
871 }
872 }
873
874 static STATUS php_pqres_success(PGresult *res TSRMLS_DC)
875 {
876 switch (PQresultStatus(res)) {
877 case PGRES_BAD_RESPONSE:
878 case PGRES_NONFATAL_ERROR:
879 case PGRES_FATAL_ERROR:
880 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", PQresultErrorMessage(res));
881 return FAILURE;
882 default:
883 return SUCCESS;
884 }
885 }
886
887 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_listen, 0, 0, 0)
888 ZEND_ARG_INFO(0, channel)
889 ZEND_ARG_INFO(0, callable)
890 ZEND_END_ARG_INFO();
891 static PHP_METHOD(pqconn, listen) {
892 char *channel_str = NULL;
893 int channel_len = 0;
894 php_pqconn_listener_t listener;
895
896 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sf", &channel_str, &channel_len, &listener.fci, &listener.fcc)) {
897 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
898
899 obj->poller = PQconsumeInput;
900
901 if (obj->conn) {
902 PGresult *res;
903 char cmd[1024];
904
905 slprintf(cmd, sizeof(cmd), "LISTEN %s", channel_str);
906 res = PQexec(obj->conn, cmd);
907
908 if (res) {
909 if (SUCCESS == php_pqres_success(res TSRMLS_CC)) {
910 php_pqconn_add_listener(obj, channel_str, channel_len, &listener TSRMLS_CC);
911 RETVAL_TRUE;
912 } else {
913 RETVAL_FALSE;
914 }
915 PQclear(res);
916 } else {
917 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not install listener: %s", PQerrorMessage(obj->conn));
918 RETVAL_FALSE;
919 }
920
921 php_pqconn_notify_listeners(getThis(), obj TSRMLS_CC);
922
923 } else {
924 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Connection not initialized");
925 RETVAL_FALSE;
926 }
927 }
928
929 }
930
931 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_notify, 0, 0, 2)
932 ZEND_ARG_INFO(0, channel)
933 ZEND_ARG_INFO(0, message)
934 ZEND_END_ARG_INFO();
935 static PHP_METHOD(pqconn, notify) {
936 char *channel_str, *message_str;
937 int channel_len, message_len;
938
939 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &channel_str, &channel_len, &message_str, &message_len)) {
940 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
941
942 if (obj->conn) {
943 PGresult *res;
944 char *params[2] = {channel_str, message_str};
945
946 res = PQexecParams(obj->conn, "select pg_notify($1, $2)", 2, NULL, (const char *const*) params, NULL, NULL, 0);
947
948 if (res) {
949 if (SUCCESS == php_pqres_success(res TSRMLS_CC)) {
950 RETVAL_TRUE;
951 } else {
952 RETVAL_FALSE;
953 }
954 PQclear(res);
955 } else {
956 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not notify listeners: %s", PQerrorMessage(obj->conn));
957 RETVAL_FALSE;
958 }
959
960 php_pqconn_notify_listeners(getThis(), obj TSRMLS_CC);
961
962 } else {
963 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Connection not initialized");
964 RETVAL_FALSE;
965 }
966 }
967 }
968
969 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_poll, 0, 0, 0)
970 ZEND_END_ARG_INFO();
971 static PHP_METHOD(pqconn, poll) {
972 if (SUCCESS == zend_parse_parameters_none()) {
973 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
974
975 if (obj->conn) {
976 if (obj->poller) {
977 if (obj->poller == PQconsumeInput) {
978 RETVAL_LONG(obj->poller(obj->conn) * PGRES_POLLING_OK);
979 php_pqconn_notify_listeners(getThis(), obj TSRMLS_CC);
980 return;
981 } else {
982 RETURN_LONG(obj->poller(obj->conn));
983 }
984 } else {
985 php_error_docref(NULL TSRMLS_CC, E_WARNING, "No asynchronous operation active");
986 }
987 } else {
988 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Connection not initialized");
989 }
990 RETURN_FALSE;
991 }
992 }
993
994 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_exec, 0, 0, 1)
995 ZEND_ARG_INFO(0, query)
996 ZEND_END_ARG_INFO();
997 static PHP_METHOD(pqconn, exec) {
998 zend_error_handling zeh;
999 char *query_str;
1000 int query_len;
1001
1002 zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC);
1003 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &query_str, &query_len)) {
1004 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
1005
1006 if (obj->conn) {
1007 PGresult *res = PQexec(obj->conn, query_str);
1008
1009 php_pqconn_notify_listeners(getThis(), obj TSRMLS_CC);
1010
1011 if (res) {
1012 if (SUCCESS == php_pqres_success(res TSRMLS_CC)) {
1013 return_value->type = IS_OBJECT;
1014 return_value->value.obj = php_pqres_create_object_ex(php_pqres_class_entry, res, NULL TSRMLS_CC);
1015 }
1016 } else {
1017 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not execute query: %s", PQerrorMessage(obj->conn));
1018 }
1019 } else {
1020 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Connection not initialized");
1021 }
1022 }
1023 zend_restore_error_handling(&zeh TSRMLS_CC);
1024 }
1025
1026 static int apply_to_oid(void *p, void *arg TSRMLS_DC)
1027 {
1028 Oid **types = arg;
1029 zval **ztype = p;
1030
1031 if (Z_TYPE_PP(ztype) != IS_LONG) {
1032 convert_to_long_ex(ztype);
1033 }
1034
1035 **types = Z_LVAL_PP(ztype);
1036 ++*types;
1037
1038 if (*ztype != *(zval **)p) {
1039 zval_ptr_dtor(ztype);
1040 }
1041 return ZEND_HASH_APPLY_KEEP;
1042 }
1043
1044 static int apply_to_param(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key)
1045 {
1046 char ***params;
1047 HashTable *zdtor;
1048 zval **zparam = p;
1049
1050 params = (char ***) va_arg(argv, char ***);
1051 zdtor = (HashTable *) va_arg(argv, HashTable *);
1052
1053 if (Z_TYPE_PP(zparam) == IS_NULL) {
1054 **params = NULL;
1055 ++*params;
1056 } else {
1057 if (Z_TYPE_PP(zparam) != IS_STRING) {
1058 convert_to_string_ex(zparam);
1059 }
1060
1061 **params = Z_STRVAL_PP(zparam);
1062 ++*params;
1063
1064 if (*zparam != *(zval **)p) {
1065 zend_hash_next_index_insert(zdtor, zparam, sizeof(zval *), NULL);
1066 }
1067 }
1068 return ZEND_HASH_APPLY_KEEP;
1069 }
1070
1071 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_exec_params, 0, 0, 2)
1072 ZEND_ARG_INFO(0, query)
1073 ZEND_ARG_ARRAY_INFO(0, params, 0)
1074 ZEND_ARG_ARRAY_INFO(0, types, 1)
1075 ZEND_END_ARG_INFO();
1076 static PHP_METHOD(pqconn, execParams) {
1077 zend_error_handling zeh;
1078 char *query_str;
1079 int query_len;
1080 zval *zparams;
1081 zval *ztypes = NULL;
1082
1083 zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC);
1084 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa/|a/!", &query_str, &query_len, &zparams, &ztypes)) {
1085 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
1086
1087 if (obj->conn) {
1088 PGresult *res;
1089 int count = 0;
1090 Oid *types = NULL;
1091 char **params = NULL;
1092 HashTable zdtor;
1093
1094 ZEND_INIT_SYMTABLE(&zdtor);
1095
1096 if (ztypes && zend_hash_num_elements(Z_ARRVAL_P(ztypes))) {
1097 Oid *tmp;
1098
1099 tmp = types = ecalloc(zend_hash_num_elements(Z_ARRVAL_P(ztypes)), sizeof(Oid));
1100 zend_hash_apply_with_argument(Z_ARRVAL_P(ztypes), apply_to_oid, &tmp TSRMLS_CC);
1101 }
1102 if ((count = zend_hash_num_elements(Z_ARRVAL_P(zparams)))) {
1103 char **tmp;
1104
1105 tmp = params = ecalloc(zend_hash_num_elements(Z_ARRVAL_P(zparams)), sizeof(char *));
1106 zend_hash_apply_with_arguments(Z_ARRVAL_P(zparams) TSRMLS_CC, apply_to_param, 2, &tmp, &zdtor);
1107 }
1108
1109 res = PQexecParams(obj->conn, query_str, count, types, (const char *const*) params, NULL, NULL, 0);
1110
1111 zend_hash_destroy(&zdtor);
1112 if (types) {
1113 efree(types);
1114 }
1115 if (params) {
1116 efree(params);
1117 }
1118
1119 php_pqconn_notify_listeners(getThis(), obj TSRMLS_CC);
1120
1121 if (res) {
1122 if (SUCCESS == php_pqres_success(res TSRMLS_CC)) {
1123 return_value->type = IS_OBJECT;
1124 return_value->value.obj = php_pqres_create_object_ex(php_pqres_class_entry, res, NULL TSRMLS_CC);
1125 }
1126 } else {
1127 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not execute query: %s", PQerrorMessage(obj->conn));
1128 }
1129 } else {
1130 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Connection not initialized");
1131 }
1132 }
1133 zend_restore_error_handling(&zeh TSRMLS_CC);
1134 }
1135
1136 static STATUS php_pqconn_prepare(PGconn *conn, const char *name, const char *query, HashTable *typest TSRMLS_DC)
1137 {
1138 Oid *types = NULL;
1139 int count = 0;
1140 PGresult *res;
1141
1142 if (typest && (count = zend_hash_num_elements(typest))) {
1143 Oid *tmp;
1144
1145 tmp = types = ecalloc(count, sizeof(Oid));
1146 zend_hash_apply_with_argument(typest, apply_to_oid, &tmp TSRMLS_CC);
1147 }
1148
1149 res = PQprepare(conn, name, query, count, types);
1150
1151 if (types) {
1152 efree(types);
1153 }
1154
1155 if (res) {
1156 if (PGRES_COMMAND_OK == PQresultStatus(res)) {
1157 return SUCCESS;
1158 } else {
1159 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not prepare statement: %s", PQresultErrorMessage(res));
1160 }
1161 PQclear(res);
1162 } else {
1163 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not prepare statement: %s", PQerrorMessage(conn));
1164 }
1165 return FAILURE;
1166 }
1167
1168 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_prepare, 0, 0, 2)
1169 ZEND_ARG_INFO(0, "name")
1170 ZEND_ARG_INFO(0, "query")
1171 ZEND_ARG_ARRAY_INFO(0, "types", 1)
1172 ZEND_END_ARG_INFO();
1173 static PHP_METHOD(pqconn, prepare) {
1174 zend_error_handling zeh;
1175 zval *ztypes = NULL;
1176 char *name_str, *query_str;
1177 int name_len, *query_len;
1178
1179 zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC);
1180 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|a/!", &name_str, &name_len, &query_str, &query_len, &ztypes)) {
1181 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
1182
1183 if (obj->conn) {
1184 if (SUCCESS == php_pqconn_prepare(obj->conn, name_str, query_str, ztypes ? Z_ARRVAL_P(ztypes) : NULL TSRMLS_CC)) {
1185 return_value->type = IS_OBJECT;
1186 return_value->value.obj = php_pqstm_create_object_ex(php_pqstm_class_entry, getThis(), name_str, NULL TSRMLS_CC);
1187 }
1188 } else {
1189 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Connection not initialized");
1190 }
1191 }
1192 }
1193
1194 static zend_function_entry php_pqconn_methods[] = {
1195 PHP_ME(pqconn, __construct, ai_pqconn_construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
1196 PHP_ME(pqconn, reset, ai_pqconn_reset, ZEND_ACC_PUBLIC)
1197 PHP_ME(pqconn, poll, ai_pqconn_poll, ZEND_ACC_PUBLIC)
1198 PHP_ME(pqconn, exec, ai_pqconn_exec, ZEND_ACC_PUBLIC)
1199 PHP_ME(pqconn, execParams, ai_pqconn_exec_params, ZEND_ACC_PUBLIC)
1200 PHP_ME(pqconn, prepare, ai_pqconn_prepare, ZEND_ACC_PUBLIC)
1201 PHP_ME(pqconn, listen, ai_pqconn_listen, ZEND_ACC_PUBLIC)
1202 PHP_ME(pqconn, notify, ai_pqconn_notify, ZEND_ACC_PUBLIC)
1203 {0}
1204 };
1205
1206 static zval **php_pqres_iteration(zval *this_ptr, php_pqres_object_t *obj, php_pqres_fetch_t fetch_type TSRMLS_DC)
1207 {
1208 zval **row = NULL;
1209 php_pqres_fetch_t orig_fetch;
1210
1211 if (!obj) {
1212 obj = zend_object_store_get_object(getThis() TSRMLS_CC);
1213 }
1214
1215 if (!obj->iter) {
1216 obj->iter = (php_pqres_iterator_t *) php_pqres_iterator_init(Z_OBJCE_P(getThis()), getThis(), 0 TSRMLS_CC);
1217 obj->iter->zi.funcs->rewind((zend_object_iterator *) obj->iter TSRMLS_CC);
1218 }
1219 orig_fetch = obj->iter->fetch_type;
1220 obj->iter->fetch_type = fetch_type;
1221 if (SUCCESS == obj->iter->zi.funcs->valid((zend_object_iterator *) obj->iter TSRMLS_CC)) {
1222 obj->iter->zi.funcs->get_current_data((zend_object_iterator *) obj->iter, &row TSRMLS_CC);
1223 obj->iter->zi.funcs->move_forward((zend_object_iterator *) obj->iter TSRMLS_CC);
1224 }
1225 obj->iter->fetch_type = orig_fetch;
1226
1227 return row ? row : NULL;
1228 }
1229
1230 ZEND_BEGIN_ARG_INFO_EX(ai_pqres_fetch_row, 0, 0, 0)
1231 ZEND_ARG_INFO(0, fetch_type)
1232 ZEND_END_ARG_INFO();
1233 static PHP_METHOD(pqres, fetchRow) {
1234 zend_error_handling zeh;
1235 php_pqres_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
1236 long fetch_type = obj->iter ? obj->iter->fetch_type : PHP_PQRES_FETCH_ARRAY;
1237
1238 zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC);
1239 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &fetch_type)) {
1240 zval **row = php_pqres_iteration(getThis(), obj, fetch_type TSRMLS_CC);
1241
1242 if (row) {
1243 RETVAL_ZVAL(*row, 1, 0);
1244 } else {
1245 RETVAL_FALSE;
1246 }
1247 }
1248 zend_restore_error_handling(&zeh TSRMLS_CC);
1249 }
1250
1251 static zval **column_at(zval *row, int col TSRMLS_DC)
1252 {
1253 zval **data = NULL;
1254 HashTable *ht = HASH_OF(row);
1255 int count = zend_hash_num_elements(ht);
1256
1257 if (col < count) {
1258 zend_hash_internal_pointer_reset(ht);
1259 while (col-- > 0) {
1260 zend_hash_move_forward(ht);
1261 }
1262 zend_hash_get_current_data(ht, (void *) &data);
1263 } else {
1264 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Column index %d does excess column count %d", col, count);
1265 }
1266 return data;
1267 }
1268
1269 ZEND_BEGIN_ARG_INFO_EX(ai_pqres_fetch_col, 0, 0, 0)
1270 ZEND_ARG_INFO(0, col_num)
1271 ZEND_END_ARG_INFO();
1272 static PHP_METHOD(pqres, fetchCol) {
1273 zend_error_handling zeh;
1274 long fetch_col = 0;
1275
1276 zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC);
1277 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &fetch_col)) {
1278 php_pqres_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
1279 zval **row = php_pqres_iteration(getThis(), obj, obj->iter ? obj->iter->fetch_type : 0 TSRMLS_CC);
1280
1281 if (row) {
1282 zval **col = column_at(*row, fetch_col TSRMLS_CC);
1283
1284 if (col) {
1285 RETVAL_ZVAL(*col, 1, 0);
1286 } else {
1287 RETVAL_FALSE;
1288 }
1289 } else {
1290 RETVAL_FALSE;
1291 }
1292 }
1293 zend_restore_error_handling(&zeh TSRMLS_CC);
1294
1295 }
1296
1297 static zend_function_entry php_pqres_methods[] = {
1298 PHP_ME(pqres, fetchRow, ai_pqres_fetch_row, ZEND_ACC_PUBLIC)
1299 PHP_ME(pqres, fetchCol, ai_pqres_fetch_col, ZEND_ACC_PUBLIC)
1300 {0}
1301 };
1302
1303 ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_construct, 0, 0, 3)
1304 ZEND_ARG_OBJ_INFO(0, "Connection", "pq\\Connection", 0)
1305 ZEND_ARG_INFO(0, "name")
1306 ZEND_ARG_INFO(0, "query")
1307 ZEND_ARG_ARRAY_INFO(0, "types", 1)
1308 ZEND_END_ARG_INFO();
1309 static PHP_METHOD(pqstm, __construct) {
1310 zend_error_handling zeh;
1311 zval *zconn, *ztypes = NULL;
1312 char *name_str, *query_str;
1313 int name_len, *query_len;
1314
1315 zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC);
1316 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Oss|a/!", &zconn, php_pqconn_class_entry, &name_str, &name_len, &query_str, &query_len, &ztypes)) {
1317 php_pqstm_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
1318 php_pqconn_object_t *conn_obj = zend_object_store_get_object(getThis() TSRMLS_CC);
1319
1320 if (conn_obj->conn) {
1321 if (SUCCESS == php_pqconn_prepare(conn_obj->conn, name_str, query_str, ztypes ? Z_ARRVAL_P(ztypes) : NULL TSRMLS_CC)) {
1322 Z_ADDREF_P(zconn);
1323 obj->conn = zconn;
1324 obj->name = estrdup(name_str);
1325 }
1326 } else {
1327 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Connection not initialized");
1328 }
1329 }
1330 }
1331
1332 ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_exec, 0, 0, 0)
1333 ZEND_ARG_ARRAY_INFO(0, "params", 1)
1334 ZEND_END_ARG_INFO();
1335 static PHP_METHOD(pqstm, exec) {
1336 zend_error_handling zeh;
1337 zval *zparams = NULL;
1338
1339 zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC);
1340 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a/!", &zparams)) {
1341 php_pqstm_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
1342
1343 if (obj->conn && obj->name) {
1344 php_pqconn_object_t *conn_obj = zend_object_store_get_object(obj->conn TSRMLS_CC);
1345
1346 if (conn_obj->conn) {
1347 int count = 0;
1348 char **params = NULL;
1349 HashTable zdtor;
1350 PGresult *res;
1351
1352 ZEND_INIT_SYMTABLE(&zdtor);
1353
1354 if (zparams && (count = zend_hash_num_elements(Z_ARRVAL_P(zparams)))) {
1355 char **tmp;
1356
1357 tmp = params = ecalloc(count, sizeof(char *));
1358 zend_hash_apply_with_arguments(Z_ARRVAL_P(zparams) TSRMLS_CC, apply_to_param, 2, &tmp, &zdtor);
1359 }
1360
1361 res = PQexecPrepared(conn_obj->conn, obj->name, count, (const char *const*) params, NULL, NULL, 0);
1362
1363 if (params) {
1364 efree(params);
1365 }
1366 zend_hash_destroy(&zdtor);
1367
1368 if (res) {
1369 if (SUCCESS == php_pqres_success(res TSRMLS_CC)) {
1370 return_value->type = IS_OBJECT;
1371 return_value->value.obj = php_pqres_create_object_ex(php_pqres_class_entry, res, NULL TSRMLS_CC);
1372 }
1373 } else {
1374 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not execute statement: %s", PQerrorMessage(conn_obj->conn));
1375 }
1376 } else {
1377 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Connection not initialized");
1378 }
1379 } else {
1380 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Statement not initialized");
1381 }
1382 }
1383 zend_restore_error_handling(&zeh TSRMLS_CC);
1384 }
1385
1386 ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_desc, 0, 0, 0)
1387 ZEND_END_ARG_INFO();
1388 static PHP_METHOD(pqstm, desc) {
1389 zend_error_handling zeh;
1390
1391 zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC);
1392 if (SUCCESS == zend_parse_parameters_none()) {
1393 php_pqstm_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
1394
1395 if (obj->conn && obj->name) {
1396 php_pqconn_object_t *conn_obj = zend_object_store_get_object(obj->conn TSRMLS_CC);
1397
1398 if (conn_obj->conn) {
1399 PGresult *res = PQdescribePrepared(conn_obj->conn, obj->name);
1400
1401 if (res) {
1402 if (SUCCESS == php_pqres_success(res TSRMLS_CC)) {
1403 int p, params;
1404
1405 array_init(return_value);
1406 for (p = 0, params = PQnparams(res); p < params; ++p) {
1407 add_next_index_long(return_value, PQparamtype(res, p));
1408 }
1409 }
1410 } else {
1411 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not describe statement: %s", PQerrorMessage(conn_obj->conn));
1412 }
1413 } else {
1414 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Connection not initialized");
1415 }
1416 } else {
1417 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Statement not initialized");
1418 }
1419 }
1420 zend_restore_error_handling(&zeh TSRMLS_CC);
1421 }
1422
1423 static zend_function_entry php_pqstm_methods[] = {
1424 PHP_ME(pqstm, __construct, ai_pqstm_construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
1425 PHP_ME(pqstm, exec, ai_pqstm_exec, ZEND_ACC_PUBLIC)
1426 PHP_ME(pqstm, desc, ai_pqstm_desc, ZEND_ACC_PUBLIC)
1427 {0}
1428 };
1429
1430 /* {{{ PHP_MINIT_FUNCTION
1431 */
1432 PHP_MINIT_FUNCTION(pq)
1433 {
1434 zend_class_entry ce = {0};
1435 php_pq_object_prophandler_t ph = {0};
1436
1437 zend_hash_init(&php_pqconn_object_prophandlers, 1, NULL, NULL, 1);
1438 INIT_NS_CLASS_ENTRY(ce, "pq", "Connection", php_pqconn_methods);
1439 php_pqconn_class_entry = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC);
1440 php_pqconn_class_entry->create_object = php_pqconn_create_object;
1441 memcpy(&php_pqconn_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
1442 php_pqconn_object_handlers.read_property = php_pqconn_object_read_prop;
1443 php_pqconn_object_handlers.write_property = php_pqconn_object_write_prop;
1444 php_pqconn_object_handlers.clone_obj = NULL;
1445 php_pqconn_object_handlers.get_property_ptr_ptr = NULL;
1446
1447 zend_declare_property_long(php_pqconn_class_entry, ZEND_STRL("status"), CONNECTION_BAD, ZEND_ACC_PUBLIC TSRMLS_CC);
1448 ph.read = php_pqconn_object_read_status;
1449 zend_hash_add(&php_pqconn_object_prophandlers, "status", sizeof("status"), (void *) &ph, sizeof(ph), NULL);
1450
1451 zend_declare_property_long(php_pqconn_class_entry, ZEND_STRL("transactionStatus"), PQTRANS_UNKNOWN, ZEND_ACC_PUBLIC TSRMLS_CC);
1452 ph.read = php_pqconn_object_read_transaction_status;
1453 zend_hash_add(&php_pqconn_object_prophandlers, "transactionStatus", sizeof("transactionStatus"), (void *) &ph, sizeof(ph), NULL);
1454
1455 zend_declare_property_null(php_pqconn_class_entry, ZEND_STRL("socket"), ZEND_ACC_PUBLIC TSRMLS_CC);
1456 ph.read = NULL; /* forward to std prophandler */
1457 zend_hash_add(&php_pqconn_object_prophandlers, "socket", sizeof("socket"), (void *) &ph, sizeof(ph), NULL);
1458
1459 zend_declare_property_null(php_pqconn_class_entry, ZEND_STRL("errorMessage"), ZEND_ACC_PUBLIC TSRMLS_CC);
1460 ph.read = php_pqconn_object_read_error_message;
1461 zend_hash_add(&php_pqconn_object_prophandlers, "errorMessage", sizeof("errorMessage"), (void *) &ph, sizeof(ph), NULL);
1462
1463 zend_declare_property_null(php_pqconn_class_entry, ZEND_STRL("types"), ZEND_ACC_PUBLIC TSRMLS_CC);
1464 ph.read = php_pqconn_object_read_types;
1465 zend_hash_add(&php_pqconn_object_prophandlers, "types", sizeof("types"), (void *) &ph, sizeof(ph), NULL);
1466
1467 zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("OK"), CONNECTION_OK TSRMLS_CC);
1468 zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("BAD"), CONNECTION_BAD TSRMLS_CC);
1469 zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("STARTED"), CONNECTION_STARTED TSRMLS_CC);
1470 zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("MADE"), CONNECTION_MADE TSRMLS_CC);
1471 zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("AWAITING_RESPONSE"), CONNECTION_AWAITING_RESPONSE TSRMLS_CC);
1472 zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("AUTH_OK"), CONNECTION_AUTH_OK TSRMLS_CC);
1473 zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("SSL_STARTUP"), CONNECTION_SSL_STARTUP TSRMLS_CC);
1474 zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("SETENV"), CONNECTION_SETENV TSRMLS_CC);
1475
1476 zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("TRANS_IDLE"), PQTRANS_IDLE TSRMLS_CC);
1477 zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("TRANS_ACTIVE"), PQTRANS_ACTIVE TSRMLS_CC);
1478 zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("TRANS_INTRANS"), PQTRANS_INTRANS TSRMLS_CC);
1479 zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("TRANS_INERROR"), PQTRANS_INERROR TSRMLS_CC);
1480 zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("TRANS_UNKNOWN"), PQTRANS_UNKNOWN TSRMLS_CC);
1481
1482 zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("POLLING_FAILED"), PGRES_POLLING_FAILED TSRMLS_CC);
1483 zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("POLLING_READING"), PGRES_POLLING_READING TSRMLS_CC);
1484 zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("POLLING_WRITING"), PGRES_POLLING_WRITING TSRMLS_CC);
1485 zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("POLLING_OK"), PGRES_POLLING_OK TSRMLS_CC);
1486
1487 zend_hash_init(&php_pqres_object_prophandlers, 1, NULL, NULL, 1);
1488 memset(&ce, 0, sizeof(ce));
1489 INIT_NS_CLASS_ENTRY(ce, "pq", "Result", php_pqres_methods);
1490 php_pqres_class_entry = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC);
1491 php_pqres_class_entry->create_object = php_pqres_create_object;
1492 php_pqres_class_entry->iterator_funcs.funcs = &php_pqres_iterator_funcs;
1493 php_pqres_class_entry->get_iterator = php_pqres_iterator_init;
1494
1495 memcpy(&php_pqres_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
1496 php_pqres_object_handlers.read_property = php_pqres_object_read_prop;
1497 php_pqres_object_handlers.write_property = php_pqres_object_write_prop;
1498
1499 zend_declare_property_null(php_pqres_class_entry, ZEND_STRL("status"), ZEND_ACC_PUBLIC TSRMLS_CC);
1500 ph.read = php_pqres_object_read_status;
1501 zend_hash_add(&php_pqres_object_prophandlers, "status", sizeof("status"), (void *) &ph, sizeof(ph), NULL);
1502
1503 zend_declare_property_null(php_pqres_class_entry, ZEND_STRL("errorMessage"), ZEND_ACC_PUBLIC TSRMLS_CC);
1504 ph.read = php_pqres_object_read_error_message;
1505 zend_hash_add(&php_pqres_object_prophandlers, "errorMessage", sizeof("errorMessage"), (void *) &ph, sizeof(ph), NULL);
1506
1507 zend_declare_property_long(php_pqres_class_entry, ZEND_STRL("numRows"), 0, ZEND_ACC_PUBLIC TSRMLS_CC);
1508 ph.read = php_pqres_object_read_num_rows;
1509 zend_hash_add(&php_pqres_object_prophandlers, "numRows", sizeof("numRows"), (void *) &ph, sizeof(ph), NULL);
1510
1511 zend_declare_property_long(php_pqres_class_entry, ZEND_STRL("numCols"), 0, ZEND_ACC_PUBLIC TSRMLS_CC);
1512 ph.read = php_pqres_object_read_num_cols;
1513 zend_hash_add(&php_pqres_object_prophandlers, "numCols", sizeof("numCols"), (void *) &ph, sizeof(ph), NULL);
1514
1515 zend_declare_property_long(php_pqres_class_entry, ZEND_STRL("affectedRows"), 0, ZEND_ACC_PUBLIC TSRMLS_CC);
1516 ph.read = php_pqres_object_read_affected_rows;
1517 zend_hash_add(&php_pqres_object_prophandlers, "affectedRows", sizeof("affectedRows"), (void *) &ph, sizeof(ph), NULL);
1518
1519 zend_declare_property_long(php_pqres_class_entry, ZEND_STRL("fetchType"), PHP_PQRES_FETCH_ARRAY, ZEND_ACC_PUBLIC TSRMLS_CC);
1520 ph.read = php_pqres_object_read_fetch_type;
1521 ph.write = php_pqres_object_write_fetch_type;
1522 zend_hash_add(&php_pqres_object_prophandlers, "fetchType", sizeof("fetchType"), (void *) &ph, sizeof(ph), NULL);
1523 ph.write = NULL;
1524
1525 zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("EMPTY_QUERY"), PGRES_EMPTY_QUERY TSRMLS_CC);
1526 zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("COMMAND_OK"), PGRES_COMMAND_OK TSRMLS_CC);
1527 zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("TUPLES_OK"), PGRES_TUPLES_OK TSRMLS_CC);
1528 zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("COPY_OUT"), PGRES_COPY_OUT TSRMLS_CC);
1529 zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("COPY_IN"), PGRES_COPY_IN TSRMLS_CC);
1530 zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("BAD_RESPONSE"), PGRES_BAD_RESPONSE TSRMLS_CC);
1531 zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("NONFATAL_ERROR"), PGRES_NONFATAL_ERROR TSRMLS_CC);
1532 zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("FATAL_ERROR"), PGRES_FATAL_ERROR TSRMLS_CC);
1533 zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("COPY_BOTH"), PGRES_COPY_BOTH TSRMLS_CC);
1534 zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("SINGLE_TUPLE"), PGRES_SINGLE_TUPLE TSRMLS_CC);
1535
1536 zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("FETCH_ARRAY"), PHP_PQRES_FETCH_ARRAY TSRMLS_CC);
1537 zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("FETCH_ASSOC"), PHP_PQRES_FETCH_ASSOC TSRMLS_CC);
1538 zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("FETCH_OBJECT"), PHP_PQRES_FETCH_OBJECT TSRMLS_CC);
1539
1540 zend_hash_init(&php_pqstm_object_prophandlers, 1, NULL, NULL, 1);
1541 memset(&ce, 0, sizeof(ce));
1542 INIT_NS_CLASS_ENTRY(ce, "pq", "Statement", php_pqstm_methods);
1543 php_pqstm_class_entry = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC);
1544 php_pqstm_class_entry->create_object = php_pqstm_create_object;
1545
1546 memcpy(&php_pqstm_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
1547 php_pqstm_object_handlers.read_property = php_pqstm_object_read_prop;
1548 php_pqstm_object_handlers.write_property = php_pqstm_object_write_prop;
1549
1550 zend_declare_property_null(php_pqstm_class_entry, ZEND_STRL("name"), ZEND_ACC_PUBLIC TSRMLS_CC);
1551 ph.read = php_pqstm_object_read_name;
1552 zend_hash_add(&php_pqstm_object_prophandlers, "name", sizeof("name"), (void *) &ph, sizeof(ph), NULL);
1553
1554 zend_declare_property_null(php_pqstm_class_entry, ZEND_STRL("connection"), ZEND_ACC_PUBLIC TSRMLS_CC);
1555 ph.read = php_pqstm_object_read_connection;
1556 zend_hash_add(&php_pqstm_object_prophandlers, "connection", sizeof("connection"), (void *) &ph, sizeof(ph), NULL);
1557
1558 /*
1559 REGISTER_INI_ENTRIES();
1560 */
1561 return SUCCESS;
1562 }
1563 /* }}} */
1564
1565 /* {{{ PHP_MSHUTDOWN_FUNCTION
1566 */
1567 PHP_MSHUTDOWN_FUNCTION(pq)
1568 {
1569 /* uncomment this line if you have INI entries
1570 UNREGISTER_INI_ENTRIES();
1571 */
1572 return SUCCESS;
1573 }
1574 /* }}} */
1575
1576 /* {{{ PHP_MINFO_FUNCTION
1577 */
1578 PHP_MINFO_FUNCTION(pq)
1579 {
1580 php_info_print_table_start();
1581 php_info_print_table_header(2, "pq support", "enabled");
1582 php_info_print_table_end();
1583
1584 /* Remove comments if you have entries in php.ini
1585 DISPLAY_INI_ENTRIES();
1586 */
1587 }
1588 /* }}} */
1589
1590
1591
1592 /*
1593 * Local variables:
1594 * tab-width: 4
1595 * c-basic-offset: 4
1596 * End:
1597 * vim600: noet sw=4 ts=4 fdm=marker
1598 * vim<600: noet sw=4 ts=4
1599 */