19declare(strict_types=1);
52 self::T_TEXT => [
'length',
'notnull',
'default',
'fixed'],
53 self::T_INTEGER => [
'length',
'notnull',
'default',
'unsigned'],
54 self::T_FLOAT => [
'notnull',
'default'],
55 self::T_DATE => [
'notnull',
'default'],
56 self::T_TIME => [
'notnull',
'default'],
57 self::T_TIMESTAMP => [
'notnull',
'default'],
58 self::T_CLOB => [
'notnull',
'default'],
59 self::T_BLOB => [
'notnull',
'default'],
65 "text" => [
"length",
"notnull",
"default",
"fixed"],
66 "integer" => [
"length",
"notnull",
"default",
"unsigned"],
67 "float" => [
"notnull",
"default"],
68 "date" => [
"notnull",
"default"],
69 "time" => [
"notnull",
"default"],
70 "timestamp" => [
"notnull",
"default"],
71 "clob" => [
"length",
"notnull",
"default"],
72 "blob" => [
"length",
"notnull",
"default"],
75 self::T_INTEGER => [1, 2, 3, 4, 8],
175 "CONSTRAINT_CATALOG",
289 "GEOMETRYCOLLECTION",
378 "MASTER_AUTO_POSITION",
380 "MASTER_CONNECT_RETRY",
382 "MASTER_HEARTBEAT_PERIOD",
388 "MASTER_RETRY_COUNT",
396 "MASTER_SSL_CRLPATH",
398 "MASTER_SSL_VERIFY_SERVER_CERT",
399 "MASTER_TLS_VERSION",
403 "MAX_CONNECTIONS_PER_HOUR",
404 "MAX_QUERIES_PER_HOUR",
407 "MAX_STATEMENT_TIME",
408 "MAX_UPDATES_PER_HOUR",
409 "MAX_USER_CONNECTIONS",
421 "MINUTE_MICROSECOND",
450 "NO_WRITE_TO_BINLOG",
533 "REPLICATE_DO_TABLE",
534 "REPLICATE_IGNORE_DB",
535 "REPLICATE_IGNORE_TABLE",
536 "REPLICATE_REWRITE_DB",
537 "REPLICATE_WILD_DO_TABLE",
538 "REPLICATE_WILD_IGNORE_TABLE",
568 "SECOND_MICROSECOND",
600 "SQL_AFTER_MTS_GAPS",
605 "SQL_CALC_FOUND_ROWS",
624 "STATS_SAMPLE_PAGES",
835 if ($this->query_utils ===
null) {
848 'timestamp' =>
'1970-01-01 00:00:00',
849 'time' =>
'00:00:00',
850 'date' =>
'1970-01-01',
860 if (!preg_match(self::DEFINITION_TABLE_NAME, $table_name)) {
861 throw new ilDatabaseException(
'Table name must only contain _a-z0-9 and must start with a-z.');
865 throw new ilDatabaseException(
"Invalid table name '" . $table_name .
"' (Reserved Word).");
868 if (stripos($table_name,
"sys_") === 0) {
869 throw new ilDatabaseException(
"Invalid table name '" . $table_name .
"'. Name must not start with 'sys_'.");
872 if (strlen($table_name) > self::MAX_TABLE_IDENTIFIER_LENGTH) {
874 .
"'. Maximum table identifer length is " . self::MAX_TABLE_IDENTIFIER_LENGTH .
" bytes.");
915 if (!preg_match(
"/^[a-z]+[_a-z0-9]*$/", $column_name)) {
917 .
"'. Column name must only contain _a-z0-9 and must start with a-z.");
921 throw new ilDatabaseException(
"Invalid column name '" . $column_name .
"' (Reserved Word).");
924 if (stripos($column_name,
"sys_") === 0) {
925 throw new ilDatabaseException(
"Invalid column name '" . $column_name .
"'. Name must not start with 'sys_'.");
928 if (strlen($column_name) > 30) {
929 throw new ilDatabaseException(
"Invalid column name '" . $column_name .
"'. Maximum column identifer length is 30 bytes.");
940 if (!preg_match(
"/^[a-z]+[_a-z0-9]*$/", $a_name)) {
941 throw new ilDatabaseException(
"Invalid column name '" . $a_name .
"'. Column name must only contain _a-z0-9 and must start with a-z.");
948 if (strlen($a_name) > 3) {
949 throw new ilDatabaseException(
"Invalid index name '" . $a_name .
"'. Maximum index identifer length is 3 bytes.");
962 switch ($a_def[
"type"]) {
964 throw new ilDatabaseException(
"Invalid column type '" . $a_def[
"type"] .
"'. Use integer(1) instead.");
967 throw new ilDatabaseException(
"Invalid column type '" . $a_def[
"type"] .
"'. Use float or integer instead.");
970 throw new ilDatabaseException(
"Invalid column type '" . $a_def[
"type"] .
"'. Allowed types are: "
977 foreach (array_keys($a_def) as $k) {
979 throw new ilDatabaseException(
"Attribute '" . $k .
"' is not allowed for column type '" . $a_def[
"type"] .
"'.");
985 switch ($a_def[
"type"]) {
987 if ((!isset($a_def[
"length"]) || $a_def[
"length"] < 1 || $a_def[
"length"] >
$max_length[self::T_TEXT]) && isset($a_def[
"length"])) {
988 throw new ilDatabaseException(
"Invalid length '" . $a_def[
"length"] .
"' for type text." .
" Length must be >=1 and <= "
994 if (isset($a_def[
"length"]) && !in_array((
int) $a_def[
"length"],
$max_length[self::T_INTEGER],
true)) {
995 throw new ilDatabaseException(
"Invalid length '" . $a_def[
"length"] .
"' for type integer." .
" Length must be "
996 . implode(
', ',
$max_length[self::T_INTEGER]) .
" (bytes).");
998 if ($a_def[
"unsigned"] ??
null) {
1009 return in_array($attribute, $this->allowed_attributes[$type],
true);
1056 return $this->db_instance;
1067 if (!empty($db->options[
'datatype_map'])) {
1068 foreach ($db->options[
'datatype_map'] as $type => $mapped_type) {
1069 if (array_key_exists($mapped_type, $types)) {
1070 $types[$type] = $types[$mapped_type];
1071 } elseif (!empty($db->options[
'datatype_map_callback'][$type])) {
1072 $parameter = [
'type' => $type,
'mapped_type' => $mapped_type];
1073 $default = call_user_func_array(
1074 $db->options[
'datatype_map_callback'][$type],
1075 [&$db, __FUNCTION__, $parameter]
1077 $types[$type] = $default;
1095 if (!empty($db->options[
'datatype_map'][$type])) {
1096 $type = $db->options[
'datatype_map'][$type];
1097 if (!empty($db->options[
'datatype_map_callback'][$type])) {
1098 $parameter = [
'type' => $type,
'name' => $name,
'field' => $field];
1100 return call_user_func_array(
1101 $db->options[
'datatype_map_callback'][$type],
1102 [&$db, __FUNCTION__, $parameter]
1105 $field[
'type'] = $type;
1108 if (!method_exists($this,
"get{$type}Declaration")) {
1112 return $this->{
"get{$type}Declaration"}($name, $field);
1119 switch ($field[
'type']) {
1121 $length = empty($field[
'length']) ? $db->options[
'default_text_field_length'] : $field[
'length'];
1122 $fixed = empty($field[
'fixed']) ? false : $field[
'fixed'];
1124 return $length ?
'CHAR(' . $length .
')' :
'CHAR(' . $db->options[
'default_text_field_length'] .
')';
1127 return $length ?
'VARCHAR(' . $length .
')' :
'TEXT';
1137 return 'CHAR (' . strlen(
'YYYY-MM-DD') .
')';
1139 return 'CHAR (' . strlen(
'HH:MM:SS') .
')';
1141 return 'CHAR (' . strlen(
'YYYY-MM-DD HH:MM:SS') .
')';
1151 $name = $db->quoteIdentifier($name,
true);
1152 $declaration_options =
'';
1153 $fd = $db->getFieldDefinition();
1155 $declaration_options = $fd->getDeclarationOptions($field);
1169 if (array_key_exists(
'default', $field)) {
1170 if ($field[
'default'] ===
'') {
1173 if (empty($field[
'notnull'])) {
1174 $field[
'default'] =
null;
1179 if ($field[
'default'] ===
''
1180 && isset($db->options[
"portability"])
1181 && ($db->options[
'portability'] & 32)
1183 $field[
'default'] =
' ';
1186 $default =
' DEFAULT ' . $this->
quote($field[
'default'], $field[
'type']);
1187 } elseif (empty($field[
'notnull'])) {
1188 $default =
' DEFAULT NULL';
1191 $notnull = empty($field[
'notnull']) ?
'' :
' NOT NULL';
1193 if (isset($field[
"notnull"]) && $field[
'notnull'] ===
false) {
1200 return $charset . $default . $notnull . $collation;
1233 $notnull = empty($field[
'notnull']) ?
'' :
' NOT NULL';
1234 $name = $db->quoteIdentifier($name,
true);
1243 $notnull = empty($field[
'notnull']) ?
'' :
' NOT NULL';
1244 $name = $db->quoteIdentifier($name,
true);
1286 $type = empty($current[
'type']) ? null : $current[
'type'];
1288 if (!method_exists($this,
"compare{$type}Definition")) {
1291 if (!empty($db->options[
'datatype_map_callback'][$type])) {
1292 $parameter = [
'current' => $current,
'previous' => $previous];
1294 return call_user_func_array(
1295 $db->options[
'datatype_map_callback'][$type],
1296 [&$db, __FUNCTION__, $parameter]
1303 if (empty($previous[
'type']) || $previous[
'type'] != $type) {
1307 $change = $this->{
"compare{$type}Definition"}($current, $previous);
1309 if ($previous[
'type'] != $type) {
1310 $change[
'type'] =
true;
1313 $previous_notnull = empty($previous[
'notnull']) ? false : $previous[
'notnull'];
1314 $notnull = empty($current[
'notnull']) ? false : $current[
'notnull'];
1315 if ($previous_notnull !== $notnull) {
1316 $change[
'notnull'] =
true;
1319 $alt = $previous_notnull ?
'' :
null;
1320 $previous_default = array_key_exists(
1323 ) ? $previous[
'default'] : $alt;
1324 $alt = $notnull ?
'' :
null;
1325 $default = array_key_exists(
'default', $current) ? $current[
'default'] : $alt;
1326 if ($previous_default !== $default) {
1327 $change[
'default'] =
true;
1339 $previous_unsigned = empty($previous[
'unsigned']) ? false : $previous[
'unsigned'];
1340 $unsigned = empty($current[
'unsigned']) ? false : $current[
'unsigned'];
1341 if ($previous_unsigned != $unsigned) {
1342 $change[
'unsigned'] =
true;
1344 $previous_autoincrement = empty($previous[
'autoincrement']) ? false : $previous[
'autoincrement'];
1345 $autoincrement = empty($current[
'autoincrement']) ? false : $current[
'autoincrement'];
1346 if ($previous_autoincrement != $autoincrement) {
1347 $change[
'autoincrement'] =
true;
1359 $previous_length = empty($previous[
'length']) ? 0 : $previous[
'length'];
1360 $length = empty($current[
'length']) ? 0 : $current[
'length'];
1361 if ($previous_length != $length) {
1362 $change[
'length'] =
true;
1364 $previous_fixed = empty($previous[
'fixed']) ? 0 : $previous[
'fixed'];
1365 $fixed = empty($current[
'fixed']) ? 0 : $current[
'fixed'];
1366 if ($previous_fixed != $fixed) {
1367 $change[
'fixed'] =
true;
1422 public function quote($value, ?
string $type =
null,
bool $quote =
true,
bool $escape_wildcards =
false): string
1432 return (
int) $value;
1435 protected function quoteText(
string $value,
bool $quote,
bool $escape_wildcards): string
1443 $value = $db->escape($value, $escape_wildcards);
1445 return "'" . $value .
"'";
1454 if (is_string($value) && preg_match(
'/^(\w+:\/\/)(.*)$/', $value, $match)) {
1456 if ($match[1] ===
'file://') {
1460 #$value = @fopen($value, 'r');
1463 if (is_resource($value)) {
1468 while (!@feof($fp)) {
1469 $value .= @fread($fp, $db->options[
'lob_buffer_length']);
1482 protected function quoteLOB($value,
bool $quote,
bool $escape_wildcards): string
1486 return $this->
quoteText($value, $quote, $escape_wildcards);
1491 protected function quoteCLOB($value,
bool $quote,
bool $escape_wildcards): string
1493 return $this->
quoteLOB($value, $quote, $escape_wildcards);
1498 protected function quoteBLOB($value,
bool $quote,
bool $escape_wildcards): string
1500 return $this->
quoteLOB($value, $quote, $escape_wildcards);
1505 return ($value ? 1 : 0);
1508 protected function quoteDate(
string $value,
bool $quote,
bool $escape_wildcards): string
1510 if ($value ===
'CURRENT_DATE') {
1511 return 'CURRENT_DATE';
1514 return $this->
quoteText($value, $quote, $escape_wildcards);
1520 protected function quoteTimestamp(
int $value,
bool $quote,
bool $escape_wildcards): string
1528 protected function quoteTime(
int $value,
bool $quote,
bool $escape_wildcards): string
1533 protected function quoteFloat(
string $value,
bool $quote,
bool $escape_wildcards): ?string
1535 if (preg_match(
'/^(.*)e([-+])(\d+)$/i', $value, $matches)) {
1536 $decimal = $this->
quoteDecimal($matches[1], $quote, $escape_wildcards);
1537 $sign = $matches[2];
1538 $exponent = str_pad($matches[3], 2,
'0', STR_PAD_LEFT);
1539 return $decimal .
'E' . $sign . $exponent;
1542 return $this->
quoteDecimal($value, $quote, $escape_wildcards);
1545 protected function quoteDecimal(
string $value,
bool $quote,
bool $escape_wildcards): ?string
1547 $value = preg_replace(
'/[^\d\.,\-+eE]/',
'', $value);
1548 if (preg_match(
'/[^.0-9]/', (
string) $value) && strpos((
string) $value,
',')) {
1550 if (!strpos((
string) $value,
'.')) {
1552 $value = strrev(str_replace(
',',
'.', strrev((
string) $value)));
1554 } elseif (strpos((
string) $value,
'.') && strpos((
string) $value,
'.') < strpos((
string) $value,
',')) {
1555 $value = str_replace(
'.',
'', $value);
1557 $value = strrev(str_replace(
',',
'.', strrev($value)));
1560 $value = str_replace(
',',
'', $value);
1576 if (preg_match(
'/^(\w+:\/\/)(.*)$/', $file, $match) && $match[1] ===
'file://') {
1580 $fp = @fopen($file,
'wb');
1581 while (!@feof($lob)) {
1582 $result = @fread($lob, $db->options[
'lob_buffer_length']);
1583 $read = strlen($result);
1584 if (@fwrite($fp, $result, $read) !== $read) {
1597 if (is_null($lob[
'value'])) {
1598 $lob[
'value'] = $lob[
'resource'];
1600 $lob[
'loaded'] =
true;
1605 protected function readLOB(array $lob,
int $length): string
1607 return substr((
string) $lob[
'value'], $lob[
'position'], $length);
1615 return $lob[
'endOfLOB'];
1623 $lob_data = stream_get_meta_data($lob);
1624 $lob_index = $lob_data[
'wrapper_data']->lob_index;
1626 if (isset($this->lobs[$lob_index])) {
1627 unset($this->lobs[$lob_index]);
1637 public function matchPattern(array $pattern, $operator =
null, $field =
null): string
1642 if (!is_null($operator)) {
1643 $operator = strtoupper((
string) $operator);
1644 switch ($operator) {
1647 if (is_null($field)) {
1648 throw new ilDatabaseException(
'case insensitive LIKE matching requires passing the field name');
1650 $db->loadModule(
'Function');
1651 $match = $db->lower($field) .
' LIKE ';
1655 $match = is_null($field) ?
'LIKE ' : $field .
' LIKE ';
1662 foreach ($pattern as $key => $value) {
1663 if ($key % 2 !== 0) {
1666 if ($operator ===
'ILIKE') {
1667 $value = strtolower((
string) $value);
1669 $escaped = $db->escape($value);
1670 $match .= $db->escapePattern($escaped);
1690 $db_type = strtok($field[
'type'],
'(), ');
1691 if (!empty($db->options[
'nativetype_map_callback'][$db_type])) {
1692 return call_user_func_array($db->options[
'nativetype_map_callback'][$db_type], [$db, $field]);
1710 if (!empty($db->options[
'datatype_map'][$type])) {
1711 $type = $db->options[
'datatype_map'][$type];
1712 if (!empty($db->options[
'datatype_map_callback'][$type])) {
1713 $parameter = [
'type' => $type];
1715 return call_user_func_array(
1716 $db->options[
'datatype_map_callback'][$type],
1717 [&$db, __FUNCTION__, $parameter]
Class ilDBPdoFieldDefinition.
const DEFINITION_TABLE_NAME
isAllowedAttribute(string $attribute, string $type)
ilMySQLQueryUtils $query_utils
getDateDeclaration(string $name, array $field)
quoteInteger($value, bool $quote, bool $escape_wildcards)
setMaxLength(array $max_length)
mapNativeDatatypeInternal(array $field)
compareIntegerDefinition(array $current, array $previous)
compareTextDefinition(array $current, array $previous)
checkColumnName(string $column_name)
readLOB(array $lob, int $length)
compareBooleanDefinition(array $current, array $previous)
checkIndexName(string $a_name)
checkColumnDefinition(array $a_def)
getIntegerDeclaration(string $name, array $field)
getCollationFieldDeclaration(string $collation)
quoteBLOB($value, bool $quote, bool $escape_wildcards)
quoteCLOB($value, bool $quote, bool $escape_wildcards)
static ilDBPdoFieldDefinition $instance
getCLOBDeclaration(string $name, array $field)
const SEQUENCE_COLUMNS_NAME
quoteLOB($value, bool $quote, bool $escape_wildcards)
compareCLOBDefinition(array $current, array $previous)
quoteText(string $value, bool $quote, bool $escape_wildcards)
getDeclaration(string $type, string $name, array $field)
getCharsetFieldDeclaration(string $charset)
quoteDecimal(string $value, bool $quote, bool $escape_wildcards)
writeLOBToFile($lob, string $file)
checkTableName(string $table_name)
const MAX_TABLE_IDENTIFIER_LENGTH
getBooleanDeclaration(string $name, array $field)
mapNativeDatatype(array $field)
setAvailableTypes(array $available_types)
getInternalDeclaration(string $name, array $field)
const DEFAULT_TEXT_LENGTH
quoteTime(int $value, bool $quote, bool $escape_wildcards)
quoteBoolean(bool $value, bool $quote, bool $escape_wildcards)
getTypeDeclaration(array $field)
compareDefinition(array $current, array $previous)
quoteDate(string $value, bool $quote, bool $escape_wildcards)
isReserved(string $table_name)
setAllowedAttributes(array $allowed_attributes)
array $allowed_attributes
getBLOBDeclaration(string $name, array $field)
compareTimeDefinition(array $current, array $previous)
compareDateDefinition(array $current, array $previous)
const DEFINITION_COLUMN_NAME
getTextDeclaration(string $name, array $field)
getDecimalDeclaration(string $name, array $field)
quote($value, ?string $type=null, bool $quote=true, bool $escape_wildcards=false)
__construct(protected \ilDBInterface $db_instance)
ilDBPdoFieldDefinition constructor.
getDeclarationOptions(array $field)
const DEFAULT_DECIMAL_PLACES
quoteFloat(string $value, bool $quote, bool $escape_wildcards)
compareTimestampDefinition(array $current, array $previous)
array $allowed_attributes_old
mapPrepareDatatype(string $type)
getFloatDeclaration(string $name, array $field)
setReservedMysql(array $reserved_mysql)
compareFloatDefinition(array $current, array $previous)
array $valid_default_values
matchPattern(array $pattern, $operator=null, $field=null)
compareBLOBDefinition(array $current, array $previous)
getTimeDeclaration(string $name, array $field)
quoteTimestamp(int $value, bool $quote, bool $escape_wildcards)
compareDecimalDefinition(array $current, array $previous)
getTimestampDeclaration(string $name, array $field)
Class ilDatabaseException.