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