109 private array $_reported_keys = [];
111 private array $_tx = [];
113 private int $_db_timer_depth = 0;
122 $this->oValidator =
dbx()->get_system_obj(
'dbxValidator');
124 $this->_connected = 0;
126 $this->_insert_id = 0;
127 $this->_update_count = 0;
128 $this->_delete_count = 0;
129 $this->_insert_count = 0;
130 $this->_dbMessage =
'';
131 $this->_validation_error = 0;
132 $this->_validation_warning = 0;
133 $this->_validation_error_flds = array();
134 $this->_validation_warning_flds = array();
135 $this->_fld_id =
'id';
153 $this->_error_status =
'';
154 $this->_error_text =
'';
155 $this->_dbMessage =
'';
164 return (string)$this->_error_status;
173 return (string)$this->_error_text;
182 return (int)$this->_insert_id;
192 private function set_db_error(
string $status,
string $text): void {
193 $this->_error_status = $status;
194 $this->_error_text = $text;
195 $this->_dbMessage = $text;
196 $this->_error = $text;
209 private function report_db_error(
string $status,
string $rid,
string $why,
string $what,
string $dedupKey =
''): void {
210 $message = trim($why);
211 if (trim($what) !==
'') {
212 $message .= ($message !==
'' ?
': ' :
'') . trim($what);
214 $this->set_db_error($status, $message);
216 if (!(
int)$this->_report_error) {
220 if ($dedupKey !==
'') {
221 if (isset($this->_reported_keys[$dedupKey])) {
224 $this->_reported_keys[$dedupKey] = 1;
227 $sysMsgStatus = $status ===
'access' ?
'security' :
'error';
230 dbx()->sys_msg($sysMsgStatus,
'db', $rid, $why, $what);
231 }
catch (Throwable $e) {
233 'DBX database error [%s] %s (%s): %s; reporting failed: %s',
245 $this->set_db_error($status, $message);
252 private function connection_error_reason(Throwable $e): string {
253 $message = strtolower($e->getMessage());
255 if (str_contains($message,
'unknown database')
256 || str_contains($message,
'database does not exist')
257 || str_contains($message,
'does not exist')
259 return 'Datenbank nicht vorhanden';
262 if (str_contains($message,
'access denied')
263 || str_contains($message,
'authentication failed')
264 || str_contains($message,
'password authentication failed')
266 return 'Datenbank-Anmeldung fehlgeschlagen';
269 if (str_contains($message,
'connection refused')
270 || str_contains($message,
'server has gone away')
271 || str_contains($message,
'lost connection')
272 || str_contains($message,
'timed out')
273 || str_contains($message,
'timeout')
274 || str_contains($message,
'no connection could be made')
275 || str_contains($message,
'actively refused')
276 || str_contains($message,
'2002')
277 || str_contains($message,
'2003')
279 return 'Datenbankserver nicht erreichbar';
282 return 'Datenbankverbindung fehlgeschlagen';
290 private function is_missing_database_error(
string $message): bool {
291 $message = strtolower($message);
293 return str_contains($message,
'unknown database')
294 || str_contains($message,
'database does not exist')
295 || str_contains($message,
'cannot open database')
296 || preg_match(
'/(?:sqlstate\[)?3d000/', $message) === 1
297 || preg_match(
'/(?:error|code)[^0-9]*1049/', $message) === 1;
324 $pdo =
new PDO(
"sqlite:$databasePath");
325 $pdo->exec(
'PRAGMA locking_mode=NORMAL');
326 $pdo->beginTransaction();
327 }
catch (PDOException $e) {
330 if (isset(
$pdo) &&
$pdo->inTransaction()) {
336 dbx()->debug(
"SQLITE db ($databasePath) Lock=($isLocked)");
366 function dbConnect(
$server, $dbType, $dbHost, $dbName =
'', $dbUser =
'', $dbPass =
'', $dbPort =
'') {
370 if (!isset($this->db[
$server])) {
374 $dbName =
dbx()->config_path_resolve($dbHost . $dbName);
375 $this->db[
$server] =
new PDO(
"sqlite:$dbName");
380 $dsn =
"mysql:host=$dbHost";
381 if ($dbPort !==
'') {
382 $dsn .=
";port=$dbPort";
384 if ($dbName !==
'') {
385 $dsn .=
";dbname=$dbName";
387 $dsn .=
";charset=utf8mb4";
388 $this->db[
$server] =
new PDO($dsn, $dbUser, $dbPass, [
389 PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
390 PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
391 PDO::ATTR_TIMEOUT => max(1, $this->_connect_timeout),
397 $this->db[
$server] =
new PDO(
"pgsql:host=$dbHost;dbname=$dbName", $dbUser, $dbPass);
401 $this->db[
$server] =
new PDO(
"sqlsrv:Server=$dbHost;Database=$dbName", $dbUser, $dbPass);
405 $dbtns =
"(DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = //$dbHost)(PORT = $dbPort))
406 (CONNECT_DATA = (SERVICE_NAME = $dbName) ))";
408 $this->db[
$server] =
new PDO(
"oci:dbname=$dbtns;charset=utf8", $dbUser, $dbPass, [
409 PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
410 PDO::ATTR_EMULATE_PREPARES =>
false,
411 PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
416 $this->db[
$server] =
new PDO(
"firebird:dbname=$dbHost:$dbName", $dbUser, $dbPass);
420 $this->db[
$server] =
new PDO(
"cubrid:host=$dbHost;dbname=$dbName", $dbUser, $dbPass);
424 $this->db[
$server] =
new PDO(
"dblib:host=$dbHost;dbname=$dbName", $dbUser, $dbPass);
428 $this->db[
$server] =
new PDO(
"ibm:DRIVER={IBM DB2 ODBC DRIVER};DATABASE=$dbName;HOSTNAME=$dbHost;PORT=$dbPort;PROTOCOL=TCPIP;UID=$dbUser;PWD=$dbPass;");
432 $this->db[
$server] =
new PDO(
"informix:host=$dbHost;service=$dbPort;database=$dbName;server=$server;protocol=onsoctcp;UID=$dbUser;PWD=$dbPass");
436 $this->db[
$server] =
new PDO(
"odbc:$dbName", $dbUser, $dbPass);
440 throw new PDOException(
"Unsupported database type: $dbType");
443 $this->db[
$server]->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
444 }
catch (Throwable $e) {
446 $dbMessage = $e->getMessage();
448 $this->_query =
"Connect Server ($server) Type=($dbType) Host=($dbHost) dbName=($dbName) dbUser=($dbUser) Port=($dbPort)";
450 $this->report_db_error(
453 $this->connection_error_reason($e),
463 private function quote_db_identifier_for_type(
string $dbType,
string $name): string {
464 $dbType = strtolower(trim($dbType));
465 if ($dbType ===
'mysql') {
466 return '`' . str_replace(
'`',
'``', $name) .
'`';
468 if ($dbType ===
'sqlsrv' || $dbType ===
'dblib') {
469 return '[' . str_replace(
']',
']]', $name) .
']';
471 return '"' . str_replace(
'"',
'""', $name) .
'"';
474 private function pdo_quote_value(PDO $pdo,
string $value): string {
475 $quoted = $pdo->quote(
$value);
476 if ($quoted ===
false) {
477 return "'" . str_replace(
"'",
"''",
$value) .
"'";
483 $dbType = strtolower(trim((string)($dbConfig[
'type'] ??
'')));
484 $dbName = trim((
string)($dbConfig[
'dbname'] ?? ($dbConfig[
'name'] ??
'')));
486 if ($dbType ===
'sqlite') {
487 $host =
dbx()->config_path_resolve(rtrim((
string)($dbConfig[
'host'] ??
''),
"/\\") .
'/');
489 $path =
dbx()->config_path_resolve(rtrim((
string)($dbConfig[
'host'] ??
''),
"/\\") .
'/' . $dbName);
490 return ($dbName !==
'' && is_file($path)) ? 1 : 0;
492 return is_dir($host) ? 1 : 0;
495 if ($dbType ===
'' || ($withDatabase && $dbName ===
'')) {
499 $tmpServer =
'__dbx_check_db_' . md5($dbType . $dbName . microtime(
true));
500 if (isset($this->db[$tmpServer])) {
501 unset($this->db[$tmpServer]);
504 $ok = $this->dbConnect(
507 $dbConfig[
'host'] ??
'',
508 $withDatabase ? $dbName :
'',
509 $dbConfig[
'user'] ??
'',
510 $dbConfig[
'pass'] ??
'',
511 $dbConfig[
'port'] ??
''
514 if (isset($this->db[$tmpServer])) {
515 unset($this->db[$tmpServer]);
522 $dbType = strtolower(trim((string)($dbConfig[
'type'] ??
'')));
523 $dbName = (string)($dbConfig[
'dbname'] ?? ($dbConfig[
'name'] ??
''));
524 $dbName = trim($dbName);
526 if ($dbName ===
'') {
530 if ($dbType ===
'sqlite') {
531 $host =
dbx()->config_path_resolve(rtrim((
string)($dbConfig[
'host'] ??
''),
"/\\") .
'/');
532 if ($host !==
'' && !is_dir($host)) {
533 @mkdir($host, 0777,
true);
535 return is_dir($host) ? 1 : 0;
544 $adminDb = (string)($dbConfig[
'admin_dbname'] ?? $dbConfig[
'maintenance_db'] ??
'postgres');
548 $adminDb = (string)($dbConfig[
'admin_dbname'] ?? $dbConfig[
'maintenance_db'] ??
'master');
554 $tmpServer =
'__dbx_create_db_' . md5(
$server . $dbType . microtime(
true));
555 if (isset($this->db[$tmpServer])) {
556 unset($this->db[$tmpServer]);
559 $ok = $this->dbConnect(
562 $dbConfig[
'host'] ??
'',
564 $dbConfig[
'user'] ??
'',
565 $dbConfig[
'pass'] ??
'',
566 $dbConfig[
'port'] ??
''
569 if (!
$ok || !isset($this->db[$tmpServer])) {
570 if (isset($this->db[$tmpServer])) {
571 unset($this->db[$tmpServer]);
577 $pdo = $this->db[$tmpServer];
580 $sql =
'CREATE DATABASE IF NOT EXISTS ' . $this->quote_db_identifier_for_type($dbType, $dbName)
581 .
' CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci';
586 $exists = $pdo->query(
'SELECT 1 FROM pg_database WHERE datname = ' . $this->pdo_quote_value($pdo, $dbName));
587 if (!$exists || !$exists->fetchColumn()) {
588 $pdo->exec(
'CREATE DATABASE ' . $this->quote_db_identifier_for_type($dbType, $dbName) .
" ENCODING 'UTF8'");
594 $literal = $this->pdo_quote_value($pdo, $dbName);
595 $sql =
'IF DB_ID(N' . $literal .
') IS NULL CREATE DATABASE ' . $this->quote_db_identifier_for_type($dbType, $dbName);
600 unset($this->db[$tmpServer]);
601 dbx()->debug(
"database ensured Server=($server) Type=($dbType) DB=($dbName)");
603 }
catch (PDOException $e) {
604 $this->_dbMessage = $e->getMessage();
605 $this->report_db_error(
'db', (
string)
$server,
'Datenbank nicht vorhanden', $e->getMessage(),
'db-create|' .
$server);
606 unset($this->db[$tmpServer]);
646 $this->_server =
'try:' .
$server;
647 $this->_connected = 0;
654 if (isset($this->db[
$server]) && $this->db[
$server] instanceof PDO) {
657 $this->_connected = 1;
665 if (!isset($this->db[
$server])) {
669 if (preg_match(
'/\.(db3|sqlite|sqlite3)$/i',
$server)) {
670 $activ_modul =
dbx()->get_system_var(
'dbx_activ_modul',
'dbx');
672 if (strpos(
$server,
'|') !==
false) {
673 $parts = explode(
'|',
$server, 2);
674 $sqlite_modul = trim($parts[0]);
675 $sqlite_name = trim($parts[1]);
677 if ($sqlite_modul ===
'modul' || $sqlite_modul ===
'') {
678 $sqlite_modul = $activ_modul;
681 $sqlite_modul = $activ_modul;
687 $file1 =
dbx()->os_path(
dbx()->
get_base_dir() .
'dbx/modules/dbx/db/' . $sqlite_name);
688 $file2 =
dbx()->os_path(
dbx()->
get_base_dir() .
'dbx/modules/' . $sqlite_modul .
'/db/' . $sqlite_name);
690 if (file_exists($file2)) {
692 } elseif (file_exists($file1)) {
694 $sqlite_modul =
'dbx';
697 $this->report_db_error(
700 'Datenbank nicht vorhanden',
710 @mkdir(
$dir, 0777,
true);
714 'host' =>
dbx()->config_path_store(
$dir .
'/',
true),
715 'dbname' => basename(
$file),
732 dbx()->debug(
"## no config ERROR connect_db_server Server=($server)",
$config);
733 $this->report_db_error(
736 'Datenbank nicht vorhanden',
737 'Keine Server-Konfiguration',
743 if (!$this->db_server_config_is_active(
$server, $dbConfig)) {
744 $this->_dbMessage =
'Datenbankserver deaktiviert';
745 $this->_error = $this->_dbMessage;
751 $dbName = $dbConfig[
'dbname'] ?? ($dbConfig[
'name'] ??
'');
754 $ok = $this->dbConnect(
756 $dbConfig[
'type'] ??
'sqlite',
757 $dbConfig[
'host'] ??
'',
759 $dbConfig[
'user'] ??
'',
760 $dbConfig[
'pass'] ??
'',
761 $dbConfig[
'port'] ??
''
765 && trim((
string)$dbName) !==
''
766 && $this->is_missing_database_error((
string)$this->_dbMessage)
768 $firstMessage = $this->_dbMessage;
769 if ($this->ensure_database_exists(
$server, $dbConfig)) {
770 if (isset($this->db[
$server])) {
773 $this->_dbMessage =
'';
775 $ok = $this->dbConnect(
777 $dbConfig[
'type'] ??
'mysql',
778 $dbConfig[
'host'] ??
'',
780 $dbConfig[
'user'] ??
'',
781 $dbConfig[
'pass'] ??
'',
782 $dbConfig[
'port'] ??
''
784 } elseif ($this->_dbMessage ===
'' && $firstMessage !==
'') {
785 $this->_dbMessage = $firstMessage;
793 $this->clear_db_error();
795 $dbType = $this->get_db_type(
$server);
796 $this->_connected = 1;
798 $this->_dbtype = $dbType;
811 $type = strtolower(trim((string)($dbConfig[
'type'] ??
'')));
812 $name = trim((
string)($dbConfig[
'dbname'] ?? ($dbConfig[
'name'] ??
'')));
814 if ($type ===
'sqlite' || $type ===
'sqlite3' || preg_match(
'/\.(db3|sqlite|sqlite3)$/i',
$server .
' ' . $name)) {
818 if (!array_key_exists(
'activ', $dbConfig)) {
822 $active = strtolower(trim((
string)$dbConfig[
'activ']));
823 return !in_array($active, array(
'',
'0',
'false',
'no',
'nein',
'off',
'deaktiv',
'inactive',
'disabled'),
true);
846 } elseif (preg_match(
'/\.(db3|sqlite|sqlite3)$/i', (
string)
$server)) {
864 $bindings =
$config[
'dd_server_bindings'] ?? array();
866 return is_array($bindings) ? $bindings : array();
874 private function find_dd_server_binding(
879 $exact = $ddModule .
'|' . $ddName;
883 if (array_key_exists($candidate, $bindings)) {
886 'server' => trim((
string)$bindings[$candidate]),
891 $lowerCandidates = array_map(
'strtolower',
$candidates);
892 foreach ($bindings as $key =>
$server) {
893 $position = array_search(strtolower(trim((
string)$key)), $lowerCandidates,
true);
894 if ($position !==
false) {
896 'key' => (
string)$key,
897 'server' => trim((
string)
$server),
902 return array(
'key' =>
'',
'server' =>
'');
919 if (preg_match(
'/\.(db3|sqlite|sqlite3)$/i',
$server)) {
920 $parts = strpos(
$server,
'|') !==
false
923 $module = trim((
string)($parts[0] ??
''));
924 $file = trim((
string)($parts[1] ??
''));
926 return (
$module ===
'' || preg_match(
'/^[A-Za-z0-9_]+$/',
$module) === 1)
928 && preg_match(
'/^[A-Za-z0-9_.-]+\.(db3|sqlite|sqlite3)$/i',
$file) === 1;
934 return is_array($dbConfig)
935 && $this->db_server_config_is_active(
$server, $dbConfig);
955 $dd_status = $dd_sys[
'dd_status'] ?? 0;
956 $dd_modul = (string)($dd_sys[
'dd_modul'] ??
'');
957 $dd_name = (string)($dd_sys[
'dd_name'] ??
'');
959 if ($dd_status != 1) {
963 'declared_server' =>
'',
964 'resolved_server' =>
'',
965 'source' =>
'missing-dd',
970 $cache =
$_SESSION[
'dbx'][
'cache'][
'dd'][$dd_modul][$dd_name] ?? array();
971 $declared = trim((
string)(
972 $cache[
'declared_server']
973 ?? ($cache[
'table'][
'server'] ??
'default')
975 $binding = $this->find_dd_server_binding(
976 $this->get_dd_server_bindings(),
983 : $this->is_valid_dd_server_binding(
$resolved);
989 $dd_modul .
'|' . $dd_name,
990 'ungueltige lokale DD-Serverbindung',
997 'dd' => $dd_modul .
'|' . $dd_name,
999 'declared_server' => $declared,
1001 'source' =>
$binding[
'key'] !==
'' ?
'local-binding' :
'dd-default',
1017 return (
string)(
$binding[
'resolved_server'] ??
'');
1031 $csv_seperator =
';';
1034 $dd_status = $dd_sys[
'dd_status'] ?? 0;
1035 $dd_modul = $dd_sys[
'dd_modul'] ??
'';
1036 $dd_name = $dd_sys[
'dd_name'] ??
'';
1038 if ($dd_status == 1) {
1039 $csv_seperator =
$_SESSION[
'dbx'][
'cache'][
'dd'][$dd_modul][$dd_name][
'table'][
'csv'] ??
';';
1042 return $csv_seperator;
1057 $dd_status = $dd_sys[
'dd_status'] ?? 0;
1058 $dd_modul = $dd_sys[
'dd_modul'] ??
'';
1059 $dd_name = $dd_sys[
'dd_name'] ??
'';
1061 if ($dd_status == 1) {
1062 $primary =
$_SESSION[
'dbx'][
'cache'][
'dd'][$dd_modul][$dd_name][
'table'][
'primary'] ??
'';
1064 if ($primary !==
'') {
1129 'int' => [
'mysql' =>
'INT',
'sqlite' =>
'INTEGER',
'pgsql' =>
'INTEGER',
'sqlsrv' =>
'INT',
'oci' =>
'NUMBER',
'firebird' =>
'INTEGER',
'cubrid' =>
'INTEGER',
'dblib' =>
'INT',
'ibm' =>
'INTEGER',
'informix' =>
'INTEGER',
'odbc' =>
'INTEGER'],
1130 'varchar' => [
'mysql' =>
'VARCHAR',
'sqlite' =>
'TEXT',
'pgsql' =>
'VARCHAR',
'sqlsrv' =>
'VARCHAR',
'oci' =>
'VARCHAR2',
'firebird' =>
'VARCHAR',
'cubrid' =>
'VARCHAR',
'dblib' =>
'VARCHAR',
'ibm' =>
'VARCHAR',
'informix' =>
'VARCHAR',
'odbc' =>
'VARCHAR'],
1131 'text' => [
'mysql' =>
'TEXT',
'sqlite' =>
'TEXT',
'pgsql' =>
'TEXT',
'sqlsrv' =>
'TEXT',
'oci' =>
'CLOB',
'firebird' =>
'BLOB',
'cubrid' =>
'STRING',
'dblib' =>
'TEXT',
'ibm' =>
'CLOB',
'informix' =>
'TEXT',
'odbc' =>
'LONGVARCHAR'],
1132 'bool' => [
'mysql' =>
'TINYINT(1)',
'sqlite' =>
'INTEGER',
'pgsql' =>
'BOOLEAN',
'sqlsrv' =>
'BIT',
'oci' =>
'NUMBER(1)',
'firebird' =>
'SMALLINT',
'cubrid' =>
'SMALLINT',
'dblib' =>
'BIT',
'ibm' =>
'SMALLINT',
'informix' =>
'BOOLEAN',
'odbc' =>
'BOOLEAN'],
1133 'date' => [
'mysql' =>
'DATE',
'sqlite' =>
'TEXT',
'pgsql' =>
'DATE',
'sqlsrv' =>
'DATE',
'oci' =>
'DATE',
'firebird' =>
'DATE',
'cubrid' =>
'DATE',
'dblib' =>
'DATE',
'ibm' =>
'DATE',
'informix' =>
'DATE',
'odbc' =>
'DATE']
1137 $type = $typeMap[
$field[
'type']][$dbType] ??
$field[
'type'];
1139 $sql =
"ALTER TABLE $table ADD COLUMN {$field['name']} $type";
1142 $sql .=
"({$field['length']})";
1145 if (isset(
$field[
'default']) && (
$field[
'default'] !==
'' ||
$field[
'default'] === 0)) {
1146 $sql .=
" DEFAULT '{$field['default']}'";
1149 dbx()->debug(
"ADD-FLD SQL=($sql)");
1151 if ((
$field[
'index'] ??
'') ===
'PRI') {
1152 $sqlIndex =
"ALTER TABLE $table ADD PRIMARY KEY ({$field['name']})";
1153 dbx()->debug(
"ADD-PRIMARY-KEY SQL=($sqlIndex)");
1154 } elseif ((
$field[
'index'] ??
'') ===
'MU') {
1155 $sqlIndex =
"CREATE INDEX idx_{$field['name']} ON $table ({$field['name']})";
1156 dbx()->debug(
"ADD-INDEX SQL=($sqlIndex)");
1180 $dd_status = $dd_sys[
'dd_status'] ?? 0;
1181 $dd_modul = $dd_sys[
'dd_modul'] ??
'';
1182 $dd_name = $dd_sys[
'dd_name'] ??
'';
1184 if ($dd_status == 1 && !$rec) {
1185 $dd_table =
$_SESSION[
'dbx'][
'cache'][
'dd'][$dd_modul][$dd_name][
'table'][
'table'] ?? 0;
1188 if ($dd_status == 1 && $rec) {
1189 $dd_table =
$_SESSION[
'dbx'][
'cache'][
'dd'][$dd_modul][$dd_name][
'table'] ?? 0;
1190 if (is_array($dd_table)) {
1213 $dd_status = $dd_sys[
'dd_status'] ?? 0;
1214 $dd_modul = $dd_sys[
'dd_modul'] ??
'';
1215 $dd_name = $dd_sys[
'dd_name'] ??
'';
1217 if ($dd_status == 1 && !$rec) {
1218 $dd_sync =
$_SESSION[
'dbx'][
'cache'][
'dd'][$dd_modul][$dd_name][
'table'][
'autosync'] ?? 0;
1221 if ($dd_status == 1 && $rec) {
1222 $dd_sync =
$_SESSION[
'dbx'][
'cache'][
'dd'][$dd_modul][$dd_name][
'autosync'] ?? 0;
1270 $activ_modul =
dbx()->get_system_var(
'dbx_activ_modul',
'dbx');
1271 $is_explicit_modul =
false;
1272 $active_language = function_exists(
'dbx_lng_current')
1274 : strtolower(trim((string)
dbx()->get_system_var(
'dbx_lng',
'de')));
1276 if ($active_language ===
'' || !preg_match(
'/^[a-z]{2,3}$/', $active_language)) {
1277 $active_language =
'de';
1280 $cache_matches_language =
static function (array $cache,
string $language):
bool {
1281 $table = isset($cache[
'table']) && is_array($cache[
'table']) ? $cache[
'table'] : array();
1282 $dynamic = !empty($cache[
'language_dynamic']) || ((
$table[
'language'] ??
'') ===
'*');
1288 return strtolower(trim((
string) (
$table[
'language'] ??
''))) === $language;
1291 if (strpos(
$dd,
'|') !==
false) {
1292 $parts = explode(
'|',
$dd, 2);
1293 $dd_modul = trim($parts[0]);
1294 $dd_name = trim($parts[1]);
1295 $is_explicit_modul =
true;
1297 if ($dd_modul ===
'modul' || $dd_modul ===
'') {
1298 $dd_modul = $activ_modul;
1301 $dd_modul = $activ_modul;
1305 if (isset(
$_SESSION[
'dbx'][
'cache'][
'dd'][$dd_modul][$dd_name])) {
1306 $cached_dd =
$_SESSION[
'dbx'][
'cache'][
'dd'][$dd_modul][$dd_name];
1307 if ($cache_matches_language($cached_dd, $active_language)) {
1308 $dd_sys[
'dd_status'] = 1;
1309 $dd_sys[
'dd_modul'] = $dd_modul;
1310 $dd_sys[
'dd_name'] = $dd_name;
1315 unset(
$_SESSION[
'dbx'][
'cache'][
'dd'][$dd_modul][$dd_name]);
1318 if (!$is_explicit_modul && isset(
$_SESSION[
'dbx'][
'cache'][
'dd'][
'dbx'][$dd_name])) {
1319 $cached_dd =
$_SESSION[
'dbx'][
'cache'][
'dd'][
'dbx'][$dd_name];
1320 if ($cache_matches_language($cached_dd, $active_language)) {
1321 $dd_sys[
'dd_status'] = 1;
1322 $dd_sys[
'dd_modul'] =
'dbx';
1323 $dd_sys[
'dd_name'] = $dd_name;
1328 unset(
$_SESSION[
'dbx'][
'cache'][
'dd'][
'dbx'][$dd_name]);
1333 $dd_file1 =
dbx()->os_path(
dbx()->
get_base_dir() .
'dbx/modules/dbx/dd/' . $dd_name .
'.dd.php');
1334 $dd_file2 =
dbx()->os_path(
dbx()->
get_base_dir() .
"dbx/modules/$dd_modul/dd/" . $dd_name .
'.dd.php');
1336 if (file_exists($dd_file2)) {
1337 $dd_file = $dd_file2;
1338 } elseif (file_exists($dd_file1)) {
1339 $dd_file = $dd_file1;
1343 $dd_sys[
'dd_modul'] = $dd_modul;
1344 $dd_sys[
'dd_name'] = $dd_name;
1346 dbx()->debug(
"##dbxDB-load_dd=($dd) modul=($dd_modul) dd=($dd_name) file=($dd_file) aktiv modul=($activ_modul)");
1348 if (empty($dd_file)) {
1349 dbx()->sys_msg(
'error',
'dd',
$dd,
'missing',
'No dd Path');
1350 $dd_sys[
'dd_status'] = 0;
1357 $dd_sys[
'dd_status'] = -1;
1368 $language_dynamic = ((
$table[
'language'] ??
'') ===
'*');
1369 if ($language_dynamic) {
1370 $language = $active_language;
1371 $table_base = trim((
string) (
$table[
'table'] ??
''));
1372 if ($table_base !==
'') {
1373 $language_dd_name = $table_base .
'_' . $language;
1374 $language_dd_file1 =
dbx()->os_path(
1375 dbx()->
get_base_dir() .
'dbx/modules/dbx/dd/' . $language_dd_name .
'.dd.php'
1377 $language_dd_file2 =
dbx()->os_path(
1378 dbx()->
get_base_dir() .
'dbx/modules/' . $dd_modul .
'/dd/' . $language_dd_name .
'.dd.php'
1380 $language_dd_file =
'';
1382 if (file_exists($language_dd_file2)) {
1383 $language_dd_file = $language_dd_file2;
1384 } elseif (file_exists($language_dd_file1)) {
1385 $language_dd_file = $language_dd_file1;
1388 if ($language_dd_file !==
'') {
1390 include $language_dd_file;
1391 $dd_file = $language_dd_file;
1393 $table[
'table'] = $language_dd_name;
1394 $table[
'datadic'] = $language_dd_name;
1395 $table[
'language'] = $language;
1400 dbx()->register_editor_file(
'dd', $dd_file);
1410 if (isset(
$table[
'server'])
1411 && is_string(
$table[
'server'])
1412 && strpos(
$table[
'server'],
'|') ===
false
1413 && preg_match(
'/\.(db3|sqlite|sqlite3)$/i',
$table[
'server'])
1417 if ($dd_modul !==
'' && file_exists($sqlite_file)) {
1422 $_SESSION[
'dbx'][
'cache'][
'dd'][$dd_modul][$dd_name] = [
1426 'file' => $this->normalize_editor_file_path($dd_file),
1427 'language_dynamic' => $language_dynamic,
1428 'declared_server' => (string)(
$table[
'server'] ??
'default'),
1433 $dd_sys[
'dd_status'] = 1;
1434 $dd_sys[
'dd_modul'] = $dd_modul;
1435 $dd_sys[
'dd_name'] = $dd_name;
1448 $dd_status = $dd_sys[
'dd_status'] ?? 0;
1449 $dd_modul = $dd_sys[
'dd_modul'] ??
'';
1450 $dd_name = $dd_sys[
'dd_name'] ??
'';
1452 if ($dd_status != 1 || $dd_modul ===
'' || $dd_name ===
'') {
1456 return $_SESSION[
'dbx'][
'cache'][
'dd'][$dd_modul][$dd_name][
'file'] ??
'';
1465 private function normalize_editor_file_path(
string $file): string {
1466 return
dbx()->editor_file_path(
$file);
1486 if (!$this->connect_db_server(
$server)) {
1492 for ($try = 0; $try < $maxRetry; $try++) {
1494 $stmt = $this->db[
$server]->prepare($sql);
1498 }
catch (PDOException $e) {
1499 if (empty($this->_tx[
$server]) && $this->isRetryable($e) && $try < $maxRetry - 1) {
1500 usleep(120000 + random_int(0, 150000));
1504 $this->report_db_error(
1509 'sql|' .
$server .
'|' . md5($sql)
1533 private function db_timer_dd_section(
string $type,
string $dd): string {
1540 $prefix = $type ===
'save' ?
'db-save-' :
'db-select-';
1541 $safe = preg_replace(
'/[^A-Za-z0-9]+/',
'-',
$dd);
1542 $safe = trim((
string) $safe,
'-');
1548 $max = 80 - strlen($prefix);
1549 if (strlen($safe) > $max) {
1550 $hash = substr(md5(
$dd), 0, 6);
1551 $safe = rtrim(substr($safe, 0, max(1, $max - 7)),
'-') .
'-' . $hash;
1554 return $prefix . $safe;
1583 private function db_timers_start(
string $type,
string $dd =
''): array {
1584 if ((int)
dbx()->get_system_var(
'dbx_performance_timer_store', 0,
'int') === 1) {
1588 if ($this->_db_timer_depth > 0) {
1592 $this->_db_timer_depth++;
1594 $type = $type ===
'save' ?
'save' :
'select';
1595 $base = $type ===
'save' ?
'db-save' :
'db-select';
1597 'db-total' =>
'db total',
1601 $ddSection = $this->db_timer_dd_section($type,
$dd);
1602 if ($ddSection !==
'') {
1606 foreach (
$sections as $section => $info) {
1607 dbx()->timer($section, $info);
1622 private function db_timers_stop(array
$sections): void {
1627 for ($i = count(
$sections) - 1; $i >= 0; $i--) {
1631 $this->_db_timer_depth = max(0, $this->_db_timer_depth - 1);
1648 return $this->beginServer(
$server);
1665 return $this->rollbackServer(
$server);
1685 private function beginServer(
string $server): int {
1686 if (!$this->connect_db_server(
$server)) {
1690 if (!empty($this->_tx[
$server])) {
1694 $type = $this->get_db_type(
$server);
1700 $pdo->exec(
'BEGIN IMMEDIATE TRANSACTION');
1704 $pdo->beginTransaction();
1708 $pdo->beginTransaction();
1713 }
catch (PDOException $e) {
1714 $this->report_db_error(
'sql',
$server,
'SQL-Fehler', $e->getMessage(),
'sql-tx-begin|' .
$server);
1730 private function rollbackServer(
string $server): int {
1731 if (!$this->connect_db_server(
$server)) {
1735 if (empty($this->_tx[
$server])) {
1739 $type = $this->get_db_type(
$server);
1743 if ($type ===
'sqlite') {
1744 $pdo->exec(
'ROLLBACK');
1751 }
catch (PDOException $e) {
1752 $this->report_db_error(
'sql',
$server,
'SQL-Fehler', $e->getMessage(),
'sql-tx-rollback|' .
$server);
1772 dbx()->debug(
"commit($server)");
1786 if ($type ===
'sqlite') {
1787 $pdo->exec(
'COMMIT');
1794 }
catch (PDOException $e) {
1795 $this->report_db_error(
'sql',
$server,
'SQL-Fehler', $e->getMessage(),
'sql-tx-commit|' .
$server);
1809 private function isRetryable(PDOException $e): bool {
1810 $msg = strtolower($e->getMessage());
1813 str_contains($msg,
'deadlock') ||
1814 str_contains($msg,
'locked') ||
1815 str_contains($msg,
'lock wait timeout') ||
1816 str_contains($msg,
'database is locked') ||
1817 str_contains($msg,
'busy');
1836 for ($try = 0; $try < $maxRetry; $try++) {
1838 $this->db[
$server]->exec($sql);
1840 }
catch (PDOException $e) {
1841 if (empty($this->_tx[
$server]) && $this->isRetryable($e) && $try < $maxRetry - 1) {
1842 usleep(120000 + random_int(0, 150000));
1846 $this->report_db_error(
'sql',
$server,
'SQL-Fehler', $e->getMessage(),
'sql-exec|' .
$server);
1888 $timers = $this->db_timers_start(
'select',
$dd);
1893 if (!is_object($stmt)) {
1895 $this->report_db_error(
'sql',
$server,
'SQL-Fehler',
'SELECT fehlgeschlagen',
'sql-select|' .
$server);
1900 return $stmt->fetchAll(PDO::FETCH_ASSOC);
1901 }
catch (PDOException | Exception $e) {
1902 $this->report_db_error(
'sql',
$server,
'SQL-Fehler', $e->getMessage(),
'sql-select|' .
$server);
1905 $this->db_timers_stop($timers);
1916 private function array_stripslashes(array|
string $data): array|string {
1917 if (is_array($data)) {
1918 foreach ($data as $key =>
$value) {
1919 $data[$key] = $this->array_stripslashes(
$value);
1923 if (is_string($data)) {
1924 $data = stripslashes($data);
1949 public function create_select(
string $select, array $flds,
int $and = 1): string {
1950 $searchTerms = array_filter(explode(
' ', trim($select)));
1952 if (!$searchTerms) {
1956 $server = $this->_server ?:
'default';
1958 $whereConditions = [];
1960 foreach ($searchTerms as $term) {
1961 $escaped = $this->escape($term,
$server);
1965 foreach (array_keys($flds) as
$field) {
1966 $orParts[] =
"$field LIKE '%$escaped%'";
1969 $whereConditions[] =
'(' . implode(
' OR ', $orParts) .
')';
1972 return implode($and ?
' AND ' :
' OR ', $whereConditions);
1984 $this->_insert_id = 0;
1987 $timers = $this->db_timers_start(
'save');
1992 $this->db_timers_stop($timers);
1996 $retval = $this->db[
$server]->lastInsertId();
2000 $this->_insert_count++;
2003 $this->_insert_id = $retval;
2019 $this->_delete_count = 0;
2024 $count = $stmt->rowCount();
2027 dbx()->debug(
"delete count server=($server) count=($count) sql=($sql)");
2029 $this->_delete_count =
$count;
2071 $connect = $this->connect_db_server(
$server);
2072 $pos = strpos($query,
' ') ?: $iChars;
2073 $queryType = strtoupper(substr(trim($query), 0, min($pos, $iChars)));
2076 if ($this->get_error_status() ===
'') {
2077 $this->report_db_error(
'db',
$server,
'Datenbank nicht vorhanden',
'Server not connected',
'db-raw|' .
$server);
2079 $this->_query = $query;
2084 if ($queryType ===
'SELECT' || $queryType ===
'PRAGMA' || $queryType ===
'SHOW') {
2085 return $this->select_query(
$server, $query);
2088 if ($queryType ===
'INSERT') {
2089 return $this->insert_query(
$server, $query);
2092 if ($queryType ===
'UPDATE') {
2093 return $this->update_query(
$server, $query);
2096 if ($queryType ===
'DELETE') {
2097 return $this->delete_query(
$server, $query);
2103 }
catch (PDOException $e) {
2104 $this->_query = $query;
2105 $this->report_db_error(
'sql',
$server,
'SQL-Fehler', $e->getMessage(),
'sql-raw|' .
$server);
2141 public function select(
string $dd =
'', $where =
'', $columns =
'*', $orderby =
'', $asc_desc =
'ASC', $groupby =
'', $max = 0, $offset = 0, $verify_access = 1) {
2152 if ($verify_access) {
2156 $this->report_db_error(
'access', (
string)
$dd,
'Zugriff verweigert',
'select',
'access-select|' .
$dd);
2168 if (!is_array($columns)) {
2169 $columns = strpos($columns,
',') !==
false ? explode(
',', $columns) : [$columns];
2172 foreach ($columns as $no =>
$field) {
2173 $xfield = is_int($no) ? trim(
$field) : trim($no);
2175 if ($xfield ===
'*') {
2180 if ($this->is_fld_name(
$dd, $xfield)) {
2187 $query =
"SELECT $fields FROM $dbtab ";
2190 $query .=
"WHERE $where ";
2194 $query .=
"GROUP BY $groupby ";
2198 $orderby = trim($orderby);
2199 $orderby = rtrim($orderby,
';');
2201 if (stripos($orderby,
' ASC') !==
false || stripos($orderby,
' DESC') !==
false) {
2202 $query .=
"ORDER BY $orderby ";
2204 $query .=
"ORDER BY $orderby $asc_desc ";
2209 if ($dbType ===
'mysql') {
2210 $query .=
"LIMIT $offset, $max ";
2212 $query .=
"LIMIT $max OFFSET $offset ";
2232 public function select1(
string $dd, $where =
'', $columns =
'*', $verify_access = 1) {
2233 $db_records = $this->
select(
$dd, $where, $columns,
'',
'ASC',
'', 1, 0, $verify_access);
2235 if (!is_array($db_records)) {
2239 if (!isset($db_records[0]) || !is_array($db_records[0])) {
2243 return $db_records[0];
2259 public function select_tree(
string $folder_dd,
string $item_dd =
'', array $opt = []): array {
2261 if (!is_array($tree_def)) $tree_def = [];
2263 if ($item_dd ===
'' && !
empty($tree_def[
'tree_items_dd'])) {
2264 $item_dd = (string)$tree_def[
'tree_items_dd'];
2267 $folder_id = (string)($opt[
'folder_id'] ?? $tree_def[
'tree_id'] ??
'id');
2268 $folder_parent = (string)($opt[
'folder_parent'] ?? $tree_def[
'tree_parent'] ??
'parent_id');
2269 $folder_title = (string)($opt[
'folder_title'] ?? $tree_def[
'tree_label'] ??
'name');
2270 $folder_rights = (string)($opt[
'folder_rights'] ?? $tree_def[
'tree_rights'] ??
'group_read');
2271 $folder_order = (string)($opt[
'folder_order'] ?? $tree_def[
'tree_order'] ?? $folder_title);
2272 $folder_where = (string)($opt[
'folder_where'] ??
'');
2274 $item_id = (string)($opt[
'item_id'] ?? $tree_def[
'tree_items_id'] ??
'id');
2275 $item_parent = (string)($opt[
'item_parent'] ?? $tree_def[
'tree_items_parent'] ??
'folder');
2276 $item_title = (string)($opt[
'item_title'] ?? $tree_def[
'tree_items_label'] ??
'title');
2277 $item_rights = (string)($opt[
'item_rights'] ?? $tree_def[
'tree_items_rights'] ??
'group_read');
2278 $item_order = (string)($opt[
'item_order'] ?? $tree_def[
'tree_items_order'] ?? $item_title);
2279 $item_where = (string)($opt[
'item_where'] ??
'');
2281 $root = (int)($opt[
'root'] ?? 0);
2282 $verify_access = (int)($opt[
'verify_access'] ?? 1);
2284 $folder_cols = $this->tree_columns($folder_dd, [
2296 $folders = $this->select($folder_dd, $folder_where, $folder_cols, $folder_order,
'ASC',
'', 0, 0, $verify_access);
2297 if (!is_array($folders)) {
2304 if ($item_dd !==
'') {
2305 $item_cols = $this->tree_columns($item_dd, [
2320 $items = $this->select($item_dd, $item_where, $item_cols, $item_order,
'ASC',
'', 0, 0, $verify_access);
2330 foreach ($folders as $row) {
2331 if (!is_array($row))
continue;
2333 $id = (int)($row[$folder_id] ?? 0);
2334 if ($id <= 0)
continue;
2336 $parent = (int)($row[$folder_parent] ?? 0);
2337 if ($parent === $id) {
2342 $node[
'_node_id'] =
'folder-' . $id;
2343 $node[
'_type'] =
'folder';
2345 $node[
'_parent'] = $parent;
2346 $node[
'_title'] = (string)($row[$folder_title] ?? (
'Ordner ' . $id));
2347 $node[
'_rights'] = (string)($row[$folder_rights] ??
'');
2348 $node[
'_children'] = [];
2350 $byParent[$parent][] = $node;
2353 foreach (
$items as $row) {
2354 if (!is_array($row))
continue;
2356 $id = (int)($row[$item_id] ?? 0);
2357 if ($id <= 0)
continue;
2359 $parent = (int)($row[$item_parent] ?? 0);
2362 $node[
'_node_id'] =
'page-' . $id;
2363 $node[
'_type'] =
'page';
2365 $node[
'_parent'] = $parent;
2366 $node[
'_title'] = (string)($row[$item_title] ?? (
'Seite ' . $id));
2367 $node[
'_rights'] = (string)($row[$item_rights] ??
'');
2368 $node[
'_children'] = [];
2370 $byParent[$parent][] = $node;
2373 $build =
function ($parent, $level) use (&$build, &$byParent, &$flat) {
2374 $children = $byParent[$parent] ?? [];
2377 foreach ($children as $node) {
2378 $node[
'_level'] = $level;
2380 if (($node[
'_type'] ??
'') ===
'folder') {
2381 $node[
'_children'] = $build((
int)$node[
'_id'], $level + 1);
2391 $nodes = $build(
$root, 0);
2396 'folders' => array_values($folders),
2397 'items' => array_values(
$items),
2401 private function tree_columns(
string $dd, array $columns): array {
2404 foreach ($columns as
$field) {
2412 return array_values(
$out);
2430 if (!isset($field_values[
$field])) {
2435 if (($field_values[
'id'] ?? 0) <= 0) {
2436 unset($field_values[
'id']);
2439 return $field_values;
2474 function insert(
$dd, $field_values, $verify_access = 1, $verify_fields = 1, $verify_values = 1, $trace = 1) {
2476 $this->_insert_id = 0;
2477 $this->_validation_error = 0;
2478 $this->_validation_warning = 0;
2483 if (
$dd !==
'dbxTrace') {
2484 $uid =
dbx()->user();
2485 $now = $this->now_ms();
2487 $field_values[
'update_date'] =
$now;
2488 $field_values[
'update_uid'] = $uid;
2491 'create_date' =>
$now,
2492 'create_uid' => $uid,
2497 if (
$dd !==
'dbxTrace') {
2502 $this->report_db_error(
'access', (
string)
$dd,
'Zugriff verweigert',
'insert',
'access-insert|' .
$dd);
2506 if ($verify_fields) {
2510 if ($verify_values) {
2514 if ($this->_validation_error) {
2518 if (isset($field_values[
'id']) && $field_values[
'id'] == 0) {
2519 unset($field_values[
'id']);
2524 $this->report_db_error(
'db', (
string)
$server,
'Datenbank nicht vorhanden', (
string)
$server,
'db-insert|' .
$server);
2529 $fields = array_keys($field_values);
2532 $sql =
"INSERT INTO $tab (" .
2535 implode(
',', $placeholders) .
2540 for ($try = 0; $try < $maxRetry; $try++) {
2542 $timers = $this->db_timers_start(
'save', (
string)
$dd);
2545 $stmt = $this->db[
$server]->prepare($sql);
2546 $stmt->execute(array_values($field_values));
2548 $id = $this->db[
$server]->lastInsertId();
2550 $this->db_timers_stop($timers);
2556 if (!$id && !
empty($field_values[$pk])) {
2557 $id = $field_values[$pk];
2561 $this->_insert_id = $id;
2563 if ($id > 0 &&
$dd !==
'dbxTrace') {
2564 $this->_insert_count++;
2567 if ($trace &&
$dd !==
'dbxTrace') {
2569 $write_trace = $table_def[
'trace'] ?? 0;
2572 $uid =
dbx()->user();
2573 $now = $this->now_ms();
2574 $modul =
dbx()->get_system_var(
'dbx_activ_modul',
'dbx');
2575 $run1 =
dbx()->get_modul_var(
'dbx_run1');
2576 $run2 =
dbx()->get_modul_var(
'dbx_run2');
2577 $run3 =
dbx()->get_modul_var(
'dbx_run3');
2578 $source = ($uid > 0) ?
'user' :
'system';
2581 'create_date' =>
$now,
2582 'create_uid' => $uid,
2583 'update_date' =>
$now,
2584 'update_uid' => $uid,
2586 'action' =>
'insert',
2589 'data_json' => json_encode([
2590 'action' =>
'insert',
2601 'delta' => $field_values
2602 ], JSON_UNESCAPED_UNICODE)
2605 $trace_result = $this->
insert(
'dbxTrace', $traceData, 0, 0, 0, 0);
2607 if ($trace_result !== 1) {
2612 'trace insert failed',
2613 json_encode($traceData, JSON_UNESCAPED_UNICODE)
2619 $this->_insert_id = $id;
2624 }
catch (PDOException $e) {
2625 if (
empty($this->_tx[
$server]) && $this->isRetryable($e) && $try < $maxRetry - 1) {
2626 usleep(120000 + random_int(0, 150000));
2630 $this->report_db_error(
'sql', (
string)
$server,
'SQL-Fehler', $e->getMessage(),
'sql-insert|' .
$server);
2669 function update(
$dd, $field_values, $where, $verify_access = 1, $verify_fields = 1, $verify_values = 1, $trace = 1) {
2671 $this->_validation_error = 0;
2672 $this->_validation_warning = 0;
2678 $uid =
dbx()->user();
2679 $now = $this->now_ms();
2682 $field_values[
'update_date'] =
$now;
2683 $field_values[
'update_uid'] = $uid;
2685 if ($verify_access) {
2694 $this->report_db_error(
'access', (
string)
$dd,
'Zugriff verweigert',
'update',
'access-update|' .
$dd);
2709 $write_trace = $table_def[
'trace'] ?? 0;
2712 $modul =
dbx()->get_system_var(
'dbx_activ_modul',
'dbx');
2713 $run1 =
dbx()->get_modul_var(
'dbx_run1');
2714 $run2 =
dbx()->get_modul_var(
'dbx_run2');
2715 $run3 =
dbx()->get_modul_var(
'dbx_run3');
2716 $source = ($uid > 0) ?
'user' :
'system';
2717 $beforeRows = $this->
select(
$dd, $where);
2721 if ($verify_fields) {
2725 if ($verify_values) {
2729 if ($this->_validation_error) {
2735 $this->report_db_error(
'db', (
string)
$server,
'Datenbank nicht vorhanden', (
string)
$server,
'db-update|' .
$server);
2748 $set[] =
"$field = ?";
2752 $sql =
"UPDATE $tab SET " . implode(
',', $set) .
" WHERE $where";
2756 for ($try = 0; $try < $maxRetry; $try++) {
2758 $timers = $this->db_timers_start(
'save', (
string)
$dd);
2761 $stmt = $this->db[
$server]->prepare($sql);
2762 $stmt->execute($vals);
2764 $count = $stmt->rowCount();
2766 $this->db_timers_stop($timers);
2769 $this->_update_count =
$count;
2771 if ($trace && $write_trace && is_array($beforeRows)) {
2772 foreach ($beforeRows as $row) {
2773 $id = $row[$pk] ??
null;
2776 $before_changed = [];
2778 foreach ($field_values as $k => $v) {
2779 $old = $row[$k] ??
null;
2781 if ((
string) $old !== (
string) $v) {
2783 $before_changed[$k] = $old;
2788 $rec = json_encode([
2789 'action' =>
'update',
2799 'before' => $before_changed,
2801 ], JSON_UNESCAPED_UNICODE);
2803 $this->
insert(
'dbxTrace', [
2804 'create_date' =>
$now,
2805 'create_uid' => $uid,
2806 'update_date' =>
$now,
2807 'update_uid' => $uid,
2809 'action' =>
'update',
2819 }
catch (PDOException $e) {
2820 if (
empty($this->_tx[
$server]) && $this->isRetryable($e) && $try < $maxRetry - 1) {
2821 usleep(120000 + random_int(0, 150000));
2825 $this->report_db_error(
'sql', (
string)
$server,
'SQL-Fehler', $e->getMessage(),
'sql-update|' .
$server);
2844 $this->_update_count = 0;
2847 $timers = $this->db_timers_start(
'save');
2852 $this->db_timers_stop($timers);
2856 $retval = $stmt->rowCount();
2859 $this->_update_count = $retval;
2896 function save(
$dd, $field_values, $where, $verify_access = 1, $verify_fields = 1, $verify_values = 1, $trace = 1) {
2902 if ($verify_access) {
2913 $ok = $this->
update(
$dd, $field_values, $where, $verify_access, $verify_fields, $verify_values, $trace);
2916 if ($this->_update_count > 0) {
2920 $exists = $this->
count(
$dd, $where);
2932 $ok = $this->
insert(
$dd, $field_values, $verify_access, $verify_fields, $verify_values, $trace);
2935 dbx()->debug(
"#SAVE# ($ok) dd=($dd) W=($where)");
2936 dbx()->debug(
"#SAVE# Fields", $field_values);
2960 function delete(
$dd, $where, $verify_access = 1, $trace = 1) {
2978 if ($verify_access) {
3000 $write_trace = $table_def[
'trace'] ?? 0;
3002 if (!$write_trace) {
3003 $write_trace = $table_def[
'trash'] ?? 0;
3007 $beforeRows = $this->
select(
$dd, $where);
3009 $uid =
dbx()->user();
3010 $now = $this->now_ms();
3011 $modul =
dbx()->get_system_var(
'dbx_activ_modul',
'dbx');
3012 $run1 =
dbx()->get_modul_var(
'dbx_run1');
3013 $run2 =
dbx()->get_modul_var(
'dbx_run2');
3014 $run3 =
dbx()->get_modul_var(
'dbx_run3');
3015 $source = ($uid > 0) ?
'user' :
'system';
3019 $query =
'DELETE FROM ' . $tab .
' WHERE ' . $where .
';';
3029 if ($trace && $write_trace && is_array($beforeRows)) {
3032 foreach ($beforeRows as $row) {
3033 $id = $row[$pk] ??
null;
3035 $rec = json_encode([
3036 'action' =>
'delete',
3048 ], JSON_UNESCAPED_UNICODE);
3050 $this->
insert(
'dbxTrace', [
3051 'create_date' =>
$now,
3052 'create_uid' => $uid,
3053 'update_date' =>
$now,
3054 'update_uid' => $uid,
3056 'action' =>
'delete',
3086 $explicitServer =
$server !==
'';
3088 if ($where ==
'new') {
3108 if (!$explicitServer) {
3111 $this->report_db_error(
'access', (
string)
$dd,
'Zugriff verweigert',
'count',
'access-count|' .
$dd);
3117 $where = is_array($where) ?
'' : $where;
3120 $query =
"SELECT COUNT(*) AS cnt FROM $dbtab";
3122 if (!
empty($where)) {
3123 $query .=
" WHERE $where";
3126 $timers = $this->db_timers_start(
'select', $explicitServer ?
'' : (
string)
$dd);
3131 if (!is_object($stmt)) {
3133 $this->report_db_error(
'sql', (
string)
$server,
'SQL-Fehler',
'COUNT fehlgeschlagen',
'sql-count|' .
$server);
3138 $row = $stmt->fetch(PDO::FETCH_ASSOC);
3140 if (is_array($row) && isset($row[
'cnt'])) {
3141 $count = (int) $row[
'cnt'];
3145 }
catch (PDOException | Exception $e) {
3146 $this->report_db_error(
'sql', (
string)
$server,
'SQL-Fehler', $e->getMessage(),
'sql-count|' .
$server);
3149 $this->db_timers_stop($timers);
3167 $rows = $this->select_query(
3169 "SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'sqlite_sequence' LIMIT 1"
3190 dbx()->debug(
"#empty dd=($dd) Server=($server) Tab=($dbtab) Type=($dbType)");
3192 if (!$dbtab || !
$server || !$dbType) {
3198 $sql =
"TRUNCATE TABLE $dbtab";
3202 $sql =
"ALTER TABLE $dbtab AUTO_INCREMENT = 1";
3208 dbx()->debug(
"#truncate Server=($server) Tab=($dbtab)");
3210 $sql =
"DELETE FROM $dbtab";
3214 $sql =
"DELETE FROM sqlite_sequence WHERE name='$dbtab'";
3218 dbx()->debug(
"truncate=($ok)");
3222 $sql =
"TRUNCATE TABLE $dbtab RESTART IDENTITY";
3227 $sql =
"TRUNCATE TABLE $dbtab";
3238 $sql =
"DELETE FROM $dbtab";
3247 "no type def ($dbType)",
3294 if (!
$server || !$dbtab || !$dbType) {
3300 $table = $this->quote_db_identifier_for_type($dbType, $dbtab);
3307 dbx()->debug(
"#optimize tab skipped dd=($dd) Server=($server) Tab=($dbtab) Type=($dbType)");
3318 private function resolve_sqlite_db_path(
string $server): string {
3319 if (!preg_match(
'/\.(db3|sqlite|sqlite3)$/i',
$server)) {
3323 $activ_modul =
dbx()->get_system_var(
'dbx_activ_modul',
'dbx');
3327 if (strpos(
$server,
'|') !==
false) {
3328 $parts = explode(
'|',
$server, 2);
3329 $sqlite_modul = trim($parts[0]);
3330 $sqlite_name = trim($parts[1]);
3332 if ($sqlite_modul ===
'modul' || $sqlite_modul ===
'') {
3333 $sqlite_modul = $activ_modul;
3336 $sqlite_modul = $activ_modul;
3340 $file1 =
dbx()->os_path(
dbx()->
get_base_dir() .
'dbx/modules/dbx/db/' . $sqlite_name);
3341 $file2 =
dbx()->os_path(
dbx()->
get_base_dir() .
'dbx/modules/' . $sqlite_modul .
'/db/' . $sqlite_name);
3343 if (is_file($file1)) {
3346 if (is_file($file2)) {
3362 private function log_db_schema_issue(
string $key,
string $rid,
string $why,
string $what): void {
3367 $this->report_db_error(
'db', $rid, $why, $what, $key);
3399 if (!preg_match(
'/^[A-Za-z_][A-Za-z0-9_]*$/', (
string)$dbtab)) {
3405 if ($dbType ===
'sqlite') {
3408 $this->log_db_schema_issue(
3411 'Datenbank nicht vorhanden',
3419 $this->log_db_schema_issue(
3422 'Datenbank nicht vorhanden',
3423 trim((
string)$this->_dbMessage) !==
'' ? (
string)$this->_dbMessage : (
string)
$server
3428 if (!isset($this->db[
$server]) || !is_object($this->db[
$server])) {
3429 $this->log_db_schema_issue(
3432 'Datenbank nicht vorhanden',
3443 $stmt = $this->db[
$server]->prepare(
3444 "SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ? LIMIT 1"
3446 $stmt->execute(array($dbtab));
3447 $exists = $stmt->fetchColumn() ? 1 : 0;
3451 $stmt = $this->db[
$server]->prepare(
'SHOW TABLES LIKE ?');
3452 $stmt->execute(array($dbtab));
3453 $exists = $stmt->fetchColumn() ? 1 : 0;
3457 $stmt = $this->db[
$server]->prepare(
3458 "SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = ? LIMIT 1"
3460 $stmt->execute(array($dbtab));
3461 $exists = $stmt->fetchColumn() ? 1 : 0;
3465 $stmt = $this->db[
$server]->prepare(
3466 'SELECT 1 FROM user_tables WHERE table_name = ? AND ROWNUM = 1'
3468 $stmt->execute(array(strtoupper($dbtab)));
3469 $exists = $stmt->fetchColumn() ? 1 : 0;
3473 $stmt = $this->db[
$server]->prepare(
3474 "SELECT 1 FROM information_schema.tables WHERE table_name = ?"
3476 $stmt->execute(array($dbtab));
3477 $exists = $stmt->fetchColumn() ? 1 : 0;
3481 $stmt = $this->db[
$server]->prepare(
'SELECT 1 FROM ' . $dbtab .
' WHERE 1 = 0 LIMIT 1');
3488 $why =
'Tabelle nicht vorhanden';
3489 $what = $dbtab .
' (' .
$server .
')';
3491 if ($dbType ===
'sqlite') {
3492 $countStmt = $this->db[
$server]->query(
3493 "SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%'"
3495 $tableCount = $countStmt ? (int)$countStmt->fetchColumn() : 0;
3496 if ($tableCount === 0) {
3497 $why =
'Datenbank nicht vorhanden';
3498 $what = $this->resolve_sqlite_db_path(
$server) ?: (string)
$server;
3502 $this->log_db_schema_issue(
3503 'table-missing|' .
$server .
'|' . $dbtab,
3511 }
catch (PDOException $e) {
3512 $this->log_db_schema_issue(
3513 'db-schema|' .
$server .
'|' . $dbtab,
3515 'Datenbank nicht vorhanden',
3516 trim((
string)$e->getMessage()) !==
'' ? (
string)$e->getMessage() : ($dbtab .
' (' .
$server .
')')
3531 $exist = file_exists($dd_file);
3558 $tableRows = array();
3562 $sql =
"SHOW TABLES";
3567 $sql =
"SELECT name FROM sqlite_master"
3568 .
" WHERE type='table' AND name NOT LIKE 'sqlite_%'";
3573 $sql =
"SELECT table_name FROM user_tables";
3578 $sql =
"SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'";
3583 $sql =
"SELECT table_name FROM information_schema.tables";
3588 $tableRows = array();
3589 dbx()->sys_msg(
'warning',
'db',
$server,
"unsupported db type ($dbType)",
'get_db_tables');
3593 if (is_array($tableRows)) {
3594 foreach ($tableRows as $tableRow) {
3595 $tableName = reset($tableRow);
3597 if ($tableName != $not) {
3606 'name' => $tableName,
3628 dbx()->debug(
"F-Load dd=($dd)");
3631 $dd_status = $dd_sys[
'dd_status'] ?? 0;
3632 $dd_modul = $dd_sys[
'dd_modul'] ??
'';
3633 $dd_name = $dd_sys[
'dd_name'] ??
'';
3635 if ($dd_status == 1) {
3636 $table =
$_SESSION[
'dbx'][
'cache'][
'dd'][$dd_modul][$dd_name][
'table'] ?? 0;
3663 $dd_status = $dd_sys[
'dd_status'] ?? 0;
3664 $dd_modul = $dd_sys[
'dd_modul'] ??
'';
3665 $dd_name = $dd_sys[
'dd_name'] ??
'';
3667 if ($dd_status == 1) {
3671 if ($label && $dd_status == 1) {
3676 $xname =
$field[
'name'] ??
'';
3679 if ($xname ===
'') {
3683 if ($label == 1 && isset(
$field[
'label']) &&
$field[
'label'] !==
'') {
3684 $xlabel =
$field[
'label'];
3687 $xfields[$xname] = $xlabel;
3711 $flds = explode(
",", $flds);
3713 if (is_array($flds)) {
3714 foreach ($flds as $no =>
$field) {
3745 if (empty($f[
'name'])) {
3749 $cols[] = $f[
'name'];
3752 return implode(
',', $cols);
3780 if (empty($f[
'name'])) {
3786 if (str_starts_with($name,
'_')) {
3790 $ddType = $f[
'type'] ??
'text';
3791 $gridType = $this->map_dd_type_to_grid_type($ddType);
3794 if (!empty($f[
'label']) && is_string($f[
'label'])) {
3795 $label = trim($f[
'label']);
3798 $label = str_replace([
':',
'[',
']'],
'-', $label);
3804 $fieldWithLabel = $name .
'[' . $label .
']';
3806 $protect = isset($f[
'protect']) ? (string) $f[
'protect'] :
'0';
3809 if (!empty($f[
'group']) && is_string($f[
'group'])) {
3810 $group =
'@' . trim($f[
'group']);
3813 if ($protect ===
'2') {
3814 $cols[] = $fieldWithLabel .
':' . $gridType .
':!v' . $group;
3818 if ($protect ===
'1') {
3819 $cols[] = $fieldWithLabel .
':' . $gridType .
':p' . $group;
3823 $cols[] = $fieldWithLabel .
':' . $gridType . $group;
3826 return implode(
',', $cols);
3837 $t = strtolower(trim($ddType));
3896 if ($convert ==
'auto') {
3897 if (is_string(
$array) && str_starts_with(
$array,
'a:')) {
3898 $convert =
'serial';
3899 } elseif (is_array(
$array)) {
3900 foreach (
$array as $val) {
3901 if (is_array($val)) {
3902 $convert =
'serial';
3907 if ($convert ==
'auto') {
3911 $convert =
'serial';
3915 if ($convert ==
'serial' && is_string(
$array)) {
3916 $unserialized = @unserialize(
$array);
3918 if ($unserialized !==
false && is_array($unserialized)) {
3921 foreach (
$array as $val) {
3922 if (is_array($val)) {
3923 return serialize(
$array);
3931 if ($convert ==
'list' && is_array(
$array)) {
3962 dbx()->debug(
"check-acces Mode=($mode) Tab=($dd)");
3964 if (
dbx()->
can(
'dbxRunAsAdmin')) {
3976 if (
$mode ==
'insert') {
3977 $modeKey =
'create';
3978 $ownerKey =
'create_owner';
3981 if (
$mode ==
'update') {
3982 $modeKey =
'update';
3983 $ownerKey =
'update_owner';
3986 if (
$mode ==
'delete') {
3987 $modeKey =
'delete';
3988 $ownerKey =
'delete_owner';
3991 if (
$mode ==
'select') {
3993 $ownerKey =
'read_owner';
3996 $groups = $modeKey !==
'' ? (string)(
$table[$modeKey] ??
'') :
'';
3997 $access =
dbx()->can(access_groups:
$groups);
3999 if (!$access &&
dbx()->
user() > 0) {
4000 if (preg_match(
'/\bowner\b/', (
string)
$groups)) {
4001 $access =
$mode ===
'insert' ? 1 : 2;
4005 if (!$access && $ownerKey !==
'' &&
dbx()->
user() > 0) {
4006 $ownerGroups = trim((
string)(
$table[$ownerKey] ??
''));
4007 if ($ownerGroups !==
'') {
4008 if ($ownerGroups ===
'*' || preg_match(
'/\bowner\b/', $ownerGroups) ||
dbx()->
can(access_groups: $ownerGroups)) {
4009 $access =
$mode ===
'insert' ? 1 : 2;
4016 dbx()->debug(
"check-acces dd=($dd) Mode=($mode) Groups=($groups) Access=($access)");
4043 $db_field_values = [];
4044 $this->_validation_error = 0;
4045 $this->_validation_warning = 0;
4046 $this->_validation_error_flds = [];
4047 $this->_validation_warning_flds = [];
4049 $validate_rules = $this->_validatior_rules;
4050 $validate_type = $this->_validatior_type;
4051 $validate_error = $this->_validatior_error;
4052 $validate_mode = $this->_validatior_mode;
4057 return $db_field_values;
4061 $name =
$field[
'name'] ??
'';
4062 $length =
$field[
'length'] ?? 0;
4063 $type =
$field[
'type'] ??
'';
4066 if ($name !==
'' && isset($field_values[$name])) {
4068 $value = $field_values[$name];
4081 if ($type && $length > 0) {
4082 $rules = $type .
'|max=' . $length;
4099 if ($validate_error) {
4100 $this->_validation_error_flds[] = $err;
4101 $this->_validation_error++;
4103 $this->_validation_warning_flds[] = $err;
4104 $this->_validation_warning++;
4107 if ($validate_mode ===
'clean') {
4110 if (strpos((
string)
$rules,
'array') !==
false) {
4127 if ($validate_mode ===
'unset') {
4133 $db_field_values[$name] =
$value;
4138 return $db_field_values;
4156 if (!is_array($db_fields) ||
empty($db_fields)) {
4160 $valid_names = array();
4162 foreach ($db_fields as
$field) {
4164 $valid_names[
$field[
'name']] = 1;
4168 return array_filter($field_values,
function ($name) use ($valid_names) {
4169 return isset($valid_names[$name]);
4170 }, ARRAY_FILTER_USE_KEY);
4191 if (!is_array($dd_fields)) {
4195 foreach ($dd_fields as
$field) {
4196 $name =
$field[
'name'] ??
'';
4204 if (!isset(
$field[
'default']) && in_array((
$field[
'type'] ??
''), [
'int',
'longint'])) {
4225 private function is_fld_name(
string $dd, $xfield) {
4226 $db_fields = $this->get_dd_fields(
$dd);
4228 if (!is_array($db_fields) || empty($db_fields) || !is_string($xfield) || $xfield ===
'') {
4232 foreach ($db_fields as
$field) {
4233 if (!isset(
$field[
'name'])) {
4237 if (
$field[
'name'] === $xfield) {
4285 if (!is_array($where)) {
4289 $conditions = array();
4292 if (isset($where[
'raw'])) {
4293 if (!empty($where[
'trusted'])) {
4294 return $this->check_where((
string) $where[
'raw'],
$owner,
$dd);
4297 dbx()->sys_msg(
'warning',
'db',
$dd,
'raw where blocked', (
string) $where[
'raw']);
4298 return $this->check_where(
'',
$owner,
$dd);
4301 if (isset($where[
'search']) && is_array($where[
'search'])) {
4302 $search = $where[
'search'];
4303 $built = $this->build_search_where(
4305 $search[
'value'] ??
'',
4306 is_array($search[
'like'] ??
null) ? $search[
'like'] : array(),
4307 is_array($search[
'equal'] ??
null) ? $search[
'equal'] : array(),
4308 (
string) ($search[
'mode'] ??
'starts_with')
4311 if ($built !==
'') {
4312 $conditions[] = $built;
4317 if (in_array(
$field, array(
'search',
'raw',
'trusted'),
true)) {
4328 $mode = (string) (
$value[
'mode'] ??
'starts_with');
4330 if (isset(
$value[
'like'])) {
4331 $likeValue = $this->escape_like((
string)
$value[
'like'],
$server);
4332 $pattern = $likeValue .
'%';
4334 if (
$mode ===
'contains') {
4335 $pattern =
'%' . $likeValue .
'%';
4336 } elseif (
$mode ===
'ends_with') {
4337 $pattern =
'%' . $likeValue;
4338 } elseif (
$mode ===
'exact') {
4339 $pattern = $likeValue;
4342 $conditions[] =
"$field LIKE '$pattern' ESCAPE '\\'";
4346 if (array_key_exists(
'value',
$value)) {
4354 $conditions[] =
"$field IS NULL";
4363 $conditions[] =
"$field = " . (int)
$value;
4365 $conditions[] =
"$field = '" . $this->escape((
string)
$value,
$server) .
"'";
4369 return $this->check_where(implode(
' AND ', $conditions),
$owner,
$dd);
4373 $key = $this->_fld_id ??
'id';
4375 if ($where ==
'new' || $where ==
'0') {
4388 $where =
"$key = $where";
4392 $uid =
dbx()->user();
4393 $owner_field =
'owner';
4397 $configured_owner_field = trim((
string)($table_def[
'owner_field'] ??
''));
4398 if ($configured_owner_field !==
'' && $this->is_fld_name(
$dd, $configured_owner_field)) {
4399 $owner_field = $configured_owner_field;
4404 $where =
'(' . $where .
') AND ' . $owner_field .
' = ' . $uid;
4406 $where = $owner_field .
' = ' . $uid;
4428 return str_replace(
"'",
"''", $string);
4431 return substr($this->db[
$server]->quote($string), 1, -1);
4471 $search = trim((string) $search);
4473 if ($search ===
'') {
4477 if (!$this->oValidator->validate($search,
'sqlsearch|max=128',
'search')) {
4478 dbx()->sys_msg(
'warning',
'db', $dd,
'invalid search', $search);
4488 $likeValue = $this->escape_like($search,
$server);
4489 $exactValue = $this->escape($search,
$server);
4490 $conditions = array();
4492 foreach ($likeFields as
$field) {
4499 $pattern = $likeValue;
4501 if (
$mode ===
'contains') {
4502 $pattern =
'%' . $likeValue .
'%';
4503 } elseif (
$mode ===
'ends_with') {
4504 $pattern =
'%' . $likeValue;
4505 } elseif (
$mode !==
'exact') {
4506 $pattern = $likeValue .
'%';
4509 $conditions[] =
"$field LIKE '$pattern' ESCAPE '\\'";
4512 foreach ($equalFields as
$field) {
4519 $conditions[] =
"$field = '$exactValue'";
4522 if (!count($conditions)) {
4526 return '(' . implode(
' OR ', $conditions) .
')';
4534 private function now_ms(): string {
4535 return (new DateTime())->format(
'Y-m-d H:i:s.v');
Zentrale Datenbank- und DD-Systemklasse von DBX.
insert($dd, $field_values, $verify_access=1, $verify_fields=1, $verify_values=1, $trace=1)
Fügt einen neuen Datensatz in die Datenbank ein.
build_search_where(string $dd, $search, array $likeFields, array $equalFields=array(), string $mode='starts_with')
Baut eine sichere Such-WHERE fuer einfache Report-Suchen.
$_report_error
1 = DB-Fehler automatisch in dbxSysMsg schreiben, 0 = nicht
get_dd_file(string $dd)
Liefert den Dateipfad einer geladenen DD fuer Editor-Marker.
$_validation_warning_flds
get_dd_autosync($dd, $rec=0)
Gibt das Autosync-Flag einer DD zurück.
select_tree(string $folder_dd, string $item_dd='', array $opt=[])
Liefert generische Tree-Daten aus einer Parent-Tabelle und optionalen Items.
save($dd, $field_values, $where, $verify_access=1, $verify_fields=1, $verify_values=1, $trace=1)
Speichert einen Datensatz mit UPSERT-ähnlichem Verhalten.
get_table_exist($dd, $dbtab='')
Prueft, ob eine Tabelle existiert.
db_server_config_is_active(string $server, array $dbConfig)
Prueft den Aktivstatus eines konfigurierten SQL-Servers.
can_connect_database_config(array $dbConfig, bool $withDatabase=false)
insert_query($server, $sql)
Führt eine INSERT-Abfrage aus und gibt die zuletzt eingefügte ID zurück.
get_error_status()
Liefert die Fehlerart des letzten DB-Aufrufs.
rawQuery(string $server, string $query)
Führt eine rohe SQL-Abfrage auf dem angegebenen Server aus.
sqlite_sequence_exists(string $server)
Prueft ohne Schema-Warnung, ob SQLite seine AUTOINCREMENT-Tabelle hat.
int $_connect_timeout
Maximale Wartezeit fuer einen DB-Verbindungsaufbau in Sekunden.
count($dd, $where='', $server='')
Zählt Datensätze einer DD oder Tabelle.
load_dd(string $dd)
Lädt eine Datenbeschreibung (DD) aus Datei und speichert sie im Session-Cache.
__destruct()
Gibt interne Referenzen beim Zerstören des Objekts frei.
get_dd_cols(string $dd)
Gibt alle DD-Spaltennamen als CSV-String zurück.
escape_like($value, $server)
Escaped einen User-Suchwert fuer SQL-LIKE.
get_dd_sort_desc($dd)
Platzhalter für Sortierrichtung einer DD.
optimize_tab($dd)
Optimiert eine Tabelle anhand ihrer DD nach Wartungsaktionen.
empty($dd)
Leert eine Tabelle anhand ihrer DD.
check_values($dd, $field_values, $verify_values=1)
Überprüft und validiert übergebene Feldwerte anhand der DD-Regeln.
exec(string $server, string $sql)
Führt eine SQL-Anweisung aus, die keine Daten zurückgibt (z.
dbConnect($server, $dbType, $dbHost, $dbName='', $dbUser='', $dbPass='', $dbPort='')
Stellt eine Verbindung zu einer Datenbank her und speichert sie in $this->db[$server].
get_error_text()
Liefert den Fehlertext des letzten DB-Aufrufs.
query(string $server, string $sql)
Führt eine SQL-Abfrage auf dem angegebenen Server aus.
update_query($server, $sql)
Führt eine UPDATE-Abfrage aus und gibt die Anzahl der betroffenen Zeilen zurück.
$_error_status
db|sql|access
select(string $dd='', $where='', $columns=' *', $orderby='', $asc_desc='ASC', $groupby='', $max=0, $offset=0, $verify_access=1)
Führt eine SELECT-Abfrage auf der angegebenen DD aus.
ensure_database_exists(string $server, array $dbConfig)
get_dd_server_binding_info(string $dd)
Liefert Herkunft und Ergebnis der Serveraufloesung einer DD.
begin(string $dd)
Startet eine Transaktion für die Datenbank der angegebenen DD.
delete_tab($dd)
Leert eine Datenbank-Tabelle anhand ihrer DD und setzt die ID zurueck.
get_dd_grid_cols(string $dd)
Erzeugt die neue Grid-Column-Syntax aus einer DD.
get_dd_table_def($dd)
Gibt die komplette Table-Definition einer DD zurück.
normalize_where(string $dd, $where='', int $owner=0)
Normalisiert und erweitert eine WHERE-Bedingung für SQL-Abfragen.
empty_record(string $dd)
Erstellt einen leeren Datensatz mit Standardwerten basierend auf der DD.
is_valid_dd_server_binding(string $server)
Prueft eine lokale DD-Serverbindung, ohne eine Verbindung aufzubauen.
create_select(string $select, array $flds, int $and=1)
Erzeugt eine SQL-WHERE-Bedingung für eine Volltextsuche über mehrere Felder.
add_db_fld($server, $table, $field)
Fügt ein neues Feld zu einer bestehenden Tabelle in der Datenbank hinzu.
rollback(string $dd)
Führt ein Rollback für die Datenbank der angegebenen DD aus.
get_dd_table($dd, $rec=0)
Gibt den Tabellennamen oder die komplette Table-Definition einer DD zurück.
check_access(string $mode, string $dd)
Prüft die Zugriffsrechte für eine Operation auf einer DD.
check_fields($dd, $field_values)
Filtert ungültige Felder aus einem Eingabearray heraus.
exec_query($server, $sql)
Führt eine generische SQL-Exec-Anweisung aus.
commit($dd)
Committet eine laufende Transaktion der Datenbank einer DD.
clear_db_error()
Setzt den letzten DB-Fehlerzustand zurueck.
__construct()
Initialisiert das DBX-Datenbankobjekt mit Standardwerten und lädt den zentralen Validator.
get_csv_seperator($dd)
Gibt das CSV-Trennzeichen für eine DD zurück.
get_insert_id()
Liefert die letzte Insert-ID nach erfolgreichem insert().
update($dd, $field_values, $where, $verify_access=1, $verify_fields=1, $verify_values=1, $trace=1)
Führt ein UPDATE-Statement auf einer Tabelle aus.
get_new_record(string $dd, array $field_values=[])
Erstellt einen neuen Datensatz mit Standardwerten.
get_rpt_fields($dd, $flds, $label=1)
Ermittelt die Report-Felder einer DD für eine übergebene Feldliste.
map_dd_type_to_grid_type(string $ddType)
Mappt DD-Feldtypen auf Grid-Feldtypen.
escape($string, $server)
Escaped einen String für die sichere Verwendung in einer SQL-Abfrage.
delete_query($server, $sql)
Führt eine DELETE-Abfrage aus und gibt die Anzahl der betroffenen Zeilen zurück.
get_dd_exist($dd)
Prüft, ob eine DD-Datei im Standardmodul dbx existiert.
get_dd_primary(string $dd)
Ermittelt den Primärschlüssel einer Datenbeschreibung (DD).
get_dd_server(string $dd)
Ermittelt den lokal wirksamen Server einer Datenbeschreibung.
isSQLiteDatabaseLocked($databasePath)
Prüft, ob eine SQLite-Datenbank aktuell gelockt ist.
get_db_type($server)
Ermittelt den Datenbanktyp für einen Server.
select_query(string $server, string $sql, string $dd='')
Fuehrt eine rohe SELECT-Abfrage auf einem konfigurierten DB-Server aus.
get_db_tables($server, $not='sqlite_sequence')
Liefert alle Tabellen eines Servers mit Datensatzanzahl zurück.
select1(string $dd, $where='', $columns=' *', $verify_access=1)
Führt eine SELECT-Abfrage aus und gibt genau einen Datensatz zurück.
get_dd_fields($dd, $label=0)
Gibt die Felddefinitionen einer DD zurück.
check_where($where, $owner=0, $dd='')
get_convert_array($field, $array, $convert='auto')
Konvertiert Array-Werte automatisch in String-/Serialisierungsformate.
get_dd_server_bindings()
Liefert die installationsbezogenen Serverbindungen der DDs.
connect_db_server(string $server)
Verbindet sich mit einem angegebenen Datenbankserver.
get_dd_sort_flds($dd)
Platzhalter für Sortierfelder einer DD.
user($key='id')
Liest einen Wert des aktuellen Benutzers.
get_base_dir(int $cutData=0)
Liefert das Basisverzeichnis der Installation.
dbx_lng_current()
Lädt eine Klasse aus dem Cache oder erstellt eine neue Instanz der Klasse.
can($access_groups='', $user_groups='')
Prueft Gruppenrechte gegen den aktuellen oder uebergebenen Benutzer.
is_int_value($value)
Prueft, ob ein Wert als Integer verwendbar ist.
if($stored !=='files/test/') $resolved
if($resolved !==$expectedBase . 'files/test/') $config
foreach($heroTemplates as $templateFile) $sections
if(!empty( $validation[ 'errors'])) if($service->isAllowedDesignFile('../escape.php')|| $service->isAllowedDesignFile('htm/evil.php')) $delta
if(array_key_exists('install', $otherModule)) $missingFile
if(!defined( 'IMG_WEBP')) define( 'IMG_WEBP'
if( $demoId<=0) foreach(array('create_date', 'create_uid', 'update_date', 'update_uid', 'owner',) as $systemField) $items
DBX schema administration.