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/docs/changelog.md CHANGED
@@ -1,4 +1,6 @@
1
1
  ## Changelog
2
+ __5.4.0__
3
+ Support for `Select for update` and `skip locked`. [#193](https://github.com/alfateam/orange-orm/issues/193)
2
4
  __5.3.6__
3
5
  Fix npm package contents by excluding the demo project and local install artifacts.
4
6
  __5.3.5__
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orange-orm",
3
- "version": "5.3.6",
3
+ "version": "5.4.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
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 (JSON.stringify(a) !== JSON.stringify(b))
112
+ if (!deepEqual(a, b))
131
113
  throw new Error('A, b are not equal');
132
114
  }
133
115
 
134
- module.exports = applyPatch;
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;
@@ -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 (strategy !== undefined) {
592
- const { limit, ...cleanStrategy } = { ...strategy };
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, strategy);
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
- let { limit, ...strategy } = { ...context.strategy };
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
- const { limit, ...strategy } = { ...fetchingStrategyMap.get(obj) };
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
- rootMap.set(array, { json: cloneFromDb(array), strategy, originalArray: [...array] });
884
- fetchingStrategyMap.set(array, strategy);
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
- rootMap.set(row, { json: cloneFromDb(row), strategy });
943
- fetchingStrategyMap.set(row, strategy);
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 + ' ' + quote(alias)).append(joinSql).append(whereSql).append(orderBy + offset);
21
+ return newParameterized(selectClause + limit + columnSql + ' from ' + name + ' ' + quotedAlias + tableHint).append(joinSql).append(whereSql).append(orderBy + offset + lockClause);
18
22
 
19
23
  }
20
24
 
@@ -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(_alias) {
2
- return ' FOR UPDATE';
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
- const quote = require('../table/quote');
2
-
3
- module.exports = function(alias) {
4
- return ' FOR UPDATE OF ' + quote(alias);
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(alias) {
4
- return ' FOR UPDATE OF ' + quote(alias);
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
- const quote = require('../table/quote');
2
-
3
- module.exports = function(alias) {
4
- return ' FOR UPDATE OF ' + quote(alias);
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
- const quote = require('../table/quote');
2
-
3
- module.exports = function(context, alias) {
4
- return ' FOR UPDATE OF ' + quote(context, alias);
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
  }
@@ -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 = 2; i < primaryColumns.length; i++) {
6
- key = arguments[i+1];
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 negotiateExclusive = require('./singleQuery/negotiateExclusive');
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 exclusiveClause = negotiateExclusive(table, alias, exclusive);
17
- return newParameterized('select' + safeLimit + ' ' + columnSql + ' from ' + name + ' ' + quote(context, alias))
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 + exclusiveClause);
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 sql = ' JOIN ' + quote(rightTable._dbName) + ' ' + quote(rightAlias) + ' ON (';
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;