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) {
660  $this->ilDBInterface->modifyTableColumn(
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 }
lowerCaseColumnNames($a_table_name)
lower case column names
replaceEmptyStringsWithNull($a_table)
Replace empty strings with null values.
This class includes methods that help to abstract ILIAS 3.10.x MySQL tables for the use with MDB2 abs...
$result
global $DIC
Definition: saml.php:7
fixClobNotNull($a_table)
This is only used on tables that have already been abstracted but missed the "full treatment"...
$index
Definition: metadata.php:60
Class ilDatabaseException.
removeAutoIncrement($a_table_name, $a_auto_inc_field)
Remove auto_increment attribute of a field.
countRecords($a_table_name)
Check number of records before and after.
$r
Definition: example_031.php:79
$nd
Definition: error.php:10
foreach($_POST as $key=> $value) $res
addAutoIncrementSequence($a_table, $a_auto_inc_field)
Add autoincrement sequence.
fixIndexNames($a_table)
This is only used on tables that have already been abstracted but missed the "full treatment"...
replaceEmptyDatesWithNull($a_table)
Replace empty dates with null.
storeStep($a_table, $a_step)
Store performed step.
alterTable($a_table, $a_fields, $a_set_text_ts_fields_notnull_false=true, $pk="")
lowerCaseTableName($a_table_name)
Lower case table and field names.
$row
performAbstraction($a_table_name, $a_set_text_ts_fields_notnull_false=true)
Converts an existing (MySQL) ILIAS table in an abstract table.
fixDatetimeValues($a_table)
This is only used on tables that have already been abstracted but missed the "full treatment"...
addIndices($a_table, $a_indices)
Add indices.
global $ilDB
$def
Definition: croninfo.php:21
removeIndices($a_table, $a_indices)
Remove Indices.
This class gives all kind of DB information using the database manager and reverse module...
removeConstraints($a_table, $a_constraints)
Remove Constraints.
removePrimaryKey($a_table, $a_pk)
Remove primary key from table.
addConstraints($a_table, $a_constraints)
Add constraints.
for($i=6; $i< 13; $i++) for($i=1; $i< 13; $i++) $d
Definition: date.php:296