remult 3.4.0-next.9 → 3.4.1

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.
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CustomSqlFilterBuilder = exports.FilterConsumerBridgeToSqlRequest = void 0;
3
+ exports.CustomSqlFilterBuilder = exports.FilterConsumerBridgeToSqlRequest = exports.LIKE_ESCAPE_SQL = void 0;
4
+ exports.escapeLike = escapeLike;
4
5
  exports.isDbReadonly = isDbReadonly;
5
6
  exports.shouldNotCreateField = shouldNotCreateField;
6
7
  exports.shouldCreateEntity = shouldCreateEntity;
@@ -11,12 +12,24 @@ var tslib_1 = require("tslib");
11
12
  var sql_database_js_1 = require("../data-providers/sql-database.js");
12
13
  var remult_proxy_js_1 = require("../remult-proxy.js");
13
14
  var RepositoryImplementation_js_1 = require("../remult3/RepositoryImplementation.js");
15
+ var valueConverters_js_1 = require("../valueConverters.js");
14
16
  var entityDbName_js_1 = require("./entityDbName.js");
15
17
  var fieldDbName_js_1 = require("./fieldDbName.js");
18
+ // `[` starts a character class in sql server's like. `]` on its own is a literal
19
+ // everywhere, so escaping the opening bracket is enough. The other engines treat
20
+ // an escaped non-wildcard as that literal, so one escape set works for all of them.
21
+ var REGEX_LIKE_SPECIAL = /[!%_\[]/g;
22
+ // `!` not `\`: a backslash in a string literal is an escape in mysql and in pg without standard_conforming_strings
23
+ exports.LIKE_ESCAPE_SQL = " escape '!'";
24
+ function escapeLike(val) {
25
+ return val.replace(REGEX_LIKE_SPECIAL, '!$&');
26
+ }
16
27
  var FilterConsumerBridgeToSqlRequest = /** @class */ (function () {
17
- function FilterConsumerBridgeToSqlRequest(r, nameProvider) {
28
+ function FilterConsumerBridgeToSqlRequest(r, nameProvider, jsonColumnStorage) {
29
+ if (jsonColumnStorage === void 0) { jsonColumnStorage = 'native'; }
18
30
  this.r = r;
19
31
  this.nameProvider = nameProvider;
32
+ this.jsonColumnStorage = jsonColumnStorage;
20
33
  this.where = '';
21
34
  this._addWhere = true;
22
35
  this.promises = [];
@@ -81,7 +94,7 @@ var FilterConsumerBridgeToSqlRequest = /** @class */ (function () {
81
94
  case 1:
82
95
  if (!!orElements_1_1.done) return [3 /*break*/, 4];
83
96
  element = orElements_1_1.value;
84
- f = new FilterConsumerBridgeToSqlRequest(this.r, this.nameProvider);
97
+ f = new FilterConsumerBridgeToSqlRequest(this.r, this.nameProvider, this.jsonColumnStorage);
85
98
  f._addWhere = false;
86
99
  element.__applyToConsumer(f);
87
100
  return [4 /*yield*/, f.resolveWhere()];
@@ -128,7 +141,7 @@ var FilterConsumerBridgeToSqlRequest = /** @class */ (function () {
128
141
  return tslib_1.__generator(this, function (_a) {
129
142
  switch (_a.label) {
130
143
  case 0:
131
- f = new FilterConsumerBridgeToSqlRequest(this.r, this.nameProvider);
144
+ f = new FilterConsumerBridgeToSqlRequest(this.r, this.nameProvider, this.jsonColumnStorage);
132
145
  f._addWhere = false;
133
146
  element.__applyToConsumer(f);
134
147
  return [4 /*yield*/, f.resolveWhere()];
@@ -163,7 +176,9 @@ var FilterConsumerBridgeToSqlRequest = /** @class */ (function () {
163
176
  this.addToWhere(this.nameProvider.$dbNameOf(col) +
164
177
  ' in (' +
165
178
  val
166
- .map(function (x) { return toDbSql(_this.r, col, col.valueConverter.toDb(x)); })
179
+ .map(function (x) {
180
+ return toDbSql(_this.r, col, (0, valueConverters_js_1.toDbColumn)(col, x, _this.jsonColumnStorage));
181
+ })
167
182
  .join(',') +
168
183
  ')');
169
184
  else
@@ -191,53 +206,29 @@ var FilterConsumerBridgeToSqlRequest = /** @class */ (function () {
191
206
  this.add(col, val, '<');
192
207
  };
193
208
  FilterConsumerBridgeToSqlRequest.prototype.containsCaseInsensitive = function (col, val) {
194
- var _this = this;
195
- this.promises.push((function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
196
- return tslib_1.__generator(this, function (_a) {
197
- this.addToWhere('lower (' +
198
- this.nameProvider.$dbNameOf(col) +
199
- ") like lower ('%" +
200
- val.replace(/'/g, "''") +
201
- "%')");
202
- return [2 /*return*/];
203
- });
204
- }); })());
209
+ this.like(col, '%' + escapeLike(val) + '%');
205
210
  };
206
211
  FilterConsumerBridgeToSqlRequest.prototype.notContainsCaseInsensitive = function (col, val) {
207
- var _this = this;
208
- this.promises.push((function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
209
- return tslib_1.__generator(this, function (_a) {
210
- this.addToWhere('not lower (' +
211
- this.nameProvider.$dbNameOf(col) +
212
- ") like lower ('%" +
213
- val.replace(/'/g, "''") +
214
- "%')");
215
- return [2 /*return*/];
216
- });
217
- }); })());
212
+ this.like(col, '%' + escapeLike(val) + '%', 'not ');
218
213
  };
219
214
  FilterConsumerBridgeToSqlRequest.prototype.startsWithCaseInsensitive = function (col, val) {
220
- var _this = this;
221
- this.promises.push((function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
222
- return tslib_1.__generator(this, function (_a) {
223
- this.addToWhere('lower (' +
224
- this.nameProvider.$dbNameOf(col) +
225
- ") like lower ('" +
226
- val.replace(/'/g, "''") +
227
- "%')");
228
- return [2 /*return*/];
229
- });
230
- }); })());
215
+ this.like(col, escapeLike(val) + '%');
231
216
  };
232
217
  FilterConsumerBridgeToSqlRequest.prototype.endsWithCaseInsensitive = function (col, val) {
218
+ this.like(col, '%' + escapeLike(val));
219
+ };
220
+ FilterConsumerBridgeToSqlRequest.prototype.like = function (col, pattern, not) {
233
221
  var _this = this;
222
+ if (not === void 0) { not = ''; }
234
223
  this.promises.push((function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
235
224
  return tslib_1.__generator(this, function (_a) {
236
- this.addToWhere('lower (' +
225
+ this.addToWhere(not +
226
+ 'lower (' +
237
227
  this.nameProvider.$dbNameOf(col) +
238
- ") like lower ('%" +
239
- val.replace(/'/g, "''") +
240
- "')");
228
+ ') like lower (' +
229
+ this.r.param(pattern) +
230
+ ')' +
231
+ exports.LIKE_ESCAPE_SQL);
241
232
  return [2 /*return*/];
242
233
  });
243
234
  }); })());
@@ -251,7 +242,7 @@ var FilterConsumerBridgeToSqlRequest = /** @class */ (function () {
251
242
  ' ' +
252
243
  operator +
253
244
  ' ' +
254
- toDbSql(this.r, col, col.valueConverter.toDb(val));
245
+ toDbSql(this.r, col, (0, valueConverters_js_1.toDbColumn)(col, val, this.jsonColumnStorage));
255
246
  this.addToWhere(x);
256
247
  return [2 /*return*/];
257
248
  });
@@ -274,7 +265,7 @@ var FilterConsumerBridgeToSqlRequest = /** @class */ (function () {
274
265
  switch (_a.label) {
275
266
  case 0:
276
267
  if (!(databaseCustom === null || databaseCustom === void 0 ? void 0 : databaseCustom.buildSql)) return [3 /*break*/, 2];
277
- item = new CustomSqlFilterBuilder(this.r, this.nameProvider.wrapIdentifier);
268
+ item = new CustomSqlFilterBuilder(this.r, this.nameProvider.wrapIdentifier, this.jsonColumnStorage);
278
269
  return [4 /*yield*/, databaseCustom.buildSql(item)];
279
270
  case 1:
280
271
  sql = _a.sent();
@@ -296,10 +287,12 @@ exports.FilterConsumerBridgeToSqlRequest = FilterConsumerBridgeToSqlRequest;
296
287
  * Represents a custom SQL filter builder.
297
288
  */
298
289
  var CustomSqlFilterBuilder = /** @class */ (function () {
299
- function CustomSqlFilterBuilder(r, wrapIdentifier) {
290
+ function CustomSqlFilterBuilder(r, wrapIdentifier, jsonColumnStorage) {
291
+ if (jsonColumnStorage === void 0) { jsonColumnStorage = 'native'; }
300
292
  var _this = this;
301
293
  this.r = r;
302
294
  this.wrapIdentifier = wrapIdentifier;
295
+ this.jsonColumnStorage = jsonColumnStorage;
303
296
  this.sql = '';
304
297
  /**
305
298
  * Adds a parameter value.
@@ -308,8 +301,8 @@ var CustomSqlFilterBuilder = /** @class */ (function () {
308
301
  * @returns {string} - The SQL token.
309
302
  */
310
303
  this.param = function (val, field) {
311
- if (typeof field === 'object' && field.valueConverter.toDb) {
312
- val = field.valueConverter.toDb(val);
304
+ if (field) {
305
+ val = (0, valueConverters_js_1.toDbColumn)(field, val, _this.jsonColumnStorage);
313
306
  }
314
307
  return _this.r.param(val);
315
308
  };
@@ -0,0 +1,3 @@
1
+ /** Return this from {@link walkFilter}'s visitor to drop that field key. */
2
+ export declare const omitFilterField: unique symbol;
3
+ export declare function walkFilter(filter: unknown, visit: (key: string, value: unknown) => unknown): unknown;
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.omitFilterField = void 0;
4
+ exports.walkFilter = walkFilter;
5
+ var tslib_1 = require("tslib");
6
+ /** Return this from {@link walkFilter}'s visitor to drop that field key. */
7
+ exports.omitFilterField = Symbol('omitFilterField');
8
+ function isCompoundFilterKey(key) {
9
+ return key === '$and' || key === '$or' || key === '$not';
10
+ }
11
+ // `$and`, `$or`, and `$not` are entered. Every other key is a field, passed to `visit`.
12
+ // `visit` returns the value to keep, or `omitFilterField` to drop the key.
13
+ function walkFilter(filter, visit) {
14
+ var e_1, _a, e_2, _b;
15
+ if (Array.isArray(filter)) {
16
+ var result_1 = [];
17
+ try {
18
+ for (var filter_1 = tslib_1.__values(filter), filter_1_1 = filter_1.next(); !filter_1_1.done; filter_1_1 = filter_1.next()) {
19
+ var item = filter_1_1.value;
20
+ result_1.push(walkFilter(item, visit));
21
+ }
22
+ }
23
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
24
+ finally {
25
+ try {
26
+ if (filter_1_1 && !filter_1_1.done && (_a = filter_1.return)) _a.call(filter_1);
27
+ }
28
+ finally { if (e_1) throw e_1.error; }
29
+ }
30
+ return result_1;
31
+ }
32
+ if (!filter || typeof filter !== 'object')
33
+ return filter;
34
+ var result = {};
35
+ try {
36
+ for (var _c = tslib_1.__values(Object.keys(filter)), _d = _c.next(); !_d.done; _d = _c.next()) {
37
+ var key = _d.value;
38
+ var value = filter[key];
39
+ if (isCompoundFilterKey(key)) {
40
+ result[key] = walkFilter(value, visit);
41
+ continue;
42
+ }
43
+ var next = visit(key, value);
44
+ if (next !== exports.omitFilterField)
45
+ result[key] = next;
46
+ }
47
+ }
48
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
49
+ finally {
50
+ try {
51
+ if (_d && !_d.done && (_b = _c.return)) _b.call(_c);
52
+ }
53
+ finally { if (e_2) throw e_2.error; }
54
+ }
55
+ return result;
56
+ }
@@ -18,6 +18,11 @@ export interface SqlImplementation extends HasWrapIdentifier {
18
18
  export interface HasWrapIdentifier {
19
19
  wrapIdentifier?(name: string): string;
20
20
  }
21
+ /** How a provider stores `Fields.json`: a real json column, a text column, or text only on write (mysql2 parses on read). */
22
+ export type JsonColumnStorage = 'native' | 'text' | 'textOnWrite';
23
+ export interface HasJsonColumnStorage {
24
+ jsonColumnStorage: JsonColumnStorage;
25
+ }
21
26
  export interface SqlCommandFactory extends HasWrapIdentifier {
22
27
  createCommand(): SqlCommand;
23
28
  execute(sql: string): Promise<SqlResult>;
@@ -1,4 +1,5 @@
1
- import type { ValueConverter } from './column-interfaces.js';
1
+ import type { FieldMetadata, ValueConverter } from './column-interfaces.js';
2
+ import type { JsonColumnStorage } from './sql-command.js';
2
3
  export declare class ValueConverters {
3
4
  static readonly Date: ValueConverter<Date>;
4
5
  static readonly DateOnly: ValueConverter<Date>;
@@ -13,3 +14,5 @@ export declare class ValueConverters {
13
14
  static readonly JsonString: ValueConverter<any>;
14
15
  static readonly JsonValue: ValueConverter<any>;
15
16
  }
17
+ export declare function fromDbColumn(field: FieldMetadata, value: any, storage: JsonColumnStorage): any;
18
+ export declare function toDbColumn(field: FieldMetadata, value: any, storage: JsonColumnStorage): any;
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ValueConverters = void 0;
4
+ exports.fromDbColumn = fromDbColumn;
5
+ exports.toDbColumn = toDbColumn;
4
6
  var tslib_1 = require("tslib");
5
7
  var inputTypes_js_1 = require("../inputTypes.js");
6
8
  var zeroDate = new Date('0000-01-01');
@@ -213,6 +215,17 @@ var ValueConverters = /** @class */ (function () {
213
215
  return ValueConverters;
214
216
  }());
215
217
  exports.ValueConverters = ValueConverters;
218
+ function fromDbColumn(field, value, storage) {
219
+ if (storage === 'text' && field.valueConverter.fieldTypeInDb === 'json')
220
+ return ValueConverters.JsonString.fromDb(value);
221
+ return field.valueConverter.fromDb(value);
222
+ }
223
+ function toDbColumn(field, value, storage) {
224
+ if ((storage === 'text' || storage === 'textOnWrite') &&
225
+ field.valueConverter.fieldTypeInDb === 'json')
226
+ return ValueConverters.JsonString.toDb(value);
227
+ return field.valueConverter.toDb(value);
228
+ }
216
229
  function enforceString(value) {
217
230
  if (value === null || value === undefined)
218
231
  return value;