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