ILIAS  release_5-2 Revision v5.2.25-18-g3f80b828510
class.ilDBPdoFieldDefinition.php
Go to the documentation of this file.
1 <?php
2 
8 abstract 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  );
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  );
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 
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() {
614  return $this->reserved_mysql;
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() {
646  return $this->reserved_oracle;
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() {
781  return $this->available_types;
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 
compareCLOBDefinition($current, $previous)
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
compareTimestampDefinition($current, $previous)
setAllowedAttributes($allowed_attributes)
quoteDate($value, $quote, $escape_wildcards)
quote($value, $type=null, $quote=true, $escape_wildcards=false)
$result
baseConvertResult($value, $type, $rtrim=true)
quoteLOB($value, $quote, $escape_wildcards)
quoteInteger($value, $quote, $escape_wildcards)
const MDB2_PORTABILITY_EMPTY_TO_NULL
Portability: convert empty values to null strings in data output by query*() and fetch*().
Definition: MDB2.php:203
quoteTimestamp($value, $quote, $escape_wildcards)
compareBLOBDefinition($current, $previous)
Class ilDBPdoFieldDefinition.
quoteDecimal($value, $quote, $escape_wildcards)
compareBooleanDefinition($current, $previous)
compareFloatDefinition($current, $previous)
Add rich text string
The name of the decorator.
Class ilDatabaseException.
compareDateDefinition($current, $previous)
Interface ilDBInterface.
compareTextDefinition($current, $previous)
quoteFloat($value, $quote, $escape_wildcards)
matchPattern($pattern, $operator=null, $field=null)
Create styles array
The data for the language used.
quoteCLOB($value, $quote, $escape_wildcards)
quoteBoolean($value, $quote, $escape_wildcards)
quoteText($value, $quote, $escape_wildcards)
Class ilMySQLQueryUtils.
if(!file_exists("$old.txt")) if($old===$new) if(file_exists("$new.txt")) $file
quoteBLOB($value, $quote, $escape_wildcards)
compareIntegerDefinition($current, $previous)
quoteTime($value, $quote, $escape_wildcards)
__construct(\ilDBInterface $ilDBInterface)
ilDBPdoFieldDefinition constructor.
if(! $in) $columns
Definition: Utf8Test.php:45
convertResultRow($types, $row, $rtrim=true)
compareTimeDefinition($current, $previous)
mapNativeDatatypeInternal($field)
convertResult($value, $type, $rtrim=true)
compareDecimalDefinition($current, $previous)