d652ba6ba6a143785b68652d338a89b27b1ccbf8
[m6w6/ext-pq] / src / php_pqcopy.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 <libpq-events.h>
21
22 #include "php_pq.h"
23 #include "php_pq_misc.h"
24 #include "php_pq_object.h"
25 #include "php_pqexc.h"
26 #include "php_pqres.h"
27 #include "php_pqconn.h"
28 #include "php_pqcopy.h"
29
30 zend_class_entry *php_pqcopy_class_entry;
31 static zend_object_handlers php_pqcopy_object_handlers;
32 static HashTable php_pqcopy_object_prophandlers;
33
34 static void php_pqcopy_object_free(void *o TSRMLS_DC)
35 {
36 php_pqcopy_object_t *obj = o;
37 #if DBG_GC
38 fprintf(stderr, "FREE copy(#%d) %p (conn(#%d): %p)\n", obj->zv.handle, obj, obj->intern->conn->zv.handle, obj->intern->conn);
39 #endif
40 if (obj->intern) {
41 efree(obj->intern->expression);
42 efree(obj->intern->options);
43 php_pq_object_delref(obj->intern->conn TSRMLS_CC);
44 efree(obj->intern);
45 obj->intern = NULL;
46 }
47 zend_object_std_dtor((zend_object *) o TSRMLS_CC);
48 efree(obj);
49 }
50
51 zend_object_value php_pqcopy_create_object_ex(zend_class_entry *ce, php_pqcopy_t *intern, php_pqcopy_object_t **ptr TSRMLS_DC)
52 {
53 php_pqcopy_object_t *o;
54
55 o = ecalloc(1, sizeof(*o));
56 zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
57 object_properties_init((zend_object *) o, ce);
58 o->prophandler = &php_pqcopy_object_prophandlers;
59
60 if (ptr) {
61 *ptr = o;
62 }
63
64 if (intern) {
65 o->intern = intern;
66 }
67
68 o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_pqcopy_object_free, NULL TSRMLS_CC);
69 o->zv.handlers = &php_pqcopy_object_handlers;
70
71 return o->zv;
72 }
73
74 static zend_object_value php_pqcopy_create_object(zend_class_entry *class_type TSRMLS_DC)
75 {
76 return php_pqcopy_create_object_ex(class_type, NULL, NULL TSRMLS_CC);
77 }
78
79 static void php_pqcopy_object_read_connection(zval *object, void *o, zval *return_value TSRMLS_DC)
80 {
81 php_pqcopy_object_t *obj = o;
82
83 php_pq_object_to_zval(obj->intern->conn, &return_value TSRMLS_CC);
84 }
85
86 static void php_pqcopy_object_read_direction(zval *object, void *o, zval *return_value TSRMLS_DC)
87 {
88 php_pqcopy_object_t *obj = o;
89
90 RETVAL_LONG(obj->intern->direction);
91 }
92
93 static void php_pqcopy_object_read_expression(zval *object, void *o, zval *return_value TSRMLS_DC)
94 {
95 php_pqcopy_object_t *obj = o;
96
97 RETURN_STRING(obj->intern->expression, 1);
98 }
99
100 static void php_pqcopy_object_read_options(zval *object, void *o, zval *return_value TSRMLS_DC)
101 {
102 php_pqcopy_object_t *obj = o;
103
104 RETURN_STRING(obj->intern->options, 1);
105 }
106
107 ZEND_BEGIN_ARG_INFO_EX(ai_pqcopy_construct, 0, 0, 3)
108 ZEND_ARG_OBJ_INFO(0, connection, pq\\Connection, 0)
109 ZEND_ARG_INFO(0, expression)
110 ZEND_ARG_INFO(0, direction)
111 ZEND_ARG_INFO(0, options)
112 ZEND_END_ARG_INFO();
113 static PHP_METHOD(pqcopy, __construct) {
114 zend_error_handling zeh;
115 zval *zconn;
116 char *expr_str, *opt_str = "";
117 int expr_len, opt_len = 0;
118 long direction;
119 STATUS rv;
120
121 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
122 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Osl|s", &zconn, php_pqconn_class_entry, &expr_str, &expr_len, &direction, &opt_str, &opt_len);
123 zend_restore_error_handling(&zeh TSRMLS_CC);
124
125 if (SUCCESS == rv) {
126 php_pqconn_object_t *conn_obj = zend_object_store_get_object(zconn TSRMLS_CC);
127
128 if (!conn_obj->intern) {
129 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
130 } else {
131 php_pqcopy_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
132 smart_str cmd = {0};
133 PGresult *res;
134
135 smart_str_appends(&cmd, "COPY ");
136 smart_str_appendl(&cmd, expr_str, expr_len);
137
138 switch (direction) {
139 case PHP_PQCOPY_FROM_STDIN:
140 smart_str_appends(&cmd, " FROM STDIN ");
141 break;
142 case PHP_PQCOPY_TO_STDOUT:
143 smart_str_appends(&cmd, " TO STDOUT ");
144 break;
145 default:
146 throw_exce(EX_RUNTIME TSRMLS_CC, "Invalid COPY direction, expected one of FROM_STDIN (%d) TO_STDOUT (%d), got %ld", PHP_PQCOPY_FROM_STDIN, PHP_PQCOPY_TO_STDOUT, direction);
147 smart_str_free(&cmd);
148 return;
149 }
150 smart_str_appendl(&cmd, opt_str, opt_len);
151 smart_str_0(&cmd);
152
153 res = PQexec(conn_obj->intern->conn, cmd.c);
154
155 if (!res) {
156 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to start %s (%s)", cmd.c, PHP_PQerrorMessage(obj->intern->conn->intern->conn));
157 } else {
158 if (SUCCESS == php_pqres_success(res TSRMLS_CC)) {
159 obj->intern = ecalloc(1, sizeof(*obj->intern));
160 obj->intern->direction = direction;
161 obj->intern->expression = estrdup(expr_str);
162 obj->intern->options = estrdup(opt_str);
163 obj->intern->conn = conn_obj;
164 php_pq_object_addref(conn_obj TSRMLS_CC);
165 }
166
167 PHP_PQclear(res);
168 }
169
170 smart_str_free(&cmd);
171 php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
172 }
173 }
174 }
175
176 ZEND_BEGIN_ARG_INFO_EX(ai_pqcopy_put, 0, 0, 1)
177 ZEND_ARG_INFO(0, data)
178 ZEND_END_ARG_INFO();
179 static PHP_METHOD(pqcopy, put) {
180 zend_error_handling zeh;
181 char *data_str;
182 int data_len;
183 STATUS rv;
184
185 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
186 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &data_str, &data_len);
187 zend_restore_error_handling(&zeh TSRMLS_CC);
188
189 if (SUCCESS == rv) {
190 php_pqcopy_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
191
192 if (!obj->intern) {
193 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\COPY not initialized");
194 } else if (obj->intern->direction != PHP_PQCOPY_FROM_STDIN) {
195 throw_exce(EX_BAD_METHODCALL TSRMLS_CC, "pq\\COPY was not initialized with FROM_STDIN");
196 } else {
197 if (1 != PQputCopyData(obj->intern->conn->intern->conn, data_str, data_len)) {
198 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to put COPY data (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
199 }
200 php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
201 }
202 }
203 }
204
205 ZEND_BEGIN_ARG_INFO_EX(ai_pqcopy_end, 0, 0, 0)
206 ZEND_ARG_INFO(0, error)
207 ZEND_END_ARG_INFO();
208 static PHP_METHOD(pqcopy, end) {
209 zend_error_handling zeh;
210 char *error_str = NULL;
211 int error_len = 0;
212 STATUS rv;
213
214 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
215 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &error_str, &error_len);
216 zend_restore_error_handling(&zeh TSRMLS_CC);
217
218 if (SUCCESS == rv) {
219 php_pqcopy_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
220
221 if (!obj->intern) {
222 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\COPY not intitialized");
223 } else if (obj->intern->direction != PHP_PQCOPY_FROM_STDIN) {
224 throw_exce(EX_BAD_METHODCALL TSRMLS_CC, "pq\\COPY was not intitialized with FROM_STDIN");
225 } else {
226 if (1 != PQputCopyEnd(obj->intern->conn->intern->conn, error_str)) {
227 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to end COPY (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
228 } else {
229 PGresult *res = PQgetResult(obj->intern->conn->intern->conn);
230
231 if (!res) {
232 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to fetch COPY result (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
233 } else {
234 php_pqres_success(res TSRMLS_CC);
235 PHP_PQclear(res);
236 }
237 }
238
239 php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
240 }
241 }
242 }
243
244 ZEND_BEGIN_ARG_INFO_EX(ai_pqcopy_get, 0, 0, 1)
245 ZEND_ARG_INFO(1, data)
246 ZEND_END_ARG_INFO();
247 static PHP_METHOD(pqcopy, get) {
248 zend_error_handling zeh;
249 zval *zdata;
250 STATUS rv;
251
252 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
253 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zdata);
254 zend_restore_error_handling(&zeh TSRMLS_CC);
255
256 if (SUCCESS == rv) {
257 php_pqcopy_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
258
259 if (!obj->intern) {
260 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\COPY not initialized");
261 } else if (obj->intern->direction != PHP_PQCOPY_TO_STDOUT) {
262 throw_exce(EX_RUNTIME TSRMLS_CC, "pq\\COPY was not intialized with TO_STDOUT");
263 } else {
264 PGresult *res;
265 char *buffer = NULL;
266 int bytes = PQgetCopyData(obj->intern->conn->intern->conn, &buffer, 0);
267
268 switch (bytes) {
269 case -2:
270 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to fetch COPY data (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
271 break;
272
273 case -1:
274 res = PQgetResult(obj->intern->conn->intern->conn);
275
276 if (!res) {
277 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to fetch COPY result (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
278 } else {
279 php_pqres_success(res TSRMLS_CC);
280 PHP_PQclear(res);
281 RETVAL_FALSE;
282 }
283 break;
284
285 default:
286 zval_dtor(zdata);
287 if (buffer) {
288 ZVAL_STRINGL(zdata, buffer, bytes, 1);
289 } else {
290 ZVAL_EMPTY_STRING(zdata);
291 }
292 RETVAL_TRUE;
293 break;
294 }
295
296 if (buffer) {
297 PQfreemem(buffer);
298 }
299 }
300 }
301 }
302
303 static zend_function_entry php_pqcopy_methods[] = {
304 PHP_ME(pqcopy, __construct, ai_pqcopy_construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
305 PHP_ME(pqcopy, put, ai_pqcopy_put, ZEND_ACC_PUBLIC)
306 PHP_ME(pqcopy, end, ai_pqcopy_end, ZEND_ACC_PUBLIC)
307 PHP_ME(pqcopy, get, ai_pqcopy_get, ZEND_ACC_PUBLIC)
308 {0}
309 };
310
311 PHP_MSHUTDOWN_FUNCTION(pqcopy)
312 {
313 zend_hash_destroy(&php_pqcopy_object_prophandlers);
314 return SUCCESS;
315 }
316
317 PHP_MINIT_FUNCTION(pqcopy)
318 {
319 zend_class_entry ce = {0};
320 php_pq_object_prophandler_t ph = {0};
321
322 INIT_NS_CLASS_ENTRY(ce, "pq", "COPY", php_pqcopy_methods);
323 php_pqcopy_class_entry = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC);
324 php_pqcopy_class_entry->create_object = php_pqcopy_create_object;
325
326 memcpy(&php_pqcopy_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
327 php_pqcopy_object_handlers.read_property = php_pq_object_read_prop;
328 php_pqcopy_object_handlers.write_property = php_pq_object_write_prop;
329 php_pqcopy_object_handlers.clone_obj = NULL;
330 php_pqcopy_object_handlers.get_property_ptr_ptr = NULL;
331 php_pqcopy_object_handlers.get_gc = NULL;
332 php_pqcopy_object_handlers.get_properties = php_pq_object_properties;
333 php_pqcopy_object_handlers.get_debug_info = php_pq_object_debug_info;
334
335 zend_hash_init(&php_pqcopy_object_prophandlers, 4, NULL, NULL, 1);
336
337 zend_declare_property_null(php_pqcopy_class_entry, ZEND_STRL("connection"), ZEND_ACC_PUBLIC TSRMLS_CC);
338 ph.read = php_pqcopy_object_read_connection;
339 zend_hash_add(&php_pqcopy_object_prophandlers, "connection", sizeof("connection"), (void *) &ph, sizeof(ph), NULL);
340
341 zend_declare_property_null(php_pqcopy_class_entry, ZEND_STRL("expression"), ZEND_ACC_PUBLIC TSRMLS_CC);
342 ph.read = php_pqcopy_object_read_expression;
343 zend_hash_add(&php_pqcopy_object_prophandlers, "expression", sizeof("expression"), (void *) &ph, sizeof(ph), NULL);
344
345 zend_declare_property_null(php_pqcopy_class_entry, ZEND_STRL("direction"), ZEND_ACC_PUBLIC TSRMLS_CC);
346 ph.read = php_pqcopy_object_read_direction;
347 zend_hash_add(&php_pqcopy_object_prophandlers, "direction", sizeof("direction"), (void *) &ph, sizeof(ph), NULL);
348
349 zend_declare_property_null(php_pqcopy_class_entry, ZEND_STRL("options"), ZEND_ACC_PUBLIC TSRMLS_CC);
350 ph.read = php_pqcopy_object_read_options;
351 zend_hash_add(&php_pqcopy_object_prophandlers, "options", sizeof("options"), (void *) &ph, sizeof(ph), NULL);
352
353 zend_declare_class_constant_long(php_pqcopy_class_entry, ZEND_STRL("FROM_STDIN"), PHP_PQCOPY_FROM_STDIN TSRMLS_CC);
354 zend_declare_class_constant_long(php_pqcopy_class_entry, ZEND_STRL("TO_STDOUT"), PHP_PQCOPY_TO_STDOUT TSRMLS_CC);
355
356 return SUCCESS;
357 }
358 /*
359 * Local variables:
360 * tab-width: 4
361 * c-basic-offset: 4
362 * End:
363 * vim600: noet sw=4 ts=4 fdm=marker
364 * vim<600: noet sw=4 ts=4
365 */