From 0c5ad2eb74559ac511cc2b4ca026132af53a1c14 Mon Sep 17 00:00:00 2001 From: Sergei Tigrov Date: Mon, 26 Feb 2024 02:01:34 +0700 Subject: [PATCH] Rename composite type to structured type (#338) * Rename composite type to structured type * Remove `Structured` folder, add FQN * Fix test issues * Add line to CHANGELOG.md [skip ci] * fix changelog --------- Co-authored-by: Sergei Predvoditelev --- CHANGELOG.md | 2 +- ...er.php => StructuredExpressionBuilder.php} | 12 ++--- src/ColumnSchema.php | 22 ++++---- src/DQLQueryBuilder.php | 5 +- src/Schema.php | 20 ++++---- ...xpression.php => StructuredExpression.php} | 23 ++++++--- ...mpositeParser.php => StructuredParser.php} | 10 ++-- tests/ColumnSchemaTest.php | 12 ++--- tests/CompositeExpressionTest.php | 24 --------- tests/CompositeParserTest.php | 32 ------------ tests/Provider/QueryBuilderProvider.php | 50 +++++++++---------- ...rovider.php => StructuredTypeProvider.php} | 32 ++++++------ tests/SchemaTest.php | 14 +++--- tests/StructuredExpressionTest.php | 24 +++++++++ tests/StructuredParserTest.php | 32 ++++++++++++ tests/Support/Fixture/pgsql.sql | 26 +++++----- 16 files changed, 173 insertions(+), 167 deletions(-) rename src/Builder/{CompositeExpressionBuilder.php => StructuredExpressionBuilder.php} (86%) rename src/{Composite/CompositeExpression.php => StructuredExpression.php} (74%) rename src/{Composite/CompositeParser.php => StructuredParser.php} (86%) delete mode 100644 tests/CompositeExpressionTest.php delete mode 100644 tests/CompositeParserTest.php rename tests/Provider/{CompositeTypeProvider.php => StructuredTypeProvider.php} (94%) create mode 100644 tests/StructuredExpressionTest.php create mode 100644 tests/StructuredParserTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 43bd6d535..9465e9272 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ - Bug #316, #6: Support table view constraints (@Tigrov) - Enh #324: Change property `Schema::$typeMap` to constant `Schema::TYPE_MAP` (@Tigrov) -- Enh #303: Support composite types (@Tigrov) +- Enh #303, #338: Support structured type (@Tigrov) - Enh #330: Create instance of `ArrayParser` directly (@Tigrov) - Bug #331: Exclude from index column names fields specified in `INCLUDE` clause (@Tigrov) - Enh #334: Minor `DDLQueryBuilder` refactoring (@Tigrov) diff --git a/src/Builder/CompositeExpressionBuilder.php b/src/Builder/StructuredExpressionBuilder.php similarity index 86% rename from src/Builder/CompositeExpressionBuilder.php rename to src/Builder/StructuredExpressionBuilder.php index 5a531b126..ddc0ff342 100644 --- a/src/Builder/CompositeExpressionBuilder.php +++ b/src/Builder/StructuredExpressionBuilder.php @@ -10,16 +10,16 @@ use Yiisoft\Db\Exception\NotSupportedException; use Yiisoft\Db\Expression\ExpressionBuilderInterface; use Yiisoft\Db\Expression\ExpressionInterface; -use Yiisoft\Db\Pgsql\Composite\CompositeExpression; +use Yiisoft\Db\Pgsql\StructuredExpression; use Yiisoft\Db\Query\QueryInterface; use Yiisoft\Db\QueryBuilder\QueryBuilderInterface; use function implode; /** - * Builds expressions for {@see CompositeExpression} for PostgreSQL Server. + * Builds expressions for {@see StructuredExpression} for PostgreSQL Server. */ -final class CompositeExpressionBuilder implements ExpressionBuilderInterface +final class StructuredExpressionBuilder implements ExpressionBuilderInterface { public function __construct(private QueryBuilderInterface $queryBuilder) { @@ -28,7 +28,7 @@ public function __construct(private QueryBuilderInterface $queryBuilder) /** * The method builds the raw SQL from the expression that won't be additionally escaped or quoted. * - * @param CompositeExpression $expression The expression build. + * @param StructuredExpression $expression The expression build. * @param array $params The binding parameters. * * @throws Exception @@ -71,7 +71,7 @@ public function build(ExpressionInterface $expression, array &$params = []): str * @throws InvalidConfigException * @throws NotSupportedException */ - private function buildPlaceholders(CompositeExpression $expression, array &$params): array + private function buildPlaceholders(StructuredExpression $expression, array &$params): array { $value = $expression->getNormalizedValue(); @@ -101,7 +101,7 @@ private function buildPlaceholders(CompositeExpression $expression, array &$para /** * @return string The typecast expression based on {@see type}. */ - private function getTypeHint(CompositeExpression $expression): string + private function getTypeHint(StructuredExpression $expression): string { $type = $expression->getType(); diff --git a/src/ColumnSchema.php b/src/ColumnSchema.php index ef9beec20..c035f1504 100644 --- a/src/ColumnSchema.php +++ b/src/ColumnSchema.php @@ -10,8 +10,6 @@ use Yiisoft\Db\Expression\ArrayExpression; use Yiisoft\Db\Expression\ExpressionInterface; use Yiisoft\Db\Expression\JsonExpression; -use Yiisoft\Db\Pgsql\Composite\CompositeExpression; -use Yiisoft\Db\Pgsql\Composite\CompositeParser; use Yiisoft\Db\Schema\AbstractColumnSchema; use Yiisoft\Db\Schema\ColumnSchemaInterface; use Yiisoft\Db\Schema\SchemaInterface; @@ -62,7 +60,7 @@ final class ColumnSchema extends AbstractColumnSchema private string|null $sequenceName = null; /** - * @var ColumnSchemaInterface[] Columns metadata of the composite type. + * @var ColumnSchemaInterface[] Columns metadata of the structured type. * @psalm-var array */ private array $columns = []; @@ -83,7 +81,7 @@ public function dbTypecast(mixed $value): mixed return $value; } - if ($this->getType() === Schema::TYPE_COMPOSITE) { + if ($this->getType() === Schema::TYPE_STRUCTURED) { $value = $this->dbTypecastArray($value, $this->dimension); } @@ -146,7 +144,7 @@ private function dbTypecastValue(mixed $value): mixed ? str_pad(decbin($value), (int) $this->getSize(), '0', STR_PAD_LEFT) : (string) $value, - Schema::TYPE_COMPOSITE => new CompositeExpression($value, $this->getDbType(), $this->columns), + Schema::TYPE_STRUCTURED => new StructuredExpression($value, $this->getDbType(), $this->columns), default => $this->typecast($value), }; @@ -203,19 +201,19 @@ private function phpTypecastValue(mixed $value): mixed SchemaInterface::TYPE_JSON => json_decode((string) $value, true, 512, JSON_THROW_ON_ERROR), - Schema::TYPE_COMPOSITE => $this->phpTypecastComposite($value), + Schema::TYPE_STRUCTURED => $this->phpTypecastStructured($value), default => parent::phpTypecast($value), }; } /** - * Converts the input value according to the composite type after retrieval from the database. + * Converts the input value according to the structured type after retrieval from the database. */ - private function phpTypecastComposite(mixed $value): array|null + private function phpTypecastStructured(mixed $value): array|null { if (is_string($value)) { - $value = (new CompositeParser())->parse($value); + $value = (new StructuredParser())->parse($value); } if (!is_iterable($value)) { @@ -276,9 +274,9 @@ public function sequenceName(string|null $sequenceName): void } /** - * Set columns of the composite type. + * Set columns of the structured type. * - * @param ColumnSchemaInterface[] $columns The metadata of the composite type columns. + * @param ColumnSchemaInterface[] $columns The metadata of the structured type columns. * @psalm-param array $columns */ public function columns(array $columns): void @@ -287,7 +285,7 @@ public function columns(array $columns): void } /** - * Get the metadata of the composite type columns. + * Get the metadata of the structured type columns. * * @return ColumnSchemaInterface[] */ diff --git a/src/DQLQueryBuilder.php b/src/DQLQueryBuilder.php index 1ed4a5771..098c57a7f 100644 --- a/src/DQLQueryBuilder.php +++ b/src/DQLQueryBuilder.php @@ -8,9 +8,8 @@ use Yiisoft\Db\Expression\ExpressionBuilderInterface; use Yiisoft\Db\Expression\JsonExpression; use Yiisoft\Db\Pgsql\Builder\ArrayExpressionBuilder; -use Yiisoft\Db\Pgsql\Builder\CompositeExpressionBuilder; +use Yiisoft\Db\Pgsql\Builder\StructuredExpressionBuilder; use Yiisoft\Db\Pgsql\Builder\JsonExpressionBuilder; -use Yiisoft\Db\Pgsql\Composite\CompositeExpression; use Yiisoft\Db\QueryBuilder\AbstractDQLQueryBuilder; use Yiisoft\Db\QueryBuilder\Condition\LikeCondition; @@ -52,7 +51,7 @@ protected function defaultExpressionBuilders(): array return array_merge(parent::defaultExpressionBuilders(), [ ArrayExpression::class => ArrayExpressionBuilder::class, JsonExpression::class => JsonExpressionBuilder::class, - CompositeExpression::class => CompositeExpressionBuilder::class, + StructuredExpression::class => StructuredExpressionBuilder::class, ]); } } diff --git a/src/Schema.php b/src/Schema.php index 8a39802cd..115878d05 100644 --- a/src/Schema.php +++ b/src/Schema.php @@ -88,9 +88,9 @@ final class Schema extends AbstractPdoSchema */ public const TYPE_BIT = 'bit'; /** - * Define the abstract column type as `composite`. + * Define the abstract column type as `structured`. */ - public const TYPE_COMPOSITE = 'composite'; + public const TYPE_STRUCTURED = 'structured'; /** * The mapping from physical column types (keys) to abstract column types (values). @@ -826,11 +826,11 @@ protected function loadColumnSchema(array $info): ColumnSchemaInterface } if ($info['type_type'] === 'c') { - $column->type(self::TYPE_COMPOSITE); - $composite = $this->resolveTableName((string) $column->getDbType()); + $column->type(self::TYPE_STRUCTURED); + $structured = $this->resolveTableName((string) $column->getDbType()); - if ($this->findColumns($composite)) { - $column->columns($composite->getColumns()); + if ($this->findColumns($structured)) { + $column->columns($structured->getColumns()); } } else { $column->type(self::TYPE_MAP[(string) $column->getDbType()] ?? self::TYPE_STRING); @@ -839,12 +839,12 @@ protected function loadColumnSchema(array $info): ColumnSchemaInterface $column->phpType($this->getColumnPhpType($column)); $column->defaultValue($this->normalizeDefaultValue($defaultValue, $column)); - if ($column->getType() === self::TYPE_COMPOSITE && $column->getDimension() === 0) { + if ($column->getType() === self::TYPE_STRUCTURED && $column->getDimension() === 0) { /** @psalm-var array|null $defaultValue */ $defaultValue = $column->getDefaultValue(); if (is_array($defaultValue)) { - foreach ($column->getColumns() as $compositeColumnName => $compositeColumn) { - $compositeColumn->defaultValue($defaultValue[$compositeColumnName] ?? null); + foreach ($column->getColumns() as $structuredColumnName => $structuredColumn) { + $structuredColumn->defaultValue($defaultValue[$structuredColumnName] ?? null); } } } @@ -863,7 +863,7 @@ protected function getColumnPhpType(ColumnSchemaInterface $column): string { return match ($column->getType()) { self::TYPE_BIT => self::PHP_TYPE_INTEGER, - self::TYPE_COMPOSITE => self::PHP_TYPE_ARRAY, + self::TYPE_STRUCTURED => self::PHP_TYPE_ARRAY, default => parent::getColumnPhpType($column), }; } diff --git a/src/Composite/CompositeExpression.php b/src/StructuredExpression.php similarity index 74% rename from src/Composite/CompositeExpression.php rename to src/StructuredExpression.php index 56ba9e0b8..f317c3870 100644 --- a/src/Composite/CompositeExpression.php +++ b/src/StructuredExpression.php @@ -2,24 +2,31 @@ declare(strict_types=1); -namespace Yiisoft\Db\Pgsql\Composite; +namespace Yiisoft\Db\Pgsql; use Traversable; use Yiisoft\Db\Expression\ExpressionInterface; use Yiisoft\Db\Schema\ColumnSchemaInterface; +use function array_key_exists; +use function array_keys; +use function is_iterable; +use function iterator_to_array; + /** - * Represents a composite type SQL expression. + * Represents a structured type SQL expression. + * + * @see https://en.wikipedia.org/wiki/Structured_type * * For example: * * ```php - * new CompositeExpression(['price' => 10, 'currency_code' => 'USD']); + * new StructuredExpression(['price' => 10, 'currency_code' => 'USD']); * ``` * * Will be encoded to `ROW(10, USD)` */ -class CompositeExpression implements ExpressionInterface +class StructuredExpression implements ExpressionInterface { /** * @param ColumnSchemaInterface[] $columns @@ -33,7 +40,7 @@ public function __construct( } /** - * The composite type name. + * The structured type name. * * Defaults to `null` which means the type is not explicitly specified. * @@ -46,7 +53,7 @@ public function getType(): string|null } /** - * The composite type columns that are used for value normalization and type casting. + * The structured type columns that are used for value normalization and type casting. * * @return ColumnSchemaInterface[] */ @@ -56,7 +63,7 @@ public function getColumns(): array } /** - * The content of the composite type. It can be represented as an associative array of composite type column names + * The content of the structured type. It can be represented as an associative array of structured type column names * and values. */ public function getValue(): mixed @@ -65,7 +72,7 @@ public function getValue(): mixed } /** - * Sorted values according to the order of composite type columns, + * Sorted values according to the order of structured type columns, * indexed keys are replaced with column names, * missing elements are filled in with default values, * excessive elements are removed. diff --git a/src/Composite/CompositeParser.php b/src/StructuredParser.php similarity index 86% rename from src/Composite/CompositeParser.php rename to src/StructuredParser.php index 5e2064dff..0327904d3 100644 --- a/src/Composite/CompositeParser.php +++ b/src/StructuredParser.php @@ -2,15 +2,17 @@ declare(strict_types=1); -namespace Yiisoft\Db\Pgsql\Composite; +namespace Yiisoft\Db\Pgsql; + +use function in_array; /** - * Composite type representation to PHP array parser for PostgreSQL Server. + * Structured type representation to PHP array parser for PostgreSQL Server. */ -final class CompositeParser +final class StructuredParser { /** - * Converts composite type value from PostgreSQL to PHP array + * Converts structured (composite) type value from PostgreSQL to PHP array * * @param string $value String to convert. */ diff --git a/tests/ColumnSchemaTest.php b/tests/ColumnSchemaTest.php index fc037d160..11ed992f7 100644 --- a/tests/ColumnSchemaTest.php +++ b/tests/ColumnSchemaTest.php @@ -212,14 +212,14 @@ public function testPrimaryKeyOfView() $this->assertFalse($tableSchema->getColumn('C_index_2_2')->isPrimaryKey()); } - public function testCompositeType(): void + public function testStructuredType(): void { $db = $this->getConnection(true); $command = $db->createCommand(); $schema = $db->getSchema(); - $tableSchema = $schema->getTableSchema('test_composite_type'); + $tableSchema = $schema->getTableSchema('test_structured_type'); - $command->insert('test_composite_type', [ + $command->insert('test_structured_type', [ 'price_col' => ['value' => 10.0, 'currency_code' => 'USD'], 'price_array' => [ null, @@ -235,7 +235,7 @@ public function testCompositeType(): void ], ])->execute(); - $query = (new Query($db))->from('test_composite_type')->one(); + $query = (new Query($db))->from('test_structured_type')->one(); $priceColPhpType = $tableSchema->getColumn('price_col')->phpTypecast($query['price_col']); $priceDefaultPhpType = $tableSchema->getColumn('price_default')->phpTypecast($query['price_default']); @@ -275,14 +275,14 @@ public function testCompositeType(): void $priceArray = $tableSchema->getColumn('price_array'); $this->assertEquals( - new ArrayExpression([], 'currency_money_composite', 1), + new ArrayExpression([], 'currency_money_structured', 1), $priceArray->dbTypecast(1), 'For scalar value returns empty array' ); $priceArray2 = $tableSchema->getColumn('price_array2'); $this->assertEquals( - new ArrayExpression([null, null], 'currency_money_composite', 2), + new ArrayExpression([null, null], 'currency_money_structured', 2), $priceArray2->dbTypecast([null, null]), 'Double array of null values' ); diff --git a/tests/CompositeExpressionTest.php b/tests/CompositeExpressionTest.php deleted file mode 100644 index 846c42530..000000000 --- a/tests/CompositeExpressionTest.php +++ /dev/null @@ -1,24 +0,0 @@ -assertSame($expected, $compositeExpression->getNormalizedValue()); - } -} diff --git a/tests/CompositeParserTest.php b/tests/CompositeParserTest.php deleted file mode 100644 index 6e1bfb4bf..000000000 --- a/tests/CompositeParserTest.php +++ /dev/null @@ -1,32 +0,0 @@ -assertSame([null], $compositeParser->parse('()')); - $this->assertSame([0 => null, 1 => null], $compositeParser->parse('(,)')); - $this->assertSame([0 => '10.0', 1 => 'USD'], $compositeParser->parse('(10.0,USD)')); - $this->assertSame([0 => '1', 1 => '-2', 2 => null, 3 => '42'], $compositeParser->parse('(1,-2,,42)')); - $this->assertSame([0 => ''], $compositeParser->parse('("")')); - $this->assertSame( - [0 => '[",","null",true,"false","f"]'], - $compositeParser->parse('("[\",\",\"null\",true,\"false\",\"f\"]")') - ); - - // Default values can have any expressions - $this->assertSame(null, $compositeParser->parse("'(10.0,USD)::composite_type'")); - } -} diff --git a/tests/Provider/QueryBuilderProvider.php b/tests/Provider/QueryBuilderProvider.php index d015f812c..65302f5a1 100644 --- a/tests/Provider/QueryBuilderProvider.php +++ b/tests/Provider/QueryBuilderProvider.php @@ -7,7 +7,7 @@ use Yiisoft\Db\Expression\ArrayExpression; use Yiisoft\Db\Expression\Expression; use Yiisoft\Db\Expression\JsonExpression; -use Yiisoft\Db\Pgsql\Composite\CompositeExpression; +use Yiisoft\Db\Pgsql\StructuredExpression; use Yiisoft\Db\Pgsql\Tests\Support\ColumnSchemaBuilder; use Yiisoft\Db\Pgsql\Tests\Support\TestTrait; use Yiisoft\Db\Query\Query; @@ -253,59 +253,59 @@ public static function buildCondition(): array [['<=', 'id', new ArrayExpression([1])], '"id" <= ARRAY[:qp0]', [':qp0' => 1]], [['&&', 'id', new ArrayExpression([1])], '"id" && ARRAY[:qp0]', [':qp0' => 1]], - /* composite conditions */ - 'composite without type' => [ - ['=', 'price_col', new CompositeExpression(['value' => 10, 'currency_code' => 'USD'])], + /* structured conditions */ + 'structured without type' => [ + ['=', 'price_col', new StructuredExpression(['value' => 10, 'currency_code' => 'USD'])], '[[price_col]] = ROW(:qp0, :qp1)', [':qp0' => 10, ':qp1' => 'USD'], ], - 'composite with type' => [ - ['=', 'price_col', new CompositeExpression(['value' => 10, 'currency_code' => 'USD'], 'currency_money_composite')], - '[[price_col]] = ROW(:qp0, :qp1)::currency_money_composite', + 'structured with type' => [ + ['=', 'price_col', new StructuredExpression(['value' => 10, 'currency_code' => 'USD'], 'currency_money_structured')], + '[[price_col]] = ROW(:qp0, :qp1)::currency_money_structured', [':qp0' => 10, ':qp1' => 'USD'], ], - 'composite with columns' => [ - ['=', 'price_col', new CompositeExpression(['value' => 10, 'currency_code' => 'USD'], 'currency_money_composite', $priceColumns)], - '[[price_col]] = ROW(:qp0, :qp1)::currency_money_composite', + 'structured with columns' => [ + ['=', 'price_col', new StructuredExpression(['value' => 10, 'currency_code' => 'USD'], 'currency_money_structured', $priceColumns)], + '[[price_col]] = ROW(:qp0, :qp1)::currency_money_structured', [':qp0' => 10.0, ':qp1' => 'USD'], ], - 'scalar can not be converted to composite' => [['=', 'price_col', new CompositeExpression(1)], '"price_col" = NULL', []], - 'array of composite' => [ + 'scalar can not be converted to structured' => [['=', 'price_col', new StructuredExpression(1)], '"price_col" = NULL', []], + 'array of structured' => [ ['=', 'price_array', new ArrayExpression( [ null, - new CompositeExpression(['value' => 11.11, 'currency_code' => 'USD']), - new CompositeExpression(['value' => null, 'currency_code' => null]), + new StructuredExpression(['value' => 11.11, 'currency_code' => 'USD']), + new StructuredExpression(['value' => null, 'currency_code' => null]), ] )], '"price_array" = ARRAY[:qp0, ROW(:qp1, :qp2), ROW(:qp3, :qp4)]', [':qp0' => null, ':qp1' => 11.11, ':qp2' => 'USD', ':qp3' => null, ':qp4' => null], ], - 'composite null value' => [['=', 'price_col', new CompositeExpression(null)], '"price_col" = NULL', []], - 'composite null values' => [ - ['=', 'price_col', new CompositeExpression([null, null])], '"price_col" = ROW(:qp0, :qp1)', [':qp0' => null, ':qp1' => null], + 'structured null value' => [['=', 'price_col', new StructuredExpression(null)], '"price_col" = NULL', []], + 'structured null values' => [ + ['=', 'price_col', new StructuredExpression([null, null])], '"price_col" = ROW(:qp0, :qp1)', [':qp0' => null, ':qp1' => null], ], - 'composite query' => [ - ['=', 'price_col', new CompositeExpression( + 'structured query' => [ + ['=', 'price_col', new StructuredExpression( (new Query(self::getDb()))->select('price')->from('product')->where(['id' => 1]) )], '[[price_col]] = (SELECT [[price]] FROM [[product]] WHERE [[id]]=:qp0)', [':qp0' => 1], ], - 'composite query with type' => [ + 'structured query with type' => [ [ '=', 'price_col', - new CompositeExpression( + new StructuredExpression( (new Query(self::getDb()))->select('price')->from('product')->where(['id' => 1]), - 'currency_money_composite' + 'currency_money_structured' ), ], - '[[price_col]] = (SELECT [[price]] FROM [[product]] WHERE [[id]]=:qp0)::currency_money_composite', + '[[price_col]] = (SELECT [[price]] FROM [[product]] WHERE [[id]]=:qp0)::currency_money_structured', [':qp0' => 1], ], - 'traversable objects are supported in composite' => [ - ['=', 'price_col', new CompositeExpression(new TraversableObject([10, 'USD']))], + 'traversable objects are supported in structured' => [ + ['=', 'price_col', new StructuredExpression(new TraversableObject([10, 'USD']))], '[[price_col]] = ROW(:qp0, :qp1)', [':qp0' => 10, ':qp1' => 'USD'], ], diff --git a/tests/Provider/CompositeTypeProvider.php b/tests/Provider/StructuredTypeProvider.php similarity index 94% rename from tests/Provider/CompositeTypeProvider.php rename to tests/Provider/StructuredTypeProvider.php index a19ff9861..c7c4540ff 100644 --- a/tests/Provider/CompositeTypeProvider.php +++ b/tests/Provider/StructuredTypeProvider.php @@ -8,7 +8,7 @@ use Yiisoft\Db\Pgsql\Tests\Support\ColumnSchemaBuilder; use Yiisoft\Db\Tests\Support\TraversableObject; -final class CompositeTypeProvider extends \Yiisoft\Db\Tests\Provider\SchemaProvider +final class StructuredTypeProvider extends \Yiisoft\Db\Tests\Provider\SchemaProvider { public static function columns(): array { @@ -29,8 +29,8 @@ public static function columns(): array 'defaultValue' => null, ], 'price_col' => [ - 'type' => 'composite', - 'dbType' => 'currency_money_composite', + 'type' => 'structured', + 'dbType' => 'currency_money_structured', 'phpType' => 'array', 'primaryKey' => false, 'allowNull' => true, @@ -70,8 +70,8 @@ public static function columns(): array ], ], 'price_default' => [ - 'type' => 'composite', - 'dbType' => 'currency_money_composite', + 'type' => 'structured', + 'dbType' => 'currency_money_structured', 'phpType' => 'array', 'primaryKey' => false, 'allowNull' => true, @@ -111,8 +111,8 @@ public static function columns(): array ], ], 'price_array' => [ - 'type' => 'composite', - 'dbType' => 'currency_money_composite', + 'type' => 'structured', + 'dbType' => 'currency_money_structured', 'phpType' => 'array', 'primaryKey' => false, 'allowNull' => true, @@ -157,8 +157,8 @@ public static function columns(): array ], ], 'price_array2' => [ - 'type' => 'composite', - 'dbType' => 'currency_money_composite', + 'type' => 'structured', + 'dbType' => 'currency_money_structured', 'phpType' => 'array', 'primaryKey' => false, 'allowNull' => true, @@ -199,8 +199,8 @@ public static function columns(): array ], ], 'range_price_col' => [ - 'type' => 'composite', - 'dbType' => 'range_price_composite', + 'type' => 'structured', + 'dbType' => 'range_price_structured', 'phpType' => 'array', 'primaryKey' => false, 'allowNull' => true, @@ -216,8 +216,8 @@ public static function columns(): array 'dimension' => 0, 'columns' => [ 'price_from' => [ - 'type' => 'composite', - 'dbType' => 'currency_money_composite', + 'type' => 'structured', + 'dbType' => 'currency_money_structured', 'phpType' => 'array', 'primaryKey' => false, 'allowNull' => true, @@ -257,8 +257,8 @@ public static function columns(): array ], ], 'price_to' => [ - 'type' => 'composite', - 'dbType' => 'currency_money_composite', + 'type' => 'structured', + 'dbType' => 'currency_money_structured', 'phpType' => 'array', 'primaryKey' => false, 'allowNull' => true, @@ -300,7 +300,7 @@ public static function columns(): array ], ], ], - 'test_composite_type', + 'test_structured_type', ], ]; } diff --git a/tests/SchemaTest.php b/tests/SchemaTest.php index e68ab4c36..5fe34a4c5 100644 --- a/tests/SchemaTest.php +++ b/tests/SchemaTest.php @@ -581,13 +581,13 @@ public function testGetViewNames(): void $db->close(); } - /** @dataProvider \Yiisoft\Db\Pgsql\Tests\Provider\CompositeTypeProvider::columns */ - public function testCompositeTypeColumnSchema(array $columns, string $tableName): void + /** @dataProvider \Yiisoft\Db\Pgsql\Tests\Provider\StructuredTypeProvider::columns */ + public function testStructuredTypeColumnSchema(array $columns, string $tableName): void { - $this->testCompositeTypeColumnSchemaRecursive($columns, $tableName); + $this->testStructuredTypeColumnSchemaRecursive($columns, $tableName); } - private function testCompositeTypeColumnSchemaRecursive(array $columns, string $tableName): void + private function testStructuredTypeColumnSchemaRecursive(array $columns, string $tableName): void { $this->columnSchema($columns, $tableName); @@ -595,12 +595,12 @@ private function testCompositeTypeColumnSchemaRecursive(array $columns, string $ $table = $db->getTableSchema($tableName, true); foreach ($table->getColumns() as $name => $column) { - if ($column->getType() === 'composite') { + if ($column->getType() === Schema::TYPE_STRUCTURED) { $this->assertTrue( isset($columns[$name]['columns']), - "Columns of composite type `$name` do not exist, dbType is `{$column->getDbType()}`." + "Columns of structured type `$name` do not exist, dbType is `{$column->getDbType()}`." ); - $this->testCompositeTypeColumnSchemaRecursive($columns[$name]['columns'], $column->getDbType()); + $this->testStructuredTypeColumnSchemaRecursive($columns[$name]['columns'], $column->getDbType()); } } diff --git a/tests/StructuredExpressionTest.php b/tests/StructuredExpressionTest.php new file mode 100644 index 000000000..5949f67bd --- /dev/null +++ b/tests/StructuredExpressionTest.php @@ -0,0 +1,24 @@ +assertSame($expected, $structuredExpression->getNormalizedValue()); + } +} diff --git a/tests/StructuredParserTest.php b/tests/StructuredParserTest.php new file mode 100644 index 000000000..4cc28902f --- /dev/null +++ b/tests/StructuredParserTest.php @@ -0,0 +1,32 @@ +assertSame([null], $parser->parse('()')); + $this->assertSame([0 => null, 1 => null], $parser->parse('(,)')); + $this->assertSame([0 => '10.0', 1 => 'USD'], $parser->parse('(10.0,USD)')); + $this->assertSame([0 => '1', 1 => '-2', 2 => null, 3 => '42'], $parser->parse('(1,-2,,42)')); + $this->assertSame([0 => ''], $parser->parse('("")')); + $this->assertSame( + [0 => '[",","null",true,"false","f"]'], + $parser->parse('("[\",\",\"null\",true,\"false\",\"f\"]")') + ); + + // Default values can have any expressions + $this->assertSame(null, $parser->parse("'(10.0,USD)::structured_type'")); + } +} diff --git a/tests/Support/Fixture/pgsql.sql b/tests/Support/Fixture/pgsql.sql index 6b786a9de..ece6899a6 100644 --- a/tests/Support/Fixture/pgsql.sql +++ b/tests/Support/Fixture/pgsql.sql @@ -464,26 +464,26 @@ CREATE TABLE "table_uuid" ( "col" varchar(16) ); -DROP TYPE IF EXISTS "currency_money_composite" CASCADE; -DROP TYPE IF EXISTS "range_price_composite" CASCADE; -DROP TABLE IF EXISTS "test_composite_type" CASCADE; +DROP TYPE IF EXISTS "currency_money_structured" CASCADE; +DROP TYPE IF EXISTS "range_price_structured" CASCADE; +DROP TABLE IF EXISTS "test_structured_type" CASCADE; -CREATE TYPE "currency_money_composite" AS ( +CREATE TYPE "currency_money_structured" AS ( "value" numeric(10,2), "currency_code" char(3) ); -CREATE TYPE "range_price_composite" AS ( - "price_from" "currency_money_composite", - "price_to" "currency_money_composite" +CREATE TYPE "range_price_structured" AS ( + "price_from" "currency_money_structured", + "price_to" "currency_money_structured" ); -CREATE TABLE "test_composite_type" +CREATE TABLE "test_structured_type" ( "id" SERIAL NOT NULL PRIMARY KEY, - "price_col" "currency_money_composite", - "price_default" "currency_money_composite" DEFAULT '(5,USD)', - "price_array" "currency_money_composite"[] DEFAULT '{null,"(10.55,USD)","(-1,)"}', - "price_array2" "currency_money_composite"[][], - "range_price_col" "range_price_composite" DEFAULT '("(0,USD)","(100,USD)")' + "price_col" "currency_money_structured", + "price_default" "currency_money_structured" DEFAULT '(5,USD)', + "price_array" "currency_money_structured"[] DEFAULT '{null,"(10.55,USD)","(-1,)"}', + "price_array2" "currency_money_structured"[][], + "range_price_col" "range_price_structured" DEFAULT '("(0,USD)","(100,USD)")' );