Skip to content

Commit 85a5146

Browse files
[10.x] Add support for getting native columns' attributes (#48357)
* add support for getting native columns' attributes * fix test and formatting * deprecate `processColumnListing` * use table-valued pragma on SQLite * fix a typo * revert changes on a deprecated function * formatting --------- Co-authored-by: Taylor Otwell <[email protected]>
1 parent 4e43ca0 commit 85a5146

21 files changed

+328
-54
lines changed

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ class MySqlProcessor extends Processor
77
/**
88
* Process the results of a column listing query.
99
*
10+
* @deprecated Will be removed in a future Laravel version.
11+
*
1012
* @param array $results
1113
* @return array
1214
*/
@@ -16,4 +18,28 @@ public function processColumnListing($results)
1618
return ((object) $result)->column_name;
1719
}, $results);
1820
}
21+
22+
/**
23+
* Process the results of a columns query.
24+
*
25+
* @param array $results
26+
* @return array
27+
*/
28+
public function processColumns($results)
29+
{
30+
return array_map(function ($result) {
31+
$result = (object) $result;
32+
33+
return [
34+
'name' => $result->name,
35+
'type_name' => $result->type_name,
36+
'type' => $result->type,
37+
'collation' => $result->collation,
38+
'nullable' => $result->nullable === 'YES',
39+
'default' => $result->default,
40+
'auto_increment' => $result->extra === 'auto_increment',
41+
'comment' => $result->comment,
42+
];
43+
}, $results);
44+
}
1945
}

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ public function processInsertGetId(Builder $query, $sql, $values, $sequence = nu
3333
/**
3434
* Process the results of a column listing query.
3535
*
36+
* @deprecated Will be removed in a future Laravel version.
37+
*
3638
* @param array $results
3739
* @return array
3840
*/
@@ -42,4 +44,30 @@ public function processColumnListing($results)
4244
return ((object) $result)->column_name;
4345
}, $results);
4446
}
47+
48+
/**
49+
* Process the results of a columns query.
50+
*
51+
* @param array $results
52+
* @return array
53+
*/
54+
public function processColumns($results)
55+
{
56+
return array_map(function ($result) {
57+
$result = (object) $result;
58+
59+
$autoincrement = $result->default !== null && str_starts_with($result->default, 'nextval(');
60+
61+
return [
62+
'name' => $result->name,
63+
'type_name' => $result->type_name,
64+
'type' => $result->type,
65+
'collation' => $result->collation,
66+
'nullable' => (bool) $result->nullable,
67+
'default' => $autoincrement ? null : $result->default,
68+
'auto_increment' => $autoincrement,
69+
'comment' => $result->comment,
70+
];
71+
}, $results);
72+
}
4573
}

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,24 @@ public function processInsertGetId(Builder $query, $sql, $values, $sequence = nu
3939
/**
4040
* Process the results of a column listing query.
4141
*
42+
* @deprecated Will be removed in a future Laravel version.
43+
*
4244
* @param array $results
4345
* @return array
4446
*/
4547
public function processColumnListing($results)
4648
{
4749
return $results;
4850
}
51+
52+
/**
53+
* Process the results of a columns query.
54+
*
55+
* @param array $results
56+
* @return array
57+
*/
58+
public function processColumns($results)
59+
{
60+
return $results;
61+
}
4962
}

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ class SQLiteProcessor extends Processor
77
/**
88
* Process the results of a column listing query.
99
*
10+
* @deprecated Will be removed in a future Laravel version.
11+
*
1012
* @param array $results
1113
* @return array
1214
*/
@@ -16,4 +18,32 @@ public function processColumnListing($results)
1618
return ((object) $result)->name;
1719
}, $results);
1820
}
21+
22+
/**
23+
* Process the results of a columns query.
24+
*
25+
* @param array $results
26+
* @return array
27+
*/
28+
public function processColumns($results)
29+
{
30+
$hasPrimaryKey = array_sum(array_column($results, 'primary')) === 1;
31+
32+
return array_map(function ($result) use ($hasPrimaryKey) {
33+
$result = (object) $result;
34+
35+
$type = strtolower($result->type);
36+
37+
return [
38+
'name' => $result->name,
39+
'type_name' => strtok($type, '('),
40+
'type' => $type,
41+
'collation' => null,
42+
'nullable' => (bool) $result->nullable,
43+
'default' => $result->default,
44+
'auto_increment' => $hasPrimaryKey && $result->primary && $type === 'integer',
45+
'comment' => null,
46+
];
47+
}, $results);
48+
}
1949
}

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ protected function processInsertGetIdForOdbc(Connection $connection)
5858
/**
5959
* Process the results of a column listing query.
6060
*
61+
* @deprecated Will be removed in a future Laravel version.
62+
*
6163
* @param array $results
6264
* @return array
6365
*/
@@ -67,4 +69,35 @@ public function processColumnListing($results)
6769
return ((object) $result)->name;
6870
}, $results);
6971
}
72+
73+
/**
74+
* Process the results of a columns query.
75+
*
76+
* @param array $results
77+
* @return array
78+
*/
79+
public function processColumns($results)
80+
{
81+
return array_map(function ($result) {
82+
$result = (object) $result;
83+
84+
$type = match ($typeName = $result->type_name) {
85+
'binary', 'varbinary', 'char', 'varchar', 'nchar', 'nvarchar' => $result->length == -1 ? $typeName.'(max)' : $typeName."($result->length)",
86+
'decimal', 'numeric' => $typeName."($result->precision,$result->places)",
87+
'float', 'datetime2', 'datetimeoffset', 'time' => $typeName."($result->precision)",
88+
default => $typeName,
89+
};
90+
91+
return [
92+
'name' => $result->name,
93+
'type_name' => $result->type_name,
94+
'type' => $type,
95+
'collation' => $result->collation,
96+
'nullable' => (bool) $result->nullable,
97+
'default' => $result->default,
98+
'auto_increment' => (bool) $result->autoincrement,
99+
'comment' => $result->comment,
100+
];
101+
}, $results);
102+
}
70103
}

src/Illuminate/Database/Schema/Builder.php

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -233,13 +233,26 @@ public function whenTableDoesntHaveColumn(string $table, string $column, Closure
233233
*
234234
* @param string $table
235235
* @param string $column
236+
* @param bool $fullDefinition
236237
* @return string
237238
*/
238-
public function getColumnType($table, $column)
239+
public function getColumnType($table, $column, $fullDefinition = false)
239240
{
240241
$table = $this->connection->getTablePrefix().$table;
241242

242-
return $this->connection->getDoctrineColumn($table, $column)->getType()->getName();
243+
if (! $this->connection->usingNativeSchemaOperations()) {
244+
return $this->connection->getDoctrineColumn($table, $column)->getType()->getName();
245+
}
246+
247+
$columns = $this->getColumns($table);
248+
249+
foreach ($columns as $value) {
250+
if (strtolower($value['name']) === $column) {
251+
return $fullDefinition ? $value['type'] : $value['type_name'];
252+
}
253+
}
254+
255+
throw new InvalidArgumentException("There is no column with name '$column' on table '$table'.");
243256
}
244257

245258
/**
@@ -250,11 +263,22 @@ public function getColumnType($table, $column)
250263
*/
251264
public function getColumnListing($table)
252265
{
253-
$results = $this->connection->selectFromWriteConnection($this->grammar->compileColumnListing(
254-
$this->connection->getTablePrefix().$table
255-
));
266+
return array_column($this->getColumns($table), 'name');
267+
}
268+
269+
/**
270+
* Get the columns for a given table.
271+
*
272+
* @param string $table
273+
* @return array
274+
*/
275+
public function getColumns($table)
276+
{
277+
$table = $this->connection->getTablePrefix().$table;
256278

257-
return $this->connection->getPostProcessor()->processColumnListing($results);
279+
return $this->connection->getPostProcessor()->processColumns(
280+
$this->connection->selectFromWriteConnection($this->grammar->compileColumns($table))
281+
);
258282
}
259283

260284
/**

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,35 @@ public function compileTableExists()
7878
/**
7979
* Compile the query to determine the list of columns.
8080
*
81+
* @deprecated Will be removed in a future Laravel version.
82+
*
8183
* @return string
8284
*/
8385
public function compileColumnListing()
8486
{
8587
return 'select column_name as `column_name` from information_schema.columns where table_schema = ? and table_name = ?';
8688
}
8789

90+
/**
91+
* Compile the query to determine the columns.
92+
*
93+
* @param string $database
94+
* @param string $table
95+
* @return string
96+
*/
97+
public function compileColumns($database, $table)
98+
{
99+
return sprintf(
100+
'select column_name as `name`, data_type as `type_name`, column_type as `type`, '
101+
.'collation_name as `collation`, is_nullable as `nullable`, '
102+
.'column_default as `default`, column_comment AS `comment`, extra as `extra` '
103+
.'from information_schema.columns where table_schema = %s and table_name = %s '
104+
.'order by ordinal_position asc',
105+
$this->quoteString($database),
106+
$this->quoteString($table)
107+
);
108+
}
109+
88110
/**
89111
* Compile a create table command.
90112
*

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,13 +81,39 @@ public function compileTableExists()
8181
/**
8282
* Compile the query to determine the list of columns.
8383
*
84+
* @deprecated Will be removed in a future Laravel version.
85+
*
8486
* @return string
8587
*/
8688
public function compileColumnListing()
8789
{
8890
return 'select column_name from information_schema.columns where table_catalog = ? and table_schema = ? and table_name = ?';
8991
}
9092

93+
/**
94+
* Compile the query to determine the columns.
95+
*
96+
* @param string $database
97+
* @param string $schema
98+
* @param string $table
99+
* @return string
100+
*/
101+
public function compileColumns($database, $schema, $table)
102+
{
103+
return sprintf(
104+
'select quote_ident(a.attname) as name, t.typname as type_name, format_type(a.atttypid, a.atttypmod) as type, '
105+
.'(select tc.collcollate from pg_catalog.pg_collation tc where tc.oid = a.attcollation) as collation, '
106+
.'not a.attnotnull as nullable, '
107+
.'(select pg_get_expr(adbin, adrelid) from pg_attrdef where c.oid = pg_attrdef.adrelid and pg_attrdef.adnum = a.attnum) as default, '
108+
.'(select pg_description.description from pg_description where pg_description.objoid = c.oid and a.attnum = pg_description.objsubid) as comment '
109+
.'from pg_attribute a, pg_class c, pg_type t, pg_namespace n '
110+
.'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 '
111+
.'order by a.attnum',
112+
$this->quoteString($table),
113+
$this->quoteString($schema)
114+
);
115+
}
116+
91117
/**
92118
* Compile a create table command.
93119
*

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ public function compileTableExists()
3939
/**
4040
* Compile the query to determine the list of columns.
4141
*
42+
* @deprecated Will be removed in a future Laravel version.
43+
*
4244
* @param string $table
4345
* @return string
4446
*/
@@ -47,6 +49,21 @@ public function compileColumnListing($table)
4749
return 'pragma table_info('.$this->wrap(str_replace('.', '__', $table)).')';
4850
}
4951

52+
/**
53+
* Compile the query to determine the columns.
54+
*
55+
* @param string $table
56+
* @return string
57+
*/
58+
public function compileColumns($table)
59+
{
60+
return sprintf(
61+
"select name, type, not 'notnull' as 'nullable', dflt_value as 'default', pk as 'primary' "
62+
.'from pragma_table_info(%s) order by cid asc',
63+
$this->wrap(str_replace('.', '__', $table))
64+
);
65+
}
66+
5067
/**
5168
* Compile a create table command.
5269
*

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ public function compileTableExists()
7979
/**
8080
* Compile the query to determine the list of columns.
8181
*
82+
* @deprecated Will be removed in a future Laravel version.
83+
*
8284
* @param string $table
8385
* @return string
8486
*/
@@ -87,6 +89,31 @@ public function compileColumnListing($table)
8789
return "select name from sys.columns where object_id = object_id('$table')";
8890
}
8991

92+
/**
93+
* Compile the query to determine the columns.
94+
*
95+
* @param string $table
96+
* @return string
97+
*/
98+
public function compileColumns($table)
99+
{
100+
return sprintf(
101+
'select col.name, type.name as type_name, '
102+
.'col.max_length as length, col.precision as precision, col.scale as places, '
103+
.'col.is_nullable as nullable, def.definition as [default], '
104+
.'col.is_identity as autoincrement, col.collation_name as collation, '
105+
.'cast(prop.value as nvarchar(max)) as comment '
106+
.'from sys.columns as col '
107+
.'join sys.types as type on col.user_type_id = type.user_type_id '
108+
.'join sys.objects as obj on col.object_id = obj.object_id '
109+
.'join sys.schemas as scm on obj.schema_id = scm.schema_id '
110+
.'left join sys.default_constraints def on col.default_object_id = def.object_id and col.object_id = def.parent_object_id '
111+
."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' "
112+
."where obj.type = 'U' and obj.name = %s and scm.name = SCHEMA_NAME()",
113+
$this->quoteString($table),
114+
);
115+
}
116+
90117
/**
91118
* Compile a create table command.
92119
*

0 commit comments

Comments
 (0)