dbgate-datalib 5.2.2 → 5.2.4-alpha.1

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.
@@ -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 ChangeSet {
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 ChangeSet, ChangeSetItem];
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
- lodash_1.default.isEqual(x.condition, definition.condition));
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
- lodash_1.default.isEqual(x.condition, definition.condition));
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 || changeSet.updates.length > 0 || changeSet.inserts.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;
@@ -33,6 +33,7 @@ export interface GridConfig extends GridConfigColumns {
33
33
  formViewRecordNumber?: number;
34
34
  formFilterColumns: string[];
35
35
  formColumnFilterText?: string;
36
+ multiColumnFilter?: string;
36
37
  }
37
38
  export interface GridCache {
38
39
  refreshTime: number;
@@ -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
- compileFilters(): Condition;
120
+ compileJslFilters(): Condition;
119
121
  switchToFormView(rowIndex: any): void;
120
122
  switchToJsonView(): void;
121
123
  formViewNavigate(command: any, allRowCount: any): void;
@@ -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 (!baseObj)
328
- return null;
329
- if (baseObj.pureName != col.pureName || baseObj.schemaName != col.schemaName) {
330
- return null;
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
- condition: insertedRowIndex == null ? this.getChangeSetCondition(row) : null,
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
- exprType: 'column',
373
- columnName: orderColumnName,
374
- direction: 'ASC',
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
- compileFilters() {
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 {
@@ -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
  }
@@ -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 => ({
@@ -8,7 +8,7 @@ export interface MacroDefinition {
8
8
  name: string;
9
9
  group: string;
10
10
  description?: string;
11
- type: 'transformValue';
11
+ type: 'transformValue' | 'transformRow';
12
12
  code: string;
13
13
  args?: MacroArgument[];
14
14
  }
@@ -46,6 +46,7 @@ export interface PerspectiveNodeConfig {
46
46
  };
47
47
  isAutoGenerated?: true | undefined;
48
48
  isNodeChecked?: boolean;
49
+ multiColumnFilter?: string;
49
50
  position?: {
50
51
  x: number;
51
52
  y: number;