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