xref: /PHP-5.6/ext/oci8/oci8_lob.c (revision 49493a2d)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 5                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2016 The PHP Group                                |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Authors: Stig S�ther Bakken <ssb@php.net>                            |
16    |          Thies C. Arntzen <thies@thieso.net>                         |
17    |                                                                      |
18    | Collection support by Andy Sautins <asautins@veripost.net>           |
19    | Temporary LOB support by David Benson <dbenson@mancala.com>          |
20    | ZTS per process OCIPLogon by Harald Radi <harald.radi@nme.at>        |
21    |                                                                      |
22    | Redesigned by: Antony Dovgal <antony@zend.com>                       |
23    |                Andi Gutmans <andi@zend.com>                          |
24    |                Wez Furlong <wez@omniti.com>                          |
25    +----------------------------------------------------------------------+
26 */
27 
28 /* $Id$ */
29 
30 
31 
32 #ifdef HAVE_CONFIG_H
33 #include "config.h"
34 #endif
35 
36 #include "php.h"
37 #include "ext/standard/info.h"
38 #include "php_ini.h"
39 
40 #if HAVE_OCI8
41 
42 #include "php_oci8.h"
43 #include "php_oci8_int.h"
44 
45 /* for import/export functions */
46 #include <fcntl.h>
47 
48 #ifndef O_BINARY
49 #define O_BINARY 0
50 #endif
51 
52 /* {{{ php_oci_lob_create()
53  Create LOB descriptor and allocate all the resources needed */
php_oci_lob_create(php_oci_connection * connection,long type TSRMLS_DC)54 php_oci_descriptor *php_oci_lob_create (php_oci_connection *connection, long type TSRMLS_DC)
55 {
56 	php_oci_descriptor *descriptor;
57 	sword errstatus;
58 
59 	switch (type) {
60 		case OCI_DTYPE_FILE:
61 		case OCI_DTYPE_LOB:
62 		case OCI_DTYPE_ROWID:
63 			/* these three are allowed */
64 			break;
65 		default:
66 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown descriptor type %ld", type);
67 			return NULL;
68 			break;
69 	}
70 
71 	descriptor = ecalloc(1, sizeof(php_oci_descriptor));
72 	descriptor->type = type;
73 	descriptor->connection = connection;
74 	zend_list_addref(descriptor->connection->id);
75 
76 	PHP_OCI_CALL_RETURN(errstatus, OCIDescriptorAlloc, (connection->env, (dvoid*)&(descriptor->descriptor), descriptor->type, (size_t) 0, (dvoid **) 0));
77 
78 	if (errstatus != OCI_SUCCESS) {
79 		OCI_G(errcode) = php_oci_error(OCI_G(err), errstatus TSRMLS_CC);
80 		PHP_OCI_HANDLE_ERROR(connection, OCI_G(errcode));
81 		efree(descriptor);
82 		return NULL;
83 	} else {
84 		OCI_G(errcode) = 0; /* retain backwards compat with OCI8 1.4 */
85 	}
86 
87 	PHP_OCI_REGISTER_RESOURCE(descriptor, le_descriptor);
88 
89 	descriptor->lob_current_position = 0;
90 	descriptor->lob_size = -1;				/* we should set it to -1 to know, that it's just not initialized */
91 	descriptor->buffering = PHP_OCI_LOB_BUFFER_DISABLED;				/* buffering is off by default */
92 	descriptor->charset_form = SQLCS_IMPLICIT;	/* default value */
93 	descriptor->charset_id = connection->charset;
94 	descriptor->is_open = 0;
95 	descriptor->chunk_size = 0;
96 
97 	if (descriptor->type == OCI_DTYPE_LOB || descriptor->type == OCI_DTYPE_FILE) {
98 		/* add Lobs & Files to hash. we'll flush them at the end */
99 		if (!connection->descriptors) {
100 			ALLOC_HASHTABLE(connection->descriptors);
101 			zend_hash_init(connection->descriptors, 0, NULL, php_oci_descriptor_flush_hash_dtor, 0);
102 			connection->descriptor_count = 0;
103 		}
104 
105 		descriptor->index = (connection->descriptor_count)++;
106 		if (connection->descriptor_count == LONG_MAX) {
107 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Internal descriptor counter has reached limit");
108 			php_oci_connection_descriptors_free(connection TSRMLS_CC);
109 			return NULL;
110 		}
111 
112 		zend_hash_index_update(connection->descriptors,descriptor->index,&descriptor,sizeof(php_oci_descriptor *),NULL);
113 	}
114 	return descriptor;
115 
116 }
117 /* }}} */
118 
119 /* {{{ php_oci_lob_get_length()
120  Get length of the LOB. The length is cached so we don't need to ask Oracle every time */
php_oci_lob_get_length(php_oci_descriptor * descriptor,ub4 * length TSRMLS_DC)121 int php_oci_lob_get_length (php_oci_descriptor *descriptor, ub4 *length TSRMLS_DC)
122 {
123 	php_oci_connection *connection = descriptor->connection;
124 	sword errstatus;
125 
126 	*length = 0;
127 
128 	if (descriptor->lob_size >= 0) {
129 		*length = descriptor->lob_size;
130 		return 0;
131 	} else {
132 		if (descriptor->type == OCI_DTYPE_FILE) {
133 			PHP_OCI_CALL_RETURN(errstatus, OCILobFileOpen, (connection->svc, connection->err, descriptor->descriptor, OCI_FILE_READONLY));
134 			if (errstatus != OCI_SUCCESS) {
135 				connection->errcode = php_oci_error(connection->err, errstatus TSRMLS_CC);
136 				PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
137 				return 1;
138 			}
139 		}
140 
141 		PHP_OCI_CALL_RETURN(errstatus, OCILobGetLength, (connection->svc, connection->err, descriptor->descriptor, (ub4 *)length));
142 
143 		if (errstatus != OCI_SUCCESS) {
144 			connection->errcode = php_oci_error(connection->err, errstatus TSRMLS_CC);
145 			PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
146 			return 1;
147 		}
148 
149 		descriptor->lob_size = *length;
150 
151 		if (descriptor->type == OCI_DTYPE_FILE) {
152 			PHP_OCI_CALL_RETURN(errstatus, OCILobFileClose, (connection->svc, connection->err, descriptor->descriptor));
153 
154 			if (errstatus != OCI_SUCCESS) {
155 				connection->errcode = php_oci_error(connection->err, errstatus TSRMLS_CC);
156 				PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
157 				return 1;
158 			}
159 		}
160 
161 		connection->errcode = 0; /* retain backwards compat with OCI8 1.4 */
162 	}
163 	return 0;
164 }
165 /* }}} */
166 
167 /* {{{ php_oci_lob_callback()
168    Append LOB portion to a memory buffer */
php_oci_lob_callback(dvoid * ctxp,CONST dvoid * bufxp,oraub8 len,ub1 piece,dvoid ** changed_bufpp,oraub8 * changed_lenp)169 sb4 php_oci_lob_callback (dvoid *ctxp, CONST dvoid *bufxp, oraub8 len, ub1 piece, dvoid **changed_bufpp, oraub8 *changed_lenp)
170 {
171 	ub4 lenp = (ub4) len;
172 	php_oci_lob_ctx *ctx = (php_oci_lob_ctx *)ctxp;
173 
174 	switch (piece)
175 	{
176 		case OCI_LAST_PIECE:
177 			if ((*(ctx->lob_len) + lenp) > (ctx->alloc_len)) {
178 				/* this should not happen ever */
179 				*(ctx->lob_data) = NULL;
180 				*(ctx->lob_len) = 0;
181 				return OCI_ERROR;
182 			}
183 			memcpy(*(ctx->lob_data) + *(ctx->lob_len), bufxp, (size_t) lenp);
184 			*(ctx->lob_len) += lenp;
185 			*(*(ctx->lob_data) + *(ctx->lob_len)) = 0x00;
186 			return OCI_CONTINUE;
187 
188 		case OCI_FIRST_PIECE:
189 		case OCI_NEXT_PIECE:
190 			if ((*(ctx->lob_len) + lenp) > ctx->alloc_len) {
191 				/* this should not happen ever */
192 				*(ctx->lob_data) = NULL;
193 				*(ctx->lob_len) = 0;
194 				return OCI_ERROR;
195 			}
196 			memcpy(*(ctx->lob_data) + *(ctx->lob_len), bufxp, (size_t) lenp);
197 			*(ctx->lob_len) += lenp;
198 			return OCI_CONTINUE;
199 
200 		default: {
201 			TSRMLS_FETCH();
202 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unexpected LOB piece id received (value:%d)", piece);
203 			*(ctx->lob_data) = NULL;
204 			*(ctx->lob_len) = 0;
205 			return OCI_ERROR;
206 		}
207 	}
208 }
209 /* }}} */
210 
211 /* {{{ php_oci_lob_calculate_buffer()
212    Work out the size for LOB buffering */
php_oci_lob_calculate_buffer(php_oci_descriptor * descriptor,long read_length TSRMLS_DC)213 static inline int php_oci_lob_calculate_buffer(php_oci_descriptor *descriptor, long read_length TSRMLS_DC)
214 {
215 	php_oci_connection *connection = descriptor->connection;
216 	ub4 chunk_size;
217 	sword errstatus;
218 
219 	if (descriptor->type == OCI_DTYPE_FILE) {
220 		return read_length;
221 	}
222 
223 	if (!descriptor->chunk_size) {
224 		PHP_OCI_CALL_RETURN(errstatus, OCILobGetChunkSize, (connection->svc, connection->err, descriptor->descriptor, &chunk_size));
225 
226 		if (errstatus != OCI_SUCCESS) {
227 			connection->errcode = php_oci_error(connection->err, errstatus TSRMLS_CC);
228 			PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
229 			return read_length; /* we have to return original length here */
230 		}
231 		descriptor->chunk_size = chunk_size;
232 		connection->errcode = 0; /* retain backwards compat with OCI8 1.4 */
233 	}
234 
235 	if ((read_length % descriptor->chunk_size) != 0) {
236 		return descriptor->chunk_size * ((read_length / descriptor->chunk_size) + 1);
237 	}
238 	return read_length;
239 }
240 /* }}} */
241 
242 /* {{{ php_oci_lob_read()
243  Read specified portion of the LOB into the buffer */
php_oci_lob_read(php_oci_descriptor * descriptor,long read_length,long initial_offset,char ** data,ub4 * data_len TSRMLS_DC)244 int php_oci_lob_read (php_oci_descriptor *descriptor, long read_length, long initial_offset, char **data, ub4 *data_len TSRMLS_DC)
245 {
246 	php_oci_connection *connection = descriptor->connection;
247 	ub4 length = 0;
248 	int buffer_size = PHP_OCI_LOB_BUFFER_SIZE;
249 	php_oci_lob_ctx ctx;
250 	ub1 *bufp;
251 	oraub8 bytes_read, offset = 0;
252 	oraub8 requested_len = read_length; /* this is by default */
253 	oraub8 chars_read = 0;
254 	int is_clob = 0;
255 	sb4 bytes_per_char = 1;
256 	sword errstatus;
257 
258 	*data_len = 0;
259 	*data = NULL;
260 
261 	ctx.lob_len = data_len;
262 	ctx.lob_data = data;
263 	ctx.alloc_len = 0;
264 
265 	if (php_oci_lob_get_length(descriptor, &length TSRMLS_CC)) {
266 		return 1;
267 	}
268 
269 	if (length <= 0) {
270 		return 0;
271 	}
272 
273 	if (initial_offset > length) {
274 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset must be less than size of the LOB");
275 		return 1;
276 	}
277 
278 	if (read_length == -1) {
279 		requested_len = length;
280 	}
281 
282 	if (requested_len > (length - initial_offset)) {
283 		requested_len = length - initial_offset;
284 	}
285 
286 	if (requested_len <= 0) {
287 		return 0;
288 	}
289 
290 	offset = initial_offset;
291 
292 	if (descriptor->type == OCI_DTYPE_FILE) {
293 		PHP_OCI_CALL_RETURN(errstatus, OCILobFileOpen, (connection->svc, connection->err, descriptor->descriptor, OCI_FILE_READONLY));
294 
295 		if (errstatus != OCI_SUCCESS) {
296 			connection->errcode = php_oci_error(connection->err, errstatus TSRMLS_CC);
297 			PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
298 			return 1;
299 		}
300 	} else {
301 		ub2 charset_id = 0;
302 
303 		PHP_OCI_CALL_RETURN(errstatus, OCILobCharSetId, (connection->env, connection->err, descriptor->descriptor, &charset_id));
304 
305 		if (errstatus != OCI_SUCCESS) {
306 			connection->errcode = php_oci_error(connection->err, errstatus TSRMLS_CC);
307 			PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
308 			return 1;
309 		}
310 
311 		if (charset_id > 0) { /* charset_id is always > 0 for [N]CLOBs */
312 			is_clob = 1;
313 		}
314 	}
315 
316 	if (is_clob) {
317 		PHP_OCI_CALL_RETURN(errstatus, OCINlsNumericInfoGet, (connection->env, connection->err, &bytes_per_char, OCI_NLS_CHARSET_MAXBYTESZ));
318 
319 		if (errstatus != OCI_SUCCESS) {
320 			connection->errcode = php_oci_error(connection->err, errstatus TSRMLS_CC);
321 			PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
322 			return 1;
323 		}
324 	} else {
325 		/* BLOBs don't have encoding, so bytes_per_char == 1 */
326 	}
327 
328 	ctx.alloc_len = (requested_len + 1) * bytes_per_char;
329 	*data = ecalloc(bytes_per_char, requested_len + 1);
330 
331 	if (is_clob) {
332 		chars_read = requested_len;
333 		bytes_read = 0;
334 	} else {
335 		chars_read = 0;
336 		bytes_read = requested_len;
337 	}
338 
339 	buffer_size = (requested_len < buffer_size ) ? requested_len : buffer_size;		/* optimize buffer size */
340 	buffer_size = php_oci_lob_calculate_buffer(descriptor, buffer_size TSRMLS_CC);	/* use chunk size */
341 
342 	bufp = (ub1 *) ecalloc(1, buffer_size);
343 	PHP_OCI_CALL_RETURN(errstatus, OCILobRead2,
344 		(
345 			connection->svc,
346 			connection->err,
347 			descriptor->descriptor,
348 			(oraub8 *)&bytes_read,							/* IN/OUT bytes toread/read */
349 			(oraub8 *)&chars_read,							/* IN/OUT chars toread/read */
350 			(oraub8) offset + 1,							/* offset (starts with 1) */
351 			(dvoid *) bufp,
352 			(oraub8) buffer_size,							/* size of buffer */
353 			OCI_FIRST_PIECE,
354 			(dvoid *)&ctx,
355 			(OCICallbackLobRead2) php_oci_lob_callback,				/* callback... */
356 			(ub2) descriptor->charset_id,			   /* The character set ID of the buffer data. */
357 			(ub1) descriptor->charset_form					  /* The character set form of the buffer data. */
358 		)
359 	);
360 
361 	efree(bufp);
362 
363 	if (is_clob) {
364 		offset = descriptor->lob_current_position + chars_read;
365 	} else {
366 		offset = descriptor->lob_current_position + bytes_read;
367 	}
368 
369 	if (errstatus != OCI_SUCCESS) {
370 		connection->errcode = php_oci_error(connection->err, errstatus TSRMLS_CC);
371 		PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
372 		if (*data) {
373 			efree(*data);
374 			*data = NULL;
375 		}
376 		*data_len = 0;
377 		return 1;
378 	}
379 
380 	descriptor->lob_current_position = (int)offset;
381 
382 	if (descriptor->type == OCI_DTYPE_FILE) {
383 		PHP_OCI_CALL_RETURN(errstatus, OCILobFileClose, (connection->svc, connection->err, descriptor->descriptor));
384 
385 		if (errstatus != OCI_SUCCESS) {
386 			connection->errcode = php_oci_error(connection->err, errstatus TSRMLS_CC);
387 			PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
388 			if (*data) {
389 				efree(*data);
390 				*data = NULL;
391 			}
392 			*data_len = 0;
393 			return 1;
394 		}
395 	}
396 
397 	connection->errcode = 0; /* retain backwards compat with OCI8 1.4 */
398 	return 0;
399 }
400 /* }}} */
401 
402 /* {{{ php_oci_lob_write()
403  Write data to the LOB */
php_oci_lob_write(php_oci_descriptor * descriptor,ub4 offset,char * data,int data_len,ub4 * bytes_written TSRMLS_DC)404 int php_oci_lob_write (php_oci_descriptor *descriptor, ub4 offset, char *data, int data_len, ub4 *bytes_written TSRMLS_DC)
405 {
406 	OCILobLocator *lob		   = (OCILobLocator *) descriptor->descriptor;
407 	php_oci_connection *connection = (php_oci_connection *) descriptor->connection;
408 	ub4 lob_length;
409 	sword errstatus;
410 
411 	*bytes_written = 0;
412 	if (php_oci_lob_get_length(descriptor, &lob_length TSRMLS_CC)) {
413 		return 1;
414 	}
415 
416 	if (!data || data_len <= 0) {
417 		return 0;
418 	}
419 
420 	if (offset > descriptor->lob_current_position) {
421 		offset = descriptor->lob_current_position;
422 	}
423 
424 	PHP_OCI_CALL_RETURN(errstatus, OCILobWrite,
425 			(
426 				connection->svc,
427 				connection->err,
428 				lob,
429 				(ub4 *)&data_len,
430 				(ub4) offset + 1,
431 				(dvoid *) data,
432 				(ub4) data_len,
433 				OCI_ONE_PIECE,
434 				(dvoid *)0,
435 				(OCICallbackLobWrite) 0,
436 				(ub2) descriptor->charset_id,
437 				(ub1) descriptor->charset_form
438 			)
439 		);
440 
441 	if (errstatus) {
442 		connection->errcode = php_oci_error(connection->err, errstatus TSRMLS_CC);
443 		PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
444 		*bytes_written = 0;
445 		return 1;
446 	}
447 	*bytes_written = data_len;
448 	descriptor->lob_current_position += data_len;
449 
450 	if (descriptor->lob_current_position > descriptor->lob_size) {
451 		descriptor->lob_size = descriptor->lob_current_position;
452 	}
453 
454 	/* marking buffer as used */
455 	if (descriptor->buffering == PHP_OCI_LOB_BUFFER_ENABLED) {
456 		descriptor->buffering = PHP_OCI_LOB_BUFFER_USED;
457 	}
458 
459 	connection->errcode = 0; /* retain backwards compat with OCI8 1.4 */
460 	return 0;
461 }
462 /* }}} */
463 
464 /* {{{ php_oci_lob_set_buffering()
465  Turn buffering off/onn for this particular LOB */
php_oci_lob_set_buffering(php_oci_descriptor * descriptor,int on_off TSRMLS_DC)466 int php_oci_lob_set_buffering (php_oci_descriptor *descriptor, int on_off TSRMLS_DC)
467 {
468 	php_oci_connection *connection = descriptor->connection;
469 	sword errstatus;
470 
471 	if (!on_off && descriptor->buffering == PHP_OCI_LOB_BUFFER_DISABLED) {
472 		/* disabling when it's already off */
473 		return 0;
474 	}
475 
476 	if (on_off && descriptor->buffering != PHP_OCI_LOB_BUFFER_DISABLED) {
477 		/* enabling when it's already on */
478 		return 0;
479 	}
480 
481 	if (on_off) {
482 		PHP_OCI_CALL_RETURN(errstatus, OCILobEnableBuffering, (connection->svc, connection->err, descriptor->descriptor));
483 	} else {
484 		PHP_OCI_CALL_RETURN(errstatus, OCILobDisableBuffering, (connection->svc, connection->err, descriptor->descriptor));
485 	}
486 
487 	if (errstatus != OCI_SUCCESS) {
488 		connection->errcode = php_oci_error(connection->err, errstatus TSRMLS_CC);
489 		PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
490 		return 1;
491 	}
492 	descriptor->buffering = on_off ? PHP_OCI_LOB_BUFFER_ENABLED : PHP_OCI_LOB_BUFFER_DISABLED;
493 	connection->errcode = 0; /* retain backwards compat with OCI8 1.4 */
494 	return 0;
495 }
496 /* }}} */
497 
498 /* {{{ php_oci_lob_get_buffering()
499  Return current buffering state for the LOB */
php_oci_lob_get_buffering(php_oci_descriptor * descriptor)500 int php_oci_lob_get_buffering (php_oci_descriptor *descriptor)
501 {
502 	if (descriptor->buffering != PHP_OCI_LOB_BUFFER_DISABLED) {
503 		return 1;
504 	} else {
505 		return 0;
506 	}
507 }
508 /* }}} */
509 
510 /* {{{ php_oci_lob_copy()
511  Copy one LOB (or its part) to another one */
php_oci_lob_copy(php_oci_descriptor * descriptor_dest,php_oci_descriptor * descriptor_from,long length TSRMLS_DC)512 int php_oci_lob_copy (php_oci_descriptor *descriptor_dest, php_oci_descriptor *descriptor_from, long length TSRMLS_DC)
513 {
514 	php_oci_connection *connection = descriptor_dest->connection;
515 	ub4 length_dest, length_from, copy_len;
516 	sword errstatus;
517 
518 	if (php_oci_lob_get_length(descriptor_dest, &length_dest TSRMLS_CC)) {
519 		return 1;
520 	}
521 
522 	if (php_oci_lob_get_length(descriptor_from, &length_from TSRMLS_CC)) {
523 		return 1;
524 	}
525 
526 	if (length == -1) {
527 		copy_len = length_from - descriptor_from->lob_current_position;
528 	} else {
529 		copy_len = length;
530 	}
531 
532 	if ((int)copy_len <= 0) {
533 		/* silently fail, there is nothing to copy */
534 		return 1;
535 	}
536 
537 	PHP_OCI_CALL_RETURN(errstatus, OCILobCopy,
538 			(
539 			 connection->svc,
540 			 connection->err,
541 			 descriptor_dest->descriptor,
542 			 descriptor_from->descriptor,
543 			 copy_len,
544 			 descriptor_dest->lob_current_position+1,
545 			 descriptor_from->lob_current_position+1
546 			)
547 	);
548 
549 	if (errstatus != OCI_SUCCESS) {
550 		connection->errcode = php_oci_error(connection->err, errstatus TSRMLS_CC);
551 		PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
552 		return 1;
553 	}
554 
555 	connection->errcode = 0; /* retain backwards compat with OCI8 1.4 */
556 	return 0;
557 }
558 /* }}} */
559 
560 /* {{{ php_oci_lob_close()
561  Close LOB */
php_oci_lob_close(php_oci_descriptor * descriptor TSRMLS_DC)562 int php_oci_lob_close (php_oci_descriptor *descriptor TSRMLS_DC)
563 {
564 	php_oci_connection *connection = descriptor->connection;
565 	sword errstatus;
566 
567 	if (descriptor->is_open) {
568 		PHP_OCI_CALL_RETURN(errstatus, OCILobClose, (connection->svc, connection->err, descriptor->descriptor));
569 
570 		if (errstatus != OCI_SUCCESS) {
571 			connection->errcode = php_oci_error(connection->err, errstatus TSRMLS_CC);
572 			PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
573 			return 1;
574 		}
575 		connection->errcode = 0; /* retain backwards compat with OCI8 1.4 */
576 	}
577 
578 	if (php_oci_temp_lob_close(descriptor TSRMLS_CC)) {
579 		return 1;
580 	}
581 
582 	return 0;
583 }
584 /* }}} */
585 
586 /* {{{ php_oci_temp_lob_close()
587    Close Temporary LOB */
php_oci_temp_lob_close(php_oci_descriptor * descriptor TSRMLS_DC)588 int php_oci_temp_lob_close (php_oci_descriptor *descriptor TSRMLS_DC)
589 {
590 	php_oci_connection *connection = descriptor->connection;
591 	int is_temporary;
592 	sword errstatus;
593 
594 	PHP_OCI_CALL_RETURN(errstatus, OCILobIsTemporary, (connection->env,connection->err, descriptor->descriptor, &is_temporary));
595 
596 	if (errstatus != OCI_SUCCESS) {
597 		connection->errcode = php_oci_error(connection->err, errstatus TSRMLS_CC);
598 		PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
599 		return 1;
600 	}
601 
602 	if (is_temporary) {
603 		PHP_OCI_CALL_RETURN(errstatus, OCILobFreeTemporary, (connection->svc, connection->err, descriptor->descriptor));
604 
605 		if (errstatus != OCI_SUCCESS) {
606 			connection->errcode = php_oci_error(connection->err, errstatus TSRMLS_CC);
607 			PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
608 			return 1;
609 		}
610 	}
611 	connection->errcode = 0; /* retain backwards compat with OCI8 1.4 */
612 	return 0;
613 }
614 /* }}} */
615 
616 /* {{{ php_oci_lob_flush()
617  Flush buffers for the LOB (only if they have been used) */
php_oci_lob_flush(php_oci_descriptor * descriptor,long flush_flag TSRMLS_DC)618 int php_oci_lob_flush(php_oci_descriptor *descriptor, long flush_flag TSRMLS_DC)
619 {
620 	OCILobLocator *lob = descriptor->descriptor;
621 	php_oci_connection *connection = descriptor->connection;
622 	sword errstatus;
623 
624 	if (!lob) {
625 		return 1;
626 	}
627 
628 	switch (flush_flag) {
629 		case 0:
630 		case OCI_LOB_BUFFER_FREE:
631 			/* only these two are allowed */
632 			break;
633 		default:
634 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid flag value: %ld", flush_flag);
635 			return 1;
636 			break;
637 	}
638 
639 	/* do not really flush buffer, but report success
640 	 * to suppress OCI error when flushing not used buffer
641 	 * */
642 	if (descriptor->buffering != PHP_OCI_LOB_BUFFER_USED) {
643 		return 0;
644 	}
645 
646 	PHP_OCI_CALL_RETURN(errstatus, OCILobFlushBuffer, (connection->svc, connection->err, lob, flush_flag));
647 
648 	if (errstatus != OCI_SUCCESS) {
649 		connection->errcode = php_oci_error(connection->err, errstatus TSRMLS_CC);
650 		PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
651 		return 1;
652 	}
653 
654 	/* marking buffer as enabled and not used */
655 	descriptor->buffering = PHP_OCI_LOB_BUFFER_ENABLED;
656 	connection->errcode = 0; /* retain backwards compat with OCI8 1.4 */
657 	return 0;
658 }
659 /* }}} */
660 
661 /* {{{ php_oci_lob_free()
662  Close LOB descriptor and free associated resources */
php_oci_lob_free(php_oci_descriptor * descriptor TSRMLS_DC)663 void php_oci_lob_free (php_oci_descriptor *descriptor TSRMLS_DC)
664 {
665 	if (!descriptor || !descriptor->connection) {
666 		return;
667 	}
668 
669 	if (descriptor->connection->descriptors) {
670 		/* delete descriptor from the hash */
671 		zend_hash_index_del(descriptor->connection->descriptors, descriptor->index);
672 		if (zend_hash_num_elements(descriptor->connection->descriptors) == 0) {
673 			descriptor->connection->descriptor_count = 0;
674 		} else {
675 			if (descriptor->index + 1 == descriptor->connection->descriptor_count) {
676 				/* If the descriptor being freed is the end-most one
677 				 * allocated, then the descriptor_count is reduced so
678 				 * a future descriptor can reuse the hash table index.
679 				 * This can prevent the hash index range increasing in
680 				 * the common case that each descriptor is
681 				 * allocated/used/freed before another descriptor is
682 				 * needed.  However it is possible that a script frees
683 				 * descriptors in arbitrary order which would prevent
684 				 * descriptor_count ever being reduced to zero until
685 				 * zend_hash_num_elements() returns 0.
686 				 */
687 				descriptor->connection->descriptor_count--;
688 			}
689 		}
690 	}
691 
692 	/* flushing Lobs & Files with buffering enabled */
693 	if ((descriptor->type == OCI_DTYPE_FILE || descriptor->type == OCI_DTYPE_LOB) && descriptor->buffering == PHP_OCI_LOB_BUFFER_USED) {
694 		php_oci_lob_flush(descriptor, OCI_LOB_BUFFER_FREE TSRMLS_CC);
695 	}
696 
697 	if (descriptor->type == OCI_DTYPE_LOB) {
698 		php_oci_temp_lob_close(descriptor TSRMLS_CC);
699 	}
700 
701 	PHP_OCI_CALL(OCIDescriptorFree, (descriptor->descriptor, descriptor->type));
702 
703 	zend_list_delete(descriptor->connection->id);
704 	efree(descriptor);
705 }
706 /* }}} */
707 
708 /* {{{ php_oci_lob_import()
709  Import LOB contents from the given file */
php_oci_lob_import(php_oci_descriptor * descriptor,char * filename TSRMLS_DC)710 int php_oci_lob_import (php_oci_descriptor *descriptor, char *filename TSRMLS_DC)
711 {
712 	int fp;
713 	ub4 loblen;
714 	OCILobLocator *lob = (OCILobLocator *)descriptor->descriptor;
715 	php_oci_connection *connection = descriptor->connection;
716 	char buf[8192];
717 	ub4 offset = 1;
718 	sword errstatus;
719 
720 #if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 3) || (PHP_MAJOR_VERSION > 5)
721 	/* Safe mode has been removed in PHP 5.4 */
722 	if (php_check_open_basedir(filename TSRMLS_CC)) {
723 #else
724 	if ((PG(safe_mode) && (!php_checkuid(filename, NULL, CHECKUID_CHECK_FILE_AND_DIR))) || php_check_open_basedir(filename TSRMLS_CC)) {
725 #endif
726 		return 1;
727 	}
728 
729 	if ((fp = VCWD_OPEN(filename, O_RDONLY|O_BINARY)) == -1) {
730 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can't open file %s", filename);
731 		return 1;
732 	}
733 
734 	while ((loblen = read(fp, &buf, sizeof(buf))) > 0) {
735 		PHP_OCI_CALL_RETURN(errstatus,
736 				OCILobWrite,
737 				(
738 					connection->svc,
739 					connection->err,
740 					lob,
741 					&loblen,
742 					offset,
743 					(dvoid *) &buf,
744 					loblen,
745 					OCI_ONE_PIECE,
746 					(dvoid *)0,
747 					(OCICallbackLobWrite) 0,
748 					(ub2) descriptor->charset_id,
749 					(ub1) descriptor->charset_form
750 				)
751 		);
752 
753 		if (errstatus != OCI_SUCCESS) {
754 			connection->errcode = php_oci_error(connection->err, errstatus TSRMLS_CC);
755 			PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
756 			close(fp);
757 			return 1;
758 		} else {
759 			connection->errcode = 0; /* retain backwards compat with OCI8 1.4 */
760 		}
761 		offset += loblen;
762 	}
763 	close(fp);
764 
765 	return 0;
766 }
767  	/* }}} */
768 
769 /* {{{ php_oci_lob_append()
770  Append data to the end of the LOB */
771 int php_oci_lob_append (php_oci_descriptor *descriptor_dest, php_oci_descriptor *descriptor_from TSRMLS_DC)
772 {
773 	php_oci_connection *connection = descriptor_dest->connection;
774 	OCILobLocator *lob_dest = descriptor_dest->descriptor;
775 	OCILobLocator *lob_from = descriptor_from->descriptor;
776 	ub4 dest_len, from_len;
777 	sword errstatus;
778 
779 	if (php_oci_lob_get_length(descriptor_dest, &dest_len TSRMLS_CC)) {
780 		return 1;
781 	}
782 
783 	if (php_oci_lob_get_length(descriptor_from, &from_len TSRMLS_CC)) {
784 		return 1;
785 	}
786 
787 	if (from_len <= 0) {
788 		return 0;
789 	}
790 
791 	PHP_OCI_CALL_RETURN(errstatus, OCILobAppend, (connection->svc, connection->err, lob_dest, lob_from));
792 
793 	if (errstatus != OCI_SUCCESS) {
794 		connection->errcode = php_oci_error(connection->err, errstatus TSRMLS_CC);
795 		PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
796 		return 1;
797 	}
798 	connection->errcode = 0; /* retain backwards compat with OCI8 1.4 */
799 	return 0;
800 }
801 /* }}} */
802 
803 /* {{{ php_oci_lob_truncate()
804  Truncate LOB to the given length */
805 int php_oci_lob_truncate (php_oci_descriptor *descriptor, long new_lob_length TSRMLS_DC)
806 {
807 	php_oci_connection *connection = descriptor->connection;
808 	OCILobLocator *lob = descriptor->descriptor;
809 	ub4 lob_length;
810 	sword errstatus;
811 
812 	if (php_oci_lob_get_length(descriptor, &lob_length TSRMLS_CC)) {
813 		return 1;
814 	}
815 
816 	if (lob_length <= 0) {
817 		return 0;
818 	}
819 
820 	if (new_lob_length < 0) {
821 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Size must be greater than or equal to 0");
822 		return 1;
823 	}
824 
825 	if (new_lob_length > lob_length) {
826 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Size must be less than or equal to the current LOB size");
827 		return 1;
828 	}
829 
830 	PHP_OCI_CALL_RETURN(errstatus, OCILobTrim, (connection->svc, connection->err, lob, new_lob_length));
831 
832 	if (errstatus != OCI_SUCCESS) {
833 		connection->errcode = php_oci_error(connection->err, errstatus TSRMLS_CC);
834 		PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
835 		return 1;
836 	}
837 
838 	descriptor->lob_size = new_lob_length;
839 	connection->errcode = 0; /* retain backwards compat with OCI8 1.4 */
840 
841 	return 0;
842 }
843 /* }}} */
844 
845 /* {{{ php_oci_lob_erase()
846  Erase (or fill with whitespaces, depending on LOB type) the LOB (or its part) */
847 int php_oci_lob_erase (php_oci_descriptor *descriptor, long offset, ub4 length, ub4 *bytes_erased TSRMLS_DC)
848 {
849 	php_oci_connection *connection = descriptor->connection;
850 	OCILobLocator *lob = descriptor->descriptor;
851 	ub4 lob_length;
852 	sword errstatus;
853 
854 	*bytes_erased = 0;
855 
856 	if (php_oci_lob_get_length(descriptor, &lob_length TSRMLS_CC)) {
857 		return 1;
858 	}
859 
860 	if (offset == -1) {
861 		offset = descriptor->lob_current_position;
862 	}
863 
864 	if (length == -1) {
865 		length = lob_length;
866 	}
867 
868 	PHP_OCI_CALL_RETURN(errstatus, OCILobErase, (connection->svc, connection->err, lob, (ub4 *)&length, offset+1));
869 
870 	if (errstatus != OCI_SUCCESS) {
871 		connection->errcode = php_oci_error(connection->err, errstatus TSRMLS_CC);
872 		PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
873 		return 1;
874 	}
875 
876 	*bytes_erased = length;
877 	connection->errcode = 0; /* retain backwards compat with OCI8 1.4 */
878 	return 0;
879 }
880 /* }}} */
881 
882 /* {{{ php_oci_lob_is_equal()
883  Compare two LOB descriptors and figure out if they are pointing to the same LOB */
884 int php_oci_lob_is_equal (php_oci_descriptor *descriptor_first, php_oci_descriptor *descriptor_second, boolean *result TSRMLS_DC)
885 {
886 	php_oci_connection *connection = descriptor_first->connection;
887 	OCILobLocator *first_lob   = descriptor_first->descriptor;
888 	OCILobLocator *second_lob  = descriptor_second->descriptor;
889 	sword errstatus;
890 
891 	PHP_OCI_CALL_RETURN(errstatus, OCILobIsEqual, (connection->env, first_lob, second_lob, result));
892 
893 	if (errstatus) {
894 		connection->errcode = php_oci_error(connection->err, errstatus TSRMLS_CC);
895 		PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
896 		return 1;
897 	}
898 	connection->errcode = 0; /* retain backwards compat with OCI8 1.4 */
899 	return 0;
900 }
901 /* }}} */
902 
903 /* {{{ php_oci_lob_write_tmp()
904  Create temporary LOB and write data to it */
905 int php_oci_lob_write_tmp (php_oci_descriptor *descriptor, long type, char *data, int data_len TSRMLS_DC)
906 {
907 	php_oci_connection *connection = descriptor->connection;
908 	OCILobLocator *lob		   = descriptor->descriptor;
909 	ub4 bytes_written = 0;
910 	sword errstatus;
911 
912 	switch (type) {
913 		case OCI_TEMP_BLOB:
914 		case OCI_TEMP_CLOB:
915 			/* only these two are allowed */
916 			break;
917 		default:
918 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid temporary lob type: %ld", type);
919 			return 1;
920 			break;
921 	}
922 
923 	if (data_len < 0) {
924 		return 1;
925 	}
926 
927 	PHP_OCI_CALL_RETURN(errstatus, OCILobCreateTemporary,
928 			(
929 			 connection->svc,
930 			 connection->err,
931 			 lob,
932 			 OCI_DEFAULT,
933 			 OCI_DEFAULT,
934 			 (ub1)type,
935 			 OCI_ATTR_NOCACHE,
936 			 OCI_DURATION_SESSION
937 			)
938 	);
939 
940 	if (errstatus) {
941 		connection->errcode = php_oci_error(connection->err, errstatus TSRMLS_CC);
942 		PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
943 		return 1;
944 	}
945 
946 	PHP_OCI_CALL_RETURN(errstatus, OCILobOpen, (connection->svc, connection->err, lob, OCI_LOB_READWRITE));
947 
948 	if (errstatus) {
949 		connection->errcode = php_oci_error(connection->err, errstatus TSRMLS_CC);
950 		PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
951 		return 1;
952 	}
953 
954 	descriptor->is_open = 1;
955 	connection->errcode = 0; /* retain backwards compat with OCI8 1.4 */
956 
957 	return php_oci_lob_write(descriptor, 0, data, data_len, &bytes_written TSRMLS_CC);
958 }
959 /* }}} */
960 
961 #endif /* HAVE_OCI8 */
962 
963 /*
964  * Local variables:
965  * tab-width: 4
966  * c-basic-offset: 4
967  * End:
968  * vim600: noet sw=4 ts=4 fdm=marker
969  * vim<600: noet sw=4 ts=4
970  */
971