From 35d3032df58766a357e2c6186d2f24e128ccf317 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Tue, 28 Mar 2023 15:39:13 +0900 Subject: [PATCH] CoreLibs DB\IO params methods --- www/admin/class_test.db.php | 144 ++- www/admin/class_test.smarty.php | 12 +- www/admin/class_test.varistype.php | 72 +- www/composer.lock | 2 +- www/vendor/composer/installed.json | 2 +- www/vendor/composer/installed.php | 2 +- .../publish/last.published | 2 +- .../corelibs-composer-all/src/ACL/Login.php | 2 +- .../corelibs-composer-all/src/DB/IO.php | 536 ++++++++-- .../src/DB/SQL/Interface/SqlFunctions.php | 36 + .../src/DB/SQL/PgSQL.php | 71 +- .../src/Template/SmartyExtend.php | 84 +- .../test/phpunit/DB/CoreLibsDBIOTest.php | 976 ++++++++++++++---- 13 files changed, 1562 insertions(+), 379 deletions(-) diff --git a/www/admin/class_test.db.php b/www/admin/class_test.db.php index 3b459152..036a8829 100644 --- a/www/admin/class_test.db.php +++ b/www/admin/class_test.db.php @@ -57,7 +57,7 @@ echo "DB_CONFIG_SET constant:
" . print_r(DB_CONFIG, true) . "

"; print "DB client encoding: " . $db->dbGetEncoding() . "
"; print "DB search path: " . $db->dbGetSchema() . "
"; -$to_db_version = '13.6'; +$to_db_version = '15.2'; print "VERSION DB: " . $db->dbVersion() . "
"; print "VERSION LONG DB: " . $db->dbVersionInfo('server', false) . "
"; print "VERSION NUMERIC DB: " . $db->dbVersionNumeric() . "
"; @@ -167,15 +167,28 @@ print "DIRECT INSERT PREVIOUS INSERTED: " . print_r($db->dbReturnRow("SELECT test_foo_id, test FROM test_foo " . "WHERE test_foo_id = " . (int)$last_insert_pk), true) . "
"; $__last_insert_pk = (int)$last_insert_pk; -$q = <<dbReturnRow($query), true) . "
"; +print "LAST ERROR: " . $db->dbGetLastError() . "
"; +print "
"; +$query = <<dbReturnRow($q), true) . "
"; -print "LAST ERROR: " . $db->dbGetLastError() . "
"; -print "
"; +print "RETURN ROW PARAMS: " . print_r( + $db->dbReturnRowParams( + $query, + [$__last_insert_pk] + ), + true +) . "
"; // PREPARED INSERT $db->dbPrepare("ins_test_foo", "INSERT INTO test_foo (test) VALUES ($1) RETURNING test"); @@ -196,7 +209,9 @@ foreach (['pk_name', 'count', 'query', 'returning_id'] as $key) { } $query = <<dbGetReturningExt(), true) . " | " . "RETURNING RETURN: " . print_r($db->dbGetReturningArray(), true) . "
"; +$status = $db->dbExecParams($query, ['EOM BAR TEST PARAMS ' . time()]); +print "EOM STRING EXEC PARAMS INSERT[ins_test_foo_eom] STATUS: " . Support::printToString($status) . " |
" + . " |
" + . "PRIMARY KEY: " . Support::printToString($db->dbGetInsertPK()) . " | " + . "RETURNING EXT: " . print_r($db->dbGetReturningExt(), true) . " | " + . "RETURNING RETURN: " . print_r($db->dbGetReturningArray(), true) . "
"; + +$query = <<dbExecParams( + $query, + [ + 'EOM BAR TEST PARAMS MULTI ' . time(), + true, + 'string a', + 1, + 1.5, + '1h' + ] +); +$__last_insert_id = $db->dbGetInsertPK(); +print "EOM STRING EXEC PARAMS MULTI INSERT[ins_test_foo_eom] STATUS: " . Support::printToString($status) . " |
" + . " |
" + . "PRIMARY KEY: " . Support::printToString($db->dbGetInsertPK()) . " | " + . "RETURNING EXT: " . print_r($db->dbGetReturningExt(), true) . " | " + . "RETURNING RETURN: " . print_r($db->dbGetReturningArray(), true) . "
"; +$query = <<dbReturnRowParams( + $query, + [$__last_insert_id] + ) +) . "
"; + // returning test with multiple entries // $status = $db->db_exec( // "INSERT INTO test_foo (test) VALUES " @@ -276,6 +340,7 @@ print "UPDATE WITH PK " . Support::printToString($last_insert_pk) . "QUERY: " . $db->dbGetQuery() . " |
" . "RETURNING EXT: " . print_r($db->dbGetReturningExt(), true) . " | " . "RETURNING ARRAY: " . print_r($db->dbGetReturningArray(), true) . "
"; +$db->dbExec("INSERT INTO test_foo (test) VALUES ('STAND ALONE')"); // INSERT WITH NO RETURNING $status = $db->dbExec("INSERT INTO test_foobar (type, integer) VALUES ('WITHOUT DATA', 456)"); @@ -294,6 +359,7 @@ print "INSERT WITH NO PRIMARY KEY WITH RETURNING STATUS: " . Support::printToStr print ""; +print "PREPARE QUERIES
"; // READ PREPARE $q_prep = "SELECT test_foo_id, test, some_bool, string_a, number_a, " . "number_a_numeric, some_time " @@ -303,13 +369,12 @@ $q_prep = "SELECT test_foo_id, test, some_bool, string_a, number_a, " if ($db->dbPrepare('sel_test_foo', $q_prep) === false) { print "Error in sel_test_foo prepare
"; } else { - $max_rows = 6; // do not run this in dbFetchArray directly as // dbFetchArray(dbExecute(...)) // this will end in an endless loop - $cursor = $db->dbExecute('sel_test_foo', []); $i = 1; - while (($res = $db->dbFetchArray($cursor, true)) !== false) { + $cursor = $db->dbExecute('sel_test_foo', ['SOMETHING DIFFERENT']); + while (is_array($res = $db->dbFetchArray($cursor, true))) { print "DB PREP EXEC FETCH ARR: " . $i . ":
" . print_r($res, true) . "

"; $i++; } @@ -322,6 +387,37 @@ if ($db->dbPrepare('sel_test_foo', $q_prep) === false) { . "
" . print_r($db->dbGetCombinedErrorHistory(), true) . "

"; } +// sel test with ANY () type +$q_prep = "SELECT test_foo_id, test, some_bool, string_a, number_a, " + . "number_a_numeric, some_time " + . "FROM test_foo " + . "WHERE test = ANY($1) " + . "ORDER BY test_foo_id DESC LIMIT 5"; +if ($db->dbPrepare('sel_test_foo_any', $q_prep) === false) { + print "Error in sel_test_foo_any prepare
"; +} else { + // do not run this in dbFetchArray directly as + // dbFetchArray(dbExecute(...)) + // this will end in an endless loop + $values = [ + 'SOMETHING DIFFERENT', + 'STAND ALONE', + 'I DO NOT EXIST' + ]; + $query_value = '{' + . join(',', $values) + . '}'; + print "Read: $query_value
"; + $cursor = $db->dbExecute('sel_test_foo_any', [ + $query_value + ]); + $i = 1; + while (($res = $db->dbFetchArray($cursor, true)) !== false) { + print "DB PREP EXEC FETCH ANY ARR: " . $i . ":
" . print_r($res, true) . "

"; + $i++; + } +} + echo "
"; print "EOM STYLE STRINGS
"; $test_bar = $db->dbEscapeLiteral('SOMETHING DIFFERENT'); @@ -338,6 +434,21 @@ while (is_array($res = $db->dbReturn($q))) { print "ROW:
" . print_r($res, true) . "

"; } echo "
"; +print "DB RETURN PARAMS
"; +$q = <<dbReturnParams($q, ['SOMETHING DIFFERENT'])) +) { + print "ROW:
" . print_r($res, true) . "

"; +} +echo "
"; // NOTE: try to replacate connection still exists if script is run a second time // open pg bouncer connection @@ -418,7 +529,18 @@ print "Wrote to DB tabel $table with data " . print_r($data, true) . " and got p // return Array Test $query = "SELECT type, sdate, integer FROM foobar"; $data = $db->dbReturnArray($query, true); -print "Rows: " . $db->dbGetNumRows() . ", Full foobar list:
" . print_r($data, true) . "

"; +print "RETURN ARRAY: " . $db->dbGetNumRows() . ", Full foobar list:
" . print_r($data, true) . "

"; +$query = <<dbReturnArrayParams($query, ['schmalz'], true); +print "RETURN ARRAY PARAMS: " . $db->dbGetNumRows() . ", Full foobar list:
"
+	. print_r($data, true) . "

"; // trigger a warning print "WARNING NEXT
"; diff --git a/www/admin/class_test.smarty.php b/www/admin/class_test.smarty.php index ebe095a4..34f97687 100644 --- a/www/admin/class_test.smarty.php +++ b/www/admin/class_test.smarty.php @@ -46,6 +46,14 @@ $smarty = new CoreLibs\Template\SmartyExtend( CACHE_ID, COMPILE_ID, ); +$adm = new CoreLibs\Admin\Backend( + new CoreLibs\DB\IO(DB_CONFIG, $log), + $log, + new CoreLibs\Create\Session(EDIT_SESSION_NAME), + $l10n, + 80 +); +$adm->DATA['adm_set'] = 'SET from admin class'; $PAGE_NAME = 'TEST CLASS: SMARTY'; print ""; @@ -146,8 +154,10 @@ $smarty->setSmartyVarsAdmin( 'admin_stylesheet' => ADMIN_STYLESHEET, 'admin_javascript' => ADMIN_JAVASCRIPT, 'page_width' => PAGE_WIDTH, + 'content_path' => CONTENT_PATH, 'user_name' => $_SESSION['USER_NAME'] ?? '' - ] + ], + $adm ); // error message diff --git a/www/admin/class_test.varistype.php b/www/admin/class_test.varistype.php index c1fb2153..9ea875ff 100644 --- a/www/admin/class_test.varistype.php +++ b/www/admin/class_test.varistype.php @@ -6,14 +6,12 @@ declare(strict_types=1); -$DEBUG_ALL_OVERRIDE = 0; // set to 1 to debug on live/remote server locations -$DEBUG_ALL = 1; -$PRINT_ALL = 1; -$DB_DEBUG = 1; +$DEBUG_ALL_OVERRIDE = false; // set to 1 to debug on live/remote server locations +$DEBUG_ALL = true; +$PRINT_ALL = true; +$DB_DEBUG = true; -if ($DEBUG_ALL) { - error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR); -} +error_reporting(E_ALL | E_STRICT | E_ERROR | E_WARNING | E_PARSE | E_COMPILE_ERROR); ob_start(); @@ -25,8 +23,8 @@ require 'config.php'; $LOG_FILE_ID = 'classTest-VarIsType'; ob_end_flush(); -use CoreLibs\Convert\VarSetType; -use CoreLibs\Convert\VarSetTypeNull; +use CoreLibs\Convert\SetVarType; +use CoreLibs\Convert\SetVarTypeNull; use CoreLibs\Debug\Support; $log = new CoreLibs\Debug\Logging([ @@ -35,9 +33,9 @@ $log = new CoreLibs\Debug\Logging([ // add file date 'print_file_date' => true, // set debug and print flags - 'debug_all' => $DEBUG_ALL ?? false, + 'debug_all' => $DEBUG_ALL, 'echo_all' => $ECHO_ALL ?? false, - 'print_all' => $PRINT_ALL ?? false, + 'print_all' => $PRINT_ALL, ]); $PAGE_NAME = 'TEST CLASS: CONVERT\VARISTYPE'; @@ -48,28 +46,28 @@ print ''; print '

' . $PAGE_NAME . '

'; -print "Test A str set: " . VarSetType::setStr(5, 'set int') . "
"; -print "Test A str make int: " . VarSetType::makeStr(5, 'make int') . "
"; -print "Test A str make object: " . VarSetType::makeStr($log, 'Object') . "
"; -print "Test A str make null: " . VarSetType::makeStr(null, 'null') . "
"; -print "Test B int set: " . VarSetType::setInt("5", -1) . "
"; -print "Test B int make string: " . VarSetType::makeInt("5", -1) . "
"; -print "Test B' int make float: " . VarSetType::makeInt("5.5", -1) . "
"; -print "Test B'' int make class: " . VarSetType::makeInt($log, -1) . "
"; -print "Test B''' int make hex: " . VarSetType::makeInt("0x55", -1) . "
"; -print "Test B''' int make hex: " . VarSetType::makeInt(0x55, -1) . "
"; -print "Test C float make: " . VarSetType::makeFloat("13,232.95", -1) . "
"; +print "Test A str set: " . SetVarType::setStr(5, 'set int') . "
"; +print "Test A str make int: " . SetVarType::makeStr(5, 'make int') . "
"; +print "Test A str make object: " . SetVarType::makeStr($log, 'Object') . "
"; +print "Test A str make null: " . SetVarType::makeStr(null, 'null') . "
"; +print "Test B int set: " . SetVarType::setInt("5", -1) . "
"; +print "Test B int make string: " . SetVarType::makeInt("5", -1) . "
"; +print "Test B' int make float: " . SetVarType::makeInt("5.5", -1) . "
"; +print "Test B'' int make class: " . SetVarType::makeInt($log, -1) . "
"; +print "Test B''' int make hex: " . SetVarType::makeInt("0x55", -1) . "
"; +print "Test B''' int make hex: " . SetVarType::makeInt(0x55, -1) . "
"; +print "Test C float make: " . SetVarType::makeFloat("13,232.95", -1) . "
"; print "Test D floatval: " . floatval("13,232.95") . "
"; print "Test E filter_var: " . filter_var("13,232.95", FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION) . "
"; print "Test F filter_var: " . filter_var("string", FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION) . "
"; -print "Test G make bool: " . VarSetType::makeBool("on") . "
"; -print "Test G make bool: " . VarSetType::makeBool(null) . "
"; -print "Test G make bool: " . VarSetType::makeBool("null") . "
"; -print "Test G make bool: " . VarSetType::makeBool($log) . "
"; -print "Test G make bool: " . VarSetTypeNull::makeBool($log) . "
"; +print "Test G make bool: " . SetVarType::makeBool("on") . "
"; +print "Test G make bool: " . SetVarType::makeBool(null) . "
"; +print "Test G make bool: " . SetVarType::makeBool("null") . "
"; +print "Test G make bool: " . SetVarType::makeBool($log) . "
"; +print "Test G make bool: " . SetVarTypeNull::makeBool($log) . "
"; print "
"; @@ -84,30 +82,30 @@ $checks = [ foreach ($checks as $string) { print "** SET NOT NULL: (" . gettype($string) . ")
"; print "Str: " . Support::printToString($string) . ": -" - . Support::printToString(VarSetType::setStr($string)) . "-
"; + . Support::printToString(SetVarType::setStr($string)) . "-
"; print "Int: " . Support::printToString($string) . ": -" - . Support::printToString(VarSetType::setInt($string)) . "-
"; + . Support::printToString(SetVarType::setInt($string)) . "-
"; print "Float: " . Support::printToString($string) . ": -" - . Support::printToString(VarSetType::setFloat($string)) . "-
"; + . Support::printToString(SetVarType::setFloat($string)) . "-
"; print "Bool: " . Support::printToString($string) . ": -" - . Support::printToString(VarSetType::setBool($string)) . "-
"; + . Support::printToString(SetVarType::setBool($string)) . "-
"; print "Array: " . Support::printToString($string) . ": -" - . Support::printToString(VarSetType::setArray($string)) . "-
"; + . Support::printToString(SetVarType::setArray($string)) . "-
"; print "
"; } foreach ($checks as $string) { print "** SET NULL: (" . gettype($string) . ")
"; print "Str: " . Support::printToString($string) . ": -" - . Support::printToString(VarSetTypeNull::setStr($string)) . "-
"; + . Support::printToString(SetVarTypeNull::setStr($string)) . "-
"; print "Int: " . Support::printToString($string) . ": -" - . Support::printToString(VarSetTypeNull::setInt($string)) . "-
"; + . Support::printToString(SetVarTypeNull::setInt($string)) . "-
"; print "Float: " . Support::printToString($string) . ": -" - . Support::printToString(VarSetTypeNull::setFloat($string)) . "-
"; + . Support::printToString(SetVarTypeNull::setFloat($string)) . "-
"; print "Bool: " . Support::printToString($string) . ": -" - . Support::printToString(VarSetTypeNull::setBool($string)) . "-
"; + . Support::printToString(SetVarTypeNull::setBool($string)) . "-
"; print "Array: " . Support::printToString($string) . ": -" - . Support::printToString(VarSetTypeNull::setArray($string)) . "-
"; + . Support::printToString(SetVarTypeNull::setArray($string)) . "-
"; print "
"; } diff --git a/www/composer.lock b/www/composer.lock index 1b5f9d1c..416fa01f 100644 --- a/www/composer.lock +++ b/www/composer.lock @@ -12,7 +12,7 @@ "dist": { "type": "path", "url": "/storage/var/www/html/developers/clemens/core_data/composer-packages/CoreLibs-Composer-All", - "reference": "583edbfe0a2220c2257017379139eb6b40fd1cf1" + "reference": "9616d956cb2e2e672125f16d7832e4aa36702197" }, "require": { "php": ">=8.1" diff --git a/www/vendor/composer/installed.json b/www/vendor/composer/installed.json index 82a7feb6..63f9e6df 100644 --- a/www/vendor/composer/installed.json +++ b/www/vendor/composer/installed.json @@ -7,7 +7,7 @@ "dist": { "type": "path", "url": "/storage/var/www/html/developers/clemens/core_data/composer-packages/CoreLibs-Composer-All", - "reference": "583edbfe0a2220c2257017379139eb6b40fd1cf1" + "reference": "9616d956cb2e2e672125f16d7832e4aa36702197" }, "require": { "php": ">=8.1" diff --git a/www/vendor/composer/installed.php b/www/vendor/composer/installed.php index c2ef2c55..773e6f08 100644 --- a/www/vendor/composer/installed.php +++ b/www/vendor/composer/installed.php @@ -13,7 +13,7 @@ 'egrajp/corelibs-composer-all' => array( 'pretty_version' => 'dev-development', 'version' => 'dev-development', - 'reference' => '583edbfe0a2220c2257017379139eb6b40fd1cf1', + 'reference' => '9616d956cb2e2e672125f16d7832e4aa36702197', 'type' => 'library', 'install_path' => __DIR__ . '/../egrajp/corelibs-composer-all', 'aliases' => array(), diff --git a/www/vendor/egrajp/corelibs-composer-all/publish/last.published b/www/vendor/egrajp/corelibs-composer-all/publish/last.published index 904be6d4..404f7d9a 100644 --- a/www/vendor/egrajp/corelibs-composer-all/publish/last.published +++ b/www/vendor/egrajp/corelibs-composer-all/publish/last.published @@ -1 +1 @@ -8.0.5 +8.0.7 diff --git a/www/vendor/egrajp/corelibs-composer-all/src/ACL/Login.php b/www/vendor/egrajp/corelibs-composer-all/src/ACL/Login.php index d41dd33c..3255606e 100644 --- a/www/vendor/egrajp/corelibs-composer-all/src/ACL/Login.php +++ b/www/vendor/egrajp/corelibs-composer-all/src/ACL/Login.php @@ -428,7 +428,7 @@ class Login /** * Set options - * Current allowed + * Current allowed: * target : site target * debug * auto_login : self start login process diff --git a/www/vendor/egrajp/corelibs-composer-all/src/DB/IO.php b/www/vendor/egrajp/corelibs-composer-all/src/DB/IO.php index 9f878e96..aa681ea6 100644 --- a/www/vendor/egrajp/corelibs-composer-all/src/DB/IO.php +++ b/www/vendor/egrajp/corelibs-composer-all/src/DB/IO.php @@ -69,12 +69,16 @@ * - sets the SQL query (will be set with the $query parameter from method) * if u leave the parameter free the class will try to use this var, but this * method is not so reccomended +* $params +* - array for query parameters, if not set, ignored * $num_rows * - the number of rows returned by a SELECT or alterd bei UPDATE/INSERT * $num_fields * - the number of fields from the SELECT, is usefull if you do a SELECT * * $field_names * - array of field names (in the order of the return) +* $field_types +* - array of field types * $insert_id * - for INSERT with auto_increment PK, the ID is stored here * $error_msg @@ -258,7 +262,6 @@ use CoreLibs\Create\Uids; // below no ignore is needed if we want to use PgSql interface checks with PHP 8.0 // as main system. Currently all @var sets are written as object -/** @#phan-file-suppress PhanUndeclaredTypeProperty,PhanUndeclaredTypeParameter,PhanUndeclaredTypeReturnType */ class IO { @@ -286,10 +289,12 @@ class IO private $to_encoding = ''; /** @var string */ private $query; // the query string at the moment + /** @var array */ + private $params; // current params for query // only inside // basic vars /** @var \PgSql\Connection|false|null */ // replace object with PgSql\Connection| - private $dbh; // the dbh handler, if disconnected by command is null, bool:false/int:-1 on error, + private $dbh; // the dbh handler, if disconnected by command is null, bool:false on error, /** @var bool */ private $db_debug = false; // DB_DEBUG ... (if set prints out debug msgs) /** @var string */ @@ -321,8 +326,10 @@ class IO private $num_rows; // how many rows have been found /** @var int */ private $num_fields; // how many fields has the query - /** @var array */ - private $field_names = []; // array with the field names of the current query + /** @var array array with the field names of the current query */ + private $field_names = []; + /** @var array field type names */ + private $field_types = []; /** @var array */ private $insert_id_arr = []; // always return as array, even if only one /** @var string */ @@ -648,17 +655,20 @@ class IO /** * calls the basic class debug with strip command - * @param string $debug_id group id for debug - * @param string $error_string error message or debug data - * @param string $id db debug group - * @param string $type query identifier (Q, I, etc) - * @return void has no return + * @param string $debug_id group id for debug + * @param string $error_string error message or debug data + * @param string $id db debug group + * @param string $type query identifier (Q, I, etc) + * @param array $error_data Optional error data as array + * Will be printed after main error string + * @return void */ protected function __dbDebug( string $debug_id, string $error_string, string $id = '', - string $type = '' + string $type = '', + array $error_data = [] ): void { // NOTE prefix allows html for echo output, will be stripped on file print $prefix = ''; @@ -679,6 +689,11 @@ class IO if ($prefix) { $prefix .= '- '; } + if ($error_data !== []) { + $error_string .= '
[' + . $this->log->prAr($error_data) + . ']'; + } $this->log->debug($debug_id, $error_string, true, $prefix); } @@ -860,20 +875,31 @@ class IO /** * for debug purpose replaces $1, $2, etc with actual data - * @param string $stm_name prepared statement name - * @param array $data the data array - * @return string string of query with data inside + * @param string $query Query to replace values in + * @param array $data The data array + * @return string string of query with data inside */ - private function __dbDebugPrepare(string $stm_name, array $data = []): string + private function __dbDebugPrepare(string $query, array $data = []): string { + // skip anything if there is no data + if ($data === []) { + return $query; + } // get the keys from data array $keys = array_keys($data); - // because the placeholders start with $ and at 1, we need to increase each key and prefix it with a $ char + // because the placeholders start with $ and at 1, + // we need to increase each key and prefix it with a $ char for ($i = 0, $iMax = count($keys); $i < $iMax; $i++) { $keys[$i] = '$' . ($keys[$i] + 1); + // prefix data set with parameter pos + $data[$i] = $keys[$i] . ':' . $data[$i]; } // simply replace the $1, $2, ... with the actual data and return it - return str_replace(array_reverse($keys), array_reverse($data), $this->prepare_cursor[$stm_name]['query']); + return str_replace( + array_reverse($keys), + array_reverse($data), + $query + ); } /** @@ -977,6 +1003,33 @@ class IO return $return; } + /** + * Checks if the placeholder count in the query matches the params given + * on call + * + * @param string $query Query to check + * @param int $params_count The parms count expected + * @return bool True for params count ok, else false + */ + private function __dbCheckQueryParams(string $query, int $params_count): bool + { + // search for $1, $2, in the query and push it into the control array + // skip counts for same eg $1, $1, $2 = 2 and not 3 + preg_match_all('/(\$[0-9]{1,})/', $query, $match); + $placeholder_count = count(array_unique($match[1])); + if ($params_count != $placeholder_count) { + $this->__dbError( + 23, + false, + 'Array data count does not match prepared fields. Need: ' + . $placeholder_count . ', has: ' + . $params_count + ); + return false; + } + return true; + } + /** * sub function for dbExec and dbExecAsync * - checks query is set @@ -985,13 +1038,17 @@ class IO * - checks for insert if returning is set/pk name * - sets internal hash for query * - checks multiple call count - * @param string $query query string - * @param string $pk_name primary key - * [if set to NULL no returning will be added] - * @return string|false queryt hash OR bool false on error + * @param string $query Query string + * @param array $params Query params, needed for hash creation + * @param string $pk_name primary key + * [if set to NULL no returning will be added] + * @return string|false queryt hash OR bool false on error */ - private function __dbPrepareExec(string $query, string $pk_name): string|false - { + private function __dbPrepareExec( + string $query, + array $params, + string $pk_name + ): string|false { // reset current cursor before exec $this->cursor = false; // clear matches for regex lookups @@ -1001,6 +1058,8 @@ class IO $this->returning_id = false; // set the query $this->query = $query; + // current params + $this->params = $params; // no query set if (empty($this->query)) { $this->__dbError(11); @@ -1066,10 +1125,18 @@ class IO // $this->debug('DB IO', 'Q: '.$this->query.', RETURN: '.$this->returning_id); // for DEBUG, only on first time ;) if ($this->db_debug) { - $this->__dbDebug('db', $this->query, '__dbPrepareExec', 'Q'); + $this->__dbDebug( + 'db', + $this->__dbDebugPrepare( + $this->query, + $this->params + ), + '__dbPrepareExec', + ($this->params === [] ? 'Q' : 'Qp'), + ); } // import protection, hash needed - $query_hash = $this->dbGetQueryHash($this->query); + $query_hash = $this->dbGetQueryHash($this->query, $this->params); // if the array index does not exists set it 0 if (!array_key_exists($query_hash, $this->query_called)) { $this->query_called[$query_hash] = 0; @@ -1121,7 +1188,11 @@ class IO // set field names $this->field_names = []; for ($i = 0; $i < $this->num_fields; $i++) { - $this->field_names[] = $this->db_functions->__dbFieldName($this->cursor, $i); + $this->field_names[] = $this->db_functions->__dbFieldName($this->cursor, $i) ?: ''; + } + $this->field_types = []; + for ($i = 0; $i < $this->num_fields; $i++) { + $this->field_types[] = $this->db_functions->__dbFieldType($this->cursor, $i) ?: ''; } } elseif ($this->__checkQueryForInsert($this->query)) { // if not select do here @@ -1745,6 +1816,8 @@ class IO * the stored array will be deleted ... * - if set to 3, after EACH row, the data will be reset, * no caching is done except for basic (count, etc) + * Wrapper for dbReturnParams + * * @param string $query Query string * @param int $cache reset status: default: USE_CACHE * USE_CACHE/0: normal read from cache on second run @@ -1760,6 +1833,41 @@ class IO string $query, int $cache = self::USE_CACHE, bool $assoc_only = false + ): array|false { + return $this->dbReturnParams($query, [], $cache, $assoc_only); + } + + /** + * single running function, if called creates hash from + * query string and so can itself call exec/return calls + * caches data, so next time called with IDENTICAL (!!!!) + * [this means 1:1 bit to bit identical query] returns cached + * data, or with reset flag set calls data from DB again + * NOTE on $cache param: + * - if set to 0, if same query run again, will read from cache + * - if set to 1, the data will be read new and cached, cache reset a new run + * (wheres 1 reads cache AND destroys at end of read) + * - if set to 2, at the end of the query (last row returned), + * the stored array will be deleted ... + * - if set to 3, after EACH row, the data will be reset, + * no caching is done except for basic (count, etc) + * + * @param string $query Query string + * @param array $params Query parameters + * @param int $cache reset status: default: USE_CACHE + * USE_CACHE/0: normal read from cache on second run + * READ_NEW/1: write to cache, clean before new run + * CLEAR_CACHE/2: write cache, clean after finished + * NO_CACHE/3: don't write cache + * @param bool $assoc_only True to only returned the named and not + * index position ones + * @return array|false return array data or false on error/end + */ + public function dbReturnParams( + string $query, + array $params = [], + int $cache = self::USE_CACHE, + bool $assoc_only = false ): array|false { $this->__dbErrorReset(); if (!$query) { @@ -1767,7 +1875,7 @@ class IO return false; } // create hash from query ... - $query_hash = $this->dbGetQueryHash($query); + $query_hash = $this->dbGetQueryHash($query, $params); // pre declare array if (!isset($this->cursor_ext[$query_hash])) { $this->cursor_ext[$query_hash] = [ @@ -1787,6 +1895,8 @@ class IO 'pos' => 0, // the query used in this call 'query' => '', + // parameter + 'params' => [], // cache flag from method call 'cache_flag' => $cache, // flag if we only have assoc data @@ -1816,6 +1926,13 @@ class IO $this->__dbError(17, false, $this->cursor_ext[$query_hash]['query']); return false; } + // set the query parameters + $this->cursor_ext[$query_hash]['params'] = $params; + // check if params count matches + // checks if the params count given matches the expected count + if ($this->__dbCheckQueryParams($query, count($params)) === false) { + return false; + } // set first call to false $first_call = false; // init return als false @@ -1851,8 +1968,18 @@ class IO if (!$this->__dbCheckConnectionOk()) { return false; } - $this->cursor_ext[$query_hash]['cursor'] = - $this->db_functions->__dbQuery($this->cursor_ext[$query_hash]['query']); + if ($this->cursor_ext[$query_hash]['params'] === []) { + $this->cursor_ext[$query_hash]['cursor'] = + $this->db_functions->__dbQuery( + $this->cursor_ext[$query_hash]['query'] + ); + } else { + $this->cursor_ext[$query_hash]['cursor'] = + $this->db_functions->__dbQueryParams( + $this->cursor_ext[$query_hash]['query'], + $this->cursor_ext[$query_hash]['params'] + ); + } // if still no cursor ... if (!$this->cursor_ext[$query_hash]['cursor']) { if ($this->db_debug) { @@ -1889,6 +2016,16 @@ class IO ); } $this->field_names = $this->cursor_ext[$query_hash]['field_names']; + // field types + $this->cursor_ext[$query_hash]['field_types'] = []; + for ($i = 0; $i < $this->cursor_ext[$query_hash]['num_fields']; $i++) { + $this->cursor_ext[$query_hash]['field_types'][] = + $this->db_functions->__dbFieldType( + $this->cursor_ext[$query_hash]['cursor'], + $i + ); + } + $this->field_types = $this->cursor_ext[$query_hash]['field_types']; // reset first call var $first_call = false; // reset the internal pos counter @@ -2009,9 +2146,10 @@ class IO * like num_rows, num_fields, etc depending on query * for INSERT INTO queries it is highly recommended to set the pk_name to avoid an * additional read from the database for the PK NAME + * Wrapper for dbExecParams without params * @param string $query the query, if not given, * the query class var will be used - * if this was not set, method will quit with a 0 (failure) + * if this was not set, method will quit with false * @param string $pk_name optional primary key name, for insert id * return if the pk name is very different * if pk name is table name and _id, pk_name @@ -2019,16 +2157,50 @@ class IO * if NULL is given here, no RETURNING will be auto added * @return \PgSql\Result|false cursor for this query or false on error */ - public function dbExec(string $query = '', string $pk_name = ''): \PgSql\Result|false - { + public function dbExec( + string $query = '', + string $pk_name = '' + ): \PgSql\Result|false { + // just calls the same without any params + // which will trigger normal pg_query call + return $this->dbExecParams($query, [], $pk_name); + } + + /** + * Execute any query, but with the use of placeholders + * + * @param string $query Query, if not given, query class var will be used + * if this was not set, method will quit with false + * @param array $params Parameters to be replaced. + * NOTE: bytea data cannot be used here (pg_query_params) + * @param string $pk_name Optional primary key name, for insert id + * return if the pk name is very different + * if pk name is table name and _id, pk_name + * is not needed to be set + * if NULL is given here, no RETURNING will be auto added + * @return \PgSql\Result|false cursor for this query or false on error + */ + public function dbExecParams( + string $query = '', + array $params = [], + string $pk_name = '' + ): \PgSql\Result|false { $this->__dbErrorReset(); // prepare and check if we can actually run it - if ($this->__dbPrepareExec($query, $pk_name) === false) { + if ($this->__dbPrepareExec($query, $params, $pk_name) === false) { // bail if no query hash set return false; } + // checks if the params count given matches the expected count + if ($this->__dbCheckQueryParams($query, count($params)) === false) { + return false; + } // ** actual db exec call - $cursor = $this->db_functions->__dbQuery($this->query); + if ($params === []) { + $cursor = $this->db_functions->__dbQuery($this->query); + } else { + $cursor = $this->db_functions->__dbQueryParams($this->query, $params); + } // if we faield, just set the master cursors to false too $this->cursor = $cursor; if ($cursor === false) { @@ -2079,12 +2251,30 @@ class IO /** * returns the FIRST row of the given query + * wrapper for dbReturnRowParms * @param string $query the query to be executed * @param bool $assoc_only if true, only return assoc entry (default false) * @return array|false row array or false on error */ public function dbReturnRow(string $query, bool $assoc_only = false): array|false { + return $this->dbReturnRowParams($query, [], $assoc_only); + } + + /** + * Returns the first row only for the given query + * Uses db_query_params + * + * @param string $query the query to be executed + * @param array $params params to be used in query + * @param bool $assoc_only if true, only return assoc entry (default false) + * @return array|false row array or false on error + */ + public function dbReturnRowParams( + string $query, + array $params = [], + bool $assoc_only = false + ): array|false { $this->__dbErrorReset(); if (!$query) { $this->__dbError(11); @@ -2096,7 +2286,11 @@ class IO $this->__dbError(17, false, $query); return false; } - $cursor = $this->dbExec($query); + // checks if the params count given matches the expected count + if ($this->__dbCheckQueryParams($query, count($params)) === false) { + return false; + } + $cursor = $this->dbExecParams($query, $params); if ($cursor === false) { return false; } @@ -2105,13 +2299,31 @@ class IO } /** - * createds an array of hashes of the query (all data) + * creates an array of hashes of the query (all data) + * Wrapper for dbReturnArrayParams * @param string $query the query to be executed * @param bool $assoc_only if true, only name ref are returned (default true) * @return array|false array of hashes (row -> fields), false on error */ public function dbReturnArray(string $query, bool $assoc_only = true): array|false { + return $this->dbReturnArrayParams($query, [], $assoc_only); + } + + /** + * Creates an array of hashes of all data returned from the query + * uses db_query_param + * + * @param string $query the query to be executed + * @param array $params params to be used in query + * @param bool $assoc_only if true, only name ref are returned (default true) + * @return array|false array of hashes (row -> fields), false on error + */ + public function dbReturnArrayParams( + string $query, + array $params = [], + bool $assoc_only = true + ): array|false { $this->__dbErrorReset(); if (!$query) { $this->__dbError(11); @@ -2122,17 +2334,16 @@ class IO $this->__dbError(17, false, $query); return false; } - $cursor = $this->dbExec($query); + // checks if the params count given matches the expected count + if ($this->__dbCheckQueryParams($query, count($params)) === false) { + return false; + } + $cursor = $this->dbExecParams($query, $params); if ($cursor === false) { return false; } $rows = []; while (is_array($res = $this->dbFetchArray($cursor, $assoc_only))) { - // $data = []; - // for ($i = 0; $i < $this->num_fields; $i++) { - // $data[$this->field_names[$i]] = $res[$this->field_names[$i]] ?? null; - // } - // $rows[] = $data; $rows[] = $res; } return $rows; @@ -2144,13 +2355,15 @@ class IO /** * resets all data stored to this query - * @param string $query The Query whose cache should be cleaned - * @return bool false if query not found, true if success + * @param string $query The Query whose cache should be cleaned + * @param array $params If the query is params type we need params + * data to create a unique call one, optional + * @return bool False if query not found, true if success */ - public function dbCacheReset(string $query): bool + public function dbCacheReset(string $query, array $params = []): bool { $this->__dbErrorReset(); - $query_hash = $this->dbGetQueryHash($query); + $query_hash = $this->dbGetQueryHash($query, $params); // clears cache for this query if (empty($this->cursor_ext[$query_hash]['query'])) { $this->__dbError(18); @@ -2168,24 +2381,27 @@ class IO * returns the full array for cursor ext * or cursor for one query * or detail data fonr one query cursor data - * @param string|null $query Query string, if not null convert to hash - * and return set cursor ext for only this - * if not found or null return null - * @param string $query_field [=''] optional query field to get + * + * @param string|null $query Query string, if not null convert to hash + * and return set cursor ext for only this + * if not found or null return null + * @param array $params Optional params for query hash get + * @param string $query_field [=''] optional query field to get * @return array|string|int|\PgSql\Result|null - * Cursor Extended array full if no parameter - * Key is hash string from query run - * Or cursor data entry if query field is set - * If nothing found return null + * Cursor Extended array full if no parameter + * Key is hash string from query run + * Or cursor data entry if query field is set + * If nothing found return null */ public function dbGetCursorExt( - $query = null, + ?string $query = null, + array $params = [], string $query_field = '' ): array|string|int|\PgSql\Result|null { if ($query === null) { return $this->cursor_ext; } - $query_hash = $this->dbGetQueryHash($query); + $query_hash = $this->dbGetQueryHash($query, $params); if ( is_array($this->cursor_ext) && isset($this->cursor_ext[$query_hash]) @@ -2202,33 +2418,39 @@ class IO /** * returns the current position the read out - * @param string $query query to find in cursor_ext - * @return int|false query position (row pos), false on error + * + * @param string $query Query to find in cursor_ext + * @param array $params If the query is params type we need params + * data to create a unique call one, optional + * @return int|false query position (row pos), false on error */ - public function dbGetCursorPos(string $query): int|false + public function dbGetCursorPos(string $query, array $params = []): int|false { $this->__dbErrorReset(); if (!$query) { $this->__dbError(11); return false; } - $query_hash = $this->dbGetQueryHash($query); + $query_hash = $this->dbGetQueryHash($query, $params); return (int)$this->cursor_ext[$query_hash]['pos']; } /** * returns the number of rows for the current select query - * @param string $query query to find in cursor_ext - * @return int|false query position (row pos), false on error + * + * @param string $query Query to find in cursor_ext + * @param array $params If the query is params type we need params + * data to create a unique call one, optional + * @return int|false query position (row pos), false on error */ - public function dbGetCursorNumRows(string $query): int|false + public function dbGetCursorNumRows(string $query, array $params = []): int|false { $this->__dbErrorReset(); if (!$query) { $this->__dbError(11); return false; } - $query_hash = $this->dbGetQueryHash($query); + $query_hash = $this->dbGetQueryHash($query, $params); return (int)$this->cursor_ext[$query_hash]['num_rows']; } @@ -2239,22 +2461,27 @@ class IO /** * resets the call times for the max query called to 0 * USE CAREFULLY: rather make the query prepare -> execute - * @param string $query query string - * @return void has no return + * + * @param string $query query string + * @param array $params If the query is params type we need params + * data to create a unique call one, optional + * @return void */ - public function dbResetQueryCalled(string $query): void + public function dbResetQueryCalled(string $query, array $params = []): void { - $this->query_called[$this->dbGetQueryHash($query)] = 0; + $this->query_called[$this->dbGetQueryHash($query, $params)] = 0; } /** * gets how often a query was called already - * @param string $query query string - * @return int count of times the query was executed + * @param string $query query string + * @param array $params If the query is params type we need params + * data to create a unique call one, optional + * @return int count of times the query was executed */ - public function dbGetQueryCalled(string $query): int + public function dbGetQueryCalled(string $query, array $params = []): int { - $query_hash = $this->dbGetQueryHash($query); + $query_hash = $this->dbGetQueryHash($query, $params); if (!empty($this->query_called[$query_hash])) { return $this->query_called[$query_hash]; } else { @@ -2268,7 +2495,8 @@ class IO /** * prepares a query - * for INSERT INTO queries it is highly recommended to set the pk_name to avoid an additional + * for INSERT INTO queries it is highly recommended + * to set the pk_name to avoid an additional * read from the database for the PK NAME * @param string $stm_name statement name * @param string $query queryt string to run @@ -2432,7 +2660,15 @@ class IO return false; } if ($this->db_debug) { - $this->__dbDebug('db', $this->__dbDebugPrepare($stm_name, $data), 'dbExecPrep', 'Q'); + $this->__dbDebug( + 'db', + $this->__dbDebugPrepare( + $this->prepare_cursor[$stm_name]['query'], + $data + ), + 'dbExecPrep', + 'Qp' + ); } $result = $this->db_functions->__dbExecute($stm_name, $data); if ($result === false) { @@ -2471,27 +2707,58 @@ class IO // *************************** /** - * executres the query async so other methods can be run during this - * for INSERT INTO queries it is highly recommended to set the pk_name - * to avoid an additional read from the database for the PK NAME + * executes the query async so other methods can be run at the same time + * Wrapper for dbExecParamsAsync * NEEDS : dbCheckAsync * @param string $query query to run * @param string $pk_name optional primary key name, only used with * insert for returning call * @return bool true if async query was sent ok, - * false if error happened + * false on error */ public function dbExecAsync(string $query, string $pk_name = ''): bool { + return $this->dbExecParamsAsync($query, [], $pk_name); + } + + /** + * eexecutes the query async so other methods can be run at the same time + * Runs with db_send_query_params + * NEEDS : dbCheckAsync + * + * @param string $query query to run + * @param array $params + * @param string $pk_name optional primary key name, only used with + * insert for returning call + * @return bool true if async query was sent ok, + * false on error + */ + public function dbExecParamsAsync( + string $query, + array $params = [], + string $pk_name = '' + ): bool { $this->__dbErrorReset(); // prepare and check if we can actually run the query - if (($query_hash = $this->__dbPrepareExec($query, $pk_name)) === false) { + if ( + ($query_hash = $this->__dbPrepareExec($query, $params, $pk_name)) === false + ) { // bail if no hash set return false; } + // checks if the params count given matches the expected count + if ($this->__dbCheckQueryParams($query, count($params)) === false) { + return false; + } + // ** actual db exec call + if ($params === []) { + $status = $this->db_functions->__dbSendQuery($this->query); + } else { + $status = $this->db_functions->__dbSendQueryParams($this->query, $params); + } // run the async query, this just returns true or false // the actually result is in dbCheckAsync - if (!$this->db_functions->__dbSendQuery($this->query)) { + if (!$status) { // if failed, process here $this->__dbError(40); return false; @@ -2503,6 +2770,42 @@ class IO } } + /** + * TODO write dbPrepareAsync + * Asnychronus prepare call + * NEEDS : dbCheckAsync + * + * @param string $stm_name + * @param string $query + * @param string $pk_name + * @return bool + */ + public function dbPrepareAsync( + string $stm_name, + string $query, + string $pk_name = '' + ): bool { + $status = $this->db_functions->__dbSendPrepare($stm_name, $query); + return $status; + } + + /** + * TODO write dbExecuteAsync + * Asynchronus execute call + * NEEDS : dbCheckAsync + * + * @param string $stm_name + * @param array $data + * @return bool + */ + public function dbExecuteAsync( + string $stm_name, + array $data = [] + ): bool { + $status = $this->db_functions->__dbSendExecute($stm_name, $data); + return $status; + } + /** * checks a previous async query and returns data if finished * NEEDS : dbExecAsync @@ -2953,6 +3256,7 @@ class IO /** * Return current database handler + * * @return \PgSql\Connection|false|null */ public function dbGetDbh(): \PgSql\Connection|false|null @@ -2963,16 +3267,25 @@ class IO /** * Returns hash for query * Hash is used in all internal storage systems for return data - * @param string $query The query to create the hash from - * @return string Hash, as set by hash lpng + * + * @param string $query The query to create the hash from + * @param array $params If the query is params type we need params + * data to create a unique call one, optional + * @return string Hash, as set by hash long */ - public function dbGetQueryHash(string $query): string + public function dbGetQueryHash(string $query, array $params = []): string { - return Hash::__hashLong($query); + return Hash::__hashLong( + $query . ( + $params !== [] ? + '#' . json_encode($params) : '' + ) + ); } /** * Get current set query + * * @return string Current set query string */ public function dbGetQuery(): string @@ -2982,6 +3295,7 @@ class IO /** * Clear current query + * * @return void */ public function dbResetQuery(): void @@ -2989,6 +3303,26 @@ class IO $this->query = ''; } + /** + * Get current set params + * + * @return array + */ + public function dbGetParams(): array + { + return $this->params; + } + + /** + * Rset current set params + * + * @return void + */ + public function dbResetParams(): void + { + $this->params = []; + } + // *************************** // INTERNAL VARIABLES READ POST QUERY RUN // *************************** @@ -3031,12 +3365,14 @@ class IO * * Replacement for insert_id_ext array access before * - * @param string|null $key - * @param integer|null $pos - * @return array|string|int|null + * @param string|null $key Key to find in insert_id_arr + * @param integer|null $pos Multiple in array, which row to search in + * @return array|string|int|null Return value, null for error/not found */ - public function dbGetReturningExt(?string $key = null, ?int $pos = null): array|string|int|null - { + public function dbGetReturningExt( + ?string $key = null, + ?int $pos = null + ): array|string|int|null { // return as is if key is null if ($key === null) { if (count($this->insert_id_arr) == 1) { @@ -3080,6 +3416,7 @@ class IO /** * Always returns the returning block as an array + * * @return array All returning data as array. even if one row only */ public function dbGetReturningArray(): array @@ -3091,6 +3428,7 @@ class IO * returns current number of rows that where * affected by UPDATE/SELECT, etc * null on empty + * * @return int|null Number of rows or null if not set */ public function dbGetNumRows(): ?int @@ -3100,6 +3438,7 @@ class IO /** * Number of fields in select query + * * @return integer|null Number of fields in select or null if not set */ public function dbGetNumFields(): ?int @@ -3109,13 +3448,26 @@ class IO /** * Return field names from query - * @return array Field names as array + * Order based on order in query + * + * @return array Field names as array */ public function dbGetFieldNames(): array { return $this->field_names; } + /** + * Return field types from query + * Order based on order in query, use field names to get position + * + * @return array Field types as array + */ + public function dbGetFieldTypes(): array + { + return $this->field_types; + } + /** * Returns the value for given key in statement * Will write error if statemen id does not exist @@ -3129,8 +3481,10 @@ class IO * Not ethat returnin_id also can return false * but will not set an error entry */ - public function dbGetPrepareCursorValue(string $stm_name, string $key): null|string|int|bool - { + public function dbGetPrepareCursorValue( + string $stm_name, + string $key + ): null|string|int|bool { // if no statement name if (empty($stm_name)) { $this->__dbError( diff --git a/www/vendor/egrajp/corelibs-composer-all/src/DB/SQL/Interface/SqlFunctions.php b/www/vendor/egrajp/corelibs-composer-all/src/DB/SQL/Interface/SqlFunctions.php index 36ef0f51..5aa7fdb5 100644 --- a/www/vendor/egrajp/corelibs-composer-all/src/DB/SQL/Interface/SqlFunctions.php +++ b/www/vendor/egrajp/corelibs-composer-all/src/DB/SQL/Interface/SqlFunctions.php @@ -42,6 +42,15 @@ interface SqlFunctions */ public function __dbSendQuery(string $query): bool; + /** + * Undocumented function + * + * @param string $query + * @param array $params + * @return bool + */ + public function __dbSendQueryParams(string $query, array $params): bool; + /** * Undocumented function * @@ -74,6 +83,24 @@ interface SqlFunctions */ public function __dbExecute(string $name, array $data): \PgSql\Result|false; + /** + * Undocumented function + * + * @param string $name + * @param string $query + * @return bool + */ + public function __dbSendPrepare(string $name, string $query): bool; + + /** + * Undocumented function + * + * @param string $name + * @param array $params + * @return bool + */ + public function __dbSendExecute(string $name, array $params): bool; + /** * Undocumented function * @@ -99,6 +126,15 @@ interface SqlFunctions */ public function __dbFieldName(\PgSql\Result|false $cursor, int $i): string|false; + /** + * Undocumented function + * + * @param \PgSql\Result|false $cursor + * @param int $i + * @return string|false + */ + public function __dbFieldType(\PgSql\Result|false $cursor, int $i): string|false; + /** * Undocumented function * diff --git a/www/vendor/egrajp/corelibs-composer-all/src/DB/SQL/PgSQL.php b/www/vendor/egrajp/corelibs-composer-all/src/DB/SQL/PgSQL.php index b9da6d3a..9f42f76a 100644 --- a/www/vendor/egrajp/corelibs-composer-all/src/DB/SQL/PgSQL.php +++ b/www/vendor/egrajp/corelibs-composer-all/src/DB/SQL/PgSQL.php @@ -33,7 +33,11 @@ * pg_affected_rows (*) * pg_fetch_array * pg_query +* pg_query_params * pg_send_query +* pg_send_query_params +* pg_send_prepare +* pg_send_execute * pg_get_result * pg_connection_busy * pg_close @@ -50,6 +54,7 @@ namespace CoreLibs\DB\SQL; // below no ignore is needed if we want to use PgSql interface checks with PHP 8.0 // as main system. Currently all @var sets are written as object /** @#phan-file-suppress PhanUndeclaredTypeProperty,PhanUndeclaredTypeParameter,PhanUndeclaredTypeReturnType */ +/** @phan-file-suppress PhanTypeMismatchArgumentInternal, PhanTypeMismatchReturn */ class PgSQL implements Interface\SqlFunctions { @@ -93,8 +98,7 @@ class PgSQL implements Interface\SqlFunctions } /** - * Proposed - * wrapperf or pg_query_params for queries in the style of + * wrapper for pg_query_params for queries in the style of * SELECT foo FROM bar WHERE foobar = $1 * * @param string $query Query string with placeholders $1, .. @@ -132,6 +136,22 @@ class PgSQL implements Interface\SqlFunctions return $result ? true : false; } + /** + * sends an async query to the server with params + * + * @param string $query Query string with placeholders $1, .. + * @param array $params Matching parameters for each placerhold + * @return bool true/false Query sent successful status + */ + public function __dbSendQueryParams(string $query, array $params): bool + { + if (is_bool($this->dbh)) { + return false; + } + $result = pg_send_query_params($this->dbh, $query, $params); + return $result ? true : false; + } + /** * wrapper for pg_get_result * @@ -208,6 +228,38 @@ class PgSQL implements Interface\SqlFunctions return $result; } + /** + * Asnyc send for a prepared statement + * + * @param string $name + * @param string $query + * @return bool + */ + public function __dbSendPrepare(string $name, string $query): bool + { + if (is_bool($this->dbh)) { + return false; + } + $result = pg_send_prepare($this->dbh, $name, $query); + return $result ? true : false; + } + + /** + * Asnyc ssend for a prepared statement execution + * + * @param string $name + * @param array $params + * @return bool + */ + public function __dbSendExecute(string $name, array $params): bool + { + if (is_bool($this->dbh)) { + return false; + } + $result = pg_send_execute($this->dbh, $name, $params); + return $result ? true : false; + } + /** * wrapper for pg_num_rows * @@ -251,6 +303,21 @@ class PgSQL implements Interface\SqlFunctions return pg_field_name($cursor, $i); } + /** + * wrapper for pg_field_name + * + * @param \PgSql\Result|false $cursor cursor + * @param int $i field position + * @return string|false field type name or false + */ + public function __dbFieldType(\PgSql\Result|false $cursor, int $i): string|false + { + if (is_bool($cursor)) { + return false; + } + return pg_field_type($cursor, $i); + } + /** * wrapper for pg_fetch_array * if through/true false, use __dbResultType(true) diff --git a/www/vendor/egrajp/corelibs-composer-all/src/Template/SmartyExtend.php b/www/vendor/egrajp/corelibs-composer-all/src/Template/SmartyExtend.php index 093bd830..9b2d916d 100644 --- a/www/vendor/egrajp/corelibs-composer-all/src/Template/SmartyExtend.php +++ b/www/vendor/egrajp/corelibs-composer-all/src/Template/SmartyExtend.php @@ -453,8 +453,8 @@ class SmartyExtend extends \Smarty * this is for frontend type and will not set any only admin needed variables * * @param array $options list with the following value: - * compile_dir :BASE . TEMPLATES_C - * cache_dir :BASE . CACHE + * compile_dir :BASE . TEMPLATES_C + * cache_dir :BASE . CACHE * js :JS * css :CSS * font :FONT @@ -462,17 +462,19 @@ class SmartyExtend extends \Smarty * g_title :G_TITLE * stylesheet :STYLESHEET * javascript :JAVASCRIPT - * @param \CoreLibs\Admin\Backend|null $cms Optinal Admin Backend for - * smarty variables merge + * @param array $smarty_data array of three keys + * that hold smarty set strings + * HEADER, DATA, DEBUG_DATA * @return void */ public function setSmartyVarsFrontend( array $options, - ?\CoreLibs\Admin\Backend $cms + array $smarty_data ): void { $this->setSmartyVars( false, - $cms, + $smarty_data, + null, $options['compile_dir'] ?? null, $options['cache_dir'] ?? null, $options['js'] ?? null, @@ -484,6 +486,7 @@ class SmartyExtend extends \Smarty null, null, null, + null, $options['stylesheet'] ?? null, $options['javascript'] ?? null ); @@ -493,8 +496,8 @@ class SmartyExtend extends \Smarty * wrapper call for setSmartyVars * this is only for admin interface and will set additional variables * @param array $options list with the following value: - * compile_dir :BASE . TEMPLATES_C - * cache_dir :BASE . CACHE + * compile_dir :BASE . TEMPLATES_C + * cache_dir :BASE . CACHE * js :JS * css :CSS * font :FONT @@ -503,6 +506,7 @@ class SmartyExtend extends \Smarty * admin_stylesheet :ADMIN_STYLESHEET * admin_javascript :ADMIN_JAVASCRIPT * page_width :PAGE_WIDTH + * content_path :CONTENT_PATH * user_name :_SESSION['USER_NAME'] * @param \CoreLibs\Admin\Backend|null $cms Optinal Admin Backend for * smarty variables merge @@ -512,8 +516,18 @@ class SmartyExtend extends \Smarty array $options, ?\CoreLibs\Admin\Backend $cms = null ): void { + // if we have cms data, check for array blocks and build + $smarty_data = []; + if ($cms !== null) { + $smarty_data = [ + 'HEADER' => $cms->HEADER, + 'DATA' => $cms->DATA, + 'DEBUG_DATA' => $cms->DEBUG_DATA + ]; + } $this->setSmartyVars( true, + $smarty_data, $cms, $options['compile_dir'] ?? null, $options['cache_dir'] ?? null, @@ -525,6 +539,7 @@ class SmartyExtend extends \Smarty $options['admin_stylesheet'] ?? null, $options['admin_javascript'] ?? null, $options['page_width'] ?? null, + $options['content_path'] ?? null, $options['user_name'] ?? null, null, null @@ -537,6 +552,7 @@ class SmartyExtend extends \Smarty * * @param bool $admin_call default false * will set admin only variables + * @param array $smarty_data smarty data to merge * @param \CoreLibs\Admin\Backend|null $cms Optinal Admin Backend for * smarty variables merge * @param string|null $compile_dir BASE . TEMPLATES_C @@ -549,6 +565,7 @@ class SmartyExtend extends \Smarty * @param string|null $set_admin_stylesheet ADMIN_STYLESHEET * @param string|null $set_admin_javascript ADMIN_JAVASCRIPT * @param string|null $set_page_width PAGE_WIDTH + * @param string|null $set_content_path CONTENT_PATH (only if $cms set and admin) * @param string|null $set_user_name _SESSION['USER_NAME'] * @param string|null $set_stylesheet STYLESHEET * @param string|null $set_javascript JAVASCRIPT @@ -556,6 +573,7 @@ class SmartyExtend extends \Smarty */ private function setSmartyVars( bool $admin_call, + array $smarty_data = [], ?\CoreLibs\Admin\Backend $cms = null, ?string $compile_dir = null, ?string $cache_dir = null, @@ -567,6 +585,7 @@ class SmartyExtend extends \Smarty ?string $set_admin_stylesheet = null, ?string $set_admin_javascript = null, ?string $set_page_width = null, + ?string $set_content_path = null, ?string $set_user_name = null, ?string $set_stylesheet = null, ?string $set_javascript = null, @@ -593,6 +612,9 @@ class SmartyExtend extends \Smarty $set_stylesheet === null || $set_javascript === null ) + ) || + ( + $admin_call === true && $cms !== null && $set_content_path === null ) ) { /** @deprecated setSmartyVars call without parameters */ @@ -612,25 +634,12 @@ class SmartyExtend extends \Smarty $set_admin_stylesheet = $set_admin_stylesheet ?? ADMIN_STYLESHEET; $set_admin_javascript = $set_admin_javascript ?? ADMIN_JAVASCRIPT; $set_page_width = $set_page_width ?? PAGE_WIDTH; + $set_content_path = $set_content_path ?? CONTENT_PATH; $set_stylesheet = $set_stylesheet ?? STYLESHEET; $set_javascript = $set_javascript ?? JAVASCRIPT; $set_user_name = $set_user_name ?? $_SESSION['USER_NAME'] ?? ''; - // depreacte call globals cms on null 4mcs - if ( - $cms === null && - isset($GLOBALS['cms']) - ) { - /** @deprecated setSmartyVars globals cms is deprecated */ - trigger_error( - 'Calling setSmartyVars without cms parameter when needed is deprecated', - E_USER_DEPRECATED - ); - } - // this is ugly - $cms = $cms ?? $GLOBALS['cms'] ?? null; - if ($cms instanceof \CoreLibs\Admin\Backend) { - $this->mergeCmsSmartyVars($cms); - } + // merge additional smarty data + $this->mergeCmsSmartyVars($smarty_data); // trigger flags $this->HEADER['USE_PROTOTYPE'] = $this->USE_PROTOTYPE; @@ -672,12 +681,27 @@ class SmartyExtend extends \Smarty $this->DATA['FORM_ACTION'] = $this->FORM_ACTION; // special for admin if ($admin_call === true) { + // depreacte call globals cms on null 4mcs + if ( + $cms === null && + isset($GLOBALS['cms']) + ) { + /** @deprecated setSmartyVars globals cms is deprecated */ + trigger_error( + 'Calling setSmartyVars without cms parameter when needed is deprecated', + E_USER_DEPRECATED + ); + } + // this is ugly + $cms = $cms ?? $GLOBALS['cms'] ?? null; // set ACL extra show if ($cms instanceof \CoreLibs\Admin\Backend) { $this->DATA['show_ea_extra'] = $cms->acl['show_ea_extra'] ?? false; $this->DATA['ADMIN'] = $cms->acl['admin'] ?? 0; // top menu - $this->DATA['nav_menu'] = $cms->adbTopMenu(); + $this->DATA['nav_menu'] = $cms->adbTopMenu( + $set_content_path + ); $this->DATA['nav_menu_count'] = count($this->DATA['nav_menu']); // messages = ['msg' =>, 'class' => 'error/warning/...'] $this->DATA['messages'] = $cms->messages; @@ -737,18 +761,18 @@ class SmartyExtend extends \Smarty /** * merge outside object HEADER/DATA/DEBUG_DATA vars into the smarty class * - * @param \CoreLibs\Admin\Backend $cms object that has header/data/debug_data + * @param array $smarty_data array that has header/data/debug_data * @return void */ - public function mergeCmsSmartyVars(\CoreLibs\Admin\Backend $cms): void + public function mergeCmsSmartyVars(array $smarty_data): void { // array merge HEADER, DATA, DEBUG DATA foreach (['HEADER', 'DATA', 'DEBUG_DATA'] as $ext_smarty) { if ( - isset($cms->{$ext_smarty}) && - is_array($cms->{$ext_smarty}) + isset($smarty_data[$ext_smarty]) && + is_array($smarty_data[$ext_smarty]) ) { - $this->{$ext_smarty} = array_merge($this->{$ext_smarty}, $cms->{$ext_smarty}); + $this->{$ext_smarty} = array_merge($this->{$ext_smarty}, $smarty_data[$ext_smarty]); } } } diff --git a/www/vendor/egrajp/corelibs-composer-all/test/phpunit/DB/CoreLibsDBIOTest.php b/www/vendor/egrajp/corelibs-composer-all/test/phpunit/DB/CoreLibsDBIOTest.php index c1908825..1a052255 100644 --- a/www/vendor/egrajp/corelibs-composer-all/test/phpunit/DB/CoreLibsDBIOTest.php +++ b/www/vendor/egrajp/corelibs-composer-all/test/phpunit/DB/CoreLibsDBIOTest.php @@ -1434,7 +1434,7 @@ final class CoreLibsDBIOTest extends TestCase } // - db exec test for insert/update/select/etc - // dbExec, dbResetQueryCalled, dbGetQueryCalled + // dbExec, dbExecParams, dbResetQueryCalled, dbGetQueryCalled /** * provide queries with return results @@ -1444,17 +1444,19 @@ final class CoreLibsDBIOTest extends TestCase public function queryDbExecProvider(): array { // 0: query - // 1: optional primary key name, null for empty test - // 2: expectes result (bool, object (>=8.1)/resource (<8.1)) - // 3: warning - // 4: error - // 5: run times, not set is once, true is max + 1 + // 1: params, null for force dbEXec + // 2: optional primary key name, null for empty test + // 3: expectes result (bool, object) + // 4: warning + // 5: error + // 6: run times, not set is once, true is max + 1 return [ // insert 'table with pk insert' => [ 'INSERT INTO table_with_primary_key (row_date) VALUES (NOW())', + null, '', - 'resource/object', + 'object', '', '', ], @@ -1462,15 +1464,44 @@ final class CoreLibsDBIOTest extends TestCase 'table with pk insert null' => [ 'INSERT INTO table_with_primary_key (row_date) VALUES (NOW())', null, - 'resource/object', + null, + 'object', + '', + '', + ], + // insert with params + 'table with pk insert params' => [ + 'INSERT INTO table_with_primary_key (row_varchar) VALUES ($1)', + ['test'], + '', + 'object', + '', + '', + ], + // insert with params, null primary key + 'table with pk insert params' => [ + 'INSERT INTO table_with_primary_key (row_varchar) VALUES ($1)', + ['test'], + null, + 'object', '', '', ], // insert to table with no pk (31?) 'table with no pk insert' => [ 'INSERT INTO table_without_primary_key (row_date) VALUES (NOW())', + null, '', - 'resource/object', + 'object', + '', + '', + ], + // insert to table with no pk (31?) with params + 'table with no pk insert params' => [ + 'INSERT INTO table_without_primary_key (row_varchar) VALUES ($1)', + ['test'], + null, + 'object', '', '', ], @@ -1481,50 +1512,73 @@ final class CoreLibsDBIOTest extends TestCase . '(NOW()), ' . '(NOW()), ' . '(NOW())', + null, '', - 'resource/object', + 'object', '32', '', ], // Skip PK READING 'table with pk insert and NULL pk name' => [ 'INSERT INTO table_with_primary_key (row_date) VALUES (NOW())', + null, 'NULL', - 'resource/object', + 'object', '', '', ], // insert with pk set 'table with pk insert and pk name' => [ 'INSERT INTO table_with_primary_key (row_date) VALUES (NOW())', + null, 'table_with_primary_key_id', - 'resource/object', + 'object', '', '', ], // update 'table with pk update' => [ 'UPDATE table_with_primary_key SET row_date = NOW()', + null, '', - 'resource/object', + 'object', '', '', ], 'table with pk select' => [ 'SELECT * FROM table_with_primary_key', + null, '', - 'resource/object', + 'object', '', '', ], // no query set, error 11 'no query set' => [ '', + null, '', false, '', '11', ], + // wrong params coutn for insert + 'wrong params count' => [ + 'INSERT INTO table_with_primary_key (row_varchar) VALUES ($1, $2)', + ['test'], + null, + false, + '', + '23' + ], + 'wrong params count, null params' => [ + 'INSERT INTO table_with_primary_key (row_varchar) VALUES ($1, $2)', + null, + null, + false, + '', + '23' + ], // no db connection setable (16) [needs Mocking] // TODO failed db connection // connection busy [async] (41) @@ -1532,8 +1586,9 @@ final class CoreLibsDBIOTest extends TestCase // same query run too many times (30) 'same query run too many times' => [ 'SELECT row_date FROM table_with_primary_key', + null, '', - 'resource/object', + 'object', '', '30', true, @@ -1541,6 +1596,7 @@ final class CoreLibsDBIOTest extends TestCase // execution failed (13) 'invalid query' => [ 'INVALID', + null, '', false, '', @@ -1555,6 +1611,7 @@ final class CoreLibsDBIOTest extends TestCase // FIX with socket check type 'invalid returning' => [ 'INSERT INTO table_with_primary_key (row_date) VALUES (NOW()) RETURNING invalid', + null, '', false, '', @@ -1564,19 +1621,21 @@ final class CoreLibsDBIOTest extends TestCase } /** - * pure dbExec checker + * pure dbExec/dbExecParams checker * does not check __dbPostExec run, this will be done in the dbGet* functions * tests (internal read data post exec group) * * @covers ::dbExec + * @covers ::dbExecParams * @covers ::dbGetQueryCalled * @covers ::dbResetQueryCalled * @dataProvider queryDbExecProvider * @testdox dbExec $query and pk $pk_name with $expected_return (Warning: $warning/Error: $error) [$_dataName] * * @param string $query + * @param array|null $params * @param string|null $pk_name - * @param object|resource|bool $expected_return + * @param object|bool $expected_return * @param string $warning * @param string $error * @param bool $run_many_times @@ -1584,6 +1643,7 @@ final class CoreLibsDBIOTest extends TestCase */ public function testDbExec( string $query, + ?array $params, ?string $pk_name, $expected_return, string $warning, @@ -1608,50 +1668,69 @@ final class CoreLibsDBIOTest extends TestCase ); // if expected result is not a bool - // for PHP 8.1 or higher it has to be an object - // for anything before PHP 8.1 this has to be a resource + // it has to be an object type PgSql\Result if (is_bool($expected_return)) { + // supress ANY errors here + if ($pk_name === null && $params === null) { + $result = @$db->dbExec($query); + } elseif ($params === null) { + $result = @$db->dbExec($query, $pk_name); + } elseif ($pk_name === null) { + $result = @$db->dbExecParams($query, $params); + } else { + $result = @$db->dbExecParams($query, $params, $pk_name); + } $this->assertEquals( $expected_return, - // supress ANY errors here - $pk_name === null ? - @$db->dbExec($query) : - @$db->dbExec($query, $pk_name) + $result ); } else { - $result = $pk_name === null ? - $db->dbExec($query) : - $db->dbExec($query, $pk_name); - // if PHP or newer, must be Object PgSql\Result - if (\CoreLibs\Check\PhpVersion::checkPHPVersion('8.1')) { - $this->assertIsObject( - $result - ); - // also check that this is correct instance type - $this->assertInstanceOf( - 'PgSql\Result', - $result - ); + if ($pk_name === null && $params === null) { + $result = $db->dbExec($query); + } elseif ($params === null) { + $result = $db->dbExec($query, $pk_name); + } elseif ($pk_name === null) { + $result = $db->dbExecParams($query, $params); } else { - $this->assertIsResource( - $result - ); + $result = $db->dbExecParams($query, $params, $pk_name); } + // if PHP or newer, must be Object PgSql\Result + $this->assertIsObject( + $result + ); + // also check that this is correct instance type + $this->assertInstanceOf( + 'PgSql\Result', + $result + ); } // if we have more than one run time // re-run same query and then catch error if ($run_many_times) { for ($i = 1; $i <= $db->dbGetMaxQueryCall() + 1; $i++) { - $pk_name === null ? - $db->dbExec($query) : + if ($pk_name === null && $params === null) { + $db->dbExec($query); + } elseif ($params === null) { $db->dbExec($query, $pk_name); + } elseif ($pk_name === null) { + $db->dbExecParams($query, $params); + } else { + $db->dbExecParams($query, $params, $pk_name); + } + } + if ($pk_name === null && $params === null) { + $result = $db->dbExec($query); + } elseif ($params === null) { + $result = $db->dbExec($query, $pk_name); + } elseif ($pk_name === null) { + $result = $db->dbExecParams($query, $params); + } else { + $result = $db->dbExecParams($query, $params, $pk_name); } // will fail now $this->assertFalse( - $pk_name === null ? - $db->dbExec($query) : - $db->dbExec($query, $pk_name) + $result ); // check query called matching $current_count = $db->dbGetQueryCalled($query); @@ -1688,16 +1767,19 @@ final class CoreLibsDBIOTest extends TestCase { $insert_query = "INSERT INTO table_with_primary_key (row_int, uid) VALUES (1, 'A')"; $read_query = "SELECT row_int, uid FROM table_with_primary_key WHERE uid = 'A'"; + $read_query_params = "SELECT row_int, uid FROM table_with_primary_key WHERE uid = $1"; // 0: query - // 1: flag (assoc) - // 2: result - // 3: warning - // 4: error - // 5: insert query + // 1: params (null) for other + // 2: flag (assoc) + // 3: result + // 4: warning + // 5: error + // 6: insert query return [ 'valid select' => [ $read_query, null, + null, [ 'row_int' => 1, 0 => 1, @@ -1710,6 +1792,7 @@ final class CoreLibsDBIOTest extends TestCase ], 'valid select, assoc only false' => [ $read_query, + null, false, [ 'row_int' => 1, @@ -1723,6 +1806,7 @@ final class CoreLibsDBIOTest extends TestCase ], 'valid select, assoc only true' => [ $read_query, + null, true, [ 'row_int' => 1, @@ -1732,9 +1816,36 @@ final class CoreLibsDBIOTest extends TestCase '', $insert_query, ], + // params read + 'valid select, params' => [ + $read_query_params, + [ + 'A' + ], + true, + [ + 'row_int' => 1, + 'uid' => 'A', + ], + '', + '', + $insert_query + ], + // errors + 'wrong params count' => [ + $read_query_params, + [], + true, + false, + '', + '23', + $insert_query + ], + // params, wrong count 'empty select' => [ '', null, + null, false, '', '11', @@ -1743,6 +1854,7 @@ final class CoreLibsDBIOTest extends TestCase 'insert query' => [ $insert_query, null, + null, false, '', '17', @@ -1756,10 +1868,12 @@ final class CoreLibsDBIOTest extends TestCase * Undocumented function * * @covers ::dbReturnRow + * @covers ::dbReturnRowParams * @dataProvider returnRowProvider * @testdox dbReturnRow $query and assoc $flag_assoc with $expected (Warning: $warning/Error: $error) [$_dataName] * * @param string $query + * @param array|null $params * @param bool|null $flag_assoc * @param array|bool $expected * @param string $warning @@ -1769,6 +1883,7 @@ final class CoreLibsDBIOTest extends TestCase */ public function testDbReturnRow( string $query, + ?array $params, ?bool $flag_assoc, $expected, string $warning, @@ -1783,12 +1898,20 @@ final class CoreLibsDBIOTest extends TestCase ); // insert data before we can test, from expected array $db->dbExec($insert_data); + // run + if ($flag_assoc === null && $params === null) { + $result = $db->dbReturnRow($query); + } elseif ($params === null) { + $result = $db->dbReturnRow($query, $flag_assoc); + } elseif ($flag_assoc === null) { + $result = $db->dbReturnRowParams($query, $params); + } else { + $result = $db->dbReturnRowParams($query, $params, $flag_assoc); + } // compare $this->assertEquals( $expected, - $flag_assoc === null ? - $db->dbReturnRow($query) : - $db->dbReturnRow($query, $flag_assoc) + $result ); // get last error/warnings // if string for warning or error is not empty check @@ -1814,16 +1937,19 @@ final class CoreLibsDBIOTest extends TestCase $insert_query = "INSERT INTO table_with_primary_key (row_int, uid) VALUES " . "(1, 'A'), (2, 'B')"; $read_query = "SELECT row_int, uid FROM table_with_primary_key"; + $read_query_params = "SELECT row_int, uid FROM table_with_primary_key WHERE uid = $1"; // 0: query - // 1: flag (assoc) - // 2: result - // 3: warning - // 4: error - // 5: insert query + // 1: params (null) for other + // 2: flag (assoc) + // 3: result + // 4: warning + // 5: error + // 6: insert query return [ 'valid select' => [ $read_query, null, + null, [ [ 'row_int' => 1, @@ -1840,6 +1966,7 @@ final class CoreLibsDBIOTest extends TestCase ], 'valid select, assoc ' => [ $read_query, + null, false, [ [ @@ -1859,9 +1986,37 @@ final class CoreLibsDBIOTest extends TestCase '', $insert_query, ], + // params read + 'valid select, params' => [ + $read_query_params, + [ + 'A' + ], + true, + [ + [ + 'row_int' => 1, + 'uid' => 'A', + ] + ], + '', + '', + $insert_query + ], + // errors + 'wrong params count' => [ + $read_query_params, + [], + true, + false, + '', + '23', + $insert_query + ], 'empty select' => [ '', null, + null, false, '', '11', @@ -1870,6 +2025,7 @@ final class CoreLibsDBIOTest extends TestCase 'insert query' => [ $insert_query, null, + null, false, '', '17', @@ -1887,6 +2043,7 @@ final class CoreLibsDBIOTest extends TestCase * @testdox dbReturnArray $query and assoc $flag_assoc with $expected (Warning: $warning/Error: $error) [$_dataName] * * @param string $query + * @param array|null $params * @param boolean|null $flag_assoc * @param array|bool $expected * @param string $warning @@ -1894,8 +2051,9 @@ final class CoreLibsDBIOTest extends TestCase * @param string $insert_data * @return void */ - public function testDbReturnArrray( + public function testDbReturnArray( string $query, + ?array $params, ?bool $flag_assoc, $expected, string $warning, @@ -1910,12 +2068,20 @@ final class CoreLibsDBIOTest extends TestCase ); // insert data before we can test, from expected array $db->dbExec($insert_data); + // run + if ($flag_assoc === null && $params === null) { + $result = $db->dbReturnArray($query); + } elseif ($params === null) { + $result = $db->dbReturnArray($query, $flag_assoc); + } elseif ($flag_assoc === null) { + $result = $db->dbReturnArrayParams($query, $params); + } else { + $result = $db->dbReturnArrayParams($query, $params, $flag_assoc); + } // compare $this->assertEquals( $expected, - $flag_assoc === null ? - $db->dbReturnArray($query) : - $db->dbReturnArray($query, $flag_assoc) + $result ); // get last error/warnings // if string for warning or error is not empty check @@ -1941,6 +2107,7 @@ final class CoreLibsDBIOTest extends TestCase $insert_query = "INSERT INTO table_with_primary_key (row_int, uid) VALUES " . "(1, 'A'), (2, 'B')"; $read_query = "SELECT row_int, uid FROM table_with_primary_key"; + $read_query_params = "SELECT row_int, uid FROM table_with_primary_key WHERE uid = $1"; $row_a = [ 'row_int' => 1, 0 => 1, @@ -1962,27 +2129,32 @@ final class CoreLibsDBIOTest extends TestCase 'uid' => 'B', ]; // 0: read query - // 1: reset flag, null for default - // 2: assoc flag, null for default - // 3: expected return (cursor_ext/data) - // 4: step through, or normal loop read - // 5: cursor ext compare array - // 6: first only, extended cursor (for each step) - // 7: warning - // 8: error - // 9: insert data + // 1: params, null for not used + // 2: reset flag, null for default + // 3: assoc flag, null for default + // 4: expected return (cursor_ext/data) + // 5: step through, or normal loop read + // 6: cursor ext compare array + // 7*: first only, extended cursor (for each step) [not implemented yet] + // 8: warning + // 9: error + // 10: insert data return [ // *** READ STEP BY STEP // default cache: USE_CACHE 'valid select, default cache settings' => [ + // 0-3 $read_query, null, null, + null, + // 4 $row_a, + // 5 true, + // 6 // check cursor_ext [ - // if <8.1 check against resource 'cursor' => 'PgSql\Result', 'data' => [ 0 => $row_a, @@ -1991,10 +2163,15 @@ final class CoreLibsDBIOTest extends TestCase 'row_int', 'uid' ], + 'field_types' => [ + 'int4', + 'varchar' + ], 'num_fields' => 2, 'num_rows' => 2, 'pos' => 1, 'query' => $read_query, + 'params' => [], 'read_rows' => 1, 'cache_flag' => \CoreLibs\DB\IO::USE_CACHE, 'assoc_flag' => false, @@ -2003,6 +2180,7 @@ final class CoreLibsDBIOTest extends TestCase 'read_finished' => false, 'db_read_finished' => false, ], + // 7 // extended cursor per step (first only true) [ // second row @@ -2020,10 +2198,15 @@ final class CoreLibsDBIOTest extends TestCase 'row_int', 'uid' ], + 'field_types' => [ + 'int4', + 'varchar' + ], 'num_fields' => 2, 'num_rows' => 2, 'pos' => 2, 'query' => $read_query, + 'params' => [], 'read_rows' => 2, 'cache_flag' => \CoreLibs\DB\IO::USE_CACHE, 'assoc_flag' => false, @@ -2046,10 +2229,15 @@ final class CoreLibsDBIOTest extends TestCase 'row_int', 'uid' ], + 'field_types' => [ + 'int4', + 'varchar' + ], 'num_fields' => 2, 'num_rows' => 2, 'pos' => 0, 'query' => $read_query, + 'params' => [], 'read_rows' => 2, 'cache_flag' => \CoreLibs\DB\IO::USE_CACHE, 'assoc_flag' => false, @@ -2060,12 +2248,15 @@ final class CoreLibsDBIOTest extends TestCase ] ] ], + // warn/error '', '', + // insert $insert_query ], 'valid select, use cache, assoc only' => [ $read_query, + null, \CoreLibs\DB\IO::USE_CACHE, true, $row_a_assoc, @@ -2080,10 +2271,15 @@ final class CoreLibsDBIOTest extends TestCase 'row_int', 'uid' ], + 'field_types' => [ + 'int4', + 'varchar' + ], 'num_fields' => 2, 'num_rows' => 2, 'pos' => 1, 'query' => $read_query, + 'params' => [], 'read_rows' => 1, 'cache_flag' => \CoreLibs\DB\IO::USE_CACHE, 'assoc_flag' => true, @@ -2108,10 +2304,15 @@ final class CoreLibsDBIOTest extends TestCase 'row_int', 'uid' ], + 'field_types' => [ + 'int4', + 'varchar' + ], 'num_fields' => 2, 'num_rows' => 2, 'pos' => 2, 'query' => $read_query, + 'params' => [], 'read_rows' => 2, 'cache_flag' => \CoreLibs\DB\IO::USE_CACHE, 'assoc_flag' => true, @@ -2134,10 +2335,15 @@ final class CoreLibsDBIOTest extends TestCase 'row_int', 'uid' ], + 'field_types' => [ + 'int4', + 'varchar' + ], 'num_fields' => 2, 'num_rows' => 2, 'pos' => 0, 'query' => $read_query, + 'params' => [], 'read_rows' => 2, 'cache_flag' => \CoreLibs\DB\IO::USE_CACHE, 'assoc_flag' => true, @@ -2154,6 +2360,7 @@ final class CoreLibsDBIOTest extends TestCase ], 'valid select, read new, assoc only' => [ $read_query, + null, \CoreLibs\DB\IO::READ_NEW, true, $row_a_assoc, @@ -2167,10 +2374,15 @@ final class CoreLibsDBIOTest extends TestCase 'row_int', 'uid' ], + 'field_types' => [ + 'int4', + 'varchar' + ], 'num_fields' => 2, 'num_rows' => 2, 'pos' => 1, 'query' => $read_query, + 'params' => [], 'read_rows' => 1, 'cache_flag' => \CoreLibs\DB\IO::READ_NEW, 'assoc_flag' => true, @@ -2195,10 +2407,15 @@ final class CoreLibsDBIOTest extends TestCase 'row_int', 'uid' ], + 'field_types' => [ + 'int4', + 'varchar' + ], 'num_fields' => 2, 'num_rows' => 2, 'pos' => 2, 'query' => $read_query, + 'params' => [], 'read_rows' => 2, 'cache_flag' => \CoreLibs\DB\IO::READ_NEW, 'assoc_flag' => true, @@ -2221,10 +2438,15 @@ final class CoreLibsDBIOTest extends TestCase 'row_int', 'uid' ], + 'field_types' => [ + 'int4', + 'varchar' + ], 'num_fields' => 2, 'num_rows' => 2, 'pos' => 0, 'query' => $read_query, + 'params' => [], 'read_rows' => 2, 'cache_flag' => \CoreLibs\DB\IO::READ_NEW, 'assoc_flag' => true, @@ -2241,6 +2463,7 @@ final class CoreLibsDBIOTest extends TestCase ], 'valid select, clear cache, assoc only' => [ $read_query, + null, \CoreLibs\DB\IO::CLEAR_CACHE, true, $row_a_assoc, @@ -2254,10 +2477,15 @@ final class CoreLibsDBIOTest extends TestCase 'row_int', 'uid' ], + 'field_types' => [ + 'int4', + 'varchar' + ], 'num_fields' => 2, 'num_rows' => 2, 'pos' => 1, 'query' => $read_query, + 'params' => [], 'read_rows' => 1, 'cache_flag' => \CoreLibs\DB\IO::CLEAR_CACHE, 'assoc_flag' => true, @@ -2282,10 +2510,15 @@ final class CoreLibsDBIOTest extends TestCase 'row_int', 'uid' ], + 'field_types' => [ + 'int4', + 'varchar' + ], 'num_fields' => 2, 'num_rows' => 2, 'pos' => 2, 'query' => $read_query, + 'params' => [], 'read_rows' => 2, 'cache_flag' => \CoreLibs\DB\IO::CLEAR_CACHE, 'assoc_flag' => true, @@ -2305,10 +2538,15 @@ final class CoreLibsDBIOTest extends TestCase 'row_int', 'uid' ], + 'field_types' => [ + 'int4', + 'varchar' + ], 'num_fields' => 2, 'num_rows' => 2, 'pos' => 0, 'query' => $read_query, + 'params' => [], 'read_rows' => 2, 'cache_flag' => \CoreLibs\DB\IO::CLEAR_CACHE, 'assoc_flag' => true, @@ -2325,6 +2563,7 @@ final class CoreLibsDBIOTest extends TestCase ], 'valid select, no cache, assoc only' => [ $read_query, + null, \CoreLibs\DB\IO::NO_CACHE, true, $row_a_assoc, @@ -2336,10 +2575,15 @@ final class CoreLibsDBIOTest extends TestCase 'row_int', 'uid' ], + 'field_types' => [ + 'int4', + 'varchar' + ], 'num_fields' => 2, 'num_rows' => 2, 'pos' => 1, 'query' => $read_query, + 'params' => [], 'read_rows' => 1, 'cache_flag' => \CoreLibs\DB\IO::NO_CACHE, 'assoc_flag' => true, @@ -2361,10 +2605,15 @@ final class CoreLibsDBIOTest extends TestCase 'row_int', 'uid' ], + 'field_types' => [ + 'int4', + 'varchar' + ], 'num_fields' => 2, 'num_rows' => 2, 'pos' => 2, 'query' => $read_query, + 'params' => [], 'read_rows' => 2, 'cache_flag' => \CoreLibs\DB\IO::NO_CACHE, 'assoc_flag' => true, @@ -2384,10 +2633,15 @@ final class CoreLibsDBIOTest extends TestCase 'row_int', 'uid' ], + 'field_types' => [ + 'int4', + 'varchar' + ], 'num_fields' => 2, 'num_rows' => 2, 'pos' => 0, 'query' => $read_query, + 'params' => [], 'read_rows' => 2, 'cache_flag' => \CoreLibs\DB\IO::NO_CACHE, 'assoc_flag' => true, @@ -2402,23 +2656,151 @@ final class CoreLibsDBIOTest extends TestCase '', $insert_query ], + 'valid select params, no cache, assoc only' => [ + // 0-3 + $read_query_params, + ['A'], + \CoreLibs\DB\IO::NO_CACHE, + true, + // 4 + $row_a_assoc, + // 5 + true, + // 6 cursor + [ + 'cursor' => 'PgSql\Result', + 'data' => [], + 'field_names' => [ + 'row_int', + 'uid' + ], + 'field_types' => [ + 'int4', + 'varchar' + ], + 'num_fields' => 2, + 'num_rows' => 1, + 'pos' => 1, + 'query' => $read_query_params, + 'params' => ['A'], + 'read_rows' => 1, + 'cache_flag' => \CoreLibs\DB\IO::NO_CACHE, + 'assoc_flag' => true, + 'cached' => false, + 'finished' => false, + 'read_finished' => true, + 'db_read_finished' => true, + ], + // 7 extended cursor + [ + // second row/last row + [ + 'data' => [ + 0 => $row_b_assoc, + ], + 'cursor' => [ + 'cursor' => 'PgSql\Result', + 'data' => [], + 'field_names' => [ + 'row_int', + 'uid' + ], + 'field_types' => [ + 'int4', + 'varchar' + ], + 'num_fields' => 2, + 'num_rows' => 1, + 'pos' => 2, + 'query' => $read_query_params, + 'params' => ['A'], + 'read_rows' => 2, + 'cache_flag' => \CoreLibs\DB\IO::NO_CACHE, + 'assoc_flag' => true, + 'cached' => false, + 'finished' => true, + 'read_finished' => true, + 'db_read_finished' => true, + ] + ], + // end row, false + [ + 'data' => false, + 'cursor' => [ + 'cursor' => 1, + 'data' => [], + 'field_names' => [ + 'row_int', + 'uid' + ], + 'field_types' => [ + 'int4', + 'varchar' + ], + 'num_fields' => 2, + 'num_rows' => 1, + 'pos' => 0, + 'query' => $read_query_params, + 'params' => ['A'], + 'read_rows' => 2, + 'cache_flag' => \CoreLibs\DB\IO::NO_CACHE, + 'assoc_flag' => true, + 'cached' => false, + 'finished' => true, + 'read_finished' => true, + 'db_read_finished' => true, + ] + ] + ], + // 8-10 + '', + '', + $insert_query + ], // *** READ STEP BY STEP, ERROR TRIGGER 'empty select error' => [ + // 0-3 '', null, null, + null, + // 4 false, + // 5 true, + // 6 [], + // 7 [], + // 8-10 '', '11', $insert_query, ], + 'params count wrong' => [ + // 0-3 + $read_query_params, + [], + null, + null, + // 4 + false, + // 5 + true, + // 6 + [], + // 7 + [], + // 8-10 + '', + '23', + $insert_query, + ], 'insert query error' => [ $insert_query, null, null, + null, false, true, [], @@ -2433,6 +2815,7 @@ final class CoreLibsDBIOTest extends TestCase $read_query, null, null, + null, [$row_a, $row_b,], false, [ @@ -2445,10 +2828,15 @@ final class CoreLibsDBIOTest extends TestCase 'row_int', 'uid' ], + 'field_types' => [ + 'int4', + 'varchar' + ], 'num_fields' => 2, 'num_rows' => 2, 'pos' => 0, 'query' => $read_query, + 'params' => [], 'read_rows' => 2, 'cache_flag' => \CoreLibs\DB\IO::USE_CACHE, 'assoc_flag' => false, @@ -2465,6 +2853,7 @@ final class CoreLibsDBIOTest extends TestCase // READ_NEW 'valid select, full read READ NEW' => [ $read_query, + null, \CoreLibs\DB\IO::READ_NEW, null, [$row_a, $row_b,], @@ -2479,10 +2868,15 @@ final class CoreLibsDBIOTest extends TestCase 'row_int', 'uid' ], + 'field_types' => [ + 'int4', + 'varchar' + ], 'num_fields' => 2, 'num_rows' => 2, 'pos' => 0, 'query' => $read_query, + 'params' => [], 'read_rows' => 2, 'cache_flag' => \CoreLibs\DB\IO::READ_NEW, 'assoc_flag' => false, @@ -2499,6 +2893,7 @@ final class CoreLibsDBIOTest extends TestCase // CLEAR_CACHE 'valid select, full read CLEAR CACHE' => [ $read_query, + null, \CoreLibs\DB\IO::CLEAR_CACHE, null, [$row_a, $row_b,], @@ -2511,10 +2906,15 @@ final class CoreLibsDBIOTest extends TestCase 'row_int', 'uid' ], + 'field_types' => [ + 'int4', + 'varchar' + ], 'num_fields' => 2, 'num_rows' => 2, 'pos' => 0, 'query' => $read_query, + 'params' => [], 'read_rows' => 2, 'cache_flag' => \CoreLibs\DB\IO::CLEAR_CACHE, 'assoc_flag' => false, @@ -2530,6 +2930,7 @@ final class CoreLibsDBIOTest extends TestCase ], 'valid select, full read NO CACHE' => [ $read_query, + null, \CoreLibs\DB\IO::NO_CACHE, null, [$row_a, $row_b,], @@ -2542,10 +2943,15 @@ final class CoreLibsDBIOTest extends TestCase 'row_int', 'uid' ], + 'field_types' => [ + 'int4', + 'varchar' + ], 'num_fields' => 2, 'num_rows' => 2, 'pos' => 0, 'query' => $read_query, + 'params' => [], 'read_rows' => 2, 'cache_flag' => \CoreLibs\DB\IO::NO_CACHE, 'assoc_flag' => false, @@ -2565,14 +2971,16 @@ final class CoreLibsDBIOTest extends TestCase /** * dbReturn cursor extended checks * - * @param \CoreLibs\DB\IO $db - * @param string $query - * @param array $cursor_ext_checks + * @param \CoreLibs\DB\IO $db + * @param string $query + * @param array|null $params + * @param array $cursor_ext_checks * @return void */ private function subAssertCursorExtTestDbReturnFunction( \CoreLibs\DB\IO $db, string $query, + ?array $params, array $cursor_ext_checks ): void { // cursor check @@ -2581,7 +2989,11 @@ final class CoreLibsDBIOTest extends TestCase empty($db->dbGetLastError()) && count($cursor_ext_checks) ) { - $cursor_ext = $db->dbGetCursorExt($query); + if ($params === null) { + $cursor_ext = $db->dbGetCursorExt($query); + } else { + $cursor_ext = $db->dbGetCursorExt($query, $params); + } foreach ($cursor_ext_checks as $key => $expected) { if ($key != 'cursor') { $this->assertEquals( @@ -2598,7 +3010,7 @@ final class CoreLibsDBIOTest extends TestCase $cursor_ext[$key], 'assert equal cursor ext cursor int 1' ); - } elseif (\CoreLibs\Check\PhpVersion::checkPHPVersion('8.1')) { + } else { $this->assertIsObject( $cursor_ext[$key], 'assert is object cursor ext cursor' @@ -2609,21 +3021,50 @@ final class CoreLibsDBIOTest extends TestCase $cursor_ext[$key], 'assert is instance of cursor ext cursor' ); - } else { - $this->assertIsResource( - $cursor_ext[$key], - 'assert is resource cursor ext cursor' - ); } } } } } + /** + * Undocumented function + * + * @param \CoreLibs\DB\IO $db + * @param string $query + * @param array|null $params + * @param int|null $flag_cache + * @param bool|null $flag_assoc + * @return array|false + */ + private function subDbReturnCall( + \CoreLibs\DB\IO $db, + string $query, + ?array $params, + ?int $flag_cache, + ?bool $flag_assoc, + ): array|false { + if ($flag_cache === null && $flag_assoc === null && $params === null) { + $result = $db->dbReturn($query); + } elseif ($flag_assoc === null && $params === null) { + $result = $db->dbReturn($query, $flag_cache); + } elseif ($params === null) { + $result = $db->dbReturn($query, $flag_cache, $flag_assoc); + } elseif ($flag_cache === null && $flag_assoc === null) { + $result = $db->dbReturnParams($query, $params); + } elseif ($flag_assoc === null) { + $result = $db->dbReturnParams($query, $params, $flag_cache); + } else { + $result = $db->dbReturnParams($query, $params, $flag_cache, $flag_assoc); + } + return $result; + } + /** * dbReturn Function Test * * @covers ::dbReturn + * @covers ::dbReturnParams * @covers ::dbCacheReset * @covers ::dbGetCursorExt * @covers ::dbCursorPos @@ -2632,6 +3073,7 @@ final class CoreLibsDBIOTest extends TestCase * @testdox dbReturn Read Frist $read_first_only only and cache $flag_cache and assoc $flag_assoc with (Warning: $warning/Error: $error) [$_dataName] * * @param string $query + * @param array|null $params * @param integer|null $flag_cache * @param boolean|null $flag_assoc * @param array|bool $expected @@ -2645,6 +3087,7 @@ final class CoreLibsDBIOTest extends TestCase */ public function testDbReturnFunction( string $query, + ?array $params, ?int $flag_cache, ?bool $flag_assoc, $expected, @@ -2666,20 +3109,28 @@ final class CoreLibsDBIOTest extends TestCase // all checks below if ($read_first_only === true) { + // run query + $result = $this->subDbReturnCall( + $db, + $query, + $params, + $flag_cache, + $flag_assoc + ); // simple assert first read, then discard result // compare data $this->assertEquals( $expected, - $flag_cache === null && $flag_assoc === null ? - $db->dbReturn($query) : - ($flag_assoc === null ? - $db->dbReturn($query, $flag_cache) : - $db->dbReturn($query, $flag_cache, $flag_assoc) - ), + $result, 'Assert dbReturn first only equal' ); $this->subAssertErrorTest($db, $warning, $error); - $this->subAssertCursorExtTestDbReturnFunction($db, $query, $cursor_ext_checks); + $this->subAssertCursorExtTestDbReturnFunction( + $db, + $query, + $params, + $cursor_ext_checks + ); // extended checks. read until end of data and check per steck cursor data foreach ($cursor_ext_checks_step as $_cursor_ext_checks) { // each step matches a read step @@ -2691,12 +3142,13 @@ final class CoreLibsDBIOTest extends TestCase $pos = 0; while ( is_array( - $res = $flag_cache === null && $flag_assoc === null ? - $db->dbReturn($query) : - ($flag_assoc === null ? - $db->dbReturn($query, $flag_cache) : - $db->dbReturn($query, $flag_cache, $flag_assoc) - ) + $res = $this->subDbReturnCall( + $db, + $query, + $params, + $flag_cache, + $flag_assoc + ) ) ) { $data[] = $res; @@ -2704,27 +3156,45 @@ final class CoreLibsDBIOTest extends TestCase // check cursor pos $this->assertEquals( $pos, - $db->dbGetCursorPos($query), + ($params === null ? + $db->dbGetCursorPos($query) : + $db->dbGetCursorPos($query, $params) + ), 'Assert dbReturn pos' ); } // does count match for returned data and the cursor num rows $this->assertEquals( count($data), - $db->dbGetCursorNumRows($query), + ($params === null ? + $db->dbGetCursorNumRows($query) : + $db->dbGetCursorNumRows($query, $params) + ), 'Assert dbReturn num rows' ); // run cursor ext checks after first run - $this->subAssertCursorExtTestDbReturnFunction($db, $query, $cursor_ext_checks); + $this->subAssertCursorExtTestDbReturnFunction( + $db, + $query, + $params, + $cursor_ext_checks + ); // does data match // try get cursor data for non existing, must be null $this->assertNull( - $db->dbGetCursorExt($query, 'nonexistingfield') + $db->dbGetCursorExt($query, [], 'nonexistingfield') ); // does reset data work, query cursor must be null - $db->dbCacheReset($query); + if ($params === null) { + $db->dbCacheReset($query); + } else { + $db->dbCacheReset($query, $params); + } $this->assertNull( - $db->dbGetCursorExt($query), + ($params === null ? + $db->dbGetCursorExt($query) : + $db->dbGetCursorExt($query, $params) + ), 'Assert dbReturn reset cache' ); // New run after reset @@ -2733,12 +3203,13 @@ final class CoreLibsDBIOTest extends TestCase $pos = 0; while ( is_array( - $res = $flag_cache === null && $flag_assoc === null ? - $db->dbReturn($query) : - ($flag_assoc === null ? - $db->dbReturn($query, $flag_cache) : - $db->dbReturn($query, $flag_cache, $flag_assoc) - ) + $res = $this->subDbReturnCall( + $db, + $query, + $params, + $flag_cache, + $flag_assoc + ) ) ) { $data[] = $res; @@ -2746,21 +3217,30 @@ final class CoreLibsDBIOTest extends TestCase // check cursor pos $this->assertEquals( $pos, - $db->dbGetCursorPos($query), + ($params === null ? + $db->dbGetCursorPos($query) : + $db->dbGetCursorPos($query, $params) + ), 'Assert dbReturn double read 1 pos: ' . $flag_cache ); } - $this->subAssertCursorExtTestDbReturnFunction($db, $query, $cursor_ext_checks); + $this->subAssertCursorExtTestDbReturnFunction( + $db, + $query, + $params, + $cursor_ext_checks + ); $data_second = []; $pos_second = 0; while ( is_array( - $res = $flag_cache === null && $flag_assoc === null ? - $db->dbReturn($query) : - ($flag_assoc === null ? - $db->dbReturn($query, $flag_cache) : - $db->dbReturn($query, $flag_cache, $flag_assoc) - ) + $res = $this->subDbReturnCall( + $db, + $query, + $params, + $flag_cache, + $flag_assoc + ) ) ) { $data_second[] = $res; @@ -2768,7 +3248,10 @@ final class CoreLibsDBIOTest extends TestCase // check cursor pos $this->assertEquals( $pos_second, - $db->dbGetCursorPos($query), + ($params === null ? + $db->dbGetCursorPos($query) : + $db->dbGetCursorPos($query, $params) + ), 'Assert dbReturn double read 2 pos: ' . $flag_cache ); } @@ -2783,7 +3266,12 @@ final class CoreLibsDBIOTest extends TestCase 'Assert first and second run are equal: count' ); // run cursor ext checks after second run - $this->subAssertCursorExtTestDbReturnFunction($db, $query, $cursor_ext_checks); + $this->subAssertCursorExtTestDbReturnFunction( + $db, + $query, + $params, + $cursor_ext_checks + ); } // reset all data @@ -3119,20 +3607,14 @@ final class CoreLibsDBIOTest extends TestCase // if result type, or if forced bool if (is_string($expected_prepare) && $expected_prepare == 'result') { // if PHP or newer, must be Object PgSql\Result - if (\CoreLibs\Check\PhpVersion::checkPHPVersion('8.1')) { - $this->assertIsObject( - $prepare_result - ); - // also check that this is correct instance type - $this->assertInstanceOf( - 'PgSql\Result', - $prepare_result - ); - } else { - $this->assertIsResource( - $prepare_result - ); - } + $this->assertIsObject( + $prepare_result + ); + // also check that this is correct instance type + $this->assertInstanceOf( + 'PgSql\Result', + $prepare_result + ); } else { $this->assertEquals( $expected_prepare, @@ -3149,21 +3631,15 @@ final class CoreLibsDBIOTest extends TestCase $db->dbExecute($stm_name, $query_data); if ($expected_execute == 'result') { // if PHP or newer, must be Object PgSql\Result - if (\CoreLibs\Check\PhpVersion::checkPHPVersion('8.1')) { - $this->assertIsObject( - $execute_result - ); - // also check that this is correct instance type - $this->assertInstanceOf( - 'PgSql\Result', - $execute_result - ); - // if this is an select use dbFetchArray to get data and test - } else { - $this->assertIsResource( - $execute_result - ); - } + $this->assertIsObject( + $execute_result + ); + // also check that this is correct instance type + $this->assertInstanceOf( + 'PgSql\Result', + $execute_result + ); + // if this is an select use dbFetchArray to get data and test } else { $this->assertEquals( $expected_execute, @@ -3881,8 +4357,10 @@ final class CoreLibsDBIOTest extends TestCase // NOTE that query can have multiple inserts // NOTE if there are different INSERTS before the primary keys // will not match anymore. Must be updated by hand - $table_with_primary_key_id = 55; + // IMPORTANT: if this is stand alone the primary key will not match and fail + $table_with_primary_key_id = 66; // 0: query + returning + // 1: params // 1: pk name for db exec // 2: key name/value or null (dbGetReturningExt) // 3: pos or null (dbGetReturningExt) @@ -3898,6 +4376,7 @@ final class CoreLibsDBIOTest extends TestCase null, null, null, + null, [ 'row_varchar' => 'Text', 'row_varchar_literal' => 'Other', @@ -3930,6 +4409,7 @@ final class CoreLibsDBIOTest extends TestCase null, null, null, + null, [ 'row_varchar' => 'Text', 'row_varchar_literal' => 'Other', @@ -3960,6 +4440,7 @@ final class CoreLibsDBIOTest extends TestCase null, null, null, + null, [ 0 => [ 'row_varchar' => 'Text', @@ -4003,6 +4484,7 @@ final class CoreLibsDBIOTest extends TestCase null, null, null, + null, [ 'row_varchar' => 'Text', 'row_varchar_literal' => 'Other', @@ -4031,6 +4513,7 @@ final class CoreLibsDBIOTest extends TestCase null, null, null, + null, [ 'row_varchar' => 'Text', 'row_varchar_literal' => 'Other', @@ -4058,6 +4541,7 @@ final class CoreLibsDBIOTest extends TestCase * @testdox Check returning cursor using $pk_name with $key and $pos [$_dataName] * * @param string $query + * @param arraysetLogLevelAll('debug', true); @@ -4081,9 +4566,15 @@ final class CoreLibsDBIOTest extends TestCase ); // insert data - $pk_name === null ? - $db->dbExec($query) : + if ($pk_name === null && $params === null) { + $db->dbExec($query); + } elseif ($params === null) { $db->dbExec($query, $pk_name); + } elseif ($pk_name === null) { + $db->dbExecParams($query, $params); + } else { + $db->dbExecParams($query, $params, $pk_name); + } // get the last value for PK and match that somehow @@ -4123,15 +4614,18 @@ final class CoreLibsDBIOTest extends TestCase public function getMethodsProvider(): array { // 0: run query - // 1: optional insert query (if select or needed) - // 2: optional compare query, if not set 0 is used - // 3: num rows - // 4: column count - // 5: column names + // 1: query params + // 2: optional insert query (if select or needed) + // 3: optional compare query, if not set 0 is used + // 4: num rows + // 5: column count + // 6: column names + // 7: column types return [ 'select data' => [ "SELECT row_varchar, row_varchar_literal, row_int, row_date " . "FROM table_with_primary_key", + null, "INSERT INTO table_with_primary_key " . "(row_varchar, row_varchar_literal, row_int, row_date) " . "VALUES " @@ -4142,6 +4636,7 @@ final class CoreLibsDBIOTest extends TestCase 2, 4, ['row_varchar', 'row_varchar_literal', 'row_int', 'row_date'], + ['varchar', 'varchar', 'int4', 'date'], ], // insert 'insert data' => [ @@ -4152,6 +4647,7 @@ final class CoreLibsDBIOTest extends TestCase . "('Foxtrott', 'Tango', 789, 2.2, '1982-10-15'), " . "('Schlamm', 'Beizinger', 100, 3.14, '1990-1-1') ", null, + null, "INSERT INTO table_with_primary_key " . "(row_varchar, row_varchar_literal, row_int, row_numeric, row_date) " . "VALUES " @@ -4163,12 +4659,14 @@ final class CoreLibsDBIOTest extends TestCase 3, 0, [], + [], ], // update 'update data' => [ "UPDATE table_with_primary_key SET " . "row_varchar = 'CHANGE A', row_int = 999 " . "WHERE uid = 'A'", + null, "INSERT INTO table_with_primary_key " . "(uid, row_varchar, row_varchar_literal, row_int, row_date) " . "VALUES " @@ -4179,7 +4677,26 @@ final class CoreLibsDBIOTest extends TestCase 1, 0, [], - ] + [], + ], + // select with params and proper query hashing + 'select data, params' => [ + "SELECT row_varchar, row_varchar_literal, row_int, row_date " + . "FROM table_with_primary_key " + . "WHERE row_varchar = $1", + ['Text'], + "INSERT INTO table_with_primary_key " + . "(row_varchar, row_varchar_literal, row_int, row_date) " + . "VALUES " + . "('Text', 'Other', 123, '2022-03-01'), " + . "('Foxtrott', 'Tango', 789, '1982-10-15') ", + null, + // + 1, + 4, + ['row_varchar', 'row_varchar_literal', 'row_int', 'row_date'], + ['varchar', 'varchar', 'int4', 'date'], + ], // something other (schema change?) ]; } @@ -4197,20 +4714,24 @@ final class CoreLibsDBIOTest extends TestCase * @testdox Check check rows: $expected_rows and cols: $expected_cols [$_dataName] * * @param string $query + * @param array|null $params, * @param string|null $insert_query * @param string|null $compare_query * @param integer $expected_rows * @param integer $expected_cols - * @param array $expected_col_names + * @param array $expected_col_names + * @param array $expected_col_types * @return void */ public function testDbGetMethods( string $query, + ?array $params, ?string $insert_query, ?string $compare_query, int $expected_rows, int $expected_cols, - array $expected_col_names + array $expected_col_names, + array $expected_col_types ): void { // self::$log->setLogLevelAll('debug', true); // self::$log->setLogLevelAll('print', true); @@ -4223,7 +4744,11 @@ final class CoreLibsDBIOTest extends TestCase $db->dbExec($insert_query); } - $db->dbExec($query); + if ($params === null) { + $db->dbExec($query); + } else { + $db->dbExecParams($query, $params); + } $this->assertEquals( $compare_query ?? $query, @@ -4231,36 +4756,46 @@ final class CoreLibsDBIOTest extends TestCase ); $this->assertEquals( // perhaps move that somewhere else? - \CoreLibs\Create\Hash::__hashLong($query), - $db->dbGetQueryHash($query) + \CoreLibs\Create\Hash::__hashLong( + $query . ( + $params !== null && $params !== [] ? + '#' . json_encode($params) : '' + ) + ), + ($params === null ? + $db->dbGetQueryHash($query) : + $db->dbGetQueryHash($query, $params) + ) ); $this->assertEquals( $expected_rows, - $db->dbGetNumRows() + $db->dbGetNumRows(), + 'Failed assert dbGetNumRows' ); $this->assertEquals( $expected_cols, - $db->dbGetNumFields() + $db->dbGetNumFields(), + 'Failed assert dbGetNumFields' ); $this->assertEquals( $expected_col_names, - $db->dbGetFieldNames() + $db->dbGetFieldNames(), + 'Failed assert dbGetFieldNames' + ); + $this->assertEquals( + $expected_col_types, + $db->dbGetFieldTypes(), + 'Failed assert dbGetFieldTypes' ); $dbh = $db->dbGetDbh(); - if (\CoreLibs\Check\PhpVersion::checkPHPVersion('8.1')) { - $this->assertIsObject( - $dbh - ); - // also check that this is correct instance type - $this->assertInstanceOf( - 'PgSql\Connection', - $dbh - ); - } else { - $this->assertIsResource( - $dbh - ); - } + $this->assertIsObject( + $dbh + ); + // also check that this is correct instance type + $this->assertInstanceOf( + 'PgSql\Connection', + $dbh + ); // if this is a select query, db dbReturn, dbReturnRow, dbReturnArray too if (preg_match("/^(select|show|with) /i", $query)) { @@ -4334,18 +4869,20 @@ final class CoreLibsDBIOTest extends TestCase public function asyncProvider(): array { // 0: query - // 1: primary key - // 2: exepected exec return - // 3: warning - // 4: error - // 5: exepcted check return 1st - // 6: final result - // 7: warning - // 8: error + // 1: params + // 2: primary key + // 3: exepected exec return + // 4: warning + // 5: error + // 6: exepcted check return 1st + // 7: final result + // 8: warning + // 9: error return [ 'run simple async query' => [ "SELECT pg_sleep(1)", null, + null, // exec result true, '', @@ -4357,12 +4894,44 @@ final class CoreLibsDBIOTest extends TestCase '', '' ], + 'run simple async query, params' => [ + "SELECT pg_sleep($1)", + [1], + null, + // exec result + true, + '', + '', + // check first + true, + // check final + 'result', + '', + '' + ], + // error on wrong params + 'wrong params count' => [ + "SELECT pg_sleep($1)", + [], + null, + // exec result + false, + '', + '23', + // check first + false, + // check final + false, + '', + '42' + ], // send query failed (E40) // result failed (E43) // no query running (E42) 'no async query running' => [ '', null, + null, // false, '', @@ -4381,11 +4950,13 @@ final class CoreLibsDBIOTest extends TestCase * Undocumented function * * @covers ::dbExecAsync + * @covers ::dbExecParamsAsync * @covers ::dbCheckAsync * @dataProvider asyncProvider * @testdox async query $query with $expected_exec (warning $warning_exec/error $error_exec) and $expected_check/$expected_final (warning $warning_final/error $error_final) [$_dataName] * * @param string $query + * @param array|null $params * @param string|null $pk_name * @param boolean $expected_exec * @param string $warning_exec @@ -4398,6 +4969,7 @@ final class CoreLibsDBIOTest extends TestCase */ public function testDbExecAsync( string $query, + ?array $params, ?string $pk_name, bool $expected_exec, string $warning_exec, @@ -4415,9 +4987,15 @@ final class CoreLibsDBIOTest extends TestCase ); // exec the query - $result_exec = $pk_name === null ? - $db->dbExecAsync($query) : - $db->dbExecAsync($query, $pk_name); + if ($pk_name === null && $params === null) { + $result_exec = $db->dbExecAsync($query); + } elseif ($params === null) { + $result_exec = $db->dbExecAsync($query, $pk_name); + } elseif ($pk_name === null) { + $result_exec = $db->dbExecParamsAsync($query, $params); + } else { + $result_exec = $db->dbExecParamsAsync($query, $params, $pk_name); + } $this->assertEquals( $expected_exec, $result_exec @@ -4439,20 +5017,14 @@ final class CoreLibsDBIOTest extends TestCase // check after final if ($expected_final == 'result') { // post end check - if (\CoreLibs\Check\PhpVersion::checkPHPVersion('8.1')) { - $this->assertIsObject( - $result_check - ); - // also check that this is correct instance type - $this->assertInstanceOf( - 'PgSql\Result', - $result_check - ); - } else { - $this->assertIsResource( - $result_check - ); - } + $this->assertIsObject( + $result_check + ); + // also check that this is correct instance type + $this->assertInstanceOf( + 'PgSql\Result', + $result_check + ); } else { // else compar check $this->assertEquals(