refactor type conversion
[m6w6/ext-pq] / src / php_pqres.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
19 #include <ext/spl/spl_iterators.h>
20 #include <libpq-events.h>
21
22 #include "php_pq.h"
23 #include "php_pq_misc.h"
24 #include "php_pq_object.h"
25 #include "php_pqexc.h"
26 #include "php_pqres.h"
27
28 zend_class_entry *php_pqres_class_entry;
29 static zend_object_handlers php_pqres_object_handlers;
30 static HashTable php_pqres_object_prophandlers;
31 static zend_object_iterator_funcs php_pqres_iterator_funcs;
32
33 static zend_object_iterator *php_pqres_iterator_init(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC)
34 {
35 php_pqres_iterator_t *iter;
36 zval *prop, *zfetch_type;
37
38 iter = ecalloc(1, sizeof(*iter));
39 iter->zi.funcs = &php_pqres_iterator_funcs;
40 iter->zi.data = object;
41 /* do not addref, because the iterator lives inside this object anyway */
42
43 zfetch_type = prop = zend_read_property(ce, object, ZEND_STRL("fetchType"), 0 TSRMLS_CC);
44 if (Z_TYPE_P(zfetch_type) != IS_LONG) {
45 convert_to_long_ex(&zfetch_type);
46 }
47 iter->fetch_type = Z_LVAL_P(zfetch_type);
48 if (zfetch_type != prop) {
49 zval_ptr_dtor(&zfetch_type);
50 }
51 if (Z_REFCOUNT_P(prop)) {
52 zval_ptr_dtor(&prop);
53 } else {
54 zval_dtor(prop);
55 FREE_ZVAL(prop);
56 }
57
58 return (zend_object_iterator *) iter;
59 }
60
61 static void php_pqres_iterator_dtor(zend_object_iterator *i TSRMLS_DC)
62 {
63 php_pqres_iterator_t *iter = (php_pqres_iterator_t *) i;
64
65 if (iter->current_val) {
66 zval_ptr_dtor(&iter->current_val);
67 iter->current_val = NULL;
68 }
69 efree(iter);
70 }
71
72 static STATUS php_pqres_iterator_valid(zend_object_iterator *i TSRMLS_DC)
73 {
74 php_pqres_iterator_t *iter = (php_pqres_iterator_t *) i;
75 php_pqres_object_t *obj = zend_object_store_get_object(iter->zi.data TSRMLS_CC);
76
77 switch (PQresultStatus(obj->intern->res)) {
78 case PGRES_TUPLES_OK:
79 case PGRES_SINGLE_TUPLE:
80 if (PQntuples(obj->intern->res) <= iter->index) {
81 return FAILURE;
82 }
83 break;
84 default:
85 return FAILURE;
86 }
87
88 return SUCCESS;
89 }
90
91 zval *php_pqres_typed_zval(php_pqres_t *res, char *val, size_t len, Oid typ TSRMLS_DC)
92 {
93 zval *zv, **zconv;
94
95 MAKE_STD_ZVAL(zv);
96
97 if (SUCCESS == zend_hash_index_find(&res->converters, typ, (void *) &zconv)) {
98 zval *tmp = NULL;
99
100 ZVAL_STRINGL(zv, val, len, 1);
101 zend_call_method_with_1_params(zconv, NULL, NULL, "convertfromstring", &tmp, zv);
102
103 if (tmp) {
104 zval_ptr_dtor(&zv);
105 zv = tmp;
106 }
107
108 return zv;
109 }
110
111 switch (typ) {
112 #ifdef HAVE_PHP_PQ_TYPE_H
113 # undef PHP_PQ_TYPE
114 # include "php_pq_type.h"
115 case PHP_PQ_OID_BOOL:
116 if (!(res->auto_convert & PHP_PQRES_CONV_BOOL)) {
117 goto noconversion;
118 }
119 ZVAL_BOOL(zv, *val == 't');
120 break;
121 #if SIZEOF_LONG >= 8
122 case PHP_PQ_OID_INT8:
123 case PHP_PQ_OID_TID:
124 #endif
125 case PHP_PQ_OID_INT4:
126 case PHP_PQ_OID_INT2:
127 case PHP_PQ_OID_XID:
128 case PHP_PQ_OID_OID:
129 if (!(res->auto_convert & PHP_PQRES_CONV_INT)) {
130 goto noconversion;
131 }
132 ZVAL_LONG(zv, zend_atol(val, len));
133 break;
134
135 case PHP_PQ_OID_FLOAT4:
136 case PHP_PQ_OID_FLOAT8:
137 if (!(res->auto_convert & PHP_PQRES_CONV_FLOAT)) {
138 goto noconversion;
139 }
140 ZVAL_DOUBLE(zv, zend_strtod(val, NULL));
141 break;
142
143 case PHP_PQ_OID_DATE:
144 if (!(res->auto_convert & PHP_PQRES_CONV_DATETIME)) {
145 goto noconversion;
146 }
147 php_pqdt_from_string(val, len, "Y-m-d", zv TSRMLS_CC);
148 break;
149
150 case PHP_PQ_OID_ABSTIME:
151 if (!(res->auto_convert & PHP_PQRES_CONV_DATETIME)) {
152 goto noconversion;
153 }
154 php_pqdt_from_string(val, len, "Y-m-d H:i:s", zv TSRMLS_CC);
155 break;
156
157 case PHP_PQ_OID_TIMESTAMP:
158 if (!(res->auto_convert & PHP_PQRES_CONV_DATETIME)) {
159 goto noconversion;
160 }
161 php_pqdt_from_string(val, len, "Y-m-d H:i:s.u", zv TSRMLS_CC);
162 break;
163
164 case PHP_PQ_OID_TIMESTAMPTZ:
165 if (!(res->auto_convert & PHP_PQRES_CONV_DATETIME)) {
166 goto noconversion;
167 }
168 php_pqdt_from_string(val, len, "Y-m-d H:i:s.uO", zv TSRMLS_CC);
169 break;
170
171 default:
172 if (!(res->auto_convert & PHP_PQRES_CONV_ARRAY)) {
173 goto noconversion;
174 }
175 if (PHP_PQ_TYPE_IS_ARRAY(typ) && (Z_ARRVAL_P(zv) = php_pq_parse_array(res, val, len, PHP_PQ_TYPE_OF_ARRAY(typ) TSRMLS_CC))) {
176 Z_TYPE_P(zv) = IS_ARRAY;
177 } else {
178 noconversion:
179 ZVAL_STRINGL(zv, val, len, 1);
180 }
181 break;
182 #else
183 case 16: /* BOOL */
184 if (res->auto_convert & PHP_PQRES_CONV_BOOL) {
185 ZVAL_BOOL(zv, *val == 't');
186 break;
187 }
188 default:
189 ZVAL_STRINGL(zv, val, len, 1);
190 #endif
191 }
192
193 return zv;
194 }
195
196 zval *php_pqres_row_to_zval(PGresult *res, unsigned row, php_pqres_fetch_t fetch_type, zval **data_ptr TSRMLS_DC)
197 {
198 zval *data = NULL;
199 int c, cols;
200 php_pqres_object_t *res_obj = PQresultInstanceData(res, php_pqconn_event);
201
202 if (data_ptr) {
203 data = *data_ptr;
204 }
205 if (!data) {
206 MAKE_STD_ZVAL(data);
207 if (PHP_PQRES_FETCH_OBJECT == fetch_type) {
208 object_init(data);
209 } else {
210 array_init(data);
211 }
212 if (data_ptr) {
213 *data_ptr = data;
214 }
215 }
216
217 if (PQntuples(res) > row) {
218 for (c = 0, cols = PQnfields(res); c < cols; ++c) {
219 if (PQgetisnull(res, row, c)) {
220 switch (fetch_type) {
221 case PHP_PQRES_FETCH_OBJECT:
222 add_property_null(data, PQfname(res, c));
223 break;
224
225 case PHP_PQRES_FETCH_ASSOC:
226 add_assoc_null(data, PQfname(res, c));
227 break;
228
229 case PHP_PQRES_FETCH_ARRAY:
230 add_index_null(data, c);
231 break;
232 }
233 } else {
234 zval *zv;
235
236 zv = php_pq_typed_zval(&res_obj->intern->converters, PQgetvalue(res, row, c), PQgetlength(res, row, c), PQftype(res, c) TSRMLS_CC);
237
238 switch (fetch_type) {
239 case PHP_PQRES_FETCH_OBJECT:
240 add_property_zval(data, PQfname(res, c), zv);
241 zval_ptr_dtor(&zv);
242 break;
243
244 case PHP_PQRES_FETCH_ASSOC:
245 add_assoc_zval(data, PQfname(res, c), zv);
246 break;
247
248 case PHP_PQRES_FETCH_ARRAY:
249 add_index_zval(data, c, zv);
250 break;
251 }
252 }
253 }
254 }
255
256 return data;
257 }
258
259 static void php_pqres_iterator_current(zend_object_iterator *i, zval ***data_ptr TSRMLS_DC)
260 {
261 php_pqres_iterator_t *iter = (php_pqres_iterator_t *) i;
262 php_pqres_object_t *obj = zend_object_store_get_object(iter->zi.data TSRMLS_CC);
263
264 if (!iter->current_val) {
265 iter->current_val = php_pqres_row_to_zval(obj->intern->res, iter->index, iter->fetch_type, NULL TSRMLS_CC);
266 }
267 *data_ptr = &iter->current_val;
268 }
269
270 #if PHP_VERSION_ID >= 50500
271 static void php_pqres_iterator_key(zend_object_iterator *i, zval *key TSRMLS_DC)
272 {
273 php_pqres_iterator_t *iter = (php_pqres_iterator_t *) i;
274
275 ZVAL_LONG(key, iter->index);
276 }
277 #else
278 static int php_pqres_iterator_key(zend_object_iterator *i, char **key_str, uint *key_len, ulong *key_num TSRMLS_DC)
279 {
280 php_pqres_iterator_t *iter = (php_pqres_iterator_t *) i;
281
282 *key_num = (ulong) iter->index;
283
284 return HASH_KEY_IS_LONG;
285 }
286 #endif
287
288 static void php_pqres_iterator_invalidate(zend_object_iterator *i TSRMLS_DC)
289 {
290 php_pqres_iterator_t *iter = (php_pqres_iterator_t *) i;
291
292 if (iter->current_val) {
293 zval_ptr_dtor(&iter->current_val);
294 iter->current_val = NULL;
295 }
296 }
297
298 static void php_pqres_iterator_next(zend_object_iterator *i TSRMLS_DC)
299 {
300 php_pqres_iterator_t *iter = (php_pqres_iterator_t *) i;
301
302 php_pqres_iterator_invalidate(i TSRMLS_CC);
303 ++iter->index;
304 }
305
306 static void php_pqres_iterator_rewind(zend_object_iterator *i TSRMLS_DC)
307 {
308 php_pqres_iterator_t *iter = (php_pqres_iterator_t *) i;
309
310 php_pqres_iterator_invalidate(i TSRMLS_CC);
311 iter->index = 0;
312 }
313
314 static zend_object_iterator_funcs php_pqres_iterator_funcs = {
315 php_pqres_iterator_dtor,
316 /* check for end of iteration (FAILURE or SUCCESS if data is valid) */
317 php_pqres_iterator_valid,
318 /* fetch the item data for the current element */
319 php_pqres_iterator_current,
320 /* fetch the key for the current element (return HASH_KEY_IS_STRING or HASH_KEY_IS_LONG) (optional, may be NULL) */
321 php_pqres_iterator_key,
322 /* step forwards to next element */
323 php_pqres_iterator_next,
324 /* rewind to start of data (optional, may be NULL) */
325 php_pqres_iterator_rewind,
326 /* invalidate current value/key (optional, may be NULL) */
327 php_pqres_iterator_invalidate
328 };
329
330 static int php_pqres_count_elements(zval *object, long *count TSRMLS_DC)
331 {
332 php_pqres_object_t *obj = zend_object_store_get_object(object TSRMLS_CC);
333
334 if (!obj->intern) {
335 return FAILURE;
336 } else {
337 *count = (long) PQntuples(obj->intern->res);
338 return SUCCESS;
339 }
340 }
341
342 STATUS php_pqres_success(PGresult *res TSRMLS_DC)
343 {
344 zval *zexc;
345
346 switch (PQresultStatus(res)) {
347 case PGRES_BAD_RESPONSE:
348 case PGRES_NONFATAL_ERROR:
349 case PGRES_FATAL_ERROR:
350 zexc = throw_exce(EX_SQL TSRMLS_CC, "%s", PHP_PQresultErrorMessage(res));
351 zend_update_property_string(Z_OBJCE_P(zexc), zexc, ZEND_STRL("sqlstate"), PQresultErrorField(res, PG_DIAG_SQLSTATE) TSRMLS_CC);
352 return FAILURE;
353 default:
354 return SUCCESS;
355 }
356 }
357
358 void php_pqres_init_instance_data(PGresult *res, php_pqconn_object_t *conn_obj, php_pqres_object_t **ptr TSRMLS_DC)
359 {
360 php_pqres_object_t *obj;
361 php_pqres_t *r = ecalloc(1, sizeof(*r));
362
363 r->res = res;
364 zend_hash_init(&r->bound, 0, 0, ZVAL_PTR_DTOR, 0);
365 zend_hash_init(&r->converters, 0, 0, ZVAL_PTR_DTOR, 0);
366 zend_hash_copy(&r->converters, &conn_obj->intern->converters, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
367
368 r->default_fetch_type = conn_obj->intern->default_fetch_type;
369
370 php_pqres_create_object_ex(php_pqres_class_entry, r, &obj TSRMLS_CC);
371 PQresultSetInstanceData(res, php_pqconn_event, obj);
372
373 if (ptr) {
374 *ptr = obj;
375 }
376 }
377
378 static void php_pqres_object_free(void *o TSRMLS_DC)
379 {
380 php_pqres_object_t *obj = o;
381 #if DBG_GC
382 fprintf(stderr, "FREE res(#%d) %p\n", obj->zv.handle, obj);
383 #endif
384 if (obj->intern) {
385 if (obj->intern->res) {
386 PQresultSetInstanceData(obj->intern->res, php_pqconn_event, NULL);
387 PQclear(obj->intern->res);
388 obj->intern->res = NULL;
389 }
390
391 if (obj->intern->iter) {
392 php_pqres_iterator_dtor((zend_object_iterator *) obj->intern->iter TSRMLS_CC);
393 obj->intern->iter = NULL;
394 }
395
396 zend_hash_destroy(&obj->intern->bound);
397 zend_hash_destroy(&obj->intern->converters);
398
399 efree(obj->intern);
400 obj->intern = NULL;
401 }
402 zend_object_std_dtor((zend_object *) o TSRMLS_CC);
403 efree(obj);
404 }
405
406 zend_object_value php_pqres_create_object_ex(zend_class_entry *ce, php_pqres_t *intern, php_pqres_object_t **ptr TSRMLS_DC)
407 {
408 php_pqres_object_t *o;
409
410 o = ecalloc(1, sizeof(*o));
411 zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
412 object_properties_init((zend_object *) o, ce);
413 o->prophandler = &php_pqres_object_prophandlers;
414
415 if (ptr) {
416 *ptr = o;
417 }
418
419 if (intern) {
420 o->intern = intern;
421 }
422
423 o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_pqres_object_free, NULL TSRMLS_CC);
424 o->zv.handlers = &php_pqres_object_handlers;
425
426 return o->zv;
427 }
428
429 static zend_object_value php_pqres_create_object(zend_class_entry *class_type TSRMLS_DC)
430 {
431 return php_pqres_create_object_ex(class_type, NULL, NULL TSRMLS_CC);
432 }
433
434 static void php_pqres_object_read_status(zval *object, void *o, zval *return_value TSRMLS_DC)
435 {
436 php_pqres_object_t *obj = o;
437
438 RETVAL_LONG(PQresultStatus(obj->intern->res));
439 }
440
441 static void php_pqres_object_read_status_message(zval *object, void *o, zval *return_value TSRMLS_DC)
442 {
443 php_pqres_object_t *obj = o;
444
445 RETVAL_STRING(PQresStatus(PQresultStatus(obj->intern->res))+sizeof("PGRES"), 1);
446 }
447
448 static void php_pqres_object_read_error_message(zval *object, void *o, zval *return_value TSRMLS_DC)
449 {
450 php_pqres_object_t *obj = o;
451 char *error = PHP_PQresultErrorMessage(obj->intern->res);
452
453 if (error) {
454 RETVAL_STRING(error, 1);
455 } else {
456 RETVAL_NULL();
457 }
458 }
459
460 static void php_pqres_object_read_num_rows(zval *object, void *o, zval *return_value TSRMLS_DC)
461 {
462 php_pqres_object_t *obj = o;
463
464 RETVAL_LONG(PQntuples(obj->intern->res));
465 }
466
467 static void php_pqres_object_read_num_cols(zval *object, void *o, zval *return_value TSRMLS_DC)
468 {
469 php_pqres_object_t *obj = o;
470
471 RETVAL_LONG(PQnfields(obj->intern->res));
472 }
473
474 static void php_pqres_object_read_affected_rows(zval *object, void *o, zval *return_value TSRMLS_DC)
475 {
476 php_pqres_object_t *obj = o;
477
478 RETVAL_LONG(atoi(PQcmdTuples(obj->intern->res)));
479 }
480
481 static void php_pqres_object_read_fetch_type(zval *object, void *o, zval *return_value TSRMLS_DC)
482 {
483 php_pqres_object_t *obj = o;
484
485 if (obj->intern->iter) {
486 RETVAL_LONG(obj->intern->iter->fetch_type);
487 } else {
488 RETVAL_LONG(obj->intern->default_fetch_type);
489 }
490 }
491
492 static void php_pqres_object_write_fetch_type(zval *object, void *o, zval *value TSRMLS_DC)
493 {
494 php_pqres_object_t *obj = o;
495 zval *zfetch_type = value;
496
497 if (Z_TYPE_P(value) != IS_LONG) {
498 if (Z_REFCOUNT_P(value) > 1) {
499 zval *tmp;
500 MAKE_STD_ZVAL(tmp);
501 ZVAL_ZVAL(tmp, zfetch_type, 1, 0);
502 convert_to_long(tmp);
503 zfetch_type = tmp;
504 } else {
505 convert_to_long_ex(&zfetch_type);
506 }
507 }
508
509 if (!obj->intern->iter) {
510 obj->intern->iter = (php_pqres_iterator_t *) php_pqres_iterator_init(Z_OBJCE_P(object), object, 0 TSRMLS_CC);
511 obj->intern->iter->zi.funcs->rewind((zend_object_iterator *) obj->intern->iter TSRMLS_CC);
512 }
513 obj->intern->iter->fetch_type = Z_LVAL_P(zfetch_type);
514
515 if (zfetch_type != value) {
516 zval_ptr_dtor(&zfetch_type);
517 }
518 }
519
520 static STATUS php_pqres_iteration(zval *this_ptr, php_pqres_object_t *obj, php_pqres_fetch_t fetch_type, zval ***row TSRMLS_DC)
521 {
522 STATUS rv;
523 php_pqres_fetch_t orig_fetch;
524
525 if (!obj) {
526 obj = zend_object_store_get_object(getThis() TSRMLS_CC);
527 }
528
529 if (obj->intern->iter) {
530 obj->intern->iter->zi.funcs->move_forward((zend_object_iterator *) obj->intern->iter TSRMLS_CC);
531 } else {
532 obj->intern->iter = (php_pqres_iterator_t *) php_pqres_iterator_init(Z_OBJCE_P(getThis()), getThis(), 0 TSRMLS_CC);
533 obj->intern->iter->zi.funcs->rewind((zend_object_iterator *) obj->intern->iter TSRMLS_CC);
534 }
535 orig_fetch = obj->intern->iter->fetch_type;
536 obj->intern->iter->fetch_type = fetch_type;
537 if (SUCCESS == (rv = obj->intern->iter->zi.funcs->valid((zend_object_iterator *) obj->intern->iter TSRMLS_CC))) {
538 obj->intern->iter->zi.funcs->get_current_data((zend_object_iterator *) obj->intern->iter, row TSRMLS_CC);
539 }
540 obj->intern->iter->fetch_type = orig_fetch;
541
542 return rv;
543 }
544
545 typedef struct php_pqres_col {
546 char *name;
547 int num;
548 } php_pqres_col_t;
549
550 static STATUS column_nn(php_pqres_object_t *obj, zval *zcol, php_pqres_col_t *col TSRMLS_DC)
551 {
552 long index = -1;
553 char *name = NULL;
554
555 switch (Z_TYPE_P(zcol)) {
556 case IS_LONG:
557 index = Z_LVAL_P(zcol);
558 break;
559
560 default:
561 convert_to_string(zcol);
562 /* no break */
563
564 case IS_STRING:
565 if (!is_numeric_string(Z_STRVAL_P(zcol), Z_STRLEN_P(zcol), &index, NULL, 0)) {
566 name = Z_STRVAL_P(zcol);
567 }
568 break;
569 }
570
571 if (name) {
572 col->name = name;
573 col->num = PQfnumber(obj->intern->res, name);
574 } else {
575 col->name = PQfname(obj->intern->res, index);
576 col->num = index;
577 }
578
579 if (!col->name) {
580 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to find column at index %ld", index);
581 return FAILURE;
582 }
583 if (col->num == -1) {
584 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to find column with name '%s'", name);
585 return FAILURE;
586 }
587 return SUCCESS;
588 }
589
590 ZEND_BEGIN_ARG_INFO_EX(ai_pqres_bind, 0, 0, 2)
591 ZEND_ARG_INFO(0, col)
592 ZEND_ARG_INFO(1, ref)
593 ZEND_END_ARG_INFO();
594 static PHP_METHOD(pqres, bind) {
595 zval *zcol, *zref;
596
597 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/z", &zcol, &zref)) {
598 php_pqres_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
599
600 if (!obj->intern) {
601 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Result not initialized");
602 } else {
603 php_pqres_col_t col;
604
605 if (SUCCESS != column_nn(obj, zcol, &col TSRMLS_CC)) {
606 RETVAL_FALSE;
607 } else {
608 Z_ADDREF_P(zref);
609
610 if (SUCCESS != zend_hash_index_update(&obj->intern->bound, col.num, (void *) &zref, sizeof(zval *), NULL)) {
611 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to bind column %s@%d", col.name, col.num);
612 RETVAL_FALSE;
613 } else {
614 zend_hash_sort(&obj->intern->bound, zend_qsort, compare_index, 0 TSRMLS_CC);
615 RETVAL_TRUE;
616 }
617 }
618 }
619 }
620 }
621
622 static int apply_bound(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key)
623 {
624 zval **zvalue, **zbound = p;
625 zval **zrow = va_arg(argv, zval **);
626 STATUS *rv = va_arg(argv, STATUS *);
627
628 if (SUCCESS != zend_hash_index_find(Z_ARRVAL_PP(zrow), key->h, (void *) &zvalue)) {
629 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to find column ad index %lu", key->h);
630 *rv = FAILURE;
631 return ZEND_HASH_APPLY_STOP;
632 } else {
633 zval_dtor(*zbound);
634 ZVAL_COPY_VALUE(*zbound, *zvalue);
635 ZVAL_NULL(*zvalue);
636 zval_ptr_dtor(zvalue);
637 Z_ADDREF_P(*zbound);
638 *zvalue = *zbound;
639 *rv = SUCCESS;
640 return ZEND_HASH_APPLY_KEEP;
641 }
642 }
643
644 ZEND_BEGIN_ARG_INFO_EX(ai_pqres_fetch_bound, 0, 0, 0)
645 ZEND_END_ARG_INFO();
646 static PHP_METHOD(pqres, fetchBound) {
647 zend_error_handling zeh;
648 STATUS rv;
649
650 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
651 rv = zend_parse_parameters_none();
652 zend_restore_error_handling(&zeh TSRMLS_CC);
653
654 if (SUCCESS == rv) {
655 php_pqres_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
656
657 if (!obj->intern) {
658 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Result not initialized");
659 } else {
660 zval **row = NULL;
661
662 if (SUCCESS == php_pqres_iteration(getThis(), obj, PHP_PQRES_FETCH_ARRAY, &row TSRMLS_CC) && row) {
663 zend_replace_error_handling(EH_THROW, exce(EX_RUNTIME), &zeh TSRMLS_CC);
664 zend_hash_apply_with_arguments(&obj->intern->bound TSRMLS_CC, apply_bound, 2, row, &rv);
665 zend_restore_error_handling(&zeh TSRMLS_CC);
666
667 if (SUCCESS != rv) {
668 zval_ptr_dtor(row);
669 } else {
670 RETVAL_ZVAL(*row, 1, 0);
671 }
672 }
673 }
674 }
675 }
676
677 ZEND_BEGIN_ARG_INFO_EX(ai_pqres_fetch_row, 0, 0, 0)
678 ZEND_ARG_INFO(0, fetch_type)
679 ZEND_END_ARG_INFO();
680 static PHP_METHOD(pqres, fetchRow) {
681 zend_error_handling zeh;
682 php_pqres_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
683 long fetch_type = -1;
684 STATUS rv;
685
686 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
687 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &fetch_type);
688 zend_restore_error_handling(&zeh TSRMLS_CC);
689
690 if (SUCCESS == rv) {
691 if (!obj->intern) {
692 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Result not initialized");
693 } else {
694 zval **row = NULL;
695
696 if (fetch_type == -1) {
697 fetch_type = obj->intern->iter ? obj->intern->iter->fetch_type : obj->intern->default_fetch_type;
698 }
699
700 zend_replace_error_handling(EH_THROW, exce(EX_RUNTIME), &zeh TSRMLS_CC);
701 php_pqres_iteration(getThis(), obj, fetch_type, &row TSRMLS_CC);
702 zend_restore_error_handling(&zeh TSRMLS_CC);
703
704 if (row) {
705 RETVAL_ZVAL(*row, 1, 0);
706 }
707 }
708 }
709 }
710
711 static zval **column_at(zval *row, int col TSRMLS_DC)
712 {
713 zval **data = NULL;
714 HashTable *ht = HASH_OF(row);
715 int count = zend_hash_num_elements(ht);
716
717 if (col >= count) {
718 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Column index %d exceeds column count %d", col, count);
719 } else {
720 zend_hash_internal_pointer_reset(ht);
721 while (col-- > 0) {
722 zend_hash_move_forward(ht);
723 }
724 zend_hash_get_current_data(ht, (void *) &data);
725 }
726 return data;
727 }
728
729 ZEND_BEGIN_ARG_INFO_EX(ai_pqres_fetch_col, 0, 0, 2)
730 ZEND_ARG_INFO(0, col)
731 ZEND_ARG_INFO(1, ref)
732 ZEND_END_ARG_INFO();
733 static PHP_METHOD(pqres, fetchCol) {
734 zend_error_handling zeh;
735 zval *zcol, *zref;
736 STATUS rv;
737
738 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
739 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/z", &zcol, &zref);
740 zend_restore_error_handling(&zeh TSRMLS_CC);
741
742 if (SUCCESS == rv) {
743 php_pqres_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
744
745 if (!obj->intern) {
746 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Result not initialized");
747 } else {
748 zval **row = NULL;
749
750 zend_replace_error_handling(EH_THROW, exce(EX_RUNTIME), &zeh TSRMLS_CC);
751 php_pqres_iteration(getThis(), obj, obj->intern->iter ? obj->intern->iter->fetch_type : 0, &row TSRMLS_CC);
752 if (!row) {
753 RETVAL_FALSE;
754 } else {
755 php_pqres_col_t col;
756
757 if (SUCCESS != column_nn(obj, zcol, &col TSRMLS_CC)) {
758 RETVAL_FALSE;
759 } else {
760 zval **zres = column_at(*row, col.num TSRMLS_CC);
761
762 if (!zres) {
763 RETVAL_FALSE;
764 } else {
765 zval_dtor(zref);
766 ZVAL_ZVAL(zref, *zres, 1, 0);
767 RETVAL_TRUE;
768 }
769 }
770 }
771 zend_restore_error_handling(&zeh TSRMLS_CC);
772 }
773 }
774 }
775
776 static int apply_to_col(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key)
777 {
778 zval **c = p;
779 php_pqres_object_t *obj = va_arg(argv, php_pqres_object_t *);
780 php_pqres_col_t *col, **cols = va_arg(argv, php_pqres_col_t **);
781 STATUS *rv = va_arg(argv, STATUS *);
782
783 col = *cols;
784
785 if (SUCCESS != column_nn(obj, *c, col TSRMLS_CC)) {
786 *rv = FAILURE;
787 return ZEND_HASH_APPLY_STOP;
788 } else {
789 *rv = SUCCESS;
790 ++*cols;
791 return ZEND_HASH_APPLY_KEEP;
792 }
793 }
794
795 static php_pqres_col_t *php_pqres_convert_to_cols(php_pqres_object_t *obj, HashTable *ht TSRMLS_DC)
796 {
797 php_pqres_col_t *tmp, *cols = ecalloc(zend_hash_num_elements(ht), sizeof(*cols));
798 STATUS rv = SUCCESS;
799
800 tmp = cols;
801 zend_hash_apply_with_arguments(ht TSRMLS_CC, apply_to_col, 2, obj, &tmp, &rv);
802
803 if (SUCCESS == rv) {
804 return cols;
805 } else {
806 efree(cols);
807 return NULL;
808 }
809 }
810
811 ZEND_BEGIN_ARG_INFO_EX(ai_pqres_map, 0, 0, 0)
812 ZEND_ARG_INFO(0, keys)
813 ZEND_ARG_INFO(0, vals)
814 ZEND_ARG_INFO(0, fetch_type)
815 ZEND_END_ARG_INFO();
816 static PHP_METHOD(pqres, map) {
817 zend_error_handling zeh;
818 zval *zkeys = 0, *zvals = 0;
819 long fetch_type = -1;
820 STATUS rv;
821
822 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
823 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z/!z/!l", &zkeys, &zvals, &fetch_type);
824 zend_restore_error_handling(&zeh TSRMLS_CC);
825
826 if (SUCCESS == rv) {
827 php_pqres_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
828
829 if (!obj->intern) {
830 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Result not initialized");
831 } else {
832 int ks = 0, vs = 0;
833 php_pqres_col_t def = {PQfname(obj->intern->res, 0), 0}, *keys = NULL, *vals = NULL;
834
835 if (zkeys) {
836 convert_to_array(zkeys);
837
838 if ((ks = zend_hash_num_elements(Z_ARRVAL_P(zkeys)))) {
839 keys = php_pqres_convert_to_cols(obj, Z_ARRVAL_P(zkeys) TSRMLS_CC);
840 } else {
841 ks = 1;
842 keys = &def;
843 }
844 } else {
845 ks = 1;
846 keys = &def;
847 }
848 if (zvals) {
849 convert_to_array(zvals);
850
851 if ((vs = zend_hash_num_elements(Z_ARRVAL_P(zvals)))) {
852 vals = php_pqres_convert_to_cols(obj, Z_ARRVAL_P(zvals) TSRMLS_CC);
853 }
854 }
855
856 if (fetch_type == -1) {
857 fetch_type = obj->intern->iter ? obj->intern->iter->fetch_type : obj->intern->default_fetch_type;
858 }
859
860 if (keys) {
861 int rows, r;
862 zval **cur;
863
864 switch (fetch_type) {
865 case PHP_PQRES_FETCH_ARRAY:
866 case PHP_PQRES_FETCH_ASSOC:
867 array_init(return_value);
868 break;
869 case PHP_PQRES_FETCH_OBJECT:
870 object_init(return_value);
871 break;
872 }
873 for (r = 0, rows = PQntuples(obj->intern->res); r < rows; ++r) {
874 int k, v;
875
876 cur = &return_value;
877 for (k = 0; k < ks; ++k) {
878 char *key = PQgetvalue(obj->intern->res, r, keys[k].num);
879 int len = PQgetlength(obj->intern->res, r, keys[k].num);
880
881 if (SUCCESS != zend_symtable_find(HASH_OF(*cur), key, len + 1, (void *) &cur)) {
882 zval *tmp;
883
884 MAKE_STD_ZVAL(tmp);
885 switch (fetch_type) {
886 case PHP_PQRES_FETCH_ARRAY:
887 case PHP_PQRES_FETCH_ASSOC:
888 array_init(tmp);
889 break;
890 case PHP_PQRES_FETCH_OBJECT:
891 object_init(tmp);
892 break;
893 }
894 if (SUCCESS != zend_symtable_update(HASH_OF(*cur), key, len + 1, (void *) &tmp, sizeof(zval *), (void *) &cur)) {
895 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to create map");
896 goto err;
897 }
898 }
899 }
900 if (vals && vs) {
901 for (v = 0; v < vs; ++v) {
902 char *val = PQgetvalue(obj->intern->res, r, vals[v].num);
903 int len = PQgetlength(obj->intern->res, r, vals[v].num);
904
905 switch (fetch_type) {
906 case PHP_PQRES_FETCH_ARRAY:
907 add_index_stringl(*cur, vals[v].num, val, len, 1);
908 break;
909 case PHP_PQRES_FETCH_ASSOC:
910 add_assoc_stringl(*cur, vals[v].name, val, len, 1);
911 break;
912 case PHP_PQRES_FETCH_OBJECT:
913 add_property_stringl(*cur, vals[v].name, val, len, 1);
914 break;
915 }
916 }
917 } else {
918 php_pqres_row_to_zval(obj->intern->res, r, fetch_type, cur TSRMLS_CC);
919 }
920 }
921 }
922
923 err:
924 if (keys && keys != &def) {
925 efree(keys);
926 }
927 if (vals) {
928 efree(vals);
929 }
930 }
931 }
932 }
933
934 ZEND_BEGIN_ARG_INFO_EX(ai_pqres_fetch_all, 0, 0, 0)
935 ZEND_ARG_INFO(0, fetch_type)
936 ZEND_END_ARG_INFO();
937 static PHP_METHOD(pqres, fetchAll) {
938 zend_error_handling zeh;
939 long fetch_type = -1;
940 STATUS rv;
941
942 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
943 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &fetch_type);
944 zend_restore_error_handling(&zeh TSRMLS_CC);
945
946 if (SUCCESS == rv) {
947 php_pqres_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
948 if (!obj->intern) {
949 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Result not initialized");
950 } else {
951 int r, rows = PQntuples(obj->intern->res);
952
953 if (fetch_type == -1) {
954 fetch_type = obj->intern->iter ? obj->intern->iter->fetch_type : obj->intern->default_fetch_type;
955 }
956
957 array_init(return_value);
958 for (r = 0; r < rows; ++r) {
959 add_next_index_zval(return_value, php_pqres_row_to_zval(obj->intern->res, r, fetch_type, NULL TSRMLS_CC));
960 }
961 }
962 }
963 }
964
965 ZEND_BEGIN_ARG_INFO_EX(ai_pqres_count, 0, 0, 0)
966 ZEND_END_ARG_INFO();
967 static PHP_METHOD(pqres, count) {
968 if (SUCCESS == zend_parse_parameters_none()) {
969 long count;
970
971 if (SUCCESS != php_pqres_count_elements(getThis(), &count TSRMLS_CC)) {
972 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Result not initialized");
973 } else {
974 RETVAL_LONG(count);
975 }
976 }
977 }
978
979 ZEND_BEGIN_ARG_INFO_EX(ai_pqres_desc, 0, 0, 0)
980 ZEND_END_ARG_INFO();
981 static PHP_METHOD(pqres, desc) {
982 if (SUCCESS == zend_parse_parameters_none()) {
983 php_pqres_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
984
985 if (!obj->intern) {
986 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Result not initialized");
987 } else {
988 int p, params;
989
990 array_init(return_value);
991 for (p = 0, params = PQnparams(obj->intern->res); p < params; ++p) {
992 add_next_index_long(return_value, PQparamtype(obj->intern->res, p));
993 }
994 }
995 }
996 }
997
998 static zend_function_entry php_pqres_methods[] = {
999 PHP_ME(pqres, bind, ai_pqres_bind, ZEND_ACC_PUBLIC)
1000 PHP_ME(pqres, fetchBound, ai_pqres_fetch_bound, ZEND_ACC_PUBLIC)
1001 PHP_ME(pqres, fetchRow, ai_pqres_fetch_row, ZEND_ACC_PUBLIC)
1002 PHP_ME(pqres, fetchCol, ai_pqres_fetch_col, ZEND_ACC_PUBLIC)
1003 PHP_ME(pqres, fetchAll, ai_pqres_fetch_all, ZEND_ACC_PUBLIC)
1004 PHP_ME(pqres, count, ai_pqres_count, ZEND_ACC_PUBLIC)
1005 PHP_ME(pqres, map, ai_pqres_map, ZEND_ACC_PUBLIC)
1006 PHP_ME(pqres, desc, ai_pqres_desc, ZEND_ACC_PUBLIC)
1007 {0}
1008 };
1009
1010 PHP_MSHUTDOWN_FUNCTION(pqres)
1011 {
1012 zend_hash_destroy(&php_pqres_object_prophandlers);
1013 return SUCCESS;
1014 }
1015
1016 PHP_MINIT_FUNCTION(pqres)
1017 {
1018 zend_class_entry ce = {0};
1019 php_pq_object_prophandler_t ph = {0};
1020
1021 INIT_NS_CLASS_ENTRY(ce, "pq", "Result", php_pqres_methods);
1022 php_pqres_class_entry = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC);
1023 php_pqres_class_entry->create_object = php_pqres_create_object;
1024 php_pqres_class_entry->iterator_funcs.funcs = &php_pqres_iterator_funcs;
1025 php_pqres_class_entry->get_iterator = php_pqres_iterator_init;
1026 zend_class_implements(php_pqres_class_entry TSRMLS_CC, 2, zend_ce_traversable, spl_ce_Countable);
1027
1028 memcpy(&php_pqres_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
1029 php_pqres_object_handlers.read_property = php_pq_object_read_prop;
1030 php_pqres_object_handlers.write_property = php_pq_object_write_prop;
1031 php_pqres_object_handlers.clone_obj = NULL;
1032 php_pqres_object_handlers.get_property_ptr_ptr = NULL;
1033 php_pqres_object_handlers.get_gc = NULL;
1034 php_pqres_object_handlers.get_debug_info = php_pq_object_debug_info;
1035 php_pqres_object_handlers.get_properties = php_pq_object_properties;
1036 php_pqres_object_handlers.count_elements = php_pqres_count_elements;
1037
1038 zend_hash_init(&php_pqres_object_prophandlers, 6, NULL, NULL, 1);
1039
1040 zend_declare_property_null(php_pqres_class_entry, ZEND_STRL("status"), ZEND_ACC_PUBLIC TSRMLS_CC);
1041 ph.read = php_pqres_object_read_status;
1042 zend_hash_add(&php_pqres_object_prophandlers, "status", sizeof("status"), (void *) &ph, sizeof(ph), NULL);
1043
1044 zend_declare_property_null(php_pqres_class_entry, ZEND_STRL("statusMessage"), ZEND_ACC_PUBLIC TSRMLS_CC);
1045 ph.read = php_pqres_object_read_status_message;
1046 zend_hash_add(&php_pqres_object_prophandlers, "statusMessage", sizeof("statusMessage"), (void *) &ph, sizeof(ph), NULL);
1047
1048 zend_declare_property_null(php_pqres_class_entry, ZEND_STRL("errorMessage"), ZEND_ACC_PUBLIC TSRMLS_CC);
1049 ph.read = php_pqres_object_read_error_message;
1050 zend_hash_add(&php_pqres_object_prophandlers, "errorMessage", sizeof("errorMessage"), (void *) &ph, sizeof(ph), NULL);
1051
1052 zend_declare_property_long(php_pqres_class_entry, ZEND_STRL("numRows"), 0, ZEND_ACC_PUBLIC TSRMLS_CC);
1053 ph.read = php_pqres_object_read_num_rows;
1054 zend_hash_add(&php_pqres_object_prophandlers, "numRows", sizeof("numRows"), (void *) &ph, sizeof(ph), NULL);
1055
1056 zend_declare_property_long(php_pqres_class_entry, ZEND_STRL("numCols"), 0, ZEND_ACC_PUBLIC TSRMLS_CC);
1057 ph.read = php_pqres_object_read_num_cols;
1058 zend_hash_add(&php_pqres_object_prophandlers, "numCols", sizeof("numCols"), (void *) &ph, sizeof(ph), NULL);
1059
1060 zend_declare_property_long(php_pqres_class_entry, ZEND_STRL("affectedRows"), 0, ZEND_ACC_PUBLIC TSRMLS_CC);
1061 ph.read = php_pqres_object_read_affected_rows;
1062 zend_hash_add(&php_pqres_object_prophandlers, "affectedRows", sizeof("affectedRows"), (void *) &ph, sizeof(ph), NULL);
1063
1064 zend_declare_property_long(php_pqres_class_entry, ZEND_STRL("fetchType"), PHP_PQRES_FETCH_ARRAY, ZEND_ACC_PUBLIC TSRMLS_CC);
1065 ph.read = php_pqres_object_read_fetch_type;
1066 ph.write = php_pqres_object_write_fetch_type;
1067 zend_hash_add(&php_pqres_object_prophandlers, "fetchType", sizeof("fetchType"), (void *) &ph, sizeof(ph), NULL);
1068 ph.write = NULL;
1069
1070 zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("EMPTY_QUERY"), PGRES_EMPTY_QUERY TSRMLS_CC);
1071 zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("COMMAND_OK"), PGRES_COMMAND_OK TSRMLS_CC);
1072 zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("TUPLES_OK"), PGRES_TUPLES_OK TSRMLS_CC);
1073 zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("COPY_OUT"), PGRES_COPY_OUT TSRMLS_CC);
1074 zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("COPY_IN"), PGRES_COPY_IN TSRMLS_CC);
1075 zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("BAD_RESPONSE"), PGRES_BAD_RESPONSE TSRMLS_CC);
1076 zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("NONFATAL_ERROR"), PGRES_NONFATAL_ERROR TSRMLS_CC);
1077 zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("FATAL_ERROR"), PGRES_FATAL_ERROR TSRMLS_CC);
1078 zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("COPY_BOTH"), PGRES_COPY_BOTH TSRMLS_CC);
1079 zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("SINGLE_TUPLE"), PGRES_SINGLE_TUPLE TSRMLS_CC);
1080
1081 zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("FETCH_ARRAY"), PHP_PQRES_FETCH_ARRAY TSRMLS_CC);
1082 zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("FETCH_ASSOC"), PHP_PQRES_FETCH_ASSOC TSRMLS_CC);
1083 zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("FETCH_OBJECT"), PHP_PQRES_FETCH_OBJECT TSRMLS_CC);
1084
1085 return SUCCESS;
1086 }
1087
1088 /*
1089 * Local variables:
1090 * tab-width: 4
1091 * c-basic-offset: 4
1092 * End:
1093 * vim600: noet sw=4 ts=4 fdm=marker
1094 * vim<600: noet sw=4 ts=4
1095 */