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:
parent
316c62ff10
commit
9255d2d54b
1
TODO
1
TODO
@ -1,4 +1,5 @@
|
||||
* COPY: getAsync & putAsync
|
||||
* CURSOR: *Async()
|
||||
* fetchInto/fetchCtor?
|
||||
* unlisten?
|
||||
* unregister event handler?
|
||||
|
@ -50,6 +50,7 @@ if test "$PHP_PQ" != "no"; then
|
||||
src/php_pqstm.c\
|
||||
src/php_pqtxn.c\
|
||||
src/php_pqtypes.c\
|
||||
src/php_pqcur.c\
|
||||
"
|
||||
PHP_NEW_EXTENSION(pq, $PQ_SRC, $ext_shared)
|
||||
PHP_ADD_BUILD_DIR($ext_builddir/src)
|
||||
|
4
php_pq.h
4
php_pq.h
@ -23,9 +23,9 @@ zend_module_entry pq_module_entry;
|
||||
#ifdef PHP_WIN32
|
||||
# define PHP_PQ_API __declspec(dllexport)
|
||||
#elif defined(__GNUC__) && __GNUC__ >= 4
|
||||
# define PHP_PQ_API __attribute__ ((visibility("default")))
|
||||
# define PHP_PQ_API extern __attribute__ ((visibility("default")))
|
||||
#else
|
||||
# define PHP_PQ_API
|
||||
# define PHP_PQ_API extern
|
||||
#endif
|
||||
|
||||
#ifdef ZTS
|
||||
|
@ -15,19 +15,25 @@
|
||||
#endif
|
||||
|
||||
#include <php.h>
|
||||
#include <Zend/zend_closures.h>
|
||||
|
||||
#include "php_pq_callback.h"
|
||||
|
||||
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) {
|
||||
zend_fcall_info_args_clear(&cb->fci, 1);
|
||||
zval_ptr_dtor(&cb->fci.function_name);
|
||||
if (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)
|
||||
@ -55,6 +61,41 @@ zval *php_pq_callback_to_zval(php_pq_callback_t *cb)
|
||||
|
||||
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:
|
||||
* tab-width: 4
|
||||
|
@ -18,12 +18,14 @@
|
||||
typedef struct php_pq_callback {
|
||||
zend_fcall_info fci;
|
||||
zend_fcall_info_cache fcc;
|
||||
void *data;
|
||||
struct php_pq_callback *recursion;
|
||||
} php_pq_callback_t;
|
||||
|
||||
void php_pq_callback_dtor(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);
|
||||
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
|
||||
|
||||
|
@ -62,6 +62,7 @@ static PHP_MINIT_FUNCTION(pq)
|
||||
PHP_MINIT_CALL(pqres);
|
||||
PHP_MINIT_CALL(pqstm);
|
||||
PHP_MINIT_CALL(pqtxn);
|
||||
PHP_MINIT_CALL(pqcur);
|
||||
|
||||
PHP_MINIT_CALL(pqcopy);
|
||||
PHP_MINIT_CALL(pqlob);
|
||||
|
165
src/php_pqconn.c
165
src/php_pqconn.c
@ -31,6 +31,7 @@
|
||||
#include "php_pqres.h"
|
||||
#include "php_pqstm.h"
|
||||
#include "php_pqtxn.h"
|
||||
#include "php_pqcur.h"
|
||||
|
||||
zend_class_entry *php_pqconn_class_entry;
|
||||
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);
|
||||
#endif
|
||||
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_dtor(&obj->intern->factory);
|
||||
php_pq_callback_dtor(&obj->intern->onevent);
|
||||
zend_hash_destroy(&obj->intern->listeners);
|
||||
zend_hash_destroy(&obj->intern->converters);
|
||||
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);
|
||||
|
||||
/* release instance data */
|
||||
memset(evdata, 0, sizeof(*evdata));
|
||||
//memset(evdata, 0, sizeof(*evdata));
|
||||
efree(evdata);
|
||||
}
|
||||
}
|
||||
@ -626,7 +627,7 @@ static PHP_METHOD(pqconn, listen) {
|
||||
zend_error_handling zeh;
|
||||
char *channel_str = NULL;
|
||||
int channel_len = 0;
|
||||
php_pq_callback_t listener;
|
||||
php_pq_callback_t listener = {{0}};
|
||||
STATUS rv;
|
||||
|
||||
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;
|
||||
char *channel_str = NULL;
|
||||
int channel_len = 0;
|
||||
php_pq_callback_t listener;
|
||||
php_pq_callback_t listener = {{0}};
|
||||
STATUS rv;
|
||||
|
||||
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_ARG_INFO(0, type)
|
||||
ZEND_ARG_INFO(0, name)
|
||||
ZEND_ARG_INFO(0, query)
|
||||
ZEND_ARG_ARRAY_INFO(0, types, 1)
|
||||
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_ARG_INFO(0, type)
|
||||
ZEND_ARG_INFO(0, name)
|
||||
ZEND_ARG_INFO(0, query)
|
||||
ZEND_ARG_ARRAY_INFO(0, types, 1)
|
||||
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_ARG_INFO(0, string)
|
||||
ZEND_END_ARG_INFO();
|
||||
@ -1435,7 +1584,7 @@ static PHP_METHOD(pqconn, on) {
|
||||
zend_error_handling zeh;
|
||||
char *type_str;
|
||||
int type_len;
|
||||
php_pq_callback_t cb;
|
||||
php_pq_callback_t cb = {{0}};
|
||||
STATUS rv;
|
||||
|
||||
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, prepare, ai_pqconn_prepare, 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, listenAsync, ai_pqconn_listen_async, ZEND_ACC_PUBLIC)
|
||||
PHP_ME(pqconn, notify, ai_pqconn_notify, ZEND_ACC_PUBLIC)
|
||||
|
@ -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_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_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_MSHUTDOWN_FUNCTION(pqconn);
|
||||
|
304
src/php_pqcur.c
Normal file
304
src/php_pqcur.c
Normal 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
54
src/php_pqcur.h
Normal 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
|
||||
*/
|
@ -670,7 +670,7 @@ static PHP_METHOD(pqres, fetchCol) {
|
||||
RETVAL_FALSE;
|
||||
} else {
|
||||
zval_dtor(zref);
|
||||
ZVAL_ZVAL(zref, *zres, 1, 1);
|
||||
ZVAL_ZVAL(zref, *zres, 1, 0);
|
||||
RETVAL_TRUE;
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
| modification, are permitted provided that the conditions mentioned |
|
||||
| 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
57
tests/async008.phpt
Normal 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
36
tests/cursor001.phpt
Normal 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===
|
Loading…
Reference in New Issue
Block a user