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