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