From 3881a2a83d7a71168d5fd7f4d24ab60664a0edd0 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Tue, 8 Mar 2022 06:14:41 +0900 Subject: [PATCH] DB\IO fix primary key return Fixed table lookup if tables are in quotes (") If no primary key name is set return empty. If no primary key ID is returned, return null. --- 4dev/tests/CoreLibsDBIOTest.php | 170 ++++++++++++++++++++++++++++++-- www/lib/CoreLibs/DB/IO.php | 41 ++++---- 2 files changed, 183 insertions(+), 28 deletions(-) diff --git a/4dev/tests/CoreLibsDBIOTest.php b/4dev/tests/CoreLibsDBIOTest.php index bd784bbb..3b9679c7 100644 --- a/4dev/tests/CoreLibsDBIOTest.php +++ b/4dev/tests/CoreLibsDBIOTest.php @@ -211,16 +211,16 @@ final class CoreLibsDBIOTest extends TestCase public function versionProvider(): array { return [ - 'compare = ok' => [ '=13.5.0', true ], + 'compare = ok' => [ '=13.6.0', true ], 'compare = bad' => [ '=9.2.0', false ], 'compare < ok' => [ '<20.0.0', true ], 'compare < bad' => [ '<9.2.0', false ], 'compare <= ok a' => [ '<=20.0.0', true ], - 'compare <= ok b' => [ '<=13.5.0', true ], + 'compare <= ok b' => [ '<=13.6.0', true ], 'compare <= false' => [ '<=9.2.0', false ], 'compare > ok' => [ '>9.2.0', true ], 'compare > bad' => [ '>20.2.0', false ], - 'compare >= ok a' => [ '>=13.5.0', true ], + 'compare >= ok a' => [ '>=13.6.0', true ], 'compare >= ok b' => [ '>=9.2.0', true ], 'compare >= bad' => [ '>=20.0.0', false ], ]; @@ -229,7 +229,7 @@ final class CoreLibsDBIOTest extends TestCase /** * NOTE * Version tests will fail if versions change - * Current base as Version 13.5 for equal check + * 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 * @@ -256,7 +256,7 @@ final class CoreLibsDBIOTest extends TestCase // ->willReturn('13.1.0'); // print "DB: " . $stub->dbVersion() . "\n"; // print "TEST: " . ($stub->dbCompareVersion('=13.1.0') ? 'YES' : 'NO') . "\n"; - // print "TEST: " . ($stub->dbCompareVersion('=13.5.0') ? 'YES' : 'NO') . "\n"; + // print "TEST: " . ($stub->dbCompareVersion('=13.6.0') ? 'YES' : 'NO') . "\n"; // $mock = $this->getMockBuilder(CoreLibs\DB\IO::class) // ->addMethods(['dbVersion']) // ->ge‌​tMock(); @@ -2781,14 +2781,164 @@ final class CoreLibsDBIOTest extends TestCase $db->dbClose(); } - // - data debug - // dbDumpData + // - 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', + ], + // TODO other tests + ]; + } + + /** + * 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; + $result = $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 + + public function returingProvider(): array + { + // NOTE that query can have multiple inserts + // 0: query + returning + // 1: key name/value or null + // 2: pos or null + // 3: matching return value + // 4: full returning array value + return [ + 'single insert (PK)' => [ + "INSERT INTO table_with_primary_key " + . "(row_varchar, row_varchar_literalm row_int, row_date) " + . "VALUES " + . "() " + ] + ]; + } + + public function testDbReturning(): void + { + // self::$log->setLogLevelAll('debug', true); + // self::$log->setLogLevelAll('print', true); + $db = new \CoreLibs\DB\IO( + self::$db_config['valid'], + self::$log + ); + + // 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) - // dbGetReturning, dbGetInsertPKName, dbGetInsertPK, dbGetReturningExt, - // dbGetReturningArray, dbGetNumRows, dbGetNumFields, - // dbGetFieldNames, dbGetQuery, dbGetQueryHash, dbGetDbh + // dbGetNumRows, dbGetNumFields, dbGetFieldNames, + // dbGetQuery, dbGetQueryHash, dbGetDbh // - complex write sets // dbWriteData, dbWriteDataExt + // - data debug + // dbDumpData // - deprecated tests [no need to test perhaps] // getInsertReturn, getReturning, getInsertPK, getReturningExt, // getCursorExt, getNumRows diff --git a/www/lib/CoreLibs/DB/IO.php b/www/lib/CoreLibs/DB/IO.php index 91151bd8..4c502ca2 100644 --- a/www/lib/CoreLibs/DB/IO.php +++ b/www/lib/CoreLibs/DB/IO.php @@ -878,11 +878,11 @@ class IO { $matches = []; if (preg_match("/^SELECT /i", $query)) { - preg_match("/ (FROM) (([\w_]+)\.)?([\w_]+) /i", $query, $matches); + preg_match("/ (FROM) \"?(([\w_]+)\.)?([\w_]+)\"? /i", $query, $matches); } else { - preg_match("/(INSERT INTO|DELETE FROM|UPDATE) (([\w_]+)\.)?([\w_]+) /i", $query, $matches); + preg_match("/(INSERT INTO|DELETE FROM|UPDATE) \"?(([\w_]+)\.)?([\w_]+)\"? /i", $query, $matches); } - return [$matches[3], $matches[4]]; + return [$matches[3] ?? '', $matches[4] ?? '']; } /** @@ -1000,7 +1000,9 @@ class IO if (!array_key_exists($table, $this->pk_name_table) || !$this->pk_name_table[$table]) { $this->pk_name_table[$table] = $this->db_functions->__dbPrimaryKey($table, $schema); } - $this->pk_name = $this->pk_name_table[$table] ? $this->pk_name_table[$table] : 'NULL'; + $this->pk_name = + $this->pk_name_table[$table] ? + $this->pk_name_table[$table] : 'NULL'; } if ( !preg_match("/ returning /i", $this->query) && @@ -2824,19 +2826,6 @@ class IO // INTERNAL VARIABLES READ POST QUERY RUN // *************************** - /** - * return current set insert_id as is - * @return array|string|int|bool|null Primary key value, most likely int - * Array for multiple return set - * Empty string for unset - * Null for error - * @deprecated Use ->dbGetInsertPK(); - */ - public function dbGetReturning() - { - return $this->dbGetInsertPK(); - } - /** * returns current set primary key name for last run query * Is empty string if not setable @@ -2845,7 +2834,7 @@ class IO */ public function dbGetInsertPKName(): string { - return $this->insert_id_pk_name; + return $this->insert_id_pk_name ?? ''; } /** @@ -2857,6 +2846,9 @@ class IO */ public function dbGetInsertPK() { + if (empty($this->insert_id_pk_name)) { + return null; + } return $this->dbGetReturningExt($this->insert_id_pk_name); } @@ -3020,6 +3012,19 @@ class IO // all call below are no longer in use and throw deprecated errors // *************************** + /** + * return current set insert_id as is + * @return array|string|int|bool|null Primary key value, most likely int + * Array for multiple return set + * Empty string for unset + * Null for error + * @deprecated Use ->dbGetInsertPK(); + */ + public function dbGetReturning() + { + return $this->dbGetInsertPK(); + } + /** * returns the db init error * if failed to connect it is set to true