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
|
|
|
|