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 <Zend/zend_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_pqstm.h"
28 zend_class_entry
*php_pqstm_class_entry
;
29 static zend_object_handlers php_pqstm_object_handlers
;
30 static HashTable php_pqstm_object_prophandlers
;
32 static void php_pqstm_deallocate(php_pqstm_object_t
*obj
, zend_bool async
, zend_bool silent
)
34 if (obj
->intern
->allocated
) {
35 char *quoted_name
= PQescapeIdentifier(obj
->intern
->conn
->intern
->conn
, obj
->intern
->name
, strlen(obj
->intern
->name
));
40 smart_str_appends(&cmd
, "DEALLOCATE ");
41 smart_str_appends(&cmd
, quoted_name
);
45 if (PQsendQuery(obj
->intern
->conn
->intern
->conn
, smart_str_v(&cmd
))) {
46 obj
->intern
->conn
->intern
->poller
= PQconsumeInput
;
47 php_pqconn_notify_listeners(obj
->intern
->conn
);
49 throw_exce(EX_IO
, "Failed to deallocate statement (%s)", PHP_PQerrorMessage(obj
->intern
->conn
->intern
->conn
));
54 if ((res
= php_pq_exec(obj
->intern
->conn
->intern
->conn
, smart_str_v(&cmd
)))) {
57 throw_exce(EX_RUNTIME
, "Failed to deallocate statement (%s)", PHP_PQerrorMessage(obj
->intern
->conn
->intern
->conn
));
61 PQfreemem(quoted_name
);
65 obj
->intern
->allocated
= 0;
66 zend_hash_str_del(&obj
->intern
->conn
->intern
->statements
, obj
->intern
->name
, strlen(obj
->intern
->name
));
70 static void php_pqstm_object_free(zend_object
*o
)
72 php_pqstm_object_t
*obj
= PHP_PQ_OBJ(NULL
, o
);
74 fprintf(stderr
, "FREE stm(#%d) %p (conn(#%d): %p)\n", obj
->zo
.handle
, obj
, obj
->intern
->conn
->zo
.handle
, obj
->intern
->conn
);
77 if (obj
->intern
->conn
->intern
) {
78 php_pq_callback_dtor(&obj
->intern
->conn
->intern
->onevent
);
79 php_pqstm_deallocate(obj
, 0, 1);
80 php_pq_object_delref(obj
->intern
->conn
);
82 efree(obj
->intern
->name
);
83 efree(obj
->intern
->query
);
84 zend_hash_destroy(&obj
->intern
->bound
);
85 if (obj
->intern
->params
) {
86 php_pq_params_free(&obj
->intern
->params
);
91 php_pq_object_dtor(o
);
94 php_pqstm_object_t
*php_pqstm_create_object_ex(zend_class_entry
*ce
, php_pqstm_t
*intern
)
96 return php_pq_object_create(ce
, intern
, sizeof(php_pqstm_object_t
),
97 &php_pqstm_object_handlers
, &php_pqstm_object_prophandlers
);
100 static zend_object
*php_pqstm_create_object(zend_class_entry
*class_type
)
102 return &php_pqstm_create_object_ex(class_type
, NULL
)->zo
;
105 static void php_pqstm_object_read_name(zval
*object
, void *o
, zval
*return_value
)
107 php_pqstm_object_t
*obj
= o
;
109 RETVAL_STRING(obj
->intern
->name
);
112 static void php_pqstm_object_read_connection(zval
*object
, void *o
, zval
*return_value
)
114 php_pqstm_object_t
*obj
= o
;
116 php_pq_object_to_zval(obj
->intern
->conn
, return_value
);
119 static void php_pqstm_object_gc_connection(zval
*object
, void *o
, zval
*return_value
)
121 php_pqstm_object_t
*obj
= o
;
124 php_pq_object_to_zval_no_addref(obj
->intern
->conn
, &zconn
);
125 add_next_index_zval(return_value
, &zconn
);
128 static void php_pqstm_object_read_query(zval
*object
, void *o
, zval
*return_value
)
130 php_pqstm_object_t
*obj
= o
;
132 RETVAL_STRING(obj
->intern
->query
);
135 static void php_pqstm_object_read_types(zval
*object
, void *o
, zval
*return_value
)
138 php_pqstm_object_t
*obj
= o
;
140 array_init_size(return_value
, obj
->intern
->params
->type
.count
);
141 for (i
= 0; i
< obj
->intern
->params
->type
.count
; i
++) {
142 add_next_index_long(return_value
, (long)obj
->intern
->params
->type
.oids
[i
]);
146 php_pqstm_t
*php_pqstm_init(php_pqconn_object_t
*conn
, const char *name
, const char *query
, php_pq_params_t
*params
)
148 php_pqstm_t
*stm
= ecalloc(1, sizeof(*stm
));
150 php_pq_object_addref(conn
);
152 stm
->name
= estrdup(name
);
153 stm
->params
= params
;
154 stm
->query
= estrdup(query
);
157 ZEND_INIT_SYMTABLE(&stm
->bound
);
159 zend_hash_str_add_ptr(&conn
->intern
->statements
, name
, strlen(name
), stm
);
164 ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_construct
, 0, 0, 3)
165 ZEND_ARG_OBJ_INFO(0, connection
, pq
\\Connection
, 0)
166 ZEND_ARG_INFO(0, name
)
167 ZEND_ARG_INFO(0, query
)
168 ZEND_ARG_ARRAY_INFO(0, types
, 1)
169 ZEND_ARG_INFO(0, async
)
171 static PHP_METHOD(pqstm
, __construct
) {
172 zend_error_handling zeh
;
173 zval
*zconn
, *ztypes
= NULL
;
174 char *name_str
, *query_str
;
175 size_t name_len
, *query_len
;
179 zend_replace_error_handling(EH_THROW
, exce(EX_INVALID_ARGUMENT
), &zeh
);
180 rv
= zend_parse_parameters(ZEND_NUM_ARGS(), "Oss|a/!b", &zconn
, php_pqconn_class_entry
, &name_str
, &name_len
, &query_str
, &query_len
, &ztypes
, &async
);
181 zend_restore_error_handling(&zeh
);
184 php_pqstm_object_t
*obj
= PHP_PQ_OBJ(getThis(), NULL
);
185 php_pqconn_object_t
*conn_obj
= PHP_PQ_OBJ(zconn
, NULL
);
188 throw_exce(EX_BAD_METHODCALL
, "pq\\Statement already initialized");
189 } else if (!conn_obj
->intern
) {
190 throw_exce(EX_UNINITIALIZED
, "pq\\Connection not initialized");
192 php_pq_params_t
*params
= php_pq_params_init(&conn_obj
->intern
->converters
, ztypes
? Z_ARRVAL_P(ztypes
) : NULL
, NULL
);
195 rv
= php_pqconn_prepare_async(zconn
, conn_obj
, name_str
, query_str
, params
);
197 rv
= php_pqconn_prepare(zconn
, conn_obj
, name_str
, query_str
, params
);
201 obj
->intern
= php_pqstm_init(conn_obj
, name_str
, query_str
, params
);
206 ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_bind
, 0, 0, 2)
207 ZEND_ARG_INFO(0, param_no
)
208 ZEND_ARG_INFO(1, param_ref
)
210 static PHP_METHOD(pqstm
, bind
) {
213 zend_error_handling zeh
;
216 zend_replace_error_handling(EH_THROW
, exce(EX_INVALID_ARGUMENT
), &zeh
);
217 rv
= zend_parse_parameters(ZEND_NUM_ARGS(), "lz", ¶m_no
, ¶m_ref
);
218 zend_restore_error_handling(&zeh
);
221 php_pqstm_object_t
*obj
= PHP_PQ_OBJ(getThis(), NULL
);
224 throw_exce(EX_UNINITIALIZED
, "pq\\Statement not initialized");
225 } else if (!obj
->intern
->allocated
) {
226 throw_exce(EX_UNINITIALIZED
, "pq\\Statement has been deallocated");
228 Z_ADDREF_P(param_ref
);
229 zend_hash_index_update(&obj
->intern
->bound
, param_no
, param_ref
);
230 zend_hash_sort(&obj
->intern
->bound
, php_pq_compare_index
, 0);
235 ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_exec
, 0, 0, 0)
236 ZEND_ARG_ARRAY_INFO(0, params
, 1)
238 static PHP_METHOD(pqstm
, exec
) {
239 zend_error_handling zeh
;
240 zval
*zparams
= NULL
;
243 zend_replace_error_handling(EH_THROW
, exce(EX_INVALID_ARGUMENT
), &zeh
);
244 rv
= zend_parse_parameters(ZEND_NUM_ARGS(), "|a/!", &zparams
);
245 zend_restore_error_handling(&zeh
);
248 php_pqstm_object_t
*obj
= PHP_PQ_OBJ(getThis(), NULL
);
251 throw_exce(EX_UNINITIALIZED
, "pq\\Statement not initialized");
252 } else if (!obj
->intern
->allocated
) {
253 throw_exce(EX_UNINITIALIZED
, "pq\\Statement has been deallocated");
257 php_pq_params_set_params(obj
->intern
->params
, zparams
? Z_ARRVAL_P(zparams
) : &obj
->intern
->bound
);
258 res
= php_pq_exec_prepared(obj
->intern
->conn
->intern
->conn
, obj
->intern
->name
, obj
->intern
->params
->param
.count
, (const char *const*) obj
->intern
->params
->param
.strings
, NULL
, NULL
, 0);
259 php_pq_params_set_params(obj
->intern
->params
, NULL
);
262 throw_exce(EX_RUNTIME
, "Failed to execute statement (%s)", PHP_PQerrorMessage(obj
->intern
->conn
->intern
->conn
));
263 } else if (SUCCESS
== php_pqres_success(res
)) {
264 php_pq_object_to_zval_no_addref(PQresultInstanceData(res
, php_pqconn_event
), return_value
);
265 php_pqconn_notify_listeners(obj
->intern
->conn
);
271 ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_exec_async
, 0, 0, 0)
272 ZEND_ARG_ARRAY_INFO(0, params
, 1)
273 ZEND_ARG_INFO(0, callable
)
275 static PHP_METHOD(pqstm
, execAsync
) {
276 zend_error_handling zeh
;
277 zval
*zparams
= NULL
;
278 php_pq_callback_t resolver
= {{0}};
281 zend_replace_error_handling(EH_THROW
, exce(EX_INVALID_ARGUMENT
), &zeh
);
282 rv
= zend_parse_parameters(ZEND_NUM_ARGS(), "|a/!f", &zparams
, &resolver
.fci
, &resolver
.fcc
);
283 zend_restore_error_handling(&zeh
);
286 php_pqstm_object_t
*obj
= PHP_PQ_OBJ(getThis(), NULL
);
289 throw_exce(EX_UNINITIALIZED
, "pq\\Statement not initialized");
290 } else if (!obj
->intern
->allocated
) {
291 throw_exce(EX_UNINITIALIZED
, "pq\\Statement has been deallocated");
295 php_pq_params_set_params(obj
->intern
->params
, zparams
? Z_ARRVAL_P(zparams
) : &obj
->intern
->bound
);
296 rc
= PQsendQueryPrepared(obj
->intern
->conn
->intern
->conn
, obj
->intern
->name
, obj
->intern
->params
->param
.count
, (const char *const*) obj
->intern
->params
->param
.strings
, NULL
, NULL
, 0);
297 php_pq_params_set_params(obj
->intern
->params
, NULL
);
300 throw_exce(EX_IO
, "Failed to execute statement (%s)", PHP_PQerrorMessage(obj
->intern
->conn
->intern
->conn
));
301 #if HAVE_PQSETSINGLEROWMODE
302 } else if (obj
->intern
->conn
->intern
->unbuffered
&& !PQsetSingleRowMode(obj
->intern
->conn
->intern
->conn
)) {
303 throw_exce(EX_RUNTIME
, "Failed to enable unbuffered mode (%s)", PHP_PQerrorMessage(obj
->intern
->conn
->intern
->conn
));
306 php_pq_callback_recurse(&obj
->intern
->conn
->intern
->onevent
, &resolver
);
307 obj
->intern
->conn
->intern
->poller
= PQconsumeInput
;
310 php_pqconn_notify_listeners(obj
->intern
->conn
);
315 ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_desc
, 0, 0, 0)
317 static PHP_METHOD(pqstm
, desc
) {
318 zend_error_handling zeh
;
321 zend_replace_error_handling(EH_THROW
, exce(EX_INVALID_ARGUMENT
), &zeh
);
322 rv
= zend_parse_parameters_none();
323 zend_restore_error_handling(&zeh
);
326 php_pqstm_object_t
*obj
= PHP_PQ_OBJ(getThis(), NULL
);
329 throw_exce(EX_UNINITIALIZED
, "pq\\Statement not initialized");
330 } else if (!obj
->intern
->allocated
) {
331 throw_exce(EX_UNINITIALIZED
, "pq\\Statement has been deallocated");
333 PGresult
*res
= PQdescribePrepared(obj
->intern
->conn
->intern
->conn
, obj
->intern
->name
);
336 throw_exce(EX_RUNTIME
, "Failed to describe statement (%s)", PHP_PQerrorMessage(obj
->intern
->conn
->intern
->conn
));
338 if (SUCCESS
== php_pqres_success(res
)) {
341 array_init(return_value
);
342 for (p
= 0, params
= PQnparams(res
); p
< params
; ++p
) {
343 add_next_index_long(return_value
, PQparamtype(res
, p
));
346 php_pqres_clear(res
);
347 php_pqconn_notify_listeners(obj
->intern
->conn
);
353 ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_desc_async
, 0, 0, 1)
354 ZEND_ARG_INFO(0, callable
)
356 static PHP_METHOD(pqstm
, descAsync
) {
357 zend_error_handling zeh
;
358 php_pq_callback_t resolver
= {{0}};
361 zend_replace_error_handling(EH_THROW
, exce(EX_INVALID_ARGUMENT
), &zeh
);
362 rv
= zend_parse_parameters(ZEND_NUM_ARGS(), "f", &resolver
.fci
, &resolver
.fcc
);
363 zend_restore_error_handling(&zeh
);
366 php_pqstm_object_t
*obj
= PHP_PQ_OBJ(getThis(), NULL
);
369 throw_exce(EX_UNINITIALIZED
, "pq\\Statement not initialized");
370 } else if (!obj
->intern
->allocated
) {
371 throw_exce(EX_UNINITIALIZED
, "pq\\Statement has been deallocated");
372 } else if (!PQsendDescribePrepared(obj
->intern
->conn
->intern
->conn
, obj
->intern
->name
)) {
373 throw_exce(EX_IO
, "Failed to describe statement: %s", PHP_PQerrorMessage(obj
->intern
->conn
->intern
->conn
));
375 php_pq_callback_recurse(&obj
->intern
->conn
->intern
->onevent
, &resolver
);
376 obj
->intern
->conn
->intern
->poller
= PQconsumeInput
;
377 php_pqconn_notify_listeners(obj
->intern
->conn
);
382 static zend_always_inline
void php_pqstm_deallocate_handler(INTERNAL_FUNCTION_PARAMETERS
, zend_bool async
)
384 zend_error_handling zeh
;
387 zend_replace_error_handling(EH_THROW
, exce(EX_INVALID_ARGUMENT
), &zeh
);
388 rv
= zend_parse_parameters_none();
389 zend_restore_error_handling(&zeh
);
392 php_pqstm_object_t
*obj
= PHP_PQ_OBJ(getThis(), NULL
);
395 throw_exce(EX_UNINITIALIZED
, "pq\\Statement not initialized");
397 php_pqstm_deallocate(obj
, async
, 0);
402 ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_deallocate
, 0, 0, 0)
404 static PHP_METHOD(pqstm
, deallocate
)
406 php_pqstm_deallocate_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU
, 0);
409 ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_deallocate_async
, 0, 0, 0)
411 static PHP_METHOD(pqstm
, deallocateAsync
)
413 php_pqstm_deallocate_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU
, 1);
416 static inline void php_pqstm_prepare_handler(INTERNAL_FUNCTION_PARAMETERS
, zend_bool async
)
418 zend_error_handling zeh
;
421 zend_replace_error_handling(EH_THROW
, exce(EX_INVALID_ARGUMENT
), &zeh
);
422 rv
= zend_parse_parameters_none();
423 zend_restore_error_handling(&zeh
);
426 php_pqstm_object_t
*obj
= PHP_PQ_OBJ(getThis(), NULL
);
429 throw_exce(EX_UNINITIALIZED
, "pq\\Statement not initialized");
430 } else if (!obj
->intern
->allocated
) {
432 rv
= php_pqconn_prepare_async(NULL
, obj
->intern
->conn
, obj
->intern
->name
, obj
->intern
->query
, obj
->intern
->params
);
434 rv
= php_pqconn_prepare(NULL
, obj
->intern
->conn
, obj
->intern
->name
, obj
->intern
->query
, obj
->intern
->params
);
438 obj
->intern
->allocated
= 1;
440 zend_hash_str_add_ptr(&obj
->intern
->conn
->intern
->statements
,
441 obj
->intern
->name
, strlen(obj
->intern
->name
), obj
->intern
);
447 ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_prepare
, 0, 0, 0)
449 static PHP_METHOD(pqstm
, prepare
)
451 php_pqstm_prepare_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU
, 0);
454 ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_prepare_async
, 0, 0, 0)
456 static PHP_METHOD(pqstm
, prepareAsync
)
458 php_pqstm_prepare_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU
, 1);
461 static zend_function_entry php_pqstm_methods
[] = {
462 PHP_ME(pqstm
, __construct
, ai_pqstm_construct
, ZEND_ACC_PUBLIC
)
463 PHP_ME(pqstm
, bind
, ai_pqstm_bind
, ZEND_ACC_PUBLIC
)
464 PHP_ME(pqstm
, deallocate
, ai_pqstm_deallocate
, ZEND_ACC_PUBLIC
)
465 PHP_ME(pqstm
, deallocateAsync
, ai_pqstm_deallocate_async
, ZEND_ACC_PUBLIC
)
466 PHP_ME(pqstm
, desc
, ai_pqstm_desc
, ZEND_ACC_PUBLIC
)
467 PHP_ME(pqstm
, descAsync
, ai_pqstm_desc_async
, ZEND_ACC_PUBLIC
)
468 PHP_ME(pqstm
, exec
, ai_pqstm_exec
, ZEND_ACC_PUBLIC
)
469 PHP_ME(pqstm
, execAsync
, ai_pqstm_exec_async
, ZEND_ACC_PUBLIC
)
470 PHP_ME(pqstm
, prepare
, ai_pqstm_prepare
, ZEND_ACC_PUBLIC
)
471 PHP_ME(pqstm
, prepareAsync
, ai_pqstm_prepare_async
, ZEND_ACC_PUBLIC
)
475 PHP_MSHUTDOWN_FUNCTION(pqstm
)
477 zend_hash_destroy(&php_pqstm_object_prophandlers
);
481 PHP_MINIT_FUNCTION(pqstm
)
483 zend_class_entry ce
= {0};
484 php_pq_object_prophandler_t ph
= {0};
486 INIT_NS_CLASS_ENTRY(ce
, "pq", "Statement", php_pqstm_methods
);
487 php_pqstm_class_entry
= zend_register_internal_class_ex(&ce
, NULL
);
488 php_pqstm_class_entry
->create_object
= php_pqstm_create_object
;
490 memcpy(&php_pqstm_object_handlers
, zend_get_std_object_handlers(), sizeof(zend_object_handlers
));
491 php_pqstm_object_handlers
.offset
= XtOffsetOf(php_pqstm_object_t
, zo
);
492 php_pqstm_object_handlers
.free_obj
= php_pqstm_object_free
;
493 php_pqstm_object_handlers
.read_property
= php_pq_object_read_prop
;
494 php_pqstm_object_handlers
.write_property
= php_pq_object_write_prop
;
495 php_pqstm_object_handlers
.clone_obj
= NULL
;
496 php_pqstm_object_handlers
.get_property_ptr_ptr
= php_pq_object_get_prop_ptr_null
;
497 php_pqstm_object_handlers
.get_gc
= php_pq_object_get_gc
;
498 php_pqstm_object_handlers
.get_properties
= php_pq_object_properties
;
499 php_pqstm_object_handlers
.get_debug_info
= php_pq_object_debug_info
;
501 zend_hash_init(&php_pqstm_object_prophandlers
, 4, NULL
, php_pq_object_prophandler_dtor
, 1);
503 zend_declare_property_null(php_pqstm_class_entry
, ZEND_STRL("name"), ZEND_ACC_PUBLIC
);
504 ph
.read
= php_pqstm_object_read_name
;
505 zend_hash_str_add_mem(&php_pqstm_object_prophandlers
, "name", sizeof("name")-1, (void *) &ph
, sizeof(ph
));
507 zend_declare_property_null(php_pqstm_class_entry
, ZEND_STRL("connection"), ZEND_ACC_PUBLIC
);
508 ph
.read
= php_pqstm_object_read_connection
;
509 ph
.gc
= php_pqstm_object_gc_connection
;
510 zend_hash_str_add_mem(&php_pqstm_object_prophandlers
, "connection", sizeof("connection")-1, (void *) &ph
, sizeof(ph
));
513 zend_declare_property_null(php_pqstm_class_entry
, ZEND_STRL("query"), ZEND_ACC_PUBLIC
);
514 ph
.read
= php_pqstm_object_read_query
;
515 zend_hash_str_add_mem(&php_pqstm_object_prophandlers
, "query", sizeof("query")-1, (void *) &ph
, sizeof(ph
));
517 zend_declare_property_null(php_pqstm_class_entry
, ZEND_STRL("types"), ZEND_ACC_PUBLIC
);
518 ph
.read
= php_pqstm_object_read_types
;
519 zend_hash_str_add_mem(&php_pqstm_object_prophandlers
, "types", sizeof("types")-1, (void *) &ph
, sizeof(ph
));
529 * vim600: noet sw=4 ts=4 fdm=marker
530 * vim<600: noet sw=4 ts=4