1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace Doctrine\DBAL\Platforms; |
6
|
|
|
|
7
|
|
|
use Doctrine\DBAL\Schema\ColumnDiff; |
8
|
|
|
use Doctrine\DBAL\Schema\ForeignKeyConstraint; |
9
|
|
|
use Doctrine\DBAL\Schema\Identifier; |
10
|
|
|
use Doctrine\DBAL\Schema\Index; |
11
|
|
|
use Doctrine\DBAL\Schema\Sequence; |
12
|
|
|
use Doctrine\DBAL\Schema\TableDiff; |
13
|
|
|
use Doctrine\DBAL\Types\BigIntType; |
14
|
|
|
use Doctrine\DBAL\Types\BinaryType; |
15
|
|
|
use Doctrine\DBAL\Types\BlobType; |
16
|
|
|
use Doctrine\DBAL\Types\IntegerType; |
17
|
|
|
use Doctrine\DBAL\Types\Type; |
18
|
|
|
use UnexpectedValueException; |
19
|
|
|
use function array_diff; |
20
|
|
|
use function array_merge; |
21
|
|
|
use function array_unique; |
22
|
|
|
use function array_values; |
23
|
|
|
use function count; |
24
|
|
|
use function explode; |
25
|
|
|
use function implode; |
26
|
|
|
use function in_array; |
27
|
|
|
use function is_array; |
28
|
|
|
use function is_bool; |
29
|
|
|
use function is_numeric; |
30
|
|
|
use function is_string; |
31
|
|
|
use function sprintf; |
32
|
|
|
use function strpos; |
33
|
|
|
use function strtolower; |
34
|
|
|
use function trim; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* Provides the behavior, features and SQL dialect of the PostgreSQL 9.4+ database platform. |
38
|
|
|
* |
39
|
|
|
* @todo Rename: PostgreSQLPlatform |
40
|
|
|
*/ |
41
|
|
|
class PostgreSqlPlatform extends AbstractPlatform |
42
|
|
|
{ |
43
|
|
|
/** @var bool */ |
44
|
|
|
private $useBooleanTrueFalseStrings = true; |
45
|
|
|
|
46
|
|
|
/** @var string[][] PostgreSQL booleans literals */ |
47
|
|
|
private $booleanLiterals = [ |
48
|
|
|
'true' => [ |
49
|
|
|
't', |
50
|
|
|
'true', |
51
|
|
|
'y', |
52
|
|
|
'yes', |
53
|
|
|
'on', |
54
|
|
|
'1', |
55
|
|
|
], |
56
|
|
|
'false' => [ |
57
|
|
|
'f', |
58
|
|
|
'false', |
59
|
|
|
'n', |
60
|
|
|
'no', |
61
|
|
|
'off', |
62
|
|
|
'0', |
63
|
|
|
], |
64
|
|
|
]; |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* PostgreSQL has different behavior with some drivers |
68
|
|
|
* with regard to how booleans have to be handled. |
69
|
|
|
* |
70
|
|
|
* Enables use of 'true'/'false' or otherwise 1 and 0 instead. |
71
|
|
|
*/ |
72
|
6731 |
|
public function setUseBooleanTrueFalseStrings(bool $flag) : void |
73
|
|
|
{ |
74
|
6731 |
|
$this->useBooleanTrueFalseStrings = $flag; |
75
|
6731 |
|
} |
76
|
|
|
|
77
|
|
|
/** |
78
|
|
|
* {@inheritDoc} |
79
|
|
|
*/ |
80
|
|
|
public function getNowExpression() : string |
81
|
|
|
{ |
82
|
|
|
return 'LOCALTIMESTAMP(0)'; |
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
/** |
86
|
|
|
* {@inheritDoc} |
87
|
|
|
*/ |
88
|
7153 |
|
public function getRegexpExpression() : string |
89
|
|
|
{ |
90
|
7153 |
|
return 'SIMILAR TO'; |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
/** |
94
|
|
|
* {@inheritDoc} |
95
|
|
|
*/ |
96
|
2891 |
|
public function getLocateExpression(string $string, string $substring, ?string $start = null) : string |
97
|
|
|
{ |
98
|
2891 |
|
if ($start !== null) { |
99
|
2891 |
|
$string = $this->getSubstringExpression($string, $start); |
100
|
|
|
|
101
|
2891 |
|
return 'CASE WHEN (POSITION(' . $substring . ' IN ' . $string . ') = 0) THEN 0 ELSE (POSITION(' . $substring . ' IN ' . $string . ') + ' . $start . ' - 1) END'; |
102
|
|
|
} |
103
|
|
|
|
104
|
2891 |
|
return sprintf('POSITION(%s IN %s)', $substring, $string); |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
/** |
108
|
|
|
* {@inheritdoc} |
109
|
|
|
*/ |
110
|
3083 |
|
protected function getDateArithmeticIntervalExpression(string $date, string $operator, string $interval, string $unit) : string |
111
|
|
|
{ |
112
|
3083 |
|
if ($unit === DateIntervalUnit::QUARTER) { |
113
|
2939 |
|
$interval = $this->multiplyInterval($interval, 3); |
114
|
2939 |
|
$unit = DateIntervalUnit::MONTH; |
115
|
|
|
} |
116
|
|
|
|
117
|
3083 |
|
return '(' . $date . ' ' . $operator . ' (' . $interval . " || ' " . $unit . "')::interval)"; |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
/** |
121
|
|
|
* {@inheritDoc} |
122
|
|
|
*/ |
123
|
2717 |
|
public function getDateDiffExpression(string $date1, string $date2) : string |
124
|
|
|
{ |
125
|
2717 |
|
return '(DATE(' . $date1 . ')-DATE(' . $date2 . '))'; |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
/** |
129
|
|
|
* {@inheritDoc} |
130
|
|
|
*/ |
131
|
2418 |
|
public function supportsSequences() : bool |
132
|
|
|
{ |
133
|
2418 |
|
return true; |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
/** |
137
|
|
|
* {@inheritDoc} |
138
|
|
|
*/ |
139
|
2397 |
|
public function supportsSchemas() : bool |
140
|
|
|
{ |
141
|
2397 |
|
return true; |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
/** |
145
|
|
|
* {@inheritdoc} |
146
|
|
|
*/ |
147
|
2247 |
|
public function getDefaultSchemaName() : string |
148
|
|
|
{ |
149
|
2247 |
|
return 'public'; |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
/** |
153
|
|
|
* {@inheritDoc} |
154
|
|
|
*/ |
155
|
2316 |
|
public function supportsIdentityColumns() : bool |
156
|
|
|
{ |
157
|
2316 |
|
return true; |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
/** |
161
|
|
|
* {@inheritdoc} |
162
|
|
|
*/ |
163
|
8445 |
|
public function supportsPartialIndexes() : bool |
164
|
|
|
{ |
165
|
8445 |
|
return true; |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
/** |
169
|
|
|
* {@inheritdoc} |
170
|
|
|
*/ |
171
|
6719 |
|
public function usesSequenceEmulatedIdentityColumns() : bool |
172
|
|
|
{ |
173
|
6719 |
|
return true; |
174
|
|
|
} |
175
|
|
|
|
176
|
|
|
/** |
177
|
|
|
* {@inheritdoc} |
178
|
|
|
*/ |
179
|
7464 |
|
public function getIdentitySequenceName(string $tableName, string $columnName) : string |
180
|
|
|
{ |
181
|
7464 |
|
return $tableName . '_' . $columnName . '_seq'; |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
/** |
185
|
|
|
* {@inheritDoc} |
186
|
|
|
*/ |
187
|
8559 |
|
public function supportsCommentOnStatement() : bool |
188
|
|
|
{ |
189
|
8559 |
|
return true; |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
/** |
193
|
|
|
* {@inheritDoc} |
194
|
|
|
*/ |
195
|
3 |
|
public function prefersSequences() : bool |
196
|
|
|
{ |
197
|
3 |
|
return true; |
198
|
|
|
} |
199
|
|
|
|
200
|
|
|
/** |
201
|
|
|
* {@inheritDoc} |
202
|
|
|
*/ |
203
|
8661 |
|
public function hasNativeGuidType() : bool |
204
|
|
|
{ |
205
|
8661 |
|
return true; |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
/** |
209
|
|
|
* {@inheritDoc} |
210
|
|
|
*/ |
211
|
2421 |
|
public function getListDatabasesSQL() : string |
212
|
|
|
{ |
213
|
2421 |
|
return 'SELECT datname FROM pg_database'; |
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
/** |
217
|
|
|
* {@inheritDoc} |
218
|
|
|
*/ |
219
|
2397 |
|
public function getListNamespacesSQL() : string |
220
|
|
|
{ |
221
|
2397 |
|
return "SELECT schema_name AS nspname |
222
|
|
|
FROM information_schema.schemata |
223
|
|
|
WHERE schema_name NOT LIKE 'pg\_%' |
224
|
|
|
AND schema_name != 'information_schema'"; |
225
|
|
|
} |
226
|
|
|
|
227
|
|
|
/** |
228
|
|
|
* {@inheritDoc} |
229
|
|
|
*/ |
230
|
1612 |
|
public function getListSequencesSQL(string $database) : string |
231
|
|
|
{ |
232
|
1612 |
|
return "SELECT sequence_name AS relname, |
233
|
|
|
sequence_schema AS schemaname |
234
|
|
|
FROM information_schema.sequences |
235
|
|
|
WHERE sequence_schema NOT LIKE 'pg\_%' |
236
|
|
|
AND sequence_schema != 'information_schema'"; |
237
|
|
|
} |
238
|
|
|
|
239
|
|
|
/** |
240
|
|
|
* {@inheritDoc} |
241
|
|
|
*/ |
242
|
2765 |
|
public function getListTablesSQL() : string |
243
|
|
|
{ |
244
|
2765 |
|
return "SELECT quote_ident(table_name) AS table_name, |
245
|
|
|
table_schema AS schema_name |
246
|
|
|
FROM information_schema.tables |
247
|
|
|
WHERE table_schema NOT LIKE 'pg\_%' |
248
|
|
|
AND table_schema != 'information_schema' |
249
|
|
|
AND table_name != 'geometry_columns' |
250
|
|
|
AND table_name != 'spatial_ref_sys' |
251
|
|
|
AND table_type != 'VIEW'"; |
252
|
|
|
} |
253
|
|
|
|
254
|
|
|
/** |
255
|
|
|
* {@inheritDoc} |
256
|
|
|
*/ |
257
|
2319 |
|
public function getListViewsSQL(string $database) : string |
258
|
|
|
{ |
259
|
2319 |
|
return 'SELECT quote_ident(table_name) AS viewname, |
260
|
|
|
table_schema AS schemaname, |
261
|
|
|
view_definition AS definition |
262
|
|
|
FROM information_schema.views |
263
|
|
|
WHERE view_definition IS NOT NULL'; |
264
|
|
|
} |
265
|
|
|
|
266
|
|
|
/** |
267
|
|
|
* {@inheritDoc} |
268
|
|
|
*/ |
269
|
7347 |
|
public function getListTableForeignKeysSQL(string $table, ?string $database = null) : string |
270
|
|
|
{ |
271
|
|
|
return 'SELECT quote_ident(r.conname) as conname, pg_catalog.pg_get_constraintdef(r.oid, true) as condef |
272
|
|
|
FROM pg_catalog.pg_constraint r |
273
|
|
|
WHERE r.conrelid = |
274
|
|
|
( |
275
|
|
|
SELECT c.oid |
276
|
|
|
FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n |
277
|
7347 |
|
WHERE ' . $this->getTableWhereClause($table) . " AND n.oid = c.relnamespace |
278
|
|
|
) |
279
|
|
|
AND r.contype = 'f'"; |
280
|
|
|
} |
281
|
|
|
|
282
|
|
|
/** |
283
|
|
|
* {@inheritDoc} |
284
|
|
|
*/ |
285
|
2480 |
|
public function getCreateViewSQL(string $name, string $sql) : string |
286
|
|
|
{ |
287
|
2480 |
|
return 'CREATE VIEW ' . $name . ' AS ' . $sql; |
288
|
|
|
} |
289
|
|
|
|
290
|
|
|
/** |
291
|
|
|
* {@inheritDoc} |
292
|
|
|
*/ |
293
|
2480 |
|
public function getDropViewSQL(string $name) : string |
294
|
|
|
{ |
295
|
2480 |
|
return 'DROP VIEW ' . $name; |
296
|
|
|
} |
297
|
|
|
|
298
|
|
|
/** |
299
|
|
|
* {@inheritDoc} |
300
|
|
|
*/ |
301
|
6203 |
|
public function getListTableConstraintsSQL(string $table) : string |
302
|
|
|
{ |
303
|
6203 |
|
$table = new Identifier($table); |
304
|
6203 |
|
$table = $this->quoteStringLiteral($table->getName()); |
305
|
|
|
|
306
|
6203 |
|
return sprintf( |
307
|
|
|
<<<'SQL' |
308
|
3 |
|
SELECT |
309
|
|
|
quote_ident(relname) as relname |
310
|
|
|
FROM |
311
|
|
|
pg_class |
312
|
|
|
WHERE oid IN ( |
313
|
|
|
SELECT indexrelid |
314
|
|
|
FROM pg_index, pg_class |
315
|
|
|
WHERE pg_class.relname = %s |
316
|
|
|
AND pg_class.oid = pg_index.indrelid |
317
|
|
|
AND (indisunique = 't' OR indisprimary = 't') |
318
|
|
|
) |
319
|
|
|
SQL |
320
|
|
|
, |
321
|
6203 |
|
$table |
322
|
|
|
); |
323
|
|
|
} |
324
|
|
|
|
325
|
|
|
/** |
326
|
|
|
* {@inheritDoc} |
327
|
|
|
* |
328
|
|
|
* @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html |
329
|
|
|
*/ |
330
|
7290 |
|
public function getListTableIndexesSQL(string $table, ?string $currentDatabase = null) : string |
331
|
|
|
{ |
332
|
|
|
return 'SELECT quote_ident(relname) as relname, pg_index.indisunique, pg_index.indisprimary, |
333
|
|
|
pg_index.indkey, pg_index.indrelid, |
334
|
|
|
pg_get_expr(indpred, indrelid) AS where |
335
|
|
|
FROM pg_class, pg_index |
336
|
|
|
WHERE oid IN ( |
337
|
|
|
SELECT indexrelid |
338
|
|
|
FROM pg_index si, pg_class sc, pg_namespace sn |
339
|
7290 |
|
WHERE ' . $this->getTableWhereClause($table, 'sc', 'sn') . ' AND sc.oid=si.indrelid AND sc.relnamespace = sn.oid |
340
|
|
|
) AND pg_index.indexrelid = oid'; |
341
|
|
|
} |
342
|
|
|
|
343
|
7359 |
|
private function getTableWhereClause(string $table, string $classAlias = 'c', string $namespaceAlias = 'n') : string |
344
|
|
|
{ |
345
|
7359 |
|
$whereClause = $namespaceAlias . ".nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast') AND "; |
346
|
7359 |
|
if (strpos($table, '.') !== false) { |
347
|
7268 |
|
[$schema, $table] = explode('.', $table); |
348
|
7268 |
|
$schema = $this->quoteStringLiteral($schema); |
349
|
|
|
} else { |
350
|
7350 |
|
$schema = "ANY(string_to_array((select replace(replace(setting,'\"\$user\"',user),' ','') from pg_catalog.pg_settings where name = 'search_path'),','))"; |
351
|
|
|
} |
352
|
|
|
|
353
|
7359 |
|
$table = new Identifier($table); |
354
|
7359 |
|
$table = $this->quoteStringLiteral($table->getName()); |
355
|
|
|
|
356
|
7359 |
|
return $whereClause . sprintf( |
357
|
18 |
|
'%s.relname = %s AND %s.nspname = %s', |
358
|
7359 |
|
$classAlias, |
359
|
7359 |
|
$table, |
360
|
7359 |
|
$namespaceAlias, |
361
|
7359 |
|
$schema |
362
|
|
|
); |
363
|
|
|
} |
364
|
|
|
|
365
|
|
|
/** |
366
|
|
|
* {@inheritDoc} |
367
|
|
|
*/ |
368
|
7252 |
|
public function getListTableColumnsSQL(string $table, ?string $database = null) : string |
369
|
|
|
{ |
370
|
|
|
return "SELECT |
371
|
|
|
a.attnum, |
372
|
|
|
quote_ident(a.attname) AS field, |
373
|
|
|
t.typname AS type, |
374
|
|
|
format_type(a.atttypid, a.atttypmod) AS complete_type, |
375
|
|
|
(SELECT tc.collcollate FROM pg_catalog.pg_collation tc WHERE tc.oid = a.attcollation) AS collation, |
376
|
|
|
(SELECT t1.typname FROM pg_catalog.pg_type t1 WHERE t1.oid = t.typbasetype) AS domain_type, |
377
|
|
|
(SELECT format_type(t2.typbasetype, t2.typtypmod) FROM |
378
|
|
|
pg_catalog.pg_type t2 WHERE t2.typtype = 'd' AND t2.oid = a.atttypid) AS domain_complete_type, |
379
|
|
|
a.attnotnull AS isnotnull, |
380
|
|
|
(SELECT 't' |
381
|
|
|
FROM pg_index |
382
|
|
|
WHERE c.oid = pg_index.indrelid |
383
|
|
|
AND pg_index.indkey[0] = a.attnum |
384
|
|
|
AND pg_index.indisprimary = 't' |
385
|
|
|
) AS pri, |
386
|
|
|
(SELECT pg_get_expr(adbin, adrelid) |
387
|
|
|
FROM pg_attrdef |
388
|
|
|
WHERE c.oid = pg_attrdef.adrelid |
389
|
|
|
AND pg_attrdef.adnum=a.attnum |
390
|
|
|
) AS default, |
391
|
|
|
(SELECT pg_description.description |
392
|
|
|
FROM pg_description WHERE pg_description.objoid = c.oid AND a.attnum = pg_description.objsubid |
393
|
|
|
) AS comment |
394
|
|
|
FROM pg_attribute a, pg_class c, pg_type t, pg_namespace n |
395
|
7252 |
|
WHERE " . $this->getTableWhereClause($table, 'c', 'n') . ' |
396
|
|
|
AND a.attnum > 0 |
397
|
|
|
AND a.attrelid = c.oid |
398
|
|
|
AND a.atttypid = t.oid |
399
|
|
|
AND n.oid = c.relnamespace |
400
|
|
|
ORDER BY a.attnum'; |
401
|
|
|
} |
402
|
|
|
|
403
|
|
|
/** |
404
|
|
|
* {@inheritDoc} |
405
|
|
|
*/ |
406
|
8536 |
|
public function getCreateDatabaseSQL(string $database) : string |
407
|
|
|
{ |
408
|
8536 |
|
return 'CREATE DATABASE ' . $database; |
409
|
|
|
} |
410
|
|
|
|
411
|
|
|
/** |
412
|
|
|
* Returns the SQL statement for disallowing new connections on the given database. |
413
|
|
|
* |
414
|
|
|
* This is useful to force DROP DATABASE operations which could fail because of active connections. |
415
|
|
|
* |
416
|
|
|
* @param string $database The name of the database to disallow new connections for. |
417
|
|
|
*/ |
418
|
7193 |
|
public function getDisallowDatabaseConnectionsSQL(string $database) : string |
419
|
|
|
{ |
420
|
7193 |
|
return "UPDATE pg_database SET datallowconn = 'false' WHERE datname = " . $this->quoteStringLiteral($database); |
421
|
|
|
} |
422
|
|
|
|
423
|
|
|
/** |
424
|
|
|
* Returns the SQL statement for closing currently active connections on the given database. |
425
|
|
|
* |
426
|
|
|
* This is useful to force DROP DATABASE operations which could fail because of active connections. |
427
|
|
|
* |
428
|
|
|
* @param string $database The name of the database to close currently active connections for. |
429
|
|
|
*/ |
430
|
7899 |
|
public function getCloseActiveDatabaseConnectionsSQL(string $database) : string |
431
|
|
|
{ |
432
|
|
|
return 'SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = ' |
433
|
7899 |
|
. $this->quoteStringLiteral($database); |
434
|
|
|
} |
435
|
|
|
|
436
|
|
|
/** |
437
|
|
|
* {@inheritDoc} |
438
|
|
|
*/ |
439
|
8341 |
|
public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) : string |
440
|
|
|
{ |
441
|
8341 |
|
$query = ''; |
442
|
|
|
|
443
|
8341 |
|
if ($foreignKey->hasOption('match')) { |
444
|
7178 |
|
$query .= ' MATCH ' . $foreignKey->getOption('match'); |
445
|
|
|
} |
446
|
|
|
|
447
|
8341 |
|
$query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey); |
448
|
|
|
|
449
|
8341 |
|
if ($foreignKey->hasOption('deferrable') && $foreignKey->getOption('deferrable') !== false) { |
450
|
7178 |
|
$query .= ' DEFERRABLE'; |
451
|
|
|
} else { |
452
|
8341 |
|
$query .= ' NOT DEFERRABLE'; |
453
|
|
|
} |
454
|
|
|
|
455
|
8341 |
|
if ($foreignKey->hasOption('deferred') && $foreignKey->getOption('deferred') !== false) { |
456
|
7178 |
|
$query .= ' INITIALLY DEFERRED'; |
457
|
|
|
} else { |
458
|
8341 |
|
$query .= ' INITIALLY IMMEDIATE'; |
459
|
|
|
} |
460
|
|
|
|
461
|
8341 |
|
return $query; |
462
|
|
|
} |
463
|
|
|
|
464
|
|
|
/** |
465
|
|
|
* {@inheritDoc} |
466
|
|
|
*/ |
467
|
7575 |
|
public function getAlterTableSQL(TableDiff $diff) : array |
468
|
|
|
{ |
469
|
7575 |
|
$sql = []; |
470
|
7575 |
|
$commentsSQL = []; |
471
|
7575 |
|
$columnSql = []; |
472
|
|
|
|
473
|
7575 |
|
foreach ($diff->addedColumns as $column) { |
474
|
6694 |
|
if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { |
475
|
|
|
continue; |
476
|
|
|
} |
477
|
|
|
|
478
|
6694 |
|
$query = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); |
479
|
6694 |
|
$sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; |
480
|
|
|
|
481
|
6694 |
|
$comment = $this->getColumnComment($column); |
482
|
|
|
|
483
|
6694 |
|
if ($comment === null || $comment === '') { |
484
|
6691 |
|
continue; |
485
|
|
|
} |
486
|
|
|
|
487
|
5628 |
|
$commentsSQL[] = $this->getCommentOnColumnSQL( |
488
|
5628 |
|
$diff->getName($this)->getQuotedName($this), |
489
|
5628 |
|
$column->getQuotedName($this), |
490
|
3 |
|
$comment |
491
|
|
|
); |
492
|
|
|
} |
493
|
|
|
|
494
|
7575 |
|
foreach ($diff->removedColumns as $column) { |
495
|
7302 |
|
if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { |
496
|
|
|
continue; |
497
|
|
|
} |
498
|
|
|
|
499
|
7302 |
|
$query = 'DROP ' . $column->getQuotedName($this); |
500
|
7302 |
|
$sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; |
501
|
|
|
} |
502
|
|
|
|
503
|
7575 |
|
foreach ($diff->changedColumns as $columnDiff) { |
504
|
|
|
/** @var $columnDiff \Doctrine\DBAL\Schema\ColumnDiff */ |
505
|
7551 |
|
if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { |
506
|
|
|
continue; |
507
|
|
|
} |
508
|
|
|
|
509
|
7551 |
|
if ($this->isUnchangedBinaryColumn($columnDiff)) { |
510
|
6403 |
|
continue; |
511
|
|
|
} |
512
|
|
|
|
513
|
7548 |
|
$oldColumnName = $columnDiff->getOldColumnName()->getQuotedName($this); |
514
|
7548 |
|
$column = $columnDiff->column; |
515
|
|
|
|
516
|
7548 |
|
if ($columnDiff->hasChanged('type') || $columnDiff->hasChanged('precision') || $columnDiff->hasChanged('scale') || $columnDiff->hasChanged('fixed')) { |
517
|
7426 |
|
$type = $column->getType(); |
518
|
|
|
|
519
|
|
|
// SERIAL/BIGSERIAL are not "real" types and we can't alter a column to that type |
520
|
7426 |
|
$columnDefinition = $column->toArray(); |
521
|
7426 |
|
$columnDefinition['autoincrement'] = false; |
522
|
|
|
|
523
|
|
|
// here was a server version check before, but DBAL API does not support this anymore. |
524
|
7426 |
|
$query = 'ALTER ' . $oldColumnName . ' TYPE ' . $type->getSQLDeclaration($columnDefinition, $this); |
525
|
7426 |
|
$sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; |
526
|
|
|
} |
527
|
|
|
|
528
|
7548 |
|
if ($columnDiff->hasChanged('default') || $this->typeChangeBreaksDefaultValue($columnDiff)) { |
529
|
7087 |
|
$defaultClause = $column->getDefault() === null |
530
|
7066 |
|
? ' DROP DEFAULT' |
531
|
7087 |
|
: ' SET' . $this->getDefaultValueDeclarationSQL($column->toArray()); |
532
|
7087 |
|
$query = 'ALTER ' . $oldColumnName . $defaultClause; |
533
|
7087 |
|
$sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; |
534
|
|
|
} |
535
|
|
|
|
536
|
7548 |
|
if ($columnDiff->hasChanged('notnull')) { |
537
|
5731 |
|
$query = 'ALTER ' . $oldColumnName . ' ' . ($column->getNotnull() ? 'SET' : 'DROP') . ' NOT NULL'; |
538
|
5731 |
|
$sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; |
539
|
|
|
} |
540
|
|
|
|
541
|
7548 |
|
if ($columnDiff->hasChanged('autoincrement')) { |
542
|
2540 |
|
if ($column->getAutoincrement()) { |
543
|
|
|
// add autoincrement |
544
|
2540 |
|
$seqName = $this->getIdentitySequenceName($diff->name, $oldColumnName); |
545
|
|
|
|
546
|
2540 |
|
$sql[] = 'CREATE SEQUENCE ' . $seqName; |
547
|
2540 |
|
$sql[] = "SELECT setval('" . $seqName . "', (SELECT MAX(" . $oldColumnName . ') FROM ' . $diff->getName($this)->getQuotedName($this) . '))'; |
548
|
2540 |
|
$query = 'ALTER ' . $oldColumnName . " SET DEFAULT nextval('" . $seqName . "')"; |
549
|
2540 |
|
$sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; |
550
|
|
|
} else { |
551
|
|
|
// Drop autoincrement, but do NOT drop the sequence. It might be re-used by other tables or have |
552
|
2534 |
|
$query = 'ALTER ' . $oldColumnName . ' DROP DEFAULT'; |
553
|
2534 |
|
$sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; |
554
|
|
|
} |
555
|
|
|
} |
556
|
|
|
|
557
|
7548 |
|
$newComment = $this->getColumnComment($column); |
558
|
7548 |
|
$oldComment = $this->getOldColumnComment($columnDiff); |
559
|
|
|
|
560
|
7548 |
|
if ($columnDiff->hasChanged('comment') || ($columnDiff->fromColumn !== null && $oldComment !== $newComment)) { |
561
|
7124 |
|
$commentsSQL[] = $this->getCommentOnColumnSQL( |
562
|
7124 |
|
$diff->getName($this)->getQuotedName($this), |
563
|
7124 |
|
$column->getQuotedName($this), |
564
|
9 |
|
$newComment |
565
|
|
|
); |
566
|
|
|
} |
567
|
|
|
|
568
|
7548 |
|
if (! $columnDiff->hasChanged('length')) { |
569
|
7545 |
|
continue; |
570
|
|
|
} |
571
|
|
|
|
572
|
5178 |
|
$query = 'ALTER ' . $oldColumnName . ' TYPE ' . $column->getType()->getSQLDeclaration($column->toArray(), $this); |
573
|
5178 |
|
$sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query; |
574
|
|
|
} |
575
|
|
|
|
576
|
7575 |
|
foreach ($diff->renamedColumns as $oldColumnName => $column) { |
577
|
6626 |
|
if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { |
578
|
|
|
continue; |
579
|
|
|
} |
580
|
|
|
|
581
|
6626 |
|
$oldColumnName = new Identifier($oldColumnName); |
582
|
|
|
|
583
|
6626 |
|
$sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . |
584
|
6626 |
|
' RENAME COLUMN ' . $oldColumnName->getQuotedName($this) . ' TO ' . $column->getQuotedName($this); |
585
|
|
|
} |
586
|
|
|
|
587
|
7575 |
|
$tableSql = []; |
588
|
|
|
|
589
|
7575 |
|
if (! $this->onSchemaAlterTable($diff, $tableSql)) { |
590
|
7575 |
|
$sql = array_merge($sql, $commentsSQL); |
591
|
|
|
|
592
|
7575 |
|
$newName = $diff->getNewName(); |
593
|
|
|
|
594
|
7575 |
|
if ($newName !== null) { |
595
|
5731 |
|
$sql[] = sprintf( |
596
|
6 |
|
'ALTER TABLE %s RENAME TO %s', |
597
|
5731 |
|
$diff->getName($this)->getQuotedName($this), |
598
|
5731 |
|
$newName->getQuotedName($this) |
599
|
|
|
); |
600
|
|
|
} |
601
|
|
|
|
602
|
7575 |
|
$sql = array_merge( |
603
|
7575 |
|
$this->getPreAlterTableIndexForeignKeySQL($diff), |
604
|
7575 |
|
$sql, |
605
|
7575 |
|
$this->getPostAlterTableIndexForeignKeySQL($diff) |
606
|
|
|
); |
607
|
|
|
} |
608
|
|
|
|
609
|
7575 |
|
return array_merge($sql, $tableSql, $columnSql); |
610
|
|
|
} |
611
|
|
|
|
612
|
|
|
/** |
613
|
|
|
* Checks whether a given column diff is a logically unchanged binary type column. |
614
|
|
|
* |
615
|
|
|
* Used to determine whether a column alteration for a binary type column can be skipped. |
616
|
|
|
* Doctrine's {@link \Doctrine\DBAL\Types\BinaryType} and {@link \Doctrine\DBAL\Types\BlobType} |
617
|
|
|
* are mapped to the same database column type on this platform as this platform |
618
|
|
|
* does not have a native VARBINARY/BINARY column type. Therefore the {@link \Doctrine\DBAL\Schema\Comparator} |
619
|
|
|
* might detect differences for binary type columns which do not have to be propagated |
620
|
|
|
* to database as there actually is no difference at database level. |
621
|
|
|
* |
622
|
|
|
* @param ColumnDiff $columnDiff The column diff to check against. |
623
|
|
|
* |
624
|
|
|
* @return bool True if the given column diff is an unchanged binary type column, false otherwise. |
625
|
|
|
*/ |
626
|
7551 |
|
private function isUnchangedBinaryColumn(ColumnDiff $columnDiff) : bool |
627
|
|
|
{ |
628
|
7551 |
|
$columnType = $columnDiff->column->getType(); |
629
|
|
|
|
630
|
7551 |
|
if (! $columnType instanceof BinaryType && ! $columnType instanceof BlobType) { |
631
|
7548 |
|
return false; |
632
|
|
|
} |
633
|
|
|
|
634
|
6403 |
|
$fromColumn = $columnDiff->fromColumn; |
635
|
|
|
|
636
|
6403 |
|
if ($fromColumn !== null) { |
637
|
6403 |
|
$fromColumnType = $fromColumn->getType(); |
638
|
|
|
|
639
|
6403 |
|
if (! $fromColumnType instanceof BinaryType && ! $fromColumnType instanceof BlobType) { |
640
|
|
|
return false; |
641
|
|
|
} |
642
|
|
|
|
643
|
6403 |
|
return count(array_diff($columnDiff->changedProperties, ['type', 'length', 'fixed'])) === 0; |
644
|
|
|
} |
645
|
|
|
|
646
|
|
|
if ($columnDiff->hasChanged('type')) { |
647
|
|
|
return false; |
648
|
|
|
} |
649
|
|
|
|
650
|
|
|
return count(array_diff($columnDiff->changedProperties, ['length', 'fixed'])) === 0; |
651
|
|
|
} |
652
|
|
|
|
653
|
|
|
/** |
654
|
|
|
* {@inheritdoc} |
655
|
|
|
*/ |
656
|
6336 |
|
protected function getRenameIndexSQL(string $oldIndexName, Index $index, string $tableName) : array |
657
|
|
|
{ |
658
|
6336 |
|
if (strpos($tableName, '.') !== false) { |
659
|
5156 |
|
[$schema] = explode('.', $tableName); |
660
|
5156 |
|
$oldIndexName = $schema . '.' . $oldIndexName; |
661
|
|
|
} |
662
|
|
|
|
663
|
6336 |
|
return ['ALTER INDEX ' . $oldIndexName . ' RENAME TO ' . $index->getQuotedName($this)]; |
664
|
|
|
} |
665
|
|
|
|
666
|
|
|
/** |
667
|
|
|
* {@inheritdoc} |
668
|
|
|
*/ |
669
|
7139 |
|
public function getCommentOnColumnSQL(string $tableName, string $columnName, ?string $comment) : string |
670
|
|
|
{ |
671
|
7139 |
|
$tableName = new Identifier($tableName); |
672
|
7139 |
|
$columnName = new Identifier($columnName); |
673
|
7139 |
|
$comment = $comment === null ? 'NULL' : $this->quoteStringLiteral($comment); |
674
|
|
|
|
675
|
7139 |
|
return sprintf( |
676
|
24 |
|
'COMMENT ON COLUMN %s.%s IS %s', |
677
|
7139 |
|
$tableName->getQuotedName($this), |
678
|
7139 |
|
$columnName->getQuotedName($this), |
679
|
7139 |
|
$comment |
680
|
|
|
); |
681
|
|
|
} |
682
|
|
|
|
683
|
|
|
/** |
684
|
|
|
* {@inheritDoc} |
685
|
|
|
*/ |
686
|
7627 |
|
public function getCreateSequenceSQL(Sequence $sequence) : string |
687
|
|
|
{ |
688
|
7627 |
|
return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) . |
689
|
7627 |
|
' INCREMENT BY ' . $sequence->getAllocationSize() . |
690
|
7627 |
|
' MINVALUE ' . $sequence->getInitialValue() . |
691
|
7627 |
|
' START ' . $sequence->getInitialValue() . |
692
|
7627 |
|
$this->getSequenceCacheSQL($sequence); |
693
|
|
|
} |
694
|
|
|
|
695
|
|
|
/** |
696
|
|
|
* {@inheritDoc} |
697
|
|
|
*/ |
698
|
|
|
public function getAlterSequenceSQL(Sequence $sequence) : string |
699
|
|
|
{ |
700
|
|
|
return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) . |
701
|
|
|
' INCREMENT BY ' . $sequence->getAllocationSize() . |
702
|
|
|
$this->getSequenceCacheSQL($sequence); |
703
|
|
|
} |
704
|
|
|
|
705
|
|
|
/** |
706
|
|
|
* Cache definition for sequences |
707
|
|
|
*/ |
708
|
7627 |
|
private function getSequenceCacheSQL(Sequence $sequence) : string |
709
|
|
|
{ |
710
|
7627 |
|
if ($sequence->getCache() > 1) { |
711
|
6453 |
|
return ' CACHE ' . $sequence->getCache(); |
712
|
|
|
} |
713
|
|
|
|
714
|
7624 |
|
return ''; |
715
|
|
|
} |
716
|
|
|
|
717
|
|
|
/** |
718
|
|
|
* {@inheritDoc} |
719
|
|
|
*/ |
720
|
7624 |
|
public function getDropSequenceSQL($sequence) : string |
721
|
|
|
{ |
722
|
7624 |
|
if ($sequence instanceof Sequence) { |
723
|
|
|
$sequence = $sequence->getQuotedName($this); |
724
|
|
|
} |
725
|
|
|
|
726
|
7624 |
|
return 'DROP SEQUENCE ' . $sequence . ' CASCADE'; |
727
|
|
|
} |
728
|
|
|
|
729
|
|
|
/** |
730
|
|
|
* {@inheritDoc} |
731
|
|
|
*/ |
732
|
7397 |
|
public function getCreateSchemaSQL(string $schemaName) : string |
733
|
|
|
{ |
734
|
7397 |
|
return 'CREATE SCHEMA ' . $schemaName; |
735
|
|
|
} |
736
|
|
|
|
737
|
|
|
/** |
738
|
|
|
* {@inheritDoc} |
739
|
|
|
*/ |
740
|
7269 |
|
public function getDropForeignKeySQL($foreignKey, $table) : string |
741
|
|
|
{ |
742
|
7269 |
|
return $this->getDropConstraintSQL($foreignKey, $table); |
743
|
|
|
} |
744
|
|
|
|
745
|
|
|
/** |
746
|
|
|
* {@inheritDoc} |
747
|
|
|
*/ |
748
|
8556 |
|
protected function _getCreateTableSQL(string $tableName, array $columns, array $options = []) : array |
749
|
|
|
{ |
750
|
8556 |
|
$queryFields = $this->getColumnDeclarationListSQL($columns); |
751
|
|
|
|
752
|
8556 |
|
if (isset($options['primary']) && ! empty($options['primary'])) { |
753
|
7614 |
|
$keyColumns = array_unique(array_values($options['primary'])); |
754
|
7614 |
|
$queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')'; |
755
|
|
|
} |
756
|
|
|
|
757
|
8556 |
|
$query = 'CREATE TABLE ' . $tableName . ' (' . $queryFields . ')'; |
758
|
|
|
|
759
|
8556 |
|
$sql = [$query]; |
760
|
|
|
|
761
|
8556 |
|
if (isset($options['indexes']) && ! empty($options['indexes'])) { |
762
|
7328 |
|
foreach ($options['indexes'] as $index) { |
763
|
7328 |
|
$sql[] = $this->getCreateIndexSQL($index, $tableName); |
764
|
|
|
} |
765
|
|
|
} |
766
|
|
|
|
767
|
8556 |
|
if (isset($options['foreignKeys'])) { |
768
|
7244 |
|
foreach ((array) $options['foreignKeys'] as $definition) { |
769
|
6980 |
|
$sql[] = $this->getCreateForeignKeySQL($definition, $tableName); |
770
|
|
|
} |
771
|
|
|
} |
772
|
|
|
|
773
|
8556 |
|
return $sql; |
774
|
|
|
} |
775
|
|
|
|
776
|
|
|
/** |
777
|
|
|
* Converts a single boolean value. |
778
|
|
|
* |
779
|
|
|
* First converts the value to its native PHP boolean type |
780
|
|
|
* and passes it to the given callback function to be reconverted |
781
|
|
|
* into any custom representation. |
782
|
|
|
* |
783
|
|
|
* @param mixed $value The value to convert. |
784
|
|
|
* @param callable $callback The callback function to use for converting the real boolean value. |
785
|
|
|
* |
786
|
|
|
* @return mixed |
787
|
|
|
* |
788
|
|
|
* @throws UnexpectedValueException |
789
|
|
|
*/ |
790
|
7850 |
|
private function convertSingleBooleanValue($value, callable $callback) |
791
|
|
|
{ |
792
|
7850 |
|
if ($value === null) { |
793
|
1970 |
|
return $callback(null); |
794
|
|
|
} |
795
|
|
|
|
796
|
7850 |
|
if (is_bool($value) || is_numeric($value)) { |
797
|
7798 |
|
return $callback((bool) $value); |
798
|
|
|
} |
799
|
|
|
|
800
|
7202 |
|
if (! is_string($value)) { |
801
|
|
|
return $callback(true); |
802
|
|
|
} |
803
|
|
|
|
804
|
|
|
/** |
805
|
|
|
* Better safe than sorry: http://php.net/in_array#106319 |
806
|
|
|
*/ |
807
|
7202 |
|
if (in_array(strtolower(trim($value)), $this->booleanLiterals['false'], true)) { |
808
|
7146 |
|
return $callback(false); |
809
|
|
|
} |
810
|
|
|
|
811
|
6843 |
|
if (in_array(strtolower(trim($value)), $this->booleanLiterals['true'], true)) { |
812
|
6840 |
|
return $callback(true); |
813
|
|
|
} |
814
|
|
|
|
815
|
6603 |
|
throw new UnexpectedValueException(sprintf( |
816
|
3 |
|
'Unrecognized boolean literal, %s given.', |
817
|
6603 |
|
$value |
818
|
|
|
)); |
819
|
|
|
} |
820
|
|
|
|
821
|
|
|
/** |
822
|
|
|
* Converts one or multiple boolean values. |
823
|
|
|
* |
824
|
|
|
* First converts the value(s) to their native PHP boolean type |
825
|
|
|
* and passes them to the given callback function to be reconverted |
826
|
|
|
* into any custom representation. |
827
|
|
|
* |
828
|
|
|
* @param mixed $item The value(s) to convert. |
829
|
|
|
* @param callable $callback The callback function to use for converting the real boolean value(s). |
830
|
|
|
* |
831
|
|
|
* @return mixed |
832
|
|
|
*/ |
833
|
7850 |
|
private function doConvertBooleans($item, callable $callback) |
834
|
|
|
{ |
835
|
7850 |
|
if (is_array($item)) { |
836
|
|
|
foreach ($item as $key => $value) { |
837
|
|
|
$item[$key] = $this->convertSingleBooleanValue($value, $callback); |
838
|
|
|
} |
839
|
|
|
|
840
|
|
|
return $item; |
841
|
|
|
} |
842
|
|
|
|
843
|
7850 |
|
return $this->convertSingleBooleanValue($item, $callback); |
844
|
|
|
} |
845
|
|
|
|
846
|
|
|
/** |
847
|
|
|
* {@inheritDoc} |
848
|
|
|
* |
849
|
|
|
* Postgres wants boolean values converted to the strings 'true'/'false'. |
850
|
|
|
*/ |
851
|
7817 |
|
public function convertBooleans($item) |
852
|
|
|
{ |
853
|
7817 |
|
if (! $this->useBooleanTrueFalseStrings) { |
854
|
6731 |
|
return parent::convertBooleans($item); |
855
|
|
|
} |
856
|
|
|
|
857
|
7811 |
|
return $this->doConvertBooleans( |
858
|
7811 |
|
$item, |
859
|
|
|
static function ($boolean) : string { |
860
|
7811 |
|
if ($boolean === null) { |
861
|
|
|
return 'NULL'; |
862
|
|
|
} |
863
|
|
|
|
864
|
7811 |
|
return $boolean === true ? 'true' : 'false'; |
865
|
7811 |
|
} |
866
|
|
|
); |
867
|
|
|
} |
868
|
|
|
|
869
|
|
|
/** |
870
|
|
|
* {@inheritDoc} |
871
|
|
|
*/ |
872
|
7116 |
|
public function convertBooleansToDatabaseValue($item) |
873
|
|
|
{ |
874
|
7116 |
|
if (! $this->useBooleanTrueFalseStrings) { |
875
|
6678 |
|
return parent::convertBooleansToDatabaseValue($item); |
876
|
|
|
} |
877
|
|
|
|
878
|
7113 |
|
return $this->doConvertBooleans( |
879
|
7113 |
|
$item, |
880
|
|
|
static function ($boolean) : ?int { |
881
|
7110 |
|
return $boolean === null ? null : (int) $boolean; |
882
|
7113 |
|
} |
883
|
|
|
); |
884
|
|
|
} |
885
|
|
|
|
886
|
|
|
/** |
887
|
|
|
* {@inheritDoc} |
888
|
|
|
*/ |
889
|
7042 |
|
public function convertFromBoolean($item) : ?bool |
890
|
|
|
{ |
891
|
7042 |
|
if (in_array($item, $this->booleanLiterals['false'], true)) { |
892
|
6643 |
|
return false; |
893
|
|
|
} |
894
|
|
|
|
895
|
7024 |
|
return parent::convertFromBoolean($item); |
896
|
|
|
} |
897
|
|
|
|
898
|
|
|
/** |
899
|
|
|
* {@inheritDoc} |
900
|
|
|
*/ |
901
|
7009 |
|
public function getSequenceNextValSQL(string $sequenceName) : string |
902
|
|
|
{ |
903
|
7009 |
|
return "SELECT NEXTVAL('" . $sequenceName . "')"; |
904
|
|
|
} |
905
|
|
|
|
906
|
|
|
/** |
907
|
|
|
* {@inheritDoc} |
908
|
|
|
*/ |
909
|
7128 |
|
public function getSetTransactionIsolationSQL(int $level) : string |
910
|
|
|
{ |
911
|
|
|
return 'SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL ' |
912
|
7128 |
|
. $this->_getTransactionIsolationLevelSQL($level); |
913
|
|
|
} |
914
|
|
|
|
915
|
|
|
/** |
916
|
|
|
* {@inheritDoc} |
917
|
|
|
*/ |
918
|
6939 |
|
public function getBooleanTypeDeclarationSQL(array $columnDef) : string |
919
|
|
|
{ |
920
|
6939 |
|
return 'BOOLEAN'; |
921
|
|
|
} |
922
|
|
|
|
923
|
|
|
/** |
924
|
|
|
* {@inheritDoc} |
925
|
|
|
*/ |
926
|
8547 |
|
public function getIntegerTypeDeclarationSQL(array $columnDef) : string |
927
|
|
|
{ |
928
|
8547 |
|
if (! empty($columnDef['autoincrement'])) { |
929
|
8199 |
|
return 'SERIAL'; |
930
|
|
|
} |
931
|
|
|
|
932
|
8402 |
|
return 'INT'; |
933
|
|
|
} |
934
|
|
|
|
935
|
|
|
/** |
936
|
|
|
* {@inheritDoc} |
937
|
|
|
*/ |
938
|
7808 |
|
public function getBigIntTypeDeclarationSQL(array $columnDef) : string |
939
|
|
|
{ |
940
|
7808 |
|
if (! empty($columnDef['autoincrement'])) { |
941
|
7796 |
|
return 'BIGSERIAL'; |
942
|
|
|
} |
943
|
|
|
|
944
|
2463 |
|
return 'BIGINT'; |
945
|
|
|
} |
946
|
|
|
|
947
|
|
|
/** |
948
|
|
|
* {@inheritDoc} |
949
|
|
|
*/ |
950
|
7957 |
|
public function getSmallIntTypeDeclarationSQL(array $columnDef) : string |
951
|
|
|
{ |
952
|
7957 |
|
if (! empty($columnDef['autoincrement'])) { |
953
|
7228 |
|
return 'SMALLSERIAL'; |
954
|
|
|
} |
955
|
|
|
|
956
|
7957 |
|
return 'SMALLINT'; |
957
|
|
|
} |
958
|
|
|
|
959
|
|
|
/** |
960
|
|
|
* {@inheritDoc} |
961
|
|
|
*/ |
962
|
6378 |
|
public function getGuidTypeDeclarationSQL(array $field) : string |
963
|
|
|
{ |
964
|
6378 |
|
return 'UUID'; |
965
|
|
|
} |
966
|
|
|
|
967
|
|
|
/** |
968
|
|
|
* {@inheritDoc} |
969
|
|
|
*/ |
970
|
7899 |
|
public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) : string |
971
|
|
|
{ |
972
|
7899 |
|
return 'TIMESTAMP(0) WITHOUT TIME ZONE'; |
973
|
|
|
} |
974
|
|
|
|
975
|
|
|
/** |
976
|
|
|
* {@inheritDoc} |
977
|
|
|
*/ |
978
|
1952 |
|
public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration) : string |
979
|
|
|
{ |
980
|
1952 |
|
return 'TIMESTAMP(0) WITH TIME ZONE'; |
981
|
|
|
} |
982
|
|
|
|
983
|
|
|
/** |
984
|
|
|
* {@inheritDoc} |
985
|
|
|
*/ |
986
|
2705 |
|
public function getDateTypeDeclarationSQL(array $fieldDeclaration) : string |
987
|
|
|
{ |
988
|
2705 |
|
return 'DATE'; |
989
|
|
|
} |
990
|
|
|
|
991
|
|
|
/** |
992
|
|
|
* {@inheritDoc} |
993
|
|
|
*/ |
994
|
2699 |
|
public function getTimeTypeDeclarationSQL(array $fieldDeclaration) : string |
995
|
|
|
{ |
996
|
2699 |
|
return 'TIME(0) WITHOUT TIME ZONE'; |
997
|
|
|
} |
998
|
|
|
|
999
|
|
|
/** |
1000
|
|
|
* {@inheritDoc} |
1001
|
|
|
*/ |
1002
|
|
|
protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) : string |
1003
|
|
|
{ |
1004
|
|
|
return ''; |
1005
|
|
|
} |
1006
|
|
|
|
1007
|
|
|
/** |
1008
|
|
|
* {@inheritDoc} |
1009
|
|
|
*/ |
1010
|
8350 |
|
protected function getVarcharTypeDeclarationSQLSnippet(int $length, bool $fixed) : string |
1011
|
|
|
{ |
1012
|
8350 |
|
return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)') |
1013
|
8350 |
|
: ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'); |
1014
|
|
|
} |
1015
|
|
|
|
1016
|
|
|
/** |
1017
|
|
|
* {@inheritdoc} |
1018
|
|
|
*/ |
1019
|
7378 |
|
protected function getBinaryTypeDeclarationSQLSnippet(int $length, bool $fixed) : string |
1020
|
|
|
{ |
1021
|
7378 |
|
return 'BYTEA'; |
1022
|
|
|
} |
1023
|
|
|
|
1024
|
|
|
/** |
1025
|
|
|
* {@inheritDoc} |
1026
|
|
|
*/ |
1027
|
7396 |
|
public function getClobTypeDeclarationSQL(array $field) : string |
1028
|
|
|
{ |
1029
|
7396 |
|
return 'TEXT'; |
1030
|
|
|
} |
1031
|
|
|
|
1032
|
|
|
/** |
1033
|
|
|
* {@inheritDoc} |
1034
|
|
|
*/ |
1035
|
7705 |
|
public function getName() : string |
1036
|
|
|
{ |
1037
|
7705 |
|
return 'postgresql'; |
1038
|
|
|
} |
1039
|
|
|
|
1040
|
|
|
/** |
1041
|
|
|
* {@inheritDoc} |
1042
|
|
|
* |
1043
|
|
|
* PostgreSQL returns all column names in SQL result sets in lowercase. |
1044
|
|
|
*/ |
1045
|
|
|
public function getSQLResultCasing(string $column) : string |
1046
|
|
|
{ |
1047
|
|
|
return strtolower($column); |
1048
|
|
|
} |
1049
|
|
|
|
1050
|
|
|
/** |
1051
|
|
|
* {@inheritDoc} |
1052
|
|
|
*/ |
1053
|
1880 |
|
public function getDateTimeTzFormatString() : string |
1054
|
|
|
{ |
1055
|
1880 |
|
return 'Y-m-d H:i:sO'; |
1056
|
|
|
} |
1057
|
|
|
|
1058
|
|
|
/** |
1059
|
|
|
* {@inheritDoc} |
1060
|
|
|
*/ |
1061
|
1776 |
|
public function getEmptyIdentityInsertSQL(string $tableName, string $identifierColumnName) : string |
1062
|
|
|
{ |
1063
|
1776 |
|
return 'INSERT INTO ' . $tableName . ' (' . $identifierColumnName . ') VALUES (DEFAULT)'; |
1064
|
|
|
} |
1065
|
|
|
|
1066
|
|
|
/** |
1067
|
|
|
* {@inheritDoc} |
1068
|
|
|
*/ |
1069
|
6943 |
|
public function getTruncateTableSQL(string $tableName, bool $cascade = false) : string |
1070
|
|
|
{ |
1071
|
6943 |
|
$tableIdentifier = new Identifier($tableName); |
1072
|
6943 |
|
$sql = 'TRUNCATE ' . $tableIdentifier->getQuotedName($this); |
1073
|
|
|
|
1074
|
6943 |
|
if ($cascade) { |
1075
|
|
|
$sql .= ' CASCADE'; |
1076
|
|
|
} |
1077
|
|
|
|
1078
|
6943 |
|
return $sql; |
1079
|
|
|
} |
1080
|
|
|
|
1081
|
|
|
/** |
1082
|
|
|
* {@inheritDoc} |
1083
|
|
|
*/ |
1084
|
|
|
public function getReadLockSQL() : string |
1085
|
|
|
{ |
1086
|
|
|
return 'FOR SHARE'; |
1087
|
|
|
} |
1088
|
|
|
|
1089
|
|
|
/** |
1090
|
|
|
* {@inheritDoc} |
1091
|
|
|
*/ |
1092
|
8176 |
|
protected function initializeDoctrineTypeMappings() : void |
1093
|
|
|
{ |
1094
|
8176 |
|
$this->doctrineTypeMapping = [ |
1095
|
|
|
'bigint' => 'bigint', |
1096
|
|
|
'bigserial' => 'bigint', |
1097
|
|
|
'bool' => 'boolean', |
1098
|
|
|
'boolean' => 'boolean', |
1099
|
|
|
'bpchar' => 'string', |
1100
|
|
|
'bytea' => 'blob', |
1101
|
|
|
'char' => 'string', |
1102
|
|
|
'date' => 'date', |
1103
|
|
|
'datetime' => 'datetime', |
1104
|
|
|
'decimal' => 'decimal', |
1105
|
|
|
'double' => 'float', |
1106
|
|
|
'double precision' => 'float', |
1107
|
|
|
'float' => 'float', |
1108
|
|
|
'float4' => 'float', |
1109
|
|
|
'float8' => 'float', |
1110
|
|
|
'inet' => 'string', |
1111
|
|
|
'int' => 'integer', |
1112
|
|
|
'int2' => 'smallint', |
1113
|
|
|
'int4' => 'integer', |
1114
|
|
|
'int8' => 'bigint', |
1115
|
|
|
'integer' => 'integer', |
1116
|
|
|
'interval' => 'string', |
1117
|
|
|
'json' => Type::JSON, |
|
|
|
|
1118
|
|
|
'money' => 'decimal', |
1119
|
|
|
'numeric' => 'decimal', |
1120
|
|
|
'serial' => 'integer', |
1121
|
|
|
'serial4' => 'integer', |
1122
|
|
|
'serial8' => 'bigint', |
1123
|
|
|
'real' => 'float', |
1124
|
|
|
'smallint' => 'smallint', |
1125
|
|
|
'text' => 'text', |
1126
|
|
|
'time' => 'time', |
1127
|
|
|
'timestamp' => 'datetime', |
1128
|
|
|
'timestamptz' => 'datetimetz', |
1129
|
|
|
'timetz' => 'time', |
1130
|
|
|
'tsvector' => 'text', |
1131
|
|
|
'uuid' => 'guid', |
1132
|
|
|
'varchar' => 'string', |
1133
|
|
|
'year' => 'date', |
1134
|
|
|
'_varchar' => 'string', |
1135
|
|
|
]; |
1136
|
8176 |
|
} |
1137
|
|
|
|
1138
|
|
|
/** |
1139
|
|
|
* {@inheritDoc} |
1140
|
|
|
*/ |
1141
|
8350 |
|
public function getVarcharMaxLength() : int |
1142
|
|
|
{ |
1143
|
8350 |
|
return 65535; |
1144
|
|
|
} |
1145
|
|
|
|
1146
|
|
|
/** |
1147
|
|
|
* {@inheritdoc} |
1148
|
|
|
*/ |
1149
|
3 |
|
public function getBinaryMaxLength() : int |
1150
|
|
|
{ |
1151
|
3 |
|
return 0; |
1152
|
|
|
} |
1153
|
|
|
|
1154
|
|
|
/** |
1155
|
|
|
* {@inheritdoc} |
1156
|
|
|
*/ |
1157
|
7381 |
|
public function getBinaryDefaultLength() : int |
1158
|
|
|
{ |
1159
|
7381 |
|
return 0; |
1160
|
|
|
} |
1161
|
|
|
|
1162
|
|
|
/** |
1163
|
|
|
* {@inheritdoc} |
1164
|
|
|
*/ |
1165
|
8797 |
|
public function hasNativeJsonType() : bool |
1166
|
|
|
{ |
1167
|
8797 |
|
return true; |
1168
|
|
|
} |
1169
|
|
|
|
1170
|
|
|
/** |
1171
|
|
|
* {@inheritDoc} |
1172
|
|
|
*/ |
1173
|
2830 |
|
protected function getReservedKeywordsClass() : string |
1174
|
|
|
{ |
1175
|
2830 |
|
return Keywords\PostgreSQLKeywords::class; |
1176
|
|
|
} |
1177
|
|
|
|
1178
|
|
|
/** |
1179
|
|
|
* {@inheritDoc} |
1180
|
|
|
*/ |
1181
|
3137 |
|
public function getBlobTypeDeclarationSQL(array $field) : string |
1182
|
|
|
{ |
1183
|
3137 |
|
return 'BYTEA'; |
1184
|
|
|
} |
1185
|
|
|
|
1186
|
|
|
/** |
1187
|
|
|
* {@inheritdoc} |
1188
|
|
|
*/ |
1189
|
8586 |
|
public function getDefaultValueDeclarationSQL(array $field) : string |
1190
|
|
|
{ |
1191
|
8586 |
|
if ($this->isSerialField($field)) { |
1192
|
8208 |
|
return ''; |
1193
|
|
|
} |
1194
|
|
|
|
1195
|
7653 |
|
return parent::getDefaultValueDeclarationSQL($field); |
1196
|
|
|
} |
1197
|
|
|
|
1198
|
|
|
/** |
1199
|
|
|
* {@inheritdoc} |
1200
|
|
|
*/ |
1201
|
|
|
public function supportsColumnCollation() : bool |
1202
|
|
|
{ |
1203
|
|
|
return true; |
1204
|
|
|
} |
1205
|
|
|
|
1206
|
|
|
/** |
1207
|
|
|
* {@inheritdoc} |
1208
|
|
|
*/ |
1209
|
7278 |
|
public function getColumnCollationDeclarationSQL(string $collation) : string |
1210
|
|
|
{ |
1211
|
7278 |
|
return 'COLLATE ' . $this->quoteSingleIdentifier($collation); |
1212
|
|
|
} |
1213
|
|
|
|
1214
|
|
|
/** |
1215
|
|
|
* {@inheritdoc} |
1216
|
|
|
*/ |
1217
|
2678 |
|
public function getJsonTypeDeclarationSQL(array $field) : string |
1218
|
|
|
{ |
1219
|
2678 |
|
return 'JSON'; |
1220
|
|
|
} |
1221
|
|
|
|
1222
|
|
|
/** |
1223
|
|
|
* @param mixed[] $field |
1224
|
|
|
*/ |
1225
|
8586 |
|
private function isSerialField(array $field) : bool |
1226
|
|
|
{ |
1227
|
8586 |
|
return isset($field['type'], $field['autoincrement']) |
1228
|
8586 |
|
&& $field['autoincrement'] === true |
1229
|
8586 |
|
&& $this->isNumericType($field['type']); |
1230
|
|
|
} |
1231
|
|
|
|
1232
|
|
|
/** |
1233
|
|
|
* Check whether the type of a column is changed in a way that invalidates the default value for the column |
1234
|
|
|
*/ |
1235
|
7545 |
|
private function typeChangeBreaksDefaultValue(ColumnDiff $columnDiff) : bool |
1236
|
|
|
{ |
1237
|
7545 |
|
if ($columnDiff->fromColumn === null) { |
1238
|
6565 |
|
return $columnDiff->hasChanged('type'); |
1239
|
|
|
} |
1240
|
|
|
|
1241
|
7378 |
|
$oldTypeIsNumeric = $this->isNumericType($columnDiff->fromColumn->getType()); |
1242
|
7378 |
|
$newTypeIsNumeric = $this->isNumericType($columnDiff->column->getType()); |
1243
|
|
|
|
1244
|
|
|
// default should not be changed when switching between numeric types and the default comes from a sequence |
1245
|
7378 |
|
return $columnDiff->hasChanged('type') |
1246
|
7378 |
|
&& ! ($oldTypeIsNumeric && $newTypeIsNumeric && $columnDiff->column->getAutoincrement()); |
1247
|
|
|
} |
1248
|
|
|
|
1249
|
8220 |
|
private function isNumericType(Type $type) : bool |
1250
|
|
|
{ |
1251
|
8220 |
|
return $type instanceof IntegerType || $type instanceof BigIntType; |
1252
|
|
|
} |
1253
|
|
|
|
1254
|
7548 |
|
private function getOldColumnComment(ColumnDiff $columnDiff) : ?string |
1255
|
|
|
{ |
1256
|
7548 |
|
if ($columnDiff->fromColumn === null) { |
1257
|
6568 |
|
return null; |
1258
|
|
|
} |
1259
|
|
|
|
1260
|
7378 |
|
return $this->getColumnComment($columnDiff->fromColumn); |
1261
|
|
|
} |
1262
|
|
|
} |
1263
|
|
|
|
This class constant has been deprecated. The supplier of the class has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.