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