ILIAS  release_8 Revision v8.25
class.ilDBGenerator.php
Go to the documentation of this file.
1<?php
2
3declare(strict_types=1);
4
28class ilDBGenerator
29{
30 protected string $target_encoding = 'UTF-8';
31 protected array $whitelist = array();
32 protected array $blacklist = array();
33 protected array $tables = array();
34 protected array $filter = array();
35 protected ilDBManager $manager;
36 protected ilDBReverse $reverse;
37 protected ilDBInterface $il_db;
38 protected ilDBAnalyzer $analyzer;
39 protected array $allowed_attributes = [];
40 protected array $fields = [];
41
45 public function __construct()
46 {
47 global $DIC;
48 $ilDB = $DIC->database();
49
50 $this->manager = $ilDB->loadModule(ilDBConstants::MODULE_MANAGER);
51 $this->reverse = $ilDB->loadModule(ilDBConstants::MODULE_REVERSE);
52 $this->il_db = $ilDB;
53 $this->analyzer = new ilDBAnalyzer();
54 $this->allowed_attributes = $ilDB->getAllowedAttributes();
55 }
56
61 public static function lookupAbstractedTables(): array
62 {
63 global $DIC;
64 $ilDB = $DIC->database();
65
66 $query = "SELECT DISTINCT(table_name) FROM abstraction_progress ";
67 $res = $ilDB->query($query);
68 $names = array();
69 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
70 $names[] = $row->table_name;
71 }
72
73 // tables that have been already created in an abstracted
74 // way or tables that have been renamed after being abstracted
75 // (see db_update script)
76 $abs_tables = array_merge($names, array(
77 'acc_access_key',
78 'acc_user_access_key',
79 'ldap_rg_mapping',
80 'page_anchor',
81 'qpl_question_orderinghorizontal',
82 'qpl_question_fileupload',
83 'chat_smilies',
84 'style_color',
85 'style_template_class',
86 'style_template',
87 'page_style_usage',
88 'style_setting',
89 'page_editor_settings',
90 'mep_data',
91 'license_data',
92 'loginname_history',
93 'mep_item',
94 'qpl_a_cloze',
95 'qpl_a_imagemap',
96 'qpl_a_matching',
97 'qpl_num_range',
98 'qpl_qst_cloze',
99 'qpl_qst_essay',
100 'qpl_qst_fileupload',
101 'qpl_qst_flash',
102 'qpl_qst_horder',
103 'qpl_qst_imagemap',
104 'qpl_qst_javaapplet',
105 'qpl_qst_matching',
106 'qpl_qst_mc',
107 'qpl_qst_numeric',
108 'qpl_qst_ordering',
109 'qpl_qst_sc',
110 'qpl_qst_textsubset',
111 'qpl_qst_type',
112 'qpl_sol_sug',
113 'udf_text',
114 'udf_clob',
115 'xmlnestedsettmp',
116 'cache_text',
117 'cache_clob',
118 'qpl_a_errortext',
119 'qpl_qst_errortext',
120 'tst_rnd_cpy',
121 'tst_rnd_qpl_title',
122 'qpl_a_mdef',
123 ));
124
125 return $abs_tables;
126 }
127
142 public function setTargetEncoding(string $a_encoding): void
143 {
144 $this->target_encoding = $a_encoding;
145 }
146
147 public function getTargetEncoding(): string
148 {
149 return $this->target_encoding;
150 }
151
157 public function setBlackList(array $a_blacklist): void
158 {
159 $this->blacklist = $a_blacklist;
160 }
161
162 public function getBlackList(): array
163 {
164 return $this->blacklist;
165 }
166
173 public function setWhiteList(array $a_whitelist): void
174 {
175 $this->whitelist = $a_whitelist;
176 }
177
178 public function getWhiteList(): array
179 {
180 return $this->whitelist;
181 }
182
183 public function setFilter(string $a_filter, string $a_value): void
184 {
185 $this->filter[$a_filter] = $a_value;
186 }
187
191 public function getTables(): array
192 {
193 return $this->tables = $this->manager->listTables();
194 }
195
199 public function checkProcessing(string $a_table): bool
200 {
201 // check black list
202 if (in_array($a_table, $this->blacklist, true)) {
203 return false;
204 }
205
206 // check white list
207 if (count($this->whitelist) > 0 && !in_array($a_table, $this->whitelist, true)) {
208 return false;
209 }
210
211 return true;
212 }
213
217 protected function openFile(string $a_path)
218 {
219 $start = '';
220 $file = fopen($a_path, 'wb');
221 $start .= "\t" . 'global $ilDB;' . "\n\n";
222 fwrite($file, $start);
223
224 return $file;
225 }
226
230 protected function closeFile($fp): void
231 {
232 fclose($fp);
233 }
234
239 public function buildDBGenerationScript(string $a_filename = ""): void
240 {
241 if (@is_dir($a_filename)) {
242 $is_dir = true;
243 $path = $a_filename;
244 } else {
245 $is_dir = false;
246 $path = '';
247 }
248
249 $file = "";
250 if ($a_filename !== "" && !$is_dir) {
251 $file = fopen($a_filename, 'wb');
252
253 $start = '<?php' . "\n" . 'function setupILIASDatabase()' . "\n{\n";
254 $start .= "\t" . 'global $ilDB;' . "\n\n";
255 fwrite($file, $start);
256 } else {
257 echo "<pre>";
258 }
259
260 foreach ($this->getTables() as $table) {
261 if ($this->checkProcessing($table)) {
262 if ($a_filename !== "") {
263 flush();
264 }
265
266 if ($is_dir) {
267 $file = $this->openFile($path . '/' . $table);
268 }
269
270 // create table statement
271 $this->buildCreateTableStatement($table, $file);
272
273 // primary key
274 $this->buildAddPrimaryKeyStatement($table, $file);
275
276 // indices
277 $this->buildAddIndexStatements($table, $file);
278
279 // constraints (currently unique keys)
280 $this->buildAddUniqueConstraintStatements($table, $file);
281
282 // auto increment sequence
283 $this->buildCreateSequenceStatement($table, $file);
284
285 if (in_array($table, array('usr_session_stats', 'usr_session_raw', 'il_plugin'))) {
286 continue;
287 }
288
289 // inserts
290 if ($is_dir) {
291 $this->buildInsertStatement($table, $path);
292 #$this->buildInsertStatementsXML($table,$path);
293 } else {
294 $this->buildInsertStatements($table, $file);
295 }
296
297 if ($is_dir) {
298 $this->closeFile($file);
299 }
300 } elseif ($a_filename !== "") {
301 echo "<br><b>missing: " . $table . "</b>";
302 flush();
303 }
304 }
305
306 // sequence(s) without table (of same name)
307 $this->buildSingularSequenceStatement($file);
308
309 if ($a_filename === "") {
310 echo "</pre>";
311 } elseif (!$is_dir) {
312 $end = "\n}\n?>\n";
313 $ok = fwrite($file, $end);
314 fclose($file);
315 }
316 }
317
323 public function buildCreateTableStatement(string $a_table, $a_file = null): void
324 {
325 $fields = $this->analyzer->getFieldInformation($a_table, true);
326 $this->fields = $fields;
327 $create_st = "\n\n//\n// " . $a_table . "\n//\n";
328 $create_st .= '$fields = array (' . "\n";
329 $f_sep = "";
330 foreach ($fields as $f => $def) {
331 $create_st .= "\t" . $f_sep . '"' . $f . '" => array (' . "\n";
332 $f_sep = ",";
333 $a_sep = "";
334 foreach ($def as $k => $v) {
335 if ($k !== "nativetype" && $k !== "alt_types" && $k !== "autoincrement" && !is_null($v)) {
336 switch ($k) {
337 case "notnull":
338 case "unsigned":
339 case "fixed":
340 $v = $v ? "true" : "false";
341 break;
342
343 case "default":
344 case "type":
345 $v = '"' . $v . '"';
346 break;
347
348 default:
349 break;
350 }
351 $create_st .= "\t\t" . $a_sep . '"' . $k . '" => ' . $v . "\n";
352 $a_sep = ",";
353 }
354 }
355 $create_st .= "\t" . ')' . "\n";
356 }
357 $create_st .= ');' . "\n";
358 $create_st .= '$ilDB->createTable("' . $a_table . '", $fields);' . "\n";
359
360 if ($a_file === null) {
361 echo $create_st;
362 } else {
363 fwrite($a_file, $create_st);
364 }
365 }
366
370 public function buildAddPrimaryKeyStatement(string $a_table, $a_file = null): void
371 {
372 $pk = $this->analyzer->getPrimaryKeyInformation($a_table);
373
374 if (is_array($pk["fields"]) && count($pk["fields"]) > 0) {
375 $pk_st = "\n" . '$pk_fields = array(';
376 $sep = "";
377 foreach ($pk["fields"] as $f => $pos) {
378 $pk_st .= $sep . '"' . $f . '"';
379 $sep = ",";
380 }
381 $pk_st .= ");\n";
382 $pk_st .= '$ilDB->addPrimaryKey("' . $a_table . '", $pk_fields);' . "\n";
383
384 if ($a_file === null) {
385 echo $pk_st;
386 } else {
387 fwrite($a_file, $pk_st);
388 }
389 }
390 }
391
395 public function buildAddIndexStatements(string $a_table, $a_file = null): void
396 {
397 $ind = $this->analyzer->getIndicesInformation($a_table, true);
398
399 if (is_array($ind)) {
400 foreach ($ind as $i) {
401 if ($i["fulltext"]) {
402 $ft = ", true";
403 } else {
404 $ft = ", false";
405 }
406 $in_st = "\n" . '$in_fields = array(';
407 $sep = "";
408 foreach ($i["fields"] as $f => $pos) {
409 $in_st .= $sep . '"' . $f . '"';
410 $sep = ",";
411 }
412 $in_st .= ");\n";
413 $in_st .= '$ilDB->addIndex("' . $a_table . '", $in_fields, "' . $i["name"] . '"' . $ft . ');' . "\n";
414
415 if ($a_file === null) {
416 echo $in_st;
417 } else {
418 fwrite($a_file, $in_st);
419 }
420 }
421 }
422 }
423
427 private function printOrWrite(string $string, $file_handle = null): void
428 {
429 if ($file_handle === null) {
430 echo $string;
431 } else {
432 fwrite($file_handle, $string);
433 }
434 }
435
439 public function buildAddUniqueConstraintStatements(string $a_table, $file_handle = null): void
440 {
441 $con = $this->analyzer->getConstraintsInformation($a_table, true);
442
443 if (is_array($con)) {
444 $in_st = '';
445 foreach ($con as $i) {
446 $in_st = "\n" . '$in_fields = array(';
447 $sep = "";
448 foreach ($i["fields"] as $f => $pos) {
449 $in_st .= $sep . '"' . $f . '"';
450 $sep = ",";
451 }
452 $in_st .= ");\n";
453 $in_st .= '$ilDB->addUniqueConstraint("' . $a_table . '", $in_fields, "' . $i["name"] . '");' . "\n";
454 }
455 $this->printOrWrite($in_st, $file_handle);
456 }
457 }
458
463 public function buildCreateSequenceStatement(string $a_table, $file_handle = null): void
464 {
465 $seq = $this->analyzer->hasSequence($a_table);
466 if ($seq !== false) {
467 $seq_st = "\n" . '$ilDB->createSequence("' . $a_table . '", ' . (int) $seq . ');' . "\n";
468
469 $this->printOrWrite($seq_st, $file_handle);
470 }
471 }
472
476 public function buildSingularSequenceStatement($file_handle = null): void
477 {
478 $r = $this->manager->listSequences();
479
480 foreach ($r as $seq) {
481 if (!in_array($seq, $this->tables, true)) {
482 // 12570
483 if ($seq === "sahs_sc13_seq") {
484 continue;
485 }
486
487 $create_st = "\n" . '$ilDB->createSequence("' . $seq . '");' . "\n";
488
489 $this->printOrWrite($create_st, $file_handle);
490 }
491 }
492 }
493
497 public function buildInsertStatement(string $a_table, string $a_basedir): bool
498 {
499 global $DIC;
500 $ilLogger = $DIC->logger()->root();
501
502 $ilLogger->log('Starting export of:' . $a_table);
503
504 $set = $this->il_db->query("SELECT * FROM " . $this->il_db->quoteIdentifier($a_table));
505 $row = 0;
506
507 umask(0000);
508 if (!mkdir(
509 $concurrentDirectory = $a_basedir . '/' . $a_table . '_inserts',
510 fileperms($a_basedir)
511 ) && !is_dir($concurrentDirectory)) {
512 throw new \RuntimeException(sprintf('Directory "%s" was not created', $concurrentDirectory));
513 }
514
515 $filenum = 1;
516 while ($rec = $this->il_db->fetchAssoc($set)) {
517 $values = array();
518 foreach ($rec as $f => $v) {
519 if ($this->fields[$f]['type'] === 'text' && $this->fields[$f]['length'] >= 1000) {
520 $v = $this->shortenText($a_table, $f, $v, $this->fields[$f]['length']);
521 }
522
523 $values[$f] = array(
524 $this->fields[$f]['type'],
525 $v,
526 );
527 }
528
529 $rows[$a_table][$row++] = $values;
530
531 if ($row >= 1000) {
532 $ilLogger->log('Writing insert statements after 1000 lines...');
533 $fp = fopen($a_basedir . '/' . $a_table . '_inserts/' . $filenum++ . '.data', 'wb');
534 fwrite($fp, serialize($rows));
535 fclose($fp);
536
537 $row = 0;
538 unset($rows);
539 }
540 }
541 if (isset($rows)) {
542 $fp = fopen($a_basedir . '/' . $a_table . '_inserts/' . $filenum++ . '.data', 'wb');
543 fwrite($fp, serialize($rows) . "\n");
544 fclose($fp);
545 }
546
547 $ilLogger->log('Finished export of: ' . $a_table);
548 if (function_exists('memory_get_usage')) {
549 $ilLogger->log('Memory usage: ' . memory_get_usage(true));
550 }
551
552 return true;
553 }
554
558 public function buildInsertStatements(string $a_table, $file_handle = null): void
559 {
560 if ($a_table === "lng_data") {
561 return;
562 }
563
564 $set = $this->il_db->query("SELECT * FROM " . $this->il_db->quoteIdentifier($a_table));
565 $ins_st = "";
566 while ($rec = $this->il_db->fetchAssoc($set)) {
567 $fields = array();
568 $types = array();
569 $values = array();
570 $i_str = array();
571 foreach ($rec as $f => $v) {
572 $v = str_replace('\\', '\\\\', $v);
573 $i_str[] = "'" . $f . "' => array('" . $this->fields[$f]["type"] . "', '" . str_replace(
574 "'",
575 "\'",
576 $v
577 ) . "')";
578 }
579 $ins_st = "\n" . '$ilDB->insert("' . $a_table . '", array(' . "\n";
580 $ins_st .= implode(", ", $i_str) . "));\n";
581
582 $this->printOrWrite($ins_st, $file_handle);
583 $ins_st = "";
584 }
585 }
586
590 protected function shortenText(string $table, string $field, string $a_value, int $a_size): string
591 {
592 global $DIC;
593 $ilLogger = $DIC->logger()->root();
594
595 if ($this->getTargetEncoding() === 'UTF-8') {
596 return $a_value;
597 }
598 // Convert to target encoding
599 $shortened = mb_convert_encoding($a_value, $this->getTargetEncoding(), 'UTF-8');
600 // Shorten
601 $shortened = ilStr::shortenText($shortened, 0, $a_size, $this->getTargetEncoding());
602 // Convert back to UTF-8
603 $shortened = mb_convert_encoding($shortened, 'UTF-8', $this->getTargetEncoding());
604
605 if (strlen($a_value) != strlen($shortened)) {
606 $ilLogger->log('Table : ' . $table);
607 $ilLogger->log('Field : ' . $field);
608 $ilLogger->log('Type : ' . $this->fields[$field]['type']);
609 $ilLogger->log('Length : ' . $this->fields[$field]['length']);
610 $ilLogger->log('Before : ' . $a_value);
611 $ilLogger->log('Shortened : ' . $shortened);
612 $ilLogger->log('Strlen Before: ' . strlen($a_value));
613 $ilLogger->log('Strlen After : ' . strlen($shortened));
614 }
615
616 return $shortened;
617 }
618}
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static shortenText(string $a_string, int $a_start_pos, int $a_num_bytes, string $a_encoding='UTF-8')
Shorten text to the given number of bytes.
global $DIC
Definition: feed.php:28
$errors fields
Definition: imgupload.php:67
Interface ilDBInterface.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
$path
Definition: ltiservices.php:32
$res
Definition: ltiservices.php:69
$i
Definition: metadata.php:41
__construct(Container $dic, ilPlugin $plugin)
@inheritDoc
$query
$rows
Definition: xhr_table.php:10