flush
[m6w6/ext-pq] / src / php_pqlob.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 <libpq-events.h>
18 #include <libpq/libpq-fs.h>
19 #include <php.h>
20
21 #include "php_pq.h"
22 #include "php_pq_misc.h"
23 #include "php_pq_object.h"
24 #include "php_pqexc.h"
25 #include "php_pqlob.h"
26
27 zend_class_entry *php_pqlob_class_entry;
28 static zend_object_handlers php_pqlob_object_handlers;
29 static HashTable php_pqlob_object_prophandlers;
30
31 static void php_pqlob_object_free(zend_object *o)
32 {
33 php_pqlob_object_t *obj = PHP_PQ_OBJ(NULL, o);
34 #if DBG_GC
35 fprintf(stderr, "FREE lob(#%d) %p (txn(#%d): %p)\n", obj->zv.handle, obj, obj->intern->txn->zv.handle, obj->intern->txn);
36 #endif
37 if (obj->intern) {
38 if (obj->intern->lofd) {
39 lo_close(obj->intern->txn->intern->conn->intern->conn, obj->intern->lofd);
40 }
41 /* invalidate the stream */
42 if (obj->intern->stream) {
43 zend_list_delete(obj->intern->stream->res);
44 obj->intern->stream = NULL;
45 }
46 php_pq_object_delref(obj->intern->txn);
47 efree(obj->intern);
48 obj->intern = NULL;
49 }
50 zend_object_std_dtor(o);
51 efree(obj);
52 }
53
54 php_pqlob_object_t *php_pqlob_create_object_ex(zend_class_entry *ce, php_pqlob_t *intern)
55 {
56 php_pqlob_object_t *o;
57
58 o = ecalloc(1, sizeof(*o) + zend_object_properties_size(ce));
59 zend_object_std_init(&o->zo, ce);
60 object_properties_init(&o->zo, ce);
61 o->prophandler = &php_pqlob_object_prophandlers;
62
63 if (intern) {
64 o->intern = intern;
65 }
66
67 o->zo.handlers = &php_pqlob_object_handlers;
68
69 return o;
70 }
71
72 static zend_object *php_pqlob_create_object(zend_class_entry *class_type)
73 {
74 return &php_pqlob_create_object_ex(class_type, NULL)->zo;
75 }
76
77 static void php_pqlob_object_read_transaction(zval *object, void *o, zval *return_value)
78 {
79 php_pqlob_object_t *obj = o;
80
81 php_pq_object_to_zval(obj->intern->txn, return_value);
82 }
83
84 static void php_pqlob_object_read_oid(zval *object, void *o, zval *return_value)
85 {
86 php_pqlob_object_t *obj = o;
87
88 RETVAL_LONG(obj->intern->loid);
89 }
90
91 static void php_pqlob_object_update_stream(zval *this_ptr, php_pqlob_object_t *obj, zval *zstream);
92
93 static void php_pqlob_object_read_stream(zval *object, void *o, zval *return_value)
94 {
95 php_pqlob_object_t *obj = o;
96 zval zstream;
97
98 if (!obj->intern->stream) {
99 php_pqlob_object_update_stream(object, obj, &zstream);
100 } else {
101 php_stream_to_zval(obj->intern->stream, &zstream);
102 }
103
104 RETVAL_ZVAL(&zstream, 1, 1);
105 }
106
107 static size_t php_pqlob_stream_write(php_stream *stream, const char *buffer, size_t length TSRMLS_DC)
108 {
109 php_pqlob_object_t *obj = stream->abstract;
110 int written = 0;
111
112 if (obj) {
113 written = lo_write(obj->intern->txn->intern->conn->intern->conn, obj->intern->lofd, buffer, length);
114
115 if (written < 0) {
116 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to write to LOB with oid=%u (%s)", obj->intern->loid, PHP_PQerrorMessage(obj->intern->txn->intern->conn->intern->conn));
117 }
118
119 php_pqconn_notify_listeners(obj->intern->txn->intern->conn TSRMLS_CC);
120 }
121
122 return written;
123 }
124
125 static size_t php_pqlob_stream_read(php_stream *stream, char *buffer, size_t length TSRMLS_DC)
126 {
127 php_pqlob_object_t *obj = stream->abstract;
128 int read = 0;
129
130 if (obj) {
131
132 if (!buffer && !length) {
133 if (lo_tell(obj->intern->txn->intern->conn->intern->conn, obj->intern->lofd) == lo_lseek(obj->intern->txn->intern->conn->intern->conn, obj->intern->lofd, 0, SEEK_CUR)) {
134 return EOF;
135 }
136 } else {
137 read = lo_read(obj->intern->txn->intern->conn->intern->conn, obj->intern->lofd, buffer, length);
138
139 if (read < 0) {
140 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to read from LOB with oid=%d (%s)", obj->intern->loid, PHP_PQerrorMessage(obj->intern->txn->intern->conn->intern->conn));
141 }
142 }
143
144 php_pqconn_notify_listeners(obj->intern->txn->intern->conn TSRMLS_CC);
145 }
146
147 return read;
148 }
149
150 static ZEND_RESULT_CODE php_pqlob_stream_close(php_stream *stream, int close_handle TSRMLS_DC)
151 {
152 return SUCCESS;
153 }
154
155 static int php_pqlob_stream_flush(php_stream *stream TSRMLS_DC)
156 {
157 return SUCCESS;
158 }
159
160 static ZEND_RESULT_CODE php_pqlob_stream_seek(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC)
161 {
162 ZEND_RESULT_CODE rv = FAILURE;
163 php_pqlob_object_t *obj = stream->abstract;
164
165 if (obj) {
166 int position = lo_lseek(obj->intern->txn->intern->conn->intern->conn, obj->intern->lofd, offset, whence);
167
168 if (position < 0) {
169 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to seek offset in LOB with oid=%d (%s)", obj->intern->loid, PHP_PQerrorMessage(obj->intern->txn->intern->conn->intern->conn));
170 rv = FAILURE;
171 } else {
172 *newoffset = position;
173 rv = SUCCESS;
174 }
175
176 php_pqconn_notify_listeners(obj->intern->txn->intern->conn TSRMLS_CC);
177 }
178
179 return rv;
180 }
181
182 static php_stream_ops php_pqlob_stream_ops = {
183 /* stdio like functions - these are mandatory! */
184 php_pqlob_stream_write,
185 php_pqlob_stream_read,
186 php_pqlob_stream_close,
187 php_pqlob_stream_flush,
188
189 "pq\\LOB stream",
190
191 /* these are optional */
192 php_pqlob_stream_seek,
193 NULL, /* cast */
194 NULL, /* stat */
195 NULL, /* set_option */
196 };
197
198 static void php_pqlob_object_update_stream(zval *zpqlob, php_pqlob_object_t *obj, zval *zstream)
199 {
200 zval zmember;
201
202 ZVAL_STRINGL(&zmember, "stream", sizeof("stream")-1);
203
204 if (!obj) {
205 obj = PHP_PQ_OBJ(zpqlob, NULL);
206 }
207 obj->intern->stream = php_stream_alloc(&php_pqlob_stream_ops, obj, NULL, "r+b");
208 obj->intern->stream->flags |= PHP_STREAM_FLAG_NO_FCLOSE;
209 php_stream_to_zval(obj->intern->stream, zstream);
210
211 zend_get_std_object_handlers()->write_property(zpqlob, &zmember, zstream, NULL);
212 }
213
214 ZEND_BEGIN_ARG_INFO_EX(ai_pqlob_construct, 0, 0, 1)
215 ZEND_ARG_OBJ_INFO(0, transaction, pq\\Transaction, 0)
216 ZEND_ARG_INFO(0, oid)
217 ZEND_ARG_INFO(0, mode)
218 ZEND_END_ARG_INFO();
219 static PHP_METHOD(pqlob, __construct) {
220 zend_error_handling zeh;
221 zval *ztxn;
222 zend_long mode = INV_WRITE|INV_READ, loid = InvalidOid;
223 ZEND_RESULT_CODE rv;
224
225 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh);
226 rv = zend_parse_parameters(ZEND_NUM_ARGS(), "O|ll", &ztxn, php_pqtxn_class_entry, &loid, &mode);
227 zend_restore_error_handling(&zeh);
228
229 if (SUCCESS == rv) {
230 php_pqlob_object_t *obj = PHP_PQ_OBJ(getThis(), NULL);
231 php_pqtxn_object_t *txn_obj = PHP_PQ_OBJ(ztxn, NULL);
232
233 if (obj->intern) {
234 throw_exce(EX_BAD_METHODCALL, "pq\\LOB already initialized");
235 } else if (!txn_obj->intern) {
236 throw_exce(EX_UNINITIALIZED, "pq\\Transaction not initialized");
237 } else if (!txn_obj->intern->open) {
238 throw_exce(EX_RUNTIME, "pq\\Transation already closed");
239 } else {
240 if (loid == InvalidOid) {
241 loid = lo_creat(txn_obj->intern->conn->intern->conn, mode);
242 }
243
244 if (loid == InvalidOid) {
245 throw_exce(EX_RUNTIME, "Failed to create large object with mode '%s' (%s)", php_pq_strmode(mode), PHP_PQerrorMessage(txn_obj->intern->conn->intern->conn));
246 } else {
247 int lofd = lo_open(txn_obj->intern->conn->intern->conn, loid, mode);
248
249 if (lofd < 0) {
250 throw_exce(EX_RUNTIME, "Failed to open large object with oid=%u with mode '%s' (%s)", loid, php_pq_strmode(mode), PHP_PQerrorMessage(txn_obj->intern->conn->intern->conn));
251 } else {
252 obj->intern = ecalloc(1, sizeof(*obj->intern));
253 obj->intern->lofd = lofd;
254 obj->intern->loid = loid;
255 php_pq_object_addref(txn_obj);
256 obj->intern->txn = txn_obj;
257 }
258 }
259
260 php_pqconn_notify_listeners(txn_obj->intern->conn);
261 }
262 }
263 }
264
265 ZEND_BEGIN_ARG_INFO_EX(ai_pqlob_write, 0, 0, 1)
266 ZEND_ARG_INFO(0, data)
267 ZEND_END_ARG_INFO();
268 static PHP_METHOD(pqlob, write) {
269 zend_error_handling zeh;
270 char *data_str;
271 size_t data_len;
272 ZEND_RESULT_CODE rv;
273
274 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh);
275 rv = zend_parse_parameters(ZEND_NUM_ARGS(), "s", &data_str, &data_len);
276 zend_restore_error_handling(&zeh);
277
278 if (SUCCESS == rv) {
279 php_pqlob_object_t *obj = PHP_PQ_OBJ(getThis(), NULL);
280
281 if (!obj->intern) {
282 throw_exce(EX_UNINITIALIZED, "pq\\LOB not initialized");
283 } else {
284 int written = lo_write(obj->intern->txn->intern->conn->intern->conn, obj->intern->lofd, data_str, data_len);
285
286 if (written < 0) {
287 throw_exce(EX_RUNTIME, "Failed to write to LOB with oid=%u (%s)", obj->intern->loid, PHP_PQerrorMessage(obj->intern->txn->intern->conn->intern->conn));
288 } else {
289 RETVAL_LONG(written);
290 }
291
292 php_pqconn_notify_listeners(obj->intern->txn->intern->conn);
293 }
294 }
295 }
296
297 ZEND_BEGIN_ARG_INFO_EX(ai_pqlob_read, 0, 0, 0)
298 ZEND_ARG_INFO(0, length)
299 ZEND_ARG_INFO(1, read)
300 ZEND_END_ARG_INFO();
301 static PHP_METHOD(pqlob, read) {
302 zend_error_handling zeh;
303 zend_long length = 0x1000;
304 zval *zread = NULL;
305 ZEND_RESULT_CODE rv;
306
307 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh);
308 rv = zend_parse_parameters(ZEND_NUM_ARGS(), "|lz!", &length, &zread);
309 zend_restore_error_handling(&zeh);
310
311 if (SUCCESS == rv) {
312 php_pqlob_object_t *obj = PHP_PQ_OBJ(getThis(), NULL);
313
314 if (!obj->intern) {
315 throw_exce(EX_UNINITIALIZED, "pq\\LOB not initialized");
316 } else {
317 zend_string *buffer = zend_string_alloc(length, 0);
318 int read = lo_read(obj->intern->txn->intern->conn->intern->conn, obj->intern->lofd, &buffer->val[0], length);
319
320 if (read < 0) {
321 zend_string_release(buffer);
322 throw_exce(EX_RUNTIME, "Failed to read from LOB with oid=%d (%s)", obj->intern->loid, PHP_PQerrorMessage(obj->intern->txn->intern->conn->intern->conn));
323 } else {
324 if (zread) {
325 ZVAL_DEREF(zread);
326 zval_dtor(zread);
327 ZVAL_LONG(zread, read);
328 }
329 buffer->val[buffer->len = read] = '\0';
330 RETVAL_STR(buffer);
331 }
332
333 php_pqconn_notify_listeners(obj->intern->txn->intern->conn);
334 }
335 }
336 }
337
338 ZEND_BEGIN_ARG_INFO_EX(ai_pqlob_seek, 0, 0, 1)
339 ZEND_ARG_INFO(0, offset)
340 ZEND_ARG_INFO(0, whence)
341 ZEND_END_ARG_INFO();
342 static PHP_METHOD(pqlob, seek) {
343 zend_error_handling zeh;
344 zend_long offset, whence = SEEK_SET;
345 ZEND_RESULT_CODE rv;
346
347 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh);
348 rv = zend_parse_parameters(ZEND_NUM_ARGS(), "l|l", &offset, &whence);
349 zend_restore_error_handling(&zeh);
350
351 if (SUCCESS == rv) {
352 php_pqlob_object_t *obj = PHP_PQ_OBJ(getThis(), NULL);
353
354 if (!obj->intern) {
355 throw_exce(EX_UNINITIALIZED, "pq\\LOB not initialized");
356 } else {
357 int position = lo_lseek(obj->intern->txn->intern->conn->intern->conn, obj->intern->lofd, offset, whence);
358
359 if (position < 0) {
360 throw_exce(EX_RUNTIME, "Failed to seek offset in LOB with oid=%d (%s)", obj->intern->loid, PHP_PQerrorMessage(obj->intern->txn->intern->conn->intern->conn));
361 } else {
362 RETVAL_LONG(position);
363 }
364
365 php_pqconn_notify_listeners(obj->intern->txn->intern->conn);
366 }
367 }
368 }
369
370 ZEND_BEGIN_ARG_INFO_EX(ai_pqlob_tell, 0, 0, 0)
371 ZEND_END_ARG_INFO();
372 static PHP_METHOD(pqlob, tell) {
373 zend_error_handling zeh;
374 ZEND_RESULT_CODE rv;
375
376 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh);
377 rv = zend_parse_parameters_none();
378 zend_restore_error_handling(&zeh);
379
380 if (SUCCESS == rv) {
381 php_pqlob_object_t *obj = PHP_PQ_OBJ(getThis(), NULL);
382
383 if (!obj->intern) {
384 throw_exce(EX_UNINITIALIZED, "pq\\LOB not initialized");
385 } else {
386 int position = lo_tell(obj->intern->txn->intern->conn->intern->conn, obj->intern->lofd);
387
388 if (position < 0) {
389 throw_exce(EX_RUNTIME, "Failed to tell offset in LOB with oid=%d (%s)", obj->intern->loid, PHP_PQerrorMessage(obj->intern->txn->intern->conn->intern->conn));
390 } else {
391 RETVAL_LONG(position);
392 }
393
394 php_pqconn_notify_listeners(obj->intern->txn->intern->conn);
395 }
396 }
397 }
398
399 ZEND_BEGIN_ARG_INFO_EX(ai_pqlob_truncate, 0, 0, 0)
400 ZEND_ARG_INFO(0, length)
401 ZEND_END_ARG_INFO();
402 static PHP_METHOD(pqlob, truncate) {
403 zend_error_handling zeh;
404 zend_long length = 0;
405 ZEND_RESULT_CODE rv;
406
407 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh);
408 rv = zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &length);
409 zend_restore_error_handling(&zeh);
410
411 if (SUCCESS == rv) {
412 php_pqlob_object_t *obj = PHP_PQ_OBJ(getThis(), NULL);
413
414 if (!obj->intern) {
415 throw_exce(EX_UNINITIALIZED, "pq\\LOB not initialized");
416 } else {
417 int rc = lo_truncate(obj->intern->txn->intern->conn->intern->conn, obj->intern->lofd, length);
418
419 if (rc != 0) {
420 throw_exce(EX_RUNTIME, "Failed to truncate LOB with oid=%d (%s)", obj->intern->loid, PHP_PQerrorMessage(obj->intern->txn->intern->conn->intern->conn));
421 }
422
423 php_pqconn_notify_listeners(obj->intern->txn->intern->conn);
424 }
425 }
426 }
427
428 static zend_function_entry php_pqlob_methods[] = {
429 PHP_ME(pqlob, __construct, ai_pqlob_construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
430 PHP_ME(pqlob, write, ai_pqlob_write, ZEND_ACC_PUBLIC)
431 PHP_ME(pqlob, read, ai_pqlob_read, ZEND_ACC_PUBLIC)
432 PHP_ME(pqlob, seek, ai_pqlob_seek, ZEND_ACC_PUBLIC)
433 PHP_ME(pqlob, tell, ai_pqlob_tell, ZEND_ACC_PUBLIC)
434 PHP_ME(pqlob, truncate, ai_pqlob_truncate, ZEND_ACC_PUBLIC)
435 {0}
436 };
437
438 PHP_MSHUTDOWN_FUNCTION(pqlob)
439 {
440 zend_hash_destroy(&php_pqlob_object_prophandlers);
441 return SUCCESS;
442 }
443
444 PHP_MINIT_FUNCTION(pqlob)
445 {
446 zend_class_entry ce = {0};
447 php_pq_object_prophandler_t ph = {0};
448
449 INIT_NS_CLASS_ENTRY(ce, "pq", "LOB", php_pqlob_methods);
450 php_pqlob_class_entry = zend_register_internal_class_ex(&ce, NULL);
451 php_pqlob_class_entry->create_object = php_pqlob_create_object;
452
453 memcpy(&php_pqlob_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
454 php_pqlob_object_handlers.offset = XtOffsetOf(php_pqlob_object_t, zo);
455 php_pqlob_object_handlers.free_obj = php_pqlob_object_free;
456 php_pqlob_object_handlers.read_property = php_pq_object_read_prop;
457 php_pqlob_object_handlers.write_property = php_pq_object_write_prop;
458 php_pqlob_object_handlers.clone_obj = NULL;
459 php_pqlob_object_handlers.get_property_ptr_ptr = NULL;
460 php_pqlob_object_handlers.get_gc = NULL;
461 php_pqlob_object_handlers.get_properties = php_pq_object_properties;
462 php_pqlob_object_handlers.get_debug_info = php_pq_object_debug_info;
463
464 zend_hash_init(&php_pqlob_object_prophandlers, 3, NULL, NULL, 1);
465
466 zend_declare_property_null(php_pqlob_class_entry, ZEND_STRL("transaction"), ZEND_ACC_PUBLIC);
467 ph.read = php_pqlob_object_read_transaction;
468 zend_hash_str_add_mem(&php_pqlob_object_prophandlers, "transaction", sizeof("transaction")-1, (void *) &ph, sizeof(ph));
469
470 zend_declare_property_long(php_pqlob_class_entry, ZEND_STRL("oid"), InvalidOid, ZEND_ACC_PUBLIC);
471 ph.read = php_pqlob_object_read_oid;
472 zend_hash_str_add_mem(&php_pqlob_object_prophandlers, "oid", sizeof("oid"), (void *) &ph, sizeof(ph));
473
474 zend_declare_property_null(php_pqlob_class_entry, ZEND_STRL("stream"), ZEND_ACC_PUBLIC);
475 ph.read = php_pqlob_object_read_stream;
476 zend_hash_str_add_mem(&php_pqlob_object_prophandlers, "stream", sizeof("stream"), (void *) &ph, sizeof(ph));
477
478 zend_declare_class_constant_long(php_pqlob_class_entry, ZEND_STRL("INVALID_OID"), InvalidOid);
479 zend_declare_class_constant_long(php_pqlob_class_entry, ZEND_STRL("R"), INV_READ);
480 zend_declare_class_constant_long(php_pqlob_class_entry, ZEND_STRL("W"), INV_WRITE);
481 zend_declare_class_constant_long(php_pqlob_class_entry, ZEND_STRL("RW"), INV_READ|INV_WRITE);
482
483 return SUCCESS;
484 }
485
486 /*
487 * Local variables:
488 * tab-width: 4
489 * c-basic-offset: 4
490 * End:
491 * vim600: noet sw=4 ts=4 fdm=marker
492 * vim<600: noet sw=4 ts=4
493 */