1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Lagdo\DbAdmin\DbAdmin; |
4
|
|
|
|
5
|
|
|
use Lagdo\DbAdmin\Driver\Entity\TableFieldEntity; |
6
|
|
|
use Lagdo\DbAdmin\Driver\Entity\ForeignKeyEntity; |
7
|
|
|
|
8
|
|
|
use Exception; |
9
|
|
|
|
10
|
|
|
/** |
11
|
|
|
* Admin table functions |
12
|
|
|
*/ |
13
|
|
|
class TableAdmin extends AbstractAdmin |
14
|
|
|
{ |
15
|
|
|
/** |
16
|
|
|
* The current table status |
17
|
|
|
* |
18
|
|
|
* @var mixed |
19
|
|
|
*/ |
20
|
|
|
protected $tableStatus = null; |
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* @var array |
24
|
|
|
*/ |
25
|
|
|
protected $referencableTables = []; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* @var array |
29
|
|
|
*/ |
30
|
|
|
protected $foreignKeys = []; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* Get the current table status |
34
|
|
|
* |
35
|
|
|
* @param string $table |
36
|
|
|
* |
37
|
|
|
* @return mixed |
38
|
|
|
*/ |
39
|
|
|
protected function status(string $table) |
40
|
|
|
{ |
41
|
|
|
if (!$this->tableStatus) { |
42
|
|
|
$this->tableStatus = $this->driver->tableStatusOrName($table, true); |
43
|
|
|
} |
44
|
|
|
return $this->tableStatus; |
45
|
|
|
} |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* Print links after select heading |
49
|
|
|
* Copied from selectLinks() in adminer.inc.php |
50
|
|
|
* |
51
|
|
|
* @param string $set New item options, NULL for no new item |
52
|
|
|
* |
53
|
|
|
* @return array |
54
|
|
|
*/ |
55
|
|
|
protected function getTableLinks($set = null) |
56
|
|
|
{ |
57
|
|
|
$links = [ |
58
|
|
|
'select' => $this->trans->lang('Select data'), |
59
|
|
|
]; |
60
|
|
|
if ($this->driver->support('table') || $this->driver->support('indexes')) { |
61
|
|
|
$links['table'] = $this->trans->lang('Show structure'); |
62
|
|
|
} |
63
|
|
|
if ($this->driver->support('table')) { |
64
|
|
|
$links['alter'] = $this->trans->lang('Alter table'); |
65
|
|
|
} |
66
|
|
|
if ($set !== null) { |
67
|
|
|
$links['edit'] = $this->trans->lang('New item'); |
68
|
|
|
} |
69
|
|
|
// $links['docs'] = \doc_link([$this->driver->jush() => $this->driver->tableHelp($name)], '?'); |
70
|
|
|
|
71
|
|
|
return $links; |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
/** |
75
|
|
|
* Get details about a table |
76
|
|
|
* |
77
|
|
|
* @param string $table The table name |
78
|
|
|
* |
79
|
|
|
* @return array |
80
|
|
|
*/ |
81
|
|
|
public function getTableInfo(string $table) |
82
|
|
|
{ |
83
|
|
|
$mainActions = [ |
84
|
|
|
'edit-table' => $this->trans->lang('Alter table'), |
85
|
|
|
'drop-table' => $this->trans->lang('Drop table'), |
86
|
|
|
'select-table' => $this->trans->lang('Select'), |
87
|
|
|
'insert-table' => $this->trans->lang('New item'), |
88
|
|
|
]; |
89
|
|
|
|
90
|
|
|
// From table.inc.php |
91
|
|
|
$status = $this->status($table); |
92
|
|
|
$name = $this->util->tableName($status); |
93
|
|
|
$title = $this->trans->lang('Table') . ': ' . ($name != '' ? $name : $this->util->html($table)); |
94
|
|
|
|
95
|
|
|
$comment = $status->comment; |
96
|
|
|
|
97
|
|
|
$tabs = [ |
98
|
|
|
'fields' => $this->trans->lang('Columns'), |
99
|
|
|
// 'indexes' => $this->trans->lang('Indexes'), |
100
|
|
|
// 'foreign-keys' => $this->trans->lang('Foreign keys'), |
101
|
|
|
// 'triggers' => $this->trans->lang('Triggers'), |
102
|
|
|
]; |
103
|
|
|
if ($this->driver->isView($status)) { |
104
|
|
|
if ($this->driver->support('view_trigger')) { |
105
|
|
|
$tabs['triggers'] = $this->trans->lang('Triggers'); |
106
|
|
|
} |
107
|
|
|
} else { |
108
|
|
|
if ($this->driver->support('indexes')) { |
109
|
|
|
$tabs['indexes'] = $this->trans->lang('Indexes'); |
110
|
|
|
} |
111
|
|
|
if ($this->driver->supportForeignKeys($status)) { |
112
|
|
|
$tabs['foreign-keys'] = $this->trans->lang('Foreign keys'); |
113
|
|
|
} |
114
|
|
|
if ($this->driver->support('trigger')) { |
115
|
|
|
$tabs['triggers'] = $this->trans->lang('Triggers'); |
116
|
|
|
} |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
return \compact('mainActions', 'title', 'comment', 'tabs'); |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
/** |
123
|
|
|
* Get the fields of a table |
124
|
|
|
* |
125
|
|
|
* @param string $table The table name |
126
|
|
|
* |
127
|
|
|
* @return array |
128
|
|
|
*/ |
129
|
|
|
public function getTableFields(string $table) |
130
|
|
|
{ |
131
|
|
|
// From table.inc.php |
132
|
|
|
$fields = $this->driver->fields($table); |
133
|
|
|
if (empty($fields)) { |
134
|
|
|
throw new Exception($this->driver->error()); |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
$mainActions = $this->getTableLinks(); |
138
|
|
|
|
139
|
|
|
$tabs = [ |
140
|
|
|
'fields' => $this->trans->lang('Columns'), |
141
|
|
|
// 'indexes' => $this->trans->lang('Indexes'), |
142
|
|
|
// 'foreign-keys' => $this->trans->lang('Foreign keys'), |
143
|
|
|
// 'triggers' => $this->trans->lang('Triggers'), |
144
|
|
|
]; |
145
|
|
|
if ($this->driver->support('indexes')) { |
146
|
|
|
$tabs['indexes'] = $this->trans->lang('Indexes'); |
147
|
|
|
} |
148
|
|
|
if ($this->driver->supportForeignKeys($this->status($table))) { |
149
|
|
|
$tabs['foreign-keys'] = $this->trans->lang('Foreign keys'); |
150
|
|
|
} |
151
|
|
|
if ($this->driver->support('trigger')) { |
152
|
|
|
$tabs['triggers'] = $this->trans->lang('Triggers'); |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
$headers = [ |
156
|
|
|
$this->trans->lang('Name'), |
157
|
|
|
$this->trans->lang('Type'), |
158
|
|
|
$this->trans->lang('Collation'), |
159
|
|
|
]; |
160
|
|
|
$hasComment = $this->driver->support('comment'); |
161
|
|
|
if ($hasComment) { |
162
|
|
|
$headers[] = $this->trans->lang('Comment'); |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
$details = []; |
166
|
|
|
foreach ($fields as $field) { |
167
|
|
|
$type = $this->util->html($field->fullType); |
168
|
|
|
if ($field->null) { |
169
|
|
|
$type .= ' <i>nullable</i>'; // ' <i>NULL</i>'; |
170
|
|
|
} |
171
|
|
|
if ($field->autoIncrement) { |
172
|
|
|
$type .= ' <i>' . $this->trans->lang('Auto Increment') . '</i>'; |
173
|
|
|
} |
174
|
|
|
if ($field->default !== '') { |
175
|
|
|
$type .= /*' ' . $this->trans->lang('Default value') .*/ ' [<b>' . $this->util->html($field->default) . '</b>]'; |
176
|
|
|
} |
177
|
|
|
$detail = [ |
178
|
|
|
'name' => $this->util->html($field->name), |
179
|
|
|
'type' => $type, |
180
|
|
|
'collation' => $this->util->html($field->collation), |
181
|
|
|
]; |
182
|
|
|
if ($hasComment) { |
183
|
|
|
$detail['comment'] = $this->util->html($field->comment); |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
$details[] = $detail; |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
return \compact('mainActions', 'headers', 'details'); |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
/** |
193
|
|
|
* Get the indexes of a table |
194
|
|
|
* |
195
|
|
|
* @param string $table The table name |
196
|
|
|
* |
197
|
|
|
* @return array|null |
198
|
|
|
*/ |
199
|
|
|
public function getTableIndexes(string $table) |
200
|
|
|
{ |
201
|
|
|
if (!$this->driver->support('indexes')) { |
202
|
|
|
return null; |
203
|
|
|
} |
204
|
|
|
|
205
|
|
|
// From table.inc.php |
206
|
|
|
$indexes = $this->driver->indexes($table); |
207
|
|
|
$mainActions = [ |
208
|
|
|
'create' => $this->trans->lang('Alter indexes'), |
209
|
|
|
]; |
210
|
|
|
|
211
|
|
|
$headers = [ |
212
|
|
|
$this->trans->lang('Name'), |
213
|
|
|
$this->trans->lang('Type'), |
214
|
|
|
$this->trans->lang('Column'), |
215
|
|
|
]; |
216
|
|
|
|
217
|
|
|
$details = []; |
218
|
|
|
// From adminer.inc.php |
219
|
|
|
foreach ($indexes as $name => $index) { |
220
|
|
|
\ksort($index->columns); // enforce correct columns order |
221
|
|
|
$print = []; |
222
|
|
|
foreach ($index->columns as $key => $val) { |
223
|
|
|
$value = '<i>' . $this->util->html($val) . '</i>'; |
224
|
|
|
if (\array_key_exists($key, $index->lengths)) { |
225
|
|
|
$value .= '(' . $index->lengths[$key] . ')'; |
226
|
|
|
} |
227
|
|
|
if (\array_key_exists($key, $index->descs)) { |
228
|
|
|
$value .= ' DESC'; |
229
|
|
|
} |
230
|
|
|
$print[] = $value; |
231
|
|
|
} |
232
|
|
|
$details[] = [ |
233
|
|
|
'name' => $this->util->html($name), |
234
|
|
|
'type' => $index->type, |
235
|
|
|
'desc' => \implode(', ', $print), |
236
|
|
|
]; |
237
|
|
|
} |
238
|
|
|
|
239
|
|
|
return \compact('mainActions', 'headers', 'details'); |
240
|
|
|
} |
241
|
|
|
|
242
|
|
|
/** |
243
|
|
|
* Get the foreign keys of a table |
244
|
|
|
* |
245
|
|
|
* @param string $table The table name |
246
|
|
|
* |
247
|
|
|
* @return array|null |
248
|
|
|
*/ |
249
|
|
|
public function getTableForeignKeys(string $table) |
250
|
|
|
{ |
251
|
|
|
$status = $this->status($table); |
252
|
|
|
if (!$this->driver->supportForeignKeys($status)) { |
253
|
|
|
return null; |
254
|
|
|
} |
255
|
|
|
|
256
|
|
|
// From table.inc.php |
257
|
|
|
$mainActions = [ |
258
|
|
|
$this->trans->lang('Add foreign key'), |
259
|
|
|
]; |
260
|
|
|
|
261
|
|
|
$headers = [ |
262
|
|
|
$this->trans->lang('Name'), |
263
|
|
|
$this->trans->lang('Source'), |
264
|
|
|
$this->trans->lang('Target'), |
265
|
|
|
$this->trans->lang('ON DELETE'), |
266
|
|
|
$this->trans->lang('ON UPDATE'), |
267
|
|
|
]; |
268
|
|
|
|
269
|
|
|
$foreignKeys = $this->driver->foreignKeys($table); |
270
|
|
|
$details = []; |
271
|
|
|
// From table.inc.php |
272
|
|
|
foreach ($foreignKeys as $name => $foreignKey) { |
273
|
|
|
$target = ''; |
274
|
|
|
if ($foreignKey->database != '') { |
275
|
|
|
$target .= '<b>' . $this->util->html($foreignKey->database) . '</b>.'; |
276
|
|
|
} |
277
|
|
|
if ($foreignKey->schema != '') { |
278
|
|
|
$target .= '<b>' . $this->util->html($foreignKey->schema) . '</b>.'; |
279
|
|
|
} |
280
|
|
|
$target = $this->util->html($foreignKey->table) . |
281
|
|
|
'(' . \implode(', ', \array_map(function ($key) { |
282
|
|
|
return $this->util->html($key); |
283
|
|
|
}, $foreignKey->target)) . ')'; |
284
|
|
|
$details[] = [ |
285
|
|
|
'name' => $this->util->html($name), |
286
|
|
|
'source' => '<i>' . \implode( |
287
|
|
|
'</i>, <i>', |
288
|
|
|
\array_map(function ($key) { |
289
|
|
|
return $this->util->html($key); |
290
|
|
|
}, $foreignKey->source) |
291
|
|
|
) . '</i>', |
292
|
|
|
'target' => $target, |
293
|
|
|
'onDelete' => $this->util->html($foreignKey->onDelete), |
294
|
|
|
'onUpdate' => $this->util->html($foreignKey->onUpdate), |
295
|
|
|
]; |
296
|
|
|
} |
297
|
|
|
|
298
|
|
|
return \compact('mainActions', 'headers', 'details'); |
299
|
|
|
} |
300
|
|
|
|
301
|
|
|
/** |
302
|
|
|
* Get the triggers of a table |
303
|
|
|
* |
304
|
|
|
* @param string $table The table name |
305
|
|
|
* |
306
|
|
|
* @return array|null |
307
|
|
|
*/ |
308
|
|
|
public function getTableTriggers(string $table) |
309
|
|
|
{ |
310
|
|
|
if (!$this->driver->support('trigger')) { |
311
|
|
|
return null; |
312
|
|
|
} |
313
|
|
|
|
314
|
|
|
$mainActions = [ |
315
|
|
|
$this->trans->lang('Add trigger'), |
316
|
|
|
]; |
317
|
|
|
|
318
|
|
|
$headers = [ |
319
|
|
|
$this->trans->lang('Name'), |
320
|
|
|
' ', |
321
|
|
|
' ', |
322
|
|
|
' ', |
323
|
|
|
]; |
324
|
|
|
|
325
|
|
|
$details = []; |
326
|
|
|
// From table.inc.php |
327
|
|
|
$triggers = $this->driver->triggers($table); |
328
|
|
|
foreach ($triggers as $name => $trigger) { |
329
|
|
|
$details[] = [ |
330
|
|
|
$this->util->html($trigger->timing), |
331
|
|
|
$this->util->html($trigger->event), |
332
|
|
|
$this->util->html($name), |
333
|
|
|
$this->trans->lang('Alter'), |
334
|
|
|
]; |
335
|
|
|
} |
336
|
|
|
|
337
|
|
|
return \compact('mainActions', 'headers', 'details'); |
338
|
|
|
} |
339
|
|
|
|
340
|
|
|
/** |
341
|
|
|
* Get foreign keys |
342
|
|
|
* |
343
|
|
|
* @param string $table The table name |
344
|
|
|
* |
345
|
|
|
* @return void |
346
|
|
|
*/ |
347
|
|
|
private function getForeignKeys(string $table = '') |
348
|
|
|
{ |
349
|
|
|
$this->referencableTables = $this->driver->referencableTables($table); |
350
|
|
|
$this->foreignKeys = []; |
351
|
|
|
foreach ($this->referencableTables as $tableName => $field) { |
352
|
|
|
$name = \str_replace('`', '``', $tableName) . '`' . \str_replace('`', '``', $field->name); |
353
|
|
|
// not escapeId() - used in JS |
354
|
|
|
$this->foreignKeys[$name] = $tableName; |
355
|
|
|
} |
356
|
|
|
} |
357
|
|
|
|
358
|
|
|
/** |
359
|
|
|
* Get field types |
360
|
|
|
* |
361
|
|
|
* @param string $type The type name |
362
|
|
|
* |
363
|
|
|
* @return array |
364
|
|
|
*/ |
365
|
|
|
public function getFieldTypes(string $type = '') |
366
|
|
|
{ |
367
|
|
|
// From includes/editing.inc.php |
368
|
|
|
$extraTypes = []; |
369
|
|
|
if ($type && !$this->driver->typeExists($type) && !isset($this->foreignKeys[$type]) && |
370
|
|
|
!\array_key_exists($this->trans->lang('Current'), $extraTypes)) { |
371
|
|
|
$extraTypes[$this->trans->lang('Current')] = [$type]; |
372
|
|
|
} |
373
|
|
|
if ($this->foreignKeys) { |
|
|
|
|
374
|
|
|
$this->driver->setStructuredType($this->trans->lang('Foreign keys'), $this->foreignKeys); |
375
|
|
|
} |
376
|
|
|
return \array_merge($extraTypes, $this->driver->structuredTypes()); |
377
|
|
|
} |
378
|
|
|
|
379
|
|
|
/** |
380
|
|
|
* Get required data for create/update on tables |
381
|
|
|
* |
382
|
|
|
* @param string $table The table name |
383
|
|
|
* |
384
|
|
|
* @return array |
385
|
|
|
*/ |
386
|
|
|
public function getTableData(string $table = '') |
387
|
|
|
{ |
388
|
|
|
$mainActions = [ |
389
|
|
|
'table-save' => $this->trans->lang('Save'), |
390
|
|
|
'table-cancel' => $this->trans->lang('Cancel'), |
391
|
|
|
]; |
392
|
|
|
|
393
|
|
|
// From create.inc.php |
394
|
|
|
$status = []; |
395
|
|
|
$fields = []; |
396
|
|
|
if ($table !== '') { |
397
|
|
|
$status = $this->driver->tableStatus($table); |
398
|
|
|
if (!$status) { |
399
|
|
|
throw new Exception($this->trans->lang('No tables.')); |
400
|
|
|
} |
401
|
|
|
$fields = $this->driver->fields($table); |
402
|
|
|
} |
403
|
|
|
|
404
|
|
|
$this->getForeignKeys($table); |
405
|
|
|
|
406
|
|
|
$hasAutoIncrement = false; |
407
|
|
|
foreach ($fields as &$field) { |
408
|
|
|
$hasAutoIncrement = $hasAutoIncrement || $field->autoIncrement; |
409
|
|
|
$field->hasDefault = $field->default !== null; |
410
|
|
|
if (\preg_match('~^CURRENT_TIMESTAMP~i', $field->onUpdate)) { |
411
|
|
|
$field->onUpdate = 'CURRENT_TIMESTAMP'; |
412
|
|
|
} |
413
|
|
|
|
414
|
|
|
$type = $field->type; |
415
|
|
|
$field->types = $this->getFieldTypes($type); |
416
|
|
|
$field->lengthRequired = !$field->length && \preg_match('~var(char|binary)$~', $type); |
417
|
|
|
$field->collationHidden = !\preg_match('~(char|text|enum|set)$~', $type); |
418
|
|
|
$field->unsignedHidden = !(!$type || \preg_match($this->driver->numberRegex(), $type)); |
419
|
|
|
$field->onUpdateHidden = !\preg_match('~timestamp|datetime~', $type); |
420
|
|
|
$field->onDeleteHidden = !\preg_match('~`~', $type); |
421
|
|
|
} |
422
|
|
|
$options = [ |
423
|
|
|
'hasAutoIncrement' => $hasAutoIncrement, |
424
|
|
|
'onUpdate' => ['CURRENT_TIMESTAMP'], |
425
|
|
|
'onDelete' => $this->driver->onActions(), |
426
|
|
|
]; |
427
|
|
|
|
428
|
|
|
$collations = $this->driver->collations(); |
429
|
|
|
$engines = $this->driver->engines(); |
430
|
|
|
$support = [ |
431
|
|
|
'columns' => $this->driver->support('columns'), |
432
|
|
|
'comment' => $this->driver->support('comment'), |
433
|
|
|
'partitioning' => $this->driver->support('partitioning'), |
434
|
|
|
'move_col' => $this->driver->support('move_col'), |
435
|
|
|
'drop_col' => $this->driver->support('drop_col'), |
436
|
|
|
]; |
437
|
|
|
|
438
|
|
|
$foreignKeys = $this->foreignKeys; |
439
|
|
|
$unsigned = $this->driver->unsigned(); |
440
|
|
|
// Give the var a better name |
441
|
|
|
$table = $status; |
442
|
|
|
return \compact('mainActions', 'table', 'foreignKeys', 'fields', |
443
|
|
|
'options', 'collations', 'engines', 'support', 'unsigned'); |
444
|
|
|
} |
445
|
|
|
|
446
|
|
|
/** |
447
|
|
|
* Get fields for a new column |
448
|
|
|
* |
449
|
|
|
* @return TableFieldEntity |
450
|
|
|
*/ |
451
|
|
|
public function getTableField() |
452
|
|
|
{ |
453
|
|
|
$this->getForeignKeys(); |
454
|
|
|
$field = new TableFieldEntity(); |
455
|
|
|
$field->types = $this->getFieldTypes(); |
456
|
|
|
return $field; |
457
|
|
|
} |
458
|
|
|
|
459
|
|
|
/** |
460
|
|
|
* Create or alter a table |
461
|
|
|
* |
462
|
|
|
* @param array $values The table values |
463
|
|
|
* @param string $table The table name |
464
|
|
|
* @param array $origFields The table fields |
465
|
|
|
* |
466
|
|
|
* @return array |
467
|
|
|
*/ |
468
|
|
|
private function createOrAlterTable(array $values, string $table = '', array $origFields = []) |
469
|
|
|
{ |
470
|
|
|
// From create.inc.php |
471
|
|
|
$values['fields'] = (array)$values['fields']; |
472
|
|
|
if ($values['autoIncrementCol']) { |
473
|
|
|
$values['fields'][$values['autoIncrementCol']]['autoIncrement'] = true; |
474
|
|
|
} |
475
|
|
|
|
476
|
|
|
$fields = []; |
477
|
|
|
$allFields = []; |
478
|
|
|
$useAllFields = false; |
479
|
|
|
$foreign = []; |
480
|
|
|
$origField = \reset($origFields); |
481
|
|
|
$after = ' FIRST'; |
482
|
|
|
|
483
|
|
|
$this->getForeignKeys(); |
484
|
|
|
|
485
|
|
|
foreach ($values['fields'] as $key => $field) { |
486
|
|
|
$orig = $field['orig']; |
487
|
|
|
$field = TableFieldEntity::make($field); |
488
|
|
|
$foreignKey = $this->foreignKeys[$field->type] ?? null; |
489
|
|
|
//! can collide with user defined type |
490
|
|
|
$typeField = ($foreignKey === null ? $field : |
491
|
|
|
TableFieldEntity::make($this->referencableTables[$foreignKey])); |
492
|
|
|
// Originally, deleted fields have the "field" field set to an empty string. |
493
|
|
|
// But in our implementation, the "name" field is not set. |
494
|
|
|
if ($field->name != '') { |
495
|
|
|
$field->autoIncrement = ($key == $values['autoIncrementCol']); |
496
|
|
|
|
497
|
|
|
$processedField = $this->util->processField($field, $typeField); |
498
|
|
|
$allFields[] = [$orig, $processedField, $after]; |
499
|
|
|
if (!$origField || $field->changed($origField)) { |
500
|
|
|
$fields[] = [$orig, $processedField, $after]; |
501
|
|
|
if ($orig != '' || $after) { |
502
|
|
|
$useAllFields = true; |
503
|
|
|
} |
504
|
|
|
} |
505
|
|
|
if ($foreignKey !== null) { |
506
|
|
|
$fkey = new ForeignKeyEntity(); |
507
|
|
|
$fkey->table = $this->foreignKeys[$field->type]; |
508
|
|
|
$fkey->source = [$field->name]; |
509
|
|
|
$fkey->target = [$typeField['field']]; |
510
|
|
|
$fkey->onDelete = $field->onDelete; |
511
|
|
|
$foreign[$this->driver->escapeId($field->name)] = |
512
|
|
|
($table != '' && $this->driver->jush() != 'sqlite' ? 'ADD' : ' ') . |
513
|
|
|
$this->driver->formatForeignKey($fkey); |
514
|
|
|
} |
515
|
|
|
$after = ' AFTER ' . $this->driver->escapeId($field->name); |
516
|
|
|
} elseif ($orig != '') { |
517
|
|
|
// A missing "name" field and a not empty "orig" field means the column is to be dropped. |
518
|
|
|
// We also append null in the array because the drivers code accesses field at position 1. |
519
|
|
|
$useAllFields = true; |
520
|
|
|
$fields[] = [$orig, null]; |
521
|
|
|
} |
522
|
|
|
if ($orig != '') { |
523
|
|
|
$origField = \next($origFields); |
524
|
|
|
if (!$origField) { |
525
|
|
|
$after = ''; |
526
|
|
|
} |
527
|
|
|
} |
528
|
|
|
} |
529
|
|
|
|
530
|
|
|
// For now, partitioning is not implemented |
531
|
|
|
$partitioning = ''; |
532
|
|
|
// if($partition_by[$values['partition_by']]) |
533
|
|
|
// { |
534
|
|
|
// $partitions = []; |
535
|
|
|
// if($values['partition_by'] == 'RANGE' || $values['partition_by'] == 'LIST') |
536
|
|
|
// { |
537
|
|
|
// foreach(\array_filter($values['partition_names']) as $key => $val) |
538
|
|
|
// { |
539
|
|
|
// $value = $values['partition_values'][$key]; |
540
|
|
|
// $partitions[] = "\n PARTITION " . $this->driver->escapeId($val) . |
541
|
|
|
// ' VALUES ' . ($values['partition_by'] == 'RANGE' ? 'LESS THAN' : 'IN') . |
542
|
|
|
// ($value != '' ? ' ($value)' : ' MAXVALUE'); //! SQL injection |
543
|
|
|
// } |
544
|
|
|
// } |
545
|
|
|
// $partitioning .= "\nPARTITION BY $values[partition_by]($values[partition])" . |
546
|
|
|
// ($partitions // $values['partition'] can be expression, not only column |
547
|
|
|
// ? ' (' . \implode(',', $partitions) . "\n)" |
548
|
|
|
// : ($values['partitions'] ? ' PARTITIONS ' . (+$values['partitions']) : '') |
549
|
|
|
// ); |
550
|
|
|
// } |
551
|
|
|
// elseif($this->driver->support('partitioning') && |
552
|
|
|
// \preg_match('~partitioned~', $this->tableStatus->Create_options)) |
553
|
|
|
// { |
554
|
|
|
// $partitioning .= "\nREMOVE PARTITIONING"; |
555
|
|
|
// } |
556
|
|
|
|
557
|
|
|
if (!isset($values['comment'])) { |
558
|
|
|
$values['comment'] = ''; |
559
|
|
|
} |
560
|
|
|
if (!isset($values['engine']) || !$values['engine']) { |
561
|
|
|
$values['engine'] = ''; |
562
|
|
|
} |
563
|
|
|
if (!isset($values['collation']) || !$values['collation']) { |
564
|
|
|
$values['collation'] = ''; |
565
|
|
|
} |
566
|
|
|
|
567
|
|
|
if ($this->tableStatus != null) { |
568
|
|
|
// if ($values['comment'] == $this->tableStatus->comment) { |
569
|
|
|
// $values['comment'] = null; |
570
|
|
|
// } |
571
|
|
|
if ($values['engine'] == $this->tableStatus->engine) { |
572
|
|
|
$values['engine'] = ''; |
573
|
|
|
} |
574
|
|
|
if ($values['collation'] == $this->tableStatus->collation) { |
575
|
|
|
$values['collation'] = ''; |
576
|
|
|
} |
577
|
|
|
} |
578
|
|
|
|
579
|
|
|
$name = \trim($values['name']); |
580
|
|
|
$autoIncrement = $this->util->number($this->util->input()->getAutoIncrementStep()); |
581
|
|
|
if ($this->driver->jush() == 'sqlite' && ($useAllFields || $foreign)) { |
582
|
|
|
$fields = $allFields; |
583
|
|
|
} |
584
|
|
|
|
585
|
|
|
$success = $this->driver->alterTable($table, $name, $fields, $foreign, $values['comment'], |
586
|
|
|
$values['engine'], $values['collation'], \intval($autoIncrement), $partitioning); |
587
|
|
|
|
588
|
|
|
$error = $this->driver->error(); |
589
|
|
|
|
590
|
|
|
return \compact('success', 'error'); |
591
|
|
|
} |
592
|
|
|
|
593
|
|
|
/** |
594
|
|
|
* Create a table |
595
|
|
|
* |
596
|
|
|
* @param array $values The table values |
597
|
|
|
* |
598
|
|
|
* @return array |
599
|
|
|
*/ |
600
|
|
|
public function createTable(array $values) |
601
|
|
|
{ |
602
|
|
|
$results = $this->createOrAlterTable($values); |
603
|
|
|
$results['message'] = $this->trans->lang('Table has been created.'); |
604
|
|
|
return $results; |
605
|
|
|
} |
606
|
|
|
|
607
|
|
|
/** |
608
|
|
|
* Alter a table |
609
|
|
|
* |
610
|
|
|
* @param string $table The table name |
611
|
|
|
* @param array $values The table values |
612
|
|
|
* |
613
|
|
|
* @return array |
614
|
|
|
*/ |
615
|
|
|
public function alterTable(string $table, array $values) |
616
|
|
|
{ |
617
|
|
|
$origFields = $this->driver->fields($table); |
618
|
|
|
$this->tableStatus = $this->driver->tableStatus($table); |
619
|
|
|
if (!$this->tableStatus) { |
620
|
|
|
throw new Exception($this->trans->lang('No tables.')); |
621
|
|
|
} |
622
|
|
|
|
623
|
|
|
$results = $this->createOrAlterTable($values, $table, $origFields); |
624
|
|
|
$results['message'] = $this->trans->lang('Table has been altered.'); |
625
|
|
|
return $results; |
626
|
|
|
} |
627
|
|
|
|
628
|
|
|
/** |
629
|
|
|
* Drop a table |
630
|
|
|
* |
631
|
|
|
* @param string $table The table name |
632
|
|
|
* |
633
|
|
|
* @return array |
634
|
|
|
*/ |
635
|
|
|
public function dropTable(string $table) |
636
|
|
|
{ |
637
|
|
|
$success = $this->driver->dropTables([$table]); |
638
|
|
|
|
639
|
|
|
$error = $this->driver->error(); |
640
|
|
|
|
641
|
|
|
$message = $this->trans->lang('Table has been dropped.'); |
642
|
|
|
|
643
|
|
|
return \compact('success', 'message', 'error'); |
644
|
|
|
} |
645
|
|
|
} |
646
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.