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