1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Phinx |
4
|
|
|
* |
5
|
|
|
* (The MIT license) |
6
|
|
|
* Copyright (c) 2015 Rob Morgan |
7
|
|
|
* |
8
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
9
|
|
|
* of this software and associated * documentation files (the "Software"), to |
10
|
|
|
* deal in the Software without restriction, including without limitation the |
11
|
|
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
12
|
|
|
* sell copies of the Software, and to permit persons to whom the Software is |
13
|
|
|
* furnished to do so, subject to the following conditions: |
14
|
|
|
* |
15
|
|
|
* The above copyright notice and this permission notice shall be included in |
16
|
|
|
* all copies or substantial portions of the Software. |
17
|
|
|
* |
18
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
19
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
20
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
21
|
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
22
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
23
|
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
24
|
|
|
* IN THE SOFTWARE. |
25
|
|
|
* |
26
|
|
|
* @package Phinx |
27
|
|
|
* @subpackage Phinx\Db\Adapter |
28
|
|
|
*/ |
29
|
|
|
namespace Phinx\Db\Adapter; |
30
|
|
|
|
31
|
|
|
use Phinx\Db\Table; |
32
|
|
|
use Phinx\Db\Table\Column; |
33
|
|
|
use Phinx\Db\Table\Index; |
34
|
|
|
use Phinx\Db\Table\ForeignKey; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* Phinx MySQL Adapter. |
38
|
|
|
* |
39
|
|
|
* @author Rob Morgan <[email protected]> |
40
|
|
|
*/ |
41
|
|
|
class MysqlAdapter extends PdoAdapter implements AdapterInterface |
42
|
|
|
{ |
43
|
|
|
|
44
|
|
|
protected $signedColumnTypes = ['integer' => true, 'biginteger' => true, 'float' => true, 'decimal' => true, 'boolean' => true]; |
45
|
|
|
|
46
|
|
|
const TEXT_TINY = 255; |
47
|
|
|
const TEXT_SMALL = 255; /* deprecated, alias of TEXT_TINY */ |
48
|
|
|
const TEXT_REGULAR = 65535; |
49
|
|
|
const TEXT_MEDIUM = 16777215; |
50
|
|
|
const TEXT_LONG = 4294967295; |
51
|
|
|
|
52
|
|
|
// According to https://dev.mysql.com/doc/refman/5.0/en/blob.html BLOB sizes are the same as TEXT |
53
|
|
|
const BLOB_TINY = 255; |
54
|
|
|
const BLOB_SMALL = 255; /* deprecated, alias of BLOB_TINY */ |
55
|
|
|
const BLOB_REGULAR = 65535; |
56
|
|
|
const BLOB_MEDIUM = 16777215; |
57
|
|
|
const BLOB_LONG = 4294967295; |
58
|
|
|
|
59
|
|
|
const INT_TINY = 255; |
60
|
|
|
const INT_SMALL = 65535; |
61
|
|
|
const INT_MEDIUM = 16777215; |
62
|
|
|
const INT_REGULAR = 4294967295; |
63
|
|
|
const INT_BIG = 18446744073709551615; |
64
|
|
|
|
65
|
|
|
const TYPE_YEAR = 'year'; |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* {@inheritdoc} |
69
|
|
|
*/ |
70
|
80 |
|
public function connect() |
71
|
|
|
{ |
72
|
80 |
|
if ($this->connection === null) { |
73
|
80 |
|
if (!class_exists('PDO') || !in_array('mysql', \PDO::getAvailableDrivers(), true)) { |
74
|
|
|
// @codeCoverageIgnoreStart |
75
|
|
|
throw new \RuntimeException('You need to enable the PDO_Mysql extension for Phinx to run properly.'); |
76
|
|
|
// @codeCoverageIgnoreEnd |
77
|
|
|
} |
78
|
|
|
|
79
|
80 |
|
$db = null; |
80
|
80 |
|
$options = $this->getOptions(); |
81
|
|
|
|
82
|
80 |
|
$dsn = 'mysql:'; |
83
|
|
|
|
84
|
80 |
|
if (!empty($options['unix_socket'])) { |
85
|
|
|
// use socket connection |
86
|
|
|
$dsn .= 'unix_socket=' . $options['unix_socket']; |
87
|
|
|
} else { |
88
|
|
|
// use network connection |
89
|
80 |
|
$dsn .= 'host=' . $options['host']; |
90
|
80 |
|
if (!empty($options['port'])) { |
91
|
80 |
|
$dsn .= ';port=' . $options['port']; |
92
|
80 |
|
} |
93
|
|
|
} |
94
|
|
|
|
95
|
80 |
|
$dsn .= ';dbname=' . $options['name']; |
96
|
|
|
|
97
|
|
|
// charset support |
98
|
80 |
|
if (!empty($options['charset'])) { |
99
|
|
|
$dsn .= ';charset=' . $options['charset']; |
100
|
|
|
} |
101
|
|
|
|
102
|
80 |
|
$driverOptions = [\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION]; |
103
|
|
|
|
104
|
|
|
// support arbitrary \PDO::MYSQL_ATTR_* driver options and pass them to PDO |
105
|
|
|
// http://php.net/manual/en/ref.pdo-mysql.php#pdo-mysql.constants |
106
|
80 |
View Code Duplication |
foreach ($options as $key => $option) { |
|
|
|
|
107
|
80 |
|
if (strpos($key, 'mysql_attr_') === 0) { |
108
|
|
|
$driverOptions[constant('\PDO::' . strtoupper($key))] = $option; |
109
|
|
|
} |
110
|
80 |
|
} |
111
|
|
|
|
112
|
|
|
try { |
113
|
80 |
|
$db = new \PDO($dsn, $options['user'], $options['pass'], $driverOptions); |
114
|
80 |
|
} catch (\PDOException $exception) { |
115
|
1 |
|
throw new \InvalidArgumentException(sprintf( |
116
|
1 |
|
'There was a problem connecting to the database: %s', |
117
|
1 |
|
$exception->getMessage() |
118
|
1 |
|
)); |
119
|
|
|
} |
120
|
|
|
|
121
|
80 |
|
$this->setConnection($db); |
122
|
80 |
|
} |
123
|
80 |
|
} |
124
|
|
|
|
125
|
|
|
/** |
126
|
|
|
* {@inheritdoc} |
127
|
|
|
*/ |
128
|
81 |
|
public function disconnect() |
129
|
|
|
{ |
130
|
81 |
|
$this->connection = null; |
131
|
81 |
|
} |
132
|
|
|
|
133
|
|
|
/** |
134
|
|
|
* {@inheritdoc} |
135
|
|
|
*/ |
136
|
6 |
|
public function hasTransactions() |
137
|
|
|
{ |
138
|
6 |
|
return true; |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
/** |
142
|
|
|
* {@inheritdoc} |
143
|
|
|
*/ |
144
|
6 |
|
public function beginTransaction() |
145
|
|
|
{ |
146
|
6 |
|
$this->execute('START TRANSACTION'); |
147
|
6 |
|
} |
148
|
|
|
|
149
|
|
|
/** |
150
|
|
|
* {@inheritdoc} |
151
|
|
|
*/ |
152
|
6 |
|
public function commitTransaction() |
153
|
|
|
{ |
154
|
6 |
|
$this->execute('COMMIT'); |
155
|
6 |
|
} |
156
|
|
|
|
157
|
|
|
/** |
158
|
|
|
* {@inheritdoc} |
159
|
|
|
*/ |
160
|
1 |
|
public function rollbackTransaction() |
161
|
|
|
{ |
162
|
1 |
|
$this->execute('ROLLBACK'); |
163
|
1 |
|
} |
164
|
|
|
|
165
|
|
|
/** |
166
|
|
|
* {@inheritdoc} |
167
|
|
|
*/ |
168
|
112 |
|
public function quoteTableName($tableName) |
169
|
|
|
{ |
170
|
112 |
|
return str_replace('.', '`.`', $this->quoteColumnName($tableName)); |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
/** |
174
|
|
|
* {@inheritdoc} |
175
|
|
|
*/ |
176
|
112 |
|
public function quoteColumnName($columnName) |
177
|
|
|
{ |
178
|
112 |
|
return '`' . str_replace('`', '``', $columnName) . '`'; |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
/** |
182
|
|
|
* {@inheritdoc} |
183
|
|
|
*/ |
184
|
82 |
View Code Duplication |
public function hasTable($tableName) |
|
|
|
|
185
|
|
|
{ |
186
|
82 |
|
$options = $this->getOptions(); |
187
|
|
|
|
188
|
82 |
|
$exists = $this->fetchRow(sprintf( |
189
|
|
|
"SELECT TABLE_NAME |
190
|
|
|
FROM INFORMATION_SCHEMA.TABLES |
191
|
82 |
|
WHERE TABLE_SCHEMA = '%s' AND TABLE_NAME = '%s'", |
192
|
82 |
|
$options['name'], |
193
|
|
|
$tableName |
194
|
82 |
|
)); |
195
|
|
|
|
196
|
82 |
|
return !empty($exists); |
197
|
|
|
} |
198
|
|
|
|
199
|
|
|
/** |
200
|
|
|
* {@inheritdoc} |
201
|
|
|
*/ |
202
|
82 |
|
public function createTable(Table $table) |
203
|
|
|
{ |
204
|
|
|
// This method is based on the MySQL docs here: http://dev.mysql.com/doc/refman/5.1/en/create-index.html |
205
|
|
|
$defaultOptions = [ |
206
|
82 |
|
'engine' => 'InnoDB', |
207
|
|
|
'collation' => 'utf8_general_ci' |
208
|
82 |
|
]; |
209
|
82 |
|
$options = array_merge($defaultOptions, $table->getOptions()); |
210
|
|
|
|
211
|
|
|
// Add the default primary key |
212
|
82 |
|
$columns = $table->getPendingColumns(); |
213
|
82 |
|
if (!isset($options['id']) || (isset($options['id']) && $options['id'] === true)) { |
214
|
68 |
|
$column = new Column(); |
215
|
68 |
|
$column->setName('id') |
216
|
68 |
|
->setType('integer') |
217
|
68 |
|
->setSigned(isset($options['signed']) ? $options['signed'] : true) |
218
|
68 |
|
->setIdentity(true); |
219
|
|
|
|
220
|
68 |
|
array_unshift($columns, $column); |
221
|
68 |
|
$options['primary_key'] = 'id'; |
222
|
82 |
|
} elseif (isset($options['id']) && is_string($options['id'])) { |
223
|
|
|
// Handle id => "field_name" to support AUTO_INCREMENT |
224
|
2 |
|
$column = new Column(); |
225
|
2 |
|
$column->setName($options['id']) |
226
|
2 |
|
->setType('integer') |
227
|
2 |
|
->setIdentity(true); |
228
|
|
|
|
229
|
2 |
|
array_unshift($columns, $column); |
230
|
2 |
|
$options['primary_key'] = $options['id']; |
231
|
2 |
|
} |
232
|
|
|
|
233
|
|
|
// TODO - process table options like collation etc |
234
|
|
|
|
235
|
|
|
// process table engine (default to InnoDB) |
236
|
82 |
|
$optionsStr = 'ENGINE = InnoDB'; |
237
|
82 |
|
if (isset($options['engine'])) { |
238
|
82 |
|
$optionsStr = sprintf('ENGINE = %s', $options['engine']); |
239
|
82 |
|
} |
240
|
|
|
|
241
|
|
|
// process table collation |
242
|
82 |
|
if (isset($options['collation'])) { |
243
|
82 |
|
$charset = explode('_', $options['collation']); |
244
|
82 |
|
$optionsStr .= sprintf(' CHARACTER SET %s', $charset[0]); |
245
|
82 |
|
$optionsStr .= sprintf(' COLLATE %s', $options['collation']); |
246
|
82 |
|
} |
247
|
|
|
|
248
|
|
|
// set the table comment |
249
|
82 |
|
if (isset($options['comment'])) { |
250
|
2 |
|
$optionsStr .= sprintf(" COMMENT=%s ", $this->getConnection()->quote($options['comment'])); |
251
|
2 |
|
} |
252
|
|
|
|
253
|
82 |
|
$sql = 'CREATE TABLE '; |
254
|
82 |
|
$sql .= $this->quoteTableName($table->getName()) . ' ('; |
255
|
82 |
View Code Duplication |
foreach ($columns as $column) { |
|
|
|
|
256
|
82 |
|
$sql .= $this->quoteColumnName($column->getName()) . ' ' . $this->getColumnSqlDefinition($column) . ', '; |
257
|
82 |
|
} |
258
|
|
|
|
259
|
|
|
// set the primary key(s) |
260
|
82 |
View Code Duplication |
if (isset($options['primary_key'])) { |
|
|
|
|
261
|
82 |
|
$sql = rtrim($sql); |
262
|
82 |
|
$sql .= ' PRIMARY KEY ('; |
263
|
82 |
|
if (is_string($options['primary_key'])) { // handle primary_key => 'id' |
264
|
81 |
|
$sql .= $this->quoteColumnName($options['primary_key']); |
265
|
82 |
|
} elseif (is_array($options['primary_key'])) { // handle primary_key => array('tag_id', 'resource_id') |
|
|
|
|
266
|
|
|
// PHP 5.4 will allow access of $this, so we can call quoteColumnName() directly in the |
267
|
|
|
// anonymous function, but for now just hard-code the adapter quotes |
268
|
2 |
|
$sql .= implode( |
269
|
2 |
|
',', |
270
|
2 |
|
array_map( |
271
|
2 |
|
function ($v) { |
272
|
2 |
|
return '`' . $v . '`'; |
273
|
2 |
|
}, |
274
|
2 |
|
$options['primary_key'] |
275
|
2 |
|
) |
276
|
2 |
|
); |
277
|
2 |
|
} |
278
|
82 |
|
$sql .= ')'; |
279
|
82 |
|
} else { |
280
|
1 |
|
$sql = substr(rtrim($sql), 0, -1); // no primary keys |
281
|
|
|
} |
282
|
|
|
|
283
|
|
|
// set the indexes |
284
|
82 |
|
$indexes = $table->getIndexes(); |
285
|
82 |
|
foreach ($indexes as $index) { |
286
|
10 |
|
$sql .= ', ' . $this->getIndexSqlDefinition($index); |
287
|
82 |
|
} |
288
|
|
|
|
289
|
|
|
// set the foreign keys |
290
|
82 |
|
$foreignKeys = $table->getForeignKeys(); |
291
|
82 |
|
foreach ($foreignKeys as $foreignKey) { |
292
|
2 |
|
$sql .= ', ' . $this->getForeignKeySqlDefinition($foreignKey); |
293
|
82 |
|
} |
294
|
|
|
|
295
|
82 |
|
$sql .= ') ' . $optionsStr; |
296
|
82 |
|
$sql = rtrim($sql) . ';'; |
297
|
|
|
|
298
|
|
|
// execute the sql |
299
|
82 |
|
$this->execute($sql); |
300
|
82 |
|
} |
301
|
|
|
|
302
|
|
|
/** |
303
|
|
|
* {@inheritdoc} |
304
|
|
|
*/ |
305
|
5 |
|
public function renameTable($tableName, $newTableName) |
306
|
|
|
{ |
307
|
5 |
|
$this->execute(sprintf('RENAME TABLE %s TO %s', $this->quoteTableName($tableName), $this->quoteTableName($newTableName))); |
308
|
5 |
|
} |
309
|
|
|
|
310
|
|
|
/** |
311
|
|
|
* {@inheritdoc} |
312
|
|
|
*/ |
313
|
5 |
|
public function dropTable($tableName) |
314
|
|
|
{ |
315
|
5 |
|
$this->execute(sprintf('DROP TABLE %s', $this->quoteTableName($tableName))); |
316
|
5 |
|
} |
317
|
|
|
|
318
|
|
|
/** |
319
|
|
|
* {@inheritdoc} |
320
|
|
|
*/ |
321
|
1 |
|
public function truncateTable($tableName) |
322
|
|
|
{ |
323
|
1 |
|
$sql = sprintf( |
324
|
1 |
|
'TRUNCATE TABLE %s', |
325
|
1 |
|
$this->quoteTableName($tableName) |
326
|
1 |
|
); |
327
|
|
|
|
328
|
1 |
|
$this->execute($sql); |
329
|
1 |
|
} |
330
|
|
|
|
331
|
|
|
/** |
332
|
|
|
* {@inheritdoc} |
333
|
|
|
*/ |
334
|
12 |
|
public function getColumns($tableName) |
335
|
|
|
{ |
336
|
12 |
|
$columns = []; |
337
|
12 |
|
$rows = $this->fetchAll(sprintf('SHOW COLUMNS FROM %s', $this->quoteTableName($tableName))); |
338
|
12 |
|
foreach ($rows as $columnInfo) { |
339
|
12 |
|
$phinxType = $this->getPhinxType($columnInfo['Type']); |
340
|
|
|
|
341
|
12 |
|
$column = new Column(); |
342
|
12 |
|
$column->setName($columnInfo['Field']) |
343
|
12 |
|
->setNull($columnInfo['Null'] !== 'NO') |
344
|
12 |
|
->setDefault($columnInfo['Default']) |
345
|
12 |
|
->setType($phinxType['name']) |
346
|
12 |
|
->setLimit($phinxType['limit']); |
347
|
|
|
|
348
|
12 |
|
if ($columnInfo['Extra'] === 'auto_increment') { |
349
|
12 |
|
$column->setIdentity(true); |
350
|
12 |
|
} |
351
|
|
|
|
352
|
12 |
|
if (isset($phinxType['values'])) { |
353
|
3 |
|
$column->setValues($phinxType['values']); |
354
|
3 |
|
} |
355
|
|
|
|
356
|
12 |
|
$columns[] = $column; |
357
|
12 |
|
} |
358
|
|
|
|
359
|
12 |
|
return $columns; |
360
|
|
|
} |
361
|
|
|
|
362
|
|
|
/** |
363
|
|
|
* {@inheritdoc} |
364
|
|
|
*/ |
365
|
79 |
View Code Duplication |
public function hasColumn($tableName, $columnName) |
|
|
|
|
366
|
|
|
{ |
367
|
79 |
|
$rows = $this->fetchAll(sprintf('SHOW COLUMNS FROM %s', $this->quoteTableName($tableName))); |
368
|
79 |
|
foreach ($rows as $column) { |
369
|
79 |
|
if (strcasecmp($column['Field'], $columnName) === 0) { |
370
|
77 |
|
return true; |
371
|
|
|
} |
372
|
77 |
|
} |
373
|
|
|
|
374
|
21 |
|
return false; |
375
|
|
|
} |
376
|
|
|
|
377
|
|
|
/** |
378
|
|
|
* {@inheritdoc} |
379
|
|
|
*/ |
380
|
|
View Code Duplication |
public function addColumn(Table $table, Column $column) |
|
|
|
|
381
|
|
|
{ |
382
|
|
|
$sql = sprintf( |
383
|
95 |
|
'ALTER TABLE %s ADD %s %s', |
384
|
|
|
$this->quoteTableName($table->getName()), |
385
|
95 |
|
$this->quoteColumnName($column->getName()), |
386
|
10 |
|
$this->getColumnSqlDefinition($column) |
387
|
95 |
|
); |
388
|
79 |
|
|
389
|
79 |
|
if ($column->getAfter()) { |
390
|
95 |
|
$sql .= ' AFTER ' . $this->quoteColumnName($column->getAfter()); |
391
|
|
|
} |
392
|
|
|
|
393
|
|
|
$this->execute($sql); |
394
|
|
|
} |
395
|
|
|
|
396
|
18 |
|
/** |
397
|
|
|
* {@inheritdoc} |
398
|
18 |
|
*/ |
399
|
18 |
|
public function renameColumn($tableName, $columnName, $newColumnName) |
400
|
18 |
|
{ |
401
|
18 |
|
$rows = $this->fetchAll(sprintf('DESCRIBE %s', $this->quoteTableName($tableName))); |
402
|
18 |
|
foreach ($rows as $row) { |
403
|
18 |
|
if (strcasecmp($row['Field'], $columnName) === 0) { |
404
|
|
|
$null = ($row['Null'] == 'NO') ? 'NOT NULL' : 'NULL'; |
405
|
18 |
|
$extra = ' ' . strtoupper($row['Extra']); |
406
|
2 |
|
if (!is_null($row['Default'])) { |
407
|
2 |
|
$extra .= $this->getDefaultValueDefinition($row['Default']); |
408
|
|
|
} |
409
|
18 |
|
$definition = $row['Type'] . ' ' . $null . $extra; |
410
|
18 |
|
|
411
|
|
|
$this->execute( |
412
|
|
|
sprintf( |
413
|
|
|
'ALTER TABLE %s CHANGE COLUMN %s %s %s', |
414
|
|
|
$this->quoteTableName($tableName), |
415
|
7 |
|
$this->quoteColumnName($columnName), |
416
|
|
|
$this->quoteColumnName($newColumnName), |
417
|
7 |
|
$definition |
418
|
7 |
|
) |
419
|
7 |
|
); |
420
|
5 |
|
return; |
421
|
5 |
|
} |
422
|
5 |
|
} |
423
|
1 |
|
|
424
|
1 |
|
throw new \InvalidArgumentException(sprintf( |
425
|
5 |
|
'The specified column doesn\'t exist: ' |
426
|
|
|
. $columnName |
427
|
5 |
|
)); |
428
|
5 |
|
} |
429
|
5 |
|
|
430
|
5 |
|
/** |
431
|
5 |
|
* {@inheritdoc} |
432
|
5 |
|
*/ |
433
|
|
View Code Duplication |
public function changeColumn($tableName, $columnName, Column $newColumn) |
|
|
|
|
434
|
5 |
|
{ |
435
|
5 |
|
$after = $newColumn->getAfter() ? ' AFTER ' . $this->quoteColumnName($newColumn->getAfter()) : ''; |
436
|
5 |
|
$this->execute( |
437
|
|
|
sprintf( |
438
|
6 |
|
'ALTER TABLE %s CHANGE %s %s %s%s', |
439
|
|
|
$this->quoteTableName($tableName), |
440
|
2 |
|
$this->quoteColumnName($columnName), |
441
|
|
|
$this->quoteColumnName($newColumn->getName()), |
442
|
|
|
$this->getColumnSqlDefinition($newColumn), |
443
|
2 |
|
$after |
444
|
|
|
) |
445
|
|
|
); |
446
|
|
|
} |
447
|
|
|
|
448
|
|
|
/** |
449
|
5 |
|
* {@inheritdoc} |
450
|
|
|
*/ |
451
|
5 |
|
public function dropColumn($tableName, $columnName) |
452
|
5 |
|
{ |
453
|
5 |
|
$this->execute( |
454
|
5 |
|
sprintf( |
455
|
5 |
|
'ALTER TABLE %s DROP COLUMN %s', |
456
|
5 |
|
$this->quoteTableName($tableName), |
457
|
5 |
|
$this->quoteColumnName($columnName) |
458
|
5 |
|
) |
459
|
|
|
); |
460
|
5 |
|
} |
461
|
5 |
|
|
462
|
5 |
|
/** |
463
|
|
|
* Get an array of indexes from a particular table. |
464
|
|
|
* |
465
|
|
|
* @param string $tableName Table Name |
466
|
|
|
* @return array |
467
|
5 |
|
*/ |
468
|
|
View Code Duplication |
protected function getIndexes($tableName) |
|
|
|
|
469
|
5 |
|
{ |
470
|
5 |
|
$indexes = []; |
471
|
5 |
|
$rows = $this->fetchAll(sprintf('SHOW INDEXES FROM %s', $this->quoteTableName($tableName))); |
472
|
5 |
|
foreach ($rows as $row) { |
473
|
5 |
|
if (!isset($indexes[$row['Key_name']])) { |
474
|
5 |
|
$indexes[$row['Key_name']] = ['columns' => []]; |
475
|
5 |
|
} |
476
|
5 |
|
$indexes[$row['Key_name']]['columns'][] = strtolower($row['Column_name']); |
477
|
|
|
} |
478
|
|
|
return $indexes; |
479
|
|
|
} |
480
|
|
|
|
481
|
|
|
/** |
482
|
|
|
* {@inheritdoc} |
483
|
|
|
*/ |
484
|
19 |
|
public function hasIndex($tableName, $columns) |
485
|
|
|
{ |
486
|
19 |
|
if (is_string($columns)) { |
487
|
19 |
|
$columns = [$columns]; // str to array |
488
|
19 |
|
} |
489
|
18 |
|
|
490
|
18 |
|
$columns = array_map('strtolower', $columns); |
491
|
18 |
|
$indexes = $this->getIndexes($tableName); |
492
|
18 |
|
|
493
|
19 |
|
foreach ($indexes as $index) { |
494
|
19 |
|
if ($columns == $index['columns']) { |
495
|
|
|
return true; |
496
|
|
|
} |
497
|
|
|
} |
498
|
|
|
|
499
|
|
|
return false; |
500
|
14 |
|
} |
501
|
|
|
|
502
|
14 |
|
/** |
503
|
6 |
|
* {@inheritdoc} |
504
|
6 |
|
*/ |
505
|
|
View Code Duplication |
public function hasIndexByName($tableName, $indexName) |
|
|
|
|
506
|
14 |
|
{ |
507
|
14 |
|
$indexes = $this->getIndexes($tableName); |
508
|
|
|
|
509
|
14 |
|
foreach ($indexes as $name => $index) { |
510
|
14 |
|
if ($name === $indexName) { |
511
|
12 |
|
return true; |
512
|
|
|
} |
513
|
13 |
|
} |
514
|
|
|
|
515
|
11 |
|
return false; |
516
|
|
|
} |
517
|
|
|
|
518
|
|
|
/** |
519
|
|
|
* {@inheritdoc} |
520
|
|
|
*/ |
521
|
1 |
|
public function addIndex(Table $table, Index $index) |
522
|
|
|
{ |
523
|
1 |
|
$this->execute( |
524
|
|
|
sprintf( |
525
|
1 |
|
'ALTER TABLE %s ADD %s', |
526
|
1 |
|
$this->quoteTableName($table->getName()), |
527
|
1 |
|
$this->getIndexSqlDefinition($index) |
528
|
|
|
) |
529
|
1 |
|
); |
530
|
|
|
} |
531
|
|
|
|
532
|
|
|
/** |
533
|
|
|
* {@inheritdoc} |
534
|
|
|
*/ |
535
|
|
|
public function dropIndex($tableName, $columns) |
536
|
|
|
{ |
537
|
4 |
|
if (is_string($columns)) { |
538
|
|
|
$columns = [$columns]; // str to array |
539
|
4 |
|
} |
540
|
4 |
|
|
541
|
4 |
|
$indexes = $this->getIndexes($tableName); |
542
|
4 |
|
$columns = array_map('strtolower', $columns); |
543
|
4 |
|
|
544
|
4 |
|
foreach ($indexes as $indexName => $index) { |
545
|
4 |
|
if ($columns == $index['columns']) { |
546
|
4 |
|
$this->execute( |
547
|
|
|
sprintf( |
548
|
|
|
'ALTER TABLE %s DROP INDEX %s', |
549
|
|
|
$this->quoteTableName($tableName), |
550
|
|
|
$this->quoteColumnName($indexName) |
551
|
3 |
|
) |
552
|
|
|
); |
553
|
3 |
|
return; |
554
|
2 |
|
} |
555
|
2 |
|
} |
556
|
|
|
} |
557
|
3 |
|
|
558
|
3 |
|
/** |
559
|
|
|
* {@inheritdoc} |
560
|
3 |
|
*/ |
561
|
3 |
View Code Duplication |
public function dropIndexByName($tableName, $indexName) |
|
|
|
|
562
|
3 |
|
{ |
563
|
3 |
|
$indexes = $this->getIndexes($tableName); |
564
|
3 |
|
|
565
|
3 |
|
foreach ($indexes as $name => $index) { |
566
|
3 |
|
//$a = array_diff($columns, $index['columns']); |
|
|
|
|
567
|
3 |
|
if ($name === $indexName) { |
568
|
3 |
|
$this->execute( |
569
|
3 |
|
sprintf( |
570
|
|
|
'ALTER TABLE %s DROP INDEX %s', |
571
|
3 |
|
$this->quoteTableName($tableName), |
572
|
1 |
|
$this->quoteColumnName($indexName) |
573
|
|
|
) |
574
|
|
|
); |
575
|
|
|
return; |
576
|
|
|
} |
577
|
2 |
|
} |
578
|
|
|
} |
579
|
2 |
|
|
580
|
|
|
/** |
581
|
2 |
|
* {@inheritdoc} |
582
|
|
|
*/ |
583
|
2 |
View Code Duplication |
public function hasForeignKey($tableName, $columns, $constraint = null) |
|
|
|
|
584
|
2 |
|
{ |
585
|
2 |
|
if (is_string($columns)) { |
586
|
2 |
|
$columns = [$columns]; // str to array |
587
|
2 |
|
} |
588
|
2 |
|
$foreignKeys = $this->getForeignKeys($tableName); |
589
|
2 |
|
if ($constraint) { |
|
|
|
|
590
|
2 |
|
if (isset($foreignKeys[$constraint])) { |
591
|
2 |
|
return !empty($foreignKeys[$constraint]); |
592
|
|
|
} |
593
|
2 |
|
return false; |
594
|
|
|
} else { |
595
|
|
|
foreach ($foreignKeys as $key) { |
596
|
|
|
if ($columns == $key['columns']) { |
597
|
|
|
return true; |
598
|
|
|
} |
599
|
21 |
|
} |
600
|
|
|
return false; |
601
|
21 |
|
} |
602
|
5 |
|
} |
603
|
5 |
|
|
604
|
21 |
|
/** |
605
|
21 |
|
* Get an array of foreign keys from a particular table. |
606
|
6 |
|
* |
607
|
4 |
|
* @param string $tableName Table Name |
608
|
|
|
* @return array |
609
|
4 |
|
*/ |
610
|
|
View Code Duplication |
protected function getForeignKeys($tableName) |
|
|
|
|
611
|
15 |
|
{ |
612
|
12 |
|
$foreignKeys = []; |
613
|
10 |
|
$rows = $this->fetchAll(sprintf( |
614
|
|
|
"SELECT |
615
|
11 |
|
CONSTRAINT_NAME, |
616
|
11 |
|
TABLE_NAME, |
617
|
|
|
COLUMN_NAME, |
618
|
|
|
REFERENCED_TABLE_NAME, |
619
|
|
|
REFERENCED_COLUMN_NAME |
620
|
|
|
FROM information_schema.KEY_COLUMN_USAGE |
621
|
|
|
WHERE REFERENCED_TABLE_SCHEMA = DATABASE() |
622
|
|
|
AND REFERENCED_TABLE_NAME IS NOT NULL |
623
|
|
|
AND TABLE_NAME = '%s' |
624
|
|
|
ORDER BY POSITION_IN_UNIQUE_CONSTRAINT", |
625
|
|
|
$tableName |
626
|
22 |
|
)); |
627
|
|
|
foreach ($rows as $row) { |
628
|
22 |
|
$foreignKeys[$row['CONSTRAINT_NAME']]['table'] = $row['TABLE_NAME']; |
629
|
22 |
|
$foreignKeys[$row['CONSTRAINT_NAME']]['columns'][] = $row['COLUMN_NAME']; |
630
|
|
|
$foreignKeys[$row['CONSTRAINT_NAME']]['referenced_table'] = $row['REFERENCED_TABLE_NAME']; |
631
|
|
|
$foreignKeys[$row['CONSTRAINT_NAME']]['referenced_columns'][] = $row['REFERENCED_COLUMN_NAME']; |
632
|
|
|
} |
633
|
|
|
return $foreignKeys; |
634
|
|
|
} |
635
|
|
|
|
636
|
|
|
/** |
637
|
|
|
* {@inheritdoc} |
638
|
|
|
*/ |
639
|
|
|
public function addForeignKey(Table $table, ForeignKey $foreignKey) |
640
|
22 |
|
{ |
641
|
|
|
$this->execute( |
642
|
22 |
|
sprintf( |
643
|
22 |
|
'ALTER TABLE %s ADD %s', |
644
|
19 |
|
$this->quoteTableName($table->getName()), |
645
|
19 |
|
$this->getForeignKeySqlDefinition($foreignKey) |
646
|
19 |
|
) |
647
|
19 |
|
); |
648
|
22 |
|
} |
649
|
22 |
|
|
650
|
|
|
/** |
651
|
|
|
* {@inheritdoc} |
652
|
|
|
*/ |
653
|
|
View Code Duplication |
public function dropForeignKey($tableName, $columns, $constraint = null) |
|
|
|
|
654
|
|
|
{ |
655
|
15 |
|
if (is_string($columns)) { |
656
|
|
|
$columns = [$columns]; // str to array |
657
|
15 |
|
} |
658
|
15 |
|
|
659
|
15 |
|
|
660
|
15 |
|
if ($constraint) { |
|
|
|
|
661
|
15 |
|
$this->execute( |
662
|
15 |
|
sprintf( |
663
|
15 |
|
'ALTER TABLE %s DROP FOREIGN KEY %s', |
664
|
15 |
|
$this->quoteTableName($tableName), |
665
|
|
|
$constraint |
666
|
|
|
) |
667
|
|
|
); |
668
|
|
|
return; |
669
|
8 |
|
} else { |
670
|
|
|
foreach ($columns as $column) { |
671
|
8 |
|
$rows = $this->fetchAll(sprintf( |
672
|
3 |
|
"SELECT |
673
|
3 |
|
CONSTRAINT_NAME |
674
|
|
|
FROM information_schema.KEY_COLUMN_USAGE |
675
|
|
|
WHERE REFERENCED_TABLE_SCHEMA = DATABASE() |
676
|
8 |
|
AND REFERENCED_TABLE_NAME IS NOT NULL |
677
|
8 |
|
AND TABLE_NAME = '%s' |
678
|
8 |
|
AND COLUMN_NAME = '%s' |
679
|
8 |
|
ORDER BY POSITION_IN_UNIQUE_CONSTRAINT", |
680
|
8 |
|
$tableName, |
681
|
|
|
$column |
682
|
8 |
|
)); |
683
|
8 |
|
foreach ($rows as $row) { |
684
|
8 |
|
$this->dropForeignKey($tableName, $columns, $row['CONSTRAINT_NAME']); |
685
|
|
|
} |
686
|
7 |
|
} |
687
|
7 |
|
} |
688
|
|
|
} |
689
|
|
|
|
690
|
|
|
/** |
691
|
|
|
* {@inheritdoc} |
692
|
|
|
*/ |
693
|
|
|
public function getSqlType($type, $limit = null) |
694
|
|
|
{ |
695
|
7 |
|
switch ($type) { |
696
|
7 |
|
case static::PHINX_TYPE_STRING: |
697
|
|
|
return ['name' => 'varchar', 'limit' => $limit ? $limit : 255]; |
698
|
7 |
|
break; |
|
|
|
|
699
|
7 |
|
case static::PHINX_TYPE_CHAR: |
700
|
7 |
|
return ['name' => 'char', 'limit' => $limit ? $limit : 255]; |
701
|
7 |
|
break; |
|
|
|
|
702
|
7 |
View Code Duplication |
case static::PHINX_TYPE_TEXT: |
|
|
|
|
703
|
|
|
if ($limit) { |
|
|
|
|
704
|
7 |
|
$sizes = [ |
705
|
|
|
// Order matters! Size must always be tested from longest to shortest! |
706
|
|
|
'longtext' => static::TEXT_LONG, |
707
|
|
|
'mediumtext' => static::TEXT_MEDIUM, |
708
|
|
|
'text' => static::TEXT_REGULAR, |
709
|
96 |
|
'tinytext' => static::TEXT_SMALL, |
710
|
|
|
]; |
711
|
|
|
foreach ($sizes as $name => $length) { |
712
|
96 |
|
if ($limit >= $length) { |
713
|
87 |
|
return ['name' => $name]; |
714
|
|
|
} |
715
|
96 |
|
} |
716
|
4 |
|
} |
717
|
|
|
return ['name' => 'text']; |
718
|
96 |
|
break; |
|
|
|
|
719
|
9 |
|
case static::PHINX_TYPE_BINARY: |
720
|
|
|
return ['name' => 'binary', 'limit' => $limit ? $limit : 255]; |
721
|
|
|
break; |
|
|
|
|
722
|
6 |
|
case static::PHINX_TYPE_VARBINARY: |
723
|
6 |
|
return ['name' => 'varbinary', 'limit' => $limit ? $limit : 255]; |
724
|
6 |
|
break; |
|
|
|
|
725
|
6 |
View Code Duplication |
case static::PHINX_TYPE_BLOB: |
|
|
|
|
726
|
6 |
|
if ($limit) { |
|
|
|
|
727
|
6 |
|
$sizes = [ |
728
|
6 |
|
// Order matters! Size must always be tested from longest to shortest! |
729
|
6 |
|
'longblob' => static::BLOB_LONG, |
730
|
|
|
'mediumblob' => static::BLOB_MEDIUM, |
731
|
5 |
|
'blob' => static::BLOB_REGULAR, |
732
|
|
|
'tinyblob' => static::BLOB_SMALL, |
733
|
5 |
|
]; |
734
|
|
|
foreach ($sizes as $name => $length) { |
735
|
95 |
|
if ($limit >= $length) { |
736
|
5 |
|
return ['name' => $name]; |
737
|
|
|
} |
738
|
95 |
|
} |
739
|
3 |
|
} |
740
|
|
|
return ['name' => 'blob']; |
741
|
95 |
|
break; |
|
|
|
|
742
|
1 |
|
case static::PHINX_TYPE_INTEGER: |
743
|
|
|
if ($limit && $limit >= static::INT_TINY) { |
|
|
|
|
744
|
|
|
$sizes = [ |
745
|
1 |
|
// Order matters! Size must always be tested from longest to shortest! |
746
|
1 |
|
'bigint' => static::INT_BIG, |
747
|
1 |
|
'int' => static::INT_REGULAR, |
748
|
1 |
|
'mediumint' => static::INT_MEDIUM, |
749
|
1 |
|
'smallint' => static::INT_SMALL, |
750
|
1 |
|
'tinyint' => static::INT_TINY, |
751
|
1 |
|
]; |
752
|
1 |
|
$limits = [ |
753
|
|
|
'int' => 11, |
754
|
1 |
|
'bigint' => 20, |
755
|
|
|
]; |
756
|
1 |
|
foreach ($sizes as $name => $length) { |
757
|
|
|
if ($limit >= $length) { |
758
|
95 |
|
$def = ['name' => $name]; |
759
|
82 |
|
if (isset($limits[$name])) { |
760
|
|
|
$def['limit'] = $limits[$name]; |
761
|
|
|
} |
762
|
6 |
|
return $def; |
763
|
6 |
|
} |
764
|
6 |
|
} |
765
|
6 |
|
} elseif (!$limit) { |
|
|
|
|
766
|
6 |
|
$limit = 11; |
767
|
6 |
|
} |
768
|
|
|
return ['name' => 'int', 'limit' => $limit]; |
769
|
6 |
|
break; |
|
|
|
|
770
|
6 |
|
case static::PHINX_TYPE_BIG_INTEGER: |
771
|
6 |
|
return ['name' => 'bigint', 'limit' => 20]; |
772
|
6 |
|
break; |
|
|
|
|
773
|
6 |
|
case static::PHINX_TYPE_FLOAT: |
774
|
6 |
|
return ['name' => 'float']; |
775
|
6 |
|
break; |
|
|
|
|
776
|
2 |
|
case static::PHINX_TYPE_DECIMAL: |
777
|
2 |
|
return ['name' => 'decimal']; |
778
|
6 |
|
break; |
|
|
|
|
779
|
|
|
case static::PHINX_TYPE_DATETIME: |
780
|
5 |
|
return ['name' => 'datetime']; |
781
|
82 |
|
break; |
|
|
|
|
782
|
76 |
|
case static::PHINX_TYPE_TIMESTAMP: |
783
|
76 |
|
return ['name' => 'timestamp']; |
784
|
82 |
|
break; |
|
|
|
|
785
|
|
|
case static::PHINX_TYPE_TIME: |
786
|
86 |
|
return ['name' => 'time']; |
787
|
82 |
|
break; |
|
|
|
|
788
|
|
|
case static::PHINX_TYPE_DATE: |
789
|
86 |
|
return ['name' => 'date']; |
790
|
7 |
|
break; |
|
|
|
|
791
|
|
|
case static::PHINX_TYPE_BOOLEAN: |
792
|
84 |
|
return ['name' => 'tinyint', 'limit' => 1]; |
793
|
5 |
|
break; |
|
|
|
|
794
|
|
|
case static::PHINX_TYPE_UUID: |
795
|
83 |
|
return ['name' => 'char', 'limit' => 36]; |
796
|
7 |
|
// Geospatial database types |
797
|
|
|
case static::PHINX_TYPE_GEOMETRY: |
798
|
83 |
|
case static::PHINX_TYPE_POINT: |
799
|
80 |
|
case static::PHINX_TYPE_LINESTRING: |
800
|
|
|
case static::PHINX_TYPE_POLYGON: |
801
|
83 |
|
return ['name' => $type]; |
802
|
4 |
|
case static::PHINX_TYPE_ENUM: |
803
|
|
|
return ['name' => 'enum']; |
804
|
83 |
|
break; |
|
|
|
|
805
|
4 |
|
case static::PHINX_TYPE_SET: |
806
|
|
|
return ['name' => 'set']; |
807
|
83 |
|
break; |
|
|
|
|
808
|
80 |
|
case static::TYPE_YEAR: |
809
|
|
|
if (!$limit || in_array($limit, [2, 4])) { |
|
|
|
|
810
|
10 |
|
$limit = 4; |
811
|
2 |
|
} |
812
|
|
|
return ['name' => 'year', 'limit' => $limit]; |
813
|
10 |
|
break; |
|
|
|
|
814
|
10 |
|
case static::PHINX_TYPE_JSON: |
815
|
10 |
|
return ['name' => 'json']; |
816
|
10 |
|
break; |
|
|
|
|
817
|
5 |
|
default: |
818
|
8 |
|
throw new \RuntimeException('The type: "' . $type . '" is not supported.'); |
819
|
5 |
|
} |
820
|
|
|
} |
821
|
6 |
|
|
822
|
4 |
|
/** |
823
|
|
|
* Returns Phinx type by SQL type |
824
|
2 |
|
* |
825
|
|
|
* @param string $sqlTypeDef |
826
|
|
|
* @throws \RuntimeException |
827
|
|
|
* @internal param string $sqlType SQL type |
828
|
|
|
* @returns string Phinx type |
829
|
|
|
*/ |
830
|
2 |
|
public function getPhinxType($sqlTypeDef) |
831
|
|
|
{ |
832
|
|
|
$matches = []; |
833
|
2 |
|
if (!preg_match('/^([\w]+)(\(([\d]+)*(,([\d]+))*\))*(.+)*$/', $sqlTypeDef, $matches)) { |
834
|
2 |
|
throw new \RuntimeException('Column type ' . $sqlTypeDef . ' is not supported'); |
835
|
2 |
|
} else { |
836
|
|
|
$limit = null; |
837
|
|
|
$precision = null; |
838
|
|
|
$type = $matches[1]; |
839
|
|
View Code Duplication |
if (count($matches) > 2) { |
|
|
|
|
840
|
|
|
$limit = $matches[3] ? (int) $matches[3] : null; |
841
|
|
|
} |
842
|
|
|
if (count($matches) > 4) { |
843
|
|
|
$precision = (int) $matches[5]; |
844
|
|
|
} |
845
|
|
View Code Duplication |
if ($type === 'tinyint' && $limit === 1) { |
|
|
|
|
846
|
17 |
|
$type = static::PHINX_TYPE_BOOLEAN; |
847
|
|
|
$limit = null; |
848
|
17 |
|
} |
849
|
17 |
|
switch ($type) { |
850
|
1 |
|
case 'varchar': |
851
|
|
|
$type = static::PHINX_TYPE_STRING; |
852
|
16 |
|
if ($limit === 255) { |
853
|
16 |
|
$limit = null; |
854
|
16 |
|
} |
855
|
16 |
|
break; |
856
|
14 |
View Code Duplication |
case 'char': |
|
|
|
|
857
|
14 |
|
$type = static::PHINX_TYPE_CHAR; |
858
|
16 |
|
if ($limit === 255) { |
859
|
4 |
|
$limit = null; |
860
|
4 |
|
} |
861
|
16 |
|
if ($limit === 36) { |
862
|
3 |
|
$type = static::PHINX_TYPE_UUID; |
863
|
3 |
|
} |
864
|
3 |
|
break; |
865
|
|
|
case 'tinyint': |
866
|
16 |
|
$type = static::PHINX_TYPE_INTEGER; |
867
|
6 |
|
$limit = static::INT_TINY; |
868
|
6 |
|
break; |
869
|
3 |
|
case 'smallint': |
870
|
3 |
|
$type = static::PHINX_TYPE_INTEGER; |
871
|
6 |
|
$limit = static::INT_SMALL; |
872
|
16 |
|
break; |
873
|
5 |
|
case 'mediumint': |
874
|
5 |
|
$type = static::PHINX_TYPE_INTEGER; |
875
|
1 |
|
$limit = static::INT_MEDIUM; |
876
|
1 |
|
break; |
877
|
5 |
|
case 'int': |
878
|
2 |
|
$type = static::PHINX_TYPE_INTEGER; |
879
|
2 |
|
if ($limit === 11) { |
880
|
5 |
|
$limit = null; |
881
|
16 |
|
} |
882
|
2 |
|
break; |
883
|
2 |
|
case 'bigint': |
884
|
2 |
|
if ($limit === 20) { |
885
|
16 |
|
$limit = null; |
886
|
2 |
|
} |
887
|
2 |
|
$type = static::PHINX_TYPE_BIG_INTEGER; |
888
|
2 |
|
break; |
889
|
16 |
|
case 'blob': |
890
|
2 |
|
$type = static::PHINX_TYPE_BINARY; |
891
|
2 |
|
break; |
892
|
2 |
|
case 'tinyblob': |
893
|
16 |
|
$type = static::PHINX_TYPE_BINARY; |
894
|
15 |
|
$limit = static::BLOB_TINY; |
895
|
15 |
|
break; |
896
|
12 |
|
case 'mediumblob': |
897
|
12 |
|
$type = static::PHINX_TYPE_BINARY; |
898
|
15 |
|
$limit = static::BLOB_MEDIUM; |
899
|
11 |
|
break; |
900
|
6 |
|
case 'longblob': |
901
|
4 |
|
$type = static::PHINX_TYPE_BINARY; |
902
|
4 |
|
$limit = static::BLOB_LONG; |
903
|
6 |
|
break; |
904
|
6 |
|
case 'tinytext': |
905
|
10 |
|
$type = static::PHINX_TYPE_TEXT; |
906
|
2 |
|
$limit = static::TEXT_TINY; |
907
|
2 |
|
break; |
908
|
10 |
|
case 'mediumtext': |
909
|
1 |
|
$type = static::PHINX_TYPE_TEXT; |
910
|
1 |
|
$limit = static::TEXT_MEDIUM; |
911
|
1 |
|
break; |
912
|
10 |
|
case 'longtext': |
913
|
1 |
|
$type = static::PHINX_TYPE_TEXT; |
914
|
1 |
|
$limit = static::TEXT_LONG; |
915
|
1 |
|
break; |
916
|
10 |
|
} |
917
|
1 |
|
|
918
|
1 |
|
// Call this to check if parsed type is supported. |
919
|
1 |
|
$this->getSqlType($type, $limit); |
920
|
10 |
|
|
921
|
2 |
|
$phinxType = [ |
922
|
2 |
|
'name' => $type, |
923
|
2 |
|
'limit' => $limit, |
924
|
9 |
|
'precision' => $precision |
925
|
2 |
|
]; |
926
|
2 |
|
|
927
|
2 |
|
if (static::PHINX_TYPE_ENUM == $type) { |
928
|
8 |
|
$phinxType['values'] = explode("','", trim($matches[6], "()'")); |
929
|
2 |
|
} |
930
|
2 |
|
|
931
|
2 |
|
return $phinxType; |
932
|
|
|
} |
933
|
|
|
} |
934
|
|
|
|
935
|
16 |
|
/** |
936
|
|
|
* {@inheritdoc} |
937
|
|
|
*/ |
938
|
15 |
|
public function createDatabase($name, $options = []) |
939
|
15 |
|
{ |
940
|
|
|
$charset = isset($options['charset']) ? $options['charset'] : 'utf8'; |
941
|
15 |
|
|
942
|
|
View Code Duplication |
if (isset($options['collation'])) { |
|
|
|
|
943
|
15 |
|
$this->execute(sprintf('CREATE DATABASE `%s` DEFAULT CHARACTER SET `%s` COLLATE `%s`', $name, $charset, $options['collation'])); |
944
|
3 |
|
} else { |
945
|
3 |
|
$this->execute(sprintf('CREATE DATABASE `%s` DEFAULT CHARACTER SET `%s`', $name, $charset)); |
946
|
|
|
} |
947
|
15 |
|
} |
948
|
|
|
|
949
|
|
|
/** |
950
|
|
|
* {@inheritdoc} |
951
|
|
|
*/ |
952
|
|
|
public function hasDatabase($name) |
953
|
|
|
{ |
954
|
83 |
|
$rows = $this->fetchAll( |
955
|
|
|
sprintf( |
956
|
83 |
|
'SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = \'%s\'', |
957
|
|
|
$name |
958
|
83 |
|
) |
959
|
1 |
|
); |
960
|
1 |
|
|
961
|
82 |
|
foreach ($rows as $row) { |
962
|
|
|
if (!empty($row)) { |
963
|
83 |
|
return true; |
964
|
|
|
} |
965
|
|
|
} |
966
|
|
|
|
967
|
|
|
return false; |
968
|
4 |
|
} |
969
|
|
|
|
970
|
4 |
|
/** |
971
|
4 |
|
* {@inheritdoc} |
972
|
4 |
|
*/ |
973
|
|
|
public function dropDatabase($name) |
974
|
4 |
|
{ |
975
|
4 |
|
$this->execute(sprintf('DROP DATABASE IF EXISTS `%s`', $name)); |
976
|
|
|
} |
977
|
4 |
|
|
978
|
3 |
|
/** |
979
|
3 |
|
* Gets the MySQL Column Definition for a Column object. |
980
|
|
|
* |
981
|
3 |
|
* @param Column $column Column |
982
|
|
|
* @return string |
983
|
3 |
|
*/ |
984
|
|
|
protected function getColumnSqlDefinition(Column $column) |
985
|
|
|
{ |
986
|
|
|
$isCustomColumn = $column instanceof Table\CustomColumn; |
987
|
|
|
if ($isCustomColumn) { |
988
|
|
|
$def = $column->getType(); |
989
|
81 |
|
} else { |
990
|
|
|
$def = ''; |
991
|
81 |
|
$sqlType = $this->getSqlType($column->getType(), $column->getLimit()); |
992
|
81 |
|
$def .= strtoupper($sqlType['name']); |
993
|
|
|
if ($column->getPrecision() && $column->getScale()) { |
994
|
|
|
$def .= '(' . $column->getPrecision() . ',' . $column->getScale() . ')'; |
995
|
|
|
} elseif (isset($sqlType['limit'])) { |
996
|
|
|
$def .= '(' . $sqlType['limit'] . ')'; |
997
|
|
|
} |
998
|
|
View Code Duplication |
if (($values = $column->getValues()) && is_array($values)) { |
|
|
|
|
999
|
|
|
$def .= "('" . implode("', '", $values) . "')"; |
1000
|
89 |
|
} |
1001
|
|
|
$def .= $column->getEncoding() ? ' CHARACTER SET ' . $column->getEncoding() : ''; |
1002
|
89 |
|
$def .= $column->getCollation() ? ' COLLATE ' . $column->getCollation() : ''; |
1003
|
|
|
$def .= (!$column->isSigned() && isset($this->signedColumnTypes[$column->getType()])) ? ' unsigned' : ''; |
1004
|
89 |
|
} |
1005
|
89 |
|
$def .= ($column->isNull() == false) ? ' NOT NULL' : ' NULL'; |
|
|
|
|
1006
|
89 |
|
if (!$isCustomColumn) { |
1007
|
2 |
|
$def .= $column->isIdentity() ? ' AUTO_INCREMENT' : ''; |
1008
|
89 |
|
} |
1009
|
86 |
|
$def .= $this->getDefaultValueDefinition($column->getDefault()); |
1010
|
86 |
|
|
1011
|
89 |
|
if ($column->getComment()) { |
1012
|
5 |
|
$def .= ' COMMENT ' . $this->getConnection()->quote($column->getComment()); |
1013
|
5 |
|
} |
1014
|
89 |
|
|
1015
|
89 |
|
if ($column->getUpdate()) { |
1016
|
89 |
|
$def .= ' ON UPDATE ' . $column->getUpdate(); |
1017
|
89 |
|
} |
1018
|
89 |
|
|
1019
|
89 |
|
return $def; |
1020
|
|
|
} |
1021
|
89 |
|
|
1022
|
2 |
|
/** |
1023
|
2 |
|
* Gets the MySQL Index Definition for an Index object. |
1024
|
|
|
* |
1025
|
89 |
|
* @param Index $index Index |
1026
|
1 |
|
* @return string |
1027
|
1 |
|
*/ |
1028
|
|
|
protected function getIndexSqlDefinition(Index $index) |
1029
|
89 |
|
{ |
1030
|
|
|
$def = ''; |
1031
|
|
|
$limit = ''; |
1032
|
|
|
if ($index->getLimit()) { |
1033
|
|
|
$limit = '(' . $index->getLimit() . ')'; |
1034
|
|
|
} |
1035
|
|
|
|
1036
|
|
|
if ($index->getType() == Index::UNIQUE) { |
1037
|
|
|
$def .= ' UNIQUE'; |
1038
|
16 |
|
} |
1039
|
|
|
|
1040
|
16 |
|
if ($index->getType() == Index::FULLTEXT) { |
1041
|
16 |
|
$def .= ' FULLTEXT'; |
1042
|
16 |
|
} |
1043
|
2 |
|
|
1044
|
2 |
|
$def .= ' KEY'; |
1045
|
|
|
|
1046
|
16 |
|
if (is_string($index->getName())) { |
1047
|
5 |
|
$def .= ' `' . $index->getName() . '`'; |
1048
|
5 |
|
} |
1049
|
|
|
|
1050
|
16 |
|
$def .= ' (`' . implode('`,`', $index->getColumns()) . '`' . $limit . ')'; |
1051
|
1 |
|
|
1052
|
1 |
|
return $def; |
1053
|
|
|
} |
1054
|
16 |
|
|
1055
|
|
|
/** |
1056
|
16 |
|
* Gets the MySQL Foreign Key Definition for an ForeignKey object. |
1057
|
5 |
|
* |
1058
|
5 |
|
* @param ForeignKey $foreignKey |
1059
|
|
|
* @return string |
1060
|
16 |
|
*/ |
1061
|
|
View Code Duplication |
protected function getForeignKeySqlDefinition(ForeignKey $foreignKey) |
|
|
|
|
1062
|
16 |
|
{ |
1063
|
|
|
$def = ''; |
1064
|
|
|
if ($foreignKey->getConstraint()) { |
1065
|
|
|
$def .= ' CONSTRAINT ' . $this->quoteColumnName($foreignKey->getConstraint()); |
|
|
|
|
1066
|
|
|
} |
1067
|
|
|
$columnNames = []; |
1068
|
|
|
foreach ($foreignKey->getColumns() as $column) { |
1069
|
|
|
$columnNames[] = $this->quoteColumnName($column); |
1070
|
|
|
} |
1071
|
17 |
|
$def .= ' FOREIGN KEY (' . implode(',', $columnNames) . ')'; |
1072
|
|
|
$refColumnNames = []; |
1073
|
17 |
|
foreach ($foreignKey->getReferencedColumns() as $column) { |
1074
|
17 |
|
$refColumnNames[] = $this->quoteColumnName($column); |
1075
|
5 |
|
} |
1076
|
5 |
|
$def .= ' REFERENCES ' . $this->quoteTableName($foreignKey->getReferencedTable()->getName()) . ' (' . implode(',', $refColumnNames) . ')'; |
1077
|
17 |
|
if ($foreignKey->getOnDelete()) { |
1078
|
17 |
|
$def .= ' ON DELETE ' . $foreignKey->getOnDelete(); |
1079
|
17 |
|
} |
1080
|
17 |
|
if ($foreignKey->getOnUpdate()) { |
1081
|
17 |
|
$def .= ' ON UPDATE ' . $foreignKey->getOnUpdate(); |
1082
|
17 |
|
} |
1083
|
17 |
|
return $def; |
1084
|
17 |
|
} |
1085
|
17 |
|
|
1086
|
17 |
|
/** |
1087
|
17 |
|
* Describes a database table. This is a MySQL adapter specific method. |
1088
|
2 |
|
* |
1089
|
2 |
|
* @param string $tableName Table name |
1090
|
17 |
|
* @return array |
1091
|
2 |
|
*/ |
1092
|
2 |
View Code Duplication |
public function describeTable($tableName) |
|
|
|
|
1093
|
17 |
|
{ |
1094
|
|
|
$options = $this->getOptions(); |
1095
|
|
|
|
1096
|
|
|
// mysql specific |
1097
|
|
|
$sql = sprintf( |
1098
|
|
|
"SELECT * |
1099
|
|
|
FROM information_schema.tables |
1100
|
|
|
WHERE table_schema = '%s' |
1101
|
|
|
AND table_name = '%s'", |
1102
|
2 |
|
$options['name'], |
1103
|
|
|
$tableName |
1104
|
2 |
|
); |
1105
|
|
|
|
1106
|
|
|
return $this->fetchRow($sql); |
1107
|
2 |
|
} |
1108
|
|
|
|
1109
|
|
|
/** |
1110
|
|
|
* Returns MySQL column types (inherited and MySQL specified). |
1111
|
2 |
|
* @return array |
1112
|
2 |
|
*/ |
1113
|
|
|
public function getColumnTypes() |
1114
|
2 |
|
{ |
1115
|
|
|
return array_merge(parent::getColumnTypes(), ['enum', 'set', 'year', 'json']); |
1116
|
2 |
|
} |
1117
|
|
|
} |
1118
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.