remult 0.25.8 → 0.26.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.
Files changed (102) hide show
  1. package/README.md +194 -129
  2. package/esm/graphql.js +131 -59
  3. package/esm/index.js +1 -1
  4. package/esm/migrations/compare-migration-snapshots.js +61 -0
  5. package/esm/migrations/generate-migrations.js +68 -0
  6. package/esm/migrations/index.js +2 -0
  7. package/esm/migrations/migrate.js +90 -0
  8. package/esm/migrations/migration-types.js +36 -0
  9. package/esm/migrations/update-migrations-ts-file.js +115 -0
  10. package/esm/postgres/postgres-data-provider.js +24 -3
  11. package/esm/postgres/schema-builder.js +35 -26
  12. package/esm/remult-better-sqlite3.js +47 -0
  13. package/esm/remult-bun-sqlite.js +41 -0
  14. package/esm/remult-knex/index.js +180 -51
  15. package/esm/remult-mongo.js +2 -2
  16. package/esm/remult-sql-js.js +3 -78
  17. package/esm/remult-sqlite-core.js +104 -0
  18. package/esm/server/expressBridge.js +130 -29
  19. package/esm/server/initAsyncHooks.js +75 -1
  20. package/esm/src/CompoundIdField.js +8 -2
  21. package/esm/src/context.js +20 -24
  22. package/esm/src/data-api.js +71 -30
  23. package/esm/src/data-providers/rest-data-provider.js +17 -15
  24. package/esm/src/data-providers/sql-database.js +32 -7
  25. package/esm/src/data-providers/web-sql-data-provider.js +1 -9
  26. package/esm/src/filter/filter-consumer-bridge-to-sql-request.js +37 -11
  27. package/esm/src/filter/filter-interfaces.js +229 -17
  28. package/esm/src/isOfType.js +9 -0
  29. package/esm/src/remult3/Fields.js +70 -18
  30. package/esm/src/remult3/RepositoryImplementation.js +47 -21
  31. package/esm/src/remult3/addValidator.js +15 -0
  32. package/esm/src/remult3/remult3.js +28 -57
  33. package/esm/src/server-action.js +4 -4
  34. package/esm/src/validators.js +6 -3
  35. package/esm/src/valueConverters.js +1 -1
  36. package/graphql.js +156 -71
  37. package/index.d.ts +4 -4
  38. package/index.js +2 -1
  39. package/migrations/compare-migration-snapshots.d.ts +27 -0
  40. package/migrations/compare-migration-snapshots.js +165 -0
  41. package/migrations/generate-migrations.d.ts +21 -0
  42. package/migrations/generate-migrations.js +104 -0
  43. package/migrations/index.d.ts +3 -0
  44. package/migrations/index.js +7 -0
  45. package/migrations/migrate.d.ts +27 -0
  46. package/migrations/migrate.js +175 -0
  47. package/migrations/migration-types.d.ts +48 -0
  48. package/migrations/migration-types.js +88 -0
  49. package/migrations/update-migrations-ts-file.d.ts +1 -0
  50. package/migrations/update-migrations-ts-file.js +141 -0
  51. package/package.json +21 -1
  52. package/postgres/postgres-data-provider.d.ts +8 -3
  53. package/postgres/postgres-data-provider.js +46 -3
  54. package/postgres/schema-builder.js +81 -48
  55. package/remult-better-sqlite3.d.ts +12 -0
  56. package/remult-better-sqlite3.js +64 -0
  57. package/remult-bun-sqlite.d.ts +11 -0
  58. package/remult-bun-sqlite.js +56 -0
  59. package/remult-knex/index.d.ts +12 -5
  60. package/remult-knex/index.js +257 -90
  61. package/remult-mongo.d.ts +2 -2
  62. package/remult-mongo.js +2 -2
  63. package/remult-sql-js.d.ts +2 -15
  64. package/remult-sql-js.js +11 -152
  65. package/remult-sqlite-core.d.ts +21 -0
  66. package/remult-sqlite-core.js +217 -0
  67. package/server/expressBridge.d.ts +30 -1
  68. package/server/expressBridge.js +140 -32
  69. package/server/initAsyncHooks.d.ts +16 -0
  70. package/server/initAsyncHooks.js +128 -2
  71. package/src/CompoundIdField.js +8 -2
  72. package/src/column-interfaces.d.ts +9 -3
  73. package/src/context.d.ts +8 -6
  74. package/src/context.js +27 -36
  75. package/src/data-api.d.ts +13 -9
  76. package/src/data-api.js +184 -135
  77. package/src/data-providers/rest-data-provider.js +26 -15
  78. package/src/data-providers/sql-database.d.ts +17 -5
  79. package/src/data-providers/sql-database.js +31 -7
  80. package/src/data-providers/web-sql-data-provider.d.ts +1 -2
  81. package/src/data-providers/web-sql-data-provider.js +4 -8
  82. package/src/entity.d.ts +64 -6
  83. package/src/filter/filter-consumer-bridge-to-sql-request.d.ts +31 -7
  84. package/src/filter/filter-consumer-bridge-to-sql-request.js +46 -21
  85. package/src/filter/filter-interfaces.d.ts +106 -39
  86. package/src/filter/filter-interfaces.js +312 -32
  87. package/src/isOfType.d.ts +2 -0
  88. package/src/isOfType.js +14 -0
  89. package/src/remult3/Fields.d.ts +36 -0
  90. package/src/remult3/Fields.js +81 -19
  91. package/src/remult3/RepositoryImplementation.d.ts +7 -3
  92. package/src/remult3/RepositoryImplementation.js +106 -71
  93. package/src/remult3/addValidator.d.ts +2 -0
  94. package/src/remult3/addValidator.js +20 -0
  95. package/src/remult3/remult3.d.ts +6 -2
  96. package/src/remult3/remult3.js +28 -57
  97. package/src/server-action.d.ts +1 -1
  98. package/src/server-action.js +5 -4
  99. package/src/sql-command.d.ts +6 -1
  100. package/src/validators.d.ts +1 -0
  101. package/src/validators.js +10 -4
  102. package/src/valueConverters.js +1 -1
@@ -23,17 +23,31 @@ export class DataApi {
23
23
  }
24
24
  async httpPost(res, req, body, serializeContext) {
25
25
  const action = req?.get('__action');
26
- if (action?.startsWith(liveQueryAction))
26
+ function validateWhereInBody() {
27
+ if (!body?.where) {
28
+ throw {
29
+ message: `POST with action ${action} must have a where clause in the body`,
30
+ httpStatusCode: 400,
31
+ };
32
+ }
33
+ }
34
+ if (action?.startsWith(liveQueryAction)) {
35
+ validateWhereInBody();
27
36
  return this.liveQuery(res, req, body, serializeContext, action.substring(liveQueryAction.length));
37
+ }
28
38
  switch (action) {
29
39
  case 'get':
40
+ validateWhereInBody();
30
41
  return this.getArray(res, req, body);
31
42
  case 'count':
43
+ validateWhereInBody();
32
44
  return this.count(res, req, body);
33
45
  case 'deleteMany':
46
+ validateWhereInBody();
34
47
  return this.deleteMany(res, req, body);
35
48
  case 'updateMany':
36
- return this.updateMany(res, req, body);
49
+ validateWhereInBody();
50
+ return this.updateManyImplementation(res, req, body);
37
51
  case 'endLiveQuery':
38
52
  await this.remult.liveQueryStorage.remove(body.id);
39
53
  res.success('ok');
@@ -50,26 +64,28 @@ export class DataApi {
50
64
  }
51
65
  await this.doOnId(response, id, async (row) => response.success(this.repository.getEntityRef(row).toApiJson()));
52
66
  }
53
- async count(response, request, filterBody) {
67
+ async count(response, request, body) {
54
68
  if (!this.repository.metadata.apiReadAllowed) {
55
69
  response.forbidden();
56
70
  return;
57
71
  }
58
72
  try {
59
73
  response.success({
60
- count: +(await this.repository.count(await this.buildWhere(request, filterBody))),
74
+ count: +(await this.repository.count(await this.buildWhere(request, body))),
61
75
  });
62
76
  }
63
77
  catch (err) {
64
- response.error(err);
78
+ response.error(err, this.repository.metadata);
65
79
  }
66
80
  }
67
- async deleteMany(response, request, filterBody) {
81
+ async deleteMany(response, request, body) {
68
82
  try {
83
+ let deleted = 0;
84
+ let where = await this.buildWhere(request, body);
85
+ Filter.throwErrorIfFilterIsEmpty(where, 'deleteMany');
69
86
  return await doTransaction(this.remult, async () => {
70
- let deleted = 0;
71
87
  for await (const x of this.repository.query({
72
- where: await this.buildWhere(request, filterBody),
88
+ where,
73
89
  include: this.includeNone(),
74
90
  })) {
75
91
  await this.actualDelete(x);
@@ -79,15 +95,15 @@ export class DataApi {
79
95
  });
80
96
  }
81
97
  catch (err) {
82
- response.error(err);
98
+ response.error(err, this.repository.metadata);
83
99
  }
84
100
  }
85
- async getArrayImpl(response, request, filterBody) {
101
+ async getArrayImpl(response, request, body) {
86
102
  let findOptions = {
87
103
  load: () => [],
88
104
  include: this.includeNone(),
89
105
  };
90
- findOptions.where = await this.buildWhere(request, filterBody);
106
+ findOptions.where = await this.buildWhere(request, body);
91
107
  if (request) {
92
108
  let sort = request.get('_sort');
93
109
  if (sort != undefined) {
@@ -146,29 +162,29 @@ export class DataApi {
146
162
  }
147
163
  return include;
148
164
  }
149
- async getArray(response, request, filterBody) {
165
+ async getArray(response, request, body) {
150
166
  if (!this.repository.metadata.apiReadAllowed) {
151
167
  response.forbidden();
152
168
  return;
153
169
  }
154
170
  try {
155
- const { r } = await this.getArrayImpl(response, request, filterBody);
171
+ const { r } = await this.getArrayImpl(response, request, body);
156
172
  response.success(r);
157
173
  }
158
174
  catch (err) {
159
175
  if (err.isForbiddenError)
160
176
  response.forbidden();
161
177
  else
162
- response.error(err);
178
+ response.error(err, this.repository.metadata);
163
179
  }
164
180
  }
165
- async liveQuery(response, request, filterBody, serializeContext, queryChannel) {
181
+ async liveQuery(response, request, body, serializeContext, queryChannel) {
166
182
  if (!this.repository.metadata.apiReadAllowed) {
167
183
  response.forbidden();
168
184
  return;
169
185
  }
170
186
  try {
171
- const r = await this.getArrayImpl(response, request, filterBody);
187
+ const r = await this.getArrayImpl(response, request, body);
172
188
  const data = {
173
189
  requestJson: await serializeContext(),
174
190
  findOptionsJson: findOptionsToJson(r.findOptions, this.repository.metadata),
@@ -185,10 +201,10 @@ export class DataApi {
185
201
  if (err.isForbiddenError)
186
202
  response.forbidden();
187
203
  else
188
- response.error(err);
204
+ response.error(err, this.repository.metadata);
189
205
  }
190
206
  }
191
- async buildWhere(request, filterBody) {
207
+ async buildWhere(request, body) {
192
208
  var where = [];
193
209
  if (this.repository.metadata.options.apiPrefilter) {
194
210
  if (typeof this.repository.metadata.options.apiPrefilter === 'function')
@@ -197,19 +213,29 @@ export class DataApi {
197
213
  where.push(this.repository.metadata.options.apiPrefilter);
198
214
  }
199
215
  if (request) {
200
- where.push(buildFilterFromRequestParameters(this.repository.metadata, {
216
+ let f = buildFilterFromRequestParameters(this.repository.metadata, {
201
217
  get: (key) => {
202
- let result = request.get(key);
218
+ let result = body?.where?.[key];
219
+ if (result !== undefined)
220
+ return result;
221
+ result = request.get(key);
203
222
  if (key.startsWith(customUrlToken) &&
204
223
  result &&
205
224
  typeof result === 'string')
206
225
  return JSON.parse(result);
207
226
  return result;
208
227
  },
209
- }));
228
+ });
229
+ if (this.repository.metadata.options.apiPreprocessFilter) {
230
+ f = await this.repository.metadata.options.apiPreprocessFilter(f, {
231
+ metadata: this.repository.metadata,
232
+ getFilterPreciseValues: async (filter) => {
233
+ return Filter.getPreciseValues(this.repository.metadata, filter || f);
234
+ },
235
+ });
236
+ }
237
+ where.push(f);
210
238
  }
211
- if (filterBody)
212
- where.push(Filter.entityFilterFromJson(this.repository.metadata, filterBody));
213
239
  return { $and: where };
214
240
  }
215
241
  async doOnId(response, id, what) {
@@ -232,21 +258,36 @@ export class DataApi {
232
258
  if (r.length == 0)
233
259
  response.notFound();
234
260
  else if (r.length > 1)
235
- response.error({ message: 'id is not unique' });
261
+ response.error({
262
+ message: `id "${id}" is not unique for entity ` +
263
+ this.repository.metadata.key,
264
+ }, this.repository.metadata, 500);
236
265
  else
237
266
  await what(r[0]);
238
267
  });
239
268
  }
240
269
  catch (err) {
241
- response.error(err);
270
+ response.error(err, this.repository.metadata);
271
+ }
272
+ }
273
+ async updateManyThroughPutRequest(response, request, body) {
274
+ const action = request?.get('__action');
275
+ if (action == 'emptyId') {
276
+ return this.put(response, '', body);
242
277
  }
278
+ return this.updateManyImplementation(response, request, {
279
+ where: undefined,
280
+ set: body,
281
+ });
243
282
  }
244
- async updateMany(response, request, body) {
283
+ async updateManyImplementation(response, request, body) {
245
284
  try {
285
+ let where = await this.buildWhere(request, body);
286
+ Filter.throwErrorIfFilterIsEmpty(where, 'deleteMany');
246
287
  return await doTransaction(this.remult, async () => {
247
288
  let updated = 0;
248
289
  for await (const x of this.repository.query({
249
- where: await this.buildWhere(request, body.where),
290
+ where,
250
291
  include: this.includeNone(),
251
292
  })) {
252
293
  await this.actualUpdate(x, body.set);
@@ -256,7 +297,7 @@ export class DataApi {
256
297
  });
257
298
  }
258
299
  catch (err) {
259
- response.error(err);
300
+ response.error(err, this.repository.metadata);
260
301
  }
261
302
  }
262
303
  async actualUpdate(row, body) {
@@ -311,9 +352,9 @@ export class DataApi {
311
352
  }
312
353
  catch (err) {
313
354
  if (err.isForbiddenError)
314
- response.forbidden();
355
+ response.forbidden(err.message);
315
356
  else
316
- response.error(err);
357
+ response.error(err, this.repository.metadata);
317
358
  }
318
359
  }
319
360
  }
@@ -120,26 +120,27 @@ export class RestEntityDataProvider {
120
120
  return run('count').then((r) => +r.count);
121
121
  }
122
122
  async deleteMany(where) {
123
- const { run } = this.buildFindRequest({ where }, true);
123
+ const { run } = this.buildFindRequest({ where }, 'delete');
124
124
  return run('deleteMany').then((r) => +r.deleted);
125
125
  }
126
126
  async updateMany(where, data) {
127
- const { run } = this.buildFindRequest({ where }, true);
128
- return run('updateMany', { set: this.toJsonOfIncludedKeys(data) }).then((r) => +r.updated);
127
+ const { run } = this.buildFindRequest({ where }, 'put');
128
+ return run('updateMany', this.toJsonOfIncludedKeys(data)).then((r) => +r.updated);
129
129
  }
130
130
  find(options) {
131
131
  let { run } = this.buildFindRequest(options);
132
132
  return run().then((x) => x.map((y) => this.translateFromJson(y)));
133
133
  }
134
134
  //@internal
135
- buildFindRequest(options, forcePost = false) {
135
+ buildFindRequest(options, method) {
136
+ if (!method)
137
+ method = 'get';
136
138
  let url = new UrlBuilder(this.url());
137
139
  let filterObject;
138
140
  if (options) {
139
141
  if (options.where) {
140
142
  filterObject = options.where.toJson(); // options.where.__applyToConsumer(new FilterConsumnerBridgeToUrlBuilder(url));
141
- if (!forcePost &&
142
- addFilterToUrlAndReturnTrueIfSuccessful(filterObject, url))
143
+ if (addFilterToUrlAndReturnTrueIfSuccessful(filterObject, url))
143
144
  filterObject = undefined;
144
145
  }
145
146
  if (options.orderBy && options.orderBy.Segments) {
@@ -173,15 +174,12 @@ export class RestEntityDataProvider {
173
174
  }
174
175
  if (action)
175
176
  u.add('__action', action);
176
- if (filterObject || forcePost) {
177
- if (!body)
178
- body = filterObject;
179
- else
180
- body = { ...body, where: filterObject };
177
+ if (filterObject) {
178
+ body = { set: body, where: filterObject };
181
179
  return this.http().post(u.url, body);
182
180
  }
183
181
  else
184
- return this.http().get(u.url);
182
+ return this.http()[method](u.url, body);
185
183
  };
186
184
  return {
187
185
  createKey: () => JSON.stringify({ url, filterObject }),
@@ -201,7 +199,8 @@ export class RestEntityDataProvider {
201
199
  }
202
200
  update(id, data) {
203
201
  return this.http()
204
- .put(this.url() + '/' + encodeURIComponent(id), this.toJsonOfIncludedKeys(data))
202
+ .put(this.url() +
203
+ (id != '' ? '/' + encodeURIComponent(id) : '?__action=emptyId'), this.toJsonOfIncludedKeys(data))
205
204
  .then((y) => this.translateFromJson(y));
206
205
  }
207
206
  toJsonOfIncludedKeys(data) {
@@ -213,8 +212,11 @@ export class RestEntityDataProvider {
213
212
  }
214
213
  return result;
215
214
  }
216
- delete(id) {
217
- return this.http().delete(this.url() + '/' + encodeURIComponent(id));
215
+ async delete(id) {
216
+ if (id == '')
217
+ await this.deleteMany(Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(id)));
218
+ else
219
+ return this.http().delete(this.url() + '/' + encodeURIComponent(id));
218
220
  }
219
221
  insert(data) {
220
222
  return this.http()
@@ -6,14 +6,16 @@ import { getRepository } from '../remult3/RepositoryImplementation.js';
6
6
  import { Sort } from '../sort.js';
7
7
  import { ValueConverters } from '../valueConverters.js';
8
8
  import { getRepositoryInternals } from '../remult3/repository-internals.js';
9
+ import { isOfType } from '../isOfType.js';
9
10
  // @dynamic
10
11
  export class SqlDatabase {
11
12
  sql;
12
- static getDb(remult) {
13
- const r = (remult || defaultRemult).dataProvider;
14
- if (!r.createCommand)
15
- throw 'the data provider is not an SqlDatabase';
16
- return r;
13
+ static getDb(dataProvider) {
14
+ const r = (dataProvider || defaultRemult.dataProvider);
15
+ if (isOfType(r, 'createCommand'))
16
+ return r;
17
+ else
18
+ throw 'the data provider is not an SqlCommandFactory';
17
19
  }
18
20
  createCommand() {
19
21
  return new LogSQLCommand(this.sql.createCommand(), SqlDatabase.LogToConsole);
@@ -74,6 +76,7 @@ export class SqlDatabase {
74
76
  transaction: (z) => x.transaction(z),
75
77
  supportsJsonColumnType: this.sql.supportsJsonColumnType,
76
78
  wrapIdentifier: this.wrapIdentifier,
79
+ end: this.end,
77
80
  }));
78
81
  }
79
82
  finally {
@@ -81,6 +84,16 @@ export class SqlDatabase {
81
84
  }
82
85
  });
83
86
  }
87
+ /**
88
+ * Creates a raw filter for entity filtering.
89
+ * @param {CustomSqlFilterBuilderFunction} build - The custom SQL filter builder function.
90
+ * @returns {EntityFilter<any>} - The entity filter with a custom SQL filter.
91
+ * @example
92
+ * SqlDatabase.rawFilter(({param}) =>
93
+ `"customerId" in (select id from customers where city = ${param(customerCity)})`
94
+ )
95
+ * @see [Leveraging Database Capabilities with Raw SQL in Custom Filters](https://remult.dev/docs/custom-filter.html#leveraging-database-capabilities-with-raw-sql-in-custom-filters)
96
+ */
84
97
  static rawFilter(build) {
85
98
  return {
86
99
  [customDatabaseFilterToken]: {
@@ -88,12 +101,12 @@ export class SqlDatabase {
88
101
  },
89
102
  };
90
103
  }
91
- static async filterToRaw(repo, condition, sqlCommand, dbNames) {
104
+ static async filterToRaw(repo, condition, sqlCommand, dbNames, wrapIdentifier = (x) => x) {
92
105
  if (!sqlCommand) {
93
106
  sqlCommand = new myDummySQLCommand();
94
107
  }
95
108
  const r = getRepository(repo);
96
- var b = new FilterConsumerBridgeToSqlRequest(sqlCommand, dbNames || (await dbNamesOf(r.metadata)));
109
+ var b = new FilterConsumerBridgeToSqlRequest(sqlCommand, dbNames || (await dbNamesOf(r.metadata, wrapIdentifier)));
97
110
  b._addWhere = false;
98
111
  await (await getRepositoryInternals(r)._translateWhereToFilter(condition)).__applyToConsumer(b);
99
112
  return await b.resolveWhere();
@@ -116,8 +129,15 @@ export class SqlDatabase {
116
129
  this.sql = sql;
117
130
  if (sql.wrapIdentifier)
118
131
  this.wrapIdentifier = (x) => sql.wrapIdentifier(x);
132
+ if (isOfType(sql, 'provideMigrationBuilder')) {
133
+ this.provideMigrationBuilder = (x) => sql.provideMigrationBuilder(x);
134
+ }
135
+ if (isOfType(sql, 'end'))
136
+ this.end = () => sql.end();
119
137
  }
138
+ provideMigrationBuilder;
120
139
  createdEntities = [];
140
+ end;
121
141
  }
122
142
  const icons = new Map([
123
143
  // CRUD
@@ -336,6 +356,11 @@ class ActualSQLServerDataProvider {
336
356
  statement += ' returning ' + select;
337
357
  return r.execute(statement).then((sqlResult) => {
338
358
  this.sql._getSourceSql().afterMutation?.();
359
+ if (sqlResult.rows.length != 1)
360
+ throw new Error('Failed to update row with id ' +
361
+ id +
362
+ ', rows updated: ' +
363
+ sqlResult.rows.length);
339
364
  return this.buildResultRow(colKeys, sqlResult.rows[0], sqlResult);
340
365
  });
341
366
  }
@@ -13,15 +13,7 @@ export class WebSqlDataProvider {
13
13
  //@ts-ignore
14
14
  this.db = window.openDatabase(databaseName, '1.0', databaseName, databaseSize);
15
15
  }
16
- // return instance of websql.Database class - see @types/websql
17
- static getDb(remult) {
18
- const sql = SqlDatabase.getDb(remult);
19
- const me = sql._getSourceSql();
20
- if (!me.db) {
21
- throw 'the data provider is not a WebSqlDataProvider';
22
- }
23
- return me.db;
24
- }
16
+ async end() { }
25
17
  getLimitSqlSyntax(limit, offset) {
26
18
  return ' limit ' + limit + ' offset ' + offset;
27
19
  }
@@ -126,34 +126,55 @@ export class FilterConsumerBridgeToSqlRequest {
126
126
  databaseCustom(databaseCustom) {
127
127
  this.promises.push((async () => {
128
128
  if (databaseCustom?.buildSql) {
129
- let item = new CustomSqlFilterBuilder(this.r);
130
- await databaseCustom.buildSql(item);
131
- if (item.sql) {
132
- this.addToWhere('(' + item.sql + ')');
129
+ let item = new CustomSqlFilterBuilder(this.r, this.nameProvider.wrapIdentifier);
130
+ let sql = await databaseCustom.buildSql(item);
131
+ if (typeof sql !== 'string')
132
+ sql = item.sql;
133
+ if (sql) {
134
+ this.addToWhere('(' + sql + ')');
133
135
  }
134
136
  }
135
137
  })());
136
138
  }
137
139
  }
140
+ /**
141
+ * Represents a custom SQL filter builder.
142
+ */
138
143
  export class CustomSqlFilterBuilder {
139
144
  r;
140
- constructor(r) {
145
+ wrapIdentifier;
146
+ constructor(r, wrapIdentifier) {
141
147
  this.r = r;
148
+ this.wrapIdentifier = wrapIdentifier;
149
+ this.param.bind(this);
150
+ this.filterToRaw.bind(this);
142
151
  }
143
152
  sql = '';
144
- /** @deprecated @deprecated use `param` instead*/
153
+ /** @deprecated use `param` instead*/
145
154
  addParameterAndReturnSqlToken(val) {
146
155
  return this.param(val);
147
156
  }
148
- param(val, field) {
157
+ /**
158
+ * Adds a parameter value.
159
+ * @param {valueType} val - The value to add as a parameter.
160
+ * @param {FieldMetadata<valueType>} [field] - The field metadata.
161
+ * @returns {string} - The SQL token.
162
+ */
163
+ param = (val, field) => {
149
164
  if (typeof field === 'object' && field.valueConverter.toDb) {
150
165
  val = field.valueConverter.toDb(val);
151
166
  }
152
167
  return this.r.param(val);
153
- }
154
- async filterToRaw(repo, condition) {
155
- return SqlDatabase.filterToRaw(repo, condition, this);
156
- }
168
+ };
169
+ /**
170
+ * Converts an entity filter into a raw SQL condition - and appends to it any `backendPrefilter` and `backendPreprocessFilter`
171
+ * @param {RepositoryOverloads<entityType>} repo - The repository.
172
+ * @param {EntityFilter<entityType>} condition - The entity filter.
173
+ * @returns {Promise<string>} - The raw SQL.
174
+ */
175
+ filterToRaw = async (repo, condition) => {
176
+ return SqlDatabase.filterToRaw(repo, condition, this, undefined, this.wrapIdentifier);
177
+ };
157
178
  }
158
179
  export function isDbReadonly(field, dbNames) {
159
180
  return (field.dbReadOnly ||
@@ -164,6 +185,10 @@ export function shouldNotCreateField(field, dbNames) {
164
185
  return (field.isServerExpression ||
165
186
  (field.options.sqlExpression && field.dbName != dbNames.$dbNameOf(field)));
166
187
  }
188
+ export function shouldCreateEntity(entity, e) {
189
+ return (!entity.options.sqlExpression &&
190
+ e.$entityName.toLowerCase().indexOf('from ') < 0);
191
+ }
167
192
  export async function dbNamesOf(repo, wrapIdentifierOrOptions) {
168
193
  let options = typeof wrapIdentifierOrOptions === 'function'
169
194
  ? { wrapIdentifier: wrapIdentifierOrOptions }
@@ -185,6 +210,7 @@ export async function dbNamesOf(repo, wrapIdentifierOrOptions) {
185
210
  key = field.key;
186
211
  return result[key];
187
212
  },
213
+ wrapIdentifier: options.wrapIdentifier,
188
214
  };
189
215
  for (const field of meta.fields) {
190
216
  let r = await fieldDbName(field, meta, options.wrapIdentifier);