2 +--------------------------------------------------------------------+
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 +--------------------------------------------------------------------+
18 #include <ext/standard/php_smart_str.h>
21 #include "php_pq_misc.h"
22 #include "php_pq_object.h"
23 #include "php_pqexc.h"
24 #include "php_pqconn.h"
25 #include "php_pqres.h"
26 #include "php_pqcur.h"
28 zend_class_entry
*php_pqcur_class_entry
;
29 static zend_object_handlers php_pqcur_object_handlers
;
30 static HashTable php_pqcur_object_prophandlers
;
32 static void cur_close(php_pqcur_object_t
*obj
, zend_bool async
, zend_bool silent TSRMLS_DC
)
34 if (obj
->intern
->open
&& obj
->intern
->conn
->intern
) {
38 smart_str_appends(&cmd
, "CLOSE ");
39 smart_str_appends(&cmd
, obj
->intern
->name
);
43 if (PQsendQuery(obj
->intern
->conn
->intern
->conn
, cmd
.c
)) {
44 obj
->intern
->conn
->intern
->poller
= PQconsumeInput
;
45 php_pqconn_notify_listeners(obj
->intern
->conn TSRMLS_CC
);
47 throw_exce(EX_IO TSRMLS_CC
, "Failed to close cursor (%s)", PHP_PQerrorMessage(obj
->intern
->conn
->intern
->conn
));
50 if ((res
= php_pq_exec(obj
->intern
->conn
->intern
->conn
, cmd
.c
))) {
53 throw_exce(EX_RUNTIME TSRMLS_CC
, "Failed to close cursor (%s)", PHP_PQerrorMessage(obj
->intern
->conn
->intern
->conn
));
58 obj
->intern
->open
= 0;
62 static void cur_open(INTERNAL_FUNCTION_PARAMETERS
, zend_bool async
)
64 zend_error_handling zeh
;
66 php_pqcur_object_t
*obj
;
68 zend_replace_error_handling(EH_THROW
, exce(EX_INVALID_ARGUMENT
), &zeh TSRMLS_CC
);
69 rv
= zend_parse_parameters_none();
70 zend_restore_error_handling(&zeh TSRMLS_CC
);
76 obj
= zend_object_store_get_object(getThis() TSRMLS_CC
);
79 throw_exce(EX_UNINITIALIZED TSRMLS_CC
, "pq\\Cursor not initialized");
81 } else if (obj
->intern
->open
) {
86 rv
= php_pqconn_declare_async(NULL
, obj
->intern
->conn
, obj
->intern
->decl TSRMLS_CC
);
88 rv
= php_pqconn_declare(NULL
, obj
->intern
->conn
, obj
->intern
->decl TSRMLS_CC
);
92 obj
->intern
->open
= 1;
96 static void cur_fetch_or_move(INTERNAL_FUNCTION_PARAMETERS
, const char *action
, zend_bool async
)
101 php_pq_callback_t resolver
= {{0}};
102 zend_error_handling zeh
;
104 zend_replace_error_handling(EH_THROW
, exce(EX_INVALID_ARGUMENT
), &zeh TSRMLS_CC
);
105 rv
= zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, async
? "|sf" : "|s", &spec_str
, &spec_len
, &resolver
.fci
, &resolver
.fcc
);
106 zend_restore_error_handling(&zeh TSRMLS_CC
);
109 php_pqcur_object_t
*obj
= zend_object_store_get_object(getThis() TSRMLS_CC
);
112 throw_exce(EX_UNINITIALIZED TSRMLS_CC
, "pq\\Cursor not initialized");
116 smart_str_appends(&cmd
, *action
== 'f' ? "FETCH " : "MOVE ");
117 smart_str_appendl(&cmd
, spec_str
, spec_len
);
118 smart_str_appends(&cmd
, " FROM ");
119 smart_str_appends(&cmd
, obj
->intern
->name
);
123 int rc
= PQsendQuery(obj
->intern
->conn
->intern
->conn
, cmd
.c
);
126 throw_exce(EX_IO TSRMLS_CC
, "Failed to %s cursor (%s)", *action
== 'f' ? "fetch from" : "move in", PHP_PQerrorMessage(obj
->intern
->conn
->intern
->conn
));
127 #if HAVE_PQSETSINGLEROWMODE
128 } else if (obj
->intern
->conn
->intern
->unbuffered
&& !PQsetSingleRowMode(obj
->intern
->conn
->intern
->conn
)) {
129 throw_exce(EX_RUNTIME TSRMLS_CC
, "Failed to enable unbuffered mode (%s)", PHP_PQerrorMessage(obj
->intern
->conn
->intern
->conn
));
132 php_pq_callback_recurse(&obj
->intern
->conn
->intern
->onevent
, &resolver TSRMLS_CC
);
133 obj
->intern
->conn
->intern
->poller
= PQconsumeInput
;
136 PGresult
*res
= php_pq_exec(obj
->intern
->conn
->intern
->conn
, cmd
.c
);
139 throw_exce(EX_RUNTIME TSRMLS_CC
, "Failed to %s cursor (%s)", *action
== 'f' ? "fetch from" : "move in", PHP_PQerrorMessage(obj
->intern
->conn
->intern
->conn
));
140 } else if (SUCCESS
== php_pqres_success(res TSRMLS_CC
)) {
141 php_pq_object_to_zval_no_addref(PQresultInstanceData(res
, php_pqconn_event
), &return_value TSRMLS_CC
);
145 smart_str_free(&cmd
);
146 php_pqconn_notify_listeners(obj
->intern
->conn TSRMLS_CC
);
151 static void php_pqcur_object_free(void *o TSRMLS_DC
)
153 php_pqcur_object_t
*obj
= o
;
155 fprintf(stderr
, "FREE cur(#%d) %p (conn: %p)\n", obj
->zv
.handle
, obj
, obj
->intern
->conn
);
158 cur_close(obj
, 0, 1 TSRMLS_CC
);
159 php_pq_object_delref(obj
->intern
->conn TSRMLS_CC
);
160 efree(obj
->intern
->decl
);
161 efree(obj
->intern
->name
);
165 zend_object_std_dtor((zend_object
*) o TSRMLS_CC
);
169 zend_object_value
php_pqcur_create_object_ex(zend_class_entry
*ce
, php_pqcur_t
*intern
, php_pqcur_object_t
**ptr TSRMLS_DC
)
171 php_pqcur_object_t
*o
;
173 o
= ecalloc(1, sizeof(*o
));
174 zend_object_std_init((zend_object
*) o
, ce TSRMLS_CC
);
175 object_properties_init((zend_object
*) o
, ce
);
176 o
->prophandler
= &php_pqcur_object_prophandlers
;
186 o
->zv
.handle
= zend_objects_store_put((zend_object
*) o
, NULL
, php_pqcur_object_free
, NULL TSRMLS_CC
);
187 o
->zv
.handlers
= &php_pqcur_object_handlers
;
192 static zend_object_value
php_pqcur_create_object(zend_class_entry
*class_type TSRMLS_DC
)
194 return php_pqcur_create_object_ex(class_type
, NULL
, NULL TSRMLS_CC
);
197 static void php_pqcur_object_read_name(zval
*object
, void *o
, zval
*return_value TSRMLS_DC
)
199 php_pqcur_object_t
*obj
= o
;
201 RETVAL_STRING(obj
->intern
->name
, 1);
204 static void php_pqcur_object_read_connection(zval
*object
, void *o
, zval
*return_value TSRMLS_DC
)
206 php_pqcur_object_t
*obj
= o
;
208 php_pq_object_to_zval(obj
->intern
->conn
, &return_value TSRMLS_CC
);
211 static void php_pqcur_object_read_query(zval
*object
, void *o
, zval
*return_value TSRMLS_DC
)
213 php_pqcur_object_t
*obj
= o
;
215 RETVAL_STRING(obj
->intern
->decl
+ obj
->intern
->query_offset
, 1);
218 static void php_pqcur_object_read_flags(zval
*object
, void *o
, zval
*return_value TSRMLS_DC
)
220 php_pqcur_object_t
*obj
= o
;
222 RETVAL_LONG(obj
->intern
->flags
);
225 char *php_pqcur_declare_str(const char *name_str
, size_t name_len
, unsigned flags
, const char *query_str
, size_t query_len
, int *query_offset
)
227 size_t decl_len
= name_len
+ query_len
+ sizeof("DECLARE BINARY INSENSITIVE NO SCROLL CURSOR WITH HOLD FOR ");
230 decl_str
= emalloc(decl_len
);
231 decl_len
= slprintf(decl_str
, decl_len
, "DECLARE %s %s %s %s CURSOR %s FOR %s",
233 (flags
& PHP_PQ_DECLARE_BINARY
) ? "BINARY" : "",
234 (flags
& PHP_PQ_DECLARE_INSENSITIVE
) ? "INSENSITIVE" : "",
235 (flags
& PHP_PQ_DECLARE_NO_SCROLL
) ? "NO SCROLL" :
236 (flags
& PHP_PQ_DECLARE_SCROLL
) ? "SCROLL" : "",
237 (flags
& PHP_PQ_DECLARE_WITH_HOLD
) ? "WITH HOLD" : "",
242 /* sizeof() includes the terminating null byte, so no need for spaces in the string literals */
243 *query_offset
= sizeof("DECLARE")
245 + ((flags
& PHP_PQ_DECLARE_BINARY
) ? sizeof("BINARY") : 1)
246 + ((flags
& PHP_PQ_DECLARE_INSENSITIVE
) ? sizeof("INSENSITIVE") : 1)
247 + ((flags
& PHP_PQ_DECLARE_NO_SCROLL
) ? sizeof("NO SCROLL") :
248 (flags
& PHP_PQ_DECLARE_SCROLL
) ? sizeof("SCROLL") : 1)
250 + ((flags
& PHP_PQ_DECLARE_WITH_HOLD
) ? sizeof("WITH HOLD") : 1)
257 php_pqcur_t
*php_pqcur_init(php_pqconn_object_t
*conn
, const char *name
, char *decl
, int query_offset
, long flags TSRMLS_DC
)
259 php_pqcur_t
*cur
= ecalloc(1, sizeof(*cur
));
261 php_pq_object_addref(conn TSRMLS_CC
);
263 cur
->name
= estrdup(name
);
265 cur
->query_offset
= query_offset
;
272 ZEND_BEGIN_ARG_INFO_EX(ai_pqcur___construct
, 0, 0, 4)
273 ZEND_ARG_OBJ_INFO(0, connection
, pq
\\Connection
, 0)
274 ZEND_ARG_INFO(0, name
)
275 ZEND_ARG_INFO(0, flags
)
276 ZEND_ARG_INFO(0, query
)
277 ZEND_ARG_INFO(0, async
)
279 static PHP_METHOD(pqcur
, __construct
) {
280 zend_error_handling zeh
;
281 char *name_str
, *query_str
;
282 int name_len
, query_len
;
288 zend_replace_error_handling(EH_THROW
, exce(EX_INVALID_ARGUMENT
), &zeh TSRMLS_CC
);
289 rv
= zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "Osls|b", &zconn
, php_pqconn_class_entry
, &name_str
, &name_len
, &flags
, &query_str
, &query_len
, &async
);
290 zend_restore_error_handling(&zeh TSRMLS_CC
);
293 php_pqcur_object_t
*obj
= zend_object_store_get_object(getThis() TSRMLS_CC
);
294 php_pqconn_object_t
*conn_obj
= zend_object_store_get_object(zconn TSRMLS_CC
);
297 throw_exce(EX_BAD_METHODCALL TSRMLS_CC
, "pq\\Cursor already initialized");
298 } if (!conn_obj
->intern
) {
299 throw_exce(EX_UNINITIALIZED TSRMLS_CC
, "pq\\Connection not initialized");
302 char *decl
= php_pqcur_declare_str(name_str
, name_len
, flags
, query_str
, query_len
, &query_offset
);
305 rv
= php_pqconn_declare_async(zconn
, conn_obj
, decl TSRMLS_CC
);
307 rv
= php_pqconn_declare(zconn
, conn_obj
, decl TSRMLS_CC
);
313 obj
->intern
= php_pqcur_init(conn_obj
, name_str
, decl
, query_offset
, flags TSRMLS_CC
);
319 ZEND_BEGIN_ARG_INFO_EX(ai_pqcur_open
, 0, 0, 0)
321 static PHP_METHOD(pqcur
, open
)
323 cur_open(INTERNAL_FUNCTION_PARAM_PASSTHRU
, 0);
326 ZEND_BEGIN_ARG_INFO_EX(ai_pqcur_openAsync
, 0, 0, 0)
328 static PHP_METHOD(pqcur
, openAsync
)
330 cur_open(INTERNAL_FUNCTION_PARAM_PASSTHRU
, 1);
333 ZEND_BEGIN_ARG_INFO_EX(ai_pqcur_close
, 0, 0, 0)
335 static PHP_METHOD(pqcur
, close
)
337 zend_error_handling zeh
;
340 zend_replace_error_handling(EH_THROW
, exce(EX_INVALID_ARGUMENT
), &zeh TSRMLS_CC
);
341 rv
= zend_parse_parameters_none();
342 zend_restore_error_handling(&zeh TSRMLS_CC
);
345 php_pqcur_object_t
*obj
= zend_object_store_get_object(getThis() TSRMLS_CC
);
348 throw_exce(EX_UNINITIALIZED TSRMLS_CC
, "pq\\Cursor not initialized");
350 cur_close(obj
, 0, 0 TSRMLS_CC
);
355 ZEND_BEGIN_ARG_INFO_EX(ai_pqcur_closeAsync
, 0, 0, 0)
357 static PHP_METHOD(pqcur
, closeAsync
)
359 zend_error_handling zeh
;
362 zend_replace_error_handling(EH_THROW
, exce(EX_INVALID_ARGUMENT
), &zeh TSRMLS_CC
);
363 rv
= zend_parse_parameters_none();
364 zend_restore_error_handling(&zeh TSRMLS_CC
);
367 php_pqcur_object_t
*obj
= zend_object_store_get_object(getThis() TSRMLS_CC
);
370 throw_exce(EX_UNINITIALIZED TSRMLS_CC
, "pq\\Cursor not initialized");
372 cur_close(obj
, 1, 0 TSRMLS_CC
);
377 ZEND_BEGIN_ARG_INFO_EX(ai_pqcur_fetch
, 0, 0, 1)
378 ZEND_ARG_INFO(0, spec
)
380 static PHP_METHOD(pqcur
, fetch
)
382 cur_fetch_or_move(INTERNAL_FUNCTION_PARAM_PASSTHRU
, "fetch", 0);
385 ZEND_BEGIN_ARG_INFO_EX(ai_pqcur_move
, 0, 0, 0)
386 ZEND_ARG_INFO(0, spec
)
388 static PHP_METHOD(pqcur
, move
)
390 cur_fetch_or_move(INTERNAL_FUNCTION_PARAM_PASSTHRU
, "move", 0);
393 ZEND_BEGIN_ARG_INFO_EX(ai_pqcur_fetchAsync
, 0, 0, 0)
394 ZEND_ARG_INFO(0, spec
)
395 ZEND_ARG_INFO(0, callback
)
397 static PHP_METHOD(pqcur
, fetchAsync
)
399 cur_fetch_or_move(INTERNAL_FUNCTION_PARAM_PASSTHRU
, "fetch", 1);
402 ZEND_BEGIN_ARG_INFO_EX(ai_pqcur_moveAsync
, 0, 0, 0)
403 ZEND_ARG_INFO(0, spec
)
404 ZEND_ARG_INFO(0, callback
)
406 static PHP_METHOD(pqcur
, moveAsync
)
408 cur_fetch_or_move(INTERNAL_FUNCTION_PARAM_PASSTHRU
, "move", 1);
411 static zend_function_entry php_pqcur_methods
[] = {
412 PHP_ME(pqcur
, __construct
, ai_pqcur___construct
, ZEND_ACC_PUBLIC
|ZEND_ACC_CTOR
)
413 PHP_ME(pqcur
, open
, ai_pqcur_open
, ZEND_ACC_PUBLIC
)
414 PHP_ME(pqcur
, openAsync
, ai_pqcur_open
, ZEND_ACC_PUBLIC
)
415 PHP_ME(pqcur
, close
, ai_pqcur_close
, ZEND_ACC_PUBLIC
)
416 PHP_ME(pqcur
, closeAsync
, ai_pqcur_closeAsync
, ZEND_ACC_PUBLIC
)
417 PHP_ME(pqcur
, fetch
, ai_pqcur_fetch
, ZEND_ACC_PUBLIC
)
418 PHP_ME(pqcur
, move
, ai_pqcur_move
, ZEND_ACC_PUBLIC
)
419 PHP_ME(pqcur
, fetchAsync
, ai_pqcur_fetchAsync
, ZEND_ACC_PUBLIC
)
420 PHP_ME(pqcur
, moveAsync
, ai_pqcur_moveAsync
, ZEND_ACC_PUBLIC
)
424 PHP_MSHUTDOWN_FUNCTION(pqcur
)
426 zend_hash_destroy(&php_pqcur_object_prophandlers
);
430 PHP_MINIT_FUNCTION(pqcur
)
432 zend_class_entry ce
= {0};
433 php_pq_object_prophandler_t ph
= {0};
435 INIT_NS_CLASS_ENTRY(ce
, "pq", "Cursor", php_pqcur_methods
);
436 php_pqcur_class_entry
= zend_register_internal_class_ex(&ce
, NULL
, NULL TSRMLS_CC
);
437 php_pqcur_class_entry
->create_object
= php_pqcur_create_object
;
439 memcpy(&php_pqcur_object_handlers
, zend_get_std_object_handlers(), sizeof(zend_object_handlers
));
440 php_pqcur_object_handlers
.read_property
= php_pq_object_read_prop
;
441 php_pqcur_object_handlers
.write_property
= php_pq_object_write_prop
;
442 php_pqcur_object_handlers
.clone_obj
= NULL
;
443 php_pqcur_object_handlers
.get_property_ptr_ptr
= NULL
;
444 php_pqcur_object_handlers
.get_gc
= NULL
;
445 php_pqcur_object_handlers
.get_properties
= php_pq_object_properties
;
446 php_pqcur_object_handlers
.get_debug_info
= php_pq_object_debug_info
;
448 zend_hash_init(&php_pqcur_object_prophandlers
, 4, NULL
, NULL
, 1);
450 zend_declare_class_constant_long(php_pqcur_class_entry
, ZEND_STRL("BINARY"), PHP_PQ_DECLARE_BINARY TSRMLS_CC
);
451 zend_declare_class_constant_long(php_pqcur_class_entry
, ZEND_STRL("INSENSITIVE"), PHP_PQ_DECLARE_INSENSITIVE TSRMLS_CC
);
452 zend_declare_class_constant_long(php_pqcur_class_entry
, ZEND_STRL("WITH_HOLD"), PHP_PQ_DECLARE_WITH_HOLD TSRMLS_CC
);
453 zend_declare_class_constant_long(php_pqcur_class_entry
, ZEND_STRL("SCROLL"), PHP_PQ_DECLARE_SCROLL TSRMLS_CC
);
454 zend_declare_class_constant_long(php_pqcur_class_entry
, ZEND_STRL("NO_SCROLL"), PHP_PQ_DECLARE_NO_SCROLL TSRMLS_CC
);
456 zend_declare_property_null(php_pqcur_class_entry
, ZEND_STRL("name"), ZEND_ACC_PUBLIC TSRMLS_CC
);
457 ph
.read
= php_pqcur_object_read_name
;
458 zend_hash_add(&php_pqcur_object_prophandlers
, "name", sizeof("name"), (void *) &ph
, sizeof(ph
), NULL
);
460 zend_declare_property_null(php_pqcur_class_entry
, ZEND_STRL("connection"), ZEND_ACC_PUBLIC TSRMLS_CC
);
461 ph
.read
= php_pqcur_object_read_connection
;
462 zend_hash_add(&php_pqcur_object_prophandlers
, "connection", sizeof("connection"), (void *) &ph
, sizeof(ph
), NULL
);
464 zend_declare_property_null(php_pqcur_class_entry
, ZEND_STRL("query"), ZEND_ACC_PUBLIC TSRMLS_CC
);
465 ph
.read
= php_pqcur_object_read_query
;
466 zend_hash_add(&php_pqcur_object_prophandlers
, "query", sizeof("query"), (void *) &ph
, sizeof(ph
), NULL
);
468 zend_declare_property_null(php_pqcur_class_entry
, ZEND_STRL("flags"), ZEND_ACC_PUBLIC TSRMLS_CC
);
469 ph
.read
= php_pqcur_object_read_flags
;
470 zend_hash_add(&php_pqcur_object_prophandlers
, "flags", sizeof("flags"), (void *) &ph
, sizeof(ph
), NULL
);
480 * vim600: noet sw=4 ts=4 fdm=marker
481 * vim<600: noet sw=4 ts=4