Binding driver options are ignored when calling addBindingsToStatement method
cwoodrgr opened this issue · comments
Summary of problem or feature request
While using the shortcut method to call a procedure: DB::executeProcedureWithCursor($procedureName, $bindings), I needed to pass options with the binding but found out that there does not appear to be a way to handle them currently.
Code snippet of problem
Current method ignores options:
public function addBindingsToStatement(PDOStatement $stmt, array $bindings)
{
foreach ($bindings as $key => &$binding) {
$value = &$binding;
$type = PDO::PARAM_STR;
$length = -1;
if (is_array($binding)) {
$value = &$binding['value'];
$type = array_key_exists('type', $binding) ? $binding['type'] : PDO::PARAM_STR;
$length = array_key_exists('length', $binding) ? $binding['length'] : -1;
}
$stmt->bindParam(':'.$key, $value, $type, $length);
}
return $stmt;
}
Updating the method to something like this appears to work:
public function addBindingsToStatement(PDOStatement $stmt, array $bindings)
{
foreach ($bindings as $key => &$binding) {
$value = &$binding;
$type = PDO::PARAM_STR;
$length = -1;
$options = null;
if (is_array($binding)) {
$value = &$binding['value'];
$type = array_key_exists('type', $binding) ? $binding['type'] : PDO::PARAM_STR;
$length = array_key_exists('length', $binding) ? $binding['length'] : -1;
$options = array_key_exists('options', $binding) ? $binding['options'] : $options;
}
$stmt->bindParam(':'.$key, $value, $type, $length, $options);
}
return $stmt;
}
System details
- Operating System: Ubuntu 22.04
- PHP Version: 8.1.13
- Laravel Version: 9.52.9
- Laravel-OCI8 Version: 9.5.0
Sorry, but can you provide some snippets for the use case?
Please do not hesitate to submit a PR, tagging for enhancement. Thanks!
A potential use case:
I have a procedure defined like this for testing purposes:
PROCEDURE Test_Array (p_array IN serial_numbers_t,
x_array OUT rc_serial_numbers_out)
IS
BEGIN
OPEN x_array FOR SELECT * FROM TABLE (p_array);
END Test_Array;
serial_numbers_t is a named type:
create or replace type SERIAL_NUMBERS_T is table of VARCHAR2(30);
Calling the procedure manually like this works:
use Yajra\Pdo\Oci8\Statement;
$pdo = DB::connection('oracle_w')->getPdo();
$serialNumbers = ['1', '2'];
$collection = $pdo->getNewCollection('SERIAL_NUMBERS_T', 'APPS');
foreach ($serialNumbers as $value) {
$collection->append($value);
}
$options = array("type_name" => "SERIAL_NUMBERS_T","schema" => "APPS");
$stmt_rec = $pdo->prepare("begin apps.test_pkg.test_array(:p_array, :x_array); end;");
$stmt_rec->bindParam(':p_array', $collection, SQLT_NTY, -1, $options);
$cursor = null;
$stmt_rec->bindParam(':x_array', $cursor, PDO::PARAM_STMT);
$stmt_rec->execute();
$statement = new Statement($cursor, $pdo, $pdo->getOptions());
$statement->execute();
$results = $statement->fetchAll(PDO::FETCH_OBJ);
$statement->closeCursor();
dd($results);
Results:
array:2 [
0 => {#4515
+"column_value": "1"
}
1 => {#4504
+"column_value": "2"
}
]
Ideally I want to use the shortcut method you provide in this package.
First I tried just passing a basic array:
DB::connection('oracle_w')
->executeProcedureWithCursor('apps.test_pkg.test_array', ['p_array' => ['1', '2']]);
This did not work
Yajra\Pdo\Oci8\Exceptions\Oci8Exception
Error Code : 6550
Error Message : ORA- 06550: line 1, column 7:
PLS- 00306: wrong number or types of arguments in call to 'TEST_ARRAY'
ORA- 06550: line 1, column 7:
PL/SQL: Statement ignored
Then I tried defining the :p_array bind using an OCICollection:
$values = ['1', '2'];
$collection = DB::connection('oracle_w')
->getPdo()
->getNewCollection('SERIAL_NUMBERS_T', 'APPS');
foreach ($values as $value) {
$collection->append($value);
}
$binds['p_array'] = [
'value' => $collection,
'type' => SQLT_NTY,
'length' => -1,
'options' => [
'type_name' => 'SERIAL_NUMBERS_T',
'schema' => 'APPS'
]
];
DB::connection('oracle_w')
->executeProcedureWithCursor('apps.test_pkg.test_array', $binds);
This resulted in the following exception:
WARNING oci_new_collection(): OCI-21560: argument 7 is null, invalid, or out of range in vendor/yajra/laravel-pdo-via-oci8/src/Pdo/Oci8.php on line 597.
TYPE ERROR Yajra\Pdo\Oci8::getNewCollection(): Return value must be of type OCICollection, bool returned in phar:///tmp/tinker.phar/vendor/psy/psysh/src/Exception/TypeErrorException.php on line 20.
Yajra\Pdo\Oci8::getNewCollection(): Return value must be of type OCICollection, bool returned
/vendor/yajra/laravel-pdo-via-oci8/src/Pdo/Oci8.php:21
/vendor/yajra/laravel-pdo-via-oci8/src/Pdo/Oci8/Statement.php:327
in Yajra\Pdo\Oci8::getNewCollection()
/vendor/yajra/laravel-oci8/src/Oci8/Oci8Connection.php:422
in Yajra\Pdo\Oci8\Statement::bindParam()
/vendor/yajra/laravel-oci8/src/Oci8/Oci8Connection.php:260
in Yajra\Oci8\Oci8Connection::addBindingsToStatement()
I then tried modifying the addBindingsToStatement method in Oci8Connection.php to the following:
public function addBindingsToStatement(PDOStatement $stmt, array $bindings)
{
foreach ($bindings as $key => &$binding) {
$value = &$binding;
$type = PDO::PARAM_STR;
$length = -1;
$options = null;
if (is_array($binding)) {
$value = &$binding['value'];
$type = array_key_exists('type', $binding) ? $binding['type'] : PDO::PARAM_STR;
$length = array_key_exists('length', $binding) ? $binding['length'] : -1;
$options = array_key_exists('options', $binding) ? $binding['options'] : $options;
}
$stmt->bindParam(':'.$key, $value, $type, $length, $options);
}
return $stmt;
}
After making the change, I re-ran the code and got the expected result:
[
{#4269
+"column_value": "1",
},
{#4268
+"column_value": "2",
},
]
If this is acceptable, I can submit a PR.
How do you handle a PR that should be applied to multiple branches? (ex. 9.x, 10.x, etc...)
How do you handle a PR that should be applied to multiple branches? (ex. 9.x, 10.x, etc...)
Via cherry-pick if possible. I also accept a PR per branch but would take a lot of work.
The proposed changes are acceptable, looking forward to the PR. Thanks a lot for providing the details.
fixed via #814