fix Unicode LOB problems using callbacks to read LOBs
using chunk_size*X buffer should speed up LOB reading a bit

many thanks to Massimo Squillace <msquillace at sogei dot it> for the patch.
This commit is contained in:
Antony Dovgal 2006-06-07 13:36:51 +00:00
parent e0844e09bd
commit 9123bee257
2 changed files with 147 additions and 80 deletions

View File

@ -144,25 +144,92 @@ int php_oci_lob_get_length (php_oci_descriptor *descriptor, ub4 *length TSRMLS_D
return 0;
} /* }}} */
/* {{{ php_oci_lob_callback()
Append LOB portion to a memory buffer */
#if defined(HAVE_OCI_LOB_READ2)
sb4 php_oci_lob_callback (dvoid *ctxp, CONST dvoid *bufxp, oraub8 len, ub1 piece, dvoid **changed_bufpp, oraub8 *changed_lenp)
#else
sb4 php_oci_lob_callback (dvoid *ctxp, CONST dvoid *bufxp, ub4 len, ub1 piece)
#endif
{
ub4 lenp = (ub4) len;
php_oci_lob_ctx *ctx = (php_oci_lob_ctx *)ctxp;
switch (piece)
{
case OCI_LAST_PIECE:
*(ctx->lob_data) = erealloc(*(ctx->lob_data), (size_t) (*(ctx->lob_len) + lenp + 1));
memcpy(*(ctx->lob_data) + *(ctx->lob_len), bufxp, (size_t) lenp);
*(ctx->lob_len) += lenp;
*(*(ctx->lob_data) + *(ctx->lob_len)) = 0x00;
return OCI_CONTINUE;
case OCI_FIRST_PIECE:
case OCI_NEXT_PIECE:
*(ctx->lob_data) = erealloc(*(ctx->lob_data), (size_t) (*(ctx->lob_len) + lenp));
memcpy(*(ctx->lob_data) + *(ctx->lob_len), bufxp, (size_t) lenp);
*(ctx->lob_len) += lenp;
return OCI_CONTINUE;
default: {
TSRMLS_FETCH();
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unexpected LOB piece id received (value:%d)", piece);
efree(*(ctx->lob_data));
*(ctx->lob_data) = NULL;
*(ctx->lob_len) = 0;
return OCI_ERROR;
}
}
}
/* }}} */
/* {{{ php_oci_lob_calculate_buffer() */
static inline int php_oci_lob_calculate_buffer(php_oci_descriptor *descriptor, long read_length TSRMLS_DC)
{
php_oci_connection *connection = descriptor->connection;
ub4 chunk_size;
if (!descriptor->chunk_size) {
connection->errcode = PHP_OCI_CALL(OCILobGetChunkSize, (connection->svc, connection->err, descriptor->descriptor, &chunk_size));
if (connection->errcode != OCI_SUCCESS) {
php_oci_error(connection->err, connection->errcode TSRMLS_CC);
PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
return read_length; /* we have to return original length here */
}
descriptor->chunk_size = chunk_size;
}
if ((read_length % descriptor->chunk_size) != 0) {
return descriptor->chunk_size * ((read_length / descriptor->chunk_size) + 1);
}
return read_length;
}
/* }}} */
/* {{{ php_oci_lob_read()
Read specified portion of the LOB into the buffer */
int php_oci_lob_read (php_oci_descriptor *descriptor, long read_length, long initial_offset, char **data, ub4 *data_len TSRMLS_DC)
{
php_oci_connection *connection = descriptor->connection;
ub4 length = 0;
int buffer_size = PHP_OCI_LOB_BUFFER_SIZE;
php_oci_lob_ctx ctx;
ub1 *bufp;
#if defined(HAVE_OCI_LOB_READ2)
oraub8 bytes_read, bytes_total = 0, offset = 0;
oraub8 bytes_read, offset = 0;
oraub8 requested_len = read_length; /* this is by default */
oraub8 chars_read = 0;
#else
int bytes_read, bytes_total = 0, offset = 0;
int requested_len = read_length; /* this is by default */
int chars_read = 0;
#endif
int is_clob = 0;
#else
int bytes_read, offset = 0;
int requested_len = read_length; /* this is by default */
#endif
*data_len = 0;
*data = NULL;
ctx.lob_len = data_len;
ctx.lob_data = data;
if (php_oci_lob_get_length(descriptor, &length TSRMLS_CC)) {
return 1;
@ -188,6 +255,8 @@ int php_oci_lob_read (php_oci_descriptor *descriptor, long read_length, long ini
if (requested_len <= 0) {
return 0;
}
offset = initial_offset;
if (descriptor->type == OCI_DTYPE_FILE) {
connection->errcode = PHP_OCI_CALL(OCILobFileOpen, (connection->svc, connection->err, descriptor->descriptor, OCI_FILE_READONLY));
@ -214,82 +283,70 @@ int php_oci_lob_read (php_oci_descriptor *descriptor, long read_length, long ini
is_clob = 1;
}
}
#endif
*data = (char *)emalloc(requested_len + 1);
bytes_read = requested_len;
offset = initial_offset;
/* TODO
* We need to make sure this function works with Unicode LOBs
* */
#if defined(HAVE_OCI_LOB_READ2)
do {
if (is_clob) {
chars_read = requested_len;
bytes_read = 0;
} else {
chars_read = 0;
connection->errcode = PHP_OCI_CALL(OCILobRead2,
(
connection->svc,
connection->err,
descriptor->descriptor,
(oraub8 *)&bytes_read, /* IN/OUT bytes toread/read */
(oraub8 *)&chars_read,
(oraub8) offset + 1, /* offset (starts with 1) */
(dvoid *) ((char *) *data + *data_len),
(oraub8) requested_len, /* size of buffer */
0,
NULL,
(OCICallbackLobRead2) 0, /* callback... */
(ub2) connection->charset, /* The character set ID of the buffer data. */
(ub1) SQLCS_IMPLICIT /* The character set form of the buffer data. */
)
);
bytes_read = requested_len;
}
bytes_total += bytes_read;
if (is_clob) {
offset += chars_read;
} else {
offset += bytes_read;
}
*data_len += bytes_read;
if (connection->errcode != OCI_NEED_DATA) {
break;
}
*data = erealloc(*data, *data_len + PHP_OCI_LOB_BUFFER_SIZE + 1);
} while (connection->errcode == OCI_NEED_DATA);
buffer_size = (requested_len < buffer_size ) ? requested_len : buffer_size; /* optimize buffer size */
buffer_size = php_oci_lob_calculate_buffer(descriptor, buffer_size TSRMLS_CC); /* use chunk size */
bufp = (ub1 *) ecalloc(1, buffer_size);
connection->errcode = PHP_OCI_CALL(OCILobRead2,
(
connection->svc,
connection->err,
descriptor->descriptor,
(oraub8 *)&bytes_read, /* IN/OUT bytes toread/read */
(oraub8 *)&chars_read, /* IN/OUT chars toread/read */
(oraub8) offset + 1, /* offset (starts with 1) */
(dvoid *) bufp,
(oraub8) buffer_size, /* size of buffer */
OCI_FIRST_PIECE,
(dvoid *)&ctx,
(OCICallbackLobRead2) php_oci_lob_callback, /* callback... */
(ub2) connection->charset, /* The character set ID of the buffer data. */
(ub1) SQLCS_IMPLICIT /* The character set form of the buffer data. */
)
);
efree(bufp);
if (is_clob) {
offset = descriptor->lob_current_position + chars_read;
} else {
offset = descriptor->lob_current_position + bytes_read;
}
#else
do {
connection->errcode = PHP_OCI_CALL(OCILobRead,
(
connection->svc,
connection->err,
descriptor->descriptor,
&bytes_read, /* IN/OUT bytes toread/read */
offset + 1, /* offset (starts with 1) */
(dvoid *) ((char *) *data + *data_len),
requested_len, /* size of buffer */
(dvoid *)0,
(OCICallbackLobRead) 0, /* callback... */
(ub2) connection->charset, /* The character set ID of the buffer data. */
(ub1) SQLCS_IMPLICIT /* The character set form of the buffer data. */
)
);
bytes_read = requested_len;
buffer_size = (requested_len < buffer_size ) ? requested_len : buffer_size; /* optimize buffer size */
buffer_size = php_oci_lob_calculate_buffer(descriptor, buffer_size TSRMLS_CC); /* use chunk size */
bytes_total += bytes_read;
offset += bytes_read;
*data_len += bytes_read;
if (connection->errcode != OCI_NEED_DATA) {
break;
}
*data = erealloc(*data, *data_len + PHP_OCI_LOB_BUFFER_SIZE + 1);
} while (connection->errcode == OCI_NEED_DATA);
bufp = (ub1 *) ecalloc(1, buffer_size);
connection->errcode = PHP_OCI_CALL(OCILobRead,
(
connection->svc,
connection->err,
descriptor->descriptor,
&bytes_read, /* IN/OUT bytes toread/read */
offset + 1, /* offset (starts with 1) */
(dvoid *) bufp,
(ub4) buffer_size, /* size of buffer */
(dvoid *)&ctx,
(OCICallbackLobRead) php_oci_lob_callback, /* callback... */
(ub2) connection->charset, /* The character set ID of the buffer data. */
(ub1) SQLCS_IMPLICIT /* The character set form of the buffer data. */
)
);
efree(bufp);
offset = descriptor->lob_current_position + bytes_read;
#endif
@ -298,10 +355,11 @@ int php_oci_lob_read (php_oci_descriptor *descriptor, long read_length, long ini
PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
efree(*data);
*data = NULL;
*data_len = 0;
return 1;
}
descriptor->lob_current_position = offset;
descriptor->lob_current_position = (int)offset;
if (descriptor->type == OCI_DTYPE_FILE) {
connection->errcode = PHP_OCI_CALL(OCILobFileClose, (connection->svc, connection->err, descriptor->descriptor));
@ -311,13 +369,11 @@ int php_oci_lob_read (php_oci_descriptor *descriptor, long read_length, long ini
PHP_OCI_HANDLE_ERROR(connection, connection->errcode);
efree(*data);
*data = NULL;
*data_len = 0;
return 1;
}
}
*data = erealloc(*data, *data_len + 1);
(*data)[ *data_len ] = 0;
return 0;
} /* }}} */

View File

@ -78,7 +78,7 @@ extern zend_class_entry *oci_coll_class_entry_ptr;
#define PHP_OCI_MAX_NAME_LEN 64
#define PHP_OCI_MAX_DATA_SIZE INT_MAX
#define PHP_OCI_PIECE_SIZE (64*1024)-1
#define PHP_OCI_LOB_BUFFER_SIZE 32768
#define PHP_OCI_LOB_BUFFER_SIZE 1048576l /* 1Mb seems to be the most reasonable buffer size for LOB reading */
#define PHP_OCI_ASSOC 1<<0
#define PHP_OCI_NUM 1<<1
@ -126,8 +126,14 @@ typedef struct { /* php_oci_descriptor {{{ */
int lob_current_position; /* LOB internal pointer */
int lob_size; /* cached LOB size. -1 = Lob wasn't initialized yet */
int buffering; /* cached buffering flag. 0 - off, 1 - on, 2 - on and buffer was used */
ub4 chunk_size; /* chunk size of the LOB. 0 - unknown */
} php_oci_descriptor; /* }}} */
typedef struct { /* php_oci_lob_ctx {{{ */
char **lob_data; /* address of pointer to LOB data */
ub4 *lob_len; /* address of LOB length variable (bytes) */
} php_oci_lob_ctx; /* }}} */
typedef struct { /* php_oci_collection {{{ */
int id;
php_oci_connection *connection; /* parent connection handle */
@ -318,6 +324,11 @@ int php_oci_lob_append (php_oci_descriptor *, php_oci_descriptor * TSRMLS_DC);
int php_oci_lob_truncate (php_oci_descriptor *, long TSRMLS_DC);
int php_oci_lob_erase (php_oci_descriptor *, long, long, ub4 * TSRMLS_DC);
int php_oci_lob_is_equal (php_oci_descriptor *, php_oci_descriptor *, boolean * TSRMLS_DC);
#if defined(HAVE_OCI_LOB_READ2)
sb4 php_oci_lob_callback (dvoid *ctxp, CONST dvoid *bufxp, oraub8 len, ub1 piece, dvoid **changed_bufpp, oraub8 *changed_lenp);
#else
sb4 php_oci_lob_callback (dvoid *ctxp, CONST dvoid *bufxp, ub4 len, ub1 piece);
#endif
/* }}} */