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