From 79acf748ba9c4f9d9f980bd936ff65d12278acde Mon Sep 17 00:00:00 2001 From: Maxim Smakouz Date: Fri, 29 Mar 2024 12:49:56 +0200 Subject: [PATCH 1/6] Add test case --- .../Driver/Common/Schema/ConsistencyTest.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/Database/Functional/Driver/Common/Schema/ConsistencyTest.php b/tests/Database/Functional/Driver/Common/Schema/ConsistencyTest.php index 4e271c88..5cb9a149 100644 --- a/tests/Database/Functional/Driver/Common/Schema/ConsistencyTest.php +++ b/tests/Database/Functional/Driver/Common/Schema/ConsistencyTest.php @@ -164,6 +164,20 @@ public function testEnum(): void $this->assertTrue($schema->column('target')->compare($column)); } + public function testEnumWithSingleValue(): void + { + $schema = $this->schema('table'); + $this->assertFalse($schema->exists()); + + $column = $schema->enum('target', ['catalog']); + + $schema->save(); + $schema = $this->schema('table'); + $this->assertTrue($schema->exists()); + $this->assertTrue($schema->column('target')->compare($column)); + $this->assertSame(['catalog'], $schema->column('target')->getEnumValues()); + } + public function testJson(): void { $schema = $this->schema('table'); From d9ded284629b0e205f35232c57102532df2b5935 Mon Sep 17 00:00:00 2001 From: Maxim Smakouz Date: Fri, 29 Mar 2024 13:09:55 +0200 Subject: [PATCH 2/6] Fix single enum value search --- src/Driver/Postgres/Schema/PostgresColumn.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Driver/Postgres/Schema/PostgresColumn.php b/src/Driver/Postgres/Schema/PostgresColumn.php index e2d80bc5..6b32e95b 100644 --- a/src/Driver/Postgres/Schema/PostgresColumn.php +++ b/src/Driver/Postgres/Schema/PostgresColumn.php @@ -707,6 +707,13 @@ private static function resolveConstrains( $column->enumValues = $enumValues; $column->constrainName = $constraint['conname']; $column->constrained = true; + } else { + $pattern = '/CHECK \\(\\(\\([A-Za-z]+\\)::(.+) = \'([A-Za-z]+)\'::(.+)\\)\\)/i'; + if (\preg_match($pattern, $constraint['consrc'], $matches) && !empty($matches[2])) { + $column->enumValues = [$matches[2]]; + $column->constrainName = $constraint['conname']; + $column->constrained = true; + } } } } From 824d963b94c6e927c0d5ea84ca47d6809d3f789e Mon Sep 17 00:00:00 2001 From: Maxim Smakouz Date: Fri, 29 Mar 2024 17:47:07 +0200 Subject: [PATCH 3/6] Improve code, add unit tests --- src/Driver/Postgres/Schema/PostgresColumn.php | 47 +++++++------ .../Postgres/Schema/PostgresColumnTest.php | 67 +++++++++++++++++++ 2 files changed, 95 insertions(+), 19 deletions(-) create mode 100644 tests/Database/Unit/Driver/Postgres/Schema/PostgresColumnTest.php diff --git a/src/Driver/Postgres/Schema/PostgresColumn.php b/src/Driver/Postgres/Schema/PostgresColumn.php index 6b32e95b..548c4054 100644 --- a/src/Driver/Postgres/Schema/PostgresColumn.php +++ b/src/Driver/Postgres/Schema/PostgresColumn.php @@ -692,28 +692,12 @@ private static function resolveConstrains( ); foreach ($constraints as $constraint) { - if (preg_match('/ARRAY\[([^\]]+)\]/', $constraint['consrc'], $matches)) { - $enumValues = explode(',', $matches[1]); - foreach ($enumValues as &$value) { - if (preg_match("/^'?(.*?)'?::(.+)/", trim($value, ' ()'), $matches)) { - //In database: 'value'::TYPE - $value = $matches[1]; - } - - unset($value); - } - unset($value); + $values = static::parseEnumValues($constraint['consrc']); - $column->enumValues = $enumValues; + if ($values !== []) { + $column->enumValues = $values; $column->constrainName = $constraint['conname']; $column->constrained = true; - } else { - $pattern = '/CHECK \\(\\(\\([A-Za-z]+\\)::(.+) = \'([A-Za-z]+)\'::(.+)\\)\\)/i'; - if (\preg_match($pattern, $constraint['consrc'], $matches) && !empty($matches[2])) { - $column->enumValues = [$matches[2]]; - $column->constrainName = $constraint['conname']; - $column->constrained = true; - } } } } @@ -736,4 +720,29 @@ private static function resolveEnum(DriverInterface $driver, self $column): void ); } } + + private static function parseEnumValues(string $constraint): array + { + if (\preg_match('/ARRAY\[([^\]]+)\]/', $constraint, $matches)) { + $enumValues = \explode(',', $matches[1]); + foreach ($enumValues as &$value) { + if (\preg_match("/^'?(.*?)'?::(.+)/", \trim($value, ' ()'), $matches)) { + //In database: 'value'::TYPE + $value = $matches[1]; + } + + unset($value); + } + unset($value); + + return $enumValues; + } + + $pattern = '/CHECK \\(\\(\\([a-zA-Z0-9_]+\\)::(.+) = \'([a-zA-Z0-9_]+)\'::(.+)\\)\\)/i'; + if (\preg_match($pattern, $constraint, $matches) && !empty($matches[2])) { + return [$matches[2]]; + } + + return []; + } } diff --git a/tests/Database/Unit/Driver/Postgres/Schema/PostgresColumnTest.php b/tests/Database/Unit/Driver/Postgres/Schema/PostgresColumnTest.php new file mode 100644 index 00000000..4ca501a1 --- /dev/null +++ b/tests/Database/Unit/Driver/Postgres/Schema/PostgresColumnTest.php @@ -0,0 +1,67 @@ +setAccessible(true); + + $this->assertSame($expected, $ref->invoke($column, $constrain)); + } + + public static function enumConstrainsDataProvider(): \Traversable + { + // simple single value + yield ["CHECK (((target)::text = 'catalog'::text))", ['catalog']]; + + // single value with underscore + yield ["CHECK (((type)::text = 'user_profile'::text))", ['user_profile']]; + + // single value with number + yield ["CHECK (((type)::text = 'user_profile_2015'::text))", ['user_profile_2015']]; + yield ["CHECK (((type)::text = 'user_profile2015'::text))", ['user_profile2015']]; + + // single value in table with underscore + yield ["CHECK (((log_type)::text = 'order'::text))", ['order']]; + + // single value in table with number + yield ["CHECK (((type_2013)::text = 'user_profile'::text))", ['user_profile']]; + yield ["CHECK (((type2)::text = 'catalog'::text))", ['catalog']]; + + // simple multiple values + yield ["CHECK (((target)::text = ANY (ARRAY['catalog'::text, 'view'::text])))", ['catalog', 'view']]; + + // multiple values in table with underscore + yield ["CHECK (((log_type)::text = ANY (ARRAY['catalog'::text, 'view'::text])))", ['catalog', 'view']]; + + // multiple values in table with number + yield ["CHECK (((log_type2)::text = ANY (ARRAY['catalog'::text, 'view'::text])))", ['catalog', 'view']]; + yield ["CHECK (((log_type_2)::text = ANY (ARRAY['catalog'::text, 'view'::text])))", ['catalog', 'view']]; + + // multiple values with underscore + yield ["CHECK (((type)::text = ANY (ARRAY['user_profile'::text, 'view'::text])))", ['user_profile', 'view']]; + + // multiple values with number + yield [ + "CHECK (((type)::text = ANY (ARRAY['user_profile_2'::text, 'view'::text])))", + ['user_profile_2', 'view'] + ]; + yield [ + "CHECK (((type)::text = ANY (ARRAY['user_profile2'::text, 'view'::text])))", + ['user_profile2', 'view'] + ]; + } +} From 446d2b37ea514d1cb16a8c70bef37f30b2dfcd98 Mon Sep 17 00:00:00 2001 From: Maxim Smakouz Date: Fri, 29 Mar 2024 18:23:16 +0200 Subject: [PATCH 4/6] Improve regular expression --- src/Driver/Postgres/Schema/PostgresColumn.php | 4 ++-- .../Unit/Driver/Postgres/Schema/PostgresColumnTest.php | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Driver/Postgres/Schema/PostgresColumn.php b/src/Driver/Postgres/Schema/PostgresColumn.php index 548c4054..c6a5f3b6 100644 --- a/src/Driver/Postgres/Schema/PostgresColumn.php +++ b/src/Driver/Postgres/Schema/PostgresColumn.php @@ -726,7 +726,7 @@ private static function parseEnumValues(string $constraint): array if (\preg_match('/ARRAY\[([^\]]+)\]/', $constraint, $matches)) { $enumValues = \explode(',', $matches[1]); foreach ($enumValues as &$value) { - if (\preg_match("/^'?(.*?)'?::(.+)/", \trim($value, ' ()'), $matches)) { + if (\preg_match("/^'?([a-zA-Z0-9_]++)'?::([a-zA-Z0-9_]++)/", \trim($value, ' ()'), $matches)) { //In database: 'value'::TYPE $value = $matches[1]; } @@ -738,7 +738,7 @@ private static function parseEnumValues(string $constraint): array return $enumValues; } - $pattern = '/CHECK \\(\\(\\([a-zA-Z0-9_]+\\)::(.+) = \'([a-zA-Z0-9_]+)\'::(.+)\\)\\)/i'; + $pattern = '/CHECK \\(\\(\\([a-zA-Z0-9_]++\\)::([a-z]++) = \'([a-zA-Z0-9_]++)\'::([a-z]++)\\)\\)/i'; if (\preg_match($pattern, $constraint, $matches) && !empty($matches[2])) { return [$matches[2]]; } diff --git a/tests/Database/Unit/Driver/Postgres/Schema/PostgresColumnTest.php b/tests/Database/Unit/Driver/Postgres/Schema/PostgresColumnTest.php index 4ca501a1..b58069bc 100644 --- a/tests/Database/Unit/Driver/Postgres/Schema/PostgresColumnTest.php +++ b/tests/Database/Unit/Driver/Postgres/Schema/PostgresColumnTest.php @@ -63,5 +63,9 @@ public static function enumConstrainsDataProvider(): \Traversable "CHECK (((type)::text = ANY (ARRAY['user_profile2'::text, 'view'::text])))", ['user_profile2', 'view'] ]; + + // different type casting TODO: it can be unnecessary + yield ["CHECK (((target)::foo = 'catalog'::bar))", ['catalog']]; + yield ["CHECK (((log_type)::foo = ANY (ARRAY['catalog'::bar, 'view'::baz])))", ['catalog', 'view']]; } } From cc9e43c5c9b8fd0f22831cfb244c85a9f8cf5b8f Mon Sep 17 00:00:00 2001 From: Maxim Smakouz Date: Fri, 29 Mar 2024 18:24:27 +0200 Subject: [PATCH 5/6] Fix CS --- .../Unit/Driver/Postgres/Schema/PostgresColumnTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Database/Unit/Driver/Postgres/Schema/PostgresColumnTest.php b/tests/Database/Unit/Driver/Postgres/Schema/PostgresColumnTest.php index b58069bc..db9ce7ff 100644 --- a/tests/Database/Unit/Driver/Postgres/Schema/PostgresColumnTest.php +++ b/tests/Database/Unit/Driver/Postgres/Schema/PostgresColumnTest.php @@ -57,11 +57,11 @@ public static function enumConstrainsDataProvider(): \Traversable // multiple values with number yield [ "CHECK (((type)::text = ANY (ARRAY['user_profile_2'::text, 'view'::text])))", - ['user_profile_2', 'view'] + ['user_profile_2', 'view'], ]; yield [ "CHECK (((type)::text = ANY (ARRAY['user_profile2'::text, 'view'::text])))", - ['user_profile2', 'view'] + ['user_profile2', 'view'], ]; // different type casting TODO: it can be unnecessary From 85104870bc66a9578c501cd34e7d1ff1d1fe5ccf Mon Sep 17 00:00:00 2001 From: Maxim Smakouz Date: Fri, 29 Mar 2024 18:34:58 +0200 Subject: [PATCH 6/6] Add more unit tests --- .../Unit/Driver/Postgres/Schema/PostgresColumnTest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/Database/Unit/Driver/Postgres/Schema/PostgresColumnTest.php b/tests/Database/Unit/Driver/Postgres/Schema/PostgresColumnTest.php index db9ce7ff..6b6ab955 100644 --- a/tests/Database/Unit/Driver/Postgres/Schema/PostgresColumnTest.php +++ b/tests/Database/Unit/Driver/Postgres/Schema/PostgresColumnTest.php @@ -24,6 +24,9 @@ public function testParseEnumValues(string $constrain, array $expected): void public static function enumConstrainsDataProvider(): \Traversable { + yield ['', []]; + yield ['foo', []]; + // simple single value yield ["CHECK (((target)::text = 'catalog'::text))", ['catalog']];