masterrecord 0.2.34 → 0.3.0
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/.claude/settings.local.json +25 -1
- package/Entity/entityModel.js +6 -0
- package/Entity/entityTrackerModel.js +20 -3
- package/Entity/fieldTransformer.js +266 -0
- package/Migrations/migrationMySQLQuery.js +145 -1
- package/Migrations/migrationPostgresQuery.js +402 -0
- package/Migrations/migrationSQLiteQuery.js +145 -1
- package/Migrations/schema.js +131 -28
- package/QueryLanguage/queryMethods.js +193 -15
- package/QueryLanguage/queryParameters.js +136 -0
- package/QueryLanguage/queryScript.js +14 -5
- package/SQLLiteEngine.js +309 -19
- package/context.js +57 -12
- package/docs/INCLUDES_CLARIFICATION.md +202 -0
- package/docs/METHODS_REFERENCE.md +184 -0
- package/docs/MIGRATIONS_GUIDE.md +699 -0
- package/docs/POSTGRESQL_SETUP.md +415 -0
- package/examples/jsonArrayTransformer.js +215 -0
- package/mySQLEngine.js +249 -17
- package/package.json +6 -6
- package/postgresEngine.js +434 -491
- package/postgresSyncConnect.js +209 -0
- package/readme.md +1121 -265
- package/test/anyCommaStringTest.js +237 -0
- package/test/anyMethodTest.js +176 -0
- package/test/findByIdTest.js +227 -0
- package/test/includesFeatureTest.js +183 -0
- package/test/includesTransformTest.js +110 -0
- package/test/newMethodTest.js +330 -0
- package/test/newMethodUnitTest.js +320 -0
- package/test/parameterizedPlaceholderTest.js +159 -0
- package/test/postgresEngineTest.js +463 -0
- package/test/postgresIntegrationTest.js +381 -0
- package/test/securityTest.js +268 -0
- package/test/singleDollarPlaceholderTest.js +238 -0
- package/test/tablePrefixTest.js +100 -0
- package/test/transformerTest.js +287 -0
- package/test/verifyFindById.js +169 -0
- package/test/verifyNewMethod.js +191 -0
- package/test/whereChainingTest.js +88 -0
package/postgresEngine.js
CHANGED
|
@@ -1,618 +1,561 @@
|
|
|
1
|
-
// Version 0.0.
|
|
2
|
-
|
|
1
|
+
// Version 0.1.0 - Complete PostgreSQL implementation with pg 8.16.3
|
|
2
|
+
const tools = require('./Tools');
|
|
3
|
+
const FieldTransformer = require('./Entity/fieldTransformer');
|
|
4
|
+
const { Pool } = require('pg');
|
|
3
5
|
|
|
4
6
|
class postgresEngine {
|
|
5
7
|
|
|
6
|
-
|
|
8
|
+
constructor() {
|
|
9
|
+
this.pool = null;
|
|
10
|
+
this.db = null;
|
|
11
|
+
this.dbType = 'postgres';
|
|
12
|
+
this.unsupportedWords = ["order"];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Initialize PostgreSQL connection pool
|
|
17
|
+
* @param {Object} config - PostgreSQL connection config
|
|
18
|
+
*/
|
|
19
|
+
async initialize(config) {
|
|
20
|
+
this.pool = new Pool({
|
|
21
|
+
host: config.host || 'localhost',
|
|
22
|
+
port: config.port || 5432,
|
|
23
|
+
database: config.database,
|
|
24
|
+
user: config.user,
|
|
25
|
+
password: config.password,
|
|
26
|
+
max: config.max || 20,
|
|
27
|
+
idleTimeoutMillis: config.idleTimeoutMillis || 30000,
|
|
28
|
+
connectionTimeoutMillis: config.connectionTimeoutMillis || 2000,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// Test connection
|
|
32
|
+
try {
|
|
33
|
+
const client = await this.pool.connect();
|
|
34
|
+
console.log('PostgreSQL connected successfully');
|
|
35
|
+
client.release();
|
|
36
|
+
} catch (err) {
|
|
37
|
+
console.error('PostgreSQL connection error:', err);
|
|
38
|
+
throw err;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
7
41
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
42
|
+
/**
|
|
43
|
+
* UPDATE with parameterized query
|
|
44
|
+
*/
|
|
45
|
+
async update(query) {
|
|
46
|
+
if (query.arg && query.arg.query && query.arg.params) {
|
|
47
|
+
// Parameterized UPDATE
|
|
48
|
+
const params = [...query.arg.params, query.primaryKeyValue];
|
|
49
|
+
return await this._runWithParams(query.arg.query, params);
|
|
50
|
+
} else {
|
|
51
|
+
// Fallback for legacy support
|
|
52
|
+
const sqlQuery = `UPDATE ${query.tableName} SET ${query.arg} WHERE ${query.tableName}.${query.primaryKey} = $1`;
|
|
53
|
+
return await this._runWithParams(sqlQuery, [query.primaryKeyValue]);
|
|
54
|
+
}
|
|
13
55
|
}
|
|
14
56
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
57
|
+
/**
|
|
58
|
+
* DELETE with parameterized query
|
|
59
|
+
*/
|
|
60
|
+
async delete(queryObject) {
|
|
61
|
+
const sqlObject = this._buildDeleteObject(queryObject);
|
|
62
|
+
const sqlQuery = `DELETE FROM ${sqlObject.tableName} WHERE ${sqlObject.tableName}.${sqlObject.primaryKey} = $1`;
|
|
63
|
+
return await this._runWithParams(sqlQuery, [sqlObject.value]);
|
|
19
64
|
}
|
|
20
65
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
66
|
+
/**
|
|
67
|
+
* INSERT with parameterized query
|
|
68
|
+
* Postgres uses RETURNING to get the inserted ID
|
|
69
|
+
*/
|
|
70
|
+
async insert(queryObject) {
|
|
71
|
+
const sqlObject = this._buildSQLInsertObjectParameterized(queryObject, queryObject.__entity);
|
|
72
|
+
if (sqlObject === -1) {
|
|
73
|
+
throw new Error('INSERT failed: No columns to insert');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Get primary key name for RETURNING clause
|
|
77
|
+
const primaryKey = tools.getPrimaryKeyObject(queryObject.__entity);
|
|
78
|
+
const query = `INSERT INTO ${sqlObject.tableName} (${sqlObject.columns}) VALUES (${sqlObject.placeholders}) RETURNING ${primaryKey}`;
|
|
79
|
+
|
|
80
|
+
const result = await this._runWithParams(query, sqlObject.params);
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
id: result.rows[0][primaryKey]
|
|
28
84
|
};
|
|
29
|
-
return open;
|
|
30
85
|
}
|
|
31
86
|
|
|
32
|
-
|
|
33
|
-
|
|
87
|
+
/**
|
|
88
|
+
* SELECT single record
|
|
89
|
+
*/
|
|
90
|
+
async get(query, entity, context) {
|
|
34
91
|
try {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
else{
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
else{
|
|
43
|
-
queryString = this.buildQuery(query, entity, context);
|
|
44
|
-
}
|
|
92
|
+
let queryString;
|
|
93
|
+
if (query.raw) {
|
|
94
|
+
queryString = { query: query.raw, params: [] };
|
|
95
|
+
} else if (typeof query === 'string') {
|
|
96
|
+
queryString = { query: query, params: [] };
|
|
97
|
+
} else {
|
|
98
|
+
queryString = this.buildQuery(query, entity, context);
|
|
45
99
|
}
|
|
46
|
-
|
|
100
|
+
|
|
101
|
+
if (queryString.query) {
|
|
47
102
|
console.log("SQL:", queryString.query);
|
|
48
|
-
|
|
49
|
-
|
|
103
|
+
console.log("Params:", queryString.params || []);
|
|
104
|
+
const result = await this._runWithParams(queryString.query, queryString.params || []);
|
|
105
|
+
return result.rows[0] || null;
|
|
50
106
|
}
|
|
51
107
|
return null;
|
|
52
108
|
} catch (err) {
|
|
53
|
-
console.error(err);
|
|
109
|
+
console.error('PostgreSQL get error:', err);
|
|
54
110
|
return null;
|
|
55
111
|
}
|
|
56
112
|
}
|
|
57
113
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
114
|
+
/**
|
|
115
|
+
* SELECT COUNT
|
|
116
|
+
*/
|
|
117
|
+
async getCount(queryObject, entity, context) {
|
|
118
|
+
const query = queryObject.script;
|
|
61
119
|
try {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
else{
|
|
66
|
-
if(query.count === undefined){
|
|
120
|
+
let queryString;
|
|
121
|
+
if (query.raw) {
|
|
122
|
+
queryString = { query: query.raw, params: [] };
|
|
123
|
+
} else {
|
|
124
|
+
if (query.count === undefined) {
|
|
67
125
|
query.count = "none";
|
|
68
126
|
}
|
|
69
|
-
|
|
70
|
-
queryString
|
|
127
|
+
const entityAlias = this.getEntity(entity.__name, query.entityMap);
|
|
128
|
+
queryString = {
|
|
129
|
+
query: `SELECT ${this.buildCount(query, entity)} ${this.buildFrom(query, entity)} ${this.buildWhere(query, entity)}`,
|
|
130
|
+
params: query.parameters ? query.parameters.getParams() : []
|
|
131
|
+
};
|
|
71
132
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
console.log("SQL:",
|
|
75
|
-
|
|
76
|
-
|
|
133
|
+
|
|
134
|
+
if (queryString.query) {
|
|
135
|
+
console.log("SQL:", queryString.query);
|
|
136
|
+
console.log("Params:", queryString.params);
|
|
137
|
+
const result = await this._runWithParams(queryString.query, queryString.params);
|
|
138
|
+
return result.rows[0] || null;
|
|
77
139
|
}
|
|
78
140
|
return null;
|
|
79
141
|
} catch (err) {
|
|
80
|
-
console.error(err);
|
|
142
|
+
console.error('PostgreSQL getCount error:', err);
|
|
81
143
|
return null;
|
|
82
144
|
}
|
|
83
145
|
}
|
|
84
146
|
|
|
85
|
-
|
|
86
|
-
|
|
147
|
+
/**
|
|
148
|
+
* SELECT multiple records
|
|
149
|
+
*/
|
|
150
|
+
async all(query, entity, context) {
|
|
87
151
|
try {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
else{
|
|
92
|
-
|
|
152
|
+
let selectQuery;
|
|
153
|
+
if (query.raw) {
|
|
154
|
+
selectQuery = { query: query.raw, params: [] };
|
|
155
|
+
} else {
|
|
93
156
|
selectQuery = this.buildQuery(query, entity, context);
|
|
94
157
|
}
|
|
95
|
-
|
|
158
|
+
|
|
159
|
+
if (selectQuery.query) {
|
|
96
160
|
console.log("SQL:", selectQuery.query);
|
|
97
|
-
|
|
98
|
-
|
|
161
|
+
console.log("Params:", selectQuery.params || []);
|
|
162
|
+
const result = await this._runWithParams(selectQuery.query, selectQuery.params || []);
|
|
163
|
+
return result.rows || [];
|
|
99
164
|
}
|
|
100
|
-
return
|
|
165
|
+
return [];
|
|
101
166
|
} catch (err) {
|
|
102
|
-
console.error(err);
|
|
103
|
-
return
|
|
167
|
+
console.error('PostgreSQL all error:', err);
|
|
168
|
+
return [];
|
|
104
169
|
}
|
|
105
170
|
}
|
|
106
171
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
whereClaus = query.where.expr.replace("!= null", "is not null");
|
|
113
|
-
}
|
|
114
|
-
query.where.expr = whereClaus;
|
|
115
|
-
}
|
|
116
|
-
|
|
172
|
+
/**
|
|
173
|
+
* Execute raw SQL with parameters
|
|
174
|
+
*/
|
|
175
|
+
async exec(query, params = []) {
|
|
176
|
+
return await this._runWithParams(query, params);
|
|
117
177
|
}
|
|
118
178
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
else{
|
|
126
|
-
return `COUNT(*)`
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
else{
|
|
130
|
-
return ""
|
|
131
|
-
}
|
|
132
|
-
}
|
|
179
|
+
/**
|
|
180
|
+
* Build complete SELECT query with parameters
|
|
181
|
+
*/
|
|
182
|
+
buildQuery(query, entity, context) {
|
|
183
|
+
const entityStr = this.getEntity(entity.__name, query.entityMap);
|
|
184
|
+
const params = query.parameters ? query.parameters.getParams() : [];
|
|
133
185
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
var queryObject = {};
|
|
137
|
-
queryObject.entity = this.getEntity(entity.__name, query.entityMap);
|
|
138
|
-
queryObject.select = this.buildSelect(query, entity);
|
|
139
|
-
queryObject.count = this.buildCount(query, entity);
|
|
140
|
-
queryObject.from = this.buildFrom(query, entity);
|
|
141
|
-
queryObject.include = this.buildInclude(query, entity, context, queryObject);
|
|
142
|
-
queryObject.where = this.buildWhere(query, entity);
|
|
143
|
-
queryObject.and = this.buildAnd(query, entity);
|
|
144
|
-
queryObject.take = this.buildTake(query);
|
|
145
|
-
queryObject.skip = this.buildSkip(query);
|
|
146
|
-
queryObject.orderBy = this.buildOrderBy(query);
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
var queryString = `${queryObject.select} ${queryObject.count} ${queryObject.from} ${queryObject.include} ${queryObject.where} ${queryObject.and} ${queryObject.orderBy} ${queryObject.take} ${queryObject.skip}`;
|
|
150
|
-
return {
|
|
151
|
-
query : queryString,
|
|
152
|
-
entity : this.getEntity(entity.__name, query.entityMap)
|
|
153
|
-
}
|
|
186
|
+
const sql = `SELECT ${this.buildSelectString(query, entity)} ${this.buildFrom(query, entity)} ${this.buildWhere(query, entity)} ${this.buildAnd(query, entity)} ${this.buildLimit(query)} ${this.buildSkip(query)} ${this.buildOrderBy(query)}`;
|
|
154
187
|
|
|
188
|
+
return {
|
|
189
|
+
query: sql,
|
|
190
|
+
params: params
|
|
191
|
+
};
|
|
155
192
|
}
|
|
156
193
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
var orderByType = "ASC";
|
|
161
|
-
var orderByEntity = query.orderBy;
|
|
162
|
-
var strQuery = "";
|
|
163
|
-
if(orderByEntity === false){
|
|
164
|
-
orderByType = "DESC";
|
|
165
|
-
orderByEntity = query.orderByDesc;
|
|
166
|
-
}
|
|
167
|
-
if(orderByEntity){
|
|
168
|
-
var entity = this.getEntity(query.parentName, query.entityMap);
|
|
169
|
-
var fieldList = "";
|
|
170
|
-
for (const item in orderByEntity.selectFields) {
|
|
171
|
-
fieldList += `${entity}.${orderByEntity.selectFields[item]}, `;
|
|
172
|
-
};
|
|
173
|
-
fieldList = fieldList.replace(/,\s*$/, "");
|
|
174
|
-
strQuery = "ORDER BY";
|
|
175
|
-
strQuery += ` ${fieldList} ${orderByType}`;
|
|
194
|
+
buildSelectString(query, entity) {
|
|
195
|
+
if (query.select) {
|
|
196
|
+
return query.select;
|
|
176
197
|
}
|
|
177
|
-
return
|
|
198
|
+
return `${tools.convertEntityToSelectParameterString(entity)}`;
|
|
178
199
|
}
|
|
179
200
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
else{
|
|
185
|
-
return "";
|
|
201
|
+
buildCount(query, entity) {
|
|
202
|
+
const entityStr = this.getEntity(entity.__name, query.entityMap);
|
|
203
|
+
if (query.count === "none") {
|
|
204
|
+
return `COUNT(${entityStr}.*)`;
|
|
186
205
|
}
|
|
206
|
+
return `COUNT(${entityStr}.${query.count})`;
|
|
187
207
|
}
|
|
188
208
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
return `OFFSET ${query.skip}`
|
|
192
|
-
}
|
|
193
|
-
else{
|
|
194
|
-
return "";
|
|
195
|
-
}
|
|
209
|
+
buildFrom(query, entity) {
|
|
210
|
+
return `FROM ${entity.__name}`;
|
|
196
211
|
}
|
|
197
212
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
if(andEntity){
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
+
/**
|
|
214
|
+
* Build AND clause with placeholder detection
|
|
215
|
+
*/
|
|
216
|
+
buildAnd(query, mainQuery) {
|
|
217
|
+
const andEntity = query.and;
|
|
218
|
+
let strQuery = "";
|
|
219
|
+
const $that = this;
|
|
220
|
+
|
|
221
|
+
if (andEntity) {
|
|
222
|
+
const entity = this.getEntity(query.parentName, query.entityMap);
|
|
223
|
+
const andList = [];
|
|
224
|
+
|
|
225
|
+
for (let entityPart in andEntity) {
|
|
226
|
+
const itemEntity = andEntity[entityPart];
|
|
227
|
+
for (let table in itemEntity[query.parentName]) {
|
|
228
|
+
const item = itemEntity[query.parentName][table];
|
|
213
229
|
for (let exp in item.expressions) {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
230
|
+
let field = tools.capitalizeFirstLetter(item.expressions[exp].field);
|
|
231
|
+
let entityRef = entity;
|
|
232
|
+
|
|
233
|
+
if (mainQuery[field] && mainQuery[field].isNavigational) {
|
|
234
|
+
entityRef = $that.getEntity(field, query.entityMap);
|
|
235
|
+
field = item.fields[1];
|
|
220
236
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
237
|
+
|
|
238
|
+
let func = item.expressions[exp].func;
|
|
239
|
+
const arg = item.expressions[exp].arg;
|
|
240
|
+
|
|
241
|
+
// Handle NULL
|
|
242
|
+
if (arg === "null") {
|
|
243
|
+
if (func === "=") func = "IS";
|
|
244
|
+
if (func === "!=") func = "IS NOT";
|
|
228
245
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
246
|
+
|
|
247
|
+
if (strQuery === "") {
|
|
248
|
+
if (arg === "null") {
|
|
249
|
+
strQuery = `${entityRef}.${field} ${func} ${arg}`;
|
|
250
|
+
} else {
|
|
251
|
+
// Check if arg is a parameterized placeholder
|
|
252
|
+
const isPlaceholder = (arg === '?' || /^\$\d+$/.test(arg));
|
|
253
|
+
if (isPlaceholder || func === "IN") {
|
|
254
|
+
strQuery = `${entityRef}.${field} ${func} ${arg}`;
|
|
255
|
+
} else {
|
|
256
|
+
strQuery = `${entityRef}.${field} ${func} '${arg}'`;
|
|
257
|
+
}
|
|
234
258
|
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
259
|
+
} else {
|
|
260
|
+
if (arg === "null") {
|
|
261
|
+
strQuery = `${strQuery} AND ${entityRef}.${field} ${func} ${arg}`;
|
|
262
|
+
} else {
|
|
263
|
+
// Check if arg is a parameterized placeholder
|
|
264
|
+
const isPlaceholder = (arg === '?' || /^\$\d+$/.test(arg));
|
|
265
|
+
if (isPlaceholder || func === "IN") {
|
|
266
|
+
strQuery = `${strQuery} AND ${entityRef}.${field} ${func} ${arg}`;
|
|
267
|
+
} else {
|
|
268
|
+
strQuery = `${strQuery} AND ${entityRef}.${field} ${func} '${arg}'`;
|
|
269
|
+
}
|
|
241
270
|
}
|
|
242
|
-
|
|
243
271
|
}
|
|
244
272
|
}
|
|
245
273
|
andList.push(strQuery);
|
|
246
274
|
}
|
|
247
275
|
}
|
|
276
|
+
|
|
277
|
+
if (andList.length > 0) {
|
|
278
|
+
return `AND ${andList.join(" AND ")}`;
|
|
279
|
+
}
|
|
248
280
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
str = `and ${andList.join(" and ")}`;
|
|
252
|
-
}
|
|
253
|
-
return str
|
|
281
|
+
|
|
282
|
+
return "";
|
|
254
283
|
}
|
|
255
284
|
|
|
256
|
-
|
|
257
|
-
|
|
285
|
+
/**
|
|
286
|
+
* Build WHERE clause with placeholder detection
|
|
287
|
+
*/
|
|
288
|
+
buildWhere(query, mainQuery) {
|
|
289
|
+
const whereEntity = query.where;
|
|
290
|
+
let strQuery = "";
|
|
291
|
+
const $that = this;
|
|
258
292
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
var entity = this.getEntity(query.parentName, query.entityMap);
|
|
293
|
+
if (whereEntity) {
|
|
294
|
+
const entity = this.getEntity(query.parentName, query.entityMap);
|
|
295
|
+
const item = whereEntity[query.parentName].query;
|
|
263
296
|
|
|
264
|
-
var item = whereEntity[query.parentName].query;
|
|
265
297
|
for (let exp in item.expressions) {
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
}
|
|
273
|
-
if(item.expressions[exp].arg === "null"){
|
|
274
|
-
if(item.expressions[exp].func === "="){
|
|
275
|
-
item.expressions[exp].func = "is"
|
|
276
|
-
}
|
|
277
|
-
if(item.expressions[exp].func === "!="){
|
|
278
|
-
item.expressions[exp].func = "is not"
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
if(strQuery === ""){
|
|
282
|
-
if(item.expressions[exp].arg === "null"){
|
|
283
|
-
strQuery = `WHERE ${entity}.${field} ${item.expressions[exp].func} ${item.expressions[exp].arg}`;
|
|
284
|
-
}else{
|
|
285
|
-
if(item.expressions[exp].func === "IN"){
|
|
286
|
-
strQuery = `WHERE ${entity}.${field} ${item.expressions[exp].func} ${item.expressions[exp].arg}`;
|
|
287
|
-
}
|
|
288
|
-
else{
|
|
289
|
-
strQuery = `WHERE ${entity}.${field} ${item.expressions[exp].func} '${item.expressions[exp].arg}'`;
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
else{
|
|
294
|
-
if(item.expressions[exp].arg === "null"){
|
|
295
|
-
strQuery = `${strQuery} and ${entity}.${field} ${item.expressions[exp].func} ${item.expressions[exp].arg}`;
|
|
296
|
-
}else{
|
|
297
|
-
strQuery = `${strQuery} and ${entity}.${field} ${item.expressions[exp].func} '${item.expressions[exp].arg}'`;
|
|
298
|
-
}
|
|
299
|
-
|
|
298
|
+
let field = tools.capitalizeFirstLetter(item.expressions[exp].field);
|
|
299
|
+
let entityRef = entity;
|
|
300
|
+
|
|
301
|
+
if (mainQuery[field] && mainQuery[field].isNavigational) {
|
|
302
|
+
entityRef = $that.getEntity(field, query.entityMap);
|
|
303
|
+
field = item.fields[1];
|
|
300
304
|
}
|
|
301
|
-
}
|
|
302
305
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
}
|
|
306
|
-
return strQuery;
|
|
307
|
-
}
|
|
306
|
+
let func = item.expressions[exp].func;
|
|
307
|
+
const arg = item.expressions[exp].arg;
|
|
308
308
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
var $that = this;
|
|
314
|
-
if(includeEntity){
|
|
315
|
-
var parentObj = includeEntity[query.parentName];
|
|
316
|
-
var currentContext = "";
|
|
317
|
-
if(includeEntity.selectFields){
|
|
318
|
-
currentContext = context[tools.capitalizeFirstLetter(includeEntity.selectFields[0])];
|
|
309
|
+
// Handle NULL
|
|
310
|
+
if (arg === "null") {
|
|
311
|
+
if (func === "=") func = "IS";
|
|
312
|
+
if (func === "!=") func = "IS NOT";
|
|
319
313
|
}
|
|
320
|
-
|
|
321
|
-
if(
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
314
|
+
|
|
315
|
+
if (strQuery === "") {
|
|
316
|
+
if (arg === "null") {
|
|
317
|
+
strQuery = `WHERE ${entityRef}.${field} ${func} ${arg}`;
|
|
318
|
+
} else if (func === "IN") {
|
|
319
|
+
strQuery = `WHERE ${entityRef}.${field} ${func} ${arg}`;
|
|
320
|
+
} else {
|
|
321
|
+
// Check if arg is a parameterized placeholder ($1, $2, etc.)
|
|
322
|
+
const isPlaceholder = (arg === '?' || /^\$\d+$/.test(arg));
|
|
323
|
+
if (isPlaceholder) {
|
|
324
|
+
strQuery = `WHERE ${entityRef}.${field} ${func} ${arg}`;
|
|
325
|
+
} else {
|
|
326
|
+
strQuery = `WHERE ${entityRef}.${field} ${func} '${arg}'`;
|
|
327
|
+
}
|
|
331
328
|
}
|
|
332
|
-
|
|
333
|
-
if(
|
|
334
|
-
|
|
335
|
-
}else{
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
329
|
+
} else {
|
|
330
|
+
if (arg === "null") {
|
|
331
|
+
strQuery = `${strQuery} AND ${entityRef}.${field} ${func} ${arg}`;
|
|
332
|
+
} else if (func === "IN") {
|
|
333
|
+
strQuery = `${strQuery} AND ${entityRef}.${field} ${func} ${arg}`;
|
|
334
|
+
} else {
|
|
335
|
+
// Check if arg is a parameterized placeholder
|
|
336
|
+
const isPlaceholder = (arg === '?' || /^\$\d+$/.test(arg));
|
|
337
|
+
if (isPlaceholder) {
|
|
338
|
+
strQuery = `${strQuery} AND ${entityRef}.${field} ${func} ${arg}`;
|
|
339
|
+
} else {
|
|
340
|
+
strQuery = `${strQuery} AND ${entityRef}.${field} ${func} '${arg}'`;
|
|
341
|
+
}
|
|
339
342
|
}
|
|
340
|
-
|
|
341
|
-
var innerQuery = $that.buildQuery(parentObj, currentContext.__entity, context);
|
|
342
|
-
|
|
343
|
-
includeQuery += `LEFT JOIN (${innerQuery.query}) AS ${innerQuery.entity} ON ${ mainEntity}.${mainPrimaryKey} = ${innerQuery.entity}.${foreignKey} `;
|
|
344
|
-
|
|
345
343
|
}
|
|
346
344
|
}
|
|
347
345
|
}
|
|
348
|
-
return includeQuery;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
buildFrom(query, entity){
|
|
352
|
-
var entityName = this.getEntity(entity.__name, query.entityMap);
|
|
353
|
-
if(entityName ){
|
|
354
|
-
return `FROM ${entity.__name } AS ${entityName}`;
|
|
355
|
-
}
|
|
356
|
-
else{ return "" }
|
|
357
|
-
}
|
|
358
346
|
|
|
359
|
-
|
|
360
|
-
// this means that there is a select statement
|
|
361
|
-
var select = "SELECT";
|
|
362
|
-
var arr = "";
|
|
363
|
-
var $that = this;
|
|
364
|
-
if(query.select){
|
|
365
|
-
for (const item in query.select.selectFields) {
|
|
366
|
-
arr += `${$that.getEntity(entity.__name, query.entityMap)}.${query.select.selectFields[item]}, `;
|
|
367
|
-
};
|
|
368
|
-
|
|
369
|
-
}
|
|
370
|
-
else{
|
|
371
|
-
var entityList = this.getEntityList(entity);
|
|
372
|
-
for (const item in entityList) {
|
|
373
|
-
arr += `${$that.getEntity(entity.__name, query.entityMap)}.${entityList[item]}, `;
|
|
374
|
-
};
|
|
375
|
-
}
|
|
376
|
-
arr = arr.replace(/,\s*$/, "");
|
|
377
|
-
return `${select} ${arr} `;
|
|
347
|
+
return strQuery;
|
|
378
348
|
}
|
|
379
349
|
|
|
380
|
-
|
|
381
|
-
if(
|
|
382
|
-
|
|
350
|
+
buildLimit(query) {
|
|
351
|
+
if (query.take) {
|
|
352
|
+
return `LIMIT ${query.take}`;
|
|
383
353
|
}
|
|
354
|
+
return "";
|
|
384
355
|
}
|
|
385
356
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
if(entity[item].primary === true){
|
|
390
|
-
return entity[item].name;
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
};
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
getForeignTable(name, entity){
|
|
397
|
-
if(entity && name){
|
|
398
|
-
return entity[name].foreignTable;
|
|
357
|
+
buildSkip(query) {
|
|
358
|
+
if (query.skip) {
|
|
359
|
+
return `OFFSET ${query.skip}`;
|
|
399
360
|
}
|
|
361
|
+
return "";
|
|
400
362
|
}
|
|
401
363
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
else{
|
|
412
|
-
return "";
|
|
364
|
+
buildOrderBy(query) {
|
|
365
|
+
if (query.orderBy) {
|
|
366
|
+
const entityStr = this.getEntity(query.parentName, query.entityMap);
|
|
367
|
+
return `ORDER BY ${entityStr}.${query.orderBy} ASC`;
|
|
368
|
+
} else if (query.orderByDescending) {
|
|
369
|
+
const entityStr = this.getEntity(query.parentName, query.entityMap);
|
|
370
|
+
return `ORDER BY ${entityStr}.${query.orderByDescending} DESC`;
|
|
413
371
|
}
|
|
372
|
+
return "";
|
|
414
373
|
}
|
|
415
374
|
|
|
416
|
-
getEntity(name,
|
|
417
|
-
for (let
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
return map.entity
|
|
375
|
+
getEntity(name, list) {
|
|
376
|
+
for (let i = 0; i < list.length; i++) {
|
|
377
|
+
if (list[i].name === name) {
|
|
378
|
+
return list[i].entity;
|
|
421
379
|
}
|
|
422
380
|
}
|
|
423
|
-
return
|
|
424
|
-
}
|
|
381
|
+
return name;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Build parameterized INSERT object for PostgreSQL
|
|
386
|
+
* Uses $1, $2, $3... instead of ?
|
|
387
|
+
*/
|
|
388
|
+
_buildSQLInsertObjectParameterized(fields, modelEntity) {
|
|
389
|
+
const $that = this;
|
|
390
|
+
const columnNames = [];
|
|
391
|
+
const params = [];
|
|
392
|
+
let paramIndex = 1;
|
|
393
|
+
|
|
394
|
+
for (const column in modelEntity) {
|
|
395
|
+
if (column.indexOf("__") === -1) {
|
|
396
|
+
let fieldColumn = fields[column];
|
|
397
|
+
|
|
398
|
+
if ((fieldColumn !== undefined && fieldColumn !== null) && typeof(fieldColumn) !== "object") {
|
|
399
|
+
// Apply toDatabase transformer
|
|
400
|
+
try {
|
|
401
|
+
fieldColumn = FieldTransformer.toDatabase(fieldColumn, modelEntity[column], modelEntity.__name, column);
|
|
402
|
+
} catch (transformError) {
|
|
403
|
+
throw new Error(`INSERT failed: ${transformError.message}`);
|
|
404
|
+
}
|
|
425
405
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
if(!ent.startsWith("_")){
|
|
432
|
-
if(!entity[ent].foreignKey){
|
|
433
|
-
if($that.chechUnsupportedWords(ent)){
|
|
434
|
-
entitiesList.push(`'${ent}'`);
|
|
435
|
-
}
|
|
436
|
-
else{
|
|
437
|
-
entitiesList.push(ent);
|
|
438
|
-
}
|
|
406
|
+
// Validate and coerce type
|
|
407
|
+
try {
|
|
408
|
+
fieldColumn = $that._validateAndCoerceFieldType(fieldColumn, modelEntity[column], modelEntity.__name, column);
|
|
409
|
+
} catch (typeError) {
|
|
410
|
+
throw new Error(`INSERT failed: ${typeError.message}`);
|
|
439
411
|
}
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
412
|
+
|
|
413
|
+
// Skip auto-increment primary keys
|
|
414
|
+
if (modelEntity[column].auto !== true) {
|
|
415
|
+
columnNames.push(column);
|
|
416
|
+
params.push(fieldColumn);
|
|
444
417
|
}
|
|
445
418
|
}
|
|
446
419
|
}
|
|
447
|
-
|
|
448
|
-
}
|
|
420
|
+
}
|
|
449
421
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
var text = this.unsupportedWords[item];
|
|
453
|
-
if(text === word){
|
|
454
|
-
return true
|
|
455
|
-
}
|
|
422
|
+
if (columnNames.length === 0) {
|
|
423
|
+
return -1;
|
|
456
424
|
}
|
|
457
|
-
return false;
|
|
458
|
-
}
|
|
459
425
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
}
|
|
426
|
+
// Generate PostgreSQL placeholders: $1, $2, $3...
|
|
427
|
+
const placeholders = params.map((_, index) => `$${index + 1}`).join(', ');
|
|
463
428
|
|
|
464
|
-
|
|
465
|
-
|
|
429
|
+
return {
|
|
430
|
+
tableName: modelEntity.__name,
|
|
431
|
+
columns: columnNames.join(', '),
|
|
432
|
+
placeholders: placeholders,
|
|
433
|
+
params: params
|
|
434
|
+
};
|
|
466
435
|
}
|
|
467
436
|
|
|
468
|
-
|
|
469
|
-
|
|
437
|
+
_buildDeleteObject(queryObject) {
|
|
438
|
+
const primaryKey = tools.getPrimaryKeyObject(queryObject.__entity);
|
|
439
|
+
return {
|
|
440
|
+
tableName: queryObject.__entity.__name,
|
|
441
|
+
primaryKey: primaryKey,
|
|
442
|
+
value: queryObject[primaryKey]
|
|
443
|
+
};
|
|
470
444
|
}
|
|
471
445
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
// TODO Boolean value is a string with a letter
|
|
480
|
-
switch(model.__entity[dirtyFields[column]].type){
|
|
481
|
-
case "integer" :
|
|
482
|
-
//model.__entity[dirtyFields[column]].skipGetFunction = true;
|
|
483
|
-
argument = argument === null ? `[${dirtyFields[column]}] = ${model[dirtyFields[column]]},` : `${argument} [${dirtyFields[column]}] = ${model[dirtyFields[column]]},`;
|
|
484
|
-
//model.__entity[dirtyFields[column]].skipGetFunction = false;
|
|
485
|
-
break;
|
|
486
|
-
case "string" :
|
|
487
|
-
argument = argument === null ? `[${dirtyFields[column]}] = '${$that._santizeSingleQuotes(model[dirtyFields[column]])}',` : `${argument} [${dirtyFields[column]}] = '${$that._santizeSingleQuotes(model[dirtyFields[column]])}',`;
|
|
488
|
-
break;
|
|
489
|
-
case "boolean" :
|
|
490
|
-
var bool = "";
|
|
491
|
-
if(model.__entity[dirtyFields[column]].valueConversion){
|
|
492
|
-
bool = tools.convertBooleanToNumber(model[dirtyFields[column]]);
|
|
493
|
-
}
|
|
494
|
-
else{
|
|
495
|
-
bool = model[dirtyFields[column]];
|
|
496
|
-
}
|
|
497
|
-
argument = argument === null ? `[${dirtyFields[column]}] = '${bool}',` : `${argument} [${dirtyFields[column]}] = ${bool},`;
|
|
498
|
-
break;
|
|
499
|
-
case "time" :
|
|
500
|
-
argument = argument === null ? `[${dirtyFields[column]}] = '${model[dirtyFields[column]]}',` : `${argument} [${dirtyFields[column]}] = ${model[dirtyFields[column]]},`;
|
|
501
|
-
break;
|
|
502
|
-
case "belongsTo" :
|
|
503
|
-
var fore = `_${dirtyFields[column]}`;
|
|
504
|
-
argument = argument === null ? `[${model.__entity[dirtyFields[column]].foreignKey}] = '${model[fore]}',` : `${argument} [${model.__entity[dirtyFields[column]].foreignKey}] = '${model[fore]}',`;
|
|
505
|
-
break;
|
|
506
|
-
case "hasMany" :
|
|
507
|
-
argument = argument === null ? `[${dirtyFields[column]}] = '${model[dirtyFields[column]]}',` : `${argument} [${dirtyFields[column]}] = '${model[dirtyFields[column]]}',`;
|
|
508
|
-
break;
|
|
509
|
-
default:
|
|
510
|
-
argument = argument === null ? `[${dirtyFields[column]}] = '${model[dirtyFields[column]]}',` : `${argument} [${dirtyFields[column]}] = '${model[dirtyFields[column]]}',`;
|
|
446
|
+
/**
|
|
447
|
+
* Validate and coerce field type
|
|
448
|
+
*/
|
|
449
|
+
_validateAndCoerceFieldType(value, fieldDef, entityName, fieldName) {
|
|
450
|
+
if (value === null || value === undefined) {
|
|
451
|
+
if (fieldDef.nullable === false || fieldDef.notNullable === true) {
|
|
452
|
+
throw new Error(`Field '${entityName}.${fieldName}' cannot be null`);
|
|
511
453
|
}
|
|
454
|
+
return null;
|
|
512
455
|
}
|
|
513
|
-
return argument.replace(/,\s*$/, "");
|
|
514
|
-
}
|
|
515
456
|
|
|
516
|
-
|
|
517
|
-
_buildDeleteObject(currentModel){
|
|
518
|
-
var primaryKey = currentModel.__Key === undefined ? tools.getPrimaryKeyObject(currentModel.__entity) : currentModel.__Key;
|
|
519
|
-
var value = currentModel.__value === undefined ? currentModel[primaryKey] : currentModel.__value;
|
|
520
|
-
var tableName = currentModel.__tableName === undefined ? currentModel.__entity.__name : currentModel.__tableName;
|
|
521
|
-
return {tableName: tableName, primaryKey : primaryKey, value : value};
|
|
522
|
-
}
|
|
457
|
+
const fieldType = fieldDef.type;
|
|
523
458
|
|
|
459
|
+
switch (fieldType) {
|
|
460
|
+
case 'string':
|
|
461
|
+
case 'text':
|
|
462
|
+
return String(value);
|
|
524
463
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
column = modelEntity[column].foreignKey === undefined ? column : modelEntity[column].foreignKey;
|
|
542
|
-
break;
|
|
543
|
-
case "string" :
|
|
544
|
-
fieldColumn = `'${$that._santizeSingleQuotes(fields[column])}'`;
|
|
545
|
-
break;
|
|
546
|
-
case "time" :
|
|
547
|
-
fieldColumn = fields[column];
|
|
548
|
-
break;
|
|
549
|
-
}
|
|
464
|
+
case 'integer':
|
|
465
|
+
case 'int':
|
|
466
|
+
const intVal = parseInt(value, 10);
|
|
467
|
+
if (isNaN(intVal)) {
|
|
468
|
+
throw new Error(`Field '${entityName}.${fieldName}' must be an integer, got: ${value}`);
|
|
469
|
+
}
|
|
470
|
+
return intVal;
|
|
471
|
+
|
|
472
|
+
case 'float':
|
|
473
|
+
case 'double':
|
|
474
|
+
case 'decimal':
|
|
475
|
+
const floatVal = parseFloat(value);
|
|
476
|
+
if (isNaN(floatVal)) {
|
|
477
|
+
throw new Error(`Field '${entityName}.${fieldName}' must be a number, got: ${value}`);
|
|
478
|
+
}
|
|
479
|
+
return floatVal;
|
|
550
480
|
|
|
551
|
-
|
|
552
|
-
|
|
481
|
+
case 'boolean':
|
|
482
|
+
case 'bool':
|
|
483
|
+
return Boolean(value);
|
|
553
484
|
|
|
485
|
+
case 'date':
|
|
486
|
+
case 'datetime':
|
|
487
|
+
case 'timestamp':
|
|
488
|
+
if (value instanceof Date) {
|
|
489
|
+
return value;
|
|
554
490
|
}
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
var fieldObject = tools.findTrackedObject(fields.__context.__trackedEntities, column );
|
|
559
|
-
if( Object.keys(fieldObject).length > 0){
|
|
560
|
-
var primaryKey = tools.getPrimaryKeyObject(fieldObject.__entity);
|
|
561
|
-
fieldColumn = fieldObject[primaryKey];
|
|
562
|
-
column = modelEntity[column].foreignKey;
|
|
563
|
-
columns = columns === null ? `'${column}',` : `${columns} '${column}',`;
|
|
564
|
-
values = values === null ? `${fieldColumn},` : `${values} ${fieldColumn},`;
|
|
565
|
-
}else{
|
|
566
|
-
console.log("Cannot find belings to relationship")
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
break;
|
|
570
|
-
}
|
|
571
|
-
|
|
491
|
+
const dateVal = new Date(value);
|
|
492
|
+
if (isNaN(dateVal.getTime())) {
|
|
493
|
+
throw new Error(`Field '${entityName}.${fieldName}' must be a valid date, got: ${value}`);
|
|
572
494
|
}
|
|
573
|
-
|
|
574
|
-
}
|
|
575
|
-
return {tableName: modelEntity.__name, columns: columns.replace(/,\s*$/, ""), values: values.replace(/,\s*$/, "")};
|
|
495
|
+
return dateVal;
|
|
576
496
|
|
|
577
|
-
|
|
497
|
+
case 'json':
|
|
498
|
+
case 'jsonb':
|
|
499
|
+
if (typeof value === 'object') {
|
|
500
|
+
return JSON.stringify(value);
|
|
501
|
+
}
|
|
502
|
+
return value;
|
|
578
503
|
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
if (typeof string === 'string' || string instanceof String){
|
|
582
|
-
return string.replace(/'/g, "''");
|
|
504
|
+
default:
|
|
505
|
+
return value;
|
|
583
506
|
}
|
|
584
|
-
else{
|
|
585
|
-
console.log("warning - Field being passed is not a string");
|
|
586
|
-
throw "warning - Field being passed is not a string";
|
|
587
|
-
}
|
|
588
507
|
}
|
|
589
508
|
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
509
|
+
/**
|
|
510
|
+
* Execute parameterized query with pg library
|
|
511
|
+
*/
|
|
512
|
+
async _runWithParams(query, params = []) {
|
|
513
|
+
try {
|
|
514
|
+
console.log("SQL:", query);
|
|
515
|
+
console.log("Params:", params);
|
|
516
|
+
|
|
517
|
+
const client = await this.pool.connect();
|
|
518
|
+
try {
|
|
519
|
+
const result = await client.query(query, params);
|
|
520
|
+
return result;
|
|
521
|
+
} finally {
|
|
522
|
+
client.release();
|
|
523
|
+
}
|
|
524
|
+
} catch (error) {
|
|
525
|
+
console.error('PostgreSQL query error:', error);
|
|
526
|
+
throw error;
|
|
598
527
|
}
|
|
599
|
-
return mainString;;
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
_execute(query){
|
|
603
|
-
console.log("SQL:", query);
|
|
604
|
-
return this.db.exec(query);
|
|
605
528
|
}
|
|
606
529
|
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
530
|
+
/**
|
|
531
|
+
* Sanitize single quotes (legacy, prefer parameterized queries)
|
|
532
|
+
*/
|
|
533
|
+
_santizeSingleQuotes(string, context = {}) {
|
|
534
|
+
if (typeof string === 'string' || string instanceof String) {
|
|
535
|
+
return string.replace(/'/g, "''");
|
|
536
|
+
}
|
|
537
|
+
console.warn(`Warning - Field ${context.entityName}.${context.fieldName} is not a string`);
|
|
538
|
+
throw new Error(`Field ${context.entityName}.${context.fieldName} must be a string`);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* Set database connection
|
|
543
|
+
*/
|
|
544
|
+
setDB(db, type) {
|
|
545
|
+
this.db = db;
|
|
546
|
+
this.pool = db;
|
|
547
|
+
this.dbType = type || 'postgres';
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* Close database connection pool
|
|
552
|
+
*/
|
|
553
|
+
async close() {
|
|
554
|
+
if (this.pool) {
|
|
555
|
+
await this.pool.end();
|
|
556
|
+
console.log('PostgreSQL pool closed');
|
|
557
|
+
}
|
|
610
558
|
}
|
|
611
|
-
|
|
612
|
-
setDB(db, type){
|
|
613
|
-
this.db = db;
|
|
614
|
-
this.dbType = type; // this will let us know which type of sqlengine to use.
|
|
615
|
-
}
|
|
616
559
|
}
|
|
617
560
|
|
|
618
|
-
module.exports = postgresEngine;
|
|
561
|
+
module.exports = postgresEngine;
|