diff --git a/4dev/tests/CoreLibsDBIOTest.php b/4dev/tests/CoreLibsDBIOTest.php index 28814d52..e5f3505d 100644 --- a/4dev/tests/CoreLibsDBIOTest.php +++ b/4dev/tests/CoreLibsDBIOTest.php @@ -270,6 +270,7 @@ final class CoreLibsDBIOTest extends TestCase // 0: connection array // 1: status after connection // 2: info string + // 3: ??? return [ 'invalid connection' => [ self::$db_config['invalid'], @@ -413,18 +414,12 @@ final class CoreLibsDBIOTest extends TestCase self::$db_config[$connection], self::$log ); - if ($set === null) { - // equals to do nothing - $this->assertEquals( - $expected, - $db->dbSetDebug() - ); - } else { - $this->assertEquals( - $expected, + $this->assertEquals( + $expected, + $set === null ? + $db->dbSetDebug() : $db->dbSetDebug($set) - ); - } + ); // must always match $this->assertEquals( $expected, @@ -452,18 +447,12 @@ final class CoreLibsDBIOTest extends TestCase self::$db_config[$connection], self::$log ); - if ($toggle === null) { - // equals to do nothing - $this->assertEquals( - $expected, - $db->dbToggleDebug() - ); - } else { - $this->assertEquals( - $expected, + $this->assertEquals( + $expected, + $toggle === null ? + $db->dbToggleDebug() : $db->dbToggleDebug($toggle) - ); - } + ); // must always match $this->assertEquals( $expected, @@ -557,7 +546,10 @@ final class CoreLibsDBIOTest extends TestCase ); $this->assertEquals( $expected_flag, - $db->dbSetMaxQueryCall($max_calls) + // TODO special test with null call too + $max_calls === null ? + $db->dbSetMaxQueryCall() : + $db->dbSetMaxQueryCall($max_calls) ); $this->assertEquals( $expected_max_calls, @@ -615,6 +607,7 @@ final class CoreLibsDBIOTest extends TestCase // 'UTF8' // ], // other tests includ perhaps mocking for error? + // TODO actual data check ]; } @@ -732,28 +725,33 @@ final class CoreLibsDBIOTest extends TestCase return [ 'source "t" to true' => [ 't', + false, + true, + ], + 'source "t" to true null flag' => [ + 't', + null, true, - false ], 'source "true" to true' => [ 'true', + false, true, - false ], 'source "f" to false' => [ 'f', false, - false + false, ], 'source "false" to false' => [ 'false', false, - false + false, ], 'source anything to true' => [ 'something', - true, false, + true, ], 'source empty to false' => [ '', @@ -762,13 +760,13 @@ final class CoreLibsDBIOTest extends TestCase ], 'source bool true to "t"' => [ true, - 't', true, + 't', ], 'source bool false to "f"' => [ false, - 'f', true, + 'f', ], ]; } @@ -781,11 +779,11 @@ final class CoreLibsDBIOTest extends TestCase * @testdox Have $source and convert ($reverse) to $expected [$_dataName] * * @param string|bool $source + * @param bool|null $reverse * @param string|bool $expected - * @param bool $reverse * @return void */ - public function testDbBoolean($source, $expected, bool $reverse): void + public function testDbBoolean($source, ?bool $reverse, $expected): void { $db = new \CoreLibs\DB\IO( self::$db_config['valid'], @@ -793,7 +791,9 @@ final class CoreLibsDBIOTest extends TestCase ); $this->assertEquals( $expected, - $db->dbBoolean($source, $reverse) + $reverse === null ? + $db->dbBoolean($source) : + $db->dbBoolean($source, $reverse) ); $db->dbClose(); } @@ -818,6 +818,11 @@ final class CoreLibsDBIOTest extends TestCase false, '41 years 9 mons 18 days' ], + 'interval a null micro time' => [ + '41 years 9 mons 18 days', + null, + '41 years 9 mons 18 days' + ], 'interval a-1' => [ '41 years 9 mons 18 days 12:31:11', false, @@ -879,11 +884,11 @@ final class CoreLibsDBIOTest extends TestCase * @testdox Have $source and convert ($show_micro) to $expected [$_dataName] * * @param string $source - * @param bool $show_micro + * @param bool|null $show_micro * @param string $expected * @return void */ - public function testDbTimeFormat(string $source, bool $show_micro, string $expected): void + public function testDbTimeFormat(string $source, ?bool $show_micro, string $expected): void { $db = new \CoreLibs\DB\IO( self::$db_config['valid'], @@ -891,7 +896,9 @@ final class CoreLibsDBIOTest extends TestCase ); $this->assertEquals( $expected, - $db->dbTimeFormat($source, $show_micro) + $show_micro === null ? + $db->dbTimeFormat($source) : + $db->dbTimeFormat($source, $show_micro) ); $db->dbClose(); } @@ -1232,6 +1239,38 @@ final class CoreLibsDBIOTest extends TestCase ] ] ], + 'simple table null schema' => [ + 'test_meta', + null, + [ + 'row_1' => [ + 'num' => 1, + 'type' => 'varchar', + 'len' => -1, + 'not null' => false, + 'has default' => false, + 'array dims' => 0, + 'is enum' => false, + 'is base' => 1, + 'is composite' => false, + 'is pesudo' => false, + 'description' => '', + ], + 'row_2' => [ + 'num' => 2, + 'type' => 'int4', + 'len' => 4, + 'not null' => false, + 'has default' => false, + 'array dims' => 0, + 'is enum' => false, + 'is base' => 1, + 'is composite' => false, + 'is pesudo' => false, + 'description' => '', + ] + ] + ], 'table does not exist' => [ 'non_existing', 'public', @@ -1248,11 +1287,11 @@ final class CoreLibsDBIOTest extends TestCase * @testdox Check table $table in schema $schema with $expected [$_dataName] * * @param string $table - * @param string $schema + * @param string|null $schema * @param array|bool $expected * @return void */ - public function testDbShowTableMetaData(string $table, string $schema, $expected): void + public function testDbShowTableMetaData(string $table, ?string $schema, $expected): void { $db = new \CoreLibs\DB\IO( self::$db_config['valid'], @@ -1263,14 +1302,16 @@ final class CoreLibsDBIOTest extends TestCase $this->assertEquals( $expected, - $db->dbShowTableMetaData($table, $schema) + $schema === null ? + $db->dbShowTableMetaData($table) : + $db->dbShowTableMetaData($table, $schema) ); $db->dbClose(); } // - db exec test for insert/update/select/etc - // dbExec + // dbExec, dbResetQueryCalled, dbGetQueryCalled /** * provide queries with return results @@ -1280,7 +1321,7 @@ final class CoreLibsDBIOTest extends TestCase public function queryDbExecProvider(): array { // 0: query - // 1: optional primary key name + // 1: optional primary key name, null for empty test // 2: expectes result (bool, object (>=8.1)/resource (<8.1)) // 3: warning // 4: error @@ -1294,6 +1335,14 @@ final class CoreLibsDBIOTest extends TestCase '', '', ], + // insert but null primary key + 'table with pk insert null' => [ + 'INSERT INTO table_with_primary_key (row_date) VALUES (NOW())', + null, + 'resource/object', + '', + '', + ], // insert to table with no pk (31?) 'table with no pk insert' => [ 'INSERT INTO table_without_primary_key (row_date) VALUES (NOW())', @@ -1378,6 +1427,7 @@ final class CoreLibsDBIOTest extends TestCase // NOTE: After an error was encountered, queries after this // will return a true connection busy although it was error // https://bugs.php.net/bug.php?id=36469 + // TODO: fix wron error 42 after error insert 'invalid returning' => [ 'INSERT INTO table_with_primary_key (row_date) VALUES (NOW()) RETURNING invalid', '', @@ -1394,24 +1444,29 @@ final class CoreLibsDBIOTest extends TestCase * tests (internal read data post exec group) * * @covers ::dbExec + * @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 string $pk_name - * @param [type] $expected_return + * @param string|null $pk_name + * @param object|resource|bool $expected_return + * @param string $warning + * @param string $error + * @param bool $run_many_times * @return void */ public function testDbExec( string $query, - string $pk_name, + ?string $pk_name, $expected_return, string $warning, string $error, bool $run_many_times = false ): void { - self::$log->setLogLevelAll('debug', true); - self::$log->setLogLevelAll('print', true); + // self::$log->setLogLevelAll('debug', true); + // self::$log->setLogLevelAll('print', true); $db = new \CoreLibs\DB\IO( self::$db_config['valid'], self::$log @@ -1428,58 +1483,621 @@ final class CoreLibsDBIOTest extends TestCase $this->assertEquals( $expected_return, // supress ANY errors here - @$db->dbExec($query, $pk_name) + $pk_name === null ? + @$db->dbExec($query) : + @$db->dbExec($query, $pk_name) ); + $last_warning = $db->dbGetLastWarning(); + $last_error = $db->dbGetLastError(); } else { // if PHP or newer, must be Object PgSql\Result if (\CoreLibs\Check\PhpVersion::checkPHPVersion('8.1')) { + $result = $pk_name === null ? + $db->dbExec($query) : + $db->dbExec($query, $pk_name); + $last_warning = $db->dbGetLastWarning(); + $last_error = $db->dbGetLastError(); $this->assertIsObject( - $db->dbExec($query, $pk_name) + $result ); // also check that this is correct instance type $this->assertInstanceOf( 'PgSql\Result', - $db->dbExec($query, $pk_name) + $result ); } else { $this->assertIsResource( - $db->dbExec($query, $pk_name) + $pk_name === null ? + $db->dbExec($query) : + $db->dbExec($query, $pk_name) ); + $last_warning = $db->dbGetLastWarning(); + $last_error = $db->dbGetLastError(); } } // 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++) { - $db->dbExec($query, $pk_name); + $pk_name === null ? + $db->dbExec($query) : + $db->dbExec($query, $pk_name); } // will fail now $this->assertFalse( - $db->dbExec($query, $pk_name) + $pk_name === null ? + $db->dbExec($query) : + $db->dbExec($query, $pk_name) + ); + $last_warning = $db->dbGetLastWarning(); + $last_error = $db->dbGetLastError(); + // check query called matching + $current_count = $db->dbGetQueryCalled($query); + $this->assertEquals( + $db->dbGetMaxQueryCall() + 1, + $current_count + ); + // reset query called and check again + $this->assertEquals( + 0, + $db->dbResetQueryCalled($query) ); } + // if string for warning or error is not empty check $this->assertEquals( $warning, - $db->dbGetLastWarning() + $last_warning ); $this->assertEquals( $error, - $db->dbGetLastError() + $last_error ); // reset all data $db->dbExec("TRUNCATE table_with_primary_key"); $db->dbExec("TRUNCATE table_without_primary_key"); + // close connection + $db->dbClose(); + } + // - return one database row + // dbReturnRow + + /** + * Undocumented function + * + * @return array + */ + public function returnRowProvider(): array + { + $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'"; + // 0: query + // 1: flag (assoc) + // 2: result + // 3: warning + // 4: error + // 5: insert query + return [ + 'valid select' => [ + $read_query, + null, + [ + 'row_int' => 1, + 0 => 1, + 'uid' => 'A', + 1 => 'A' + ], + '', + '', + $insert_query, + ], + 'valid select, assoc only false' => [ + $read_query, + false, + [ + 'row_int' => 1, + 0 => 1, + 'uid' => 'A', + 1 => 'A' + ], + '', + '', + $insert_query, + ], + 'valid select, assoc only true' => [ + $read_query, + true, + [ + 'row_int' => 1, + 'uid' => 'A', + ], + '', + '', + $insert_query, + ], + 'empty select' => [ + '', + null, + false, + '', + '11', + $insert_query, + ], + 'insert query' => [ + $insert_query, + null, + false, + '', + '17', + $insert_query + ], + // invalid QUERY + ]; + } + + /** + * Undocumented function + * + * @covers ::dbReturnRow + * @dataProvider returnRowProvider + * @testdox dbReturnRow $query and assoc $flag_assoc with $expected (Warning: $warning/Error: $error) [$_dataName] + * + * @param string $query + * @param bool|null $flag_assoc + * @param array|bool $expected + * @param string $warning + * @param string $error + * @param string $insert_data + * @return void + */ + public function testDbReturnRow( + string $query, + ?bool $flag_assoc, + $expected, + string $warning, + string $error, + string $insert_data, + ): void { + // self::$log->setLogLevelAll('debug', true); + // self::$log->setLogLevelAll('print', true); + $db = new \CoreLibs\DB\IO( + self::$db_config['valid'], + self::$log + ); + // insert data before we can test, from expected array + $db->dbExec($insert_data); + // compare + $this->assertEquals( + $expected, + $flag_assoc === null ? + $db->dbReturnRow($query) : + $db->dbReturnRow($query, $flag_assoc) + ); + // get last error/warnings + $last_warning = $db->dbGetLastWarning(); + $last_error = $db->dbGetLastError(); + // print "ER: " . $last_error . "/" . $last_warning . "\n"; + // if string for warning or error is not empty check + $this->assertEquals( + $warning, + $last_warning + ); + $this->assertEquals( + $error, + $last_error + ); + + // reset all data + $db->dbExec("TRUNCATE table_with_primary_key"); + $db->dbExec("TRUNCATE table_without_primary_key"); + // close connection + $db->dbClose(); + } + + // - return all database rows + // dbReturnArray + + /** + * Undocumented function + * + * @return array + */ + public function returnArrayProvider(): array + { + $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"; + // 0: query + // 1: flag (assoc) + // 2: result + // 3: warning + // 4: error + // 5: insert query + return [ + 'valid select' => [ + $read_query, + null, + [ + [ + 'row_int' => 1, + 'uid' => 'A', + ], + [ + 'row_int' => 2, + 'uid' => 'B', + ], + ], + '', + '', + $insert_query, + ], + 'valid select, assoc ' => [ + $read_query, + false, + [ + [ + 'row_int' => 1, + 0 => 1, + 'uid' => 'A', + 1 => 'A' + ], + [ + 'row_int' => 2, + 0 => 2, + 'uid' => 'B', + 1 => 'B' + ], + ], + '', + '', + $insert_query, + ], + 'empty select' => [ + '', + null, + false, + '', + '11', + $insert_query, + ], + 'insert query' => [ + $insert_query, + null, + false, + '', + '17', + $insert_query + ], + // invalid QUERY + ]; + } + + /** + * Undocumented function + * + * @covers ::dbReturnArray + * @dataProvider returnArrayProvider + * @testdox dbReturnArray $query and assoc $flag_assoc with $expected (Warning: $warning/Error: $error) [$_dataName] + * + * @param string $query + * @param boolean|null $flag_assoc + * @param array|bool $expected + * @param string $warning + * @param string $error + * @param string $insert_data + * @return void + */ + public function testDbReturnArrray( + string $query, + ?bool $flag_assoc, + $expected, + string $warning, + string $error, + string $insert_data, + ): void { + // self::$log->setLogLevelAll('debug', true); + // self::$log->setLogLevelAll('print', true); + $db = new \CoreLibs\DB\IO( + self::$db_config['valid'], + self::$log + ); + // insert data before we can test, from expected array + $db->dbExec($insert_data); + // compare + $this->assertEquals( + $expected, + $flag_assoc === null ? + $db->dbReturnArray($query) : + $db->dbReturnArray($query, $flag_assoc) + ); + // get last error/warnings + $last_warning = $db->dbGetLastWarning(); + $last_error = $db->dbGetLastError(); + // if string for warning or error is not empty check + $this->assertEquals( + $warning, + $last_warning + ); + $this->assertEquals( + $error, + $last_error + ); + + // reset all data + $db->dbExec("TRUNCATE table_with_primary_key"); + $db->dbExec("TRUNCATE table_without_primary_key"); + // close connection + $db->dbClose(); + } + + // - loop data return flow + // dbReturn, dbCacheReset, dbCursorPos, dbCursorNumRows, dbGetCursorExt + + /** + * Undocumented function + * + * @return array + */ + public function dbReturnProvider(): array + { + $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"; + // 0: read query + // 1: reset flag, null for default + // 2: assoc flag, null for default + // 3: expected return + // 4: read first, read all flag + // 5: read all check array + // 6: warning + // 7: error + // 8: insert data + return [ + 'valid select' => [ + $read_query, + null, + null, + [ + 'row_int' => 1, + 0 => 1, + 'uid' => 'A', + 1 => 'A' + ], + true, + [], + '', + '', + $insert_query + ], + 'valid select, default cache, assoc only' => [ + $read_query, + \CoreLibs\DB\IO::USE_CACHE, + true, + [ + 'row_int' => 1, + 'uid' => 'A', + ], + true, + [], + '', + '', + $insert_query + ], + 'empty select' => [ + '', + null, + null, + false, + true, + [], + '', + '11', + $insert_query, + ], + 'insert query' => [ + $insert_query, + null, + null, + false, + true, + [], + '', + '17', + $insert_query + ], + // from here on a complex read all full tests + 'valid select, full read' => [ + $read_query, + null, + null, + [ + 'row_int' => 1, + 0 => 1, + 'uid' => 'A', + 1 => 'A' + ], + false, + [], + '', + '', + $insert_query + ], + ]; + } + + /** + * Undocumented function + * + * @covers ::dbReturn + * @covers ::dbCacheReset + * @covers ::dbGetCursorExt + * @covers ::dbCursorPos + * @covers ::dbCursorNumRows + * @dataProvider dbReturnProvider + * @testdox dbReturn $query and cache $flag_cache and assoc $flag_assoc with $expected (Warning: $warning/Error: $error) [$_dataName] + * + * @param string $query + * @param integer|null $flag_cache + * @param boolean|null $flag_assoc + * @param array|bool $expected + * @param bool $read_first_only + * @param array $cursor_ext_checks + * @param string $warning + * @param string $error + * @param string $insert_data + * @return void + */ + public function testDbReturn( + string $query, + ?int $flag_cache, + ?bool $flag_assoc, + $expected, + bool $read_first_only, + array $cursor_ext_checks, + string $warning, + string $error, + string $insert_data, + ): void { + // self::$log->setLogLevelAll('debug', true); + // self::$log->setLogLevelAll('print', true); + $db = new \CoreLibs\DB\IO( + self::$db_config['valid'], + self::$log + ); + // insert data before we can test, from expected array + $db->dbExec($insert_data); + + // all checks below + if ($read_first_only === true) { + // simple assert first read, then discard result + // compare + $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) + ) + ); + // get last error/warnings + $last_warning = $db->dbGetLastWarning(); + $last_error = $db->dbGetLastError(); + // if string for warning or error is not empty check + $this->assertEquals( + $warning, + $last_warning + ); + $this->assertEquals( + $error, + $last_error + ); + } else { + // all tests here have valid returns already, error checks not needed + // read all, and then do result compare + // cursor ext data checks (field names, rows, pos, data) + // do cache reset test + $data = []; + $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) + ) + ) + ) { + $data[] = $res; + $pos++; + // check cursor pos + $this->assertEquals( + $pos, + $db->dbGetCursorPos($query) + ); + } + // does count match for returned data and the cursor num rows + $this->assertEquals( + count($data), + $db->dbGetCursorNumRows($query) + ); + // does data match + // try get cursor data for non existing, must be null + $this->assertNull( + $db->dbGetCursorExt($query, 'nonexistingfield') + ); + // does reset data work, query cursor must be null + $db->dbCacheReset($query); + $this->assertNull( + $db->dbGetCursorExt($query) + ); + } + + // reset all data + $db->dbExec("TRUNCATE table_with_primary_key"); + $db->dbExec("TRUNCATE table_without_primary_key"); + // close connection + $db->dbClose(); + } + + // - prepared query execute + // dbPrepare, dbExecute, dbFetchArray + + public function preparedProvider(): array + { + // 0: statement name + // 1: query to prepare + // 2: primary key name: null for default run + // 3: arguments for query (double array 0: for select, 0..n for insert/update) + // 4: expected prepare return + // 5: prepare warning + // 6: prepare error + // 7: expected execute return + // 8: execute warning + // 9: execute error + // 10: execute data to check (array) + // 11: insert data + return [ + + ]; + } + + // bool|object|resource + + public function testDbPrepared( + string $stm_name, + string $query, + ?string $pk_name, + array $query_data, + $expected_prepare, + string $warning_prepare, + string $error_prepare, + $expected_execute, + string $warning_execute, + string $error_execute, + array $excute_data, + string $insert_data, + ): void { + // self::$log->setLogLevelAll('debug', true); + // self::$log->setLogLevelAll('print', true); + $db = new \CoreLibs\DB\IO( + self::$db_config['valid'], + self::$log + ); + // insert data before we can test, from expected array + $db->dbExec($insert_data); + + // reset all data + $db->dbExec("TRUNCATE table_with_primary_key"); + $db->dbExec("TRUNCATE table_without_primary_key"); + // close connection $db->dbClose(); } // - db execution tests - // dbReturn, dbCacheReset, - // dbFetchArray, dbReturnRow, dbReturnArray, - // dbCursorPos, dbCursorNumRows, - // dbPrepare, dbExecute // dbExecAsync, dbCheckAsync // - encoding conversion on read // dbSetToEncoding, dbGetToEncoding @@ -1487,10 +2105,8 @@ final class CoreLibsDBIOTest extends TestCase // dbDumpData // - internal read data (post exec) // dbGetReturning, dbGetInsertPKName, dbGetInsertPK, dbGetReturningExt, - // dbGetReturningArray, dbGetCursorExt, dbGetNumRows, dbGetNumFields, - // dbGetFieldNames, dbGetQuery - // getHadError, getHadWarning, - // dbResetQueryCalled, dbGetQueryCalled + // dbGetReturningArray, dbGetNumRows, dbGetNumFields, + // dbGetFieldNames, dbGetQuery, dbGetQueryHash, dbGetDbh // - complex write sets // dbWriteData, dbWriteDataExt // - deprecated tests [no need to test perhaps] @@ -1498,21 +2114,6 @@ final class CoreLibsDBIOTest extends TestCase // getCursorExt, getNumRows // - error handling // dbGetCombinedErrorHistory, dbGetLastError, dbGetLastWarning - - /** - * grouped DB IO test - * - * @testdox DB\IO Class tests - * - * @return void - */ - public function testDBIO() - { - $this->assertTrue(true, 'DB IO Tests not implemented'); - $this->markTestIncomplete( - 'DB\IO Tests have not yet been implemented' - ); - } } // __END__ diff --git a/www/admin/class_test.db.php b/www/admin/class_test.db.php index ebf7ae55..895f0901 100644 --- a/www/admin/class_test.db.php +++ b/www/admin/class_test.db.php @@ -55,16 +55,26 @@ echo "DB_CONFIG_SET constant:
" . print_r(DB_CONFIG, true) . "

"; print "DB Client encoding: " . $db->dbGetEncoding() . "
"; while (is_array($res = $db->dbReturn("SELECT * FROM max_test", DbIo::USE_CACHE, true))) { - print "TIME: " . $res['time'] . "
"; + print "UUD/TIME: " . $res['uid'] . "/" . $res['time'] . "
"; } print "CACHED DATA:
" . print_r($db->dbGetCursorExt(), true) . "

"; -while (is_array($res = $db->dbReturn("SELECT * FROM max_test"))) { - print "[CACHED] TIME: " . $res['time'] . "
"; +while (is_array($res = $db->dbReturn("SELECT * FROM max_test", DbIo::USE_CACHE))) { + print "[CACHED] UID/TIME: " . $res['uid'] . "/" . $res['time'] . "
"; + // print "****RES:
" . print_r($res, true) . "

"; } +// print "CACHED REREAD DATA:
" . print_r($db->dbGetCursorExt(), true) . "

"; +while (is_array($res = $db->dbReturn("SELECT * FROM max_test", DbIo::NO_CACHE))) { + print "[NO CACHE] UID.TIME: " . $res['uid'] . "/" . $res['time'] . "
"; + // print "****RES:
" . print_r($res, true) . "

"; +} +print "NO CACHED DATA:
" . print_r($db->dbGetCursorExt(), true) . "

"; // alternate check for valid data // while (($res = $db->dbReturn("SELECT * FROM max_test")) !== false) { // print "[CACHED] TIME: " . $res['time'] . "
"; // } +// while (is_array($res = $db->dbReturn("SELECT * FROM max_test", DbIo::USE_CACHE))) { +// print "UUD/TIME: " . $res['uid'] . "/" . $res['time'] . "
"; +// } print "
";
 
diff --git a/www/admin/class_test.uids.php b/www/admin/class_test.uids.php
index b3e3b06e..d0e808a0 100644
--- a/www/admin/class_test.uids.php
+++ b/www/admin/class_test.uids.php
@@ -41,6 +41,7 @@ $log = new CoreLibs\Debug\Logging([
 ]);
 $basic = new CoreLibs\Basic($log);
 $_uids = new CoreLibs\Create\Uids();
+use CoreLibs\Create\Uids;
 $uids_class = 'CoreLibs\Create\Uids';
 
 print "TEST CLASS: UIDS";
@@ -57,6 +58,10 @@ print "S::UUIDV4: " . $uids_class::uuidv4() . "
"; print "S::UNIQID (d): " . $uids_class::uniqId() . "
"; print "S::UNIQID (md5): " . $uids_class::uniqId('md5') . "
"; print "S::UNIQID (sha256): " . $uids_class::uniqId('sha256') . "
"; +// uniq ids +print "UNIQU ID SHORT : " . Uids::uniqIdShort() . "
"; +print "UNIQU ID LONG : " . Uids::uniqIdLong() . "
"; + // DEPRECATED /* print "D/UUIDV4: ".$basic->uuidv4()."
"; print "/DUNIQID (d): ".$basic->uniqId()."
"; */ diff --git a/www/lib/CoreLibs/ACL/Login.php b/www/lib/CoreLibs/ACL/Login.php index 957b481a..0167c370 100644 --- a/www/lib/CoreLibs/ACL/Login.php +++ b/www/lib/CoreLibs/ACL/Login.php @@ -439,7 +439,7 @@ class Login extends \CoreLibs\DB\IO if (!is_array($res)) { $this->login_error = 1009; $this->permission_okay = false; - } elseif (empty($this->dbGetCursorExt($q, 'num_rows'))) { + } elseif (empty($this->dbGetCursorNumRows($q))) { // username is wrong, but we throw for wrong username // and wrong password the same error $this->login_error = 1010; diff --git a/www/lib/CoreLibs/DB/IO.php b/www/lib/CoreLibs/DB/IO.php index 8f8c805c..9eb17390 100644 --- a/www/lib/CoreLibs/DB/IO.php +++ b/www/lib/CoreLibs/DB/IO.php @@ -436,7 +436,6 @@ class IO '22' => 'Query Execute failed', '23' => 'Query Execute failed, data array does not match placeholders', '24' => 'Missing prepared query entry for execute.', - '25' => 'Prepare query data is not in array format.', '30' => 'Query call in a possible endless loop. ' . 'Was called more than ' . $this->MAX_QUERY_CALL . ' times', '31' => 'Could not fetch PK after query insert', @@ -885,14 +884,61 @@ class IO private function __dbCheckConnectionOk(int $timeout_seconds = 3): bool { // check that no other query is running right now - // FIXME: do this in a loop? __dbConnectionBusySocketWait? max check timeout - if ($this->db_functions->__dbConnectionBusy()) { + // below does return false after error too + // if ($this->db_functions->__dbConnectionBusy()) { + if ($this->db_functions->__dbConnectionBusySocketWait($timeout_seconds)) { $this->__dbError(41); return false; } return true; } + /** + * dbReturn + * Read data from previous written data cache + * @param string $query_hash The hash for the current query + * @param boolean $assoc_only Only return assoc value (key named) + * @return array Current position query data from cache + */ + private function __dbReturnCacheRead(string $query_hash, bool $assoc_only): array + { + // unset return value ... + $return = []; + // current cursor hash element + $cursor_hash = $this->cursor_ext[$query_hash]; + // position in reading for current cursor hash + $cursor_pos = $cursor_hash['pos']; + // max fields in current cursor hash + $max_fields = $cursor_hash['num_fields'] ?? 0; + // read voer each field + for ($pos = 0; $pos < $max_fields; $pos++) { + // numbered pos element data (only exists in full read) + $cursor_data_pos = $cursor_hash['data'][$cursor_pos][$pos] ?? null; + // field name position from field names + $field_name_pos = $cursor_hash['field_names'][$pos] ?? null; + // field name data + $cursor_data_name = $cursor_hash['data'][$cursor_pos][$field_name_pos] ?? null; + // create mixed return array + if ( + $assoc_only === false && + $cursor_data_pos !== null + ) { + $return[$pos] = $cursor_data_pos; + } + // named part (is alreays read) + if (!empty($field_name_pos)) { + // read pos first, fallback to name if not set + if ($cursor_data_pos !== null) { + $return[$field_name_pos] = $cursor_data_pos; + } else { + $return[$field_name_pos] = $cursor_data_name; + } + } + } + $this->cursor_ext[$query_hash]['pos'] ++; + return $return; + } + /** * sub function for dbExec and dbExecAsync * - checks query is set @@ -908,7 +954,7 @@ class IO private function __dbPrepareExec(string $query, string $pk_name) { // reset current cursor before exec - $this->cursor = null; + $this->cursor = false; // clear matches for regex lookups $matches = []; // to either use the returning method @@ -980,7 +1026,7 @@ class IO $this->__dbDebug('db', $this->query, '__dbPrepareExec', 'Q'); } // import protection, hash needed - $query_hash = Hash::__hashLong($this->query); + $query_hash = $this->dbGetQueryHash($this->query); // 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; @@ -1129,13 +1175,11 @@ class IO $this->__dbWarning( 33, false, - $stm_name . ': RETURNING returned no data', - 'DB_WARNING' + $stm_name . ': RETURNING returned no data' ); } } elseif (count($this->insert_id_arr) > 1) { // this error handling is only for INSERT (), (), ... sets - $this->warning_id = 32; if ($stm_name === null) { $this->__dbWarning(32, $cursor, '[dbExec]'); } else { @@ -1346,7 +1390,7 @@ class IO { // set start array if ($query) { - $array = $this->cursor_ext[Hash::__hashLong($query)] ?? []; + $array = $this->cursor_ext[$this->dbGetQueryHash($query)] ?? []; } else { $array = $this->cursor_ext; } @@ -1585,7 +1629,8 @@ class IO * 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 $reset param: + * NOTE on $cache param: + * - if set to 0, if same query run again, will read from cache * - if set to 1, at the end of the query (last row returned), * the stored array will be deleted ... * - if set to 2, the data will be read new and cached @@ -1593,25 +1638,28 @@ class IO * - 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 int $reset reset status: + * @param int $cache reset status: default: USE_CACHE * USE_CACHE/0: normal read from cache on second run * CLEAR_CACHE/1: read cache, clean at the end * READ_NEW/2: read new, clean at end - * NO_CACHE/3: never cache + * NO_CACHE/3: never cache [default] * @param bool $assoc_only True to only returned the named and not * index position ones * @return array|bool return array data or false on error/end * @#suppress PhanTypeMismatchDimFetch */ - public function dbReturn(string $query, int $reset = self::USE_CACHE, bool $assoc_only = false) - { + public function dbReturn( + string $query, + int $cache = self::USE_CACHE, + bool $assoc_only = false + ) { $this->__dbErrorReset(); if (!$query) { $this->__dbError(11); return false; } // create hash from query ... - $query_hash = Hash::__hashLong($query); + $query_hash = $this->dbGetQueryHash($query); // pre declare array if (!isset($this->cursor_ext[$query_hash])) { $this->cursor_ext[$query_hash] = [ @@ -1635,9 +1683,10 @@ class IO } // init return als false $return = false; - // if it is a call with reset in it we reset the cursor, so we get an uncached return + // if it is a call with reset in it we reset the cursor, + // so we get an uncached return // but only for the FIRST call (pos == 0) - if ($reset && !$this->cursor_ext[$query_hash]['pos']) { + if ($cache > self::USE_CACHE && !$this->cursor_ext[$query_hash]['pos']) { $this->cursor_ext[$query_hash]['cursor'] = null; } // $this->debug('MENU', 'Reset: '.$reset.', Cursor: ' @@ -1687,6 +1736,7 @@ class IO $this->cursor_ext[$query_hash]['num_fields'] = $this->db_functions->__dbNumFields($this->cursor_ext[$query_hash]['cursor']); // set field names + $this->cursor_ext[$query_hash]['field_names'] = []; for ($i = 0; $i < $this->cursor_ext[$query_hash]['num_fields']; $i++) { $this->cursor_ext[$query_hash]['field_names'][] = $this->db_functions->__dbFieldName( @@ -1702,7 +1752,10 @@ class IO $this->cursor_ext[$query_hash]['read_rows'] = 0; } // read data for further work ... but only if necessarry - if ($this->cursor_ext[$query_hash]['read_rows'] == $this->cursor_ext[$query_hash]['num_rows']) { + if ( + $this->cursor_ext[$query_hash]['read_rows'] == + $this->cursor_ext[$query_hash]['num_rows'] + ) { $return = false; } else { $return = $this->__dbConvertEncoding( @@ -1717,7 +1770,7 @@ class IO } } // check if cached call or reset call ... - if (!$return && !$reset) { + if (!$return && $cache == self::USE_CACHE) { // check if end of output ... if ($this->cursor_ext[$query_hash]['pos'] >= $this->cursor_ext[$query_hash]['num_rows']) { $this->cursor_ext[$query_hash]['pos'] = 0; @@ -1727,62 +1780,25 @@ class IO $this->cursor_ext[$query_hash]['cursor'] = 1; $return = false; } else { - // unset return value ... - $return = []; - for ($i = 0; $i < $this->cursor_ext[$query_hash]['num_fields']; $i++) { - // FIXME: find out why phan throws all those array errors - // create mixed return array - if ( - $assoc_only === false && - isset($this->cursor_ext[$query_hash]['data'][$this->cursor_ext[$query_hash]['pos']][$i]) - ) { - $return[$i] = - /** @phan-suppress-next-line PhanTypeArraySuspiciousNullable */ - $this->cursor_ext[$query_hash]['data'] - [$this->cursor_ext[$query_hash]['pos']][$i]; - } - // named part - if (!empty($this->cursor_ext[$query_hash]['field_names'][$i])) { - if ( - isset( - $this->cursor_ext[$query_hash]['data'] - [$this->cursor_ext[$query_hash]['pos']][$i] - ) - ) { - /** @phan-suppress-next-line PhanTypeArraySuspiciousNullable */ - $return[$this->cursor_ext[$query_hash]['field_names'][$i]] = - /** @phan-suppress-next-line PhanTypeArraySuspiciousNullable */ - $this->cursor_ext[$query_hash]['data'] - [$this->cursor_ext[$query_hash]['pos']][$i]; - } else { - // throws PhanTypeMismatchDimFetch error, but in this - // case we know we will access only named array parts - /** @phan-suppress-next-line PhanTypeArraySuspiciousNullable */ - $return[$this->cursor_ext[$query_hash]['field_names'][$i]] = - /** @phan-suppress-next-line PhanTypeMismatchDimFetch,PhanTypeArraySuspiciousNullable */ - $this->cursor_ext[$query_hash]['data'][$this->cursor_ext[$query_hash] - /** @phan-suppress-next-line PhanTypeArraySuspiciousNullable */ - ['pos']][$this->cursor_ext[$query_hash]['field_names'][$i]]; - } - } - } - $this->cursor_ext[$query_hash]['pos'] ++; + // cached data read + $return = $this->__dbReturnCacheRead($query_hash, $assoc_only); } } else { // return row, if last && reset, then unset the hole hash array if ( !$return && - ($reset == self::CLEAR_CACHE || $reset == self::NO_CACHE) && + ($cache == self::CLEAR_CACHE || $cache == self::NO_CACHE) && $this->cursor_ext[$query_hash]['pos'] ) { - // unset only the field names here of course - $this->cursor_ext[$query_hash]['field_names'] = null; + // unset data block only + $this->cursor_ext[$query_hash]['data'] = []; $this->cursor_ext[$query_hash]['pos'] = 0; } elseif ( - !$return && $reset == self::READ_NEW && + !$return && $cache == self::READ_NEW && $this->cursor_ext[$query_hash]['pos'] ) { - // at end of read reset pos & set cursor to 1 (so it does not get lost in session transfer) + // at end of read reset pos & set cursor to 1 + // (so it does not get lost in session transfer) $this->cursor_ext[$query_hash]['pos'] = 0; $this->cursor_ext[$query_hash]['cursor'] = 1; $return = false; @@ -1792,13 +1808,14 @@ class IO // internal position counter $this->cursor_ext[$query_hash]['pos'] ++; $this->cursor_ext[$query_hash]['read_rows'] ++; - // if reset is <3 caching is done, else no - if ($reset < self::NO_CACHE) { - $temp = []; - foreach ($return as $field_name => $data) { - $temp[$field_name] = $data; - } - $this->cursor_ext[$query_hash]['data'][] = $temp; + // if reset is < NO_CACHE level caching is done, else no + if ($cache < self::NO_CACHE) { + // why was this here? + // $temp = []; + // foreach ($return as $field_name => $data) { + // $temp[$field_name] = $data; + // } + $this->cursor_ext[$query_hash]['data'][] = $return; } } // cached data if } // cached or not if @@ -1913,7 +1930,7 @@ class IO * @param bool $assoc_only if true, only name ref are returned * @return array|bool array of hashes (row -> fields), false on error */ - public function dbReturnArray(string $query, bool $assoc_only = false) + public function dbReturnArray(string $query, bool $assoc_only = true) { $this->__dbErrorReset(); if (!$query) { @@ -1931,17 +1948,18 @@ class IO } $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; + // $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; } // *************************** - // CURSOR CACHE RESET + // CURSOR EXT CACHE RESET // *************************** /** @@ -1952,9 +1970,9 @@ class IO public function dbCacheReset(string $query): bool { $this->__dbErrorReset(); - $query_hash = Hash::__hashLong($query); + $query_hash = $this->dbGetQueryHash($query); // clears cache for this query - if (!$this->cursor_ext[$query_hash]['query']) { + if (empty($this->cursor_ext[$query_hash]['query'])) { $this->__dbError(18); return false; } @@ -1963,22 +1981,55 @@ class IO } // *************************** - // CURSOR POSITON CHECK + // CURSOR EXT DATA CHECK // *************************** + /** + * 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 + * @return array|string|int|resource|object|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_field = '') + { + if ($query !== null) { + $query_hash = $this->dbGetQueryHash($query); + if ( + is_array($this->cursor_ext) && + isset($this->cursor_ext[$query_hash]) + ) { + if (empty($query_field)) { + return $this->cursor_ext[$query_hash]; + } else { + return $this->cursor_ext[$query_hash][$query_field] ?? null; + } + } else { + return null; + } + } + return $this->cursor_ext; + } + /** * returns the current position the read out * @param string $query query to find in cursor_ext * @return int|bool query position (row pos), false on error */ - public function dbCursorPos(string $query) + public function dbGetCursorPos(string $query) { $this->__dbErrorReset(); if (!$query) { $this->__dbError(11); return false; } - $query_hash = Hash::__hashLong($query); + $query_hash = $this->dbGetQueryHash($query); return (int)$this->cursor_ext[$query_hash]['pos']; } @@ -1987,14 +2038,14 @@ class IO * @param string $query query to find in cursor_ext * @return int|bool query position (row pos), false on error */ - public function dbCursorNumRows(string $query) + public function dbGetCursorNumRows(string $query) { $this->__dbErrorReset(); if (!$query) { $this->__dbError(11); return false; } - $query_hash = Hash::__hashLong($query); + $query_hash = $this->dbGetQueryHash($query); return (int)$this->cursor_ext[$query_hash]['num_rows']; } @@ -2010,7 +2061,7 @@ class IO */ public function dbResetQueryCalled(string $query): void { - $this->query_called[Hash::__hashLong($query)] = 0; + $this->query_called[$this->dbGetQueryHash($query)] = 0; } /** @@ -2020,7 +2071,7 @@ class IO */ public function dbGetQueryCalled(string $query): int { - $query_hash = Hash::__hashLong($query); + $query_hash = $this->dbGetQueryHash($query); if ($this->query_called[$query_hash]) { return $this->query_called[$query_hash]; } else { @@ -2122,7 +2173,7 @@ class IO } } else { // thrown warning - $this->warning_id = 20; + $this->__dbWarning(20, false, $stm_name); return true; } } @@ -2146,14 +2197,7 @@ class IO ); return false; } - if (!is_array($data)) { - $this->__dbError( - 25, - false, - $stm_name . ': Prepared query Data has to be given in array form.' - ); - return false; - } + // if the count does not match if ($this->prepare_cursor[$stm_name]['count'] != count($data)) { $this->__dbError( 23, @@ -2623,6 +2667,48 @@ class IO return $this->to_encoding; } + // *************************** + // QUERY DATA AND DB HANDLER + // *************************** + + /** + * Return current database handler + * @return object|resource|bool|int|null + */ + public function dbGetDbh() + { + return $this->dbh; + } + + /** + * 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 + */ + public function dbGetQueryHash(string $query): string + { + return Hash::__hashLong($query); + } + + /** + * Get current set query + * @return string Current set query string + */ + public function dbGetQuery(): string + { + return $this->query; + } + + /** + * Clear current query + * @return void + */ + public function dbResetQuery(): void + { + $this->query = ''; + } + // *************************** // INTERNAL VARIABLES READ POST QUERY RUN // *************************** @@ -2732,37 +2818,6 @@ class IO return $this->insert_id_arr; } - /** - * returns the full array for cursor ext - * @param string|null $q Query string, if not null convert to hash - * and return set cursor ext for only this - * if not found or null return null - * @return array|string|int|resource|object|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($q = null, string $query_field = '') - { - if ($q !== null) { - $query_hash = Hash::__hashLong($q); - if ( - is_array($this->cursor_ext) && - isset($this->cursor_ext[$query_hash]) - ) { - if (empty($query_field)) { - return $this->cursor_ext[$query_hash]; - } else { - return $this->cursor_ext[$query_hash][$query_field]; - } - } else { - return null; - } - } - return $this->cursor_ext; - } - /** * returns current number of rows that where * affected by UPDATE/SELECT, etc @@ -2792,32 +2847,9 @@ class IO return $this->field_names; } - /** - * Return current database handler - * @return object|resource|bool|int|null - */ - public function dbGetDbh() - { - return $this->dbh; - } - - /** - * Get current set query - * @return string Current set query string - */ - public function dbGetQuery(): string - { - return $this->query; - } - - /** - * Clear current query - * @return void - */ - public function dbResetQuery(): void - { - $this->query = ''; - } + // *************************** + // ERROR AND WARNING DATA + // *************************** /** * Sets error number that was last @@ -2871,9 +2903,10 @@ class IO return $this->error_history_long; } - /******************************** */ + // *************************** // DEPEREACTED CALLS - /******************************** */ + // all call below are no longer in use and throw deprecated errors + // *************************** /** * returns the db init error diff --git a/www/lib/CoreLibs/DB/SQL/PgSQL.php b/www/lib/CoreLibs/DB/SQL/PgSQL.php index 2965cc86..fda5258a 100644 --- a/www/lib/CoreLibs/DB/SQL/PgSQL.php +++ b/www/lib/CoreLibs/DB/SQL/PgSQL.php @@ -586,9 +586,10 @@ class PgSQL $socket = [pg_socket($this->dbh)]; while ($busy) { // Will wait on that socket until that happens or the timeout is reached - stream_select($socket, null, null, $timeout_seconds); + stream_select($socket, $null, $null, $timeout_seconds); $busy = pg_connection_busy($this->dbh); } + return $busy; } /** diff --git a/www/lib/CoreLibs/Output/Form/Generate.php b/www/lib/CoreLibs/Output/Form/Generate.php index e2d0befd..78923e06 100644 --- a/www/lib/CoreLibs/Output/Form/Generate.php +++ b/www/lib/CoreLibs/Output/Form/Generate.php @@ -2249,14 +2249,14 @@ class Generate extends \CoreLibs\DB\Extended\ArrayIO while (is_array($res = $this->dbReturn($data_array['query']))) { /** @phan-suppress-next-line PhanTypeInvalidDimOffset */ $this->log->debug('edit', 'Q[' . $this->dbGetQueryHash($data_array['query']) . '] pos: ' - . $this->dbGetCursorExt($data_array['query'], 'pos') + . $this->dbGetCursorPos($data_array['query']) . ' | want: ' . ($data_array['preset'] ?? '-') . ' | set: ' . ($data['preset'][$el_name] ?? '-')); // first is default for this element if ( isset($data_array['preset']) && (!isset($data['preset'][$el_name]) || empty($data['preset'][$el_name])) && - ($this->dbGetCursorExt($data_array['query'], 'pos') == $data_array['preset']) + ($this->dbGetCursorPos($data_array['query']) == $data_array['preset']) ) { $data['preset'][$el_name] = $res[0]; }