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