remult 3.3.19-next.2 → 3.4.0-next.2

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 (58) hide show
  1. package/SseSubscriptionServer.d.ts +2 -0
  2. package/SseSubscriptionServer.js +4 -2
  3. package/esm/SseSubscriptionServer.js +3 -1
  4. package/esm/index.js +6 -7
  5. package/esm/live-query/data-provider-live-query-storage.js +3 -1
  6. package/esm/remult-d1.js +0 -1
  7. package/esm/remult-knex/index.js +2 -112
  8. package/esm/remult-mongo.js +8 -17
  9. package/esm/remult-sqlite-core.js +0 -1
  10. package/esm/server/remult-api-server.js +6 -1
  11. package/esm/src/data-api.js +1 -0
  12. package/esm/src/data-interfaces.js +4 -4
  13. package/esm/src/data-providers/array-entity-data-provider.js +2 -14
  14. package/esm/src/data-providers/json-data-provider.js +2 -2
  15. package/esm/src/data-providers/rest-data-provider.js +12 -11
  16. package/esm/src/data-providers/sql-database.js +43 -110
  17. package/esm/src/live-query/LiveQueryClient.js +163 -52
  18. package/esm/src/live-query/SseSubscriptionClient.js +101 -43
  19. package/esm/src/live-query/SubscriptionChannel.js +37 -3
  20. package/esm/src/live-query/SubscriptionServer.js +11 -3
  21. package/esm/src/remult3/RepositoryImplementation.js +60 -90
  22. package/esm/src/remult3/remult3.js +6 -0
  23. package/index.d.ts +17 -31
  24. package/index.js +2 -5
  25. package/live-query/data-provider-live-query-storage.d.ts +4 -1
  26. package/live-query/data-provider-live-query-storage.js +18 -15
  27. package/package.json +1 -1
  28. package/remult-d1.d.ts +0 -1
  29. package/remult-d1.js +0 -1
  30. package/remult-knex/index.js +44 -329
  31. package/remult-mongo.js +36 -80
  32. package/remult-sqlite-core.d.ts +0 -1
  33. package/remult-sqlite-core.js +0 -1
  34. package/server/remult-api-server.js +13 -9
  35. package/src/data-api.js +1 -0
  36. package/src/data-interfaces.d.ts +3 -2
  37. package/src/data-interfaces.js +4 -4
  38. package/src/data-providers/array-entity-data-provider.d.ts +2 -2
  39. package/src/data-providers/array-entity-data-provider.js +14 -92
  40. package/src/data-providers/json-data-provider.js +2 -2
  41. package/src/data-providers/rest-data-provider.d.ts +3 -2
  42. package/src/data-providers/rest-data-provider.js +17 -18
  43. package/src/data-providers/sql-database.js +105 -254
  44. package/src/live-query/LiveQueryClient.js +215 -97
  45. package/src/live-query/SseSubscriptionClient.js +143 -83
  46. package/src/live-query/SubscriptionChannel.d.ts +8 -0
  47. package/src/live-query/SubscriptionChannel.js +47 -9
  48. package/src/live-query/SubscriptionServer.d.ts +7 -2
  49. package/src/live-query/SubscriptionServer.js +20 -10
  50. package/src/remult3/RepositoryImplementation.d.ts +1 -1
  51. package/src/remult3/RepositoryImplementation.js +270 -384
  52. package/src/remult3/remult3.d.ts +8 -2
  53. package/src/remult3/remult3.js +6 -0
  54. package/src/sql-command.d.ts +0 -2
  55. package/src/standard-schema/index.d.ts +1 -1
  56. package/esm/src/data-providers/indexed-db-data-provider.js +0 -811
  57. package/src/data-providers/indexed-db-data-provider.d.ts +0 -39
  58. package/src/data-providers/indexed-db-data-provider.js +0 -1387
@@ -1,6 +1,8 @@
1
1
  import type { GenericResponse } from './server/index.js';
2
2
  import type { Remult } from './src/context.js';
3
3
  import type { SubscriptionServer } from './src/live-query/SubscriptionServer.js';
4
+ /** Keep below typical proxy idle (30s) and Heroku H15 (55s). */
5
+ export declare const ssePingMs = 15000;
4
6
  export declare class SseSubscriptionServer implements SubscriptionServer {
5
7
  private canUserConnectToChannel?;
6
8
  constructor(canUserConnectToChannel?: ((channel: string, remult: Remult) => boolean) | undefined);
@@ -1,8 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.clientConnection = exports.SseSubscriptionServer = void 0;
3
+ exports.clientConnection = exports.SseSubscriptionServer = exports.ssePingMs = void 0;
4
4
  var tslib_1 = require("tslib");
5
5
  var SseSubscriptionClient_js_1 = require("./src/live-query/SseSubscriptionClient.js");
6
+ /** Keep below typical proxy idle (30s) and Heroku H15 (55s). */
7
+ exports.ssePingMs = 15000;
6
8
  var SseSubscriptionServer = /** @class */ (function () {
7
9
  function SseSubscriptionServer(canUserConnectToChannel) {
8
10
  this.canUserConnectToChannel = canUserConnectToChannel;
@@ -137,7 +139,7 @@ var clientConnection = /** @class */ (function () {
137
139
  this.write('', 'keep-alive');
138
140
  this.timeOutRef = setTimeout(function () {
139
141
  _this.sendLiveMessage();
140
- }, 45000);
142
+ }, exports.ssePingMs);
141
143
  };
142
144
  return clientConnection;
143
145
  }());
@@ -1,4 +1,6 @@
1
1
  import { ConnectionNotFoundError } from './src/live-query/SseSubscriptionClient.js';
2
+ /** Keep below typical proxy idle (30s) and Heroku H15 (55s). */
3
+ export const ssePingMs = 15_000;
2
4
  export class SseSubscriptionServer {
3
5
  canUserConnectToChannel;
4
6
  //@internal
@@ -101,6 +103,6 @@ export class clientConnection {
101
103
  this.write('', 'keep-alive');
102
104
  this.timeOutRef = setTimeout(() => {
103
105
  this.sendLiveMessage();
104
- }, 45000);
106
+ }, ssePingMs);
105
107
  }
106
108
  }
package/esm/index.js CHANGED
@@ -9,32 +9,31 @@ export { LabelTransformer } from './src/remult3/RepositoryImplementation.js';
9
9
  export const CaptionTransformer = LabelTransformer;
10
10
  export { Entity } from './src/remult3/entity.js';
11
11
  export { getEntityRef } from './src/remult3/getEntityRef.js';
12
- export { Field, Fields, Relations } from './src/remult3/Fields.js';
12
+ export { Field, Fields, Relations, } from './src/remult3/Fields.js';
13
13
  export { IdEntity } from './src/remult3/IdEntity.js';
14
14
  export { describeClass, describeBackendMethods, describeEntity, } from './src/remult3/classDescribers.js';
15
- export { EntityError } from './src/data-interfaces.js'; //V
15
+ export { EntityError, } from './src/data-interfaces.js'; //V
16
16
  export { RestDataProvider } from './src/data-providers/rest-data-provider.js'; //V
17
17
  export { InMemoryDataProvider } from './src/data-providers/in-memory-database.js'; //V
18
18
  export { SqlDatabase } from './src/data-providers/sql-database.js'; //V
19
19
  export { CustomSqlFilterBuilder, dbNamesOf, } from './src/filter/filter-consumer-bridge-to-sql-request.js';
20
- export { JsonDataProvider } from './src/data-providers/json-data-provider.js'; //V
20
+ export { JsonDataProvider, } from './src/data-providers/json-data-provider.js'; //V
21
21
  export { JsonEntityOpfsStorage } from './src/data-providers/json-entity-opfs-storage.js';
22
22
  export { JsonEntityIndexedDbStorage } from './src/data-providers/json-entity-indexed-db-data-provider.js';
23
- export { IndexedDbDataProvider, IndexedDbIndexBuilder, } from './src/data-providers/indexed-db-data-provider.js';
24
23
  //export * from './src/data-api'; //reconsider if to make internal
25
24
  export { Controller, BackendMethod, ProgressListener, ForbiddenError, } from './src/server-action.js';
26
25
  export { Allow, Remult, withRemult, isBackend, EventSource, } from './src/context.js';
27
26
  export { Sort } from './src/sort.js';
28
27
  export { CompoundIdField } from './src/CompoundIdField.js';
29
- export { Filter } from './src/filter/filter-interfaces.js';
28
+ export { Filter, } from './src/filter/filter-interfaces.js';
30
29
  export { UrlBuilder } from './urlBuilder.js';
31
30
  export { Validators, valueValidator, createValidator, createValidatorWithArgs, createValueValidatorWithArgs, createValueValidator, } from './src/validators.js';
32
31
  export { ValueConverters } from './src/valueConverters.js';
33
32
  export { remult } from './src/remult-proxy.js';
34
33
  import { remult } from './src/remult-proxy.js';
35
34
  //export { getId } from './src/remult3/getId';
36
- export { InMemoryLiveQueryStorage } from './src/live-query/SubscriptionServer.js';
37
- export { SubscriptionChannel } from './src/live-query/SubscriptionChannel.js';
35
+ export { InMemoryLiveQueryStorage, } from './src/live-query/SubscriptionServer.js';
36
+ export { SubscriptionChannel, } from './src/live-query/SubscriptionChannel.js';
38
37
  /**
39
38
  * A convenient shortcut function to quickly obtain a repository for a specific entity type in Remult.
40
39
  *
@@ -52,12 +52,14 @@ export class DataProviderLiveQueryStorage {
52
52
  }
53
53
  async keepAliveAndReturnUnknownQueryIds(queryIds) {
54
54
  const repo = await this.repo;
55
+ const versions = {};
55
56
  for (const query of await repo.find({ where: { id: queryIds } })) {
56
57
  query.lastUsedIso = new Date().toISOString();
57
58
  await repo.save(query);
59
+ versions[query.id] = query.data?.version ?? 0;
58
60
  queryIds = queryIds.filter((x) => x !== query.id);
59
61
  }
60
- return queryIds;
62
+ return { unknownQueryIds: queryIds, versions };
61
63
  }
62
64
  }
63
65
  let LiveQueryStorageEntity = class LiveQueryStorageEntity extends EntityBase {
package/esm/remult-d1.js CHANGED
@@ -6,7 +6,6 @@ export function createD1DataProvider(d1) {
6
6
  }
7
7
  export class D1DataProvider extends SqliteCoreDataProvider {
8
8
  d1;
9
- maxParametersInOneSqlStatement = 100;
10
9
  /**
11
10
  * For production or local d1 using binding
12
11
  *
@@ -234,87 +234,16 @@ class KnexEntityDataProvider {
234
234
  return undefined;
235
235
  return getRowAfterUpdate(this.entity, this, data, id, 'update');
236
236
  }
237
- async delete(ids) {
238
- if (ids.length === 0)
239
- return;
237
+ async delete(id) {
240
238
  const e = await this.init();
241
239
  let f = new FilterConsumerBridgeToKnexRequest(e, this.rawSqlWrapIdentifier);
242
- Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(...ids)).__applyToConsumer(f);
240
+ Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(id)).__applyToConsumer(f);
243
241
  let where = await f.resolveWhere();
244
242
  await this.getEntityFrom(e)
245
243
  .delete()
246
244
  .where((b) => where.forEach((w) => w(b)));
247
245
  }
248
246
  async insert(data, options) {
249
- if (data.length === 0)
250
- return [];
251
- if (isMysqlKnex(this.knex) &&
252
- isAutoIncrement(this.entity.idMetadata.field) &&
253
- options?.select !== 'none') {
254
- const result = [];
255
- for (const row of data)
256
- result.push(await this.insertOne(row, options));
257
- return result;
258
- }
259
- const e = await this.init();
260
- const batches = splitByMaxParameters(data, (row) => countKnexInsertBindParameters(this.entity, e, row), knexMaxParametersInOneSqlStatement(this.knex));
261
- const result = [];
262
- for (const batch of batches)
263
- result.push(...(await this.insertBatch(batch, e, options)));
264
- return result;
265
- }
266
- async insertBatch(batch, e, options) {
267
- const cols = [];
268
- for (const x of this.entity.fields) {
269
- if (isDbReadonly(x, e))
270
- continue;
271
- if (batch.some((row) => translateValueAndHandleArrayAndHandleArray(x, row[x.key]) !=
272
- undefined))
273
- cols.push(x);
274
- }
275
- const insertObjects = [];
276
- for (const row of batch) {
277
- const insertObject = {};
278
- for (const x of cols) {
279
- const v = translateValueAndHandleArrayAndHandleArray(x, row[x.key]);
280
- const key = await e.$dbNameOf(x);
281
- if (v != undefined)
282
- insertObject[key] = v;
283
- else if (x.allowNull)
284
- insertObject[key] = null;
285
- else
286
- insertObject[key] = this.knex.raw('DEFAULT');
287
- }
288
- insertObjects.push(insertObject);
289
- }
290
- let insert = this.getEntityFrom(e).insert(insertObjects);
291
- const wantRows = options?.select !== 'none';
292
- if (isAutoIncrement(this.entity.idMetadata.field) && wantRows) {
293
- const result = await insert.returning(this.entity.idMetadata.field.dbName);
294
- const ids = result.map((r) => r && typeof r === 'object'
295
- ? r[this.entity.idMetadata.field.dbName]
296
- : r);
297
- return this.loadRowsByIds(ids);
298
- }
299
- await insert;
300
- if (!wantRows)
301
- return batch.map(() => undefined);
302
- return this.loadRowsByIds(batch.map((row) => this.entity.idMetadata.getId(row)));
303
- }
304
- async loadRowsByIds(ids) {
305
- const found = await this.find({
306
- where: Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(...ids)),
307
- });
308
- const byId = new Map(found.map((row) => [this.entity.idMetadata.getId(row) + '', row]));
309
- return ids.map((id) => {
310
- const row = byId.get(id + '');
311
- if (!row)
312
- throw new Error(`Failed to insert row - result contained ${found.length} rows`);
313
- return row;
314
- });
315
- }
316
- //@internal
317
- async insertOne(data, options) {
318
247
  const e = await this.init();
319
248
  let insertObject = {};
320
249
  for (const x of this.entity.fields) {
@@ -662,45 +591,6 @@ export async function createKnexDataProvider(config) {
662
591
  let result = new KnexDataProvider(k);
663
592
  return result;
664
593
  }
665
- const defaultMaxParametersInOneSqlStatement = 2000;
666
- function isMysqlKnex(knex) {
667
- const client = knex.client.config.client;
668
- return client === 'mysql2' || client === 'mysql';
669
- }
670
- function knexMaxParametersInOneSqlStatement(knex) {
671
- const client = String(knex.client.config.client ?? '');
672
- if (client.includes('sqlite'))
673
- return 999;
674
- return defaultMaxParametersInOneSqlStatement;
675
- }
676
- function splitByMaxParameters(items, parametersInItem, maxParameters) {
677
- const batches = [];
678
- let batch = [];
679
- let params = 0;
680
- for (const item of items) {
681
- const n = parametersInItem(item);
682
- if (batch.length > 0 && params + n > maxParameters) {
683
- batches.push(batch);
684
- batch = [];
685
- params = 0;
686
- }
687
- batch.push(item);
688
- params += n;
689
- }
690
- if (batch.length)
691
- batches.push(batch);
692
- return batches;
693
- }
694
- function countKnexInsertBindParameters(entity, e, row) {
695
- let n = 0;
696
- for (const x of entity.fields) {
697
- if (isDbReadonly(x, e))
698
- continue;
699
- if (translateValueAndHandleArrayAndHandleArray(x, row[x.key]) != undefined)
700
- n++;
701
- }
702
- return n;
703
- }
704
594
  function translateValueAndHandleArrayAndHandleArray(field, val) {
705
595
  let result = field.valueConverter.toDb(val);
706
596
  if (Array.isArray(result))
@@ -266,31 +266,22 @@ class MongoEntityDataProvider {
266
266
  return undefined;
267
267
  return getRowAfterUpdate(this.entity, this, data, id, 'update');
268
268
  }
269
- async delete(ids) {
270
- if (ids.length === 0)
271
- return;
269
+ async delete(id) {
272
270
  const { e, collection } = await this.collection();
273
271
  let f = new FilterConsumerBridgeToMongo(e);
274
- Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(...ids)).__applyToConsumer(f);
275
- await collection.deleteMany(await f.resolveWhere(), {
272
+ Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(id)).__applyToConsumer(f);
273
+ await collection.deleteOne(await f.resolveWhere(), {
276
274
  session: this.session,
277
275
  });
278
276
  }
279
277
  async insert(data, options) {
280
- if (data.length === 0)
281
- return [];
282
278
  let { collection, e } = await this.collection();
283
- const docs = [];
284
- for (const row of data)
285
- docs.push(await this.translateToDb(row, e));
286
- const r = await collection.insertMany(docs, { session: this.session });
279
+ let r = await collection.insertOne(await this.translateToDb(data, e), {
280
+ session: this.session,
281
+ });
287
282
  if (options?.select === 'none')
288
- return data.map(() => undefined);
289
- const result = [];
290
- for (let i = 0; i < data.length; i++) {
291
- result.push(await this.translateFromDb(await collection.findOne({ _id: r.insertedIds[i] }, { session: this.session }), e));
292
- }
293
- return result;
283
+ return undefined;
284
+ return await this.translateFromDb(await collection.findOne({ _id: r.insertedId }, { session: this.session }), e);
294
285
  }
295
286
  async collection() {
296
287
  const e = await dbNamesOf(this.entity);
@@ -13,7 +13,6 @@ export class SqliteCoreDataProvider {
13
13
  this.doesNotSupportReturningSyntaxOnlyForUpdate = doesNotSupportReturningSyntaxOnlyForUpdate;
14
14
  }
15
15
  orderByNullsFirst;
16
- maxParametersInOneSqlStatement = 999;
17
16
  getLimitSqlSyntax(limit, offset) {
18
17
  return ' limit ' + limit + ' offset ' + offset;
19
18
  }
@@ -281,7 +281,12 @@ export class RemultServerImplementation {
281
281
  }));
282
282
  }
283
283
  r.route(this.options.rootPath + '/' + liveQueryKeepAliveRoute).post(this.process(async (remult, req, res, reqInfo, origRes, origReq) => {
284
- res.success(await remult.liveQueryStorage.keepAliveAndReturnUnknownQueryIds(await this.coreOptions.getRequestBody(origReq)));
284
+ const body = await this.coreOptions.getRequestBody(origReq);
285
+ const queryIds = Array.isArray(body)
286
+ ? body
287
+ : (body?.queryIds ?? []);
288
+ const result = await remult.liveQueryStorage.keepAliveAndReturnUnknownQueryIds(queryIds);
289
+ res.success(Array.isArray(body) ? result.unknownQueryIds : result);
285
290
  }));
286
291
  }
287
292
  this.entities.forEach((e) => {
@@ -358,6 +358,7 @@ export class DataApi {
358
358
  requestJson: await serializeContext(),
359
359
  findOptionsJson: findOptionsToJson(r.findOptions, this.repository.metadata),
360
360
  lastIds: r.r.map((y) => this.repository.metadata.idMetadata.getId(y)),
361
+ version: 0,
361
362
  };
362
363
  await this.remult.liveQueryStorage.add({
363
364
  entityKey: this.repository.metadata.key,
@@ -40,11 +40,11 @@ class EntityDataProviderPromiseWrapper {
40
40
  constructor(dataProvider) {
41
41
  this.dataProvider = dataProvider;
42
42
  }
43
- delete(ids) {
44
- return this.dataProvider.then((dp) => dp.delete(ids));
43
+ delete(id) {
44
+ return this.dataProvider.then((dp) => dp.delete(id));
45
45
  }
46
- insert(data, options) {
47
- return this.dataProvider.then((dp) => dp.insert(data, options));
46
+ insert(data) {
47
+ return this.dataProvider.then((dp) => dp.insert(data));
48
48
  }
49
49
  count(where) {
50
50
  return this.dataProvider.then((dp) => dp.count(where));
@@ -318,30 +318,18 @@ export class ArrayEntityDataProvider {
318
318
  }
319
319
  throw new Error(`ArrayEntityDataProvider: Couldn't find row with id "${id}" in entity "${this.entity.key}" to update`);
320
320
  }
321
- async delete(ids) {
322
- for (const id of ids)
323
- await this.deleteOne(id);
324
- }
325
- //@internal
326
- async deleteOne(id) {
321
+ async delete(id) {
327
322
  const names = await this.init();
328
323
  let idMatches = this.idMatches(id, names);
329
324
  for (let i = 0; i < this.rows().length; i++) {
330
325
  if (idMatches(this.rows()[i])) {
331
326
  this.rows().splice(i, 1);
332
- return;
327
+ return Promise.resolve();
333
328
  }
334
329
  }
335
330
  throw new Error(`ArrayEntityDataProvider: Couldn't find row with id "${id}" in entity "${this.entity.key}" to delete`);
336
331
  }
337
332
  async insert(data) {
338
- const result = [];
339
- for (const row of data)
340
- result.push(await this.insertOne(row));
341
- return result;
342
- }
343
- //@internal
344
- async insertOne(data) {
345
333
  const names = await this.init();
346
334
  let j = this.translateToJson(data, names);
347
335
  let idf = this.entity.idMetadata.field;
@@ -49,8 +49,8 @@ class JsonEntityDataProvider {
49
49
  return x;
50
50
  }))));
51
51
  }
52
- delete(ids) {
53
- return (this.p = this.p.then(() => this.loadEntityData((dp, save) => dp.delete(ids).then(async (x) => {
52
+ delete(id) {
53
+ return (this.p = this.p.then(() => this.loadEntityData((dp, save) => dp.delete(id).then(async (x) => {
54
54
  await save();
55
55
  return x;
56
56
  }))));
@@ -303,20 +303,21 @@ export class RestEntityDataProvider {
303
303
  }
304
304
  return result;
305
305
  }
306
- async delete(ids) {
307
- if (ids.length === 0)
308
- return;
309
- if (ids.length === 1 && ids[0] !== '')
310
- return this.http().delete(this.url() + '/' + encodeURIComponent(ids[0]));
311
- await this.deleteMany(Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(...ids)));
306
+ async delete(id) {
307
+ if (id == '')
308
+ await this.deleteMany(Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(id)));
309
+ else
310
+ return this.http().delete(this.url() + '/' + encodeURIComponent(id));
312
311
  }
313
312
  insert(data, options) {
314
313
  return this.http()
315
- .post(this.url() + (options?.select === 'none' ? '?_select=$none' : ''), data.map((row) => this.translateToJson(row)))
316
- .then((y) => {
317
- const rows = Array.isArray(y) ? y : [y];
318
- return rows.map((row) => row ? this.translateFromJson(row) : undefined);
319
- });
314
+ .post(this.url() + (options?.select === 'none' ? '?_select=$none' : ''), this.translateToJson(data))
315
+ .then((y) => this.translateFromJson(y));
316
+ }
317
+ insertMany(data, options) {
318
+ return this.http()
319
+ .post(this.url() + (options?.select === 'none' ? '?_select=$none' : ''), data.map((data) => this.translateToJson(data)))
320
+ .then((y) => y.map((y) => this.translateFromJson(y)));
320
321
  }
321
322
  }
322
323
  export function addFilterToUrlAndReturnTrueIfSuccessful(filter, url) {
@@ -117,8 +117,6 @@ export class SqlDatabase {
117
117
  doesNotSupportReturningSyntax: this.sql.doesNotSupportReturningSyntax,
118
118
  doesNotSupportReturningSyntaxOnlyForUpdate: this.sql.doesNotSupportReturningSyntaxOnlyForUpdate,
119
119
  orderByNullsFirst: this.sql.orderByNullsFirst,
120
- afterMutation: this.sql.afterMutation,
121
- maxParametersInOneSqlStatement: this.sql.maxParametersInOneSqlStatement,
122
120
  }));
123
121
  }
124
122
  finally {
@@ -195,35 +193,6 @@ export class SqlDatabase {
195
193
  createdEntities = [];
196
194
  end;
197
195
  }
198
- const defaultMaxParametersInOneSqlStatement = 2000;
199
- function splitByMaxParameters(items, parametersInItem, maxParameters) {
200
- const batches = [];
201
- let batch = [];
202
- let params = 0;
203
- for (const item of items) {
204
- const n = parametersInItem(item);
205
- if (batch.length > 0 && params + n > maxParameters) {
206
- batches.push(batch);
207
- batch = [];
208
- params = 0;
209
- }
210
- batch.push(item);
211
- params += n;
212
- }
213
- if (batch.length)
214
- batches.push(batch);
215
- return batches;
216
- }
217
- function countInsertBindParameters(entity, e, row) {
218
- let n = 0;
219
- for (const x of entity.fields) {
220
- if (isDbReadonly(x, e))
221
- continue;
222
- if (x.valueConverter.toDb(row[x.key]) != undefined)
223
- n++;
224
- }
225
- return n;
226
- }
227
196
  const icons = new Map([
228
197
  // CRUD
229
198
  ['INSERT', '⚪'], // Used to insert new data into a database.
@@ -472,13 +441,11 @@ class ActualSQLEntityDataProvider {
472
441
  return this.buildResultRow(colKeys, sqlResult.rows[0], sqlResult);
473
442
  });
474
443
  }
475
- async delete(ids) {
476
- if (ids.length === 0)
477
- return;
444
+ async delete(id) {
478
445
  let e = await this.init();
479
446
  let r = this.sql.createCommand();
480
447
  let f = new FilterConsumerBridgeToSqlRequest(r, e);
481
- Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(...ids)).__applyToConsumer(f);
448
+ Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(id)).__applyToConsumer(f);
482
449
  let statement = 'delete from ' + e.$entityName;
483
450
  statement += await f.resolveWhere();
484
451
  return r.execute(statement).then(() => {
@@ -486,90 +453,56 @@ class ActualSQLEntityDataProvider {
486
453
  });
487
454
  }
488
455
  async insert(data, options) {
489
- if (data.length === 0)
490
- return [];
491
456
  let e = await this.init();
492
- const maxParams = this.strategy.maxParametersInOneSqlStatement ??
493
- defaultMaxParametersInOneSqlStatement;
494
- const batches = splitByMaxParameters(data, (row) => countInsertBindParameters(this.entity, e, row), maxParams);
495
- const result = [];
496
- for (const batch of batches)
497
- result.push(...(await this.insertBatch(batch, e, options)));
498
- return result;
499
- }
500
- //@internal
501
- async insertOne(data, options) {
502
- return (await this.insert([data], options))[0];
503
- }
504
- async insertBatch(batch, e, options) {
505
457
  let r = this.sql.createCommand();
506
- const cols = [];
507
- for (const x of this.entity.fields) {
508
- if (isDbReadonly(x, e))
509
- continue;
510
- if (batch.some((row) => x.valueConverter.toDb(row[x.key]) != undefined))
511
- cols.push(x);
512
- }
513
- let colSql = '';
458
+ let cols = '';
514
459
  let vals = '';
515
- for (let i = 0; i < cols.length; i++) {
516
- if (i)
517
- colSql += ', ';
518
- colSql += e.$dbNameOf(cols[i]);
519
- }
520
- for (let i = 0; i < batch.length; i++) {
521
- if (i)
522
- vals += ',';
523
- vals += '(';
524
- for (let j = 0; j < cols.length; j++) {
525
- if (j)
526
- vals += ', ';
527
- const x = cols[j];
528
- const v = x.valueConverter.toDb(batch[i][x.key]);
529
- if (v != undefined)
460
+ let added = false;
461
+ for (const x of this.entity.fields) {
462
+ if (isDbReadonly(x, e)) {
463
+ }
464
+ else {
465
+ let v = x.valueConverter.toDb(data[x.key]);
466
+ if (v != undefined) {
467
+ if (!added)
468
+ added = true;
469
+ else {
470
+ cols += ', ';
471
+ vals += ', ';
472
+ }
473
+ cols += e.$dbNameOf(x);
530
474
  vals += toDbSql(r, x, v);
531
- else if (x.allowNull)
532
- vals += 'null';
533
- else
534
- vals += 'DEFAULT';
475
+ }
535
476
  }
536
- vals += ')';
537
477
  }
538
- let statement = `insert into ${e.$entityName} (${colSql}) values ${vals}`;
478
+ let statement = `insert into ${e.$entityName} (${cols}) values (${vals})`;
539
479
  let { colKeys, select } = await this.buildSelect(e, r, undefined, undefined);
540
- const source = this.sql._getSourceSql();
541
- const wantRows = options?.select !== 'none';
542
- if (!source.doesNotSupportReturningSyntax && wantRows)
480
+ if (!this.sql._getSourceSql().doesNotSupportReturningSyntax &&
481
+ !(options?.select === 'none'))
543
482
  statement += ' returning ' + select;
544
- const sql = await r.execute(statement);
545
- source.afterMutation?.();
546
- if (!wantRows)
547
- return batch.map(() => undefined);
548
- if (source.doesNotSupportReturningSyntax) {
549
- if (isAutoIncrement(this.entity.idMetadata.field)) {
550
- const lastId = sql.rows[0];
551
- if (typeof lastId !== 'number')
552
- throw new Error('Auto increment, for a database that is does not support returning syntax, should return an array with the single last added id. Instead it returned: ' +
553
- JSON.stringify(lastId));
554
- const ids = [];
555
- for (let id = lastId - batch.length + 1; id <= lastId; id++)
556
- ids.push(id);
557
- return this.loadRowsByIds(ids);
483
+ return await r.execute(statement).then((sql) => {
484
+ this.sql._getSourceSql().afterMutation?.();
485
+ if (this.sql._getSourceSql().doesNotSupportReturningSyntax) {
486
+ if (isAutoIncrement(this.entity.idMetadata.field)) {
487
+ const id = sql.rows[0];
488
+ if (typeof id !== 'number')
489
+ throw new Error('Auto increment, for a database that is does not support returning syntax, should return an array with the single last added id. Instead it returned: ' +
490
+ JSON.stringify(id));
491
+ if (options?.select === 'none')
492
+ return undefined;
493
+ return this.find({
494
+ where: new Filter((x) => x.isEqualTo(this.entity.idMetadata.field, id)),
495
+ }).then((r) => r[0]);
496
+ }
497
+ else {
498
+ if (options?.select === 'none')
499
+ return undefined;
500
+ return getRowAfterUpdate(this.entity, this, data, undefined, 'insert');
501
+ }
558
502
  }
559
- return this.loadRowsByIds(batch.map((row) => this.entity.idMetadata.getId(row)));
560
- }
561
- return sql.rows.map((row) => this.buildResultRow(colKeys, row, sql));
562
- }
563
- async loadRowsByIds(ids) {
564
- const found = await this.find({
565
- where: Filter.fromEntityFilter(this.entity, this.entity.idMetadata.getIdFilter(...ids)),
566
- });
567
- const byId = new Map(found.map((row) => [this.entity.idMetadata.getId(row) + '', row]));
568
- return ids.map((id) => {
569
- const row = byId.get(id + '');
570
- if (!row)
571
- throw new Error(`Failed to insert row - result contained ${found.length} rows`);
572
- return row;
503
+ if (options?.select === 'none')
504
+ return undefined;
505
+ return this.buildResultRow(colKeys, sql.rows[0], sql);
573
506
  });
574
507
  }
575
508
  }