Add oci_set_prefetch_lob()

This commit is contained in:
Christopher Jones 2021-11-28 12:06:25 +11:00
parent 6606cc04fe
commit ac91b83ceb
No known key found for this signature in database
GPG Key ID: F3DF7046B54073C9
12 changed files with 298 additions and 60 deletions

View File

@ -28,9 +28,10 @@ PHP 8.2 UPGRADE NOTES
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.
. Added an oci8.prefetch_lob_size directive and oci_set_prefetch_lob()
function 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

View File

@ -211,7 +211,13 @@ if test "$PHP_OCI8" != "no"; then
oci8_php_version=`expr [$]1 \* 1000000 + [$]2 \* 1000 + [$]3`
if test "$oci8_php_version" -lt "5002000"; then
AC_MSG_ERROR([You need at least PHP 5.2.0 to be able to use this version of OCI8. PHP $php_version found])
AC_MSG_ERROR([You need at least PHP 8.1.0 to be able to use this version of OCI8. Use OCI8 1.4 for PHP $php_version])
elif test "$oci8_php_version" -lt "7000000"; then
AC_MSG_ERROR([You need at least PHP 8.1.0 to be able to use this version of OCI8. Use OCI8 2.0 for PHP $php_version])
elif test "$oci8_php_version" -lt "8000000"; then
AC_MSG_ERROR([You need at least PHP 8.1.0 to be able to use this version of OCI8. Use OCI8 2.2 for PHP $php_version])
elif test "$oci8_php_version" -lt "8001000"; then
AC_MSG_ERROR([You need at least PHP 8.1.0 to be able to use this version of OCI8. Use OCI8 3.0 for PHP $php_version])
else
AC_MSG_RESULT([$php_version, ok])
fi

View File

@ -35,6 +35,9 @@
#ifdef HAVE_OCI8
/* Note to maintainers: config.m4 should/does check the minimum PHP version so
* the below checks are obsolete - but are kept 'just in case' */
/* PHP 5.2 is the minimum supported version for OCI8 2.0 */
#if PHP_MAJOR_VERSION < 5 || (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION <= 1)
#error Use PHP OCI8 1.4 for your version of PHP
@ -42,8 +45,10 @@
/* PHP 7 is the minimum supported version for OCI8 2.1 */
#error Use PHP OCI8 2.0 for your version of PHP
#elif PHP_MAJOR_VERSION < 8
/* PHP 8 is the minimum supported version for OCI8 3.0 */
/* PHP 8 is the minimum supported version for OCI8 3 */
#error Use PHP OCI8 2.2 for your version of PHP
#elif PHP_MAJOR_VERSION == 8 && PHP_MINOR_VERSION < 1
#error Use PHP OCI8 3.0 for your version of PHP
#endif
#include "php_oci8.h"
@ -174,7 +179,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)
STD_PHP_INI_ENTRY( "oci8.prefetch_lob_size", "0", PHP_INI_SYSTEM, OnUpdateLong, prefetch_lob_size, zend_oci_globals, oci_globals)
PHP_INI_END()
/* }}} */

View File

@ -381,6 +381,9 @@ function oci_set_prefetch($statement, int $rows): bool {}
*/
function ocisetprefetch($statement, int $rows): bool {}
/** @param resource $statement */
function oci_set_prefetch_lob($statement, int $prefetch_lob_size): bool {}
/** @param resource $connection */
function oci_set_client_identifier($connection, string $client_id): bool {}

View File

@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: 46ee8ce62b36639636b4f5126e20a4b4e1df2e25 */
* Stub hash: 9db587b5d431b9dfe7178fd843ae8907db737a04 */
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_oci_define_by_name, 0, 3, _IS_BOOL, 0)
ZEND_ARG_INFO(0, statement)
@ -299,6 +299,11 @@ ZEND_END_ARG_INFO()
#define arginfo_ocisetprefetch arginfo_oci_set_prefetch
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_oci_set_prefetch_lob, 0, 2, _IS_BOOL, 0)
ZEND_ARG_INFO(0, statement)
ZEND_ARG_TYPE_INFO(0, prefetch_lob_size, IS_LONG, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_oci_set_client_identifier, 0, 2, _IS_BOOL, 0)
ZEND_ARG_INFO(0, connection)
ZEND_ARG_TYPE_INFO(0, client_id, IS_STRING, 0)
@ -596,6 +601,7 @@ ZEND_FUNCTION(oci_num_fields);
ZEND_FUNCTION(oci_parse);
ZEND_FUNCTION(oci_get_implicit_resultset);
ZEND_FUNCTION(oci_set_prefetch);
ZEND_FUNCTION(oci_set_prefetch_lob);
ZEND_FUNCTION(oci_set_client_identifier);
ZEND_FUNCTION(oci_set_edition);
ZEND_FUNCTION(oci_set_module_name);
@ -710,6 +716,7 @@ static const zend_function_entry ext_functions[] = {
ZEND_FE(oci_get_implicit_resultset, arginfo_oci_get_implicit_resultset)
ZEND_FE(oci_set_prefetch, arginfo_oci_set_prefetch)
ZEND_DEP_FALIAS(ocisetprefetch, oci_set_prefetch, arginfo_ocisetprefetch)
ZEND_FE(oci_set_prefetch_lob, arginfo_oci_set_prefetch_lob)
ZEND_FE(oci_set_client_identifier, arginfo_oci_set_client_identifier)
ZEND_FE(oci_set_edition, arginfo_oci_set_edition)
ZEND_FE(oci_set_module_name, arginfo_oci_set_module_name)

View File

@ -1633,6 +1633,30 @@ PHP_FUNCTION(oci_set_prefetch)
}
/* }}} */
/* {{{ Sets the amount of LOB data to be prefetched when OCI LOB locators are fetched */
PHP_FUNCTION(oci_set_prefetch_lob)
{
zval *z_statement;
php_oci_statement *statement;
zend_long prefetch_lob_size;
ZEND_PARSE_PARAMETERS_START(2, 2)
Z_PARAM_RESOURCE(z_statement)
Z_PARAM_LONG(prefetch_lob_size)
ZEND_PARSE_PARAMETERS_END();
PHP_OCI_ZVAL_TO_STATEMENT(z_statement, statement);
if (prefetch_lob_size < 0) {
zend_argument_value_error(2, "must be greater than or equal to 0");
RETURN_THROWS();
}
statement->prefetch_lob_size = (ub4) prefetch_lob_size;
RETURN_TRUE;
}
/* }}} */
/* {{{ Sets the client identifier attribute on the connection */
PHP_FUNCTION(oci_set_client_identifier)
{

View File

@ -117,6 +117,12 @@ php_oci_statement *php_oci_statement_create(php_oci_connection *connection, char
php_oci_statement_set_prefetch(statement, (ub4)100); /* semi-arbitrary, "sensible default" */
}
if (OCI_G(prefetch_lob_size) > 0) {
statement->prefetch_lob_size = (ub4)OCI_G(prefetch_lob_size);
} else {
statement->prefetch_lob_size = 0;
}
PHP_OCI_REGISTER_RESOURCE(statement, le_statement);
OCI_G(num_statements)++;
@ -173,6 +179,7 @@ php_oci_statement *php_oci_get_implicit_resultset(php_oci_statement *statement)
GC_ADDREF(statement2->connection->id);
php_oci_statement_set_prefetch(statement2, statement->prefetch_count);
statement2->prefetch_lob_size = statement->prefetch_lob_size;
PHP_OCI_REGISTER_RESOURCE(statement2, le_statement);
@ -186,7 +193,7 @@ php_oci_statement *php_oci_get_implicit_resultset(php_oci_statement *statement)
/* {{{ php_oci_statement_set_prefetch()
Set prefetch buffer size for the statement */
int php_oci_statement_set_prefetch(php_oci_statement *statement, ub4 prefetch )
int php_oci_statement_set_prefetch(php_oci_statement *statement, ub4 prefetch)
{
sword errstatus;
@ -820,18 +827,30 @@ int php_oci_statement_execute(php_oci_statement *statement, ub4 mode)
return 1;
}
/* Enable LOB data prefetching */
if ((outcol->data_type == SQLT_CLOB || outcol->data_type == SQLT_BLOB) && statement->prefetch_lob_size > 0) {
int get_lob_len = 1; /* == true */
PHP_OCI_CALL_RETURN(errstatus, OCIAttrSet, (outcol->oci_define, OCI_HTYPE_DEFINE, &get_lob_len, 0, OCI_ATTR_LOBPREFETCH_LENGTH, statement->err));
if (errstatus != OCI_SUCCESS) {
statement->errcode = php_oci_error(statement->err, errstatus);
PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
return 1;
}
PHP_OCI_CALL_RETURN(errstatus, OCIAttrSet, (outcol->oci_define, OCI_HTYPE_DEFINE, &(statement->prefetch_lob_size), 0, OCI_ATTR_LOBPREFETCH_SIZE, statement->err));
if (errstatus != OCI_SUCCESS) {
statement->errcode = php_oci_error(statement->err, errstatus);
PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode);
return 1;
}
}
/* additional define setup */
switch (outcol->data_type) {
case SQLT_RSET:
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,

View File

@ -10,7 +10,9 @@ http://pear.php.net/dtd/package-2.0.xsd">
<description>
The OCI8 extension lets you access Oracle Database.
Use 'pecl install oci8' to install for PHP 8.
Use 'pecl install oci8' to install for PHP 8.1.
Use 'pecl install oci8-3.0.1' to install for PHP 8.0.
Use 'pecl install oci8-2.2.0' to install for PHP 7.
@ -53,7 +55,7 @@ Oracle's standard cross-version connectivity applies. For example, PHP OCI8 lin
<active>no</active>
</lead>
<date>2021-11-15</date>
<date>2021-12-11</date>
<time>12:00:00</time>
<version>
@ -66,9 +68,9 @@ Oracle's standard cross-version connectivity applies. For example, PHP OCI8 lin
</stability>
<license uri="http://www.php.net/license">PHP</license>
<notes>
This version is for PHP 8 only.
This version is for PHP 8.1 only.
Added oci8.prefetch_lob_size directive to improve LOB query performance.
Added oci8.prefetch_lob_size directive and oci_set_prefetch_lob() function to improve LOB query performance.
</notes>
<contents>
<dir name="/">
@ -373,6 +375,8 @@ Oracle's standard cross-version connectivity applies. For example, PHP OCI8 lin
<file name="lob_044.phpt" role="test" />
<file name="lob_aliases.phpt" role="test" />
<file name="lob_null.phpt" role="test" />
<file name="lob_prefetch.phpt" role="test" />
<file name="lob_prefetch_ini.phpt" role="test" />
<file name="lob_temp.phpt" role="test" />
<file name="lob_temp1.phpt" role="test" />
<file name="lob_temp2.phpt" role="test" />
@ -435,7 +439,7 @@ Oracle's standard cross-version connectivity applies. For example, PHP OCI8 lin
<dependencies>
<required>
<php>
<min>8.0.0</min>
<min>8.1.0</min>
</php>
<pearinstaller>
<min>1.4.0b1</min>

View File

@ -235,7 +235,8 @@ typedef struct {
unsigned has_data:1; /* statement has more data flag */
unsigned has_descr:1; /* statement has at least one descriptor or cursor column */
ub2 stmttype; /* statement type */
ub4 prefetch_count; /* current prefetch count */
ub4 prefetch_count; /* row prefetch count */
ub4 prefetch_lob_size; /* LOB prefetch size */
} php_oci_statement;
/* }}} */

View File

@ -13,14 +13,14 @@ require(__DIR__.'/skipif.inc');
require __DIR__.'/connect.inc';
require __DIR__.'/create_table.inc';
define("NUMROWS", 500);
define("NUMROWS", 200);
define("LOBSIZE", 64000);
$ora_sql =
"declare
c clob;
b blob;
numrows number := 500;
numrows number := " . NUMROWS . ";
dest_offset integer := 1;
src_offset integer := 1;
warn integer;
@ -44,8 +44,10 @@ $statement = oci_parse($c,$ora_sql);
oci_execute($statement);
function get_clob_loc($c, $sql) {
function get_clob_loc($c, $sql, $pfl) {
$stid = oci_parse($c, $sql);
if ($pfl >= 0)
oci_set_prefetch_lob($stid, $pfl);
oci_execute($stid);
$l = [];
while (($row = oci_fetch_array($stid, OCI_ASSOC)) != false) {
@ -56,8 +58,10 @@ function get_clob_loc($c, $sql) {
return($l);
}
function get_clob_inline($c, $sql) {
function get_clob_inline($c, $sql, $pfl) {
$stid = oci_parse($c, $sql);
if ($pfl >= 0)
oci_set_prefetch_lob($stid, $pfl);
oci_execute($stid);
$l = [];
while (($row = oci_fetch_array($stid, OCI_ASSOC+OCI_RETURN_LOBS)) != false) {
@ -81,8 +85,10 @@ function check_clobs($locarr, $inlinearr) {
}
}
function get_blob_loc($c, $sql) {
function get_blob_loc($c, $sql, $pfl) {
$stid = oci_parse($c, $sql);
if ($pfl >= 0)
oci_set_prefetch_lob($stid, $pfl);
oci_execute($stid);
$l = [];
while (($row = oci_fetch_array($stid, OCI_ASSOC)) != false) {
@ -94,22 +100,28 @@ function get_blob_loc($c, $sql) {
}
print("Test 1\n");
print("Test 1 - Default prefetch_lob_size\n");
$ig = ini_get("oci8.prefetch_lob_size");
var_dump($ig);
$r = ini_get("oci8.prefetch_lob_size");
var_dump($r);
$ig = ini_set("oci8.prefetch_lob_size", "100000");
var_dump($ig);
print("Test 2\n");
$ig = ini_get("oci8.prefetch_lob_size");
var_dump($ig);
$s = oci_parse($c, 'select * from dual');
$r = oci_set_prefetch_lob($s, 0);
var_dump($r);
print("Test 2 - CLOB prefetch_lob_size 100000\n");
try {
oci_set_prefetch_lob($s, -1);
} catch (ValueError $e) {
echo $e->getMessage(), "\n";
}
print("Test 3 - 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);
$locarr = get_clob_loc($c, $sql, 100000);
$inlinearr = get_clob_inline($c, $sql, 100000);
print(count($locarr) . "\n");
print(count($inlinearr) . "\n");
@ -117,12 +129,8 @@ 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);
$locarr = get_clob_loc($c, $sql, 100);
$inlinearr = get_clob_inline($c, $sql, 100);
print(count($locarr) . "\n");
print(count($inlinearr) . "\n");
@ -130,12 +138,8 @@ 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);
$locarr = get_blob_loc($c, $sql, 100000);
print(count($locarr) . "\n");
@ -144,20 +148,19 @@ require __DIR__.'/drop_table.inc';
?>
DONE
--EXPECTF--
Test 1
Test 1 - Default prefetch_lob_size
string(1) "0"
string(1) "0"
string(6) "100000"
Test 2 - CLOB prefetch_lob_size 100000
500
500
Test 2
bool(true)
oci_set_prefetch_lob(): Argument #2 ($prefetch_lob_size) must be greater than or equal to 0
Test 3 - CLOB prefetch_lob_size 100000
200
200
Comparing CLOBS
Test 3 - CLOB prefetch_lob_size 100
string(3) "100"
500
500
200
200
Comparing CLOBS
Test 4 - BLOB prefetch_lob_size 100000
string(6) "100000"
500
200
DONE

View File

@ -0,0 +1,165 @@
--TEST--
LOB prefetching with oci8.
--EXTENSIONS--
oci8
--SKIPIF--
<?php
$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs
require(__DIR__.'/skipif.inc');
?>
--INI--
oci8.prefetch_lob_size=100000
--FILE--
<?php
require __DIR__.'/connect.inc';
require __DIR__.'/create_table.inc';
define("NUMROWS", 200);
define("LOBSIZE", 64000);
$ora_sql =
"declare
c clob;
b blob;
numrows number := " . NUMROWS . ";
dest_offset integer := 1;
src_offset integer := 1;
warn integer;
ctx integer := dbms_lob.default_lang_ctx;
begin
for j in 1..numrows
loop
c := DBMS_RANDOM.string('L',TRUNC(DBMS_RANDOM.value(1000,1000)));
for i in 1..6
loop
c := c||c;
end loop;
dbms_lob.createtemporary(b, false);
dbms_lob.converttoblob(b, c, dbms_lob.lobmaxsize, dest_offset, src_offset, dbms_lob.default_csid, ctx, warn);
insert /*+ APPEND */ into ${schema}${table_name} (id, clob, blob) values (j, c, b);
end loop;
commit;
end;";
$statement = oci_parse($c,$ora_sql);
oci_execute($statement);
function get_clob_loc($c, $sql, $pfl) {
$stid = oci_parse($c, $sql);
if ($pfl >= 0)
oci_set_prefetch_lob($stid, $pfl);
oci_execute($stid);
$l = [];
while (($row = oci_fetch_array($stid, OCI_ASSOC)) != false) {
$l[] = $row['CLOB']->load();
$row['CLOB']->free();
if (strlen($l[0]) != LOBSIZE) { print("strlen(l) failure" . strlen($l)); exit; }
}
return($l);
}
function get_clob_inline($c, $sql, $pfl) {
$stid = oci_parse($c, $sql);
if ($pfl >= 0)
oci_set_prefetch_lob($stid, $pfl);
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, $pfl) {
$stid = oci_parse($c, $sql);
if ($pfl >= 0)
oci_set_prefetch_lob($stid, $pfl);
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 - prefetch_lob_size\n");
$r = ini_get("oci8.prefetch_lob_size");
var_dump($r);
print("Test 2 - CLOB with current oci8.prefetch_lob_size\n");
$sql = "select clob from ${schema}${table_name}" . " order by id";
$locarr = get_clob_loc($c, $sql, -1);
$inlinearr = get_clob_inline($c, $sql, -1);
print(count($locarr) . "\n");
print(count($inlinearr) . "\n");
check_clobs($locarr, $inlinearr);
print("Test 3 - CLOB override prefetch_lob_size 0\n");
$locarr = get_clob_loc($c, $sql, 0);
$inlinearr = get_clob_inline($c, $sql, 0);
print(count($locarr) . "\n");
print(count($inlinearr) . "\n");
check_clobs($locarr, $inlinearr);
print("Test 4 - CLOB override prefetch_lob_size 1000\n");
$locarr = get_clob_loc($c, $sql, 1000);
$inlinearr = get_clob_inline($c, $sql, 1000);
print(count($locarr) . "\n");
print(count($inlinearr) . "\n");
check_clobs($locarr, $inlinearr);
print("Test 5 - BLOB with current ocig8.prefetch_lob_size \n");
$sql = "select blob from ${schema}${table_name}" . " order by id";
$locarr = get_blob_loc($c, $sql, -1);
print(count($locarr) . "\n");
require __DIR__.'/drop_table.inc';
?>
DONE
--EXPECTF--
Test 1 - prefetch_lob_size
string(6) "100000"
Test 2 - CLOB with current oci8.prefetch_lob_size
200
200
Comparing CLOBS
Test 3 - CLOB override prefetch_lob_size 0
200
200
Comparing CLOBS
Test 4 - CLOB override prefetch_lob_size 1000
200
200
Comparing CLOBS
Test 5 - BLOB with current ocig8.prefetch_lob_size
200
DONE

View File

@ -1273,7 +1273,7 @@ mysqlnd.collect_memory_statistics = Off
; 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