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