1
0
mirror of https://github.com/danog/ext-pq.git synced 2024-11-30 04:19:49 +01:00

add cursor support

This commit is contained in:
Michael Wallner 2014-04-04 15:48:20 +02:00
parent 316c62ff10
commit 9255d2d54b
14 changed files with 663 additions and 13 deletions

1
TODO
View File

@ -1,4 +1,5 @@
* COPY: getAsync & putAsync * COPY: getAsync & putAsync
* CURSOR: *Async()
* fetchInto/fetchCtor? * fetchInto/fetchCtor?
* unlisten? * unlisten?
* unregister event handler? * unregister event handler?

View File

@ -50,6 +50,7 @@ if test "$PHP_PQ" != "no"; then
src/php_pqstm.c\ src/php_pqstm.c\
src/php_pqtxn.c\ src/php_pqtxn.c\
src/php_pqtypes.c\ src/php_pqtypes.c\
src/php_pqcur.c\
" "
PHP_NEW_EXTENSION(pq, $PQ_SRC, $ext_shared) PHP_NEW_EXTENSION(pq, $PQ_SRC, $ext_shared)
PHP_ADD_BUILD_DIR($ext_builddir/src) PHP_ADD_BUILD_DIR($ext_builddir/src)

View File

@ -23,9 +23,9 @@ zend_module_entry pq_module_entry;
#ifdef PHP_WIN32 #ifdef PHP_WIN32
# define PHP_PQ_API __declspec(dllexport) # define PHP_PQ_API __declspec(dllexport)
#elif defined(__GNUC__) && __GNUC__ >= 4 #elif defined(__GNUC__) && __GNUC__ >= 4
# define PHP_PQ_API __attribute__ ((visibility("default"))) # define PHP_PQ_API extern __attribute__ ((visibility("default")))
#else #else
# define PHP_PQ_API # define PHP_PQ_API extern
#endif #endif
#ifdef ZTS #ifdef ZTS

View File

@ -15,19 +15,25 @@
#endif #endif
#include <php.h> #include <php.h>
#include <Zend/zend_closures.h>
#include "php_pq_callback.h" #include "php_pq_callback.h"
void php_pq_callback_dtor(php_pq_callback_t *cb) void php_pq_callback_dtor(php_pq_callback_t *cb)
{ {
if (cb->recursion) {
php_pq_callback_dtor(cb->recursion);
efree(cb->recursion);
cb->recursion = NULL;
}
if (cb->fci.size > 0) { if (cb->fci.size > 0) {
zend_fcall_info_args_clear(&cb->fci, 1); zend_fcall_info_args_clear(&cb->fci, 1);
zval_ptr_dtor(&cb->fci.function_name); zval_ptr_dtor(&cb->fci.function_name);
if (cb->fci.object_ptr) { if (cb->fci.object_ptr) {
zval_ptr_dtor(&cb->fci.object_ptr); zval_ptr_dtor(&cb->fci.object_ptr);
} }
cb->fci.size = 0;
} }
cb->fci.size = 0;
} }
void php_pq_callback_addref(php_pq_callback_t *cb) void php_pq_callback_addref(php_pq_callback_t *cb)
@ -55,6 +61,41 @@ zval *php_pq_callback_to_zval(php_pq_callback_t *cb)
return zcb; return zcb;
} }
zend_bool php_pq_callback_is_locked(php_pq_callback_t *cb TSRMLS_DC)
{
if (cb->fci.size > 0 && Z_TYPE_P(cb->fci.function_name) == IS_OBJECT) {
const zend_function *closure = zend_get_closure_method_def(cb->fci.function_name TSRMLS_CC);
if (closure->type == ZEND_USER_FUNCTION) {
zend_execute_data *ex = EG(current_execute_data);
while (ex) {
if (ex->op_array == &closure->op_array) {
return 1;
}
ex = ex->prev_execute_data;
}
}
}
return 0;
}
void php_pq_callback_recurse(php_pq_callback_t *old, php_pq_callback_t *new TSRMLS_DC)
{
if (new && new->fci.size > 0 && php_pq_callback_is_locked(old TSRMLS_CC)) {
new->recursion = emalloc(sizeof(*old));
memcpy(new->recursion, old, sizeof(*old));
} else if (new && new->fci.size > 0) {
php_pq_callback_dtor(old);
php_pq_callback_addref(new);
memcpy(old, new, sizeof(*old));
new->fci.size = 0;
} else {
php_pq_callback_dtor(old);
}
}
/* /*
* Local variables: * Local variables:
* tab-width: 4 * tab-width: 4

View File

@ -18,12 +18,14 @@
typedef struct php_pq_callback { typedef struct php_pq_callback {
zend_fcall_info fci; zend_fcall_info fci;
zend_fcall_info_cache fcc; zend_fcall_info_cache fcc;
void *data; struct php_pq_callback *recursion;
} php_pq_callback_t; } php_pq_callback_t;
void php_pq_callback_dtor(php_pq_callback_t *cb); void php_pq_callback_dtor(php_pq_callback_t *cb);
void php_pq_callback_addref(php_pq_callback_t *cb); void php_pq_callback_addref(php_pq_callback_t *cb);
zval *php_pq_callback_to_zval(php_pq_callback_t *cb); zval *php_pq_callback_to_zval(php_pq_callback_t *cb);
zend_bool php_pq_callback_is_locked(php_pq_callback_t *cb);
void php_pq_callback_recurse(php_pq_callback_t *old, php_pq_callback_t *new TSRMLS_DC);
#endif #endif

View File

@ -62,6 +62,7 @@ static PHP_MINIT_FUNCTION(pq)
PHP_MINIT_CALL(pqres); PHP_MINIT_CALL(pqres);
PHP_MINIT_CALL(pqstm); PHP_MINIT_CALL(pqstm);
PHP_MINIT_CALL(pqtxn); PHP_MINIT_CALL(pqtxn);
PHP_MINIT_CALL(pqcur);
PHP_MINIT_CALL(pqcopy); PHP_MINIT_CALL(pqcopy);
PHP_MINIT_CALL(pqlob); PHP_MINIT_CALL(pqlob);

View File

@ -31,6 +31,7 @@
#include "php_pqres.h" #include "php_pqres.h"
#include "php_pqstm.h" #include "php_pqstm.h"
#include "php_pqtxn.h" #include "php_pqtxn.h"
#include "php_pqcur.h"
zend_class_entry *php_pqconn_class_entry; zend_class_entry *php_pqconn_class_entry;
static zend_object_handlers php_pqconn_object_handlers; static zend_object_handlers php_pqconn_object_handlers;
@ -73,9 +74,9 @@ static void php_pqconn_object_free(void *o TSRMLS_DC)
fprintf(stderr, "FREE conn(#%d) %p\n", obj->zv.handle, obj); fprintf(stderr, "FREE conn(#%d) %p\n", obj->zv.handle, obj);
#endif #endif
if (obj->intern) { if (obj->intern) {
php_pq_callback_dtor(&obj->intern->onevent);
php_resource_factory_handle_dtor(&obj->intern->factory, obj->intern->conn TSRMLS_CC); php_resource_factory_handle_dtor(&obj->intern->factory, obj->intern->conn TSRMLS_CC);
php_resource_factory_dtor(&obj->intern->factory); php_resource_factory_dtor(&obj->intern->factory);
php_pq_callback_dtor(&obj->intern->onevent);
zend_hash_destroy(&obj->intern->listeners); zend_hash_destroy(&obj->intern->listeners);
zend_hash_destroy(&obj->intern->converters); zend_hash_destroy(&obj->intern->converters);
zend_hash_destroy(&obj->intern->eventhandlers); zend_hash_destroy(&obj->intern->eventhandlers);
@ -491,7 +492,7 @@ static void php_pqconn_retire(php_persistent_handle_factory_t *f, void **handle
zend_hash_apply_with_arguments(&evdata->obj->intern->listeners TSRMLS_CC, apply_unlisten, 1, evdata->obj); zend_hash_apply_with_arguments(&evdata->obj->intern->listeners TSRMLS_CC, apply_unlisten, 1, evdata->obj);
/* release instance data */ /* release instance data */
memset(evdata, 0, sizeof(*evdata)); //memset(evdata, 0, sizeof(*evdata));
efree(evdata); efree(evdata);
} }
} }
@ -626,7 +627,7 @@ static PHP_METHOD(pqconn, listen) {
zend_error_handling zeh; zend_error_handling zeh;
char *channel_str = NULL; char *channel_str = NULL;
int channel_len = 0; int channel_len = 0;
php_pq_callback_t listener; php_pq_callback_t listener = {{0}};
STATUS rv; STATUS rv;
zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
@ -680,7 +681,7 @@ static PHP_METHOD(pqconn, listenAsync) {
zend_error_handling zeh; zend_error_handling zeh;
char *channel_str = NULL; char *channel_str = NULL;
int channel_len = 0; int channel_len = 0;
php_pq_callback_t listener; php_pq_callback_t listener = {{0}};
STATUS rv; STATUS rv;
zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
@ -1035,7 +1036,7 @@ STATUS php_pqconn_prepare(zval *object, php_pqconn_object_t *obj, const char *na
} }
ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_prepare, 0, 0, 2) ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_prepare, 0, 0, 2)
ZEND_ARG_INFO(0, type) ZEND_ARG_INFO(0, name)
ZEND_ARG_INFO(0, query) ZEND_ARG_INFO(0, query)
ZEND_ARG_ARRAY_INFO(0, types, 1) ZEND_ARG_ARRAY_INFO(0, types, 1)
ZEND_END_ARG_INFO(); ZEND_END_ARG_INFO();
@ -1100,7 +1101,7 @@ STATUS php_pqconn_prepare_async(zval *object, php_pqconn_object_t *obj, const ch
} }
ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_prepare_async, 0, 0, 2) ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_prepare_async, 0, 0, 2)
ZEND_ARG_INFO(0, type) ZEND_ARG_INFO(0, name)
ZEND_ARG_INFO(0, query) ZEND_ARG_INFO(0, query)
ZEND_ARG_ARRAY_INFO(0, types, 1) ZEND_ARG_ARRAY_INFO(0, types, 1)
ZEND_END_ARG_INFO(); ZEND_END_ARG_INFO();
@ -1141,6 +1142,154 @@ static PHP_METHOD(pqconn, prepareAsync) {
} }
} }
static inline char *declare_str(const char *name_str, size_t name_len, unsigned flags, const char *query_str, size_t query_len)
{
size_t decl_len = name_len + query_len + sizeof("DECLARE BINARY INSENSITIVE NO SCROLL CURSOR WITHOUT HOLD FOR ");
char *decl_str;
decl_str = emalloc(decl_len);
decl_len = slprintf(decl_str, decl_len, "DECLARE %s %s %s %s CURSOR %s FOR %s",
name_str,
(flags & PHP_PQ_DECLARE_BINARY) ? "BINARY" : "",
(flags & PHP_PQ_DECLARE_INSENSITIVE) ? "INSENSITIVE" : "",
(flags & PHP_PQ_DECLARE_NO_SCROLL) ? "NO SCROLL" :
(flags & PHP_PQ_DECLARE_SCROLL) ? "SCROLL" : "",
(flags & PHP_PQ_DECLARE_WITH_HOLD) ? "WITH HOLD" : "",
query_str
);
return decl_str;
}
STATUS php_pqconn_declare(zval *object, php_pqconn_object_t *obj, const char *decl TSRMLS_DC)
{
PGresult *res;
STATUS rv;
if (!obj) {
obj = zend_object_store_get_object(object TSRMLS_CC);
}
res = PQexec(obj->intern->conn, decl);
if (!res) {
rv = FAILURE;
throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to declare cursor (%s)", PHP_PQerrorMessage(obj->intern->conn));
} else {
rv = php_pqres_success(res TSRMLS_CC);
PHP_PQclear(res);
php_pqconn_notify_listeners(obj TSRMLS_CC);
}
return rv;
}
ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_declare, 0, 0, 3)
ZEND_ARG_INFO(0, name)
ZEND_ARG_INFO(0, flags)
ZEND_ARG_INFO(0, query)
ZEND_END_ARG_INFO();
static PHP_METHOD(pqconn, declare) {
zend_error_handling zeh;
char *name_str, *query_str;
int name_len, query_len;
long flags;
STATUS rv;
zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sls", &name_str, &name_len, &flags, &query_str, &query_len);
zend_restore_error_handling(&zeh TSRMLS_CC);
if (SUCCESS == rv) {
php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
if (!obj->intern) {
throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
} else {
char *decl = declare_str(name_str, name_len, flags, query_str, query_len);
if (SUCCESS != php_pqconn_declare(getThis(), obj, decl TSRMLS_CC)) {
efree(decl);
} else {
php_pqcur_t *cur = ecalloc(1, sizeof(*cur));
php_pq_object_addref(obj TSRMLS_CC);
cur->conn = obj;
cur->open = 1;
cur->name = estrdup(name_str);
cur->decl = decl;
return_value->type = IS_OBJECT;
return_value->value.obj = php_pqcur_create_object_ex(php_pqcur_class_entry, cur, NULL TSRMLS_CC);
}
}
}
}
STATUS php_pqconn_declare_async(zval *object, php_pqconn_object_t *obj, const char *decl TSRMLS_DC)
{
STATUS rv;
if (!obj) {
obj = zend_object_store_get_object(object TSRMLS_CC);
}
if (!PQsendQuery(obj->intern->conn, decl)) {
rv = FAILURE;
throw_exce(EX_IO TSRMLS_CC, "Failed to declare cursor (%s)", PHP_PQerrorMessage(obj->intern->conn));
} else if (obj->intern->unbuffered && !PQsetSingleRowMode(obj->intern->conn)) {
rv = FAILURE;
throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to enable unbuffered mode (%s)", PHP_PQerrorMessage(obj->intern->conn));
} else {
rv = SUCCESS;
obj->intern->poller = PQconsumeInput;
php_pqconn_notify_listeners(obj TSRMLS_CC);
}
return rv;
}
ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_declare_async, 0, 0, 2)
ZEND_ARG_INFO(0, name)
ZEND_ARG_INFO(0, flags)
ZEND_ARG_INFO(0, query)
ZEND_END_ARG_INFO();
static PHP_METHOD(pqconn, declareAsync) {
zend_error_handling zeh;
char *name_str, *query_str;
int name_len, query_len;
long flags;
STATUS rv;
zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sls", &name_str, &name_len, &flags, &query_str, &query_len);
zend_restore_error_handling(&zeh TSRMLS_CC);
if (SUCCESS == rv) {
php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
if (!obj->intern) {
throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
} else {
char *decl = declare_str(name_str, name_len, flags, query_str, query_len);
if (SUCCESS != php_pqconn_declare_async(getThis(), obj, decl TSRMLS_CC)) {
efree(decl);
} else {
php_pqcur_t *cur = ecalloc(1, sizeof(*cur));
php_pq_object_addref(obj TSRMLS_CC);
cur->conn = obj;
cur->open = 1;
cur->name = estrdup(name_str);
cur->decl = decl;
return_value->type = IS_OBJECT;
return_value->value.obj = php_pqcur_create_object_ex(php_pqcur_class_entry, cur, NULL TSRMLS_CC);
}
}
}
}
ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_quote, 0, 0, 1) ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_quote, 0, 0, 1)
ZEND_ARG_INFO(0, string) ZEND_ARG_INFO(0, string)
ZEND_END_ARG_INFO(); ZEND_END_ARG_INFO();
@ -1435,7 +1584,7 @@ static PHP_METHOD(pqconn, on) {
zend_error_handling zeh; zend_error_handling zeh;
char *type_str; char *type_str;
int type_len; int type_len;
php_pq_callback_t cb; php_pq_callback_t cb = {{0}};
STATUS rv; STATUS rv;
zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
@ -1514,6 +1663,8 @@ static zend_function_entry php_pqconn_methods[] = {
PHP_ME(pqconn, execParamsAsync, ai_pqconn_exec_params_async, ZEND_ACC_PUBLIC) PHP_ME(pqconn, execParamsAsync, ai_pqconn_exec_params_async, ZEND_ACC_PUBLIC)
PHP_ME(pqconn, prepare, ai_pqconn_prepare, ZEND_ACC_PUBLIC) PHP_ME(pqconn, prepare, ai_pqconn_prepare, ZEND_ACC_PUBLIC)
PHP_ME(pqconn, prepareAsync, ai_pqconn_prepare_async, ZEND_ACC_PUBLIC) PHP_ME(pqconn, prepareAsync, ai_pqconn_prepare_async, ZEND_ACC_PUBLIC)
PHP_ME(pqconn, declare, ai_pqconn_declare, ZEND_ACC_PUBLIC)
PHP_ME(pqconn, declareAsync, ai_pqconn_declare_async, ZEND_ACC_PUBLIC)
PHP_ME(pqconn, listen, ai_pqconn_listen, ZEND_ACC_PUBLIC) PHP_ME(pqconn, listen, ai_pqconn_listen, ZEND_ACC_PUBLIC)
PHP_ME(pqconn, listenAsync, ai_pqconn_listen_async, ZEND_ACC_PUBLIC) PHP_ME(pqconn, listenAsync, ai_pqconn_listen_async, ZEND_ACC_PUBLIC)
PHP_ME(pqconn, notify, ai_pqconn_notify, ZEND_ACC_PUBLIC) PHP_ME(pqconn, notify, ai_pqconn_notify, ZEND_ACC_PUBLIC)

View File

@ -53,6 +53,8 @@ STATUS php_pqconn_prepare(zval *object, php_pqconn_object_t *obj, const char *na
STATUS php_pqconn_prepare_async(zval *object, php_pqconn_object_t *obj, const char *name, const char *query, php_pq_params_t *params TSRMLS_DC); STATUS php_pqconn_prepare_async(zval *object, php_pqconn_object_t *obj, const char *name, const char *query, php_pq_params_t *params TSRMLS_DC);
STATUS php_pqconn_start_transaction(zval *zconn, php_pqconn_object_t *conn_obj, long isolation, zend_bool readonly, zend_bool deferrable TSRMLS_DC); STATUS php_pqconn_start_transaction(zval *zconn, php_pqconn_object_t *conn_obj, long isolation, zend_bool readonly, zend_bool deferrable TSRMLS_DC);
STATUS php_pqconn_start_transaction_async(zval *zconn, php_pqconn_object_t *conn_obj, long isolation, zend_bool readonly, zend_bool deferrable TSRMLS_DC); STATUS php_pqconn_start_transaction_async(zval *zconn, php_pqconn_object_t *conn_obj, long isolation, zend_bool readonly, zend_bool deferrable TSRMLS_DC);
STATUS php_pqconn_declare(zval *object, php_pqconn_object_t *obj, const char *decl TSRMLS_DC);
STATUS php_pqconn_declare_async(zval *object, php_pqconn_object_t *obj, const char *decl TSRMLS_DC);
PHP_MINIT_FUNCTION(pqconn); PHP_MINIT_FUNCTION(pqconn);
PHP_MSHUTDOWN_FUNCTION(pqconn); PHP_MSHUTDOWN_FUNCTION(pqconn);

304
src/php_pqcur.c Normal file
View File

@ -0,0 +1,304 @@
/*
+--------------------------------------------------------------------+
| PECL :: pq |
+--------------------------------------------------------------------+
| Redistribution and use in source and binary forms, with or without |
| modification, are permitted provided that the conditions mentioned |
| in the accompanying LICENSE file are met. |
+--------------------------------------------------------------------+
| Copyright (c) 2013, Michael Wallner <mike@php.net> |
+--------------------------------------------------------------------+
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <php.h>
#include <ext/standard/php_smart_str.h>
#include "php_pq.h"
#include "php_pq_misc.h"
#include "php_pq_object.h"
#include "php_pqexc.h"
#include "php_pqconn.h"
#include "php_pqres.h"
#include "php_pqcur.h"
zend_class_entry *php_pqcur_class_entry;
static zend_object_handlers php_pqcur_object_handlers;
static HashTable php_pqcur_object_prophandlers;
static void cur_close(php_pqcur_object_t *obj TSRMLS_DC)
{
if (obj->intern->open) {
PGresult *res;
smart_str cmd = {0};
smart_str_appends(&cmd, "CLOSE ");
smart_str_appends(&cmd, obj->intern->name);
smart_str_0(&cmd);
if ((res = PQexec(obj->intern->conn->intern->conn, cmd.c))) {
PHP_PQclear(res);
}
smart_str_free(&cmd);
obj->intern->open = 0;
}
}
static void cur_fetch_or_move(INTERNAL_FUNCTION_PARAMETERS, const char *action, zend_bool async)
{
char *spec_str = "1";
int spec_len = 1;
STATUS rv;
php_pq_callback_t resolver = {{0}};
zend_error_handling zeh;
zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, async ? "|sf" : "|s", &spec_str, &spec_len, &resolver.fci, &resolver.fcc);
zend_restore_error_handling(&zeh TSRMLS_CC);
if (SUCCESS == rv) {
php_pqcur_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
if (!obj->intern) {
throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Cursor not initialized");
} else {
smart_str cmd = {0};
smart_str_appends(&cmd, *action == 'f' ? "FETCH " : "MOVE ");
smart_str_appendl(&cmd, spec_str, spec_len);
smart_str_appends(&cmd, " FROM ");
smart_str_appends(&cmd, obj->intern->name);
smart_str_0(&cmd);
if (async) {
int rc = PQsendQuery(obj->intern->conn->intern->conn, cmd.c);
if (!rc) {
throw_exce(EX_IO TSRMLS_CC, "Failed to %s cursor (%s)", *action == 'f' ? "fetch from" : "move in", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
} else if (obj->intern->conn->intern->unbuffered && !PQsetSingleRowMode(obj->intern->conn->intern->conn)) {
throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to enable unbuffered mode (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
} else {
php_pq_callback_recurse(&obj->intern->conn->intern->onevent, &resolver);
obj->intern->conn->intern->poller = PQconsumeInput;
}
} else {
PGresult *res = PQexec(obj->intern->conn->intern->conn, cmd.c);
if (!res) {
throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to %s cursor (%s)", *action == 'f' ? "fetch from" : "move in", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
} else if (SUCCESS == php_pqres_success(res TSRMLS_CC)) {
php_pq_object_to_zval_no_addref(PQresultInstanceData(res, php_pqconn_event), &return_value TSRMLS_CC);
}
}
smart_str_free(&cmd);
php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
}
}
}
static void php_pqcur_object_free(void *o TSRMLS_DC)
{
php_pqcur_object_t *obj = o;
#if DBG_GC
fprintf(stderr, "FREE cur(#%d) %p (conn: %p)\n", obj->zv.handle, obj, obj->intern->conn);
#endif
if (obj->intern) {
//cur_close(obj TSRMLS_CC);
//php_pq_object_delref(obj->intern->conn TSRMLS_CC);
efree(obj->intern->decl);
efree(obj->intern->name);
efree(obj->intern);
obj->intern = NULL;
}
zend_object_std_dtor((zend_object *) o TSRMLS_CC);
efree(obj);
}
zend_object_value php_pqcur_create_object_ex(zend_class_entry *ce, php_pqcur_t *intern, php_pqcur_object_t **ptr TSRMLS_DC)
{
php_pqcur_object_t *o;
o = ecalloc(1, sizeof(*o));
zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
object_properties_init((zend_object *) o, ce);
o->prophandler = &php_pqcur_object_prophandlers;
if (ptr) {
*ptr = o;
}
if (intern) {
o->intern = intern;
}
o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_pqcur_object_free, NULL TSRMLS_CC);
o->zv.handlers = &php_pqcur_object_handlers;
return o->zv;
}
static zend_object_value php_pqcur_create_object(zend_class_entry *class_type TSRMLS_DC)
{
return php_pqcur_create_object_ex(class_type, NULL, NULL TSRMLS_CC);
}
static void php_pqcur_object_read_name(zval *object, void *o, zval *return_value TSRMLS_DC)
{
php_pqcur_object_t *obj = o;
RETVAL_STRING(obj->intern->name, 1);
}
static void php_pqcur_object_read_connection(zval *object, void *o, zval *return_value TSRMLS_DC)
{
php_pqcur_object_t *obj = o;
php_pq_object_to_zval(obj->intern->conn, &return_value TSRMLS_CC);
}
ZEND_BEGIN_ARG_INFO_EX(ai_pqcur_open, 0, 0, 0)
ZEND_END_ARG_INFO();
static PHP_METHOD(pqcur, open)
{
zend_error_handling zeh;
STATUS rv;
zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
rv = zend_parse_parameters_none();
zend_restore_error_handling(&zeh TSRMLS_CC);
if (rv == SUCCESS) {
php_pqcur_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
if (!obj->intern) {
throw_exce(EX_UNINITIALIZED, "pq\\Cursor not initialized");
} else if (!obj->intern->open) {
if (SUCCESS == php_pqconn_declare(NULL, obj->intern->conn, obj->intern->decl)) {
obj->intern->open = 1;
}
}
}
}
ZEND_BEGIN_ARG_INFO_EX(ai_pqcur_close, 0, 0, 0)
ZEND_END_ARG_INFO();
static PHP_METHOD(pqcur, close)
{
zend_error_handling zeh;
STATUS rv;
zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
rv = zend_parse_parameters_none();
zend_restore_error_handling(&zeh TSRMLS_CC);
if (rv == SUCCESS) {
php_pqcur_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
if (!obj->intern) {
throw_exce(EX_UNINITIALIZED, "pq\\Cursor not initialized");
} else {
cur_close(obj TSRMLS_CC);
}
}
}
ZEND_BEGIN_ARG_INFO_EX(ai_pqcur_fetch, 0, 0, 1)
ZEND_ARG_INFO(0, spec)
ZEND_END_ARG_INFO();
static PHP_METHOD(pqcur, fetch)
{
cur_fetch_or_move(INTERNAL_FUNCTION_PARAM_PASSTHRU, "fetch", 0);
}
ZEND_BEGIN_ARG_INFO_EX(ai_pqcur_move, 0, 0, 0)
ZEND_ARG_INFO(0, spec)
ZEND_END_ARG_INFO();
static PHP_METHOD(pqcur, move)
{
cur_fetch_or_move(INTERNAL_FUNCTION_PARAM_PASSTHRU, "move", 0);
}
ZEND_BEGIN_ARG_INFO_EX(ai_pqcur_fetchAsync, 0, 0, 0)
ZEND_ARG_INFO(0, spec)
ZEND_ARG_INFO(0, callback)
ZEND_END_ARG_INFO();
static PHP_METHOD(pqcur, fetchAsync)
{
cur_fetch_or_move(INTERNAL_FUNCTION_PARAM_PASSTHRU, "fetch", 1);
}
ZEND_BEGIN_ARG_INFO_EX(ai_pqcur_moveAsync, 0, 0, 0)
ZEND_ARG_INFO(0, spec)
ZEND_ARG_INFO(0, callback)
ZEND_END_ARG_INFO();
static PHP_METHOD(pqcur, moveAsync)
{
cur_fetch_or_move(INTERNAL_FUNCTION_PARAM_PASSTHRU, "move", 1);
}
static zend_function_entry php_pqcur_methods[] = {
PHP_ME(pqcur, open, ai_pqcur_open, ZEND_ACC_PUBLIC)
PHP_ME(pqcur, close, ai_pqcur_close, ZEND_ACC_PUBLIC)
PHP_ME(pqcur, fetch, ai_pqcur_fetch, ZEND_ACC_PUBLIC)
PHP_ME(pqcur, move, ai_pqcur_move, ZEND_ACC_PUBLIC)
PHP_ME(pqcur, fetchAsync, ai_pqcur_fetchAsync, ZEND_ACC_PUBLIC)
PHP_ME(pqcur, moveAsync, ai_pqcur_moveAsync, ZEND_ACC_PUBLIC)
{NULL, NULL, NULL}
};
PHP_MSHUTDOWN_FUNCTION(pqcur)
{
zend_hash_destroy(&php_pqcur_object_prophandlers);
return SUCCESS;
}
PHP_MINIT_FUNCTION(pqcur)
{
zend_class_entry ce = {0};
php_pq_object_prophandler_t ph = {0};
INIT_NS_CLASS_ENTRY(ce, "pq", "Cursor", php_pqcur_methods);
php_pqcur_class_entry = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC);
php_pqcur_class_entry->create_object = php_pqcur_create_object;
memcpy(&php_pqcur_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
php_pqcur_object_handlers.read_property = php_pq_object_read_prop;
php_pqcur_object_handlers.write_property = php_pq_object_write_prop;
php_pqcur_object_handlers.clone_obj = NULL;
php_pqcur_object_handlers.get_property_ptr_ptr = NULL;
php_pqcur_object_handlers.get_gc = NULL;
php_pqcur_object_handlers.get_properties = php_pq_object_properties;
php_pqcur_object_handlers.get_debug_info = php_pq_object_debug_info;
zend_hash_init(&php_pqcur_object_prophandlers, 2, NULL, NULL, 1);
zend_declare_class_constant_long(php_pqcur_class_entry, ZEND_STRL("BINARY"), PHP_PQ_DECLARE_BINARY TSRMLS_CC);
zend_declare_class_constant_long(php_pqcur_class_entry, ZEND_STRL("INSENSITIVE"), PHP_PQ_DECLARE_INSENSITIVE TSRMLS_CC);
zend_declare_class_constant_long(php_pqcur_class_entry, ZEND_STRL("WITH_HOLD"), PHP_PQ_DECLARE_WITH_HOLD TSRMLS_CC);
zend_declare_class_constant_long(php_pqcur_class_entry, ZEND_STRL("SCROLL"), PHP_PQ_DECLARE_SCROLL TSRMLS_CC);
zend_declare_class_constant_long(php_pqcur_class_entry, ZEND_STRL("NO_SCROLL"), PHP_PQ_DECLARE_NO_SCROLL TSRMLS_CC);
zend_declare_property_null(php_pqcur_class_entry, ZEND_STRL("name"), ZEND_ACC_PUBLIC TSRMLS_CC);
ph.read = php_pqcur_object_read_name;
zend_hash_add(&php_pqcur_object_prophandlers, "name", sizeof("name"), (void *) &ph, sizeof(ph), NULL);
zend_declare_property_null(php_pqcur_class_entry, ZEND_STRL("connection"), ZEND_ACC_PUBLIC TSRMLS_CC);
ph.read = php_pqcur_object_read_connection;
zend_hash_add(&php_pqcur_object_prophandlers, "connection", sizeof("connection"), (void *) &ph, sizeof(ph), NULL);
return SUCCESS;
}
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
*/

54
src/php_pqcur.h Normal file
View File

@ -0,0 +1,54 @@
/*
+--------------------------------------------------------------------+
| PECL :: pq |
+--------------------------------------------------------------------+
| Redistribution and use in source and binary forms, with or without |
| modification, are permitted provided that the conditions mentioned |
| in the accompanying LICENSE file are met. |
+--------------------------------------------------------------------+
| Copyright (c) 2013, Michael Wallner <mike@php.net> |
+--------------------------------------------------------------------+
*/
#ifndef PHP_PQCUR_H
#define PHP_PQCUR_H
#include "php_pqconn.h"
#define PHP_PQ_DECLARE_BINARY 0x01
#define PHP_PQ_DECLARE_INSENSITIVE 0x02
#define PHP_PQ_DECLARE_WITH_HOLD 0x04
#define PHP_PQ_DECLARE_SCROLL 0x10
#define PHP_PQ_DECLARE_NO_SCROLL 0x20
typedef struct php_pqcur {
php_pqconn_object_t *conn;
char *name;
char *decl;
unsigned open:1;
} php_pqcur_t;
typedef struct php_pqcur_object {
zend_object zo;
zend_object_value zv;
HashTable *prophandler;
php_pqcur_t *intern;
} php_pqcur_object_t;
zend_class_entry *php_pqcur_class_entry;
zend_object_value php_pqcur_create_object_ex(zend_class_entry *ce, php_pqcur_t *intern, php_pqcur_object_t **ptr TSRMLS_DC);
PHP_MINIT_FUNCTION(pqcur);
PHP_MSHUTDOWN_FUNCTION(pqcur);
#endif
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
*/

View File

@ -670,7 +670,7 @@ static PHP_METHOD(pqres, fetchCol) {
RETVAL_FALSE; RETVAL_FALSE;
} else { } else {
zval_dtor(zref); zval_dtor(zref);
ZVAL_ZVAL(zref, *zres, 1, 1); ZVAL_ZVAL(zref, *zres, 1, 0);
RETVAL_TRUE; RETVAL_TRUE;
} }
} }

View File

@ -6,7 +6,7 @@
| modification, are permitted provided that the conditions mentioned | | modification, are permitted provided that the conditions mentioned |
| in the accompanying LICENSE file are met. | | in the accompanying LICENSE file are met. |
+--------------------------------------------------------------------+ +--------------------------------------------------------------------+
| Copyright (c) 2013, Michael Wallner <mike@php.net> | | Copyright (c) 2013, Michael Wallner <mike@php.net> |
+--------------------------------------------------------------------+ +--------------------------------------------------------------------+
*/ */

57
tests/async008.phpt Normal file
View File

@ -0,0 +1,57 @@
--TEST--
async cursor
--SKIPIF--
<?php include "_skipif.inc"; ?>
--FILE--
<?php
echo "Test\n";
include "_setup.inc";
function complete($c) {
do {
while ($c->busy) {
$r = array($c->socket);
$w = $e = null;
if (stream_select($r, $w, $e, null)) {
$c->poll();
}
}
} while ($c->getResult());
}
$c = new pq\Connection(PQ_DSN);
$p = $c->declareAsync("mycursor", pq\Cursor::WITH_HOLD,
"SELECT * FROM generate_series(0,29) s WHERE (s%2)=0");
complete($c);
do {
$p->fetchAsync(2, function ($r) {
foreach ($r as $row) {
foreach ($row as $col) {
echo " $col";
}
echo "\n";
}
});
complete($p->connection);
$p->moveAsync(1, function ($r) use(&$keep_going) {
$keep_going = $r->affectedRows;
});
complete($p->connection);
} while ($keep_going);
?>
===DONE===
--EXPECT--
Test
0
2
6
8
12
14
18
20
24
26
===DONE===

36
tests/cursor001.phpt Normal file
View File

@ -0,0 +1,36 @@
--TEST--
cursor
--SKIPIF--
<?php include "_skipif.inc"; ?>
--FILE--
<?php
echo "Test\n";
include "_setup.inc";
$c = new pq\Connection(PQ_DSN);
$p = $c->declare("mycursor", pq\Cursor::WITH_HOLD,
"SELECT * FROM generate_series(0,29) s WHERE (s%2)=0");
for ($r = $p->fetch(2); $r->numRows; $p->move(1), $r = $p->fetch(2)) {
foreach ($r as $row) {
foreach ($row as $col) {
echo " $col";
}
echo "\n";
}
}
?>
===DONE===
--EXPECT--
Test
0
2
6
8
12
14
18
20
24
26
===DONE===