bigal 12.1.4 → 13.0.0-beta1
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/.devcontainer/devcontainer.json +16 -0
- package/.husky/pre-commit +4 -0
- package/.prettierrc.cjs +34 -0
- package/CHANGELOG.md +9 -0
- package/dist/index.cjs +2626 -0
- package/dist/index.d.cts +892 -0
- package/dist/index.d.mts +892 -0
- package/dist/index.d.ts +892 -0
- package/dist/index.mjs +2605 -0
- package/eslint.config.mjs +25 -0
- package/package.json +46 -37
- package/tsconfig.build.json +7 -0
- package/Entity.d.ts +0 -16
- package/Entity.js +0 -13
- package/Entity.js.map +0 -1
- package/IReadonlyRepository.d.ts +0 -36
- package/IReadonlyRepository.js +0 -3
- package/IReadonlyRepository.js.map +0 -1
- package/IRepository.d.ts +0 -99
- package/IRepository.js +0 -3
- package/IRepository.js.map +0 -1
- package/ReadonlyRepository.d.ts +0 -69
- package/ReadonlyRepository.js +0 -687
- package/ReadonlyRepository.js.map +0 -1
- package/Repository.d.ts +0 -69
- package/Repository.js +0 -171
- package/Repository.js.map +0 -1
- package/SqlHelper.d.ts +0 -144
- package/SqlHelper.js +0 -1081
- package/SqlHelper.js.map +0 -1
- package/decorators/ColumnBaseOptions.d.ts +0 -10
- package/decorators/ColumnBaseOptions.js +0 -3
- package/decorators/ColumnBaseOptions.js.map +0 -1
- package/decorators/ColumnCollectionOptions.d.ts +0 -15
- package/decorators/ColumnCollectionOptions.js +0 -3
- package/decorators/ColumnCollectionOptions.js.map +0 -1
- package/decorators/ColumnModelOptions.d.ts +0 -7
- package/decorators/ColumnModelOptions.js +0 -3
- package/decorators/ColumnModelOptions.js.map +0 -1
- package/decorators/ColumnTypeOptions.d.ts +0 -21
- package/decorators/ColumnTypeOptions.js +0 -3
- package/decorators/ColumnTypeOptions.js.map +0 -1
- package/decorators/TableOptions.d.ts +0 -14
- package/decorators/TableOptions.js +0 -3
- package/decorators/TableOptions.js.map +0 -1
- package/decorators/column.d.ts +0 -9
- package/decorators/column.js +0 -93
- package/decorators/column.js.map +0 -1
- package/decorators/createDateColumn.d.ts +0 -6
- package/decorators/createDateColumn.js +0 -50
- package/decorators/createDateColumn.js.map +0 -1
- package/decorators/index.d.ts +0 -6
- package/decorators/index.js +0 -23
- package/decorators/index.js.map +0 -1
- package/decorators/primaryColumn.d.ts +0 -8
- package/decorators/primaryColumn.js +0 -66
- package/decorators/primaryColumn.js.map +0 -1
- package/decorators/table.d.ts +0 -5
- package/decorators/table.js +0 -40
- package/decorators/table.js.map +0 -1
- package/decorators/updateDateColumn.d.ts +0 -6
- package/decorators/updateDateColumn.js +0 -51
- package/decorators/updateDateColumn.js.map +0 -1
- package/decorators/versionColumn.d.ts +0 -6
- package/decorators/versionColumn.js +0 -51
- package/decorators/versionColumn.js.map +0 -1
- package/errors/QueryError.d.ts +0 -8
- package/errors/QueryError.js +0 -13
- package/errors/QueryError.js.map +0 -1
- package/errors/index.d.ts +0 -1
- package/errors/index.js +0 -18
- package/errors/index.js.map +0 -1
- package/index.d.ts +0 -35
- package/index.js +0 -220
- package/index.js.map +0 -1
- package/metadata/ColumnBaseMetadata.d.ts +0 -93
- package/metadata/ColumnBaseMetadata.js +0 -19
- package/metadata/ColumnBaseMetadata.js.map +0 -1
- package/metadata/ColumnCollectionMetadata.d.ts +0 -36
- package/metadata/ColumnCollectionMetadata.js +0 -63
- package/metadata/ColumnCollectionMetadata.js.map +0 -1
- package/metadata/ColumnMetadata.d.ts +0 -4
- package/metadata/ColumnMetadata.js +0 -3
- package/metadata/ColumnMetadata.js.map +0 -1
- package/metadata/ColumnModelMetadata.d.ts +0 -18
- package/metadata/ColumnModelMetadata.js +0 -43
- package/metadata/ColumnModelMetadata.js.map +0 -1
- package/metadata/ColumnModifierMetadata.d.ts +0 -46
- package/metadata/ColumnModifierMetadata.js +0 -3
- package/metadata/ColumnModifierMetadata.js.map +0 -1
- package/metadata/ColumnTypeMetadata.d.ts +0 -43
- package/metadata/ColumnTypeMetadata.js +0 -26
- package/metadata/ColumnTypeMetadata.js.map +0 -1
- package/metadata/MetadataStorage.d.ts +0 -13
- package/metadata/MetadataStorage.js +0 -19
- package/metadata/MetadataStorage.js.map +0 -1
- package/metadata/ModelMetadata.d.ts +0 -36
- package/metadata/ModelMetadata.js +0 -81
- package/metadata/ModelMetadata.js.map +0 -1
- package/metadata/index.d.ts +0 -10
- package/metadata/index.js +0 -33
- package/metadata/index.js.map +0 -1
- package/query/Comparer.d.ts +0 -1
- package/query/Comparer.js +0 -3
- package/query/Comparer.js.map +0 -1
- package/query/CountArgs.d.ts +0 -7
- package/query/CountArgs.js +0 -3
- package/query/CountArgs.js.map +0 -1
- package/query/CountResult.d.ts +0 -5
- package/query/CountResult.js +0 -3
- package/query/CountResult.js.map +0 -1
- package/query/CreateOptions.d.ts +0 -9
- package/query/CreateOptions.js +0 -3
- package/query/CreateOptions.js.map +0 -1
- package/query/CreateUpdateOptions.d.ts +0 -4
- package/query/CreateUpdateOptions.js +0 -3
- package/query/CreateUpdateOptions.js.map +0 -1
- package/query/DeleteOptions.d.ts +0 -12
- package/query/DeleteOptions.js +0 -3
- package/query/DeleteOptions.js.map +0 -1
- package/query/DestroyResult.d.ts +0 -5
- package/query/DestroyResult.js +0 -3
- package/query/DestroyResult.js.map +0 -1
- package/query/DoNotReturnRecords.d.ts +0 -3
- package/query/DoNotReturnRecords.js +0 -3
- package/query/DoNotReturnRecords.js.map +0 -1
- package/query/FindArgs.d.ts +0 -6
- package/query/FindArgs.js +0 -3
- package/query/FindArgs.js.map +0 -1
- package/query/FindOneArgs.d.ts +0 -11
- package/query/FindOneArgs.js +0 -3
- package/query/FindOneArgs.js.map +0 -1
- package/query/FindOneResult.d.ts +0 -12
- package/query/FindOneResult.js +0 -3
- package/query/FindOneResult.js.map +0 -1
- package/query/FindResult.d.ts +0 -15
- package/query/FindResult.js +0 -3
- package/query/FindResult.js.map +0 -1
- package/query/OnConflictOptions.d.ts +0 -25
- package/query/OnConflictOptions.js +0 -3
- package/query/OnConflictOptions.js.map +0 -1
- package/query/PaginateOptions.d.ts +0 -4
- package/query/PaginateOptions.js +0 -3
- package/query/PaginateOptions.js.map +0 -1
- package/query/PopulateArgs.d.ts +0 -13
- package/query/PopulateArgs.js +0 -3
- package/query/PopulateArgs.js.map +0 -1
- package/query/ReturnSelect.d.ts +0 -5
- package/query/ReturnSelect.js +0 -3
- package/query/ReturnSelect.js.map +0 -1
- package/query/Sort.d.ts +0 -15
- package/query/Sort.js +0 -3
- package/query/Sort.js.map +0 -1
- package/query/WhereQuery.d.ts +0 -21
- package/query/WhereQuery.js +0 -3
- package/query/WhereQuery.js.map +0 -1
- package/query/index.d.ts +0 -15
- package/query/index.js +0 -32
- package/query/index.js.map +0 -1
- package/types/ClassLike.d.ts +0 -8
- package/types/ClassLike.js +0 -3
- package/types/ClassLike.js.map +0 -1
- package/types/CreateUpdateParams.d.ts +0 -9
- package/types/CreateUpdateParams.js +0 -3
- package/types/CreateUpdateParams.js.map +0 -1
- package/types/EntityPrimitiveOrId.d.ts +0 -2
- package/types/EntityPrimitiveOrId.js +0 -3
- package/types/EntityPrimitiveOrId.js.map +0 -1
- package/types/ExcludeEntityCollections.d.ts +0 -5
- package/types/ExcludeEntityCollections.js +0 -3
- package/types/ExcludeEntityCollections.js.map +0 -1
- package/types/ExcludeFunctions.d.ts +0 -4
- package/types/ExcludeFunctions.js +0 -3
- package/types/ExcludeFunctions.js.map +0 -1
- package/types/GetValueType.d.ts +0 -1
- package/types/GetValueType.js +0 -3
- package/types/GetValueType.js.map +0 -1
- package/types/IncludeFunctions.d.ts +0 -4
- package/types/IncludeFunctions.js +0 -3
- package/types/IncludeFunctions.js.map +0 -1
- package/types/IsValueOfType.d.ts +0 -1
- package/types/IsValueOfType.js +0 -3
- package/types/IsValueOfType.js.map +0 -1
- package/types/OmitEntityCollections.d.ts +0 -7
- package/types/OmitEntityCollections.js +0 -3
- package/types/OmitEntityCollections.js.map +0 -1
- package/types/OmitFunctions.d.ts +0 -7
- package/types/OmitFunctions.js +0 -3
- package/types/OmitFunctions.js.map +0 -1
- package/types/PickAsType.d.ts +0 -3
- package/types/PickAsType.js +0 -3
- package/types/PickAsType.js.map +0 -1
- package/types/PickByValueType.d.ts +0 -5
- package/types/PickByValueType.js +0 -3
- package/types/PickByValueType.js.map +0 -1
- package/types/PickFunctions.d.ts +0 -4
- package/types/PickFunctions.js +0 -3
- package/types/PickFunctions.js.map +0 -1
- package/types/Populated.d.ts +0 -9
- package/types/Populated.js +0 -3
- package/types/Populated.js.map +0 -1
- package/types/QueryResult.d.ts +0 -9
- package/types/QueryResult.js +0 -3
- package/types/QueryResult.js.map +0 -1
- package/types/QueryResultOptionalPopulated.d.ts +0 -9
- package/types/QueryResultOptionalPopulated.js +0 -3
- package/types/QueryResultOptionalPopulated.js.map +0 -1
- package/types/QueryResultPopulated.d.ts +0 -9
- package/types/QueryResultPopulated.js +0 -3
- package/types/QueryResultPopulated.js.map +0 -1
- package/types/index.d.ts +0 -17
- package/types/index.js +0 -34
- package/types/index.js.map +0 -1
package/SqlHelper.js
DELETED
|
@@ -1,1081 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
-
if (mod && mod.__esModule) return mod;
|
|
20
|
-
var result = {};
|
|
21
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
-
__setModuleDefault(result, mod);
|
|
23
|
-
return result;
|
|
24
|
-
};
|
|
25
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.buildOrderStatement = exports.buildWhereStatement = exports.getColumnsToSelect = exports.getDeleteQueryAndParams = exports.getUpdateQueryAndParams = exports.getInsertQueryAndParams = exports.getCountQueryAndParams = exports.getSelectQueryAndParams = void 0;
|
|
27
|
-
const _ = __importStar(require("lodash"));
|
|
28
|
-
const errors_1 = require("./errors");
|
|
29
|
-
/* eslint-disable @typescript-eslint/no-use-before-define */
|
|
30
|
-
/**
|
|
31
|
-
* Gets the select syntax for the specified model and filters
|
|
32
|
-
* @param {object} args - Arguments
|
|
33
|
-
* @param {object} args.repositoriesByModelNameLowered - All model schemas organized by model name
|
|
34
|
-
* @param {object} args.model - Model schema
|
|
35
|
-
* @param {string[]} [args.select] - Array of model property names to return from the query.
|
|
36
|
-
* @param {object} [args.where] - Object representing the where query
|
|
37
|
-
* @param {string[]|object[]} [args.sorts] - Property name(s) to sort by
|
|
38
|
-
* @param {number} [args.skip] - Number of records to skip
|
|
39
|
-
* @param {number} [args.limit] - Number of results to return
|
|
40
|
-
* @returns {{query: string, params: object[]}}
|
|
41
|
-
*/
|
|
42
|
-
function getSelectQueryAndParams({ repositoriesByModelNameLowered, model, select, where, sorts, skip, limit, }) {
|
|
43
|
-
let query = 'SELECT ';
|
|
44
|
-
query += getColumnsToSelect({
|
|
45
|
-
model,
|
|
46
|
-
select,
|
|
47
|
-
});
|
|
48
|
-
query += ` FROM "${model.tableName}"`;
|
|
49
|
-
const { whereStatement, params } = buildWhereStatement({
|
|
50
|
-
repositoriesByModelNameLowered,
|
|
51
|
-
model,
|
|
52
|
-
where,
|
|
53
|
-
});
|
|
54
|
-
if (whereStatement) {
|
|
55
|
-
query += ` ${whereStatement}`;
|
|
56
|
-
}
|
|
57
|
-
const orderStatement = buildOrderStatement({
|
|
58
|
-
model,
|
|
59
|
-
sorts,
|
|
60
|
-
});
|
|
61
|
-
if (orderStatement) {
|
|
62
|
-
query += ` ${orderStatement}`;
|
|
63
|
-
}
|
|
64
|
-
if (limit) {
|
|
65
|
-
if (_.isString(limit)) {
|
|
66
|
-
// eslint-disable-next-line no-param-reassign
|
|
67
|
-
limit = Number(limit);
|
|
68
|
-
}
|
|
69
|
-
if (!_.isFinite(limit)) {
|
|
70
|
-
throw new errors_1.QueryError('Limit should be a number', model, where);
|
|
71
|
-
}
|
|
72
|
-
query += ` LIMIT ${limit}`;
|
|
73
|
-
}
|
|
74
|
-
if (skip) {
|
|
75
|
-
if (_.isString(skip)) {
|
|
76
|
-
// eslint-disable-next-line no-param-reassign
|
|
77
|
-
skip = Number(skip);
|
|
78
|
-
}
|
|
79
|
-
if (!_.isFinite(skip)) {
|
|
80
|
-
throw new errors_1.QueryError('Skip should be a number', model, where);
|
|
81
|
-
}
|
|
82
|
-
query += ` OFFSET ${skip}`;
|
|
83
|
-
}
|
|
84
|
-
if (process.env.DEBUG_BIGAL?.toLowerCase() === 'true') {
|
|
85
|
-
// eslint-disable-next-line no-console
|
|
86
|
-
console.log(`BigAl: ${query}`);
|
|
87
|
-
}
|
|
88
|
-
return {
|
|
89
|
-
query,
|
|
90
|
-
params,
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
exports.getSelectQueryAndParams = getSelectQueryAndParams;
|
|
94
|
-
/**
|
|
95
|
-
* Gets the count syntax for the specified model and values
|
|
96
|
-
* @param {object} args - Arguments
|
|
97
|
-
* @param {object} args.repositoriesByModelNameLowered - All model schemas organized by model name
|
|
98
|
-
* @param {object} args.model - Model schema
|
|
99
|
-
* @param {object} [args.where] - Object representing the where query
|
|
100
|
-
* @returns {{query: string, params: object[]}}
|
|
101
|
-
*/
|
|
102
|
-
function getCountQueryAndParams({ repositoriesByModelNameLowered, model, where, }) {
|
|
103
|
-
let query = `SELECT count(*) AS "count" FROM "${model.tableName}"`;
|
|
104
|
-
const { whereStatement, params } = buildWhereStatement({
|
|
105
|
-
repositoriesByModelNameLowered,
|
|
106
|
-
model,
|
|
107
|
-
where,
|
|
108
|
-
});
|
|
109
|
-
if (whereStatement) {
|
|
110
|
-
query += ` ${whereStatement}`;
|
|
111
|
-
}
|
|
112
|
-
return {
|
|
113
|
-
query,
|
|
114
|
-
params,
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
exports.getCountQueryAndParams = getCountQueryAndParams;
|
|
118
|
-
/**
|
|
119
|
-
* Gets the insert syntax for the specified model and values
|
|
120
|
-
* @param {object} args - Arguments
|
|
121
|
-
* @param {object} args.repositoriesByModelNameLowered - All model schemas organized by model name
|
|
122
|
-
* @param {object} args.model - Model schema
|
|
123
|
-
* @param {object|object[]} args.values - Values to insert. Insert multiple records by passing an array of values.
|
|
124
|
-
* @param {object} [args.onConflict] - Options to handle conflicts due to a unique constraint or exclusion constraint error during insert
|
|
125
|
-
* @param {boolean} [args.returnRecords=true] - Determines if inserted records should be returned
|
|
126
|
-
* @param {string[]} [args.returnSelect] - Array of model property names to return from the query.
|
|
127
|
-
* @returns {{query: string, params: object[]}}
|
|
128
|
-
*/
|
|
129
|
-
function getInsertQueryAndParams({ repositoriesByModelNameLowered, model, values, returnRecords = true, returnSelect, onConflict, }) {
|
|
130
|
-
const entitiesToInsert = _.isArray(values) ? values : [values];
|
|
131
|
-
const conflictTargetColumns = [];
|
|
132
|
-
const columnsToInsert = [];
|
|
133
|
-
const columnsToMerge = [];
|
|
134
|
-
let hasEmptyMergeColumns = false;
|
|
135
|
-
// Set defaulted property values and verify required columns have a value specified
|
|
136
|
-
for (const column of model.columns) {
|
|
137
|
-
const collectionColumn = column;
|
|
138
|
-
if (!collectionColumn.collection) {
|
|
139
|
-
const { defaultsTo } = column;
|
|
140
|
-
let defaultValue;
|
|
141
|
-
if (_.isFunction(defaultsTo)) {
|
|
142
|
-
defaultValue = defaultsTo();
|
|
143
|
-
}
|
|
144
|
-
else if (!_.isUndefined(defaultsTo)) {
|
|
145
|
-
defaultValue = defaultsTo;
|
|
146
|
-
}
|
|
147
|
-
else if (column.createDate) {
|
|
148
|
-
defaultValue = new Date();
|
|
149
|
-
}
|
|
150
|
-
else if (column.updateDate) {
|
|
151
|
-
defaultValue = new Date();
|
|
152
|
-
}
|
|
153
|
-
else if (column.version) {
|
|
154
|
-
defaultValue = 1;
|
|
155
|
-
}
|
|
156
|
-
const hasDefaultValue = !_.isUndefined(defaultValue);
|
|
157
|
-
let includePropertyName = false;
|
|
158
|
-
for (const entity of entitiesToInsert) {
|
|
159
|
-
// If there is a default value for the property and if it is not defined, use the default
|
|
160
|
-
if (hasDefaultValue && _.isUndefined(entity[column.propertyName])) {
|
|
161
|
-
// @ts-expect-error - string is not assignable to T[string & keyof T] | undefined
|
|
162
|
-
entity[column.propertyName] = defaultValue;
|
|
163
|
-
}
|
|
164
|
-
if (_.isUndefined(entity[column.propertyName])) {
|
|
165
|
-
if (column.required) {
|
|
166
|
-
throw new errors_1.QueryError(`Create statement for "${model.name}" is missing value for required field: ${column.propertyName}`, model);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
else {
|
|
170
|
-
includePropertyName = true;
|
|
171
|
-
// Check and enforce max length for applicable types
|
|
172
|
-
const { maxLength, type } = column;
|
|
173
|
-
if (maxLength && ['string', 'string[]'].includes(type)) {
|
|
174
|
-
const entityValues = entity[column.propertyName];
|
|
175
|
-
const normalizedValues = (Array.isArray(entityValues) ? entityValues : [entityValues]);
|
|
176
|
-
for (const normalizedValue of normalizedValues) {
|
|
177
|
-
if (normalizedValue?.length > maxLength) {
|
|
178
|
-
throw new errors_1.QueryError(`Create statement for "${model.name}" contains a value that exceeds maxLength on field: ${column.propertyName}`, model);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
if (includePropertyName) {
|
|
185
|
-
columnsToInsert.push(column);
|
|
186
|
-
}
|
|
187
|
-
if (onConflict) {
|
|
188
|
-
if ((Array.isArray(onConflict.targets) && onConflict.targets.includes(column.propertyName)) ||
|
|
189
|
-
(!Array.isArray(onConflict.targets) && onConflict.targets.columns.includes(column.propertyName))) {
|
|
190
|
-
conflictTargetColumns.push(column);
|
|
191
|
-
}
|
|
192
|
-
if (onConflict.action === 'merge') {
|
|
193
|
-
const mergeColumns = Array.isArray(onConflict.merge) ? onConflict.merge : onConflict.merge?.columns;
|
|
194
|
-
if (mergeColumns) {
|
|
195
|
-
hasEmptyMergeColumns = !mergeColumns.length;
|
|
196
|
-
if (mergeColumns.includes(column.propertyName)) {
|
|
197
|
-
columnsToMerge.push(column);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
else if (!column.createDate && !column.primary) {
|
|
201
|
-
columnsToMerge.push(column);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
const valueCollections = entitiesToInsert.map(() => []);
|
|
208
|
-
const params = [];
|
|
209
|
-
let query = `INSERT INTO "${model.tableName}" (`;
|
|
210
|
-
for (const [columnIndex, column] of columnsToInsert.entries()) {
|
|
211
|
-
if (columnIndex > 0) {
|
|
212
|
-
query += ',';
|
|
213
|
-
}
|
|
214
|
-
query += `"${column.name}"`;
|
|
215
|
-
for (const [entityIndex, entity] of entitiesToInsert.entries()) {
|
|
216
|
-
let value;
|
|
217
|
-
const entityValue = entity[column.propertyName];
|
|
218
|
-
if (_.isNil(entityValue)) {
|
|
219
|
-
value = 'NULL';
|
|
220
|
-
}
|
|
221
|
-
else {
|
|
222
|
-
const isJsonArray = column.type === 'json' && _.isArray(entityValue);
|
|
223
|
-
const relatedModelName = column.model;
|
|
224
|
-
if (relatedModelName && _.isObject(entityValue)) {
|
|
225
|
-
const relatedModelRepository = repositoriesByModelNameLowered[relatedModelName.toLowerCase()];
|
|
226
|
-
if (!relatedModelRepository) {
|
|
227
|
-
throw new errors_1.QueryError(`Unable to find model schema (${relatedModelName}) specified as model type for "${column.propertyName}" on "${model.name}"`, model);
|
|
228
|
-
}
|
|
229
|
-
const relatedModelPrimaryKey = relatedModelRepository.model.primaryKeyColumn;
|
|
230
|
-
if (!relatedModelPrimaryKey) {
|
|
231
|
-
throw new errors_1.QueryError(`Unable to find primary key column for ${relatedModelName} when inserting ${model.name}.${column.propertyName} value.`, model);
|
|
232
|
-
}
|
|
233
|
-
const primaryKeyValue = entityValue[relatedModelPrimaryKey.propertyName];
|
|
234
|
-
if (_.isNil(primaryKeyValue)) {
|
|
235
|
-
throw new errors_1.QueryError(`Undefined primary key value for hydrated object value for "${column.propertyName}" on "${model.name}"`, model);
|
|
236
|
-
}
|
|
237
|
-
params.push(primaryKeyValue);
|
|
238
|
-
}
|
|
239
|
-
else if (isJsonArray) {
|
|
240
|
-
// Inserting an array to a json/jsonb column will result in a message: invalid input syntax for type json
|
|
241
|
-
// https://github.com/brianc/node-postgres/issues/442
|
|
242
|
-
params.push(JSON.stringify(entityValue));
|
|
243
|
-
}
|
|
244
|
-
else {
|
|
245
|
-
params.push(entityValue);
|
|
246
|
-
}
|
|
247
|
-
value = `$${params.length}`;
|
|
248
|
-
if (isJsonArray) {
|
|
249
|
-
value += '::jsonb';
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
const valuesForEntityIndex = valueCollections[entityIndex];
|
|
253
|
-
if (!valuesForEntityIndex) {
|
|
254
|
-
throw new errors_1.QueryError('Error trying to get insert values for entity index', model);
|
|
255
|
-
}
|
|
256
|
-
valuesForEntityIndex.push(value);
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
query += ') VALUES ';
|
|
260
|
-
for (const [index, valueCollection] of valueCollections.entries()) {
|
|
261
|
-
if (index > 0) {
|
|
262
|
-
query += ',';
|
|
263
|
-
}
|
|
264
|
-
query += `(${valueCollection.join(',')})`;
|
|
265
|
-
}
|
|
266
|
-
if (onConflict) {
|
|
267
|
-
query += ' ON CONFLICT (';
|
|
268
|
-
for (const [index, targetColumn] of conflictTargetColumns.entries()) {
|
|
269
|
-
if (index > 0) {
|
|
270
|
-
query += ',';
|
|
271
|
-
}
|
|
272
|
-
query += `"${targetColumn.name}"`;
|
|
273
|
-
}
|
|
274
|
-
query += ') ';
|
|
275
|
-
// ON CONFLICT (foo, bar) WHERE baz = 1
|
|
276
|
-
if (!Array.isArray(onConflict.targets)) {
|
|
277
|
-
const { whereStatement } = buildWhereStatement({
|
|
278
|
-
repositoriesByModelNameLowered,
|
|
279
|
-
model,
|
|
280
|
-
where: onConflict.targets.where,
|
|
281
|
-
params,
|
|
282
|
-
});
|
|
283
|
-
if (whereStatement) {
|
|
284
|
-
query += `${whereStatement} `;
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
if (onConflict.action === 'ignore' || hasEmptyMergeColumns) {
|
|
288
|
-
query += 'DO NOTHING';
|
|
289
|
-
}
|
|
290
|
-
else {
|
|
291
|
-
query += 'DO UPDATE SET ';
|
|
292
|
-
for (const [index, column] of columnsToMerge.entries()) {
|
|
293
|
-
if (index > 0) {
|
|
294
|
-
query += ',';
|
|
295
|
-
}
|
|
296
|
-
if (column.version) {
|
|
297
|
-
query += `"${column.name}"="${column.name}"+1`;
|
|
298
|
-
}
|
|
299
|
-
else {
|
|
300
|
-
query += `"${column.name}"=EXCLUDED."${column.name}"`;
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
if (!Array.isArray(onConflict.merge) && onConflict.merge?.where) {
|
|
304
|
-
const { whereStatement } = buildWhereStatement({
|
|
305
|
-
repositoriesByModelNameLowered,
|
|
306
|
-
model,
|
|
307
|
-
where: onConflict.merge.where,
|
|
308
|
-
params,
|
|
309
|
-
});
|
|
310
|
-
if (whereStatement) {
|
|
311
|
-
query += ` ${whereStatement}`;
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
if (returnRecords) {
|
|
317
|
-
query += ' RETURNING ';
|
|
318
|
-
query += getColumnsToSelect({
|
|
319
|
-
model,
|
|
320
|
-
select: returnSelect,
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
return {
|
|
324
|
-
query,
|
|
325
|
-
params,
|
|
326
|
-
};
|
|
327
|
-
}
|
|
328
|
-
exports.getInsertQueryAndParams = getInsertQueryAndParams;
|
|
329
|
-
/**
|
|
330
|
-
* Gets the update syntax for the specified model and values
|
|
331
|
-
* @param {object} args - Arguments
|
|
332
|
-
* @param {object} args.repositoriesByModelNameLowered - All model schemas organized by global id
|
|
333
|
-
* @param {object} args.model - Model schema
|
|
334
|
-
* @param {object} [args.where] - Object representing the where query
|
|
335
|
-
* @param {object} args.values - Values to set.
|
|
336
|
-
* @param {boolean} [args.returnRecords=true] - Determines if inserted records should be returned
|
|
337
|
-
* @param {string[]} [args.returnSelect] - Array of model property names to return from the query.
|
|
338
|
-
* @returns {{query: string, params: object[]}}
|
|
339
|
-
*/
|
|
340
|
-
function getUpdateQueryAndParams({ repositoriesByModelNameLowered, model, where, values = {}, returnRecords = true, returnSelect, }) {
|
|
341
|
-
for (const column of model.updateDateColumns) {
|
|
342
|
-
if (_.isUndefined(values[column.propertyName])) {
|
|
343
|
-
// eslint-disable-next-line no-param-reassign, @typescript-eslint/ban-ts-comment
|
|
344
|
-
// @ts-expect-error - Date is not assignable to T[string & keyof T]
|
|
345
|
-
values[column.propertyName] = new Date();
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
const params = [];
|
|
349
|
-
let query = `UPDATE "${model.tableName}" SET `;
|
|
350
|
-
let isFirstProperty = true;
|
|
351
|
-
for (const [propertyName, value] of Object.entries(values)) {
|
|
352
|
-
const column = model.columnsByPropertyName[propertyName];
|
|
353
|
-
if (column && !column.collection) {
|
|
354
|
-
if (!isFirstProperty) {
|
|
355
|
-
query += ',';
|
|
356
|
-
}
|
|
357
|
-
query += `"${column.name}"=`;
|
|
358
|
-
if (_.isNil(value)) {
|
|
359
|
-
query += 'NULL';
|
|
360
|
-
}
|
|
361
|
-
else {
|
|
362
|
-
const isJsonArray = column.type === 'json' && _.isArray(value);
|
|
363
|
-
const relatedModelName = column.model;
|
|
364
|
-
// Check and enforce max length for applicable types
|
|
365
|
-
const { maxLength, type } = column;
|
|
366
|
-
if (maxLength && ['string', 'string[]'].includes(type)) {
|
|
367
|
-
const normalizedValues = (Array.isArray(value) ? value : [value]);
|
|
368
|
-
for (const normalizedValue of normalizedValues) {
|
|
369
|
-
if (normalizedValue?.length > maxLength) {
|
|
370
|
-
throw new errors_1.QueryError(`Update statement for "${model.name}" contains a value that exceeds maxLength on field: ${column.propertyName}`, model);
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
if (relatedModelName && _.isObject(value)) {
|
|
375
|
-
const relatedModelRepository = repositoriesByModelNameLowered[relatedModelName.toLowerCase()];
|
|
376
|
-
if (!relatedModelRepository) {
|
|
377
|
-
throw new errors_1.QueryError(`Unable to find model schema (${relatedModelName}) specified as model type for "${propertyName}" on "${model.name}"`, model);
|
|
378
|
-
}
|
|
379
|
-
const relatedModelPrimaryKey = relatedModelRepository.model.primaryKeyColumn;
|
|
380
|
-
if (!relatedModelPrimaryKey) {
|
|
381
|
-
throw new errors_1.QueryError(`Unable to find primary key column for ${relatedModelName} when inserting ${model.name}.${column.propertyName} value.`, model);
|
|
382
|
-
}
|
|
383
|
-
const primaryKeyValue = value[relatedModelPrimaryKey.propertyName];
|
|
384
|
-
if (_.isNil(primaryKeyValue)) {
|
|
385
|
-
throw new errors_1.QueryError(`Undefined primary key value for hydrated object value for "${column.propertyName}" on "${model.name}"`, model);
|
|
386
|
-
}
|
|
387
|
-
params.push(primaryKeyValue);
|
|
388
|
-
}
|
|
389
|
-
else if (isJsonArray) {
|
|
390
|
-
// Inserting an array to a json/jsonb column will result in a message: invalid input syntax for type json
|
|
391
|
-
// https://github.com/brianc/node-postgres/issues/442
|
|
392
|
-
params.push(JSON.stringify(value));
|
|
393
|
-
}
|
|
394
|
-
else {
|
|
395
|
-
params.push(value);
|
|
396
|
-
}
|
|
397
|
-
query += `$${params.length}`;
|
|
398
|
-
if (isJsonArray) {
|
|
399
|
-
query += '::jsonb';
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
isFirstProperty = false;
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
for (const column of model.versionColumns) {
|
|
406
|
-
if (!_.isUndefined(values[column.propertyName])) {
|
|
407
|
-
if (!isFirstProperty) {
|
|
408
|
-
query += ',';
|
|
409
|
-
}
|
|
410
|
-
query += `"${column.name}"="${column.name}"+1`;
|
|
411
|
-
isFirstProperty = false;
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
const { whereStatement } = buildWhereStatement({
|
|
415
|
-
repositoriesByModelNameLowered,
|
|
416
|
-
model,
|
|
417
|
-
where,
|
|
418
|
-
params,
|
|
419
|
-
});
|
|
420
|
-
if (whereStatement) {
|
|
421
|
-
query += ` ${whereStatement}`;
|
|
422
|
-
}
|
|
423
|
-
if (returnRecords) {
|
|
424
|
-
query += ' RETURNING ';
|
|
425
|
-
query += getColumnsToSelect({
|
|
426
|
-
model,
|
|
427
|
-
select: returnSelect,
|
|
428
|
-
});
|
|
429
|
-
}
|
|
430
|
-
return {
|
|
431
|
-
query,
|
|
432
|
-
params,
|
|
433
|
-
};
|
|
434
|
-
}
|
|
435
|
-
exports.getUpdateQueryAndParams = getUpdateQueryAndParams;
|
|
436
|
-
/**
|
|
437
|
-
* Gets the delete syntax for the specified model and where criteria
|
|
438
|
-
* @param {object} args - Arguments
|
|
439
|
-
* @param {object} args.repositoriesByModelNameLowered - All model schemas organized by global id
|
|
440
|
-
* @param {object} args.model - Model schema
|
|
441
|
-
* @param {object} [args.where] - Object representing the where query
|
|
442
|
-
* @param {boolean} [args.returnRecords=true] - Determines if inserted records should be returned
|
|
443
|
-
* @param {string[]} [args.returnSelect] - Array of model property names to return from the query.
|
|
444
|
-
* @returns {{query: string, params: object[]}}
|
|
445
|
-
*/
|
|
446
|
-
function getDeleteQueryAndParams({ repositoriesByModelNameLowered, model, where, returnRecords = true, returnSelect, }) {
|
|
447
|
-
let query = `DELETE FROM "${model.tableName}"`;
|
|
448
|
-
const { whereStatement, params } = buildWhereStatement({
|
|
449
|
-
repositoriesByModelNameLowered,
|
|
450
|
-
model,
|
|
451
|
-
where,
|
|
452
|
-
});
|
|
453
|
-
if (whereStatement) {
|
|
454
|
-
query += ` ${whereStatement}`;
|
|
455
|
-
}
|
|
456
|
-
if (returnRecords) {
|
|
457
|
-
query += ' RETURNING ';
|
|
458
|
-
query += getColumnsToSelect({
|
|
459
|
-
model,
|
|
460
|
-
select: returnSelect,
|
|
461
|
-
});
|
|
462
|
-
}
|
|
463
|
-
return {
|
|
464
|
-
query,
|
|
465
|
-
params,
|
|
466
|
-
};
|
|
467
|
-
}
|
|
468
|
-
exports.getDeleteQueryAndParams = getDeleteQueryAndParams;
|
|
469
|
-
/**
|
|
470
|
-
* Gets SQL representing columns to select
|
|
471
|
-
* @param {object} args - Arguments
|
|
472
|
-
* @param {object} args.model - Model schema
|
|
473
|
-
* @param {string[]} [args.select] - Array of model property names to return from the query.
|
|
474
|
-
* @returns {string} SQL columns
|
|
475
|
-
* @private
|
|
476
|
-
*/
|
|
477
|
-
function getColumnsToSelect({ model, select, }) {
|
|
478
|
-
let selectColumns;
|
|
479
|
-
if (select) {
|
|
480
|
-
const { primaryKeyColumn } = model;
|
|
481
|
-
selectColumns = new Set(select);
|
|
482
|
-
// Ensure primary key column is specified
|
|
483
|
-
if (primaryKeyColumn) {
|
|
484
|
-
selectColumns.add(primaryKeyColumn.propertyName);
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
else {
|
|
488
|
-
// eslint-disable-next-line no-param-reassign
|
|
489
|
-
selectColumns = new Set();
|
|
490
|
-
for (const column of model.columns) {
|
|
491
|
-
if (!column.collection) {
|
|
492
|
-
selectColumns.add(column.propertyName);
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
let query = '';
|
|
497
|
-
for (const [index, propertyName] of Array.from(selectColumns).entries()) {
|
|
498
|
-
const column = model.columnsByPropertyName[propertyName];
|
|
499
|
-
if (!column) {
|
|
500
|
-
throw new errors_1.QueryError(`Unable to find column for property: ${propertyName} on ${model.tableName}`, model);
|
|
501
|
-
}
|
|
502
|
-
if (index > 0) {
|
|
503
|
-
query += ',';
|
|
504
|
-
}
|
|
505
|
-
if (column.name === propertyName) {
|
|
506
|
-
query += `"${propertyName}"`;
|
|
507
|
-
}
|
|
508
|
-
else {
|
|
509
|
-
query += `"${column.name}" AS "${propertyName}"`;
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
return query;
|
|
513
|
-
}
|
|
514
|
-
exports.getColumnsToSelect = getColumnsToSelect;
|
|
515
|
-
/**
|
|
516
|
-
* Builds the SQL where statement based on the where expression
|
|
517
|
-
* @param {object} args - Arguments
|
|
518
|
-
* @param {object} args.repositoriesByModelNameLowered - All model schemas organized by global id
|
|
519
|
-
* @param {object} args.model - Model schema
|
|
520
|
-
* @param {object} [args.where]
|
|
521
|
-
* @param {(number|string|object|null)[]} [args.params] - Objects to pass as parameters for the query
|
|
522
|
-
* @returns {object} {{whereStatement?: string, params: Array}}
|
|
523
|
-
* @private
|
|
524
|
-
*/
|
|
525
|
-
function buildWhereStatement({ repositoriesByModelNameLowered, model, where, params = [], }) {
|
|
526
|
-
let whereStatement;
|
|
527
|
-
if (_.isObject(where) && Object.keys(where).length) {
|
|
528
|
-
whereStatement = buildWhere({
|
|
529
|
-
repositoriesByModelNameLowered,
|
|
530
|
-
model,
|
|
531
|
-
comparer: 'and',
|
|
532
|
-
value: where,
|
|
533
|
-
params,
|
|
534
|
-
});
|
|
535
|
-
if (!whereStatement) {
|
|
536
|
-
throw new errors_1.QueryError(`WHERE statement is unexpectedly empty.`, model, where);
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
if (whereStatement) {
|
|
540
|
-
whereStatement = `WHERE ${whereStatement}`;
|
|
541
|
-
}
|
|
542
|
-
return {
|
|
543
|
-
whereStatement,
|
|
544
|
-
params,
|
|
545
|
-
};
|
|
546
|
-
}
|
|
547
|
-
exports.buildWhereStatement = buildWhereStatement;
|
|
548
|
-
/**
|
|
549
|
-
* Builds the SQL order by statement based on the array of sortable expressions
|
|
550
|
-
* @param {object} args - Arguments
|
|
551
|
-
* @param {object} args.model - Model schema
|
|
552
|
-
* @param {string[]|object[]} args.sorts - Property name(s) to sort by
|
|
553
|
-
* @returns {string} SQL order by statement
|
|
554
|
-
* @private
|
|
555
|
-
*/
|
|
556
|
-
function buildOrderStatement({ model, sorts }) {
|
|
557
|
-
if (_.isNil(sorts) || !_.some(sorts)) {
|
|
558
|
-
return '';
|
|
559
|
-
}
|
|
560
|
-
let orderStatement = 'ORDER BY ';
|
|
561
|
-
for (const [index, orderProperty] of sorts.entries()) {
|
|
562
|
-
if (index > 0) {
|
|
563
|
-
orderStatement += ',';
|
|
564
|
-
}
|
|
565
|
-
const { propertyName, descending } = orderProperty;
|
|
566
|
-
const column = model.columnsByPropertyName[propertyName];
|
|
567
|
-
if (!column) {
|
|
568
|
-
throw new errors_1.QueryError(`Property (${propertyName}) not found in model (${model.name}).`, model);
|
|
569
|
-
}
|
|
570
|
-
orderStatement += `"${column.name}"`;
|
|
571
|
-
if (descending) {
|
|
572
|
-
orderStatement += ' DESC';
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
return orderStatement;
|
|
576
|
-
}
|
|
577
|
-
exports.buildOrderStatement = buildOrderStatement;
|
|
578
|
-
/**
|
|
579
|
-
* Builds a portion of the where statement based on the propertyName
|
|
580
|
-
* @param {object} args - Arguments
|
|
581
|
-
* @param {object} args.repositoriesByModelNameLowered - All model schemas organized by global id
|
|
582
|
-
* @param {object} args.model - Model schema
|
|
583
|
-
* @param {string} [args.propertyName] - Name of property to query by
|
|
584
|
-
* @param {string} [args.comparer] - Comparison operator
|
|
585
|
-
* @param {boolean} [args.isNegated=false] - If it is negated comparison
|
|
586
|
-
* @param {object|string|number|boolean} [args.value] - Value to compare. Can also represent a complex where query
|
|
587
|
-
* @param {object[]} args.params - Objects to pass as parameters for the query
|
|
588
|
-
* @returns {string} - Query text
|
|
589
|
-
* @private
|
|
590
|
-
*/
|
|
591
|
-
function buildWhere({ repositoriesByModelNameLowered, model, propertyName, comparer, isNegated = false, value, params = [], }) {
|
|
592
|
-
switch (comparer ?? propertyName) {
|
|
593
|
-
case '!':
|
|
594
|
-
case 'not':
|
|
595
|
-
return buildWhere({
|
|
596
|
-
repositoriesByModelNameLowered,
|
|
597
|
-
model,
|
|
598
|
-
propertyName,
|
|
599
|
-
isNegated: true,
|
|
600
|
-
value,
|
|
601
|
-
params,
|
|
602
|
-
});
|
|
603
|
-
case 'or':
|
|
604
|
-
return buildOrOperatorStatement({
|
|
605
|
-
repositoriesByModelNameLowered,
|
|
606
|
-
model,
|
|
607
|
-
isNegated,
|
|
608
|
-
value: value,
|
|
609
|
-
params,
|
|
610
|
-
});
|
|
611
|
-
case 'contains':
|
|
612
|
-
if (_.isArray(value)) {
|
|
613
|
-
const values = value.map((val) => {
|
|
614
|
-
if (!_.isString(val)) {
|
|
615
|
-
throw new errors_1.QueryError(`Expected all array values to be strings for "contains" constraint. Property (${propertyName ?? ''}) in model (${model.name}).`, model);
|
|
616
|
-
}
|
|
617
|
-
return `%${val}%`;
|
|
618
|
-
});
|
|
619
|
-
return buildWhere({
|
|
620
|
-
repositoriesByModelNameLowered,
|
|
621
|
-
model,
|
|
622
|
-
propertyName,
|
|
623
|
-
comparer: 'like',
|
|
624
|
-
isNegated,
|
|
625
|
-
value: values,
|
|
626
|
-
params,
|
|
627
|
-
});
|
|
628
|
-
}
|
|
629
|
-
if (_.isString(value)) {
|
|
630
|
-
return buildWhere({
|
|
631
|
-
repositoriesByModelNameLowered,
|
|
632
|
-
model,
|
|
633
|
-
propertyName,
|
|
634
|
-
comparer: 'like',
|
|
635
|
-
isNegated,
|
|
636
|
-
value: `%${value}%`,
|
|
637
|
-
params,
|
|
638
|
-
});
|
|
639
|
-
}
|
|
640
|
-
throw new errors_1.QueryError(`Expected value to be a string for "contains" constraint. Property (${propertyName ?? ''}) in model (${model.name}).`, model);
|
|
641
|
-
case 'startsWith':
|
|
642
|
-
if (_.isArray(value)) {
|
|
643
|
-
const values = value.map((val) => {
|
|
644
|
-
if (!_.isString(val)) {
|
|
645
|
-
throw new errors_1.QueryError(`Expected all array values to be strings for "startsWith" constraint. Property (${propertyName ?? ''}) in model (${model.name}).`, model);
|
|
646
|
-
}
|
|
647
|
-
return `${val}%`;
|
|
648
|
-
});
|
|
649
|
-
return buildWhere({
|
|
650
|
-
repositoriesByModelNameLowered,
|
|
651
|
-
model,
|
|
652
|
-
propertyName,
|
|
653
|
-
comparer: 'like',
|
|
654
|
-
isNegated,
|
|
655
|
-
value: values,
|
|
656
|
-
params,
|
|
657
|
-
});
|
|
658
|
-
}
|
|
659
|
-
if (_.isString(value)) {
|
|
660
|
-
return buildWhere({
|
|
661
|
-
repositoriesByModelNameLowered,
|
|
662
|
-
model,
|
|
663
|
-
propertyName,
|
|
664
|
-
comparer: 'like',
|
|
665
|
-
isNegated,
|
|
666
|
-
value: `${value}%`,
|
|
667
|
-
params,
|
|
668
|
-
});
|
|
669
|
-
}
|
|
670
|
-
throw new errors_1.QueryError(`Expected value to be a string for "startsWith" constraint. Property (${propertyName ?? ''}) in model (${model.name}).`, model);
|
|
671
|
-
case 'endsWith':
|
|
672
|
-
if (_.isArray(value)) {
|
|
673
|
-
const values = value.map((val) => {
|
|
674
|
-
if (!_.isString(val)) {
|
|
675
|
-
throw new errors_1.QueryError(`Expected all array values to be strings for "endsWith" constraint. Property (${propertyName ?? ''}) in model (${model.name}).`, model);
|
|
676
|
-
}
|
|
677
|
-
return `%${val}`;
|
|
678
|
-
});
|
|
679
|
-
return buildWhere({
|
|
680
|
-
repositoriesByModelNameLowered,
|
|
681
|
-
model,
|
|
682
|
-
propertyName,
|
|
683
|
-
comparer: 'like',
|
|
684
|
-
isNegated,
|
|
685
|
-
value: values,
|
|
686
|
-
params,
|
|
687
|
-
});
|
|
688
|
-
}
|
|
689
|
-
if (_.isString(value)) {
|
|
690
|
-
return buildWhere({
|
|
691
|
-
repositoriesByModelNameLowered,
|
|
692
|
-
model,
|
|
693
|
-
propertyName,
|
|
694
|
-
comparer: 'like',
|
|
695
|
-
isNegated,
|
|
696
|
-
value: `%${value}`,
|
|
697
|
-
params,
|
|
698
|
-
});
|
|
699
|
-
}
|
|
700
|
-
throw new errors_1.QueryError(`Expected value to be a string for "endsWith" constraint. Property (${propertyName ?? ''}) in model (${model.name}).`, model);
|
|
701
|
-
case 'like':
|
|
702
|
-
return buildLikeOperatorStatement({
|
|
703
|
-
model,
|
|
704
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
705
|
-
propertyName: propertyName,
|
|
706
|
-
isNegated,
|
|
707
|
-
value: value,
|
|
708
|
-
params,
|
|
709
|
-
});
|
|
710
|
-
default: {
|
|
711
|
-
if (_.isUndefined(value)) {
|
|
712
|
-
throw new errors_1.QueryError(`Attempting to query with an undefined value. ${propertyName ?? ''} on ${model.name}`, model);
|
|
713
|
-
}
|
|
714
|
-
if (propertyName) {
|
|
715
|
-
const column = model.columnsByPropertyName[propertyName];
|
|
716
|
-
if (column && _.isObject(value)) {
|
|
717
|
-
if (column.primary) {
|
|
718
|
-
const primaryKeyValue = value[column.propertyName];
|
|
719
|
-
if (!_.isNil(primaryKeyValue)) {
|
|
720
|
-
// Treat `value` as a hydrated object
|
|
721
|
-
return buildWhere({
|
|
722
|
-
repositoriesByModelNameLowered,
|
|
723
|
-
model,
|
|
724
|
-
propertyName,
|
|
725
|
-
comparer,
|
|
726
|
-
isNegated,
|
|
727
|
-
value: primaryKeyValue,
|
|
728
|
-
params,
|
|
729
|
-
});
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
else if (column.model) {
|
|
733
|
-
const relatedModelRepository = repositoriesByModelNameLowered[column.model.toLowerCase()];
|
|
734
|
-
if (!relatedModelRepository) {
|
|
735
|
-
throw new errors_1.QueryError(`Unable to find model schema (${column.model}) specified in where clause for "${column.propertyName}"`, model);
|
|
736
|
-
}
|
|
737
|
-
const relatedModelPrimaryKey = relatedModelRepository.model.primaryKeyColumn;
|
|
738
|
-
if (!relatedModelPrimaryKey) {
|
|
739
|
-
throw new errors_1.QueryError(`Unable to find primary key column for ${column.model} specified in where clause for ${model.name}.${column.propertyName}`, model);
|
|
740
|
-
}
|
|
741
|
-
const primaryKeyValue = value[relatedModelPrimaryKey.propertyName];
|
|
742
|
-
if (!_.isNil(primaryKeyValue)) {
|
|
743
|
-
// Treat `value` as a hydrated object
|
|
744
|
-
return buildWhere({
|
|
745
|
-
repositoriesByModelNameLowered,
|
|
746
|
-
model,
|
|
747
|
-
propertyName,
|
|
748
|
-
comparer,
|
|
749
|
-
isNegated,
|
|
750
|
-
value: primaryKeyValue,
|
|
751
|
-
params,
|
|
752
|
-
});
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
if (_.isArray(value)) {
|
|
758
|
-
if (!value.length) {
|
|
759
|
-
const columnTypeFromPropertyName = propertyName ? model.columnsByPropertyName[propertyName] : null;
|
|
760
|
-
const columnTypeFromComparer = comparer ? model.columnsByPropertyName[comparer] : null;
|
|
761
|
-
const arrayColumn = columnTypeFromPropertyName ?? columnTypeFromComparer;
|
|
762
|
-
if (arrayColumn) {
|
|
763
|
-
const arrayColumnType = arrayColumn.type ? arrayColumn.type.toLowerCase() : '';
|
|
764
|
-
if (arrayColumnType === 'array' || arrayColumnType === 'string[]' || arrayColumnType === 'integer[]' || arrayColumnType === 'float[]' || arrayColumnType === 'boolean[]') {
|
|
765
|
-
return `"${arrayColumn.name}"${isNegated ? '<>' : '='}'{}'`;
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
if (isNegated) {
|
|
769
|
-
return '1=1';
|
|
770
|
-
}
|
|
771
|
-
return '1<>1';
|
|
772
|
-
}
|
|
773
|
-
const orConstraints = [];
|
|
774
|
-
const valueWithoutNull = [];
|
|
775
|
-
for (const item of value) {
|
|
776
|
-
if (_.isNull(item)) {
|
|
777
|
-
orConstraints.push(buildWhere({
|
|
778
|
-
repositoriesByModelNameLowered,
|
|
779
|
-
model,
|
|
780
|
-
propertyName,
|
|
781
|
-
isNegated,
|
|
782
|
-
value: null,
|
|
783
|
-
params,
|
|
784
|
-
}));
|
|
785
|
-
}
|
|
786
|
-
else if (item === '') {
|
|
787
|
-
orConstraints.push(buildWhere({
|
|
788
|
-
repositoriesByModelNameLowered,
|
|
789
|
-
model,
|
|
790
|
-
propertyName,
|
|
791
|
-
isNegated,
|
|
792
|
-
value: '',
|
|
793
|
-
params,
|
|
794
|
-
}));
|
|
795
|
-
}
|
|
796
|
-
else {
|
|
797
|
-
valueWithoutNull.push(item);
|
|
798
|
-
}
|
|
799
|
-
}
|
|
800
|
-
if (valueWithoutNull.length === 1) {
|
|
801
|
-
orConstraints.push(buildWhere({
|
|
802
|
-
repositoriesByModelNameLowered,
|
|
803
|
-
model,
|
|
804
|
-
propertyName,
|
|
805
|
-
isNegated,
|
|
806
|
-
value: valueWithoutNull[0],
|
|
807
|
-
params,
|
|
808
|
-
}));
|
|
809
|
-
}
|
|
810
|
-
else if (valueWithoutNull.length) {
|
|
811
|
-
const columnTypeFromPropertyName = propertyName ? model.columnsByPropertyName[propertyName] : null;
|
|
812
|
-
const columnTypeFromComparer = comparer ? model.columnsByPropertyName[comparer] : null;
|
|
813
|
-
const columnType = columnTypeFromPropertyName ?? columnTypeFromComparer;
|
|
814
|
-
if (columnType) {
|
|
815
|
-
let columnTypeLowered = columnType.type ? columnType.type.toLowerCase() : '';
|
|
816
|
-
if (columnTypeLowered === 'array' || columnTypeLowered === 'string[]' || columnTypeLowered === 'integer[]' || columnTypeLowered === 'float[]' || columnTypeLowered === 'boolean[]') {
|
|
817
|
-
for (const val of valueWithoutNull) {
|
|
818
|
-
orConstraints.push(buildWhere({
|
|
819
|
-
repositoriesByModelNameLowered,
|
|
820
|
-
model,
|
|
821
|
-
propertyName,
|
|
822
|
-
isNegated,
|
|
823
|
-
value: val,
|
|
824
|
-
params,
|
|
825
|
-
}));
|
|
826
|
-
}
|
|
827
|
-
}
|
|
828
|
-
else {
|
|
829
|
-
// If it's an array of values for a model relationship, try to find the type of the primary key for the related model
|
|
830
|
-
if (!columnTypeLowered) {
|
|
831
|
-
const columnAsModelType = columnType;
|
|
832
|
-
if (columnAsModelType.model) {
|
|
833
|
-
const relatedModelRepository = repositoriesByModelNameLowered[columnAsModelType.model.toLowerCase()];
|
|
834
|
-
if (!relatedModelRepository) {
|
|
835
|
-
throw new errors_1.QueryError(`Unable to find model schema (${columnAsModelType.model}) specified in where clause for "${columnAsModelType.propertyName}"`, model);
|
|
836
|
-
}
|
|
837
|
-
const relatedModelPrimaryKey = relatedModelRepository.model.primaryKeyColumn;
|
|
838
|
-
if (!relatedModelPrimaryKey) {
|
|
839
|
-
throw new errors_1.QueryError(`Unable to find primary key column for ${columnAsModelType.model} specified in where clause for ${model.name}.${columnAsModelType.propertyName}`, model);
|
|
840
|
-
}
|
|
841
|
-
columnTypeLowered = relatedModelPrimaryKey.type ? relatedModelPrimaryKey.type.toLowerCase() : '';
|
|
842
|
-
}
|
|
843
|
-
}
|
|
844
|
-
let castType;
|
|
845
|
-
switch (columnTypeLowered) {
|
|
846
|
-
case 'int':
|
|
847
|
-
case 'integer':
|
|
848
|
-
case 'integer[]':
|
|
849
|
-
castType = '::INTEGER[]';
|
|
850
|
-
break;
|
|
851
|
-
case 'float':
|
|
852
|
-
case 'float[]':
|
|
853
|
-
castType = '::NUMERIC[]';
|
|
854
|
-
break;
|
|
855
|
-
case 'boolean':
|
|
856
|
-
case 'boolean[]':
|
|
857
|
-
castType = '::BOOLEAN[]';
|
|
858
|
-
break;
|
|
859
|
-
default:
|
|
860
|
-
castType = '::TEXT[]';
|
|
861
|
-
break;
|
|
862
|
-
}
|
|
863
|
-
params.push(valueWithoutNull);
|
|
864
|
-
orConstraints.push(`"${columnType.name}"${isNegated ? '<>ALL' : '=ANY'}($${params.length}${castType})`);
|
|
865
|
-
}
|
|
866
|
-
}
|
|
867
|
-
// TODO: Validate that when columnType is null, it is expected to return existing constraints
|
|
868
|
-
}
|
|
869
|
-
if (orConstraints.length === 1) {
|
|
870
|
-
return orConstraints[0] ?? '';
|
|
871
|
-
}
|
|
872
|
-
if (isNegated) {
|
|
873
|
-
return orConstraints.join(' AND ');
|
|
874
|
-
}
|
|
875
|
-
if (orConstraints.length) {
|
|
876
|
-
return `(${orConstraints.join(' OR ')})`;
|
|
877
|
-
}
|
|
878
|
-
return '';
|
|
879
|
-
}
|
|
880
|
-
if (_.isObject(value) && !_.isDate(value)) {
|
|
881
|
-
const andValues = [];
|
|
882
|
-
for (const [key, where] of Object.entries(value)) {
|
|
883
|
-
// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
|
|
884
|
-
let subQueryComparer;
|
|
885
|
-
if (isComparer(key)) {
|
|
886
|
-
subQueryComparer = key;
|
|
887
|
-
}
|
|
888
|
-
else {
|
|
889
|
-
// eslint-disable-next-line no-param-reassign
|
|
890
|
-
propertyName = key;
|
|
891
|
-
}
|
|
892
|
-
andValues.push(buildWhere({
|
|
893
|
-
repositoriesByModelNameLowered,
|
|
894
|
-
model,
|
|
895
|
-
propertyName,
|
|
896
|
-
comparer: subQueryComparer,
|
|
897
|
-
isNegated,
|
|
898
|
-
value: where,
|
|
899
|
-
params,
|
|
900
|
-
}));
|
|
901
|
-
}
|
|
902
|
-
return andValues.join(' AND ');
|
|
903
|
-
}
|
|
904
|
-
return buildComparisonOperatorStatement({
|
|
905
|
-
model,
|
|
906
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
907
|
-
propertyName: propertyName,
|
|
908
|
-
comparer,
|
|
909
|
-
isNegated,
|
|
910
|
-
value: value,
|
|
911
|
-
params,
|
|
912
|
-
});
|
|
913
|
-
}
|
|
914
|
-
}
|
|
915
|
-
}
|
|
916
|
-
function buildOrOperatorStatement({ repositoriesByModelNameLowered, model, isNegated, value, params = [], }) {
|
|
917
|
-
const orClauses = [];
|
|
918
|
-
for (const constraint of value) {
|
|
919
|
-
const orClause = buildWhere({
|
|
920
|
-
repositoriesByModelNameLowered,
|
|
921
|
-
model,
|
|
922
|
-
isNegated,
|
|
923
|
-
value: constraint,
|
|
924
|
-
params,
|
|
925
|
-
});
|
|
926
|
-
if (orClause) {
|
|
927
|
-
orClauses.push(`(${orClause})`);
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
if (orClauses.length === 1) {
|
|
931
|
-
return orClauses[0] ?? '';
|
|
932
|
-
}
|
|
933
|
-
if (isNegated) {
|
|
934
|
-
return orClauses.join(' AND ');
|
|
935
|
-
}
|
|
936
|
-
if (orClauses.length) {
|
|
937
|
-
return `(${orClauses.join(' OR ')})`;
|
|
938
|
-
}
|
|
939
|
-
return '';
|
|
940
|
-
}
|
|
941
|
-
function buildLikeOperatorStatement({ model, propertyName, isNegated, value, params }) {
|
|
942
|
-
if (_.isArray(value)) {
|
|
943
|
-
if (!value.length) {
|
|
944
|
-
if (isNegated) {
|
|
945
|
-
return '1=1';
|
|
946
|
-
}
|
|
947
|
-
return '1<>1';
|
|
948
|
-
}
|
|
949
|
-
if (value.length > 1) {
|
|
950
|
-
const orConstraints = [];
|
|
951
|
-
for (const item of value) {
|
|
952
|
-
if (_.isNull(item)) {
|
|
953
|
-
orConstraints.push(buildLikeOperatorStatement({
|
|
954
|
-
model,
|
|
955
|
-
propertyName,
|
|
956
|
-
isNegated,
|
|
957
|
-
value: null,
|
|
958
|
-
params,
|
|
959
|
-
}));
|
|
960
|
-
}
|
|
961
|
-
else if (item === '') {
|
|
962
|
-
orConstraints.push(buildLikeOperatorStatement({
|
|
963
|
-
model,
|
|
964
|
-
propertyName,
|
|
965
|
-
isNegated,
|
|
966
|
-
value: '',
|
|
967
|
-
params,
|
|
968
|
-
}));
|
|
969
|
-
}
|
|
970
|
-
else {
|
|
971
|
-
orConstraints.push(buildLikeOperatorStatement({
|
|
972
|
-
model,
|
|
973
|
-
propertyName,
|
|
974
|
-
isNegated,
|
|
975
|
-
value: item,
|
|
976
|
-
params,
|
|
977
|
-
}));
|
|
978
|
-
}
|
|
979
|
-
}
|
|
980
|
-
if (orConstraints.length === 1) {
|
|
981
|
-
return orConstraints[0] ?? '';
|
|
982
|
-
}
|
|
983
|
-
if (isNegated) {
|
|
984
|
-
return orConstraints.join(' AND ');
|
|
985
|
-
}
|
|
986
|
-
if (orConstraints.length) {
|
|
987
|
-
return `(${orConstraints.join(' OR ')})`;
|
|
988
|
-
}
|
|
989
|
-
return '';
|
|
990
|
-
}
|
|
991
|
-
// eslint-disable-next-line no-param-reassign
|
|
992
|
-
value = value[0];
|
|
993
|
-
}
|
|
994
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
995
|
-
const column = model.columnsByPropertyName[propertyName];
|
|
996
|
-
if (!column) {
|
|
997
|
-
throw new errors_1.QueryError(`Unable to find property ${propertyName} on model ${model.name}`, model);
|
|
998
|
-
}
|
|
999
|
-
if (_.isNull(value)) {
|
|
1000
|
-
return `"${column.name}" ${isNegated ? 'IS NOT' : 'IS'} NULL`;
|
|
1001
|
-
}
|
|
1002
|
-
if (_.isString(value)) {
|
|
1003
|
-
if (value) {
|
|
1004
|
-
// NOTE: This is doing a case-insensitive pattern match
|
|
1005
|
-
params.push(value);
|
|
1006
|
-
const columnType = column.type?.toLowerCase();
|
|
1007
|
-
if (columnType === 'array' || columnType === 'string[]') {
|
|
1008
|
-
return `${isNegated ? 'NOT ' : ''}EXISTS(SELECT 1 FROM (SELECT unnest("${column.name}") AS "unnested_${column.name}") __unnested WHERE "unnested_${column.name}" ILIKE $${params.length})`;
|
|
1009
|
-
}
|
|
1010
|
-
return `"${column.name}"${isNegated ? ' NOT' : ''} ILIKE $${params.length}`;
|
|
1011
|
-
}
|
|
1012
|
-
return `"${column.name}" ${isNegated ? '!=' : '='} ''`;
|
|
1013
|
-
}
|
|
1014
|
-
throw new errors_1.QueryError(`Expected value to be a string for "like" constraint. Property (${propertyName}) in model (${model.name}).`, model);
|
|
1015
|
-
}
|
|
1016
|
-
function buildComparisonOperatorStatement({ model, propertyName, comparer, isNegated, value, params = [] }) {
|
|
1017
|
-
const column = model.columnsByPropertyName[propertyName];
|
|
1018
|
-
if (!column) {
|
|
1019
|
-
throw new errors_1.QueryError(`Unable to find property ${propertyName} on model ${model.name}`, model);
|
|
1020
|
-
}
|
|
1021
|
-
if (_.isNull(value)) {
|
|
1022
|
-
return `"${column.name}" ${isNegated ? 'IS NOT' : 'IS'} NULL`;
|
|
1023
|
-
}
|
|
1024
|
-
params.push(value);
|
|
1025
|
-
const columnType = column.type;
|
|
1026
|
-
const supportsLessThanGreaterThan = columnType !== 'array' && columnType !== 'json';
|
|
1027
|
-
switch (comparer) {
|
|
1028
|
-
case '<':
|
|
1029
|
-
if (!supportsLessThanGreaterThan) {
|
|
1030
|
-
throw new errors_1.QueryError(`< operator is not supported for ${columnType || 'unknown'} type. ${propertyName || ''} on ${model.name}`, model);
|
|
1031
|
-
}
|
|
1032
|
-
return `"${column.name}"${isNegated ? '>=' : '<'}$${params.length}`;
|
|
1033
|
-
case '<=':
|
|
1034
|
-
if (!supportsLessThanGreaterThan) {
|
|
1035
|
-
throw new errors_1.QueryError(`<= operator is not supported for ${columnType || 'unknown'} type. ${propertyName || ''} on ${model.name}`, model);
|
|
1036
|
-
}
|
|
1037
|
-
return `"${column.name}"${isNegated ? '>' : '<='}$${params.length}`;
|
|
1038
|
-
case '>':
|
|
1039
|
-
if (!supportsLessThanGreaterThan) {
|
|
1040
|
-
throw new errors_1.QueryError(`> operator is not supported for ${columnType || 'unknown'} type. ${propertyName || ''} on ${model.name}`, model);
|
|
1041
|
-
}
|
|
1042
|
-
return `"${column.name}"${isNegated ? '<=' : '>'}$${params.length}`;
|
|
1043
|
-
case '>=':
|
|
1044
|
-
if (!supportsLessThanGreaterThan) {
|
|
1045
|
-
throw new errors_1.QueryError(`>= operator is not supported for ${columnType || 'unknown'} type. ${propertyName || ''} on ${model.name}`, model);
|
|
1046
|
-
}
|
|
1047
|
-
return `"${column.name}"${isNegated ? '<' : '>='}$${params.length}`;
|
|
1048
|
-
default:
|
|
1049
|
-
if (columnType && (columnType === 'array' || columnType.endsWith('[]'))) {
|
|
1050
|
-
return `$${params.length}${isNegated ? '<>ALL(' : '=ANY('}"${column.name}")`;
|
|
1051
|
-
}
|
|
1052
|
-
return `"${column.name}"${isNegated ? '<>' : '='}$${params.length}`;
|
|
1053
|
-
}
|
|
1054
|
-
}
|
|
1055
|
-
/**
|
|
1056
|
-
* Determines if the specified value is a comparer
|
|
1057
|
-
* @param {string} value
|
|
1058
|
-
* @returns {boolean}
|
|
1059
|
-
* @private
|
|
1060
|
-
*/
|
|
1061
|
-
function isComparer(value) {
|
|
1062
|
-
switch (value) {
|
|
1063
|
-
case '!':
|
|
1064
|
-
case 'not':
|
|
1065
|
-
case 'or':
|
|
1066
|
-
case 'and':
|
|
1067
|
-
case 'contains':
|
|
1068
|
-
case 'startsWith':
|
|
1069
|
-
case 'endsWith':
|
|
1070
|
-
case 'like':
|
|
1071
|
-
case '<':
|
|
1072
|
-
case '<=':
|
|
1073
|
-
case '>':
|
|
1074
|
-
case '>=':
|
|
1075
|
-
return true;
|
|
1076
|
-
default:
|
|
1077
|
-
return false;
|
|
1078
|
-
}
|
|
1079
|
-
}
|
|
1080
|
-
/* eslint-enable @typescript-eslint/no-use-before-define */
|
|
1081
|
-
//# sourceMappingURL=SqlHelper.js.map
|