0 follower

Final Class Yiisoft\Db\Mssql\Schema

InheritanceYiisoft\Db\Mssql\Schema » Yiisoft\Db\Driver\Pdo\AbstractPdoSchema

Implements the MSSQL Server specific schema, supporting MSSQL Server 2017 and above.

Psalm Types

Name Value
ColumnArray array{check: string|null, collation_name: string|null, column_name: string, column_default: string|null, is_nullable: string, data_type: string, size: integer|string|null, numeric_scale: integer|string|null, is_identity: string, is_computed: string, comment: string|null, primaryKey: boolean, schema: string|null, table: string}

Protected Properties

Hide inherited properties

Property Type Description Defined By
$defaultSchema string The default schema used for the current session. Yiisoft\Db\Mssql\Schema

Property Details

Hide inherited properties

$defaultSchema protected property

The default schema used for the current session.

protected string $defaultSchema 'dbo'

Method Details

Hide inherited methods

findColumns() protected method

Collects the metadata of table columns.

protected boolean findColumns ( Yiisoft\Db\Mssql\TableSchema $table )
$table Yiisoft\Db\Mssql\TableSchema

The table metadata.

return boolean

Whether the table exists in the database.

                protected function findColumns(TableSchemaInterface $table): bool
{
    $schemaName = $table->getSchemaName();
    $tableName = $table->getName();
    $catalogName = $table->getCatalogName();
    $columnsTableName = '[INFORMATION_SCHEMA].[COLUMNS]';
    $whereSql = '[t].[table_name] = :table_name';
    $whereParams = [':table_name' => $tableName];
    if ($catalogName !== '') {
        $columnsTableName = "[$catalogName].$columnsTableName";
        $whereSql .= ' AND [t].[table_catalog] = :catalog';
        $whereParams[':catalog'] = $catalogName;
    }
    if ($schemaName !== '') {
        $whereSql .= ' AND [t].[table_schema] = :schema_name';
        $whereParams[':schema_name'] = $schemaName;
    }
    $sql = <<<SQL
    SELECT
        [t].[column_name],
        [t].[column_default],
        [t].[is_nullable],
        [t].[data_type],
        COALESCE(NULLIF([t].[character_maximum_length], -1), [t].[numeric_precision], [t].[datetime_precision]) AS [size],
        [t].[numeric_scale],
        [t].[collation_name],
        COLUMNPROPERTY(OBJECT_ID([t].[table_schema] + '.' + [t].[table_name]), [t].[column_name], 'IsIdentity') AS [is_identity],
        COLUMNPROPERTY(OBJECT_ID([t].[table_schema] + '.' + [t].[table_name]), [t].[column_name], 'IsComputed') AS [is_computed],
        [ext].[value] as [comment],
        [c].[definition] AS [check]
    FROM $columnsTableName AS [t]
    LEFT JOIN [sys].[extended_properties] AS [ext]
        ON [ext].[class] = 1
            AND [ext].[class_desc] = 'OBJECT_OR_COLUMN'
            AND [ext].[name] = 'MS_Description'
            AND [ext].[major_id] = OBJECT_ID([t].[table_schema] + '.' + [t].[table_name])
            AND [ext].[minor_id] = COLUMNPROPERTY([ext].[major_id], [t].[column_name], 'ColumnID')
    LEFT JOIN [sys].[check_constraints] AS [c]
        ON [c].[parent_object_id] = OBJECT_ID([t].[table_schema] + '.' + [t].[table_name])
            AND [c].[parent_column_id] = COLUMNPROPERTY([c].[parent_object_id], [t].[column_name], 'ColumnID')
    WHERE $whereSql
    SQL;
    try {
        $columns = $this->db->createCommand($sql, $whereParams)->queryAll();
        if (empty($columns)) {
            return false;
        }
    } catch (Exception) {
        return false;
    }
    $primaryKey = $this->getTablePrimaryKey($table->getFullName(), true);
    /** @psalm-suppress MixedArgumentTypeCoercion */
    $primaryKeys = array_fill_keys((array) $primaryKey?->columnNames, true);
    foreach ($columns as $info) {
        $info = array_change_key_case($info);
        /** @psalm-var ColumnArray $info */
        $info['primaryKey'] = isset($primaryKeys[$info['column_name']]);
        $info['schema'] = $schemaName;
        $info['table'] = $tableName;
        $column = $this->loadColumn($info);
        if ($column->isPrimaryKey() && $column->isAutoIncrement()) {
            $table->sequenceName('');
        }
        $table->column($info['column_name'], $column);
    }
    return true;
}

            
findSchemaNames() protected method

protected array findSchemaNames ( )

                protected function findSchemaNames(): array
{
    $sql = <<<SQL
    SELECT [s].[name]
    FROM [sys].[schemas] AS [s]
    INNER JOIN [sys].[database_principals] AS [p] ON [p].[principal_id] = [s].[principal_id]
    WHERE [p].[is_fixed_role] = 0 AND [p].[sid] IS NOT NULL
    ORDER BY [s].[name] ASC
    SQL;
    /** @var string[] */
    return $this->db->createCommand($sql)->queryColumn();
}

            
findTableComment() protected method

protected void findTableComment ( \Yiisoft\Db\Schema\TableSchemaInterface $tableSchema )
$tableSchema \Yiisoft\Db\Schema\TableSchemaInterface

                protected function findTableComment(TableSchemaInterface $tableSchema): void
{
    $schemaName = $tableSchema->getSchemaName() ?: $this->defaultSchema;
    $schemaName = "N'$schemaName'";
    $tableName = 'N' . $this->db->getQuoter()->quoteValue($tableSchema->getName());
    $sql = <<<SQL
    SELECT [value]
    FROM fn_listextendedproperty (
        N'MS_description',
        'SCHEMA', $schemaName,
        'TABLE', $tableName,
        DEFAULT, DEFAULT)
    SQL;
    $comment = $this->db->createCommand($sql)->queryScalar();
    $tableSchema->comment(is_string($comment) ? $comment : null);
}

            
findTableNames() protected method

protected array findTableNames ( string $schema '' )
$schema string

                protected function findTableNames(string $schema = ''): array
{
    if ($schema === '') {
        $schema = $this->defaultSchema;
    }
    $sql = <<<SQL
    SELECT [t].[table_name]
    FROM [INFORMATION_SCHEMA].[TABLES] AS [t]
    WHERE [t].[table_schema] = :schema AND [t].[table_type] IN ('BASE TABLE', 'VIEW')
    ORDER BY [t].[table_name]
    SQL;
    /** @var string[] */
    return $this->db->createCommand($sql, [':schema' => $schema])->queryColumn();
}

            
findViewNames() protected method

protected array findViewNames ( string $schema '' )
$schema string

                protected function findViewNames(string $schema = ''): array
{
    if ($schema === '') {
        $schema = $this->defaultSchema;
    }
    $sql = <<<SQL
    SELECT [t].[table_name]
    FROM [INFORMATION_SCHEMA].[TABLES] AS [t]
    WHERE [t].[table_schema] = :schema AND [t].[table_type] = 'VIEW'
    ORDER BY [t].[table_name]
    SQL;
    /** @var string[] */
    return $this->db->createCommand($sql, [':schema' => $schema])->queryColumn();
}

            
loadResultColumn() protected method

protected \Yiisoft\Db\Schema\Column\ColumnInterface|null loadResultColumn ( array $metadata )
$metadata array

                protected function loadResultColumn(array $metadata): ?ColumnInterface
{
    if (empty($metadata['sqlsrv:decl_type'])) {
        return null;
    }
    $dbType = $metadata['sqlsrv:decl_type'];
    $columnInfo = ['source' => ColumnInfoSource::QUERY_RESULT];
    if (str_ends_with($dbType, ' identity')) {
        $columnInfo['autoIncrement'] = true;
        $dbType = substr($dbType, 0, -9);
    }
    if (!empty($metadata['table'])) {
        $columnInfo['table'] = $metadata['table'];
        $columnInfo['name'] = $metadata['name'];
    } elseif (!empty($metadata['name'])) {
        $columnInfo['name'] = $metadata['name'];
    }
    if (!empty($metadata['len'])) {
        $columnInfo['size'] = match ($dbType) {
            'time', 'datetime', 'datetime2', 'datetimeoffset' => $metadata['precision'],
            default => $metadata['len'],
        };
    }
    match ($dbType) {
        'decimal', 'numeric' => $columnInfo['scale'] = $metadata['precision'],
        default => null,
    };
    return $this->db->getColumnFactory()->fromDbType($dbType, $columnInfo);
}

            
loadTableChecks() protected method

protected array loadTableChecks ( string $tableName )
$tableName string

                protected function loadTableChecks(string $tableName): array
{
    /** @var Check[] */
    return $this->loadTableConstraints($tableName, self::CHECKS);
}

            
loadTableDefaultValues() protected method

protected array loadTableDefaultValues ( string $tableName )
$tableName string

                protected function loadTableDefaultValues(string $tableName): array
{
    /** @var DefaultValue[] */
    return $this->loadTableConstraints($tableName, self::DEFAULTS);
}

            
loadTableForeignKeys() protected method

protected array loadTableForeignKeys ( string $tableName )
$tableName string

                protected function loadTableForeignKeys(string $tableName): array
{
    /** @var ForeignKey[] */
    return $this->loadTableConstraints($tableName, self::FOREIGN_KEYS);
}

            
loadTableIndexes() protected method

protected array loadTableIndexes ( string $tableName )
$tableName string

                protected function loadTableIndexes(string $tableName): array
{
    $sql = <<<SQL
    SELECT
        [i].[name] AS [name],
        [iccol].[name] AS [column_name],
        [i].[is_unique],
        [i].[is_primary_key]
    FROM [sys].[indexes] AS [i]
    INNER JOIN [sys].[index_columns] AS [ic]
        ON [ic].[object_id] = [i].[object_id] AND [ic].[index_id] = [i].[index_id]
    INNER JOIN [sys].[columns] AS [iccol]
        ON [iccol].[object_id] = [ic].[object_id] AND [iccol].[column_id] = [ic].[column_id]
    WHERE [i].[object_id] = OBJECT_ID(:fullName)
    ORDER BY [ic].[key_ordinal] ASC
    SQL;
    $indexes = $this->db->createCommand($sql, [':fullName' => $tableName])->queryAll();
    /** @psalm-var list<array<string,mixed>> $indexes */
    $indexes = array_map(array_change_key_case(...), $indexes);
    $indexes = DbArrayHelper::arrange($indexes, ['name']);
    $result = [];
    /**
     * @psalm-var array<
     *   string,
     *   list<array{name: string, column_name: string, is_unique: string, is_primary_key: string}>
     * > $indexes
     */
    foreach ($indexes as $name => $index) {
        $result[$name] = new Index(
            $name,
            array_column($index, 'column_name'),
            (bool) $index[0]['is_unique'],
            (bool) $index[0]['is_primary_key'],
        );
    }
    return $result;
}

            
loadTableSchema() protected method

protected \Yiisoft\Db\Schema\TableSchemaInterface|null loadTableSchema ( string $name )
$name string

                protected function loadTableSchema(string $name): ?TableSchemaInterface
{
    /** @psalm-suppress MixedArgument */
    $table = new TableSchema(...$this->db->getQuoter()->getTableNameParts($name));
    if ($this->findColumns($table)) {
        $this->findTableComment($table);
        $this->findConstraints($table);
        return $table;
    }
    return null;
}

            
resolveFullName() protected method

protected string resolveFullName ( string $name, string $schemaName '', string $catalogName '', string $serverName '' )
$name string
$schemaName string
$catalogName string
$serverName string

                protected function resolveFullName(
    string $name,
    string $schemaName = '',
    string $catalogName = '',
    string $serverName = '',
): string {
    $quoter = $this->db->getQuoter();
    $fullName = $quoter->getRawTableName($name);
    if ($schemaName === '' || $schemaName === $this->defaultSchema) {
        return $fullName;
    }
    $fullName = $quoter->getRawTableName($schemaName) . ".$fullName";
    if ($catalogName === '') {
        return $fullName;
    }
    $fullName = $quoter->getRawTableName($catalogName) . ".$fullName";
    if ($serverName === '') {
        return $fullName;
    }
    return $quoter->getRawTableName($serverName) . ".$fullName";
}