@ruiapp/rapid-core 0.1.72 → 0.1.74
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/dist/bootstrapApplicationConfig.d.ts +82 -0
- package/dist/dataAccess/entityManager.d.ts +8 -1
- package/dist/index.js +277 -24
- package/dist/queryBuilder/queryBuilder.d.ts +5 -0
- package/dist/types.d.ts +18 -3
- package/package.json +1 -1
- package/src/bootstrapApplicationConfig.ts +602 -552
- package/src/dataAccess/entityManager.ts +107 -1
- package/src/plugins/fileManage/actionHandlers/downloadFile.ts +33 -28
- package/src/plugins/metaManage/MetaManagePlugin.ts +65 -2
- package/src/queryBuilder/queryBuilder.ts +99 -17
- package/src/types.ts +617 -593
|
@@ -14,6 +14,8 @@ import {
|
|
|
14
14
|
IRpdDataAccessor,
|
|
15
15
|
RemoveEntityRelationsOptions,
|
|
16
16
|
RpdDataModel,
|
|
17
|
+
RpdDataModelIndex,
|
|
18
|
+
RpdDataModelIndexOptions,
|
|
17
19
|
RpdDataModelProperty,
|
|
18
20
|
UpdateEntityByIdOptions,
|
|
19
21
|
} from "~/types";
|
|
@@ -22,7 +24,7 @@ import { mapDbRowToEntity, mapEntityToDbRow } from "./entityMapper";
|
|
|
22
24
|
import { mapPropertyNameToColumnName } from "./propertyMapper";
|
|
23
25
|
import { IRpdServer, RapidPlugin } from "~/core/server";
|
|
24
26
|
import { getEntityPartChanges } from "~/helpers/entityHelpers";
|
|
25
|
-
import { filter, find, first, forEach, isArray, isNumber, isObject, isString, keys, map, reject, uniq } from "lodash";
|
|
27
|
+
import { cloneDeep, filter, find, first, forEach, isArray, isNumber, isObject, isString, keys, map, reject, uniq } from "lodash";
|
|
26
28
|
import {
|
|
27
29
|
getEntityPropertiesIncludingBase,
|
|
28
30
|
getEntityProperty,
|
|
@@ -35,6 +37,7 @@ import { ColumnSelectOptions, CountRowOptions, FindRowOptions, FindRowOrderByOpt
|
|
|
35
37
|
import { newEntityOperationError } from "~/utilities/errorUtility";
|
|
36
38
|
import { getNowStringWithTimezone } from "~/utilities/timeUtility";
|
|
37
39
|
import { or } from "xstate";
|
|
40
|
+
import { RouteContext } from "~/core/routeContext";
|
|
38
41
|
|
|
39
42
|
export type FindOneRelationEntitiesOptions = {
|
|
40
43
|
server: IRpdServer;
|
|
@@ -703,6 +706,26 @@ async function createEntity(server: IRpdServer, dataAccessor: IRpdDataAccessor,
|
|
|
703
706
|
routeContext,
|
|
704
707
|
});
|
|
705
708
|
|
|
709
|
+
// check unique constraints
|
|
710
|
+
if (!options.postponeUniquenessCheck) {
|
|
711
|
+
if (model.indexes && model.indexes.length) {
|
|
712
|
+
for (const indexConfig of model.indexes) {
|
|
713
|
+
if (!indexConfig.unique) {
|
|
714
|
+
continue;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
const duplicate = await willEntityDuplicate(server, dataAccessor, {
|
|
718
|
+
routeContext: options.routeContext,
|
|
719
|
+
entityToSave: entity,
|
|
720
|
+
indexConfig,
|
|
721
|
+
});
|
|
722
|
+
if (duplicate) {
|
|
723
|
+
throw new Error(getEntityDuplicatedErrorMessage(server, model, indexConfig));
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
|
|
706
729
|
const oneRelationPropertiesToCreate: RpdDataModelProperty[] = [];
|
|
707
730
|
const manyRelationPropertiesToCreate: RpdDataModelProperty[] = [];
|
|
708
731
|
keys(entity).forEach((propertyCode) => {
|
|
@@ -936,6 +959,27 @@ async function updateEntityById(server: IRpdServer, dataAccessor: IRpdDataAccess
|
|
|
936
959
|
|
|
937
960
|
changes = getEntityPartChanges(server, model, entity, entityToSave);
|
|
938
961
|
|
|
962
|
+
// check unique constraints
|
|
963
|
+
if (!options.postponeUniquenessCheck) {
|
|
964
|
+
if (model.indexes && model.indexes.length) {
|
|
965
|
+
for (const indexConfig of model.indexes) {
|
|
966
|
+
if (!indexConfig.unique) {
|
|
967
|
+
continue;
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
const duplicate = await willEntityDuplicate(server, dataAccessor, {
|
|
971
|
+
routeContext: options.routeContext,
|
|
972
|
+
entityId: id,
|
|
973
|
+
entityToSave: changes,
|
|
974
|
+
indexConfig,
|
|
975
|
+
});
|
|
976
|
+
if (duplicate) {
|
|
977
|
+
throw new Error(getEntityDuplicatedErrorMessage(server, model, indexConfig));
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
|
|
939
983
|
const oneRelationPropertiesToUpdate: RpdDataModelProperty[] = [];
|
|
940
984
|
const manyRelationPropertiesToUpdate: RpdDataModelProperty[] = [];
|
|
941
985
|
keys(changes).forEach((propertyCode) => {
|
|
@@ -1134,6 +1178,68 @@ async function updateEntityById(server: IRpdServer, dataAccessor: IRpdDataAccess
|
|
|
1134
1178
|
return updatedEntity;
|
|
1135
1179
|
}
|
|
1136
1180
|
|
|
1181
|
+
export type CheckEntityDuplicatedOptions = {
|
|
1182
|
+
routeContext?: RouteContext;
|
|
1183
|
+
entityId?: number;
|
|
1184
|
+
entityToSave: any;
|
|
1185
|
+
indexConfig: RpdDataModelIndex;
|
|
1186
|
+
};
|
|
1187
|
+
|
|
1188
|
+
async function willEntityDuplicate(server: IRpdServer, dataAccessor: IRpdDataAccessor, options: CheckEntityDuplicatedOptions): Promise<boolean> {
|
|
1189
|
+
const { entityId, entityToSave, routeContext, indexConfig } = options;
|
|
1190
|
+
|
|
1191
|
+
let filters: EntityFilterOptions[] = [];
|
|
1192
|
+
if (indexConfig.conditions) {
|
|
1193
|
+
filters = cloneDeep(indexConfig.conditions);
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
for (const propConfig of indexConfig.properties) {
|
|
1197
|
+
let propCode: string;
|
|
1198
|
+
if (isString(propConfig)) {
|
|
1199
|
+
propCode = propConfig;
|
|
1200
|
+
} else {
|
|
1201
|
+
propCode = propConfig.code;
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
if (!entityToSave.hasOwnProperty(propCode)) {
|
|
1205
|
+
// skip duplicate checking when any index prop missing in entityToSave.
|
|
1206
|
+
return false;
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
filters.push({
|
|
1210
|
+
operator: "eq",
|
|
1211
|
+
field: propCode,
|
|
1212
|
+
value: entityToSave[propCode],
|
|
1213
|
+
});
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
const entityInDb = await findEntity(server, dataAccessor, {
|
|
1217
|
+
filters,
|
|
1218
|
+
routeContext,
|
|
1219
|
+
});
|
|
1220
|
+
|
|
1221
|
+
if (entityId) {
|
|
1222
|
+
return entityInDb && entityInDb.Id !== entityId;
|
|
1223
|
+
} else {
|
|
1224
|
+
return !!entityInDb;
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
function getEntityDuplicatedErrorMessage(server: IRpdServer, model: RpdDataModel, indexConfig: RpdDataModelIndex) {
|
|
1229
|
+
const propertyNames = indexConfig.properties.map((propConfig) => {
|
|
1230
|
+
let propCode: string;
|
|
1231
|
+
if (isString(propConfig)) {
|
|
1232
|
+
propCode = propConfig;
|
|
1233
|
+
} else {
|
|
1234
|
+
propCode = propConfig.code;
|
|
1235
|
+
}
|
|
1236
|
+
const prop = getEntityPropertyByCode(server, model, propCode);
|
|
1237
|
+
return prop.name;
|
|
1238
|
+
});
|
|
1239
|
+
|
|
1240
|
+
return `已存在 ${propertyNames.join(", ")} 相同的记录。`;
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1137
1243
|
export default class EntityManager<TEntity = any> {
|
|
1138
1244
|
#server: IRpdServer;
|
|
1139
1245
|
#dataAccessor: IRpdDataAccessor;
|
|
@@ -1,28 +1,33 @@
|
|
|
1
|
-
import path from "path";
|
|
2
|
-
import { readFile } from "~/utilities/fsUtility";
|
|
3
|
-
import { ActionHandlerContext } from "~/core/actionHandler";
|
|
4
|
-
import { RapidPlugin } from "~/core/server";
|
|
5
|
-
|
|
6
|
-
export const code = "downloadFile";
|
|
7
|
-
|
|
8
|
-
export async function handler(plugin: RapidPlugin, ctx: ActionHandlerContext, options: any) {
|
|
9
|
-
const { server, applicationConfig, routerContext, input } = ctx;
|
|
10
|
-
const { request, response } = routerContext;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { readFile } from "~/utilities/fsUtility";
|
|
3
|
+
import { ActionHandlerContext } from "~/core/actionHandler";
|
|
4
|
+
import { RapidPlugin } from "~/core/server";
|
|
5
|
+
|
|
6
|
+
export const code = "downloadFile";
|
|
7
|
+
|
|
8
|
+
export async function handler(plugin: RapidPlugin, ctx: ActionHandlerContext, options: any) {
|
|
9
|
+
const { server, applicationConfig, routerContext, input } = ctx;
|
|
10
|
+
const { request, response } = routerContext;
|
|
11
|
+
//TODO: only public files can download by this handler
|
|
12
|
+
|
|
13
|
+
let fileKey: string = input.fileKey;
|
|
14
|
+
|
|
15
|
+
if (!fileKey && input.fileId) {
|
|
16
|
+
const dataAccessor = ctx.server.getDataAccessor({
|
|
17
|
+
singularCode: "ecm_storage_object",
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const storageObject = await dataAccessor.findById(input.fileId);
|
|
21
|
+
if (!storageObject) {
|
|
22
|
+
ctx.output = { error: new Error("Storage object not found.") };
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
fileKey = storageObject.key;
|
|
27
|
+
}
|
|
28
|
+
const filePathName = path.join(server.config.localFileStoragePath, fileKey);
|
|
29
|
+
const attachmentFileName = input.fileName || path.basename(fileKey);
|
|
30
|
+
|
|
31
|
+
response.body = await readFile(filePathName);
|
|
32
|
+
response.headers.set("Content-Disposition", `attachment; filename="${encodeURIComponent(attachmentFileName)}"`);
|
|
33
|
+
}
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
QuoteTableOptions,
|
|
8
8
|
RpdApplicationConfig,
|
|
9
9
|
RpdDataModel,
|
|
10
|
+
RpdDataModelIndex,
|
|
10
11
|
RpdDataModelProperty,
|
|
11
12
|
RpdDataPropertyTypes,
|
|
12
13
|
RpdEntityCreateEventPayload,
|
|
@@ -24,8 +25,8 @@ import {
|
|
|
24
25
|
import * as listMetaModels from "./actionHandlers/listMetaModels";
|
|
25
26
|
import * as listMetaRoutes from "./actionHandlers/listMetaRoutes";
|
|
26
27
|
import * as getMetaModelDetail from "./actionHandlers/getMetaModelDetail";
|
|
27
|
-
import { find } from "lodash";
|
|
28
|
-
import { getEntityPropertiesIncludingBase, isRelationProperty } from "~/helpers/metaHelper";
|
|
28
|
+
import { find, isString, map } from "lodash";
|
|
29
|
+
import { getEntityPropertiesIncludingBase, getEntityPropertyByCode, isOneRelationProperty, isRelationProperty } from "~/helpers/metaHelper";
|
|
29
30
|
|
|
30
31
|
class MetaManager implements RapidPlugin {
|
|
31
32
|
get code(): string {
|
|
@@ -366,6 +367,18 @@ async function syncDatabaseSchema(server: IRpdServer, applicationConfig: RpdAppl
|
|
|
366
367
|
);
|
|
367
368
|
}
|
|
368
369
|
}
|
|
370
|
+
|
|
371
|
+
// generate indexes
|
|
372
|
+
for (const model of applicationConfig.models) {
|
|
373
|
+
if (!model.indexes || !model.indexes.length) {
|
|
374
|
+
continue;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
for (const index of model.indexes) {
|
|
378
|
+
const sqlCreateIndex = generateTableIndexDDL(queryBuilder, server, model, index);
|
|
379
|
+
await server.tryQueryDatabaseObject(sqlCreateIndex, []);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
369
382
|
}
|
|
370
383
|
|
|
371
384
|
function generateCreateColumnDDL(
|
|
@@ -422,6 +435,56 @@ function generateLinkTableDDL(
|
|
|
422
435
|
return columnDDL;
|
|
423
436
|
}
|
|
424
437
|
|
|
438
|
+
function generateTableIndexDDL(queryBuilder: IQueryBuilder, server: IRpdServer, model: RpdDataModel, index: RpdDataModelIndex) {
|
|
439
|
+
let indexName = index.name;
|
|
440
|
+
if (!indexName) {
|
|
441
|
+
indexName = model.tableName;
|
|
442
|
+
for (const indexProp of index.properties) {
|
|
443
|
+
const propCode = isString(indexProp) ? indexProp : indexProp.code;
|
|
444
|
+
const property = getEntityPropertyByCode(server, model, propCode);
|
|
445
|
+
if (!isRelationProperty(property)) {
|
|
446
|
+
indexName += "_" + property.columnName;
|
|
447
|
+
} else if (isOneRelationProperty(property)) {
|
|
448
|
+
indexName += "_" + property.targetIdColumnName;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
indexName += index.unique ? "_uindex" : "_index";
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
const indexColumns = map(index.properties, (indexProp) => {
|
|
455
|
+
let columnName: string;
|
|
456
|
+
const propCode = isString(indexProp) ? indexProp : indexProp.code;
|
|
457
|
+
const property = getEntityPropertyByCode(server, model, propCode);
|
|
458
|
+
if (!isRelationProperty(property)) {
|
|
459
|
+
columnName = property.columnName;
|
|
460
|
+
} else if (isOneRelationProperty(property)) {
|
|
461
|
+
columnName = property.targetIdColumnName;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
if (isString(indexProp)) {
|
|
465
|
+
return columnName;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
if (indexProp.order === "desc") {
|
|
469
|
+
return `${columnName} desc`;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
return columnName;
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
let ddl = `CREATE ${index.unique ? "UNIQUE" : ""} INDEX ${indexName} `;
|
|
476
|
+
ddl += `ON ${queryBuilder.quoteTable({
|
|
477
|
+
schema: model.schema,
|
|
478
|
+
tableName: model.tableName,
|
|
479
|
+
})} (${indexColumns.join(", ")})`;
|
|
480
|
+
|
|
481
|
+
if (index.conditions) {
|
|
482
|
+
ddl += ` WHERE ${queryBuilder.buildFiltersExpression(model, index.conditions)}`;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
return ddl;
|
|
486
|
+
}
|
|
487
|
+
|
|
425
488
|
const pgPropertyTypeColumnMap: Partial<Record<RpdDataPropertyTypes, string>> = {
|
|
426
489
|
integer: "int4",
|
|
427
490
|
long: "int8",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { find } from "lodash";
|
|
1
|
+
import { find, isBoolean, isNull, isNumber, isString, isUndefined } from "lodash";
|
|
2
2
|
import { RpdDataModel, RpdDataModelProperty, CreateEntityOptions, QuoteTableOptions, DatabaseQuery } from "../types";
|
|
3
3
|
import {
|
|
4
4
|
CountRowOptions,
|
|
@@ -32,6 +32,10 @@ export interface BuildQueryContext {
|
|
|
32
32
|
builder: QueryBuilder;
|
|
33
33
|
params: any[];
|
|
34
34
|
emitTableAlias: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* emit parameter value to sql literal.
|
|
37
|
+
*/
|
|
38
|
+
paramToLiteral: boolean;
|
|
35
39
|
}
|
|
36
40
|
|
|
37
41
|
export interface InitQueryBuilderOptions {
|
|
@@ -82,6 +86,7 @@ export default class QueryBuilder {
|
|
|
82
86
|
builder: this,
|
|
83
87
|
params: [],
|
|
84
88
|
emitTableAlias: true,
|
|
89
|
+
paramToLiteral: false,
|
|
85
90
|
};
|
|
86
91
|
let { fields: columns, filters, orderBy, pagination } = options;
|
|
87
92
|
let command = "SELECT ";
|
|
@@ -143,6 +148,7 @@ export default class QueryBuilder {
|
|
|
143
148
|
builder: this,
|
|
144
149
|
params: [],
|
|
145
150
|
emitTableAlias: true,
|
|
151
|
+
paramToLiteral: false,
|
|
146
152
|
};
|
|
147
153
|
let { fields: columns, filters, orderBy, pagination } = options;
|
|
148
154
|
let command = "SELECT ";
|
|
@@ -210,6 +216,7 @@ export default class QueryBuilder {
|
|
|
210
216
|
builder: this,
|
|
211
217
|
params: [],
|
|
212
218
|
emitTableAlias: false,
|
|
219
|
+
paramToLiteral: false,
|
|
213
220
|
};
|
|
214
221
|
let { filters } = options;
|
|
215
222
|
let command = 'SELECT COUNT(*)::int as "count" FROM ';
|
|
@@ -233,6 +240,7 @@ export default class QueryBuilder {
|
|
|
233
240
|
builder: this,
|
|
234
241
|
params: [],
|
|
235
242
|
emitTableAlias: true,
|
|
243
|
+
paramToLiteral: false,
|
|
236
244
|
};
|
|
237
245
|
let { filters } = options;
|
|
238
246
|
let command = 'SELECT COUNT(*)::int as "count" FROM ';
|
|
@@ -259,6 +267,7 @@ export default class QueryBuilder {
|
|
|
259
267
|
builder: this,
|
|
260
268
|
params,
|
|
261
269
|
emitTableAlias: false,
|
|
270
|
+
paramToLiteral: false,
|
|
262
271
|
};
|
|
263
272
|
const { entity } = options;
|
|
264
273
|
let command = "INSERT INTO ";
|
|
@@ -302,6 +311,7 @@ export default class QueryBuilder {
|
|
|
302
311
|
builder: this,
|
|
303
312
|
params,
|
|
304
313
|
emitTableAlias: false,
|
|
314
|
+
paramToLiteral: false,
|
|
305
315
|
};
|
|
306
316
|
let { entity, filters } = options;
|
|
307
317
|
let command = "UPDATE ";
|
|
@@ -349,6 +359,7 @@ export default class QueryBuilder {
|
|
|
349
359
|
builder: this,
|
|
350
360
|
params,
|
|
351
361
|
emitTableAlias: false,
|
|
362
|
+
paramToLiteral: false,
|
|
352
363
|
};
|
|
353
364
|
let { filters } = options;
|
|
354
365
|
let command = "DELETE FROM ";
|
|
@@ -365,6 +376,19 @@ export default class QueryBuilder {
|
|
|
365
376
|
params: ctx.params,
|
|
366
377
|
};
|
|
367
378
|
}
|
|
379
|
+
|
|
380
|
+
buildFiltersExpression(model: RpdDataModel, filters: RowFilterOptions[]) {
|
|
381
|
+
const params: any[] = [];
|
|
382
|
+
const ctx: BuildQueryContext = {
|
|
383
|
+
model,
|
|
384
|
+
builder: this,
|
|
385
|
+
params,
|
|
386
|
+
emitTableAlias: false,
|
|
387
|
+
paramToLiteral: true,
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
return buildFiltersQuery(ctx, filters);
|
|
391
|
+
}
|
|
368
392
|
}
|
|
369
393
|
|
|
370
394
|
export function buildFiltersQuery(ctx: BuildQueryContext, filters: RowFilterOptions[]) {
|
|
@@ -434,8 +458,13 @@ function buildInFilterQuery(ctx: BuildQueryContext, filter: FindRowSetFilterOpti
|
|
|
434
458
|
} else {
|
|
435
459
|
command += " <> ";
|
|
436
460
|
}
|
|
437
|
-
|
|
438
|
-
|
|
461
|
+
|
|
462
|
+
if (ctx.paramToLiteral) {
|
|
463
|
+
// TODO: implement it
|
|
464
|
+
} else {
|
|
465
|
+
ctx.params.push(filter.value);
|
|
466
|
+
command += `ANY($${ctx.params.length}::${filter.itemType || "int"}[])`;
|
|
467
|
+
}
|
|
439
468
|
|
|
440
469
|
return command;
|
|
441
470
|
}
|
|
@@ -444,8 +473,13 @@ function buildContainsFilterQuery(ctx: BuildQueryContext, filter: FindRowRelatio
|
|
|
444
473
|
let command = ctx.builder.quoteColumn(ctx.model, filter.field, ctx.emitTableAlias);
|
|
445
474
|
|
|
446
475
|
command += " LIKE ";
|
|
447
|
-
|
|
448
|
-
|
|
476
|
+
|
|
477
|
+
if (ctx.paramToLiteral) {
|
|
478
|
+
// TODO: implement it
|
|
479
|
+
} else {
|
|
480
|
+
ctx.params.push(`%${filter.value}%`);
|
|
481
|
+
command += "$" + ctx.params.length;
|
|
482
|
+
}
|
|
449
483
|
|
|
450
484
|
return command;
|
|
451
485
|
}
|
|
@@ -454,8 +488,12 @@ function buildNotContainsFilterQuery(ctx: BuildQueryContext, filter: FindRowRela
|
|
|
454
488
|
let command = ctx.builder.quoteColumn(ctx.model, filter.field, ctx.emitTableAlias);
|
|
455
489
|
|
|
456
490
|
command += " NOT LIKE ";
|
|
457
|
-
ctx.
|
|
458
|
-
|
|
491
|
+
if (ctx.paramToLiteral) {
|
|
492
|
+
// TODO: implement it
|
|
493
|
+
} else {
|
|
494
|
+
ctx.params.push(`%${filter.value}%`);
|
|
495
|
+
command += "$" + ctx.params.length;
|
|
496
|
+
}
|
|
459
497
|
|
|
460
498
|
return command;
|
|
461
499
|
}
|
|
@@ -464,8 +502,13 @@ function buildStartsWithFilterQuery(ctx: BuildQueryContext, filter: FindRowRelat
|
|
|
464
502
|
let command = ctx.builder.quoteColumn(ctx.model, filter.field, ctx.emitTableAlias);
|
|
465
503
|
|
|
466
504
|
command += " LIKE ";
|
|
467
|
-
|
|
468
|
-
|
|
505
|
+
|
|
506
|
+
if (ctx.paramToLiteral) {
|
|
507
|
+
// TODO: implement it
|
|
508
|
+
} else {
|
|
509
|
+
ctx.params.push(`${filter.value}%`);
|
|
510
|
+
command += "$" + ctx.params.length;
|
|
511
|
+
}
|
|
469
512
|
|
|
470
513
|
return command;
|
|
471
514
|
}
|
|
@@ -474,8 +517,13 @@ function buildNotStartsWithFilterQuery(ctx: BuildQueryContext, filter: FindRowRe
|
|
|
474
517
|
let command = ctx.builder.quoteColumn(ctx.model, filter.field, ctx.emitTableAlias);
|
|
475
518
|
|
|
476
519
|
command += " NOT LIKE ";
|
|
477
|
-
|
|
478
|
-
|
|
520
|
+
|
|
521
|
+
if (ctx.paramToLiteral) {
|
|
522
|
+
// TODO: implement it
|
|
523
|
+
} else {
|
|
524
|
+
ctx.params.push(`${filter.value}%`);
|
|
525
|
+
command += "$" + ctx.params.length;
|
|
526
|
+
}
|
|
479
527
|
|
|
480
528
|
return command;
|
|
481
529
|
}
|
|
@@ -484,8 +532,13 @@ function buildEndsWithFilterQuery(ctx: BuildQueryContext, filter: FindRowRelatio
|
|
|
484
532
|
let command = ctx.builder.quoteColumn(ctx.model, filter.field, ctx.emitTableAlias);
|
|
485
533
|
|
|
486
534
|
command += " LIKE ";
|
|
487
|
-
|
|
488
|
-
|
|
535
|
+
|
|
536
|
+
if (ctx.paramToLiteral) {
|
|
537
|
+
// TODO: implement it
|
|
538
|
+
} else {
|
|
539
|
+
ctx.params.push(`%${filter.value}`);
|
|
540
|
+
command += "$" + ctx.params.length;
|
|
541
|
+
}
|
|
489
542
|
|
|
490
543
|
return command;
|
|
491
544
|
}
|
|
@@ -494,8 +547,13 @@ function buildNotEndsWithFilterQuery(ctx: BuildQueryContext, filter: FindRowRela
|
|
|
494
547
|
let command = ctx.builder.quoteColumn(ctx.model, filter.field, ctx.emitTableAlias);
|
|
495
548
|
|
|
496
549
|
command += " NOT LIKE ";
|
|
497
|
-
|
|
498
|
-
|
|
550
|
+
|
|
551
|
+
if (ctx.paramToLiteral) {
|
|
552
|
+
// TODO: implement it
|
|
553
|
+
} else {
|
|
554
|
+
ctx.params.push(`%${filter.value}`);
|
|
555
|
+
command += "$" + ctx.params.length;
|
|
556
|
+
}
|
|
499
557
|
|
|
500
558
|
return command;
|
|
501
559
|
}
|
|
@@ -505,8 +563,32 @@ function buildRelationalFilterQuery(ctx: BuildQueryContext, filter: FindRowRelat
|
|
|
505
563
|
|
|
506
564
|
command += relationalOperatorsMap.get(filter.operator);
|
|
507
565
|
|
|
508
|
-
ctx.
|
|
509
|
-
|
|
566
|
+
if (ctx.paramToLiteral) {
|
|
567
|
+
command += formatValueToSqlLiteral(filter.value);
|
|
568
|
+
} else {
|
|
569
|
+
ctx.params.push(filter.value);
|
|
570
|
+
command += "$" + ctx.params.length;
|
|
571
|
+
}
|
|
510
572
|
|
|
511
573
|
return command;
|
|
512
574
|
}
|
|
575
|
+
|
|
576
|
+
function formatValueToSqlLiteral(value: any) {
|
|
577
|
+
if (isNull(value) || isUndefined(value)) {
|
|
578
|
+
return "null";
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
if (isString(value)) {
|
|
582
|
+
return `'${value.replaceAll("'", "''")}'`;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
if (isBoolean(value)) {
|
|
586
|
+
return value ? "true" : "false";
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
if (isNumber(value)) {
|
|
590
|
+
return value.toString();
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
return `'${value.toString().replaceAll("'", "''")}'`;
|
|
594
|
+
}
|