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