ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
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 if (@is_file('../Services/Database/classes/class.ilDBAnalyzer.php')) {
49 include_once '../Services/Database/classes/class.ilDBAnalyzer.php';
50 } else {
51 include_once './Services/Database/classes/class.ilDBAnalyzer.php';
52 }
53 $this->analyzer = new ilDBAnalyzer();
54 $this->setTestMode(false);
55 }
56
57
61 public function setTestMode($a_testmode)
62 {
63 $this->testmode = $a_testmode;
64 }
65
66
70 public function getTestMode()
71 {
72 return $this->testmode;
73 }
74
75
87 public function performAbstraction($a_table_name, $a_set_text_ts_fields_notnull_false = true)
88 {
89 // to do: log this procedure
90
91 // count number of records at the beginning
92 $nr_rec = $this->countRecords($a_table_name);
93
94 // convert table name to lowercase
95 if (!$this->getTestMode()) {
96 $this->lowerCaseTableName($a_table_name);
97 $a_table_name = strtolower($a_table_name);
98 $this->storeStep($a_table_name, 10);
99 }
100
101 // get auto increment information
102 $auto_inc_field = $this->analyzer->getAutoIncrementField($a_table_name);
103
104 // get primary key information
105 $pk = $this->analyzer->getPrimaryKeyInformation($a_table_name);
106
107 // get indices information
108 $indices = $this->analyzer->getIndicesInformation($a_table_name);
109
110 // get constraints information
111 $constraints = $this->analyzer->getConstraintsInformation($a_table_name);
112
113 // get field information
114 $fields = $this->analyzer->getFieldInformation($a_table_name);
115
116 if (!$this->getTestMode()) {
117 // remove auto increment
118 $this->removeAutoIncrement($a_table_name, $auto_inc_field, $fields);
119 $this->storeStep($a_table_name, 20);
120
121 // remove primary key
122 $this->removePrimaryKey($a_table_name, $pk);
123 $this->storeStep($a_table_name, 30);
124
125 // remove indices
126 $this->removeIndices($a_table_name, $indices);
127 $this->storeStep($a_table_name, 40);
128
129 // remove constraints
130 $this->removeConstraints($a_table_name, $constraints);
131 $this->storeStep($a_table_name, 45);
132 }
133
134 // alter table using mdb2 field types
135 $this->alterTable($a_table_name, $fields, $a_set_text_ts_fields_notnull_false, $pk);
136 if ($this->getTestMode()) {
137 $a_table_name = strtolower($a_table_name) . "_copy";
138 } else {
139 $this->storeStep($a_table_name, 50);
140 }
141
142 // lower case field names
143 $this->lowerCaseColumnNames($a_table_name);
144 if (!$this->getTestMode()) {
145 $this->storeStep($a_table_name, 60);
146 }
147
148 // add primary key
149 $this->addPrimaryKey($a_table_name, $pk);
150 if (!$this->getTestMode()) {
151 $this->storeStep($a_table_name, 70);
152 }
153
154 // add indices
155 $this->addIndices($a_table_name, $indices);
156 if (!$this->getTestMode()) {
157 $this->storeStep($a_table_name, 80);
158 }
159
160 // add constraints
161 $this->addConstraints($a_table_name, $constraints);
162 if (!$this->getTestMode()) {
163 $this->storeStep($a_table_name, 85);
164 }
165
166 // add "auto increment" sequence
167 if ($auto_inc_field != "") {
168 $this->addAutoIncrementSequence($a_table_name, $auto_inc_field);
169 }
170 if (!$this->getTestMode()) {
171 $this->storeStep($a_table_name, 90);
172 }
173
174 // replace empty strings with null values in text fields
175 $this->replaceEmptyStringsWithNull($a_table_name);
176 if (!$this->getTestMode()) {
177 $this->storeStep($a_table_name, 100);
178 }
179
180 // replace empty "0000-00-00..." dates with null
181 $this->replaceEmptyDatesWithNull($a_table_name);
182 if (!$this->getTestMode()) {
183 $this->storeStep($a_table_name, 110);
184 }
185
186 $nr_rec2 = $this->countRecords($a_table_name);
187
188 if (!$this->getTestMode()) {
189 if ($nr_rec != $nr_rec2) {
190 throw new ilDatabaseException(
191 "ilMySQLAbstraction: Unexpected difference in table record number, table '" . $a_table_name . "'." . " Before: " . ((int) $nr_rec) . ", After: " . ((int) $nr_rec2) . "."
192 );
193 }
194 }
195 }
196
197
205 public function countRecords($a_table_name)
206 {
207 global $DIC;
208 $ilDB = $DIC->database();
209
210 $st = $ilDB->prepare("SELECT count(*) AS cnt FROM `" . $a_table_name . "`");
211 $res = $ilDB->execute($st);
212 $rec = $ilDB->fetchAssoc($res);
213
214 return $rec["cnt"];
215 }
216
217
224 public function storeStep($a_table, $a_step)
225 {
226 $st = $this->ilDBInterface->prepareManip(
227 "REPLACE INTO abstraction_progress (table_name, step)" . " VALUES (?,?)",
228 array(
229 "text",
230 "integer",
231 )
232 );
233 $this->ilDBInterface->execute(
234 $st,
235 array(
236 $a_table,
237 $a_step,
238 )
239 );
240 }
241
242
250 public function replaceEmptyStringsWithNull($a_table)
251 {
252 global $DIC;
253 $ilDB = $DIC->database();
254
255 $fields = $this->analyzer->getFieldInformation($a_table);
256 $upfields = array();
257 foreach ($fields as $field => $def) {
258 if ($def["type"] == "text"
259 && ($def["length"] >= 1 && $def["length"] <= 4000)
260 ) {
261 $upfields[] = $field;
262 }
263 }
264 foreach ($upfields as $uf) {
265 $ilDB->query("UPDATE `" . $a_table . "` SET `" . $uf . "` = null WHERE `" . $uf . "` = ''");
266 }
267 }
268
269
277 public function replaceEmptyDatesWithNull($a_table)
278 {
279 global $DIC;
280 $ilDB = $DIC->database();
281
282 if (!$this->ilDBInterface->tableExists($a_table)) {
283 return;
284 }
285
286 $fields = $this->analyzer->getFieldInformation($a_table);
287 $upfields = array();
288 foreach ($fields as $field => $def) {
289 if ($def["type"] == "timestamp") {
290 $upfields[] = $field;
291 }
292 }
293 foreach ($upfields as $uf) {
294 $ilDB->query("UPDATE `" . $a_table . "` SET `" . $uf . "` = null WHERE `" . $uf . "` = '0000-00-00 00:00:00'");
295 }
296
297 $upfields = array();
298 reset($fields);
299 foreach ($fields as $field => $def) {
300 if ($def["type"] == "date") {
301 $upfields[] = $field;
302 }
303 }
304 foreach ($upfields as $uf) {
305 $ilDB->query("UPDATE `" . $a_table . "` SET `" . $uf . "` = null WHERE `" . $uf . "` = '0000-00-00'");
306 }
307 }
308
309
315 public function lowerCaseTableName($a_table_name)
316 {
317 global $DIC;
318 $ilDB = $DIC->database();
319
320 if ($a_table_name != strtolower($a_table_name)) {
321 // this may look strange, but it does not work directly
322 // (seems that mysql does not see no difference whether upper or lowercase characters are used
323 mysql_query("ALTER TABLE `" . $a_table_name . "` RENAME `" . strtolower($a_table_name) . "xxx" . "`");
324 mysql_query("ALTER TABLE `" . strtolower($a_table_name) . "xxx" . "` RENAME `" . strtolower($a_table_name) . "`");
325 }
326 }
327
328
334 public function lowerCaseColumnNames($a_table_name)
335 {
336 global $DIC;
337 $ilDB = $DIC->database();
338
339 $result = mysql_query("SHOW COLUMNS FROM `" . $a_table_name . "`");
340 while ($row = mysql_fetch_assoc($result)) {
341 if ($row["Field"] != strtolower($row["Field"])) {
342 $ilDB->renameTableColumn($a_table_name, $row["Field"], strtolower($row["Field"]));
343 }
344 }
345 }
346
347
354 public function removeAutoIncrement($a_table_name, $a_auto_inc_field)
355 {
356 if ($a_auto_inc_field != "") {
357 $this->ilDBInterface->modifyTableColumn($a_table_name, $a_auto_inc_field, array());
358 }
359 }
360
361
368 public function removePrimaryKey($a_table, $a_pk)
369 {
370 if ($a_pk["name"] != "") {
371 $this->ilDBInterface->dropPrimaryKey($a_table, $a_pk["name"]);
372 }
373 }
374
375
382 public function removeIndices($a_table, $a_indices)
383 {
384 if (is_array($a_indices)) {
385 foreach ($a_indices as $index) {
386 $this->ilDBInterface->query("ALTER TABLE `" . $a_table . "` DROP INDEX `" . $index["name"] . "`");
387 }
388 }
389 }
390
391
398 public function removeConstraints($a_table, $a_constraints)
399 {
400 if (is_array($a_constraints)) {
401 foreach ($a_constraints as $c) {
402 if ($c["type"] == "unique") {
403 $this->ilDBInterface->query("ALTER TABLE `" . $a_table . "` DROP INDEX `" . $c["name"] . "`");
404 }
405 }
406 }
407 }
408
409
419 public function alterTable($a_table, $a_fields, $a_set_text_ts_fields_notnull_false = true, $pk = "")
420 {
421 $n_fields = array();
422 foreach ($a_fields as $field => $d) {
423 $def = $this->reverse->getTableFieldDefinition($a_table, $field);
424 $this->ilDBInterface->handleError($def);
425 $best_alt = $this->analyzer->getBestDefinitionAlternative($def);
426 $def = $def[$best_alt];
427
428 // remove "current_timestamp" default for timestamps (not supported)
429 if (strtolower($def["nativetype"]) == "timestamp"
430 && strtolower($def["default"]) == "current_timestamp"
431 ) {
432 unset($def["default"]);
433 }
434
435 if (strtolower($def["type"]) == "float") {
436 unset($def["length"]);
437 }
438
439 // remove all invalid attributes
440 foreach ($def as $k => $v) {
441 if (!in_array(
442 $k,
443 array(
444 "type",
445 "default",
446 "notnull",
447 "length",
448 "unsigned",
449 "fixed",
450 )
451 )
452 ) {
453 unset($def[$k]);
454 }
455 }
456
457 // determine length for decimal type
458 if ($def["type"] == "decimal") {
459 $l_arr = explode(",", $def["length"]);
460 $def["length"] = $l_arr[0];
461 }
462
463 // remove length values for float
464 if ($def["type"] == "float") {
465 unset($def["length"]);
466 }
467
468 // set notnull to false for text/timestamp/date fields
469 if ($a_set_text_ts_fields_notnull_false
470 && ($def["type"] == "text"
471 || $def["type"] == "timestamp"
472 || $def["type"] == "date")
473 && (!is_array($pk) || !isset($field, $pk["fields"][$field]))
474 ) {
475 $def["notnull"] = false;
476 }
477
478 // set unsigned to false for integers
479 if ($def["type"] == "integer") {
480 $def["unsigned"] = false;
481 }
482
483 // set notnull to false for blob and clob
484 if ($def["type"] == "blob" || $def["type"] == "clob") {
485 $def["notnull"] = false;
486 }
487
488 // remove "0000-00-00..." default values
489 if (($def["type"] == "timestamp" && $def["default"] == "0000-00-00 00:00:00")
490 || ($def["type"] == "date" && $def["default"] == "0000-00-00")
491 ) {
492 unset($def["default"]);
493 }
494
495 $a = array();
496 foreach ($def as $k => $v) {
497 $a[$k] = $v;
498 }
499 $def["definition"] = $a;
500
501 $n_fields[$field] = $def;
502 }
503
504 $changes = array(
505 "change" => $n_fields,
506 );
507
508 if (!$this->getTestMode()) {
509 $r = $this->manager->alterTable($a_table, $changes, false);
510 } else {
511 $r = $this->manager->createTable(strtolower($a_table) . "_copy", $n_fields);
512 }
513
514 return true;
515 }
516
517
522 public function addPrimaryKey($a_table, $a_pk)
523 {
524 if (is_array($a_pk["fields"])) {
525 $fields = array();
526 foreach ($a_pk["fields"] as $f => $pos) {
527 $fields[] = strtolower($f);
528 }
529 $this->ilDBInterface->addPrimaryKey($a_table, $fields);
530 }
531 }
532
533
540 public function addIndices($a_table, $a_indices)
541 {
542 if (is_array($a_indices)) {
543 $all_valid = true;
544
545 foreach ($a_indices as $index) {
546 if (strlen($index["name"]) > 3) {
547 $all_valid = false;
548 }
549 }
550
551 $cnt = 1;
552 foreach ($a_indices as $index) {
553 if (is_array($index["fields"])) {
554 if (!$all_valid) {
555 $index["name"] = "i" . $cnt;
556 }
557 $fields = array();
558 foreach ($index["fields"] as $f => $pos) {
559 $fields[] = strtolower($f);
560 }
561 $this->ilDBInterface->addIndex($a_table, $fields, strtolower($index["name"]), $index["fulltext"]);
562 $cnt++;
563 }
564 }
565 }
566 }
567
568
575 public function addConstraints($a_table, $a_constraints)
576 {
577 if (is_array($a_constraints)) {
578 $all_valid = true;
579
580 foreach ($a_constraints as $c) {
581 if (strlen($c["name"]) > 3) {
582 $all_valid = false;
583 }
584 }
585
586 $cnt = 1;
587 foreach ($a_constraints as $c) {
588 if (is_array($c["fields"])) {
589 if (!$all_valid) {
590 $c["name"] = "c" . $cnt;
591 }
592 $fields = array();
593 foreach ($c["fields"] as $f => $pos) {
594 $fields[] = strtolower($f);
595 }
596 $this->ilDBInterface->addUniqueConstraint($a_table, $fields, strtolower($c["name"]));
597 $cnt++;
598 }
599 }
600 }
601 }
602
603
608 public function fixIndexNames($a_table)
609 {
610 if (!$this->ilDBInterface->tableExists($a_table)) {
611 return;
612 }
613 $all_valid = true;
614 $indices = $this->analyzer->getIndicesInformation($a_table);
615 foreach ($indices as $index) {
616 if (strlen($index["name"]) > 3) {
617 $all_valid = false;
618 }
619 }
620
621 if (!$all_valid) {
622 foreach ($indices as $index) {
623 $this->ilDBInterface->dropIndex($a_table, $index["name"]);
624 }
625 $this->addIndices($a_table, $indices);
626 }
627 }
628
629
636 public function addAutoIncrementSequence($a_table, $a_auto_inc_field)
637 {
638 if ($a_auto_inc_field != "") {
639 $set = $this->ilDBInterface->query("SELECT MAX(`" . strtolower($a_auto_inc_field) . "`) ma FROM `" . $a_table . "`");
640 $rec = $this->ilDBInterface->fetchAssoc($set);
641 $next = $rec["ma"] + 1;
642 $this->ilDBInterface->createSequence($a_table, $next);
643 }
644 }
645
646
651 public function fixClobNotNull($a_table)
652 {
653 if (!$this->ilDBInterface->tableExists($a_table)) {
654 return;
655 }
656 $all_valid = true;
657 $fields = $this->analyzer->getFieldInformation($a_table);
658 foreach ($fields as $name => $def) {
659 if ($def["type"] == "clob" && $def["notnull"] == true) {
661 $a_table,
662 $name,
663 array(
664 "type" => "clob",
665 "notnull" => false,
666 )
667 );
668 }
669 }
670 }
671
672
677 public function fixDatetimeValues($a_table)
678 {
679 if (!$this->ilDBInterface->tableExists($a_table)) {
680 return;
681 }
682 $all_valid = true;
683 $fields = $this->analyzer->getFieldInformation($a_table);
684 foreach ($fields as $name => $def) {
685 if ($def["type"] == "timestamp"
686 && ($def["notnull"] == true || $def["default"] == "0000-00-00 00:00:00")
687 ) {
688 $nd = array(
689 "type" => "timestamp",
690 "notnull" => false,
691 );
692 if ($def["default"] == "0000-00-00 00:00:00") {
693 $nd["default"] = null;
694 }
695 $this->ilDBInterface->modifyTableColumn($a_table, $name, $nd);
696 }
697 if ($def["type"] == "date"
698 && ($def["notnull"] == true || $def["default"] == "0000-00-00")
699 ) {
700 $nd = array(
701 "type" => "date",
702 "notnull" => false,
703 );
704 if ($def["default"] == "0000-00-00") {
705 $nd["default"] = null;
706 }
707 $this->ilDBInterface->modifyTableColumn($a_table, $name, $nd);
708 }
709 }
710 }
711}
$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.
$def
Definition: croninfo.php:21
for( $i=6;$i< 13;$i++) for($i=1; $i< 13; $i++) $d
Definition: date.php:296
$nd
Definition: error.php:10
$r
Definition: example_031.php:79
Interface ilDBInterface.
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)
$index
Definition: metadata.php:60
$row
global $DIC
Definition: saml.php:7
foreach($_POST as $key=> $value) $res
global $ilDB