Skip to content

Commit

Permalink
[10.x] Add support for getting native columns' attributes (laravel#48357
Browse files Browse the repository at this point in the history
)

* 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]>
  • Loading branch information
hafezdivandari and taylorotwell authored Oct 26, 2023
1 parent 4e43ca0 commit 85a5146
Show file tree
Hide file tree
Showing 21 changed files with 328 additions and 54 deletions.
26 changes: 26 additions & 0 deletions src/Illuminate/Database/Query/Processors/MySqlProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ class MySqlProcessor extends Processor
/**
* Process the results of a column listing query.
*
* @deprecated Will be removed in a future Laravel version.
*
* @param array $results
* @return array
*/
Expand All @@ -16,4 +18,28 @@ public function processColumnListing($results)
return ((object) $result)->column_name;
}, $results);
}

/**
* Process the results of a columns query.
*
* @param array $results
* @return array
*/
public function processColumns($results)
{
return array_map(function ($result) {
$result = (object) $result;

return [
'name' => $result->name,
'type_name' => $result->type_name,
'type' => $result->type,
'collation' => $result->collation,
'nullable' => $result->nullable === 'YES',
'default' => $result->default,
'auto_increment' => $result->extra === 'auto_increment',
'comment' => $result->comment,
];
}, $results);
}
}
28 changes: 28 additions & 0 deletions src/Illuminate/Database/Query/Processors/PostgresProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ public function processInsertGetId(Builder $query, $sql, $values, $sequence = nu
/**
* Process the results of a column listing query.
*
* @deprecated Will be removed in a future Laravel version.
*
* @param array $results
* @return array
*/
Expand All @@ -42,4 +44,30 @@ public function processColumnListing($results)
return ((object) $result)->column_name;
}, $results);
}

/**
* Process the results of a columns query.
*
* @param array $results
* @return array
*/
public function processColumns($results)
{
return array_map(function ($result) {
$result = (object) $result;

$autoincrement = $result->default !== null && str_starts_with($result->default, 'nextval(');

return [
'name' => $result->name,
'type_name' => $result->type_name,
'type' => $result->type,
'collation' => $result->collation,
'nullable' => (bool) $result->nullable,
'default' => $autoincrement ? null : $result->default,
'auto_increment' => $autoincrement,
'comment' => $result->comment,
];
}, $results);
}
}
13 changes: 13 additions & 0 deletions src/Illuminate/Database/Query/Processors/Processor.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,24 @@ public function processInsertGetId(Builder $query, $sql, $values, $sequence = nu
/**
* Process the results of a column listing query.
*
* @deprecated Will be removed in a future Laravel version.
*
* @param array $results
* @return array
*/
public function processColumnListing($results)
{
return $results;
}

/**
* Process the results of a columns query.
*
* @param array $results
* @return array
*/
public function processColumns($results)
{
return $results;
}
}
30 changes: 30 additions & 0 deletions src/Illuminate/Database/Query/Processors/SQLiteProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ class SQLiteProcessor extends Processor
/**
* Process the results of a column listing query.
*
* @deprecated Will be removed in a future Laravel version.
*
* @param array $results
* @return array
*/
Expand All @@ -16,4 +18,32 @@ public function processColumnListing($results)
return ((object) $result)->name;
}, $results);
}

/**
* Process the results of a columns query.
*
* @param array $results
* @return array
*/
public function processColumns($results)
{
$hasPrimaryKey = array_sum(array_column($results, 'primary')) === 1;

return array_map(function ($result) use ($hasPrimaryKey) {
$result = (object) $result;

$type = strtolower($result->type);

return [
'name' => $result->name,
'type_name' => strtok($type, '('),
'type' => $type,
'collation' => null,
'nullable' => (bool) $result->nullable,
'default' => $result->default,
'auto_increment' => $hasPrimaryKey && $result->primary && $type === 'integer',
'comment' => null,
];
}, $results);
}
}
33 changes: 33 additions & 0 deletions src/Illuminate/Database/Query/Processors/SqlServerProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ protected function processInsertGetIdForOdbc(Connection $connection)
/**
* Process the results of a column listing query.
*
* @deprecated Will be removed in a future Laravel version.
*
* @param array $results
* @return array
*/
Expand All @@ -67,4 +69,35 @@ public function processColumnListing($results)
return ((object) $result)->name;
}, $results);
}

/**
* Process the results of a columns query.
*
* @param array $results
* @return array
*/
public function processColumns($results)
{
return array_map(function ($result) {
$result = (object) $result;

$type = match ($typeName = $result->type_name) {
'binary', 'varbinary', 'char', 'varchar', 'nchar', 'nvarchar' => $result->length == -1 ? $typeName.'(max)' : $typeName."($result->length)",
'decimal', 'numeric' => $typeName."($result->precision,$result->places)",
'float', 'datetime2', 'datetimeoffset', 'time' => $typeName."($result->precision)",
default => $typeName,
};

return [
'name' => $result->name,
'type_name' => $result->type_name,
'type' => $type,
'collation' => $result->collation,
'nullable' => (bool) $result->nullable,
'default' => $result->default,
'auto_increment' => (bool) $result->autoincrement,
'comment' => $result->comment,
];
}, $results);
}
}
36 changes: 30 additions & 6 deletions src/Illuminate/Database/Schema/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -233,13 +233,26 @@ public function whenTableDoesntHaveColumn(string $table, string $column, Closure
*
* @param string $table
* @param string $column
* @param bool $fullDefinition
* @return string
*/
public function getColumnType($table, $column)
public function getColumnType($table, $column, $fullDefinition = false)
{
$table = $this->connection->getTablePrefix().$table;

return $this->connection->getDoctrineColumn($table, $column)->getType()->getName();
if (! $this->connection->usingNativeSchemaOperations()) {
return $this->connection->getDoctrineColumn($table, $column)->getType()->getName();
}

$columns = $this->getColumns($table);

foreach ($columns as $value) {
if (strtolower($value['name']) === $column) {
return $fullDefinition ? $value['type'] : $value['type_name'];
}
}

throw new InvalidArgumentException("There is no column with name '$column' on table '$table'.");
}

/**
Expand All @@ -250,11 +263,22 @@ public function getColumnType($table, $column)
*/
public function getColumnListing($table)
{
$results = $this->connection->selectFromWriteConnection($this->grammar->compileColumnListing(
$this->connection->getTablePrefix().$table
));
return array_column($this->getColumns($table), 'name');
}

/**
* Get the columns for a given table.
*
* @param string $table
* @return array
*/
public function getColumns($table)
{
$table = $this->connection->getTablePrefix().$table;

return $this->connection->getPostProcessor()->processColumnListing($results);
return $this->connection->getPostProcessor()->processColumns(
$this->connection->selectFromWriteConnection($this->grammar->compileColumns($table))
);
}

/**
Expand Down
22 changes: 22 additions & 0 deletions src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,35 @@ public function compileTableExists()
/**
* Compile the query to determine the list of columns.
*
* @deprecated Will be removed in a future Laravel version.
*
* @return string
*/
public function compileColumnListing()
{
return 'select column_name as `column_name` from information_schema.columns where table_schema = ? and table_name = ?';
}

/**
* Compile the query to determine the columns.
*
* @param string $database
* @param string $table
* @return string
*/
public function compileColumns($database, $table)
{
return sprintf(
'select column_name as `name`, data_type as `type_name`, column_type as `type`, '
.'collation_name as `collation`, is_nullable as `nullable`, '
.'column_default as `default`, column_comment AS `comment`, extra as `extra` '
.'from information_schema.columns where table_schema = %s and table_name = %s '
.'order by ordinal_position asc',
$this->quoteString($database),
$this->quoteString($table)
);
}

/**
* Compile a create table command.
*
Expand Down
26 changes: 26 additions & 0 deletions src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,39 @@ public function compileTableExists()
/**
* Compile the query to determine the list of columns.
*
* @deprecated Will be removed in a future Laravel version.
*
* @return string
*/
public function compileColumnListing()
{
return 'select column_name from information_schema.columns where table_catalog = ? and table_schema = ? and table_name = ?';
}

/**
* Compile the query to determine the columns.
*
* @param string $database
* @param string $schema
* @param string $table
* @return string
*/
public function compileColumns($database, $schema, $table)
{
return sprintf(
'select quote_ident(a.attname) as name, t.typname as type_name, format_type(a.atttypid, a.atttypmod) as type, '
.'(select tc.collcollate from pg_catalog.pg_collation tc where tc.oid = a.attcollation) as collation, '
.'not a.attnotnull as nullable, '
.'(select pg_get_expr(adbin, adrelid) from pg_attrdef where c.oid = pg_attrdef.adrelid and pg_attrdef.adnum = a.attnum) as default, '
.'(select pg_description.description from pg_description where pg_description.objoid = c.oid and a.attnum = pg_description.objsubid) as comment '
.'from pg_attribute a, pg_class c, pg_type t, pg_namespace n '
.'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 '
.'order by a.attnum',
$this->quoteString($table),
$this->quoteString($schema)
);
}

/**
* Compile a create table command.
*
Expand Down
17 changes: 17 additions & 0 deletions src/Illuminate/Database/Schema/Grammars/SQLiteGrammar.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ public function compileTableExists()
/**
* Compile the query to determine the list of columns.
*
* @deprecated Will be removed in a future Laravel version.
*
* @param string $table
* @return string
*/
Expand All @@ -47,6 +49,21 @@ public function compileColumnListing($table)
return 'pragma table_info('.$this->wrap(str_replace('.', '__', $table)).')';
}

/**
* Compile the query to determine the columns.
*
* @param string $table
* @return string
*/
public function compileColumns($table)
{
return sprintf(
"select name, type, not 'notnull' as 'nullable', dflt_value as 'default', pk as 'primary' "
.'from pragma_table_info(%s) order by cid asc',
$this->wrap(str_replace('.', '__', $table))
);
}

/**
* Compile a create table command.
*
Expand Down
27 changes: 27 additions & 0 deletions src/Illuminate/Database/Schema/Grammars/SqlServerGrammar.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ public function compileTableExists()
/**
* Compile the query to determine the list of columns.
*
* @deprecated Will be removed in a future Laravel version.
*
* @param string $table
* @return string
*/
Expand All @@ -87,6 +89,31 @@ public function compileColumnListing($table)
return "select name from sys.columns where object_id = object_id('$table')";
}

/**
* Compile the query to determine the columns.
*
* @param string $table
* @return string
*/
public function compileColumns($table)
{
return sprintf(
'select col.name, type.name as type_name, '
.'col.max_length as length, col.precision as precision, col.scale as places, '
.'col.is_nullable as nullable, def.definition as [default], '
.'col.is_identity as autoincrement, col.collation_name as collation, '
.'cast(prop.value as nvarchar(max)) as comment '
.'from sys.columns as col '
.'join sys.types as type on col.user_type_id = type.user_type_id '
.'join sys.objects as obj on col.object_id = obj.object_id '
.'join sys.schemas as scm on obj.schema_id = scm.schema_id '
.'left join sys.default_constraints def on col.default_object_id = def.object_id and col.object_id = def.parent_object_id '
."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' "
."where obj.type = 'U' and obj.name = %s and scm.name = SCHEMA_NAME()",
$this->quoteString($table),
);
}

/**
* Compile a create table command.
*
Expand Down
Loading

0 comments on commit 85a5146

Please sign in to comment.