Skip to content

Commit 14b4bd8

Browse files
authored
Add ColumnDefinitionBuilder (#364)
1 parent 384c326 commit 14b4bd8

12 files changed

+185
-23
lines changed

CHANGELOG.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,20 @@
33
## 2.0.0 under development
44

55
- Enh #336: Implement `SqlParser` and `ExpressionBuilder` driver classes (@Tigrov)
6-
- Enh #315: Implement `ColumnSchemaInterface` classes according to the data type of database table columns
6+
- New #315: Implement `ColumnSchemaInterface` classes according to the data type of database table columns
77
for type casting performance. Related with yiisoft/db#752 (@Tigrov)
88
- Chg #348: Replace call of `SchemaInterface::getRawTableName()` to `QuoterInterface::getRawTableName()` (@Tigrov)
99
- Enh #349: Add method chaining for column classes (@Tigrov)
10-
- Enh #350: Add array overlaps and JSON overlaps condition builders (@Tigrov)
10+
- New #350: Add array overlaps and JSON overlaps condition builders (@Tigrov)
1111
- Enh #353: Update `bit` type according to main PR yiisoft/db#860 (@Tigrov)
1212
- Enh #354: Refactor PHP type of `ColumnSchemaInterface` instances (@Tigrov)
1313
- Enh #356: Raise minimum PHP version to `^8.1` with minor refactoring (@Tigrov)
14-
- Enh #355: Implement `ColumnFactory` class (@Tigrov)
14+
- New #355: Implement `ColumnFactory` class (@Tigrov)
1515
- Enh #359: Separate column type constants (@Tigrov)
1616
- Enh #359: Remove `Schema::TYPE_ARRAY` and `Schema::TYPE_STRUCTURED` constants (@Tigrov)
17-
- Enh #360: Realize `ColumnBuilder` class (@Tigrov)
17+
- New #360: Realize `ColumnBuilder` class (@Tigrov)
1818
- Enh #362: Update according changes in `ColumnSchemaInterface` (@Tigrov)
19+
- New #364: Add `ColumnDefinitionBuilder` class (@Tigrov)
1920

2021
## 1.3.0 March 21, 2024
2122

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yiisoft\Db\Pgsql\Column;
6+
7+
use Yiisoft\Db\Constant\ColumnType;
8+
use Yiisoft\Db\QueryBuilder\AbstractColumnDefinitionBuilder;
9+
use Yiisoft\Db\Schema\Column\ColumnSchemaInterface;
10+
11+
final class ColumnDefinitionBuilder extends AbstractColumnDefinitionBuilder
12+
{
13+
protected const GENERATE_UUID_EXPRESSION = 'gen_random_uuid()';
14+
15+
protected const TYPES_WITH_SIZE = [
16+
'bit',
17+
'bit varying',
18+
'varbit',
19+
'decimal',
20+
'numeric',
21+
'char',
22+
'character',
23+
'bpchar',
24+
'character varying',
25+
'varchar',
26+
'time',
27+
'timetz',
28+
'timestamp',
29+
'timestamptz',
30+
'interval',
31+
];
32+
33+
protected const TYPES_WITH_SCALE = [
34+
'decimal',
35+
'numeric',
36+
];
37+
38+
public function build(ColumnSchemaInterface $column): string
39+
{
40+
return $this->buildType($column)
41+
. $this->buildNotNull($column)
42+
. $this->buildPrimaryKey($column)
43+
. $this->buildUnique($column)
44+
. $this->buildDefault($column)
45+
. $this->buildCheck($column)
46+
. $this->buildReferences($column)
47+
. $this->buildExtra($column);
48+
}
49+
50+
protected function buildType(ColumnSchemaInterface $column): string
51+
{
52+
if ($column instanceof \Yiisoft\Db\Schema\Column\ArrayColumnSchema) {
53+
return $this->buildType($column->getColumn()) . str_repeat('[]', $column->getDimension());
54+
}
55+
56+
return parent::buildType($column);
57+
}
58+
59+
protected function getDbType(ColumnSchemaInterface $column): string
60+
{
61+
/** @psalm-suppress DocblockTypeContradiction */
62+
return match ($column->getType()) {
63+
ColumnType::BOOLEAN => 'boolean',
64+
ColumnType::BIT => 'varbit',
65+
ColumnType::TINYINT => $column->isAutoIncrement() ? 'smallserial' : 'smallint',
66+
ColumnType::SMALLINT => $column->isAutoIncrement() ? 'smallserial' : 'smallint',
67+
ColumnType::INTEGER => $column->isAutoIncrement() ? 'serial' : 'integer',
68+
ColumnType::BIGINT => $column->isAutoIncrement() ? 'bigserial' : 'bigint',
69+
ColumnType::FLOAT => 'real',
70+
ColumnType::DOUBLE => 'double precision',
71+
ColumnType::DECIMAL => 'numeric',
72+
ColumnType::MONEY => 'money',
73+
ColumnType::CHAR => 'char',
74+
ColumnType::STRING => 'varchar',
75+
ColumnType::TEXT => 'text',
76+
ColumnType::BINARY => 'bytea',
77+
ColumnType::UUID => 'uuid',
78+
ColumnType::DATETIME => 'timestamp',
79+
ColumnType::TIMESTAMP => 'timestamp',
80+
ColumnType::DATE => 'date',
81+
ColumnType::TIME => 'time',
82+
ColumnType::STRUCTURED => 'jsonb',
83+
ColumnType::JSON => 'jsonb',
84+
default => 'varchar',
85+
};
86+
}
87+
}

src/Column/ColumnFactory.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ final class ColumnFactory extends AbstractColumnFactory
104104
'cidr' => ColumnType::STRING,
105105
'inet' => ColumnType::STRING,
106106
'macaddr' => ColumnType::STRING,
107+
'macaddr8' => ColumnType::STRING,
107108
'tsquery' => ColumnType::STRING,
108109
'tsvector' => ColumnType::STRING,
109110
'txid_snapshot' => ColumnType::STRING,

src/Connection.php

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@
77
use Yiisoft\Db\Driver\Pdo\AbstractPdoConnection;
88
use Yiisoft\Db\Driver\Pdo\PdoCommandInterface;
99
use Yiisoft\Db\Exception\InvalidArgumentException;
10-
use Yiisoft\Db\Pgsql\Column\ColumnFactory;
1110
use Yiisoft\Db\QueryBuilder\QueryBuilderInterface;
12-
use Yiisoft\Db\Schema\Column\ColumnFactoryInterface;
1311
use Yiisoft\Db\Schema\Quoter;
1412
use Yiisoft\Db\Schema\QuoterInterface;
1513
use Yiisoft\Db\Schema\SchemaInterface;
@@ -46,11 +44,6 @@ public function createTransaction(): TransactionInterface
4644
return new Transaction($this);
4745
}
4846

49-
public function getColumnFactory(): ColumnFactoryInterface
50-
{
51-
return new ColumnFactory();
52-
}
53-
5447
public function getLastInsertID(string $sequenceName = null): string
5548
{
5649
if ($sequenceName === null) {

src/DDLQueryBuilder.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ public function alterColumn(string $table, string $column, ColumnInterface|strin
4444
return "ALTER TABLE $tableName ALTER COLUMN $columnName $type";
4545
}
4646

47+
/** @psalm-suppress DeprecatedMethod */
4748
$type = 'TYPE ' . $this->queryBuilder->getColumnType($type);
4849
$multiAlterStatement = [];
4950
$constraintPrefix = preg_replace('/[^a-z0-9_]/i', '', $table . '_' . $column);

src/QueryBuilder.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use Yiisoft\Db\Constant\ColumnType;
88
use Yiisoft\Db\Constant\PseudoType;
9+
use Yiisoft\Db\Pgsql\Column\ColumnDefinitionBuilder;
910
use Yiisoft\Db\QueryBuilder\AbstractQueryBuilder;
1011
use Yiisoft\Db\Schema\QuoterInterface;
1112
use Yiisoft\Db\Schema\SchemaInterface;
@@ -52,6 +53,8 @@ public function __construct(QuoterInterface $quoter, SchemaInterface $schema)
5253
$ddlBuilder = new DDLQueryBuilder($this, $quoter, $schema);
5354
$dmlBuilder = new DMLQueryBuilder($this, $quoter, $schema);
5455
$dqlBuilder = new DQLQueryBuilder($this, $quoter);
55-
parent::__construct($quoter, $schema, $ddlBuilder, $dmlBuilder, $dqlBuilder);
56+
$columnDefinitionBuilder = new ColumnDefinitionBuilder($this);
57+
58+
parent::__construct($quoter, $schema, $ddlBuilder, $dmlBuilder, $dqlBuilder, $columnDefinitionBuilder);
5659
}
5760
}

src/Schema.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@
1919
use Yiisoft\Db\Expression\Expression;
2020
use Yiisoft\Db\Helper\DbArrayHelper;
2121
use Yiisoft\Db\Pgsql\Column\ArrayColumnSchema;
22+
use Yiisoft\Db\Pgsql\Column\ColumnFactory;
2223
use Yiisoft\Db\Pgsql\Column\SequenceColumnSchemaInterface;
2324
use Yiisoft\Db\Schema\Builder\ColumnInterface;
25+
use Yiisoft\Db\Schema\Column\ColumnFactoryInterface;
2426
use Yiisoft\Db\Schema\Column\ColumnSchemaInterface;
2527
use Yiisoft\Db\Schema\Column\StructuredColumnSchema;
2628
use Yiisoft\Db\Schema\TableSchemaInterface;
@@ -102,11 +104,17 @@ final class Schema extends AbstractPdoSchema
102104
*/
103105
protected string|array $tableQuoteCharacter = '"';
104106

107+
/** @deprecated Use {@see ColumnBuilder} instead. Will be removed in 2.0. */
105108
public function createColumn(string $type, array|int|string $length = null): ColumnInterface
106109
{
107110
return new Column($type, $length);
108111
}
109112

113+
public function getColumnFactory(): ColumnFactoryInterface
114+
{
115+
return new ColumnFactory();
116+
}
117+
110118
/**
111119
* Resolves the table name and schema name (if any).
112120
*
@@ -718,7 +726,7 @@ protected function findColumns(TableSchemaInterface $table): bool
718726
*/
719727
private function loadColumnSchema(array $info): ColumnSchemaInterface
720728
{
721-
$columnFactory = $this->db->getColumnFactory();
729+
$columnFactory = $this->getColumnFactory();
722730
$dbType = $info['data_type'];
723731

724732
if (!in_array($info['type_scheme'], [$this->defaultSchema, 'pg_catalog'], true)) {

tests/ColumnFactoryTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public function testFromDbType(string $dbType, string $expectedType, string $exp
2121
parent::testFromDbType($dbType, $expectedType, $expectedInstanceOf);
2222

2323
$db = $this->getConnection();
24-
$columnFactory = $db->getColumnFactory();
24+
$columnFactory = $db->getSchema()->getColumnFactory();
2525

2626
// With dimension
2727
$column = $columnFactory->fromDbType($dbType, ['dimension' => 1]);
@@ -60,7 +60,7 @@ public function testFromType(string $type, string $expectedType, string $expecte
6060
parent::testFromType($type, $expectedType, $expectedInstanceOf);
6161

6262
$db = $this->getConnection();
63-
$columnFactory = $db->getColumnFactory();
63+
$columnFactory = $db->getSchema()->getColumnFactory();
6464

6565
// With dimension
6666
$column = $columnFactory->fromType($type, ['dimension' => 1]);

tests/ConnectionTest.php

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
use Yiisoft\Db\Exception\Exception;
1111
use Yiisoft\Db\Exception\InvalidConfigException;
1212
use Yiisoft\Db\Exception\NotSupportedException;
13-
use Yiisoft\Db\Pgsql\Column\ColumnFactory;
1413
use Yiisoft\Db\Pgsql\Tests\Support\TestTrait;
1514
use Yiisoft\Db\Tests\Common\CommonConnectionTest;
1615
use Yiisoft\Db\Transaction\TransactionInterface;
@@ -134,11 +133,4 @@ static function (ConnectionInterface $db) {
134133

135134
$db->close();
136135
}
137-
138-
public function testGetColumnFactory(): void
139-
{
140-
$db = $this->getConnection();
141-
142-
$this->assertInstanceOf(ColumnFactory::class, $db->getColumnFactory());
143-
}
144136
}

tests/Provider/QueryBuilderProvider.php

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Yiisoft\Db\Pgsql\Tests\Provider;
66

77
use Yiisoft\Db\Constant\ColumnType;
8+
use Yiisoft\Db\Constant\PseudoType;
89
use Yiisoft\Db\Expression\ArrayExpression;
910
use Yiisoft\Db\Expression\Expression;
1011
use Yiisoft\Db\Expression\JsonExpression;
@@ -536,4 +537,60 @@ public static function overlapsCondition(): array
536537

537538
return $data;
538539
}
540+
541+
public static function buildColumnDefinition(): array
542+
{
543+
$values = parent::buildColumnDefinition();
544+
545+
$values[PseudoType::PK][0] = 'serial PRIMARY KEY';
546+
$values[PseudoType::UPK][0] = 'serial PRIMARY KEY';
547+
$values[PseudoType::BIGPK][0] = 'bigserial PRIMARY KEY';
548+
$values[PseudoType::UBIGPK][0] = 'bigserial PRIMARY KEY';
549+
$values[PseudoType::UUID_PK][0] = 'uuid PRIMARY KEY DEFAULT gen_random_uuid()';
550+
$values[PseudoType::UUID_PK_SEQ][0] = 'uuid PRIMARY KEY DEFAULT gen_random_uuid()';
551+
$values['primaryKey()'][0] = 'serial PRIMARY KEY';
552+
$values['smallPrimaryKey()'][0] = 'smallserial PRIMARY KEY';
553+
$values['bigPrimaryKey()'][0] = 'bigserial PRIMARY KEY';
554+
$values['uuidPrimaryKey()'][0] = 'uuid PRIMARY KEY DEFAULT gen_random_uuid()';
555+
$values['bit()'][0] = 'varbit';
556+
$values['bit(1)'][0] = 'varbit(1)';
557+
$values['bit(8)'][0] = 'varbit(8)';
558+
$values['bit(1000)'][0] = 'varbit(1000)';
559+
$values['tinyint()'][0] = 'smallint';
560+
$values['tinyint(2)'][0] = 'smallint';
561+
$values['smallint(4)'][0] = 'smallint';
562+
$values['integer(8)'][0] = 'integer';
563+
$values['bigint(15)'][0] = 'bigint';
564+
$values['float()'][0] = 'real';
565+
$values['float(10)'][0] = 'real';
566+
$values['float(10,2)'][0] = 'real';
567+
$values['double()'][0] = 'double precision';
568+
$values['double(10)'][0] = 'double precision';
569+
$values['double(10,2)'][0] = 'double precision';
570+
$values['decimal()'][0] = 'numeric(10,0)';
571+
$values['decimal(5)'][0] = 'numeric(5,0)';
572+
$values['decimal(5,2)'][0] = 'numeric(5,2)';
573+
$values['decimal(null)'][0] = 'numeric';
574+
$values['money()'][0] = 'money';
575+
$values['money(10)'][0] = 'money';
576+
$values['money(10,2)'][0] = 'money';
577+
$values['money(null)'][0] = 'money';
578+
$values['text(1000)'][0] = 'text';
579+
$values['binary()'][0] = 'bytea';
580+
$values['binary(1000)'][0] = 'bytea';
581+
$values['uuid()'][0] = 'uuid';
582+
$values['datetime()'][0] = 'timestamp(0)';
583+
$values['datetime(6)'][0] = 'timestamp(6)';
584+
$values['datetime(null)'][0] = 'timestamp';
585+
$values['array()'][0] = 'varchar[]';
586+
$values['structured()'][0] = 'jsonb';
587+
$values["structured('structured_type')"][0] = 'structured_type';
588+
$values['json()'][0] = 'jsonb';
589+
$values['json(100)'][0] = 'jsonb';
590+
$values['unsigned()'][0] = 'integer';
591+
$values['scale(2)'][0] = 'numeric(10,2)';
592+
$values['integer(8)->scale(2)'][0] = 'integer';
593+
594+
return $values;
595+
}
539596
}

0 commit comments

Comments
 (0)