DB\IO update with better error reporting and auto update for placeholders in queries

This commit is contained in:
2023-10-16 15:02:30 +09:00
parent 1ef91305d4
commit 13602bdd73
6 changed files with 984 additions and 315 deletions

View File

@@ -44,7 +44,7 @@ class EditBase
* construct form generator * construct form generator
* *
* phpcs:ignore * phpcs:ignore
* @param array{db_name:string,db_user:string,db_pass:string,db_host:string,db_port:int,db_schema:string,db_encoding:string,db_type:string,db_ssl:string,db_convert_type?:string[]} $db_config db config array, mandatory * @param array{db_name:string,db_user:string,db_pass:string,db_host:string,db_port:int,db_schema:string,db_encoding:string,db_type:string,db_ssl:string,db_convert_type?:string[],db_convert_placeholder?:bool,db_convert_placeholder_target?:string,db_debug_replace_placeholder?:bool} $db_config db config array, mandatory
* @param \CoreLibs\Logging\Logging $log Logging class, null auto set * @param \CoreLibs\Logging\Logging $log Logging class, null auto set
* @param \CoreLibs\Language\L10n $l10n l10n language class, null auto set * @param \CoreLibs\Language\L10n $l10n l10n language class, null auto set
* @param \CoreLibs\ACL\Login $login login class for ACL settings * @param \CoreLibs\ACL\Login $login login class for ACL settings

View File

@@ -55,7 +55,7 @@ class ArrayIO extends \CoreLibs\DB\IO
* primary key name automatically (from array) * primary key name automatically (from array)
* *
* phpcs:ignore * phpcs:ignore
* @param array{db_name:string,db_user:string,db_pass:string,db_host:string,db_port:int,db_schema:string,db_encoding:string,db_type:string,db_ssl:string,db_convert_type?:string[]} $db_config db connection config * @param array{db_name:string,db_user:string,db_pass:string,db_host:string,db_port:int,db_schema:string,db_encoding:string,db_type:string,db_ssl:string,db_convert_type?:string[],db_convert_placeholder?:bool,db_convert_placeholder_target?:string,db_debug_replace_placeholder?:bool} $db_config db connection config
* @param array<mixed> $table_array table array config * @param array<mixed> $table_array table array config
* @param string $table_name table name string * @param string $table_name table name string
* @param \CoreLibs\Logging\Logging $log Logging class * @param \CoreLibs\Logging\Logging $log Logging class

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,220 @@
<?php
/**
* AUTOR: Clemens Schwaighofer
* CREATED: 2023/10/10
* DESCRIPTION:
* Convert placeholders in query from PDO style ? or :named to \PG style $number
* pr the other way around
*/
declare(strict_types=1);
namespace CoreLibs\DB\Support;
class ConvertPlaceholder
{
/**
* Convert PDO type query with placeholders to \PG style and vica versa
* For PDO to: ? and :named
* For \PG to: $number
*
* If the query has a mix of ?, :named or $numbrer the \OutOfRangeException exception
* will be thrown
*
* If the convert_to is either pg or pdo, nothing will be changed
*
* found has -1 if an error occoured in the preg_match_all call
*
* @param string $query Query with placeholders to convert
* @param array<mixed> $params The parameters that are used for the query, and will be updated
* @param string $convert_to Either pdo or pg, will be converted to lower case for check
* @return array{original:array{query:string,params:array<mixed>},type:''|'named'|'numbered'|'question_mark',found:int,matches:array<string>,params_lookup:array<mixed>,query:string,params:array<mixed>}
* @throws \OutOfRangeException 200
*/
public static function convertPlaceholderInQuery(
string $query,
array $params,
string $convert_to = 'pg'
): array {
$convert_to = strtolower($convert_to);
$matches = [];
$pattern = '/'
// prefix string part, must match towards
. '(?:\'.*?\')?\s*(?:\?\?|[(=,])\s*'
// match for replace part
. '(?:'
// digit -> ignore
. '\d+|'
// other string -> ignore
. '(?:\'.*?\')|'
// :name named part (PDO)
. '(:\w+)|'
// ? question mark part (PDO)
. '(?:(?:\?\?)?\s*(\?{1}))|'
// $n numbered part (\PG php)
. '(\$[1-9]{1}(?:[0-9]{1,})?)'
// end match
. ')'
// single line -> add line break to matches in "."
. '/s';
// matches:
// 1: :named
// 2: ? question mark
// 3: $n numbered
$found = preg_match_all($pattern, $query, $matches, PREG_UNMATCHED_AS_NULL);
// if false or null set to -1
// || $found === null
if ($found === false) {
$found = -1;
}
/** @var array<string> 1: named */
$named_matches = array_filter($matches[1]);
/** @var array<string> 2: open ? */
$qmark_matches = array_filter($matches[2]);
/** @var array<string> 3: $n matches */
$numbered_matches = array_filter($matches[3]);
// count matches
$count_named = count($named_matches);
$count_qmark = count($qmark_matches);
$count_numbered = count($numbered_matches);
// throw if mixed
if (
($count_named && $count_qmark) ||
($count_named && $count_numbered) ||
($count_qmark && $count_numbered)
) {
throw new \OutOfRangeException('Cannot have named, question mark and numbered in the same query', 200);
}
// rebuild
$matches_return = [];
$type = '';
$query_new = '';
$params_new = [];
$params_lookup = [];
if ($count_named && $convert_to == 'pg') {
$type = 'named';
$matches_return = $named_matches;
// only check for :named
$pattern_replace = '/((?:\'.*?\')?\s*(?:\?\?|[(=,])\s*)(\d+|(?:\'.*?\')|(:\w+))/s';
// 0: full
// 1: pre part
// 2: keep part UNLESS '3' is set
// 3: replace part :named
$pos = 0;
$query_new = preg_replace_callback(
$pattern_replace,
function ($matches) use (&$pos, &$params_new, &$params_lookup, $params) {
// only count up if $match[3] is not yet in lookup table
if (!empty($matches[3]) && empty($params_lookup[$matches[3]])) {
$pos++;
$params_lookup[$matches[3]] = '$' . $pos;
$params_new[] = $params[$matches[3]] ??
throw new \RuntimeException(
'Cannot lookup ' . $matches[3] . ' in params list',
210
);
}
// add the connectors back (1), and the data sets only if no replacement will be done
return $matches[1] . (
empty($matches[3]) ?
$matches[2] :
$params_lookup[$matches[3]] ??
throw new \RuntimeException(
'Cannot lookup ' . $matches[3] . ' in params lookup list',
211
)
);
},
$query
);
} elseif ($count_qmark && $convert_to == 'pg') {
$type = 'question_mark';
$matches_return = $qmark_matches;
// order and data stays the same
$params_new = $params;
// only check for ?
$pattern_replace = '/((?:\'.*?\')?\s*(?:\?\?|[(=,])\s*)(\d+|(?:\'.*?\')|(?:(?:\?\?)?\s*(\?{1})))/s';
// 0: full
// 1: pre part
// 2: keep part UNLESS '3' is set
// 3: replace part ?
$pos = 0;
$query_new = preg_replace_callback(
$pattern_replace,
function ($matches) use (&$pos, &$params_lookup) {
// only count pos up for actual replacements we will do
if (!empty($matches[3])) {
$pos++;
$params_lookup[] = '$' . $pos;
}
// add the connectors back (1), and the data sets only if no replacement will be done
return $matches[1] . (
empty($matches[3]) ?
$matches[2] :
'$' . $pos
);
},
$query
);
// for each ?:DTN: -> replace with $1 ... $n, any remaining :DTN: remove
} elseif ($count_numbered && $convert_to == 'pdo') {
// convert numbered to named
$type = 'numbered';
$matches_return = $numbered_matches;
// only check for $n
$pattern_replace = '/((?:\'.*?\')?\s*(?:\?\?|[(=,])\s*)(\d+|(?:\'.*?\')|(\$[1-9]{1}(?:[0-9]{1,})?))/s';
// 0: full
// 1: pre part
// 2: keep part UNLESS '3' is set
// 3: replace part $numbered
$pos = 0;
$query_new = preg_replace_callback(
$pattern_replace,
function ($matches) use (&$pos, &$params_new, &$params_lookup, $params) {
// only count up if $match[3] is not yet in lookup table
if (!empty($matches[3]) && empty($params_lookup[$matches[3]])) {
$pos++;
$params_lookup[$matches[3]] = ':' . $pos . '_named';
$params_new[] = $params[($pos - 1)] ??
throw new \RuntimeException(
'Cannot lookup ' . ($pos - 1) . ' in params list',
220
);
}
// add the connectors back (1), and the data sets only if no replacement will be done
return $matches[1] . (
empty($matches[3]) ?
$matches[2] :
$params_lookup[$matches[3]] ??
throw new \RuntimeException(
'Cannot lookup ' . $matches[3] . ' in params lookup list',
221
)
);
},
$query
);
}
// return, old query is always set
return [
// original
'original' => [
'query' => $query,
'params' => $params,
],
// type found, empty if nothing was done
'type' => $type,
// int: found, not found; -1: problem (set from false)
'found' => (int)$found,
'matches' => $matches_return,
// old to new lookup check
'params_lookup' => $params_lookup,
// new
'query' => $query_new ?? '',
'params' => $params_new,
];
}
}
// __END__

View File

@@ -310,7 +310,7 @@ class Generate
* construct form generator * construct form generator
* *
* phpcs:ignore * phpcs:ignore
* @param array{db_name:string,db_user:string,db_pass:string,db_host:string,db_port:int,db_schema:string,db_encoding:string,db_type:string,db_ssl:string,db_convert_type?:string[]} $db_config db config array, mandatory * @param array{db_name:string,db_user:string,db_pass:string,db_host:string,db_port:int,db_schema:string,db_encoding:string,db_type:string,db_ssl:string,db_convert_type?:string[],db_convert_placeholder?:bool,db_convert_placeholder_target?:string,db_debug_replace_placeholder?:bool} $db_config db config array, mandatory
* @param \CoreLibs\Logging\Logging $log Logging class * @param \CoreLibs\Logging\Logging $log Logging class
* @param \CoreLibs\Language\L10n $l10n l10n language class * @param \CoreLibs\Language\L10n $l10n l10n language class
* @param array<string,mixed> $login_acl Login ACL array, * @param array<string,mixed> $login_acl Login ACL array,

View File

@@ -232,7 +232,7 @@ final class CoreLibsDBIOTest extends TestCase
$this->assertEquals( $this->assertEquals(
$error, $error,
$last_error, $last_error,
'Assert query warning' 'Assert query error'
); );
return [$last_warning, $last_error]; return [$last_warning, $last_error];
} }
@@ -251,8 +251,6 @@ final class CoreLibsDBIOTest extends TestCase
*/ */
public function testDbVersion(): void public function testDbVersion(): void
{ {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO( $db = new \CoreLibs\DB\IO(
self::$db_config['valid'], self::$db_config['valid'],
self::$log self::$log
@@ -276,8 +274,6 @@ final class CoreLibsDBIOTest extends TestCase
*/ */
public function testDbVersionNumeric(): void public function testDbVersionNumeric(): void
{ {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO( $db = new \CoreLibs\DB\IO(
self::$db_config['valid'], self::$db_config['valid'],
self::$log self::$log
@@ -306,8 +302,6 @@ final class CoreLibsDBIOTest extends TestCase
*/ */
public function testDbVersionInfoParameters(): void public function testDbVersionInfoParameters(): void
{ {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO( $db = new \CoreLibs\DB\IO(
self::$db_config['valid'], self::$db_config['valid'],
self::$log self::$log
@@ -365,8 +359,6 @@ final class CoreLibsDBIOTest extends TestCase
*/ */
public function testDbVersionInfo(string $parameter, string $expected): void public function testDbVersionInfo(string $parameter, string $expected): void
{ {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO( $db = new \CoreLibs\DB\IO(
self::$db_config['valid'], self::$db_config['valid'],
self::$log self::$log
@@ -1592,8 +1584,6 @@ final class CoreLibsDBIOTest extends TestCase
string $error, string $error,
bool $run_many_times = false bool $run_many_times = false
): void { ): void {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO( $db = new \CoreLibs\DB\IO(
self::$db_config['valid'], self::$db_config['valid'],
self::$log self::$log
@@ -1832,8 +1822,6 @@ final class CoreLibsDBIOTest extends TestCase
string $error, string $error,
string $insert_data string $insert_data
): void { ): void {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO( $db = new \CoreLibs\DB\IO(
self::$db_config['valid'], self::$db_config['valid'],
self::$log self::$log
@@ -2002,8 +1990,6 @@ final class CoreLibsDBIOTest extends TestCase
string $error, string $error,
string $insert_data string $insert_data
): void { ): void {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO( $db = new \CoreLibs\DB\IO(
self::$db_config['valid'], self::$db_config['valid'],
self::$log self::$log
@@ -3069,8 +3055,6 @@ final class CoreLibsDBIOTest extends TestCase
string $error, string $error,
string $insert_data string $insert_data
): void { ): void {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO( $db = new \CoreLibs\DB\IO(
self::$db_config['valid'], self::$db_config['valid'],
self::$log self::$log
@@ -3465,7 +3449,7 @@ final class CoreLibsDBIOTest extends TestCase
$read_query, $read_query,
null, null,
null, null,
// // warning: 20
true, '20', '', true, '20', '',
// //
'result', '', '', 'result', '', '',
@@ -3482,6 +3466,31 @@ final class CoreLibsDBIOTest extends TestCase
'returning_id' => false, 'returning_id' => false,
], ],
], ],
// prepare with different statement name
'prepare query with same statement name, different query' => [
'double_error',
$read_query,
// primary key
null,
// arguments (none)
null,
// expected return false, warning: no, error: 26
false, '', '26',
// return expected, warning, error
'', '', '',
// dummy query for second prepare with wrong query
$read_query . ' WHERE uid = $3',
[],
//
$insert_query,
//
[
'pk_name' => '',
'count' => 0,
'query' => 'SELECT row_int, uid FROM table_with_primary_key',
'returning_id' => false,
],
],
// insert wrong data count compared to needed (execute 23) // insert wrong data count compared to needed (execute 23)
'wrong parmeter count' => [ 'wrong parmeter count' => [
'wrong_param_count', 'wrong_param_count',
@@ -3554,8 +3563,6 @@ final class CoreLibsDBIOTest extends TestCase
string $insert_data, string $insert_data,
array $prepare_cursor, array $prepare_cursor,
): void { ): void {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO( $db = new \CoreLibs\DB\IO(
self::$db_config['valid'], self::$db_config['valid'],
self::$log self::$log
@@ -3575,6 +3582,9 @@ final class CoreLibsDBIOTest extends TestCase
$db->dbPrepare($stm_name, $query) : $db->dbPrepare($stm_name, $query) :
$db->dbPrepare($stm_name, $query, $pk_name); $db->dbPrepare($stm_name, $query, $pk_name);
} }
if ($error_prepare == '26') {
$prepare_result = $db->dbPrepare($stm_name, $expected_data_query);
}
// if result type, or if forced bool // if result type, or if forced bool
if (is_string($expected_prepare) && $expected_prepare == 'result') { if (is_string($expected_prepare) && $expected_prepare == 'result') {
// if PHP or newer, must be Object PgSql\Result // if PHP or newer, must be Object PgSql\Result
@@ -3597,66 +3607,68 @@ final class CoreLibsDBIOTest extends TestCase
// for non fail prepare test exec // for non fail prepare test exec
// check test result // check test result
$execute_result = $query_data === null ? if (!$error_prepare) {
$db->dbExecute($stm_name) : $execute_result = $query_data === null ?
$db->dbExecute($stm_name, $query_data); $db->dbExecute($stm_name) :
if ($expected_execute == 'result') { $db->dbExecute($stm_name, $query_data);
// if PHP or newer, must be Object PgSql\Result if ($expected_execute == 'result') {
$this->assertIsObject( // if PHP or newer, must be Object PgSql\Result
$execute_result $this->assertIsObject(
); $execute_result
// also check that this is correct instance type );
$this->assertInstanceOf( // also check that this is correct instance type
'PgSql\Result', $this->assertInstanceOf(
$execute_result 'PgSql\Result',
); $execute_result
// if this is an select use dbFetchArray to get data and test );
} else { // if this is an select use dbFetchArray to get data and test
$this->assertEquals( } else {
$expected_execute, $this->assertEquals(
$execute_result $expected_execute,
); $execute_result
} );
// error/warning check }
$this->subAssertErrorTest($db, $warning_execute, $error_execute); // error/warning check
// now check test result if expected return is result $this->subAssertErrorTest($db, $warning_execute, $error_execute);
if ( // now check test result if expected return is result
$expected_execute == 'result' && if (
!empty($expected_data_query) $expected_execute == 'result' &&
) { !empty($expected_data_query)
// $expected_data_query ) {
// $expected_data // $expected_data_query
$rows = $db->dbReturnArray($expected_data_query); // $expected_data
$this->assertEquals( $rows = $db->dbReturnArray($expected_data_query);
$expected_data, $this->assertEquals(
$rows $expected_data,
); $rows
} );
if ( }
$expected_execute == 'result' && if (
$execute_result !== false && $expected_execute == 'result' &&
empty($expected_data_query) && $execute_result !== false &&
count($expected_data) empty($expected_data_query) &&
) { count($expected_data)
// compare previously read data to compare data ) {
$compare_data = []; // compare previously read data to compare data
// read in the query data $compare_data = [];
while (is_array($row = $db->dbFetchArray($execute_result, true))) { // read in the query data
$compare_data[] = $row; while (is_array($row = $db->dbFetchArray($execute_result, true))) {
$compare_data[] = $row;
}
$this->assertEquals(
$expected_data,
$compare_data
);
} }
$this->assertEquals(
$expected_data,
$compare_data
);
}
// check dbGetPrepareCursorValue // check dbGetPrepareCursorValue
foreach (['pk_name', 'count', 'query', 'returning_id'] as $key) { foreach (['pk_name', 'count', 'query', 'returning_id'] as $key) {
$this->assertEquals( $this->assertEquals(
$prepare_cursor[$key], $prepare_cursor[$key],
$db->dbGetPrepareCursorValue($stm_name, $key), $db->dbGetPrepareCursorValue($stm_name, $key),
'Prepared cursor: ' . $key . ': failed assertion' 'Prepared cursor: ' . $key . ': failed assertion'
); );
}
} }
// reset all data // reset all data
@@ -3844,8 +3856,6 @@ final class CoreLibsDBIOTest extends TestCase
string $expected_get_var, string $expected_get_var,
string $expected_get_db string $expected_get_db
): void { ): void {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO( $db = new \CoreLibs\DB\IO(
self::$db_config[$connection], self::$db_config[$connection],
self::$log self::$log
@@ -3910,7 +3920,10 @@ final class CoreLibsDBIOTest extends TestCase
// 'main::run::run::run::run::run::run::run::runBare::runTest::testDbErrorHandling::dbSetMaxQueryCall // 'main::run::run::run::run::run::run::run::runBare::runTest::testDbErrorHandling::dbSetMaxQueryCall
'source' => "/^(include::)?main::(run::)+runBare::runTest::testDbErrorHandling::dbSetMaxQueryCall$/", 'source' => "/^(include::)?main::(run::)+runBare::runTest::testDbErrorHandling::dbSetMaxQueryCall$/",
'pg_error' => '', 'pg_error' => '',
'msg' => '', 'message' => '',
'context' => [
'max_calls' => 0
]
] ]
], ],
'trigger warning' => [ 'trigger warning' => [
@@ -3943,8 +3956,6 @@ final class CoreLibsDBIOTest extends TestCase
string $error_id, string $error_id,
array $expected_history array $expected_history
): void { ): void {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO( $db = new \CoreLibs\DB\IO(
self::$db_config['valid'], self::$db_config['valid'],
self::$log self::$log
@@ -3970,7 +3981,7 @@ final class CoreLibsDBIOTest extends TestCase
foreach ($expected_history as $key => $value) { foreach ($expected_history as $key => $value) {
// check if starts with / because this is regex (timestamp) // check if starts with / because this is regex (timestamp)
// if (substr($expected_2, 0, 1) == '/) { // if (substr($expected_2, 0, 1) == '/) {
if (strpos($value, '/') === 0) { if (!is_array($value) && strpos($value, '/') === 0) {
// this is regex // this is regex
$this->assertMatchesRegularExpression( $this->assertMatchesRegularExpression(
$value, $value,
@@ -4058,8 +4069,6 @@ final class CoreLibsDBIOTest extends TestCase
bool $expected_set_flag, bool $expected_set_flag,
string $expected_get_encoding string $expected_get_encoding
): void { ): void {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO( $db = new \CoreLibs\DB\IO(
self::$db_config[$connection], self::$db_config[$connection],
self::$log self::$log
@@ -4141,8 +4150,6 @@ final class CoreLibsDBIOTest extends TestCase
?string $encoding_php, ?string $encoding_php,
string $text string $text
): void { ): void {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO( $db = new \CoreLibs\DB\IO(
self::$db_config[$connection], self::$db_config[$connection],
self::$log self::$log
@@ -4272,8 +4279,6 @@ final class CoreLibsDBIOTest extends TestCase
string $table, string $table,
string $primary_key string $primary_key
): void { ): void {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO( $db = new \CoreLibs\DB\IO(
self::$db_config['valid'], self::$db_config['valid'],
self::$log self::$log
@@ -4330,7 +4335,7 @@ final class CoreLibsDBIOTest extends TestCase
// NOTE if there are different INSERTS before the primary keys // NOTE if there are different INSERTS before the primary keys
// will not match anymore. Must be updated by hand // will not match anymore. Must be updated by hand
// IMPORTANT: if this is stand alone the primary key will not match and fail // IMPORTANT: if this is stand alone the primary key will not match and fail
$table_with_primary_key_id = 68; $table_with_primary_key_id = 70;
// 0: query + returning // 0: query + returning
// 1: params // 1: params
// 1: pk name for db exec // 1: pk name for db exec
@@ -4530,8 +4535,6 @@ final class CoreLibsDBIOTest extends TestCase
array|string|int|null $expected_ret_ext, array|string|int|null $expected_ret_ext,
array $expected_ret_arr array $expected_ret_arr
): void { ): void {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO( $db = new \CoreLibs\DB\IO(
self::$db_config['valid'], self::$db_config['valid'],
self::$log self::$log
@@ -4875,8 +4878,6 @@ final class CoreLibsDBIOTest extends TestCase
array $expected_col_names, array $expected_col_names,
array $expected_col_types array $expected_col_types
): void { ): void {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO( $db = new \CoreLibs\DB\IO(
self::$db_config['valid'], self::$db_config['valid'],
self::$log self::$log
@@ -5030,6 +5031,147 @@ final class CoreLibsDBIOTest extends TestCase
$db->dbClose(); $db->dbClose();
} }
// query placeholder convert
public function queryPlaceholderReplaceProvider(): array
{
// WHERE row_varchar = $1
return [
'select, no change' => [
'query' => <<<SQL
SELECT row_varchar, row_varchar_literal, row_int, row_date
FROM table_with_primary_key
SQL,
'params' => [],
'found' => 0,
'expected_query' => '',
'expected_params' => [],
],
'select, params ?' => [
'query' => <<<SQL
SELECT row_varchar, row_varchar_literal, row_int, row_date
FROM table_with_primary_key
WHERE row_varchar = ?
SQL,
'params' => ['string a'],
'found' => 1,
'expected_query' => <<<SQL
SELECT row_varchar, row_varchar_literal, row_int, row_date
FROM table_with_primary_key
WHERE row_varchar = $1
SQL,
'expected_params' => ['string a'],
],
'select, params :' => [
'query' => <<<SQL
SELECT row_varchar, row_varchar_literal, row_int, row_date
FROM table_with_primary_key
WHERE row_varchar = :row_varchar
SQL,
'params' => [':row_varchar' => 'string a'],
'found' => 1,
'expected_query' => <<<SQL
SELECT row_varchar, row_varchar_literal, row_int, row_date
FROM table_with_primary_key
WHERE row_varchar = $1
SQL,
'expected_params' => ['string a'],
]
];
}
/**
* test query string with placeholders convert
*
* @dataProvider queryPlaceholderReplaceProvider
* @testdox Query replacement test [$_dataName]
*
* @param string $query
* @param array $params
* @param string $expected_query
* @param array $expected_params
* @return void
*/
public function testQueryPlaceholderReplace(
string $query,
array $params,
int $expected_found,
string $expected_query,
array $expected_params
): void {
$db = new \CoreLibs\DB\IO(
self::$db_config['valid'],
self::$log
);
$db->dbSetConvertPlaceholder(true);
//
if ($db->dbCheckQueryForSelect($query)) {
$res = $db->dbReturnRowParams($query, $params);
$converted = $db->dbGetPlaceholderConverted();
} else {
$db->dbExecParams($query, $params);
$converted = $db->dbGetPlaceholderConverted();
}
$this->assertEquals(
$expected_found,
$converted['found'],
'Found not equal'
);
$this->assertEquals(
$expected_query,
$converted['query'],
'Query not equal'
);
$this->assertEquals(
$expected_params,
$converted['params'],
'Params not equal'
);
}
/**
* test exception for placeholder convert
* -> internally converted to error
*
* @testdox Query Replace error tests
*
* @return void
*/
public function testQueryPlaceholderReplaceException(): void
{
$db = new \CoreLibs\DB\IO(
self::$db_config['valid'],
self::$log
);
$db->dbSetConvertPlaceholder(true);
$db->dbExecParams(
<<<SQL
SELECT foo FROM bar
WHERE a = ? and b = :bname
SQL,
['a', 'b']
);
$this->assertEquals(
200,
$db->dbGetLastError()
);
// catch unset, for :names
$db->dbExecParams(
<<<SQL
SELECT foo FROM bar
WHERE a = :aname and b = :bname
SQL,
[':foo' => 'a', ':bname' => 'b']
);
$this->assertEquals(
210,
$db->dbGetLastError()
);
// TODO: other way around for to pdo
}
// TODO implement below checks // TODO implement below checks
// - complex write sets // - complex write sets
// dbWriteData, dbWriteDataExt // dbWriteData, dbWriteDataExt
@@ -5158,8 +5300,6 @@ final class CoreLibsDBIOTest extends TestCase
string $warning_final, string $warning_final,
string $error_final string $error_final
): void { ): void {
// self::$log->setLogLevelAll('debug', true);
// self::$log->setLogLevelAll('print', true);
$db = new \CoreLibs\DB\IO( $db = new \CoreLibs\DB\IO(
self::$db_config['valid'], self::$db_config['valid'],
self::$log self::$log