af0eb15ea8c9707e69e09b64abaf913835205fa2
[m6w6/ext-pq] / src / php_pqstm.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 #include <ext/standard/php_smart_str.h>
19
20 #include "php_pq.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"
27
28 zend_class_entry *php_pqstm_class_entry;
29 static zend_object_handlers php_pqstm_object_handlers;
30 static HashTable php_pqstm_object_prophandlers;
31
32 static void php_pqstm_object_free(void *o TSRMLS_DC)
33 {
34 php_pqstm_object_t *obj = o;
35 #if DBG_GC
36 fprintf(stderr, "FREE stm(#%d) %p (conn(#%d): %p)\n", obj->zv.handle, obj, obj->intern->conn->zv.handle, obj->intern->conn);
37 #endif
38 if (obj->intern) {
39 char *quoted_name = PQescapeIdentifier(obj->intern->conn->intern->conn, obj->intern->name, strlen(obj->intern->name));
40
41 php_pq_callback_dtor(&obj->intern->conn->intern->onevent);
42
43 if (quoted_name) {
44 PGresult *res;
45 smart_str cmd = {0};
46
47 smart_str_appends(&cmd, "DEALLOCATE ");
48 smart_str_appends(&cmd, quoted_name);
49 smart_str_0(&cmd);
50 PQfreemem(quoted_name);
51
52 if ((res = PQexec(obj->intern->conn->intern->conn, cmd.c))) {
53 PHP_PQclear(res);
54 }
55 smart_str_free(&cmd);
56 }
57
58 php_pq_object_delref(obj->intern->conn TSRMLS_CC);
59 efree(obj->intern->name);
60 zend_hash_destroy(&obj->intern->bound);
61 if (obj->intern->params) {
62 php_pq_params_free(&obj->intern->params);
63 }
64 efree(obj->intern);
65 obj->intern = NULL;
66 }
67 zend_object_std_dtor((zend_object *) o TSRMLS_CC);
68 efree(obj);
69 }
70
71 zend_object_value php_pqstm_create_object_ex(zend_class_entry *ce, php_pqstm_t *intern, php_pqstm_object_t **ptr TSRMLS_DC)
72 {
73 php_pqstm_object_t *o;
74
75 o = ecalloc(1, sizeof(*o));
76 zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
77 object_properties_init((zend_object *) o, ce);
78 o->prophandler = &php_pqstm_object_prophandlers;
79
80 if (ptr) {
81 *ptr = o;
82 }
83
84 if (intern) {
85 o->intern = intern;
86 }
87
88 o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_pqstm_object_free, NULL TSRMLS_CC);
89 o->zv.handlers = &php_pqstm_object_handlers;
90
91 return o->zv;
92 }
93
94 static zend_object_value php_pqstm_create_object(zend_class_entry *class_type TSRMLS_DC)
95 {
96 return php_pqstm_create_object_ex(class_type, NULL, NULL TSRMLS_CC);
97 }
98
99 static void php_pqstm_object_read_name(zval *object, void *o, zval *return_value TSRMLS_DC)
100 {
101 php_pqstm_object_t *obj = o;
102
103 RETVAL_STRING(obj->intern->name, 1);
104 }
105
106 static void php_pqstm_object_read_connection(zval *object, void *o, zval *return_value TSRMLS_DC)
107 {
108 php_pqstm_object_t *obj = o;
109
110 php_pq_object_to_zval(obj->intern->conn, &return_value TSRMLS_CC);
111 }
112
113
114 ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_construct, 0, 0, 3)
115 ZEND_ARG_OBJ_INFO(0, Connection, pq\\Connection, 0)
116 ZEND_ARG_INFO(0, name)
117 ZEND_ARG_INFO(0, query)
118 ZEND_ARG_ARRAY_INFO(0, types, 1)
119 ZEND_ARG_INFO(0, async)
120 ZEND_END_ARG_INFO();
121 static PHP_METHOD(pqstm, __construct) {
122 zend_error_handling zeh;
123 zval *zconn, *ztypes = NULL;
124 char *name_str, *query_str;
125 int name_len, *query_len;
126 zend_bool async = 0;
127 STATUS rv;
128
129 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
130 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Oss|a/!b", &zconn, php_pqconn_class_entry, &name_str, &name_len, &query_str, &query_len, &ztypes, &async);
131 zend_restore_error_handling(&zeh TSRMLS_CC);
132
133 if (SUCCESS == rv) {
134 php_pqstm_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
135 php_pqconn_object_t *conn_obj = zend_object_store_get_object(zconn TSRMLS_CC);
136
137 if (!conn_obj->intern) {
138 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
139 } else {
140 php_pq_params_t *params = php_pq_params_init(&conn_obj->intern->converters, ztypes ? Z_ARRVAL_P(ztypes) : NULL, NULL TSRMLS_CC);
141
142 if (async) {
143 rv = php_pqconn_prepare_async(zconn, conn_obj, name_str, query_str, params TSRMLS_CC);
144 } else {
145 rv = php_pqconn_prepare(zconn, conn_obj, name_str, query_str, params TSRMLS_CC);
146 }
147
148 if (SUCCESS == rv) {
149 php_pqstm_t *stm = ecalloc(1, sizeof(*stm));
150
151 php_pq_object_addref(conn_obj TSRMLS_CC);
152 stm->conn = conn_obj;
153 stm->name = estrdup(name_str);
154 stm->params = params;
155 ZEND_INIT_SYMTABLE(&stm->bound);
156 obj->intern = stm;
157 }
158 }
159 }
160 }
161 ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_bind, 0, 0, 2)
162 ZEND_ARG_INFO(0, param_no)
163 ZEND_ARG_INFO(1, param_ref)
164 ZEND_END_ARG_INFO();
165 static PHP_METHOD(pqstm, bind) {
166 long param_no;
167 zval **param_ref;
168 zend_error_handling zeh;
169 STATUS rv;
170
171 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
172 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lZ", &param_no, &param_ref);
173 zend_restore_error_handling(&zeh TSRMLS_CC);
174
175 if (SUCCESS == rv) {
176 php_pqstm_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
177
178 if (!obj->intern) {
179 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Statement not initialized");
180 } else {
181 SEPARATE_ZVAL_TO_MAKE_IS_REF(param_ref);
182 Z_ADDREF_PP(param_ref);
183 zend_hash_index_update(&obj->intern->bound, param_no, (void *) param_ref, sizeof(zval *), NULL);
184 zend_hash_sort(&obj->intern->bound, zend_qsort, compare_index, 0 TSRMLS_CC);
185 }
186 }
187 }
188
189 ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_exec, 0, 0, 0)
190 ZEND_ARG_ARRAY_INFO(0, params, 1)
191 ZEND_END_ARG_INFO();
192 static PHP_METHOD(pqstm, exec) {
193 zend_error_handling zeh;
194 zval *zparams = NULL;
195 STATUS rv;
196
197 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
198 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a/!", &zparams);
199 zend_restore_error_handling(&zeh TSRMLS_CC);
200
201 if (SUCCESS == rv) {
202 php_pqstm_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
203
204 if (!obj->intern) {
205 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Statement not initialized");
206 } else {
207 PGresult *res;
208
209 php_pq_params_set_params(obj->intern->params, zparams ? Z_ARRVAL_P(zparams) : &obj->intern->bound);
210 res = PQexecPrepared(obj->intern->conn->intern->conn, obj->intern->name, obj->intern->params->param.count, (const char *const*) obj->intern->params->param.strings, NULL, NULL, 0);
211 php_pq_params_set_params(obj->intern->params, NULL);
212
213 if (!res) {
214 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to execute statement (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
215 } else if (SUCCESS == php_pqres_success(res TSRMLS_CC)) {
216 php_pq_object_to_zval_no_addref(PQresultInstanceData(res, php_pqconn_event), &return_value TSRMLS_CC);
217 php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
218 }
219 }
220 }
221 }
222
223 ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_exec_async, 0, 0, 0)
224 ZEND_ARG_ARRAY_INFO(0, params, 1)
225 ZEND_ARG_INFO(0, callable)
226 ZEND_END_ARG_INFO();
227 static PHP_METHOD(pqstm, execAsync) {
228 zend_error_handling zeh;
229 zval *zparams = NULL;
230 php_pq_callback_t resolver = {{0}};
231 STATUS rv;
232
233 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
234 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a/!f", &zparams, &resolver.fci, &resolver.fcc);
235 zend_restore_error_handling(&zeh TSRMLS_CC);
236
237 if (SUCCESS == rv) {
238 php_pqstm_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
239
240 if (!obj->intern) {
241 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Statement not initialized");
242 } else {
243 int rc;
244
245 php_pq_params_set_params(obj->intern->params, zparams ? Z_ARRVAL_P(zparams) : &obj->intern->bound);
246 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);
247 php_pq_params_set_params(obj->intern->params, NULL);
248
249 if (!rc) {
250 throw_exce(EX_IO TSRMLS_CC, "Failed to execute statement (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
251 } else if (obj->intern->conn->intern->unbuffered && !PQsetSingleRowMode(obj->intern->conn->intern->conn)) {
252 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to enable unbuffered mode (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
253 } else {
254 php_pq_callback_recurse(&obj->intern->conn->intern->onevent, &resolver TSRMLS_CC);
255 obj->intern->conn->intern->poller = PQconsumeInput;
256 }
257
258 php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
259 }
260 }
261 }
262
263 ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_desc, 0, 0, 0)
264 ZEND_END_ARG_INFO();
265 static PHP_METHOD(pqstm, desc) {
266 zend_error_handling zeh;
267 STATUS rv;
268
269 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
270 rv = zend_parse_parameters_none();
271 zend_restore_error_handling(&zeh TSRMLS_CC);
272
273 if (SUCCESS == rv) {
274 php_pqstm_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
275
276 if (!obj->intern) {
277 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Statement not initialized");
278 } else {
279 PGresult *res = PQdescribePrepared(obj->intern->conn->intern->conn, obj->intern->name);
280
281 if (!res) {
282 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to describe statement (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
283 } else {
284 if (SUCCESS == php_pqres_success(res TSRMLS_CC)) {
285 int p, params;
286
287 array_init(return_value);
288 for (p = 0, params = PQnparams(res); p < params; ++p) {
289 add_next_index_long(return_value, PQparamtype(res, p));
290 }
291 }
292 PHP_PQclear(res);
293 php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
294 }
295 }
296 }
297 }
298
299 ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_desc_async, 0, 0, 1)
300 ZEND_ARG_INFO(0, callable)
301 ZEND_END_ARG_INFO();
302 static PHP_METHOD(pqstm, descAsync) {
303 zend_error_handling zeh;
304 php_pq_callback_t resolver = {{0}};
305 STATUS rv;
306
307 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
308 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "f", &resolver.fci, &resolver.fcc);
309 zend_restore_error_handling(&zeh TSRMLS_CC);
310
311 if (SUCCESS == rv) {
312 php_pqstm_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
313
314 if (!obj->intern) {
315 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Statement not initialized");
316 } else if (!PQsendDescribePrepared(obj->intern->conn->intern->conn, obj->intern->name)) {
317 throw_exce(EX_IO TSRMLS_CC, "Failed to describe statement: %s", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
318 } else {
319 php_pq_callback_recurse(&obj->intern->conn->intern->onevent, &resolver TSRMLS_CC);
320 obj->intern->conn->intern->poller = PQconsumeInput;
321 php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
322 }
323 }
324 }
325
326 static zend_function_entry php_pqstm_methods[] = {
327 PHP_ME(pqstm, __construct, ai_pqstm_construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
328 PHP_ME(pqstm, bind, ai_pqstm_bind, ZEND_ACC_PUBLIC)
329 PHP_ME(pqstm, exec, ai_pqstm_exec, ZEND_ACC_PUBLIC)
330 PHP_ME(pqstm, desc, ai_pqstm_desc, ZEND_ACC_PUBLIC)
331 PHP_ME(pqstm, execAsync, ai_pqstm_exec_async, ZEND_ACC_PUBLIC)
332 PHP_ME(pqstm, descAsync, ai_pqstm_desc_async, ZEND_ACC_PUBLIC)
333 {0}
334 };
335
336 PHP_MSHUTDOWN_FUNCTION(pqstm)
337 {
338 zend_hash_destroy(&php_pqstm_object_prophandlers);
339 return SUCCESS;
340 }
341
342 PHP_MINIT_FUNCTION(pqstm)
343 {
344 zend_class_entry ce = {0};
345 php_pq_object_prophandler_t ph = {0};
346
347 INIT_NS_CLASS_ENTRY(ce, "pq", "Statement", php_pqstm_methods);
348 php_pqstm_class_entry = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC);
349 php_pqstm_class_entry->create_object = php_pqstm_create_object;
350
351 memcpy(&php_pqstm_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
352 php_pqstm_object_handlers.read_property = php_pq_object_read_prop;
353 php_pqstm_object_handlers.write_property = php_pq_object_write_prop;
354 php_pqstm_object_handlers.clone_obj = NULL;
355 php_pqstm_object_handlers.get_property_ptr_ptr = NULL;
356 php_pqstm_object_handlers.get_gc = NULL;
357 php_pqstm_object_handlers.get_properties = php_pq_object_properties;
358 php_pqstm_object_handlers.get_debug_info = php_pq_object_debug_info;
359
360 zend_hash_init(&php_pqstm_object_prophandlers, 2, NULL, NULL, 1);
361
362 zend_declare_property_null(php_pqstm_class_entry, ZEND_STRL("name"), ZEND_ACC_PUBLIC TSRMLS_CC);
363 ph.read = php_pqstm_object_read_name;
364 zend_hash_add(&php_pqstm_object_prophandlers, "name", sizeof("name"), (void *) &ph, sizeof(ph), NULL);
365
366 zend_declare_property_null(php_pqstm_class_entry, ZEND_STRL("connection"), ZEND_ACC_PUBLIC TSRMLS_CC);
367 ph.read = php_pqstm_object_read_connection;
368 zend_hash_add(&php_pqstm_object_prophandlers, "connection", sizeof("connection"), (void *) &ph, sizeof(ph), NULL);
369
370 return SUCCESS;
371 }
372
373 /*
374 * Local variables:
375 * tab-width: 4
376 * c-basic-offset: 4
377 * End:
378 * vim600: noet sw=4 ts=4 fdm=marker
379 * vim<600: noet sw=4 ts=4
380 */