Skip to content

Commit 6f3ddc3

Browse files
[11.x] Add support for modifying generated columns (#50329)
* add generation to getColumns * add support for modifying generated column on sqlite * add support for renaming generated column on legacy mysql * add tests * force re-run tests * fix tests * fix tests * wip * wip * fix tests * wip * Update SQLiteProcessor.php --------- Co-authored-by: Taylor Otwell <[email protected]>
1 parent c089bf1 commit 6f3ddc3

14 files changed

+171
-18
lines changed

src/Illuminate/Database/Query/Processors/MySqlProcessor.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ public function processColumns($results)
2424
'default' => $result->default,
2525
'auto_increment' => $result->extra === 'auto_increment',
2626
'comment' => $result->comment ?: null,
27+
'generation' => $result->expression ? [
28+
'type' => match ($result->extra) {
29+
'STORED GENERATED' => 'stored',
30+
'VIRTUAL GENERATED' => 'virtual',
31+
default => null,
32+
},
33+
'expression' => $result->expression,
34+
] : null,
2735
];
2836
}, $results);
2937
}

src/Illuminate/Database/Query/Processors/PostgresProcessor.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,16 @@ public function processColumns($results)
9797
'type' => $result->type,
9898
'collation' => $result->collation,
9999
'nullable' => (bool) $result->nullable,
100-
'default' => $autoincrement ? null : $result->default,
100+
'default' => $result->generated ? null : $result->default,
101101
'auto_increment' => $autoincrement,
102102
'comment' => $result->comment,
103+
'generation' => $result->generated ? [
104+
'type' => match ($result->generated) {
105+
's' => 'stored',
106+
default => null,
107+
},
108+
'expression' => $result->default,
109+
] : null,
103110
];
104111
}, $results);
105112
}

src/Illuminate/Database/Query/Processors/SQLiteProcessor.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ public function processColumns($results, $sql = '')
2626
$matches
2727
) === 1 ? strtolower($matches[1]) : null;
2828

29+
$isGenerated = in_array($result->extra, [2, 3]);
30+
31+
$expression = $isGenerated && preg_match(
32+
'/\b'.preg_quote($result->name).'\b[^,]+\s+as\s+\(((?:[^()]+|\((?:[^()]+|\([^()]*\))*\))*)\)/i',
33+
$sql,
34+
$matches
35+
) === 1 ? $matches[1] : null;
36+
2937
return [
3038
'name' => $result->name,
3139
'type_name' => strtok($type, '(') ?: '',
@@ -35,6 +43,14 @@ public function processColumns($results, $sql = '')
3543
'default' => $result->default,
3644
'auto_increment' => $hasPrimaryKey && $result->primary && $type === 'integer',
3745
'comment' => null,
46+
'generation' => $isGenerated ? [
47+
'type' => match ((int) $result->extra) {
48+
3 => 'stored',
49+
2 => 'virtual',
50+
default => null,
51+
},
52+
'expression' => $expression,
53+
] : null,
3854
];
3955
}, $results);
4056
}

src/Illuminate/Database/Query/Processors/SqlServerProcessor.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ public function processColumns($results)
8282
'default' => $result->default,
8383
'auto_increment' => (bool) $result->autoincrement,
8484
'comment' => $result->comment,
85+
'generation' => $result->expression ? [
86+
'type' => $result->persisted ? 'stored' : 'virtual',
87+
'expression' => $result->expression,
88+
] : null,
8589
];
8690
}, $results);
8791
}

src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,8 @@ public function compileColumns($database, $table)
121121
return sprintf(
122122
'select column_name as `name`, data_type as `type_name`, column_type as `type`, '
123123
.'collation_name as `collation`, is_nullable as `nullable`, '
124-
.'column_default as `default`, column_comment as `comment`, extra as `extra` '
124+
.'column_default as `default`, column_comment as `comment`, '
125+
.'generation_expression as `expression`, extra as `extra` '
125126
.'from information_schema.columns where table_schema = %s and table_name = %s '
126127
.'order by ordinal_position asc',
127128
$this->quoteString($database),
@@ -343,6 +344,10 @@ public function compileRenameColumn(Blueprint $blueprint, Fluent $command, Conne
343344
'autoIncrement' => $column['auto_increment'],
344345
'collation' => $column['collation'],
345346
'comment' => $column['comment'],
347+
'virtualAs' => ! is_null($column['generation']) && $column['generation']['type'] === 'virtual'
348+
? $column['generation']['expression'] : null,
349+
'storedAs' => ! is_null($column['generation']) && $column['generation']['type'] === 'stored'
350+
? $column['generation']['expression'] : null,
346351
]));
347352

348353
return sprintf('alter table %s change %s %s %s',

src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ public function compileColumns($schema, $table)
123123
.'(select tc.collcollate from pg_catalog.pg_collation tc where tc.oid = a.attcollation) as collation, '
124124
.'not a.attnotnull as nullable, '
125125
.'(select pg_get_expr(adbin, adrelid) from pg_attrdef where c.oid = pg_attrdef.adrelid and pg_attrdef.adnum = a.attnum) as default, '
126+
.'a.attgenerated as generated, '
126127
.'col_description(c.oid, a.attnum) as comment '
127128
.'from pg_attribute a, pg_class c, pg_type t, pg_namespace n '
128129
.'where c.relname = %s and n.nspname = %s and a.attnum > 0 and a.attrelid = c.oid and a.atttypid = t.oid and n.oid = c.relnamespace '

src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public function compileViews()
8989
public function compileColumns($table)
9090
{
9191
return sprintf(
92-
'select name, type, not "notnull" as "nullable", dflt_value as "default", pk as "primary" '
92+
'select name, type, not "notnull" as "nullable", dflt_value as "default", pk as "primary", hidden as "extra" '
9393
.'from pragma_table_xinfo(%s) order by cid asc',
9494
$this->wrap(str_replace('.', '__', $table))
9595
);
@@ -250,14 +250,22 @@ public function compileChange(Blueprint $blueprint, Fluent $command, Connection
250250

251251
if ($column instanceof Fluent) {
252252
$name = $this->wrap($column);
253-
$columnNames[] = $name;
254253
$autoIncrementColumn = $column->autoIncrement ? $column->name : $autoIncrementColumn;
255254

255+
if (is_null($column->virtualAs) && is_null($column->virtualAsJson) &&
256+
is_null($column->storedAs) && is_null($column->storedAsJson)) {
257+
$columnNames[] = $name;
258+
}
259+
256260
return $this->addModifiers($name.' '.$this->getType($column), $blueprint, $column);
257261
} else {
258262
$name = $this->wrap($column['name']);
259-
$columnNames[] = $name;
260263
$autoIncrementColumn = $column['auto_increment'] ? $column['name'] : $autoIncrementColumn;
264+
$isGenerated = ! is_null($column['generation']);
265+
266+
if (! $isGenerated) {
267+
$columnNames[] = $name;
268+
}
261269

262270
return $this->addModifiers($name.' '.$column['type'], $blueprint,
263271
new ColumnDefinition([
@@ -268,6 +276,10 @@ public function compileChange(Blueprint $blueprint, Fluent $command, Connection
268276
'autoIncrement' => $column['auto_increment'],
269277
'collation' => $column['collation'],
270278
'comment' => $column['comment'],
279+
'virtualAs' => $isGenerated && $column['generation']['type'] === 'virtual'
280+
? $column['generation']['expression'] : null,
281+
'storedAs' => $isGenerated && $column['generation']['type'] === 'stored'
282+
? $column['generation']['expression'] : null,
271283
])
272284
);
273285
}

src/Illuminate/Database/Schema/Grammars/SqlServerGrammar.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,13 +107,15 @@ public function compileColumns($schema, $table)
107107
.'col.max_length as length, col.precision as precision, col.scale as places, '
108108
.'col.is_nullable as nullable, def.definition as [default], '
109109
.'col.is_identity as autoincrement, col.collation_name as collation, '
110+
.'com.definition as [expression], is_persisted as [persisted], '
110111
.'cast(prop.value as nvarchar(max)) as comment '
111112
.'from sys.columns as col '
112113
.'join sys.types as type on col.user_type_id = type.user_type_id '
113114
.'join sys.objects as obj on col.object_id = obj.object_id '
114115
.'join sys.schemas as scm on obj.schema_id = scm.schema_id '
115116
.'left join sys.default_constraints def on col.default_object_id = def.object_id and col.object_id = def.parent_object_id '
116117
."left join sys.extended_properties as prop on obj.object_id = prop.major_id and col.column_id = prop.minor_id and prop.name = 'MS_Description' "
118+
.'left join sys.computed_columns as com on col.column_id = com.column_id '
117119
."where obj.type in ('U', 'V') and obj.name = %s and scm.name = %s "
118120
.'order by col.column_id',
119121
$this->quoteString($table),

tests/Database/DatabaseMariaDbProcessorTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ public function testProcessColumns()
1616
['name' => 'email', 'type_name' => 'varchar', 'type' => 'varchar(100)', 'collation' => 'collate', 'nullable' => 'YES', 'default' => 'NULL', 'extra' => 'on update CURRENT_TIMESTAMP', 'comment' => 'NULL'],
1717
];
1818
$expected = [
19-
['name' => 'id', 'type_name' => 'bigint', 'type' => 'bigint', 'collation' => 'collate', 'nullable' => true, 'default' => '', 'auto_increment' => true, 'comment' => 'bar'],
20-
['name' => 'name', 'type_name' => 'varchar', 'type' => 'varchar(100)', 'collation' => 'collate', 'nullable' => false, 'default' => 'foo', 'auto_increment' => false, 'comment' => ''],
21-
['name' => 'email', 'type_name' => 'varchar', 'type' => 'varchar(100)', 'collation' => 'collate', 'nullable' => true, 'default' => 'NULL', 'auto_increment' => false, 'comment' => 'NULL'],
19+
['name' => 'id', 'type_name' => 'bigint', 'type' => 'bigint', 'collation' => 'collate', 'nullable' => true, 'default' => '', 'auto_increment' => true, 'comment' => 'bar', 'generation' => null],
20+
['name' => 'name', 'type_name' => 'varchar', 'type' => 'varchar(100)', 'collation' => 'collate', 'nullable' => false, 'default' => 'foo', 'auto_increment' => false, 'comment' => '', 'generation' => null],
21+
['name' => 'email', 'type_name' => 'varchar', 'type' => 'varchar(100)', 'collation' => 'collate', 'nullable' => true, 'default' => 'NULL', 'auto_increment' => false, 'comment' => 'NULL', 'generation' => null],
2222
];
2323
$this->assertEquals($expected, $processor->processColumns($listing));
2424

tests/Database/DatabaseMySqlProcessorTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ public function testProcessColumns()
1616
['name' => 'email', 'type_name' => 'varchar', 'type' => 'varchar(100)', 'collation' => 'collate', 'nullable' => 'YES', 'default' => 'NULL', 'extra' => 'on update CURRENT_TIMESTAMP', 'comment' => 'NULL'],
1717
];
1818
$expected = [
19-
['name' => 'id', 'type_name' => 'bigint', 'type' => 'bigint', 'collation' => 'collate', 'nullable' => true, 'default' => '', 'auto_increment' => true, 'comment' => 'bar'],
20-
['name' => 'name', 'type_name' => 'varchar', 'type' => 'varchar(100)', 'collation' => 'collate', 'nullable' => false, 'default' => 'foo', 'auto_increment' => false, 'comment' => ''],
21-
['name' => 'email', 'type_name' => 'varchar', 'type' => 'varchar(100)', 'collation' => 'collate', 'nullable' => true, 'default' => 'NULL', 'auto_increment' => false, 'comment' => 'NULL'],
19+
['name' => 'id', 'type_name' => 'bigint', 'type' => 'bigint', 'collation' => 'collate', 'nullable' => true, 'default' => '', 'auto_increment' => true, 'comment' => 'bar', 'generation' => null],
20+
['name' => 'name', 'type_name' => 'varchar', 'type' => 'varchar(100)', 'collation' => 'collate', 'nullable' => false, 'default' => 'foo', 'auto_increment' => false, 'comment' => null, 'generation' => null],
21+
['name' => 'email', 'type_name' => 'varchar', 'type' => 'varchar(100)', 'collation' => 'collate', 'nullable' => true, 'default' => 'NULL', 'auto_increment' => false, 'comment' => 'NULL', 'generation' => null],
2222
];
2323
$this->assertEquals($expected, $processor->processColumns($listing));
2424

0 commit comments

Comments
 (0)