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