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