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