4471 lines
99 KiB
PHP
4471 lines
99 KiB
PHP
|
|
<?php // phpcs:disable Generic.Files.LineLength
|
|||
|
|
|
|||
|
|
/*
|
|||
|
|
*** IMPORTANT ***
|
|||
|
|
This test needs a working database with certain tables setup
|
|||
|
|
DB Host: localhost
|
|||
|
|
DB Name: corelibs_db_io_test
|
|||
|
|
DB User: corelibs_db_io_test
|
|||
|
|
DB Password: corelibs_db_io_test
|
|||
|
|
DB Encoding: UTF8 (MUST!)
|
|||
|
|
User must be able to drop/create tables
|
|||
|
|
In case of changes the valid_* $db_config entries must be changed
|
|||
|
|
*** IMPORTANT ***
|
|||
|
|
|
|||
|
|
Below tables will be automatically created
|
|||
|
|
Table with Primary Key: table_with_primary_key
|
|||
|
|
Table without Primary Key: table_without_primary_key
|
|||
|
|
|
|||
|
|
Table with primary key has additional row:
|
|||
|
|
row_primary_key SERIAL PRIMARY KEY,
|
|||
|
|
Each table has the following rows
|
|||
|
|
row_int INT,
|
|||
|
|
row_numeric NUMERIC,
|
|||
|
|
row_varchar VARCHAR,
|
|||
|
|
row_json JSON
|
|||
|
|
row_jsonb JSONB,
|
|||
|
|
row_bytea BYTEA,
|
|||
|
|
row_timestamp TIMESTAMP WITHOUT TIME ZONE,
|
|||
|
|
row_date DATE,
|
|||
|
|
row_interval INTERVAL,
|
|||
|
|
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
declare(strict_types=1);
|
|||
|
|
|
|||
|
|
namespace tests;
|
|||
|
|
|
|||
|
|
use PHPUnit\Framework\TestCase;
|
|||
|
|
use PHPUnit\Framework\MockObject\MockObject;
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Test class for DB\IO + DB\SQL\PgSQL
|
|||
|
|
* This will only test the PgSQL parts
|
|||
|
|
* @coversDefaultClass \CoreLibs\DB\IO
|
|||
|
|
* @coversDefaultClass \CoreLibs\DB\SQL\PgSQL
|
|||
|
|
* @testdox \CoreLibs\DB\IO method tests for SQL\PgSQL
|
|||
|
|
*/
|
|||
|
|
final class CoreLibsDBIOTest extends TestCase
|
|||
|
|
{
|
|||
|
|
private static $db_config = [
|
|||
|
|
// self localhost/ip connection
|
|||
|
|
'valid' => [
|
|||
|
|
'db_name' => 'corelibs_db_io_test',
|
|||
|
|
'db_user' => 'corelibs_db_io_test',
|
|||
|
|
'db_pass' => 'corelibs_db_io_test',
|
|||
|
|
'db_host' => 'localhost',
|
|||
|
|
'db_port' => 5432,
|
|||
|
|
'db_schema' => 'public',
|
|||
|
|
'db_type' => 'pgsql',
|
|||
|
|
'db_encoding' => '',
|
|||
|
|
'db_ssl' => 'allow', // allow, disable, require, prefer
|
|||
|
|
'db_debug' => true,
|
|||
|
|
],
|
|||
|
|
// same as valid, but db debug is off
|
|||
|
|
'valid_debug_false' => [
|
|||
|
|
'db_name' => 'corelibs_db_io_test',
|
|||
|
|
'db_user' => 'corelibs_db_io_test',
|
|||
|
|
'db_pass' => 'corelibs_db_io_test',
|
|||
|
|
'db_host' => 'localhost',
|
|||
|
|
'db_port' => 5432,
|
|||
|
|
'db_schema' => 'public',
|
|||
|
|
'db_type' => 'pgsql',
|
|||
|
|
'db_encoding' => '',
|
|||
|
|
'db_ssl' => 'allow', // allow, disable, require, prefer
|
|||
|
|
'db_debug' => false,
|
|||
|
|
],
|
|||
|
|
// same as valid, but encoding is set
|
|||
|
|
'valid_with_encoding_utf8' => [
|
|||
|
|
'db_name' => 'corelibs_db_io_test',
|
|||
|
|
'db_user' => 'corelibs_db_io_test',
|
|||
|
|
'db_pass' => 'corelibs_db_io_test',
|
|||
|
|
'db_host' => 'localhost',
|
|||
|
|
'db_port' => 5432,
|
|||
|
|
'db_schema' => 'public',
|
|||
|
|
'db_type' => 'pgsql',
|
|||
|
|
'db_encoding' => 'UTF-8',
|
|||
|
|
'db_ssl' => 'allow', // allow, disable, require, prefer
|
|||
|
|
'db_debug' => true,
|
|||
|
|
],
|
|||
|
|
// valid with no schema set
|
|||
|
|
'valid_no_schema' => [
|
|||
|
|
'db_name' => 'corelibs_db_io_test',
|
|||
|
|
'db_user' => 'corelibs_db_io_test',
|
|||
|
|
'db_pass' => 'corelibs_db_io_test',
|
|||
|
|
'db_host' => 'localhost',
|
|||
|
|
'db_port' => 5432,
|
|||
|
|
'db_schema' => '',
|
|||
|
|
'db_type' => 'pgsql',
|
|||
|
|
'db_encoding' => '',
|
|||
|
|
'db_ssl' => 'allow', // allow, disable, require, prefer
|
|||
|
|
'db_debug' => true,
|
|||
|
|
],
|
|||
|
|
// invalid (missing db name)
|
|||
|
|
'invalid' => [
|
|||
|
|
'db_name' => '',
|
|||
|
|
'db_user' => '',
|
|||
|
|
'db_pass' => '',
|
|||
|
|
'db_host' => '',
|
|||
|
|
'db_port' => 5432,
|
|||
|
|
'db_schema' => 'public',
|
|||
|
|
'db_type' => 'pgsql',
|
|||
|
|
'db_encoding' => '',
|
|||
|
|
'db_ssl' => 'allow', // allow, disable, require, prefer
|
|||
|
|
'db_debug' => true,
|
|||
|
|
],
|
|||
|
|
];
|
|||
|
|
private static $log;
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Test if pgsql module loaded
|
|||
|
|
* Check if valid DB connection works
|
|||
|
|
* Check if tables exist and remove them
|
|||
|
|
* Create test tables
|
|||
|
|
*
|
|||
|
|
* @return void
|
|||
|
|
*/
|
|||
|
|
public static function setUpBeforeClass(): void
|
|||
|
|
{
|
|||
|
|
if (!extension_loaded('pgsql')) {
|
|||
|
|
self::markTestSkipped(
|
|||
|
|
'The PgSQL extension is not available.'
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
// define basic connection set valid and one invalid
|
|||
|
|
self::$log = new \CoreLibs\Debug\Logging([
|
|||
|
|
// 'log_folder' => __DIR__ . DIRECTORY_SEPARATOR . 'log',
|
|||
|
|
'log_folder' => DIRECTORY_SEPARATOR . 'tmp',
|
|||
|
|
'file_id' => 'CoreLibs-DB-IO-Test',
|
|||
|
|
'debug_all' => false,
|
|||
|
|
'echo_all' => false,
|
|||
|
|
'print_all' => false,
|
|||
|
|
]);
|
|||
|
|
$db = new \CoreLibs\DB\IO(
|
|||
|
|
self::$db_config['valid'],
|
|||
|
|
self::$log
|
|||
|
|
);
|
|||
|
|
if (!$db->dbGetConnectionStatus()) {
|
|||
|
|
self::markTestSkipped(
|
|||
|
|
'Cannot connect to valid Test DB for DB\IO test.'
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
// check if they already exist, drop them
|
|||
|
|
if ($db->dbShowTableMetaData('table_with_primary_key') !== false) {
|
|||
|
|
$db->dbExec("DROP TABLE table_with_primary_key");
|
|||
|
|
$db->dbExec("DROP TABLE table_without_primary_key");
|
|||
|
|
$db->dbExec("DROP TABLE test_meta");
|
|||
|
|
}
|
|||
|
|
// uid is for internal reference tests
|
|||
|
|
$base_table = <<<EOM
|
|||
|
|
uid VARCHAR,
|
|||
|
|
row_int INT,
|
|||
|
|
row_numeric NUMERIC,
|
|||
|
|
row_varchar VARCHAR,
|
|||
|
|
row_varchar_literal VARCHAR,
|
|||
|
|
row_json JSON,
|
|||
|
|
row_jsonb JSONB,
|
|||
|
|
row_bytea BYTEA,
|
|||
|
|
row_timestamp TIMESTAMP WITHOUT TIME ZONE,
|
|||
|
|
row_date DATE,
|
|||
|
|
row_interval INTERVAL,
|
|||
|
|
row_array_int INT ARRAY,
|
|||
|
|
row_array_varchar VARCHAR ARRAY
|
|||
|
|
)
|
|||
|
|
WITHOUT OIDS
|
|||
|
|
EOM;
|
|||
|
|
// create the tables
|
|||
|
|
$db->dbExec(
|
|||
|
|
// primary key name is table + '_id'
|
|||
|
|
<<<EOM
|
|||
|
|
CREATE TABLE table_with_primary_key (
|
|||
|
|
table_with_primary_key_id SERIAL PRIMARY KEY,
|
|||
|
|
$base_table
|
|||
|
|
EOM
|
|||
|
|
/* "CREATE TABLE table_with_primary_key ("
|
|||
|
|
// primary key name is table + '_id'
|
|||
|
|
. "table_with_primary_key_id SERIAL PRIMARY KEY, "
|
|||
|
|
. $base_table */
|
|||
|
|
);
|
|||
|
|
$db->dbExec(
|
|||
|
|
<<<EOM
|
|||
|
|
CREATE TABLE table_without_primary_key (
|
|||
|
|
$base_table
|
|||
|
|
EOM
|
|||
|
|
/* "CREATE TABLE table_without_primary_key ("
|
|||
|
|
. $base_table */
|
|||
|
|
);
|
|||
|
|
// create simple table for meta test
|
|||
|
|
$db->dbExec(
|
|||
|
|
<<<EOM
|
|||
|
|
CREATE TABLE test_meta (
|
|||
|
|
row_1 VARCHAR,
|
|||
|
|
row_2 INT
|
|||
|
|
) WITHOUT OIDS
|
|||
|
|
EOM
|
|||
|
|
/* "CREATE TABLE test_meta ("
|
|||
|
|
. "row_1 VARCHAR, "
|
|||
|
|
. "row_2 INT"
|
|||
|
|
. ") WITHOUT OIDS" */
|
|||
|
|
);
|
|||
|
|
// set some test schema
|
|||
|
|
$db->dbExec("CREATE SCHEMA IF NOT EXISTS testschema");
|
|||
|
|
// end connection
|
|||
|
|
$db->dbClose();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Check that we can actually do these tests
|
|||
|
|
*
|
|||
|
|
* @return void
|
|||
|
|
*/
|
|||
|
|
protected function setUp(): void
|
|||
|
|
{
|
|||
|
|
// print_r(self::$db_config);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* For all Warning and Error checks in all tests
|
|||
|
|
* DB Warning/Error checks
|
|||
|
|
*
|
|||
|
|
* @param \CoreLibs\DB\IO $db
|
|||
|
|
* @param string $warning
|
|||
|
|
* @param string $error
|
|||
|
|
* @return array
|
|||
|
|
*/
|
|||
|
|
private function subAssertErrorTest(
|
|||
|
|
\CoreLibs\DB\IO $db,
|
|||
|
|
string $warning,
|
|||
|
|
string $error
|
|||
|
|
): array {
|
|||
|
|
// 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,
|
|||
|
|
'Assert query warning'
|
|||
|
|
);
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$error,
|
|||
|
|
$last_error,
|
|||
|
|
'Assert query warning'
|
|||
|
|
);
|
|||
|
|
return [$last_warning, $last_error];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// - connected version test
|
|||
|
|
// dbVerions, dbVersionNum, dbVersionInfo, dbVersionInfoParameters,
|
|||
|
|
// dbCompareVersion
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Just checks that the return value of dbVersion matches basic regex
|
|||
|
|
*
|
|||
|
|
* @covers ::dbVersion
|
|||
|
|
* @testdox test db version string return matching retex
|
|||
|
|
*
|
|||
|
|
* @return void
|
|||
|
|
*/
|
|||
|
|
public function testDbVersion(): void
|
|||
|
|
{
|
|||
|
|
// self::$log->setLogLevelAll('debug', true);
|
|||
|
|
// self::$log->setLogLevelAll('print', true);
|
|||
|
|
$db = new \CoreLibs\DB\IO(
|
|||
|
|
self::$db_config['valid'],
|
|||
|
|
self::$log
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
$this->assertMatchesRegularExpression(
|
|||
|
|
"/^\d+\.\d+(\.\d+)?$/",
|
|||
|
|
$db->dbVersion()
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
$db->dbClose();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Undocumented function
|
|||
|
|
*
|
|||
|
|
* @covers ::dbVersionNumeric
|
|||
|
|
* @testdox test db version numeric return matching retex
|
|||
|
|
*
|
|||
|
|
* @return void
|
|||
|
|
*/
|
|||
|
|
public function testDbVersionNumeric(): void
|
|||
|
|
{
|
|||
|
|
// self::$log->setLogLevelAll('debug', true);
|
|||
|
|
// self::$log->setLogLevelAll('print', true);
|
|||
|
|
$db = new \CoreLibs\DB\IO(
|
|||
|
|
self::$db_config['valid'],
|
|||
|
|
self::$log
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
$version = $db->dbVersionNumeric();
|
|||
|
|
// must be int
|
|||
|
|
$this->assertIsInt($version);
|
|||
|
|
// assume 90606 or 130006
|
|||
|
|
// should this change, the regex below must change
|
|||
|
|
$this->assertMatchesRegularExpression(
|
|||
|
|
"/^\d{5,6}?$/",
|
|||
|
|
(string)$version
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
$db->dbClose();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Undocumented function
|
|||
|
|
*
|
|||
|
|
* @covers ::dbVersionInfoParameters
|
|||
|
|
* @testdox test db version parameters are returned as array
|
|||
|
|
*
|
|||
|
|
* @return void
|
|||
|
|
*/
|
|||
|
|
public function testDbVersionInfoParameters(): void
|
|||
|
|
{
|
|||
|
|
// self::$log->setLogLevelAll('debug', true);
|
|||
|
|
// self::$log->setLogLevelAll('print', true);
|
|||
|
|
$db = new \CoreLibs\DB\IO(
|
|||
|
|
self::$db_config['valid'],
|
|||
|
|
self::$log
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
$parameters = $db->dbVersionInfoParameters();
|
|||
|
|
|
|||
|
|
$this->assertIsArray($parameters);
|
|||
|
|
$this->assertGreaterThan(
|
|||
|
|
1,
|
|||
|
|
count($parameters)
|
|||
|
|
);
|
|||
|
|
// must have at least this
|
|||
|
|
$this->assertContains(
|
|||
|
|
'server',
|
|||
|
|
$parameters
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
$db->dbClose();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Undocumented function
|
|||
|
|
*
|
|||
|
|
* @return array
|
|||
|
|
*/
|
|||
|
|
public function versionInfoProvider(): array
|
|||
|
|
{
|
|||
|
|
return [
|
|||
|
|
'client' => [
|
|||
|
|
'client',
|
|||
|
|
"/^\d+\.\d+/"
|
|||
|
|
],
|
|||
|
|
'session authorization' => [
|
|||
|
|
'session_authorization',
|
|||
|
|
"/^\w+$/"
|
|||
|
|
],
|
|||
|
|
'test non existing' => [
|
|||
|
|
'non_existing',
|
|||
|
|
'/^$/'
|
|||
|
|
],
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Undocumented function
|
|||
|
|
*
|
|||
|
|
* @covers ::dbVersionInfo
|
|||
|
|
* @dataProvider versionInfoProvider
|
|||
|
|
* @testdox Version Info $parameter matches as $expected [$_dataName]
|
|||
|
|
*
|
|||
|
|
* @param string $parameter
|
|||
|
|
* @param string $expected
|
|||
|
|
* @return void
|
|||
|
|
*/
|
|||
|
|
public function testDbVersionInfo(string $parameter, string $expected): void
|
|||
|
|
{
|
|||
|
|
// self::$log->setLogLevelAll('debug', true);
|
|||
|
|
// self::$log->setLogLevelAll('print', true);
|
|||
|
|
$db = new \CoreLibs\DB\IO(
|
|||
|
|
self::$db_config['valid'],
|
|||
|
|
self::$log
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
$this->assertMatchesRegularExpression(
|
|||
|
|
$expected,
|
|||
|
|
$db->dbVersionInfo($parameter)
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
$db->dbClose();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Returns test list for dbCompareVersion check
|
|||
|
|
* NOTE: unless we fully mock the =version check needs to be updated
|
|||
|
|
*
|
|||
|
|
* @return array
|
|||
|
|
*/
|
|||
|
|
public function versionProvider(): array
|
|||
|
|
{
|
|||
|
|
// 1: vesion to compare to
|
|||
|
|
// 2: expected outcome
|
|||
|
|
return [
|
|||
|
|
'compare = ok' => [ '=13.6.0', true ],
|
|||
|
|
'compare = bad' => [ '=9.2.0', false ],
|
|||
|
|
'compare < ok' => [ '<99.0.0', true ],
|
|||
|
|
'compare < bad' => [ '<9.2.0', false ],
|
|||
|
|
'compare <= ok a' => [ '<=99.0.0', true ],
|
|||
|
|
'compare <= ok b' => [ '<=13.6.0', true ],
|
|||
|
|
'compare <= false' => [ '<=9.2.0', false ],
|
|||
|
|
'compare > ok' => [ '>9.2.0', true ],
|
|||
|
|
'compare > bad' => [ '>99.2.0', false ],
|
|||
|
|
'compare >= ok a' => [ '>=13.6.0', true ],
|
|||
|
|
'compare >= ok b' => [ '>=9.2.0', true ],
|
|||
|
|
'compare >= bad' => [ '>=99.0.0', false ],
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* NOTE
|
|||
|
|
* Version tests will fail if versions change
|
|||
|
|
* Current base as Version 13.6 for equal check
|
|||
|
|
* I can't mock a function on the same class when it is called in a method
|
|||
|
|
* NOTE
|
|||
|
|
*
|
|||
|
|
* @covers ::dbCompareVersion
|
|||
|
|
* @dataProvider versionProvider
|
|||
|
|
* @testdox Version $input compares as $expected [$_dataName]
|
|||
|
|
*
|
|||
|
|
* @return void
|
|||
|
|
*/
|
|||
|
|
public function testDbVerson(string $input, bool $expected): void
|
|||
|
|
{
|
|||
|
|
/** @var \CoreLibs\DB\IO&MockObject $db_io_mock */
|
|||
|
|
$db_io_mock = $this->createPartialMock(\CoreLibs\DB\IO::class, ['dbVersion']);
|
|||
|
|
$db_io_mock->method('dbVersion')->willReturn('13.6.0');
|
|||
|
|
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected,
|
|||
|
|
// $db->dbCompareVersion($input)
|
|||
|
|
$db_io_mock->dbCompareVersion($input)
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// - connect to DB test (dbGetConnectionStatus)
|
|||
|
|
// - connected get dbInfo data check (show true, false)
|
|||
|
|
// - disconnect: dbClose
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* connection DB strings list with info blocks for connection testing
|
|||
|
|
*
|
|||
|
|
* @return array
|
|||
|
|
*/
|
|||
|
|
public function connectionProvider(): array
|
|||
|
|
{
|
|||
|
|
// 0: connection array
|
|||
|
|
// 1: status after connection
|
|||
|
|
// 2: info string
|
|||
|
|
// 3: ???
|
|||
|
|
return [
|
|||
|
|
'invalid connection' => [
|
|||
|
|
self::$db_config['invalid'],
|
|||
|
|
false,
|
|||
|
|
"-DB-info-> Connected to db '' with schema 'public' as user "
|
|||
|
|
. "'' at host '' on port '5432' with ssl mode 'allow' **** "
|
|||
|
|
. "-DB-info-> DB IO Class debug output: Yes **** ",
|
|||
|
|
null,
|
|||
|
|
],
|
|||
|
|
'valid connection' => [
|
|||
|
|
self::$db_config['valid'],
|
|||
|
|
true,
|
|||
|
|
"-DB-info-> Connected to db 'corelibs_db_io_test' with "
|
|||
|
|
. "schema 'public' as user 'corelibs_db_io_test' at host "
|
|||
|
|
. "'localhost' on port '5432' with ssl mode 'allow' **** "
|
|||
|
|
. "-DB-info-> DB IO Class debug output: Yes **** ",
|
|||
|
|
null,
|
|||
|
|
],
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Connection tests and confirmation with info blocks
|
|||
|
|
*
|
|||
|
|
* @covers ::__connectToDB
|
|||
|
|
* @dataProvider connectionProvider
|
|||
|
|
* @testdox Connection will be $expected [$_dataName]
|
|||
|
|
*
|
|||
|
|
* @return void
|
|||
|
|
*/
|
|||
|
|
public function testConnection(
|
|||
|
|
array $connection,
|
|||
|
|
bool $expected_status,
|
|||
|
|
string $expected_string
|
|||
|
|
): void {
|
|||
|
|
$db = new \CoreLibs\DB\IO(
|
|||
|
|
$connection,
|
|||
|
|
self::$log
|
|||
|
|
);
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected_status,
|
|||
|
|
$db->dbGetConnectionStatus(),
|
|||
|
|
);
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected_string,
|
|||
|
|
$db->dbInfo(false, true)
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// print "DB: " . $db->dbInfo(false, true) . "\n";
|
|||
|
|
if ($db->dbGetConnectionStatus()) {
|
|||
|
|
// db close check
|
|||
|
|
$db->dbClose();
|
|||
|
|
$this->assertEquals(
|
|||
|
|
false,
|
|||
|
|
$db->dbGetConnectionStatus()
|
|||
|
|
);
|
|||
|
|
} else {
|
|||
|
|
// TODO: error checks
|
|||
|
|
// print "LAST ERROR: " . $db->dbGetLastError(true) . "\n";
|
|||
|
|
// print "ERRORS: " . print_r($db->dbGetCombinedErrorHistory(), true) . "\n";
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// - debug flag sets
|
|||
|
|
// dbGetDebug, dbSetDebug, dbToggleDebug
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* test set for setDebug
|
|||
|
|
*
|
|||
|
|
* @return array
|
|||
|
|
*/
|
|||
|
|
public function debugSetProvider(): array
|
|||
|
|
{
|
|||
|
|
return [
|
|||
|
|
'default debug set' => [
|
|||
|
|
// what base connection
|
|||
|
|
'valid',
|
|||
|
|
// actions (set)
|
|||
|
|
null,
|
|||
|
|
// set exepected
|
|||
|
|
self::$db_config['valid']['db_debug'],
|
|||
|
|
],
|
|||
|
|
'set debug to true' => [
|
|||
|
|
'valid_debug_false',
|
|||
|
|
true,
|
|||
|
|
true,
|
|||
|
|
],
|
|||
|
|
'set debug to false' => [
|
|||
|
|
'valid',
|
|||
|
|
false,
|
|||
|
|
false,
|
|||
|
|
]
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* test set for toggleDEbug
|
|||
|
|
*
|
|||
|
|
* @return array
|
|||
|
|
*/
|
|||
|
|
public function debugToggleProvider(): array
|
|||
|
|
{
|
|||
|
|
return [
|
|||
|
|
'default debug set' => [
|
|||
|
|
// what base connection
|
|||
|
|
'valid',
|
|||
|
|
// actions
|
|||
|
|
null,
|
|||
|
|
// toggle is inverse
|
|||
|
|
self::$db_config['valid']['db_debug'] ? false : true,
|
|||
|
|
],
|
|||
|
|
'toggle debug to true' => [
|
|||
|
|
'valid_debug_false',
|
|||
|
|
true,
|
|||
|
|
true,
|
|||
|
|
],
|
|||
|
|
'toggle debug to false' => [
|
|||
|
|
'valid',
|
|||
|
|
false,
|
|||
|
|
false,
|
|||
|
|
]
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Test dbSetDbug, dbGetDebug
|
|||
|
|
*
|
|||
|
|
* @covers ::dbGetDbug
|
|||
|
|
* @covers ::dbSetDebug
|
|||
|
|
* @dataProvider debugSetProvider
|
|||
|
|
* @testdox Setting debug $set will be $expected [$_dataName]
|
|||
|
|
*
|
|||
|
|
* @return void
|
|||
|
|
*/
|
|||
|
|
public function testDbSetDebug(
|
|||
|
|
string $connection,
|
|||
|
|
?bool $set,
|
|||
|
|
bool $expected
|
|||
|
|
): void {
|
|||
|
|
$db = new \CoreLibs\DB\IO(
|
|||
|
|
self::$db_config[$connection],
|
|||
|
|
self::$log
|
|||
|
|
);
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected,
|
|||
|
|
$set === null ?
|
|||
|
|
$db->dbSetDebug() :
|
|||
|
|
$db->dbSetDebug($set)
|
|||
|
|
);
|
|||
|
|
// must always match
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected,
|
|||
|
|
$db->dbGetDebug()
|
|||
|
|
);
|
|||
|
|
$db->dbClose();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Test dbToggleDebug, dbGetDebug
|
|||
|
|
*
|
|||
|
|
* @covers ::dbGetDbug
|
|||
|
|
* @covers ::dbSetDebug
|
|||
|
|
* @dataProvider debugToggleProvider
|
|||
|
|
* @testdox Toggle debug $toggle will be $expected [$_dataName]
|
|||
|
|
*
|
|||
|
|
* @return void
|
|||
|
|
*/
|
|||
|
|
public function testDbToggleDebug(
|
|||
|
|
string $connection,
|
|||
|
|
?bool $toggle,
|
|||
|
|
bool $expected
|
|||
|
|
): void {
|
|||
|
|
$db = new \CoreLibs\DB\IO(
|
|||
|
|
self::$db_config[$connection],
|
|||
|
|
self::$log
|
|||
|
|
);
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected,
|
|||
|
|
$toggle === null ?
|
|||
|
|
$db->dbToggleDebug() :
|
|||
|
|
$db->dbToggleDebug($toggle)
|
|||
|
|
);
|
|||
|
|
// must always match
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected,
|
|||
|
|
$db->dbGetDebug()
|
|||
|
|
);
|
|||
|
|
$db->dbClose();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// - set max query call sets
|
|||
|
|
// dbSetMaxQueryCall, dbGetMaxQueryCall
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* test list for max query run settings
|
|||
|
|
*
|
|||
|
|
* @return array
|
|||
|
|
*/
|
|||
|
|
public function maxQueryCallProvider(): array
|
|||
|
|
{
|
|||
|
|
// 0: max call number
|
|||
|
|
// 1: expected flag from set call
|
|||
|
|
// 2: expected number from get call
|
|||
|
|
// 3: expected last warning id
|
|||
|
|
// 4: expected last error id
|
|||
|
|
return [
|
|||
|
|
'set default' => [
|
|||
|
|
null,
|
|||
|
|
true,
|
|||
|
|
\CoreLibs\DB\IO::DEFAULT_MAX_QUERY_CALL,
|
|||
|
|
// expected warning
|
|||
|
|
'',
|
|||
|
|
// expected error
|
|||
|
|
'',
|
|||
|
|
],
|
|||
|
|
'set to -1 with warning' => [
|
|||
|
|
-1,
|
|||
|
|
true,
|
|||
|
|
-1,
|
|||
|
|
// warning 50
|
|||
|
|
'50',
|
|||
|
|
'',
|
|||
|
|
],
|
|||
|
|
'set to 0 with error' => [
|
|||
|
|
0,
|
|||
|
|
false,
|
|||
|
|
\CoreLibs\DB\IO::DEFAULT_MAX_QUERY_CALL,
|
|||
|
|
'',
|
|||
|
|
'51',
|
|||
|
|
],
|
|||
|
|
'set to -2 with error' => [
|
|||
|
|
-2,
|
|||
|
|
false,
|
|||
|
|
\CoreLibs\DB\IO::DEFAULT_MAX_QUERY_CALL,
|
|||
|
|
'',
|
|||
|
|
'51',
|
|||
|
|
],
|
|||
|
|
'set to valid value' => [
|
|||
|
|
10,
|
|||
|
|
true,
|
|||
|
|
10,
|
|||
|
|
'',
|
|||
|
|
'',
|
|||
|
|
]
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Test max query call set and get flow with warging/errors
|
|||
|
|
*
|
|||
|
|
* @covers ::dbSetMaxQueryCall
|
|||
|
|
* @covers ::dbGetMaxQueryCall
|
|||
|
|
* @dataProvider maxQueryCallProvider
|
|||
|
|
* @testdox Set max query call with $max_calls out with $expected_flag and $expected_max_calls (Warning: $warning/Error: $error) [$_dataName]
|
|||
|
|
*
|
|||
|
|
* @param integer|null $max_calls
|
|||
|
|
* @param boolean $expected_flag
|
|||
|
|
* @param integer $expected_max_calls
|
|||
|
|
* @param string $warning
|
|||
|
|
* @param string $error
|
|||
|
|
* @return void
|
|||
|
|
*/
|
|||
|
|
public function testMaxQueryCall(
|
|||
|
|
?int $max_calls,
|
|||
|
|
bool $expected_flag,
|
|||
|
|
int $expected_max_calls,
|
|||
|
|
string $warning,
|
|||
|
|
string $error
|
|||
|
|
): void {
|
|||
|
|
$db = new \CoreLibs\DB\IO(
|
|||
|
|
self::$db_config['valid'],
|
|||
|
|
self::$log
|
|||
|
|
);
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected_flag,
|
|||
|
|
// TODO special test with null call too
|
|||
|
|
$max_calls === null ?
|
|||
|
|
$db->dbSetMaxQueryCall() :
|
|||
|
|
$db->dbSetMaxQueryCall($max_calls)
|
|||
|
|
);
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected_max_calls,
|
|||
|
|
$db->dbGetMaxQueryCall()
|
|||
|
|
);
|
|||
|
|
// if string for warning or error is not empty check
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$warning,
|
|||
|
|
$db->dbGetLastWarning()
|
|||
|
|
);
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$error,
|
|||
|
|
$db->dbGetLastError()
|
|||
|
|
);
|
|||
|
|
$db->dbClose();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// - all general data from connection array
|
|||
|
|
// dbGetSetting (name, user, ecnoding, schema, host, port, ssl, debug, password)
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* returns ALL connections sets as a group with
|
|||
|
|
* conneciton name on pos 0 and the connection settings on pos 1
|
|||
|
|
*
|
|||
|
|
* @return array
|
|||
|
|
*/
|
|||
|
|
public function connectionCompleteProvider(): array
|
|||
|
|
{
|
|||
|
|
$connections = [];
|
|||
|
|
foreach (self::$db_config as $connection => $settings) {
|
|||
|
|
$connections['DB Connection: ' . $connection] = [
|
|||
|
|
$connection,
|
|||
|
|
$settings,
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
return $connections;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Test connection array settings return call
|
|||
|
|
*
|
|||
|
|
* @covers ::dbGetSetting
|
|||
|
|
* @dataProvider connectionCompleteProvider
|
|||
|
|
* @testdox Get settings for connection $connection [$_dataName]
|
|||
|
|
*
|
|||
|
|
* @param string $connection,
|
|||
|
|
* @param array $settings
|
|||
|
|
* @return void
|
|||
|
|
*/
|
|||
|
|
public function testGetSetting(string $connection, array $settings): void
|
|||
|
|
{
|
|||
|
|
$db = new \CoreLibs\DB\IO(
|
|||
|
|
self::$db_config[$connection],
|
|||
|
|
self::$log
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// each must match
|
|||
|
|
foreach (
|
|||
|
|
[
|
|||
|
|
'name' => 'db_name',
|
|||
|
|
'user' => 'db_user',
|
|||
|
|
'encoding' => 'db_encoding',
|
|||
|
|
'schema' => 'db_schema',
|
|||
|
|
'host' => 'db_host',
|
|||
|
|
'port' => 'db_port',
|
|||
|
|
'ssl' => 'db_ssl',
|
|||
|
|
'debug' => 'db_debug',
|
|||
|
|
'password' => '***',
|
|||
|
|
] as $read => $compare
|
|||
|
|
) {
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$read == 'password' ? $compare : $settings[$compare],
|
|||
|
|
$db->dbGetSetting($read)
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$db->dbClose();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// - test boolean convert
|
|||
|
|
// dbBoolean
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* test list for dbBoolean
|
|||
|
|
*
|
|||
|
|
* @return array
|
|||
|
|
*/
|
|||
|
|
public function booleanProvider(): array
|
|||
|
|
{
|
|||
|
|
// 0: set
|
|||
|
|
// 1: reverse flag
|
|||
|
|
// 2: expected
|
|||
|
|
return [
|
|||
|
|
'source "t" to true' => [
|
|||
|
|
't',
|
|||
|
|
false,
|
|||
|
|
true,
|
|||
|
|
],
|
|||
|
|
'source "t" to true null flag' => [
|
|||
|
|
't',
|
|||
|
|
null,
|
|||
|
|
true,
|
|||
|
|
],
|
|||
|
|
'source "true" to true' => [
|
|||
|
|
'true',
|
|||
|
|
false,
|
|||
|
|
true,
|
|||
|
|
],
|
|||
|
|
'source "f" to false' => [
|
|||
|
|
'f',
|
|||
|
|
false,
|
|||
|
|
false,
|
|||
|
|
],
|
|||
|
|
'source "false" to false' => [
|
|||
|
|
'false',
|
|||
|
|
false,
|
|||
|
|
false,
|
|||
|
|
],
|
|||
|
|
'source anything to true' => [
|
|||
|
|
'something',
|
|||
|
|
false,
|
|||
|
|
true,
|
|||
|
|
],
|
|||
|
|
'source empty to false' => [
|
|||
|
|
'',
|
|||
|
|
false,
|
|||
|
|
false,
|
|||
|
|
],
|
|||
|
|
'source bool true to "t"' => [
|
|||
|
|
true,
|
|||
|
|
true,
|
|||
|
|
't',
|
|||
|
|
],
|
|||
|
|
'source bool false to "f"' => [
|
|||
|
|
false,
|
|||
|
|
true,
|
|||
|
|
'f',
|
|||
|
|
],
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Undocumented function
|
|||
|
|
*
|
|||
|
|
* @covers ::dbBoolean
|
|||
|
|
* @dataProvider booleanProvider
|
|||
|
|
* @testdox Have $source and convert ($reverse) to $expected [$_dataName]
|
|||
|
|
*
|
|||
|
|
* @param string|bool $source
|
|||
|
|
* @param bool|null $reverse
|
|||
|
|
* @param string|bool $expected
|
|||
|
|
* @return void
|
|||
|
|
*/
|
|||
|
|
public function testDbBoolean($source, ?bool $reverse, $expected): void
|
|||
|
|
{
|
|||
|
|
$db = new \CoreLibs\DB\IO(
|
|||
|
|
self::$db_config['valid'],
|
|||
|
|
self::$log
|
|||
|
|
);
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected,
|
|||
|
|
$reverse === null ?
|
|||
|
|
$db->dbBoolean($source) :
|
|||
|
|
$db->dbBoolean($source, $reverse)
|
|||
|
|
);
|
|||
|
|
$db->dbClose();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// - test interval/age string conversion to
|
|||
|
|
// \CoreLibs\Combined\DateTime::stringToTime/timeStringFormat compatbile
|
|||
|
|
// dbTimeFormat
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* test list for timestamp parsers
|
|||
|
|
*
|
|||
|
|
* @return array
|
|||
|
|
*/
|
|||
|
|
public function timeFormatProvider(): array
|
|||
|
|
{
|
|||
|
|
// 0: set
|
|||
|
|
// 1: micro seconds flag
|
|||
|
|
// 2: expected
|
|||
|
|
return [
|
|||
|
|
'interval a' => [
|
|||
|
|
'41 years 9 mons 18 days',
|
|||
|
|
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,
|
|||
|
|
'41 years 9 mons 18 days 12h 31m 11s'
|
|||
|
|
],
|
|||
|
|
'interval a-2' => [
|
|||
|
|
'41 years 9 mons 18 days 12:31:11.87418',
|
|||
|
|
false,
|
|||
|
|
'41 years 9 mons 18 days 12h 31m 11s'
|
|||
|
|
],
|
|||
|
|
'interval a-2-1' => [
|
|||
|
|
'41 years 9 mons 18 days 12:31:11.87418',
|
|||
|
|
true,
|
|||
|
|
'41 years 9 mons 18 days 12h 31m 11s 87418ms'
|
|||
|
|
],
|
|||
|
|
'interval a-3' => [
|
|||
|
|
'41 years 9 mons 18 days 12:00:11',
|
|||
|
|
false,
|
|||
|
|
'41 years 9 mons 18 days 12h 11s'
|
|||
|
|
],
|
|||
|
|
'interval b' => [
|
|||
|
|
'1218 days',
|
|||
|
|
false,
|
|||
|
|
'1218 days'
|
|||
|
|
],
|
|||
|
|
'interval c' => [
|
|||
|
|
'1 year 1 day',
|
|||
|
|
false,
|
|||
|
|
'1 year 1 day'
|
|||
|
|
],
|
|||
|
|
'interval d' => [
|
|||
|
|
'12:00:05',
|
|||
|
|
false,
|
|||
|
|
'12h 5s'
|
|||
|
|
],
|
|||
|
|
'interval e' => [
|
|||
|
|
'00:00:00.12345',
|
|||
|
|
true,
|
|||
|
|
'12345ms'
|
|||
|
|
],
|
|||
|
|
'interval e-1' => [
|
|||
|
|
'00:00:00',
|
|||
|
|
true,
|
|||
|
|
'0s'
|
|||
|
|
],
|
|||
|
|
'interval a (negative)' => [
|
|||
|
|
'-41 years 9 mons 18 days 00:05:00',
|
|||
|
|
false,
|
|||
|
|
'-41 years 9 mons 18 days 5m'
|
|||
|
|
],
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Test parsing of interval strings into human readable format
|
|||
|
|
*
|
|||
|
|
* @covers ::dbTimeFormat
|
|||
|
|
* @dataProvider timeFormatProvider
|
|||
|
|
* @testdox Have $source and convert ($show_micro) to $expected [$_dataName]
|
|||
|
|
*
|
|||
|
|
* @param string $source
|
|||
|
|
* @param bool|null $show_micro
|
|||
|
|
* @param string $expected
|
|||
|
|
* @return void
|
|||
|
|
*/
|
|||
|
|
public function testDbTimeFormat(string $source, ?bool $show_micro, string $expected): void
|
|||
|
|
{
|
|||
|
|
$db = new \CoreLibs\DB\IO(
|
|||
|
|
self::$db_config['valid'],
|
|||
|
|
self::$log
|
|||
|
|
);
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected,
|
|||
|
|
$show_micro === null ?
|
|||
|
|
$db->dbTimeFormat($source) :
|
|||
|
|
$db->dbTimeFormat($source, $show_micro)
|
|||
|
|
);
|
|||
|
|
$db->dbClose();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// - convert PostreSQL arrays into PHP
|
|||
|
|
// dbArrayParse
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* test list for array convert
|
|||
|
|
*
|
|||
|
|
* @return array
|
|||
|
|
*/
|
|||
|
|
public function arrayProvider(): array
|
|||
|
|
{
|
|||
|
|
// 0: postgresql array string
|
|||
|
|
// 1: php array
|
|||
|
|
return [
|
|||
|
|
'array 1' => [
|
|||
|
|
'{1,2,3,"4 this is shit"}',
|
|||
|
|
[1, 2, 3, "4 this is shit"]
|
|||
|
|
],
|
|||
|
|
'array 2' => [
|
|||
|
|
'{{1,2,3},{4,5,6}}',
|
|||
|
|
[[1, 2, 3], [4, 5, 6]]
|
|||
|
|
],
|
|||
|
|
'array 3' => [
|
|||
|
|
'{{{1,2},{3}},{{4},{5,6}}}',
|
|||
|
|
[[[1, 2], [3]], [[4], [5, 6]]]
|
|||
|
|
],
|
|||
|
|
'array 4' => [
|
|||
|
|
'{dfasdf,"qw,,e{q\"we",\'qrer\'}',
|
|||
|
|
['dfasdf', 'qw,,e{q"we', 'qrer']
|
|||
|
|
]
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* test convert PostgreSQL array to PHP array
|
|||
|
|
*
|
|||
|
|
* @covers ::dbArrayParse
|
|||
|
|
* @dataProvider arrayProvider
|
|||
|
|
* @testdox Input array string $input to $expected [$_dataName]
|
|||
|
|
*
|
|||
|
|
* @param string $input
|
|||
|
|
* @param array|bool $expected
|
|||
|
|
* @return void
|
|||
|
|
*/
|
|||
|
|
public function testDbArrayParse(string $input, $expected): void
|
|||
|
|
{
|
|||
|
|
$db = new \CoreLibs\DB\IO(
|
|||
|
|
self::$db_config['valid'],
|
|||
|
|
self::$log
|
|||
|
|
);
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected,
|
|||
|
|
$db->dbArrayParse($input)
|
|||
|
|
);
|
|||
|
|
$db->dbClose();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// - string escape tests
|
|||
|
|
// dbEscapeString, dbEscapeLiteral, dbEscapeIdentifier,
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* test list for string encodig
|
|||
|
|
*
|
|||
|
|
* @return array
|
|||
|
|
*/
|
|||
|
|
public function stringProvider(): array
|
|||
|
|
{
|
|||
|
|
// 0: input
|
|||
|
|
// 1: expected string
|
|||
|
|
// 2: expected literal
|
|||
|
|
// 3: expected identifier
|
|||
|
|
return [
|
|||
|
|
'string normal' => [
|
|||
|
|
'Foo Bar',
|
|||
|
|
'Foo Bar',
|
|||
|
|
'\'Foo Bar\'',
|
|||
|
|
'"Foo Bar"',
|
|||
|
|
],
|
|||
|
|
'string quotes' => [
|
|||
|
|
'Foo \'" Bar',
|
|||
|
|
'Foo \'\'" Bar',
|
|||
|
|
'\'Foo \'\'" Bar\'',
|
|||
|
|
'"Foo \'"" Bar"',
|
|||
|
|
],
|
|||
|
|
'string backslash' => [
|
|||
|
|
'Foo \ Bar',
|
|||
|
|
'Foo \ Bar',
|
|||
|
|
' E\'Foo \\\\ Bar\'',
|
|||
|
|
'"Foo \ Bar"',
|
|||
|
|
],
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Check all string escape functions
|
|||
|
|
* NOTE:
|
|||
|
|
* This depends on the SETTINGS of the DB
|
|||
|
|
* The expected setting is the default encoding setting in PostgreSQL
|
|||
|
|
* #backslash_quote = safe_encoding
|
|||
|
|
* #escape_string_warning = on
|
|||
|
|
* TODO: Load current settings from DB and ajust comapre string
|
|||
|
|
*
|
|||
|
|
* @covers ::dbEscapeString
|
|||
|
|
* @covers ::dbEscapeLiteral
|
|||
|
|
* @covers ::dbEscapeIdentifier
|
|||
|
|
* @dataProvider stringProvider
|
|||
|
|
* @testdox Input string $input to $expected [$_dataName]
|
|||
|
|
*
|
|||
|
|
* @param string $input
|
|||
|
|
* @param string $expected
|
|||
|
|
* @return void
|
|||
|
|
*/
|
|||
|
|
public function testStringEscape(
|
|||
|
|
string $input,
|
|||
|
|
string $expected_string,
|
|||
|
|
string $expected_literal,
|
|||
|
|
string $expected_identifier
|
|||
|
|
): void {
|
|||
|
|
$db = new \CoreLibs\DB\IO(
|
|||
|
|
self::$db_config['valid'],
|
|||
|
|
self::$log
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// print "String: " . $input . "\n";
|
|||
|
|
// print "Escape String: -" . $db->dbEscapeString($input) . "-\n";
|
|||
|
|
// print "Escape Literal: -" . $db->dbEscapeLiteral($input) . "-\n";
|
|||
|
|
// print "Escape Identifier: -" . $db->dbEscapeIdentifier($input) . "-\n";
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected_string,
|
|||
|
|
$db->dbEscapeString($input)
|
|||
|
|
);
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected_literal,
|
|||
|
|
$db->dbEscapeLiteral($input)
|
|||
|
|
);
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected_identifier,
|
|||
|
|
$db->dbEscapeIdentifier($input)
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
$db->dbClose();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// - bytea encoding
|
|||
|
|
// dbEscapeBytea
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* test bytea encoding list
|
|||
|
|
*
|
|||
|
|
* @return array
|
|||
|
|
*/
|
|||
|
|
public function byteaProvider(): array
|
|||
|
|
{
|
|||
|
|
// 0: string in
|
|||
|
|
// 1: bytea expected
|
|||
|
|
return [
|
|||
|
|
'standard empty string' => [
|
|||
|
|
'',
|
|||
|
|
'\x'
|
|||
|
|
],
|
|||
|
|
'random values' => [
|
|||
|
|
'""9f8a!1012938123712378a../%(\'%)"!"#0"#$%\'"#$00"#$0"#0$0"#$',
|
|||
|
|
'\x2222396638612131303132393338313233373132333738612e2e2f2528272529222122233022232425272223243030222324302223302430222324'
|
|||
|
|
],
|
|||
|
|
'random text' => [
|
|||
|
|
'string d',
|
|||
|
|
'\x737472696e672064'
|
|||
|
|
]
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Test bytea escape
|
|||
|
|
* NOTE:
|
|||
|
|
* This depends on bytea encoding settings on the server
|
|||
|
|
* #bytea_output = 'hex'
|
|||
|
|
*
|
|||
|
|
* @covers ::dbEscapeBytea
|
|||
|
|
* @dataProvider byteaProvider
|
|||
|
|
* @testdox Input bytea $input to $expected [$_dataName]
|
|||
|
|
*
|
|||
|
|
* @param string $input
|
|||
|
|
* @param string $expected
|
|||
|
|
* @return void
|
|||
|
|
*/
|
|||
|
|
public function testByteaEscape(string $input, string $expected): void
|
|||
|
|
{
|
|||
|
|
$db = new \CoreLibs\DB\IO(
|
|||
|
|
self::$db_config['valid'],
|
|||
|
|
self::$log
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected,
|
|||
|
|
$db->dbEscapeBytea($input),
|
|||
|
|
'Assert error to bytea'
|
|||
|
|
);
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$input,
|
|||
|
|
$db->dbUnescapeBytea($expected),
|
|||
|
|
'Assert error from bytes'
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
$db->dbClose();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// - string escape catcher
|
|||
|
|
// dbSqlEscape
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* test list for sql escape function
|
|||
|
|
*
|
|||
|
|
* @return array
|
|||
|
|
*/
|
|||
|
|
public function sqlEscapeProvider(): array
|
|||
|
|
{
|
|||
|
|
// 0: data in
|
|||
|
|
// 1: flag
|
|||
|
|
// 2: expected output
|
|||
|
|
return [
|
|||
|
|
// int (standard)
|
|||
|
|
'integer value' => [1, 'i', 1,],
|
|||
|
|
'bad integer value' => ['za', 'i', 0,],
|
|||
|
|
'empty integer value' => ['', 'i', 'NULL',],
|
|||
|
|
'null integer value' => [null, 'i', 'NULL',],
|
|||
|
|
// float (standard)
|
|||
|
|
'float value' => [1.1, 'f', 1.1,],
|
|||
|
|
'bad float value' => ['za', 'f', 0,],
|
|||
|
|
'empty float value' => ['', 'f', 'NULL',],
|
|||
|
|
'null float value' => [null, 'f', 'NULL',],
|
|||
|
|
// text (varchar)
|
|||
|
|
'string value' => ['string value', 't', '\'string value\'',],
|
|||
|
|
'empty string value' => ['', 't', '\'\'',],
|
|||
|
|
'null string value' => [null, 't', 'NULL',],
|
|||
|
|
// text literal (don't worry about ' around string)
|
|||
|
|
'string value literal' => ['string literal', 'tl', '\'string literal\'',],
|
|||
|
|
'empty string value literal' => ['', 'tl', '\'\'',],
|
|||
|
|
'null string value literal' => [null, 'tl', 'NULL',],
|
|||
|
|
// ?d (I have no idea what that does, is like string)
|
|||
|
|
'string value d' => ['string d', 'd', '\'string d\'',],
|
|||
|
|
'empty string value d' => ['', 'd', 'NULL',],
|
|||
|
|
'null string value d' => [null, 'd', 'NULL',],
|
|||
|
|
// by bytea
|
|||
|
|
'string value d' => ['string d', 'by', '\x737472696e672064',],
|
|||
|
|
'empty string value d' => ['', 'by', 'NULL',],
|
|||
|
|
'null string value d' => [null, 'by', 'NULL',],
|
|||
|
|
// b (bool)
|
|||
|
|
'bool true value' => [true, 'b', '\'t\'',],
|
|||
|
|
'bool false value' => [false, 'b', '\'f\'',],
|
|||
|
|
'empty bool value' => ['', 'b', 'NULL',],
|
|||
|
|
'null bool value' => [null, 'b', 'NULL',],
|
|||
|
|
// i2 (integer but with 0 instead of NULL for empty)
|
|||
|
|
'integer2 value' => [1, 'i2', 1,],
|
|||
|
|
'bad integer2 value' => ['za', 'i2', 0,],
|
|||
|
|
'empty integer2 value' => ['', 'i2', 0,],
|
|||
|
|
'null integer2 value' => [null, 'i2', 0,],
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Test for the sql escape/null wrapper function
|
|||
|
|
*
|
|||
|
|
* @covers ::dbSqlEscape
|
|||
|
|
* @dataProvider sqlEscapeProvider
|
|||
|
|
* @testdox Input value $input as $flag to $expected [$_dataName]
|
|||
|
|
*
|
|||
|
|
* @param int|float|string|null $input
|
|||
|
|
* @param string $flag
|
|||
|
|
* @param int|float|string $expected
|
|||
|
|
* @return void
|
|||
|
|
*/
|
|||
|
|
public function testSqlEscape($input, string $flag, $expected): void
|
|||
|
|
{
|
|||
|
|
$db = new \CoreLibs\DB\IO(
|
|||
|
|
self::$db_config['valid'],
|
|||
|
|
self::$log
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected,
|
|||
|
|
$db->dbSqlEscape($input, $flag)
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
$db->dbClose();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// - show table data
|
|||
|
|
// dbShowTableMetaData
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* table meta data return test
|
|||
|
|
*
|
|||
|
|
* @return array
|
|||
|
|
*/
|
|||
|
|
public function tableProvider(): array
|
|||
|
|
{
|
|||
|
|
// 0: table
|
|||
|
|
// 1: schema
|
|||
|
|
// 2: expected array
|
|||
|
|
return [
|
|||
|
|
// disable the default tables, they might change
|
|||
|
|
/* 'table with primary key' => [
|
|||
|
|
'table_with_primary_key',
|
|||
|
|
'',
|
|||
|
|
[]
|
|||
|
|
],
|
|||
|
|
'table without primary key' => [
|
|||
|
|
'table_without_primary_key',
|
|||
|
|
'public',
|
|||
|
|
[]
|
|||
|
|
], */
|
|||
|
|
'simple table' => [
|
|||
|
|
'test_meta',
|
|||
|
|
'',
|
|||
|
|
[
|
|||
|
|
'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' => '',
|
|||
|
|
]
|
|||
|
|
]
|
|||
|
|
],
|
|||
|
|
'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',
|
|||
|
|
false
|
|||
|
|
],
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* test the table meta data return flow
|
|||
|
|
*
|
|||
|
|
* @covers ::dbShowTableMetaData
|
|||
|
|
* @dataProvider tableProvider
|
|||
|
|
* @testdox Check table $table in schema $schema with $expected [$_dataName]
|
|||
|
|
*
|
|||
|
|
* @param string $table
|
|||
|
|
* @param string|null $schema
|
|||
|
|
* @param array<mixed>|bool $expected
|
|||
|
|
* @return void
|
|||
|
|
*/
|
|||
|
|
public function testDbShowTableMetaData(string $table, ?string $schema, $expected): void
|
|||
|
|
{
|
|||
|
|
$db = new \CoreLibs\DB\IO(
|
|||
|
|
self::$db_config['valid'],
|
|||
|
|
self::$log
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// print "TABLE\n" . print_r($db->dbShowTableMetaData($table, $schema), true) . "\n";
|
|||
|
|
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected,
|
|||
|
|
$schema === null ?
|
|||
|
|
$db->dbShowTableMetaData($table) :
|
|||
|
|
$db->dbShowTableMetaData($table, $schema)
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
$db->dbClose();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// - db exec test for insert/update/select/etc
|
|||
|
|
// dbExec, dbResetQueryCalled, dbGetQueryCalled
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* provide queries with return results
|
|||
|
|
*
|
|||
|
|
* @return array
|
|||
|
|
*/
|
|||
|
|
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
|
|||
|
|
return [
|
|||
|
|
// insert
|
|||
|
|
'table with pk insert' => [
|
|||
|
|
'INSERT INTO table_with_primary_key (row_date) VALUES (NOW())',
|
|||
|
|
'',
|
|||
|
|
'resource/object',
|
|||
|
|
'',
|
|||
|
|
'',
|
|||
|
|
],
|
|||
|
|
// 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())',
|
|||
|
|
'',
|
|||
|
|
'resource/object',
|
|||
|
|
'',
|
|||
|
|
'',
|
|||
|
|
],
|
|||
|
|
// INSERT: returning array possible multi insert (32)
|
|||
|
|
'table with pk insert multile' => [
|
|||
|
|
'INSERT INTO table_with_primary_key (row_date) VALUES'
|
|||
|
|
. '(NOW()), '
|
|||
|
|
. '(NOW()), '
|
|||
|
|
. '(NOW()), '
|
|||
|
|
. '(NOW())',
|
|||
|
|
'',
|
|||
|
|
'resource/object',
|
|||
|
|
'32',
|
|||
|
|
'',
|
|||
|
|
],
|
|||
|
|
// Skip PK READING
|
|||
|
|
'table with pk insert and NULL pk name' => [
|
|||
|
|
'INSERT INTO table_with_primary_key (row_date) VALUES (NOW())',
|
|||
|
|
'NULL',
|
|||
|
|
'resource/object',
|
|||
|
|
'',
|
|||
|
|
'',
|
|||
|
|
],
|
|||
|
|
// insert with pk set
|
|||
|
|
'table with pk insert and pk name' => [
|
|||
|
|
'INSERT INTO table_with_primary_key (row_date) VALUES (NOW())',
|
|||
|
|
'table_with_primary_key_id',
|
|||
|
|
'resource/object',
|
|||
|
|
'',
|
|||
|
|
'',
|
|||
|
|
],
|
|||
|
|
// update
|
|||
|
|
'table with pk update' => [
|
|||
|
|
'UPDATE table_with_primary_key SET row_date = NOW()',
|
|||
|
|
'',
|
|||
|
|
'resource/object',
|
|||
|
|
'',
|
|||
|
|
'',
|
|||
|
|
],
|
|||
|
|
'table with pk select' => [
|
|||
|
|
'SELECT * FROM table_with_primary_key',
|
|||
|
|
'',
|
|||
|
|
'resource/object',
|
|||
|
|
'',
|
|||
|
|
'',
|
|||
|
|
],
|
|||
|
|
// no query set, error 11
|
|||
|
|
'no query set' => [
|
|||
|
|
'',
|
|||
|
|
'',
|
|||
|
|
false,
|
|||
|
|
'',
|
|||
|
|
'11',
|
|||
|
|
],
|
|||
|
|
// no db connection setable (16) [needs Mocking]
|
|||
|
|
// TODO failed db connection
|
|||
|
|
// connection busy [async] (41)
|
|||
|
|
// TODO connection busy
|
|||
|
|
// same query run too many times (30)
|
|||
|
|
'same query run too many times' => [
|
|||
|
|
'SELECT row_date FROM table_with_primary_key',
|
|||
|
|
'',
|
|||
|
|
'resource/object',
|
|||
|
|
'',
|
|||
|
|
'30',
|
|||
|
|
true,
|
|||
|
|
],
|
|||
|
|
// execution failed (13)
|
|||
|
|
'invalid query' => [
|
|||
|
|
'INVALID',
|
|||
|
|
'',
|
|||
|
|
false,
|
|||
|
|
'',
|
|||
|
|
'13'
|
|||
|
|
],
|
|||
|
|
// INSERT: cursor invalid for fetch PK (34) [unreachable code]
|
|||
|
|
// INSERT: returning has no data (33)
|
|||
|
|
// invalid RETURNING columns
|
|||
|
|
// 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
|
|||
|
|
// FIX with socket check type
|
|||
|
|
'invalid returning' => [
|
|||
|
|
'INSERT INTO table_with_primary_key (row_date) VALUES (NOW()) RETURNING invalid',
|
|||
|
|
'',
|
|||
|
|
false,
|
|||
|
|
'',
|
|||
|
|
'13'
|
|||
|
|
],
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* pure dbExec checker
|
|||
|
|
* does not check __dbPostExec run, this will be done in the dbGet* functions
|
|||
|
|
* 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|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,
|
|||
|
|
$expected_return,
|
|||
|
|
string $warning,
|
|||
|
|
string $error,
|
|||
|
|
bool $run_many_times = false
|
|||
|
|
): void {
|
|||
|
|
// self::$log->setLogLevelAll('debug', true);
|
|||
|
|
// self::$log->setLogLevelAll('print', true);
|
|||
|
|
$db = new \CoreLibs\DB\IO(
|
|||
|
|
self::$db_config['valid'],
|
|||
|
|
self::$log
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// clear any current query
|
|||
|
|
// $db->dbResetQuery();
|
|||
|
|
|
|||
|
|
// assert never called query is 0
|
|||
|
|
$this->assertEquals(
|
|||
|
|
0,
|
|||
|
|
$db->dbGetQueryCalled($query),
|
|||
|
|
'Assert never called query is null'
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// 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
|
|||
|
|
|
|||
|
|
if (is_bool($expected_return)) {
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected_return,
|
|||
|
|
// supress ANY errors here
|
|||
|
|
$pk_name === null ?
|
|||
|
|
@$db->dbExec($query) :
|
|||
|
|
@$db->dbExec($query, $pk_name)
|
|||
|
|
);
|
|||
|
|
} 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
|
|||
|
|
);
|
|||
|
|
} else {
|
|||
|
|
$this->assertIsResource(
|
|||
|
|
$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) :
|
|||
|
|
$db->dbExec($query, $pk_name);
|
|||
|
|
}
|
|||
|
|
// will fail now
|
|||
|
|
$this->assertFalse(
|
|||
|
|
$pk_name === null ?
|
|||
|
|
$db->dbExec($query) :
|
|||
|
|
$db->dbExec($query, $pk_name)
|
|||
|
|
);
|
|||
|
|
// 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->subAssertErrorTest($db, $warning, $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<mixed>|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
|
|||
|
|
// if string for warning or error is not empty check
|
|||
|
|
$this->subAssertErrorTest($db, $warning, $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<mixed>|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
|
|||
|
|
// if string for warning or error is not empty check
|
|||
|
|
$this->subAssertErrorTest($db, $warning, $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";
|
|||
|
|
$row_a = [
|
|||
|
|
'row_int' => 1,
|
|||
|
|
0 => 1,
|
|||
|
|
'uid' => 'A',
|
|||
|
|
1 => 'A'
|
|||
|
|
];
|
|||
|
|
$row_a_assoc = [
|
|||
|
|
'row_int' => 1,
|
|||
|
|
'uid' => 'A',
|
|||
|
|
];
|
|||
|
|
$row_b = [
|
|||
|
|
'row_int' => 2,
|
|||
|
|
0 => 2,
|
|||
|
|
'uid' => 'B',
|
|||
|
|
1 => 'B'
|
|||
|
|
];
|
|||
|
|
$row_b_assoc = [
|
|||
|
|
'row_int' => 2,
|
|||
|
|
'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
|
|||
|
|
return [
|
|||
|
|
// *** READ STEP BY STEP
|
|||
|
|
// default cache: USE_CACHE
|
|||
|
|
'valid select, default cache settings' => [
|
|||
|
|
$read_query,
|
|||
|
|
null,
|
|||
|
|
null,
|
|||
|
|
$row_a,
|
|||
|
|
true,
|
|||
|
|
// check cursor_ext
|
|||
|
|
[
|
|||
|
|
// if <8.1 check against resource
|
|||
|
|
'cursor' => 'PgSql\Result',
|
|||
|
|
'data' => [
|
|||
|
|
0 => $row_a,
|
|||
|
|
],
|
|||
|
|
'field_names' => [
|
|||
|
|
'row_int',
|
|||
|
|
'uid'
|
|||
|
|
],
|
|||
|
|
'num_fields' => 2,
|
|||
|
|
'num_rows' => 2,
|
|||
|
|
'pos' => 1,
|
|||
|
|
'query' => $read_query,
|
|||
|
|
'read_rows' => 1,
|
|||
|
|
'cache_flag' => \CoreLibs\DB\IO::USE_CACHE,
|
|||
|
|
'assoc_flag' => false,
|
|||
|
|
'cached' => true,
|
|||
|
|
'finished' => false,
|
|||
|
|
'read_finished' => false,
|
|||
|
|
'db_read_finished' => false,
|
|||
|
|
],
|
|||
|
|
// extended cursor per step (first only true)
|
|||
|
|
[
|
|||
|
|
// second row
|
|||
|
|
[
|
|||
|
|
'data' => [
|
|||
|
|
0 => $row_b,
|
|||
|
|
],
|
|||
|
|
'cursor' => [
|
|||
|
|
'cursor' => 'PgSql\Result',
|
|||
|
|
'data' => [
|
|||
|
|
0 => $row_a,
|
|||
|
|
1 => $row_b,
|
|||
|
|
],
|
|||
|
|
'field_names' => [
|
|||
|
|
'row_int',
|
|||
|
|
'uid'
|
|||
|
|
],
|
|||
|
|
'num_fields' => 2,
|
|||
|
|
'num_rows' => 2,
|
|||
|
|
'pos' => 2,
|
|||
|
|
'query' => $read_query,
|
|||
|
|
'read_rows' => 2,
|
|||
|
|
'cache_flag' => \CoreLibs\DB\IO::USE_CACHE,
|
|||
|
|
'assoc_flag' => false,
|
|||
|
|
'cached' => true,
|
|||
|
|
'finished' => false,
|
|||
|
|
'read_finished' => true,
|
|||
|
|
'db_read_finished' => true,
|
|||
|
|
]
|
|||
|
|
],
|
|||
|
|
// end row, false
|
|||
|
|
[
|
|||
|
|
'data' => false,
|
|||
|
|
'cursor' => [
|
|||
|
|
'cursor' => 1,
|
|||
|
|
'data' => [
|
|||
|
|
0 => $row_a,
|
|||
|
|
1 => $row_b,
|
|||
|
|
],
|
|||
|
|
'field_names' => [
|
|||
|
|
'row_int',
|
|||
|
|
'uid'
|
|||
|
|
],
|
|||
|
|
'num_fields' => 2,
|
|||
|
|
'num_rows' => 2,
|
|||
|
|
'pos' => 0,
|
|||
|
|
'query' => $read_query,
|
|||
|
|
'read_rows' => 2,
|
|||
|
|
'cache_flag' => \CoreLibs\DB\IO::USE_CACHE,
|
|||
|
|
'assoc_flag' => false,
|
|||
|
|
'cached' => true,
|
|||
|
|
'finished' => true,
|
|||
|
|
'read_finished' => true,
|
|||
|
|
'db_read_finished' => true,
|
|||
|
|
]
|
|||
|
|
]
|
|||
|
|
],
|
|||
|
|
'',
|
|||
|
|
'',
|
|||
|
|
$insert_query
|
|||
|
|
],
|
|||
|
|
'valid select, use cache, assoc only' => [
|
|||
|
|
$read_query,
|
|||
|
|
\CoreLibs\DB\IO::USE_CACHE,
|
|||
|
|
true,
|
|||
|
|
$row_a_assoc,
|
|||
|
|
true,
|
|||
|
|
// is same as default
|
|||
|
|
[
|
|||
|
|
'cursor' => 'PgSql\Result',
|
|||
|
|
'data' => [
|
|||
|
|
0 => $row_a_assoc,
|
|||
|
|
],
|
|||
|
|
'field_names' => [
|
|||
|
|
'row_int',
|
|||
|
|
'uid'
|
|||
|
|
],
|
|||
|
|
'num_fields' => 2,
|
|||
|
|
'num_rows' => 2,
|
|||
|
|
'pos' => 1,
|
|||
|
|
'query' => $read_query,
|
|||
|
|
'read_rows' => 1,
|
|||
|
|
'cache_flag' => \CoreLibs\DB\IO::USE_CACHE,
|
|||
|
|
'assoc_flag' => true,
|
|||
|
|
'cached' => true,
|
|||
|
|
'finished' => false,
|
|||
|
|
'read_finished' => false,
|
|||
|
|
'db_read_finished' => false,
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
// second row
|
|||
|
|
[
|
|||
|
|
'data' => [
|
|||
|
|
0 => $row_b_assoc,
|
|||
|
|
],
|
|||
|
|
'cursor' => [
|
|||
|
|
'cursor' => 'PgSql\Result',
|
|||
|
|
'data' => [
|
|||
|
|
0 => $row_a_assoc,
|
|||
|
|
1 => $row_b_assoc,
|
|||
|
|
],
|
|||
|
|
'field_names' => [
|
|||
|
|
'row_int',
|
|||
|
|
'uid'
|
|||
|
|
],
|
|||
|
|
'num_fields' => 2,
|
|||
|
|
'num_rows' => 2,
|
|||
|
|
'pos' => 2,
|
|||
|
|
'query' => $read_query,
|
|||
|
|
'read_rows' => 2,
|
|||
|
|
'cache_flag' => \CoreLibs\DB\IO::USE_CACHE,
|
|||
|
|
'assoc_flag' => true,
|
|||
|
|
'cached' => true,
|
|||
|
|
'finished' => false,
|
|||
|
|
'read_finished' => true,
|
|||
|
|
'db_read_finished' => true,
|
|||
|
|
]
|
|||
|
|
],
|
|||
|
|
// end row, false
|
|||
|
|
[
|
|||
|
|
'data' => false,
|
|||
|
|
'cursor' => [
|
|||
|
|
'cursor' => 1,
|
|||
|
|
'data' => [
|
|||
|
|
0 => $row_a_assoc,
|
|||
|
|
1 => $row_b_assoc,
|
|||
|
|
],
|
|||
|
|
'field_names' => [
|
|||
|
|
'row_int',
|
|||
|
|
'uid'
|
|||
|
|
],
|
|||
|
|
'num_fields' => 2,
|
|||
|
|
'num_rows' => 2,
|
|||
|
|
'pos' => 0,
|
|||
|
|
'query' => $read_query,
|
|||
|
|
'read_rows' => 2,
|
|||
|
|
'cache_flag' => \CoreLibs\DB\IO::USE_CACHE,
|
|||
|
|
'assoc_flag' => true,
|
|||
|
|
'cached' => true,
|
|||
|
|
'finished' => true,
|
|||
|
|
'read_finished' => true,
|
|||
|
|
'db_read_finished' => true,
|
|||
|
|
]
|
|||
|
|
]
|
|||
|
|
],
|
|||
|
|
'',
|
|||
|
|
'',
|
|||
|
|
$insert_query
|
|||
|
|
],
|
|||
|
|
'valid select, read new, assoc only' => [
|
|||
|
|
$read_query,
|
|||
|
|
\CoreLibs\DB\IO::READ_NEW,
|
|||
|
|
true,
|
|||
|
|
$row_a_assoc,
|
|||
|
|
true,
|
|||
|
|
[
|
|||
|
|
'cursor' => 'PgSql\Result',
|
|||
|
|
'data' => [
|
|||
|
|
0 => $row_a_assoc,
|
|||
|
|
],
|
|||
|
|
'field_names' => [
|
|||
|
|
'row_int',
|
|||
|
|
'uid'
|
|||
|
|
],
|
|||
|
|
'num_fields' => 2,
|
|||
|
|
'num_rows' => 2,
|
|||
|
|
'pos' => 1,
|
|||
|
|
'query' => $read_query,
|
|||
|
|
'read_rows' => 1,
|
|||
|
|
'cache_flag' => \CoreLibs\DB\IO::READ_NEW,
|
|||
|
|
'assoc_flag' => true,
|
|||
|
|
'cached' => true,
|
|||
|
|
'finished' => false,
|
|||
|
|
'read_finished' => false,
|
|||
|
|
'db_read_finished' => false,
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
// second row
|
|||
|
|
[
|
|||
|
|
'data' => [
|
|||
|
|
0 => $row_b_assoc,
|
|||
|
|
],
|
|||
|
|
'cursor' => [
|
|||
|
|
'cursor' => 'PgSql\Result',
|
|||
|
|
'data' => [
|
|||
|
|
0 => $row_a_assoc,
|
|||
|
|
1 => $row_b_assoc,
|
|||
|
|
],
|
|||
|
|
'field_names' => [
|
|||
|
|
'row_int',
|
|||
|
|
'uid'
|
|||
|
|
],
|
|||
|
|
'num_fields' => 2,
|
|||
|
|
'num_rows' => 2,
|
|||
|
|
'pos' => 2,
|
|||
|
|
'query' => $read_query,
|
|||
|
|
'read_rows' => 2,
|
|||
|
|
'cache_flag' => \CoreLibs\DB\IO::READ_NEW,
|
|||
|
|
'assoc_flag' => true,
|
|||
|
|
'cached' => true,
|
|||
|
|
'finished' => false,
|
|||
|
|
'read_finished' => true,
|
|||
|
|
'db_read_finished' => true,
|
|||
|
|
]
|
|||
|
|
],
|
|||
|
|
// end row, false
|
|||
|
|
[
|
|||
|
|
'data' => false,
|
|||
|
|
'cursor' => [
|
|||
|
|
'cursor' => 1,
|
|||
|
|
'data' => [
|
|||
|
|
0 => $row_a_assoc,
|
|||
|
|
1 => $row_b_assoc,
|
|||
|
|
],
|
|||
|
|
'field_names' => [
|
|||
|
|
'row_int',
|
|||
|
|
'uid'
|
|||
|
|
],
|
|||
|
|
'num_fields' => 2,
|
|||
|
|
'num_rows' => 2,
|
|||
|
|
'pos' => 0,
|
|||
|
|
'query' => $read_query,
|
|||
|
|
'read_rows' => 2,
|
|||
|
|
'cache_flag' => \CoreLibs\DB\IO::READ_NEW,
|
|||
|
|
'assoc_flag' => true,
|
|||
|
|
'cached' => true,
|
|||
|
|
'finished' => true,
|
|||
|
|
'read_finished' => true,
|
|||
|
|
'db_read_finished' => true,
|
|||
|
|
]
|
|||
|
|
]
|
|||
|
|
],
|
|||
|
|
'',
|
|||
|
|
'',
|
|||
|
|
$insert_query
|
|||
|
|
],
|
|||
|
|
'valid select, clear cache, assoc only' => [
|
|||
|
|
$read_query,
|
|||
|
|
\CoreLibs\DB\IO::CLEAR_CACHE,
|
|||
|
|
true,
|
|||
|
|
$row_a_assoc,
|
|||
|
|
true,
|
|||
|
|
[
|
|||
|
|
'cursor' => 'PgSql\Result',
|
|||
|
|
'data' => [
|
|||
|
|
0 => $row_a_assoc,
|
|||
|
|
],
|
|||
|
|
'field_names' => [
|
|||
|
|
'row_int',
|
|||
|
|
'uid'
|
|||
|
|
],
|
|||
|
|
'num_fields' => 2,
|
|||
|
|
'num_rows' => 2,
|
|||
|
|
'pos' => 1,
|
|||
|
|
'query' => $read_query,
|
|||
|
|
'read_rows' => 1,
|
|||
|
|
'cache_flag' => \CoreLibs\DB\IO::CLEAR_CACHE,
|
|||
|
|
'assoc_flag' => true,
|
|||
|
|
'cached' => true,
|
|||
|
|
'finished' => false,
|
|||
|
|
'read_finished' => false,
|
|||
|
|
'db_read_finished' => false,
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
// second row
|
|||
|
|
[
|
|||
|
|
'data' => [
|
|||
|
|
0 => $row_b_assoc,
|
|||
|
|
],
|
|||
|
|
'cursor' => [
|
|||
|
|
'cursor' => 'PgSql\Result',
|
|||
|
|
'data' => [
|
|||
|
|
0 => $row_a_assoc,
|
|||
|
|
1 => $row_b_assoc,
|
|||
|
|
],
|
|||
|
|
'field_names' => [
|
|||
|
|
'row_int',
|
|||
|
|
'uid'
|
|||
|
|
],
|
|||
|
|
'num_fields' => 2,
|
|||
|
|
'num_rows' => 2,
|
|||
|
|
'pos' => 2,
|
|||
|
|
'query' => $read_query,
|
|||
|
|
'read_rows' => 2,
|
|||
|
|
'cache_flag' => \CoreLibs\DB\IO::CLEAR_CACHE,
|
|||
|
|
'assoc_flag' => true,
|
|||
|
|
'cached' => true,
|
|||
|
|
'finished' => false,
|
|||
|
|
'read_finished' => true,
|
|||
|
|
'db_read_finished' => true,
|
|||
|
|
]
|
|||
|
|
],
|
|||
|
|
// end row, false
|
|||
|
|
[
|
|||
|
|
'data' => false,
|
|||
|
|
'cursor' => [
|
|||
|
|
'cursor' => 1,
|
|||
|
|
'data' => [],
|
|||
|
|
'field_names' => [
|
|||
|
|
'row_int',
|
|||
|
|
'uid'
|
|||
|
|
],
|
|||
|
|
'num_fields' => 2,
|
|||
|
|
'num_rows' => 2,
|
|||
|
|
'pos' => 0,
|
|||
|
|
'query' => $read_query,
|
|||
|
|
'read_rows' => 2,
|
|||
|
|
'cache_flag' => \CoreLibs\DB\IO::CLEAR_CACHE,
|
|||
|
|
'assoc_flag' => true,
|
|||
|
|
'cached' => false,
|
|||
|
|
'finished' => true,
|
|||
|
|
'read_finished' => true,
|
|||
|
|
'db_read_finished' => true,
|
|||
|
|
]
|
|||
|
|
]
|
|||
|
|
],
|
|||
|
|
'',
|
|||
|
|
'',
|
|||
|
|
$insert_query
|
|||
|
|
],
|
|||
|
|
'valid select, no cache, assoc only' => [
|
|||
|
|
$read_query,
|
|||
|
|
\CoreLibs\DB\IO::NO_CACHE,
|
|||
|
|
true,
|
|||
|
|
$row_a_assoc,
|
|||
|
|
true,
|
|||
|
|
[
|
|||
|
|
'cursor' => 'PgSql\Result',
|
|||
|
|
'data' => [],
|
|||
|
|
'field_names' => [
|
|||
|
|
'row_int',
|
|||
|
|
'uid'
|
|||
|
|
],
|
|||
|
|
'num_fields' => 2,
|
|||
|
|
'num_rows' => 2,
|
|||
|
|
'pos' => 1,
|
|||
|
|
'query' => $read_query,
|
|||
|
|
'read_rows' => 1,
|
|||
|
|
'cache_flag' => \CoreLibs\DB\IO::NO_CACHE,
|
|||
|
|
'assoc_flag' => true,
|
|||
|
|
'cached' => false,
|
|||
|
|
'finished' => false,
|
|||
|
|
'read_finished' => false,
|
|||
|
|
'db_read_finished' => false,
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
// second row
|
|||
|
|
[
|
|||
|
|
'data' => [
|
|||
|
|
0 => $row_b_assoc,
|
|||
|
|
],
|
|||
|
|
'cursor' => [
|
|||
|
|
'cursor' => 'PgSql\Result',
|
|||
|
|
'data' => [],
|
|||
|
|
'field_names' => [
|
|||
|
|
'row_int',
|
|||
|
|
'uid'
|
|||
|
|
],
|
|||
|
|
'num_fields' => 2,
|
|||
|
|
'num_rows' => 2,
|
|||
|
|
'pos' => 2,
|
|||
|
|
'query' => $read_query,
|
|||
|
|
'read_rows' => 2,
|
|||
|
|
'cache_flag' => \CoreLibs\DB\IO::NO_CACHE,
|
|||
|
|
'assoc_flag' => true,
|
|||
|
|
'cached' => false,
|
|||
|
|
'finished' => false,
|
|||
|
|
'read_finished' => true,
|
|||
|
|
'db_read_finished' => true,
|
|||
|
|
]
|
|||
|
|
],
|
|||
|
|
// end row, false
|
|||
|
|
[
|
|||
|
|
'data' => false,
|
|||
|
|
'cursor' => [
|
|||
|
|
'cursor' => 1,
|
|||
|
|
'data' => [],
|
|||
|
|
'field_names' => [
|
|||
|
|
'row_int',
|
|||
|
|
'uid'
|
|||
|
|
],
|
|||
|
|
'num_fields' => 2,
|
|||
|
|
'num_rows' => 2,
|
|||
|
|
'pos' => 0,
|
|||
|
|
'query' => $read_query,
|
|||
|
|
'read_rows' => 2,
|
|||
|
|
'cache_flag' => \CoreLibs\DB\IO::NO_CACHE,
|
|||
|
|
'assoc_flag' => true,
|
|||
|
|
'cached' => false,
|
|||
|
|
'finished' => true,
|
|||
|
|
'read_finished' => true,
|
|||
|
|
'db_read_finished' => true,
|
|||
|
|
]
|
|||
|
|
]
|
|||
|
|
],
|
|||
|
|
'',
|
|||
|
|
'',
|
|||
|
|
$insert_query
|
|||
|
|
],
|
|||
|
|
// *** READ STEP BY STEP, ERROR TRIGGER
|
|||
|
|
'empty select error' => [
|
|||
|
|
'',
|
|||
|
|
null,
|
|||
|
|
null,
|
|||
|
|
false,
|
|||
|
|
true,
|
|||
|
|
[],
|
|||
|
|
[],
|
|||
|
|
'',
|
|||
|
|
'11',
|
|||
|
|
$insert_query,
|
|||
|
|
],
|
|||
|
|
'insert query error' => [
|
|||
|
|
$insert_query,
|
|||
|
|
null,
|
|||
|
|
null,
|
|||
|
|
false,
|
|||
|
|
true,
|
|||
|
|
[],
|
|||
|
|
[],
|
|||
|
|
'',
|
|||
|
|
'17',
|
|||
|
|
$insert_query
|
|||
|
|
],
|
|||
|
|
// *** READ AS LOOP
|
|||
|
|
// from here on a complex read all full tests
|
|||
|
|
'valid select, full read DEFAULT CACHE' => [
|
|||
|
|
$read_query,
|
|||
|
|
null,
|
|||
|
|
null,
|
|||
|
|
[$row_a, $row_b,],
|
|||
|
|
false,
|
|||
|
|
[
|
|||
|
|
'cursor' => 1,
|
|||
|
|
'data' => [
|
|||
|
|
0 => $row_a,
|
|||
|
|
1 => $row_b,
|
|||
|
|
],
|
|||
|
|
'field_names' => [
|
|||
|
|
'row_int',
|
|||
|
|
'uid'
|
|||
|
|
],
|
|||
|
|
'num_fields' => 2,
|
|||
|
|
'num_rows' => 2,
|
|||
|
|
'pos' => 0,
|
|||
|
|
'query' => $read_query,
|
|||
|
|
'read_rows' => 2,
|
|||
|
|
'cache_flag' => \CoreLibs\DB\IO::USE_CACHE,
|
|||
|
|
'assoc_flag' => false,
|
|||
|
|
'cached' => true,
|
|||
|
|
'finished' => true,
|
|||
|
|
'read_finished' => true,
|
|||
|
|
'db_read_finished' => true,
|
|||
|
|
],
|
|||
|
|
[],
|
|||
|
|
'',
|
|||
|
|
'',
|
|||
|
|
$insert_query
|
|||
|
|
],
|
|||
|
|
// READ_NEW
|
|||
|
|
'valid select, full read READ NEW' => [
|
|||
|
|
$read_query,
|
|||
|
|
\CoreLibs\DB\IO::READ_NEW,
|
|||
|
|
null,
|
|||
|
|
[$row_a, $row_b,],
|
|||
|
|
false,
|
|||
|
|
[
|
|||
|
|
'cursor' => 1,
|
|||
|
|
'data' => [
|
|||
|
|
0 => $row_a,
|
|||
|
|
1 => $row_b,
|
|||
|
|
],
|
|||
|
|
'field_names' => [
|
|||
|
|
'row_int',
|
|||
|
|
'uid'
|
|||
|
|
],
|
|||
|
|
'num_fields' => 2,
|
|||
|
|
'num_rows' => 2,
|
|||
|
|
'pos' => 0,
|
|||
|
|
'query' => $read_query,
|
|||
|
|
'read_rows' => 2,
|
|||
|
|
'cache_flag' => \CoreLibs\DB\IO::READ_NEW,
|
|||
|
|
'assoc_flag' => false,
|
|||
|
|
'cached' => true,
|
|||
|
|
'finished' => true,
|
|||
|
|
'read_finished' => true,
|
|||
|
|
'db_read_finished' => true,
|
|||
|
|
],
|
|||
|
|
[],
|
|||
|
|
'',
|
|||
|
|
'',
|
|||
|
|
$insert_query
|
|||
|
|
],
|
|||
|
|
// CLEAR_CACHE
|
|||
|
|
'valid select, full read CLEAR CACHE' => [
|
|||
|
|
$read_query,
|
|||
|
|
\CoreLibs\DB\IO::CLEAR_CACHE,
|
|||
|
|
null,
|
|||
|
|
[$row_a, $row_b,],
|
|||
|
|
false,
|
|||
|
|
[
|
|||
|
|
'cursor' => 1,
|
|||
|
|
'data' => [
|
|||
|
|
],
|
|||
|
|
'field_names' => [
|
|||
|
|
'row_int',
|
|||
|
|
'uid'
|
|||
|
|
],
|
|||
|
|
'num_fields' => 2,
|
|||
|
|
'num_rows' => 2,
|
|||
|
|
'pos' => 0,
|
|||
|
|
'query' => $read_query,
|
|||
|
|
'read_rows' => 2,
|
|||
|
|
'cache_flag' => \CoreLibs\DB\IO::CLEAR_CACHE,
|
|||
|
|
'assoc_flag' => false,
|
|||
|
|
'cached' => false,
|
|||
|
|
'finished' => true,
|
|||
|
|
'read_finished' => true,
|
|||
|
|
'db_read_finished' => true,
|
|||
|
|
],
|
|||
|
|
[],
|
|||
|
|
'',
|
|||
|
|
'',
|
|||
|
|
$insert_query
|
|||
|
|
],
|
|||
|
|
'valid select, full read NO CACHE' => [
|
|||
|
|
$read_query,
|
|||
|
|
\CoreLibs\DB\IO::NO_CACHE,
|
|||
|
|
null,
|
|||
|
|
[$row_a, $row_b,],
|
|||
|
|
false,
|
|||
|
|
[
|
|||
|
|
'cursor' => 1,
|
|||
|
|
'data' => [
|
|||
|
|
],
|
|||
|
|
'field_names' => [
|
|||
|
|
'row_int',
|
|||
|
|
'uid'
|
|||
|
|
],
|
|||
|
|
'num_fields' => 2,
|
|||
|
|
'num_rows' => 2,
|
|||
|
|
'pos' => 0,
|
|||
|
|
'query' => $read_query,
|
|||
|
|
'read_rows' => 2,
|
|||
|
|
'cache_flag' => \CoreLibs\DB\IO::NO_CACHE,
|
|||
|
|
'assoc_flag' => false,
|
|||
|
|
'cached' => false,
|
|||
|
|
'finished' => true,
|
|||
|
|
'read_finished' => true,
|
|||
|
|
'db_read_finished' => true,
|
|||
|
|
],
|
|||
|
|
[],
|
|||
|
|
'',
|
|||
|
|
'',
|
|||
|
|
$insert_query
|
|||
|
|
],
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* dbReturn cursor extended checks
|
|||
|
|
*
|
|||
|
|
* @param \CoreLibs\DB\IO $db
|
|||
|
|
* @param string $query
|
|||
|
|
* @param array $cursor_ext_checks
|
|||
|
|
* @return void
|
|||
|
|
*/
|
|||
|
|
private function subAssertCursorExtTestDbReturnFunction(
|
|||
|
|
\CoreLibs\DB\IO $db,
|
|||
|
|
string $query,
|
|||
|
|
array $cursor_ext_checks
|
|||
|
|
): void {
|
|||
|
|
// cursor check
|
|||
|
|
if (
|
|||
|
|
empty($db->dbGetLastWarning()) &&
|
|||
|
|
empty($db->dbGetLastError()) &&
|
|||
|
|
count($cursor_ext_checks)
|
|||
|
|
) {
|
|||
|
|
$cursor_ext = $db->dbGetCursorExt($query);
|
|||
|
|
foreach ($cursor_ext_checks as $key => $expected) {
|
|||
|
|
if ($key != 'cursor') {
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected,
|
|||
|
|
$cursor_ext[$key],
|
|||
|
|
'assert equal cursor ext for ' . $key
|
|||
|
|
);
|
|||
|
|
} else {
|
|||
|
|
// for int, it has to be one
|
|||
|
|
// else depends on PHP version, either object or REsource
|
|||
|
|
if (is_int($expected)) {
|
|||
|
|
$this->assertEquals(
|
|||
|
|
1,
|
|||
|
|
$cursor_ext[$key],
|
|||
|
|
'assert equal cursor ext cursor int 1'
|
|||
|
|
);
|
|||
|
|
} elseif (\CoreLibs\Check\PhpVersion::checkPHPVersion('8.1')) {
|
|||
|
|
$this->assertIsObject(
|
|||
|
|
$cursor_ext[$key],
|
|||
|
|
'assert is object cursor ext cursor'
|
|||
|
|
);
|
|||
|
|
// also check that this is correct instance type
|
|||
|
|
$this->assertInstanceOf(
|
|||
|
|
$expected,
|
|||
|
|
$cursor_ext[$key],
|
|||
|
|
'assert is instance of cursor ext cursor'
|
|||
|
|
);
|
|||
|
|
} else {
|
|||
|
|
$this->assertIsResource(
|
|||
|
|
$cursor_ext[$key],
|
|||
|
|
'assert is resource cursor ext cursor'
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* dbReturn Function Test
|
|||
|
|
*
|
|||
|
|
* @covers ::dbReturn
|
|||
|
|
* @covers ::dbCacheReset
|
|||
|
|
* @covers ::dbGetCursorExt
|
|||
|
|
* @covers ::dbCursorPos
|
|||
|
|
* @covers ::dbCursorNumRows
|
|||
|
|
* @dataProvider dbReturnProvider
|
|||
|
|
* @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 integer|null $flag_cache
|
|||
|
|
* @param boolean|null $flag_assoc
|
|||
|
|
* @param array<mixed>|bool $expected
|
|||
|
|
* @param bool $read_first_only
|
|||
|
|
* @param array<mixed> $cursor_ext_checks
|
|||
|
|
* @param array<mixed> $cursor_ext_checks_step
|
|||
|
|
* @param string $warning
|
|||
|
|
* @param string $error
|
|||
|
|
* @param string $insert_data
|
|||
|
|
* @return void
|
|||
|
|
*/
|
|||
|
|
public function testDbReturnFunction(
|
|||
|
|
string $query,
|
|||
|
|
?int $flag_cache,
|
|||
|
|
?bool $flag_assoc,
|
|||
|
|
$expected,
|
|||
|
|
bool $read_first_only,
|
|||
|
|
array $cursor_ext_checks,
|
|||
|
|
array $cursor_ext_checks_step,
|
|||
|
|
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 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)
|
|||
|
|
),
|
|||
|
|
'Assert dbReturn first only equal'
|
|||
|
|
);
|
|||
|
|
$this->subAssertErrorTest($db, $warning, $error);
|
|||
|
|
$this->subAssertCursorExtTestDbReturnFunction($db, $query, $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
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
// read all, and then do result compare
|
|||
|
|
// 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),
|
|||
|
|
'Assert dbReturn pos'
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
// does count match for returned data and the cursor num rows
|
|||
|
|
$this->assertEquals(
|
|||
|
|
count($data),
|
|||
|
|
$db->dbGetCursorNumRows($query),
|
|||
|
|
'Assert dbReturn num rows'
|
|||
|
|
);
|
|||
|
|
// run cursor ext checks after first run
|
|||
|
|
$this->subAssertCursorExtTestDbReturnFunction($db, $query, $cursor_ext_checks);
|
|||
|
|
// 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),
|
|||
|
|
'Assert dbReturn reset cache'
|
|||
|
|
);
|
|||
|
|
// New run after reset
|
|||
|
|
// for read all, test that two reads result in same data
|
|||
|
|
$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),
|
|||
|
|
'Assert dbReturn double read 1 pos: ' . $flag_cache
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
$this->subAssertCursorExtTestDbReturnFunction($db, $query, $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)
|
|||
|
|
)
|
|||
|
|
)
|
|||
|
|
) {
|
|||
|
|
$data_second[] = $res;
|
|||
|
|
$pos_second++;
|
|||
|
|
// check cursor pos
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$pos_second,
|
|||
|
|
$db->dbGetCursorPos($query),
|
|||
|
|
'Assert dbReturn double read 2 pos: ' . $flag_cache
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$data,
|
|||
|
|
$data_second,
|
|||
|
|
'Assert first and second run are equal: return data'
|
|||
|
|
);
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$pos,
|
|||
|
|
$pos_second,
|
|||
|
|
'Assert first and second run are equal: count'
|
|||
|
|
);
|
|||
|
|
// run cursor ext checks after second run
|
|||
|
|
$this->subAssertCursorExtTestDbReturnFunction($db, $query, $cursor_ext_checks);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 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, dbGetPrepareCursorValue
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Undocumented function
|
|||
|
|
*
|
|||
|
|
* @return array
|
|||
|
|
*/
|
|||
|
|
public function preparedProvider(): 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: statement name
|
|||
|
|
// 1: query to prepare
|
|||
|
|
// 2: primary key name: null for default run
|
|||
|
|
// 3: arguments for query (single array for all)
|
|||
|
|
// 4: expected prepare return
|
|||
|
|
// 5: prepare warning
|
|||
|
|
// 6: prepare error
|
|||
|
|
// 7: expected execute return
|
|||
|
|
// 8: execute warning
|
|||
|
|
// 9: execute error
|
|||
|
|
// 11: read query (if insert/update)
|
|||
|
|
// 11: execute data to check (array)
|
|||
|
|
// 12: insert data
|
|||
|
|
// 13: prepated cursor array data match values
|
|||
|
|
return [
|
|||
|
|
// insert
|
|||
|
|
'prepare query insert' => [
|
|||
|
|
// base statements 0-3
|
|||
|
|
'insert',
|
|||
|
|
"INSERT INTO table_with_primary_key (row_int, uid) VALUES "
|
|||
|
|
. "($1, $2)",
|
|||
|
|
null,
|
|||
|
|
[990, 'TEST A'],
|
|||
|
|
// prepare (4-6)
|
|||
|
|
'result', '', '',
|
|||
|
|
// execute
|
|||
|
|
'result', '', '',
|
|||
|
|
// check query and compare data (for insert/update)
|
|||
|
|
$read_query,
|
|||
|
|
[
|
|||
|
|
[
|
|||
|
|
'row_int' => 990,
|
|||
|
|
'uid' => 'TEST A',
|
|||
|
|
],
|
|||
|
|
],
|
|||
|
|
// insert data (for select)
|
|||
|
|
'',
|
|||
|
|
// get prepared data
|
|||
|
|
[
|
|||
|
|
'pk_name' => 'table_with_primary_key_id',
|
|||
|
|
'count' => 2,
|
|||
|
|
'query' => 'INSERT INTO table_with_primary_key (row_int, uid) '
|
|||
|
|
. 'VALUES ($1, $2) RETURNING table_with_primary_key_id',
|
|||
|
|
'returning_id' => true,
|
|||
|
|
],
|
|||
|
|
],
|
|||
|
|
// update
|
|||
|
|
'prepare query update' => [
|
|||
|
|
'update',
|
|||
|
|
"UPDATE table_with_primary_key SET "
|
|||
|
|
. "row_int = $1, "
|
|||
|
|
. "row_varchar = $2 "
|
|||
|
|
. "WHERE uid = $3",
|
|||
|
|
null,
|
|||
|
|
[550, 'I AM NEW TEXT', 'TEST A'],
|
|||
|
|
//
|
|||
|
|
'result', '', '',
|
|||
|
|
//
|
|||
|
|
'result', '', '',
|
|||
|
|
//
|
|||
|
|
"SELECT row_int, row_varchar FROM table_with_primary_key "
|
|||
|
|
. "WHERE uid = 'TEST A'",
|
|||
|
|
[
|
|||
|
|
[
|
|||
|
|
'row_int' => 550,
|
|||
|
|
'row_varchar' => 'I AM NEW TEXT',
|
|||
|
|
],
|
|||
|
|
],
|
|||
|
|
//
|
|||
|
|
"INSERT INTO table_with_primary_key (row_int, uid) VALUES "
|
|||
|
|
. "(111, 'TEST A')",
|
|||
|
|
//
|
|||
|
|
[
|
|||
|
|
'pk_name' => '',
|
|||
|
|
'count' => 3,
|
|||
|
|
'query' => 'UPDATE table_with_primary_key SET row_int = $1, '
|
|||
|
|
. 'row_varchar = $2 WHERE uid = $3',
|
|||
|
|
'returning_id' => false,
|
|||
|
|
],
|
|||
|
|
],
|
|||
|
|
// select
|
|||
|
|
'prepare select query' => [
|
|||
|
|
'select',
|
|||
|
|
$read_query
|
|||
|
|
. " WHERE uid = $1",
|
|||
|
|
null,
|
|||
|
|
['A'],
|
|||
|
|
//
|
|||
|
|
'result', '', '',
|
|||
|
|
// execute here needs to read too
|
|||
|
|
'result', '', '',
|
|||
|
|
//
|
|||
|
|
'',
|
|||
|
|
[
|
|||
|
|
[
|
|||
|
|
'row_int' => 1,
|
|||
|
|
'uid' => 'A',
|
|||
|
|
],
|
|||
|
|
],
|
|||
|
|
//
|
|||
|
|
$insert_query,
|
|||
|
|
//
|
|||
|
|
[
|
|||
|
|
'pk_name' => '',
|
|||
|
|
'count' => 1,
|
|||
|
|
'query' => 'SELECT row_int, uid FROM table_with_primary_key WHERE uid = $1',
|
|||
|
|
'returning_id' => false,
|
|||
|
|
],
|
|||
|
|
],
|
|||
|
|
// any query but with no parameters
|
|||
|
|
'prepare select query no parameter' => [
|
|||
|
|
'select_noparam',
|
|||
|
|
$read_query,
|
|||
|
|
null,
|
|||
|
|
null,
|
|||
|
|
//
|
|||
|
|
'result', '', '',
|
|||
|
|
// execute here needs to read too
|
|||
|
|
'result', '', '',
|
|||
|
|
//
|
|||
|
|
'',
|
|||
|
|
[
|
|||
|
|
[
|
|||
|
|
'row_int' => 1,
|
|||
|
|
'uid' => 'A',
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
'row_int' => 2,
|
|||
|
|
'uid' => 'B',
|
|||
|
|
],
|
|||
|
|
],
|
|||
|
|
//
|
|||
|
|
$insert_query,
|
|||
|
|
//
|
|||
|
|
[
|
|||
|
|
'pk_name' => '',
|
|||
|
|
'count' => 0,
|
|||
|
|
'query' => 'SELECT row_int, uid FROM table_with_primary_key',
|
|||
|
|
'returning_id' => false,
|
|||
|
|
],
|
|||
|
|
],
|
|||
|
|
// no statement name (25)
|
|||
|
|
'empty statement' => [
|
|||
|
|
'',
|
|||
|
|
'SELECT',
|
|||
|
|
null,
|
|||
|
|
[],
|
|||
|
|
//
|
|||
|
|
false, '', '25',
|
|||
|
|
//
|
|||
|
|
false, '', '25',
|
|||
|
|
//
|
|||
|
|
'',
|
|||
|
|
[],
|
|||
|
|
//
|
|||
|
|
'',
|
|||
|
|
//
|
|||
|
|
[
|
|||
|
|
'pk_name' => '',
|
|||
|
|
'count' => 0,
|
|||
|
|
'query' => '',
|
|||
|
|
'returning_id' => false,
|
|||
|
|
],
|
|||
|
|
],
|
|||
|
|
// no query (prepare 11)
|
|||
|
|
// no prepared cursor found with statement name (execute 24)
|
|||
|
|
'empty query' => [
|
|||
|
|
'Empty Query',
|
|||
|
|
'',
|
|||
|
|
null,
|
|||
|
|
[],
|
|||
|
|
//
|
|||
|
|
false, '', '11',
|
|||
|
|
//
|
|||
|
|
false, '', '24',
|
|||
|
|
//
|
|||
|
|
'',
|
|||
|
|
[],
|
|||
|
|
//
|
|||
|
|
'',
|
|||
|
|
//
|
|||
|
|
[
|
|||
|
|
'pk_name' => '',
|
|||
|
|
'count' => 0,
|
|||
|
|
'query' => '',
|
|||
|
|
'returning_id' => false,
|
|||
|
|
],
|
|||
|
|
],
|
|||
|
|
// no db connection (prepare/execute 16)
|
|||
|
|
// TODO no db connection test
|
|||
|
|
// connection busy (prepare/execute 41)
|
|||
|
|
// TODO connection busy test
|
|||
|
|
// query could not be prepare (prepare 21)
|
|||
|
|
// TODO query could not be prepared test
|
|||
|
|
// some query with same statement name exists (prepare W20)
|
|||
|
|
'prepare query with same statement name' => [
|
|||
|
|
'double',
|
|||
|
|
$read_query,
|
|||
|
|
null,
|
|||
|
|
null,
|
|||
|
|
//
|
|||
|
|
true, '20', '',
|
|||
|
|
//
|
|||
|
|
'result', '', '',
|
|||
|
|
// no query but data for data only compare
|
|||
|
|
'',
|
|||
|
|
[],
|
|||
|
|
//
|
|||
|
|
$insert_query,
|
|||
|
|
//
|
|||
|
|
[
|
|||
|
|
'pk_name' => '',
|
|||
|
|
'count' => 0,
|
|||
|
|
'query' => 'SELECT row_int, uid FROM table_with_primary_key',
|
|||
|
|
'returning_id' => false,
|
|||
|
|
],
|
|||
|
|
],
|
|||
|
|
// insert wrong data count compared to needed (execute 23)
|
|||
|
|
'wrong parmeter count' => [
|
|||
|
|
'wrong_param_count',
|
|||
|
|
"INSERT INTO table_with_primary_key (row_int, uid) VALUES "
|
|||
|
|
. "($1, $2)",
|
|||
|
|
null,
|
|||
|
|
[],
|
|||
|
|
//
|
|||
|
|
'result', '', '',
|
|||
|
|
//
|
|||
|
|
false, '', '23',
|
|||
|
|
//
|
|||
|
|
'',
|
|||
|
|
[],
|
|||
|
|
//
|
|||
|
|
'',
|
|||
|
|
//
|
|||
|
|
[
|
|||
|
|
'pk_name' => 'table_with_primary_key_id',
|
|||
|
|
'count' => 2,
|
|||
|
|
'query' => 'INSERT INTO table_with_primary_key (row_int, uid) VALUES '
|
|||
|
|
. '($1, $2) RETURNING table_with_primary_key_id',
|
|||
|
|
'returning_id' => true,
|
|||
|
|
],
|
|||
|
|
],
|
|||
|
|
// execute does not return a result (22)
|
|||
|
|
// TODO execute does not return a result
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Undocumented function
|
|||
|
|
*
|
|||
|
|
* @covers ::dbPrepare
|
|||
|
|
* @covers ::dbExecute
|
|||
|
|
* @covers ::dbFetchArray
|
|||
|
|
* @covers ::dbGetPrepareCursorValue
|
|||
|
|
* @dataProvider preparedProvider
|
|||
|
|
* @testdox prepared query $stm_name with $expected_prepare (warning $warning_prepare/error $error_prepare) and $expected_execute (warning $warning_execute/error $error_execute) [$_dataName]
|
|||
|
|
*
|
|||
|
|
* @param string $stm_name
|
|||
|
|
* @param string $query
|
|||
|
|
* @param string|null $pk_name
|
|||
|
|
* @param array|null $query_data
|
|||
|
|
* @param bool|string $expected_prepare
|
|||
|
|
* @param string $warning_prepare
|
|||
|
|
* @param string $error_prepare
|
|||
|
|
* @param bool|string $expected_execute
|
|||
|
|
* @param string $warning_execute
|
|||
|
|
* @param string $error_execute
|
|||
|
|
* @param string $expected_data_query
|
|||
|
|
* @param array $expected_data
|
|||
|
|
* @param string $insert_data
|
|||
|
|
* @param array $prepare_cursor
|
|||
|
|
* @return void
|
|||
|
|
*/
|
|||
|
|
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,
|
|||
|
|
string $expected_data_query,
|
|||
|
|
array $expected_data,
|
|||
|
|
string $insert_data,
|
|||
|
|
array $prepare_cursor,
|
|||
|
|
): 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
|
|||
|
|
if (!empty($insert_data)) {
|
|||
|
|
$db->dbExec($insert_data);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// test prepare
|
|||
|
|
$prepare_result = $pk_name === null ?
|
|||
|
|
$db->dbPrepare($stm_name, $query) :
|
|||
|
|
$db->dbPrepare($stm_name, $query, $pk_name);
|
|||
|
|
// if warning is 20, call prepare again
|
|||
|
|
if ($warning_prepare == '20') {
|
|||
|
|
$prepare_result = $pk_name === null ?
|
|||
|
|
$db->dbPrepare($stm_name, $query) :
|
|||
|
|
$db->dbPrepare($stm_name, $query, $pk_name);
|
|||
|
|
}
|
|||
|
|
// 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
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected_prepare,
|
|||
|
|
$prepare_result
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
// error/warning check
|
|||
|
|
$this->subAssertErrorTest($db, $warning_prepare, $error_prepare);
|
|||
|
|
|
|||
|
|
// for non fail prepare test exec
|
|||
|
|
// check test result
|
|||
|
|
$execute_result = $query_data === null ?
|
|||
|
|
$db->dbExecute($stm_name) :
|
|||
|
|
$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
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected_execute,
|
|||
|
|
$execute_result
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
// error/warning check
|
|||
|
|
$this->subAssertErrorTest($db, $warning_execute, $error_execute);
|
|||
|
|
// now check test result if expected return is result
|
|||
|
|
if (
|
|||
|
|
$expected_execute == 'result' &&
|
|||
|
|
!empty($expected_data_query)
|
|||
|
|
) {
|
|||
|
|
// $expected_data_query
|
|||
|
|
// $expected_data
|
|||
|
|
$rows = $db->dbReturnArray($expected_data_query);
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected_data,
|
|||
|
|
$rows
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
if (
|
|||
|
|
$expected_execute == 'result' &&
|
|||
|
|
$execute_result !== false &&
|
|||
|
|
empty($expected_data_query) &&
|
|||
|
|
count($expected_data)
|
|||
|
|
) {
|
|||
|
|
// compare previously read data to compare data
|
|||
|
|
$compare_data = [];
|
|||
|
|
// read in the query data
|
|||
|
|
while (is_array($row = $db->dbFetchArray($execute_result, true))) {
|
|||
|
|
$compare_data[] = $row;
|
|||
|
|
}
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected_data,
|
|||
|
|
$compare_data
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// check dbGetPrepareCursorValue
|
|||
|
|
foreach (['pk_name', 'count', 'query', 'returning_id'] as $key) {
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$prepare_cursor[$key],
|
|||
|
|
$db->dbGetPrepareCursorValue($stm_name, $key),
|
|||
|
|
'Prepared cursor: ' . $key . ': failed assertion'
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// reset all data
|
|||
|
|
$db->dbExec("TRUNCATE table_with_primary_key");
|
|||
|
|
$db->dbExec("TRUNCATE table_without_primary_key");
|
|||
|
|
// close connection
|
|||
|
|
$db->dbClose();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// dedicated error checks for prepare cursor return
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Undocumented function
|
|||
|
|
*
|
|||
|
|
* @return array
|
|||
|
|
*/
|
|||
|
|
public function preparedProviderValue(): array
|
|||
|
|
{
|
|||
|
|
// 1: query (can be empty for do not set)
|
|||
|
|
// 2: stm name
|
|||
|
|
// 3: key
|
|||
|
|
// 4: expected error return
|
|||
|
|
return [
|
|||
|
|
'no error' => [
|
|||
|
|
"SELECT row_int, uid FROM table_with_primary_key",
|
|||
|
|
'read',
|
|||
|
|
'pk_name',
|
|||
|
|
''
|
|||
|
|
],
|
|||
|
|
'statement name empty' => [
|
|||
|
|
'',
|
|||
|
|
'',
|
|||
|
|
'',
|
|||
|
|
'101',
|
|||
|
|
],
|
|||
|
|
'key empty' => [
|
|||
|
|
'',
|
|||
|
|
'read',
|
|||
|
|
'',
|
|||
|
|
'102',
|
|||
|
|
],
|
|||
|
|
'key invalid' => [
|
|||
|
|
'',
|
|||
|
|
'read',
|
|||
|
|
'invalid',
|
|||
|
|
'102',
|
|||
|
|
],
|
|||
|
|
'statement name not found' => [
|
|||
|
|
'',
|
|||
|
|
'invalid',
|
|||
|
|
'pk_name',
|
|||
|
|
'103',
|
|||
|
|
],
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* test return prepare cursor errors
|
|||
|
|
*
|
|||
|
|
* @covers ::dbGetPrepareCursorValue
|
|||
|
|
* @dataProvider preparedProviderValue
|
|||
|
|
* @testdox prepared query $stm_name with $key expect error id $error_id [$_dataName]
|
|||
|
|
*
|
|||
|
|
* @param string $query
|
|||
|
|
* @param string $stm_name
|
|||
|
|
* @param string $key
|
|||
|
|
* @param string $error_id
|
|||
|
|
* @return void
|
|||
|
|
*/
|
|||
|
|
public function testDbGetPrepareCursorValue(
|
|||
|
|
string $query,
|
|||
|
|
string $stm_name,
|
|||
|
|
string $key,
|
|||
|
|
$error_id
|
|||
|
|
): void {
|
|||
|
|
$db = new \CoreLibs\DB\IO(
|
|||
|
|
self::$db_config['valid'],
|
|||
|
|
self::$log
|
|||
|
|
);
|
|||
|
|
if (!empty($query)) {
|
|||
|
|
$db->dbPrepare($stm_name, $query);
|
|||
|
|
$db->dbExecute($stm_name);
|
|||
|
|
}
|
|||
|
|
$db->dbGetPrepareCursorValue($stm_name, $key);
|
|||
|
|
// match check error
|
|||
|
|
$last_error = $db->dbGetLastError();
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$error_id,
|
|||
|
|
$last_error,
|
|||
|
|
'get prepare cursor value error check'
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// - schema set/get tests
|
|||
|
|
// dbGetSchema, dbSetSchema
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Undocumented function
|
|||
|
|
*
|
|||
|
|
* @return array
|
|||
|
|
*/
|
|||
|
|
public function schemaProvider(): array
|
|||
|
|
{
|
|||
|
|
// 0: db connection
|
|||
|
|
// 1: schema to set
|
|||
|
|
// 2: set result
|
|||
|
|
// 3: set error
|
|||
|
|
// 4: get result flagged
|
|||
|
|
// 5: get result DB
|
|||
|
|
return [
|
|||
|
|
'schema get check only' => [
|
|||
|
|
'valid',
|
|||
|
|
null,
|
|||
|
|
true,
|
|||
|
|
'',
|
|||
|
|
'public',
|
|||
|
|
'public',
|
|||
|
|
],
|
|||
|
|
'new schema set' => [
|
|||
|
|
'valid',
|
|||
|
|
'public',
|
|||
|
|
true,
|
|||
|
|
'',
|
|||
|
|
'public',
|
|||
|
|
'public',
|
|||
|
|
],
|
|||
|
|
'no schema set, set new schema' => [
|
|||
|
|
'valid_no_schema',
|
|||
|
|
'public',
|
|||
|
|
true,
|
|||
|
|
'',
|
|||
|
|
'public',
|
|||
|
|
'public',
|
|||
|
|
],
|
|||
|
|
'try to set empty schema' => [
|
|||
|
|
'valid',
|
|||
|
|
'',
|
|||
|
|
false,
|
|||
|
|
'70',
|
|||
|
|
'public',
|
|||
|
|
'public',
|
|||
|
|
],
|
|||
|
|
// invalid schema (does not throw error)
|
|||
|
|
'try to set empty schema' => [
|
|||
|
|
'valid',
|
|||
|
|
'invalid',
|
|||
|
|
false,
|
|||
|
|
'71',
|
|||
|
|
'public',
|
|||
|
|
'public',
|
|||
|
|
],
|
|||
|
|
// valid different schema
|
|||
|
|
'try to set new valid schema' => [
|
|||
|
|
'valid',
|
|||
|
|
'testschema',
|
|||
|
|
true,
|
|||
|
|
'',
|
|||
|
|
'testschema',
|
|||
|
|
'testschema',
|
|||
|
|
]
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Undocumented function
|
|||
|
|
*
|
|||
|
|
* @covers ::dbSetSchema
|
|||
|
|
* @covers ::dbGetSchema
|
|||
|
|
* @dataProvider schemaProvider
|
|||
|
|
* @testdox set schema $schema on $connection with $expected_set (error $error_set) and get $expected_get_var/$expected_get_db [$_dataName]
|
|||
|
|
*
|
|||
|
|
* @param string $connection
|
|||
|
|
* @param string|null $schema
|
|||
|
|
* @param boolean $expected_set
|
|||
|
|
* @param string $error_set
|
|||
|
|
* @param string $expected_get_var
|
|||
|
|
* @param string $expected_get_db
|
|||
|
|
* @return void
|
|||
|
|
*/
|
|||
|
|
public function testDbSchema(
|
|||
|
|
string $connection,
|
|||
|
|
?string $schema,
|
|||
|
|
bool $expected_set,
|
|||
|
|
string $error_set,
|
|||
|
|
string $expected_get_var,
|
|||
|
|
string $expected_get_db
|
|||
|
|
): void {
|
|||
|
|
// self::$log->setLogLevelAll('debug', true);
|
|||
|
|
// self::$log->setLogLevelAll('print', true);
|
|||
|
|
$db = new \CoreLibs\DB\IO(
|
|||
|
|
self::$db_config[$connection],
|
|||
|
|
self::$log
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// schema is not null, we do set testing
|
|||
|
|
if ($schema !== null) {
|
|||
|
|
$result_set = $db->dbSetSchema($schema);
|
|||
|
|
$last_error = $db->dbGetLastError();
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected_set,
|
|||
|
|
$result_set
|
|||
|
|
);
|
|||
|
|
// error/warning check
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$error_set,
|
|||
|
|
$last_error,
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// get current set from db
|
|||
|
|
$result_get_var = $db->dbGetSchema(true);
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected_get_var,
|
|||
|
|
$result_get_var
|
|||
|
|
);
|
|||
|
|
$result_get_db = $db->dbGetSchema();
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected_get_db,
|
|||
|
|
$result_get_db
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// close connection
|
|||
|
|
$db->dbClose();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// - check error and warning handling
|
|||
|
|
// dbGetCombinedErrorHistory, dbGetLastError, dbGetLastWarning
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Undocumented function
|
|||
|
|
*
|
|||
|
|
* @return array
|
|||
|
|
*/
|
|||
|
|
public function errorHandlingProvider(): array
|
|||
|
|
{
|
|||
|
|
// 0: some error call
|
|||
|
|
// 1: type (error/warning)
|
|||
|
|
// 2: error/warning code
|
|||
|
|
// 3: return array matcher (excluding time)
|
|||
|
|
return [
|
|||
|
|
'trigger error' => [
|
|||
|
|
0,
|
|||
|
|
'error',
|
|||
|
|
'51',
|
|||
|
|
[
|
|||
|
|
'timestamp' => "/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{1,}$/",
|
|||
|
|
'level' => 'error',
|
|||
|
|
'id' => '51',
|
|||
|
|
'error' => 'Max query call needs to be set to at least 1',
|
|||
|
|
// run:: can be +1 if called in set and not direct
|
|||
|
|
'source' => "/^main::run::run::run::run::run::run::(run::)?runBare::runTest::testDbErrorHandling::dbSetMaxQueryCall$/",
|
|||
|
|
'pg_error' => '',
|
|||
|
|
'msg' => '',
|
|||
|
|
]
|
|||
|
|
],
|
|||
|
|
'trigger warning' => [
|
|||
|
|
-1,
|
|||
|
|
'warning',
|
|||
|
|
'50',
|
|||
|
|
[]
|
|||
|
|
],
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Undocumented function
|
|||
|
|
*
|
|||
|
|
* @covers ::dbGetLastError
|
|||
|
|
* @covers ::dbGetLastWarning
|
|||
|
|
* @covers ::dbGetCombinedErrorHistory
|
|||
|
|
* @dataProvider errorHandlingProvider
|
|||
|
|
* @testdox error $call_value for type $type with $error_id [$_dataName]
|
|||
|
|
*
|
|||
|
|
* @param integer $call_value
|
|||
|
|
* @param string $type
|
|||
|
|
* @param string $error_id
|
|||
|
|
* @param array $error_history
|
|||
|
|
* @return void
|
|||
|
|
*/
|
|||
|
|
public function testDbErrorHandling(
|
|||
|
|
int $call_value,
|
|||
|
|
string $type,
|
|||
|
|
string $error_id,
|
|||
|
|
array $expected_history
|
|||
|
|
): void {
|
|||
|
|
// self::$log->setLogLevelAll('debug', true);
|
|||
|
|
// self::$log->setLogLevelAll('print', true);
|
|||
|
|
$db = new \CoreLibs\DB\IO(
|
|||
|
|
self::$db_config['valid'],
|
|||
|
|
self::$log
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// trigger the error call
|
|||
|
|
$db->dbSetMaxQueryCall($call_value);
|
|||
|
|
if ($type == 'error') {
|
|||
|
|
$last_error = $db->dbGetLastError();
|
|||
|
|
} else {
|
|||
|
|
$last_error = $db->dbGetLastWarning();
|
|||
|
|
}
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$error_id,
|
|||
|
|
$last_error
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
$error_history = $db->dbGetCombinedErrorHistory();
|
|||
|
|
// pop first error off
|
|||
|
|
$first_error_element = array_shift($error_history);
|
|||
|
|
// get first row element
|
|||
|
|
// comarep all, except timestamp that is a regex
|
|||
|
|
foreach ($expected_history as $key => $value) {
|
|||
|
|
// check if starts with / because this is regex (timestamp)
|
|||
|
|
// if (substr($expected_2, 0, 1) == '/) {
|
|||
|
|
if (strpos($value, '/') === 0) {
|
|||
|
|
// this is regex
|
|||
|
|
$this->assertMatchesRegularExpression(
|
|||
|
|
$value,
|
|||
|
|
$first_error_element[0][$key]
|
|||
|
|
);
|
|||
|
|
} else {
|
|||
|
|
// assert equal
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$value,
|
|||
|
|
$first_error_element[0][$key],
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// close connection
|
|||
|
|
$db->dbClose();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// - encoding settings (exclude encoding test, just set)
|
|||
|
|
// dbGetEncoding, dbSetEncoding
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* test encoding change list for dbSetEncoding
|
|||
|
|
*
|
|||
|
|
* @return array
|
|||
|
|
*/
|
|||
|
|
public function encodingProvider(): array
|
|||
|
|
{
|
|||
|
|
// 0: connection
|
|||
|
|
// 1: set encoding
|
|||
|
|
// 2: expected return from set
|
|||
|
|
// 2: expected to get
|
|||
|
|
// 3: error id
|
|||
|
|
return [
|
|||
|
|
'default set no encoding' => [
|
|||
|
|
'valid',
|
|||
|
|
'',
|
|||
|
|
false,
|
|||
|
|
// I expect that the default DB is set to UTF8
|
|||
|
|
'UTF8',
|
|||
|
|
'',
|
|||
|
|
],
|
|||
|
|
'set to Shift JIS' => [
|
|||
|
|
'valid',
|
|||
|
|
'ShiftJIS',
|
|||
|
|
true,
|
|||
|
|
'SJIS',
|
|||
|
|
'',
|
|||
|
|
],
|
|||
|
|
'set to Invalid' => [
|
|||
|
|
'valid',
|
|||
|
|
'Invalid',
|
|||
|
|
false,
|
|||
|
|
'UTF8',
|
|||
|
|
'81',
|
|||
|
|
],
|
|||
|
|
'set to empty' => [
|
|||
|
|
'valid',
|
|||
|
|
'',
|
|||
|
|
false,
|
|||
|
|
'UTF8',
|
|||
|
|
'80',
|
|||
|
|
]
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* change DB encoding, only function set test, not test of encoding change
|
|||
|
|
* TODO: add encoding changed test with DB insert
|
|||
|
|
*
|
|||
|
|
* @covers ::dbSetEncoding
|
|||
|
|
* @covers ::dbGetEncoding
|
|||
|
|
* @dataProvider encodingProvider
|
|||
|
|
* @testdox Set encoding on $connection to $set_encoding expect $expected_set_flag and $expected_get_encoding [$_dataName]
|
|||
|
|
*
|
|||
|
|
* @param string $connection
|
|||
|
|
* @param string $set_encoding
|
|||
|
|
* @param boolean $expected_set_flag
|
|||
|
|
* @param string $expected_get_encoding
|
|||
|
|
* @return void
|
|||
|
|
*/
|
|||
|
|
public function testEncoding(
|
|||
|
|
string $connection,
|
|||
|
|
string $set_encoding,
|
|||
|
|
bool $expected_set_flag,
|
|||
|
|
string $expected_get_encoding
|
|||
|
|
): void {
|
|||
|
|
// self::$log->setLogLevelAll('debug', true);
|
|||
|
|
// self::$log->setLogLevelAll('print', true);
|
|||
|
|
$db = new \CoreLibs\DB\IO(
|
|||
|
|
self::$db_config[$connection],
|
|||
|
|
self::$log
|
|||
|
|
);
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected_set_flag,
|
|||
|
|
// avoid bubbling up error
|
|||
|
|
@$db->dbSetEncoding($set_encoding)
|
|||
|
|
);
|
|||
|
|
// show query
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected_get_encoding,
|
|||
|
|
$db->dbGetEncoding()
|
|||
|
|
);
|
|||
|
|
// pg_version
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected_get_encoding,
|
|||
|
|
$db->dbVersionInfo('client_encoding')
|
|||
|
|
);
|
|||
|
|
$db->dbClose();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// - encoding conversion on read and test encoding conversion on db connection
|
|||
|
|
// dbSetToEncoding, dbGetToEncoding
|
|||
|
|
// [and test encoding transfer with both types]
|
|||
|
|
// dbSetEncoding, dbGetEncoding
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Undocumented function
|
|||
|
|
*
|
|||
|
|
* @return array
|
|||
|
|
*/
|
|||
|
|
public function encodingConversionProvider(): array
|
|||
|
|
{
|
|||
|
|
// 0: connection
|
|||
|
|
// 1: target encoding (or alias)
|
|||
|
|
// 2: optional name for php for proper alias matching
|
|||
|
|
// 3: text to check
|
|||
|
|
return [
|
|||
|
|
'convert from UTF8 to SJIS' => [
|
|||
|
|
'valid',
|
|||
|
|
'SJIS',
|
|||
|
|
null,
|
|||
|
|
'日本語カタカナひらがな'
|
|||
|
|
],
|
|||
|
|
// SHIFT_JIS_2004/SJIS-win
|
|||
|
|
// EUC_JP/EUC-JP
|
|||
|
|
//
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* tests actually text conversion and not only just setting
|
|||
|
|
* NOTE: database is always stored as UTF8 in our case so all
|
|||
|
|
* tests check conversion FROM utf8 to a target.
|
|||
|
|
* Also because only SJIS is of interest, only this one is tested
|
|||
|
|
* https://www.postgresql.org/docs/current/multibyte.html#MULTIBYTE-CHARSET-SUPPORTED
|
|||
|
|
* SHIFT_JIS_2004
|
|||
|
|
* SJIS (Mskanji, ShiftJIS, WIN932, Windows932)
|
|||
|
|
* EUC_JP
|
|||
|
|
* EUC_JIS_2004
|
|||
|
|
*
|
|||
|
|
* @covers ::dbSetToEncoding
|
|||
|
|
* @covers ::dbGetToEncoding
|
|||
|
|
* @covers ::dbSetEncoding
|
|||
|
|
* @covers ::dbGetEncoding
|
|||
|
|
* @dataProvider encodingConversionProvider
|
|||
|
|
* @testdox Check encoding on $connection with $encoding [$_dataName]
|
|||
|
|
*
|
|||
|
|
* @param string $connection
|
|||
|
|
* @param string $encoding
|
|||
|
|
* @param string|null $encoding_php
|
|||
|
|
* @param string $text
|
|||
|
|
* @return void
|
|||
|
|
*/
|
|||
|
|
public function testEncodingConversion(
|
|||
|
|
string $connection,
|
|||
|
|
string $encoding,
|
|||
|
|
?string $encoding_php,
|
|||
|
|
string $text
|
|||
|
|
): void {
|
|||
|
|
// self::$log->setLogLevelAll('debug', true);
|
|||
|
|
// self::$log->setLogLevelAll('print', true);
|
|||
|
|
$db = new \CoreLibs\DB\IO(
|
|||
|
|
self::$db_config[$connection],
|
|||
|
|
self::$log
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// convert in php unless encoding is the smae
|
|||
|
|
if (strtolower($encoding) != 'utf8') {
|
|||
|
|
$encoded = mb_convert_encoding($text, $encoding_php ?? $encoding, 'UTF-8');
|
|||
|
|
} else {
|
|||
|
|
$encoded = $text;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// insert data
|
|||
|
|
$insert_query = "INSERT INTO table_with_primary_key (row_varchar, uid) VALUES "
|
|||
|
|
. "(" . $db->dbEscapeLiteral($text) . ", 'A')";
|
|||
|
|
$db->dbExec($insert_query);
|
|||
|
|
// for check read
|
|||
|
|
$read_query = "SELECT row_varchar, uid FROM table_with_primary_key WHERE uid = 'A'";
|
|||
|
|
|
|||
|
|
// TEST 1 in class
|
|||
|
|
// test to encoding (conversion with mb_convert_encoding)
|
|||
|
|
$db->dbSetToEncoding($encoding);
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$encoding,
|
|||
|
|
$db->dbGetToEncoding()
|
|||
|
|
);
|
|||
|
|
// read query, check that row_varchar matches
|
|||
|
|
$row = $db->dbReturnRow($read_query, true);
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$encoded,
|
|||
|
|
$row['row_varchar']
|
|||
|
|
);
|
|||
|
|
// reset to encoding to empty
|
|||
|
|
$db->dbSetToEncoding('');
|
|||
|
|
// and check
|
|||
|
|
$this->assertEquals(
|
|||
|
|
'',
|
|||
|
|
$db->dbGetToEncoding()
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// TEST 2 DB side
|
|||
|
|
// same test with setting database encoding
|
|||
|
|
// if connection encoding differts
|
|||
|
|
if (strtolower($db->dbGetSetting('encoding')) != strtolower($encoding)) {
|
|||
|
|
$db->dbSetEncoding($encoding);
|
|||
|
|
}
|
|||
|
|
// read from DB and check encoding
|
|||
|
|
$row = $db->dbReturnRow($read_query, true);
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$encoded,
|
|||
|
|
$row['row_varchar']
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// reset all data
|
|||
|
|
$db->dbExec("TRUNCATE table_with_primary_key");
|
|||
|
|
$db->dbExec("TRUNCATE table_without_primary_key");
|
|||
|
|
// close connection
|
|||
|
|
$db->dbClose();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// - get primary key
|
|||
|
|
// dbGetReturning, dbGetInsertPK, dbGetInsertPKName
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Undocumented function
|
|||
|
|
*
|
|||
|
|
* @return array
|
|||
|
|
*/
|
|||
|
|
public function primaryKeyProvider(): array
|
|||
|
|
{
|
|||
|
|
// 0: insert query add (returning, etc)
|
|||
|
|
// 1: pk_name, null for default
|
|||
|
|
// 2: table name
|
|||
|
|
// 3: primary key or empty if none
|
|||
|
|
return [
|
|||
|
|
'normal all auto' => [
|
|||
|
|
'',
|
|||
|
|
null,
|
|||
|
|
'table_with_primary_key',
|
|||
|
|
'table_with_primary_key_id',
|
|||
|
|
],
|
|||
|
|
'table without primary key' => [
|
|||
|
|
'',
|
|||
|
|
null,
|
|||
|
|
'table_without_primary_key',
|
|||
|
|
''
|
|||
|
|
],
|
|||
|
|
// valid primary key name
|
|||
|
|
'normal, with pk_name' => [
|
|||
|
|
'',
|
|||
|
|
'table_with_primary_key_id',
|
|||
|
|
'table_with_primary_key',
|
|||
|
|
'table_with_primary_key_id',
|
|||
|
|
],
|
|||
|
|
// returning name no pk name
|
|||
|
|
'normal, with returning' => [
|
|||
|
|
'RETURNING table_with_primary_key_id',
|
|||
|
|
null,
|
|||
|
|
'table_with_primary_key',
|
|||
|
|
'table_with_primary_key_id',
|
|||
|
|
],
|
|||
|
|
// both pk name and returning
|
|||
|
|
'normal, with returning' => [
|
|||
|
|
'RETURNING table_with_primary_key_id',
|
|||
|
|
'table_with_primary_key_id',
|
|||
|
|
'table_with_primary_key',
|
|||
|
|
'table_with_primary_key_id',
|
|||
|
|
],
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Undocumented function
|
|||
|
|
*
|
|||
|
|
* @covers ::dbGetInsertPK
|
|||
|
|
* @covers ::dbGetInsertPKName
|
|||
|
|
* @dataProvider primaryKeyProvider
|
|||
|
|
* @testdox Check returning pk $insert with $pk_name [$_dataName]
|
|||
|
|
*
|
|||
|
|
* @param string $insert
|
|||
|
|
* @param string|null $pk_name
|
|||
|
|
* @return void
|
|||
|
|
*/
|
|||
|
|
public function testGetPrimaryKey(
|
|||
|
|
string $insert,
|
|||
|
|
?string $pk_name,
|
|||
|
|
string $table,
|
|||
|
|
string $primary_key
|
|||
|
|
): void {
|
|||
|
|
// self::$log->setLogLevelAll('debug', true);
|
|||
|
|
// self::$log->setLogLevelAll('print', true);
|
|||
|
|
$db = new \CoreLibs\DB\IO(
|
|||
|
|
self::$db_config['valid'],
|
|||
|
|
self::$log
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// basic query
|
|||
|
|
$insert_query = "INSERT INTO " . $db->dbEscapeIdentifier($table)
|
|||
|
|
. " (uid) "
|
|||
|
|
. "VALUES ('A') " . $insert;
|
|||
|
|
$pk_name === null ?
|
|||
|
|
$db->dbExec($insert_query) :
|
|||
|
|
$db->dbExec($insert_query, $pk_name);
|
|||
|
|
// $db_get_returning = $db->dbGetReturning();
|
|||
|
|
$db_get_insert_pk_name = $db->dbGetInsertPKName();
|
|||
|
|
$db_get_insert_pk = $db->dbGetInsertPK();
|
|||
|
|
|
|||
|
|
$read_query = "SELECT "
|
|||
|
|
. (!empty($primary_key) ?
|
|||
|
|
$db->dbEscapeIdentifier($primary_key) . ", " : ""
|
|||
|
|
)
|
|||
|
|
. "uid "
|
|||
|
|
. "FROM " . $db->dbEscapeIdentifier($table)
|
|||
|
|
. " WHERE uid = 'A'";
|
|||
|
|
$row = $db->dbReturnRow($read_query, true);
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$row[$primary_key] ?? null,
|
|||
|
|
$db_get_insert_pk
|
|||
|
|
);
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$primary_key,
|
|||
|
|
$db_get_insert_pk_name
|
|||
|
|
);
|
|||
|
|
$this->assertTrue(true);
|
|||
|
|
|
|||
|
|
|
|||
|
|
// reset all data
|
|||
|
|
$db->dbExec("TRUNCATE table_with_primary_key");
|
|||
|
|
$db->dbExec("TRUNCATE table_without_primary_key");
|
|||
|
|
// close connection
|
|||
|
|
$db->dbClose();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// - complex returning data checks
|
|||
|
|
// dbGetReturningExt, dbGetReturningArray
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Undocumented function
|
|||
|
|
*
|
|||
|
|
* @return array
|
|||
|
|
*/
|
|||
|
|
public function returningPrvoider(): array
|
|||
|
|
{
|
|||
|
|
// 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;
|
|||
|
|
// 0: query + returning
|
|||
|
|
// 1: pk name for db exec
|
|||
|
|
// 2: key name/value or null (dbGetReturningExt)
|
|||
|
|
// 3: pos or null (dbGetReturningExt)
|
|||
|
|
// 4: matching return value (dbGetReturningExt)
|
|||
|
|
// 5: full returning array value (dbGetReturningArray)
|
|||
|
|
return [
|
|||
|
|
'single insert (PK)' => [
|
|||
|
|
"INSERT INTO table_with_primary_key "
|
|||
|
|
. "(row_varchar, row_varchar_literal, row_int, row_date) "
|
|||
|
|
. "VALUES "
|
|||
|
|
. "('Text', 'Other', 123, '2022-03-01') "
|
|||
|
|
. "RETURNING row_varchar, row_varchar_literal, row_int, row_date",
|
|||
|
|
null,
|
|||
|
|
null,
|
|||
|
|
null,
|
|||
|
|
[
|
|||
|
|
'row_varchar' => 'Text',
|
|||
|
|
'row_varchar_literal' => 'Other',
|
|||
|
|
'row_int' => 123,
|
|||
|
|
'row_date' => '2022-03-01',
|
|||
|
|
// 'table_with_primary_key_id' => "/^\d+$/",
|
|||
|
|
'table_with_primary_key_id' => $table_with_primary_key_id + 1,
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
0 => [
|
|||
|
|
'row_varchar' => 'Text',
|
|||
|
|
'row_varchar_literal' => 'Other',
|
|||
|
|
'row_int' => 123,
|
|||
|
|
'row_date' => '2022-03-01',
|
|||
|
|
// 'table_with_primary_key_id' => "/^\d+$/",
|
|||
|
|
'table_with_primary_key_id' => $table_with_primary_key_id + 1,
|
|||
|
|
]
|
|||
|
|
]
|
|||
|
|
],
|
|||
|
|
// same but as EOM
|
|||
|
|
'single insert (PK), EOM string' => [
|
|||
|
|
<<<EOM
|
|||
|
|
INSERT INTO table_with_primary_key (
|
|||
|
|
row_varchar, row_varchar_literal, row_int, row_date
|
|||
|
|
) VALUES (
|
|||
|
|
'Text', 'Other', 123, '2022-03-01'
|
|||
|
|
)
|
|||
|
|
RETURNING row_varchar, row_varchar_literal, row_int, row_date
|
|||
|
|
EOM,
|
|||
|
|
null,
|
|||
|
|
null,
|
|||
|
|
null,
|
|||
|
|
[
|
|||
|
|
'row_varchar' => 'Text',
|
|||
|
|
'row_varchar_literal' => 'Other',
|
|||
|
|
'row_int' => 123,
|
|||
|
|
'row_date' => '2022-03-01',
|
|||
|
|
// 'table_with_primary_key_id' => "/^\d+$/",
|
|||
|
|
'table_with_primary_key_id' => $table_with_primary_key_id + 2,
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
0 => [
|
|||
|
|
'row_varchar' => 'Text',
|
|||
|
|
'row_varchar_literal' => 'Other',
|
|||
|
|
'row_int' => 123,
|
|||
|
|
'row_date' => '2022-03-01',
|
|||
|
|
// 'table_with_primary_key_id' => "/^\d+$/",
|
|||
|
|
'table_with_primary_key_id' => $table_with_primary_key_id + 2,
|
|||
|
|
]
|
|||
|
|
]
|
|||
|
|
],
|
|||
|
|
// double insert (PK)
|
|||
|
|
'dobule insert (PK)' => [
|
|||
|
|
"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') "
|
|||
|
|
. "RETURNING row_varchar, row_varchar_literal, row_int, row_date",
|
|||
|
|
null,
|
|||
|
|
null,
|
|||
|
|
null,
|
|||
|
|
[
|
|||
|
|
0 => [
|
|||
|
|
'row_varchar' => 'Text',
|
|||
|
|
'row_varchar_literal' => 'Other',
|
|||
|
|
'row_int' => 123,
|
|||
|
|
'row_date' => '2022-03-01',
|
|||
|
|
'table_with_primary_key_id' => $table_with_primary_key_id + 3,
|
|||
|
|
],
|
|||
|
|
1 => [
|
|||
|
|
'row_varchar' => 'Foxtrott',
|
|||
|
|
'row_varchar_literal' => 'Tango',
|
|||
|
|
'row_int' => 789,
|
|||
|
|
'row_date' => '1982-10-15',
|
|||
|
|
'table_with_primary_key_id' => $table_with_primary_key_id + 4,
|
|||
|
|
],
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
0 => [
|
|||
|
|
'row_varchar' => 'Text',
|
|||
|
|
'row_varchar_literal' => 'Other',
|
|||
|
|
'row_int' => 123,
|
|||
|
|
'row_date' => '2022-03-01',
|
|||
|
|
'table_with_primary_key_id' => $table_with_primary_key_id + 3,
|
|||
|
|
],
|
|||
|
|
1 => [
|
|||
|
|
'row_varchar' => 'Foxtrott',
|
|||
|
|
'row_varchar_literal' => 'Tango',
|
|||
|
|
'row_int' => 789,
|
|||
|
|
'row_date' => '1982-10-15',
|
|||
|
|
'table_with_primary_key_id' => $table_with_primary_key_id + 4,
|
|||
|
|
],
|
|||
|
|
]
|
|||
|
|
],
|
|||
|
|
// insert into table with no primary key
|
|||
|
|
'single insert (No PK)' => [
|
|||
|
|
"INSERT INTO table_without_primary_key "
|
|||
|
|
. "(row_varchar, row_varchar_literal, row_int, row_date) "
|
|||
|
|
. "VALUES "
|
|||
|
|
. "('Text', 'Other', 123, '2022-03-01') "
|
|||
|
|
. "RETURNING row_varchar, row_varchar_literal, row_int, row_date",
|
|||
|
|
null,
|
|||
|
|
null,
|
|||
|
|
null,
|
|||
|
|
[
|
|||
|
|
'row_varchar' => 'Text',
|
|||
|
|
'row_varchar_literal' => 'Other',
|
|||
|
|
'row_int' => 123,
|
|||
|
|
'row_date' => '2022-03-01',
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
0 => [
|
|||
|
|
'row_varchar' => 'Text',
|
|||
|
|
'row_varchar_literal' => 'Other',
|
|||
|
|
'row_int' => 123,
|
|||
|
|
'row_date' => '2022-03-01',
|
|||
|
|
]
|
|||
|
|
]
|
|||
|
|
],
|
|||
|
|
// same as above but as EOM string
|
|||
|
|
'single insert (No PK), EOM string' => [
|
|||
|
|
<<<EOM
|
|||
|
|
INSERT INTO table_without_primary_key (
|
|||
|
|
row_varchar, row_varchar_literal, row_int, row_date
|
|||
|
|
) VALUES (
|
|||
|
|
'Text', 'Other', 123, '2022-03-01'
|
|||
|
|
)
|
|||
|
|
RETURNING row_varchar, row_varchar_literal, row_int, row_date
|
|||
|
|
EOM,
|
|||
|
|
null,
|
|||
|
|
null,
|
|||
|
|
null,
|
|||
|
|
[
|
|||
|
|
'row_varchar' => 'Text',
|
|||
|
|
'row_varchar_literal' => 'Other',
|
|||
|
|
'row_int' => 123,
|
|||
|
|
'row_date' => '2022-03-01',
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
0 => [
|
|||
|
|
'row_varchar' => 'Text',
|
|||
|
|
'row_varchar_literal' => 'Other',
|
|||
|
|
'row_int' => 123,
|
|||
|
|
'row_date' => '2022-03-01',
|
|||
|
|
]
|
|||
|
|
]
|
|||
|
|
],
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Undocumented function
|
|||
|
|
*
|
|||
|
|
* @covers ::dbGetReturningExt
|
|||
|
|
* @covers ::dbGetReturningArray
|
|||
|
|
* @dataProvider returningPrvoider
|
|||
|
|
* @testdox Check returning cursor using $pk_name with $key and $pos [$_dataName]
|
|||
|
|
*
|
|||
|
|
* @param string $query
|
|||
|
|
* @param string|null $pk_name
|
|||
|
|
* @param string|null $key
|
|||
|
|
* @param integer|null $pos
|
|||
|
|
* @param array<mixed>|string|int|null $expected_ret_ext
|
|||
|
|
* @param array $expected_ret_arr
|
|||
|
|
* @return void
|
|||
|
|
*/
|
|||
|
|
public function testDbReturning(
|
|||
|
|
string $query,
|
|||
|
|
?string $pk_name,
|
|||
|
|
?string $key,
|
|||
|
|
?int $pos,
|
|||
|
|
$expected_ret_ext,
|
|||
|
|
array $expected_ret_arr
|
|||
|
|
): void {
|
|||
|
|
// self::$log->setLogLevelAll('debug', true);
|
|||
|
|
// self::$log->setLogLevelAll('print', true);
|
|||
|
|
$db = new \CoreLibs\DB\IO(
|
|||
|
|
self::$db_config['valid'],
|
|||
|
|
self::$log
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// insert data
|
|||
|
|
$pk_name === null ?
|
|||
|
|
$db->dbExec($query) :
|
|||
|
|
$db->dbExec($query, $pk_name);
|
|||
|
|
|
|||
|
|
// get the last value for PK and match that somehow
|
|||
|
|
|
|||
|
|
$returning_ext = $db->dbGetReturningExt($key, $pos);
|
|||
|
|
$returning_arr = $db->dbGetReturningArray();
|
|||
|
|
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected_ret_ext,
|
|||
|
|
$returning_ext,
|
|||
|
|
'Returning extended failed'
|
|||
|
|
);
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected_ret_arr,
|
|||
|
|
$returning_arr,
|
|||
|
|
'Returning Array failed'
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// print "EXT: " . print_r($returning_ext, true) . "\n";
|
|||
|
|
// print "ARR: " . print_r($returning_arr, true) . "\n";
|
|||
|
|
|
|||
|
|
// reset all data
|
|||
|
|
$db->dbExec("TRUNCATE table_with_primary_key");
|
|||
|
|
$db->dbExec("TRUNCATE table_without_primary_key");
|
|||
|
|
// close connection
|
|||
|
|
$db->dbClose();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// - internal read data (post exec)
|
|||
|
|
// dbGetNumRows, dbGetNumFields, dbGetFieldNames,
|
|||
|
|
// dbGetQuery, dbGetQueryHash, dbGetDbh
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Undocumented function
|
|||
|
|
*
|
|||
|
|
* @return array
|
|||
|
|
*/
|
|||
|
|
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
|
|||
|
|
return [
|
|||
|
|
'select data' => [
|
|||
|
|
"SELECT row_varchar, row_varchar_literal, row_int, row_date "
|
|||
|
|
. "FROM table_with_primary_key",
|
|||
|
|
"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,
|
|||
|
|
//
|
|||
|
|
2,
|
|||
|
|
4,
|
|||
|
|
['row_varchar', 'row_varchar_literal', 'row_int', 'row_date'],
|
|||
|
|
],
|
|||
|
|
// insert
|
|||
|
|
'insert data' => [
|
|||
|
|
"INSERT INTO table_with_primary_key "
|
|||
|
|
. "(row_varchar, row_varchar_literal, row_int, row_numeric, row_date) "
|
|||
|
|
. "VALUES "
|
|||
|
|
. "('Text', 'Other', 123, 1.0, '2022-03-01'), "
|
|||
|
|
. "('Foxtrott', 'Tango', 789, 2.2, '1982-10-15'), "
|
|||
|
|
. "('Schlamm', 'Beizinger', 100, 3.14, '1990-1-1') ",
|
|||
|
|
null,
|
|||
|
|
"INSERT INTO table_with_primary_key "
|
|||
|
|
. "(row_varchar, row_varchar_literal, row_int, row_numeric, row_date) "
|
|||
|
|
. "VALUES "
|
|||
|
|
. "('Text', 'Other', 123, 1.0, '2022-03-01'), "
|
|||
|
|
. "('Foxtrott', 'Tango', 789, 2.2, '1982-10-15'), "
|
|||
|
|
. "('Schlamm', 'Beizinger', 100, 3.14, '1990-1-1') "
|
|||
|
|
. " RETURNING table_with_primary_key_id",
|
|||
|
|
//
|
|||
|
|
3,
|
|||
|
|
0,
|
|||
|
|
[],
|
|||
|
|
],
|
|||
|
|
// update
|
|||
|
|
'update data' => [
|
|||
|
|
"UPDATE table_with_primary_key SET "
|
|||
|
|
. "row_varchar = 'CHANGE A', row_int = 999 "
|
|||
|
|
. "WHERE uid = 'A'",
|
|||
|
|
"INSERT INTO table_with_primary_key "
|
|||
|
|
. "(uid, row_varchar, row_varchar_literal, row_int, row_date) "
|
|||
|
|
. "VALUES "
|
|||
|
|
. "('A', 'Text', 'Other', 123, '2022-03-01'), "
|
|||
|
|
. "('B', 'Foxtrott', 'Tango', 789, '1982-10-15') ",
|
|||
|
|
null,
|
|||
|
|
//
|
|||
|
|
1,
|
|||
|
|
0,
|
|||
|
|
[],
|
|||
|
|
]
|
|||
|
|
// something other (schema change?)
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Undocumented function
|
|||
|
|
*
|
|||
|
|
* @covers ::dbGetNumRows
|
|||
|
|
* @covers ::dbGetNumFields
|
|||
|
|
* @covers ::dbGetFieldNames
|
|||
|
|
* @covers ::dbGetQuery
|
|||
|
|
* @covers ::dbGetQueryHash
|
|||
|
|
* @covers ::dbGetDbh
|
|||
|
|
* @dataProvider getMethodsProvider
|
|||
|
|
* @testdox Check check rows: $expected_rows and cols: $expected_cols [$_dataName]
|
|||
|
|
*
|
|||
|
|
* @param string $query
|
|||
|
|
* @param string|null $insert_query
|
|||
|
|
* @param string|null $compare_query
|
|||
|
|
* @param integer $expected_rows
|
|||
|
|
* @param integer $expected_cols
|
|||
|
|
* @param array $expected_col_names
|
|||
|
|
* @return void
|
|||
|
|
*/
|
|||
|
|
public function testDbGetMethods(
|
|||
|
|
string $query,
|
|||
|
|
?string $insert_query,
|
|||
|
|
?string $compare_query,
|
|||
|
|
int $expected_rows,
|
|||
|
|
int $expected_cols,
|
|||
|
|
array $expected_col_names
|
|||
|
|
): void {
|
|||
|
|
// self::$log->setLogLevelAll('debug', true);
|
|||
|
|
// self::$log->setLogLevelAll('print', true);
|
|||
|
|
$db = new \CoreLibs\DB\IO(
|
|||
|
|
self::$db_config['valid'],
|
|||
|
|
self::$log
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
if (!empty($insert_query)) {
|
|||
|
|
$db->dbExec($insert_query);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$db->dbExec($query);
|
|||
|
|
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$compare_query ?? $query,
|
|||
|
|
$db->dbGetQuery()
|
|||
|
|
);
|
|||
|
|
$this->assertEquals(
|
|||
|
|
// perhaps move that somewhere else?
|
|||
|
|
\CoreLibs\Create\Hash::__hashLong($query),
|
|||
|
|
$db->dbGetQueryHash($query)
|
|||
|
|
);
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected_rows,
|
|||
|
|
$db->dbGetNumRows()
|
|||
|
|
);
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected_cols,
|
|||
|
|
$db->dbGetNumFields()
|
|||
|
|
);
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected_col_names,
|
|||
|
|
$db->dbGetFieldNames()
|
|||
|
|
);
|
|||
|
|
$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
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// if this is a select query, db dbReturn, dbReturnRow, dbReturnArray too
|
|||
|
|
if (preg_match("/^(select|show|with) /i", $query)) {
|
|||
|
|
// dbReturn
|
|||
|
|
$db->dbReturn($query);
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected_rows,
|
|||
|
|
$db->dbGetNumRows()
|
|||
|
|
);
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected_cols,
|
|||
|
|
$db->dbGetNumFields()
|
|||
|
|
);
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected_col_names,
|
|||
|
|
$db->dbGetFieldNames()
|
|||
|
|
);
|
|||
|
|
// dbReturnRow
|
|||
|
|
// will return ALL rows there, but returns only the first
|
|||
|
|
$db->dbReturnRow($query);
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected_rows,
|
|||
|
|
$db->dbGetNumRows()
|
|||
|
|
);
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected_cols,
|
|||
|
|
$db->dbGetNumFields()
|
|||
|
|
);
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected_col_names,
|
|||
|
|
$db->dbGetFieldNames()
|
|||
|
|
);
|
|||
|
|
// dbReturnArray
|
|||
|
|
$db->dbReturnArray($query);
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected_rows,
|
|||
|
|
$db->dbGetNumRows()
|
|||
|
|
);
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected_cols,
|
|||
|
|
$db->dbGetNumFields()
|
|||
|
|
);
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected_col_names,
|
|||
|
|
$db->dbGetFieldNames()
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// reset all data
|
|||
|
|
$db->dbExec("TRUNCATE table_with_primary_key");
|
|||
|
|
$db->dbExec("TRUNCATE table_without_primary_key");
|
|||
|
|
// close connection
|
|||
|
|
$db->dbClose();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// TODO implement below checks
|
|||
|
|
// - complex write sets
|
|||
|
|
// dbWriteData, dbWriteDataExt
|
|||
|
|
// - data debug
|
|||
|
|
// dbDumpData
|
|||
|
|
|
|||
|
|
// ASYNC at the end because it has 1s timeout
|
|||
|
|
// - asynchronous executions
|
|||
|
|
// dbExecAsync, dbCheckAsync
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Undocumented function
|
|||
|
|
*
|
|||
|
|
* @return array
|
|||
|
|
*/
|
|||
|
|
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
|
|||
|
|
return [
|
|||
|
|
'run simple async query' => [
|
|||
|
|
"SELECT pg_sleep(1)",
|
|||
|
|
null,
|
|||
|
|
// exec result
|
|||
|
|
true,
|
|||
|
|
'',
|
|||
|
|
'',
|
|||
|
|
// check first
|
|||
|
|
true,
|
|||
|
|
// check final
|
|||
|
|
'result',
|
|||
|
|
'',
|
|||
|
|
''
|
|||
|
|
],
|
|||
|
|
// send query failed (E40)
|
|||
|
|
// result failed (E43)
|
|||
|
|
// no query running (E42)
|
|||
|
|
'no async query running' => [
|
|||
|
|
'',
|
|||
|
|
null,
|
|||
|
|
//
|
|||
|
|
false,
|
|||
|
|
'',
|
|||
|
|
'11',
|
|||
|
|
//
|
|||
|
|
false,
|
|||
|
|
//
|
|||
|
|
false,
|
|||
|
|
'',
|
|||
|
|
'42'
|
|||
|
|
]
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Undocumented function
|
|||
|
|
*
|
|||
|
|
* @covers ::dbExecAsync
|
|||
|
|
* @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 string|null $pk_name
|
|||
|
|
* @param boolean $expected_exec
|
|||
|
|
* @param string $warning_exec
|
|||
|
|
* @param string $error_exec
|
|||
|
|
* @param bool $expected_check
|
|||
|
|
* @param bool|object|resource $expected_final
|
|||
|
|
* @param string $warning_final
|
|||
|
|
* @param string $error_final
|
|||
|
|
* @return void
|
|||
|
|
*/
|
|||
|
|
public function testDbExecAsync(
|
|||
|
|
string $query,
|
|||
|
|
?string $pk_name,
|
|||
|
|
bool $expected_exec,
|
|||
|
|
string $warning_exec,
|
|||
|
|
string $error_exec,
|
|||
|
|
bool $expected_check,
|
|||
|
|
$expected_final,
|
|||
|
|
string $warning_final,
|
|||
|
|
string $error_final
|
|||
|
|
): void {
|
|||
|
|
// self::$log->setLogLevelAll('debug', true);
|
|||
|
|
// self::$log->setLogLevelAll('print', true);
|
|||
|
|
$db = new \CoreLibs\DB\IO(
|
|||
|
|
self::$db_config['valid'],
|
|||
|
|
self::$log
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// exec the query
|
|||
|
|
$result_exec = $pk_name === null ?
|
|||
|
|
$db->dbExecAsync($query) :
|
|||
|
|
$db->dbExecAsync($query, $pk_name);
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected_exec,
|
|||
|
|
$result_exec
|
|||
|
|
);
|
|||
|
|
// error/warning check
|
|||
|
|
$this->subAssertErrorTest($db, $warning_exec, $error_exec);
|
|||
|
|
|
|||
|
|
$run = 1;
|
|||
|
|
// first loop check
|
|||
|
|
while (($result_check = $db->dbCheckAsync()) === true) {
|
|||
|
|
if ($run == 1) {
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected_check,
|
|||
|
|
$result_check
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
$run++;
|
|||
|
|
}
|
|||
|
|
// 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
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
// else compar check
|
|||
|
|
$this->assertEquals(
|
|||
|
|
$expected_final,
|
|||
|
|
$result_check
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
// error/warning check
|
|||
|
|
$this->subAssertErrorTest($db, $warning_final, $error_final);
|
|||
|
|
|
|||
|
|
// reset all data
|
|||
|
|
$db->dbExec("TRUNCATE table_with_primary_key");
|
|||
|
|
$db->dbExec("TRUNCATE table_without_primary_key");
|
|||
|
|
// close connection
|
|||
|
|
$db->dbClose();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// __END__
|