1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace Yiisoft\Db\Mssql; |
6
|
|
|
|
7
|
|
|
use Throwable; |
8
|
|
|
use Yiisoft\Db\Constraint\CheckConstraint; |
9
|
|
|
use Yiisoft\Db\Constraint\Constraint; |
10
|
|
|
use Yiisoft\Db\Constraint\DefaultValueConstraint; |
11
|
|
|
use Yiisoft\Db\Constraint\ForeignKeyConstraint; |
12
|
|
|
use Yiisoft\Db\Constraint\IndexConstraint; |
13
|
|
|
use Yiisoft\Db\Driver\Pdo\AbstractPdoSchema; |
14
|
|
|
use Yiisoft\Db\Exception\Exception; |
15
|
|
|
use Yiisoft\Db\Exception\InvalidConfigException; |
16
|
|
|
use Yiisoft\Db\Helper\DbArrayHelper; |
17
|
|
|
use Yiisoft\Db\Schema\Builder\ColumnInterface; |
18
|
|
|
use Yiisoft\Db\Schema\ColumnSchemaInterface; |
19
|
|
|
use Yiisoft\Db\Schema\TableSchemaInterface; |
20
|
|
|
|
21
|
|
|
use function array_map; |
22
|
|
|
use function explode; |
23
|
|
|
use function is_array; |
24
|
|
|
use function md5; |
25
|
|
|
use function preg_match; |
26
|
|
|
use function serialize; |
27
|
|
|
use function str_replace; |
28
|
|
|
use function strcasecmp; |
29
|
|
|
use function stripos; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* Implements the MSSQL Server specific schema, supporting MSSQL Server 2017 and above. |
33
|
|
|
* |
34
|
|
|
* @psalm-type ColumnArray = array{ |
35
|
|
|
* column_name: string, |
36
|
|
|
* is_nullable: string, |
37
|
|
|
* data_type: string, |
38
|
|
|
* column_default: string|null, |
39
|
|
|
* is_identity: string, |
40
|
|
|
* is_computed: string, |
41
|
|
|
* comment: null|string |
42
|
|
|
* } |
43
|
|
|
* @psalm-type ConstraintArray = array< |
44
|
|
|
* array-key, |
45
|
|
|
* array { |
46
|
|
|
* name: string, |
47
|
|
|
* column_name: string, |
48
|
|
|
* type: string, |
49
|
|
|
* foreign_table_schema: string|null, |
50
|
|
|
* foreign_table_name: string|null, |
51
|
|
|
* foreign_column_name: string|null, |
52
|
|
|
* on_update: string, |
53
|
|
|
* on_delete: string, |
54
|
|
|
* check_expr: string, |
55
|
|
|
* default_expr: string |
56
|
|
|
* } |
57
|
|
|
* > |
58
|
|
|
*/ |
59
|
|
|
final class Schema extends AbstractPdoSchema |
60
|
|
|
{ |
61
|
|
|
/** |
62
|
|
|
* @var string|null The default schema used for the current session. |
63
|
|
|
*/ |
64
|
|
|
protected string|null $defaultSchema = 'dbo'; |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* Mapping from physical column types (keys) to abstract column types (values). |
68
|
|
|
* |
69
|
|
|
* @var string[] |
70
|
|
|
*/ |
71
|
|
|
private const TYPE_MAP = [ |
72
|
|
|
/** Exact numbers */ |
73
|
|
|
'bigint' => self::TYPE_BIGINT, |
74
|
|
|
'numeric' => self::TYPE_DECIMAL, |
75
|
|
|
'bit' => self::TYPE_SMALLINT, |
76
|
|
|
'smallint' => self::TYPE_SMALLINT, |
77
|
|
|
'decimal' => self::TYPE_DECIMAL, |
78
|
|
|
'smallmoney' => self::TYPE_MONEY, |
79
|
|
|
'int' => self::TYPE_INTEGER, |
80
|
|
|
'tinyint' => self::TYPE_TINYINT, |
81
|
|
|
'money' => self::TYPE_MONEY, |
82
|
|
|
|
83
|
|
|
/** Approximate numbers */ |
84
|
|
|
'float' => self::TYPE_FLOAT, |
85
|
|
|
'double' => self::TYPE_DOUBLE, |
86
|
|
|
'real' => self::TYPE_FLOAT, |
87
|
|
|
|
88
|
|
|
/** Date and time */ |
89
|
|
|
'date' => self::TYPE_DATE, |
90
|
|
|
'datetimeoffset' => self::TYPE_DATETIME, |
91
|
|
|
'datetime2' => self::TYPE_DATETIME, |
92
|
|
|
'smalldatetime' => self::TYPE_DATETIME, |
93
|
|
|
'datetime' => self::TYPE_DATETIME, |
94
|
|
|
'time' => self::TYPE_TIME, |
95
|
|
|
|
96
|
|
|
/** Character strings */ |
97
|
|
|
'char' => self::TYPE_CHAR, |
98
|
|
|
'varchar' => self::TYPE_STRING, |
99
|
|
|
'text' => self::TYPE_TEXT, |
100
|
|
|
|
101
|
|
|
/** Unicode character strings */ |
102
|
|
|
'nchar' => self::TYPE_CHAR, |
103
|
|
|
'nvarchar' => self::TYPE_STRING, |
104
|
|
|
'ntext' => self::TYPE_TEXT, |
105
|
|
|
|
106
|
|
|
/** Binary strings */ |
107
|
|
|
'binary' => self::TYPE_BINARY, |
108
|
|
|
'varbinary' => self::TYPE_BINARY, |
109
|
|
|
'image' => self::TYPE_BINARY, |
110
|
|
|
|
111
|
|
|
/** |
112
|
|
|
* Other data types 'cursor' type can't be used with tables |
113
|
|
|
*/ |
114
|
|
|
'timestamp' => self::TYPE_TIMESTAMP, |
115
|
|
|
'hierarchyid' => self::TYPE_STRING, |
116
|
|
|
'uniqueidentifier' => self::TYPE_STRING, |
117
|
|
|
'sql_variant' => self::TYPE_STRING, |
118
|
|
|
'xml' => self::TYPE_STRING, |
119
|
|
|
'table' => self::TYPE_STRING, |
120
|
|
|
]; |
121
|
|
|
|
122
|
14 |
|
public function createColumn(string $type, array|int|string|null $length = null): ColumnInterface |
123
|
|
|
{ |
124
|
14 |
|
return new Column($type, $length); |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
/** |
128
|
|
|
* Resolves the table name and schema name (if any). |
129
|
|
|
* |
130
|
|
|
* @param string $name The table name. |
131
|
|
|
* |
132
|
|
|
* @return TableSchemaInterface The resolved table name. |
133
|
|
|
*/ |
134
|
577 |
|
protected function resolveTableName(string $name): TableSchemaInterface |
135
|
|
|
{ |
136
|
577 |
|
$resolvedName = new TableSchema(); |
137
|
|
|
|
138
|
577 |
|
$parts = array_reverse( |
139
|
577 |
|
$this->db->getQuoter()->getTableNameParts($name) |
140
|
577 |
|
); |
141
|
|
|
|
142
|
577 |
|
$resolvedName->name($parts[0] ?? ''); |
143
|
577 |
|
$resolvedName->schemaName($parts[1] ?? $this->defaultSchema); |
144
|
577 |
|
$resolvedName->catalogName($parts[2] ?? null); |
145
|
577 |
|
$resolvedName->serverName($parts[3] ?? null); |
146
|
|
|
|
147
|
577 |
|
if (empty($parts[2]) && $resolvedName->getSchemaName() === $this->defaultSchema) { |
148
|
571 |
|
$resolvedName->fullName($parts[0]); |
149
|
|
|
} else { |
150
|
7 |
|
$resolvedName->fullName(implode('.', array_reverse($parts))); |
151
|
|
|
} |
152
|
|
|
|
153
|
577 |
|
return $resolvedName; |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
/** |
157
|
|
|
* Returns all schema names in the database, including the default one but not system schemas. |
158
|
|
|
* |
159
|
|
|
* This method should be overridden by child classes to support this feature because the default implementation |
160
|
|
|
* simply throws an exception. |
161
|
|
|
* |
162
|
|
|
* @throws Exception |
163
|
|
|
* @throws InvalidConfigException |
164
|
|
|
* @throws Throwable |
165
|
|
|
* |
166
|
|
|
* @return array Schema names in the database, except system schemas. |
167
|
|
|
* |
168
|
|
|
* @link https://docs.microsoft.com/en-us/sql/relational-databases/system-catalog-views/sys-database-principals-transact-sql |
169
|
|
|
*/ |
170
|
1 |
|
protected function findSchemaNames(): array |
171
|
|
|
{ |
172
|
1 |
|
$sql = <<<SQL |
173
|
|
|
SELECT [s].[name] |
174
|
|
|
FROM [sys].[schemas] AS [s] |
175
|
|
|
INNER JOIN [sys].[database_principals] AS [p] ON [p].[principal_id] = [s].[principal_id] |
176
|
|
|
WHERE [p].[is_fixed_role] = 0 AND [p].[sid] IS NOT NULL |
177
|
|
|
ORDER BY [s].[name] ASC |
178
|
1 |
|
SQL; |
179
|
|
|
|
180
|
1 |
|
return $this->db->createCommand($sql)->queryColumn(); |
181
|
|
|
} |
182
|
|
|
|
183
|
|
|
/** |
184
|
|
|
* @throws Exception |
185
|
|
|
* @throws InvalidConfigException |
186
|
|
|
* @throws Throwable |
187
|
|
|
*/ |
188
|
500 |
|
protected function findTableComment(TableSchemaInterface $tableSchema): void |
189
|
|
|
{ |
190
|
500 |
|
$schemaName = $tableSchema->getSchemaName() |
191
|
500 |
|
? "N'" . (string) $tableSchema->getSchemaName() . "'" : 'SCHEMA_NAME()'; |
192
|
500 |
|
$tableName = 'N' . (string) $this->db->getQuoter()->quoteValue($tableSchema->getName()); |
193
|
|
|
|
194
|
500 |
|
$sql = <<<SQL |
195
|
500 |
|
SELECT [value] |
196
|
|
|
FROM fn_listextendedproperty ( |
197
|
|
|
N'MS_description', |
198
|
500 |
|
'SCHEMA', $schemaName, |
199
|
500 |
|
'TABLE', $tableName, |
200
|
|
|
DEFAULT, DEFAULT) |
201
|
500 |
|
SQL; |
202
|
|
|
|
203
|
500 |
|
$comment = $this->db->createCommand($sql)->queryScalar(); |
204
|
|
|
|
205
|
500 |
|
$tableSchema->comment(is_string($comment) ? $comment : null); |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
/** |
209
|
|
|
* Returns all table names in the database. |
210
|
|
|
* |
211
|
|
|
* This method should be overridden by child classes to support this feature because the default implementation |
212
|
|
|
* simply throws an exception. |
213
|
|
|
* |
214
|
|
|
* @param string $schema The schema of the tables. |
215
|
|
|
* Defaults to empty string, meaning the current or default schema. |
216
|
|
|
* |
217
|
|
|
* @throws Exception |
218
|
|
|
* @throws InvalidConfigException |
219
|
|
|
* @throws Throwable |
220
|
|
|
* |
221
|
|
|
* @return array All tables name in the database. The names have NO schema name prefix. |
222
|
|
|
*/ |
223
|
11 |
|
protected function findTableNames(string $schema = ''): array |
224
|
|
|
{ |
225
|
11 |
|
if ($schema === '') { |
226
|
11 |
|
$schema = $this->defaultSchema; |
227
|
|
|
} |
228
|
|
|
|
229
|
11 |
|
$sql = <<<SQL |
230
|
|
|
SELECT [t].[table_name] |
231
|
|
|
FROM [INFORMATION_SCHEMA].[TABLES] AS [t] |
232
|
|
|
WHERE [t].[table_schema] = :schema AND [t].[table_type] IN ('BASE TABLE', 'VIEW') |
233
|
|
|
ORDER BY [t].[table_name] |
234
|
11 |
|
SQL; |
235
|
|
|
|
236
|
11 |
|
return $this->db->createCommand($sql, [':schema' => $schema])->queryColumn(); |
237
|
|
|
} |
238
|
|
|
|
239
|
|
|
/** |
240
|
|
|
* Loads the metadata for the specified table. |
241
|
|
|
* |
242
|
|
|
* @param string $name The table name. |
243
|
|
|
* |
244
|
|
|
* @throws Exception |
245
|
|
|
* @throws InvalidConfigException |
246
|
|
|
* @throws Throwable |
247
|
|
|
* |
248
|
|
|
* @return TableSchemaInterface|null DBMS-dependent table metadata, `null` if the table doesn't exist. |
249
|
|
|
*/ |
250
|
500 |
|
protected function loadTableSchema(string $name): TableSchemaInterface|null |
251
|
|
|
{ |
252
|
500 |
|
$table = $this->resolveTableName($name); |
253
|
500 |
|
$this->findPrimaryKeys($table); |
254
|
500 |
|
$this->findTableComment($table); |
255
|
|
|
|
256
|
500 |
|
if ($this->findColumns($table)) { |
257
|
447 |
|
$this->findForeignKeys($table); |
258
|
447 |
|
return $table; |
259
|
|
|
} |
260
|
|
|
|
261
|
174 |
|
return null; |
262
|
|
|
} |
263
|
|
|
|
264
|
|
|
/** |
265
|
|
|
* Loads a primary key for the given table. |
266
|
|
|
* |
267
|
|
|
* @param string $tableName The table name. |
268
|
|
|
* |
269
|
|
|
* @throws Exception |
270
|
|
|
* @throws InvalidConfigException |
271
|
|
|
* @throws Throwable |
272
|
|
|
* |
273
|
|
|
* @return Constraint|null The primary key for the given table, `null` if the table has no primary key. |
274
|
|
|
*/ |
275
|
51 |
|
protected function loadTablePrimaryKey(string $tableName): Constraint|null |
276
|
|
|
{ |
277
|
|
|
/** @psalm-var mixed $tablePrimaryKey */ |
278
|
51 |
|
$tablePrimaryKey = $this->loadTableConstraints($tableName, self::PRIMARY_KEY); |
279
|
51 |
|
return $tablePrimaryKey instanceof Constraint ? $tablePrimaryKey : null; |
280
|
|
|
} |
281
|
|
|
|
282
|
|
|
/** |
283
|
|
|
* Loads all foreign keys for the given table. |
284
|
|
|
* |
285
|
|
|
* @param string $tableName The table name. |
286
|
|
|
* |
287
|
|
|
* @throws Exception |
288
|
|
|
* @throws InvalidConfigException |
289
|
|
|
* @throws Throwable |
290
|
|
|
* |
291
|
|
|
* @return array The foreign keys for the given table. |
292
|
|
|
*/ |
293
|
8 |
|
protected function loadTableForeignKeys(string $tableName): array |
294
|
|
|
{ |
295
|
|
|
/** @psalm-var mixed $tableForeignKeys */ |
296
|
8 |
|
$tableForeignKeys = $this->loadTableConstraints($tableName, self::FOREIGN_KEYS); |
297
|
8 |
|
return is_array($tableForeignKeys) ? $tableForeignKeys : []; |
298
|
|
|
} |
299
|
|
|
|
300
|
|
|
/** |
301
|
|
|
* Loads all indexes for the given table. |
302
|
|
|
* |
303
|
|
|
* @param string $tableName The table name. |
304
|
|
|
* |
305
|
|
|
* @throws Exception |
306
|
|
|
* @throws InvalidConfigException |
307
|
|
|
* @throws Throwable |
308
|
|
|
* |
309
|
|
|
* @return IndexConstraint[] Indexes for the given table. |
310
|
|
|
*/ |
311
|
40 |
|
protected function loadTableIndexes(string $tableName): array |
312
|
|
|
{ |
313
|
40 |
|
$sql = <<<SQL |
314
|
|
|
SELECT |
315
|
|
|
[i].[name] AS [name], |
316
|
|
|
[iccol].[name] AS [column_name], |
317
|
|
|
[i].[is_unique] AS [index_is_unique], |
318
|
|
|
[i].[is_primary_key] AS [index_is_primary] |
319
|
|
|
FROM [sys].[indexes] AS [i] |
320
|
|
|
INNER JOIN [sys].[index_columns] AS [ic] |
321
|
|
|
ON [ic].[object_id] = [i].[object_id] AND [ic].[index_id] = [i].[index_id] |
322
|
|
|
INNER JOIN [sys].[columns] AS [iccol] |
323
|
|
|
ON [iccol].[object_id] = [ic].[object_id] AND [iccol].[column_id] = [ic].[column_id] |
324
|
|
|
WHERE [i].[object_id] = OBJECT_ID(:fullName) |
325
|
|
|
ORDER BY [ic].[key_ordinal] ASC |
326
|
40 |
|
SQL; |
327
|
|
|
|
328
|
40 |
|
$resolvedName = $this->resolveTableName($tableName); |
329
|
40 |
|
$indexes = $this->db->createCommand($sql, [':fullName' => $resolvedName->getFullName()])->queryAll(); |
330
|
|
|
|
331
|
|
|
/** @psalm-var array[] $indexes */ |
332
|
40 |
|
$indexes = array_map('array_change_key_case', $indexes); |
333
|
40 |
|
$indexes = DbArrayHelper::index($indexes, null, ['name']); |
334
|
|
|
|
335
|
40 |
|
$result = []; |
336
|
|
|
|
337
|
|
|
/** |
338
|
|
|
* @psalm-var array< |
339
|
|
|
* string, |
340
|
|
|
* array< |
341
|
|
|
* array-key, |
342
|
|
|
* array{name: string, column_name: string, index_is_unique: string, index_is_primary: string} |
343
|
|
|
* > |
344
|
|
|
* > $indexes |
345
|
|
|
*/ |
346
|
40 |
|
foreach ($indexes as $name => $index) { |
347
|
37 |
|
$result[] = (new IndexConstraint()) |
348
|
37 |
|
->primary((bool) $index[0]['index_is_primary']) |
349
|
37 |
|
->unique((bool) $index[0]['index_is_unique']) |
350
|
37 |
|
->columnNames(DbArrayHelper::getColumn($index, 'column_name')) |
351
|
37 |
|
->name($name); |
352
|
|
|
} |
353
|
|
|
|
354
|
40 |
|
return $result; |
355
|
|
|
} |
356
|
|
|
|
357
|
|
|
/** |
358
|
|
|
* Loads all unique constraints for the given table. |
359
|
|
|
* |
360
|
|
|
* @param string $tableName The table name. |
361
|
|
|
* |
362
|
|
|
* @throws Exception|InvalidConfigException|Throwable |
363
|
|
|
* |
364
|
|
|
* @return array The unique constraints for the given table. |
365
|
|
|
*/ |
366
|
17 |
|
protected function loadTableUniques(string $tableName): array |
367
|
|
|
{ |
368
|
|
|
/** @psalm-var mixed $tableUniques */ |
369
|
17 |
|
$tableUniques = $this->loadTableConstraints($tableName, self::UNIQUES); |
370
|
17 |
|
return is_array($tableUniques) ? $tableUniques : []; |
371
|
|
|
} |
372
|
|
|
|
373
|
|
|
/** |
374
|
|
|
* Loads all check constraints for the given table. |
375
|
|
|
* |
376
|
|
|
* @param string $tableName The table name. |
377
|
|
|
* |
378
|
|
|
* @throws Exception |
379
|
|
|
* @throws InvalidConfigException |
380
|
|
|
* @throws Throwable |
381
|
|
|
* |
382
|
|
|
* @return array The check constraints for the given table. |
383
|
|
|
*/ |
384
|
17 |
|
protected function loadTableChecks(string $tableName): array |
385
|
|
|
{ |
386
|
|
|
/** @psalm-var mixed $tableCheck */ |
387
|
17 |
|
$tableCheck = $this->loadTableConstraints($tableName, self::CHECKS); |
388
|
17 |
|
return is_array($tableCheck) ? $tableCheck : []; |
389
|
|
|
} |
390
|
|
|
|
391
|
|
|
/** |
392
|
|
|
* Loads all default value constraints for the given table. |
393
|
|
|
* |
394
|
|
|
* @param string $tableName The table name. |
395
|
|
|
* |
396
|
|
|
* @throws Exception |
397
|
|
|
* @throws InvalidConfigException |
398
|
|
|
* @throws Throwable |
399
|
|
|
* |
400
|
|
|
* @return array The default value constraints for the given table. |
401
|
|
|
*/ |
402
|
16 |
|
protected function loadTableDefaultValues(string $tableName): array |
403
|
|
|
{ |
404
|
|
|
/** @psalm-var mixed $tableDefault */ |
405
|
16 |
|
$tableDefault = $this->loadTableConstraints($tableName, self::DEFAULTS); |
406
|
16 |
|
return is_array($tableDefault) ? $tableDefault : []; |
407
|
|
|
} |
408
|
|
|
|
409
|
|
|
/** |
410
|
|
|
* Creates a column schema for the database. |
411
|
|
|
* |
412
|
|
|
* This method may be overridden by child classes to create a DBMS-specific column schema. |
413
|
|
|
* |
414
|
|
|
* @param string $name Name of the column. |
415
|
|
|
*/ |
416
|
447 |
|
protected function createColumnSchema(string $name): ColumnSchema |
417
|
|
|
{ |
418
|
447 |
|
return new ColumnSchema($name); |
419
|
|
|
} |
420
|
|
|
|
421
|
|
|
/** |
422
|
|
|
* Loads the column information into a {@see ColumnSchemaInterface} object. |
423
|
|
|
* |
424
|
|
|
* @psalm-param ColumnArray $info The column information. |
425
|
|
|
*/ |
426
|
447 |
|
protected function loadColumnSchema(array $info): ColumnSchemaInterface |
427
|
|
|
{ |
428
|
447 |
|
$dbType = $info['data_type']; |
429
|
|
|
|
430
|
447 |
|
$column = $this->createColumnSchema($info['column_name']); |
431
|
447 |
|
$column->allowNull($info['is_nullable'] === 'YES'); |
432
|
447 |
|
$column->dbType($dbType); |
433
|
447 |
|
$column->enumValues([]); // MSSQL has only vague equivalents to enum. |
434
|
447 |
|
$column->primaryKey(false); // The primary key will be determined in the `findColumns()` method. |
435
|
447 |
|
$column->autoIncrement($info['is_identity'] === '1'); |
436
|
447 |
|
$column->computed($info['is_computed'] === '1'); |
437
|
447 |
|
$column->unsigned(stripos($dbType, 'unsigned') !== false); |
438
|
447 |
|
$column->comment($info['comment'] ?? ''); |
439
|
447 |
|
$column->type(self::TYPE_STRING); |
440
|
|
|
|
441
|
447 |
|
if (preg_match('/^(\w+)(?:\(([^)]+)\))?/', $dbType, $matches)) { |
442
|
447 |
|
$type = $matches[1]; |
443
|
|
|
|
444
|
447 |
|
if (isset(self::TYPE_MAP[$type])) { |
445
|
447 |
|
$column->type(self::TYPE_MAP[$type]); |
446
|
|
|
} |
447
|
|
|
|
448
|
447 |
|
if ($type === 'bit') { |
449
|
47 |
|
$column->type(self::TYPE_BOOLEAN); |
450
|
|
|
} |
451
|
|
|
|
452
|
447 |
|
if (!empty($matches[2])) { |
453
|
315 |
|
$values = explode(',', $matches[2]); |
454
|
315 |
|
$column->precision((int) $values[0]); |
455
|
315 |
|
$column->size((int) $values[0]); |
456
|
|
|
|
457
|
315 |
|
if (isset($values[1])) { |
458
|
99 |
|
$column->scale((int) $values[1]); |
459
|
|
|
} |
460
|
|
|
} |
461
|
|
|
} |
462
|
|
|
|
463
|
447 |
|
$column->phpType($this->getColumnPhpType($column)); |
464
|
447 |
|
$column->defaultValue($this->normalizeDefaultValue($info['column_default'], $column)); |
465
|
|
|
|
466
|
447 |
|
return $column; |
467
|
|
|
} |
468
|
|
|
|
469
|
|
|
/** |
470
|
|
|
* Converts column's default value according to {@see ColumnSchema::phpType} after retrieval from the database. |
471
|
|
|
* |
472
|
|
|
* @param string|null $defaultValue The default value retrieved from the database. |
473
|
|
|
* @param ColumnSchemaInterface $column The column schema object. |
474
|
|
|
* |
475
|
|
|
* @return mixed The normalized default value. |
476
|
|
|
*/ |
477
|
447 |
|
private function normalizeDefaultValue(?string $defaultValue, ColumnSchemaInterface $column): mixed |
478
|
|
|
{ |
479
|
|
|
if ( |
480
|
447 |
|
$defaultValue === null |
481
|
327 |
|
|| $defaultValue === '(NULL)' |
482
|
322 |
|
|| $column->isPrimaryKey() |
483
|
447 |
|
|| $column->isComputed() |
484
|
|
|
) { |
485
|
442 |
|
return null; |
486
|
|
|
} |
487
|
|
|
|
488
|
322 |
|
$value = $this->parseDefaultValue($defaultValue); |
489
|
|
|
|
490
|
322 |
|
return is_numeric($value) |
491
|
122 |
|
? $column->phpTypeCast($value) |
492
|
322 |
|
: $value; |
493
|
|
|
} |
494
|
|
|
|
495
|
|
|
/** |
496
|
|
|
* Collects the metadata of table columns. |
497
|
|
|
* |
498
|
|
|
* @param TableSchemaInterface $table The table metadata. |
499
|
|
|
* |
500
|
|
|
* @throws Throwable |
501
|
|
|
* |
502
|
|
|
* @return bool Whether the table exists in the database. |
503
|
|
|
*/ |
504
|
500 |
|
protected function findColumns(TableSchemaInterface $table): bool |
505
|
|
|
{ |
506
|
500 |
|
$columnsTableName = 'INFORMATION_SCHEMA.COLUMNS'; |
507
|
|
|
|
508
|
500 |
|
$whereParams = [':table_name' => $table->getName()]; |
509
|
500 |
|
$whereSql = '[t1].[table_name] = :table_name'; |
510
|
|
|
|
511
|
500 |
|
if ($table->getCatalogName() !== null) { |
512
|
|
|
$columnsTableName = "{$table->getCatalogName()}.$columnsTableName"; |
513
|
|
|
$whereSql .= ' AND [t1].[table_catalog] = :catalog'; |
514
|
|
|
$whereParams[':catalog'] = $table->getCatalogName(); |
515
|
|
|
} |
516
|
|
|
|
517
|
500 |
|
if ($table->getSchemaName() !== null) { |
518
|
500 |
|
$whereSql .= " AND [t1].[table_schema] = '{$table->getSchemaName()}'"; |
519
|
|
|
} |
520
|
|
|
|
521
|
500 |
|
$columnsTableName = $this->db->getQuoter()->quoteTableName($columnsTableName); |
522
|
|
|
|
523
|
500 |
|
$sql = <<<SQL |
524
|
500 |
|
SELECT |
525
|
|
|
[t1].[column_name], |
526
|
|
|
[t1].[is_nullable], |
527
|
|
|
CASE WHEN [t1].[data_type] IN ('char','varchar','nchar','nvarchar','binary','varbinary') THEN |
528
|
|
|
CASE WHEN [t1].[character_maximum_length] = NULL OR [t1].[character_maximum_length] = -1 THEN |
529
|
|
|
[t1].[data_type] |
530
|
|
|
ELSE |
531
|
|
|
[t1].[data_type] + '(' + LTRIM(RTRIM(CONVERT(CHAR,[t1].[character_maximum_length]))) + ')' |
532
|
|
|
END |
533
|
|
|
WHEN [t1].[data_type] IN ('decimal','numeric') THEN |
534
|
|
|
CASE WHEN [t1].[numeric_precision] = NULL OR [t1].[numeric_precision] = -1 THEN |
535
|
|
|
[t1].[data_type] |
536
|
|
|
ELSE |
537
|
|
|
[t1].[data_type] + '(' + LTRIM(RTRIM(CONVERT(CHAR,[t1].[numeric_precision]))) + ',' + LTRIM(RTRIM(CONVERT(CHAR,[t1].[numeric_scale]))) + ')' |
538
|
|
|
END |
539
|
|
|
ELSE |
540
|
|
|
[t1].[data_type] |
541
|
|
|
END AS 'data_type', |
542
|
|
|
[t1].[column_default], |
543
|
|
|
COLUMNPROPERTY(OBJECT_ID([t1].[table_schema] + '.' + [t1].[table_name]), [t1].[column_name], 'IsIdentity') AS is_identity, |
544
|
|
|
COLUMNPROPERTY(OBJECT_ID([t1].[table_schema] + '.' + [t1].[table_name]), [t1].[column_name], 'IsComputed') AS is_computed, |
545
|
|
|
( |
546
|
|
|
SELECT CONVERT(VARCHAR, [t2].[value]) |
547
|
|
|
FROM [sys].[extended_properties] AS [t2] |
548
|
|
|
WHERE |
549
|
|
|
[t2].[class] = 1 AND |
550
|
|
|
[t2].[class_desc] = 'OBJECT_OR_COLUMN' AND |
551
|
|
|
[t2].[name] = 'MS_Description' AND |
552
|
|
|
[t2].[major_id] = OBJECT_ID([t1].[TABLE_SCHEMA] + '.' + [t1].[table_name]) AND |
553
|
|
|
[t2].[minor_id] = COLUMNPROPERTY(OBJECT_ID([t1].[TABLE_SCHEMA] + '.' + [t1].[TABLE_NAME]), [t1].[COLUMN_NAME], 'ColumnID') |
554
|
|
|
) as comment |
555
|
500 |
|
FROM $columnsTableName AS [t1] |
556
|
500 |
|
WHERE $whereSql |
557
|
500 |
|
SQL; |
558
|
|
|
|
559
|
|
|
try { |
560
|
|
|
/** @psalm-var ColumnArray[] $columns */ |
561
|
500 |
|
$columns = $this->db->createCommand($sql, $whereParams)->queryAll(); |
562
|
|
|
|
563
|
500 |
|
if (empty($columns)) { |
564
|
500 |
|
return false; |
565
|
|
|
} |
566
|
|
|
} catch (Exception) { |
567
|
|
|
return false; |
568
|
|
|
} |
569
|
|
|
|
570
|
447 |
|
foreach ($columns as $column) { |
571
|
447 |
|
$column = $this->loadColumnSchema($column); |
572
|
447 |
|
foreach ($table->getPrimaryKey() as $primaryKey) { |
573
|
274 |
|
if (strcasecmp($column->getName(), $primaryKey) === 0) { |
574
|
274 |
|
$column->primaryKey(true); |
575
|
274 |
|
break; |
576
|
|
|
} |
577
|
|
|
} |
578
|
|
|
|
579
|
447 |
|
if ($column->isPrimaryKey() && $column->isAutoIncrement()) { |
580
|
262 |
|
$table->sequenceName(''); |
581
|
|
|
} |
582
|
|
|
|
583
|
447 |
|
$table->column($column->getName(), $column); |
584
|
|
|
} |
585
|
|
|
|
586
|
447 |
|
return true; |
587
|
|
|
} |
588
|
|
|
|
589
|
|
|
/** |
590
|
|
|
* Collects the constraint details for the given table and constraint type. |
591
|
|
|
* |
592
|
|
|
* @param string $type Either PRIMARY KEY or UNIQUE. |
593
|
|
|
* |
594
|
|
|
* @throws Exception |
595
|
|
|
* @throws InvalidConfigException |
596
|
|
|
* @throws Throwable |
597
|
|
|
* |
598
|
|
|
* @return array Each entry has index_name and field_name. |
599
|
|
|
*/ |
600
|
500 |
|
protected function findTableConstraints(TableSchemaInterface $table, string $type): array |
601
|
|
|
{ |
602
|
500 |
|
$keyColumnUsageTableName = 'INFORMATION_SCHEMA.KEY_COLUMN_USAGE'; |
603
|
500 |
|
$tableConstraintsTableName = 'INFORMATION_SCHEMA.TABLE_CONSTRAINTS'; |
604
|
|
|
|
605
|
500 |
|
$catalogName = $table->getCatalogName(); |
606
|
500 |
|
if ($catalogName !== null) { |
607
|
|
|
$keyColumnUsageTableName = $catalogName . '.' . $keyColumnUsageTableName; |
608
|
|
|
$tableConstraintsTableName = $catalogName . '.' . $tableConstraintsTableName; |
609
|
|
|
} |
610
|
|
|
|
611
|
500 |
|
$keyColumnUsageTableName = $this->db->getQuoter()->quoteTableName($keyColumnUsageTableName); |
612
|
500 |
|
$tableConstraintsTableName = $this->db->getQuoter()->quoteTableName($tableConstraintsTableName); |
613
|
|
|
|
614
|
500 |
|
$sql = <<<SQL |
615
|
500 |
|
SELECT |
616
|
|
|
[kcu].[constraint_name] AS [index_name], |
617
|
|
|
[kcu].[column_name] AS [field_name] |
618
|
500 |
|
FROM $keyColumnUsageTableName AS [kcu] |
619
|
500 |
|
LEFT JOIN $tableConstraintsTableName AS [tc] ON |
620
|
|
|
[kcu].[table_schema] = [tc].[table_schema] AND |
621
|
|
|
[kcu].[table_name] = [tc].[table_name] AND |
622
|
|
|
[kcu].[constraint_name] = [tc].[constraint_name] |
623
|
|
|
WHERE |
624
|
|
|
[tc].[constraint_type] = :type AND |
625
|
|
|
[kcu].[table_name] = :tableName AND |
626
|
|
|
[kcu].[table_schema] = :schemaName |
627
|
500 |
|
SQL; |
628
|
|
|
|
629
|
500 |
|
return $this->db->createCommand( |
630
|
500 |
|
$sql, |
631
|
500 |
|
[ |
632
|
500 |
|
':tableName' => $table->getName(), |
633
|
500 |
|
':schemaName' => $table->getSchemaName(), |
634
|
500 |
|
':type' => $type, |
635
|
500 |
|
] |
636
|
500 |
|
)->queryAll(); |
637
|
|
|
} |
638
|
|
|
|
639
|
|
|
/** |
640
|
|
|
* Collects the primary key column details for the given table. |
641
|
|
|
* |
642
|
|
|
* @param TableSchemaInterface $table The table metadata |
643
|
|
|
* |
644
|
|
|
* @throws Exception |
645
|
|
|
* @throws InvalidConfigException |
646
|
|
|
* @throws Throwable |
647
|
|
|
*/ |
648
|
500 |
|
protected function findPrimaryKeys(TableSchemaInterface $table): void |
649
|
|
|
{ |
650
|
|
|
/** @psalm-var array<array-key, array{index_name: string, field_name: string}> $primaryKeys */ |
651
|
500 |
|
$primaryKeys = $this->findTableConstraints($table, 'PRIMARY KEY'); |
652
|
|
|
|
653
|
500 |
|
foreach ($primaryKeys as $row) { |
654
|
274 |
|
$table->primaryKey($row['field_name']); |
655
|
|
|
} |
656
|
|
|
} |
657
|
|
|
|
658
|
|
|
/** |
659
|
|
|
* Collects the foreign key column details for the given table. |
660
|
|
|
* |
661
|
|
|
* @param TableSchemaInterface $table The table metadata |
662
|
|
|
* |
663
|
|
|
* @throws Exception |
664
|
|
|
* @throws InvalidConfigException |
665
|
|
|
* @throws Throwable |
666
|
|
|
*/ |
667
|
447 |
|
protected function findForeignKeys(TableSchemaInterface $table): void |
668
|
|
|
{ |
669
|
447 |
|
$catalogName = $table->getCatalogName(); |
670
|
447 |
|
$fk = []; |
671
|
447 |
|
$object = $table->getName(); |
672
|
447 |
|
$schemaName = $table->getSchemaName(); |
673
|
|
|
|
674
|
447 |
|
if ($schemaName !== null) { |
675
|
447 |
|
$object = $schemaName . '.' . $object; |
676
|
|
|
} |
677
|
|
|
|
678
|
447 |
|
if ($catalogName !== null) { |
679
|
|
|
$object = $catalogName . '.' . $object; |
680
|
|
|
} |
681
|
|
|
|
682
|
447 |
|
$sql = <<<SQL |
683
|
|
|
SELECT |
684
|
|
|
[fk].[name] AS [fk_name], |
685
|
|
|
[cp].[name] AS [fk_column_name], |
686
|
|
|
OBJECT_NAME([fk].[referenced_object_id]) AS [uq_table_name], |
687
|
|
|
[cr].[name] AS [uq_column_name] |
688
|
|
|
FROM [sys].[foreign_keys] AS [fk] |
689
|
|
|
INNER JOIN [sys].[foreign_key_columns] AS [fkc] |
690
|
|
|
ON [fk].[object_id] = [fkc].[constraint_object_id] |
691
|
|
|
INNER JOIN [sys].[columns] AS [cp] |
692
|
|
|
ON [fk].[parent_object_id] = [cp].[object_id] AND [fkc].[parent_column_id] = [cp].[column_id] |
693
|
|
|
INNER JOIN [sys].[columns] AS [cr] |
694
|
|
|
ON [fk].[referenced_object_id] = [cr].[object_id] AND [fkc].[referenced_column_id] = [cr].[column_id] |
695
|
|
|
WHERE [fk].[parent_object_id] = OBJECT_ID(:object) |
696
|
447 |
|
SQL; |
697
|
|
|
|
698
|
|
|
/** |
699
|
|
|
* @psalm-var array< |
700
|
|
|
* array-key, |
701
|
|
|
* array{fk_name: string, fk_column_name: string, uq_table_name: string, uq_column_name: string} |
702
|
|
|
* > $rows |
703
|
|
|
*/ |
704
|
447 |
|
$rows = $this->db->createCommand($sql, [':object' => $object])->queryAll(); |
705
|
447 |
|
$table->foreignKeys([]); |
706
|
|
|
|
707
|
447 |
|
foreach ($rows as $row) { |
708
|
10 |
|
if (!isset($table->getForeignKeys()[$row['fk_name']])) { |
709
|
10 |
|
$fk[$row['fk_name']][] = $row['uq_table_name']; |
710
|
10 |
|
$table->foreignKeys($fk); |
711
|
|
|
} |
712
|
|
|
|
713
|
10 |
|
$fk[$row['fk_name']][$row['fk_column_name']] = $row['uq_column_name']; |
714
|
10 |
|
$table->foreignKeys($fk); |
715
|
|
|
} |
716
|
|
|
} |
717
|
|
|
|
718
|
|
|
/** |
719
|
|
|
* @throws Exception |
720
|
|
|
* @throws InvalidConfigException |
721
|
|
|
* @throws Throwable |
722
|
|
|
*/ |
723
|
4 |
|
protected function findViewNames(string $schema = ''): array |
724
|
|
|
{ |
725
|
4 |
|
if ($schema === '') { |
726
|
1 |
|
$schema = $this->defaultSchema; |
727
|
|
|
} |
728
|
|
|
|
729
|
4 |
|
$sql = <<<SQL |
730
|
|
|
SELECT [t].[table_name] |
731
|
|
|
FROM [INFORMATION_SCHEMA].[TABLES] AS [t] |
732
|
|
|
WHERE [t].[table_schema] = :schema AND [t].[table_type] = 'VIEW' |
733
|
|
|
ORDER BY [t].[table_name] |
734
|
4 |
|
SQL; |
735
|
|
|
|
736
|
4 |
|
return $this->db->createCommand($sql, [':schema' => $schema])->queryColumn(); |
737
|
|
|
} |
738
|
|
|
|
739
|
|
|
/** |
740
|
|
|
* Returns all unique indexes for the given table. |
741
|
|
|
* |
742
|
|
|
* Each array element is of the following structure: |
743
|
|
|
* |
744
|
|
|
* ```php |
745
|
|
|
* [ |
746
|
|
|
* 'IndexName1' => ['col1' [, ...]], |
747
|
|
|
* 'IndexName2' => ['col2' [, ...]], |
748
|
|
|
* ] |
749
|
|
|
* ``` |
750
|
|
|
* |
751
|
|
|
* @param TableSchemaInterface $table The table metadata. |
752
|
|
|
* |
753
|
|
|
* @throws Exception |
754
|
|
|
* @throws InvalidConfigException |
755
|
|
|
* @throws Throwable |
756
|
|
|
* |
757
|
|
|
* @return array All unique indexes for the given table. |
758
|
|
|
*/ |
759
|
1 |
|
public function findUniqueIndexes(TableSchemaInterface $table): array |
760
|
|
|
{ |
761
|
1 |
|
$result = []; |
762
|
|
|
|
763
|
|
|
/** @psalm-var array<array-key, array{index_name: string, field_name: string}> $tableUniqueConstraints */ |
764
|
1 |
|
$tableUniqueConstraints = $this->findTableConstraints($table, 'UNIQUE'); |
765
|
|
|
|
766
|
1 |
|
foreach ($tableUniqueConstraints as $row) { |
767
|
1 |
|
$result[$row['index_name']][] = $row['field_name']; |
768
|
|
|
} |
769
|
|
|
|
770
|
1 |
|
return $result; |
771
|
|
|
} |
772
|
|
|
|
773
|
|
|
/** |
774
|
|
|
* Loads multiple types of constraints and returns the specified ones. |
775
|
|
|
* |
776
|
|
|
* @param string $tableName table name. |
777
|
|
|
* @param string $returnType return type: |
778
|
|
|
* - primaryKey |
779
|
|
|
* - foreignKeys |
780
|
|
|
* - uniques |
781
|
|
|
* - checks |
782
|
|
|
* - defaults |
783
|
|
|
* |
784
|
|
|
* @throws Exception |
785
|
|
|
* @throws InvalidConfigException |
786
|
|
|
* @throws Throwable |
787
|
|
|
* |
788
|
|
|
* @return mixed Constraints of the specified type. |
789
|
|
|
*/ |
790
|
109 |
|
private function loadTableConstraints(string $tableName, string $returnType): mixed |
791
|
|
|
{ |
792
|
109 |
|
$sql = <<<SQL |
793
|
|
|
SELECT |
794
|
|
|
[o].[name] AS [name], |
795
|
|
|
COALESCE([ccol].[name], [dcol].[name], [fccol].[name], [kiccol].[name]) AS [column_name], |
796
|
|
|
RTRIM([o].[type]) AS [type], |
797
|
|
|
OBJECT_SCHEMA_NAME([f].[referenced_object_id]) AS [foreign_table_schema], |
798
|
|
|
OBJECT_NAME([f].[referenced_object_id]) AS [foreign_table_name], |
799
|
|
|
[ffccol].[name] AS [foreign_column_name], |
800
|
|
|
[f].[update_referential_action_desc] AS [on_update], |
801
|
|
|
[f].[delete_referential_action_desc] AS [on_delete], |
802
|
|
|
[c].[definition] AS [check_expr], |
803
|
|
|
[d].[definition] AS [default_expr] |
804
|
|
|
FROM (SELECT OBJECT_ID(:fullName) AS [object_id]) AS [t] |
805
|
|
|
INNER JOIN [sys].[objects] AS [o] |
806
|
|
|
ON [o].[parent_object_id] = [t].[object_id] AND [o].[type] IN ('PK', 'UQ', 'C', 'D', 'F') |
807
|
|
|
LEFT JOIN [sys].[check_constraints] AS [c] |
808
|
|
|
ON [c].[object_id] = [o].[object_id] |
809
|
|
|
LEFT JOIN [sys].[columns] AS [ccol] |
810
|
|
|
ON [ccol].[object_id] = [c].[parent_object_id] AND [ccol].[column_id] = [c].[parent_column_id] |
811
|
|
|
LEFT JOIN [sys].[default_constraints] AS [d] |
812
|
|
|
ON [d].[object_id] = [o].[object_id] |
813
|
|
|
LEFT JOIN [sys].[columns] AS [dcol] |
814
|
|
|
ON [dcol].[object_id] = [d].[parent_object_id] AND [dcol].[column_id] = [d].[parent_column_id] |
815
|
|
|
LEFT JOIN [sys].[key_constraints] AS [k] |
816
|
|
|
ON [k].[object_id] = [o].[object_id] |
817
|
|
|
LEFT JOIN [sys].[index_columns] AS [kic] |
818
|
|
|
ON [kic].[object_id] = [k].[parent_object_id] AND [kic].[index_id] = [k].[unique_index_id] |
819
|
|
|
LEFT JOIN [sys].[columns] AS [kiccol] |
820
|
|
|
ON [kiccol].[object_id] = [kic].[object_id] AND [kiccol].[column_id] = [kic].[column_id] |
821
|
|
|
LEFT JOIN [sys].[foreign_keys] AS [f] |
822
|
|
|
ON [f].[object_id] = [o].[object_id] |
823
|
|
|
LEFT JOIN [sys].[foreign_key_columns] AS [fc] |
824
|
|
|
ON [fc].[constraint_object_id] = [o].[object_id] |
825
|
|
|
LEFT JOIN [sys].[columns] AS [fccol] |
826
|
|
|
ON [fccol].[object_id] = [fc].[parent_object_id] AND [fccol].[column_id] = [fc].[parent_column_id] |
827
|
|
|
LEFT JOIN [sys].[columns] AS [ffccol] |
828
|
|
|
ON [ffccol].[object_id] = [fc].[referenced_object_id] AND [ffccol].[column_id] = [fc].[referenced_column_id] |
829
|
|
|
ORDER BY [kic].[key_ordinal] ASC, [fc].[constraint_column_id] ASC |
830
|
109 |
|
SQL; |
831
|
|
|
|
832
|
109 |
|
$resolvedName = $this->resolveTableName($tableName); |
833
|
109 |
|
$constraints = $this->db->createCommand($sql, [':fullName' => $resolvedName->getFullName()])->queryAll(); |
834
|
|
|
|
835
|
|
|
/** @psalm-var array[] $constraints */ |
836
|
109 |
|
$constraints = array_map('array_change_key_case', $constraints); |
837
|
109 |
|
$constraints = DbArrayHelper::index($constraints, null, ['type', 'name']); |
838
|
|
|
|
839
|
109 |
|
$result = [ |
840
|
109 |
|
self::PRIMARY_KEY => null, |
841
|
109 |
|
self::FOREIGN_KEYS => [], |
842
|
109 |
|
self::UNIQUES => [], |
843
|
109 |
|
self::CHECKS => [], |
844
|
109 |
|
self::DEFAULTS => [], |
845
|
109 |
|
]; |
846
|
|
|
|
847
|
|
|
/** @psalm-var array<array-key, array> $constraints */ |
848
|
109 |
|
foreach ($constraints as $type => $names) { |
849
|
|
|
/** |
850
|
|
|
* @psalm-var object|string|null $name |
851
|
|
|
* @psalm-var ConstraintArray $constraint |
852
|
|
|
*/ |
853
|
100 |
|
foreach ($names as $name => $constraint) { |
854
|
|
|
switch ($type) { |
855
|
100 |
|
case 'PK': |
856
|
|
|
/** @psalm-var Constraint */ |
857
|
66 |
|
$result[self::PRIMARY_KEY] = (new Constraint()) |
858
|
66 |
|
->columnNames(DbArrayHelper::getColumn($constraint, 'column_name')) |
859
|
66 |
|
->name($name); |
860
|
66 |
|
break; |
861
|
94 |
|
case 'F': |
862
|
23 |
|
$result[self::FOREIGN_KEYS][] = (new ForeignKeyConstraint()) |
863
|
23 |
|
->foreignSchemaName($constraint[0]['foreign_table_schema']) |
864
|
23 |
|
->foreignTableName($constraint[0]['foreign_table_name']) |
865
|
23 |
|
->foreignColumnNames(DbArrayHelper::getColumn($constraint, 'foreign_column_name')) |
866
|
23 |
|
->onDelete(str_replace('_', '', $constraint[0]['on_delete'])) |
867
|
23 |
|
->onUpdate(str_replace('_', '', $constraint[0]['on_update'])) |
868
|
23 |
|
->columnNames(DbArrayHelper::getColumn($constraint, 'column_name')) |
869
|
23 |
|
->name($name); |
870
|
23 |
|
break; |
871
|
78 |
|
case 'UQ': |
872
|
72 |
|
$result[self::UNIQUES][] = (new Constraint()) |
873
|
72 |
|
->columnNames(DbArrayHelper::getColumn($constraint, 'column_name')) |
874
|
72 |
|
->name($name); |
875
|
72 |
|
break; |
876
|
42 |
|
case 'C': |
877
|
19 |
|
$result[self::CHECKS][] = (new CheckConstraint()) |
878
|
19 |
|
->expression($constraint[0]['check_expr']) |
879
|
19 |
|
->columnNames(DbArrayHelper::getColumn($constraint, 'column_name')) |
880
|
19 |
|
->name($name); |
881
|
19 |
|
break; |
882
|
39 |
|
case 'D': |
883
|
39 |
|
$result[self::DEFAULTS][] = (new DefaultValueConstraint()) |
884
|
39 |
|
->value($constraint[0]['default_expr']) |
885
|
39 |
|
->columnNames(DbArrayHelper::getColumn($constraint, 'column_name')) |
886
|
39 |
|
->name($name); |
887
|
39 |
|
break; |
888
|
|
|
} |
889
|
|
|
} |
890
|
|
|
} |
891
|
|
|
|
892
|
109 |
|
foreach ($result as $type => $data) { |
893
|
109 |
|
$this->setTableMetadata($tableName, $type, $data); |
894
|
|
|
} |
895
|
|
|
|
896
|
109 |
|
return $result[$returnType]; |
897
|
|
|
} |
898
|
|
|
|
899
|
|
|
/** |
900
|
|
|
* Returns the cache key for the specified table name. |
901
|
|
|
* |
902
|
|
|
* @param string $name The table name. |
903
|
|
|
* |
904
|
|
|
* @return array The cache key. |
905
|
|
|
* |
906
|
|
|
* @psalm-suppress DeprecatedMethod |
907
|
|
|
*/ |
908
|
627 |
|
protected function getCacheKey(string $name): array |
909
|
|
|
{ |
910
|
627 |
|
return array_merge([self::class], $this->generateCacheKey(), [$this->getRawTableName($name)]); |
|
|
|
|
911
|
|
|
} |
912
|
|
|
|
913
|
|
|
/** |
914
|
|
|
* Returns the cache tag name. |
915
|
|
|
* |
916
|
|
|
* This allows {@see refresh()} to invalidate all cached table schemas. |
917
|
|
|
* |
918
|
|
|
* @return string The cache tag name. |
919
|
|
|
*/ |
920
|
579 |
|
protected function getCacheTag(): string |
921
|
|
|
{ |
922
|
579 |
|
return md5(serialize(array_merge([self::class], $this->generateCacheKey()))); |
923
|
|
|
} |
924
|
|
|
|
925
|
322 |
|
private function parseDefaultValue(string $value): string |
926
|
|
|
{ |
927
|
322 |
|
if (preg_match('/^\'(.*)\'$/', $value, $matches)) { |
928
|
74 |
|
return $matches[1]; |
929
|
|
|
} |
930
|
|
|
|
931
|
322 |
|
if (preg_match('/^\((.*)\)$/', $value, $matches)) { |
932
|
322 |
|
return $this->parseDefaultValue($matches[1]); |
933
|
|
|
} |
934
|
|
|
|
935
|
257 |
|
return $value; |
936
|
|
|
} |
937
|
|
|
} |
938
|
|
|
|
This function has been deprecated. The supplier of the function has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.