19declare(strict_types=1);
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'],
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"],
66 self::T_INTEGER => [1, 2, 3, 4, 8],
166 "CONSTRAINT_CATALOG",
280 "GEOMETRYCOLLECTION",
369 "MASTER_AUTO_POSITION",
371 "MASTER_CONNECT_RETRY",
373 "MASTER_HEARTBEAT_PERIOD",
379 "MASTER_RETRY_COUNT",
387 "MASTER_SSL_CRLPATH",
389 "MASTER_SSL_VERIFY_SERVER_CERT",
390 "MASTER_TLS_VERSION",
394 "MAX_CONNECTIONS_PER_HOUR",
395 "MAX_QUERIES_PER_HOUR",
398 "MAX_STATEMENT_TIME",
399 "MAX_UPDATES_PER_HOUR",
400 "MAX_USER_CONNECTIONS",
412 "MINUTE_MICROSECOND",
441 "NO_WRITE_TO_BINLOG",
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",
559 "SECOND_MICROSECOND",
591 "SQL_AFTER_MTS_GAPS",
596 "SQL_CALC_FOUND_ROWS",
615 "STATS_SAMPLE_PAGES",
825 'timestamp' =>
'1970-01-01 00:00:00',
826 'time' =>
'00:00:00',
827 'date' =>
'1970-01-01',
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.');
842 throw new ilDatabaseException(
"Invalid table name '" . $table_name .
"' (Reserved Word).");
845 if (stripos($table_name,
"sys_") === 0) {
846 throw new ilDatabaseException(
"Invalid table name '" . $table_name .
"'. Name must not start with 'sys_'.");
849 if (strlen($table_name) > self::MAX_TABLE_IDENTIFIER_LENGTH) {
851 .
"'. Maximum table identifer length is " . self::MAX_TABLE_IDENTIFIER_LENGTH .
" bytes.");
892 if (!preg_match(
"/^[a-z]+[_a-z0-9]*$/", $column_name)) {
894 .
"'. Column name must only contain _a-z0-9 and must start with a-z.");
898 throw new ilDatabaseException(
"Invalid column name '" . $column_name .
"' (Reserved Word).");
901 if (stripos($column_name,
"sys_") === 0) {
902 throw new ilDatabaseException(
"Invalid column name '" . $column_name .
"'. Name must not start with 'sys_'.");
905 if (strlen($column_name) > 30) {
906 throw new ilDatabaseException(
"Invalid column name '" . $column_name .
"'. Maximum column identifer length is 30 bytes.");
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.");
925 if (strlen($a_name) > 3) {
926 throw new ilDatabaseException(
"Invalid index name '" . $a_name .
"'. Maximum index identifer length is 3 bytes.");
939 switch ($a_def[
"type"]) {
941 throw new ilDatabaseException(
"Invalid column type '" . $a_def[
"type"] .
"'. Use integer(1) instead.");
944 throw new ilDatabaseException(
"Invalid column type '" . $a_def[
"type"] .
"'. Use float or integer instead.");
947 throw new ilDatabaseException(
"Invalid column type '" . $a_def[
"type"] .
"'. Allowed types are: "
954 foreach (array_keys($a_def) as $k) {
956 throw new ilDatabaseException(
"Attribute '" . $k .
"' is not allowed for column type '" . $a_def[
"type"] .
"'.");
962 switch ($a_def[
"type"]) {
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 <= "
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).");
975 if ($a_def[
"unsigned"] ??
null) {
986 return in_array($attribute, $this->allowed_attributes[$type],
true);
1033 return $this->db_instance;
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]
1054 $types[$type] = $default;
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];
1077 return call_user_func_array(
1078 $db->options[
'datatype_map_callback'][$type],
1079 [&$db, __FUNCTION__, $parameter]
1082 $field[
'type'] = $type;
1085 if (!method_exists($this,
"get{$type}Declaration")) {
1089 return $this->{
"get{$type}Declaration"}($name, $field);
1099 $type = empty($current[
'type']) ? null : $current[
'type'];
1101 if (!method_exists($this,
"compare{$type}Definition")) {
1104 if (!empty($db->options[
'datatype_map_callback'][$type])) {
1105 $parameter = [
'current' => $current,
'previous' => $previous];
1107 return call_user_func_array(
1108 $db->options[
'datatype_map_callback'][$type],
1109 [&$db, __FUNCTION__, $parameter]
1116 if (empty($previous[
'type']) || $previous[
'type'] != $type) {
1120 $change = $this->{
"compare{$type}Definition"}($current, $previous);
1122 if ($previous[
'type'] != $type) {
1123 $change[
'type'] =
true;
1126 $previous_notnull = empty($previous[
'notnull']) ? false : $previous[
'notnull'];
1127 $notnull = empty($current[
'notnull']) ? false : $current[
'notnull'];
1128 if ($previous_notnull !== $notnull) {
1129 $change[
'notnull'] =
true;
1132 $alt = $previous_notnull ?
'' :
null;
1133 $previous_default = array_key_exists(
1136 ) ? $previous[
'default'] : $alt;
1137 $alt = $notnull ?
'' :
null;
1138 $default = array_key_exists(
'default', $current) ? $current[
'default'] : $alt;
1139 if ($previous_default !== $default) {
1140 $change[
'default'] =
true;
1149 public function quote($value, ?
string $type =
null,
bool $quote =
true,
bool $escape_wildcards =
false): string
1163 if (preg_match(
'/^(\w+:\/\/)(.*)$/', $file, $match) && $match[1] ===
'file://') {
1167 $fp = @fopen($file,
'wb');
1168 while (!@feof($lob)) {
1169 $result = @fread($lob, $db->options[
'lob_buffer_length']);
1170 $read = strlen($result);
1171 if (@fwrite($fp, $result, $read) !== $read) {
1187 $lob_data = stream_get_meta_data($lob);
1188 $lob_index = $lob_data[
'wrapper_data']->lob_index;
1190 if (isset($this->lobs[$lob_index])) {
1191 unset($this->lobs[$lob_index]);
1201 public function matchPattern(array $pattern, $operator =
null, $field =
null): string
1206 if (!is_null($operator)) {
1207 $operator = strtoupper((
string) $operator);
1208 switch ($operator) {
1211 if (is_null($field)) {
1212 throw new ilDatabaseException(
'case insensitive LIKE matching requires passing the field name');
1214 $db->loadModule(
'Function');
1215 $match = $db->lower($field) .
' LIKE ';
1219 $match = is_null($field) ?
'LIKE ' : $field .
' LIKE ';
1226 foreach ($pattern as $key => $value) {
1227 if ($key % 2 !== 0) {
1230 if ($operator ===
'ILIKE') {
1231 $value = strtolower((
string) $value);
1234 $escaped = $db->escape($value);
1235 $match .= $db->escapePattern($escaped);
1255 $db_type = strtok($field[
'type'],
'(), ');
1256 if (!empty($db->options[
'nativetype_map_callback'][$db_type])) {
1257 return call_user_func_array($db->options[
'nativetype_map_callback'][$db_type], [$db, $field]);
1270 if (!empty($db->options[
'datatype_map'][$type])) {
1271 $type = $db->options[
'datatype_map'][$type];
1272 if (!empty($db->options[
'datatype_map_callback'][$type])) {
1273 $parameter = [
'type' => $type];
1275 return call_user_func_array(
1276 $db->options[
'datatype_map_callback'][$type],
1277 [&$db, __FUNCTION__, $parameter]
1290 switch ($field[
'type']) {
1292 if (empty($field[
'length']) && array_key_exists(
'default', $field)) {
1293 $field[
'length'] = $db->varchar_max_length ??
null;
1295 $length = empty($field[
'length']) ? false : $field[
'length'];
1296 $fixed = empty($field[
'fixed']) ? false : $field[
'fixed'];
1298 return $length ?
'CHAR(' . $length .
')' :
'CHAR(255)';
1300 return $length ?
'VARCHAR(' . $length .
')' :
'TEXT';
1303 if (!empty($field[
'length'])) {
1304 $length = $field[
'length'];
1305 if ($length <= 255) {
1309 if ($length <= 65532) {
1313 if ($length <= 16_777_215) {
1314 return 'MEDIUMTEXT';
1320 if (!empty($field[
'length'])) {
1321 $length = $field[
'length'];
1322 if ($length <= 255) {
1326 if ($length <= 65532) {
1330 if ($length <= 16_777_215) {
1331 return 'MEDIUMBLOB';
1337 if (!empty($field[
'length'])) {
1338 $length = $field[
'length'];
1343 if ($length === 2) {
1347 if ($length === 3) {
1351 if ($length === 4) {
1362 return 'TINYINT(1)';
1372 $length = empty($field[
'length']) ? 18 : $field[
'length'];
1374 $scale = empty($field[
'scale']) ? $db->options[
'decimal_places'] : $field[
'scale'];
1376 return 'DECIMAL(' . $length .
',' . $scale .
')';
1387 $db_type = strtolower((
string) $field[
'type']);
1388 $db_type = strtok($db_type,
'(), ');
1389 if ($db_type ===
'national') {
1390 $db_type = strtok(
'(), ');
1392 if (!empty($field[
'length'])) {
1393 $length = strtok($field[
'length'],
', ');
1394 $decimal = strtok(
', ');
1396 $length = strtok(
'(), ');
1397 $decimal = strtok(
'(), ');
1400 $unsigned = $fixed =
null;
1403 $type[] =
'integer';
1404 $type[] =
'boolean';
1405 if (preg_match(
'/^(is|has)/', (
string) $field[
'name'])) {
1406 $type = array_reverse($type);
1408 $unsigned = preg_match(
'/ unsigned/i', (
string) $field[
'type']);
1412 $type[] =
'integer';
1413 $unsigned = preg_match(
'/ unsigned/i', (
string) $field[
'type']);
1417 $type[] =
'integer';
1418 $unsigned = preg_match(
'/ unsigned/i', (
string) $field[
'type']);
1423 $type[] =
'integer';
1424 $unsigned = preg_match(
'/ unsigned/i', (
string) $field[
'type']);
1428 $type[] =
'integer';
1429 $unsigned = preg_match(
'/ unsigned/i', (
string) $field[
'type']);
1442 if ($length ==
'1') {
1443 $type[] =
'boolean';
1444 if (preg_match(
'/^(is|has)/', (
string) $field[
'name'])) {
1445 $type = array_reverse($type);
1447 } elseif (str_contains($db_type,
'text')) {
1449 if ($decimal ===
'binary') {
1453 if ($fixed !==
false) {
1459 preg_match_all(
'/\'.+\'/U', (
string) $field[
'type'], $matches);
1462 if (is_array($matches)) {
1463 foreach ($matches[0] as $value) {
1464 $length = max($length, strlen($value) - 2);
1466 if ($length ==
'1' && count($matches[0]) === 2) {
1467 $type[] =
'boolean';
1468 if (preg_match(
'/^(is|has)/', (
string) $field[
'name'])) {
1469 $type = array_reverse($type);
1473 $type[] =
'integer';
1478 $type[] =
'integer';
1486 $type[] =
'timestamp';
1497 $unsigned = preg_match(
'/ unsigned/i', (
string) $field[
'type']);
1502 $type[] =
'decimal';
1503 $unsigned = preg_match(
'/ unsigned/i', (
string) $field[
'type']);
1504 if ($decimal !==
false) {
1505 $length = $length .
',' . $decimal;
1520 $type[] =
'integer';
1528 if ((
int) $length <= 0) {
1532 return [ $type, $length, $unsigned, $fixed ];
Class ilDBPdoMySQLFieldDefinition.
array $valid_default_values
const DEFINITION_COLUMN_NAME
getTypeDeclaration(array $field)
matchPattern(array $pattern, $operator=null, $field=null)
checkColumnDefinition(array $a_def)
getDeclaration(string $type, string $name, array $field)
checkTableName(string $table_name)
const DEFAULT_TEXT_LENGTH
isAllowedAttribute(string $attribute, string $type)
const DEFINITION_TABLE_NAME
array $allowed_attributes
setAllowedAttributes(array $allowed_attributes)
checkIndexName(string $a_name)
const MAX_TABLE_IDENTIFIER_LENGTH
mapPrepareDatatype(string $type)
writeLOBToFile($lob, string $file)
setMaxLength(array $max_length)
mapNativeDatatype(array $field)
__construct(protected \ilDBInterface $db_instance)
quote($value, ?string $type=null, bool $quote=true, bool $escape_wildcards=false)
array $allowed_attributes_old
ilMySQLQueryUtils $query_utils
compareDefinition(array $current, array $previous)
setAvailableTypes(array $available_types)
mapNativeDatatypeInternal(array $field)
setReservedMysql(array $reserved_mysql)
checkColumnName(string $column_name)
isReserved(string $table_name)
const DEFAULT_DECIMAL_PLACES
Class ilDatabaseException.