]> granicus.if.org Git - php/commitdiff
MFH:
authorAntony Dovgal <tony2001@php.net>
Wed, 7 Jun 2006 13:36:51 +0000 (13:36 +0000)
committerAntony Dovgal <tony2001@php.net>
Wed, 7 Jun 2006 13:36:51 +0000 (13:36 +0000)
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.

ext/oci8/oci8_lob.c
ext/oci8/php_oci8_int.h

index 05763ea837cd07c1ec055ca1e234b0ecdb6c6155..169489eadf35a60d917d6eb81097948bd76e41e0 100644 (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;
+       int is_clob = 0;
 #else
-       int bytes_read, bytes_total = 0, offset = 0;
+       int bytes_read, offset = 0;
        int requested_len = read_length; /* this is by default */
-       int chars_read = 0;
 #endif
-       int is_clob = 0;
 
        *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_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);
+               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 */
+
+       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_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);
+       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 */
+
+       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;
 } /* }}} */
 
index 94da6eed2acd2f26f068cfac89785b222a1da857..638cd2391a55a45e22c8de737c0a67e4d414da20 100644 (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
 
 /* }}} */