@egi/smart-db 2.3.1 → 2.3.2
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/.eslintrc.json +212 -0
- package/README.md +2 -0
- package/bin/copy-assets +6 -0
- package/package.json +1 -1
- package/src/drivers/smart-db-better-sqlite3.ts +284 -0
- package/src/drivers/smart-db-mysql.ts +159 -0
- package/src/drivers/smart-db-mysql2.ts +159 -0
- package/src/drivers/smart-db-sqlite3.ts +198 -0
- package/src/helpers/extract-db-api.ts +465 -0
- package/src/helpers/terser-tree.ts +39 -0
- package/src/models/abstract-model.ts +209 -0
- package/src/models/smart-db-core-table-model.ts +161 -0
- package/src/models/smart-db-dictionary.ts +28 -0
- package/src/models/smart-db-log-model.ts +316 -0
- package/src/models/smart-db-version-model.ts +316 -0
- package/src/models/smart-db-version-view-model.ts +347 -0
- package/src/models/sqlite-master-model.ts +140 -0
- package/src/models/sqlite-sequence-model.ts +91 -0
- package/{smart-db-api.d.ts → src/smart-db-api.ts} +3 -0
- package/src/smart-db-globals.ts +136 -0
- package/{smart-db-interfaces.d.ts → src/smart-db-interfaces.ts} +82 -34
- package/src/smart-db-log.ts +262 -0
- package/src/smart-db-sql-build-data.ts +28 -0
- package/src/smart-db-upgrade-manager.ts +171 -0
- package/src/smart-db.ts +854 -0
- package/test/data/sql-engine-tests.json +232 -0
- package/test/db/mysql/database-init.sql +11 -0
- package/test/db/sqlite3/database-init.sql +11 -0
- package/test/exer.js +28 -0
- package/test/model/smart-db-dictionary.ts +19 -0
- package/test/model/test-table-model.ts +214 -0
- package/test/test.js +273 -0
- package/test/test2.js +252 -0
- package/tsconfig.json +32 -0
- package/tsconfig.pro.json +32 -0
- package/tsconfig.test-model.json +23 -0
- package/drivers/smart-db-better-sqlite3.d.ts +0 -36
- package/drivers/smart-db-better-sqlite3.js +0 -1
- package/drivers/smart-db-mysql.d.ts +0 -26
- package/drivers/smart-db-mysql.js +0 -1
- package/drivers/smart-db-mysql2.d.ts +0 -26
- package/drivers/smart-db-mysql2.js +0 -1
- package/drivers/smart-db-sqlite3.d.ts +0 -30
- package/drivers/smart-db-sqlite3.js +0 -1
- package/helpers/extract-db-api.d.ts +0 -1
- package/helpers/extract-db-api.js +0 -1
- package/models/abstract-model.d.ts +0 -22
- package/models/abstract-model.js +0 -1
- package/models/smart-db-core-table-model.d.ts +0 -35
- package/models/smart-db-core-table-model.js +0 -1
- package/models/smart-db-dictionary.d.ts +0 -13
- package/models/smart-db-dictionary.js +0 -1
- package/models/smart-db-log-model.d.ts +0 -65
- package/models/smart-db-log-model.js +0 -1
- package/models/smart-db-version-model.d.ts +0 -65
- package/models/smart-db-version-model.js +0 -1
- package/models/smart-db-version-view-model.d.ts +0 -71
- package/models/smart-db-version-view-model.js +0 -1
- package/models/sqlite-master-model.d.ts +0 -36
- package/models/sqlite-master-model.js +0 -1
- package/models/sqlite-sequence-model.d.ts +0 -24
- package/models/sqlite-sequence-model.js +0 -1
- package/smart-db-api.js +0 -1
- package/smart-db-globals.d.ts +0 -22
- package/smart-db-globals.js +0 -1
- package/smart-db-interfaces.js +0 -1
- package/smart-db-log.d.ts +0 -58
- package/smart-db-log.js +0 -1
- package/smart-db-sql-build-data.d.ts +0 -9
- package/smart-db-sql-build-data.js +0 -1
- package/smart-db-upgrade-manager.d.ts +0 -13
- package/smart-db-upgrade-manager.js +0 -1
- package/smart-db.d.ts +0 -67
- package/smart-db.js +0 -1
package/src/smart-db.ts
ADDED
|
@@ -0,0 +1,854 @@
|
|
|
1
|
+
import _ from "lodash";
|
|
2
|
+
import {AbstractModel} from "./models/abstract-model";
|
|
3
|
+
import {SmartDbDictionary} from "./models/smart-db-dictionary";
|
|
4
|
+
import {SmartDbLogModel} from "./models/smart-db-log-model";
|
|
5
|
+
import {SmartDbVersionViewModel} from "./models/smart-db-version-view-model";
|
|
6
|
+
import {IN, smartDbToDate, toSmartDbDate, toSmartDbTimestamp} from "./smart-db-globals";
|
|
7
|
+
import {
|
|
8
|
+
AbstractModelGlobals, AttributeInfo, FieldNamingStyle, GenericModelData, IndexedGenericModelData, KeyValueList, ModelAttributeMap, SectionDescription,
|
|
9
|
+
SmartDbApi, SmartDbConnector, SmartDbDatabase, SmartDbOptions, SmartDbRunResult, SmartDbSqlComparison, SmartDbSqlOptions, SmartDbTableInfo,
|
|
10
|
+
SqlFieldDescriptor, SqlFieldOperationType, SqlLimit, SqlOperation, SqlOperationType, SqlOrderBy, SqlUpdateValues, SqlValueType, SqlWhere
|
|
11
|
+
} from "./smart-db-interfaces";
|
|
12
|
+
import {SmartDbLog, SmartDbLogLocation} from "./smart-db-log";
|
|
13
|
+
import {SmartDbSqlBuildData} from "./smart-db-sql-build-data";
|
|
14
|
+
import {SmartDbUpgradeManager} from "./smart-db-upgrade-manager";
|
|
15
|
+
|
|
16
|
+
export abstract class SmartDb implements SmartDbApi {
|
|
17
|
+
public abstract getDatabaseType(): string;
|
|
18
|
+
|
|
19
|
+
// noinspection JSUnusedGlobalSymbols
|
|
20
|
+
public abstract hasConcurrentTransactions(): boolean;
|
|
21
|
+
|
|
22
|
+
public abstract exists<T extends AbstractModel<T, D>, D extends GenericModelData>(modelClass: string | (new () => T), type?: "view" | "table" | "index", indexTableName?: string): Promise<boolean>;
|
|
23
|
+
|
|
24
|
+
public abstract existsSync<T extends AbstractModel<T, D>, D extends GenericModelData>(modelClass: string | (new () => T), type?: "view" | "table" | "index", indexTableName?: string): boolean;
|
|
25
|
+
|
|
26
|
+
public abstract exec(script: string): Promise<void>;
|
|
27
|
+
|
|
28
|
+
public abstract execSync(script: string): SmartDbDatabase | false;
|
|
29
|
+
|
|
30
|
+
// noinspection JSUnusedGlobalSymbols
|
|
31
|
+
public abstract close(): Promise<void>;
|
|
32
|
+
|
|
33
|
+
// noinspection JSUnusedGlobalSymbols
|
|
34
|
+
public abstract closeSync(): boolean;
|
|
35
|
+
|
|
36
|
+
public abstract getDbQuote(): string;
|
|
37
|
+
|
|
38
|
+
public abstract getTableInfo(table: string): Promise<SmartDbTableInfo>;
|
|
39
|
+
|
|
40
|
+
public abstract getDbConnector(): string | SmartDbConnector;
|
|
41
|
+
|
|
42
|
+
protected abstract statementRun(buildData: SmartDbSqlBuildData): Promise<SmartDbRunResult>;
|
|
43
|
+
|
|
44
|
+
protected abstract statementRunSync(buildData: SmartDbSqlBuildData): SmartDbRunResult;
|
|
45
|
+
|
|
46
|
+
protected abstract statementGet(buildData: SmartDbSqlBuildData): Promise<any>;
|
|
47
|
+
|
|
48
|
+
protected abstract statementGetSync(buildData: SmartDbSqlBuildData): any;
|
|
49
|
+
|
|
50
|
+
protected abstract statementGetAll(buildData: SmartDbSqlBuildData): Promise<any[]>;
|
|
51
|
+
|
|
52
|
+
protected abstract statementGetAllSync(buildData: SmartDbSqlBuildData): any[];
|
|
53
|
+
|
|
54
|
+
protected db: SmartDbDatabase;
|
|
55
|
+
protected dbConnector: SmartDbConnector;
|
|
56
|
+
protected lastError: Error;
|
|
57
|
+
protected lastBuildData: SmartDbSqlBuildData;
|
|
58
|
+
protected smartDbLog: SmartDbLog;
|
|
59
|
+
private dictionaries: any[] = [];
|
|
60
|
+
|
|
61
|
+
protected constructor(dbConnector: SmartDbConnector) {
|
|
62
|
+
this.dbConnector = dbConnector;
|
|
63
|
+
this.smartDbLog = new SmartDbLog(this);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
public initDb(appOptions: SmartDbOptions): Promise<SmartDbVersionViewModel[]> {
|
|
67
|
+
return new Promise<SmartDbVersionViewModel[]>((resolve, reject) => {
|
|
68
|
+
this.dictionaries.push(SmartDbDictionary);
|
|
69
|
+
|
|
70
|
+
const upgradeManager = new SmartDbUpgradeManager(this);
|
|
71
|
+
|
|
72
|
+
// ES module compatibility
|
|
73
|
+
let dirname: string;
|
|
74
|
+
if (import.meta && import.meta.url) {
|
|
75
|
+
dirname = import.meta.url.replace(/file:\/\//, "").replace(/\/[^/]*$/, "");
|
|
76
|
+
} else {
|
|
77
|
+
dirname = __dirname;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const coreOptions: SmartDbOptions = {
|
|
81
|
+
module: "smart-db-core",
|
|
82
|
+
sqlFilesDirectory: dirname + "/assets/" + this.getDatabaseType()
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
upgradeManager.prepareDatabase(coreOptions).then((coreVersion) => {
|
|
86
|
+
this.exists(SmartDbLogModel).then((exists) => {
|
|
87
|
+
this.smartDbLog.setDbLogging(exists);
|
|
88
|
+
|
|
89
|
+
upgradeManager.prepareDatabase(appOptions).then((version) => {
|
|
90
|
+
resolve([coreVersion, version]);
|
|
91
|
+
}, (err) => {
|
|
92
|
+
reject(err);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
}).catch((err) => {
|
|
96
|
+
reject(err);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
public getLogger(): SmartDbLog {
|
|
102
|
+
return this.smartDbLog;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// noinspection JSUnusedGlobalSymbols
|
|
106
|
+
public getDb(): SmartDbDatabase {
|
|
107
|
+
return this.db;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// noinspection JSUnusedGlobalSymbols
|
|
111
|
+
public getLastBuildData(): SmartDbSqlBuildData {
|
|
112
|
+
return this.lastBuildData;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// noinspection FunctionNamingConventionJS
|
|
116
|
+
public get<T extends AbstractModel<T, D>, D extends GenericModelData>(modelClass: string | (new () => T), options: SmartDbSqlOptions<T, D>): Promise<(T | D)[] | T | D | SqlValueType | IndexedGenericModelData<T, D> | string | string[]> {
|
|
117
|
+
return new Promise<(T | D)[] | T | D | SqlValueType | IndexedGenericModelData<T, D> | string | string[]>((resolve, reject) => {
|
|
118
|
+
if (!options) {
|
|
119
|
+
options = {};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const buildData = this.buildSelectStatement(modelClass, options);
|
|
123
|
+
|
|
124
|
+
if (options.firstOnly || options.count) {
|
|
125
|
+
this.statementGet(buildData).then((row) => {
|
|
126
|
+
resolve(this.prepareResultRow(modelClass, row, options));
|
|
127
|
+
}).catch((err) => {
|
|
128
|
+
reject(err);
|
|
129
|
+
});
|
|
130
|
+
} else {
|
|
131
|
+
this.statementGetAll(buildData).then((rows) => {
|
|
132
|
+
resolve(this.prepareResultRows(modelClass, rows, options));
|
|
133
|
+
}).catch((err) => {
|
|
134
|
+
reject(err);
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// noinspection FunctionNamingConventionJS
|
|
141
|
+
public getSync<T extends AbstractModel<T, D>, D extends GenericModelData>(modelClass: string | (new () => T), options: SmartDbSqlOptions<T, D>): (T | D)[] | T | D | SqlValueType | IndexedGenericModelData<T, D> | string | string[] | false {
|
|
142
|
+
return this.saveExecute(() => {
|
|
143
|
+
if (!options) {
|
|
144
|
+
options = {};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const buildData = this.buildSelectStatement(modelClass, options);
|
|
148
|
+
|
|
149
|
+
let result: (T | D)[] | T | D | SqlValueType | IndexedGenericModelData<T, D> | string | string[];
|
|
150
|
+
|
|
151
|
+
if (options.firstOnly || options.count) {
|
|
152
|
+
const row = this.statementGetSync(buildData);
|
|
153
|
+
result = this.prepareResultRow(modelClass, row, options);
|
|
154
|
+
} else {
|
|
155
|
+
const rows = this.statementGetAllSync(buildData);
|
|
156
|
+
result = this.prepareResultRows(modelClass, rows, options);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return result;
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
public getFirst<T extends AbstractModel<T, D>, D extends GenericModelData>(modelClass: string | (new () => T), where?: SqlWhere, fields?: string | string[] | null, orderBy?: SqlOrderBy): Promise<T> {
|
|
164
|
+
return this.get(modelClass, {
|
|
165
|
+
firstOnly: true,
|
|
166
|
+
where: where,
|
|
167
|
+
fields: fields,
|
|
168
|
+
orderBy: orderBy
|
|
169
|
+
}) as Promise<T>;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
public getFirstSync<T extends AbstractModel<T, D>, D extends GenericModelData>(modelClass: string | (new () => T), where?: SqlWhere, fields?: string | string[] | null, orderBy?: SqlOrderBy): T | false {
|
|
173
|
+
return this.getSync(modelClass, {
|
|
174
|
+
firstOnly: true,
|
|
175
|
+
where: where,
|
|
176
|
+
fields: fields,
|
|
177
|
+
orderBy: orderBy
|
|
178
|
+
}) as T;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
public getAll<T extends AbstractModel<T, D>, D extends GenericModelData>(modelClass: string | (new () => T), where?: SqlWhere, fields?: string | string[] | null, orderBy?: SqlOrderBy, limit?: SqlLimit): Promise<T[]> {
|
|
182
|
+
return this.get(modelClass, {
|
|
183
|
+
where: where,
|
|
184
|
+
fields: fields,
|
|
185
|
+
orderBy: orderBy,
|
|
186
|
+
limit: limit
|
|
187
|
+
}) as Promise<T[]>;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
public getAllSync<T extends AbstractModel<T, D>, D extends GenericModelData>(modelClass: string | (new () => T), where?: SqlWhere, fields?: string | string[] | null, orderBy?: SqlOrderBy, limit?: SqlLimit): T[] | false {
|
|
191
|
+
return this.getSync(modelClass, {
|
|
192
|
+
where: where,
|
|
193
|
+
fields: fields,
|
|
194
|
+
orderBy: orderBy,
|
|
195
|
+
limit: limit
|
|
196
|
+
}) as T[] | false;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
public delete<T extends AbstractModel<T, D>, D extends GenericModelData>(modelClass: string | (new () => T), where?: SqlWhere): Promise<number> {
|
|
200
|
+
return new Promise<number>((resolve, reject) => {
|
|
201
|
+
const buildData = this.buildDeleteStatement(modelClass, where);
|
|
202
|
+
this.statementRun(buildData).then((runResult) => {
|
|
203
|
+
if (runResult) {
|
|
204
|
+
resolve(runResult.changes);
|
|
205
|
+
} else {
|
|
206
|
+
reject(this.getLastError());
|
|
207
|
+
}
|
|
208
|
+
}).catch((err) => {
|
|
209
|
+
reject(err);
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
public deleteSync<T extends AbstractModel<T, D>, D extends GenericModelData>(modelClass: string | (new () => T), where?: SqlWhere): number | false {
|
|
215
|
+
return this.saveExecute(() => {
|
|
216
|
+
const buildData = this.buildDeleteStatement(modelClass, where);
|
|
217
|
+
const runResult = this.statementRunSync(buildData);
|
|
218
|
+
return (<any>runResult).changes;
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
public update<T extends AbstractModel<T, D>, D extends GenericModelData>(modelClass: string | (new () => T), values: SqlUpdateValues | T, where?: SqlWhere): Promise<number> {
|
|
223
|
+
return new Promise<number>((resolve, reject) => {
|
|
224
|
+
const buildData = this.buildUpdateStatement(modelClass, values, where);
|
|
225
|
+
this.statementRun(buildData).then((runResult) => {
|
|
226
|
+
if (runResult) {
|
|
227
|
+
resolve(runResult.changes);
|
|
228
|
+
} else {
|
|
229
|
+
reject(this.getLastError());
|
|
230
|
+
}
|
|
231
|
+
}).catch((err) => {
|
|
232
|
+
reject(err);
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
public updateSync<T extends AbstractModel<T, D>, D extends GenericModelData>(modelClass: string | (new () => T), values: SqlUpdateValues | T, where?: SqlWhere): number | false {
|
|
238
|
+
return this.saveExecute(() => {
|
|
239
|
+
const buildData = this.buildUpdateStatement(modelClass, values, where);
|
|
240
|
+
const runResult = this.statementRunSync(buildData);
|
|
241
|
+
return runResult.changes;
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
public insert<T extends AbstractModel<T, D>, D extends GenericModelData>(modelClass: string | (new () => T), values: SqlUpdateValues | T): Promise<number> {
|
|
246
|
+
return new Promise<number>((resolve, reject) => {
|
|
247
|
+
const buildData = this.buildInsertStatement(modelClass, values);
|
|
248
|
+
this.statementRun(buildData).then((runResult) => {
|
|
249
|
+
if (runResult) {
|
|
250
|
+
resolve(runResult.lastId as number);
|
|
251
|
+
} else {
|
|
252
|
+
reject(this.getLastError());
|
|
253
|
+
}
|
|
254
|
+
}).catch((err) => {
|
|
255
|
+
reject(err);
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
public insertSync<T extends AbstractModel<T, D>, D extends GenericModelData>(modelClass: string | (new () => T), values: SqlUpdateValues | T): number | false {
|
|
261
|
+
return this.saveExecute(() => {
|
|
262
|
+
const buildData = this.buildInsertStatement(modelClass, values);
|
|
263
|
+
const runResult = this.statementRunSync(buildData);
|
|
264
|
+
return runResult.lastId;
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
public getLastError(): Error {
|
|
269
|
+
return this.lastError;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
public buildSelectStatement<T extends AbstractModel<T, D>, D extends GenericModelData>(modelClass: string | (new () => T), options: SmartDbSqlOptions<T, D>): SmartDbSqlBuildData {
|
|
273
|
+
if (!options) {
|
|
274
|
+
options = {};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const buildData = this.buildSelectSectionStatement(modelClass, options);
|
|
278
|
+
|
|
279
|
+
_.forEach(["union", "minus", "intersect"], (sectionName) => {
|
|
280
|
+
let section: SectionDescription<T, D>[] = _.get(options, sectionName) as SectionDescription<T, D>[];
|
|
281
|
+
if (section) {
|
|
282
|
+
if (!_.isArray(section)) {
|
|
283
|
+
section = [section];
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
_.forEach(section, (sectionElement) => {
|
|
287
|
+
const sectionBuildData = this.buildSelectSectionStatement(sectionElement.model, sectionElement);
|
|
288
|
+
buildData.sql += " " + sectionName.toUpperCase() + " ";
|
|
289
|
+
buildData.append(sectionBuildData);
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
if (options.orderBy) {
|
|
295
|
+
buildData.sql += " ORDER BY ";
|
|
296
|
+
|
|
297
|
+
const orderBy = _.isString(options.orderBy) ? options.orderBy.split(/ *, */) : options.orderBy;
|
|
298
|
+
|
|
299
|
+
buildData.sql += orderBy.map((field) => {
|
|
300
|
+
let sortDirection = "";
|
|
301
|
+
const match = field.match(/^(\w*) (asc|desc)$/i);
|
|
302
|
+
if (match) {
|
|
303
|
+
field = match[1];
|
|
304
|
+
sortDirection = match[2].toUpperCase();
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
field = this.translateFieldName(modelClass, field);
|
|
308
|
+
|
|
309
|
+
if (sortDirection) {
|
|
310
|
+
field += " " + sortDirection;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return field;
|
|
314
|
+
}).join(", ");
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (options.limit) {
|
|
318
|
+
if (options.limit.limit) {
|
|
319
|
+
buildData.sql += " LIMIT " + options.limit.limit.toString();
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (options.limit.offset) {
|
|
323
|
+
buildData.sql += " OFFSET " + options.limit.offset.toString();
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
this.lastBuildData = buildData;
|
|
328
|
+
|
|
329
|
+
return buildData;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
public toDate(d: Date | number | string): Date | null {
|
|
333
|
+
return smartDbToDate(d);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
public toDbTimestamp(d: Date | number): string {
|
|
337
|
+
return toSmartDbTimestamp(d);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
public toDbDate(d: Date | number): string {
|
|
341
|
+
return toSmartDbDate(d);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
protected buildWhere<T extends AbstractModel<T, D>, D extends GenericModelData>(modelClass: string | (new () => T), where: SqlWhere, op?: string): SmartDbSqlBuildData {
|
|
345
|
+
const whereClause = new SmartDbSqlBuildData();
|
|
346
|
+
|
|
347
|
+
if (where && _.keys(where).length > 0) {
|
|
348
|
+
if (!op) {
|
|
349
|
+
op = "AND";
|
|
350
|
+
whereClause.sql += " WHERE ";
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// noinspection FunctionWithMultipleReturnPointsJS,FunctionTooLongJS,OverlyComplexFunctionJS
|
|
354
|
+
whereClause.sql += _.map(where, (value, key) => {
|
|
355
|
+
const upperKey = key.toUpperCase();
|
|
356
|
+
if (upperKey == "AND" || upperKey == "OR") {
|
|
357
|
+
let subClauseSql = "";
|
|
358
|
+
const subValues: any[] = _.isArray(value) ? value : [value];
|
|
359
|
+
subValues.forEach((subValue, idx) => {
|
|
360
|
+
const subValueClause = this.buildWhere(modelClass, subValue, upperKey);
|
|
361
|
+
if (idx > 0) {
|
|
362
|
+
subClauseSql += " " + upperKey + " ";
|
|
363
|
+
}
|
|
364
|
+
subClauseSql += subValueClause.sql;
|
|
365
|
+
whereClause.values = _.concat(whereClause.values, subValueClause.values);
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
subClauseSql = "(" + subClauseSql + ")";
|
|
369
|
+
|
|
370
|
+
return subClauseSql;
|
|
371
|
+
} else if (upperKey == "EXPRESSION") {
|
|
372
|
+
let compares: string[] = [];
|
|
373
|
+
_.forEach(<SmartDbSqlComparison[]>value, (comparison: SmartDbSqlComparison) => {
|
|
374
|
+
const compareField = this.prepareField(modelClass, comparison.compare, whereClause.values);
|
|
375
|
+
const withField = this.prepareField(modelClass, comparison.with, whereClause.values);
|
|
376
|
+
|
|
377
|
+
let compareOp: SqlOperationType;
|
|
378
|
+
if (_.isString(comparison.operation) && (<any>SqlOperationType)[comparison.operation]) {
|
|
379
|
+
compareOp = (<any>SqlOperationType)[comparison.operation];
|
|
380
|
+
} else {
|
|
381
|
+
compareOp = comparison.operation || SqlOperationType.EQ;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
compares.push(`${compareField} ${compareOp} ${withField}`);
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
return compares.join(" " + op + " ");
|
|
388
|
+
} else {
|
|
389
|
+
let checkFieldName = true;
|
|
390
|
+
let compareWith: string;
|
|
391
|
+
|
|
392
|
+
if (_.isArray(value)) {
|
|
393
|
+
value = IN(value as SqlValueType[]);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (_.isObject(value)) {
|
|
397
|
+
const sqlOperation = value as SqlOperation;
|
|
398
|
+
if (_.isString(sqlOperation.operation) && (<any>SqlOperationType)[sqlOperation.operation]) {
|
|
399
|
+
sqlOperation.operation = (<any>SqlOperationType)[sqlOperation.operation];
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
switch (sqlOperation.operation) {
|
|
403
|
+
case SqlOperationType.IN:
|
|
404
|
+
const placeholders = new Array((<SqlValueType[]>sqlOperation.value).length);
|
|
405
|
+
placeholders.fill("?");
|
|
406
|
+
compareWith = SqlOperationType.IN + " (" + placeholders.join(", ") + ")";
|
|
407
|
+
whereClause.values = _.concat(whereClause.values, sqlOperation.value);
|
|
408
|
+
break;
|
|
409
|
+
|
|
410
|
+
case SqlOperationType.IS_NULL:
|
|
411
|
+
case SqlOperationType.IS_NOT_NULL:
|
|
412
|
+
compareWith = sqlOperation.operation;
|
|
413
|
+
break;
|
|
414
|
+
|
|
415
|
+
case SqlOperationType.LITERAL:
|
|
416
|
+
key = sqlOperation.key;
|
|
417
|
+
|
|
418
|
+
const literalOperation: SqlOperationType = sqlOperation.literalOperation || SqlOperationType.EQ;
|
|
419
|
+
|
|
420
|
+
if (literalOperation == SqlOperationType.IN) {
|
|
421
|
+
const literalPlaceholders = new Array((<SqlValueType[]>sqlOperation.value).length);
|
|
422
|
+
literalPlaceholders.fill("?");
|
|
423
|
+
compareWith = SqlOperationType.IN + " (" + literalPlaceholders.join(", ") + ")";
|
|
424
|
+
whereClause.values = _.concat(whereClause.values, sqlOperation.value);
|
|
425
|
+
} else if (_.isUndefined(sqlOperation.value)) {
|
|
426
|
+
compareWith = literalOperation;
|
|
427
|
+
} else {
|
|
428
|
+
compareWith = literalOperation + " ?";
|
|
429
|
+
whereClause.values.push(this.makeDbValue(sqlOperation.value as SqlValueType));
|
|
430
|
+
}
|
|
431
|
+
checkFieldName = false;
|
|
432
|
+
break;
|
|
433
|
+
|
|
434
|
+
default:
|
|
435
|
+
compareWith = sqlOperation.operation + " ?";
|
|
436
|
+
whereClause.values.push(this.makeDbValue(sqlOperation.value as SqlValueType));
|
|
437
|
+
break;
|
|
438
|
+
}
|
|
439
|
+
} else {
|
|
440
|
+
if (value === null) {
|
|
441
|
+
compareWith = SqlOperationType.IS_NULL;
|
|
442
|
+
} else if (value === undefined) {
|
|
443
|
+
// this skips this value without breaking the structure
|
|
444
|
+
key = "1";
|
|
445
|
+
compareWith = "= 1";
|
|
446
|
+
checkFieldName = false;
|
|
447
|
+
} else {
|
|
448
|
+
if (_.isString(value) && value.match(/[%_]/)) {
|
|
449
|
+
compareWith = SqlOperationType.LIKE + " ?";
|
|
450
|
+
} else {
|
|
451
|
+
compareWith = SqlOperationType.EQ + " ?";
|
|
452
|
+
}
|
|
453
|
+
whereClause.values.push(this.makeDbValue(value));
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
let result: string;
|
|
458
|
+
if (checkFieldName) {
|
|
459
|
+
result = this.translateFieldName(modelClass, key) + " " + compareWith;
|
|
460
|
+
} else {
|
|
461
|
+
result = key + " " + compareWith;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
return result;
|
|
465
|
+
}
|
|
466
|
+
}).join(" " + op + " ");
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
return whereClause;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
protected buildDeleteStatement<T extends AbstractModel<T, D>, D extends GenericModelData>(modelClass: string | (new () => T), where?: SqlWhere): SmartDbSqlBuildData {
|
|
473
|
+
const tableName = this.getTableName(modelClass);
|
|
474
|
+
|
|
475
|
+
const buildData = new SmartDbSqlBuildData("DELETE FROM");
|
|
476
|
+
buildData.sql += " " + tableName;
|
|
477
|
+
|
|
478
|
+
if (where) {
|
|
479
|
+
buildData.append(this.buildWhere(modelClass, where));
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
this.lastBuildData = buildData;
|
|
483
|
+
|
|
484
|
+
return buildData;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
protected buildUpdateStatement<T extends AbstractModel<T, D>, D extends GenericModelData>(modelClass: string | (new () => T), values: SqlUpdateValues | T, where?: SqlWhere): SmartDbSqlBuildData {
|
|
488
|
+
const tableName = this.getTableName(modelClass);
|
|
489
|
+
|
|
490
|
+
const buildData = new SmartDbSqlBuildData("UPDATE");
|
|
491
|
+
buildData.sql += " " + tableName + " SET ";
|
|
492
|
+
|
|
493
|
+
const keys: string[] = [];
|
|
494
|
+
|
|
495
|
+
if (values instanceof AbstractModel) {
|
|
496
|
+
values = values.getPlainObject(FieldNamingStyle.Database);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
_.forOwn(values, (value, key) => {
|
|
500
|
+
keys.push(this.translateFieldName(modelClass, key) + " = ?");
|
|
501
|
+
buildData.values.push(this.makeDbValue(value));
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
buildData.sql += keys.join(", ");
|
|
505
|
+
|
|
506
|
+
if (where) {
|
|
507
|
+
buildData.append(this.buildWhere(modelClass, where));
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
this.lastBuildData = buildData;
|
|
511
|
+
|
|
512
|
+
return buildData;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
protected buildInsertStatement<T extends AbstractModel<T, D>, D extends GenericModelData>(modelClass: string | (new () => T), values: SqlUpdateValues | T): SmartDbSqlBuildData {
|
|
516
|
+
const tableName = this.getTableName(modelClass);
|
|
517
|
+
|
|
518
|
+
const buildData = new SmartDbSqlBuildData("INSERT");
|
|
519
|
+
buildData.sql += " INTO " + tableName;
|
|
520
|
+
|
|
521
|
+
const fields: string[] = [];
|
|
522
|
+
|
|
523
|
+
if (values instanceof AbstractModel) {
|
|
524
|
+
values = values.getPlainObject(FieldNamingStyle.Database);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
_.forOwn(values, (value, field) => {
|
|
528
|
+
field = this.translateFieldName(modelClass, field);
|
|
529
|
+
fields.push(field);
|
|
530
|
+
buildData.values.push(this.makeDbValue(value));
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
const placeholders = new Array(fields.length);
|
|
534
|
+
placeholders.fill("?");
|
|
535
|
+
|
|
536
|
+
buildData.sql += " (" + fields.join(", ") + ") VALUES (" + placeholders.join(", ") + ")";
|
|
537
|
+
|
|
538
|
+
this.lastBuildData = buildData;
|
|
539
|
+
|
|
540
|
+
return buildData;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// noinspection OverlyComplexFunctionJS,FunctionTooLongJS
|
|
544
|
+
protected prepareFieldValue<T extends AbstractModel<T, D>, D extends GenericModelData>(modelClass: string | (new () => T), fieldOperation: SqlFieldDescriptor): string {
|
|
545
|
+
let value: string = "<undefined>";
|
|
546
|
+
|
|
547
|
+
switch (fieldOperation.operation) {
|
|
548
|
+
case SqlFieldOperationType.FIELD:
|
|
549
|
+
value = this.translateFieldName(modelClass, fieldOperation.value as string);
|
|
550
|
+
break;
|
|
551
|
+
|
|
552
|
+
case SqlFieldOperationType.VALUE:
|
|
553
|
+
if (fieldOperation.value === null) {
|
|
554
|
+
value = "NULL";
|
|
555
|
+
} else if (fieldOperation.literal) {
|
|
556
|
+
value = fieldOperation.value as string;
|
|
557
|
+
} else if (_.isString(fieldOperation.value)) {
|
|
558
|
+
// quote if not numeric
|
|
559
|
+
if (isNaN(parseFloat(fieldOperation.value)) || !isFinite(<any>fieldOperation.value)) {
|
|
560
|
+
value = "'" + fieldOperation.value + "'";
|
|
561
|
+
}
|
|
562
|
+
} else if (_.isNumber(fieldOperation.value)) {
|
|
563
|
+
value = fieldOperation.value.toString();
|
|
564
|
+
} else if (_.isBoolean(fieldOperation.value)) {
|
|
565
|
+
value = fieldOperation.value ? "1" : "0";
|
|
566
|
+
} else if (_.isDate(fieldOperation.value)) {
|
|
567
|
+
value = "'" + fieldOperation.value.toISOString().substr(0, 23).replace("T", " ") + "'";
|
|
568
|
+
} else {
|
|
569
|
+
console.error("unhandled field data type", typeof fieldOperation.value, fieldOperation.value);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
break;
|
|
573
|
+
|
|
574
|
+
case SqlFieldOperationType.COUNT:
|
|
575
|
+
if (_.isArray(fieldOperation.value)) {
|
|
576
|
+
value = "COUNT(" + fieldOperation.value.join(",") + ")";
|
|
577
|
+
} else {
|
|
578
|
+
value = "COUNT(" + (fieldOperation.value || "") + ")";
|
|
579
|
+
}
|
|
580
|
+
break;
|
|
581
|
+
|
|
582
|
+
case SqlFieldOperationType.COALESCE:
|
|
583
|
+
const values = _.isArray(fieldOperation.value) ? fieldOperation.value : [fieldOperation.value];
|
|
584
|
+
const fieldValues = this.buildFieldList(modelClass, values);
|
|
585
|
+
value = "COALESCE(" + fieldValues.join(",") + ")";
|
|
586
|
+
break;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
if (fieldOperation.alias) {
|
|
590
|
+
const dbQuote = this.getDbQuote();
|
|
591
|
+
value += ` as ${dbQuote}${fieldOperation.alias}${dbQuote}`;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
return value;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
protected prepareField<T extends AbstractModel<T, D>, D extends GenericModelData>(modelClass: string | (new () => T), field: SqlValueType | SqlFieldDescriptor, values?: SqlValueType[]): string {
|
|
598
|
+
let dbField: string;
|
|
599
|
+
|
|
600
|
+
if (field === null) {
|
|
601
|
+
dbField = "NULL";
|
|
602
|
+
} else if (_.isString(field)) {
|
|
603
|
+
const matches = field.match(/^'(.*)'$/);
|
|
604
|
+
if (matches) {
|
|
605
|
+
if (values) {
|
|
606
|
+
dbField = "?";
|
|
607
|
+
values.push(matches[1]);
|
|
608
|
+
} else {
|
|
609
|
+
dbField = field;
|
|
610
|
+
}
|
|
611
|
+
} else {
|
|
612
|
+
dbField = this.translateFieldName(modelClass, field);
|
|
613
|
+
}
|
|
614
|
+
} else if ((<SqlFieldDescriptor>field).operation) {
|
|
615
|
+
dbField = this.prepareFieldValue(modelClass, <SqlFieldDescriptor>field);
|
|
616
|
+
} else if (values) {
|
|
617
|
+
dbField = "?";
|
|
618
|
+
values.push(field as SqlValueType);
|
|
619
|
+
} else {
|
|
620
|
+
dbField = this.makeArgumentDbValue(field as SqlValueType).toString();
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
return dbField;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
protected buildFieldList<T extends AbstractModel<T, D>, D extends GenericModelData>(modelClass: string | (new () => T), fields: (SqlValueType | SqlFieldDescriptor)[]): string[] {
|
|
627
|
+
const dbFields: string[] = [];
|
|
628
|
+
|
|
629
|
+
_.forEach(fields, (field) => {
|
|
630
|
+
dbFields.push(this.prepareField(modelClass, field));
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
return dbFields;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
// noinspection OverlyComplexFunctionJS
|
|
637
|
+
protected buildSelectSectionStatement<T extends AbstractModel<T, D>, D extends GenericModelData>(modelClass: string | (new () => T), options: SmartDbSqlOptions<T, D>): SmartDbSqlBuildData {
|
|
638
|
+
const tableName = this.getTableName(modelClass);
|
|
639
|
+
|
|
640
|
+
let fields: (string | SqlFieldDescriptor)[];
|
|
641
|
+
if (_.isArray(options.fields)) {
|
|
642
|
+
fields = options.fields;
|
|
643
|
+
} else if (_.isString(options.fields)) {
|
|
644
|
+
const fieldsString = options.fields.trim();
|
|
645
|
+
if (fieldsString === "" || fieldsString == "*") {
|
|
646
|
+
fields = [];
|
|
647
|
+
} else {
|
|
648
|
+
fields = fieldsString.split(/,/);
|
|
649
|
+
}
|
|
650
|
+
} else if (options.fields && options.fields.operation) {
|
|
651
|
+
fields = [options.fields];
|
|
652
|
+
} else {
|
|
653
|
+
fields = [];
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
let fieldString: string;
|
|
657
|
+
if (fields.length > 0) {
|
|
658
|
+
let dbFields = this.buildFieldList(modelClass, fields);
|
|
659
|
+
fieldString = dbFields.join(", ");
|
|
660
|
+
} else {
|
|
661
|
+
fieldString = "*";
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
if (options.distinct) {
|
|
665
|
+
fieldString = "DISTINCT " + fieldString;
|
|
666
|
+
}
|
|
667
|
+
if (options.count) {
|
|
668
|
+
fieldString = `COUNT(${fieldString})`;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
const buildData = new SmartDbSqlBuildData(`SELECT ${fieldString} FROM ${tableName}`);
|
|
672
|
+
|
|
673
|
+
if (options.where) {
|
|
674
|
+
buildData.append(this.buildWhere(modelClass, options.where));
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
if (options.groupBy) {
|
|
678
|
+
buildData.sql += " GROUP BY ";
|
|
679
|
+
|
|
680
|
+
const groupBy = _.isArray(options.groupBy) ? options.groupBy : [options.groupBy];
|
|
681
|
+
|
|
682
|
+
buildData.sql += groupBy.map((field) => {
|
|
683
|
+
return this.translateFieldName(modelClass, field);
|
|
684
|
+
}).join(", ");
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
return buildData;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
protected translateFieldName<T extends AbstractModel<T, D>, D extends GenericModelData>(modelClass: string | (new() => T), field: string): string {
|
|
691
|
+
if (_.isString(modelClass)) {
|
|
692
|
+
const modelClassName = modelClass;
|
|
693
|
+
|
|
694
|
+
let done = false;
|
|
695
|
+
_.forEach(this.dictionaries, (dictionary) => {
|
|
696
|
+
if (dictionary.models && dictionary.models[modelClassName]) {
|
|
697
|
+
modelClass = dictionary.models[modelClassName].cls;
|
|
698
|
+
done = true;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
return !done;
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
if (!_.isString(modelClass)) {
|
|
706
|
+
const attributeMap: ModelAttributeMap = (<AbstractModelGlobals<T, D>><unknown>modelClass).attributeMap;
|
|
707
|
+
const info: AttributeInfo = attributeMap[field];
|
|
708
|
+
if (info) {
|
|
709
|
+
if (info.alias) {
|
|
710
|
+
field = info.alias;
|
|
711
|
+
}
|
|
712
|
+
} else {
|
|
713
|
+
const tableName = (<AbstractModelGlobals<T, D>><unknown>modelClass).getTableName();
|
|
714
|
+
this.lastError = new Error(`unknown field '${field}' in table '${tableName}'`);
|
|
715
|
+
this.smartDbLog.logError(SmartDbLogLocation.Database, "translateFieldName", this.lastError);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
return field;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// noinspection JSMethodCanBeStatic
|
|
723
|
+
protected makeDbValue(value: SqlValueType): SqlValueType {
|
|
724
|
+
if (_.isBoolean(value)) {
|
|
725
|
+
value = value ? 1 : 0;
|
|
726
|
+
} else if (_.isDate(value)) {
|
|
727
|
+
value = toSmartDbDate(value);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
return value;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
// noinspection JSMethodCanBeStatic
|
|
734
|
+
protected makeArgumentDbValue(value: SqlValueType): SqlValueType {
|
|
735
|
+
if (_.isBoolean(value)) {
|
|
736
|
+
value = value ? 1 : 0;
|
|
737
|
+
} else if (_.isDate(value)) {
|
|
738
|
+
value = `'${toSmartDbDate(value)}'`;
|
|
739
|
+
} else if (_.isString(value)) {
|
|
740
|
+
value = `'${value}'`;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
return value;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
protected saveExecute<T>(fn: () => T): T | false {
|
|
747
|
+
let ret: T | false;
|
|
748
|
+
|
|
749
|
+
try {
|
|
750
|
+
ret = fn();
|
|
751
|
+
} catch (error) {
|
|
752
|
+
this.lastError = error instanceof Error ? error : new Error(error || "Unknown error");
|
|
753
|
+
this.smartDbLog.logFatal(SmartDbLogLocation.Database, "saveExecute-catch", this.lastError);
|
|
754
|
+
ret = false;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
return ret;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
private prepareResultRow<T extends AbstractModel<T, D>, D extends GenericModelData>(modelClass: string | (new () => T), rows: any, options: SmartDbSqlOptions<T, D>): T | D | SqlValueType {
|
|
761
|
+
let result: D | SqlValueType | T;
|
|
762
|
+
|
|
763
|
+
if (options.indexedBy) {
|
|
764
|
+
this.smartDbLog.logWarning(SmartDbLogLocation.Database, "AbstractModel.get", "option 'indexedBy' not supported without 'all'");
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
if (rows) {
|
|
768
|
+
if (options.count) {
|
|
769
|
+
result = parseInt(_.values(rows)[0], 10);
|
|
770
|
+
} else if (options.collapseRow) {
|
|
771
|
+
result = _.values(rows).join(",");
|
|
772
|
+
} else {
|
|
773
|
+
const modelClassStatics = <AbstractModelGlobals<T, D>><unknown>modelClass;
|
|
774
|
+
result = modelClassStatics.from(rows) as T;
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
return result;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
private prepareResultRows<T extends AbstractModel<T, D>, D extends GenericModelData>(modelClass: string | (new () => T), rows: (T | D)[], options: SmartDbSqlOptions<T, D>): (T | D)[] | T | D | IndexedGenericModelData<T, D> | string | string[] {
|
|
782
|
+
let result: (T | D)[] | T | D | IndexedGenericModelData<T, D> | string | string[];
|
|
783
|
+
|
|
784
|
+
if (!options.indexedBy && !options.collapseRow && _.isString(modelClass)) {
|
|
785
|
+
result = rows;
|
|
786
|
+
} else {
|
|
787
|
+
const modelClassStatics = <AbstractModelGlobals<T, D>><unknown>modelClass;
|
|
788
|
+
|
|
789
|
+
if (options.indexedBy) {
|
|
790
|
+
result = {};
|
|
791
|
+
} else {
|
|
792
|
+
result = new Array(rows.length);
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
_.forEach(rows as D[], (row, i: number) => {
|
|
796
|
+
const objectRow = modelClassStatics.from ? modelClassStatics.from(row) : null;
|
|
797
|
+
|
|
798
|
+
let indexColumn: string = null;
|
|
799
|
+
if (options.indexedBy) {
|
|
800
|
+
if (objectRow) {
|
|
801
|
+
indexColumn = objectRow.getValue(options.indexedBy) as string;
|
|
802
|
+
} else {
|
|
803
|
+
indexColumn = row[options.indexedBy] as string;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
if (options.collapseRow) {
|
|
808
|
+
const collapsedRow: string = _.values(row).join(",");
|
|
809
|
+
if (indexColumn) {
|
|
810
|
+
(<KeyValueList>result)[indexColumn] = collapsedRow;
|
|
811
|
+
} else {
|
|
812
|
+
(<string[]>result)[i] = collapsedRow;
|
|
813
|
+
}
|
|
814
|
+
} else {
|
|
815
|
+
if (indexColumn) {
|
|
816
|
+
(<IndexedGenericModelData<T, D>>result)[indexColumn] = objectRow || row;
|
|
817
|
+
} else {
|
|
818
|
+
(<IndexedGenericModelData<T, D>>result)[i] = objectRow || row;
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
});
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
return result;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
private getTableName<T extends AbstractModel<T, D>, D extends GenericModelData>(modelClass: string | (new () => T)): string {
|
|
828
|
+
let tableName: string;
|
|
829
|
+
|
|
830
|
+
if (_.isString(modelClass)) {
|
|
831
|
+
let modelClassName = modelClass;
|
|
832
|
+
|
|
833
|
+
_.forEach(this.dictionaries, (dictionary) => {
|
|
834
|
+
if (dictionary.models && dictionary.models[modelClassName]) {
|
|
835
|
+
tableName = dictionary.models[modelClassName].cls.getTableName();
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
return !tableName;
|
|
839
|
+
});
|
|
840
|
+
|
|
841
|
+
if (!tableName) {
|
|
842
|
+
tableName = modelClassName;
|
|
843
|
+
}
|
|
844
|
+
} else if (modelClass && (<AbstractModelGlobals<T, D>><unknown>modelClass).getTableName) {
|
|
845
|
+
tableName = (<AbstractModelGlobals<T, D>><unknown>modelClass).getTableName();
|
|
846
|
+
} else {
|
|
847
|
+
throw new Error(`unknown model: ${modelClass}`);
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
|
|
851
|
+
return tableName;
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
|