dbgate-datalib 5.2.2 → 5.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/ChangeSet.d.ts +11 -3
- package/lib/ChangeSet.js +16 -6
- package/lib/DataDuplicator.d.ts +60 -0
- package/lib/DataDuplicator.js +233 -0
- package/lib/GridConfig.d.ts +1 -0
- package/lib/GridDisplay.d.ts +5 -3
- package/lib/GridDisplay.js +63 -19
- package/lib/JslGridDisplay.d.ts +1 -1
- package/lib/JslGridDisplay.js +4 -1
- package/lib/MacroDefinition.d.ts +1 -1
- package/lib/PerspectiveConfig.d.ts +1 -0
- package/lib/PerspectiveTreeNode.d.ts +12 -9
- package/lib/PerspectiveTreeNode.js +96 -25
- package/lib/deleteCascade.js +12 -2
- package/lib/index.d.ts +1 -2
- package/lib/index.js +1 -4
- package/lib/runMacro.d.ts +3 -4
- package/lib/runMacro.js +38 -125
- package/package.json +5 -5
- package/lib/FreeTableGridDisplay.d.ts +0 -59
- package/lib/FreeTableGridDisplay.js +0 -32
- package/lib/FreeTableModel.d.ts +0 -16
- package/lib/FreeTableModel.js +0 -24
package/lib/ChangeSet.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { Command, Condition } from 'dbgate-sqltree';
|
|
2
|
-
import type { NamedObjectInfo, DatabaseInfo } from 'dbgate-types';
|
|
2
|
+
import type { NamedObjectInfo, DatabaseInfo, TableInfo } from 'dbgate-types';
|
|
3
|
+
import { JsonDataObjectUpdateCommand } from 'dbgate-tools';
|
|
3
4
|
export interface ChangeSetItem {
|
|
4
5
|
pureName: string;
|
|
5
6
|
schemaName?: string;
|
|
6
7
|
insertedRowIndex?: number;
|
|
8
|
+
existingRowIndex?: number;
|
|
7
9
|
document?: any;
|
|
8
10
|
condition?: {
|
|
9
11
|
[column: string]: string;
|
|
@@ -12,16 +14,22 @@ export interface ChangeSetItem {
|
|
|
12
14
|
[column: string]: string;
|
|
13
15
|
};
|
|
14
16
|
}
|
|
15
|
-
export interface
|
|
17
|
+
export interface ChangeSetItemFields {
|
|
16
18
|
inserts: ChangeSetItem[];
|
|
17
19
|
updates: ChangeSetItem[];
|
|
18
20
|
deletes: ChangeSetItem[];
|
|
19
21
|
}
|
|
22
|
+
export interface ChangeSet extends ChangeSetItemFields {
|
|
23
|
+
structure?: TableInfo;
|
|
24
|
+
dataUpdateCommands?: JsonDataObjectUpdateCommand[];
|
|
25
|
+
setColumnMode?: 'fixed' | 'variable';
|
|
26
|
+
}
|
|
20
27
|
export declare function createChangeSet(): ChangeSet;
|
|
21
28
|
export interface ChangeSetRowDefinition {
|
|
22
29
|
pureName: string;
|
|
23
30
|
schemaName: string;
|
|
24
31
|
insertedRowIndex?: number;
|
|
32
|
+
existingRowIndex?: number;
|
|
25
33
|
condition?: {
|
|
26
34
|
[column: string]: string;
|
|
27
35
|
};
|
|
@@ -30,7 +38,7 @@ export interface ChangeSetFieldDefinition extends ChangeSetRowDefinition {
|
|
|
30
38
|
uniqueName: string;
|
|
31
39
|
columnName: string;
|
|
32
40
|
}
|
|
33
|
-
export declare function findExistingChangeSetItem(changeSet: ChangeSet, definition: ChangeSetRowDefinition): [keyof
|
|
41
|
+
export declare function findExistingChangeSetItem(changeSet: ChangeSet, definition: ChangeSetRowDefinition): [keyof ChangeSetItemFields, ChangeSetItem];
|
|
34
42
|
export declare function setChangeSetValue(changeSet: ChangeSet, definition: ChangeSetFieldDefinition, value: string): ChangeSet;
|
|
35
43
|
export declare function setChangeSetRowData(changeSet: ChangeSet, definition: ChangeSetRowDefinition, document: any): ChangeSet;
|
|
36
44
|
export declare function batchUpdateChangeSet(changeSet: ChangeSet, rowDefinitions: ChangeSetRowDefinition[], dataRows: []): ChangeSet;
|
package/lib/ChangeSet.js
CHANGED
|
@@ -27,12 +27,14 @@ function findExistingChangeSetItem(changeSet, definition) {
|
|
|
27
27
|
else {
|
|
28
28
|
const inUpdates = changeSet.updates.find(x => x.pureName == definition.pureName &&
|
|
29
29
|
x.schemaName == definition.schemaName &&
|
|
30
|
-
|
|
30
|
+
((definition.existingRowIndex != null && x.existingRowIndex == definition.existingRowIndex) ||
|
|
31
|
+
(definition.existingRowIndex == null && lodash_1.default.isEqual(x.condition, definition.condition))));
|
|
31
32
|
if (inUpdates)
|
|
32
33
|
return ['updates', inUpdates];
|
|
33
34
|
const inDeletes = changeSet.deletes.find(x => x.pureName == definition.pureName &&
|
|
34
35
|
x.schemaName == definition.schemaName &&
|
|
35
|
-
|
|
36
|
+
((definition.existingRowIndex != null && x.existingRowIndex == definition.existingRowIndex) ||
|
|
37
|
+
(definition.existingRowIndex == null && lodash_1.default.isEqual(x.condition, definition.condition))));
|
|
36
38
|
if (inDeletes)
|
|
37
39
|
return ['deletes', inDeletes];
|
|
38
40
|
return ['updates', null];
|
|
@@ -58,6 +60,7 @@ function setChangeSetValue(changeSet, definition, value) {
|
|
|
58
60
|
schemaName: definition.schemaName,
|
|
59
61
|
condition: definition.condition,
|
|
60
62
|
insertedRowIndex: definition.insertedRowIndex,
|
|
63
|
+
existingRowIndex: definition.existingRowIndex,
|
|
61
64
|
fields: {
|
|
62
65
|
[definition.uniqueName]: value,
|
|
63
66
|
},
|
|
@@ -84,6 +87,7 @@ function setChangeSetRowData(changeSet, definition, document) {
|
|
|
84
87
|
schemaName: definition.schemaName,
|
|
85
88
|
condition: definition.condition,
|
|
86
89
|
insertedRowIndex: definition.insertedRowIndex,
|
|
90
|
+
existingRowIndex: definition.existingRowIndex,
|
|
87
91
|
document,
|
|
88
92
|
},
|
|
89
93
|
] });
|
|
@@ -292,6 +296,7 @@ function deleteChangeSetRows(changeSet, definition) {
|
|
|
292
296
|
pureName: definition.pureName,
|
|
293
297
|
schemaName: definition.schemaName,
|
|
294
298
|
condition: definition.condition,
|
|
299
|
+
existingRowIndex: definition.existingRowIndex,
|
|
295
300
|
},
|
|
296
301
|
] });
|
|
297
302
|
}
|
|
@@ -299,11 +304,10 @@ function deleteChangeSetRows(changeSet, definition) {
|
|
|
299
304
|
exports.deleteChangeSetRows = deleteChangeSetRows;
|
|
300
305
|
function getChangeSetInsertedRows(changeSet, name) {
|
|
301
306
|
var _a;
|
|
302
|
-
if (!name)
|
|
303
|
-
return [];
|
|
307
|
+
// if (!name) return [];
|
|
304
308
|
if (!changeSet)
|
|
305
309
|
return [];
|
|
306
|
-
const rows = changeSet.inserts.filter(x => x.pureName == name.pureName && x.schemaName == name.schemaName);
|
|
310
|
+
const rows = changeSet.inserts.filter(x => name == null || (x.pureName == name.pureName && x.schemaName == name.schemaName));
|
|
307
311
|
const maxIndex = (_a = lodash_1.default.maxBy(rows, x => x.insertedRowIndex)) === null || _a === void 0 ? void 0 : _a.insertedRowIndex;
|
|
308
312
|
if (maxIndex == null)
|
|
309
313
|
return [];
|
|
@@ -332,8 +336,14 @@ function changeSetInsertDocuments(changeSet, documents, name) {
|
|
|
332
336
|
}
|
|
333
337
|
exports.changeSetInsertDocuments = changeSetInsertDocuments;
|
|
334
338
|
function changeSetContainsChanges(changeSet) {
|
|
339
|
+
var _a;
|
|
335
340
|
if (!changeSet)
|
|
336
341
|
return false;
|
|
337
|
-
return changeSet.deletes.length > 0 ||
|
|
342
|
+
return (changeSet.deletes.length > 0 ||
|
|
343
|
+
changeSet.updates.length > 0 ||
|
|
344
|
+
changeSet.inserts.length > 0 ||
|
|
345
|
+
!!changeSet.structure ||
|
|
346
|
+
!!changeSet.setColumnMode ||
|
|
347
|
+
((_a = changeSet.dataUpdateCommands) === null || _a === void 0 ? void 0 : _a.length) > 0);
|
|
338
348
|
}
|
|
339
349
|
exports.changeSetContainsChanges = changeSetContainsChanges;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/// <reference types="lodash" />
|
|
2
|
+
import { DatabaseInfo, EngineDriver, ForeignKeyInfo, TableInfo } from 'dbgate-types';
|
|
3
|
+
export interface DataDuplicatorItem {
|
|
4
|
+
openStream: () => Promise<ReadableStream>;
|
|
5
|
+
name: string;
|
|
6
|
+
operation: 'copy' | 'lookup' | 'insertMissing';
|
|
7
|
+
matchColumns: string[];
|
|
8
|
+
}
|
|
9
|
+
export interface DataDuplicatorOptions {
|
|
10
|
+
rollbackAfterFinish?: boolean;
|
|
11
|
+
skipRowsWithUnresolvedRefs?: boolean;
|
|
12
|
+
}
|
|
13
|
+
declare class DuplicatorReference {
|
|
14
|
+
base: DuplicatorItemHolder;
|
|
15
|
+
ref: DuplicatorItemHolder;
|
|
16
|
+
isMandatory: boolean;
|
|
17
|
+
foreignKey: ForeignKeyInfo;
|
|
18
|
+
constructor(base: DuplicatorItemHolder, ref: DuplicatorItemHolder, isMandatory: boolean, foreignKey: ForeignKeyInfo);
|
|
19
|
+
get columnName(): string;
|
|
20
|
+
}
|
|
21
|
+
declare class DuplicatorItemHolder {
|
|
22
|
+
item: DataDuplicatorItem;
|
|
23
|
+
duplicator: DataDuplicator;
|
|
24
|
+
references: DuplicatorReference[];
|
|
25
|
+
backReferences: DuplicatorReference[];
|
|
26
|
+
table: TableInfo;
|
|
27
|
+
isPlanned: boolean;
|
|
28
|
+
idMap: {};
|
|
29
|
+
autoColumn: string;
|
|
30
|
+
refByColumn: {
|
|
31
|
+
[columnName: string]: DuplicatorReference;
|
|
32
|
+
};
|
|
33
|
+
isReferenced: boolean;
|
|
34
|
+
get name(): string;
|
|
35
|
+
constructor(item: DataDuplicatorItem, duplicator: DataDuplicator);
|
|
36
|
+
initializeReferences(): void;
|
|
37
|
+
createInsertObject(chunk: any): import("lodash").Omit<Pick<any, string>, string>;
|
|
38
|
+
runImport(): Promise<{
|
|
39
|
+
inserted: number;
|
|
40
|
+
mapped: number;
|
|
41
|
+
missing: number;
|
|
42
|
+
skipped: number;
|
|
43
|
+
}>;
|
|
44
|
+
}
|
|
45
|
+
export declare class DataDuplicator {
|
|
46
|
+
pool: any;
|
|
47
|
+
driver: EngineDriver;
|
|
48
|
+
db: DatabaseInfo;
|
|
49
|
+
items: DataDuplicatorItem[];
|
|
50
|
+
stream: any;
|
|
51
|
+
copyStream: (input: any, output: any) => Promise<void>;
|
|
52
|
+
options: DataDuplicatorOptions;
|
|
53
|
+
itemHolders: DuplicatorItemHolder[];
|
|
54
|
+
itemPlan: DuplicatorItemHolder[];
|
|
55
|
+
constructor(pool: any, driver: EngineDriver, db: DatabaseInfo, items: DataDuplicatorItem[], stream: any, copyStream: (input: any, output: any) => Promise<void>, options?: DataDuplicatorOptions);
|
|
56
|
+
findItemToPlan(): DuplicatorItemHolder;
|
|
57
|
+
createPlan(): void;
|
|
58
|
+
run(): Promise<void>;
|
|
59
|
+
}
|
|
60
|
+
export {};
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.DataDuplicator = void 0;
|
|
16
|
+
const dbgate_tools_1 = require("dbgate-tools");
|
|
17
|
+
const pick_1 = __importDefault(require("lodash/pick"));
|
|
18
|
+
const omit_1 = __importDefault(require("lodash/omit"));
|
|
19
|
+
const logger = (0, dbgate_tools_1.getLogger)('dataDuplicator');
|
|
20
|
+
class DuplicatorReference {
|
|
21
|
+
constructor(base, ref, isMandatory, foreignKey) {
|
|
22
|
+
this.base = base;
|
|
23
|
+
this.ref = ref;
|
|
24
|
+
this.isMandatory = isMandatory;
|
|
25
|
+
this.foreignKey = foreignKey;
|
|
26
|
+
}
|
|
27
|
+
get columnName() {
|
|
28
|
+
return this.foreignKey.columns[0].columnName;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
class DuplicatorItemHolder {
|
|
32
|
+
constructor(item, duplicator) {
|
|
33
|
+
var _a, _b, _c, _d, _e;
|
|
34
|
+
this.item = item;
|
|
35
|
+
this.duplicator = duplicator;
|
|
36
|
+
this.references = [];
|
|
37
|
+
this.backReferences = [];
|
|
38
|
+
this.isPlanned = false;
|
|
39
|
+
this.idMap = {};
|
|
40
|
+
this.refByColumn = {};
|
|
41
|
+
this.table = duplicator.db.tables.find(x => x.pureName.toUpperCase() == item.name.toUpperCase());
|
|
42
|
+
this.autoColumn = (_a = this.table.columns.find(x => x.autoIncrement)) === null || _a === void 0 ? void 0 : _a.columnName;
|
|
43
|
+
if (((_c = (_b = this.table.primaryKey) === null || _b === void 0 ? void 0 : _b.columns) === null || _c === void 0 ? void 0 : _c.length) != 1 ||
|
|
44
|
+
((_e = (_d = this.table.primaryKey) === null || _d === void 0 ? void 0 : _d.columns) === null || _e === void 0 ? void 0 : _e[0].columnName) != this.autoColumn) {
|
|
45
|
+
this.autoColumn = null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
get name() {
|
|
49
|
+
return this.item.name;
|
|
50
|
+
}
|
|
51
|
+
initializeReferences() {
|
|
52
|
+
var _a, _b;
|
|
53
|
+
for (const fk of this.table.foreignKeys) {
|
|
54
|
+
if (((_a = fk.columns) === null || _a === void 0 ? void 0 : _a.length) != 1)
|
|
55
|
+
continue;
|
|
56
|
+
const refHolder = this.duplicator.itemHolders.find(y => y.name.toUpperCase() == fk.refTableName.toUpperCase());
|
|
57
|
+
if (refHolder == null)
|
|
58
|
+
continue;
|
|
59
|
+
const isMandatory = (_b = this.table.columns.find(x => { var _a; return x.columnName == ((_a = fk.columns[0]) === null || _a === void 0 ? void 0 : _a.columnName); })) === null || _b === void 0 ? void 0 : _b.notNull;
|
|
60
|
+
const newref = new DuplicatorReference(this, refHolder, isMandatory, fk);
|
|
61
|
+
this.references.push(newref);
|
|
62
|
+
this.refByColumn[newref.columnName] = newref;
|
|
63
|
+
refHolder.isReferenced = true;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
createInsertObject(chunk) {
|
|
67
|
+
const res = (0, omit_1.default)((0, pick_1.default)(chunk, this.table.columns.map(x => x.columnName)), [this.autoColumn, ...this.backReferences.map(x => x.columnName)]);
|
|
68
|
+
for (const key in res) {
|
|
69
|
+
const ref = this.refByColumn[key];
|
|
70
|
+
if (ref) {
|
|
71
|
+
// remap id
|
|
72
|
+
res[key] = ref.ref.idMap[res[key]];
|
|
73
|
+
if (ref.isMandatory && res[key] == null) {
|
|
74
|
+
// mandatory refertence not matched
|
|
75
|
+
if (this.duplicator.options.skipRowsWithUnresolvedRefs) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
throw new Error(`Unresolved reference, base=${ref.base.name}, ref=${ref.ref.name}, ${key}=${chunk[key]}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return res;
|
|
83
|
+
}
|
|
84
|
+
runImport() {
|
|
85
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
86
|
+
const readStream = yield this.item.openStream();
|
|
87
|
+
const driver = this.duplicator.driver;
|
|
88
|
+
const pool = this.duplicator.pool;
|
|
89
|
+
let inserted = 0;
|
|
90
|
+
let mapped = 0;
|
|
91
|
+
let missing = 0;
|
|
92
|
+
let skipped = 0;
|
|
93
|
+
let lastLogged = new Date();
|
|
94
|
+
const writeStream = (0, dbgate_tools_1.createAsyncWriteStream)(this.duplicator.stream, {
|
|
95
|
+
processItem: (chunk) => __awaiter(this, void 0, void 0, function* () {
|
|
96
|
+
var _a, _b, _c;
|
|
97
|
+
if (chunk.__isStreamHeader) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const doCopy = () => __awaiter(this, void 0, void 0, function* () {
|
|
101
|
+
var _d, _e, _f;
|
|
102
|
+
// console.log('chunk', this.name, JSON.stringify(chunk));
|
|
103
|
+
const insertedObj = this.createInsertObject(chunk);
|
|
104
|
+
// console.log('insertedObj', this.name, JSON.stringify(insertedObj));
|
|
105
|
+
if (insertedObj == null) {
|
|
106
|
+
skipped += 1;
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
let res = yield (0, dbgate_tools_1.runQueryOnDriver)(pool, driver, dmp => {
|
|
110
|
+
dmp.put('^insert ^into %f (%,i) ^values (%,v)', this.table, Object.keys(insertedObj), Object.values(insertedObj));
|
|
111
|
+
if (this.autoColumn &&
|
|
112
|
+
this.isReferenced &&
|
|
113
|
+
!this.duplicator.driver.dialect.requireStandaloneSelectForScopeIdentity) {
|
|
114
|
+
dmp.selectScopeIdentity(this.table);
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
inserted += 1;
|
|
118
|
+
if (this.autoColumn && this.isReferenced) {
|
|
119
|
+
if (this.duplicator.driver.dialect.requireStandaloneSelectForScopeIdentity) {
|
|
120
|
+
res = yield (0, dbgate_tools_1.runQueryOnDriver)(pool, driver, dmp => dmp.selectScopeIdentity(this.table));
|
|
121
|
+
}
|
|
122
|
+
// console.log('IDRES', JSON.stringify(res));
|
|
123
|
+
const resId = (_f = (_e = Object.entries((_d = res === null || res === void 0 ? void 0 : res.rows) === null || _d === void 0 ? void 0 : _d[0])) === null || _e === void 0 ? void 0 : _e[0]) === null || _f === void 0 ? void 0 : _f[1];
|
|
124
|
+
if (resId != null) {
|
|
125
|
+
this.idMap[chunk[this.autoColumn]] = resId;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
switch (this.item.operation) {
|
|
130
|
+
case 'copy': {
|
|
131
|
+
yield doCopy();
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
case 'insertMissing':
|
|
135
|
+
case 'lookup': {
|
|
136
|
+
const res = yield (0, dbgate_tools_1.runQueryOnDriver)(pool, driver, dmp => dmp.put('^select %i ^from %f ^where %i = %v', this.autoColumn, this.table, this.item.matchColumns[0], chunk[this.item.matchColumns[0]]));
|
|
137
|
+
const resId = (_c = (_b = Object.entries((_a = res === null || res === void 0 ? void 0 : res.rows) === null || _a === void 0 ? void 0 : _a[0])) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c[1];
|
|
138
|
+
if (resId != null) {
|
|
139
|
+
mapped += 1;
|
|
140
|
+
this.idMap[chunk[this.autoColumn]] = resId;
|
|
141
|
+
}
|
|
142
|
+
else if (this.item.operation == 'insertMissing') {
|
|
143
|
+
yield doCopy();
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
missing += 1;
|
|
147
|
+
}
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (new Date().getTime() - lastLogged.getTime() > 5000) {
|
|
152
|
+
logger.info(`Duplicating ${this.item.name} in progress, inserted ${inserted} rows, mapped ${mapped} rows, missing ${missing} rows, skipped ${skipped} rows`);
|
|
153
|
+
lastLogged = new Date();
|
|
154
|
+
}
|
|
155
|
+
// this.idMap[oldId] = newId;
|
|
156
|
+
}),
|
|
157
|
+
});
|
|
158
|
+
yield this.duplicator.copyStream(readStream, writeStream);
|
|
159
|
+
// await this.duplicator.driver.writeQueryStream(this.duplicator.pool, {
|
|
160
|
+
// mapResultId: (oldId, newId) => {
|
|
161
|
+
// this.idMap[oldId] = newId;
|
|
162
|
+
// },
|
|
163
|
+
// });
|
|
164
|
+
return { inserted, mapped, missing, skipped };
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
class DataDuplicator {
|
|
169
|
+
constructor(pool, driver, db, items, stream, copyStream, options = {}) {
|
|
170
|
+
this.pool = pool;
|
|
171
|
+
this.driver = driver;
|
|
172
|
+
this.db = db;
|
|
173
|
+
this.items = items;
|
|
174
|
+
this.stream = stream;
|
|
175
|
+
this.copyStream = copyStream;
|
|
176
|
+
this.options = options;
|
|
177
|
+
this.itemPlan = [];
|
|
178
|
+
this.itemHolders = items.map(x => new DuplicatorItemHolder(x, this));
|
|
179
|
+
this.itemHolders.forEach(x => x.initializeReferences());
|
|
180
|
+
}
|
|
181
|
+
findItemToPlan() {
|
|
182
|
+
for (const item of this.itemHolders) {
|
|
183
|
+
if (item.isPlanned)
|
|
184
|
+
continue;
|
|
185
|
+
if (item.references.every(x => x.ref.isPlanned)) {
|
|
186
|
+
return item;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
for (const item of this.itemHolders) {
|
|
190
|
+
if (item.isPlanned)
|
|
191
|
+
continue;
|
|
192
|
+
if (item.references.every(x => x.ref.isPlanned || !x.isMandatory)) {
|
|
193
|
+
const backReferences = item.references.filter(x => !x.ref.isPlanned);
|
|
194
|
+
item.backReferences = backReferences;
|
|
195
|
+
return item;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
throw new Error('Cycle in mandatory references');
|
|
199
|
+
}
|
|
200
|
+
createPlan() {
|
|
201
|
+
while (this.itemPlan.length < this.itemHolders.length) {
|
|
202
|
+
const item = this.findItemToPlan();
|
|
203
|
+
item.isPlanned = true;
|
|
204
|
+
this.itemPlan.push(item);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
run() {
|
|
208
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
209
|
+
this.createPlan();
|
|
210
|
+
yield (0, dbgate_tools_1.runCommandOnDriver)(this.pool, this.driver, dmp => dmp.beginTransaction());
|
|
211
|
+
try {
|
|
212
|
+
for (const item of this.itemPlan) {
|
|
213
|
+
const stats = yield item.runImport();
|
|
214
|
+
logger.info(`Duplicated ${item.name}, inserted ${stats.inserted} rows, mapped ${stats.mapped} rows, missing ${stats.missing} rows, skipped ${stats.skipped} rows`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
catch (err) {
|
|
218
|
+
logger.error({ err }, `Failed duplicator job, rollbacking. ${err.message}`);
|
|
219
|
+
yield (0, dbgate_tools_1.runCommandOnDriver)(this.pool, this.driver, dmp => dmp.rollbackTransaction());
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
if (this.options.rollbackAfterFinish) {
|
|
223
|
+
logger.info('Rollbacking transaction, nothing was changed');
|
|
224
|
+
yield (0, dbgate_tools_1.runCommandOnDriver)(this.pool, this.driver, dmp => dmp.rollbackTransaction());
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
logger.info('Committing duplicator transaction');
|
|
228
|
+
yield (0, dbgate_tools_1.runCommandOnDriver)(this.pool, this.driver, dmp => dmp.commitTransaction());
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
exports.DataDuplicator = DataDuplicator;
|
package/lib/GridConfig.d.ts
CHANGED
package/lib/GridDisplay.d.ts
CHANGED
|
@@ -48,6 +48,7 @@ export declare abstract class GridDisplay {
|
|
|
48
48
|
get baseTableOrCollection(): NamedObjectInfo;
|
|
49
49
|
get baseTableOrView(): TableInfo | ViewInfo;
|
|
50
50
|
changeSetKeyFields: string[];
|
|
51
|
+
editableStructure: TableInfo;
|
|
51
52
|
sortable: boolean;
|
|
52
53
|
groupable: boolean;
|
|
53
54
|
filterable: boolean;
|
|
@@ -82,6 +83,7 @@ export declare abstract class GridDisplay {
|
|
|
82
83
|
toggleExpandedColumn(uniqueName: string, value?: boolean): void;
|
|
83
84
|
getFilter(uniqueName: string): string;
|
|
84
85
|
setFilter(uniqueName: any, value: any): void;
|
|
86
|
+
setMutliColumnFilter(value: any): void;
|
|
85
87
|
showFilter(uniqueName: any): void;
|
|
86
88
|
removeFilter(uniqueName: any): void;
|
|
87
89
|
setFilters(dct: any): void;
|
|
@@ -98,8 +100,8 @@ export declare abstract class GridDisplay {
|
|
|
98
100
|
clearFilters(): void;
|
|
99
101
|
resetConfig(): void;
|
|
100
102
|
getChangeSetCondition(row: any): Pick<any, string>;
|
|
101
|
-
getChangeSetField(row: any, uniqueName: any, insertedRowIndex: any): ChangeSetFieldDefinition;
|
|
102
|
-
getChangeSetRow(row: any, insertedRowIndex: any): ChangeSetRowDefinition;
|
|
103
|
+
getChangeSetField(row: any, uniqueName: any, insertedRowIndex: any, existingRowIndex?: any, baseNameOmitable?: boolean): ChangeSetFieldDefinition;
|
|
104
|
+
getChangeSetRow(row: any, insertedRowIndex: any, existingRowIndex: any, baseNameOmitable?: boolean): ChangeSetRowDefinition;
|
|
103
105
|
createSelect(options?: {}): Select;
|
|
104
106
|
processReferences(select: Select, displayedColumnInfo: DisplayedColumnInfo, options: any): void;
|
|
105
107
|
createColumnExpression(col: any, source: any, alias?: any): any;
|
|
@@ -115,7 +117,7 @@ export declare abstract class GridDisplay {
|
|
|
115
117
|
}[];
|
|
116
118
|
resizeColumn(uniqueName: string, computedSize: number, diff: number): void;
|
|
117
119
|
getCountQuery(): Select;
|
|
118
|
-
|
|
120
|
+
compileJslFilters(): Condition;
|
|
119
121
|
switchToFormView(rowIndex: any): void;
|
|
120
122
|
switchToJsonView(): void;
|
|
121
123
|
formViewNavigate(command: any, allRowCount: any): void;
|
package/lib/GridDisplay.js
CHANGED
|
@@ -21,6 +21,7 @@ class GridDisplay {
|
|
|
21
21
|
this.serverVersion = serverVersion;
|
|
22
22
|
this.formColumns = [];
|
|
23
23
|
this.changeSetKeyFields = null;
|
|
24
|
+
this.editableStructure = null;
|
|
24
25
|
this.sortable = false;
|
|
25
26
|
this.groupable = false;
|
|
26
27
|
this.filterable = false;
|
|
@@ -133,6 +134,30 @@ class GridDisplay {
|
|
|
133
134
|
continue;
|
|
134
135
|
}
|
|
135
136
|
}
|
|
137
|
+
if (this.baseTableOrView && this.config.multiColumnFilter) {
|
|
138
|
+
try {
|
|
139
|
+
const condition = (0, dbgate_filterparser_1.parseFilter)(this.config.multiColumnFilter, 'string');
|
|
140
|
+
if (condition) {
|
|
141
|
+
const orCondition = {
|
|
142
|
+
conditionType: 'or',
|
|
143
|
+
conditions: [],
|
|
144
|
+
};
|
|
145
|
+
for (const column of this.baseTableOrView.columns) {
|
|
146
|
+
orCondition.conditions.push(lodash_1.default.cloneDeepWith(condition, (expr) => {
|
|
147
|
+
if (expr.exprType == 'placeholder') {
|
|
148
|
+
return this.createColumnExpression(column, { alias: 'basetbl' });
|
|
149
|
+
}
|
|
150
|
+
}));
|
|
151
|
+
}
|
|
152
|
+
if (orCondition.conditions.length > 0) {
|
|
153
|
+
conditions.push(orCondition);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
catch (err) {
|
|
158
|
+
console.warn(err.message);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
136
161
|
if (conditions.length > 0) {
|
|
137
162
|
select.where = {
|
|
138
163
|
conditionType: 'and',
|
|
@@ -241,6 +266,10 @@ class GridDisplay {
|
|
|
241
266
|
this.setConfig(cfg => (Object.assign(Object.assign({}, cfg), { filters: Object.assign(Object.assign({}, cfg.filters), { [uniqueName]: value }), formViewRecordNumber: 0 })));
|
|
242
267
|
this.reload();
|
|
243
268
|
}
|
|
269
|
+
setMutliColumnFilter(value) {
|
|
270
|
+
this.setConfig(cfg => (Object.assign(Object.assign({}, cfg), { multiColumnFilter: value, formViewRecordNumber: 0 })));
|
|
271
|
+
this.reload();
|
|
272
|
+
}
|
|
244
273
|
showFilter(uniqueName) {
|
|
245
274
|
this.setConfig(cfg => {
|
|
246
275
|
if (!cfg.filters.uniqueName)
|
|
@@ -319,27 +348,30 @@ class GridDisplay {
|
|
|
319
348
|
return null;
|
|
320
349
|
return lodash_1.default.pick(row, this.changeSetKeyFields);
|
|
321
350
|
}
|
|
322
|
-
getChangeSetField(row, uniqueName, insertedRowIndex) {
|
|
351
|
+
getChangeSetField(row, uniqueName, insertedRowIndex, existingRowIndex = null, baseNameOmitable = false) {
|
|
323
352
|
const col = this.columns.find(x => x.uniqueName == uniqueName);
|
|
324
353
|
if (!col)
|
|
325
354
|
return null;
|
|
326
355
|
const baseObj = this.baseTableOrSimilar;
|
|
327
|
-
if (!
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
356
|
+
if (!baseNameOmitable) {
|
|
357
|
+
if (!baseObj)
|
|
358
|
+
return null;
|
|
359
|
+
if (baseObj.pureName != col.pureName || baseObj.schemaName != col.schemaName) {
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
331
362
|
}
|
|
332
|
-
return Object.assign(Object.assign({}, this.getChangeSetRow(row, insertedRowIndex)), { uniqueName: uniqueName, columnName: col.columnName });
|
|
363
|
+
return Object.assign(Object.assign({}, this.getChangeSetRow(row, insertedRowIndex, existingRowIndex, baseNameOmitable)), { uniqueName: uniqueName, columnName: col.columnName });
|
|
333
364
|
}
|
|
334
|
-
getChangeSetRow(row, insertedRowIndex) {
|
|
365
|
+
getChangeSetRow(row, insertedRowIndex, existingRowIndex, baseNameOmitable = false) {
|
|
335
366
|
const baseObj = this.baseTableOrSimilar;
|
|
336
|
-
if (!baseObj)
|
|
367
|
+
if (!baseNameOmitable && !baseObj)
|
|
337
368
|
return null;
|
|
338
369
|
return {
|
|
339
|
-
pureName: baseObj.pureName,
|
|
340
|
-
schemaName: baseObj.schemaName,
|
|
370
|
+
pureName: baseObj === null || baseObj === void 0 ? void 0 : baseObj.pureName,
|
|
371
|
+
schemaName: baseObj === null || baseObj === void 0 ? void 0 : baseObj.schemaName,
|
|
341
372
|
insertedRowIndex,
|
|
342
|
-
|
|
373
|
+
existingRowIndex,
|
|
374
|
+
condition: insertedRowIndex == null && existingRowIndex == null ? this.getChangeSetCondition(row) : null,
|
|
343
375
|
};
|
|
344
376
|
}
|
|
345
377
|
createSelect(options = {}) {
|
|
@@ -357,6 +389,7 @@ class GridDisplay {
|
|
|
357
389
|
return Object.assign({ exprType: 'column', alias: alias || col.columnName, source }, col);
|
|
358
390
|
}
|
|
359
391
|
createSelectBase(name, columns, options) {
|
|
392
|
+
var _a;
|
|
360
393
|
if (!columns)
|
|
361
394
|
return null;
|
|
362
395
|
const orderColumnName = columns[0].columnName;
|
|
@@ -367,13 +400,15 @@ class GridDisplay {
|
|
|
367
400
|
alias: 'basetbl',
|
|
368
401
|
},
|
|
369
402
|
columns: columns.map(col => this.createColumnExpression(col, { alias: 'basetbl' })),
|
|
370
|
-
orderBy:
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
403
|
+
orderBy: ((_a = this.driver) === null || _a === void 0 ? void 0 : _a.requiresDefaultSortCriteria)
|
|
404
|
+
? [
|
|
405
|
+
{
|
|
406
|
+
exprType: 'column',
|
|
407
|
+
columnName: orderColumnName,
|
|
408
|
+
direction: 'ASC',
|
|
409
|
+
},
|
|
410
|
+
]
|
|
411
|
+
: null,
|
|
377
412
|
};
|
|
378
413
|
const displayedColumnInfo = lodash_1.default.keyBy(this.columns.map(col => (Object.assign(Object.assign({}, col), { sourceAlias: 'basetbl' }))), 'uniqueName');
|
|
379
414
|
this.processReferences(select, displayedColumnInfo, options);
|
|
@@ -523,7 +558,7 @@ class GridDisplay {
|
|
|
523
558
|
// const sql = treeToSql(this.driver, select, dumpSqlSelect);
|
|
524
559
|
// return sql;
|
|
525
560
|
}
|
|
526
|
-
|
|
561
|
+
compileJslFilters() {
|
|
527
562
|
var _a;
|
|
528
563
|
const filters = this.config && this.config.filters;
|
|
529
564
|
if (!filters)
|
|
@@ -549,6 +584,15 @@ class GridDisplay {
|
|
|
549
584
|
// filter parse error - ignore filter
|
|
550
585
|
}
|
|
551
586
|
}
|
|
587
|
+
if (this.config.multiColumnFilter) {
|
|
588
|
+
const placeholderCondition = (0, dbgate_filterparser_1.parseFilter)(this.config.multiColumnFilter, 'string');
|
|
589
|
+
if (placeholderCondition) {
|
|
590
|
+
conditions.push({
|
|
591
|
+
conditionType: 'anyColumnPass',
|
|
592
|
+
placeholderCondition,
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
}
|
|
552
596
|
if (conditions.length == 0)
|
|
553
597
|
return null;
|
|
554
598
|
return {
|
package/lib/JslGridDisplay.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { GridDisplay, ChangeCacheFunc, ChangeConfigFunc } from './GridDisplay';
|
|
2
2
|
import { GridConfig, GridCache } from './GridConfig';
|
|
3
3
|
export declare class JslGridDisplay extends GridDisplay {
|
|
4
|
-
constructor(jslid: any, structure: any, config: GridConfig, setConfig: ChangeConfigFunc, cache: GridCache, setCache: ChangeCacheFunc, rows: any, isDynamicStructure: boolean, supportsReload: boolean);
|
|
4
|
+
constructor(jslid: any, structure: any, config: GridConfig, setConfig: ChangeConfigFunc, cache: GridCache, setCache: ChangeCacheFunc, rows: any, isDynamicStructure: boolean, supportsReload: boolean, editable?: boolean);
|
|
5
5
|
}
|
package/lib/JslGridDisplay.js
CHANGED
|
@@ -8,13 +8,16 @@ const lodash_1 = __importDefault(require("lodash"));
|
|
|
8
8
|
const GridDisplay_1 = require("./GridDisplay");
|
|
9
9
|
const CollectionGridDisplay_1 = require("./CollectionGridDisplay");
|
|
10
10
|
class JslGridDisplay extends GridDisplay_1.GridDisplay {
|
|
11
|
-
constructor(jslid, structure, config, setConfig, cache, setCache, rows, isDynamicStructure, supportsReload) {
|
|
11
|
+
constructor(jslid, structure, config, setConfig, cache, setCache, rows, isDynamicStructure, supportsReload, editable = false) {
|
|
12
12
|
var _a;
|
|
13
13
|
super(config, setConfig, cache, setCache, null);
|
|
14
14
|
this.filterable = true;
|
|
15
|
+
this.sortable = true;
|
|
15
16
|
this.supportsReload = supportsReload;
|
|
16
17
|
this.isDynamicStructure = isDynamicStructure;
|
|
17
18
|
this.filterTypeOverride = 'eval';
|
|
19
|
+
this.editable = editable;
|
|
20
|
+
this.editableStructure = editable ? structure : null;
|
|
18
21
|
if (structure === null || structure === void 0 ? void 0 : structure.columns) {
|
|
19
22
|
this.columns = lodash_1.default.uniqBy((_a = structure.columns
|
|
20
23
|
.map(col => ({
|
package/lib/MacroDefinition.d.ts
CHANGED