ILIAS  release_7 Revision v7.30-3-g800a261c036
class.ilMySQLAbstraction.php
Go to the documentation of this file.
1<?php
2/* Copyright (c) 1998-2009 ILIAS open source, Extended GPL, see docs/LICENSE */
3
13{
14
18 public $analyzer;
22 protected $ilDBInterface;
26 protected $manager;
30 protected $reverse;
34 protected $testmode;
35
36
40 public function __construct()
41 {
42 global $DIC;
43 $ilDB = $DIC->database();
44
45 $this->ilDBInterface = $ilDB;
46 $this->manager = $ilDB->loadModule(ilDBConstants::MODULE_MANAGER);
47 $this->reverse = $ilDB->loadModule(ilDBConstants::MODULE_REVERSE);
48 $this->analyzer = new ilDBAnalyzer();
49 $this->setTestMode(false);
50 }
51
52
56 public function setTestMode($a_testmode)
57 {
58 $this->testmode = $a_testmode;
59 }
60
61
65 public function getTestMode()
66 {
67 return $this->testmode;
68 }
69
70
82 public function performAbstraction($a_table_name, $a_set_text_ts_fields_notnull_false = true)
83 {
84 // to do: log this procedure
85
86 // count number of records at the beginning
87 $nr_rec = $this->countRecords($a_table_name);
88
89 // convert table name to lowercase
90 if (!$this->getTestMode()) {
91 $this->lowerCaseTableName($a_table_name);
92 $a_table_name = strtolower($a_table_name);
93 $this->storeStep($a_table_name, 10);
94 }
95
96 // get auto increment information
97 $auto_inc_field = $this->analyzer->getAutoIncrementField($a_table_name);
98
99 // get primary key information
100 $pk = $this->analyzer->getPrimaryKeyInformation($a_table_name);
101
102 // get indices information
103 $indices = $this->analyzer->getIndicesInformation($a_table_name);
104
105 // get constraints information
106 $constraints = $this->analyzer->getConstraintsInformation($a_table_name);
107
108 // get field information
109 $fields = $this->analyzer->getFieldInformation($a_table_name);
110
111 if (!$this->getTestMode()) {
112 // remove auto increment
113 $this->removeAutoIncrement($a_table_name, $auto_inc_field, $fields);
114 $this->storeStep($a_table_name, 20);
115
116 // remove primary key
117 $this->removePrimaryKey($a_table_name, $pk);
118 $this->storeStep($a_table_name, 30);
119
120 // remove indices
121 $this->removeIndices($a_table_name, $indices);
122 $this->storeStep($a_table_name, 40);
123
124 // remove constraints
125 $this->removeConstraints($a_table_name, $constraints);
126 $this->storeStep($a_table_name, 45);
127 }
128
129 // alter table using mdb2 field types
130 $this->alterTable($a_table_name, $fields, $a_set_text_ts_fields_notnull_false, $pk);
131 if ($this->getTestMode()) {
132 $a_table_name = strtolower($a_table_name) . "_copy";
133 } else {
134 $this->storeStep($a_table_name, 50);
135 }
136
137 // lower case field names
138 $this->lowerCaseColumnNames($a_table_name);
139 if (!$this->getTestMode()) {
140 $this->storeStep($a_table_name, 60);
141 }
142
143 // add primary key
144 $this->addPrimaryKey($a_table_name, $pk);
145 if (!$this->getTestMode()) {
146 $this->storeStep($a_table_name, 70);
147 }
148
149 // add indices
150 $this->addIndices($a_table_name, $indices);
151 if (!$this->getTestMode()) {
152 $this->storeStep($a_table_name, 80);
153 }
154
155 // add constraints
156 $this->addConstraints($a_table_name, $constraints);
157 if (!$this->getTestMode()) {
158 $this->storeStep($a_table_name, 85);
159 }
160
161 // add "auto increment" sequence
162 if ($auto_inc_field != "") {
163 $this->addAutoIncrementSequence($a_table_name, $auto_inc_field);
164 }
165 if (!$this->getTestMode()) {
166 $this->storeStep($a_table_name, 90);
167 }
168
169 // replace empty strings with null values in text fields
170 $this->replaceEmptyStringsWithNull($a_table_name);
171 if (!$this->getTestMode()) {
172 $this->storeStep($a_table_name, 100);
173 }
174
175 // replace empty "0000-00-00..." dates with null
176 $this->replaceEmptyDatesWithNull($a_table_name);
177 if (!$this->getTestMode()) {
178 $this->storeStep($a_table_name, 110);
179 }
180
181 $nr_rec2 = $this->countRecords($a_table_name);
182
183 if (!$this->getTestMode()) {
184 if ($nr_rec != $nr_rec2) {
185 throw new ilDatabaseException(
186 "ilMySQLAbstraction: Unexpected difference in table record number, table '" . $a_table_name . "'." . " Before: " . ((int) $nr_rec) . ", After: " . ((int) $nr_rec2) . "."
187 );
188 }
189 }
190 }
191
192
200 public function countRecords($a_table_name)
201 {
202 global $DIC;
203 $ilDB = $DIC->database();
204
205 $st = $ilDB->prepare("SELECT count(*) AS cnt FROM `" . $a_table_name . "`");
206 $res = $ilDB->execute($st);
207 $rec = $ilDB->fetchAssoc($res);
208
209 return $rec["cnt"];
210 }
211
212
219 public function storeStep($a_table, $a_step)
220 {
221 $st = $this->ilDBInterface->prepareManip(
222 "REPLACE INTO abstraction_progress (table_name, step)" . " VALUES (?,?)",
223 array(
224 "text",
225 "integer",
226 )
227 );
228 $this->ilDBInterface->execute(
229 $st,
230 array(
231 $a_table,
232 $a_step,
233 )
234 );
235 }
236
237
245 public function replaceEmptyStringsWithNull($a_table)
246 {
247 global $DIC;
248 $ilDB = $DIC->database();
249
250 $fields = $this->analyzer->getFieldInformation($a_table);
251 $upfields = array();
252 foreach ($fields as $field => $def) {
253 if ($def["type"] == "text"
254 && ($def["length"] >= 1 && $def["length"] <= 4000)
255 ) {
256 $upfields[] = $field;
257 }
258 }
259 foreach ($upfields as $uf) {
260 $ilDB->query("UPDATE `" . $a_table . "` SET `" . $uf . "` = null WHERE `" . $uf . "` = ''");
261 }
262 }
263
264
272 public function replaceEmptyDatesWithNull($a_table)
273 {
274 global $DIC;
275 $ilDB = $DIC->database();
276
277 if (!$this->ilDBInterface->tableExists($a_table)) {
278 return;
279 }
280
281 $fields = $this->analyzer->getFieldInformation($a_table);
282 $upfields = array();
283 foreach ($fields as $field => $def) {
284 if ($def["type"] == "timestamp") {
285 $upfields[] = $field;
286 }
287 }
288 foreach ($upfields as $uf) {
289 $ilDB->query("UPDATE `" . $a_table . "` SET `" . $uf . "` = null WHERE `" . $uf . "` = '0000-00-00 00:00:00'");
290 }
291
292 $upfields = array();
293 reset($fields);
294 foreach ($fields as $field => $def) {
295 if ($def["type"] == "date") {
296 $upfields[] = $field;
297 }
298 }
299 foreach ($upfields as $uf) {
300 $ilDB->query("UPDATE `" . $a_table . "` SET `" . $uf . "` = null WHERE `" . $uf . "` = '0000-00-00'");
301 }
302 }
303
304
310 public function lowerCaseTableName($a_table_name)
311 {
312 global $DIC;
313 $ilDB = $DIC->database();
314
315 if ($a_table_name != strtolower($a_table_name)) {
316 // this may look strange, but it does not work directly
317 // (seems that mysql does not see no difference whether upper or lowercase characters are used
318 mysql_query("ALTER TABLE `" . $a_table_name . "` RENAME `" . strtolower($a_table_name) . "xxx" . "`");
319 mysql_query("ALTER TABLE `" . strtolower($a_table_name) . "xxx" . "` RENAME `" . strtolower($a_table_name) . "`");
320 }
321 }
322
323
329 public function lowerCaseColumnNames($a_table_name)
330 {
331 global $DIC;
332 $ilDB = $DIC->database();
333
334 $result = mysql_query("SHOW COLUMNS FROM `" . $a_table_name . "`");
335 while ($row = mysql_fetch_assoc($result)) {
336 if ($row["Field"] != strtolower($row["Field"])) {
337 $ilDB->renameTableColumn($a_table_name, $row["Field"], strtolower($row["Field"]));
338 }
339 }
340 }
341
342
349 public function removeAutoIncrement($a_table_name, $a_auto_inc_field)
350 {
351 if ($a_auto_inc_field != "") {
352 $this->ilDBInterface->modifyTableColumn($a_table_name, $a_auto_inc_field, array());
353 }
354 }
355
356
363 public function removePrimaryKey($a_table, $a_pk)
364 {
365 if ($a_pk["name"] != "") {
366 $this->ilDBInterface->dropPrimaryKey($a_table, $a_pk["name"]);
367 }
368 }
369
370
377 public function removeIndices($a_table, $a_indices)
378 {
379 if (is_array($a_indices)) {
380 foreach ($a_indices as $index) {
381 $this->ilDBInterface->query("ALTER TABLE `" . $a_table . "` DROP INDEX `" . $index["name"] . "`");
382 }
383 }
384 }
385
386
393 public function removeConstraints($a_table, $a_constraints)
394 {
395 if (is_array($a_constraints)) {
396 foreach ($a_constraints as $c) {
397 if ($c["type"] == "unique") {
398 $this->ilDBInterface->query("ALTER TABLE `" . $a_table . "` DROP INDEX `" . $c["name"] . "`");
399 }
400 }
401 }
402 }
403
404
414 public function alterTable($a_table, $a_fields, $a_set_text_ts_fields_notnull_false = true, $pk = "")
415 {
416 $n_fields = array();
417 foreach ($a_fields as $field => $d) {
418 $def = $this->reverse->getTableFieldDefinition($a_table, $field);
419 $this->ilDBInterface->handleError($def);
420 $best_alt = $this->analyzer->getBestDefinitionAlternative($def);
421 $def = $def[$best_alt];
422
423 // remove "current_timestamp" default for timestamps (not supported)
424 if (strtolower($def["nativetype"]) == "timestamp"
425 && strtolower($def["default"]) == "current_timestamp"
426 ) {
427 unset($def["default"]);
428 }
429
430 if (strtolower($def["type"]) == "float") {
431 unset($def["length"]);
432 }
433
434 // remove all invalid attributes
435 foreach ($def as $k => $v) {
436 if (!in_array(
437 $k,
438 array(
439 "type",
440 "default",
441 "notnull",
442 "length",
443 "unsigned",
444 "fixed",
445 )
446 )
447 ) {
448 unset($def[$k]);
449 }
450 }
451
452 // determine length for decimal type
453 if ($def["type"] == "decimal") {
454 $l_arr = explode(",", $def["length"]);
455 $def["length"] = $l_arr[0];
456 }
457
458 // remove length values for float
459 if ($def["type"] == "float") {
460 unset($def["length"]);
461 }
462
463 // set notnull to false for text/timestamp/date fields
464 if ($a_set_text_ts_fields_notnull_false
465 && ($def["type"] == "text"
466 || $def["type"] == "timestamp"
467 || $def["type"] == "date")
468 && (!is_array($pk) || !isset($field, $pk["fields"][$field]))
469 ) {
470 $def["notnull"] = false;
471 }
472
473 // set unsigned to false for integers
474 if ($def["type"] == "integer") {
475 $def["unsigned"] = false;
476 }
477
478 // set notnull to false for blob and clob
479 if ($def["type"] == "blob" || $def["type"] == "clob") {
480 $def["notnull"] = false;
481 }
482
483 // remove "0000-00-00..." default values
484 if (($def["type"] == "timestamp" && $def["default"] == "0000-00-00 00:00:00")
485 || ($def["type"] == "date" && $def["default"] == "0000-00-00")
486 ) {
487 unset($def["default"]);
488 }
489
490 $a = array();
491 foreach ($def as $k => $v) {
492 $a[$k] = $v;
493 }
494 $def["definition"] = $a;
495
496 $n_fields[$field] = $def;
497 }
498
499 $changes = array(
500 "change" => $n_fields,
501 );
502
503 if (!$this->getTestMode()) {
504 $r = $this->manager->alterTable($a_table, $changes, false);
505 } else {
506 $r = $this->manager->createTable(strtolower($a_table) . "_copy", $n_fields);
507 }
508
509 return true;
510 }
511
512
517 public function addPrimaryKey($a_table, $a_pk)
518 {
519 if (is_array($a_pk["fields"])) {
520 $fields = array();
521 foreach ($a_pk["fields"] as $f => $pos) {
522 $fields[] = strtolower($f);
523 }
524 $this->ilDBInterface->addPrimaryKey($a_table, $fields);
525 }
526 }
527
528
535 public function addIndices($a_table, $a_indices)
536 {
537 if (is_array($a_indices)) {
538 $all_valid = true;
539
540 foreach ($a_indices as $index) {
541 if (strlen($index["name"]) > 3) {
542 $all_valid = false;
543 }
544 }
545
546 $cnt = 1;
547 foreach ($a_indices as $index) {
548 if (is_array($index["fields"])) {
549 if (!$all_valid) {
550 $index["name"] = "i" . $cnt;
551 }
552 $fields = array();
553 foreach ($index["fields"] as $f => $pos) {
554 $fields[] = strtolower($f);
555 }
556 $this->ilDBInterface->addIndex($a_table, $fields, strtolower($index["name"]), $index["fulltext"]);
557 $cnt++;
558 }
559 }
560 }
561 }
562
563
570 public function addConstraints($a_table, $a_constraints)
571 {
572 if (is_array($a_constraints)) {
573 $all_valid = true;
574
575 foreach ($a_constraints as $c) {
576 if (strlen($c["name"]) > 3) {
577 $all_valid = false;
578 }
579 }
580
581 $cnt = 1;
582 foreach ($a_constraints as $c) {
583 if (is_array($c["fields"])) {
584 if (!$all_valid) {
585 $c["name"] = "c" . $cnt;
586 }
587 $fields = array();
588 foreach ($c["fields"] as $f => $pos) {
589 $fields[] = strtolower($f);
590 }
591 $this->ilDBInterface->addUniqueConstraint($a_table, $fields, strtolower($c["name"]));
592 $cnt++;
593 }
594 }
595 }
596 }
597
598
603 public function fixIndexNames($a_table)
604 {
605 if (!$this->ilDBInterface->tableExists($a_table)) {
606 return;
607 }
608 $all_valid = true;
609 $indices = $this->analyzer->getIndicesInformation($a_table);
610 foreach ($indices as $index) {
611 if (strlen($index["name"]) > 3) {
612 $all_valid = false;
613 }
614 }
615
616 if (!$all_valid) {
617 foreach ($indices as $index) {
618 $this->ilDBInterface->dropIndex($a_table, $index["name"]);
619 }
620 $this->addIndices($a_table, $indices);
621 }
622 }
623
624
631 public function addAutoIncrementSequence($a_table, $a_auto_inc_field)
632 {
633 if ($a_auto_inc_field != "") {
634 $set = $this->ilDBInterface->query("SELECT MAX(`" . strtolower($a_auto_inc_field) . "`) ma FROM `" . $a_table . "`");
635 $rec = $this->ilDBInterface->fetchAssoc($set);
636 $next = $rec["ma"] + 1;
637 $this->ilDBInterface->createSequence($a_table, $next);
638 }
639 }
640
641
646 public function fixClobNotNull($a_table)
647 {
648 if (!$this->ilDBInterface->tableExists($a_table)) {
649 return;
650 }
651 $all_valid = true;
652 $fields = $this->analyzer->getFieldInformation($a_table);
653 foreach ($fields as $name => $def) {
654 if ($def["type"] == "clob" && $def["notnull"] == true) {
656 $a_table,
657 $name,
658 array(
659 "type" => "clob",
660 "notnull" => false,
661 )
662 );
663 }
664 }
665 }
666
667
672 public function fixDatetimeValues($a_table)
673 {
674 if (!$this->ilDBInterface->tableExists($a_table)) {
675 return;
676 }
677 $all_valid = true;
678 $fields = $this->analyzer->getFieldInformation($a_table);
679 foreach ($fields as $name => $def) {
680 if ($def["type"] == "timestamp"
681 && ($def["notnull"] == true || $def["default"] == "0000-00-00 00:00:00")
682 ) {
683 $nd = array(
684 "type" => "timestamp",
685 "notnull" => false,
686 );
687 if ($def["default"] == "0000-00-00 00:00:00") {
688 $nd["default"] = null;
689 }
690 $this->ilDBInterface->modifyTableColumn($a_table, $name, $nd);
691 }
692 if ($def["type"] == "date"
693 && ($def["notnull"] == true || $def["default"] == "0000-00-00")
694 ) {
695 $nd = array(
696 "type" => "date",
697 "notnull" => false,
698 );
699 if ($def["default"] == "0000-00-00") {
700 $nd["default"] = null;
701 }
702 $this->ilDBInterface->modifyTableColumn($a_table, $name, $nd);
703 }
704 }
705 }
706}
$result
An exception for terminatinating execution or to throw for unit testing.
This class gives all kind of DB information using the database manager and reverse module.
Class ilDatabaseException.
This class includes methods that help to abstract ILIAS 3.10.x MySQL tables for the use with MDB2 abs...
lowerCaseColumnNames($a_table_name)
lower case column names
fixClobNotNull($a_table)
This is only used on tables that have already been abstracted but missed the "full treatment".
performAbstraction($a_table_name, $a_set_text_ts_fields_notnull_false=true)
Converts an existing (MySQL) ILIAS table in an abstract table.
fixIndexNames($a_table)
This is only used on tables that have already been abstracted but missed the "full treatment".
countRecords($a_table_name)
Check number of records before and after.
replaceEmptyDatesWithNull($a_table)
Replace empty dates with null.
removeConstraints($a_table, $a_constraints)
Remove Constraints.
removeIndices($a_table, $a_indices)
Remove Indices.
alterTable($a_table, $a_fields, $a_set_text_ts_fields_notnull_false=true, $pk="")
replaceEmptyStringsWithNull($a_table)
Replace empty strings with null values.
fixDatetimeValues($a_table)
This is only used on tables that have already been abstracted but missed the "full treatment".
lowerCaseTableName($a_table_name)
Lower case table and field names.
removePrimaryKey($a_table, $a_pk)
Remove primary key from table.
addConstraints($a_table, $a_constraints)
Add constraints.
addIndices($a_table, $a_indices)
Add indices.
storeStep($a_table, $a_step)
Store performed step.
removeAutoIncrement($a_table_name, $a_auto_inc_field)
Remove auto_increment attribute of a field.
addAutoIncrementSequence($a_table, $a_auto_inc_field)
Add autoincrement sequence.
$c
Definition: cli.php:37
for( $i=6;$i< 13;$i++) for($i=1; $i< 13; $i++) $d
Definition: date.php:296
$nd
Definition: error.php:12
global $DIC
Definition: goto.php:24
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
execute($stmt, $data=array())
fetchAssoc($query_result)
addPrimaryKey($table_name, $primary_keys)
query($query)
Run a (read-only) Query on the database.
modifyTableColumn($table, $column, $attributes)
tableExists($table_name)
createSequence($table_name, $start=1)
prepareManip($a_query, $a_types=null)
dropIndex($a_table, $a_name="i1")
addIndex($table_name, $fields, $index_name='', $fulltext=false)
if($format !==null) $name
Definition: metadata.php:230
$index
Definition: metadata.php:128
$a
thx to https://mlocati.github.io/php-cs-fixer-configurator for the examples
foreach($_POST as $key=> $value) $res
global $ilDB