ILIAS  trunk Revision v12.0_alpha-1227-g7ff6d300864
ilDBPdoMySQLFieldDefinition.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
22
29{
30 public array $lobs;
31 public const DEFAULT_DECIMAL_PLACES = 2;
32 public const DEFAULT_TEXT_LENGTH = 4000;
33 public const DEFINITION_COLUMN_NAME = "/^[a-z]+[_a-z0-9]*$/";
34 public const DEFINITION_TABLE_NAME = "/^[a-z]+[_a-z0-9]*$/";
35
36 private const MAX_TABLE_IDENTIFIER_LENGTH = 63;
37
38 protected static self $instance;
42 public array $allowed_attributes_old = [
43 self::T_TEXT => ['length', 'notnull', 'default', 'fixed'],
44 self::T_INTEGER => ['length', 'notnull', 'default', 'unsigned'],
45 self::T_FLOAT => ['notnull', 'default'],
46 self::T_DATE => ['notnull', 'default'],
47 self::T_TIME => ['notnull', 'default'],
48 self::T_TIMESTAMP => ['notnull', 'default'],
49 self::T_CLOB => ['notnull', 'default'],
50 self::T_BLOB => ['notnull', 'default'],
51 ];
55 public array $allowed_attributes = [
56 "text" => ["length", "notnull", "default", "fixed"],
57 "integer" => ["length", "notnull", "default", "unsigned"],
58 "float" => ["notnull", "default"],
59 "date" => ["notnull", "default"],
60 "time" => ["notnull", "default"],
61 "timestamp" => ["notnull", "default"],
62 "clob" => ["length", "notnull", "default"],
63 "blob" => ["length", "notnull", "default"],
64 ];
65 protected array $max_length = [
66 self::T_INTEGER => [1, 2, 3, 4, 8],
67 self::T_TEXT => 4000,
68 ];
72 protected array $available_types = [
81 ];
85 protected array $reserved_mysql = [
86 "ACCESSIBLE",
87 "ACCOUNT",
88 "ACTION",
89 "ADD",
90 "AFTER",
91 "AGAINST",
92 "AGGREGATE",
93 "ALGORITHM",
94 "ALL",
95 "ALTER",
96 "ALWAYS",
97 "ANALYSE",
98 "ANALYZE",
99 "AND",
100 "ANY",
101 "AS",
102 "ASC",
103 "ASCII",
104 "ASENSITIVE",
105 "AT",
106 "AUTHORS",
107 "AUTOEXTEND_SIZE",
108 "AUTO_INCREMENT",
109 "AVG",
110 "AVG_ROW_LENGTH",
111 "BACKUP",
112 "BEFORE",
113 "BEGIN",
114 "BETWEEN",
115 "BIGINT",
116 "BINARY",
117 "BINLOG",
118 "BIT",
119 "BLOB",
120 "BLOCK",
121 "BOOL",
122 "BOOLEAN",
123 "BOTH",
124 "BTREE",
125 "BY",
126 "BYTE",
127 "CACHE",
128 "CALL",
129 "CASCADE",
130 "CASCADED",
131 "CASE",
132 "CATALOG_NAME",
133 "CHAIN",
134 "CHANGE",
135 "CHANGED",
136 "CHANNEL",
137 "CHAR",
138 "CHARACTER",
139 "CHARSET",
140 "CHECK",
141 "CHECKSUM",
142 "CIPHER",
143 "CLASS_ORIGIN",
144 "CLIENT",
145 "CLOSE",
146 "COALESCE",
147 "CODE",
148 "COLLATE",
149 "COLLATION",
150 "COLUMN",
151 "COLUMNS",
152 "COLUMN_FORMAT",
153 "COLUMN_NAME",
154 "COMMENT",
155 "COMMIT",
156 "COMMITTED",
157 "COMPACT",
158 "COMPLETION",
159 "COMPRESSED",
160 "COMPRESSION",
161 "CONCURRENT",
162 "CONDITION",
163 "CONNECTION",
164 "CONSISTENT",
165 "CONSTRAINT",
166 "CONSTRAINT_CATALOG",
167 "CONSTRAINT_NAME",
168 "CONSTRAINT_SCHEMA",
169 "CONTAINS",
170 "CONTEXT",
171 "CONTINUE",
172 "CONTRIBUTORS",
173 "CONVERT",
174 "CPU",
175 "CREATE",
176 "CROSS",
177 "CUBE",
178 "CURRENT",
179 "CURRENT_DATE",
180 "CURRENT_TIME",
181 "CURRENT_TIMESTAMP",
182 "CURRENT_USER",
183 "CURSOR",
184 "CURSOR_NAME",
185 "DATA",
186 "DATABASE",
187 "DATABASES",
188 "DATAFILE",
189 "DATE",
190 "DATETIME",
191 "DAY",
192 "DAY_HOUR",
193 "DAY_MICROSECOND",
194 "DAY_MINUTE",
195 "DAY_SECOND",
196 "DEALLOCATE",
197 "DEC",
198 "DECIMAL",
199 "DECLARE",
200 "DEFAULT",
201 "DEFAULT_AUTH",
202 "DEFINER",
203 "DELAYED",
204 "DELAY_KEY_WRITE",
205 "DELETE",
206 "DESC",
207 "DESCRIBE",
208 "DES_KEY_FILE",
209 "DETERMINISTIC",
210 "DIAGNOSTICS",
211 "DIRECTORY",
212 "DISABLE",
213 "DISCARD",
214 "DISK",
215 "DISTINCT",
216 "DISTINCTROW",
217 "DIV",
218 "DO",
219 "DOUBLE",
220 "DROP",
221 "DUAL",
222 "DUMPFILE",
223 "DUPLICATE",
224 "DYNAMIC",
225 "EACH",
226 "ELSE",
227 "ELSEIF",
228 "ENABLE",
229 "ENCLOSED",
230 "ENCRYPTION",
231 "END",
232 "ENDS",
233 "ENGINE",
234 "ENGINES",
235 "ENUM",
236 "ERROR",
237 "ERRORS",
238 "ESCAPE",
239 "ESCAPED",
240 "EVENT",
241 "EVENTS",
242 "EVERY",
243 "EXCHANGE",
244 "EXECUTE",
245 "EXISTS",
246 "EXIT",
247 "EXPANSION",
248 "EXPIRE",
249 "EXPLAIN",
250 "EXPORT",
251 "EXTENDED",
252 "EXTENT_SIZE",
253 "FALSE",
254 "FAST",
255 "FAULTS",
256 "FETCH",
257 "FIELDS",
258 "FILE",
259 "FILE_BLOCK_SIZE",
260 "FILTER",
261 "FIRST",
262 "FIXED",
263 "FLOAT",
264 "FLOAT4",
265 "FLOAT8",
266 "FLUSH",
267 "FOLLOWS",
268 "FOR",
269 "FORCE",
270 "FOREIGN",
271 "FORMAT",
272 "FOUND",
273 "FROM",
274 "FULL",
275 "FULLTEXT",
276 "FUNCTION",
277 "GENERAL",
278 "GENERATED",
279 "GEOMETRY",
280 "GEOMETRYCOLLECTION",
281 "GET",
282 "GET_FORMAT",
283 "GLOBAL",
284 "GRANT",
285 "GRANTS",
286 "GROUP",
287 "GROUP_REPLICATION",
288 "HANDLER",
289 "HASH",
290 "HAVING",
291 "HELP",
292 "HIGH_PRIORITY",
293 "HOST",
294 "HOSTS",
295 "HOUR",
296 "HOUR_MICROSECOND",
297 "HOUR_MINUTE",
298 "HOUR_SECOND",
299 "IDENTIFIED",
300 "IF",
301 "IGNORE",
302 "IGNORE_SERVER_IDS",
303 "IMPORT",
304 "IN",
305 "INDEX",
306 "INDEXES",
307 "INFILE",
308 "INITIAL_SIZE",
309 "INNER",
310 "INOUT",
311 "INSENSITIVE",
312 "INSERT",
313 "INSERT_METHOD",
314 "INSTALL",
315 "INSTANCE",
316 "INT",
317 "INT1",
318 "INT2",
319 "INT3",
320 "INT4",
321 "INT8",
322 "INTEGER",
323 "INTERVAL",
324 "INTO",
325 "INVOKER",
326 "IO",
327 "IO_AFTER_GTIDS",
328 "IO_BEFORE_GTIDS",
329 "IO_THREAD",
330 "IPC",
331 "IS",
332 "ISOLATION",
333 "ISSUER",
334 "ITERATE",
335 "JOIN",
336 "JSON",
337 "KEY",
338 "KEYS",
339 "KEY_BLOCK_SIZE",
340 "KILL",
341 "LANGUAGE",
342 "LAST",
343 "LEADING",
344 "LEAVE",
345 "LEAVES",
346 "LEFT",
347 "LESS",
348 "LEVEL",
349 "LIKE",
350 "LIMIT",
351 "LINEAR",
352 "LINES",
353 "LINESTRING",
354 "LIST",
355 "LOAD",
356 "LOCAL",
357 "LOCALTIME",
358 "LOCALTIMESTAMP",
359 "LOCK",
360 "LOCKS",
361 "LOGFILE",
362 "LOGS",
363 "LONG",
364 "LONGBLOB",
365 "LONGTEXT",
366 "LOOP",
367 "LOW_PRIORITY",
368 "MASTER",
369 "MASTER_AUTO_POSITION",
370 "MASTER_BIND",
371 "MASTER_CONNECT_RETRY",
372 "MASTER_DELAY",
373 "MASTER_HEARTBEAT_PERIOD",
374 "MASTER_HOST",
375 "MASTER_LOG_FILE",
376 "MASTER_LOG_POS",
377 "MASTER_PASSWORD",
378 "MASTER_PORT",
379 "MASTER_RETRY_COUNT",
380 "MASTER_SERVER_ID",
381 "MASTER_SSL",
382 "MASTER_SSL_CA",
383 "MASTER_SSL_CAPATH",
384 "MASTER_SSL_CERT",
385 "MASTER_SSL_CIPHER",
386 "MASTER_SSL_CRL",
387 "MASTER_SSL_CRLPATH",
388 "MASTER_SSL_KEY",
389 "MASTER_SSL_VERIFY_SERVER_CERT",
390 "MASTER_TLS_VERSION",
391 "MASTER_USER",
392 "MATCH",
393 "MAXVALUE",
394 "MAX_CONNECTIONS_PER_HOUR",
395 "MAX_QUERIES_PER_HOUR",
396 "MAX_ROWS",
397 "MAX_SIZE",
398 "MAX_STATEMENT_TIME",
399 "MAX_UPDATES_PER_HOUR",
400 "MAX_USER_CONNECTIONS",
401 "MEDIUM",
402 "MEDIUMBLOB",
403 "MEDIUMINT",
404 "MEDIUMTEXT",
405 "MEMORY",
406 "MERGE",
407 "MESSAGE_TEXT",
408 "MICROSECOND",
409 "MIDDLEINT",
410 "MIGRATE",
411 "MINUTE",
412 "MINUTE_MICROSECOND",
413 "MINUTE_SECOND",
414 "MIN_ROWS",
415 "MOD",
416 "MODE",
417 "MODIFIES",
418 "MODIFY",
419 "MONTH",
420 "MULTILINESTRING",
421 "MULTIPOINT",
422 "MULTIPOLYGON",
423 "MUTEX",
424 "MYSQL_ERRNO",
425 "NAME",
426 "NAMES",
427 "NATIONAL",
428 "NATURAL",
429 "NCHAR",
430 "NDB",
431 "NDBCLUSTER",
432 "NEVER",
433 "NEW",
434 "NEXT",
435 "NO",
436 "NODEGROUP",
437 "NONBLOCKING",
438 "NONE",
439 "NOT",
440 "NO_WAIT",
441 "NO_WRITE_TO_BINLOG",
442 "NULL",
443 "NUMBER",
444 "NUMERIC",
445 "NVARCHAR",
446 "OFFSET",
447 "OLD_PASSWORD",
448 "ON",
449 "ONE",
450 "ONE_SHOT",
451 "ONLY",
452 "OPEN",
453 "OPTIMIZE",
454 "OPTIMIZER_COSTS",
455 "OPTION",
456 "OPTIONALLY",
457 "OPTIONS",
458 "OR",
459 "ORDER",
460 "OUT",
461 "OUTER",
462 "OUTFILE",
463 "OWNER",
464 "PACK_KEYS",
465 "PAGE",
466 "PARSER",
467 "PARSE_GCOL_EXPR",
468 "PARTIAL",
469 "PARTITION",
470 "PARTITIONING",
471 "PARTITIONS",
472 "PASSWORD",
473 "PHASE",
474 "PLUGIN",
475 "PLUGINS",
476 "PLUGIN_DIR",
477 "POINT",
478 "POLYGON",
479 "PORT",
480 "PRECEDES",
481 "PRECISION",
482 "PREPARE",
483 "PRESERVE",
484 "PREV",
485 "PRIMARY",
486 "PRIVILEGES",
487 "PROCEDURE",
488 "PROCESSLIST",
489 "PROFILE",
490 "PROFILES",
491 "PROXY",
492 "PURGE",
493 "QUARTER",
494 "QUERY",
495 "QUICK",
496 "RANGE",
497 "READ",
498 "READS",
499 "READ_ONLY",
500 "READ_WRITE",
501 "REAL",
502 "REBUILD",
503 "RECOVER",
504 "REDOFILE",
505 "REDO_BUFFER_SIZE",
506 "REDUNDANT",
507 "REFERENCES",
508 "REGEXP",
509 "RELAY",
510 "RELAYLOG",
511 "RELAY_LOG_FILE",
512 "RELAY_LOG_POS",
513 "RELAY_THREAD",
514 "RELEASE",
515 "RELOAD",
516 "REMOVE",
517 "RENAME",
518 "REORGANIZE",
519 "REPAIR",
520 "REPEAT",
521 "REPEATABLE",
522 "REPLACE",
523 "REPLICATE_DO_DB",
524 "REPLICATE_DO_TABLE",
525 "REPLICATE_IGNORE_DB",
526 "REPLICATE_IGNORE_TABLE",
527 "REPLICATE_REWRITE_DB",
528 "REPLICATE_WILD_DO_TABLE",
529 "REPLICATE_WILD_IGNORE_TABLE",
530 "REPLICATION",
531 "REQUIRE",
532 "RESET",
533 "RESIGNAL",
534 "RESTORE",
535 "RESTRICT",
536 "RESUME",
537 "RETURN",
538 "RETURNED_SQLSTATE",
539 "RETURNS",
540 "REVERSE",
541 "REVOKE",
542 "RIGHT",
543 "RLIKE",
544 "ROLLBACK",
545 "ROLLUP",
546 "ROTATE",
547 "ROUTINE",
548 "ROW",
549 "ROWS",
550 "ROW_COUNT",
551 "ROW_FORMAT",
552 "RTREE",
553 "SAVEPOINT",
554 "SCHEDULE",
555 "SCHEMA",
556 "SCHEMAS",
557 "SCHEMA_NAME",
558 "SECOND",
559 "SECOND_MICROSECOND",
560 "SECURITY",
561 "SELECT",
562 "SENSITIVE",
563 "SEPARATOR",
564 "SERIAL",
565 "SERIALIZABLE",
566 "SERVER",
567 "SESSION",
568 "SET",
569 "SHARE",
570 "SHOW",
571 "SHUTDOWN",
572 "SIGNAL",
573 "SIGNED",
574 "SIMPLE",
575 "SLAVE",
576 "SLOW",
577 "SMALLINT",
578 "SNAPSHOT",
579 "SOCKET",
580 "SOME",
581 "SONAME",
582 "SOUNDS",
583 "SOURCE",
584 "SPATIAL",
585 "SPECIFIC",
586 "SQL",
587 "SQLEXCEPTION",
588 "SQLSTATE",
589 "SQLWARNING",
590 "SQL_AFTER_GTIDS",
591 "SQL_AFTER_MTS_GAPS",
592 "SQL_BEFORE_GTIDS",
593 "SQL_BIG_RESULT",
594 "SQL_BUFFER_RESULT",
595 "SQL_CACHE",
596 "SQL_CALC_FOUND_ROWS",
597 "SQL_NO_CACHE",
598 "SQL_SMALL_RESULT",
599 "SQL_THREAD",
600 "SQL_TSI_DAY",
601 "SQL_TSI_HOUR",
602 "SQL_TSI_MINUTE",
603 "SQL_TSI_MONTH",
604 "SQL_TSI_QUARTER",
605 "SQL_TSI_SECOND",
606 "SQL_TSI_WEEK",
607 "SQL_TSI_YEAR",
608 "SSL",
609 "STACKED",
610 "START",
611 "STARTING",
612 "STARTS",
613 "STATS_AUTO_RECALC",
614 "STATS_PERSISTENT",
615 "STATS_SAMPLE_PAGES",
616 "STATUS",
617 "STOP",
618 "STORAGE",
619 "STORED",
620 "STRAIGHT_JOIN",
621 "STRING",
622 "SUBCLASS_ORIGIN",
623 "SUBJECT",
624 "SUBPARTITION",
625 "SUBPARTITIONS",
626 "SUPER",
627 "SUSPEND",
628 "SWAPS",
629 "SWITCHES",
630 "TABLE",
631 "TABLES",
632 "TABLESPACE",
633 "TABLE_CHECKSUM",
634 "TABLE_NAME",
635 "TEMPORARY",
636 "TEMPTABLE",
637 "TERMINATED",
638 "TEXT",
639 "THAN",
640 "THEN",
641 "TIME",
642 "TIMESTAMP",
643 "TIMESTAMPADD",
644 "TIMESTAMPDIFF",
645 "TINYBLOB",
646 "TINYINT",
647 "TINYTEXT",
648 "TO",
649 "TRAILING",
650 "TRANSACTION",
651 "TRIGGER",
652 "TRIGGERS",
653 "TRUE",
654 "TRUNCATE",
655 "TYPE",
656 "TYPES",
657 "UNCOMMITTED",
658 "UNDEFINED",
659 "UNDO",
660 "UNDOFILE",
661 "UNDO_BUFFER_SIZE",
662 "UNICODE",
663 "UNINSTALL",
664 "UNION",
665 "UNIQUE",
666 "UNKNOWN",
667 "UNLOCK",
668 "UNSIGNED",
669 "UNTIL",
670 "UPDATE",
671 "UPGRADE",
672 "USAGE",
673 "USE",
674 "USER",
675 "USER_RESOURCES",
676 "USE_FRM",
677 "USING",
678 "UTC_DATE",
679 "UTC_TIME",
680 "UTC_TIMESTAMP",
681 "VALIDATION",
682 "VALUE",
683 "VALUES",
684 "VARBINARY",
685 "VARCHAR",
686 "VARCHARACTER",
687 "VARIABLES",
688 "VARYING",
689 "VIEW",
690 "VIRTUAL",
691 "WAIT",
692 "WARNINGS",
693 "WEEK",
694 "WEIGHT_STRING",
695 "WHEN",
696 "WHERE",
697 "WHILE",
698 "WITH",
699 "WITHOUT",
700 "WORK",
701 "WRAPPER",
702 "WRITE",
703 "X509",
704 "XA",
705 "XID",
706 "XML",
707 "XOR",
708 "YEAR",
709 "YEAR_MONTH",
710 "ZEROFILL",
711 ];
715 protected array $reserved_postgres = [
716 "ALL",
717 "ANALYSE",
718 "ANALYZE",
719 "AND",
720 "ANY",
721 "ARRAY",
722 "AS",
723 "ASC",
724 "ASYMMETRIC",
725 "AUTHORIZATION",
726 "BETWEEN",
727 "BINARY",
728 "BOTH",
729 "CASE",
730 "CAST",
731 "CHECK",
732 "COLLATE",
733 "COLUMN",
734 "CONSTRAINT",
735 "CREATE",
736 "CROSS",
737 "CURRENT_DATE",
738 "CURRENT_ROLE",
739 "CURRENT_TIME",
740 "CURRENT_TIMESTAMP",
741 "CURRENT_USER",
742 "DEFAULT",
743 "DEFERRABLE",
744 "DESC",
745 "DISTINCT",
746 "DO",
747 "ELSE",
748 "END",
749 "EXCEPT",
750 "FALSE",
751 "FOR",
752 "FOREIGN",
753 "FREEZE",
754 "FROM",
755 "FULL",
756 "GRANT",
757 "GROUP",
758 "HAVING",
759 "ILIKE",
760 "IN",
761 "INITIALLY",
762 "INNER",
763 "INTERSECT",
764 "INTO",
765 "IS",
766 "ISNULL",
767 "JOIN",
768 "LEADING",
769 "LEFT",
770 "LIKE",
771 "LIMIT",
772 "LOCALTIME",
773 "LOCALTIMESTAMP",
774 "NATURAL",
775 "NEW",
776 "NOT",
777 "NOTNULL",
778 "NULL",
779 "OFF",
780 "OFFSET",
781 "OLD",
782 "ON",
783 "ONLY",
784 "OR",
785 "ORDER",
786 "OUTER",
787 "OVERLAPS",
788 "PLACING",
789 "PRIMARY",
790 "REFERENCES",
791 "RETURNING",
792 "RIGHT",
793 "SELECT",
794 "SESSION_USER",
795 "SIMILAR",
796 "SOME",
797 "SYMMETRIC",
798 "TABLE",
799 "THEN",
800 "TO",
801 "TRAILING",
802 "TRUE",
803 "UNION",
804 "UNIQUE",
805 "USER",
806 "USING",
807 "VERBOSE",
808 "WHEN",
809 "WHERE",
810 "WITH",
811 ];
812
814
815 public function __construct(protected \ilDBInterface $db_instance)
816 {
817 }
818
819 protected array $valid_default_values = [
820 'text' => '',
821 'boolean' => true,
822 'integer' => 0,
823 'decimal' => 0.0,
824 'float' => 0.0,
825 'timestamp' => '1970-01-01 00:00:00',
826 'time' => '00:00:00',
827 'date' => '1970-01-01',
828 'clob' => '',
829 'blob' => '',
830 ];
831
835 public function checkTableName(string $table_name): bool
836 {
837 if (!preg_match(self::DEFINITION_TABLE_NAME, $table_name)) {
838 throw new ilDatabaseException('Table name must only contain _a-z0-9 and must start with a-z.');
839 }
840
841 if ($this->isReserved($table_name)) {
842 throw new ilDatabaseException("Invalid table name '" . $table_name . "' (Reserved Word).");
843 }
844
845 if (stripos($table_name, "sys_") === 0) {
846 throw new ilDatabaseException("Invalid table name '" . $table_name . "'. Name must not start with 'sys_'.");
847 }
848
849 if (strlen($table_name) > self::MAX_TABLE_IDENTIFIER_LENGTH) {
850 throw new ilDatabaseException("Invalid table name '" . $table_name
851 . "'. Maximum table identifer length is " . self::MAX_TABLE_IDENTIFIER_LENGTH . " bytes.");
852 }
853
854 return true;
855 }
856
857 public function isReserved(string $table_name): bool
858 {
859 return false;
860 }
861
865 public function getAllReserved(): array
866 {
867 return $this->getReservedMysql();
868 }
869
873 public function getReservedMysql(): array
874 {
876 }
877
881 public function setReservedMysql(array $reserved_mysql): void
882 {
883 $this->reserved_mysql = $reserved_mysql;
884 }
885
886
890 public function checkColumnName(string $column_name): bool
891 {
892 if (!preg_match("/^[a-z]+[_a-z0-9]*$/", $column_name)) {
893 throw new ilDatabaseException("Invalid column name '" . $column_name
894 . "'. Column name must only contain _a-z0-9 and must start with a-z.");
895 }
896
897 if ($this->isReserved($column_name)) {
898 throw new ilDatabaseException("Invalid column name '" . $column_name . "' (Reserved Word).");
899 }
900
901 if (stripos($column_name, "sys_") === 0) {
902 throw new ilDatabaseException("Invalid column name '" . $column_name . "'. Name must not start with 'sys_'.");
903 }
904
905 if (strlen($column_name) > 30) {
906 throw new ilDatabaseException("Invalid column name '" . $column_name . "'. Maximum column identifer length is 30 bytes.");
907 }
908
909 return true;
910 }
911
915 public function checkIndexName(string $a_name): bool
916 {
917 if (!preg_match("/^[a-z]+[_a-z0-9]*$/", $a_name)) {
918 throw new ilDatabaseException("Invalid column name '" . $a_name . "'. Column name must only contain _a-z0-9 and must start with a-z.");
919 }
920
921 if ($this->isReserved($a_name)) {
922 throw new ilDatabaseException("Invalid column name '" . $a_name . "' (Reserved Word).");
923 }
924
925 if (strlen($a_name) > 3) {
926 throw new ilDatabaseException("Invalid index name '" . $a_name . "'. Maximum index identifer length is 3 bytes.");
927 }
928
929 return true;
930 }
931
935 public function checkColumnDefinition(array $a_def): bool
936 {
937 // check valid type
938 if (!in_array($a_def["type"], $this->getAvailableTypes(), true)) {
939 switch ($a_def["type"]) {
940 case "boolean":
941 throw new ilDatabaseException("Invalid column type '" . $a_def["type"] . "'. Use integer(1) instead.");
942
943 case "decimal":
944 throw new ilDatabaseException("Invalid column type '" . $a_def["type"] . "'. Use float or integer instead.");
945
946 default:
947 throw new ilDatabaseException("Invalid column type '" . $a_def["type"] . "'. Allowed types are: "
948 . implode(', ', $this->getAvailableTypes()));
949 }
950 }
951
952 // check used attributes
953 $allowed_attributes = $this->getAllowedAttributes();
954 foreach (array_keys($a_def) as $k) {
955 if ($k !== "type" && !in_array($k, $allowed_attributes[$a_def["type"]], true)) {
956 throw new ilDatabaseException("Attribute '" . $k . "' is not allowed for column type '" . $a_def["type"] . "'.");
957 }
958 }
959
960 // type specific checks
961 $max_length = $this->getMaxLength();
962 switch ($a_def["type"]) {
963 case self::T_TEXT:
964 if ((!isset($a_def["length"]) || $a_def["length"] < 1 || $a_def["length"] > $max_length[self::T_TEXT]) && isset($a_def["length"])) {
965 throw new ilDatabaseException("Invalid length '" . $a_def["length"] . "' for type text." . " Length must be >=1 and <= "
966 . $max_length[self::T_TEXT] . ".");
967 }
968 break;
969
970 case self::T_INTEGER:
971 if (isset($a_def["length"]) && !in_array((int) $a_def["length"], $max_length[self::T_INTEGER], true)) {
972 throw new ilDatabaseException("Invalid length '" . $a_def["length"] . "' for type integer." . " Length must be "
973 . implode(', ', $max_length[self::T_INTEGER]) . " (bytes).");
974 }
975 if ($a_def["unsigned"] ?? null) {
976 throw new ilDatabaseException("Unsigned attribut must not be true for type integer.");
977 }
978 break;
979 }
980
981 return true;
982 }
983
984 public function isAllowedAttribute(string $attribute, string $type): bool
985 {
986 return in_array($attribute, $this->allowed_attributes[$type], true);
987 }
988
992 public function getAvailableTypes(): array
993 {
995 }
996
1000 public function setAvailableTypes(array $available_types): void
1001 {
1002 $this->available_types = $available_types;
1003 }
1004
1008 public function getAllowedAttributes(): array
1009 {
1011 }
1012
1016 public function setAllowedAttributes(array $allowed_attributes): void
1017 {
1018 $this->allowed_attributes = $allowed_attributes;
1019 }
1020
1021 public function getMaxLength(): array
1022 {
1023 return $this->max_length;
1024 }
1025
1026 public function setMaxLength(array $max_length): void
1027 {
1028 $this->max_length = $max_length;
1029 }
1030
1031 private function getDBInstance(): \ilDBInterface
1032 {
1033 return $this->db_instance;
1034 }
1035
1039 public function getValidTypes(): array
1040 {
1042 $db = $this->getDBInstance();
1043
1044 if (!empty($db->options['datatype_map'])) {
1045 foreach ($db->options['datatype_map'] as $type => $mapped_type) {
1046 if (array_key_exists($mapped_type, $types)) {
1047 $types[$type] = $types[$mapped_type];
1048 } elseif (!empty($db->options['datatype_map_callback'][$type])) {
1049 $parameter = ['type' => $type, 'mapped_type' => $mapped_type];
1050 $default = call_user_func_array(
1051 $db->options['datatype_map_callback'][$type],
1052 [&$db, __FUNCTION__, $parameter]
1053 );
1054 $types[$type] = $default;
1055 }
1056 }
1057 }
1058
1059 return $types;
1060 }
1061
1062
1063
1068 public function getDeclaration(string $type, string $name, array $field)
1069 {
1070 $db = $this->getDBInstance();
1071
1072 if (!empty($db->options['datatype_map'][$type])) {
1073 $type = $db->options['datatype_map'][$type];
1074 if (!empty($db->options['datatype_map_callback'][$type])) {
1075 $parameter = ['type' => $type, 'name' => $name, 'field' => $field];
1076
1077 return call_user_func_array(
1078 $db->options['datatype_map_callback'][$type],
1079 [&$db, __FUNCTION__, $parameter]
1080 );
1081 }
1082 $field['type'] = $type;
1083 }
1084
1085 if (!method_exists($this, "get{$type}Declaration")) {
1086 throw new ilDatabaseException('type not defined: ' . $type);
1087 }
1088
1089 return $this->{"get{$type}Declaration"}($name, $field);
1090 }
1091
1097 public function compareDefinition(array $current, array $previous): array
1098 {
1099 $type = empty($current['type']) ? null : $current['type'];
1100
1101 if (!method_exists($this, "compare{$type}Definition")) {
1102 $db = $this->getDBInstance();
1103
1104 if (!empty($db->options['datatype_map_callback'][$type])) {
1105 $parameter = ['current' => $current, 'previous' => $previous];
1106
1107 return call_user_func_array(
1108 $db->options['datatype_map_callback'][$type],
1109 [&$db, __FUNCTION__, $parameter]
1110 );
1111 }
1112
1113 throw new ilDatabaseException('type "' . $current['type'] . '" is not yet supported');
1114 }
1115
1116 if (empty($previous['type']) || $previous['type'] != $type) {
1117 return $current;
1118 }
1119
1120 $change = $this->{"compare{$type}Definition"}($current, $previous);
1121
1122 if ($previous['type'] != $type) {
1123 $change['type'] = true;
1124 }
1125
1126 $previous_notnull = empty($previous['notnull']) ? false : $previous['notnull'];
1127 $notnull = empty($current['notnull']) ? false : $current['notnull'];
1128 if ($previous_notnull !== $notnull) {
1129 $change['notnull'] = true;
1130 }
1131
1132 $alt = $previous_notnull ? '' : null;
1133 $previous_default = array_key_exists(
1134 'default',
1135 $previous
1136 ) ? $previous['default'] : $alt;
1137 $alt = $notnull ? '' : null;
1138 $default = array_key_exists('default', $current) ? $current['default'] : $alt;
1139 if ($previous_default !== $default) {
1140 $change['default'] = true;
1141 }
1142
1143 return $change;
1144 }
1145
1149 public function quote($value, ?string $type = null, bool $quote = true, bool $escape_wildcards = false): string
1150 {
1151 return $this->getDBInstance()->quote($value, $type ?? '');
1152 }
1153
1159 public function writeLOBToFile($lob, string $file): bool
1160 {
1161 $db = $this->getDBInstance();
1162
1163 if (preg_match('/^(\w+:\/\/)(.*)$/', $file, $match) && $match[1] === 'file://') {
1164 $file = $match[2];
1165 }
1166
1167 $fp = @fopen($file, 'wb');
1168 while (!@feof($lob)) {
1169 $result = @fread($lob, $db->options['lob_buffer_length']);
1170 $read = strlen($result);
1171 if (@fwrite($fp, $result, $read) !== $read) {
1172 @fclose($fp);
1173
1174 throw new ilDatabaseException('could not write to the output file');
1175 }
1176 }
1177 @fclose($fp);
1178
1179 return true;
1180 }
1181
1185 public function destroyLOB($lob): bool
1186 {
1187 $lob_data = stream_get_meta_data($lob);
1188 $lob_index = $lob_data['wrapper_data']->lob_index;
1189 fclose($lob);
1190 if (isset($this->lobs[$lob_index])) {
1191 unset($this->lobs[$lob_index]);
1192 }
1193
1194 return true;
1195 }
1196
1197
1201 public function matchPattern(array $pattern, $operator = null, $field = null): string
1202 {
1203 $db = $this->getDBInstance();
1204
1205 $match = '';
1206 if (!is_null($operator)) {
1207 $operator = strtoupper((string) $operator);
1208 switch ($operator) {
1209 // case insensitive
1210 case 'ILIKE':
1211 if (is_null($field)) {
1212 throw new ilDatabaseException('case insensitive LIKE matching requires passing the field name');
1213 }
1214 $db->loadModule('Function');
1215 $match = $db->lower($field) . ' LIKE ';
1216 break;
1217 // case sensitive
1218 case 'LIKE':
1219 $match = is_null($field) ? 'LIKE ' : $field . ' LIKE ';
1220 break;
1221 default:
1222 throw new ilDatabaseException('not a supported operator type:' . $operator);
1223 }
1224 }
1225 $match .= "'";
1226 foreach ($pattern as $key => $value) {
1227 if ($key % 2 !== 0) {
1228 $match .= $value;
1229 } else {
1230 if ($operator === 'ILIKE') {
1231 $value = strtolower((string) $value);
1232 }
1233 // @Todo: ilDBPdo::escape & escapePattern do nothing, probably quote instead.
1234 $escaped = $db->escape($value);
1235 $match .= $db->escapePattern($escaped);
1236 }
1237 }
1238 $match .= "'";
1239 $match .= $this->patternEscapeString();
1240
1241 return $match;
1242 }
1243
1244 public function patternEscapeString(): string
1245 {
1246 return '';
1247 }
1248
1252 public function mapNativeDatatype(array $field)
1253 {
1254 $db = $this->getDBInstance();
1255 $db_type = strtok($field['type'], '(), ');
1256 if (!empty($db->options['nativetype_map_callback'][$db_type])) {
1257 return call_user_func_array($db->options['nativetype_map_callback'][$db_type], [$db, $field]);
1258 }
1259
1260 return $this->mapNativeDatatypeInternal($field);
1261 }
1262
1266 public function mapPrepareDatatype(string $type)
1267 {
1268 $db = $this->getDBInstance();
1269
1270 if (!empty($db->options['datatype_map'][$type])) {
1271 $type = $db->options['datatype_map'][$type];
1272 if (!empty($db->options['datatype_map_callback'][$type])) {
1273 $parameter = ['type' => $type];
1274
1275 return call_user_func_array(
1276 $db->options['datatype_map_callback'][$type],
1277 [&$db, __FUNCTION__, $parameter]
1278 );
1279 }
1280 }
1281
1282 return $type;
1283 }
1284
1285 #[\Override]
1286 public function getTypeDeclaration(array $field): string
1287 {
1288 $db = $this->getDBInstance();
1289
1290 switch ($field['type']) {
1291 case 'text':
1292 if (empty($field['length']) && array_key_exists('default', $field)) {
1293 $field['length'] = $db->varchar_max_length ?? null;
1294 }
1295 $length = empty($field['length']) ? false : $field['length'];
1296 $fixed = empty($field['fixed']) ? false : $field['fixed'];
1297 if ($fixed) {
1298 return $length ? 'CHAR(' . $length . ')' : 'CHAR(255)';
1299 }
1300 return $length ? 'VARCHAR(' . $length . ')' : 'TEXT';
1301
1302 case 'clob':
1303 if (!empty($field['length'])) {
1304 $length = $field['length'];
1305 if ($length <= 255) {
1306 return 'TINYTEXT';
1307 }
1308
1309 if ($length <= 65532) {
1310 return 'TEXT';
1311 }
1312
1313 if ($length <= 16_777_215) {
1314 return 'MEDIUMTEXT';
1315 }
1316 }
1317
1318 return 'LONGTEXT';
1319 case 'blob':
1320 if (!empty($field['length'])) {
1321 $length = $field['length'];
1322 if ($length <= 255) {
1323 return 'TINYBLOB';
1324 }
1325
1326 if ($length <= 65532) {
1327 return 'BLOB';
1328 }
1329
1330 if ($length <= 16_777_215) {
1331 return 'MEDIUMBLOB';
1332 }
1333 }
1334
1335 return 'LONGBLOB';
1336 case 'integer':
1337 if (!empty($field['length'])) {
1338 $length = $field['length'];
1339 if ($length <= 1) {
1340 return 'TINYINT';
1341 }
1342
1343 if ($length === 2) {
1344 return 'SMALLINT';
1345 }
1346
1347 if ($length === 3) {
1348 return 'MEDIUMINT';
1349 }
1350
1351 if ($length === 4) {
1352 return 'INT';
1353 }
1354
1355 if ($length > 4) {
1356 return 'BIGINT';
1357 }
1358 }
1359
1360 return 'INT';
1361 case 'boolean':
1362 return 'TINYINT(1)';
1363 case 'date':
1364 return 'DATE';
1365 case 'time':
1366 return 'TIME';
1367 case 'timestamp':
1368 return 'DATETIME';
1369 case 'float':
1370 return 'DOUBLE';
1371 case 'decimal':
1372 $length = empty($field['length']) ? 18 : $field['length'];
1373 // @Todo: Change property access to method call.
1374 $scale = empty($field['scale']) ? $db->options['decimal_places'] : $field['scale'];
1375
1376 return 'DECIMAL(' . $length . ',' . $scale . ')';
1377 }
1378
1379 return '';
1380 }
1381
1385 private function mapNativeDatatypeInternal(array $field): array
1386 {
1387 $db_type = strtolower((string) $field['type']);
1388 $db_type = strtok($db_type, '(), ');
1389 if ($db_type === 'national') {
1390 $db_type = strtok('(), ');
1391 }
1392 if (!empty($field['length'])) {
1393 $length = strtok($field['length'], ', ');
1394 $decimal = strtok(', ');
1395 } else {
1396 $length = strtok('(), ');
1397 $decimal = strtok('(), ');
1398 }
1399 $type = [];
1400 $unsigned = $fixed = null;
1401 switch ($db_type) {
1402 case 'tinyint':
1403 $type[] = 'integer';
1404 $type[] = 'boolean';
1405 if (preg_match('/^(is|has)/', (string) $field['name'])) {
1406 $type = array_reverse($type);
1407 }
1408 $unsigned = preg_match('/ unsigned/i', (string) $field['type']);
1409 $length = 1;
1410 break;
1411 case 'smallint':
1412 $type[] = 'integer';
1413 $unsigned = preg_match('/ unsigned/i', (string) $field['type']);
1414 $length = 2;
1415 break;
1416 case 'mediumint':
1417 $type[] = 'integer';
1418 $unsigned = preg_match('/ unsigned/i', (string) $field['type']);
1419 $length = 3;
1420 break;
1421 case 'int':
1422 case 'integer':
1423 $type[] = 'integer';
1424 $unsigned = preg_match('/ unsigned/i', (string) $field['type']);
1425 $length = 4;
1426 break;
1427 case 'bigint':
1428 $type[] = 'integer';
1429 $unsigned = preg_match('/ unsigned/i', (string) $field['type']);
1430 $length = 8;
1431 break;
1432 case 'tinytext':
1433 case 'mediumtext':
1434 case 'longtext':
1435 case 'text':
1436 case 'varchar':
1437 $fixed = false;
1438 // no break
1439 case 'string':
1440 case 'char':
1441 $type[] = 'text';
1442 if ($length == '1') {
1443 $type[] = 'boolean';
1444 if (preg_match('/^(is|has)/', (string) $field['name'])) {
1445 $type = array_reverse($type);
1446 }
1447 } elseif (str_contains($db_type, 'text')) {
1448 $type[] = 'clob';
1449 if ($decimal === 'binary') {
1450 $type[] = 'blob';
1451 }
1452 }
1453 if ($fixed !== false) {
1454 $fixed = true;
1455 }
1456 break;
1457 case 'enum':
1458 $type[] = 'text';
1459 preg_match_all('/\'.+\'/U', (string) $field['type'], $matches);
1460 $length = 0;
1461 $fixed = false;
1462 if (is_array($matches)) {
1463 foreach ($matches[0] as $value) {
1464 $length = max($length, strlen($value) - 2);
1465 }
1466 if ($length == '1' && count($matches[0]) === 2) {
1467 $type[] = 'boolean';
1468 if (preg_match('/^(is|has)/', (string) $field['name'])) {
1469 $type = array_reverse($type);
1470 }
1471 }
1472 }
1473 $type[] = 'integer';
1474 // no break
1475 case 'set':
1476 $fixed = false;
1477 $type[] = 'text';
1478 $type[] = 'integer';
1479 break;
1480 case 'date':
1481 $type[] = 'date';
1482 $length = null;
1483 break;
1484 case 'datetime':
1485 case 'timestamp':
1486 $type[] = 'timestamp';
1487 $length = null;
1488 break;
1489 case 'time':
1490 $type[] = 'time';
1491 $length = null;
1492 break;
1493 case 'float':
1494 case 'double':
1495 case 'real':
1496 $type[] = 'float';
1497 $unsigned = preg_match('/ unsigned/i', (string) $field['type']);
1498 break;
1499 case 'unknown':
1500 case 'decimal':
1501 case 'numeric':
1502 $type[] = 'decimal';
1503 $unsigned = preg_match('/ unsigned/i', (string) $field['type']);
1504 if ($decimal !== false) {
1505 $length = $length . ',' . $decimal;
1506 }
1507 break;
1508 case 'tinyblob':
1509 case 'mediumblob':
1510 case 'longblob':
1511 case 'blob':
1512 $type[] = 'blob';
1513 $length = null;
1514 break;
1515 case 'binary':
1516 case 'varbinary':
1517 $type[] = 'blob';
1518 break;
1519 case 'year':
1520 $type[] = 'integer';
1521 $type[] = 'date';
1522 $length = null;
1523 break;
1524 default:
1525 throw new ilDatabaseException('unknown database attribute type: ' . $db_type);
1526 }
1527
1528 if ((int) $length <= 0) {
1529 $length = null;
1530 }
1531
1532 return [ $type, $length, $unsigned, $fixed ];
1533 }
1534}
Class ilDBPdoMySQLFieldDefinition.
matchPattern(array $pattern, $operator=null, $field=null)
getDeclaration(string $type, string $name, array $field)
isAllowedAttribute(string $attribute, string $type)
setAllowedAttributes(array $allowed_attributes)
__construct(protected \ilDBInterface $db_instance)
quote($value, ?string $type=null, bool $quote=true, bool $escape_wildcards=false)
compareDefinition(array $current, array $previous)
Class ilDatabaseException.
Class ilMySQLQueryUtils.
Interface ilDBInterface.