diff --git a/src/Driver/Postgres/Schema/PostgresColumn.php b/src/Driver/Postgres/Schema/PostgresColumn.php index e2d80bc5..c6a5f3b6 100644 --- a/src/Driver/Postgres/Schema/PostgresColumn.php +++ b/src/Driver/Postgres/Schema/PostgresColumn.php @@ -692,19 +692,10 @@ 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; } @@ -729,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("/^'?([a-zA-Z0-9_]++)'?::([a-zA-Z0-9_]++)/", \trim($value, ' ()'), $matches)) { + //In database: 'value'::TYPE + $value = $matches[1]; + } + + unset($value); + } + unset($value); + + return $enumValues; + } + + $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]]; + } + + return []; + } } 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'); 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..6b6ab955 --- /dev/null +++ b/tests/Database/Unit/Driver/Postgres/Schema/PostgresColumnTest.php @@ -0,0 +1,74 @@ +setAccessible(true); + + $this->assertSame($expected, $ref->invoke($column, $constrain)); + } + + public static function enumConstrainsDataProvider(): \Traversable + { + yield ['', []]; + yield ['foo', []]; + + // 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'], + ]; + + // 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']]; + } +}