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(zend_object
*o
)
33 php_pqlob_object_t
*obj
= PHP_PQ_OBJ(NULL
, o
);
35 fprintf(stderr
, "FREE lob(#%d) %p (txn(#%d): %p)\n", obj
->zo
.handle
, obj
, obj
->intern
->txn
->zo
.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
->res
);
44 obj
->intern
->stream
= NULL
;
46 php_pq_object_delref(obj
->intern
->txn
);
50 php_pq_object_dtor(o
);
53 php_pqlob_object_t
*php_pqlob_create_object_ex(zend_class_entry
*ce
, php_pqlob_t
*intern
)
55 return php_pq_object_create(ce
, intern
, sizeof(php_pqlob_object_t
),
56 &php_pqlob_object_handlers
, &php_pqlob_object_prophandlers
);
59 static zend_object
*php_pqlob_create_object(zend_class_entry
*class_type
)
61 return &php_pqlob_create_object_ex(class_type
, NULL
)->zo
;
64 static void php_pqlob_object_read_transaction(void *o
, zval
*return_value
)
66 php_pqlob_object_t
*obj
= o
;
68 php_pq_object_to_zval(obj
->intern
->txn
, return_value
);
71 static void php_pqlob_object_gc_transaction(void *o
, zval
*return_value
)
73 php_pqlob_object_t
*obj
= o
;
76 php_pq_object_to_zval_no_addref(obj
->intern
->txn
, &ztxn
);
77 add_next_index_zval(return_value
, &ztxn
);
80 static void php_pqlob_object_read_oid(void *o
, zval
*return_value
)
82 php_pqlob_object_t
*obj
= o
;
84 RETVAL_LONG(obj
->intern
->loid
);
87 static void php_pqlob_object_update_stream(php_pqlob_object_t
*obj
, zval
*zstream
);
89 static void php_pqlob_object_read_stream(void *o
, zval
*return_value
)
91 php_pqlob_object_t
*obj
= o
;
94 if (!obj
->intern
->stream
) {
95 php_pqlob_object_update_stream(obj
, &zstream
);
97 php_stream_to_zval(obj
->intern
->stream
, &zstream
);
100 RETVAL_ZVAL(&zstream
, 1, 0);
103 #if PHP_VERSION_ID < 70400
104 static size_t php_pqlob_stream_write(php_stream
*stream
, const char *buffer
, size_t length
)
106 static ssize_t
php_pqlob_stream_write(php_stream
*stream
, const char *buffer
, size_t length
)
109 php_pqlob_object_t
*obj
= stream
->abstract
;
113 written
= lo_write(obj
->intern
->txn
->intern
->conn
->intern
->conn
, obj
->intern
->lofd
, buffer
, length
);
116 php_error_docref(NULL
, E_WARNING
, "Failed to write to LOB with oid=%u (%s)", obj
->intern
->loid
, PHP_PQerrorMessage(obj
->intern
->txn
->intern
->conn
->intern
->conn
));
119 php_pqconn_notify_listeners(obj
->intern
->txn
->intern
->conn
);
122 #if PHP_VERSION_ID < 70400
123 return (written
< 0 ? 0 : written
);
129 #if PHP_VERSION_ID < 70400
130 static size_t php_pqlob_stream_read(php_stream
*stream
, char *buffer
, size_t length
)
132 static ssize_t
php_pqlob_stream_read(php_stream
*stream
, char *buffer
, size_t length
)
135 php_pqlob_object_t
*obj
= stream
->abstract
;
140 if (!buffer
&& !length
) {
141 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
)) {
145 read
= lo_read(obj
->intern
->txn
->intern
->conn
->intern
->conn
, obj
->intern
->lofd
, buffer
, length
);
148 php_error_docref(NULL
, E_WARNING
, "Failed to read from LOB with oid=%d (%s)", obj
->intern
->loid
, PHP_PQerrorMessage(obj
->intern
->txn
->intern
->conn
->intern
->conn
));
152 php_pqconn_notify_listeners(obj
->intern
->txn
->intern
->conn
);
155 #if PHP_VERSION_ID < 70400
156 return (read
< 0 ? 0 : read
);
162 static ZEND_RESULT_CODE
php_pqlob_stream_close(php_stream
*stream
, int close_handle
)
167 static int php_pqlob_stream_flush(php_stream
*stream
)
172 static ZEND_RESULT_CODE
php_pqlob_stream_seek(php_stream
*stream
, off_t offset
, int whence
, off_t
*newoffset
)
174 ZEND_RESULT_CODE rv
= FAILURE
;
175 php_pqlob_object_t
*obj
= stream
->abstract
;
178 int position
= lo_lseek(obj
->intern
->txn
->intern
->conn
->intern
->conn
, obj
->intern
->lofd
, offset
, whence
);
181 php_error_docref(NULL
, E_WARNING
, "Failed to seek offset in LOB with oid=%d (%s)", obj
->intern
->loid
, PHP_PQerrorMessage(obj
->intern
->txn
->intern
->conn
->intern
->conn
));
184 *newoffset
= position
;
188 php_pqconn_notify_listeners(obj
->intern
->txn
->intern
->conn
);
194 static php_stream_ops php_pqlob_stream_ops
= {
195 /* stdio like functions - these are mandatory! */
196 php_pqlob_stream_write
,
197 php_pqlob_stream_read
,
198 php_pqlob_stream_close
,
199 php_pqlob_stream_flush
,
203 /* these are optional */
204 php_pqlob_stream_seek
,
207 NULL
, /* set_option */
210 static void php_pqlob_object_update_stream(php_pqlob_object_t
*obj
, zval
*zstream
)
214 ZVAL_STRINGL(&zmember
, "stream", sizeof("stream")-1);
216 obj
->intern
->stream
= php_stream_alloc(&php_pqlob_stream_ops
, obj
, NULL
, "r+b");
217 obj
->intern
->stream
->flags
|= PHP_STREAM_FLAG_NO_FCLOSE
;
218 php_stream_to_zval(obj
->intern
->stream
, zstream
);
220 #if PHP_VERSION_ID >= 80000
221 zend_std_write_property(&obj
->zo
, Z_STR(zmember
), zstream
, NULL
);
223 ZVAL_OBJ(&zobj
, &obj
->zo
);
224 zend_std_write_property(&zobj
, &zmember
, zstream
, NULL
);
226 zval_ptr_dtor(&zmember
);
229 ZEND_BEGIN_ARG_INFO_EX(ai_pqlob_construct
, 0, 0, 1)
230 ZEND_ARG_OBJ_INFO(0, transaction
, pq
\\Transaction
, 0)
231 ZEND_ARG_INFO(0, oid
)
232 ZEND_ARG_INFO(0, mode
)
234 static PHP_METHOD(pqlob
, __construct
) {
235 zend_error_handling zeh
;
237 zend_long mode
= INV_WRITE
|INV_READ
, loid
= InvalidOid
;
240 zend_replace_error_handling(EH_THROW
, exce(EX_INVALID_ARGUMENT
), &zeh
);
241 rv
= zend_parse_parameters(ZEND_NUM_ARGS(), "O|ll", &ztxn
, php_pqtxn_class_entry
, &loid
, &mode
);
242 zend_restore_error_handling(&zeh
);
245 php_pqlob_object_t
*obj
= PHP_PQ_OBJ(getThis(), NULL
);
246 php_pqtxn_object_t
*txn_obj
= PHP_PQ_OBJ(ztxn
, NULL
);
249 throw_exce(EX_BAD_METHODCALL
, "pq\\LOB already initialized");
250 } else if (!txn_obj
->intern
) {
251 throw_exce(EX_UNINITIALIZED
, "pq\\Transaction not initialized");
252 } else if (!txn_obj
->intern
->open
) {
253 throw_exce(EX_RUNTIME
, "pq\\Transation already closed");
255 if (loid
== InvalidOid
) {
256 loid
= lo_creat(txn_obj
->intern
->conn
->intern
->conn
, mode
);
259 if (loid
== InvalidOid
) {
260 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
));
262 int lofd
= lo_open(txn_obj
->intern
->conn
->intern
->conn
, loid
, mode
);
265 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
));
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
);
271 obj
->intern
->txn
= txn_obj
;
275 php_pqconn_notify_listeners(txn_obj
->intern
->conn
);
280 ZEND_BEGIN_ARG_INFO_EX(ai_pqlob_write
, 0, 0, 1)
281 ZEND_ARG_INFO(0, data
)
283 static PHP_METHOD(pqlob
, write
) {
284 zend_error_handling zeh
;
289 zend_replace_error_handling(EH_THROW
, exce(EX_INVALID_ARGUMENT
), &zeh
);
290 rv
= zend_parse_parameters(ZEND_NUM_ARGS(), "s", &data_str
, &data_len
);
291 zend_restore_error_handling(&zeh
);
294 php_pqlob_object_t
*obj
= PHP_PQ_OBJ(getThis(), NULL
);
297 throw_exce(EX_UNINITIALIZED
, "pq\\LOB not initialized");
299 int written
= lo_write(obj
->intern
->txn
->intern
->conn
->intern
->conn
, obj
->intern
->lofd
, data_str
, data_len
);
302 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
));
304 RETVAL_LONG(written
);
307 php_pqconn_notify_listeners(obj
->intern
->txn
->intern
->conn
);
312 ZEND_BEGIN_ARG_INFO_EX(ai_pqlob_read
, 0, 0, 0)
313 ZEND_ARG_INFO(0, length
)
314 ZEND_ARG_INFO(1, read
)
316 static PHP_METHOD(pqlob
, read
) {
317 zend_error_handling zeh
;
318 zend_long length
= 0x1000;
322 zend_replace_error_handling(EH_THROW
, exce(EX_INVALID_ARGUMENT
), &zeh
);
323 rv
= zend_parse_parameters(ZEND_NUM_ARGS(), "|lz!", &length
, &zread
);
324 zend_restore_error_handling(&zeh
);
327 php_pqlob_object_t
*obj
= PHP_PQ_OBJ(getThis(), NULL
);
330 throw_exce(EX_UNINITIALIZED
, "pq\\LOB not initialized");
332 zend_string
*buffer
= zend_string_alloc(length
, 0);
333 int read
= lo_read(obj
->intern
->txn
->intern
->conn
->intern
->conn
, obj
->intern
->lofd
, &buffer
->val
[0], length
);
336 zend_string_release(buffer
);
337 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
));
342 ZVAL_LONG(zread
, read
);
344 buffer
->val
[buffer
->len
= read
] = '\0';
348 php_pqconn_notify_listeners(obj
->intern
->txn
->intern
->conn
);
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 zend_long offset
, whence
= SEEK_SET
;
362 zend_replace_error_handling(EH_THROW
, exce(EX_INVALID_ARGUMENT
), &zeh
);
363 rv
= zend_parse_parameters(ZEND_NUM_ARGS(), "l|l", &offset
, &whence
);
364 zend_restore_error_handling(&zeh
);
367 php_pqlob_object_t
*obj
= PHP_PQ_OBJ(getThis(), NULL
);
370 throw_exce(EX_UNINITIALIZED
, "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
, "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
);
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
);
392 rv
= zend_parse_parameters_none();
393 zend_restore_error_handling(&zeh
);
396 php_pqlob_object_t
*obj
= PHP_PQ_OBJ(getThis(), NULL
);
399 throw_exce(EX_UNINITIALIZED
, "pq\\LOB not initialized");
401 int position
= lo_tell(obj
->intern
->txn
->intern
->conn
->intern
->conn
, obj
->intern
->lofd
);
404 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
));
406 RETVAL_LONG(position
);
409 php_pqconn_notify_listeners(obj
->intern
->txn
->intern
->conn
);
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
;
419 zend_long length
= 0;
422 zend_replace_error_handling(EH_THROW
, exce(EX_INVALID_ARGUMENT
), &zeh
);
423 rv
= zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &length
);
424 zend_restore_error_handling(&zeh
);
427 php_pqlob_object_t
*obj
= PHP_PQ_OBJ(getThis(), NULL
);
430 throw_exce(EX_UNINITIALIZED
, "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
, "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
);
443 static zend_function_entry php_pqlob_methods
[] = {
444 PHP_ME(pqlob
, __construct
, ai_pqlob_construct
, ZEND_ACC_PUBLIC
)
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
);
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
.offset
= XtOffsetOf(php_pqlob_object_t
, zo
);
470 php_pqlob_object_handlers
.free_obj
= php_pqlob_object_free
;
471 php_pqlob_object_handlers
.read_property
= php_pq_object_read_prop
;
472 php_pqlob_object_handlers
.write_property
= php_pq_object_write_prop
;
473 php_pqlob_object_handlers
.clone_obj
= NULL
;
474 php_pqlob_object_handlers
.get_property_ptr_ptr
= php_pq_object_get_prop_ptr_null
;
475 php_pqlob_object_handlers
.get_gc
= php_pq_object_get_gc
;
476 php_pqlob_object_handlers
.get_properties
= php_pq_object_properties
;
477 php_pqlob_object_handlers
.get_debug_info
= php_pq_object_debug_info
;
479 zend_hash_init(&php_pqlob_object_prophandlers
, 3, NULL
, php_pq_object_prophandler_dtor
, 1);
481 zend_declare_property_null(php_pqlob_class_entry
, ZEND_STRL("transaction"), ZEND_ACC_PUBLIC
);
482 ph
.read
= php_pqlob_object_read_transaction
;
483 ph
.gc
= php_pqlob_object_gc_transaction
;
484 zend_hash_str_add_mem(&php_pqlob_object_prophandlers
, "transaction", sizeof("transaction")-1, (void *) &ph
, sizeof(ph
));
487 zend_declare_property_long(php_pqlob_class_entry
, ZEND_STRL("oid"), InvalidOid
, ZEND_ACC_PUBLIC
);
488 ph
.read
= php_pqlob_object_read_oid
;
489 zend_hash_str_add_mem(&php_pqlob_object_prophandlers
, "oid", sizeof("oid")-1, (void *) &ph
, sizeof(ph
));
491 zend_declare_property_null(php_pqlob_class_entry
, ZEND_STRL("stream"), ZEND_ACC_PUBLIC
);
492 ph
.read
= php_pqlob_object_read_stream
;
493 zend_hash_str_add_mem(&php_pqlob_object_prophandlers
, "stream", sizeof("stream")-1, (void *) &ph
, sizeof(ph
));
495 zend_declare_class_constant_long(php_pqlob_class_entry
, ZEND_STRL("INVALID_OID"), InvalidOid
);
496 zend_declare_class_constant_long(php_pqlob_class_entry
, ZEND_STRL("R"), INV_READ
);
497 zend_declare_class_constant_long(php_pqlob_class_entry
, ZEND_STRL("W"), INV_WRITE
);
498 zend_declare_class_constant_long(php_pqlob_class_entry
, ZEND_STRL("RW"), INV_READ
|INV_WRITE
);
508 * vim600: noet sw=4 ts=4 fdm=marker
509 * vim<600: noet sw=4 ts=4