ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
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 $ilDB;
43 
44  $this->ilDBInterface = $ilDB;
45  $this->manager = $ilDB->loadModule(ilDBConstants::MODULE_MANAGER);
46  $this->reverse = $ilDB->loadModule(ilDBConstants::MODULE_REVERSE);
47  if (@is_file('../Services/Database/classes/class.ilDBAnalyzer.php')) {
48  include_once '../Services/Database/classes/class.ilDBAnalyzer.php';
49  } else {
50  include_once './Services/Database/classes/class.ilDBAnalyzer.php';
51  }
52  $this->analyzer = new ilDBAnalyzer();
53  $this->setTestMode(false);
54  }
55 
56 
60  public function setTestMode($a_testmode)
61  {
62  $this->testmode = $a_testmode;
63  }
64 
65 
69  public function getTestMode()
70  {
71  return $this->testmode;
72  }
73 
74 
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("ilMySQLAbstraction: Unexpected difference in table record number, table '" . $a_table_name . "'."
186  . " Before: " . ((int) $nr_rec) . ", After: " . ((int) $nr_rec2) . ".");
187  }
188  }
189  }
190 
191 
198  public function countRecords($a_table_name)
199  {
200  global $ilDB;
201 
202  $st = $ilDB->prepare("SELECT count(*) AS cnt FROM `" . $a_table_name . "`");
203  $res = $ilDB->execute($st);
204  $rec = $ilDB->fetchAssoc($res);
205 
206  return $rec["cnt"];
207  }
208 
209 
216  public function storeStep($a_table, $a_step)
217  {
218  $st = $this->ilDBInterface->prepareManip("REPLACE INTO abstraction_progress (table_name, step)" . " VALUES (?,?)", array(
219  "text",
220  "integer",
221  ));
222  $this->ilDBInterface->execute($st, array( $a_table, $a_step ));
223  }
224 
225 
232  public function replaceEmptyStringsWithNull($a_table)
233  {
234  global $ilDB;
235 
236  $fields = $this->analyzer->getFieldInformation($a_table);
237  $upfields = array();
238  foreach ($fields as $field => $def) {
239  if ($def["type"] == "text"
240  && ($def["length"] >= 1 && $def["length"] <= 4000)
241  ) {
242  $upfields[] = $field;
243  }
244  }
245  foreach ($upfields as $uf) {
246  $ilDB->query("UPDATE `" . $a_table . "` SET `" . $uf . "` = null WHERE `" . $uf . "` = ''");
247  }
248  }
249 
250 
257  public function replaceEmptyDatesWithNull($a_table)
258  {
259  global $ilDB;
260 
261  if (!$this->ilDBInterface->tableExists($a_table)) {
262  return;
263  }
264 
265  $fields = $this->analyzer->getFieldInformation($a_table);
266  $upfields = array();
267  foreach ($fields as $field => $def) {
268  if ($def["type"] == "timestamp") {
269  $upfields[] = $field;
270  }
271  }
272  foreach ($upfields as $uf) {
273  $ilDB->query("UPDATE `" . $a_table . "` SET `" . $uf . "` = null WHERE `" . $uf . "` = '0000-00-00 00:00:00'");
274  }
275 
276  $upfields = array();
277  reset($fields);
278  foreach ($fields as $field => $def) {
279  if ($def["type"] == "date") {
280  $upfields[] = $field;
281  }
282  }
283  foreach ($upfields as $uf) {
284  $ilDB->query("UPDATE `" . $a_table . "` SET `" . $uf . "` = null WHERE `" . $uf . "` = '0000-00-00'");
285  }
286  }
287 
288 
294  public function lowerCaseTableName($a_table_name)
295  {
296  global $ilDB;
297 
298  if ($a_table_name != strtolower($a_table_name)) {
299  // this may look strange, but it does not work directly
300  // (seems that mysql does not see no difference whether upper or lowercase characters are used
301  mysql_query("ALTER TABLE `" . $a_table_name . "` RENAME `" . strtolower($a_table_name) . "xxx" . "`");
302  mysql_query("ALTER TABLE `" . strtolower($a_table_name) . "xxx" . "` RENAME `" . strtolower($a_table_name) . "`");
303  }
304  }
305 
306 
312  public function lowerCaseColumnNames($a_table_name)
313  {
314  global $ilDB;
315 
316  $result = mysql_query("SHOW COLUMNS FROM `" . $a_table_name . "`");
317  while ($row = mysql_fetch_assoc($result)) {
318  if ($row["Field"] != strtolower($row["Field"])) {
319  $ilDB->renameTableColumn($a_table_name, $row["Field"], strtolower($row["Field"]));
320  }
321  }
322  }
323 
324 
331  public function removeAutoIncrement($a_table_name, $a_auto_inc_field)
332  {
333  if ($a_auto_inc_field != "") {
334  $this->ilDBInterface->modifyTableColumn($a_table_name, $a_auto_inc_field, array());
335  }
336  }
337 
338 
345  public function removePrimaryKey($a_table, $a_pk)
346  {
347  if ($a_pk["name"] != "") {
348  $this->ilDBInterface->dropPrimaryKey($a_table, $a_pk["name"]);
349  }
350  }
351 
352 
359  public function removeIndices($a_table, $a_indices)
360  {
361  if (is_array($a_indices)) {
362  foreach ($a_indices as $index) {
363  $this->ilDBInterface->query("ALTER TABLE `" . $a_table . "` DROP INDEX `" . $index["name"] . "`");
364  }
365  }
366  }
367 
368 
375  public function removeConstraints($a_table, $a_constraints)
376  {
377  if (is_array($a_constraints)) {
378  foreach ($a_constraints as $c) {
379  if ($c["type"] == "unique") {
380  $this->ilDBInterface->query("ALTER TABLE `" . $a_table . "` DROP INDEX `" . $c["name"] . "`");
381  }
382  }
383  }
384  }
385 
386 
395  public function alterTable($a_table, $a_fields, $a_set_text_ts_fields_notnull_false = true, $pk = "")
396  {
397  $n_fields = array();
398  foreach ($a_fields as $field => $d) {
399  $def = $this->reverse->getTableFieldDefinition($a_table, $field);
400  $this->ilDBInterface->handleError($def);
401  $best_alt = $this->analyzer->getBestDefinitionAlternative($def);
402  $def = $def[$best_alt];
403 
404  // remove "current_timestamp" default for timestamps (not supported)
405  if (strtolower($def["nativetype"]) == "timestamp"
406  && strtolower($def["default"]) == "current_timestamp"
407  ) {
408  unset($def["default"]);
409  }
410 
411  if (strtolower($def["type"]) == "float") {
412  unset($def["length"]);
413  }
414 
415  // remove all invalid attributes
416  foreach ($def as $k => $v) {
417  if (!in_array($k, array( "type", "default", "notnull", "length", "unsigned", "fixed" ))) {
418  unset($def[$k]);
419  }
420  }
421 
422  // determine length for decimal type
423  if ($def["type"] == "decimal") {
424  $l_arr = explode(",", $def["length"]);
425  $def["length"] = $l_arr[0];
426  }
427 
428  // remove length values for float
429  if ($def["type"] == "float") {
430  unset($def["length"]);
431  }
432 
433  // set notnull to false for text/timestamp/date fields
434  if ($a_set_text_ts_fields_notnull_false
435  && ($def["type"] == "text"
436  || $def["type"] == "timestamp"
437  || $def["type"] == "date")
438  && (!is_array($pk) || !isset($field, $pk["fields"][$field]))
439  ) {
440  $def["notnull"] = false;
441  }
442 
443  // set unsigned to false for integers
444  if ($def["type"] == "integer") {
445  $def["unsigned"] = false;
446  }
447 
448  // set notnull to false for blob and clob
449  if ($def["type"] == "blob" || $def["type"] == "clob") {
450  $def["notnull"] = false;
451  }
452 
453  // remove "0000-00-00..." default values
454  if (($def["type"] == "timestamp" && $def["default"] == "0000-00-00 00:00:00")
455  || ($def["type"] == "date" && $def["default"] == "0000-00-00")
456  ) {
457  unset($def["default"]);
458  }
459 
460  $a = array();
461  foreach ($def as $k => $v) {
462  $a[$k] = $v;
463  }
464  $def["definition"] = $a;
465 
466  $n_fields[$field] = $def;
467  }
468 
469  $changes = array(
470  "change" => $n_fields,
471  );
472 
473  if (!$this->getTestMode()) {
474  $r = $this->manager->alterTable($a_table, $changes, false);
475  } else {
476  $r = $this->manager->createTable(strtolower($a_table) . "_copy", $n_fields);
477  }
478 
479  return true;
480  }
481 
482 
487  public function addPrimaryKey($a_table, $a_pk)
488  {
489  if (is_array($a_pk["fields"])) {
490  $fields = array();
491  foreach ($a_pk["fields"] as $f => $pos) {
492  $fields[] = strtolower($f);
493  }
494  $this->ilDBInterface->addPrimaryKey($a_table, $fields);
495  }
496  }
497 
498 
505  public function addIndices($a_table, $a_indices)
506  {
507  if (is_array($a_indices)) {
508  $all_valid = true;
509 
510  foreach ($a_indices as $index) {
511  if (strlen($index["name"]) > 3) {
512  $all_valid = false;
513  }
514  }
515 
516  $cnt = 1;
517  foreach ($a_indices as $index) {
518  if (is_array($index["fields"])) {
519  if (!$all_valid) {
520  $index["name"] = "i" . $cnt;
521  }
522  $fields = array();
523  foreach ($index["fields"] as $f => $pos) {
524  $fields[] = strtolower($f);
525  }
526  $this->ilDBInterface->addIndex($a_table, $fields, strtolower($index["name"]), $index["fulltext"]);
527  $cnt++;
528  }
529  }
530  }
531  }
532 
533 
540  public function addConstraints($a_table, $a_constraints)
541  {
542  if (is_array($a_constraints)) {
543  $all_valid = true;
544 
545  foreach ($a_constraints as $c) {
546  if (strlen($c["name"]) > 3) {
547  $all_valid = false;
548  }
549  }
550 
551  $cnt = 1;
552  foreach ($a_constraints as $c) {
553  if (is_array($c["fields"])) {
554  if (!$all_valid) {
555  $c["name"] = "c" . $cnt;
556  }
557  $fields = array();
558  foreach ($c["fields"] as $f => $pos) {
559  $fields[] = strtolower($f);
560  }
561  $this->ilDBInterface->addUniqueConstraint($a_table, $fields, strtolower($c["name"]));
562  $cnt++;
563  }
564  }
565  }
566  }
567 
568 
573  public function fixIndexNames($a_table)
574  {
575  if (!$this->ilDBInterface->tableExists($a_table)) {
576  return;
577  }
578  $all_valid = true;
579  $indices = $this->analyzer->getIndicesInformation($a_table);
580  foreach ($indices as $index) {
581  if (strlen($index["name"]) > 3) {
582  $all_valid = false;
583  }
584  }
585 
586  if (!$all_valid) {
587  foreach ($indices as $index) {
588  $this->ilDBInterface->dropIndex($a_table, $index["name"]);
589  }
590  $this->addIndices($a_table, $indices);
591  }
592  }
593 
594 
601  public function addAutoIncrementSequence($a_table, $a_auto_inc_field)
602  {
603  if ($a_auto_inc_field != "") {
604  $set = $this->ilDBInterface->query("SELECT MAX(`" . strtolower($a_auto_inc_field) . "`) ma FROM `" . $a_table . "`");
605  $rec = $this->ilDBInterface->fetchAssoc($set);
606  $next = $rec["ma"] + 1;
607  $this->ilDBInterface->createSequence($a_table, $next);
608  }
609  }
610 
611 
616  public function fixClobNotNull($a_table)
617  {
618  if (!$this->ilDBInterface->tableExists($a_table)) {
619  return;
620  }
621  $all_valid = true;
622  $fields = $this->analyzer->getFieldInformation($a_table);
623  foreach ($fields as $name => $def) {
624  if ($def["type"] == "clob" && $def["notnull"] == true) {
625  $this->ilDBInterface->modifyTableColumn($a_table, $name, array( "type" => "clob", "notnull" => false ));
626  }
627  }
628  }
629 
630 
635  public function fixDatetimeValues($a_table)
636  {
637  if (!$this->ilDBInterface->tableExists($a_table)) {
638  return;
639  }
640  $all_valid = true;
641  $fields = $this->analyzer->getFieldInformation($a_table);
642  foreach ($fields as $name => $def) {
643  if ($def["type"] == "timestamp"
644  && ($def["notnull"] == true || $def["default"] == "0000-00-00 00:00:00")
645  ) {
646  $nd = array( "type" => "timestamp", "notnull" => false );
647  if ($def["default"] == "0000-00-00 00:00:00") {
648  $nd["default"] = null;
649  }
650  $this->ilDBInterface->modifyTableColumn($a_table, $name, $nd);
651  }
652  if ($def["type"] == "date"
653  && ($def["notnull"] == true || $def["default"] == "0000-00-00")
654  ) {
655  $nd = array( "type" => "date", "notnull" => false );
656  if ($def["default"] == "0000-00-00") {
657  $nd["default"] = null;
658  }
659  $this->ilDBInterface->modifyTableColumn($a_table, $name, $nd);
660  }
661  }
662  }
663 }
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)
$index
Definition: metadata.php:60
Class ilDatabaseException.
removeAutoIncrement($a_table_name, $a_auto_inc_field)
Remove auto_increment attribute of a field.
Interface ilDBInterface.
addIndex($table_name, $fields, $index_name='', $fulltext=false)
countRecords($a_table_name)
Check number of records before and after.
if($format !==null) $name
Definition: metadata.php:146
$r
Definition: example_031.php:79
$nd
Definition: error.php:10
foreach($_POST as $key=> $value) $res
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)
Run a (read-only) Query on the database.
fetchAssoc($query_result)
$def
Definition: croninfo.php:21
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.
for($i=6; $i< 13; $i++) for($i=1; $i< 13; $i++) $d
Definition: date.php:296