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