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