ILIAS  release_5-2 Revision v5.2.25-18-g3f80b828510
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 
17  public $analyzer;
21  protected $ilDBInterface;
25  protected $manager;
29  protected $reverse;
33  protected $testmode;
34 
35 
39  public function __construct() {
40  global $ilDB;
41 
42  $this->ilDBInterface = $ilDB;
43  $this->manager = $ilDB->loadModule(ilDBConstants::MODULE_MANAGER);
44  $this->reverse = $ilDB->loadModule(ilDBConstants::MODULE_REVERSE);
45  if (@is_file('../Services/Database/classes/class.ilDBAnalyzer.php')) {
46  include_once '../Services/Database/classes/class.ilDBAnalyzer.php';
47  } else {
48  include_once './Services/Database/classes/class.ilDBAnalyzer.php';
49  }
50  $this->analyzer = new ilDBAnalyzer();
51  $this->setTestMode(false);
52  }
53 
54 
58  public function setTestMode($a_testmode) {
59  $this->testmode = $a_testmode;
60  }
61 
62 
66  public function getTestMode() {
67  return $this->testmode;
68  }
69 
70 
78  public function performAbstraction($a_table_name, $a_set_text_ts_fields_notnull_false = true) {
79  // to do: log this procedure
80 
81  // count number of records at the beginning
82  $nr_rec = $this->countRecords($a_table_name);
83 
84  // convert table name to lowercase
85  if (!$this->getTestMode()) {
86  $this->lowerCaseTableName($a_table_name);
87  $a_table_name = strtolower($a_table_name);
88  $this->storeStep($a_table_name, 10);
89  }
90 
91  // get auto increment information
92  $auto_inc_field = $this->analyzer->getAutoIncrementField($a_table_name);
93 
94  // get primary key information
95  $pk = $this->analyzer->getPrimaryKeyInformation($a_table_name);
96 
97  // get indices information
98  $indices = $this->analyzer->getIndicesInformation($a_table_name);
99 
100  // get constraints information
101  $constraints = $this->analyzer->getConstraintsInformation($a_table_name);
102 
103  // get field information
104  $fields = $this->analyzer->getFieldInformation($a_table_name);
105 
106  if (!$this->getTestMode()) {
107  // remove auto increment
108  $this->removeAutoIncrement($a_table_name, $auto_inc_field, $fields);
109  $this->storeStep($a_table_name, 20);
110 
111  // remove primary key
112  $this->removePrimaryKey($a_table_name, $pk);
113  $this->storeStep($a_table_name, 30);
114 
115  // remove indices
116  $this->removeIndices($a_table_name, $indices);
117  $this->storeStep($a_table_name, 40);
118 
119  // remove constraints
120  $this->removeConstraints($a_table_name, $constraints);
121  $this->storeStep($a_table_name, 45);
122  }
123 
124  // alter table using mdb2 field types
125  $this->alterTable($a_table_name, $fields, $a_set_text_ts_fields_notnull_false, $pk);
126  if ($this->getTestMode()) {
127  $a_table_name = strtolower($a_table_name) . "_copy";
128  } else {
129  $this->storeStep($a_table_name, 50);
130  }
131 
132  // lower case field names
133  $this->lowerCaseColumnNames($a_table_name);
134  if (!$this->getTestMode()) {
135  $this->storeStep($a_table_name, 60);
136  }
137 
138  // add primary key
139  $this->addPrimaryKey($a_table_name, $pk);
140  if (!$this->getTestMode()) {
141  $this->storeStep($a_table_name, 70);
142  }
143 
144  // add indices
145  $this->addIndices($a_table_name, $indices);
146  if (!$this->getTestMode()) {
147  $this->storeStep($a_table_name, 80);
148  }
149 
150  // add constraints
151  $this->addConstraints($a_table_name, $constraints);
152  if (!$this->getTestMode()) {
153  $this->storeStep($a_table_name, 85);
154  }
155 
156  // add "auto increment" sequence
157  if ($auto_inc_field != "") {
158  $this->addAutoIncrementSequence($a_table_name, $auto_inc_field);
159  }
160  if (!$this->getTestMode()) {
161  $this->storeStep($a_table_name, 90);
162  }
163 
164  // replace empty strings with null values in text fields
165  $this->replaceEmptyStringsWithNull($a_table_name);
166  if (!$this->getTestMode()) {
167  $this->storeStep($a_table_name, 100);
168  }
169 
170  // replace empty "0000-00-00..." dates with null
171  $this->replaceEmptyDatesWithNull($a_table_name);
172  if (!$this->getTestMode()) {
173  $this->storeStep($a_table_name, 110);
174  }
175 
176  $nr_rec2 = $this->countRecords($a_table_name);
177 
178  if (!$this->getTestMode()) {
179  if ($nr_rec != $nr_rec2) {
180  throw new ilDatabaseException("ilMySQLAbstraction: Unexpected difference in table record number, table '" . $a_table_name . "'."
181  . " Before: " . ((int)$nr_rec) . ", After: " . ((int)$nr_rec2) . ".");
182  }
183  }
184  }
185 
186 
193  public function countRecords($a_table_name) {
194  global $ilDB;
195 
196  $st = $ilDB->prepare("SELECT count(*) AS cnt FROM `" . $a_table_name . "`");
197  $res = $ilDB->execute($st);
198  $rec = $ilDB->fetchAssoc($res);
199 
200  return $rec["cnt"];
201  }
202 
203 
210  public function storeStep($a_table, $a_step) {
211  $st = $this->ilDBInterface->prepareManip("REPLACE INTO abstraction_progress (table_name, step)" . " VALUES (?,?)", array(
212  "text",
213  "integer",
214  ));
215  $this->ilDBInterface->execute($st, array( $a_table, $a_step ));
216  }
217 
218 
225  public function replaceEmptyStringsWithNull($a_table) {
226  global $ilDB;
227 
228  $fields = $this->analyzer->getFieldInformation($a_table);
229  $upfields = array();
230  foreach ($fields as $field => $def) {
231  if ($def["type"] == "text"
232  && ($def["length"] >= 1 && $def["length"] <= 4000)
233  ) {
234  $upfields[] = $field;
235  }
236  }
237  foreach ($upfields as $uf) {
238  $ilDB->query("UPDATE `" . $a_table . "` SET `" . $uf . "` = null WHERE `" . $uf . "` = ''");
239  }
240  }
241 
242 
249  public function replaceEmptyDatesWithNull($a_table) {
250  global $ilDB;
251 
252  if (!$this->ilDBInterface->tableExists($a_table)) {
253  return;
254  }
255 
256  $fields = $this->analyzer->getFieldInformation($a_table);
257  $upfields = array();
258  foreach ($fields as $field => $def) {
259  if ($def["type"] == "timestamp") {
260  $upfields[] = $field;
261  }
262  }
263  foreach ($upfields as $uf) {
264  $ilDB->query("UPDATE `" . $a_table . "` SET `" . $uf . "` = null WHERE `" . $uf . "` = '0000-00-00 00:00:00'");
265  }
266 
267  $upfields = array();
268  reset($fields);
269  foreach ($fields as $field => $def) {
270  if ($def["type"] == "date") {
271  $upfields[] = $field;
272  }
273  }
274  foreach ($upfields as $uf) {
275  $ilDB->query("UPDATE `" . $a_table . "` SET `" . $uf . "` = null WHERE `" . $uf . "` = '0000-00-00'");
276  }
277  }
278 
279 
285  public function lowerCaseTableName($a_table_name) {
286  global $ilDB;
287 
288  if ($a_table_name != strtolower($a_table_name)) {
289  // this may look strange, but it does not work directly
290  // (seems that mysql does not see no difference whether upper or lowercase characters are used
291  mysql_query("ALTER TABLE `" . $a_table_name . "` RENAME `" . strtolower($a_table_name) . "xxx" . "`");
292  mysql_query("ALTER TABLE `" . strtolower($a_table_name) . "xxx" . "` RENAME `" . strtolower($a_table_name) . "`");
293  }
294  }
295 
296 
302  public function lowerCaseColumnNames($a_table_name) {
303  global $ilDB;
304 
305  $result = mysql_query("SHOW COLUMNS FROM `" . $a_table_name . "`");
306  while ($row = mysql_fetch_assoc($result)) {
307  if ($row["Field"] != strtolower($row["Field"])) {
308  $ilDB->renameTableColumn($a_table_name, $row["Field"], strtolower($row["Field"]));
309  }
310  }
311  }
312 
313 
320  public function removeAutoIncrement($a_table_name, $a_auto_inc_field) {
321  if ($a_auto_inc_field != "") {
322  $this->ilDBInterface->modifyTableColumn($a_table_name, $a_auto_inc_field, array());
323  }
324  }
325 
326 
333  public function removePrimaryKey($a_table, $a_pk) {
334  if ($a_pk["name"] != "") {
335  $this->ilDBInterface->dropPrimaryKey($a_table, $a_pk["name"]);
336  }
337  }
338 
339 
346  public function removeIndices($a_table, $a_indices) {
347  if (is_array($a_indices)) {
348  foreach ($a_indices as $index) {
349  $this->ilDBInterface->query("ALTER TABLE `" . $a_table . "` DROP INDEX `" . $index["name"] . "`");
350  }
351  }
352  }
353 
354 
361  public function removeConstraints($a_table, $a_constraints) {
362  if (is_array($a_constraints)) {
363  foreach ($a_constraints as $c) {
364  if ($c["type"] == "unique") {
365  $this->ilDBInterface->query("ALTER TABLE `" . $a_table . "` DROP INDEX `" . $c["name"] . "`");
366  }
367  }
368  }
369  }
370 
371 
380  public function alterTable($a_table, $a_fields, $a_set_text_ts_fields_notnull_false = true, $pk = "") {
381  $n_fields = array();
382  foreach ($a_fields as $field => $d) {
383  $def = $this->reverse->getTableFieldDefinition($a_table, $field);
384  $this->ilDBInterface->handleError($def);
385  $best_alt = $this->analyzer->getBestDefinitionAlternative($def);
386  $def = $def[$best_alt];
387 
388  // remove "current_timestamp" default for timestamps (not supported)
389  if (strtolower($def["nativetype"]) == "timestamp"
390  && strtolower($def["default"]) == "current_timestamp"
391  ) {
392  unset($def["default"]);
393  }
394 
395  if (strtolower($def["type"]) == "float") {
396  unset($def["length"]);
397  }
398 
399  // remove all invalid attributes
400  foreach ($def as $k => $v) {
401  if (!in_array($k, array( "type", "default", "notnull", "length", "unsigned", "fixed" ))) {
402  unset($def[$k]);
403  }
404  }
405 
406  // determine length for decimal type
407  if ($def["type"] == "decimal") {
408  $l_arr = explode(",", $def["length"]);
409  $def["length"] = $l_arr[0];
410  }
411 
412  // remove length values for float
413  if ($def["type"] == "float") {
414  unset($def["length"]);
415  }
416 
417  // set notnull to false for text/timestamp/date fields
418  if ($a_set_text_ts_fields_notnull_false
419  && ($def["type"] == "text"
420  || $def["type"] == "timestamp"
421  || $def["type"] == "date")
422  && (!is_array($pk) || !isset($field, $pk["fields"][$field]))
423  ) {
424  $def["notnull"] = false;
425  }
426 
427  // set unsigned to false for integers
428  if ($def["type"] == "integer") {
429  $def["unsigned"] = false;
430  }
431 
432  // set notnull to false for blob and clob
433  if ($def["type"] == "blob" || $def["type"] == "clob") {
434  $def["notnull"] = false;
435  }
436 
437  // remove "0000-00-00..." default values
438  if (($def["type"] == "timestamp" && $def["default"] == "0000-00-00 00:00:00")
439  || ($def["type"] == "date" && $def["default"] == "0000-00-00")
440  ) {
441  unset($def["default"]);
442  }
443 
444  $a = array();
445  foreach ($def as $k => $v) {
446  $a[$k] = $v;
447  }
448  $def["definition"] = $a;
449 
450  $n_fields[$field] = $def;
451  }
452 
453  $changes = array(
454  "change" => $n_fields,
455  );
456 
457  if (!$this->getTestMode()) {
458  $r = $this->manager->alterTable($a_table, $changes, false);
459  } else {
460  $r = $this->manager->createTable(strtolower($a_table) . "_copy", $n_fields);
461  }
462 
463  return true;
464  }
465 
466 
471  public function addPrimaryKey($a_table, $a_pk) {
472  if (is_array($a_pk["fields"])) {
473  $fields = array();
474  foreach ($a_pk["fields"] as $f => $pos) {
475  $fields[] = strtolower($f);
476  }
477  $this->ilDBInterface->addPrimaryKey($a_table, $fields);
478  }
479  }
480 
481 
488  public function addIndices($a_table, $a_indices) {
489  if (is_array($a_indices)) {
490  $all_valid = true;
491 
492  foreach ($a_indices as $index) {
493  if (strlen($index["name"]) > 3) {
494  $all_valid = false;
495  }
496  }
497 
498  $cnt = 1;
499  foreach ($a_indices as $index) {
500  if (is_array($index["fields"])) {
501  if (!$all_valid) {
502  $index["name"] = "i" . $cnt;
503  }
504  $fields = array();
505  foreach ($index["fields"] as $f => $pos) {
506  $fields[] = strtolower($f);
507  }
508  $this->ilDBInterface->addIndex($a_table, $fields, strtolower($index["name"]), $index["fulltext"]);
509  $cnt ++;
510  }
511  }
512  }
513  }
514 
515 
522  public function addConstraints($a_table, $a_constraints) {
523  if (is_array($a_constraints)) {
524  $all_valid = true;
525 
526  foreach ($a_constraints as $c) {
527  if (strlen($c["name"]) > 3) {
528  $all_valid = false;
529  }
530  }
531 
532  $cnt = 1;
533  foreach ($a_constraints as $c) {
534  if (is_array($c["fields"])) {
535  if (!$all_valid) {
536  $c["name"] = "c" . $cnt;
537  }
538  $fields = array();
539  foreach ($c["fields"] as $f => $pos) {
540  $fields[] = strtolower($f);
541  }
542  $this->ilDBInterface->addUniqueConstraint($a_table, $fields, strtolower($c["name"]));
543  $cnt ++;
544  }
545  }
546  }
547  }
548 
549 
554  public function fixIndexNames($a_table) {
555  if (!$this->ilDBInterface->tableExists($a_table)) {
556  return;
557  }
558  $all_valid = true;
559  $indices = $this->analyzer->getIndicesInformation($a_table);
560  foreach ($indices as $index) {
561  if (strlen($index["name"]) > 3) {
562  $all_valid = false;
563  }
564  }
565 
566  if (!$all_valid) {
567  foreach ($indices as $index) {
568  $this->ilDBInterface->dropIndex($a_table, $index["name"]);
569  }
570  $this->addIndices($a_table, $indices);
571  }
572  }
573 
574 
581  public function addAutoIncrementSequence($a_table, $a_auto_inc_field) {
582  if ($a_auto_inc_field != "") {
583  $set = $this->ilDBInterface->query("SELECT MAX(`" . strtolower($a_auto_inc_field) . "`) ma FROM `" . $a_table . "`");
584  $rec = $this->ilDBInterface->fetchAssoc($set);
585  $next = $rec["ma"] + 1;
586  $this->ilDBInterface->createSequence($a_table, $next);
587  }
588  }
589 
590 
595  public function fixClobNotNull($a_table) {
596  if (!$this->ilDBInterface->tableExists($a_table)) {
597  return;
598  }
599  $all_valid = true;
600  $fields = $this->analyzer->getFieldInformation($a_table);
601  foreach ($fields as $name => $def) {
602  if ($def["type"] == "clob" && $def["notnull"] == true) {
603  $this->ilDBInterface->modifyTableColumn($a_table, $name, array( "type" => "clob", "notnull" => false ));
604  }
605  }
606  }
607 
608 
613  public function fixDatetimeValues($a_table) {
614  if (!$this->ilDBInterface->tableExists($a_table)) {
615  return;
616  }
617  $all_valid = true;
618  $fields = $this->analyzer->getFieldInformation($a_table);
619  foreach ($fields as $name => $def) {
620  if ($def["type"] == "timestamp"
621  && ($def["notnull"] == true || $def["default"] == "0000-00-00 00:00:00")
622  ) {
623  $nd = array( "type" => "timestamp", "notnull" => false );
624  if ($def["default"] == "0000-00-00 00:00:00") {
625  $nd["default"] = null;
626  }
627  $this->ilDBInterface->modifyTableColumn($a_table, $name, $nd);
628  }
629  if ($def["type"] == "date"
630  && ($def["notnull"] == true || $def["default"] == "0000-00-00")
631  ) {
632  $nd = array( "type" => "date", "notnull" => false );
633  if ($def["default"] == "0000-00-00") {
634  $nd["default"] = null;
635  }
636  $this->ilDBInterface->modifyTableColumn($a_table, $name, $nd);
637  }
638  }
639  }
640 }
prepareManip($a_query, $a_types=null)
modifyTableColumn($table, $column, $attributes)
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
execute($stmt, $data=array())
addPrimaryKey($table_name, $primary_keys)
fixClobNotNull($a_table)
This is only used on tables that have already been abstracted but missed the "full treatment"...
createSequence($table_name, $start=1)
Class ilDatabaseException.
removeAutoIncrement($a_table_name, $a_auto_inc_field)
Remove auto_increment attribute of a field.
for($col=0; $col< 50; $col++) $d
Interface ilDBInterface.
addIndex($table_name, $fields, $index_name='', $fulltext=false)
countRecords($a_table_name)
Check number of records before and after.
$r
Definition: example_031.php:79
$nd
Definition: error.php:11
tableExists($table_name)
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="")
Create styles array
The data for the language used.
lowerCaseTableName($a_table_name)
Lower case table and field names.
dropIndex($a_table, $a_name="i1")
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
query($query)
fetchAssoc($query_result)
removeIndices($a_table, $a_indices)
Remove Indices.
This class gives all kind of DB information using the MDB2 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.