orange-orm 5.3.6 → 5.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -8
- package/dist/index.browser.mjs +351 -117
- package/dist/index.mjs +401 -142
- package/docs/changelog.md +2 -0
- package/package.json +1 -1
- package/src/applyPatch.js +25 -20
- package/src/client/index.js +33 -16
- package/src/getManyDto/query/newSingleQuery.js +5 -1
- package/src/getTSDefinition.js +2 -0
- package/src/hostExpress/executePath.js +39 -2
- package/src/hostLocal.js +19 -1
- package/src/map2.d.ts +3 -1
- package/src/mySql/selectForUpdateSql.js +10 -3
- package/src/oracle/selectForUpdateSql.js +10 -5
- package/src/patchTable.js +66 -3
- package/src/pg/selectForUpdateSql.js +13 -3
- package/src/sap/selectForUpdateSql.js +5 -5
- package/src/sqlite/selectForUpdateSql.js +5 -5
- package/src/table/commands/newUpdateCommandCore.js +7 -2
- package/src/table/getMany.js +2 -2
- package/src/table/newPrimaryKeyFilter.js +2 -2
- package/src/table/query/newSingleQuery.js +7 -5
- package/src/table/query/singleQuery/joinSql/joinLegToShallowJoinSql.js +2 -2
- package/src/table/query/singleQuery/joinSql/newShallowJoinSql.js +5 -3
- package/src/table/query/singleQuery/joinSql/oneLegToShallowJoinSql.js +2 -2
- package/src/table/query/singleQuery/lockSql.js +74 -0
- package/src/table/tryGetFromDbById.js +9 -3
- package/src/tedious/getManyDto/query/newSingleQuery.js +4 -2
- package/src/tedious/selectForUpdateSql.js +17 -4
package/docs/changelog.md
CHANGED
package/package.json
CHANGED
package/src/applyPatch.js
CHANGED
|
@@ -108,27 +108,32 @@ function applyPatch({ options = {}, context }, dto, changes, column) {
|
|
|
108
108
|
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
// function assertDatesEqual(date1, date2) {
|
|
112
|
-
// if (date1 && date2) {
|
|
113
|
-
// const parts1 = date1.split('T');
|
|
114
|
-
// const time1parts = (parts1[1] || '').split(/[-+.]/);
|
|
115
|
-
// const parts2 = date2.split('T');
|
|
116
|
-
// const time2parts = (parts2[1] || '').split(/[-+.]/);
|
|
117
|
-
// while (time1parts.length !== time2parts.length) {
|
|
118
|
-
// if (time1parts.length > time2parts.length)
|
|
119
|
-
// time1parts.pop();
|
|
120
|
-
// else if (time1parts.length < time2parts.length)
|
|
121
|
-
// time2parts.pop();
|
|
122
|
-
// }
|
|
123
|
-
// date1 = `${parts1[0]}T${time1parts[0]}`;
|
|
124
|
-
// date2 = `${parts2[0]}T${time2parts[0]}`;
|
|
125
|
-
// }
|
|
126
|
-
// assertDeepEqual(date1, date2);
|
|
127
|
-
// }
|
|
128
|
-
|
|
129
111
|
function assertDeepEqual(a, b) {
|
|
130
|
-
if (
|
|
112
|
+
if (!deepEqual(a, b))
|
|
131
113
|
throw new Error('A, b are not equal');
|
|
132
114
|
}
|
|
133
115
|
|
|
134
|
-
|
|
116
|
+
function deepEqual(a, b) {
|
|
117
|
+
if (a === b) return true;
|
|
118
|
+
if (a && b && typeof a === 'object' && typeof b === 'object') {
|
|
119
|
+
if (Array.isArray(a)) {
|
|
120
|
+
if (!Array.isArray(b) || a.length !== b.length) return false;
|
|
121
|
+
for (let i = 0; i < a.length; i++) {
|
|
122
|
+
if (!deepEqual(a[i], b[i])) return false;
|
|
123
|
+
}
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
if (Array.isArray(b)) return false;
|
|
127
|
+
|
|
128
|
+
const keysA = Object.keys(a);
|
|
129
|
+
const keysB = Object.keys(b);
|
|
130
|
+
if (keysA.length !== keysB.length) return false;
|
|
131
|
+
for (const key of keysA) {
|
|
132
|
+
if (!Object.prototype.hasOwnProperty.call(b, key) || !deepEqual(a[key], b[key])) return false;
|
|
133
|
+
}
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
module.exports = applyPatch;
|
package/src/client/index.js
CHANGED
|
@@ -308,7 +308,7 @@ function rdbClient(options = {}) {
|
|
|
308
308
|
return false;
|
|
309
309
|
if (Object.keys(value).length === 0)
|
|
310
310
|
return true;
|
|
311
|
-
if ('where' in value || 'orderBy' in value || 'limit' in value || 'offset' in value)
|
|
311
|
+
if ('where' in value || 'orderBy' in value || 'limit' in value || 'offset' in value || 'forUpdate' in value || 'skipLocked' in value)
|
|
312
312
|
return true;
|
|
313
313
|
for (let key in value) {
|
|
314
314
|
const v = value[key];
|
|
@@ -558,6 +558,7 @@ function rdbClient(options = {}) {
|
|
|
558
558
|
|
|
559
559
|
function proxifyArray(array, strategy, fast) {
|
|
560
560
|
let _array = array;
|
|
561
|
+
const storedStrategy = toStoredFetchStrategy(strategy);
|
|
561
562
|
if (_reactive)
|
|
562
563
|
array = _reactive(array);
|
|
563
564
|
let handler = {
|
|
@@ -585,17 +586,17 @@ function rdbClient(options = {}) {
|
|
|
585
586
|
};
|
|
586
587
|
|
|
587
588
|
let watcher = onChange(array, () => {
|
|
588
|
-
rootMap.set(array, { json: cloneFromDb(array, fast), strategy, originalArray: [...array] });
|
|
589
|
+
rootMap.set(array, { json: cloneFromDb(array, fast), strategy: storedStrategy, originalArray: [...array] });
|
|
589
590
|
});
|
|
590
591
|
let innerProxy = new Proxy(watcher, handler);
|
|
591
|
-
if (
|
|
592
|
-
|
|
593
|
-
fetchingStrategyMap.set(array, cleanStrategy);
|
|
592
|
+
if (storedStrategy !== undefined) {
|
|
593
|
+
fetchingStrategyMap.set(array, storedStrategy);
|
|
594
594
|
}
|
|
595
595
|
return innerProxy;
|
|
596
596
|
}
|
|
597
597
|
|
|
598
598
|
function proxifyRow(row, strategy, fast) {
|
|
599
|
+
const storedStrategy = toStoredFetchStrategy(strategy);
|
|
599
600
|
let handler = {
|
|
600
601
|
get(_target, property,) {
|
|
601
602
|
if (property === 'save' || property === 'saveChanges') //call server then acceptChanges
|
|
@@ -620,10 +621,10 @@ function rdbClient(options = {}) {
|
|
|
620
621
|
|
|
621
622
|
};
|
|
622
623
|
let watcher = onChange(row, () => {
|
|
623
|
-
rootMap.set(row, { json: cloneFromDb(row, fast), strategy });
|
|
624
|
+
rootMap.set(row, { json: cloneFromDb(row, fast), strategy: storedStrategy });
|
|
624
625
|
});
|
|
625
626
|
let innerProxy = new Proxy(watcher, handler);
|
|
626
|
-
fetchingStrategyMap.set(row,
|
|
627
|
+
fetchingStrategyMap.set(row, storedStrategy);
|
|
627
628
|
return innerProxy;
|
|
628
629
|
}
|
|
629
630
|
|
|
@@ -714,7 +715,7 @@ function rdbClient(options = {}) {
|
|
|
714
715
|
let insertedPositions = getInsertedRowsPosition(array);
|
|
715
716
|
let { changed, strategy: newStrategy } = await p;
|
|
716
717
|
copyIntoArray(changed, array, [...insertedPositions, ...updatedPositions]);
|
|
717
|
-
rootMap.set(array, { json: cloneFromDb(array), strategy: newStrategy, originalArray: [...array] });
|
|
718
|
+
rootMap.set(array, { json: cloneFromDb(array), strategy: toStoredFetchStrategy(newStrategy), originalArray: [...array] });
|
|
718
719
|
}
|
|
719
720
|
|
|
720
721
|
async function patch(patch, concurrencyOptions, strategy) {
|
|
@@ -776,8 +777,7 @@ function rdbClient(options = {}) {
|
|
|
776
777
|
let context = rootMap.get(obj);
|
|
777
778
|
if (context?.strategy !== undefined) {
|
|
778
779
|
// @ts-ignore
|
|
779
|
-
|
|
780
|
-
return strategy;
|
|
780
|
+
return toStoredFetchStrategy(context.strategy);
|
|
781
781
|
}
|
|
782
782
|
}
|
|
783
783
|
}
|
|
@@ -787,9 +787,24 @@ function rdbClient(options = {}) {
|
|
|
787
787
|
return strategy;
|
|
788
788
|
else if (fetchingStrategyMap.get(obj) !== undefined) {
|
|
789
789
|
// @ts-ignore
|
|
790
|
-
|
|
790
|
+
return toStoredFetchStrategy(fetchingStrategyMap.get(obj));
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
function toStoredFetchStrategy(strategy) {
|
|
795
|
+
if (strategy === undefined || strategy === null || typeof strategy !== 'object')
|
|
791
796
|
return strategy;
|
|
797
|
+
if (Array.isArray(strategy))
|
|
798
|
+
return strategy.map(toStoredFetchStrategy);
|
|
799
|
+
const cleanStrategy = { ...strategy };
|
|
800
|
+
delete cleanStrategy.limit;
|
|
801
|
+
delete cleanStrategy.forUpdate;
|
|
802
|
+
delete cleanStrategy.skipLocked;
|
|
803
|
+
for (let name in cleanStrategy) {
|
|
804
|
+
if (name !== 'where' && cleanStrategy[name] && typeof cleanStrategy[name] === 'object')
|
|
805
|
+
cleanStrategy[name] = toStoredFetchStrategy(cleanStrategy[name]);
|
|
792
806
|
}
|
|
807
|
+
return cleanStrategy;
|
|
793
808
|
}
|
|
794
809
|
|
|
795
810
|
function clearChangesArray(array) {
|
|
@@ -880,8 +895,9 @@ function rdbClient(options = {}) {
|
|
|
880
895
|
array.splice(i + offset, 1);
|
|
881
896
|
offset--;
|
|
882
897
|
}
|
|
883
|
-
|
|
884
|
-
|
|
898
|
+
const storedStrategy = toStoredFetchStrategy(strategy);
|
|
899
|
+
rootMap.set(array, { json: cloneFromDb(array), strategy: storedStrategy, originalArray: [...array] });
|
|
900
|
+
fetchingStrategyMap.set(array, storedStrategy);
|
|
885
901
|
}
|
|
886
902
|
|
|
887
903
|
async function deleteRow(row, options) {
|
|
@@ -915,7 +931,7 @@ function rdbClient(options = {}) {
|
|
|
915
931
|
let adapter = netAdapter(url, tableName, { http: httpInterceptor, tableOptions });
|
|
916
932
|
let { changed, strategy: newStrategy } = await adapter.patch(body);
|
|
917
933
|
copyInto(changed, [row]);
|
|
918
|
-
rootMap.set(row, { json: cloneFromDb(row), strategy: newStrategy });
|
|
934
|
+
rootMap.set(row, { json: cloneFromDb(row), strategy: toStoredFetchStrategy(newStrategy) });
|
|
919
935
|
}
|
|
920
936
|
|
|
921
937
|
async function refreshRow(row, strategy) {
|
|
@@ -939,8 +955,9 @@ function rdbClient(options = {}) {
|
|
|
939
955
|
for (let p in rows[0]) {
|
|
940
956
|
row[p] = rows[0][p];
|
|
941
957
|
}
|
|
942
|
-
|
|
943
|
-
|
|
958
|
+
const storedStrategy = toStoredFetchStrategy(strategy);
|
|
959
|
+
rootMap.set(row, { json: cloneFromDb(row), strategy: storedStrategy });
|
|
960
|
+
fetchingStrategyMap.set(row, storedStrategy);
|
|
944
961
|
}
|
|
945
962
|
|
|
946
963
|
function acceptChangesRow(row) {
|
|
@@ -3,18 +3,22 @@ var newWhereSql = require('../../table/query/singleQuery/newWhereSql');
|
|
|
3
3
|
var newJoinSql = require('../../table/query/singleQuery/newJoinSql');
|
|
4
4
|
var newParameterized = require('../../table/query/newParameterized');
|
|
5
5
|
var getSessionSingleton = require('../../table/getSessionSingleton');
|
|
6
|
+
var lockSql = require('../../table/query/singleQuery/lockSql');
|
|
6
7
|
|
|
7
8
|
function _new(context,table,filter,span, alias,orderBy,limit,offset,distinct = false) {
|
|
8
9
|
var quote = getSessionSingleton(context, 'quote');
|
|
9
10
|
var name = quote(table._dbName);
|
|
11
|
+
var quotedAlias = quote(alias);
|
|
10
12
|
var columnSql = newColumnSql(context,table,span,alias,true);
|
|
11
13
|
var joinSql = newJoinSql(context, span, alias);
|
|
12
14
|
var whereSql = newWhereSql(context,table,filter,alias);
|
|
13
15
|
if (limit)
|
|
14
16
|
limit = limit + ' ';
|
|
15
17
|
const selectClause = distinct ? 'select distinct ' : 'select ';
|
|
18
|
+
const lockClause = lockSql.selectLockSql(context, span, alias);
|
|
19
|
+
const tableHint = lockSql.tableHintSql(context, span);
|
|
16
20
|
|
|
17
|
-
return newParameterized(selectClause + limit + columnSql + ' from ' + name + ' ' +
|
|
21
|
+
return newParameterized(selectClause + limit + columnSql + ' from ' + name + ' ' + quotedAlias + tableHint).append(joinSql).append(whereSql).append(orderBy + offset + lockClause);
|
|
18
22
|
|
|
19
23
|
}
|
|
20
24
|
|
package/src/getTSDefinition.js
CHANGED
|
@@ -247,6 +247,8 @@ export interface ${name}Strategy {
|
|
|
247
247
|
offset?: number;
|
|
248
248
|
orderBy?: Array<${orderByColumns(table)}> | ${orderByColumns(table)};
|
|
249
249
|
where?: (table: ${name}TableBase) => RawFilter;
|
|
250
|
+
forUpdate?: boolean;
|
|
251
|
+
skipLocked?: boolean;
|
|
250
252
|
}
|
|
251
253
|
|
|
252
254
|
${otherConcurrency}
|
|
@@ -303,7 +303,7 @@ function _executePath(context, ...rest) {
|
|
|
303
303
|
|
|
304
304
|
async function replace(subject, strategy = { insertAndForget: true }) {
|
|
305
305
|
validateStrategy(table, strategy);
|
|
306
|
-
const refinedStrategy = objectToStrategy(subject, {}, table);
|
|
306
|
+
const refinedStrategy = withLockingStrategy(objectToStrategy(subject, {}, table), strategy);
|
|
307
307
|
const JSONFilter2 = {
|
|
308
308
|
path: 'getManyDto',
|
|
309
309
|
args: [subject, refinedStrategy]
|
|
@@ -320,7 +320,7 @@ function _executePath(context, ...rest) {
|
|
|
320
320
|
|
|
321
321
|
async function update(subject, whereStrategy, strategy = { insertAndForget: true }) {
|
|
322
322
|
validateStrategy(table, strategy);
|
|
323
|
-
const refinedWhereStrategy = objectToStrategy(subject, whereStrategy, table);
|
|
323
|
+
const refinedWhereStrategy = withLockingStrategy(objectToStrategy(subject, whereStrategy, table), strategy);
|
|
324
324
|
const JSONFilter2 = {
|
|
325
325
|
path: 'getManyDto',
|
|
326
326
|
args: [null, refinedWhereStrategy]
|
|
@@ -340,6 +340,43 @@ function _executePath(context, ...rest) {
|
|
|
340
340
|
return changed;
|
|
341
341
|
}
|
|
342
342
|
|
|
343
|
+
function withLockingStrategy(fetchStrategy, strategy) {
|
|
344
|
+
const lockStrategy = extractLockingStrategy(strategy);
|
|
345
|
+
if (!lockStrategy)
|
|
346
|
+
return fetchStrategy;
|
|
347
|
+
return mergeLockingStrategy(fetchStrategy, lockStrategy);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function extractLockingStrategy(strategy) {
|
|
351
|
+
if (!strategy || typeof strategy !== 'object')
|
|
352
|
+
return;
|
|
353
|
+
const result = {};
|
|
354
|
+
if (strategy.forUpdate)
|
|
355
|
+
result.forUpdate = strategy.forUpdate;
|
|
356
|
+
if (strategy.skipLocked)
|
|
357
|
+
result.skipLocked = strategy.skipLocked;
|
|
358
|
+
for (let name in strategy) {
|
|
359
|
+
if (name === 'where' || name === 'orderBy' || name === 'limit' || name === 'offset' || name === 'forUpdate' || name === 'skipLocked')
|
|
360
|
+
continue;
|
|
361
|
+
const child = extractLockingStrategy(strategy[name]);
|
|
362
|
+
if (child)
|
|
363
|
+
result[name] = child;
|
|
364
|
+
}
|
|
365
|
+
return Object.keys(result).length > 0 ? result : undefined;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function mergeLockingStrategy(fetchStrategy, lockStrategy) {
|
|
369
|
+
const result = { ...fetchStrategy };
|
|
370
|
+
for (let name in lockStrategy) {
|
|
371
|
+
const value = lockStrategy[name];
|
|
372
|
+
if (name === 'forUpdate' || name === 'skipLocked')
|
|
373
|
+
result[name] = value;
|
|
374
|
+
else
|
|
375
|
+
result[name] = mergeLockingStrategy(result[name] && typeof result[name] === 'object' ? result[name] : {}, value);
|
|
376
|
+
}
|
|
377
|
+
return result;
|
|
378
|
+
}
|
|
379
|
+
|
|
343
380
|
function objectToStrategy(object, whereStrategy, table, strategy = {}) {
|
|
344
381
|
strategy = { ...whereStrategy, ...strategy };
|
|
345
382
|
if (Array.isArray(object)) {
|
package/src/hostLocal.js
CHANGED
|
@@ -80,7 +80,7 @@ function hostLocal() {
|
|
|
80
80
|
|| beforeCommit
|
|
81
81
|
|| afterCommit
|
|
82
82
|
|| afterRollback);
|
|
83
|
-
if (!hasTransactionHooks && readonlyOps.includes(body.path))
|
|
83
|
+
if (!hasTransactionHooks && readonlyOps.includes(body.path) && !hasLockingStrategy(body))
|
|
84
84
|
await db.transaction({ readonly: true }, fn);
|
|
85
85
|
else {
|
|
86
86
|
await db.transaction(async (context) => {
|
|
@@ -114,6 +114,24 @@ function hostLocal() {
|
|
|
114
114
|
result = await executePath(context, options);
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
|
+
|
|
118
|
+
function hasLockingStrategy(body) {
|
|
119
|
+
if (!body || !body.args)
|
|
120
|
+
return false;
|
|
121
|
+
return hasLockingStrategyCore(body.args[1]);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function hasLockingStrategyCore(strategy) {
|
|
125
|
+
if (!strategy || typeof strategy !== 'object')
|
|
126
|
+
return false;
|
|
127
|
+
if (strategy.forUpdate || strategy.skipLocked)
|
|
128
|
+
return true;
|
|
129
|
+
for (let name in strategy) {
|
|
130
|
+
if (name !== 'where' && hasLockingStrategyCore(strategy[name]))
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
117
135
|
async function query() {
|
|
118
136
|
let args = arguments;
|
|
119
137
|
let result;
|
package/src/map2.d.ts
CHANGED
|
@@ -187,7 +187,7 @@ export type OrderBy<M extends Record<string, TableDefinition<M>>, K extends keyo
|
|
|
187
187
|
>;
|
|
188
188
|
|
|
189
189
|
// Reserved property names that should not conflict with relation selectors
|
|
190
|
-
type ReservedFetchStrategyProps = 'orderBy' | 'where';
|
|
190
|
+
type ReservedFetchStrategyProps = 'orderBy' | 'where' | 'forUpdate' | 'skipLocked';
|
|
191
191
|
|
|
192
192
|
// Base fetch strategy properties (reserved props)
|
|
193
193
|
type BaseFetchStrategy<M extends Record<string, TableDefinition<M>>, K extends keyof M> = {
|
|
@@ -195,6 +195,8 @@ type BaseFetchStrategy<M extends Record<string, TableDefinition<M>>, K extends k
|
|
|
195
195
|
limit?: number;
|
|
196
196
|
offset?: number;
|
|
197
197
|
where?: WhereClause<M, K>;
|
|
198
|
+
forUpdate?: boolean;
|
|
199
|
+
skipLocked?: boolean;
|
|
198
200
|
};
|
|
199
201
|
|
|
200
202
|
export type PrimaryKeyObject<M extends Record<string, TableDefinition<M>>, K extends keyof M> =
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
-
module.exports = function(
|
|
2
|
-
|
|
3
|
-
};
|
|
1
|
+
module.exports = function(_context, lock) {
|
|
2
|
+
if (typeof lock === 'string')
|
|
3
|
+
lock = { forUpdate: true };
|
|
4
|
+
let sql = '';
|
|
5
|
+
if (lock.forUpdate)
|
|
6
|
+
sql += ' FOR UPDATE';
|
|
7
|
+
if (lock.skipLocked)
|
|
8
|
+
sql += ' SKIP LOCKED';
|
|
9
|
+
return sql;
|
|
10
|
+
};
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
module.exports = function(_context, lock) {
|
|
2
|
+
if (typeof lock === 'string')
|
|
3
|
+
lock = { forUpdate: true };
|
|
4
|
+
let sql = '';
|
|
5
|
+
if (lock.forUpdate)
|
|
6
|
+
sql = ' FOR UPDATE';
|
|
7
|
+
if (lock.skipLocked)
|
|
8
|
+
sql += ' SKIP LOCKED';
|
|
9
|
+
return sql;
|
|
10
|
+
};
|
package/src/patchTable.js
CHANGED
|
@@ -20,6 +20,7 @@ async function patchTableCore(context, table, patches, { strategy = undefined, d
|
|
|
20
20
|
const engine = getSessionSingleton(context, 'engine');
|
|
21
21
|
options = cleanOptions(options);
|
|
22
22
|
strategy = JSON.parse(JSON.stringify(strategy || {}));
|
|
23
|
+
await lockTouchedRows();
|
|
23
24
|
let changed = new Set();
|
|
24
25
|
for (let i = 0; i < patches.length; i++) {
|
|
25
26
|
let patch = { path: undefined, value: undefined, op: undefined };
|
|
@@ -39,14 +40,28 @@ async function patchTableCore(context, table, patches, { strategy = undefined, d
|
|
|
39
40
|
}
|
|
40
41
|
if (strategy['insertAndForget'])
|
|
41
42
|
return {
|
|
42
|
-
changed: [], strategy
|
|
43
|
+
changed: [], strategy: stripLockingStrategy(strategy)
|
|
43
44
|
};
|
|
44
|
-
return { changed: await toDtos(changed), strategy };
|
|
45
|
+
return { changed: await toDtos(changed), strategy: stripLockingStrategy(strategy) };
|
|
45
46
|
|
|
46
47
|
|
|
47
48
|
async function toDtos(set) {
|
|
48
49
|
set = [...set];
|
|
49
|
-
const result = await table.getManyDto(context, set, strategy);
|
|
50
|
+
const result = await table.getManyDto(context, set, stripLockingStrategy(strategy));
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function stripLockingStrategy(strategy) {
|
|
55
|
+
if (!strategy || typeof strategy !== 'object')
|
|
56
|
+
return strategy;
|
|
57
|
+
if (Array.isArray(strategy))
|
|
58
|
+
return strategy.map(stripLockingStrategy);
|
|
59
|
+
const result = {};
|
|
60
|
+
for (let name in strategy) {
|
|
61
|
+
if (name === 'forUpdate' || name === 'skipLocked')
|
|
62
|
+
continue;
|
|
63
|
+
result[name] = stripLockingStrategy(strategy[name]);
|
|
64
|
+
}
|
|
50
65
|
return result;
|
|
51
66
|
}
|
|
52
67
|
|
|
@@ -57,6 +72,54 @@ async function patchTableCore(context, table, patches, { strategy = undefined, d
|
|
|
57
72
|
return [property];
|
|
58
73
|
}
|
|
59
74
|
|
|
75
|
+
async function lockTouchedRows() {
|
|
76
|
+
if (!hasLockingStrategy(strategy))
|
|
77
|
+
return;
|
|
78
|
+
const keys = [];
|
|
79
|
+
const keySet = new Set();
|
|
80
|
+
for (let i = 0; i < patches.length; i++) {
|
|
81
|
+
const patch = patches[i];
|
|
82
|
+
const path = patch.path.split('/').slice(1);
|
|
83
|
+
if (path.length === 0)
|
|
84
|
+
continue;
|
|
85
|
+
if (patch.op === 'add' && path.length === 1)
|
|
86
|
+
continue;
|
|
87
|
+
const key = toKey(path[0]);
|
|
88
|
+
if (isTemporaryKey(key))
|
|
89
|
+
continue;
|
|
90
|
+
const keyString = JSON.stringify(key);
|
|
91
|
+
if (keySet.has(keyString))
|
|
92
|
+
continue;
|
|
93
|
+
keySet.add(keyString);
|
|
94
|
+
keys.push(key);
|
|
95
|
+
}
|
|
96
|
+
for (let i = 0; i < keys.length; i++) {
|
|
97
|
+
const row = await table.tryGetById.apply(null, [context, ...keys[i], strategy]);
|
|
98
|
+
if (!row)
|
|
99
|
+
throw new Error(`Row ${table._dbName} with id ${keys[i]} was not found.`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function hasLockingStrategy(strategy) {
|
|
104
|
+
if (!strategy || typeof strategy !== 'object')
|
|
105
|
+
return false;
|
|
106
|
+
if (strategy.forUpdate || strategy.skipLocked)
|
|
107
|
+
return true;
|
|
108
|
+
for (let name in strategy) {
|
|
109
|
+
if (name !== 'where' && hasLockingStrategy(strategy[name]))
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function isTemporaryKey(key) {
|
|
116
|
+
for (let i = 0; i < key.length; i++) {
|
|
117
|
+
if (typeof key[i] === 'string' && key[i].indexOf('~') === 0)
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
|
|
60
123
|
async function add({ path, value, op, oldValue, strategy, options }, table, row, parentRow, relation) {
|
|
61
124
|
let property = path[0];
|
|
62
125
|
path = path.slice(1);
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
const quote = require('../table/quote');
|
|
2
2
|
|
|
3
|
-
module.exports = function(
|
|
4
|
-
|
|
5
|
-
};
|
|
3
|
+
module.exports = function(context, lock) {
|
|
4
|
+
if (typeof lock === 'string')
|
|
5
|
+
lock = { aliases: [lock], forUpdate: true };
|
|
6
|
+
let sql = '';
|
|
7
|
+
if (lock.forUpdate) {
|
|
8
|
+
sql = ' FOR UPDATE';
|
|
9
|
+
if (lock.aliases && lock.aliases.length > 0)
|
|
10
|
+
sql += ' OF ' + lock.aliases.map(alias => quote(context, alias)).join(', ');
|
|
11
|
+
}
|
|
12
|
+
if (lock.skipLocked)
|
|
13
|
+
sql += ' SKIP LOCKED';
|
|
14
|
+
return sql;
|
|
15
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
return '
|
|
5
|
-
};
|
|
1
|
+
module.exports = function(_context, lock) {
|
|
2
|
+
if (lock)
|
|
3
|
+
throw new Error('select for update is not supported by SAP ASE');
|
|
4
|
+
return '';
|
|
5
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
return '
|
|
5
|
-
};
|
|
1
|
+
module.exports = function(_context, lock) {
|
|
2
|
+
if (lock)
|
|
3
|
+
throw new Error('select for update is not supported by SQLite');
|
|
4
|
+
return '';
|
|
5
|
+
};
|
|
@@ -67,7 +67,7 @@ function newUpdateCommandCore(context, table, columns, row, concurrencyState) {
|
|
|
67
67
|
const encoded = encodeJsonValue(pathState.oldValue, column);
|
|
68
68
|
const jsonPath = buildJsonPath(pathState.path);
|
|
69
69
|
const columnExpr = buildJsonExtractExpression(quote(column._dbName), jsonPath, pathState.oldValue);
|
|
70
|
-
command = appendJsonPathComparison(columnExpr, encoded);
|
|
70
|
+
command = appendJsonPathComparison(columnExpr, encoded, pathState.oldValue);
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
else {
|
|
@@ -127,7 +127,12 @@ function newUpdateCommandCore(context, table, columns, row, concurrencyState) {
|
|
|
127
127
|
return command;
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
-
function appendJsonPathComparison(columnExpr, encoded) {
|
|
130
|
+
function appendJsonPathComparison(columnExpr, encoded, oldValue) {
|
|
131
|
+
if (oldValue === undefined) {
|
|
132
|
+
command = command.append(separator).append(columnExpr).append(' IS NULL');
|
|
133
|
+
separator = ' AND ';
|
|
134
|
+
return command;
|
|
135
|
+
}
|
|
131
136
|
if (engine === 'pg') {
|
|
132
137
|
command = command.append(separator).append(columnExpr).append(' IS NOT DISTINCT FROM ').append(encoded);
|
|
133
138
|
}
|
package/src/table/getMany.js
CHANGED
|
@@ -19,8 +19,8 @@ async function getManyCore(context,table,filter,strategy,exclusive) {
|
|
|
19
19
|
return resultToRows(context, span,result);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
getMany.exclusive = function(table,filter,strategy) {
|
|
23
|
-
return getManyCore(table,filter,strategy,true);
|
|
22
|
+
getMany.exclusive = function(context,table,filter,strategy) {
|
|
23
|
+
return getManyCore(context,table,filter,strategy,true);
|
|
24
24
|
};
|
|
25
25
|
|
|
26
26
|
module.exports = getMany;
|
|
@@ -2,8 +2,8 @@ function primaryKeyFilter(context, table) {
|
|
|
2
2
|
var primaryColumns = table._primaryColumns;
|
|
3
3
|
var key = arguments[2];
|
|
4
4
|
var filter = primaryColumns[0].equal(context, key);
|
|
5
|
-
for (var i =
|
|
6
|
-
key = arguments[i+
|
|
5
|
+
for (var i = 1; i < primaryColumns.length; i++) {
|
|
6
|
+
key = arguments[i + 2];
|
|
7
7
|
var colFilter = primaryColumns[i].equal(context, key);
|
|
8
8
|
filter = filter.and(context, colFilter);
|
|
9
9
|
}
|
|
@@ -2,23 +2,25 @@ var newColumnSql = require('./singleQuery/newColumnSql');
|
|
|
2
2
|
var newJoinSql = require('./singleQuery/newJoinSql');
|
|
3
3
|
var newWhereSql = require('./singleQuery/newWhereSql');
|
|
4
4
|
var negotiateLimit = require('./singleQuery/negotiateLimit');
|
|
5
|
-
var
|
|
5
|
+
var lockSql = require('./singleQuery/lockSql');
|
|
6
6
|
var newParameterized = require('../../table/query/newParameterized');
|
|
7
7
|
var quote = require('../quote');
|
|
8
8
|
|
|
9
9
|
function _new(context, table, filter, span, alias, innerJoin, orderBy, limit, offset, exclusive) {
|
|
10
10
|
|
|
11
11
|
var name = quote(context, table._dbName);
|
|
12
|
+
var quotedAlias = quote(context, alias);
|
|
12
13
|
var columnSql = newColumnSql(context, table, span, alias);
|
|
13
14
|
var joinSql = newJoinSql(context, span, alias);
|
|
14
15
|
var whereSql = newWhereSql(context, table, filter, alias);
|
|
15
16
|
var safeLimit = negotiateLimit(limit);
|
|
16
|
-
var
|
|
17
|
-
|
|
17
|
+
var lockClause = lockSql.selectLockSql(context, span, alias, exclusive);
|
|
18
|
+
var tableHint = lockSql.tableHintSql(context, span, exclusive);
|
|
19
|
+
return newParameterized('select' + safeLimit + ' ' + columnSql + ' from ' + name + ' ' + quotedAlias + tableHint)
|
|
18
20
|
.append(innerJoin)
|
|
19
21
|
.append(joinSql)
|
|
20
22
|
.append(whereSql)
|
|
21
|
-
.append(orderBy + offset +
|
|
23
|
+
.append(orderBy + offset + lockClause);
|
|
22
24
|
}
|
|
23
25
|
|
|
24
|
-
module.exports = _new;
|
|
26
|
+
module.exports = _new;
|
|
@@ -3,7 +3,7 @@ var newShallowJoinSql = require('./newShallowJoinSql');
|
|
|
3
3
|
function toJoinSql(context,leg,alias,childAlias) {
|
|
4
4
|
var columns = leg.columns;
|
|
5
5
|
var childTable = leg.span.table;
|
|
6
|
-
return newShallowJoinSql(context,childTable,columns,childTable._primaryColumns,alias,childAlias,leg.span.where).prepend(' LEFT');
|
|
6
|
+
return newShallowJoinSql(context,childTable,columns,childTable._primaryColumns,alias,childAlias,leg.span.where,leg.span).prepend(' LEFT');
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
module.exports = toJoinSql;
|
|
9
|
+
module.exports = toJoinSql;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
const newJoinCore = require('./newShallowJoinSqlCore');
|
|
2
2
|
const getSessionSingleton = require('../../../getSessionSingleton');
|
|
3
|
+
const lockSql = require('../lockSql');
|
|
3
4
|
|
|
4
|
-
function _new(context, rightTable, leftColumns, rightColumns, leftAlias, rightAlias, filter) {
|
|
5
|
+
function _new(context, rightTable, leftColumns, rightColumns, leftAlias, rightAlias, filter, span) {
|
|
5
6
|
const quote = getSessionSingleton(context, 'quote');
|
|
6
|
-
const
|
|
7
|
+
const tableHint = lockSql.tableHintSql(context, span);
|
|
8
|
+
const sql = ' JOIN ' + quote(rightTable._dbName) + ' ' + quote(rightAlias) + tableHint + ' ON (';
|
|
7
9
|
const joinCore = newJoinCore(context, rightTable, leftColumns, rightColumns, leftAlias, rightAlias, filter);
|
|
8
10
|
return joinCore.prepend(sql).append(')');
|
|
9
11
|
}
|
|
10
12
|
|
|
11
|
-
module.exports = _new;
|
|
13
|
+
module.exports = _new;
|
|
@@ -4,7 +4,7 @@ function toJoinSql(context,leg,alias,childAlias) {
|
|
|
4
4
|
var parentTable = leg.table;
|
|
5
5
|
var columns = leg.columns;
|
|
6
6
|
var childTable = leg.span.table;
|
|
7
|
-
return newShallowJoinSql(context,childTable,parentTable._primaryColumns,columns,alias,childAlias, leg.span.where).prepend(' LEFT');
|
|
7
|
+
return newShallowJoinSql(context,childTable,parentTable._primaryColumns,columns,alias,childAlias, leg.span.where,leg.span).prepend(' LEFT');
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
module.exports = toJoinSql;
|
|
10
|
+
module.exports = toJoinSql;
|