ILIAS  trunk Revision v11.0_alpha-2638-g80c1d007f79
class.ilDBPdoManager.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
22 
28 {
30 
34  public function __construct(protected \PDO $pdo, protected \ilDBPdo $db_instance)
35  {
36  }
37 
38  public function getQueryUtils(): \ilQueryUtils
39  {
40  if ($this->query_utils === null) {
41  $this->query_utils = new ilMySQLQueryUtils($this->db_instance);
42  }
43 
44  return $this->query_utils;
45  }
46 
47  public function getDBInstance(): \ilDBPdo
48  {
49  return $this->db_instance;
50  }
51 
55  public function listTables(?string $database = null): array
56  {
57  $str = 'SHOW TABLES ' . ($database ? ' IN ' . $database : '');
58  $r = $this->pdo->query($str);
59  $tables = [];
60 
61  $sequence_identifier = "_seq";
62  while ($data = $r->fetchColumn()) {
63  if (!preg_match("/$sequence_identifier$/um", $data)) {
64  $tables[] = $data;
65  }
66  }
67 
68  return $tables;
69  }
70 
71  protected function fixSequenceName(string $sqn, bool $check = false): string
72  {
73  $seq_pattern = '/^' . preg_replace('/%s/', '([a-z0-9_]+)', ilDBConstants::SEQUENCE_FORMAT) . '$/i';
74  $seq_name = preg_replace($seq_pattern, '\\1', $sqn);
75  if ($seq_name && !strcasecmp($sqn, $this->db_instance->getSequenceName($seq_name))) {
76  return $seq_name;
77  }
78 
79  return $sqn;
80  }
81 
85  public function listSequences(?string $database = null): array
86  {
87  $query = "SHOW TABLES LIKE '%_seq'";
88  if (!is_null($database)) {
89  $query .= " FROM $database";
90  }
91 
92  $res = $this->db_instance->query($query);
93 
94  $result = [];
95  while ($table_name = $this->db_instance->fetchAssoc($res)) {
96  $sqn = $this->fixSequenceName(reset($table_name), true);
97  if ($sqn !== '' && $sqn !== '0') {
98  $result[] = $sqn;
99  }
100  }
101  if ($this->db_instance->options['portability'] ?? null) {
102  return array_map(
103  ($this->db_instance->options['field_case'] === CASE_LOWER ? 'strtolower' : 'strtoupper'),
104  $result
105  );
106  }
107 
108  return $result;
109  }
110 
114  public function createConstraint(string $table, string $name, array $definition): bool
115  {
116  $db = $this->db_instance;
117 
118  $table = $db->quoteIdentifier($table, true);
119  $name = $db->quoteIdentifier($db->getIndexName($name), true);
120  $query = "ALTER TABLE $table ADD CONSTRAINT $name";
121  if (!empty($definition['primary'])) {
122  $query .= ' PRIMARY KEY';
123  } elseif (!empty($definition['unique'])) {
124  $query .= ' UNIQUE';
125  }
126  $fields = [];
127  foreach (array_keys($definition['fields']) as $field) {
128  $fields[] = $db->quoteIdentifier($field, true);
129  }
130  $query .= ' (' . implode(', ', $fields) . ')';
131 
132  return (bool) $this->pdo->exec($query);
133  }
134 
135  public function createSequence(string $seq_name, int $start = 1, array $options = []): bool
136  {
137  $sequence_name = $this->db_instance->quoteIdentifier($this->db_instance->getSequenceName($seq_name));
138  $seqcol_name = $this->db_instance->quoteIdentifier(ilDBConstants::SEQUENCE_COLUMNS_NAME);
139 
140  $options_strings = [];
141 
142  if (!empty($options['comment'])) {
143  $options_strings['comment'] = 'COMMENT = ' . $this->db_instance->quote($options['comment'], 'text');
144  }
145 
146  if (!empty($options['charset'])) {
147  $options_strings['charset'] = 'DEFAULT CHARACTER SET ' . $options['charset'];
148  if (!empty($options['collate'])) {
149  $options_strings['charset'] .= ' COLLATE ' . $options['collate'];
150  }
151  }
152 
153  $type = false;
154  if (!empty($options['type'])) {
155  $type = $options['type'];
156  }
157  if ($type) {
158  $options_strings[] = "ENGINE = $type";
159  }
160 
161  $query = "CREATE TABLE $sequence_name ($seqcol_name INT NOT NULL AUTO_INCREMENT, PRIMARY KEY ($seqcol_name))";
162 
163  if (!empty($options_strings)) {
164  $query .= ' ' . implode(' ', $options_strings);
165  }
166  $this->pdo->exec($query);
167 
168  if ($start == 1) {
169  return true;
170  }
171 
172  $query = "INSERT INTO $sequence_name ($seqcol_name) VALUES (" . ($start - 1) . ')';
173  $this->pdo->exec($query);
174 
175  return true;
176  }
177 
181  public function alterTable(string $name, array $changes, bool $check): bool
182  {
183  $db = $this->db_instance;
184 
185  foreach (array_keys($changes) as $change_name) {
186  switch ($change_name) {
187  case 'add':
188  case 'remove':
189  case 'change':
190  case 'rename':
191  case 'name':
192  break;
193  default:
194  throw new ilDatabaseException('change type "' . $change_name . '" not yet supported');
195  }
196  }
197 
198  if ($check) {
199  return true;
200  }
201 
202  $query = '';
203  if (!empty($changes['name'])) {
204  $change_name = $db->quoteIdentifier($changes['name']);
205  $query .= 'RENAME TO ' . $change_name;
206  }
207 
208  if (!empty($changes['add']) && is_array($changes['add'])) {
209  foreach ($changes['add'] as $field_name => $field) {
210  if ($query !== '') {
211  $query .= ', ';
212  }
213  $fd = $db->getFieldDefinition();
214  if ($fd !== null) {
215  $query .= 'ADD ' . $fd->getDeclaration($field['type'], $field_name, $field);
216  }
217  }
218  }
219 
220  if (!empty($changes['remove']) && is_array($changes['remove'])) {
221  foreach (array_keys($changes['remove']) as $field_name) {
222  if ($query !== '') {
223  $query .= ', ';
224  }
225  $field_name = $db->quoteIdentifier($field_name);
226  $query .= 'DROP ' . $field_name;
227  }
228  }
229 
230  $rename = [];
231  if (!empty($changes['rename']) && is_array($changes['rename'])) {
232  foreach ($changes['rename'] as $field_name => $field) {
233  $rename[$field['name']] = $field_name;
234  }
235  }
236 
237  if (!empty($changes['change']) && is_array($changes['change'])) {
238  foreach ($changes['change'] as $field_name => $field) {
239  if ($query !== '') {
240  $query .= ', ';
241  }
242  if (isset($rename[$field_name])) {
243  $old_field_name = $rename[$field_name];
244  unset($rename[$field_name]);
245  } else {
246  $old_field_name = $field_name;
247  }
248  $old_field_name = $db->quoteIdentifier($old_field_name);
249  $fd = $this->db_instance->getFieldDefinition();
250  if ($fd !== null) {
251  $query .= "CHANGE $old_field_name " . $fd
252  ->getDeclaration(
253  $field['definition']['type'],
254  $field_name,
255  $field['definition']
256  );
257  }
258  }
259  }
260 
261  if (!empty($rename) && is_array($rename)) {
262  foreach ($rename as $renamed_field) {
263  if ($query !== '') {
264  $query .= ', ';
265  }
266  $field = $changes['rename'][$renamed_field];
267  $renamed_field = $db->quoteIdentifier($renamed_field);
268  $fd = $this->db_instance->getFieldDefinition();
269  if ($fd !== null) {
270  $query .= 'CHANGE ' . $renamed_field . ' ' . $fd
271  ->getDeclaration(
272  $field['definition']['type'],
273  $field['name'],
274  $field['definition']
275  );
276  }
277  }
278  }
279 
280  if ($query === '') {
281  return true;
282  }
283 
284  $name = $db->quoteIdentifier($name, true);
285 
286  $statement = "ALTER TABLE $name $query";
287 
288  return (bool) $this->pdo->exec($statement);
289  }
290 
291  public function createTable(string $name, array $fields, array $options = []): bool
292  {
293  $options['type'] = $this->db_instance->getStorageEngine();
294 
295  return (bool) $this->pdo->exec($this->getQueryUtils()->createTable($name, $fields, $options));
296  }
297 
298  public function getIndexName(string $idx): string
299  {
300  return $this->db_instance->getIndexName($idx);
301  }
302 
303  public function getSequenceName(string $sqn): string
304  {
305  return $this->db_instance->getSequenceName($sqn);
306  }
307 
308  public function listTableFields(string $table): array
309  {
310  $table = $this->db_instance->quoteIdentifier($table);
311  $query = "SHOW COLUMNS FROM $table";
312  $result = $this->db_instance->query($query);
313  $return = [];
314  while ($data = $this->db_instance->fetchObject($result)) {
315  $return[] = $data->Field;
316  }
317 
318  return $return;
319  }
320 
324  public function listTableConstraints(string $table): array
325  {
326  $key_name = 'Key_name';
327  $non_unique = 'Non_unique';
328 
329  $db = $this->getDBInstance();
330  if ($db->options['portability'] ?? null) {
331  if ($db->options['field_case'] == CASE_LOWER) {
332  $key_name = strtolower($key_name);
333  $non_unique = strtolower($non_unique);
334  } else {
335  $key_name = strtoupper($key_name);
336  $non_unique = strtoupper($non_unique);
337  }
338  }
339 
340  $table = $this->db_instance->quoteIdentifier($table);
341  $query = "SHOW INDEX FROM $table";
342  $result_set = $this->db_instance->query($query);
343 
344  $result = [];
345  while ($index_data = $this->db_instance->fetchAssoc($result_set)) {
346  if (!$index_data[$non_unique]) {
347  $index = $index_data[$key_name] !== 'PRIMARY' ? $this->fixIndexName($index_data[$key_name]) : 'PRIMARY';
348  if (!empty($index)) {
349  $index = strtolower($index);
350  $result[$index] = true;
351  }
352  }
353  }
354 
355  if ($this->db_instance->options['portability'] ?? null) {
356  $result = array_change_key_case($result, $this->db_instance->options['field_case']);
357  }
358 
359  return array_keys($result);
360  }
361 
365  public function listTableIndexes(string $table): array
366  {
367  $key_name = 'Key_name';
368  $non_unique = 'Non_unique';
369  if ($this->db_instance->options['portability'] ?? null) {
370  if ($this->db_instance->options['field_case'] == CASE_LOWER) {
371  $key_name = strtolower($key_name);
372  $non_unique = strtolower($non_unique);
373  } else {
374  $key_name = strtoupper($key_name);
375  $non_unique = strtoupper($non_unique);
376  }
377  }
378 
379  $table = $this->db_instance->quoteIdentifier($table);
380  $query = "SHOW INDEX FROM $table";
381  $result_set = $this->db_instance->query($query);
382  $indexes = [];
383  while ($index_data = $this->db_instance->fetchAssoc($result_set)) {
384  $indexes[] = $index_data;
385  }
386  $result = [];
387  foreach ($indexes as $index_data) {
388  if ($index_data[$non_unique] && ($index = $this->fixIndexName($index_data[$key_name]))) {
389  $result[$index] = true;
390  }
391  }
392 
393  if ($this->db_instance->options['portability'] ?? null) {
394  $result = array_change_key_case($result, $this->db_instance->options['field_case']);
395  }
396 
397  return array_keys($result);
398  }
399 
400  protected function fixIndexName(string $idx): string
401  {
402  $idx_pattern = '/^' . preg_replace('/%s/', '([a-z0-9_]+)', ilDBPdoFieldDefinition::INDEX_FORMAT) . '$/i';
403  $idx_name = preg_replace($idx_pattern, '\\1', $idx);
404  if ($idx_name && !strcasecmp($idx, $this->db_instance->getIndexName($idx_name))) {
405  return $idx_name;
406  }
407 
408  return $idx;
409  }
410 
411  public function createIndex(string $table, string $name, array $definition): bool
412  {
413  $table = $this->db_instance->quoteIdentifier($table, true);
414  $name = $this->db_instance->quoteIdentifier($this->db_instance->getIndexName($name), true);
415  $query = "CREATE INDEX $name ON $table";
416  $fields = [];
417  foreach ($definition['fields'] as $field => $fieldinfo) {
418  if (!empty($fieldinfo['length'])) {
419  $fields[] = $this->db_instance->quoteIdentifier($field, true) . '(' . $fieldinfo['length'] . ')';
420  } else {
421  $fields[] = $this->db_instance->quoteIdentifier($field, true);
422  }
423  }
424  $query .= ' (' . implode(', ', $fields) . ')';
425 
426  return (bool) $this->pdo->exec($query);
427  }
428 
429  public function dropIndex(string $table, string $name): bool
430  {
431  $table = $this->db_instance->quoteIdentifier($table, true);
432  $name = $this->db_instance->quoteIdentifier($this->db_instance->getIndexName($name), true);
433 
434  return (bool) $this->pdo->exec("DROP INDEX $name ON $table");
435  }
436 
437  public function dropSequence(string $seq_name): bool
438  {
439  $sequence_name = $this->db_instance->quoteIdentifier($this->db_instance->getSequenceName($seq_name));
440 
441  return (bool) $this->pdo->exec("DROP TABLE $sequence_name");
442  }
443 
447  public function getTableCreationQuery(string $name, array $fields, array $options = []): string
448  {
449  return $this->getQueryUtils()->createTable($name, $fields, $options);
450  }
451 
452  public function dropConstraint(string $table, string $name, bool $primary = false): bool
453  {
454  $db = $this->getDBInstance();
455  $table = $db->quoteIdentifier($table, true);
456  if ($primary || strtolower($name) === 'primary') {
457  $query = "ALTER TABLE $table DROP PRIMARY KEY";
458  } else {
459  $name = $db->quoteIdentifier($db->getIndexName($name), true);
460  $query = "ALTER TABLE $table DROP INDEX $name";
461  }
462 
463  return (bool) $this->pdo->exec($query);
464  }
465 
466  public function dropTable(string $name): bool
467  {
468  $db = $this->getDBInstance();
469  $name = $db->quoteIdentifier($name, true);
470 
471  return (bool) $this->pdo->exec("DROP TABLE $name");
472  }
473 
478  public function addForeignKey(
479  string $foreign_key_name,
480  array $field_names,
481  string $table_name,
482  array $reference_field_names,
483  string $reference_table,
484  ?ForeignKeyConstraints $on_update = null,
485  ?ForeignKeyConstraints $on_delete = null
486  ): bool {
487  $table = $this->db_instance->quoteIdentifier($table_name, true);
488  $reference_table = $this->db_instance->quoteIdentifier($reference_table, true);
489  $field_names = implode(",", $field_names);
490  $field_names = $this->db_instance->quoteIdentifier($field_names, true);
491  $reference_field_names = implode(",", $reference_field_names);
492  $reference_field_names = $this->db_instance->quoteIdentifier($reference_field_names, true);
493  $foreign_key_name = $this->db_instance->quoteIdentifier($foreign_key_name, true);
494  $update = '';
495  if ($on_update !== null) {
496  $on_update = $on_update->value;
497  $update = "ON UPDATE $on_update";
498  }
499  $delete = '';
500  if ($on_delete !== null) {
501  $on_delete = $on_delete->value;
502  $delete = "ON DELETE $on_delete";
503  }
504  $query = "ALTER TABLE
505  $table ADD CONSTRAINT
506  $foreign_key_name FOREIGN KEY ($field_names)
507  REFERENCES $reference_table ($reference_field_names)
508  $update
509  $delete
510  ";
511 
512  return (bool) $this->pdo->exec($query);
513  }
514 
515  public function dropForeignKey(string $foreign_key_name, string $table_name): bool
516  {
517  $table = $this->db_instance->quoteIdentifier($table_name, true);
518  $name = $this->db_instance->quoteIdentifier($foreign_key_name, true);
519  $query = "ALTER TABLE $table DROP FOREIGN KEY $name;";
520 
521  return (bool) $this->pdo->exec($query);
522  }
523 
524  public function foreignKeyExists(string $foreign_key_name, string $table_name): bool
525  {
526  $query = "SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_TYPE='FOREIGN KEY';";
527  $result_set = $this->db_instance->query($query);
528  while ($foreign_data = $this->db_instance->fetchAssoc($result_set)) {
529  if (array_key_exists(
530  'CONSTRAINT_NAME',
531  $foreign_data
532  ) && $foreign_data['CONSTRAINT_NAME'] === $foreign_key_name) {
533  return true;
534  }
535  }
536  return false;
537  }
538 }
$res
Definition: ltiservices.php:66
dropSequence(string $seq_name)
dropForeignKey(string $foreign_key_name, string $table_name)
Interface ilDBManager.
fixIndexName(string $idx)
getTableCreationQuery(string $name, array $fields, array $options=[])
dropConstraint(string $table, string $name, bool $primary=false)
Interface ilDBPdoManagerInterface All these methods are not in MDB 2 will be moved to a seperate inte...
listTableIndexes(string $table)
dropTable(string $name)
getIndexName(string $idx)
fixSequenceName(string $sqn, bool $check=false)
createTable(string $name, array $fields, array $options=[])
listSequences(?string $database=null)
Class pdoDB.
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
alterTable(string $name, array $changes, bool $check)
dropIndex(string $table, string $name)
createSequence(string $seq_name, int $start=1, array $options=[])
listTables(?string $database=null)
foreignKeyExists(string $foreign_key_name, string $table_name)
listTableFields(string $table)
createConstraint(string $table, string $name, array $definition)
getSequenceName(string $sqn)
Class ilMySQLQueryUtils.
Class ilDBPdoManager.
__construct(protected \PDO $pdo, protected \ilDBPdo $db_instance)
ilDBPdoManager constructor.
ilQueryUtils $query_utils
$check
Definition: buildRTE.php:81
createIndex(string $table, string $name, array $definition)
addForeignKey(string $foreign_key_name, array $field_names, string $table_name, array $reference_field_names, string $reference_table, ?ForeignKeyConstraints $on_update=null, ?ForeignKeyConstraints $on_delete=null)
Class ilQueryUtils.
listTableConstraints(string $table)
$r