2include_once
'dbxDB.class.php';
69 parent::__construct();
87 $this->_max_step_runtime = $seconds;
110 if ($chunk_size > 0) {
111 $this->_chunk_size = $chunk_size;
144 return parent::load_dd($dd);
166 'dd_status' => $dd_sys[
'dd_status'] ?? 0,
167 'dd_modul' => $dd_sys[
'dd_modul'] ??
'',
168 'dd_name' => $dd_sys[
'dd_name'] ??
'',
186 $dd_status = $dd_sys[
'dd_status'] ?? 0;
187 $dd_modul = $dd_sys[
'dd_modul'] ??
'';
188 $dd_name = $dd_sys[
'dd_name'] ??
'';
190 if ($dd_modul && $dd_name && isset($_SESSION[
'dbx'][
'cache'][
'dd'][$dd_modul][$dd_name])) {
191 unset($_SESSION[
'dbx'][
'cache'][
'dd'][$dd_modul][$dd_name]);
200 $activ_modul =
dbx()->get_system_var(
'dbx_activ_modul',
'dbx');
202 if (strpos($dd,
'|') !==
false) {
203 $parts = explode(
'|', $dd, 2);
204 $dd_modul = trim($parts[0]);
205 $dd_name = trim($parts[1]);
207 if ($dd_modul ===
'modul' || $dd_modul ===
'') {
208 $dd_modul = $activ_modul;
211 $dd_modul = $activ_modul;
215 if (isset($_SESSION[
'dbx'][
'cache'][
'dd'][$dd_modul][$dd_name])) {
216 unset($_SESSION[
'dbx'][
'cache'][
'dd'][$dd_modul][$dd_name]);
230 $dd_status = $dd_sys[
'dd_status'] ?? 0;
231 $dd_modul = $dd_sys[
'dd_modul'] ??
'';
232 $dd_name = $dd_sys[
'dd_name'] ??
'';
234 if ($dd_status <= 0) {
238 return $_SESSION[
'dbx'][
'cache'][
'dd'][$dd_modul][$dd_name][
'indexes'] ?? [];
256 $dd_status = $dd_sys[
'dd_status'] ?? 0;
257 $dd_modul = $dd_sys[
'dd_modul'] ??
'';
258 $dd_name = $dd_sys[
'dd_name'] ??
'';
260 if ($dd_status <= 0) {
265 'table' => $_SESSION[
'dbx'][
'cache'][
'dd'][$dd_modul][$dd_name][
'table'] ?? [],
266 'fields' => $_SESSION[
'dbx'][
'cache'][
'dd'][$dd_modul][$dd_name][
'fields'] ?? [],
267 'indexes' => $_SESSION[
'dbx'][
'cache'][
'dd'][$dd_modul][$dd_name][
'indexes'] ?? [],
285 $activ_modul =
dbx()->get_system_var(
'dbx_activ_modul',
'dbx');
287 if (strpos($dd,
'|') !==
false) {
288 $parts = explode(
'|', $dd, 2);
289 $dd_modul = trim($parts[0]);
290 $dd_name = trim($parts[1]);
292 if ($dd_modul ===
'modul' || $dd_modul ===
'') {
293 $dd_modul = $activ_modul;
296 $dd_modul = $activ_modul;
303 return file_exists($dd_file1) || file_exists($dd_file2);
321 if (file_exists($file)) {
354 if (!file_exists($file)) {
355 dbx()->sys_msg(
'error',
'dd', $template_name,
'missing template', $file);
359 $content = file_get_contents($file);
360 if ($content ===
false) {
365 foreach ($vars as $key => $value) {
366 if (is_array($value)) {
367 foreach ($value as $k => $v) {
371 $replace[
'{' . $key .
'}'] = (string)$value;
375 return strtr($content, $replace);
389 $modul =
dbx()->get_system_var(
'dbx_activ_modul',
'dbx');
443 $fields_block = trim($fields_block);
450 $indexes_block = trim($indexes_block);
453 'table_block' => $table_block,
454 'fields_block' => $fields_block,
455 'indexes_block' => $indexes_block,
459 dbx()->sys_msg(
'error',
'dd', $dd,
'write_dd',
'empty dd content');
464 $dir = dirname($file);
467 @mkdir(
$dir, 0777,
true);
470 $ok = file_put_contents($file, $content);
472 dbx()->sys_msg(
'error',
'dd', $dd,
'write_dd', $file);
573 'server' =>
$table[
'server'] ??
'dbXsystem',
574 'table' =>
$table[
'table'] ?? $dd,
584 'default_sort' =>
'',
585 'form-dd-table'=>
'',
593 'create_owner' =>
'*',
594 'update_owner' =>
'*',
595 'delete_owner' =>
'*',
598 foreach ($defaults as $k => $v) {
640 foreach ($defaults as $k => $v) {
666 foreach ($defaults as $k => $v) {
684 return in_array(strtolower($name), [
'id',
'create_date',
'create_uid',
'update_date',
'update_uid',
'owner'],
true);
697 return str_replace(
'_',
' ', ucfirst($name));
712 $name = strtolower((
string)(
$field[
'name'] ??
''));
713 $type = strtolower((
string)(
$field[
'type'] ??
'varchar'));
714 $length = trim((
string)(
$field[
'length'] ??
''));
716 if ($name ===
'id') {
720 if (in_array($type, [
'bool',
'boolean',
'bit'],
true)) {
724 if (in_array($type, [
'int',
'integer',
'bigint',
'smallint',
'mediumint',
'tinyint'],
true)) {
728 if (in_array($type, [
'decimal',
'numeric'],
true)) {
732 if (in_array($type, [
'float',
'double',
'real'],
true)) {
736 if (in_array($type, [
'date',
'datetime',
'timestamp'],
true)) {
740 if ($type ===
'time' || $type ===
'year') {
744 if (($type ===
'tinyint' || $type ===
'int') && $length ===
'1') {
760 $type = strtolower((
string)(
$field[
'type'] ??
'varchar'));
761 $length = trim((
string)(
$field[
'length'] ??
''));
763 if (in_array($type, [
'bool',
'boolean',
'bit'],
true)
764 || (in_array($type, [
'tinyint',
'int',
'integer'],
true) && $length ===
'1')) {
765 return 'checkbox-label';
768 if (in_array($type, [
'int',
'integer',
'bigint',
'smallint',
'mediumint',
'tinyint'],
true)) {
769 return 'integer-label';
772 if ($type ===
'date') {
776 if (in_array($type, [
'datetime',
'timestamp'],
true)) {
777 return 'datetime-label';
780 if (in_array($type, [
'text',
'mediumtext',
'longtext'],
true)) {
781 return 'textarea-label';
796 if ($value ===
null) {
800 $value = trim((
string)$value);
801 $value = trim($value,
" \t\n\r\0\x0B'\"");
821 if (preg_match(
'/^([a-zA-Z0-9_]+)\s*(?:\((.*?)\))?/i', $type, $m)) {
823 'type' => strtolower($m[1]),
824 'length' => (string)($m[2] ??
''),
829 'type' => strtolower($type),
856 $dbType = strtolower(trim($dbType));
857 $rawType = strtolower(trim($rawType));
858 $length = trim($length);
860 if (in_array($rawType, [
'bool',
'boolean'],
true)) {
864 if (in_array($rawType, [
'tinyint',
'smallint',
'mediumint',
'bigint'],
true)) {
868 if (in_array($rawType, [
'int',
'integer',
'serial',
'number'],
true)) {
872 if (in_array($rawType, [
'bit'],
true)) {
876 if (in_array($rawType, [
'date',
'time',
'year'],
true)) {
880 if (in_array($rawType, [
'datetime',
'timestamp'],
true)) {
884 if (in_array($rawType, [
'char',
'nchar'],
true)) {
888 if (in_array($rawType, [
'varchar',
'varchar2',
'nvarchar',
'string'],
true)) {
892 if (in_array($rawType, [
'tinytext',
'mediumtext',
'longtext'],
true)) {
896 if (in_array($rawType, [
'text',
'clob'],
true)) {
900 if (in_array($rawType, [
'decimal',
'numeric'],
true)) {
904 if (in_array($rawType, [
'float',
'real'],
true)) {
908 if (in_array($rawType, [
'double'],
true)) {
912 if (in_array($rawType, [
'binary',
'varbinary',
'tinyblob',
'blob',
'mediumblob',
'longblob',
'json',
'enum',
'set'],
true)) {
916 if ($dbType ===
'sqlite') {
956 foreach ($rows as $row) {
959 (
string)($row[
'Field'] ??
''),
961 (
string)($typeInfo[
'type'] ??
'varchar'),
962 (
string)($typeInfo[
'length'] ??
''),
963 ((
string)($row[
'Null'] ??
'YES') ===
'YES') ?
'1' :
'0',
964 $row[
'Default'] ??
'',
965 (
string)($row[
'Key'] ??
'')
974 foreach ($rows as $row) {
976 $index = ((int)($row[
'pk'] ?? 0) === 1) ?
'PRI' :
'';
979 (
string)($row[
'name'] ??
''),
981 (
string)($typeInfo[
'type'] ??
'text'),
982 (
string)($typeInfo[
'length'] ??
''),
983 ((
int)($row[
'notnull'] ?? 0) === 1) ?
'0' :
'1',
984 $row[
'dflt_value'] ??
'',
992 SELECT column_name, data_type, character_maximum_length, is_nullable, column_default
993 FROM information_schema.columns
995 ORDER BY ordinal_position
999 foreach ($rows as $row) {
1001 (
string)($row[
'column_name'] ??
''),
1003 (
string)($row[
'data_type'] ??
'text'),
1004 (
string)($row[
'character_maximum_length'] ??
''),
1005 ((
string)($row[
'is_nullable'] ??
'YES') ===
'YES') ?
'1' :
'0',
1006 $row[
'column_default'] ??
'',
1014 SELECT COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH, IS_NULLABLE, COLUMN_DEFAULT
1015 FROM INFORMATION_SCHEMA.COLUMNS
1017 ORDER BY ORDINAL_POSITION
1021 foreach ($rows as $row) {
1023 (
string)($row[
'COLUMN_NAME'] ??
''),
1025 (
string)($row[
'DATA_TYPE'] ??
'varchar'),
1026 (
string)($row[
'CHARACTER_MAXIMUM_LENGTH'] ??
''),
1027 ((
string)($row[
'IS_NULLABLE'] ??
'YES') ===
'YES') ?
'1' :
'0',
1028 $row[
'COLUMN_DEFAULT'] ??
'',
1036 SELECT column_name, data_type, data_length, nullable, data_default
1037 FROM user_tab_columns
1043 foreach ($rows as $row) {
1045 (
string)($row[
'COLUMN_NAME'] ??
''),
1047 (
string)($row[
'DATA_TYPE'] ??
'varchar2'),
1048 (
string)($row[
'DATA_LENGTH'] ??
''),
1049 ((
string)($row[
'NULLABLE'] ??
'Y') ===
'Y') ?
'1' :
'0',
1050 $row[
'DATA_DEFAULT'] ??
'',
1080 $sql =
"SHOW INDEX FROM " . $this->
quote_ident($server, $tableName);
1081 $rows = $this->
rawQuery($server, $sql);
1084 foreach ($rows as $row) {
1085 $keyName = (string)($row[
'Key_name'] ??
'');
1086 $colName = (string)($row[
'Column_name'] ??
'');
1087 $nonUnique = (int)($row[
'Non_unique'] ?? 1);
1089 if (!isset($grouped[$keyName])) {
1090 $grouped[$keyName] = [
1092 'type' => ($keyName ===
'PRIMARY') ?
'PRIMARY' : (($nonUnique === 0) ?
'UNIQUE' :
'INDEX'),
1094 'unique' => ($keyName ===
'PRIMARY' || $nonUnique === 0) ?
'1' :
'0',
1099 $grouped[$keyName][
'fields'][] = $colName;
1102 foreach ($grouped as $idx) {
1103 $idx[
'fields'] = implode(
',', $idx[
'fields']);
1109 $sql =
"PRAGMA index_list(" . $this->
quote_ident($server, $tableName) .
")";
1110 $rows = $this->
rawQuery($server, $sql);
1112 foreach ($rows as $row) {
1113 $name = (string)($row[
'name'] ??
'');
1114 $unique = ((int)($row[
'unique'] ?? 0) === 1) ?
'1' :
'0';
1115 $origin = (string)($row[
'origin'] ??
'');
1117 $sql2 =
"PRAGMA index_info(" . $this->
quote_ident($server, $name) .
")";
1118 $cols = $this->
rawQuery($server, $sql2);
1121 foreach ($cols as $col) {
1122 $fields[] = (string)($col[
'name'] ??
'');
1126 if ($origin ===
'pk') {
1129 } elseif ($unique ===
'1') {
1136 'fields' => implode(
',',
$fields),
1137 'unique' => $unique,
1145 SELECT indexname, indexdef
1147 WHERE tablename = " . $this->
sql_quote($server, $tableName);
1148 $rows = $this->
rawQuery($server, $sql);
1150 foreach ($rows as $row) {
1151 $name = (string)($row[
'indexname'] ??
'');
1152 $def = (string)($row[
'indexdef'] ??
'');
1154 preg_match(
'/\((.*?)\)/', $def, $m);
1158 $unique = (stripos($def,
'UNIQUE INDEX') !==
false) ?
'1' :
'0';
1159 $type = ($unique ===
'1') ?
'UNIQUE' :
'INDEX';
1165 'unique' => $unique,
1202 'index' => strtoupper((
string)
$index),
1203 'length' => $length,
1209 'placeholder' =>
'',
1256 return match ($dbType) {
1257 'mysql' =>
'`' . str_replace(
'`',
'``', $name) .
'`',
1258 'sqlsrv' =>
'[' . str_replace(
']',
']]', $name) .
']',
1259 default =>
'"' . str_replace(
'"',
'""', $name) .
'"',
1279 $type = strtolower(trim($type));
1280 $len = trim($length);
1284 return match ($type) {
1304 'longblob' =>
'BLOB',
1309 return match ($type) {
1310 'tinyint' =>
'TINYINT' . ($len !==
'' ?
'(' . $len .
')' :
''),
1311 'smallint' =>
'SMALLINT' . ($len !==
'' ?
'(' . $len .
')' :
''),
1312 'mediumint' =>
'MEDIUMINT' . ($len !==
'' ?
'(' . $len .
')' :
''),
1314 'integer' =>
'INT' . ($len !==
'' ?
'(' . $len .
')' :
'(11)'),
1315 'bigint' =>
'BIGINT' . ($len !==
'' ?
'(' . $len .
')' :
''),
1317 'numeric' =>
'DECIMAL' . ($len !==
'' ?
'(' . $len .
')' :
'(10,2)'),
1318 'float' =>
'FLOAT' . ($len !==
'' ?
'(' . $len .
')' :
''),
1320 'real' =>
'DOUBLE' . ($len !==
'' ?
'(' . $len .
')' :
''),
1321 'bit' =>
'BIT' . ($len !==
'' ?
'(' . $len .
')' :
'(1)'),
1322 'char' =>
'CHAR(' . ($len !==
'' ? $len :
'1') .
')',
1323 'binary' =>
'BINARY(' . ($len !==
'' ? $len :
'1') .
')',
1324 'varbinary' =>
'VARBINARY(' . ($len !==
'' ? $len :
'255') .
')',
1325 'tinytext' =>
'TINYTEXT',
1327 'mediumtext' =>
'MEDIUMTEXT',
1328 'longtext' =>
'LONGTEXT',
1329 'tinyblob' =>
'TINYBLOB',
1331 'mediumblob' =>
'MEDIUMBLOB',
1332 'longblob' =>
'LONGBLOB',
1334 'enum' => ($len !==
'' ?
'ENUM(' . $len .
')' :
'VARCHAR(255)'),
1335 'set' => ($len !==
'' ?
'SET(' . $len .
')' :
'VARCHAR(255)'),
1339 'datetime' =>
'DATETIME',
1340 'timestamp' =>
'TIMESTAMP',
1341 default =>
'VARCHAR(' . ($len !==
'' ? $len :
'255') .
')',
1345 return match ($type) {
1346 'int',
'integer',
'mediumint' =>
'INTEGER',
1347 'bigint' =>
'BIGINT',
1349 'tinyint' =>
'SMALLINT',
1351 'numeric' =>
'NUMERIC' . ($len !==
'' ?
'(' . $len .
')' :
''),
1354 'double' =>
'DOUBLE PRECISION',
1357 'timestamp' =>
'TIMESTAMP',
1360 'longtext' =>
'TEXT',
1361 'char' =>
'CHAR(' . ($len !==
'' ? $len :
'1') .
')',
1362 default =>
'VARCHAR(' . ($len !==
'' ? $len :
'255') .
')',
1366 return match ($type) {
1367 'int',
'integer',
'mediumint' =>
'INTEGER',
1368 'bigint' =>
'BIGINT',
1370 'tinyint' =>
'SMALLINT',
1372 'numeric' =>
'DECIMAL' . ($len !==
'' ?
'(' . $len .
')' :
''),
1378 'timestamp' =>
'TIMESTAMP',
1381 'longtext' =>
'TEXT',
1382 default =>
'VARCHAR(' . ($len !==
'' ? $len :
'255') .
')',
1398 $default = (string)$default;
1399 $u = strtoupper($default);
1401 if ($u ===
'CURRENT_TIMESTAMP') {
1402 return 'DEFAULT CURRENT_TIMESTAMP';
1406 'int',
'integer',
'bigint',
'smallint',
'mediumint',
'tinyint',
'bit',
1407 'decimal',
'numeric',
'float',
'double',
'real',
1410 if (in_array(strtolower($ddType), $numericTypes,
true) && is_numeric($default)) {
1411 return 'DEFAULT ' . $default;
1428 $name =
$field[
'name'] ??
'';
1429 $type = strtolower((
string)(
$field[
'type'] ??
'varchar'));
1430 $length = (string)(
$field[
'length'] ??
'');
1432 $default =
$field[
'default'] ??
'';
1437 if (
$index ===
'PRI' && strtolower($name) ===
'id') {
1443 return $this->
quote_ident(
$server, $name) .
' ' . $sqlType .
' AUTO_INCREMENT PRIMARY KEY';
1448 return $this->
quote_ident(
$server, $name) .
' INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY';
1453 return $this->
quote_ident(
$server, $name) .
' NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY';
1461 if ($default !==
'' && $default !==
null) {
1489 $normalized = array();
1492 $name = strtolower(trim((
string)(
$field[
'name'] ??
'')));
1498 if ($name ===
'id') {
1499 $idField = array_merge(
$field, $idField);
1503 if (strtoupper((
string)(
$field[
'index'] ??
'')) ===
'PRI') {
1510 array_unshift($normalized, $idField);
1520 return in_array($dbType, array(
1521 'sqlite',
'mysql',
'pgsql',
'sqlsrv',
'oci',
'firebird',
1522 'cubrid',
'dblib',
'ibm',
'informix',
'odbc',
1541 $primaryFields = [];
1545 if (strtoupper((
string)(
$field[
'index'] ??
'')) ===
'PRI') {
1546 $isInlinePrimary = strtolower((
string)(
$field[
'name'] ??
'')) ===
'id'
1548 if (!$isInlinePrimary) {
1549 $primaryFields[] =
$field[
'name'];
1554 if ($primaryFields) {
1556 foreach ($primaryFields as $fld) {
1559 $parts[] =
'PRIMARY KEY (' . implode(
',', $quoted) .
')';
1563 $sql .=
' ' . implode(
",\n ", $parts) .
"\n";
1580 $name = (string)(
$index[
'name'] ??
'');
1581 $type = strtoupper((
string)(
$index[
'type'] ??
'INDEX'));
1582 $fields = array_filter(array_map(
'trim', explode(
',', (
string)(
$index[
'fields'] ??
''))));
1594 if ($type ===
'UNIQUE') {
1597 .
' (' . implode(
',', $quotedFields) .
')';
1598 } elseif ($type !==
'PRIMARY') {
1601 .
' (' . implode(
',', $quotedFields) .
')';
1605 return ($type ===
'PRIMARY') ? 1 : 0;
1623 if (!preg_match(
'/\.(db3|sqlite|sqlite3)$/i',
$server)) {
1628 $modul = $dd_sys[
'dd_modul'] ??
'';
1631 if (strpos(
$server,
'|') !==
false) {
1632 $parts = explode(
'|',
$server, 2);
1633 $modul = trim($parts[0]);
1634 $name = trim($parts[1]);
1637 if ($modul ===
'' || $modul ===
'modul') {
1638 $modul =
dbx()->get_system_var(
'dbx_activ_modul',
'dbx');
1641 if ($modul ===
'') {
1646 if (!is_dir(
$dir) && !@mkdir(
$dir, 0777,
true)) {
1647 dbx()->sys_msg(
'error',
'db',
$server,
'create sqlite dir failed',
$dir);
1652 if (!file_exists($file) && @file_put_contents($file,
'') ===
false) {
1653 dbx()->sys_msg(
'error',
'db',
$server,
'create sqlite file failed', $file);
1659 $_SESSION[
'dbx'][
'config'][
'dbx'][
'db'][
$server] = [
1662 'dbname' => basename($file),
1708 if (strtoupper((
string)(
$index[
'type'] ??
'INDEX')) ===
'PRIMARY') {
1776 $primaryFields = [];
1785 if (strtoupper((
string)(
$field[
'index'] ??
'')) ===
'PRI') {
1786 $isInlinePrimary = strtolower((
string)(
$field[
'name'] ??
'')) ===
'id'
1788 if (!$isInlinePrimary) {
1789 $primaryFields[] =
$field[
'name'];
1798 if ($primaryFields) {
1800 foreach ($primaryFields as $fld) {
1803 $parts[] =
'PRIMARY KEY (' . implode(
',', $quoted) .
')';
1807 $sql .=
' ' . implode(
",\n ", $parts) .
"\n";
1816 if (strtoupper((
string)(
$index[
'type'] ??
'INDEX')) ===
'PRIMARY') {
1859 $ok = $this->
exec_query(
$server,
'TRUNCATE TABLE ' . $qTable .
' RESTART IDENTITY');
1880 $serverKey = strtolower(trim(
$server));
1881 $tableKey = strtolower(trim(
$table));
1883 if ($serverKey ===
'' || $tableKey ===
'') {
1888 foreach (glob(
$base) as $file) {
1889 $norm = str_replace(
'\\',
'/', $file);
1890 if (!preg_match(
'#/dbx/modules/([^/]+)/dd/([^/]+)\.dd\.php$#', $norm, $match)) {
1896 if ($dd ===
'new' || $dd ===
'') {
1900 $ddRef = $modul .
'|' . $dd;
1906 $modelServer = strtolower((
string)($model[
'table'][
'server'] ??
''));
1907 $modelTable = strtolower((
string)($model[
'table'][
'table'] ??
''));
1908 $serverMatch = ($modelServer === $serverKey);
1910 if (!$serverMatch && strpos($serverKey,
'|') !==
false) {
1911 [$serverModul, $serverName] = array_pad(explode(
'|', $serverKey, 2), 2,
'');
1912 $serverMatch = ($serverModul === strtolower($modul))
1913 && ($modelServer === $serverName || $modelServer ===
'modul');
1916 if ($serverMatch && $modelTable === $tableKey) {
1943 if ($match && !
empty($match[
'model'][
'fields'])) {
1946 'dd_ref' => $match[
'dd_ref'],
1947 'fields' => $match[
'model'][
'fields'] ?? [],
1948 'indexes' => $match[
'model'][
'indexes'] ?? [],
1972 string $sourceServer,
1973 string $sourceTable,
1974 string $targetServer,
1975 string $targetTable =
'',
1976 string $mode =
'step',
1977 int $createTarget = 1,
1978 int $truncateTarget = 1
1980 $targetTable = $targetTable ?: $sourceTable;
1981 $mode = strtolower((
string)$mode);
1986 $key = $this->proc_key(
'transfer_table', [
1995 if ($mode ===
'reset') {
1996 $this->clear_proc_state($key);
1999 'status' =>
'reset',
2000 'message' =>
'transfer state cleared',
2002 'step_percent' => 0,
2006 if ($mode ===
'status') {
2007 $state = $this->get_proc_state($key);
2011 'message' =>
'no active state',
2013 'step_percent' => 0,
2017 if (in_array($mode, [
'pause',
'resume',
'continue',
'cancel'],
true)) {
2018 return $this->control_proc_state($key, $mode);
2021 if ($mode ===
'restart') {
2022 $this->clear_proc_state($key);
2026 $state = $this->get_proc_state($key);
2029 if (!$sourceServer || !$sourceTable || !$targetServer || !$targetTable) {
2030 return $this->proc_error([
'proc_key' => $key],
'source or target missing');
2033 if (!$this->get_table_exist($sourceServer, $sourceTable)) {
2034 return $this->proc_error([
'proc_key' => $key],
'source table not found');
2037 $state = $this->init_proc_state(
'transfer_table', $key, [
2038 'source_server' => $sourceServer,
2039 'source_table' => $sourceTable,
2040 'target_server' => $targetServer,
2041 'target_table' => $targetTable,
2042 'create_target' => $createTarget ? 1 : 0,
2043 'truncate_target' => $truncateTarget ? 1 : 0,
2044 'backup_file' => $this->build_backup_file_name($sourceServer, $sourceTable,
true),
2045 'phase' =>
'prepare_target',
2047 'message' =>
'transfer initialized',
2051 if ($this->proc_is_waiting($state)) {
2052 return $this->proc_response($state);
2055 switch ($state[
'phase']) {
2056 case 'prepare_target':
2057 if (!$this->get_table_exist($state[
'target_server'], $state[
'target_table'])) {
2058 if (empty($state[
'create_target'])) {
2059 $state = $this->proc_error($state,
'target table not found');
2060 $this->set_proc_state($key, $state);
2064 $schema = $this->get_preferred_table_schema($state[
'source_server'], $state[
'source_table']);
2065 $fields = $schema[
'fields'] ?? [];
2066 $indexes = $schema[
'indexes'] ?? [];
2067 $ok = $this->create_db_tab_from_fields($state[
'target_server'], $state[
'target_table'],
$fields,
$indexes);
2070 $state = $this->proc_error($state,
'create target table failed');
2071 $this->set_proc_state($key, $state);
2076 if (!empty($state[
'truncate_target'])) {
2077 $this->empty_db_table($state[
'target_server'], $state[
'target_table']);
2080 $state[
'phase'] =
'backup_source';
2081 $state[
'percent'] = 10;
2082 $state[
'step_percent'] = 100;
2083 $state[
'message'] =
'target ready';
2084 $this->set_proc_state($key, $state);
2085 return $this->proc_response($state);
2087 case 'backup_source':
2088 $backup = $this->backup($state[
'source_server'], $state[
'source_table'], $state[
'backup_file'], 1);
2090 if (($backup[
'status'] ??
'') ===
'error') {
2091 $state = $this->proc_error($state,
'backup source failed');
2092 $this->set_proc_state($key, $state);
2096 if (($backup[
'status'] ??
'') !==
'finished') {
2097 $state[
'percent'] = 10 + (int)floor((($backup[
'percent'] ?? 0) * 0.4));
2098 $state[
'step_percent'] = (int)($backup[
'percent'] ?? 0);
2099 $state[
'message'] = $backup[
'message'] ??
'backup source';
2100 $this->set_proc_state($key, $state);
2101 return $this->proc_response($state);
2104 $state[
'phase'] =
'restore_target';
2105 $state[
'percent'] = 55;
2106 $state[
'step_percent'] = 100;
2107 $state[
'message'] =
'backup finished';
2108 $this->set_proc_state($key, $state);
2109 return $this->proc_response($state);
2111 case 'restore_target':
2112 $restore = $this->restore($state[
'target_server'], $state[
'target_table'], $state[
'backup_file'], [], 1);
2114 if (($restore[
'status'] ??
'') ===
'error') {
2115 $state = $this->proc_error($state,
'restore target failed');
2116 $this->set_proc_state($key, $state);
2120 if (($restore[
'status'] ??
'') !==
'finished') {
2121 $state[
'percent'] = 55 + (int)floor((($restore[
'percent'] ?? 0) * 0.4));
2122 $state[
'step_percent'] = (int)($restore[
'percent'] ?? 0);
2123 $state[
'message'] = $restore[
'message'] ??
'restore target';
2124 $this->set_proc_state($key, $state);
2125 return $this->proc_response($state);
2128 $state = $this->proc_finish($state,
'transfer finished');
2129 $this->set_proc_state($key, $state);
2133 $state = $this->proc_error($state,
'unknown transfer phase');
2134 $this->set_proc_state($key, $state);
2151 protected function proc_key(
string $type, array $parts): string
2153 return 'dbxdd_' . $type .
'_' . md5(json_encode($parts));
2165 $state =
dbx()->get_remember_var($key, [], $this->_remember_modul);
2166 return is_array($state) ? $state : [];
2179 dbx()->set_remember_var($key, $state, $this->_remember_modul);
2191 dbx()->set_remember_var($key, [], $this->_remember_modul);
2205 $state[
'proc_type'] = $type;
2206 $state[
'proc_key'] = $key;
2207 $state[
'status'] = $state[
'status'] ??
'running';
2208 $state[
'message'] = $state[
'message'] ??
'';
2209 $state[
'percent'] = $state[
'percent'] ?? 0;
2210 $state[
'step_percent'] = $state[
'step_percent'] ?? 0;
2211 $state[
'chunk_size'] = $state[
'chunk_size'] ?? $this->_chunk_size;
2212 $state[
'step_maxsec'] = $state[
'step_maxsec'] ?? $this->_max_step_runtime;
2213 $state[
'started_at'] = $state[
'started_at'] ?? date(
'Y-m-d H:i:s');
2214 $state[
'updated_at'] = date(
'Y-m-d H:i:s');
2228 return in_array(($state[
'status'] ??
''), [
'finished',
'error',
'paused',
'canceled'],
true);
2241 $cmd = strtolower(trim($cmd));
2243 if ($cmd ===
'restart') {
2247 'status' =>
'reset',
2248 'message' =>
'process restarted',
2250 'step_percent' => 0,
2251 'updated_at' => date(
'Y-m-d H:i:s'),
2260 'message' =>
'no active state',
2262 'step_percent' => 0,
2266 $status = $state[
'status'] ??
'new';
2268 if ($cmd ===
'pause') {
2269 if (!in_array($status, [
'finished',
'error',
'canceled'],
true)) {
2270 $state[
'status'] =
'paused';
2271 $state[
'paused_at'] = date(
'Y-m-d H:i:s');
2272 $state[
'message'] =
'process paused';
2274 } elseif ($cmd ===
'resume' || $cmd ===
'continue') {
2275 if (in_array($status, [
'paused',
'canceled',
'new'],
true)) {
2276 $state[
'status'] =
'running';
2277 $state[
'resumed_at'] = date(
'Y-m-d H:i:s');
2278 $state[
'message'] = ($cmd ===
'continue') ?
'process continued' :
'process resumed';
2280 } elseif ($cmd ===
'cancel') {
2281 if (!in_array($status, [
'finished',
'error'],
true)) {
2282 $state[
'status'] =
'canceled';
2283 $state[
'canceled_at'] = date(
'Y-m-d H:i:s');
2284 $state[
'message'] =
'process canceled';
2302 $state[
'updated_at'] = date(
'Y-m-d H:i:s');
2314 protected function proc_error(array $state,
string $message): array
2316 $state[
'status'] =
'error';
2317 $state[
'message'] = $message;
2318 $state[
'percent'] = $state[
'percent'] ?? 0;
2319 $state[
'step_percent'] = $state[
'step_percent'] ?? 0;
2331 protected function proc_finish(array $state,
string $message =
'finished'): array
2333 $state[
'status'] =
'finished';
2334 $state[
'message'] = $message;
2335 $state[
'percent'] = 100;
2336 $state[
'step_percent'] = 100;
2337 $state[
'finished_at'] = date(
'Y-m-d H:i:s');
2348 return microtime(
true);
2361 return (microtime(
true) - $started_at) < $max_seconds;
2378 return strtolower(preg_replace(
'/[^a-z0-9]+/i',
'', trim($name)));
2390 $value = preg_replace(
'/[^A-Za-z0-9_.-]+/',
'_', trim($value));
2391 $value = trim((
string)$value,
'._-');
2393 return $value !==
'' ? $value :
'default';
2404 if (!is_dir(
$dir)) {
2405 @mkdir(
$dir, 0777,
true);
2421 $kind = strtolower(trim($kind));
2425 $context[
'modul'] ??
'',
2426 $context[
'dd'] ??
'',
2427 $context[
'source_server'] ?? ($context[
'server'] ??
''),
2428 $context[
'source_table'] ?? ($context[
'table'] ??
''),
2429 $context[
'target_server'] ??
'',
2430 $context[
'target_table'] ??
'',
2450 $name = (string)(
$field[
'name'] ??
'');
2463 foreach ($keys as $key) {
2464 if (array_key_exists($key, $record)) {
2465 $sorted[$key] = $record[$key];
2466 unset($record[$key]);
2470 foreach ($record as $key => $value) {
2471 $sorted[$key] = $value;
2488 $sourceByLower = [];
2489 foreach ($sourceFields as
$field) {
2490 $name = (string)(
$field[
'name'] ??
'');
2492 $sourceByLower[strtolower($name)] = $name;
2496 $targetByLower = [];
2497 foreach ($targetFields as
$field) {
2498 $name = (string)(
$field[
'name'] ??
'');
2500 $targetByLower[strtolower($name)] = $name;
2505 foreach ($mapping as $source => $target) {
2506 $sourceKey = strtolower(trim((
string)$source));
2507 $targetKey = strtolower(trim((
string)$target));
2509 if ($sourceKey ===
'' || $targetKey ===
'') {
2513 if (!isset($sourceByLower[$sourceKey]) || !isset($targetByLower[$targetKey])) {
2517 $sourceName = $sourceByLower[$sourceKey];
2518 $targetName = $targetByLower[$targetKey];
2520 foreach ($clean as $oldSource => $oldTarget) {
2521 if (strcasecmp($oldTarget, $targetName) === 0) {
2522 unset($clean[$oldSource]);
2526 $clean[$sourceName] = $targetName;
2547 foreach ($sourceFields as
$field) {
2548 $name = (string)(
$field[
'name'] ??
'');
2553 $sourceExact[strtolower($name)] = $name;
2555 if ($norm !==
'' && !isset($sourceNorm[$norm])) {
2556 $sourceNorm[$norm] = $name;
2563 foreach ($targetFields as
$field) {
2564 $target = (string)(
$field[
'name'] ??
'');
2565 if ($target ===
'') {
2569 $source = $sourceExact[strtolower($target)] ??
'';
2570 if ($source !==
'') {
2571 $mapping[$source] = $target;
2572 $usedSource[strtolower($source)] =
true;
2576 foreach ($targetFields as
$field) {
2577 $target = (string)(
$field[
'name'] ??
'');
2578 if ($target ===
'') {
2582 $alreadyMapped =
false;
2583 foreach ($mapping as $mappedTarget) {
2584 if (strcasecmp($mappedTarget, $target) === 0) {
2585 $alreadyMapped =
true;
2590 if ($alreadyMapped) {
2595 $source = $sourceNorm[$norm] ??
'';
2596 if ($source !==
'' &&
empty($usedSource[strtolower($source)])) {
2597 $mapping[$source] = $target;
2598 $usedSource[strtolower($source)] =
true;
2603 foreach ($stored as $source => $target) {
2604 foreach ($mapping as $oldSource => $oldTarget) {
2605 if (strcasecmp($oldSource, $source) === 0 || strcasecmp($oldTarget, $target) === 0) {
2606 unset($mapping[$oldSource]);
2610 $mapping[$source] = $target;
2628 if (!is_file($file)) {
2630 'kind' => strtolower(trim($kind)),
2631 'context' => $context,
2638 $data = json_decode((
string)file_get_contents($file),
true);
2639 if (!is_array($data)) {
2643 $data[
'kind'] = $data[
'kind'] ?? strtolower(trim($kind));
2644 $data[
'context'] = is_array($data[
'context'] ??
null) ? $data[
'context'] : $context;
2645 $data[
'mapping'] = is_array($data[
'mapping'] ??
null) ? $data[
'mapping'] : [];
2646 $data[
'file'] = $file;
2663 return is_array($data[
'mapping'] ??
null) ? $data[
'mapping'] : [];
2676 $kind = strtolower(trim($kind));
2681 $modul = (string)($context[
'modul'] ??
'');
2682 $dd = (string)($context[
'dd'] ??
'');
2683 $server = (string)($context[
'server'] ?? ($context[
'source_server'] ??
''));
2684 $table = (string)($context[
'table'] ?? ($context[
'source_table'] ??
''));
2690 $ddRef = ($modul && $dd && strpos($dd,
'|') ===
false) ? $modul .
'|' . $dd : $dd;
2692 if ($kind ===
'db_to_dd') {
2705 $targetFields = $oldModel[
'fields'] ?? $sourceFields;
2707 $targetLabel = ($modul ? $modul .
'|' :
'') . $dd;
2708 } elseif ($kind ===
'transfer') {
2709 $sourceServer = (string)($context[
'source_server'] ??
$server);
2710 $sourceTable = (string)($context[
'source_table'] ??
$table);
2711 $targetServer = (string)($context[
'target_server'] ??
'');
2712 $targetTable = (string)($context[
'target_table'] ??
'');
2714 if ($sourceServer && $sourceTable && $this->
get_table_exist($sourceServer, $sourceTable)) {
2716 $sourceFields = $schema[
'fields'] ?? [];
2718 if ($targetServer && $targetTable && $this->
get_table_exist($targetServer, $targetTable)) {
2719 $targetFields = $this->
get_db_fields($targetServer, $targetTable);
2721 $targetFields = $sourceFields;
2724 $sourceLabel = $sourceServer .
'|' . $sourceTable;
2725 $targetLabel = $targetServer .
'|' . $targetTable;
2729 if (!$model && $dd) {
2730 $model = $this->get_dd_model($dd);
2734 $table =
$table ?: (string)($model[
'table'][
'table'] ??
'');
2740 $targetFields = $model[
'fields'] ?? [];
2742 $targetLabel = ($modul ? $modul .
'|' :
'') . $dd;
2745 $context[
'modul'] = $modul;
2746 $context[
'dd'] = $dd;
2748 $context[
'table'] =
$table;
2751 $stored = is_array($storedData[
'mapping'] ??
null) ? $storedData[
'mapping'] : [];
2758 foreach ($targetFields as $target) {
2759 $targetName = (string)($target[
'name'] ??
'');
2760 if ($targetName ===
'') {
2765 foreach ($mapping as $source => $mappedTarget) {
2766 if (strcasecmp($mappedTarget, $targetName) === 0) {
2767 $sourceName = $source;
2768 $usedSources[strtolower($source)] =
true;
2773 $source = $sourceName !==
'' && isset($sourceByName[$sourceName]) ? $sourceByName[$sourceName] : [];
2775 if ($sourceName !==
'') {
2776 $status = (strcasecmp($sourceName, $targetName) === 0) ?
'exact' :
'mapped';
2779 $status =
'type_conflict';
2784 'target' => $target,
2785 'source' => $source,
2786 'source_name' => $sourceName,
2787 'target_name' => $targetName,
2788 'status' => $status,
2792 $unmappedSources = [];
2793 foreach ($sourceFields as $source) {
2794 $name = (string)($source[
'name'] ??
'');
2795 if ($name !==
'' &&
empty($usedSources[strtolower($name)])) {
2796 $unmappedSources[] = $source;
2802 'context' => $context,
2803 'source_label' => $sourceLabel,
2804 'target_label' => $targetLabel,
2805 'source_fields' => $sourceFields,
2806 'target_fields' => $targetFields,
2807 'target_rows' => $targetRows,
2808 'unmapped_sources' => $unmappedSources,
2809 'mapping' => $mapping,
2810 'stored_mapping' => $stored,
2812 'updated_at' => $storedData[
'updated_at'] ??
'',
2831 'kind' => $model[
'kind'] ?? strtolower(trim($kind)),
2832 'context' => $model[
'context'] ?? $context,
2833 'source' => $model[
'source_label'] ??
'',
2834 'target' => $model[
'target_label'] ??
'',
2835 'mapping' => $clean,
2836 'updated_at' => date(
'Y-m-d H:i:s'),
2840 $ok = file_put_contents($file, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
2842 return $ok ===
false ? 0 : 1;
2871 dbx()->sys_msg(
'error',
'dd', $dd,
'create_dd',
'table not found');
2903 if (!is_dir(
$dir)) {
2904 @mkdir(
$dir, 0777,
true);
2907 $name =
$table .
'_' . date(
'Ymd_His') .
'.ddb';
2912 return $dir . $name;
2927 @unlink($finalFile);
2928 return @rename($tmpFile, $finalFile) ? 1 : 0;
2931 if (!class_exists(
'ZipArchive')) {
2932 $raw = preg_replace(
'/\.zip$/i',
'', $finalFile);
2934 return @rename($tmpFile, $raw) ? 1 : 0;
2937 $zipObj =
new ZipArchive();
2938 if ($zipObj->open($finalFile, ZipArchive::CREATE | ZipArchive::OVERWRITE) !==
true) {
2942 $inner = basename(preg_replace(
'/\.zip$/i',
'', $finalFile));
2943 $zipObj->addFile($tmpFile, $inner);
2980 return $this->
proc_error([
'proc_key' => $key],
'server or table missing');
2984 return $this->
proc_error([
'proc_key' => $key],
'table not found');
2989 return $this->
proc_error([
'proc_key' => $key],
'no fields found');
2995 $columns[] = $f[
'name'];
2996 if (strtoupper((
string)($f[
'index'] ??
'')) ===
'PRI' && !$pk) {
3003 return $this->
proc_error([
'proc_key' => $key],
'count failed');
3010 $tmpFile = $file .
'.part';
3017 'backup_date' => date(
'Y-m-d H:i:s'),
3018 'row_count' => $total,
3020 'zip' => $zip ? 1 : 0,
3023 file_put_contents($tmpFile, json_encode([
'meta' => $meta], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) .
"\n");
3024 file_put_contents($tmpFile, json_encode([
'columns' => $columns], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) .
"\n", FILE_APPEND);
3030 'tmp_file' => $tmpFile,
3031 'zip' => $zip ? 1 : 0,
3032 'columns' => $columns,
3037 'message' =>
'backup initialized',
3043 while ($this->
step_time_left($started, (
float)$state[
'step_maxsec'])) {
3044 if ((
int)$state[
'done'] >= (
int)$state[
'total']) {
3047 $state = $this->
proc_error($state,
'finalize backup failed');
3052 $state = $this->
proc_finish($state,
'backup finished');
3057 $chunk = (int)$state[
'chunk_size'];
3059 foreach ($state[
'columns'] as $col) {
3060 $cols[] = $this->
quote_ident($state[
'server'], $col);
3064 if (!
empty($state[
'pk'])) {
3065 $order =
' ORDER BY ' . $this->
quote_ident($state[
'server'], $state[
'pk']);
3069 if ($dbType ===
'mysql') {
3070 $limit =
' LIMIT ' . (int)$state[
'offset'] .
', ' . $chunk;
3072 $limit =
' LIMIT ' . $chunk .
' OFFSET ' . (int)$state[
'offset'];
3075 $sql =
'SELECT ' . implode(
',', $cols);
3076 $sql .=
' FROM ' . $this->
quote_ident($state[
'server'], $state[
'table']);
3077 $sql .= $order . $limit;
3079 $rows = $this->
rawQuery($state[
'server'], $sql);
3080 if (!is_array($rows)) {
3081 $state = $this->
proc_error($state,
'backup select failed');
3087 $state[
'done'] = $state[
'total'];
3088 $state[
'percent'] = 100;
3089 $state[
'step_percent'] = 100;
3094 foreach ($rows as $row) {
3096 foreach ($state[
'columns'] as $col) {
3097 $rec[] = $row[$col] ??
null;
3104 json_encode([
'records' => $records], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) .
"\n",
3109 $state[
'offset'] +=
$count;
3110 $state[
'done'] +=
$count;
3111 $state[
'percent'] = ($state[
'total'] > 0) ? (
int)floor(($state[
'done'] / $state[
'total']) * 100) : 100;
3112 $state[
'step_percent'] = $state[
'percent'];
3113 $state[
'message'] =
'backup rows ' . $state[
'done'] .
' / ' . $state[
'total'];
3139 if (!class_exists(
'ZipArchive')) {
3140 $raw = preg_replace(
'/\.zip$/i',
'', $file);
3141 return file_exists($raw) ? $raw :
'';
3145 if (!is_dir($tmpDir)) {
3146 @mkdir($tmpDir, 0777,
true);
3149 $tmpFile = $tmpDir . md5($file) .
'.ddb';
3151 if (file_exists($tmpFile)) {
3155 $zipObj =
new ZipArchive();
3156 if ($zipObj->open($file) !==
true) {
3160 if ($zipObj->numFiles < 1) {
3165 $content = $zipObj->getFromIndex(0);
3168 if ($content ===
false) {
3172 file_put_contents($tmpFile, $content);
3209 return $this->
proc_error([
'proc_key' => $key],
'server, table or file missing');
3213 return $this->
proc_error([
'proc_key' => $key],
'target table not found');
3217 if (!$sourceFile || !file_exists($sourceFile)) {
3218 return $this->
proc_error([
'proc_key' => $key],
'restore source file not readable');
3221 $fh = fopen($sourceFile,
'rb');
3223 return $this->
proc_error([
'proc_key' => $key],
'restore open source failed');
3226 $line1 = fgets($fh);
3227 $line2 = fgets($fh);
3229 $metaRec = json_decode((
string)$line1,
true);
3230 $colRec = json_decode((
string)$line2,
true);
3232 if (!is_array($metaRec) || !isset($metaRec[
'meta']) || !is_array($colRec) || !isset($colRec[
'columns'])) {
3234 return $this->
proc_error([
'proc_key' => $key],
'invalid backup format');
3237 $filePos = ftell($fh);
3242 foreach ($targetFields as $f) {
3243 $targetLookup[$f[
'name']] =
true;
3247 return $this->
proc_error([
'proc_key' => $key],
'target table not found after empty');
3254 'source_file' => $sourceFile,
3255 'zip' => $zip ? 1 : 0,
3256 'mapping' => is_array($mapping) ? $mapping : [],
3257 'source_columns' => $colRec[
'columns'],
3258 'target_lookup' => $targetLookup,
3259 'file_pos' => $filePos,
3261 'total' => (
int)($metaRec[
'meta'][
'row_count'] ?? 0),
3262 'message' =>
'restore initialized',
3267 $state = $this->
proc_error($state,
'db connect failed');
3274 $fh = fopen($state[
'source_file'],
'rb');
3276 $state = $this->
proc_error($state,
'restore open source failed');
3281 fseek($fh, (
int)$state[
'file_pos']);
3283 while ($this->
step_time_left($started, (
float)$state[
'step_maxsec'])) {
3286 if ($line ===
false) {
3289 if (!
empty($state[
'zip']) && isset($state[
'source_file']) && $state[
'source_file'] !== $state[
'file']) {
3290 @unlink($state[
'source_file']);
3293 $state = $this->
proc_finish($state,
'restore finished');
3298 $rec = json_decode(trim($line),
true);
3299 if (!is_array($rec) || !isset($rec[
'records']) || !is_array($rec[
'records'])) {
3303 foreach ($rec[
'records'] as $rowValues) {
3305 foreach ($state[
'source_columns'] as $i => $col) {
3306 $assoc[$col] = $rowValues[$i] ??
null;
3309 if (is_array($state[
'mapping'])) {
3310 foreach ($state[
'mapping'] as $old => $new) {
3311 if ((
string)$old === (
string)$new) {
3315 if (array_key_exists($old, $assoc)) {
3316 $assoc[$new] = $assoc[$old];
3317 unset($assoc[$old]);
3323 foreach ($assoc as $fld => $val) {
3324 if (isset($state[
'target_lookup'][$fld])) {
3325 $dbRec[$fld] = $val;
3329 if (isset($dbRec[
'id']) && ($dbRec[
'id'] ===
'' || $dbRec[
'id'] ===
null || $dbRec[
'id'] === 0 || $dbRec[
'id'] ===
'0')) {
3330 unset($dbRec[
'id']);
3340 $sql =
'INSERT INTO ' . $this->
quote_ident($state[
'server'], $state[
'table'])
3341 .
' (' . implode(
',', array_map(fn($f) => $this->
quote_ident($state[
'server'], $f),
$fields)) .
')'
3342 .
' VALUES (' . implode(
',', $placeholders) .
')';
3344 $stmt = $this->db[$state[
'server']]->prepare($sql);
3345 $stmt->execute(array_values($dbRec));
3350 $state[
'file_pos'] = ftell($fh);
3351 $state[
'percent'] = ($state[
'total'] > 0) ? (
int)floor(($state[
'done'] / $state[
'total']) * 100) : 0;
3352 $state[
'step_percent'] = $state[
'percent'];
3353 $state[
'message'] =
'restore rows ' . $state[
'done'] .
' / ' . $state[
'total'];
3383 if ($ddGroup === $dbGroup) {
3387 if (in_array($ddGroup, [
'string',
'text'],
true) && in_array($dbGroup, [
'string',
'text'],
true)) {
3391 if (in_array($ddGroup, [
'integer',
'bool'],
true) && in_array($dbGroup, [
'integer',
'bool'],
true)) {
3408 $type = strtolower(trim($type));
3409 $length = trim($length);
3415 if (in_array($type, [
'bool',
'boolean'],
true)) {
3419 if ($type ===
'bit') {
3420 return ($length ===
'' || $length ===
'1') ?
'bool' :
'integer';
3423 if ($type ===
'tinyint' && $length ===
'1') {
3427 if (in_array($type, [
'int',
'integer',
'smallint',
'mediumint',
'tinyint',
'bigint',
'serial',
'number'],
true)) {
3431 if (in_array($type, [
'decimal',
'numeric'],
true)) {
3435 if (in_array($type, [
'float',
'double',
'real'],
true)) {
3439 if ($type ===
'date' || $type ===
'year') {
3443 if ($type ===
'time') {
3447 if (in_array($type, [
'datetime',
'timestamp'],
true)) {
3451 if (in_array($type, [
'char',
'nchar',
'varchar',
'varchar2',
'nvarchar',
'string'],
true)) {
3455 if (in_array($type, [
'text',
'tinytext',
'mediumtext',
'longtext',
'clob'],
true)) {
3459 if (in_array($type, [
'binary',
'varbinary',
'tinyblob',
'blob',
'mediumblob',
'longblob'],
true)) {
3463 if ($type ===
'json') {
3467 if (in_array($type, [
'enum',
'set'],
true)) {
3488 $dbEngine = strtolower(trim($dbEngine));
3489 $ddType = strtolower((
string)($ddField[
'type'] ??
''));
3490 $dbType = strtolower((
string)($dbField[
'type'] ??
''));
3491 $ddGroup = $this->
schema_type_group($ddType, (
string)($ddField[
'length'] ??
''));
3492 $dbGroup = $this->
schema_type_group($dbType, (
string)($dbField[
'length'] ??
''));
3494 if ($ddType !==
'' && $dbType !==
'' && ($ddType === $dbType || $this->
is_semantic_type_match($ddType, $dbType))) {
3498 if ($dbEngine ===
'sqlite') {
3499 if (in_array($ddGroup, [
'string',
'text',
'date',
'time',
'datetime',
'json',
'enum'],
true)
3500 && in_array($dbGroup, [
'string',
'text'],
true)) {
3504 if (in_array($ddGroup, [
'integer',
'bool'],
true) && in_array($dbGroup, [
'integer',
'bool'],
true)) {
3508 if (in_array($ddGroup, [
'decimal',
'float'],
true) && in_array($dbGroup, [
'decimal',
'float',
'integer'],
true)) {
3527 $dbEngine = strtolower(trim($dbEngine));
3528 $ddType = strtolower((
string)($ddField[
'type'] ??
''));
3529 $dbType = strtolower((
string)($dbField[
'type'] ??
''));
3531 if ($ddType ===
'' || $dbType ===
'') {
3535 if ($dbEngine ===
'sqlite') {
3539 if ($ddType === $dbType) {
3543 $ddGroup = $this->
schema_type_group($ddType, (
string)($ddField[
'length'] ??
''));
3544 $dbGroup = $this->
schema_type_group($dbType, (
string)($dbField[
'length'] ??
''));
3546 if (in_array($ddGroup, [
'integer',
'bool'],
true) && in_array($dbGroup, [
'integer',
'bool'],
true)) {
3550 if ($ddGroup ===
'decimal' && $dbGroup ===
'decimal') {
3554 if ($ddGroup ===
'float' && in_array($dbGroup, [
'float',
'decimal'],
true)) {
3575 $preserve = $compatible
3576 ? [
'type',
'index',
'length',
'default',
'label',
'rules',
'tooltip',
'errormsg',
'placeholder',
'convert',
'protect',
'group',
'mask',
'data',
'options',
'tpl',
'js']
3577 : [
'label',
'tooltip',
'errormsg',
'placeholder',
'convert',
'protect',
'group',
'mask',
'data',
'js'];
3579 foreach ($preserve as $key) {
3580 if (isset($oldField[$key]) && $oldField[$key] !==
'' && $oldField[$key] !==
null) {
3581 $merged[$key] = $oldField[$key];
3585 $merged[
'name'] = $dbField[
'name'] ?? ($oldField[
'name'] ??
'');
3587 if (
empty($merged[
'label'])) {
3591 if (
empty($merged[
'rules']) || !$compatible) {
3595 if (
empty($merged[
'tpl']) || !$compatible) {
3621 'server' => $model[
'table'][
'server'] ??
'',
3622 'table' => $model[
'table'][
'table'] ??
'',
3623 'table_exists' => 0,
3624 'create_table' => 0,
3626 'missing_in_dd' => [],
3627 'type_conflicts' => [],
3628 'add_indexes' => [],
3629 'rebuild_needed' => 0,
3630 'backup_file' =>
'',
3640 $ddFields = $model[
'fields'] ?? [];
3641 $ddIndexes = $model[
'indexes'] ?? [];
3649 $plan[
'table_exists'] = $tableExists;
3651 if (!$tableExists) {
3652 $plan[
'create_table'] = 1;
3659 $dbFieldsByName = [];
3660 foreach ($dbFields as
$field) {
3664 $ddFieldsByName = [];
3665 foreach ($ddFields as
$field) {
3671 foreach ($ddFields as
$field) {
3672 $name = strtolower((
string)
$field[
'name']);
3673 if (!isset($dbFieldsByName[$name])) {
3674 $plan[
'add_fields'][] =
$field;
3678 $dbf = $dbFieldsByName[$name];
3680 $ddType = strtolower((
string)(
$field[
'type'] ??
''));
3681 $dbType = strtolower((
string)($dbf[
'type'] ??
''));
3683 $ddLen = (string)(
$field[
'length'] ??
'');
3684 $dbLen = (string)($dbf[
'length'] ??
'');
3687 $lenEqual = ($ddLen === $dbLen) || ($ddLen ===
'' || $dbLen ===
'');
3694 if ($targetDbType ===
'sqlite') {
3700 if (!$typeEqual || !$lenEqual) {
3701 $plan[
'type_conflicts'][] = [
3702 'field' =>
$field[
'name'],
3703 'dd_type' => $ddType,
3704 'db_type' => $dbType,
3705 'dd_length' => $ddLen,
3706 'db_length' => $dbLen,
3711 foreach ($dbFields as
$field) {
3712 $name = strtolower((
string)
$field[
'name']);
3713 if (!isset($ddFieldsByName[$name])) {
3714 $plan[
'missing_in_dd'][] =
$field;
3719 foreach ($dbIndexes as $idx) {
3720 $dbIndexNames[strtolower((
string)$idx[
'name'])] =
true;
3723 foreach ($ddIndexes as $idx) {
3724 $name = strtolower((
string)($idx[
'name'] ??
''));
3729 if (strtoupper((
string)($idx[
'type'] ??
'INDEX')) ===
'PRIMARY') {
3733 if (!isset($dbIndexNames[$name])) {
3734 $plan[
'add_indexes'][] = $idx;
3738 $plan[
'rebuild_needed'] = (!
empty($plan[
'type_conflicts']) || !
empty($plan[
'missing_in_dd'])) ? 1 : 0;
3767 $mode = strtolower((
string)$mode);
3771 if ($mode ===
'step') {
3775 $ddRef = ($modul && strpos((
string)$dd,
'|') ===
false) ? $modul .
'|' . $dd : (string)$dd;
3776 $key = $this->
proc_key(
'sync_dd_to_db', [$modul, $dd]);
3778 if ($mode ===
'reset') {
3782 'status' =>
'reset',
3783 'message' =>
'sync state cleared',
3785 'step_percent' => 0,
3789 if ($mode ===
'status') {
3794 'message' =>
'no active state',
3796 'step_percent' => 0,
3800 if (in_array($mode, [
'pause',
'resume',
'continue',
'cancel'],
true)) {
3804 if ($mode ===
'restart') {
3811 if (in_array($mode, [
'check',
'plan'],
true)) {
3812 $plan[
'status'] =
'finished';
3813 $plan[
'message'] =
'plan ready';
3814 $plan[
'percent'] = 100;
3815 $plan[
'step_percent'] = 100;
3827 'server' => $plan[
'server'],
3828 'table' => $plan[
'table'],
3829 'phase' =>
'prepare',
3834 'message' =>
'sync initialized',
3838 'server' => $plan[
'server'],
3839 'table' => $plan[
'table'],
3841 'backup_started' => 0,
3842 'restore_started' => 0,
3851 $table = $state[
'table'];
3852 $plan = $state[
'plan'];
3854 switch ($state[
'phase']) {
3856 if (!
empty($plan[
'create_table'])) {
3857 $state[
'phase'] =
'create_table';
3858 $state[
'message'] =
'create table';
3859 $state[
'percent'] = 10;
3860 $state[
'step_percent'] = 0;
3865 if (!
empty($plan[
'rebuild_needed'])) {
3866 if (!in_array($state[
'mode'], [
'force',
'rebuild'],
true)) {
3867 $state = $this->
proc_error($state,
'rebuild needed; use mode force or rebuild');
3872 $state[
'phase'] =
'backup_old';
3873 $state[
'message'] =
'backup old table';
3874 $state[
'percent'] = 10;
3875 $state[
'step_percent'] = 0;
3880 $state[
'phase'] =
'add_fields';
3881 $state[
'message'] =
'add missing fields';
3882 $state[
'percent'] = 20;
3883 $state[
'step_percent'] = 0;
3887 case 'create_table':
3889 $state[
'phase'] =
'add_indexes';
3890 $state[
'message'] =
'create indexes';
3891 $state[
'percent'] = 70;
3892 $state[
'step_percent'] = 100;
3894 $state = $this->
proc_error($state,
'create table failed');
3901 $fields = $plan[
'add_fields'] ?? [];
3908 $state = $this->
proc_error($state,
'add field failed: ' . (
$field[
'name'] ??
''));
3913 $state[
'field_pos']++;
3915 $state[
'percent'] = 20 + (int)floor(($state[
'field_pos'] / $max) * 40);
3916 $state[
'step_percent'] = (int)floor(($state[
'field_pos'] / $max) * 100);
3917 $state[
'message'] =
'add field ' . (
$field[
'name'] ??
'');
3921 $state[
'phase'] =
'add_indexes';
3922 $state[
'message'] =
'add indexes';
3923 $state[
'percent'] = 70;
3924 $state[
'step_percent'] = 100;
3932 $indexes = $plan[
'add_indexes'] ?? [];
3935 $idx =
$indexes[$state[
'index_pos']];
3939 $state = $this->
proc_error($state,
'add index failed: ' . ($idx[
'name'] ??
''));
3944 $state[
'index_pos']++;
3946 $state[
'percent'] = 70 + (int)floor(($state[
'index_pos'] / $max) * 20);
3947 $state[
'step_percent'] = (int)floor(($state[
'index_pos'] / $max) * 100);
3948 $state[
'message'] =
'add index ' . ($idx[
'name'] ??
'');
3952 $state = $this->
proc_finish($state,
'sync dd -> db finished');
3959 $backupFile = $state[
'plan'][
'backup_file'] ??
'';
3962 $state[
'plan'][
'backup_file'] = $backupFile;
3966 if (($bak[
'status'] ??
'') ===
'error') {
3967 $state = $this->
proc_error($state,
'backup before rebuild failed');
3972 if (($bak[
'status'] ??
'') !==
'finished') {
3973 $state[
'percent'] = 10 + (int)floor((($bak[
'percent'] ?? 0) * 0.3));
3974 $state[
'step_percent'] = (int)($bak[
'percent'] ?? 0);
3975 $state[
'message'] =
'backup old table';
3980 $state[
'phase'] =
'rename_old';
3981 $state[
'percent'] = 45;
3982 $state[
'step_percent'] = 100;
3983 $state[
'message'] =
'rename old table';
3988 $tmpOld =
$table .
'__dbxold_' . date(
'YmdHis');
3993 $state = $this->
proc_error($state,
'rename old table failed');
3998 $state[
'old_table'] = $tmpOld;
3999 $state[
'phase'] =
'create_new';
4000 $state[
'percent'] = 55;
4001 $state[
'step_percent'] = 100;
4002 $state[
'message'] =
'create new table';
4008 $state = $this->
proc_error($state,
'create new table failed');
4013 $state[
'phase'] =
'restore_new';
4014 $state[
'percent'] = 65;
4015 $state[
'step_percent'] = 100;
4016 $state[
'message'] =
'restore data into new table';
4021 $mapping = is_array($state[
'mapping'] ??
null) ? $state[
'mapping'] : [];
4024 if (($restore[
'status'] ??
'') ===
'error') {
4025 $state = $this->
proc_error($state,
'restore into new table failed');
4030 if (($restore[
'status'] ??
'') !==
'finished') {
4031 $state[
'percent'] = 65 + (int)floor((($restore[
'percent'] ?? 0) * 0.25));
4032 $state[
'step_percent'] = (int)($restore[
'percent'] ?? 0);
4033 $state[
'message'] =
'restore data into new table';
4038 $state[
'phase'] =
'drop_old';
4039 $state[
'percent'] = 92;
4040 $state[
'step_percent'] = 100;
4041 $state[
'message'] =
'drop old table';
4046 if (!
empty($state[
'old_table'])) {
4050 $state = $this->
proc_finish($state,
'sync dd -> db rebuild finished');
4055 $state = $this->
proc_error($state,
'unknown sync phase');
4116 $mode = strtolower((
string)$mode);
4120 if ($mode ===
'step' || $mode ===
'apply') {
4128 if ($mode ===
'reset') {
4132 'status' =>
'reset',
4133 'message' =>
'sync state cleared',
4135 'step_percent' => 0,
4139 if ($mode ===
'status') {
4144 'message' =>
'no active state',
4146 'step_percent' => 0,
4150 if (in_array($mode, [
'pause',
'resume',
'continue',
'cancel'],
true)) {
4154 if ($mode ===
'restart') {
4163 $syncServer =
$server ?:
'dbXsystem';
4164 $syncTable =
$table ?: $dd;
4173 $syncServer = $oldModel[
'table'][
'server'] ?? $syncServer;
4176 $syncTable = $oldModel[
'table'][
'table'] ?? $syncTable;
4181 return $this->
proc_error([
'proc_key' => $key],
'table not found');
4188 'server' => $syncServer,
4189 'table' => $syncTable,
4190 'old_model' => $oldModel,
4194 'server' => $syncServer,
4195 'table' => $syncTable,
4197 'phase' =>
'read_schema',
4199 'message' =>
'sync initialized',
4207 switch ($state[
'phase']) {
4209 $state[
'db_fields'] = $this->
get_db_fields($state[
'server'], $state[
'table']);
4210 $state[
'db_indexes'] = $this->
get_db_indexes($state[
'server'], $state[
'table']);
4211 $state[
'phase'] =
'merge_meta';
4212 $state[
'percent'] = 30;
4213 $state[
'step_percent'] = 100;
4214 $state[
'message'] =
'schema loaded';
4219 $oldModel = $state[
'old_model'] ?? [];
4222 'server' => $state[
'server'],
4223 'table' => $state[
'table'],
4226 if ($state[
'mode'] ===
'merge' && $oldModel) {
4228 'autosync',
'version',
'cache',
'trash',
'trace',
4229 'read',
'create',
'update',
'delete',
4230 'read_owner',
'create_owner',
'update_owner',
'delete_owner'
4232 if (isset($oldModel[
'table'][$k])) {
4233 $newTable[$k] = $oldModel[
'table'][$k];
4238 $oldFieldsByName = [];
4239 foreach (($oldModel[
'fields'] ?? []) as
$field) {
4240 $oldFieldsByName[strtolower((
string)(
$field[
'name'] ??
''))] =
$field;
4243 $mapping = is_array($state[
'mapping'] ??
null) ? $state[
'mapping'] : [];
4245 foreach (($state[
'db_fields'] ?? []) as
$field) {
4247 $metaName = $mapping[$name] ?? $name;
4248 $metaKey = strtolower((
string)$metaName);
4249 if (!isset($oldFieldsByName[$metaKey])) {
4251 $metaKey = strtolower((
string)$metaName);
4254 if ($state[
'mode'] ===
'merge' && isset($oldFieldsByName[$metaKey])) {
4256 $oldFieldsByName[$metaKey],
4265 $oldIndexesByName = [];
4266 foreach (($oldModel[
'indexes'] ?? []) as
$index) {
4271 foreach (($state[
'db_indexes'] ?? []) as
$index) {
4274 if ($state[
'mode'] ===
'merge' && isset($oldIndexesByName[$name])) {
4276 foreach ($oldIndexesByName[$name] as $k => $v) {
4277 if ($v !==
'' && $v !==
null) {
4287 $state[
'new_table'] = $newTable;
4288 $state[
'new_fields'] = $newFields;
4289 $state[
'new_indexes'] = $newIndexes;
4290 $state[
'phase'] =
'write_dd';
4291 $state[
'percent'] = 70;
4292 $state[
'step_percent'] = 100;
4293 $state[
'message'] =
'meta merged';
4301 $state[
'new_table'] ?? [],
4302 $state[
'new_fields'] ?? [],
4303 $state[
'new_indexes'] ?? []
4307 $state = $this->
proc_error($state,
'write dd failed');
4314 $state = $this->
proc_finish($state,
'sync db -> dd finished');
4319 $state = $this->
proc_error($state,
'unknown sync phase');
4381 if (!is_dir($path)) {
4385 $files = scandir($path);
4386 foreach ($files as $file) {
4387 if (!str_ends_with($file,
'.dd.php')) {
4391 $dd = str_replace(
'.dd.php',
'', $file);
4392 if ($dd ===
'new') {
4396 $dd_sys = $this->
load_dd($dd);
4397 if (($dd_sys[
'dd_status'] ?? 0) <= 0) {
Zentrale Datenbank- und DD-Systemklasse von DBX.
get_dd_autosync($dd, $rec=0)
Gibt das Autosync-Flag einer DD zurück.
get_table_exist($dd, $dbtab='')
Prueft, ob eine Tabelle existiert.
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.
count($dd, $where='', $server='')
Zählt Datensätze einer DD oder Tabelle.
empty($dd)
Leert eine Tabelle anhand ihrer DD.
dbConnect($server, $dbType, $dbHost, $dbName='', $dbUser='', $dbPass='', $dbPort='')
Stellt eine Verbindung zu einer Datenbank her und speichert sie in $this->db[$server].
get_dd_table($dd, $rec=0)
Gibt den Tabellennamen oder die komplette Table-Definition einer DD zurück.
exec_query($server, $sql)
Führt eine generische SQL-Exec-Anweisung aus.
escape($string, $server)
Escaped einen String für die sichere Verwendung in einer SQL-Abfrage.
get_dd_server(string $dd)
Ermittelt den Server einer Datenbeschreibung (DD).
get_db_type($server)
Ermittelt den Datenbanktyp für einen Server.
get_dd_fields($dd, $label=0)
Gibt die Felddefinitionen einer DD zurück.
connect_db_server(string $server)
Verbindet sich mit einem angegebenen Datenbankserver.
step_start_time()
Liefert einen Step-Startzeitpunkt.
init_proc_state(string $type, string $key, array $state)
Initialisiert einen technischen Prozessstatus.
get_dd_file_path(string $modul, string $dd)
Ermittelt den Dateipfad einer DD-Datei.
sync_dd_to_db($modul, $dd, $mode='step')
Synchronisiert ein DD technisch in Richtung DB.
get_schema_mapping_values(string $kind, array $context)
Gibt nur die gespeicherten Mapping-Werte zurueck.
get_step_runtime()
Gibt die maximale Laufzeit eines technischen Prozess-Schritts zurück.
save_schema_mapping(string $kind, array $context, array $mapping)
Speichert ein Schema-Mapping als wiederverwendbare Mapping-Datei.
get_preferred_table_schema(string $server, string $table)
Liefert die beste Strukturquelle fuer eine DB-Tabelle.
schema_mapping_file_part(string $value)
Baut einen sicheren Dateinamenbestandteil.
is_system_field(string $name)
Prüft, ob ein Feld ein DBX-Systemfeld ist.
map_db_type_to_dd_type(string $dbType, string $rawType, string $length='')
Mappt einen DB-Roh-Typ auf einen kanonischen DD-Typ.
build_backup_file_name(string $server, string $table, bool $zip=false)
Baut einen Backup-Dateinamen.
dd_template_value(mixed $value)
create_db_index(string $server, string $table, array $index)
Erzeugt einen DB-Index aus einer DD-Indexdefinition.
build_schema_mapping(string $kind, array $context)
Baut die Mapping-Ansicht fuer Admin-UI und Prozesse.
schema_fields_by_name(array $fields)
Baut einen Feldindex nach echtem Feldnamen.
build_create_table_sql(string $dd)
Baut das CREATE-TABLE-SQL aus einem DD.
get_chunk_size()
Gibt die Standard-Chunk-Größe für technische Prozesse zurück.
normalize_dd_field(array $field)
parse_sql_type(string $type)
Zerlegt einen SQL-Typ in Basis-Typ und Länge.
is_semantic_type_match(string $ddType, string $dbType)
Prüft, ob zwei Typen semantisch kompatibel sind.
get_db_fields($server, $tableName)
Liest die Feldstruktur einer DB-Tabelle und erzeugt daraus kanonische DD-Felddefinitionen.
prepare_restore_source_file(string $file, bool $zip)
Bereitet eine Restore-Quelldatei vor.
get_dd_template_file(string $name)
Ermittelt den absoluten Dateipfad eines DD-Templates.
finalize_backup_file(string $tmpFile, string $finalFile, bool $zip)
Finalisiert eine Backup-Datei optional als ZIP.
proc_key(string $type, array $parts)
Erzeugt einen technischen Prozessschlüssel.
sql_quote(string $server, mixed $value)
Quotet einen SQL-Wert für Roh-SQL.
add_db_field_from_dd(string $server, string $table, array $field)
Fügt ein Feld aus DD-Definition in eine bestehende DB-Tabelle ein.
normalize_schema_field_key(string $name)
Normalisiert einen Feldnamen fuer robuste Auto-Zuordnung.
dync_db_to_dd($modul, $dd, $mode='step', string $server='', string $table='')
Kompatibilitaetsalias fuer sync_db_to_dd().
build_sql_column_from_dd(string $server, array $field)
Baut eine SQL-Spaltendefinition aus einem DD-Feld.
get_dd_exist($dd)
Prüft, ob eine DD-Datei existiert.
uses_inline_auto_increment_id(string $dbType)
Liefert, ob der Treiber die Auto-ID zusammen mit PRIMARY KEY direkt an der Spalte definiert.
normalize_schema_mapping(array $mapping, array $sourceFields, array $targetFields)
Normalisiert eine technische Source->Target-Zuordnung.
clear_dd_cache(string $dd)
Löscht den DD-Cache für eine DD.
quote_ident(string $server, string $name)
Quotet einen SQL-Identifier abhängig vom DB-Typ.
create_db_tab_from_fields(string $server, string $table, array $fields, array $indexes=[])
Erzeugt eine DB-Tabelle direkt aus Feld-/Index-Metadaten.
normalize_index_record(array $index)
Normalisiert einen DD-Indexsatz.
build_dd_field_from_db_meta(string $name, string $dbType, string $rawType, string $length, string $isNull, mixed $default, string $index='')
Baut aus gelesenen DB-Metadaten einen DD-Feldsatz.
restore($server, $table, $file, $mapping=[], $zip=0)
Stellt Daten aus einem kompakten Tabellenbackup wieder her.
ensure_sqlite_db_file_for_dd(string $dd)
Legt eine fehlende SQLite-Datei fuer ein DD an, damit die Tabelle anschliessend ueber die bestehende ...
sync_db_to_dd($modul, $dd, $mode='step', string $server='', string $table='')
Synchronisiert eine physische Datenbanktabelle technisch in Richtung DD.
step_time_left(float $started_at, float $max_seconds)
Prüft, ob noch Laufzeit für den aktuellen Prozessschritt übrig ist.
empty_db_table(string $server, string $table)
Leert eine konkrete DB-Tabelle ohne DD-Aufloesung.
merge_dd_field_with_db_field(array $oldField, array $dbField, string $dbEngine='')
Fuehrt ein vorhandenes DD-Feld mit einer DB-Felddefinition zusammen.
map_dd_type_to_sql_type(string $dbType, string $type, string $length='')
Mappt einen kanonischen DD-Typ auf einen konkreten SQL-Typ des Zielsystems.
proc_finish(array $state, string $message='finished')
Erzeugt einen Fertig-Status für einen Prozess.
__construct()
Initialisiert dbxDD.
get_dd_tables($path='')
Liefert DD-Übersichtsdaten aus einem DD-Verzeichnis.
schema_type_group(string $type, string $length='')
Gruppiert DD-/DB-Feldtypen fachlich.
backup($server, $table, $file='', $zip=0)
Erstellt ein tabellenbasiertes Datenbackup.
transfer_table(string $sourceServer, string $sourceTable, string $targetServer, string $targetTable='', string $mode='step', int $createTarget=1, int $truncateTarget=1)
Transferiert eine DB-Tabelle serveruebergreifend als Schrittprozess.
get_dd_cache_info(string $dd)
Ermittelt die aufgelöste Cache-Position einer DD.
find_dd_for_db_table(string $server, string $table)
Sucht ein DD, das auf eine konkrete DB-Tabelle zeigt.
load_dd(string $dd)
Nutzt direkt die bestehende dbxDB-Logik.
proc_response(array $state)
Aktualisiert einen Prozessstatus für Rückgabe.
proc_is_waiting(array $state)
Prueft, ob ein Prozess aktuell keine weitere Arbeit ausfuehren darf.
render_dd_template(string $template_name, array $vars=[])
Rendert ein DD-Template lokal als PHP-Quelltext.
create_dd(string $modul, string $dd, string $server='dbXsystem', string $table='')
Erzeugt eine DD aus einer vorhandenen DB-Tabelle.
infer_rules_from_field(array $field)
Leitet eine Standard-Regel aus einem Feld ab.
set_chunk_size(int $chunk_size)
Setzt die Standard-Chunk-Größe für technische Prozesse.
set_proc_state(string $key, array $state)
Speichert einen technischen Prozessstatus.
load_schema_mapping(string $kind, array $context)
Laedt ein gespeichertes Schema-Mapping.
clear_proc_state(string $key)
Löscht einen technischen Prozessstatus.
get_db_indexes(string $server, string $tableName)
Liest die Indexstruktur einer DB-Tabelle.
save_dd(string $modul, string $dd, array $table, array $fields, array $indexes=[])
Speichert eine DD-Datei und leert danach den DD-Cache.
write_dd(string $modul, string $dd, array $table, array $fields, array $indexes=[])
Schreibt eine DD-Datei anhand der DD-Templates.
proc_error(array $state, string $message)
Erzeugt einen Fehlerstatus für einen Prozess.
control_proc_state(string $key, string $cmd)
Steuert einen vorhandenen Prozessstatus.
build_default_sql(string $server, mixed $default, string $ddType)
Baut einen DEFAULT-SQL-Teil für einen DD-Default.
get_dd_model(string $dd)
Gibt das komplette DD-Modell zurück.
normalize_table_record(string $dd, array $table)
Normalisiert einen DD-Tabellensatz.
enforce_auto_increment_id(array $fields)
Erzwingt die globale Tabellenidentitaet fuer jede neue Fachtabelle.
build_sync_plan_dd_to_db(string $dd)
Baut einen technischen Sync-Plan DD -> DB.
get_dd_indexes(string $dd)
Gibt die Indexdefinitionen einer DD zurück.
schema_mapping_path(string $kind, array $context)
Liefert den Pfad zur Mapping-Datei.
sort_schema_record(array $record, array $keys)
infer_tpl_from_field(array $field)
Leitet ein sinnvolles Form-Template aus einer Felddefinition ab.
normalize_field_record(array $field)
Normalisiert einen DD-Feldsatz.
normalize_default_value(mixed $value)
Normalisiert einen Default-Wert aus DB-Metadaten.
drop_db_tab(string $server, string $table)
Löscht eine Datenbanktabelle.
normalize_dd_index(array $index)
schema_mapping_dir()
Liefert das Verzeichnis fuer gespeicherte Schema-Mappings.
set_step_runtime(float $seconds)
Setzt die maximale Laufzeit eines technischen Prozess-Schritts.
auto_schema_mapping(array $sourceFields, array $targetFields, array $stored=[])
Baut ein Auto-Mapping und uebersteuert es mit gespeicherter Zuordnung.
create_db_tab(string $dd)
Erzeugt eine Datenbanktabelle aus einem DD.
get_proc_state(string $key)
Liest einen technischen Prozessstatus.
is_db_sync_field_match(string $dbEngine, array $ddField, array $dbField)
Prüft, ob ein DB-Feld physisch synchron zu einem DD-Feld ist.
infer_label_from_name(string $name)
Leitet ein Standard-Label aus dem Feldnamen ab.
is_dd_field_compatible_with_db(array $ddField, array $dbField, string $dbEngine='')
Prüft, ob ein DD-Feld fachlich zu einem DB-Feld passt.
dbx_os_path_file($path_file)
dbx_config_path_store(string $path, bool $dirTrailingSlash=false)
dbx_get_base_dir($cut_Data=0)
DBX schema administration.