orange-orm 5.3.6 → 5.4.0-beta

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.
@@ -269,6 +269,8 @@ export interface ${name}Strategy {
269
269
  offset?: number;
270
270
  orderBy?: Array<${orderByColumns(table)}> | ${orderByColumns(table)};
271
271
  where?: (table: ${name}TableBase) => RawFilter;
272
+ forUpdate?: boolean;
273
+ skipLocked?: boolean;
272
274
  }
273
275
 
274
276
  ${otherConcurrency}
@@ -1836,7 +1838,7 @@ function requireExecutePath () {
1836
1838
 
1837
1839
  async function replace(subject, strategy = { insertAndForget: true }) {
1838
1840
  validateStrategy(table, strategy);
1839
- const refinedStrategy = objectToStrategy(subject, {}, table);
1841
+ const refinedStrategy = withLockingStrategy(objectToStrategy(subject, {}, table), strategy);
1840
1842
  const JSONFilter2 = {
1841
1843
  path: 'getManyDto',
1842
1844
  args: [subject, refinedStrategy]
@@ -1853,7 +1855,7 @@ function requireExecutePath () {
1853
1855
 
1854
1856
  async function update(subject, whereStrategy, strategy = { insertAndForget: true }) {
1855
1857
  validateStrategy(table, strategy);
1856
- const refinedWhereStrategy = objectToStrategy(subject, whereStrategy, table);
1858
+ const refinedWhereStrategy = withLockingStrategy(objectToStrategy(subject, whereStrategy, table), strategy);
1857
1859
  const JSONFilter2 = {
1858
1860
  path: 'getManyDto',
1859
1861
  args: [null, refinedWhereStrategy]
@@ -1873,6 +1875,43 @@ function requireExecutePath () {
1873
1875
  return changed;
1874
1876
  }
1875
1877
 
1878
+ function withLockingStrategy(fetchStrategy, strategy) {
1879
+ const lockStrategy = extractLockingStrategy(strategy);
1880
+ if (!lockStrategy)
1881
+ return fetchStrategy;
1882
+ return mergeLockingStrategy(fetchStrategy, lockStrategy);
1883
+ }
1884
+
1885
+ function extractLockingStrategy(strategy) {
1886
+ if (!strategy || typeof strategy !== 'object')
1887
+ return;
1888
+ const result = {};
1889
+ if (strategy.forUpdate)
1890
+ result.forUpdate = strategy.forUpdate;
1891
+ if (strategy.skipLocked)
1892
+ result.skipLocked = strategy.skipLocked;
1893
+ for (let name in strategy) {
1894
+ if (name === 'where' || name === 'orderBy' || name === 'limit' || name === 'offset' || name === 'forUpdate' || name === 'skipLocked')
1895
+ continue;
1896
+ const child = extractLockingStrategy(strategy[name]);
1897
+ if (child)
1898
+ result[name] = child;
1899
+ }
1900
+ return Object.keys(result).length > 0 ? result : undefined;
1901
+ }
1902
+
1903
+ function mergeLockingStrategy(fetchStrategy, lockStrategy) {
1904
+ const result = { ...fetchStrategy };
1905
+ for (let name in lockStrategy) {
1906
+ const value = lockStrategy[name];
1907
+ if (name === 'forUpdate' || name === 'skipLocked')
1908
+ result[name] = value;
1909
+ else
1910
+ result[name] = mergeLockingStrategy(result[name] && typeof result[name] === 'object' ? result[name] : {}, value);
1911
+ }
1912
+ return result;
1913
+ }
1914
+
1876
1915
  function objectToStrategy(object, whereStrategy, table, strategy = {}) {
1877
1916
  strategy = { ...whereStrategy, ...strategy };
1878
1917
  if (Array.isArray(object)) {
@@ -2694,7 +2733,7 @@ function requireHostLocal () {
2694
2733
  || beforeCommit
2695
2734
  || afterCommit
2696
2735
  || afterRollback);
2697
- if (!hasTransactionHooks && readonlyOps.includes(body.path))
2736
+ if (!hasTransactionHooks && readonlyOps.includes(body.path) && !hasLockingStrategy(body))
2698
2737
  await db.transaction({ readonly: true }, fn);
2699
2738
  else {
2700
2739
  await db.transaction(async (context) => {
@@ -2728,6 +2767,24 @@ function requireHostLocal () {
2728
2767
  result = await executePath(context, options);
2729
2768
  }
2730
2769
  }
2770
+
2771
+ function hasLockingStrategy(body) {
2772
+ if (!body || !body.args)
2773
+ return false;
2774
+ return hasLockingStrategyCore(body.args[1]);
2775
+ }
2776
+
2777
+ function hasLockingStrategyCore(strategy) {
2778
+ if (!strategy || typeof strategy !== 'object')
2779
+ return false;
2780
+ if (strategy.forUpdate || strategy.skipLocked)
2781
+ return true;
2782
+ for (let name in strategy) {
2783
+ if (name !== 'where' && hasLockingStrategyCore(strategy[name]))
2784
+ return true;
2785
+ }
2786
+ return false;
2787
+ }
2731
2788
  async function query() {
2732
2789
  let args = arguments;
2733
2790
  let result;
@@ -3593,7 +3650,7 @@ function requireClient () {
3593
3650
  return false;
3594
3651
  if (Object.keys(value).length === 0)
3595
3652
  return true;
3596
- if ('where' in value || 'orderBy' in value || 'limit' in value || 'offset' in value)
3653
+ if ('where' in value || 'orderBy' in value || 'limit' in value || 'offset' in value || 'forUpdate' in value || 'skipLocked' in value)
3597
3654
  return true;
3598
3655
  for (let key in value) {
3599
3656
  const v = value[key];
@@ -3843,6 +3900,7 @@ function requireClient () {
3843
3900
 
3844
3901
  function proxifyArray(array, strategy, fast) {
3845
3902
  let _array = array;
3903
+ const storedStrategy = toStoredFetchStrategy(strategy);
3846
3904
  if (_reactive)
3847
3905
  array = _reactive(array);
3848
3906
  let handler = {
@@ -3870,17 +3928,17 @@ function requireClient () {
3870
3928
  };
3871
3929
 
3872
3930
  let watcher = onChange(array, () => {
3873
- rootMap.set(array, { json: cloneFromDb(array, fast), strategy, originalArray: [...array] });
3931
+ rootMap.set(array, { json: cloneFromDb(array, fast), strategy: storedStrategy, originalArray: [...array] });
3874
3932
  });
3875
3933
  let innerProxy = new Proxy(watcher, handler);
3876
- if (strategy !== undefined) {
3877
- const { limit, ...cleanStrategy } = { ...strategy };
3878
- fetchingStrategyMap.set(array, cleanStrategy);
3934
+ if (storedStrategy !== undefined) {
3935
+ fetchingStrategyMap.set(array, storedStrategy);
3879
3936
  }
3880
3937
  return innerProxy;
3881
3938
  }
3882
3939
 
3883
3940
  function proxifyRow(row, strategy, fast) {
3941
+ const storedStrategy = toStoredFetchStrategy(strategy);
3884
3942
  let handler = {
3885
3943
  get(_target, property,) {
3886
3944
  if (property === 'save' || property === 'saveChanges') //call server then acceptChanges
@@ -3905,10 +3963,10 @@ function requireClient () {
3905
3963
 
3906
3964
  };
3907
3965
  let watcher = onChange(row, () => {
3908
- rootMap.set(row, { json: cloneFromDb(row, fast), strategy });
3966
+ rootMap.set(row, { json: cloneFromDb(row, fast), strategy: storedStrategy });
3909
3967
  });
3910
3968
  let innerProxy = new Proxy(watcher, handler);
3911
- fetchingStrategyMap.set(row, strategy);
3969
+ fetchingStrategyMap.set(row, storedStrategy);
3912
3970
  return innerProxy;
3913
3971
  }
3914
3972
 
@@ -3999,7 +4057,7 @@ function requireClient () {
3999
4057
  let insertedPositions = getInsertedRowsPosition(array);
4000
4058
  let { changed, strategy: newStrategy } = await p;
4001
4059
  copyIntoArray(changed, array, [...insertedPositions, ...updatedPositions]);
4002
- rootMap.set(array, { json: cloneFromDb(array), strategy: newStrategy, originalArray: [...array] });
4060
+ rootMap.set(array, { json: cloneFromDb(array), strategy: toStoredFetchStrategy(newStrategy), originalArray: [...array] });
4003
4061
  }
4004
4062
 
4005
4063
  async function patch(patch, concurrencyOptions, strategy) {
@@ -4061,8 +4119,7 @@ function requireClient () {
4061
4119
  let context = rootMap.get(obj);
4062
4120
  if (context?.strategy !== undefined) {
4063
4121
  // @ts-ignore
4064
- let { limit, ...strategy } = { ...context.strategy };
4065
- return strategy;
4122
+ return toStoredFetchStrategy(context.strategy);
4066
4123
  }
4067
4124
  }
4068
4125
  }
@@ -4072,9 +4129,24 @@ function requireClient () {
4072
4129
  return strategy;
4073
4130
  else if (fetchingStrategyMap.get(obj) !== undefined) {
4074
4131
  // @ts-ignore
4075
- const { limit, ...strategy } = { ...fetchingStrategyMap.get(obj) };
4132
+ return toStoredFetchStrategy(fetchingStrategyMap.get(obj));
4133
+ }
4134
+ }
4135
+
4136
+ function toStoredFetchStrategy(strategy) {
4137
+ if (strategy === undefined || strategy === null || typeof strategy !== 'object')
4076
4138
  return strategy;
4139
+ if (Array.isArray(strategy))
4140
+ return strategy.map(toStoredFetchStrategy);
4141
+ const cleanStrategy = { ...strategy };
4142
+ delete cleanStrategy.limit;
4143
+ delete cleanStrategy.forUpdate;
4144
+ delete cleanStrategy.skipLocked;
4145
+ for (let name in cleanStrategy) {
4146
+ if (name !== 'where' && cleanStrategy[name] && typeof cleanStrategy[name] === 'object')
4147
+ cleanStrategy[name] = toStoredFetchStrategy(cleanStrategy[name]);
4077
4148
  }
4149
+ return cleanStrategy;
4078
4150
  }
4079
4151
 
4080
4152
  function clearChangesArray(array) {
@@ -4165,8 +4237,9 @@ function requireClient () {
4165
4237
  array.splice(i + offset, 1);
4166
4238
  offset--;
4167
4239
  }
4168
- rootMap.set(array, { json: cloneFromDb(array), strategy, originalArray: [...array] });
4169
- fetchingStrategyMap.set(array, strategy);
4240
+ const storedStrategy = toStoredFetchStrategy(strategy);
4241
+ rootMap.set(array, { json: cloneFromDb(array), strategy: storedStrategy, originalArray: [...array] });
4242
+ fetchingStrategyMap.set(array, storedStrategy);
4170
4243
  }
4171
4244
 
4172
4245
  async function deleteRow(row, options) {
@@ -4200,7 +4273,7 @@ function requireClient () {
4200
4273
  let adapter = netAdapter(url, tableName, { http: httpInterceptor, tableOptions });
4201
4274
  let { changed, strategy: newStrategy } = await adapter.patch(body);
4202
4275
  copyInto(changed, [row]);
4203
- rootMap.set(row, { json: cloneFromDb(row), strategy: newStrategy });
4276
+ rootMap.set(row, { json: cloneFromDb(row), strategy: toStoredFetchStrategy(newStrategy) });
4204
4277
  }
4205
4278
 
4206
4279
  async function refreshRow(row, strategy) {
@@ -4224,8 +4297,9 @@ function requireClient () {
4224
4297
  for (let p in rows[0]) {
4225
4298
  row[p] = rows[0][p];
4226
4299
  }
4227
- rootMap.set(row, { json: cloneFromDb(row), strategy });
4228
- fetchingStrategyMap.set(row, strategy);
4300
+ const storedStrategy = toStoredFetchStrategy(strategy);
4301
+ rootMap.set(row, { json: cloneFromDb(row), strategy: storedStrategy });
4302
+ fetchingStrategyMap.set(row, storedStrategy);
4229
4303
  }
4230
4304
 
4231
4305
  function acceptChangesRow(row) {
@@ -6731,8 +6805,8 @@ function requireNewPrimaryKeyFilter () {
6731
6805
  var primaryColumns = table._primaryColumns;
6732
6806
  var key = arguments[2];
6733
6807
  var filter = primaryColumns[0].equal(context, key);
6734
- for (var i = 2; i < primaryColumns.length; i++) {
6735
- key = arguments[i+1];
6808
+ for (var i = 1; i < primaryColumns.length; i++) {
6809
+ key = arguments[i + 2];
6736
6810
  var colFilter = primaryColumns[i].equal(context, key);
6737
6811
  filter = filter.and(context, colFilter);
6738
6812
  }
@@ -6869,6 +6943,89 @@ function requireNewColumnSql () {
6869
6943
  return newColumnSql;
6870
6944
  }
6871
6945
 
6946
+ var lockSql;
6947
+ var hasRequiredLockSql;
6948
+
6949
+ function requireLockSql () {
6950
+ if (hasRequiredLockSql) return lockSql;
6951
+ hasRequiredLockSql = 1;
6952
+ const getSessionSingleton = requireGetSessionSingleton();
6953
+
6954
+ function selectLockSql(context, span, alias, exclusive) {
6955
+ const lock = collectSelectLock(span, alias, exclusive);
6956
+ if (!hasLock(lock))
6957
+ return '';
6958
+ const encode = getSessionSingleton(context, 'selectForUpdateSql');
6959
+ if (!encode)
6960
+ return '';
6961
+ return encode(context, lock);
6962
+ }
6963
+
6964
+ function tableHintSql(context, span, exclusive) {
6965
+ const lock = spanToLock(span, exclusive);
6966
+ if (!hasLock(lock))
6967
+ return '';
6968
+ const encode = getSessionSingleton(context, 'selectForUpdateSql');
6969
+ if (!encode || !encode.tableHint)
6970
+ return '';
6971
+ return encode.tableHint(context, lock);
6972
+ }
6973
+
6974
+ function collectSelectLock(span, alias, exclusive) {
6975
+ const lock = {
6976
+ aliases: [],
6977
+ forUpdate: Boolean(exclusive),
6978
+ skipLocked: false
6979
+ };
6980
+ collect(span, alias, lock);
6981
+ if (exclusive && lock.aliases.indexOf(alias) === -1)
6982
+ lock.aliases.unshift(alias);
6983
+ return lock;
6984
+ }
6985
+
6986
+ function collect(span, alias, lock) {
6987
+ if (!span)
6988
+ return;
6989
+ if (span.forUpdate) {
6990
+ lock.forUpdate = true;
6991
+ lock.aliases.push(alias);
6992
+ }
6993
+ if (span.skipLocked)
6994
+ lock.skipLocked = true;
6995
+
6996
+ if (!span.legs)
6997
+ return;
6998
+ const visitor = {};
6999
+ visitor.visitJoin = visitJoinedLeg;
7000
+ visitor.visitOne = visitJoinedLeg;
7001
+ visitor.visitMany = function() {};
7002
+
7003
+ function visitJoinedLeg(leg) {
7004
+ collect(leg.span, alias + leg.name, lock);
7005
+ }
7006
+
7007
+ span.legs.forEach(leg => leg.accept(visitor));
7008
+ }
7009
+
7010
+ function spanToLock(span, exclusive) {
7011
+ return {
7012
+ aliases: [],
7013
+ forUpdate: Boolean(exclusive || span?.forUpdate),
7014
+ skipLocked: Boolean(span?.skipLocked)
7015
+ };
7016
+ }
7017
+
7018
+ function hasLock(lock) {
7019
+ return Boolean(lock.forUpdate || lock.skipLocked);
7020
+ }
7021
+
7022
+ lockSql = {
7023
+ selectLockSql,
7024
+ tableHintSql
7025
+ };
7026
+ return lockSql;
7027
+ }
7028
+
6872
7029
  var newShallowJoinSql;
6873
7030
  var hasRequiredNewShallowJoinSql;
6874
7031
 
@@ -6877,10 +7034,12 @@ function requireNewShallowJoinSql () {
6877
7034
  hasRequiredNewShallowJoinSql = 1;
6878
7035
  const newJoinCore = requireNewShallowJoinSqlCore();
6879
7036
  const getSessionSingleton = requireGetSessionSingleton();
7037
+ const lockSql = requireLockSql();
6880
7038
 
6881
- function _new(context, rightTable, leftColumns, rightColumns, leftAlias, rightAlias, filter) {
7039
+ function _new(context, rightTable, leftColumns, rightColumns, leftAlias, rightAlias, filter, span) {
6882
7040
  const quote = getSessionSingleton(context, 'quote');
6883
- const sql = ' JOIN ' + quote(rightTable._dbName) + ' ' + quote(rightAlias) + ' ON (';
7041
+ const tableHint = lockSql.tableHintSql(context, span);
7042
+ const sql = ' JOIN ' + quote(rightTable._dbName) + ' ' + quote(rightAlias) + tableHint + ' ON (';
6884
7043
  const joinCore = newJoinCore(context, rightTable, leftColumns, rightColumns, leftAlias, rightAlias, filter);
6885
7044
  return joinCore.prepend(sql).append(')');
6886
7045
  }
@@ -6900,7 +7059,7 @@ function requireJoinLegToShallowJoinSql () {
6900
7059
  function toJoinSql(context,leg,alias,childAlias) {
6901
7060
  var columns = leg.columns;
6902
7061
  var childTable = leg.span.table;
6903
- return newShallowJoinSql(context,childTable,columns,childTable._primaryColumns,alias,childAlias,leg.span.where).prepend(' LEFT');
7062
+ return newShallowJoinSql(context,childTable,columns,childTable._primaryColumns,alias,childAlias,leg.span.where,leg.span).prepend(' LEFT');
6904
7063
  }
6905
7064
 
6906
7065
  joinLegToShallowJoinSql = toJoinSql;
@@ -6936,7 +7095,7 @@ function requireOneLegToShallowJoinSql () {
6936
7095
  var parentTable = leg.table;
6937
7096
  var columns = leg.columns;
6938
7097
  var childTable = leg.span.table;
6939
- return newShallowJoinSql(context,childTable,parentTable._primaryColumns,columns,alias,childAlias, leg.span.where).prepend(' LEFT');
7098
+ return newShallowJoinSql(context,childTable,parentTable._primaryColumns,columns,alias,childAlias, leg.span.where,leg.span).prepend(' LEFT');
6940
7099
  }
6941
7100
 
6942
7101
  oneLegToShallowJoinSql = toJoinSql;
@@ -7056,26 +7215,6 @@ function requireNegotiateLimit () {
7056
7215
  return negotiateLimit_1;
7057
7216
  }
7058
7217
 
7059
- var negotiateExclusive_1;
7060
- var hasRequiredNegotiateExclusive;
7061
-
7062
- function requireNegotiateExclusive () {
7063
- if (hasRequiredNegotiateExclusive) return negotiateExclusive_1;
7064
- hasRequiredNegotiateExclusive = 1;
7065
- var getSessionSingleton = requireGetSessionSingleton();
7066
-
7067
- function negotiateExclusive(context, table, alias, _exclusive) {
7068
- if (table._exclusive || _exclusive) {
7069
- var encode = getSessionSingleton(context, 'selectForUpdateSql');
7070
- return encode(context, alias);
7071
- }
7072
- return '';
7073
- }
7074
-
7075
- negotiateExclusive_1 = negotiateExclusive;
7076
- return negotiateExclusive_1;
7077
- }
7078
-
7079
7218
  var newSingleQuery$1;
7080
7219
  var hasRequiredNewSingleQuery$1;
7081
7220
 
@@ -7086,23 +7225,25 @@ function requireNewSingleQuery$1 () {
7086
7225
  var newJoinSql = requireNewJoinSql();
7087
7226
  var newWhereSql = requireNewWhereSql();
7088
7227
  var negotiateLimit = requireNegotiateLimit();
7089
- var negotiateExclusive = requireNegotiateExclusive();
7228
+ var lockSql = requireLockSql();
7090
7229
  var newParameterized = requireNewParameterized();
7091
7230
  var quote = requireQuote$2();
7092
7231
 
7093
7232
  function _new(context, table, filter, span, alias, innerJoin, orderBy, limit, offset, exclusive) {
7094
7233
 
7095
7234
  var name = quote(context, table._dbName);
7235
+ var quotedAlias = quote(context, alias);
7096
7236
  var columnSql = newColumnSql(context, table, span, alias);
7097
7237
  var joinSql = newJoinSql(context, span, alias);
7098
7238
  var whereSql = newWhereSql(context, table, filter, alias);
7099
7239
  var safeLimit = negotiateLimit(limit);
7100
- var exclusiveClause = negotiateExclusive(table, alias, exclusive);
7101
- return newParameterized('select' + safeLimit + ' ' + columnSql + ' from ' + name + ' ' + quote(context, alias))
7240
+ var lockClause = lockSql.selectLockSql(context, span, alias, exclusive);
7241
+ var tableHint = lockSql.tableHintSql(context, span, exclusive);
7242
+ return newParameterized('select' + safeLimit + ' ' + columnSql + ' from ' + name + ' ' + quotedAlias + tableHint)
7102
7243
  .append(innerJoin)
7103
7244
  .append(joinSql)
7104
7245
  .append(whereSql)
7105
- .append(orderBy + offset + exclusiveClause);
7246
+ .append(orderBy + offset + lockClause);
7106
7247
  }
7107
7248
 
7108
7249
  newSingleQuery$1 = _new;
@@ -7368,7 +7509,7 @@ function requireNewUpdateCommandCore () {
7368
7509
  const encoded = encodeJsonValue(pathState.oldValue, column);
7369
7510
  const jsonPath = buildJsonPath(pathState.path);
7370
7511
  const columnExpr = buildJsonExtractExpression(quote(column._dbName), jsonPath, pathState.oldValue);
7371
- command = appendJsonPathComparison(columnExpr, encoded);
7512
+ command = appendJsonPathComparison(columnExpr, encoded, pathState.oldValue);
7372
7513
  }
7373
7514
  }
7374
7515
  else {
@@ -7428,7 +7569,12 @@ function requireNewUpdateCommandCore () {
7428
7569
  return command;
7429
7570
  }
7430
7571
 
7431
- function appendJsonPathComparison(columnExpr, encoded) {
7572
+ function appendJsonPathComparison(columnExpr, encoded, oldValue) {
7573
+ if (oldValue === undefined) {
7574
+ command = command.append(separator).append(columnExpr).append(' IS NULL');
7575
+ separator = ' AND ';
7576
+ return command;
7577
+ }
7432
7578
  if (engine === 'pg') {
7433
7579
  command = command.append(separator).append(columnExpr).append(' IS NOT DISTINCT FROM ').append(encoded);
7434
7580
  }
@@ -9412,46 +9558,14 @@ function requireGetMany () {
9412
9558
  return resultToRows(context, span,result);
9413
9559
  }
9414
9560
 
9415
- getMany.exclusive = function(table,filter,strategy) {
9416
- return getManyCore(table,filter,strategy,true);
9561
+ getMany.exclusive = function(context,table,filter,strategy) {
9562
+ return getManyCore(context,table,filter,strategy,true);
9417
9563
  };
9418
9564
 
9419
9565
  getMany_1 = getMany;
9420
9566
  return getMany_1;
9421
9567
  }
9422
9568
 
9423
- var tryGetFirstFromDb;
9424
- var hasRequiredTryGetFirstFromDb;
9425
-
9426
- function requireTryGetFirstFromDb () {
9427
- if (hasRequiredTryGetFirstFromDb) return tryGetFirstFromDb;
9428
- hasRequiredTryGetFirstFromDb = 1;
9429
- var getMany = requireGetMany();
9430
-
9431
- function tryGet(context, table, filter, strategy) {
9432
- strategy = setLimit(strategy);
9433
- return getMany(context, table, filter, strategy).then(filterRows);
9434
- }
9435
-
9436
- function filterRows(rows) {
9437
- if (rows.length > 0)
9438
- return rows[0];
9439
- return null;
9440
- }
9441
-
9442
- tryGet.exclusive = function(context, table, filter, strategy) {
9443
- strategy = setLimit(strategy);
9444
- return getMany.exclusive(context, table, filter, strategy).then(filterRows);
9445
- };
9446
-
9447
- function setLimit(strategy) {
9448
- return {...strategy, ...{limit: 1}};
9449
- }
9450
-
9451
- tryGetFirstFromDb = tryGet;
9452
- return tryGetFirstFromDb;
9453
- }
9454
-
9455
9569
  var extractStrategy;
9456
9570
  var hasRequiredExtractStrategy;
9457
9571
 
@@ -9476,25 +9590,31 @@ function requireTryGetFromDbById () {
9476
9590
  if (hasRequiredTryGetFromDbById) return tryGetFromDbById;
9477
9591
  hasRequiredTryGetFromDbById = 1;
9478
9592
  var newPrimaryKeyFilter = requireNewPrimaryKeyFilter();
9479
- var tryGetFirstFromDb = requireTryGetFirstFromDb();
9593
+ var getMany = requireGetMany();
9480
9594
  var extractStrategy = requireExtractStrategy();
9481
9595
 
9482
9596
  function tryGet(context) {
9483
9597
  var filter = newPrimaryKeyFilter.apply(null, arguments);
9484
9598
  var table = arguments[1];
9485
9599
  var strategy = extractStrategy.apply(null, arguments);
9486
- return tryGetFirstFromDb(context, table, filter, strategy);
9600
+ return getMany(context, table, filter, strategy).then(filterRows);
9487
9601
  }
9488
9602
 
9489
9603
  tryGet.exclusive = function tryGet(context) {
9490
9604
  var filter = newPrimaryKeyFilter.apply(null, arguments);
9491
9605
  var table = arguments[1];
9492
9606
  var strategy = extractStrategy.apply(null, arguments);
9493
- return tryGetFirstFromDb.exclusive(context, table, filter, strategy);
9607
+ return getMany.exclusive(context, table, filter, strategy).then(filterRows);
9494
9608
 
9495
9609
 
9496
9610
  };
9497
9611
 
9612
+ function filterRows(rows) {
9613
+ if (rows.length > 0)
9614
+ return rows[0];
9615
+ return null;
9616
+ }
9617
+
9498
9618
  tryGetFromDbById = tryGet;
9499
9619
  return tryGetFromDbById;
9500
9620
  }
@@ -11708,18 +11828,22 @@ function requireNewSingleQuery () {
11708
11828
  var newJoinSql = requireNewJoinSql();
11709
11829
  var newParameterized = requireNewParameterized();
11710
11830
  var getSessionSingleton = requireGetSessionSingleton();
11831
+ var lockSql = requireLockSql();
11711
11832
 
11712
11833
  function _new(context,table,filter,span, alias,orderBy,limit,offset,distinct = false) {
11713
11834
  var quote = getSessionSingleton(context, 'quote');
11714
11835
  var name = quote(table._dbName);
11836
+ var quotedAlias = quote(alias);
11715
11837
  var columnSql = newColumnSql(context,table,span,alias,true);
11716
11838
  var joinSql = newJoinSql(context, span, alias);
11717
11839
  var whereSql = newWhereSql(context,table,filter,alias);
11718
11840
  if (limit)
11719
11841
  limit = limit + ' ';
11720
11842
  const selectClause = distinct ? 'select distinct ' : 'select ';
11843
+ const lockClause = lockSql.selectLockSql(context, span, alias);
11844
+ const tableHint = lockSql.tableHintSql(context, span);
11721
11845
 
11722
- return newParameterized(selectClause + limit + columnSql + ' from ' + name + ' ' + quote(alias)).append(joinSql).append(whereSql).append(orderBy + offset);
11846
+ return newParameterized(selectClause + limit + columnSql + ' from ' + name + ' ' + quotedAlias + tableHint).append(joinSql).append(whereSql).append(orderBy + offset + lockClause);
11723
11847
 
11724
11848
  }
11725
11849
 
@@ -12177,6 +12301,38 @@ function requireTryGetById () {
12177
12301
  return tryGetById;
12178
12302
  }
12179
12303
 
12304
+ var tryGetFirstFromDb;
12305
+ var hasRequiredTryGetFirstFromDb;
12306
+
12307
+ function requireTryGetFirstFromDb () {
12308
+ if (hasRequiredTryGetFirstFromDb) return tryGetFirstFromDb;
12309
+ hasRequiredTryGetFirstFromDb = 1;
12310
+ var getMany = requireGetMany();
12311
+
12312
+ function tryGet(context, table, filter, strategy) {
12313
+ strategy = setLimit(strategy);
12314
+ return getMany(context, table, filter, strategy).then(filterRows);
12315
+ }
12316
+
12317
+ function filterRows(rows) {
12318
+ if (rows.length > 0)
12319
+ return rows[0];
12320
+ return null;
12321
+ }
12322
+
12323
+ tryGet.exclusive = function(context, table, filter, strategy) {
12324
+ strategy = setLimit(strategy);
12325
+ return getMany.exclusive(context, table, filter, strategy).then(filterRows);
12326
+ };
12327
+
12328
+ function setLimit(strategy) {
12329
+ return {...strategy, ...{limit: 1}};
12330
+ }
12331
+
12332
+ tryGetFirstFromDb = tryGet;
12333
+ return tryGetFirstFromDb;
12334
+ }
12335
+
12180
12336
  var newRowCache_1;
12181
12337
  var hasRequiredNewRowCache;
12182
12338
 
@@ -12697,29 +12853,34 @@ function requireApplyPatch () {
12697
12853
 
12698
12854
  }
12699
12855
 
12700
- // function assertDatesEqual(date1, date2) {
12701
- // if (date1 && date2) {
12702
- // const parts1 = date1.split('T');
12703
- // const time1parts = (parts1[1] || '').split(/[-+.]/);
12704
- // const parts2 = date2.split('T');
12705
- // const time2parts = (parts2[1] || '').split(/[-+.]/);
12706
- // while (time1parts.length !== time2parts.length) {
12707
- // if (time1parts.length > time2parts.length)
12708
- // time1parts.pop();
12709
- // else if (time1parts.length < time2parts.length)
12710
- // time2parts.pop();
12711
- // }
12712
- // date1 = `${parts1[0]}T${time1parts[0]}`;
12713
- // date2 = `${parts2[0]}T${time2parts[0]}`;
12714
- // }
12715
- // assertDeepEqual(date1, date2);
12716
- // }
12717
-
12718
12856
  function assertDeepEqual(a, b) {
12719
- if (JSON.stringify(a) !== JSON.stringify(b))
12857
+ if (!deepEqual(a, b))
12720
12858
  throw new Error('A, b are not equal');
12721
12859
  }
12722
12860
 
12861
+ function deepEqual(a, b) {
12862
+ if (a === b) return true;
12863
+ if (a && b && typeof a === 'object' && typeof b === 'object') {
12864
+ if (Array.isArray(a)) {
12865
+ if (!Array.isArray(b) || a.length !== b.length) return false;
12866
+ for (let i = 0; i < a.length; i++) {
12867
+ if (!deepEqual(a[i], b[i])) return false;
12868
+ }
12869
+ return true;
12870
+ }
12871
+ if (Array.isArray(b)) return false;
12872
+
12873
+ const keysA = Object.keys(a);
12874
+ const keysB = Object.keys(b);
12875
+ if (keysA.length !== keysB.length) return false;
12876
+ for (const key of keysA) {
12877
+ if (!Object.prototype.hasOwnProperty.call(b, key) || !deepEqual(a[key], b[key])) return false;
12878
+ }
12879
+ return true;
12880
+ }
12881
+ return false;
12882
+ }
12883
+
12723
12884
  applyPatch_1 = applyPatch;
12724
12885
  return applyPatch_1;
12725
12886
  }
@@ -12844,6 +13005,7 @@ function requirePatchTable () {
12844
13005
  const engine = getSessionSingleton(context, 'engine');
12845
13006
  options = cleanOptions(options);
12846
13007
  strategy = JSON.parse(JSON.stringify(strategy || {}));
13008
+ await lockTouchedRows();
12847
13009
  let changed = new Set();
12848
13010
  for (let i = 0; i < patches.length; i++) {
12849
13011
  let patch = { path: undefined, value: undefined, op: undefined };
@@ -12863,14 +13025,28 @@ function requirePatchTable () {
12863
13025
  }
12864
13026
  if (strategy['insertAndForget'])
12865
13027
  return {
12866
- changed: [], strategy
13028
+ changed: [], strategy: stripLockingStrategy(strategy)
12867
13029
  };
12868
- return { changed: await toDtos(changed), strategy };
13030
+ return { changed: await toDtos(changed), strategy: stripLockingStrategy(strategy) };
12869
13031
 
12870
13032
 
12871
13033
  async function toDtos(set) {
12872
13034
  set = [...set];
12873
- const result = await table.getManyDto(context, set, strategy);
13035
+ const result = await table.getManyDto(context, set, stripLockingStrategy(strategy));
13036
+ return result;
13037
+ }
13038
+
13039
+ function stripLockingStrategy(strategy) {
13040
+ if (!strategy || typeof strategy !== 'object')
13041
+ return strategy;
13042
+ if (Array.isArray(strategy))
13043
+ return strategy.map(stripLockingStrategy);
13044
+ const result = {};
13045
+ for (let name in strategy) {
13046
+ if (name === 'forUpdate' || name === 'skipLocked')
13047
+ continue;
13048
+ result[name] = stripLockingStrategy(strategy[name]);
13049
+ }
12874
13050
  return result;
12875
13051
  }
12876
13052
 
@@ -12881,6 +13057,54 @@ function requirePatchTable () {
12881
13057
  return [property];
12882
13058
  }
12883
13059
 
13060
+ async function lockTouchedRows() {
13061
+ if (!hasLockingStrategy(strategy))
13062
+ return;
13063
+ const keys = [];
13064
+ const keySet = new Set();
13065
+ for (let i = 0; i < patches.length; i++) {
13066
+ const patch = patches[i];
13067
+ const path = patch.path.split('/').slice(1);
13068
+ if (path.length === 0)
13069
+ continue;
13070
+ if (patch.op === 'add' && path.length === 1)
13071
+ continue;
13072
+ const key = toKey(path[0]);
13073
+ if (isTemporaryKey(key))
13074
+ continue;
13075
+ const keyString = JSON.stringify(key);
13076
+ if (keySet.has(keyString))
13077
+ continue;
13078
+ keySet.add(keyString);
13079
+ keys.push(key);
13080
+ }
13081
+ for (let i = 0; i < keys.length; i++) {
13082
+ const row = await table.tryGetById.apply(null, [context, ...keys[i], strategy]);
13083
+ if (!row)
13084
+ throw new Error(`Row ${table._dbName} with id ${keys[i]} was not found.`);
13085
+ }
13086
+ }
13087
+
13088
+ function hasLockingStrategy(strategy) {
13089
+ if (!strategy || typeof strategy !== 'object')
13090
+ return false;
13091
+ if (strategy.forUpdate || strategy.skipLocked)
13092
+ return true;
13093
+ for (let name in strategy) {
13094
+ if (name !== 'where' && hasLockingStrategy(strategy[name]))
13095
+ return true;
13096
+ }
13097
+ return false;
13098
+ }
13099
+
13100
+ function isTemporaryKey(key) {
13101
+ for (let i = 0; i < key.length; i++) {
13102
+ if (typeof key[i] === 'string' && key[i].indexOf('~') === 0)
13103
+ return true;
13104
+ }
13105
+ return false;
13106
+ }
13107
+
12884
13108
  async function add({ path, value, op, oldValue, strategy, options }, table, row, parentRow, relation) {
12885
13109
  let property = path[0];
12886
13110
  path = path.slice(1);
@@ -14535,10 +14759,10 @@ var hasRequiredSelectForUpdateSql$1;
14535
14759
  function requireSelectForUpdateSql$1 () {
14536
14760
  if (hasRequiredSelectForUpdateSql$1) return selectForUpdateSql$1;
14537
14761
  hasRequiredSelectForUpdateSql$1 = 1;
14538
- const quote = requireQuote$2();
14539
-
14540
- selectForUpdateSql$1 = function(context, alias) {
14541
- return ' FOR UPDATE OF ' + quote(context, alias);
14762
+ selectForUpdateSql$1 = function(_context, lock) {
14763
+ if (lock)
14764
+ throw new Error('select for update is not supported by SQLite');
14765
+ return '';
14542
14766
  };
14543
14767
  return selectForUpdateSql$1;
14544
14768
  }
@@ -16235,8 +16459,18 @@ function requireSelectForUpdateSql () {
16235
16459
  hasRequiredSelectForUpdateSql = 1;
16236
16460
  const quote = requireQuote$2();
16237
16461
 
16238
- selectForUpdateSql = function(alias) {
16239
- return ' FOR UPDATE OF ' + quote(alias);
16462
+ selectForUpdateSql = function(context, lock) {
16463
+ if (typeof lock === 'string')
16464
+ lock = { aliases: [lock], forUpdate: true };
16465
+ let sql = '';
16466
+ if (lock.forUpdate) {
16467
+ sql = ' FOR UPDATE';
16468
+ if (lock.aliases && lock.aliases.length > 0)
16469
+ sql += ' OF ' + lock.aliases.map(alias => quote(context, alias)).join(', ');
16470
+ }
16471
+ if (lock.skipLocked)
16472
+ sql += ' SKIP LOCKED';
16473
+ return sql;
16240
16474
  };
16241
16475
  return selectForUpdateSql;
16242
16476
  }