From 9cd7f41fac87a83766d54d73dd08ccb5ee11176e Mon Sep 17 00:00:00 2001 From: Christopher Jones Date: Fri, 12 Nov 2021 17:59:22 +1100 Subject: [PATCH] Add oci8.prefetch_lob_size --- NEWS | 3 + UPGRADING | 5 + ext/oci8/oci8.c | 1 + ext/oci8/oci8_statement.c | 22 ++--- ext/oci8/package.xml | 25 ++++- ext/oci8/php_oci8.h | 2 +- ext/oci8/php_oci8_int.h | 2 +- ext/oci8/tests/driver_name.phpt | 6 +- ext/oci8/tests/lob_prefetch.phpt | 163 +++++++++++++++++++++++++++++++ php.ini-development | 11 ++- php.ini-production | 9 +- 11 files changed, 226 insertions(+), 23 deletions(-) create mode 100755 ext/oci8/tests/lob_prefetch.phpt diff --git a/NEWS b/NEWS index d0d6ac06a9b..341b29b8088 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,9 @@ PHP NEWS - Core: . Fixed bug #81380 (Observer may not be initialized properly). (krakjoe) +- OCI8: + . Added oci8.prefetch_lob_size directive to tune LOB query performance + - Zip: . add ZipArchive::clearError() method . add ZipArchive::getStreamName() method diff --git a/UPGRADING b/UPGRADING index c19d4f74324..e3c3eed5976 100644 --- a/UPGRADING +++ b/UPGRADING @@ -27,6 +27,11 @@ PHP 8.2 UPGRADE NOTES . Added CURLINFO_EFFECTIVE_METHOD option and returning the effective HTTP method in curl_getinfo() return value. +- OCI8: + . Added an oci8.prefetch_lob_size directive to tune LOB query performance + by reducing the number of round-trips between PHP and Oracle Database when + fetching LOBS. This is usable with Oracle Database 12.2 or later. + - PCRE: . Added support for the "n" (NO_AUTO_CAPTURE) modifier, which makes simple `(xyz)` groups non-capturing. Only named groups like `(?xyz)` are diff --git a/ext/oci8/oci8.c b/ext/oci8/oci8.c index 8c71d574621..8efb04ea0f6 100644 --- a/ext/oci8/oci8.c +++ b/ext/oci8/oci8.c @@ -174,6 +174,7 @@ PHP_INI_BEGIN() #if ((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2))) STD_PHP_INI_BOOLEAN("oci8.events", "0", PHP_INI_SYSTEM, OnUpdateBool, events, zend_oci_globals, oci_globals) #endif + STD_PHP_INI_ENTRY( "oci8.prefetch_lob_size", "0", PHP_INI_ALL, OnUpdateLong, prefetch_lob_size, zend_oci_globals, oci_globals) PHP_INI_END() /* }}} */ diff --git a/ext/oci8/oci8_statement.c b/ext/oci8/oci8_statement.c index e76f8a54002..5ee04a6168e 100644 --- a/ext/oci8/oci8_statement.c +++ b/ext/oci8/oci8_statement.c @@ -359,7 +359,7 @@ int php_oci_statement_fetch(php_oci_statement *statement, ub4 nrows) if (errstatus == OCI_SUCCESS_WITH_INFO || errstatus == OCI_SUCCESS) { statement->has_data = 1; - /* do the stuff needed for OCIDefineByName */ + /* do the stuff needed for OCIDefineByPos */ for (i = 0; i < statement->ncolumns; i++) { column = php_oci_statement_get_column(statement, i + 1, NULL, 0); if (column == NULL) { @@ -794,7 +794,6 @@ int php_oci_statement_execute(php_oci_statement *statement, ub4 mode) OCI_DYNAMIC_FETCH /* IN mode (OCI_DEFAULT, OCI_DYNAMIC_FETCH) */ ) ); - } else { PHP_OCI_CALL_RETURN(errstatus, OCIDefineByPos, @@ -821,12 +820,19 @@ int php_oci_statement_execute(php_oci_statement *statement, ub4 mode) return 1; } - /* additional OCIDefineDynamic() call */ + /* additional define setup */ switch (outcol->data_type) { - case SQLT_RSET: - case SQLT_RDD: case SQLT_BLOB: case SQLT_CLOB: + if (OCI_G(prefetch_lob_size) > 0) { + int get_lob_len = 1; /* == true */ + ub4 prefetch_size = OCI_G(prefetch_lob_size); + PHP_OCI_CALL_RETURN(errstatus, OCIAttrSet, (outcol->oci_define, OCI_HTYPE_DEFINE, &get_lob_len, 0, OCI_ATTR_LOBPREFETCH_LENGTH, statement->err)); + PHP_OCI_CALL_RETURN(errstatus, OCIAttrSet, (outcol->oci_define, OCI_HTYPE_DEFINE, &prefetch_size, 0, OCI_ATTR_LOBPREFETCH_SIZE, statement->err)); + } + ZEND_FALLTHROUGH; + case SQLT_RSET: + case SQLT_RDD: case SQLT_BFILE: PHP_OCI_CALL_RETURN(errstatus, OCIDefineDynamic, @@ -1483,12 +1489,6 @@ sb4 php_oci_bind_out_callback( ZVAL_STRINGL(val, p, PHP_OCI_PIECE_SIZE); efree(p); } -#if 0 - Z_STRLEN_P(val) = PHP_OCI_PIECE_SIZE; /* 64K-1 is max XXX */ - Z_STRVAL_P(val) = ecalloc(1, Z_STRLEN_P(val) + 1); - /* XXX is this right? */ - ZVAL_STRINGL(val, NULL, Z_STRLEN(val) + 1); -#endif /* XXX we assume that zend-zval len has 4 bytes */ *alenpp = (ub4*) &Z_STRLEN_P(val); diff --git a/ext/oci8/package.xml b/ext/oci8/package.xml index 9134a2a655b..25de0a9867d 100644 --- a/ext/oci8/package.xml +++ b/ext/oci8/package.xml @@ -53,12 +53,12 @@ Oracle's standard cross-version connectivity applies. For example, PHP OCI8 lin no - 2021-08-02 + 2021-11-15 - 3.1.0 - 3.1.0 + 3.2.0 + 3.2.0 stable @@ -68,7 +68,7 @@ Oracle's standard cross-version connectivity applies. For example, PHP OCI8 lin This version is for PHP 8 only. - Deprecated directive oci8.old_oci_close_semantics + Added oci8.prefetch_lob_size directive to improve LOB query performance. @@ -448,6 +448,23 @@ Oracle's standard cross-version connectivity applies. For example, PHP OCI8 lin + + + 3.1.0 + 3.1.0 + + + stable + stable + + PHP + + This version is for PHP 8 only. + + Deprecated directive oci8.old_oci_close_semantics + + + 3.0.1 diff --git a/ext/oci8/php_oci8.h b/ext/oci8/php_oci8.h index 0d0da8d0829..e4476004416 100644 --- a/ext/oci8/php_oci8.h +++ b/ext/oci8/php_oci8.h @@ -41,7 +41,7 @@ */ #undef PHP_OCI8_VERSION #endif -#define PHP_OCI8_VERSION "3.1.0" +#define PHP_OCI8_VERSION "3.2.0" extern zend_module_entry oci8_module_entry; #define phpext_oci8_ptr &oci8_module_entry diff --git a/ext/oci8/php_oci8_int.h b/ext/oci8/php_oci8_int.h index 08bec33193a..b2e5f88c26b 100644 --- a/ext/oci8/php_oci8_int.h +++ b/ext/oci8/php_oci8_int.h @@ -519,9 +519,9 @@ ZEND_BEGIN_MODULE_GLOBALS(oci) /* {{{ Module globals */ zend_long persistent_timeout; /* time period after which idle persistent connection is considered expired */ zend_long statement_cache_size; /* statement cache size. used with 9i+ clients only*/ zend_long default_prefetch; /* default prefetch setting */ + zend_long prefetch_lob_size; /* amount of LOB data to read when initially getting a LOB locator */ bool privileged_connect; /* privileged connect flag (On/Off) */ bool old_oci_close_semantics; /* old_oci_close_semantics flag (to determine the way oci_close() should behave) */ - int shutdown; /* in shutdown flag */ OCIEnv *env; /* global environment handle */ diff --git a/ext/oci8/tests/driver_name.phpt b/ext/oci8/tests/driver_name.phpt index 12639227526..0b32c4bd1f6 100644 --- a/ext/oci8/tests/driver_name.phpt +++ b/ext/oci8/tests/driver_name.phpt @@ -57,11 +57,11 @@ function get_attr($conn) ?> --EXPECT-- **Test 1.1 - Default values for the attribute ************** -The value of DRIVER_NAME is PHP OCI8 : 3.1.0 +The value of DRIVER_NAME is PHP OCI8 : 3.2.0 ***Test 1.2 - Get the values from different connections ************** Testing with oci_pconnect() -The value of DRIVER_NAME is PHP OCI8 : 3.1.0 +The value of DRIVER_NAME is PHP OCI8 : 3.2.0 Testing with oci_new_connect() -The value of DRIVER_NAME is PHP OCI8 : 3.1.0 +The value of DRIVER_NAME is PHP OCI8 : 3.2.0 Done diff --git a/ext/oci8/tests/lob_prefetch.phpt b/ext/oci8/tests/lob_prefetch.phpt new file mode 100755 index 00000000000..0fbdb160330 --- /dev/null +++ b/ext/oci8/tests/lob_prefetch.phpt @@ -0,0 +1,163 @@ +--TEST-- +LOB prefetching +--EXTENSIONS-- +oci8 +--SKIPIF-- + true, 'timesten' => false); // test runs on these DBs +require(__DIR__.'/skipif.inc'); +?> +--FILE-- +load(); + $row['CLOB']->free(); + if (strlen($l[0]) != LOBSIZE) { print("strlen(l) failure" . strlen($l)); exit; } + } + return($l); +} + +function get_clob_inline($c, $sql) { + $stid = oci_parse($c, $sql); + oci_execute($stid); + $l = []; + while (($row = oci_fetch_array($stid, OCI_ASSOC+OCI_RETURN_LOBS)) != false) { + $l[] = $row['CLOB']; + if (strlen($l[0]) != LOBSIZE) { print("strlen(l) failure" . strlen($l)); exit; } + } + return($l); +} + +function check_clobs($locarr, $inlinearr) { + print("Comparing CLOBS\n"); + for ($i = 0; $i < NUMROWS; ++$i) { + if (strlen($locarr[$i]) != LOBSIZE) { + trigger_error("size mismatch at $i " . strlen($locarr[$i]), E_USER_ERROR); + exit; + } + if (strcmp($locarr[$i], $inlinearr[$i])) { + trigger_error("data mismatch at $i " . strlen($locarr[$i]) . " " . strlen($inlinearr[$i]), E_USER_ERROR); + exit; + } + } +} + +function get_blob_loc($c, $sql) { + $stid = oci_parse($c, $sql); + oci_execute($stid); + $l = []; + while (($row = oci_fetch_array($stid, OCI_ASSOC)) != false) { + $l[] = $row['BLOB']->load(); + $row['BLOB']->free(); + if (strlen($l[0]) != LOBSIZE) { print("strlen(l) failure" . strlen($l)); exit; } + } + return($l); +} + + +print("Test 1\n"); + +$ig = ini_get("oci8.prefetch_lob_size"); +var_dump($ig); + +$ig = ini_set("oci8.prefetch_lob_size", "100000"); +var_dump($ig); + +$ig = ini_get("oci8.prefetch_lob_size"); +var_dump($ig); + +print("Test 2 - CLOB prefetch_lob_size 100000\n"); + +$sql = "select clob from ${schema}${table_name}" . " order by id"; +$locarr = get_clob_loc($c, $sql); +$inlinearr = get_clob_inline($c, $sql); + +print(count($locarr) . "\n"); +print(count($inlinearr) . "\n"); +check_clobs($locarr, $inlinearr); + +print("Test 3 - CLOB prefetch_lob_size 100\n"); + +ini_set("oci8.prefetch_lob_size", "100"); +$ig = ini_get("oci8.prefetch_lob_size"); +var_dump($ig); + +$locarr = get_clob_loc($c, $sql); +$inlinearr = get_clob_inline($c, $sql); + +print(count($locarr) . "\n"); +print(count($inlinearr) . "\n"); +check_clobs($locarr, $inlinearr); + +print("Test 4 - BLOB prefetch_lob_size 100000\n"); + +ini_set("oci8.prefetch_lob_size", "100000"); +$ig = ini_get("oci8.prefetch_lob_size"); +var_dump($ig); + +$sql = "select blob from ${schema}${table_name}" . " order by id"; +$locarr = get_blob_loc($c, $sql); + +print(count($locarr) . "\n"); + +require __DIR__.'/drop_table.inc'; + +?> +DONE +--EXPECTF-- +Test 1 +string(1) "0" +string(1) "0" +string(6) "100000" +Test 2 - CLOB prefetch_lob_size 100000 +500 +500 +Comparing CLOBS +Test 3 - CLOB prefetch_lob_size 100 +string(3) "100" +500 +500 +Comparing CLOBS +Test 4 - BLOB prefetch_lob_size 100000 +string(6) "100000" +500 +DONE diff --git a/php.ini-development b/php.ini-development index 604059bfb68..768b22a933d 100644 --- a/php.ini-development +++ b/php.ini-development @@ -1254,7 +1254,7 @@ mysqlnd.collect_memory_statistics = On ;oci8.ping_interval = 60 ; Connection: Set this to a user chosen connection class to be used -; for all pooled server requests with Oracle 11g Database Resident +; for all pooled server requests with Oracle Database Resident ; Connection Pooling (DRCP). To use DRCP, this value should be set to ; the same string for all web servers running the same application, ; the database pool must be configured, and the connection string must @@ -1271,11 +1271,18 @@ mysqlnd.collect_memory_statistics = On ; https://php.net/oci8.statement-cache-size ;oci8.statement_cache_size = 20 -; Tuning: Enables statement prefetching and sets the default number of +; Tuning: Enables row prefetching and sets the default number of ; rows that will be fetched automatically after statement execution. ; https://php.net/oci8.default-prefetch ;oci8.default_prefetch = 100 +; Tuning: Sets the amount of LOB data that is internally returned from +; Oracle Database when an Oracle LOB locator is initially retrieved as +; part of a query. Setting this can improve performance by reducing +; round-trips. +; https://php.net/oci8.prefetch-lob-size +; oci8.prefetch_lob_size = 0 + ; Compatibility. Using On means oci_close() will not close ; oci_connect() and oci_new_connect() connections. ; https://php.net/oci8.old-oci-close-semantics diff --git a/php.ini-production b/php.ini-production index b39a960f302..e25092e688f 100644 --- a/php.ini-production +++ b/php.ini-production @@ -1256,7 +1256,7 @@ mysqlnd.collect_memory_statistics = Off ;oci8.ping_interval = 60 ; Connection: Set this to a user chosen connection class to be used -; for all pooled server requests with Oracle 11g Database Resident +; for all pooled server requests with Oracle Database Resident ; Connection Pooling (DRCP). To use DRCP, this value should be set to ; the same string for all web servers running the same application, ; the database pool must be configured, and the connection string must @@ -1278,6 +1278,13 @@ mysqlnd.collect_memory_statistics = Off ; https://php.net/oci8.default-prefetch ;oci8.default_prefetch = 100 +; Tuning: Sets the amount of LOB data that is internally returned from +; Oracle Database when an Oracle LOB locator is initially retrieved as +; part of a query. Setting this can improve performance by reducing +; round-trips. +; https://php.net/oci8.prefetch-lob-size +; oci8.prefetch_lob_size = 0 + ; Compatibility. Using On means oci_close() will not close ; oci_connect() and oci_new_connect() connections. ; https://php.net/oci8.old-oci-close-semantics