2 +--------------------------------------------------------------------+
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 +--------------------------------------------------------------------+
17 #include <libpq-events.h>
18 #include <libpq/libpq-fs.h>
22 #include "php_pq_misc.h"
23 #include "php_pq_object.h"
24 #include "php_pqexc.h"
25 #include "php_pqlob.h"
27 zend_class_entry
*php_pqlob_class_entry
;
28 static zend_object_handlers php_pqlob_object_handlers
;
29 static HashTable php_pqlob_object_prophandlers
;
31 static void php_pqlob_object_free(void *o TSRMLS_DC
)
33 php_pqlob_object_t
*obj
= o
;
35 fprintf(stderr
, "FREE lob(#%d) %p (txn(#%d): %p)\n", obj
->zv
.handle
, obj
, obj
->intern
->txn
->zv
.handle
, obj
->intern
->txn
);
38 if (obj
->intern
->lofd
) {
39 lo_close(obj
->intern
->txn
->intern
->conn
->intern
->conn
, obj
->intern
->lofd
);
41 /* invalidate the stream */
42 if (obj
->intern
->stream
) {
43 zend_list_delete(obj
->intern
->stream
);
44 obj
->intern
->stream
= 0;
46 php_pq_object_delref(obj
->intern
->txn TSRMLS_CC
);
50 zend_object_std_dtor((zend_object
*) o TSRMLS_CC
);
54 zend_object_value
php_pqlob_create_object_ex(zend_class_entry
*ce
, php_pqlob_t
*intern
, php_pqlob_object_t
**ptr TSRMLS_DC
)
56 php_pqlob_object_t
*o
;
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
;
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
;
77 static zend_object_value
php_pqlob_create_object(zend_class_entry
*class_type TSRMLS_DC
)
79 return php_pqlob_create_object_ex(class_type
, NULL
, NULL TSRMLS_CC
);
82 static void php_pqlob_object_read_transaction(zval
*object
, void *o
, zval
*return_value TSRMLS_DC
)
84 php_pqlob_object_t
*obj
= o
;
86 php_pq_object_to_zval(obj
->intern
->txn
, &return_value TSRMLS_CC
);
89 static void php_pqlob_object_read_oid(zval
*object
, void *o
, zval
*return_value TSRMLS_DC
)
91 php_pqlob_object_t
*obj
= o
;
93 RETVAL_LONG(obj
->intern
->loid
);
96 static void php_pqlob_object_update_stream(zval
*this_ptr
, php_pqlob_object_t
*obj
, zval
**zstream TSRMLS_DC
);
98 static void php_pqlob_object_read_stream(zval
*object
, void *o
, zval
*return_value TSRMLS_DC
)
100 php_pqlob_object_t
*obj
= o
;
102 if (!obj
->intern
->stream
) {
105 php_pqlob_object_update_stream(object
, obj
, &zstream TSRMLS_CC
);
106 RETVAL_ZVAL(zstream
, 1, 1);
108 RETVAL_RESOURCE(obj
->intern
->stream
);
109 zend_list_addref(obj
->intern
->stream
);
113 static size_t php_pqlob_stream_write(php_stream
*stream
, const char *buffer
, size_t length TSRMLS_DC
)
115 php_pqlob_object_t
*obj
= stream
->abstract
;
119 written
= lo_write(obj
->intern
->txn
->intern
->conn
->intern
->conn
, obj
->intern
->lofd
, buffer
, length
);
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
));
125 php_pqconn_notify_listeners(obj
->intern
->txn
->intern
->conn TSRMLS_CC
);
131 static size_t php_pqlob_stream_read(php_stream
*stream
, char *buffer
, size_t length TSRMLS_DC
)
133 php_pqlob_object_t
*obj
= stream
->abstract
;
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
)) {
143 read
= lo_read(obj
->intern
->txn
->intern
->conn
->intern
->conn
, obj
->intern
->lofd
, buffer
, length
);
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
));
150 php_pqconn_notify_listeners(obj
->intern
->txn
->intern
->conn TSRMLS_CC
);
156 static STATUS
php_pqlob_stream_close(php_stream
*stream
, int close_handle TSRMLS_DC
)
161 static int php_pqlob_stream_flush(php_stream
*stream TSRMLS_DC
)
166 static STATUS
php_pqlob_stream_seek(php_stream
*stream
, off_t offset
, int whence
, off_t
*newoffset TSRMLS_DC
)
169 php_pqlob_object_t
*obj
= stream
->abstract
;
172 int position
= lo_lseek(obj
->intern
->txn
->intern
->conn
->intern
->conn
, obj
->intern
->lofd
, offset
, whence
);
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
));
178 *newoffset
= position
;
182 php_pqconn_notify_listeners(obj
->intern
->txn
->intern
->conn TSRMLS_CC
);
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
,
197 /* these are optional */
198 php_pqlob_stream_seek
,
201 NULL
, /* set_option */
204 static void php_pqlob_object_update_stream(zval
*this_ptr
, php_pqlob_object_t
*obj
, zval
**zstream_ptr TSRMLS_DC
)
206 zval
*zstream
, zmember
;
209 INIT_PZVAL(&zmember
);
210 ZVAL_STRINGL(&zmember
, "stream", sizeof("stream")-1, 0);
212 MAKE_STD_ZVAL(zstream
);
214 obj
= zend_object_store_get_object(getThis() TSRMLS_CC
);
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
);
221 zend_get_std_object_handlers()->write_property(getThis(), &zmember
, zstream
, NULL TSRMLS_CC
);
224 *zstream_ptr
= zstream
;
226 zval_ptr_dtor(&zstream
);
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
)
235 static PHP_METHOD(pqlob
, __construct
) {
236 zend_error_handling zeh
;
238 long mode
= INV_WRITE
|INV_READ
, loid
= InvalidOid
;
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
);
246 php_pqlob_object_t
*obj
= zend_object_store_get_object(getThis() TSRMLS_CC
);
247 php_pqtxn_object_t
*txn_obj
= zend_object_store_get_object(ztxn TSRMLS_CC
);
250 throw_exce(EX_BAD_METHODCALL TSRMLS_CC
, "pq\\LOB already initialized");
251 } else if (!txn_obj
->intern
) {
252 throw_exce(EX_UNINITIALIZED TSRMLS_CC
, "pq\\Transaction not initialized");
253 } else if (!txn_obj
->intern
->open
) {
254 throw_exce(EX_RUNTIME TSRMLS_CC
, "pq\\Transation already closed");
256 if (loid
== InvalidOid
) {
257 loid
= lo_creat(txn_obj
->intern
->conn
->intern
->conn
, mode
);
260 if (loid
== InvalidOid
) {
261 throw_exce(EX_RUNTIME TSRMLS_CC
, "Failed to create large object with mode '%s' (%s)", php_pq_strmode(mode
), PHP_PQerrorMessage(txn_obj
->intern
->conn
->intern
->conn
));
263 int lofd
= lo_open(txn_obj
->intern
->conn
->intern
->conn
, loid
, mode
);
266 throw_exce(EX_RUNTIME TSRMLS_CC
, "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
));
268 obj
->intern
= ecalloc(1, sizeof(*obj
->intern
));
269 obj
->intern
->lofd
= lofd
;
270 obj
->intern
->loid
= loid
;
271 php_pq_object_addref(txn_obj TSRMLS_CC
);
272 obj
->intern
->txn
= txn_obj
;
276 php_pqconn_notify_listeners(txn_obj
->intern
->conn TSRMLS_CC
);
281 ZEND_BEGIN_ARG_INFO_EX(ai_pqlob_write
, 0, 0, 1)
282 ZEND_ARG_INFO(0, data
)
284 static PHP_METHOD(pqlob
, write
) {
285 zend_error_handling zeh
;
290 zend_replace_error_handling(EH_THROW
, exce(EX_INVALID_ARGUMENT
), &zeh TSRMLS_CC
);
291 rv
= zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "s", &data_str
, &data_len
);
292 zend_restore_error_handling(&zeh TSRMLS_CC
);
295 php_pqlob_object_t
*obj
= zend_object_store_get_object(getThis() TSRMLS_CC
);
298 throw_exce(EX_UNINITIALIZED TSRMLS_CC
, "pq\\LOB not initialized");
300 int written
= lo_write(obj
->intern
->txn
->intern
->conn
->intern
->conn
, obj
->intern
->lofd
, data_str
, data_len
);
303 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
));
305 RETVAL_LONG(written
);
308 php_pqconn_notify_listeners(obj
->intern
->txn
->intern
->conn TSRMLS_CC
);
313 ZEND_BEGIN_ARG_INFO_EX(ai_pqlob_read
, 0, 0, 0)
314 ZEND_ARG_INFO(0, length
)
315 ZEND_ARG_INFO(1, read
)
317 static PHP_METHOD(pqlob
, read
) {
318 zend_error_handling zeh
;
319 long length
= 0x1000;
323 zend_replace_error_handling(EH_THROW
, exce(EX_INVALID_ARGUMENT
), &zeh TSRMLS_CC
);
324 rv
= zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "|lz!", &length
, &zread
);
325 zend_restore_error_handling(&zeh TSRMLS_CC
);
328 php_pqlob_object_t
*obj
= zend_object_store_get_object(getThis() TSRMLS_CC
);
331 throw_exce(EX_UNINITIALIZED TSRMLS_CC
, "pq\\LOB not initialized");
333 char *buffer
= emalloc(length
+ 1);
334 int read
= lo_read(obj
->intern
->txn
->intern
->conn
->intern
->conn
, obj
->intern
->lofd
, buffer
, length
);
338 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
));
342 ZVAL_LONG(zread
, read
);
345 RETVAL_STRINGL(buffer
, read
, 0);
348 php_pqconn_notify_listeners(obj
->intern
->txn
->intern
->conn TSRMLS_CC
);
353 ZEND_BEGIN_ARG_INFO_EX(ai_pqlob_seek
, 0, 0, 1)
354 ZEND_ARG_INFO(0, offset
)
355 ZEND_ARG_INFO(0, whence
)
357 static PHP_METHOD(pqlob
, seek
) {
358 zend_error_handling zeh
;
359 long offset
, whence
= SEEK_SET
;
362 zend_replace_error_handling(EH_THROW
, exce(EX_INVALID_ARGUMENT
), &zeh TSRMLS_CC
);
363 rv
= zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "l|l", &offset
, &whence
);
364 zend_restore_error_handling(&zeh TSRMLS_CC
);
367 php_pqlob_object_t
*obj
= zend_object_store_get_object(getThis() TSRMLS_CC
);
370 throw_exce(EX_UNINITIALIZED TSRMLS_CC
, "pq\\LOB not initialized");
372 int position
= lo_lseek(obj
->intern
->txn
->intern
->conn
->intern
->conn
, obj
->intern
->lofd
, offset
, whence
);
375 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
));
377 RETVAL_LONG(position
);
380 php_pqconn_notify_listeners(obj
->intern
->txn
->intern
->conn TSRMLS_CC
);
385 ZEND_BEGIN_ARG_INFO_EX(ai_pqlob_tell
, 0, 0, 0)
387 static PHP_METHOD(pqlob
, tell
) {
388 zend_error_handling zeh
;
391 zend_replace_error_handling(EH_THROW
, exce(EX_INVALID_ARGUMENT
), &zeh TSRMLS_CC
);
392 rv
= zend_parse_parameters_none();
393 zend_restore_error_handling(&zeh TSRMLS_CC
);
396 php_pqlob_object_t
*obj
= zend_object_store_get_object(getThis() TSRMLS_CC
);
399 throw_exce(EX_UNINITIALIZED TSRMLS_CC
, "pq\\LOB not initialized");
401 int position
= lo_tell(obj
->intern
->txn
->intern
->conn
->intern
->conn
, obj
->intern
->lofd
);
404 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
));
406 RETVAL_LONG(position
);
409 php_pqconn_notify_listeners(obj
->intern
->txn
->intern
->conn TSRMLS_CC
);
414 ZEND_BEGIN_ARG_INFO_EX(ai_pqlob_truncate
, 0, 0, 0)
415 ZEND_ARG_INFO(0, length
)
417 static PHP_METHOD(pqlob
, truncate
) {
418 zend_error_handling zeh
;
422 zend_replace_error_handling(EH_THROW
, exce(EX_INVALID_ARGUMENT
), &zeh TSRMLS_CC
);
423 rv
= zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "|l", &length
);
424 zend_restore_error_handling(&zeh TSRMLS_CC
);
427 php_pqlob_object_t
*obj
= zend_object_store_get_object(getThis() TSRMLS_CC
);
430 throw_exce(EX_UNINITIALIZED TSRMLS_CC
, "pq\\LOB not initialized");
432 int rc
= lo_truncate(obj
->intern
->txn
->intern
->conn
->intern
->conn
, obj
->intern
->lofd
, length
);
435 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
));
438 php_pqconn_notify_listeners(obj
->intern
->txn
->intern
->conn TSRMLS_CC
);
443 static zend_function_entry php_pqlob_methods
[] = {
444 PHP_ME(pqlob
, __construct
, ai_pqlob_construct
, ZEND_ACC_PUBLIC
|ZEND_ACC_CTOR
)
445 PHP_ME(pqlob
, write
, ai_pqlob_write
, ZEND_ACC_PUBLIC
)
446 PHP_ME(pqlob
, read
, ai_pqlob_read
, ZEND_ACC_PUBLIC
)
447 PHP_ME(pqlob
, seek
, ai_pqlob_seek
, ZEND_ACC_PUBLIC
)
448 PHP_ME(pqlob
, tell
, ai_pqlob_tell
, ZEND_ACC_PUBLIC
)
449 PHP_ME(pqlob
, truncate
, ai_pqlob_truncate
, ZEND_ACC_PUBLIC
)
453 PHP_MSHUTDOWN_FUNCTION(pqlob
)
455 zend_hash_destroy(&php_pqlob_object_prophandlers
);
459 PHP_MINIT_FUNCTION(pqlob
)
461 zend_class_entry ce
= {0};
462 php_pq_object_prophandler_t ph
= {0};
464 INIT_NS_CLASS_ENTRY(ce
, "pq", "LOB", php_pqlob_methods
);
465 php_pqlob_class_entry
= zend_register_internal_class_ex(&ce
, NULL
, NULL TSRMLS_CC
);
466 php_pqlob_class_entry
->create_object
= php_pqlob_create_object
;
468 memcpy(&php_pqlob_object_handlers
, zend_get_std_object_handlers(), sizeof(zend_object_handlers
));
469 php_pqlob_object_handlers
.read_property
= php_pq_object_read_prop
;
470 php_pqlob_object_handlers
.write_property
= php_pq_object_write_prop
;
471 php_pqlob_object_handlers
.clone_obj
= NULL
;
472 php_pqlob_object_handlers
.get_property_ptr_ptr
= NULL
;
473 php_pqlob_object_handlers
.get_gc
= NULL
;
474 php_pqlob_object_handlers
.get_properties
= php_pq_object_properties
;
475 php_pqlob_object_handlers
.get_debug_info
= php_pq_object_debug_info
;
477 zend_hash_init(&php_pqlob_object_prophandlers
, 3, NULL
, NULL
, 1);
479 zend_declare_property_null(php_pqlob_class_entry
, ZEND_STRL("transaction"), ZEND_ACC_PUBLIC TSRMLS_CC
);
480 ph
.read
= php_pqlob_object_read_transaction
;
481 zend_hash_add(&php_pqlob_object_prophandlers
, "transaction", sizeof("transaction"), (void *) &ph
, sizeof(ph
), NULL
);
483 zend_declare_property_long(php_pqlob_class_entry
, ZEND_STRL("oid"), InvalidOid
, ZEND_ACC_PUBLIC TSRMLS_CC
);
484 ph
.read
= php_pqlob_object_read_oid
;
485 zend_hash_add(&php_pqlob_object_prophandlers
, "oid", sizeof("oid"), (void *) &ph
, sizeof(ph
), NULL
);
487 zend_declare_property_null(php_pqlob_class_entry
, ZEND_STRL("stream"), ZEND_ACC_PUBLIC TSRMLS_CC
);
488 ph
.read
= php_pqlob_object_read_stream
;
489 zend_hash_add(&php_pqlob_object_prophandlers
, "stream", sizeof("stream"), (void *) &ph
, sizeof(ph
), NULL
);
491 zend_declare_class_constant_long(php_pqlob_class_entry
, ZEND_STRL("INVALID_OID"), InvalidOid TSRMLS_CC
);
492 zend_declare_class_constant_long(php_pqlob_class_entry
, ZEND_STRL("R"), INV_READ TSRMLS_CC
);
493 zend_declare_class_constant_long(php_pqlob_class_entry
, ZEND_STRL("W"), INV_WRITE TSRMLS_CC
);
494 zend_declare_class_constant_long(php_pqlob_class_entry
, ZEND_STRL("RW"), INV_READ
|INV_WRITE TSRMLS_CC
);
504 * vim600: noet sw=4 ts=4 fdm=marker
505 * vim<600: noet sw=4 ts=4