Passed
Push — master ( 9b125d...f5b0c0 )
by Alexander
11:25 queued 07:35
created

DDLQueryBuilder::checkIntegrity()   A

Complexity

Conditions 5
Paths 8

Size

Total Lines 20
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 12
c 1
b 0
f 0
nc 8
nop 3
dl 0
loc 20
ccs 13
cts 13
cp 1
crap 5
rs 9.5555
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\Mssql;
6
7
use Exception;
8
use Throwable;
9
use Yiisoft\Db\Exception\InvalidArgumentException;
10
use Yiisoft\Db\Exception\InvalidConfigException;
11
use Yiisoft\Db\Exception\NotSupportedException;
12
use Yiisoft\Db\QueryBuilder\DDLQueryBuilder as AbstractDDLQueryBuilder;
13
use Yiisoft\Db\QueryBuilder\QueryBuilderInterface;
14
use Yiisoft\Db\Schema\ColumnSchemaBuilder;
15
use Yiisoft\Db\Schema\QuoterInterface;
16
use Yiisoft\Db\Schema\SchemaInterface;
17
18
use function array_diff;
19
20
final class DDLQueryBuilder extends AbstractDDLQueryBuilder
21
{
22 386
    public function __construct(
23
        private QueryBuilderInterface $queryBuilder,
24
        private QuoterInterface $quoter,
25
        private SchemaInterface $schema
26
    ) {
27 386
        parent::__construct($queryBuilder, $quoter, $schema);
28
    }
29
30
    /**
31
     * @throws InvalidArgumentException
32
     */
33 4
    public function addCommentOnColumn(string $table, string $column, string $comment): string
34
    {
35 4
        return $this->buildAddCommentSql($comment, $table, $column);
36
    }
37
38
    /**
39
     * @throws InvalidArgumentException
40
     */
41 4
    public function addCommentOnTable(string $table, string $comment): string
42
    {
43 4
        return $this->buildAddCommentSql($comment, $table);
44
    }
45
46
    /**
47
     * @throws Exception
48
     */
49 1
    public function addDefaultValue(string $name, string $table, string $column, mixed $value): string
50
    {
51
        return 'ALTER TABLE '
52 1
            . $this->quoter->quoteTableName($table)
53
            . ' ADD CONSTRAINT '
54 1
            . $this->quoter->quoteColumnName($name)
55 1
            . ' DEFAULT ' . (string) $this->quoter->quoteValue($value)
56 1
            . ' FOR ' . $this->quoter->quoteColumnName($column);
57
    }
58
59 1
    public function alterColumn(string $table, string $column, ColumnSchemaBuilder|string $type): string
60
    {
61
        return 'ALTER TABLE '
62 1
            . $this->quoter->quoteTableName($table)
63
            . ' ALTER COLUMN '
64 1
            . $this->quoter->quoteColumnName($column)
65
            . ' '
66 1
            . $this->queryBuilder->getColumnType($type);
67
    }
68
69
    /**
70
     * @throws InvalidConfigException|NotSupportedException|Throwable|\Yiisoft\Db\Exception\Exception
71
     */
72 2
    public function checkIntegrity(string $schema = '', string $table = '', bool $check = true): string
73
    {
74 2
        $enable = $check ? 'CHECK' : 'NOCHECK';
75
76
        /** @var Schema */
77 2
        $schemaInstance = $this->schema;
78 2
        $defaultSchema = $schema ?: $schemaInstance->getDefaultSchema() ?? '';
79
        /** @psalm-var string[] */
80 2
        $tableNames = $schemaInstance->getTableSchema($table)
81 2
             ? [$table] : $schemaInstance->getTableNames($defaultSchema);
82 2
        $viewNames = $schemaInstance->getViewNames($defaultSchema);
83 2
        $tableNames = array_diff($tableNames, $viewNames);
84 2
        $command = '';
85
86 2
        foreach ($tableNames as $tableName) {
87 2
            $tableName = $this->quoter->quoteTableName("$defaultSchema.$tableName");
88 2
            $command .= "ALTER TABLE $tableName $enable CONSTRAINT ALL; ";
89
        }
90
91 2
        return $command;
92
    }
93
94
    /**
95
     * @throws InvalidArgumentException
96
     */
97 2
    public function dropCommentFromColumn(string $table, string $column): string
98
    {
99 2
        return $this->buildRemoveCommentSql($table, $column);
100
    }
101
102
    /**
103
     * @throws InvalidArgumentException
104
     */
105 2
    public function dropCommentFromTable(string $table): string
106
    {
107 2
        return $this->buildRemoveCommentSql($table);
108
    }
109
110 1
    public function dropDefaultValue(string $name, string $table): string
111
    {
112
        return 'ALTER TABLE '
113 1
            . $this->quoter->quoteTableName($table)
114
            . ' DROP CONSTRAINT '
115 1
            . $this->quoter->quoteColumnName($name);
116
    }
117
118 2
    public function renameTable(string $oldName, string $newName): string
119
    {
120
        return 'sp_rename '
121 2
            . $this->quoter->quoteTableName($oldName) . ', '
122 2
            . $this->quoter->quoteTableName($newName);
123
    }
124
125 1
    public function renameColumn(string $table, string $oldName, string $newName): string
126
    {
127
        return 'sp_rename '
128 1
            . $this->quoter->quoteTableName($table) . '.'
129 1
            . $this->quoter->quoteColumnName($oldName) . ', '
130 1
            . $this->quoter->quoteColumnName($newName)
131
            . ' COLUMN';
132
    }
133
134
    /**
135
     * Builds a SQL command for adding or updating a comment to a table or a column. The command built will check if a
136
     * comment already exists. If so, it will be updated, otherwise, it will be added.
137
     *
138
     * @param string $comment the text of the comment to be added. The comment will be properly quoted by the method.
139
     * @param string $table the table to be commented or whose column is to be commented. The table name will be
140
     * properly quoted by the method.
141
     * @param string|null $column optional. The name of the column to be commented. If empty, the command will add the
142
     * comment to the table instead. The column name will be properly quoted by the method.
143
     *
144
     * @throws Exception|InvalidArgumentException if the table does not exist.
145
     *
146
     * @return string the SQL statement for adding a comment.
147
     */
148 5
    private function buildAddCommentSql(string $comment, string $table, ?string $column = null): string
149
    {
150 5
        $tableSchema = $this->schema->getTableSchema($table);
151
152 5
        if ($tableSchema === null) {
153 1
            throw new InvalidArgumentException("Table not found: $table");
154
        }
155
156 4
        $schemaName = $tableSchema->getSchemaName()
157 4
            ? "N'" . (string) $tableSchema->getSchemaName() . "'" : 'SCHEMA_NAME()';
158 4
        $tableName = 'N' . (string) $this->quoter->quoteValue($tableSchema->getName());
159 4
        $columnName = $column ? 'N' . (string) $this->quoter->quoteValue($column) : null;
160 4
        $comment = 'N' . (string) $this->quoter->quoteValue($comment);
161 4
        $functionParams = "
162
            @name = N'MS_description',
163
            @value = $comment,
164
            @level0type = N'SCHEMA', @level0name = $schemaName,
165
            @level1type = N'TABLE', @level1name = $tableName"
166 4
            . ($column ? ", @level2type = N'COLUMN', @level2name = $columnName" : '') . ';';
167
168 4
        return "
169
            IF NOT EXISTS (
170
                    SELECT 1
171
                    FROM fn_listextendedproperty (
172
                        N'MS_description',
173
                        'SCHEMA', $schemaName,
174
                        'TABLE', $tableName,
175 4
                        " . ($column ? "'COLUMN', $columnName " : ' DEFAULT, DEFAULT ') . "
176
                    )
177
            )
178
                EXEC sys.sp_addextendedproperty $functionParams
179
            ELSE
180
                EXEC sys.sp_updateextendedproperty $functionParams
181
        ";
182
    }
183
184
    /**
185
     * Builds a SQL command for removing a comment from a table or a column. The command built will check if a comment
186
     * already exists before trying to perform the removal.
187
     *
188
     * @param string $table the table that will have the comment removed or whose column will have the comment removed.
189
     * The table name will be properly quoted by the method.
190
     * @param string|null $column optional. The name of the column whose comment will be removed. If empty, the command
191
     * will remove the comment from the table instead. The column name will be properly quoted by the method.
192
     *
193
     * @throws Exception|InvalidArgumentException if the table does not exist.
194
     *
195
     * @return string the SQL statement for removing the comment.
196
     */
197 3
    private function buildRemoveCommentSql(string $table, ?string $column = null): string
198
    {
199 3
        $tableSchema = $this->schema->getTableSchema($table);
200
201 3
        if ($tableSchema === null) {
202 1
            throw new InvalidArgumentException("Table not found: $table");
203
        }
204
205 2
        $schemaName = $tableSchema->getSchemaName()
206 2
            ? "N'" . (string) $tableSchema->getSchemaName() . "'" : 'SCHEMA_NAME()';
207 2
        $tableName = 'N' . (string) $this->quoter->quoteValue($tableSchema->getName());
208 2
        $columnName = $column ? 'N' . (string) $this->quoter->quoteValue($column) : null;
209
210 2
        return "
211
            IF EXISTS (
212
                    SELECT 1
213
                    FROM fn_listextendedproperty (
214
                        N'MS_description',
215
                        'SCHEMA', $schemaName,
216
                        'TABLE', $tableName,
217 2
                        " . ($column ? "'COLUMN', $columnName " : ' DEFAULT, DEFAULT ') . "
218
                    )
219
            )
220
                EXEC sys.sp_dropextendedproperty
221
                    @name = N'MS_description',
222
                    @level0type = N'SCHEMA', @level0name = $schemaName,
223
                    @level1type = N'TABLE', @level1name = $tableName"
224 2
                    . ($column ? ", @level2type = N'COLUMN', @level2name = $columnName" : '') . ';';
225
    }
226
}
227