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