From 40067c0b53020ef6317b957ec3c83457bf68ce2c Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Tue, 9 Sep 2025 17:18:31 +0900 Subject: [PATCH] Support generated columns #2596 --- common/framework/parsers/DBTableParser.php | 15 ++++++- .../parsers/dbtable/GeneratedColumn.php | 12 ++++++ common/framework/parsers/dbtable/Table.php | 43 ++++++++++++------- tests/_data/dbtable/generated.xml | 9 ++++ .../framework/parsers/DBTableParserTest.php | 10 +++++ 5 files changed, 73 insertions(+), 16 deletions(-) create mode 100644 common/framework/parsers/dbtable/GeneratedColumn.php create mode 100644 tests/_data/dbtable/generated.xml diff --git a/common/framework/parsers/DBTableParser.php b/common/framework/parsers/DBTableParser.php index 50874a7d8..93e6ddb66 100644 --- a/common/framework/parsers/DBTableParser.php +++ b/common/framework/parsers/DBTableParser.php @@ -70,8 +70,21 @@ class DBTableParser extends BaseParser // Load columns. foreach ($xml->column as $column_info) { + // Is this column generated? + $is_generated = strval($column_info['generated'] ?? '') !== ''; + if ($is_generated) + { + $column = new DBTable\GeneratedColumn; + $column->generated = strtolower($column_info['generated']); + $column->is_stored = strtolower($column_info['stored'] ?? ''); + $column->is_stored = $column->is_stored !== 'virtual' && toBool($column->is_stored); + } + else + { + $column = new DBTable\Column; + } + // Get the column name and type. - $column = new DBTable\Column; $column->name = strval($column_info['name']); list($column->type, $column->xetype, $column->size) = self::getTypeAndSize(strval($column_info['type']), strval($column_info['size'])); diff --git a/common/framework/parsers/dbtable/GeneratedColumn.php b/common/framework/parsers/dbtable/GeneratedColumn.php new file mode 100644 index 000000000..60a9726fb --- /dev/null +++ b/common/framework/parsers/dbtable/GeneratedColumn.php @@ -0,0 +1,12 @@ +charset . ' COLLATE ' . $column->charset . '_general_ci'; } } + if ($column instanceof GeneratedColumn) + { + $columndef .= ' GENERATED ' . strtoupper($column->generated ?: 'always'); + $columndef .= ' AS (' . $column->default_value . ')'; + if ($column->is_stored) + { + $columndef .= ' STORED'; + } + } + else + { + if ($column->default_value !== null) + { + if (preg_match('/(?:int|float|double|decimal|number)/i', $column->type) && is_numeric($column->default_value)) + { + $columndef .= ' DEFAULT ' . $column->default_value; + } + elseif (preg_match('/^\w+\(\)$/', $column->default_value)) + { + $columndef .= ' DEFAULT ' . $column->default_value; + } + else + { + $columndef .= ' DEFAULT \'' . $column->default_value . '\''; + } + } + } if ($column->not_null) { $columndef .= ' NOT NULL'; } - if ($column->default_value !== null) - { - if (preg_match('/(?:int|float|double|decimal|number)/i', $column->type) && is_numeric($column->default_value)) - { - $columndef .= ' DEFAULT ' . $column->default_value; - } - elseif (preg_match('/^\w+\(\)$/', $column->default_value)) - { - $columndef .= ' DEFAULT ' . $column->default_value; - } - else - { - $columndef .= ' DEFAULT \'' . $column->default_value . '\''; - } - } if ($column->auto_increment) { $columndef .= ' AUTO_INCREMENT'; } + $columns[] = $columndef; } diff --git a/tests/_data/dbtable/generated.xml b/tests/_data/dbtable/generated.xml new file mode 100644 index 000000000..0c136faa3 --- /dev/null +++ b/tests/_data/dbtable/generated.xml @@ -0,0 +1,9 @@ + + + + + + + + +
diff --git a/tests/unit/framework/parsers/DBTableParserTest.php b/tests/unit/framework/parsers/DBTableParserTest.php index ab8b00d47..4c7b2e9ec 100644 --- a/tests/unit/framework/parsers/DBTableParserTest.php +++ b/tests/unit/framework/parsers/DBTableParserTest.php @@ -63,4 +63,14 @@ class DBTableParserTest extends \Codeception\Test\Unit $this->assertStringContainsString('CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci', $sql); $this->assertStringContainsString('ENGINE = InnoDB', $sql); } + + public function testGeneratedColumn() + { + $table = Rhymix\Framework\Parsers\DBTableParser::loadXML(\RX_BASEDIR . 'tests/_data/dbtable/generated.xml'); + $sql = $table->getCreateQuery('rx_'); + $this->assertStringContainsString('CREATE TABLE `rx_generated`', $sql); + $this->assertStringContainsString('`gentest1` BIGINT GENERATED ALWAYS AS (document_srl + member_srl) STORED NOT NULL,', $sql); + $this->assertStringContainsString('`gentest2` BIGINT GENERATED ALWAYS AS (MAX(module_srl, document_srl)),', $sql); + $this->assertStringContainsString('`gentest3` VARCHAR(40) GENERATED ALWAYS AS (CONCAT(module_srl, \'_\', document_srl)) NOT NULL,', $sql); + } }