ILIAS  release_5-2 Revision v5.2.25-18-g3f80b828510
class.ilDBPdoFieldDefinition.php
Go to the documentation of this file.
1<?php
2
8abstract class ilDBPdoFieldDefinition {
9
11 const DEFAULT_TEXT_LENGTH = 4000;
12 const DEFINITION_COLUMN_NAME = "/^[a-z]+[_a-z0-9]*$/";
13 const DEFINITION_TABLE_NAME = "/^[a-z]+[_a-z0-9]*$/";
14 const INDEX_FORMAT = '%s_idx';
15 const SEQUENCE_COLUMNS_NAME = 'sequence';
16 const SEQUENCE_FORMAT = '%s_seq';
17 const T_BLOB = 'blob';
18 const T_CLOB = 'clob';
19 const T_DATE = 'date';
20 const T_DATETIME = 'datetime';
21 const T_FLOAT = 'float';
22 const T_INTEGER = 'integer';
23 const T_TEXT = 'text';
24 const T_TIME = 'time';
25 const T_TIMESTAMP = 'timestamp';
29 protected static $instance;
34 self::T_TEXT => array( 'length', 'notnull', 'default', 'fixed' ),
35 self::T_INTEGER => array( 'length', 'notnull', 'default', 'unsigned' ),
36 self::T_FLOAT => array( 'notnull', 'default' ),
37 self::T_DATE => array( 'notnull', 'default' ),
38 self::T_TIME => array( 'notnull', 'default' ),
39 self::T_TIMESTAMP => array( 'notnull', 'default' ),
40 self::T_CLOB => array( 'notnull', 'default' ),
41 self::T_BLOB => array( 'notnull', 'default' ),
42 );
46 public $allowed_attributes = array(
47 "text" => array( "length", "notnull", "default", "fixed" ),
48 "integer" => array( "length", "notnull", "default", "unsigned" ),
49 "float" => array( "notnull", "default" ),
50 "date" => array( "notnull", "default" ),
51 "time" => array( "notnull", "default" ),
52 "timestamp" => array( "notnull", "default" ),
53 "clob" => array( "length", "notnull", "default" ),
54 "blob" => array( "length", "notnull", "default" ),
55 );
59 protected $db_instance;
63 protected $max_length = array(
64 self::T_INTEGER => array( 1, 2, 3, 4, 8 ),
65 self::T_TEXT => 4000,
66 );
70 protected $available_types = array(
71 self::T_TEXT,
72 self::T_INTEGER,
73 self::T_FLOAT,
74 self::T_DATE,
75 self::T_TIME,
76 self::T_TIMESTAMP,
77 self::T_CLOB,
78 self::T_BLOB,
79 );
83 protected $reserved_mysql = array(
84 "ACCESSIBLE",
85 "ADD",
86 "ALL",
87 "ALTER",
88 "ANALYZE",
89 "AND",
90 "AS",
91 "ASC",
92 "ASENSITIVE",
93 "BEFORE",
94 "BETWEEN",
95 "BIGINT",
96 "BINARY",
97 "BLOB",
98 "BOTH",
99 "BY",
100 "CALL",
101 "CASCADE",
102 "CASE",
103 "CHANGE",
104 "CHAR",
105 "CHARACTER",
106 "CHECK",
107 "COLLATE",
108 "COLUMN",
109 "CONDITION",
110 "CONSTRAINT",
111 "CONTINUE",
112 "CONVERT",
113 "CREATE",
114 "CROSS",
115 "CURRENT_DATE",
116 "CURRENT_TIME",
117 "CURRENT_TIMESTAMP",
118 "CURRENT_USER",
119 "CURSOR",
120 "DATABASE",
121 "DATABASES",
122 "DAY_HOUR",
123 "DAY_MICROSECOND",
124 "DAY_MINUTE",
125 "DAY_SECOND",
126 "DEC",
127 "DECIMAL",
128 "DECLARE",
129 "DEFAULT",
130 "DELAYED",
131 "DELETE",
132 "DESC",
133 "DESCRIBE",
134 "DETERMINISTIC",
135 "DISTINCT",
136 "DISTINCTROW",
137 "DIV",
138 "DOUBLE",
139 "DROP",
140 "DUAL",
141 "EACH",
142 "ELSE",
143 "ELSEIF",
144 "ENCLOSED",
145 "ESCAPED",
146 "EXISTS",
147 "EXIT",
148 "EXPLAIN",
149 "FALSE",
150 "FETCH",
151 "FLOAT",
152 "FLOAT4",
153 "FLOAT8",
154 "FOR",
155 "FORCE",
156 "FOREIGN",
157 "FROM",
158 "FULLTEXT",
159 "GRANT",
160 "GROUP",
161 "HAVING",
162 "HIGH_PRIORITY",
163 "HOUR_MICROSECOND",
164 "HOUR_MINUTE",
165 "HOUR_SECOND",
166 "IF",
167 "IGNORE",
168 "IN",
169 "INDEX",
170 "INFILE",
171 "INNER",
172 "INOUT",
173 "INSENSITIVE",
174 "INSERT",
175 "INT",
176 "INT1",
177 "INT2",
178 "INT3",
179 "INT4",
180 "INT8",
181 "INTEGER",
182 "INTERVAL",
183 "INTO",
184 "IS",
185 "ITERATE",
186 "JOIN",
187 "KEY",
188 "KEYS",
189 "KILL",
190 "LEADING",
191 "LEAVE",
192 "LEFT",
193 "LIKE",
194 "LIMIT",
195 "LINEAR",
196 "LINES",
197 "LOAD",
198 "LOCALTIME",
199 "LOCALTIMESTAMP",
200 "LOCK",
201 "LONG",
202 "LONGBLOB",
203 "LONGTEXT",
204 "LOOP",
205 "LOW_PRIORITY",
206 "MASTER_SSL_VERIFY_SERVER_CERT",
207 "MATCH",
208 "MEDIUMBLOB",
209 "MEDIUMINT",
210 "MEDIUMTEXT",
211 "MIDDLEINT",
212 "MINUTE_MICROSECOND",
213 "MINUTE_SECOND",
214 "MOD",
215 "MODIFIES",
216 "NATURAL",
217 "NOT",
218 "NO_WRITE_TO_BINLOG",
219 "NULL",
220 "NUMERIC",
221 "ON",
222 "OPTIMIZE",
223 "OPTION",
224 "OPTIONALLY",
225 "OR",
226 "ORDER",
227 "OUT",
228 "OUTER",
229 "OUTFILE",
230 "PRECISION",
231 "PRIMARY",
232 "PROCEDURE",
233 "PURGE",
234 "RANGE",
235 "READ",
236 "READS",
237 "READ_WRITE",
238 "REAL",
239 "REFERENCES",
240 "REGEXP",
241 "RELEASE",
242 "RENAME",
243 "REPEAT",
244 "REPLACE",
245 "REQUIRE",
246 "RESTRICT",
247 "RETURN",
248 "REVOKE",
249 "RIGHT",
250 "RLIKE",
251 "SCHEMA",
252 "SCHEMAS",
253 "SECOND_MICROSECOND",
254 "SELECT",
255 "SENSITIVE",
256 "SEPARATOR",
257 "SET",
258 "SHOW",
259 "SMALLINT",
260 "SPATIAL",
261 "SPECIFIC",
262 "SQL",
263 "SQLEXCEPTION",
264 "SQLSTATE",
265 "SQLWARNING",
266 "SQL_BIG_RESULT",
267 "SQL_CALC_FOUND_ROWS",
268 "SQL_SMALL_RESULT",
269 "SSL",
270 "STARTING",
271 "STRAIGHT_JOIN",
272 "TABLE",
273 "TERMINATED",
274 "THEN",
275 "TINYBLOB",
276 "TINYINT",
277 "TINYTEXT",
278 "TO",
279 "TRAILING",
280 "TRIGGER",
281 "TRUE",
282 "UNDO",
283 "UNION",
284 "UNIQUE",
285 "UNLOCK",
286 "UNSIGNED",
287 "UPDATE",
288 "USAGE",
289 "USE",
290 "USING",
291 "UTC_DATE",
292 "UTC_TIME",
293 "UTC_TIMESTAMP",
294 "VALUES",
295 "VARBINARY",
296 "VARCHAR",
297 "VARCHARACTER",
298 "VARYING",
299 "WHEN",
300 "WHERE",
301 "WHILE",
302 "WITH",
303 "WRITE",
304 "XOR",
305 "YEAR_MONTH",
306 "ZEROFILL",
307 );
311 protected $reserved_postgres = array(
312 "ALL",
313 "ANALYSE",
314 "ANALYZE",
315 "AND",
316 "ANY",
317 "ARRAY",
318 "AS",
319 "ASC",
320 "ASYMMETRIC",
321 "AUTHORIZATION",
322 "BETWEEN",
323 "BINARY",
324 "BOTH",
325 "CASE",
326 "CAST",
327 "CHECK",
328 "COLLATE",
329 "COLUMN",
330 "CONSTRAINT",
331 "CREATE",
332 "CROSS",
333 "CURRENT_DATE",
334 "CURRENT_ROLE",
335 "CURRENT_TIME",
336 "CURRENT_TIMESTAMP",
337 "CURRENT_USER",
338 "DEFAULT",
339 "DEFERRABLE",
340 "DESC",
341 "DISTINCT",
342 "DO",
343 "ELSE",
344 "END",
345 "EXCEPT",
346 "FALSE",
347 "FOR",
348 "FOREIGN",
349 "FREEZE",
350 "FROM",
351 "FULL",
352 "GRANT",
353 "GROUP",
354 "HAVING",
355 "ILIKE",
356 "IN",
357 "INITIALLY",
358 "INNER",
359 "INTERSECT",
360 "INTO",
361 "IS",
362 "ISNULL",
363 "JOIN",
364 "LEADING",
365 "LEFT",
366 "LIKE",
367 "LIMIT",
368 "LOCALTIME",
369 "LOCALTIMESTAMP",
370 "NATURAL",
371 "NEW",
372 "NOT",
373 "NOTNULL",
374 "NULL",
375 "OFF",
376 "OFFSET",
377 "OLD",
378 "ON",
379 "ONLY",
380 "OR",
381 "ORDER",
382 "OUTER",
383 "OVERLAPS",
384 "PLACING",
385 "PRIMARY",
386 "REFERENCES",
387 "RETURNING",
388 "RIGHT",
389 "SELECT",
390 "SESSION_USER",
391 "SIMILAR",
392 "SOME",
393 "SYMMETRIC",
394 "TABLE",
395 "THEN",
396 "TO",
397 "TRAILING",
398 "TRUE",
399 "UNION",
400 "UNIQUE",
401 "USER",
402 "USING",
403 "VERBOSE",
404 "WHEN",
405 "WHERE",
406 "WITH",
407 );
411 protected $reserved_oracle = array(
412 "ACCESS",
413 "ADD",
414 "ALL",
415 "ALTER",
416 "AND",
417 "ANY",
418 "AS",
419 "ASC",
420 "AUDIT",
421 "BETWEEN",
422 "BY",
423 "CHAR",
424 "CHECK",
425 "CLUSTER",
426 "COLUMN",
427 "COMMENT",
428 "COMPRESS",
429 "CONNECT",
430 "CREATE",
431 "CURRENT",
432 "DATE",
433 "DECIMAL",
434 "DEFAULT",
435 "DELETE",
436 "DESC",
437 "DISTINCT",
438 "DROP",
439 "ELSE",
440 "EXCLUSIVE",
441 "EXISTS",
442 "FILE",
443 "FLOAT",
444 "FOR",
445 "FROM",
446 "GRANT",
447 "GROUP",
448 "HAVING",
449 "IDENTIFIED",
450 "IMMEDIATE",
451 "IN",
452 "INCREMENT",
453 "INDEX",
454 "INITIAL",
455 "INSERT",
456 "INTEGER",
457 "INTERSECT",
458 "INTO",
459 "IS",
460 "LEVEL",
461 "LIKE",
462 "LOCK",
463 "LONG",
464 "MAXEXTENTS",
465 "MINUS",
466 "MLSLABEL",
467 "MODE",
468 "MODIFY",
469 "NOAUDIT",
470 "NOCOMPRESS",
471 "NOT",
472 "NOWAIT",
473 "NULL",
474 "NUMBER",
475 "OF",
476 "OFFLINE",
477 "ON",
478 "ONLINE",
479 "OPTION",
480 "OR",
481 "ORDER",
482 "PCTFREE",
483 "PRIOR",
484 "PRIVILEGES",
485 "PUBLIC",
486 "RAW",
487 "RENAME",
488 "RESOURCE",
489 "REVOKE",
490 "ROW",
491 "ROWID",
492 "ROWNUM",
493 "ROWS",
494 "SELECT",
495 "SESSION",
496 "SET",
497 "SHARE",
498 "SIZE",
499 "SMALLINT",
500 "START",
501 "SUCCESSFUL",
502 "SYNONYM",
503 "SYSDATE",
504 "TABLE",
505 "THEN",
506 "TO",
507 "TRIGGER",
508 "UID",
509 "UNION",
510 "UNIQUE",
511 "UPDATE",
512 "USER",
513 "VALIDATE",
514 "VALUES",
515 "VARCHAR",
516 "VARCHAR2",
517 "VIEW",
518 "WHENEVER",
519 "WHERE",
520 "WITH",
521 );
525 protected $query_utils;
526
527
533 public function __construct(\ilDBInterface $ilDBInterface) {
534 $this->db_instance = $ilDBInterface;
535 }
536
537
541 protected function getQueryUtils() {
542 if (!$this->query_utils) {
543 $this->query_utils = new ilMySQLQueryUtils($this->db_instance);
544 }
545
546 return $this->query_utils;
547 }
548
549
553 protected $valid_default_values = array(
554 'text' => '',
555 'boolean' => true,
556 'integer' => 0,
557 'decimal' => 0.0,
558 'float' => 0.0,
559 'timestamp' => '1970-01-01 00:00:00',
560 'time' => '00:00:00',
561 'date' => '1970-01-01',
562 'clob' => '',
563 'blob' => '',
564 );
565
566
572 public function checkTableName($table_name) {
573 if (!preg_match(self::DEFINITION_TABLE_NAME, $table_name)) {
574 throw new ilDatabaseException('Table name must only contain _a-z0-9 and must start with a-z.');
575 }
576
577 if ($this->isReserved($table_name)) {
578 throw new ilDatabaseException("Invalid table name '" . $table_name . "' (Reserved Word).");
579 }
580
581 if (strtolower(substr($table_name, 0, 4)) == "sys_") {
582 throw new ilDatabaseException("Invalid table name '" . $table_name . "'. Name must not start with 'sys_'.");
583 }
584
585 if (strlen($table_name) > 22) {
586 throw new ilDatabaseException("Invalid table name '" . $table_name . "'. Maximum table identifer length is 22 bytes.");
587 }
588
589 return true;
590 }
591
592
597 public function isReserved($table_name) {
598 return in_array(strtoupper($table_name), $this->getAllReserved());
599 }
600
601
605 public function getAllReserved() {
606 return array_merge($this->getReservedMysql(), $this->getReservedOracle(), $this->getReservedPostgres());
607 }
608
609
613 public function getReservedMysql() {
615 }
616
617
622 $this->reserved_mysql = $reserved_mysql;
623 }
624
625
629 public function getReservedPostgres() {
631 }
632
633
638 $this->reserved_postgres = $reserved_postgres;
639 }
640
641
645 public function getReservedOracle() {
647 }
648
649
654 $this->reserved_oracle = $reserved_oracle;
655 }
656
657
663 public function checkColumnName($column_name) {
664 if (!preg_match("/^[a-z]+[_a-z0-9]*$/", $column_name)) {
665 throw new ilDatabaseException("Invalid column name '" . $column_name
666 . "'. Column name must only contain _a-z0-9 and must start with a-z.");
667 }
668
669 if ($this->isReserved($column_name)) {
670 throw new ilDatabaseException("Invalid column name '" . $column_name . "' (Reserved Word).");
671 }
672
673 if (strtolower(substr($column_name, 0, 4)) == "sys_") {
674 throw new ilDatabaseException("Invalid column name '" . $column_name . "'. Name must not start with 'sys_'.");
675 }
676
677 if (strlen($column_name) > 30) {
678 throw new ilDatabaseException("Invalid column name '" . $column_name . "'. Maximum column identifer length is 30 bytes.");
679 }
680
681 return true;
682 }
683
684
690 public function checkIndexName($a_name) {
691 if (!preg_match("/^[a-z]+[_a-z0-9]*$/", $a_name)) {
692 throw new ilDatabaseException("Invalid column name '" . $a_name . "'. Column name must only contain _a-z0-9 and must start with a-z.");
693 }
694
695 if ($this->isReserved($a_name)) {
696 throw new ilDatabaseException("Invalid column name '" . $a_name . "' (Reserved Word).");
697 }
698
699 if (strlen($a_name) > 3) {
700 throw new ilDatabaseException("Invalid index name '" . $a_name . "'. Maximum index identifer length is 3 bytes.");
701 }
702
703 return true;
704 }
705
706
712 public function checkColumnDefinition($a_def) {
713 // check valid type
714 if (!in_array($a_def["type"], $this->getAvailableTypes())) {
715 switch ($a_def["type"]) {
716 case "boolean":
717 throw new ilDatabaseException("Invalid column type '" . $a_def["type"] . "'. Use integer(1) instead.");
718 break;
719
720 case "decimal":
721 throw new ilDatabaseException("Invalid column type '" . $a_def["type"] . "'. Use float or integer instead.");
722 break;
723
724 default:
725 throw new ilDatabaseException("Invalid column type '" . $a_def["type"] . "'. Allowed types are: "
726 . implode(', ', $this->getAvailableTypes()));
727 }
728 }
729
730 // check used attributes
732 foreach ($a_def as $k => $v) {
733 if ($k != "type" && !in_array($k, $allowed_attributes[$a_def["type"]])) {
734 throw new ilDatabaseException("Attribute '" . $k . "' is not allowed for column type '" . $a_def["type"] . "'.");
735 }
736 }
737
738 // type specific checks
739 $max_length = $this->getMaxLength();
740 switch ($a_def["type"]) {
741 case self::T_TEXT:
742 if ($a_def["length"] < 1 || $a_def["length"] > $max_length[self::T_TEXT]) {
743 if (isset($a_def["length"])) {
744 throw new ilDatabaseException("Invalid length '" . $a_def["length"] . "' for type text." . " Length must be >=1 and <= "
745 . $max_length[self::T_TEXT] . ".");
746 }
747 }
748 break;
749
750 case self::T_INTEGER:
751 if (!in_array($a_def["length"], $max_length[self::T_INTEGER])) {
752 if (isset($a_def["length"])) {
753 throw new ilDatabaseException("Invalid length '" . $a_def["length"] . "' for type integer." . " Length must be "
754 . implode(', ', $max_length[self::T_INTEGER]) . " (bytes).");
755 }
756 }
757 if ($a_def["unsigned"]) {
758 throw new ilDatabaseException("Unsigned attribut must not be true for type integer.");
759 }
760 break;
761 }
762
763 return true;
764 }
765
766
772 public function isAllowedAttribute($attribute, $type) {
773 return in_array($attribute, $this->allowed_attributes[$type]);
774 }
775
776
780 public function getAvailableTypes() {
782 }
783
784
789 $this->available_types = $available_types;
790 }
791
792
796 public function getAllowedAttributes() {
798 }
799
800
805 $this->allowed_attributes = $allowed_attributes;
806 }
807
808
812 public function getMaxLength() {
813 return $this->max_length;
814 }
815
816
820 public function setMaxLength($max_length) {
821 $this->max_length = $max_length;
822 }
823
824
828 protected function getDBInstance() {
829 return $this->db_instance;
830 }
831
832
836 public function getValidTypes() {
838 $db = $this->getDBInstance();
839
840 if (!empty($db->options['datatype_map'])) {
841 foreach ($db->options['datatype_map'] as $type => $mapped_type) {
842 if (array_key_exists($mapped_type, $types)) {
843 $types[$type] = $types[$mapped_type];
844 } elseif (!empty($db->options['datatype_map_callback'][$type])) {
845 $parameter = array( 'type' => $type, 'mapped_type' => $mapped_type );
846 $default = call_user_func_array($db->options['datatype_map_callback'][$type], array( &$db, __FUNCTION__, $parameter ));
847 $types[$type] = $default;
848 }
849 }
850 }
851
852 return $types;
853 }
854
855
861 protected function checkResultTypes($types) {
862 $types = is_array($types) ? $types : array( $types );
863 foreach ($types as $key => $type) {
864 if (!isset($this->valid_default_values[$type])) {
865 $db = $this->getDBInstance();
866 if (empty($db->options['datatype_map'][$type])) {
867 throw new ilDatabaseException($type . ' for ' . $key . ' is not a supported column type');
868 }
869 }
870 }
871
872 return $types;
873 }
874
875
883 protected function baseConvertResult($value, $type, $rtrim = true) {
884 switch ($type) {
885 case 'text':
886 if ($rtrim) {
887 $value = rtrim($value);
888 }
889
890 return $value;
891 case 'integer':
892 return intval($value);
893 case 'boolean':
894 return !empty($value);
895 case 'decimal':
896 return $value;
897 case 'float':
898 return doubleval($value);
899 case 'date':
900 return $value;
901 case 'time':
902 return $value;
903 case 'timestamp':
904 return $value;
905 case 'clob':
906 case 'blob':
907 $this->lobs[] = array(
908 'buffer' => null,
909 'position' => 0,
910 'lob_index' => null,
911 'endOfLOB' => false,
912 'resource' => $value,
913 'value' => null,
914 'loaded' => false,
915 );
916 end($this->lobs);
917 $lob_index = key($this->lobs);
918 $this->lobs[$lob_index]['lob_index'] = $lob_index;
919
920 return fopen('MDB2LOB://' . $lob_index . '@' . $this->db_index, 'r+');
921 }
922
923 throw new ilDatabaseException('attempt to convert result value to an unknown type :' . $type);
924 }
925
926
934 public function convertResult($value, $type, $rtrim = true) {
935 if (is_null($value)) {
936 return null;
937 }
938 $db = $this->getDBInstance();
939
940 if (!empty($db->options['datatype_map'][$type])) {
941 $type = $db->options['datatype_map'][$type];
942 if (!empty($db->options['datatype_map_callback'][$type])) {
943 $parameter = array( 'type' => $type, 'value' => $value, 'rtrim' => $rtrim );
944
945 return call_user_func_array($db->options['datatype_map_callback'][$type], array( &$db, __FUNCTION__, $parameter ));
946 }
947 }
948
949 return $this->baseConvertResult($value, $type, $rtrim);
950 }
951
952
959 public function convertResultRow($types, $row, $rtrim = true) {
960 $types = $this->sortResultFieldTypes(array_keys($row), $types);
961 foreach ($row as $key => $value) {
962 if (empty($types[$key])) {
963 continue;
964 }
965 $value = $this->convertResult($row[$key], $types[$key], $rtrim);
966
967 $row[$key] = $value;
968 }
969
970 return $row;
971 }
972
973 // }}}
974 // {{{ _sortResultFieldTypes()
975
981 protected function sortResultFieldTypes($columns, $types) {
982 $n_cols = count($columns);
983 $n_types = count($types);
984 if ($n_cols > $n_types) {
985 for ($i = $n_cols - $n_types; $i >= 0; $i --) {
986 $types[] = null;
987 }
988 }
989 $sorted_types = array();
990 foreach ($columns as $col) {
991 $sorted_types[$col] = null;
992 }
993 foreach ($types as $name => $type) {
994 if (array_key_exists($name, $sorted_types)) {
995 $sorted_types[$name] = $type;
996 unset($types[$name]);
997 }
998 }
999 // if there are left types in the array, fill the null values of the
1000 // sorted array with them, in order.
1001 if (count($types)) {
1002 reset($types);
1003 foreach (array_keys($sorted_types) as $k) {
1004 if (is_null($sorted_types[$k])) {
1005 $sorted_types[$k] = current($types);
1006 next($types);
1007 }
1008 }
1009 }
1010
1011 return $sorted_types;
1012 }
1013
1014
1022 public function getDeclaration($type, $name, $field) {
1023 $db = $this->getDBInstance();
1024
1025 if (!empty($db->options['datatype_map'][$type])) {
1026 $type = $db->options['datatype_map'][$type];
1027 if (!empty($db->options['datatype_map_callback'][$type])) {
1028 $parameter = array( 'type' => $type, 'name' => $name, 'field' => $field );
1029
1030 return call_user_func_array($db->options['datatype_map_callback'][$type], array( &$db, __FUNCTION__, $parameter ));
1031 }
1032 $field['type'] = $type;
1033 }
1034
1035 if (!method_exists($this, "get{$type}Declaration")) {
1036 throw new ilDatabaseException('type not defined: ' . $type);
1037 }
1038
1039 return $this->{"get{$type}Declaration"}($name, $field);
1040 }
1041
1042
1047 public function getTypeDeclaration($field) {
1048 $db = $this->getDBInstance();
1049
1050 switch ($field['type']) {
1051 case 'text':
1052 $length = !empty($field['length']) ? $field['length'] : $db->options['default_text_field_length'];
1053 $fixed = !empty($field['fixed']) ? $field['fixed'] : false;
1054
1055 return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(' . $db->options['default_text_field_length']
1056 . ')') : ($length ? 'VARCHAR(' . $length . ')' : 'TEXT');
1057 case 'clob':
1058 return 'TEXT';
1059 case 'blob':
1060 return 'TEXT';
1061 case 'integer':
1062 return 'INT';
1063 case 'boolean':
1064 return 'INT';
1065 case 'date':
1066 return 'CHAR (' . strlen('YYYY-MM-DD') . ')';
1067 case 'time':
1068 return 'CHAR (' . strlen('HH:MM:SS') . ')';
1069 case 'timestamp':
1070 return 'CHAR (' . strlen('YYYY-MM-DD HH:MM:SS') . ')';
1071 case 'float':
1072 return 'TEXT';
1073 case 'decimal':
1074 return 'TEXT';
1075 }
1076
1077 return '';
1078 }
1079
1080
1086 protected function getInternalDeclaration($name, $field) {
1087 $db = $this->getDBInstance();
1088
1089 $name = $db->quoteIdentifier($name, true);
1090 $declaration_options = $db->getFieldDefinition()->getDeclarationOptions($field);
1091
1092 return $name . ' ' . $this->getTypeDeclaration($field) . $declaration_options;
1093 }
1094
1095
1101 protected function getDeclarationOptions($field) {
1102 $charset = empty($field['charset']) ? '' : ' ' . $this->getCharsetFieldDeclaration($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 && ($db->options['portability'] & 32)
1117 ) {
1118 $field['default'] = ' ';
1119 }
1120 }
1121 $default = ' DEFAULT ' . $this->quote($field['default'], $field['type']);
1122 } elseif (empty($field['notnull'])) {
1123 $default = ' DEFAULT NULL';
1124 }
1125
1126 $notnull = empty($field['notnull']) ? '' : ' NOT NULL';
1127 // alex patch 28 Nov 2011 start
1128 if ($field['notnull'] === false) {
1129 $notnull = " NULL";
1130 }
1131 // alex patch 28 Nov 2011 end
1132
1133 $collation = empty($field['collation']) ? '' : ' ' . $this->getCollationFieldDeclaration($field['collation']);
1134
1135 return $charset . $default . $notnull . $collation;
1136 }
1137
1138
1143 protected function getCharsetFieldDeclaration($charset) {
1144 return '';
1145 }
1146
1147
1152 protected function getCollationFieldDeclaration($collation) {
1153 return '';
1154 }
1155
1156
1163 protected function getIntegerDeclaration($name, $field) {
1164 if (!empty($field['unsigned'])) {
1165 $db = $this->getDBInstance();
1166
1167 $db->warnings[] = "unsigned integer field \"$name\" is being declared as signed integer";
1168 }
1169
1170 return $this->getInternalDeclaration($name, $field);
1171 }
1172
1173
1180 protected function getTextDeclaration($name, $field) {
1181 return $this->getInternalDeclaration($name, $field);
1182 }
1183
1184
1190 protected function getCLOBDeclaration($name, $field) {
1191 $db = $this->getDBInstance();
1192
1193 $notnull = empty($field['notnull']) ? '' : ' NOT NULL';
1194 $name = $db->quoteIdentifier($name, true);
1195
1196 return $name . ' ' . $this->getTypeDeclaration($field) . $notnull;
1197 }
1198
1199
1205 protected function getBLOBDeclaration($name, $field) {
1206 $db = $this->getDBInstance();
1207
1208 $notnull = empty($field['notnull']) ? '' : ' NOT NULL';
1209 $name = $db->quoteIdentifier($name, true);
1210
1211 return $name . ' ' . $this->getTypeDeclaration($field) . $notnull;
1212 }
1213
1214
1220 protected function getBooleanDeclaration($name, $field) {
1221 return $this->getInternalDeclaration($name, $field);
1222 }
1223
1224
1230 protected function getDateDeclaration($name, $field) {
1231 return $this->getInternalDeclaration($name, $field);
1232 }
1233
1234
1240 protected function getTimestampDeclaration($name, $field) {
1241 return $this->getInternalDeclaration($name, $field);
1242 }
1243
1244
1250 protected function getTimeDeclaration($name, $field) {
1251 return $this->getInternalDeclaration($name, $field);
1252 }
1253
1254
1260 protected function getFloatDeclaration($name, $field) {
1261 return $this->getInternalDeclaration($name, $field);
1262 }
1263
1264
1270 protected function getDecimalDeclaration($name, $field) {
1271 return $this->getInternalDeclaration($name, $field);
1272 }
1273
1274
1281 public function compareDefinition($current, $previous) {
1282 $type = !empty($current['type']) ? $current['type'] : null;
1283
1284 if (!method_exists($this, "compare{$type}Definition")) {
1285 $db = $this->getDBInstance();
1286
1287 if (!empty($db->options['datatype_map_callback'][$type])) {
1288 $parameter = array( 'current' => $current, 'previous' => $previous );
1289 $change = call_user_func_array($db->options['datatype_map_callback'][$type], array( &$db, __FUNCTION__, $parameter ));
1290
1291 return $change;
1292 }
1293
1294 throw new ilDatabaseException('type "' . $current['type'] . '" is not yet supported');
1295 }
1296
1297 if (empty($previous['type']) || $previous['type'] != $type) {
1298 return $current;
1299 }
1300
1301 $change = $this->{"compare{$type}Definition"}($current, $previous);
1302
1303 if ($previous['type'] != $type) {
1304 $change['type'] = true;
1305 }
1306
1307 $previous_notnull = !empty($previous['notnull']) ? $previous['notnull'] : false;
1308 $notnull = !empty($current['notnull']) ? $current['notnull'] : false;
1309 if ($previous_notnull != $notnull) {
1310 $change['notnull'] = true;
1311 }
1312
1313 $previous_default = array_key_exists('default', $previous) ? $previous['default'] : ($previous_notnull ? '' : null);
1314 $default = array_key_exists('default', $current) ? $current['default'] : ($notnull ? '' : null);
1315 if ($previous_default !== $default) {
1316 $change['default'] = true;
1317 }
1318
1319 return $change;
1320 }
1321
1322
1328 protected function compareIntegerDefinition($current, $previous) {
1329 $change = array();
1330 $previous_unsigned = !empty($previous['unsigned']) ? $previous['unsigned'] : false;
1331 $unsigned = !empty($current['unsigned']) ? $current['unsigned'] : false;
1332 if ($previous_unsigned != $unsigned) {
1333 $change['unsigned'] = true;
1334 }
1335 $previous_autoincrement = !empty($previous['autoincrement']) ? $previous['autoincrement'] : false;
1336 $autoincrement = !empty($current['autoincrement']) ? $current['autoincrement'] : false;
1337 if ($previous_autoincrement != $autoincrement) {
1338 $change['autoincrement'] = true;
1339 }
1340
1341 return $change;
1342 }
1343
1344
1350 protected function compareTextDefinition($current, $previous) {
1351 $change = array();
1352 $previous_length = !empty($previous['length']) ? $previous['length'] : 0;
1353 $length = !empty($current['length']) ? $current['length'] : 0;
1354 if ($previous_length != $length) {
1355 $change['length'] = true;
1356 }
1357 $previous_fixed = !empty($previous['fixed']) ? $previous['fixed'] : 0;
1358 $fixed = !empty($current['fixed']) ? $current['fixed'] : 0;
1359 if ($previous_fixed != $fixed) {
1360 $change['fixed'] = true;
1361 }
1362
1363 return $change;
1364 }
1365
1366
1372 protected function compareCLOBDefinition($current, $previous) {
1373 return $this->compareTextDefinition($current, $previous);
1374 }
1375
1376
1382 protected function compareBLOBDefinition($current, $previous) {
1383 return $this->compareTextDefinition($current, $previous);
1384 }
1385
1386
1392 protected function compareDateDefinition($current, $previous) {
1393 return array();
1394 }
1395
1396
1402 protected function compareTimeDefinition($current, $previous) {
1403 return array();
1404 }
1405
1406
1412 protected function compareTimestampDefinition($current, $previous) {
1413 return array();
1414 }
1415
1416
1422 protected function compareBooleanDefinition($current, $previous) {
1423 return array();
1424 }
1425
1426
1432 protected function compareFloatDefinition($current, $previous) {
1433 return array();
1434 }
1435
1436
1442 protected function compareDecimalDefinition($current, $previous) {
1443 return array();
1444 }
1445
1446
1455 public function quote($value, $type = null, $quote = true, $escape_wildcards = false) {
1456 $db = $this->getDBInstance();
1457
1458 return $db->quote($value, $type);
1459
1460 if (is_null($value)
1461 || ($value === '' && $db->options['portability'] & MDB2_PORTABILITY_EMPTY_TO_NULL)
1462 ) {
1463 if (!$quote) {
1464 return null;
1465 }
1466
1467 return 'NULL';
1468 }
1469
1470 if (is_null($type)) {
1471 switch (gettype($value)) {
1472 case 'integer':
1473 $type = 'integer';
1474 break;
1475 case 'double':
1476 // todo: default to decimal as float is quite unusual
1477 // $type = 'float';
1478 $type = 'decimal';
1479 break;
1480 case 'boolean':
1481 $type = 'boolean';
1482 break;
1483 case 'array':
1484 $value = serialize($value);
1485 case 'object':
1486 $type = 'text';
1487 break;
1488 default:
1489 if (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/', $value)) {
1490 $type = 'timestamp';
1491 } elseif (preg_match('/^\d{2}:\d{2}$/', $value)) {
1492 $type = 'time';
1493 } elseif (preg_match('/^\d{4}-\d{2}-\d{2}$/', $value)) {
1494 $type = 'date';
1495 } else {
1496 $type = 'text';
1497 }
1498 break;
1499 }
1500 } elseif (!empty($db->options['datatype_map'][$type])) {
1501 $type = $db->options['datatype_map'][$type];
1502 if (!empty($db->options['datatype_map_callback'][$type])) {
1503 $parameter = array( 'type' => $type, 'value' => $value, 'quote' => $quote, 'escape_wildcards' => $escape_wildcards );
1504
1505 return call_user_func_array($db->options['datatype_map_callback'][$type], array( &$db, __FUNCTION__, $parameter ));
1506 }
1507 }
1508
1509 if (!method_exists($this, "quote{$type}")) {
1510 throw new ilDatabaseException('type not defined: ' . $type);
1511 }
1512 $value = $this->{"quote{$type}"}($value, $quote, $escape_wildcards);
1513 if ($quote && $escape_wildcards && $db->string_quoting['escape_pattern']
1514 && $db->string_quoting['escape'] !== $db->string_quoting['escape_pattern']
1515 ) {
1516 $value .= $this->patternEscapeString();
1517 }
1518
1519 return $value;
1520 }
1521
1522
1529 protected function quoteInteger($value, $quote, $escape_wildcards) {
1530 return (int)$value;
1531 }
1532
1533
1540 protected function quoteText($value, $quote, $escape_wildcards) {
1541 if (!$quote) {
1542 return $value;
1543 }
1544
1545 $db = $this->getDBInstance();
1546
1547 $value = $db->escape($value, $escape_wildcards);
1548
1549 return "'" . $value . "'";
1550 }
1551
1552
1557 protected function readFile($value) {
1558 $close = false;
1559 if (preg_match('/^(\w+:\/\/)(.*)$/', $value, $match)) {
1560 $close = true;
1561 if ($match[1] == 'file://') {
1562 $value = $match[2];
1563 }
1564 // do not try to open urls
1565 #$value = @fopen($value, 'r');
1566 }
1567
1568 if (is_resource($value)) {
1569 $db = $this->getDBInstance();
1570
1571 $fp = $value;
1572 $value = '';
1573 while (!@feof($fp)) {
1574 $value .= @fread($fp, $db->options['lob_buffer_length']);
1575 }
1576 if ($close) {
1577 @fclose($fp);
1578 }
1579 }
1580
1581 return $value;
1582 }
1583
1584
1591 protected function quoteLOB($value, $quote, $escape_wildcards) {
1592 $value = $this->readFile($value);
1593
1594 return $this->quoteText($value, $quote, $escape_wildcards);
1595 }
1596
1597
1604 protected function quoteCLOB($value, $quote, $escape_wildcards) {
1605 return $this->quoteLOB($value, $quote, $escape_wildcards);
1606 }
1607
1608
1615 protected function quoteBLOB($value, $quote, $escape_wildcards) {
1616 return $this->quoteLOB($value, $quote, $escape_wildcards);
1617 }
1618
1619
1626 protected function quoteBoolean($value, $quote, $escape_wildcards) {
1627 return ($value ? 1 : 0);
1628 }
1629
1630
1637 protected function quoteDate($value, $quote, $escape_wildcards) {
1638 if ($value === 'CURRENT_DATE') {
1639 $db = $this->getDBInstance();
1640
1641 return 'CURRENT_DATE';
1642 }
1643
1644 return $this->quoteText($value, $quote, $escape_wildcards);
1645 }
1646
1647
1654 protected function quoteTimestamp($value, $quote, $escape_wildcards) {
1655 if ($value === 'CURRENT_TIMESTAMP') {
1656 $db = $this->getDBInstance();
1657
1658 if (isset($db->function) && is_a($db->function, 'MDB2_Driver_Function_Common')) {
1659 return $db->function->now('timestamp');
1660 }
1661
1662 return 'CURRENT_TIMESTAMP';
1663 }
1664
1665 return $this->quoteText($value, $quote, $escape_wildcards);
1666 }
1667
1668
1675 protected function quoteTime($value, $quote, $escape_wildcards) {
1676 if ($value === 'CURRENT_TIME') {
1677 $db = $this->getDBInstance();
1678
1679 if (isset($db->function) && is_a($db->function, 'MDB2_Driver_Function_Common')) {
1680 return $db->function->now('time');
1681 }
1682
1683 return 'CURRENT_TIME';
1684 }
1685
1686 return $this->quoteText($value, $quote, $escape_wildcards);
1687 }
1688
1689
1696 protected function quoteFloat($value, $quote, $escape_wildcards) {
1697 if (preg_match('/^(.*)e([-+])(\d+)$/i', $value, $matches)) {
1698 $decimal = $this->quoteDecimal($matches[1], $quote, $escape_wildcards);
1699 $sign = $matches[2];
1700 $exponent = str_pad($matches[3], 2, '0', STR_PAD_LEFT);
1701 $value = $decimal . 'E' . $sign . $exponent;
1702 } else {
1703 $value = $this->quoteDecimal($value, $quote, $escape_wildcards);
1704 }
1705
1706 return $value;
1707 }
1708
1709
1716 protected function quoteDecimal($value, $quote, $escape_wildcards) {
1717 $value = (string)$value;
1718 $value = preg_replace('/[^\d\.,\-+eE]/', '', $value);
1719 if (preg_match('/[^.0-9]/', $value)) {
1720 if (strpos($value, ',')) {
1721 // 1000,00
1722 if (!strpos($value, '.')) {
1723 // convert the last "," to a "."
1724 $value = strrev(str_replace(',', '.', strrev($value)));
1725 // 1.000,00
1726 } elseif (strpos($value, '.') && strpos($value, '.') < strpos($value, ',')) {
1727 $value = str_replace('.', '', $value);
1728 // convert the last "," to a "."
1729 $value = strrev(str_replace(',', '.', strrev($value)));
1730 // 1,000.00
1731 } else {
1732 $value = str_replace(',', '', $value);
1733 }
1734 }
1735 }
1736
1737 return $value;
1738 }
1739
1740
1747 public function writeLOBToFile($lob, $file) {
1748 $db = $this->getDBInstance();
1749
1750 if (preg_match('/^(\w+:\/\/)(.*)$/', $file, $match)) {
1751 if ($match[1] == 'file://') {
1752 $file = $match[2];
1753 }
1754 }
1755
1756 $fp = @fopen($file, 'wb');
1757 while (!@feof($lob)) {
1758 $result = @fread($lob, $db->options['lob_buffer_length']);
1759 $read = strlen($result);
1760 if (@fwrite($fp, $result, $read) != $read) {
1761 @fclose($fp);
1762
1763 throw new ilDatabaseException('could not write to the output file');
1764 }
1765 }
1766 @fclose($fp);
1767
1768 return MDB2_OK;
1769 }
1770
1771
1776 protected function retrieveLOB(&$lob) {
1777 if (is_null($lob['value'])) {
1778 $lob['value'] = $lob['resource'];
1779 }
1780 $lob['loaded'] = true;
1781
1782 return MDB2_OK;
1783 }
1784
1785
1791 protected function readLOB($lob, $length) {
1792 return substr($lob['value'], $lob['position'], $length);
1793 }
1794
1795
1800 protected function endOfLOB($lob) {
1801 return $lob['endOfLOB'];
1802 }
1803
1804
1809 public function destroyLOB($lob) {
1810 $lob_data = stream_get_meta_data($lob);
1811 $lob_index = $lob_data['wrapper_data']->lob_index;
1812 fclose($lob);
1813 if (isset($this->lobs[$lob_index])) {
1814 $this->destroyLOBInternal($this->lobs[$lob_index]);
1815 unset($this->lobs[$lob_index]);
1816 }
1817
1818 return true;
1819 }
1820
1821
1826 protected function destroyLOBInternal(&$lob) {
1827 return true;
1828 }
1829
1830
1837 public function implodeArray($array, $type = false) {
1838 if (!is_array($array) || empty($array)) {
1839 return 'NULL';
1840 }
1841 if ($type) {
1842 foreach ($array as $value) {
1843 $return[] = $this->quote($value, $type);
1844 }
1845 } else {
1846 $return = $array;
1847 }
1848
1849 return implode(', ', $return);
1850 }
1851
1852
1860 public function matchPattern($pattern, $operator = null, $field = null) {
1861 $db = $this->getDBInstance();
1862
1863 $match = '';
1864 if (!is_null($operator)) {
1865 $operator = strtoupper($operator);
1866 switch ($operator) {
1867 // case insensitive
1868 case 'ILIKE':
1869 if (is_null($field)) {
1870 throw new ilDatabaseException('case insensitive LIKE matching requires passing the field name');
1871 }
1872 $db->loadModule('Function', null, true);
1873 $match = $db->function->lower($field) . ' LIKE ';
1874 break;
1875 // case sensitive
1876 case 'LIKE':
1877 $match = is_null($field) ? 'LIKE ' : $field . ' LIKE ';
1878 break;
1879 default:
1880 throw new ilDatabaseException('not a supported operator type:' . $operator);
1881 }
1882 }
1883 $match .= "'";
1884 foreach ($pattern as $key => $value) {
1885 if ($key % 2) {
1886 $match .= $value;
1887 } else {
1888 if ($operator === 'ILIKE') {
1889 $value = strtolower($value);
1890 }
1891 $escaped = $db->escape($value);
1892 $match .= $db->escapePattern($escaped);
1893 }
1894 }
1895 $match .= "'";
1896 $match .= $this->patternEscapeString();
1897
1898 return $match;
1899 }
1900
1901
1905 public function patternEscapeString() {
1906 return '';
1907 }
1908
1909
1914 public function mapNativeDatatype($field) {
1915 $db = $this->getDBInstance();
1916 $db_type = strtok($field['type'], '(), ');
1917 if (!empty($db->options['nativetype_map_callback'][$db_type])) {
1918 return call_user_func_array($db->options['nativetype_map_callback'][$db_type], array( $db, $field ));
1919 }
1920
1921 return $this->mapNativeDatatypeInternal($field);
1922 }
1923
1924
1930 abstract protected function mapNativeDatatypeInternal($field);
1931
1932
1937 public function mapPrepareDatatype($type) {
1938 $db = $this->getDBInstance();
1939
1940 if (!empty($db->options['datatype_map'][$type])) {
1941 $type = $db->options['datatype_map'][$type];
1942 if (!empty($db->options['datatype_map_callback'][$type])) {
1943 $parameter = array( 'type' => $type );
1944
1945 return call_user_func_array($db->options['datatype_map_callback'][$type], array( &$db, __FUNCTION__, $parameter ));
1946 }
1947 }
1948
1949 return $type;
1950 }
1951}
1952
$result
const MDB2_OK(!class_exists('PEAR'))
The method mapErrorCode in each MDB2_dbtype implementation maps native error codes to one of these.
Definition: MDB2.php:72
const MDB2_PORTABILITY_EMPTY_TO_NULL
Portability: convert empty values to null strings in data output by query*() and fetch*().
Definition: MDB2.php:203
if(! $in) $columns
Definition: Utf8Test.php:45
An exception for terminatinating execution or to throw for unit testing.
Class ilDBPdoFieldDefinition.
quoteText($value, $quote, $escape_wildcards)
quoteFloat($value, $quote, $escape_wildcards)
compareIntegerDefinition($current, $previous)
quoteDecimal($value, $quote, $escape_wildcards)
quoteTime($value, $quote, $escape_wildcards)
compareDecimalDefinition($current, $previous)
compareBLOBDefinition($current, $previous)
compareCLOBDefinition($current, $previous)
setAllowedAttributes($allowed_attributes)
__construct(\ilDBInterface $ilDBInterface)
ilDBPdoFieldDefinition constructor.
quote($value, $type=null, $quote=true, $escape_wildcards=false)
compareTextDefinition($current, $previous)
quoteBLOB($value, $quote, $escape_wildcards)
compareFloatDefinition($current, $previous)
quoteBoolean($value, $quote, $escape_wildcards)
compareTimestampDefinition($current, $previous)
convertResult($value, $type, $rtrim=true)
quoteLOB($value, $quote, $escape_wildcards)
compareDateDefinition($current, $previous)
mapNativeDatatypeInternal($field)
matchPattern($pattern, $operator=null, $field=null)
convertResultRow($types, $row, $rtrim=true)
quoteInteger($value, $quote, $escape_wildcards)
quoteTimestamp($value, $quote, $escape_wildcards)
baseConvertResult($value, $type, $rtrim=true)
quoteDate($value, $quote, $escape_wildcards)
compareBooleanDefinition($current, $previous)
compareTimeDefinition($current, $previous)
quoteCLOB($value, $quote, $escape_wildcards)
Class ilDatabaseException.
Class ilMySQLQueryUtils.
Interface ilDBInterface.
if(!file_exists("$old.txt")) if( $old===$new) if(file_exists("$new.txt")) $file