ILIAS  trunk Revision v12.0_alpha-1329-g1094ddb0c33
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 (!array_key_exists($type, $this->getValidTypes())) {
1086 throw new ilDatabaseException('type not defined: ' . $type);
1087 }
1088
1089 $quoted_name = $db->quoteIdentifier($name, true);
1090 $type_declaration = $this->getTypeDeclaration($field);
1091
1092 if ($type === 'clob' || $type === 'blob') {
1093 $notnull = empty($field['notnull']) ? '' : ' NOT NULL';
1094 return $quoted_name . ' ' . $type_declaration . $notnull;
1095 }
1096
1097 return $quoted_name . ' ' . $type_declaration . $this->getDeclarationOptions($field);
1098 }
1099
1100 private function getDeclarationOptions(array $field): string
1101 {
1102 $charset = empty($field['charset']) ? '' : ' ' . $field['charset'];
1103
1104 $default = '';
1105 if (array_key_exists('default', $field)) {
1106 if ($field['default'] === '') {
1107 $db = $this->getDBInstance();
1108
1109 if (empty($field['notnull'])) {
1110 $field['default'] = null;
1111 } else {
1113 $field['default'] = $valid_default_values[$field['type']];
1114 }
1115 if ($field['default'] === ''
1116 && isset($db->options['portability'])
1117 && ($db->options['portability'] & 32)
1118 ) {
1119 $field['default'] = ' ';
1120 }
1121 }
1122 $default = ' DEFAULT ' . $this->quote($field['default'], $field['type']);
1123 } elseif (empty($field['notnull'])) {
1124 $default = ' DEFAULT NULL';
1125 }
1126
1127 $notnull = empty($field['notnull']) ? '' : ' NOT NULL';
1128 if (isset($field['notnull']) && $field['notnull'] === false) {
1129 $notnull = ' NULL';
1130 }
1131
1132 $collation = empty($field['collation']) ? '' : ' ' . $field['collation'];
1133
1134 return $charset . $default . $notnull . $collation;
1135 }
1136
1142 public function compareDefinition(array $current, array $previous): array
1143 {
1144 $type = empty($current['type']) ? null : $current['type'];
1145
1146 if (!method_exists($this, "compare{$type}Definition")) {
1147 $db = $this->getDBInstance();
1148
1149 if (!empty($db->options['datatype_map_callback'][$type])) {
1150 $parameter = ['current' => $current, 'previous' => $previous];
1151
1152 return call_user_func_array(
1153 $db->options['datatype_map_callback'][$type],
1154 [&$db, __FUNCTION__, $parameter]
1155 );
1156 }
1157
1158 throw new ilDatabaseException('type "' . $current['type'] . '" is not yet supported');
1159 }
1160
1161 if (empty($previous['type']) || $previous['type'] != $type) {
1162 return $current;
1163 }
1164
1165 $change = $this->{"compare{$type}Definition"}($current, $previous);
1166
1167 if ($previous['type'] != $type) {
1168 $change['type'] = true;
1169 }
1170
1171 $previous_notnull = empty($previous['notnull']) ? false : $previous['notnull'];
1172 $notnull = empty($current['notnull']) ? false : $current['notnull'];
1173 if ($previous_notnull !== $notnull) {
1174 $change['notnull'] = true;
1175 }
1176
1177 $alt = $previous_notnull ? '' : null;
1178 $previous_default = array_key_exists(
1179 'default',
1180 $previous
1181 ) ? $previous['default'] : $alt;
1182 $alt = $notnull ? '' : null;
1183 $default = array_key_exists('default', $current) ? $current['default'] : $alt;
1184 if ($previous_default !== $default) {
1185 $change['default'] = true;
1186 }
1187
1188 return $change;
1189 }
1190
1194 public function quote($value, ?string $type = null, bool $quote = true, bool $escape_wildcards = false): string
1195 {
1196 return $this->getDBInstance()->quote($value, $type ?? '');
1197 }
1198
1204 public function writeLOBToFile($lob, string $file): bool
1205 {
1206 $db = $this->getDBInstance();
1207
1208 if (preg_match('/^(\w+:\/\/)(.*)$/', $file, $match) && $match[1] === 'file://') {
1209 $file = $match[2];
1210 }
1211
1212 $fp = @fopen($file, 'wb');
1213 while (!@feof($lob)) {
1214 $result = @fread($lob, $db->options['lob_buffer_length']);
1215 $read = strlen($result);
1216 if (@fwrite($fp, $result, $read) !== $read) {
1217 @fclose($fp);
1218
1219 throw new ilDatabaseException('could not write to the output file');
1220 }
1221 }
1222 @fclose($fp);
1223
1224 return true;
1225 }
1226
1230 public function destroyLOB($lob): bool
1231 {
1232 $lob_data = stream_get_meta_data($lob);
1233 $lob_index = $lob_data['wrapper_data']->lob_index;
1234 fclose($lob);
1235 if (isset($this->lobs[$lob_index])) {
1236 unset($this->lobs[$lob_index]);
1237 }
1238
1239 return true;
1240 }
1241
1242
1246 public function matchPattern(array $pattern, $operator = null, $field = null): string
1247 {
1248 $db = $this->getDBInstance();
1249
1250 $match = '';
1251 if (!is_null($operator)) {
1252 $operator = strtoupper((string) $operator);
1253 switch ($operator) {
1254 // case insensitive
1255 case 'ILIKE':
1256 if (is_null($field)) {
1257 throw new ilDatabaseException('case insensitive LIKE matching requires passing the field name');
1258 }
1259 $db->loadModule('Function');
1260 $match = $db->lower($field) . ' LIKE ';
1261 break;
1262 // case sensitive
1263 case 'LIKE':
1264 $match = is_null($field) ? 'LIKE ' : $field . ' LIKE ';
1265 break;
1266 default:
1267 throw new ilDatabaseException('not a supported operator type:' . $operator);
1268 }
1269 }
1270 $match .= "'";
1271 foreach ($pattern as $key => $value) {
1272 if ($key % 2 !== 0) {
1273 $match .= $value;
1274 } else {
1275 if ($operator === 'ILIKE') {
1276 $value = strtolower((string) $value);
1277 }
1278 // @Todo: ilDBPdo::escape & escapePattern do nothing, probably quote instead.
1279 $escaped = $db->escape($value);
1280 $match .= $db->escapePattern($escaped);
1281 }
1282 }
1283 $match .= "'";
1284 $match .= $this->patternEscapeString();
1285
1286 return $match;
1287 }
1288
1289 public function patternEscapeString(): string
1290 {
1291 return '';
1292 }
1293
1297 public function mapNativeDatatype(array $field)
1298 {
1299 $db = $this->getDBInstance();
1300 $db_type = strtok($field['type'], '(), ');
1301 if (!empty($db->options['nativetype_map_callback'][$db_type])) {
1302 return call_user_func_array($db->options['nativetype_map_callback'][$db_type], [$db, $field]);
1303 }
1304
1305 return $this->mapNativeDatatypeInternal($field);
1306 }
1307
1311 public function mapPrepareDatatype(string $type)
1312 {
1313 $db = $this->getDBInstance();
1314
1315 if (!empty($db->options['datatype_map'][$type])) {
1316 $type = $db->options['datatype_map'][$type];
1317 if (!empty($db->options['datatype_map_callback'][$type])) {
1318 $parameter = ['type' => $type];
1319
1320 return call_user_func_array(
1321 $db->options['datatype_map_callback'][$type],
1322 [&$db, __FUNCTION__, $parameter]
1323 );
1324 }
1325 }
1326
1327 return $type;
1328 }
1329
1330 #[\Override]
1331 public function getTypeDeclaration(array $field): string
1332 {
1333 $db = $this->getDBInstance();
1334
1335 switch ($field['type']) {
1336 case 'text':
1337 if (empty($field['length']) && array_key_exists('default', $field)) {
1338 $field['length'] = $db->varchar_max_length ?? null;
1339 }
1340 $length = empty($field['length']) ? false : $field['length'];
1341 $fixed = empty($field['fixed']) ? false : $field['fixed'];
1342 if ($fixed) {
1343 return $length ? 'CHAR(' . $length . ')' : 'CHAR(255)';
1344 }
1345 return $length ? 'VARCHAR(' . $length . ')' : 'TEXT';
1346
1347 case 'clob':
1348 if (!empty($field['length'])) {
1349 $length = $field['length'];
1350 if ($length <= 255) {
1351 return 'TINYTEXT';
1352 }
1353
1354 if ($length <= 65532) {
1355 return 'TEXT';
1356 }
1357
1358 if ($length <= 16_777_215) {
1359 return 'MEDIUMTEXT';
1360 }
1361 }
1362
1363 return 'LONGTEXT';
1364 case 'blob':
1365 if (!empty($field['length'])) {
1366 $length = $field['length'];
1367 if ($length <= 255) {
1368 return 'TINYBLOB';
1369 }
1370
1371 if ($length <= 65532) {
1372 return 'BLOB';
1373 }
1374
1375 if ($length <= 16_777_215) {
1376 return 'MEDIUMBLOB';
1377 }
1378 }
1379
1380 return 'LONGBLOB';
1381 case 'integer':
1382 if (!empty($field['length'])) {
1383 $length = $field['length'];
1384 if ($length <= 1) {
1385 return 'TINYINT';
1386 }
1387
1388 if ($length === 2) {
1389 return 'SMALLINT';
1390 }
1391
1392 if ($length === 3) {
1393 return 'MEDIUMINT';
1394 }
1395
1396 if ($length === 4) {
1397 return 'INT';
1398 }
1399
1400 if ($length > 4) {
1401 return 'BIGINT';
1402 }
1403 }
1404
1405 return 'INT';
1406 case 'boolean':
1407 return 'TINYINT(1)';
1408 case 'date':
1409 return 'DATE';
1410 case 'time':
1411 return 'TIME';
1412 case 'timestamp':
1413 return 'DATETIME';
1414 case 'float':
1415 return 'DOUBLE';
1416 case 'decimal':
1417 $length = empty($field['length']) ? 18 : $field['length'];
1418 // @Todo: Change property access to method call.
1419 $scale = empty($field['scale']) ? $db->options['decimal_places'] : $field['scale'];
1420
1421 return 'DECIMAL(' . $length . ',' . $scale . ')';
1422 }
1423
1424 return '';
1425 }
1426
1430 private function mapNativeDatatypeInternal(array $field): array
1431 {
1432 $db_type = strtolower((string) $field['type']);
1433 $db_type = strtok($db_type, '(), ');
1434 if ($db_type === 'national') {
1435 $db_type = strtok('(), ');
1436 }
1437 if (!empty($field['length'])) {
1438 $length = strtok($field['length'], ', ');
1439 $decimal = strtok(', ');
1440 } else {
1441 $length = strtok('(), ');
1442 $decimal = strtok('(), ');
1443 }
1444 $type = [];
1445 $unsigned = $fixed = null;
1446 switch ($db_type) {
1447 case 'tinyint':
1448 $type[] = 'integer';
1449 $type[] = 'boolean';
1450 if (preg_match('/^(is|has)/', (string) $field['name'])) {
1451 $type = array_reverse($type);
1452 }
1453 $unsigned = preg_match('/ unsigned/i', (string) $field['type']);
1454 $length = 1;
1455 break;
1456 case 'smallint':
1457 $type[] = 'integer';
1458 $unsigned = preg_match('/ unsigned/i', (string) $field['type']);
1459 $length = 2;
1460 break;
1461 case 'mediumint':
1462 $type[] = 'integer';
1463 $unsigned = preg_match('/ unsigned/i', (string) $field['type']);
1464 $length = 3;
1465 break;
1466 case 'int':
1467 case 'integer':
1468 $type[] = 'integer';
1469 $unsigned = preg_match('/ unsigned/i', (string) $field['type']);
1470 $length = 4;
1471 break;
1472 case 'bigint':
1473 $type[] = 'integer';
1474 $unsigned = preg_match('/ unsigned/i', (string) $field['type']);
1475 $length = 8;
1476 break;
1477 case 'tinytext':
1478 case 'mediumtext':
1479 case 'longtext':
1480 case 'text':
1481 case 'varchar':
1482 $fixed = false;
1483 // no break
1484 case 'string':
1485 case 'char':
1486 $type[] = 'text';
1487 if ($length == '1') {
1488 $type[] = 'boolean';
1489 if (preg_match('/^(is|has)/', (string) $field['name'])) {
1490 $type = array_reverse($type);
1491 }
1492 } elseif (str_contains($db_type, 'text')) {
1493 $type[] = 'clob';
1494 if ($decimal === 'binary') {
1495 $type[] = 'blob';
1496 }
1497 }
1498 if ($fixed !== false) {
1499 $fixed = true;
1500 }
1501 break;
1502 case 'enum':
1503 $type[] = 'text';
1504 preg_match_all('/\'.+\'/U', (string) $field['type'], $matches);
1505 $length = 0;
1506 $fixed = false;
1507 if (is_array($matches)) {
1508 foreach ($matches[0] as $value) {
1509 $length = max($length, strlen($value) - 2);
1510 }
1511 if ($length == '1' && count($matches[0]) === 2) {
1512 $type[] = 'boolean';
1513 if (preg_match('/^(is|has)/', (string) $field['name'])) {
1514 $type = array_reverse($type);
1515 }
1516 }
1517 }
1518 $type[] = 'integer';
1519 // no break
1520 case 'set':
1521 $fixed = false;
1522 $type[] = 'text';
1523 $type[] = 'integer';
1524 break;
1525 case 'date':
1526 $type[] = 'date';
1527 $length = null;
1528 break;
1529 case 'datetime':
1530 case 'timestamp':
1531 $type[] = 'timestamp';
1532 $length = null;
1533 break;
1534 case 'time':
1535 $type[] = 'time';
1536 $length = null;
1537 break;
1538 case 'float':
1539 case 'double':
1540 case 'real':
1541 $type[] = 'float';
1542 $unsigned = preg_match('/ unsigned/i', (string) $field['type']);
1543 break;
1544 case 'unknown':
1545 case 'decimal':
1546 case 'numeric':
1547 $type[] = 'decimal';
1548 $unsigned = preg_match('/ unsigned/i', (string) $field['type']);
1549 if ($decimal !== false) {
1550 $length = $length . ',' . $decimal;
1551 }
1552 break;
1553 case 'tinyblob':
1554 case 'mediumblob':
1555 case 'longblob':
1556 case 'blob':
1557 $type[] = 'blob';
1558 $length = null;
1559 break;
1560 case 'binary':
1561 case 'varbinary':
1562 $type[] = 'blob';
1563 break;
1564 case 'year':
1565 $type[] = 'integer';
1566 $type[] = 'date';
1567 $length = null;
1568 break;
1569 default:
1570 throw new ilDatabaseException('unknown database attribute type: ' . $db_type);
1571 }
1572
1573 if ((int) $length <= 0) {
1574 $length = null;
1575 }
1576
1577 return [ $type, $length, $unsigned, $fixed ];
1578 }
1579}
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.