ILIAS  release_8 Revision v8.24
class.ilDBPdo.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
25abstract class ilDBPdo implements ilDBInterface, ilDBPdoInterface
26{
27 public array $options = [];
28 public const FEATURE_TRANSACTIONS = 'transactions';
29 public const FEATURE_FULLTEXT = 'fulltext';
30 public const FEATURE_SLAVE = 'slave';
31 protected string $host = '';
32 protected string $dbname = '';
33 protected string $charset = 'utf8';
34 protected string $username = '';
35 protected string $password = '';
36 protected int $port = 3306;
37 protected ?PDO $pdo = null;
40 protected ?int $limit = null;
41 protected ?int $offset = null;
42 protected string $storage_engine = 'InnoDB';
43 protected string $dsn = '';
47 protected array $attributes = array(
48 // PDO::ATTR_EMULATE_PREPARES => true,
49 PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
50 );
51 protected string $db_type = '';
52 protected int $error_code = 0;
54
58 public function connect(bool $return_false_for_error = false): ?bool
59 {
60 $this->generateDSN();
61 try {
62 $options = $this->getAttributes();
63 $this->pdo = new PDO($this->getDSN(), $this->getUsername(), $this->getPassword(), $options);
64 $this->initHelpers();
65 $this->initSQLMode();
66 } catch (Exception $e) {
67 $this->error_code = $e->getCode();
68 if ($return_false_for_error) {
69 return false;
70 }
71 throw $e;
72 }
73
74 return ($this->pdo->errorCode() === PDO::ERR_NONE);
75 }
76
77 abstract public function initHelpers(): void;
78
79 protected function initSQLMode(): void
80 {
81 }
82
83 protected function getAttributes(): array
84 {
86 foreach ($this->getAdditionalAttributes() as $k => $v) {
87 $options[$k] = $v;
88 }
89
90 return $options;
91 }
92
93 protected function getAdditionalAttributes(): array
94 {
95 return array();
96 }
97
99 {
101 }
102
104 {
105 $this->field_definition = $field_definition;
106 }
107
108 public function createDatabase(string $a_name, string $a_charset = "utf8", string $a_collation = ""): bool
109 {
110 $this->setDbname('');
111 $this->generateDSN();
112 $this->connect(true);
113 try {
114 $this->query($this->manager->getQueryUtils()->createDatabase($a_name, $a_charset, $a_collation));
115 return true;
116 } catch (PDOException $e) {
117 return false;
118 }
119 }
120
124 public function getLastErrorCode()
125 {
126 if ($this->pdo instanceof PDO) {
127 return $this->pdo->errorCode();
128 }
129
130 return $this->error_code;
131 }
132
133 public function initFromIniFile(?ilIniFile $ini = null): void
134 {
135 global $DIC;
136
137 if ($ini instanceof ilIniFile) {
138 $clientIniFile = $ini;
139 } elseif ($DIC->offsetExists('ilClientIniFile')) {
140 $clientIniFile = $DIC['ilClientIniFile'];
141 } else {
142 throw new InvalidArgumentException('$tmpClientIniFile is not an instance of ilIniFile');
143 }
144
145 $this->setUsername($clientIniFile->readVariable("db", "user"));
146 $this->setHost($clientIniFile->readVariable("db", "host"));
147 $this->setPort((int) $clientIniFile->readVariable("db", "port"));
148 $this->setPassword((string) $clientIniFile->readVariable("db", "pass"));
149 $this->setDbname($clientIniFile->readVariable("db", "name"));
150 $this->setDBType($clientIniFile->readVariable("db", "type"));
151
152 $this->generateDSN();
153 }
154
155 public function generateDSN()
156 {
157 $port = $this->getPort() !== 0 ? ";port=" . $this->getPort() : "";
158 $dbname = $this->getDbname() !== '' ? ';dbname=' . $this->getDbname() : '';
159 $host = $this->getHost();
160 $charset = ';charset=' . $this->getCharset();
161 $this->dsn = 'mysql:host=' . $host . $port . $dbname . $charset;
162 }
163
164 public function quoteIdentifier(string $identifier, bool $check_option = false): string
165 {
166 return '`' . preg_replace('/[^a-zA-Z0-9_$]/', '', $identifier) . '`';
167 }
168
172 abstract public function nextId(string $table_name): int;
173
177 public function createTable(
178 string $table_name,
179 array $fields,
180 bool $drop_table = false,
181 bool $ignore_erros = false
182 ): bool {
183 // check table name
184 if (!$ignore_erros && !$this->checkTableName($table_name)) {
185 throw new ilDatabaseException("ilDB Error: createTable(" . $table_name . ")");
186 }
187
188 // check definition array
189 if (!$ignore_erros && !$this->checkTableColumns($fields)) {
190 throw new ilDatabaseException("ilDB Error: createTable(" . $table_name . ")");
191 }
192
193 if ($drop_table) {
194 $this->dropTable($table_name, false);
195 }
196
197 return $this->manager->createTable($table_name, $fields, array());
198 }
199
200 protected function checkTableColumns(array $a_cols): bool
201 {
202 foreach ($a_cols as $col => $def) {
203 if (!$this->checkColumn($col, $def)) {
204 return false;
205 }
206 }
207
208 return true;
209 }
210
211 protected function checkColumn(string $a_col, array $a_def): bool
212 {
213 if (!$this->checkColumnName($a_col)) {
214 return false;
215 }
216 return $this->checkColumnDefinition($a_def);
217 }
218
219 protected function checkColumnDefinition(array $a_def, bool $a_modify_mode = false): bool
220 {
221 return $this->field_definition->checkColumnDefinition($a_def);
222 }
223
224 public function checkColumnName(string $a_name): bool
225 {
226 return $this->field_definition->checkColumnName($a_name);
227 }
228
232 public function addPrimaryKey(string $table_name, array $primary_keys): bool
233 {
234 assert(is_array($primary_keys));
235
236 $fields = array();
237 foreach ($primary_keys as $f) {
238 $fields[$f] = array();
239 }
240 $definition = array(
241 'primary' => true,
242 'fields' => $fields,
243 );
244 $this->manager->createConstraint(
245 $table_name,
246 $this->constraintName($table_name, $this->getPrimaryKeyIdentifier()),
247 $definition
248 );
249
250 return true;
251 }
252
256 public function dropIndexByFields(string $table_name, array $fields): bool
257 {
258 foreach ($this->manager->listTableIndexes($table_name) as $idx_name) {
259 $def = $this->reverse->getTableIndexDefinition($table_name, $idx_name);
260 $idx_fields = array_keys($def['fields']);
261
262 if ($idx_fields === $fields) {
263 return $this->dropIndex($table_name, $idx_name);
264 }
265 }
266
267 return false;
268 }
269
270 public function getPrimaryKeyIdentifier(): string
271 {
272 return "PRIMARY";
273 }
274
275 public function createSequence(string $table_name, int $start = 1): bool
276 {
277 $this->manager->createSequence($table_name, $start);
278 return true;
279 }
280
281 public function tableExists(string $table_name): bool
282 {
283 $result = $this->pdo->prepare("SHOW TABLES LIKE :table_name");
284 $result->execute(array('table_name' => $table_name));
285 $return = $result->rowCount();
286 $result->closeCursor();
287
288 return $return > 0;
289 }
290
291 public function tableColumnExists(string $table_name, string $column_name): bool
292 {
293 $fields = $this->loadModule(ilDBConstants::MODULE_MANAGER)->listTableFields($table_name);
294
295 return in_array($column_name, $fields, true);
296 }
297
301 public function addTableColumn(string $table_name, string $column_name, array $attributes): bool
302 {
303 if (!$this->checkColumnName($column_name)) {
304 throw new ilDatabaseException("ilDB Error: addTableColumn(" . $table_name . ", " . $column_name . ")");
305 }
306 if (!$this->checkColumnDefinition($attributes)) {
307 throw new ilDatabaseException("ilDB Error: addTableColumn(" . $table_name . ", " . $column_name . ")");
308 }
309
310 $changes = array(
311 "add" => array(
312 $column_name => $attributes,
313 ),
314 );
315
316 return $this->manager->alterTable($table_name, $changes, false);
317 }
318
322 public function dropTable(string $table_name, bool $error_if_not_existing = true): bool
323 {
324 $ilDBPdoManager = $this->loadModule(ilDBConstants::MODULE_MANAGER);
325 $tables = $ilDBPdoManager->listTables();
326 $table_exists = in_array($table_name, $tables);
327 if (!$table_exists && $error_if_not_existing) {
328 throw new ilDatabaseException("Table $table_name does not exist");
329 }
330
331 // drop sequence
332 $sequences = $ilDBPdoManager->listSequences();
333 if (in_array($table_name, $sequences)) {
334 $ilDBPdoManager->dropSequence($table_name);
335 }
336
337 // drop table
338 if ($table_exists) {
339 $ilDBPdoManager->dropTable($table_name);
340 }
341
342 return true;
343 }
344
348 public function query(string $query): ilDBStatement
349 {
350 global $DIC;
351 $ilBench = $DIC['ilBench'] ?? null;
352
353 $query = $this->appendLimit($query);
354
355 try {
356 if ($ilBench instanceof ilBenchmark) {
357 $ilBench->startDbBench($query);
358 }
359 $res = $this->pdo->query($query);
360 if ($ilBench instanceof ilBenchmark) {
361 $ilBench->stopDbBench();
362 }
363 } catch (PDOException $e) {
364 throw new ilDatabaseException($e->getMessage() . ' QUERY: ' . $query, (int) $e->getCode());
365 }
366
367 $err = $this->pdo->errorCode();
368 if ($err !== PDO::ERR_NONE) {
369 $info = $this->pdo->errorInfo();
370 $info_message = $info[2];
371 throw new ilDatabaseException($info_message . ' QUERY: ' . $query);
372 }
373
374 return new ilPDOStatement($res);
375 }
376
377 public function fetchAll(ilDBStatement $statement, int $fetch_mode = ilDBConstants::FETCHMODE_ASSOC): array
378 {
379 $return = [];
380 while ($data = $statement->fetch($fetch_mode)) {
381 $return[] = $data;
382 }
383
384 return $return;
385 }
386
387 public function dropSequence(string $table_name): bool
388 {
389 $this->manager->dropSequence($table_name);
390 return true;
391 }
392
396 public function dropTableColumn(string $table_name, string $column_name): bool
397 {
398 $changes = array(
399 "remove" => array(
400 $column_name => array(),
401 ),
402 );
403
404 return $this->manager->alterTable($table_name, $changes, false);
405 }
406
410 public function renameTableColumn(string $table_name, string $column_old_name, string $column_new_name): bool
411 {
412 // check table name
413 if (!$this->checkColumnName($column_new_name)) {
414 throw new ilDatabaseException("ilDB Error: renameTableColumn(" . $table_name . "," . $column_old_name . "," . $column_new_name . ")");
415 }
416
417 $def = $this->reverse->getTableFieldDefinition($table_name, $column_old_name);
418
419 $analyzer = new ilDBAnalyzer($this);
420 $best_alt = $analyzer->getBestDefinitionAlternative($def);
421 $def = $def[$best_alt];
422 unset($def["nativetype"]);
423 unset($def["mdb2type"]);
424
425 $f["definition"] = $def;
426 $f["name"] = $column_new_name;
427
428 $changes = array(
429 "rename" => array(
430 $column_old_name => $f,
431 ),
432 );
433
434 return $this->manager->alterTable($table_name, $changes, false);
435 }
436
437 public function insert(string $table_name, array $values): int
438 {
439 $real = array();
440 $fields = array();
441 foreach ($values as $key => $val) {
442 $real[] = $this->quote($val[1], $val[0]);
443 $fields[] = $this->quoteIdentifier($key);
444 }
445 $values_string = implode(",", $real);
446 $fields_string = implode(",", $fields);
447 $query = "INSERT INTO " . $this->quoteIdentifier($table_name) . " (" . $fields_string . ") VALUES (" . $values_string . ")";
448
449 $query = $this->sanitizeMB4StringIfNotSupported($query);
450
451 return (int) $this->pdo->exec($query);
452 }
453
454 public function fetchObject(ilDBStatement $query_result): ?stdClass
455 {
456 $res = $query_result->fetchObject();
457 if ($res === null) {
458 $query_result->closeCursor();
459
460 return null;
461 }
462
463 return $res;
464 }
465
466 public function update(string $table_name, array $columns, array $where): int
467 {
468 $fields = array();
469 $field_values = array();
470 $placeholders = array();
471 $placeholders_full = array();
472 $types = array();
473 $values = array();
474 $lobs = false;
475 $lob = array();
476 foreach ($columns as $k => $col) {
477 $field_value = $col[1];
478 $fields[] = $k;
479 $placeholders[] = "%s";
480 $placeholders_full[] = ":$k";
481 $types[] = $col[0];
482
483 if (($col[0] === "blob" || $col[0] === "clob" || $col[0] === 'text') && is_string($field_value)) {
484 $field_value = $this->sanitizeMB4StringIfNotSupported($field_value);
485 }
486
487 // integer auto-typecast (this casts bool values to integer)
488 if ($col[0] === 'integer' && !is_null($field_value)) {
489 $field_value = (int) $field_value;
490 }
491
492 $values[] = $field_value;
493 $field_values[$k] = $field_value;
494 if ($col[0] === "blob" || $col[0] === "clob") {
495 $lobs = true;
496 }
497 }
498
499 if ($lobs) {
500 $q = "UPDATE " . $this->quoteIdentifier($table_name) . " SET ";
501 $lim = "";
502 foreach ($fields as $k => $field) {
503 $q .= $lim . $this->quoteIdentifier($field) . " = " . $placeholders_full[$k];
504 $lim = ", ";
505 }
506 $q .= " WHERE ";
507 $lim = "";
508 foreach ($where as $k => $col) {
509 $q .= $lim . $this->quoteIdentifier($k) . " = " . $this->quote($col[1], $col[0]);
510 $lim = " AND ";
511 }
512
513 $r = $this->prepareManip($q, $types);
514 $this->execute($r, $field_values);
515
516 $num_affected_rows = $r->rowCount();
517
518 $this->free($r);
519 } else {
520 foreach ($where as $k => $col) {
521 $types[] = $col[0];
522 $values[] = $col[1];
523 $field_values[$k] = $col;
524 }
525 $q = "UPDATE " . $this->quoteIdentifier($table_name) . " SET ";
526 $lim = "";
527 foreach ($fields as $k => $field) {
528 $q .= $lim . $this->quoteIdentifier($field) . " = " . $placeholders[$k];
529 $lim = ", ";
530 }
531 $q .= " WHERE ";
532 $lim = "";
533 foreach (array_keys($where) as $k) {
534 $q .= $lim . $this->quoteIdentifier($k) . " = %s";
535 $lim = " AND ";
536 }
537
538 $num_affected_rows = $this->manipulateF($q, $types, $values);
539 }
540
541 return $num_affected_rows;
542 }
543
547 public function manipulate(string $query): int
548 {
549 global $DIC;
550 $ilBench = $DIC['ilBench'] ?? null;
551 try {
552 $query = $this->sanitizeMB4StringIfNotSupported($query);
553 if ($ilBench instanceof ilBenchmark) {
554 $ilBench->startDbBench($query);
555 }
556 $num_affected_rows = $this->pdo->exec($query);
557 if ($ilBench instanceof ilBenchmark) {
558 $ilBench->stopDbBench();
559 }
560 } catch (PDOException $e) {
561 throw new ilDatabaseException($e->getMessage() . ' QUERY: ' . $query, (int) $e->getCode());
562 }
563
564 return (int) $num_affected_rows;
565 }
566
567 public function fetchAssoc(ilDBStatement $statement): ?array
568 {
569 $res = $statement->fetch(PDO::FETCH_ASSOC);
570 if ($res === null || $res === false) {
571 $statement->closeCursor();
572
573 return null;
574 }
575
576 return $res;
577 }
578
579 public function numRows(ilDBStatement $statement): int
580 {
581 return $statement->rowCount();
582 }
583
584 public function quote($value, ?string $type = null): string
585 {
586 if ($value === null) {
587 return 'NULL';
588 }
589
590 $pdo_type = PDO::PARAM_STR;
591 switch ($type) {
595 if ($value === '') {
596 return 'NULL';
597 }
598 if ($value === $this->now()) {
599 return $value;
600 }
601 $value = (string) $value;
602 break;
604 return (string) (int) $value;
606 $pdo_type = PDO::PARAM_INT;
607 $value = (string) $value;
608 break;
610 default:
611 $value = (string) $value;
612 $pdo_type = PDO::PARAM_STR;
613 break;
614 }
615
616 return $this->pdo->quote((string) $value, $pdo_type);
617 }
618
619 public function indexExistsByFields(string $table_name, array $fields): bool
620 {
621 foreach ($this->manager->listTableIndexes($table_name) as $idx_name) {
622 $def = $this->reverse->getTableIndexDefinition($table_name, $idx_name);
623 $idx_fields = array_keys($def['fields']);
624
625 if ($idx_fields === $fields) {
626 return true;
627 }
628 }
629
630 return false;
631 }
632
633 public function addIndex(string $table_name, array $fields, string $index_name = '', bool $fulltext = false): bool
634 {
635 assert(is_array($fields));
636 $this->field_definition->checkIndexName($index_name);
637
638 $definition_fields = array();
639 foreach ($fields as $f) {
640 $definition_fields[$f] = array();
641 }
642 $definition = array(
643 'fields' => $definition_fields,
644 );
645
646 if (!$fulltext) {
647 $this->manager->createIndex($table_name, $this->constraintName($table_name, $index_name), $definition);
648 } elseif ($this->supportsFulltext()) {
649 $this->addFulltextIndex($table_name, $fields, $index_name);
650 // TODO
651 }
652
653 return true;
654 }
655
659 public function addFulltextIndex(string $table, array $fields, string $a_name = "in"): bool
660 {
661 $i_name = $this->constraintName($table, $a_name) . "_idx";
662 $f_str = implode(",", $fields);
663 $q = "ALTER TABLE $table ADD FULLTEXT $i_name ($f_str)";
664 $this->query($q);
665 return true;
666 }
667
671 public function dropFulltextIndex(string $a_table, string $a_name): bool
672 {
673 $i_name = $this->constraintName($a_table, $a_name) . "_idx";
674 $this->query("ALTER TABLE $a_table DROP FULLTEXT $i_name");
675 return true;
676 }
677
681 public function isFulltextIndex(string $a_table, string $a_name): bool
682 {
683 $set = $this->query("SHOW INDEX FROM " . $a_table);
684 while ($rec = $this->fetchAssoc($set)) {
685 if ($rec["Key_name"] === $a_name && $rec["Index_type"] === "FULLTEXT") {
686 return true;
687 }
688 }
689
690 return false;
691 }
692
693 public function getIndexName(string $index_name_base): string
694 {
695 return sprintf(ilDBPdoFieldDefinition::INDEX_FORMAT, preg_replace('/[^a-z0-9_\$]/i', '_', $index_name_base));
696 }
697
698 public function getSequenceName(string $table_name): string
699 {
700 return sprintf(ilDBPdoFieldDefinition::SEQUENCE_FORMAT, preg_replace('/[^a-z0-9_\$.]/i', '_', $table_name));
701 }
702
707 public function constraintName(string $a_table, string $a_constraint): string
708 {
709 return $a_constraint;
710 }
711
712 public function getDSN(): string
713 {
714 return $this->dsn;
715 }
716
717 public function getDBType(): string
718 {
719 return $this->db_type;
720 }
721
722 public function setDBType(string $type): void
723 {
724 $this->db_type = $type;
725 }
726
732 public static function getReservedWords(): array
733 {
734 global $DIC;
735 $ilDB = $DIC->database();
736
740 $fd = $ilDB->getFieldDefinition();
741 if ($fd !== null) {
742 return $fd->getReservedMysql();
743 }
744 return [];
745 }
746
750 public function lockTables(array $tables): void
751 {
752 assert(is_array($tables));
753 $lock = $this->manager->getQueryUtils()->lock($tables);
754 $this->pdo->exec($lock);
755 }
756
761 public function unlockTables(): void
762 {
763 $this->pdo->exec($this->manager->getQueryUtils()->unlock());
764 }
765
766 public function in(string $field, array $values, bool $negate = false, string $type = ""): string
767 {
768 return $this->manager->getQueryUtils()->in($field, $values, $negate, $type);
769 }
770
775 public function queryF(string $query, array $types, array $values): ilDBStatement
776 {
777 if (!is_array($types) || !is_array($values) || count($types) !== count($values)) {
778 throw new ilDatabaseException("ilDB::queryF: Types and values must be arrays of same size. ($query)");
779 }
780 $quoted_values = array();
781 foreach ($types as $k => $t) {
782 $quoted_values[] = $this->quote($values[$k], $t);
783 }
784 $query = vsprintf($query, $quoted_values);
785
786 return $this->query($query);
787 }
788
793 public function manipulateF(string $query, array $types, array $values): int
794 {
795 if (!is_array($types) || !is_array($values) || count($types) !== count($values)) {
796 throw new ilDatabaseException("ilDB::manipulateF: types and values must be arrays of same size. ($query)");
797 }
798 $quoted_values = array();
799 foreach ($types as $k => $t) {
800 $quoted_values[] = $this->quote($values[$k], $t);
801 }
802 $query = vsprintf($query, $quoted_values);
803
804 return $this->manipulate($query);
805 }
806
810 public function useSlave(bool $bool): bool
811 {
812 return false;
813 }
814
818 public function setLimit(int $limit, int $offset = 0): void
819 {
820 $this->limit = $limit;
821 $this->offset = $offset;
822 }
823
827 public function like(string $column, string $type, string $value = "?", bool $case_insensitive = true): string
828 {
829 return $this->manager->getQueryUtils()->like($column, $type, $value, $case_insensitive);
830 }
831
835 public function now(): string
836 {
837 return $this->manager->getQueryUtils()->now();
838 }
839
840 public function replace(string $table, array $primary_keys, array $other_columns): int
841 {
842 $a_columns = array_merge($primary_keys, $other_columns);
843 $fields = [];
844 $placeholders = [];
845 $types = [];
846 $values = [];
847
848 foreach ($a_columns as $k => $col) {
849 $fields[] = $this->quoteIdentifier($k);
850 $placeholders[] = "%s";
851 $placeholders2[] = ":$k";
852 $types[] = $col[0];
853
854 // integer auto-typecast (this casts bool values to integer)
855 if ($col[0] === 'integer' && !is_null($col[1])) {
856 $col[1] = (int) $col[1];
857 }
858
859 $values[] = $col[1];
860 }
861
862 $q = "REPLACE INTO " . $table . " (" . implode(",", $fields) . ") VALUES (" . implode(",", $placeholders) . ")";
863
864 return $this->manipulateF($q, $types, $values);
865 }
866
870 public function equals(string $columns, $value, string $type, bool $emptyOrNull = false): string
871 {
872 if (!$emptyOrNull || $value != "") {
873 return $columns . " = " . $this->quote($value, $type);
874 }
875
876 return "(" . $columns . " = '' OR $columns IS NULL)";
877 }
878
879 public function getHost(): string
880 {
881 return $this->host;
882 }
883
884 public function setHost(string $host): void
885 {
886 $this->host = $host;
887 }
888
889 public function getDbname(): string
890 {
891 return $this->dbname;
892 }
893
894 public function setDbname(string $dbname): void
895 {
896 $this->dbname = $dbname;
897 }
898
899 public function getCharset(): string
900 {
901 return $this->charset;
902 }
903
904 public function setCharset(string $charset): void
905 {
906 $this->charset = $charset;
907 }
908
909 public function getUsername(): string
910 {
911 return $this->username;
912 }
913
914 public function setUsername(string $username): void
915 {
916 $this->username = $username;
917 }
918
919 public function getPassword(): string
920 {
921 return $this->password;
922 }
923
924 public function setPassword(string $password): void
925 {
926 $this->password = $password;
927 }
928
929 public function getPort(): int
930 {
931 return $this->port;
932 }
933
934 public function setPort(int $port): void
935 {
936 $this->port = $port;
937 }
938
939 public function setDBUser(string $user): void
940 {
941 $this->setUsername($user);
942 }
943
944 public function setDBPort(int $port): void
945 {
946 $this->setPort($port);
947 }
948
949 public function setDBPassword(string $password): void
950 {
951 $this->setPassword($password);
952 }
953
954 public function setDBHost(string $host): void
955 {
956 $this->setHost($host);
957 }
958
962 public function upper(string $expression): string
963 {
964 return " UPPER(" . $expression . ") ";
965 }
966
970 public function lower(string $expression): string
971 {
972 return " LOWER(" . $expression . ") ";
973 }
974
975 public function substr(string $a_exp, int $a_pos = 1, int $a_len = -1): string
976 {
977 $lenstr = "";
978 if ($a_len > -1) {
979 $lenstr = ", " . $a_len;
980 }
981 return " SUBSTR(" . $a_exp . ", " . $a_pos . $lenstr . ") ";
982 }
983
984 public function prepareManip(string $query, ?array $types = null): ilDBStatement
985 {
986 return new ilPDOStatement($this->pdo->prepare($query));
987 }
988
989 public function prepare(string $query, ?array $types = null, ?array $result_types = null): ilDBStatement
990 {
991 return new ilPDOStatement($this->pdo->prepare($query));
992 }
993
994 public function enableResultBuffering(bool $a_status): void
995 {
996 $this->pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, $a_status);
997 }
998
1002 public function execute(ilDBStatement $stmt, array $data = []): ilDBStatement
1003 {
1007 $result = $stmt->execute($data);
1008 if ($result === false) {//This may not work since execute returns an object
1009 throw new ilDatabaseException(implode(', ', $stmt->errorInfo()), (int) $stmt->errorCode());
1010 }
1011 return $stmt;
1012 }
1013
1014 public function supportsSlave(): bool
1015 {
1016 return false;
1017 }
1018
1019 public function supportsFulltext(): bool
1020 {
1021 return false;
1022 }
1023
1024 public function supportsTransactions(): bool
1025 {
1026 return false;
1027 }
1028
1029 public function supports(string $feature): bool
1030 {
1031 switch ($feature) {
1032 case self::FEATURE_TRANSACTIONS:
1033 return $this->supportsTransactions();
1034 case self::FEATURE_FULLTEXT:
1035 return $this->supportsFulltext();
1036 case self::FEATURE_SLAVE:
1037 return $this->supportsSlave();
1038 default:
1039 return false;
1040 }
1041 }
1042
1046 public function listTables(): array
1047 {
1048 return $this->manager->listTables();
1049 }
1050
1054 public function loadModule(string $module)
1055 {
1056 switch ($module) {
1058 return $this->manager;
1060 return $this->reverse;
1061 }
1062 throw new LogicException('module "' . $module . '" not available');
1063 }
1064
1068 public function getAllowedAttributes(): array
1069 {
1070 return $this->field_definition->getAllowedAttributes();
1071 }
1072
1073 public function sequenceExists(string $sequence): bool
1074 {
1075 return in_array($sequence, $this->listSequences(), true);
1076 }
1077
1078 public function listSequences(): array
1079 {
1080 return $this->manager->listSequences();
1081 }
1082
1083 public function concat(array $values, bool $allow_null = true): string
1084 {
1085 return $this->manager->getQueryUtils()->concat($values, $allow_null);
1086 }
1087
1088 protected function appendLimit(string $query): string
1089 {
1090 if ($this->limit !== null && $this->offset !== null) {
1091 $query .= ' LIMIT ' . $this->offset . ', ' . $this->limit;
1092 $this->limit = null;
1093 $this->offset = null;
1094
1095 return $query;
1096 }
1097
1098 return $query;
1099 }
1100
1101 public function locate(string $needle, string $string, int $start_pos = 1): string
1102 {
1103 return $this->manager->getQueryUtils()->locate($needle, $string, $start_pos);
1104 }
1105
1109 public function modifyTableColumn(string $table, string $column, array $attributes): bool
1110 {
1111 $def = $this->reverse->getTableFieldDefinition($table, $column);
1112
1113 $analyzer = new ilDBAnalyzer($this);
1114 $best_alt = $analyzer->getBestDefinitionAlternative($def);
1115 $def = $def[$best_alt];
1116 unset($def["nativetype"], $def["mdb2type"]);
1117
1118 // check attributes
1119 $ilDBPdoFieldDefinition = $this->field_definition;
1120
1121 $type = $attributes["type"] ?? $def["type"];
1122
1123 foreach (array_keys($def) as $k) {
1124 if ($k !== "type" && !$ilDBPdoFieldDefinition->isAllowedAttribute($k, $type)) {
1125 unset($def[$k]);
1126 }
1127 }
1128 $check_array = $def;
1129 foreach ($attributes as $k => $v) {
1130 $check_array[$k] = $v;
1131 }
1132 if (!$this->checkColumnDefinition($check_array, true)) {
1133 throw new ilDatabaseException("ilDB Error: modifyTableColumn(" . $table . ", " . $column . ")");
1134 }
1135
1136 foreach ($attributes as $a => $v) {
1137 $def[$a] = $v;
1138 }
1139
1140 $attributes["definition"] = $def;
1141
1142 $changes = array(
1143 "change" => array(
1144 $column => $attributes,
1145 ),
1146 );
1147
1148 return $this->manager->alterTable($table, $changes, false);
1149 }
1150
1151 public function free(ilDBStatement $a_st): void
1152 {
1153 $a_st->closeCursor();
1154 }
1155
1159 public function renameTable(string $name, string $new_name): bool
1160 {
1161 // check table name
1162 try {
1163 $this->checkTableName($new_name);
1164 } catch (ilDatabaseException $e) {
1165 throw new ilDatabaseException(
1166 "ilDB Error: renameTable(" . $name . "," . $new_name . ")<br />" . $e->getMessage(),
1167 $e->getCode()
1168 );
1169 }
1170
1171 $this->manager->alterTable($name, ["name" => $new_name], false);
1172 if ($this->sequenceExists($name)) {
1173 $this->manager->alterTable(
1174 $this->getSequenceName($name),
1175 ["name" => $this->getSequenceName($new_name)],
1176 false
1177 );
1178 }
1179 // The abstraction_progress is no longer used in ILIAS, see http://www.ilias.de/mantis/view.php?id=19513
1180 // $query = "UPDATE abstraction_progress " . "SET table_name = " . $this->quote($a_new_name, 'text') . " " . "WHERE table_name = "
1181 // . $this->quote($a_name, 'text');
1182 // $this->pdo->query($query);
1183
1184 return true;
1185 }
1186
1190 public function checkTableName(string $a_name): bool
1191 {
1192 return $this->field_definition->checkTableName($a_name);
1193 }
1194
1195 public static function isReservedWord(string $a_word): bool
1196 {
1197 global $DIC;
1198 return (new ilDBPdoMySQLFieldDefinition($DIC->database()))->isReserved($a_word);
1199 }
1200
1204 public function beginTransaction(): bool
1205 {
1206 if (!$this->supports(self::FEATURE_TRANSACTIONS)) {
1207 throw new ilDatabaseException("ilDB::beginTransaction: Transactions are not supported.");
1208 }
1209
1210 return $this->pdo->beginTransaction();
1211 }
1212
1216 public function commit(): bool
1217 {
1218 if (!$this->supports(self::FEATURE_TRANSACTIONS)) {
1219 throw new ilDatabaseException("ilDB::beginTransaction: Transactions are not supported.");
1220 }
1221
1222 return $this->pdo->commit();
1223 }
1224
1228 public function rollback(): bool
1229 {
1230 if (!$this->supports(self::FEATURE_TRANSACTIONS)) {
1231 throw new ilDatabaseException("ilDB::beginTransaction: Transactions are not supported.");
1232 }
1233
1234 return $this->pdo->rollBack();
1235 }
1236
1237 public function dropIndex(string $a_table, string $a_name = "i1"): bool
1238 {
1239 return $this->manager->dropIndex($a_table, $a_name);
1240 }
1241
1242 public function setStorageEngine(string $storage_engine): void
1243 {
1244 $this->storage_engine = $storage_engine;
1245 }
1246
1247 public function getStorageEngine(): string
1248 {
1249 return $this->storage_engine;
1250 }
1251
1252 public function queryCol(string $query, int $type = PDO::FETCH_ASSOC, int $colnum = 0): array
1253 {
1254 switch ($type) {
1256 $type = PDO::FETCH_ASSOC;
1257 break;
1259 $type = PDO::FETCH_OBJ;
1260 break;
1261 default:
1262 $type = PDO::FETCH_ASSOC;
1263 break;
1264 }
1265
1266 return $this->pdo->query($query, PDO::FETCH_ASSOC)->fetchAll(PDO::FETCH_COLUMN, $colnum);
1267 }
1268
1269 public function queryRow(
1270 string $query,
1271 ?array $types = null,
1272 int $fetchmode = ilDBConstants::FETCHMODE_DEFAULT
1273 ): array {
1274 switch ($fetchmode) {
1276 $type = PDO::FETCH_ASSOC;
1277 break;
1279 $type = PDO::FETCH_OBJ;
1280 break;
1281 default:
1282 $type = PDO::FETCH_ASSOC;
1283 break;
1284 }
1285
1286 return $this->pdo->query($query, $type)->fetch();
1287 }
1288
1289 public function getServerVersion(bool $native = false): int
1290 {
1291 return $this->pdo->query('SELECT VERSION()')->fetchColumn();
1292 }
1293
1294 public function escape(string $value, bool $escape_wildcards = false): string
1295 {
1296 return $value;
1297 }
1298
1299 public function escapePattern(string $text): string
1300 {
1301 return $text;
1302 }
1303
1304 public function migrateAllTablesToEngine(string $engine = ilDBConstants::MYSQL_ENGINE_INNODB): array
1305 {
1306 return array();
1307 }
1308
1313 {
1314 return array();
1315 }
1316
1320 public function supportsCollationMigration(): bool
1321 {
1322 return false;
1323 }
1324
1325 public function supportsEngineMigration(): bool
1326 {
1327 return false;
1328 }
1329
1333 public function checkIndexName(string $name): bool
1334 {
1335 $fd = $this->getFieldDefinition();
1336 if ($fd !== null) {
1337 return $fd->checkIndexName($name);
1338 }
1339 return false;
1340 }
1341
1345 public function addUniqueConstraint(string $table, array $fields, string $name = "con"): bool
1346 {
1347 assert(is_array($fields));
1348 $manager = $this->manager;
1349
1350 // check index name
1351 if (!$this->checkIndexName($name)) {
1352 throw new ilDatabaseException("ilDB Error: addUniqueConstraint(" . $table . "," . $name . ")");
1353 }
1354
1355 $fields_corrected = array();
1356 foreach ($fields as $f) {
1357 $fields_corrected[$f] = array();
1358 }
1359 $definition = array(
1360 'unique' => true,
1361 'fields' => $fields_corrected,
1362 );
1363
1364 return $manager->createConstraint($table, $this->constraintName($table, $name), $definition);
1365 }
1366
1367 public function dropUniqueConstraint(string $table, string $name = "con"): bool
1368 {
1369 return $this->manager->dropConstraint($table, $this->constraintName($table, $name), false);
1370 }
1371
1372 public function dropUniqueConstraintByFields(string $table, array $fields): bool
1373 {
1374 $analyzer = new ilDBAnalyzer();
1375 $cons = $analyzer->getConstraintsInformation($table);
1376 foreach ($cons as $c) {
1377 if ($c["type"] === "unique" && count($fields) === count($c["fields"])) {
1378 $all_in = true;
1379 foreach ($fields as $f) {
1380 if (!isset($c["fields"][$f])) {
1381 $all_in = false;
1382 }
1383 }
1384 if ($all_in) {
1385 return $this->dropUniqueConstraint($table, $c['name']);
1386 }
1387 }
1388 }
1389
1390 return false;
1391 }
1392
1393 public function getLastInsertId(): int
1394 {
1395 return (int) $this->pdo->lastInsertId();
1396 }
1397
1398 public function buildAtomQuery(): ilAtomQuery
1399 {
1400 return new ilAtomQueryLock($this);
1401 }
1402
1403 public function uniqueConstraintExists(string $table, array $fields): bool
1404 {
1405 $analyzer = new ilDBAnalyzer();
1406 $cons = $analyzer->getConstraintsInformation($table);
1407 foreach ($cons as $c) {
1408 if ($c["type"] === "unique" && count($fields) === count($c["fields"])) {
1409 $all_in = true;
1410 foreach ($fields as $f) {
1411 if (!isset($c["fields"][$f])) {
1412 $all_in = false;
1413 }
1414 }
1415 if ($all_in) {
1416 return true;
1417 }
1418 }
1419 }
1420
1421 return false;
1422 }
1423
1424 public function dropPrimaryKey(string $table_name): bool
1425 {
1426 return $this->manager->dropConstraint($table_name, "PRIMARY", true);
1427 }
1428
1429 public function executeMultiple(ilDBStatement $stmt, array $data): array
1430 {
1431 foreach ($data as $set) {
1432 $this->execute($stmt, $set);
1433 }
1434 return [];
1435 }
1436
1437 public function fromUnixtime(string $expr, bool $to_text = true): string
1438 {
1439 return "FROM_UNIXTIME(" . $expr . ")";
1440 }
1441
1442 public function unixTimestamp(): string
1443 {
1444 return "UNIX_TIMESTAMP()";
1445 }
1446
1447
1451 public function getDBVersion(): string
1452 {
1453 $d = $this->fetchObject($this->query("SELECT VERSION() AS version"));
1454
1455 if ($d !== null && $d->version) {
1456 return $d->version;
1457 }
1458 return 'Unknown';
1459 }
1460
1464 public function sanitizeMB4StringIfNotSupported(string $query): string
1465 {
1466 if (!$this->doesCollationSupportMB4Strings()) {
1467 $query_replaced = preg_replace(
1468 '/[\x{10000}-\x{10FFFF}]/u',
1470 $query
1471 );
1472 if (!empty($query_replaced)) {
1473 return $query_replaced;
1474 }
1475 }
1476
1477 return $query;
1478 }
1479
1483 public function doesCollationSupportMB4Strings(): bool
1484 {
1485 return false;
1486 }
1487
1491 public function groupConcat(string $a_field_name, string $a_seperator = ",", ?string $a_order = null): string
1492 {
1493 return $this->manager->getQueryUtils()->groupConcat($a_field_name, $a_seperator, $a_order);
1494 }
1495
1499 public function cast(string $a_field_name, string $a_dest_type): string
1500 {
1501 return $this->manager->getQueryUtils()->cast($a_field_name, $a_dest_type);
1502 }
1503
1504 public function primaryExistsByFields(string $table_name, array $fields): bool
1505 {
1506 $constraints = $this->manager->listTableConstraints($table_name);
1507
1508 if (in_array('primary', $constraints)) {
1509 $definitions = $this->reverse->getTableConstraintDefinition($table_name, 'primary');
1510 $primary_fields = array_keys($definitions['fields']);
1511 sort($primary_fields);
1512 sort($fields);
1513
1514 return $primary_fields === $fields;
1515 }
1516 return false;
1517 }
1518}
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Class ilBenchmark.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
createConstraint(string $table, string $name, array $definition)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Class pdoDB.
string $db_type
renameTable(string $name, string $new_name)
fetchAll(ilDBStatement $statement, int $fetch_mode=ilDBConstants::FETCHMODE_ASSOC)
setUsername(string $username)
dropUniqueConstraintByFields(string $table, array $fields)
supportsTransactions()
checkColumnName(string $a_name)
string $dbname
setLimit(int $limit, int $offset=0)
Set the Limit for the next Query.
createTable(string $table_name, array $fields, bool $drop_table=false, bool $ignore_erros=false)
in(string $field, array $values, bool $negate=false, string $type="")
tableExists(string $table_name)
getFieldDefinition()
manipulateF(string $query, array $types, array $values)
string $storage_engine
dropTable(string $table_name, bool $error_if_not_existing=true)
string $username
sanitizeMB4StringIfNotSupported(string $query)
string sanitized query
isFulltextIndex(string $a_table, string $a_name)
Is index a fulltext index?
numRows(ilDBStatement $statement)
setStorageEngine(string $storage_engine)
escapePattern(string $text)
free(ilDBStatement $a_st)
connect(bool $return_false_for_error=false)
insert(string $table_name, array $values)
fromUnixtime(string $expr, bool $to_text=true)
setPort(int $port)
groupConcat(string $a_field_name, string $a_seperator=",", ?string $a_order=null)
enableResultBuffering(bool $a_status)
checkIndexName(string $name)
dropIndex(string $a_table, string $a_name="i1")
addPrimaryKey(string $table_name, array $primary_keys)
substr(string $a_exp, int $a_pos=1, int $a_len=-1)
queryF(string $query, array $types, array $values)
string $dsn
getLastErrorCode()
supportsEngineMigration()
string $host
query(string $query)
getPrimaryKeyIdentifier()
executeMultiple(ilDBStatement $stmt, array $data)
string $password
getAllowedAttributes()
string[]
checkTableName(string $a_name)
setDBPassword(string $password)
setHost(string $host)
addFulltextIndex(string $table, array $fields, string $a_name="in")
sequenceExists(string $sequence)
getDBType()
Get DSN.
array $options
escape(string $value, bool $escape_wildcards=false)
ilDBPdoReverse $reverse
getServerVersion(bool $native=false)
modifyTableColumn(string $table, string $column, array $attributes)
string $charset
addUniqueConstraint(string $table, array $fields, string $name="con")
migrateAllTablesToCollation(string $collation=ilDBConstants::MYSQL_COLLATION_UTF8MB4)
@inheritDoc
setCharset(string $charset)
dropFulltextIndex(string $a_table, string $a_name)
Drop fulltext index.
getAdditionalAttributes()
dropUniqueConstraint(string $table, string $name="con")
addIndex(string $table_name, array $fields, string $index_name='', bool $fulltext=false)
getSequenceName(string $table_name)
setFieldDefinition(\ilDBPdoFieldDefinition $field_definition)
createSequence(string $table_name, int $start=1)
dropPrimaryKey(string $table_name)
const FEATURE_SLAVE
supportsCollationMigration()
@inheritDoc
appendLimit(string $query)
prepareManip(string $query, ?array $types=null)
loadModule(string $module)
setDBHost(string $host)
queryCol(string $query, int $type=PDO::FETCH_ASSOC, int $colnum=0)
setDBType(string $type)
setDbname(string $dbname)
indexExistsByFields(string $table_name, array $fields)
concat(array $values, bool $allow_null=true)
renameTableColumn(string $table_name, string $column_old_name, string $column_new_name)
equals(string $columns, $value, string $type, bool $emptyOrNull=false)
lockTables(array $tables)
checkColumn(string $a_col, array $a_def)
migrateAllTablesToEngine(string $engine=ilDBConstants::MYSQL_ENGINE_INNODB)
primaryExistsByFields(string $table_name, array $fields)
prepare(string $query, ?array $types=null, ?array $result_types=null)
ilDBPdoFieldDefinition $field_definition
array $attributes
int $error_code
ilDBPdoManager $manager
like(string $column, string $type, string $value="?", bool $case_insensitive=true)
dropIndexByFields(string $table_name, array $fields)
quoteIdentifier(string $identifier, bool $check_option=false)
doesCollationSupportMB4Strings()
@inheritDoc
const FEATURE_TRANSACTIONS
dropTableColumn(string $table_name, string $column_name)
createDatabase(string $a_name, string $a_charset="utf8", string $a_collation="")
getIndexName(string $index_name_base)
manipulate(string $query)
addTableColumn(string $table_name, string $column_name, array $attributes)
constraintName(string $a_table, string $a_constraint)
Determine contraint name by table name and constraint name.
useSlave(bool $bool)
TODO.
fetchAssoc(ilDBStatement $statement)
checkTableColumns(array $a_cols)
replace(string $table, array $primary_keys, array $other_columns)
Replace into method.
locate(string $needle, string $string, int $start_pos=1)
checkColumnDefinition(array $a_def, bool $a_modify_mode=false)
upper(string $expression)
setDBUser(string $user)
const FEATURE_FULLTEXT
setDBPort(int $port)
dropSequence(string $table_name)
uniqueConstraintExists(string $table, array $fields)
nextId(string $table_name)
getDSN()
Get DSN.
cast(string $a_field_name, string $a_dest_type)
fetchObject(ilDBStatement $query_result)
update(string $table_name, array $columns, array $where)
@description $where MUST contain existing columns only.
static isReservedWord(string $a_word)
supports(string $feature)
setPassword(string $password)
tableColumnExists(string $table_name, string $column_name)
initHelpers()
queryRow(string $query, ?array $types=null, int $fetchmode=ilDBConstants::FETCHMODE_DEFAULT)
initFromIniFile(?ilIniFile $ini=null)
quote($value, ?string $type=null)
lower(string $expression)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Class ilPDOStatement is a Wrapper Class for PDOStatement.
$c
Definition: cli.php:38
if(!file_exists(getcwd() . '/ilias.ini.php'))
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Definition: confirmReg.php:20
for( $i=6;$i< 13;$i++) for($i=1; $i< 13; $i++) $d
Definition: date.php:296
global $DIC
Definition: feed.php:28
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Interface ilDBInterface.
Interface ilDBPdoInterface.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
execute(array $a_data=null)
fetch(int $fetch_mode=ilDBConstants::FETCHMODE_ASSOC)
$res
Definition: ltiservices.php:69
if($format !==null) $name
Definition: metadata.php:247
$attributes
Definition: metadata.php:248
string $key
Consumer key/client ID value.
Definition: System.php:193
$a
thx to https://mlocati.github.io/php-cs-fixer-configurator for the examples
$query
$type
$ini
Definition: raiseError.php:4