orange-orm 4.8.0 → 4.8.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 (50) hide show
  1. package/dist/index.browser.mjs +4355 -3529
  2. package/dist/index.mjs +5185 -3581
  3. package/docs/changelog.md +7 -1
  4. package/package.json +1 -1
  5. package/src/applyPatch.js +67 -45
  6. package/src/bunPg/newTransaction.js +18 -0
  7. package/src/bunPg/wrapCommand.js +148 -0
  8. package/src/bunSqlite/newTransaction.js +18 -0
  9. package/src/bunSqlite/wrapCommand.js +30 -0
  10. package/src/bunSqlite/wrapQuery.js +1 -1
  11. package/src/d1/newTransaction.js +18 -0
  12. package/src/d1/wrapCommand.js +34 -0
  13. package/src/mssql/newTransaction.js +19 -0
  14. package/src/mssql/wrapCommand.js +129 -0
  15. package/src/mySql/newTransaction.js +18 -0
  16. package/src/mySql/wrapCommand.js +22 -0
  17. package/src/nodeSqlite/newTransaction.js +18 -0
  18. package/src/nodeSqlite/wrapCommand.js +34 -0
  19. package/src/oracle/newTransaction.js +18 -0
  20. package/src/oracle/wrapCommand.js +37 -0
  21. package/src/patchTable.js +111 -17
  22. package/src/pg/newTransaction.js +18 -0
  23. package/src/pg/wrapCommand.js +31 -0
  24. package/src/pglite/newTransaction.js +18 -0
  25. package/src/pglite/wrapCommand.js +30 -0
  26. package/src/sap/newTransaction.js +18 -0
  27. package/src/sqlite3/newTransaction.js +18 -0
  28. package/src/sqlite3/wrapCommand.js +23 -0
  29. package/src/table/commands/delete/newSingleCommand.js +3 -3
  30. package/src/table/commands/delete/singleCommand/newSingleCommandCore.js +112 -3
  31. package/src/table/commands/newDeleteCommand.js +3 -3
  32. package/src/table/commands/newRow.js +2 -2
  33. package/src/table/commands/newUpdateCommand.js +52 -2
  34. package/src/table/commands/newUpdateCommandCore.js +307 -4
  35. package/src/table/executeQueries/executeChanges.js +3 -3
  36. package/src/table/executeQueries/executeCommand.js +8 -0
  37. package/src/table/executeQueries/negotiateNullParams.js +26 -0
  38. package/src/table/executeQueries/resolveExecuteCommand.js +28 -0
  39. package/src/table/executeQueries/resolveExecuteQuery.js +1 -23
  40. package/src/table/isJsonUpdateSupported.js +5 -0
  41. package/src/table/resultToRows/decodeDbRow.js +13 -7
  42. package/src/table/resultToRows/delete.js +43 -2
  43. package/src/table/resultToRows/newDecodeDbRow.js +1 -7
  44. package/src/table.js +62 -0
  45. package/src/tedious/newTransaction.js +18 -0
  46. package/src/tedious/wrapCommand.js +121 -0
  47. package/src/validateDeleteAllowed.js +27 -10
  48. package/src/patchRow.js +0 -13
  49. package/src/table/insertDefault.js +0 -88
  50. package/src/validateDeleteConflict.js +0 -98
@@ -1,19 +1,128 @@
1
1
  var getSessionSingleton = require('../../../getSessionSingleton');
2
+ var newParameterized = require('../../../query/newParameterized');
2
3
 
3
- function newSingleCommandCore(context, table, filter, alias) {
4
+ function newSingleCommandCore(context, table, filter, alias, concurrencyState) {
4
5
  var c = {};
6
+ var quote = getSessionSingleton(context, 'quote');
7
+ var engine = getSessionSingleton(context, 'engine');
8
+ var concurrency = buildConcurrencyChecks(concurrencyState);
9
+ var parameters = filter.parameters ? filter.parameters.slice() : [];
10
+ if (concurrency && concurrency.parameters.length > 0)
11
+ parameters = parameters.concat(concurrency.parameters);
5
12
 
6
13
  c.sql = function() {
7
14
  var whereSql = filter.sql();
15
+ if (concurrency && concurrency.sql) {
16
+ if (whereSql)
17
+ whereSql += ' AND ' + concurrency.sql;
18
+ else
19
+ whereSql = concurrency.sql;
20
+ }
8
21
  if (whereSql)
9
22
  whereSql = ' where ' + whereSql;
10
23
  var deleteFromSql = getSessionSingleton(context, 'deleteFromSql');
11
24
  return deleteFromSql(table, alias, whereSql);
12
25
  };
13
26
 
14
- c.parameters = filter.parameters;
27
+ c.parameters = parameters;
15
28
 
16
29
  return c;
30
+
31
+ function buildConcurrencyChecks(state) {
32
+ const columnsState = state && state.columns;
33
+ if (!columnsState)
34
+ return;
35
+ const parts = [];
36
+ const params = [];
37
+ for (let alias in columnsState) {
38
+ const columnState = columnsState[alias];
39
+ if (!columnState || columnState.concurrency === 'overwrite')
40
+ continue;
41
+ const column = table[alias];
42
+ if (!column)
43
+ continue;
44
+ const encoded = (engine === 'mysql' && column.tsType === 'JSONColumn')
45
+ ? encodeJsonValue(columnState.oldValue, column)
46
+ : column.encode(context, columnState.oldValue);
47
+ const comparison = buildNullSafeComparison(column, encoded);
48
+ if (comparison.sql)
49
+ parts.push(comparison.sql());
50
+ if (comparison.parameters.length > 0)
51
+ params.push(...comparison.parameters);
52
+ }
53
+ if (parts.length === 0)
54
+ return;
55
+ return { sql: parts.join(' AND '), parameters: params };
56
+ }
57
+
58
+ function buildNullSafeComparison(column, encoded) {
59
+ const columnSql = quote(column._dbName);
60
+ if (engine === 'pg') {
61
+ return newParameterized(columnSql + ' IS NOT DISTINCT FROM ' + encoded.sql(), encoded.parameters);
62
+ }
63
+ if (engine === 'mysql') {
64
+ return newParameterized(columnSql + ' <=> ' + encoded.sql(), encoded.parameters);
65
+ }
66
+ if (engine === 'sqlite') {
67
+ return newParameterized(columnSql + ' IS ' + encoded.sql(), encoded.parameters);
68
+ }
69
+ if (engine === 'sap' && column.tsType === 'JSONColumn') {
70
+ if (encoded.sql() === 'null')
71
+ return newParameterized(columnSql + ' IS NULL');
72
+ const casted = newParameterized('CONVERT(VARCHAR(16384), ' + encoded.sql() + ')', encoded.parameters);
73
+ return newParameterized('CONVERT(VARCHAR(16384), ' + columnSql + ')=' + casted.sql(), casted.parameters);
74
+ }
75
+ if (engine === 'oracle' && column.tsType === 'JSONColumn') {
76
+ if (encoded.sql() === 'null')
77
+ return newParameterized(columnSql + ' IS NULL');
78
+ const jsonValue = newParameterized('JSON(' + encoded.sql() + ')', encoded.parameters);
79
+ return newParameterized('JSON_EQUAL(' + columnSql + ', ' + jsonValue.sql() + ')', jsonValue.parameters);
80
+ }
81
+ if (encoded.sql() === 'null')
82
+ return newParameterized(columnSql + ' IS NULL');
83
+ return newParameterized(columnSql + '=' + encoded.sql(), encoded.parameters);
84
+ }
85
+
86
+ function encodeJsonValue(value, column) {
87
+ if (engine === 'oracle') {
88
+ if (value === null || value === undefined)
89
+ return newParameterized('null');
90
+ if (isJsonObject(value))
91
+ return column.encode(context, value);
92
+ if (typeof value === 'boolean' || typeof value === 'number')
93
+ return newParameterized('?', [String(value)]);
94
+ return newParameterized('?', [value]);
95
+ }
96
+ if (engine === 'pg') {
97
+ const jsonValue = JSON.stringify(value === undefined ? null : value);
98
+ return newParameterized('?::jsonb', [jsonValue]);
99
+ }
100
+ if (engine === 'mysql') {
101
+ const jsonValue = JSON.stringify(value === undefined ? null : value);
102
+ return newParameterized('CAST(? AS JSON)', [jsonValue]);
103
+ }
104
+ if (engine === 'sqlite') {
105
+ if (isJsonObject(value)) {
106
+ const jsonValue = JSON.stringify(value);
107
+ return newParameterized('?', [jsonValue]);
108
+ }
109
+ if (value === null || value === undefined)
110
+ return newParameterized('null');
111
+ return newParameterized('?', [value]);
112
+ }
113
+ if (engine === 'mssql' || engine === 'mssqlNative') {
114
+ if (isJsonObject(value))
115
+ return newParameterized('JSON_QUERY(?)', [JSON.stringify(value)]);
116
+ if (value === null || value === undefined)
117
+ return newParameterized('null');
118
+ return newParameterized('?', [String(value)]);
119
+ }
120
+ return column.encode(context, value);
121
+ }
122
+
123
+ function isJsonObject(value) {
124
+ return value && typeof value === 'object';
125
+ }
17
126
  }
18
127
 
19
- module.exports = newSingleCommandCore;
128
+ module.exports = newSingleCommandCore;
@@ -1,7 +1,7 @@
1
1
  var newSingleCommand = require('./delete/newSingleCommand');
2
2
 
3
- function newCommand(context, queries, table, filter, strategy, relations) {
4
- var singleCommand = newSingleCommand(context, table, filter, relations);
3
+ function newCommand(context, queries, table, filter, strategy, relations, concurrencyState) {
4
+ var singleCommand = newSingleCommand(context, table, filter, relations, concurrencyState);
5
5
  for (var name in strategy) {
6
6
  if (!(strategy[name] === null || strategy[name]))
7
7
  continue;
@@ -15,4 +15,4 @@ function newCommand(context, queries, table, filter, strategy, relations) {
15
15
  return queries;
16
16
  }
17
17
 
18
- module.exports = newCommand;
18
+ module.exports = newCommand;
@@ -1,7 +1,7 @@
1
1
  var decodeDbRow = require('../resultToRows/decodeDbRow');
2
2
  var flags = require('../../flags');
3
3
 
4
- function newRow(context, {table, _options}) {
4
+ function newRow(context, {table, _options, shouldValidate = true}) {
5
5
  var dto = {};
6
6
  table._columns.forEach(addColumn);
7
7
 
@@ -41,7 +41,7 @@ function newRow(context, {table, _options}) {
41
41
  dto[column.alias] = pkValue;
42
42
  }
43
43
 
44
- return decodeDbRow(context, table, table, dto, true, true);
44
+ return decodeDbRow(context, table, table, dto, shouldValidate, true);
45
45
  }
46
46
 
47
47
  function isObject(object) {
@@ -16,6 +16,9 @@ function UpdateCommand(context, table, column, row) {
16
16
  this._columnList[column.alias] = column;
17
17
  this.onFieldChanged = this.onFieldChanged.bind(this);
18
18
  row.subscribeChanged(this.onFieldChanged);
19
+ this._concurrencyState = undefined;
20
+ this._concurrencySummary = undefined;
21
+ this._usesReturning = false;
19
22
  }
20
23
 
21
24
  UpdateCommand.prototype.onFieldChanged = function(_row, column) {
@@ -33,11 +36,20 @@ Object.defineProperty(UpdateCommand.prototype, 'parameters', {
33
36
  });
34
37
 
35
38
  UpdateCommand.prototype._getCoreCommand = function() {
36
- return this.__getCoreCommand(this._table, this._columnList, this._row);
39
+ return this.__getCoreCommand(this._table, this._columnList, this._row, this._concurrencyState);
37
40
  };
38
41
 
39
42
  UpdateCommand.prototype.endEdit = function() {
40
- this._getCoreCommand();
43
+ this._concurrencyState = this._row._concurrencyState;
44
+ delete this._row._concurrencyState;
45
+
46
+ const coreCommand = this._getCoreCommand();
47
+ delete this._row._jsonUpdateState;
48
+ this._usesReturning = Boolean(coreCommand._usesReturning);
49
+ this._concurrencySummary = summarizeConcurrency(this._concurrencyState);
50
+ if (this._concurrencySummary.hasConcurrency)
51
+ this.onResult = this._onConcurrencyResult.bind(this);
52
+
41
53
  this._row.unsubscribeChanged(this.onFieldChanged);
42
54
  let dto = JSON.parse(JSON.stringify(createDto(this._table, this._row)));
43
55
  this._patch = createPatch([JSON.parse(this._row._oldValues)],[dto]);
@@ -59,4 +71,42 @@ Object.defineProperty(UpdateCommand.prototype, 'disallowCompress', {
59
71
  }
60
72
  });
61
73
 
74
+ UpdateCommand.prototype._onConcurrencyResult = function(result) {
75
+ const rowCount = extractRowCount(result, this._usesReturning);
76
+ if (rowCount === undefined)
77
+ return;
78
+ if (rowCount === 0 && this._concurrencySummary.hasOptimistic) {
79
+ throw new Error('The row was changed by another user.');
80
+ }
81
+ };
82
+
83
+ function summarizeConcurrency(concurrencyState) {
84
+ const summary = { hasConcurrency: false, hasOptimistic: false };
85
+ if (!concurrencyState || !concurrencyState.columns)
86
+ return summary;
87
+ for (let name in concurrencyState.columns) {
88
+ const state = concurrencyState.columns[name];
89
+ if (!state)
90
+ continue;
91
+ const strategy = state.concurrency || 'optimistic';
92
+ if (strategy === 'overwrite')
93
+ continue;
94
+ summary.hasConcurrency = true;
95
+ if (strategy === 'optimistic')
96
+ summary.hasOptimistic = true;
97
+ }
98
+ return summary;
99
+ }
100
+
101
+ function extractRowCount(result, usesReturning) {
102
+ if (usesReturning && Array.isArray(result))
103
+ return result.length;
104
+ if (Array.isArray(result) && typeof result[0].rowsAffected === 'number')
105
+ return result[0].rowsAffected;
106
+ if (!result || typeof result !== 'object')
107
+ return;
108
+ if (typeof result.rowsAffected === 'number')
109
+ return result.rowsAffected;
110
+ }
111
+
62
112
  module.exports = newUpdateCommand;
@@ -1,20 +1,31 @@
1
1
  const getSessionSingleton = require('../getSessionSingleton');
2
2
  var newParameterized = require('../query/newParameterized');
3
+ const isJsonUpdateSupported = require('../isJsonUpdateSupported');
3
4
 
4
- function newUpdateCommandCore(context, table, columns, row) {
5
+ function newUpdateCommandCore(context, table, columns, row, concurrencyState) {
5
6
  const quote = getSessionSingleton(context, 'quote');
7
+ const engine = getSessionSingleton(context, 'engine');
6
8
  var command = newParameterized('UPDATE ' + quote(table._dbName) + ' SET');
7
9
  var separator = ' ';
8
10
 
9
11
  addColumns();
10
12
  addWhereId();
11
13
  addDiscriminators();
14
+ addConcurrencyChecks();
12
15
 
13
16
  function addColumns() {
14
17
  for (var alias in columns) {
15
18
  var column = columns[alias];
16
- var encoded = column.encode(context, row[alias]);
17
- command = command.append(separator + quote(column._dbName) + '=').append(encoded);
19
+ const columnSql = quote(column._dbName);
20
+ const jsonUpdate = row._jsonUpdateState && row._jsonUpdateState[alias];
21
+ if (jsonUpdate && jsonUpdate.patches && jsonUpdate.patches.length) {
22
+ const updated = buildJsonUpdateExpression(columnSql, jsonUpdate.patches, column);
23
+ command = command.append(separator + columnSql + '=').append(updated);
24
+ }
25
+ else {
26
+ var encoded = column.encode(context, row[alias]);
27
+ command = command.append(separator + columnSql + '=').append(encoded);
28
+ }
18
29
  separator = ',';
19
30
  }
20
31
  }
@@ -39,9 +50,301 @@ function newUpdateCommandCore(context, table, columns, row) {
39
50
  command = command.append(discriminators);
40
51
  }
41
52
 
53
+ function addConcurrencyChecks() {
54
+ const columnsState = concurrencyState && concurrencyState.columns;
55
+ if (!columnsState)
56
+ return;
57
+ for (let alias in columnsState) {
58
+ const state = columnsState[alias];
59
+ if (!state || state.concurrency === 'overwrite')
60
+ continue;
61
+ const column = table[alias];
62
+ if (!column)
63
+ continue;
64
+ if (state.paths && state.paths.length) {
65
+ for (let i = 0; i < state.paths.length; i++) {
66
+ const pathState = state.paths[i];
67
+ const encoded = encodeJsonValue(pathState.oldValue, column);
68
+ const jsonPath = buildJsonPath(pathState.path);
69
+ const columnExpr = buildJsonExtractExpression(quote(column._dbName), jsonPath, pathState.oldValue);
70
+ command = appendJsonPathComparison(columnExpr, encoded);
71
+ }
72
+ }
73
+ else {
74
+ const encoded = (engine === 'mysql' && column.tsType === 'JSONColumn')
75
+ ? encodeJsonValue(state.oldValue, column)
76
+ : column.encode(context, state.oldValue);
77
+ command = appendNullSafeComparison(column, encoded);
78
+ }
79
+ }
80
+ }
81
+
82
+ function appendNullSafeComparison(column, encoded) {
83
+ const columnSql = quote(column._dbName);
84
+ if (engine === 'pg') {
85
+ command = command.append(separator + columnSql + ' IS NOT DISTINCT FROM ').append(encoded);
86
+ }
87
+ else if (engine === 'mysql') {
88
+ command = command.append(separator + columnSql + ' <=> ').append(encoded);
89
+ }
90
+ else if (engine === 'sqlite') {
91
+ command = command.append(separator + columnSql + ' IS ').append(encoded);
92
+ }
93
+ else if (engine === 'sap' && column.tsType === 'JSONColumn') {
94
+ if (encoded.sql() === 'null') {
95
+ command = command.append(separator + columnSql + ' IS NULL');
96
+ }
97
+ else {
98
+ const casted = newParameterized('CONVERT(VARCHAR(16384), ' + encoded.sql() + ')', encoded.parameters);
99
+ command = command.append(separator + 'CONVERT(VARCHAR(16384), ' + columnSql + ')=') .append(casted);
100
+ }
101
+ }
102
+ else if (engine === 'oracle' && column.tsType === 'JSONColumn') {
103
+ if (encoded.sql() === 'null') {
104
+ command = command.append(separator + columnSql + ' IS NULL');
105
+ }
106
+ else {
107
+ const jsonValue = newParameterized('JSON(' + encoded.sql() + ')', encoded.parameters);
108
+ const compare = newParameterized('JSON_EQUAL(' + columnSql + ', ' + jsonValue.sql() + ')', jsonValue.parameters);
109
+ command = command.append(separator).append(compare);
110
+ }
111
+ }
112
+ else {
113
+ if (encoded.sql() === 'null')
114
+ command = command.append(separator + columnSql + ' IS NULL');
115
+ else
116
+ command = command.append(separator + columnSql + '=').append(encoded);
117
+ }
118
+ separator = ' AND ';
119
+ return command;
120
+ }
121
+
122
+ function appendJsonPathComparison(columnExpr, encoded) {
123
+ if (engine === 'pg') {
124
+ command = command.append(separator).append(columnExpr).append(' IS NOT DISTINCT FROM ').append(encoded);
125
+ }
126
+ else if (engine === 'mysql') {
127
+ command = command.append(separator).append(columnExpr).append(' <=> ').append(encoded);
128
+ }
129
+ else if (engine === 'sqlite') {
130
+ command = command.append(separator).append(columnExpr).append(' IS ').append(encoded);
131
+ }
132
+ else if (engine === 'oracle') {
133
+ const isJsonQuery = columnExpr.sql().indexOf('JSON_QUERY(') === 0;
134
+ if (encoded.sql() === 'null') {
135
+ command = command.append(separator).append(columnExpr).append(' IS NULL');
136
+ }
137
+ else if (isJsonQuery) {
138
+ const jsonValue = newParameterized('JSON(' + encoded.sql() + ')', encoded.parameters);
139
+ const compare = newParameterized('JSON_EQUAL(' + columnExpr.sql() + ', ' + jsonValue.sql() + ')', columnExpr.parameters.concat(jsonValue.parameters));
140
+ command = command.append(separator).append(compare);
141
+ }
142
+ else {
143
+ command = command.append(separator).append(columnExpr).append('=').append(encoded);
144
+ }
145
+ }
146
+ else {
147
+ if (encoded.sql() === 'null')
148
+ command = command.append(separator).append(columnExpr).append(' IS NULL');
149
+ else
150
+ command = command.append(separator).append(columnExpr).append('=').append(encoded);
151
+ }
152
+ separator = ' AND ';
153
+ return command;
154
+ }
155
+
156
+ function buildJsonUpdateExpression(columnSql, patches, column) {
157
+ if (!isJsonUpdateSupported(engine))
158
+ return column.encode(context, row[column.alias]);
159
+ let expr = newParameterized(columnSql);
160
+ for (let i = 0; i < patches.length; i++) {
161
+ const patch = patches[i];
162
+ expr = applyJsonPatchExpression(expr, patch, column);
163
+ }
164
+ return expr;
165
+ }
166
+
167
+ function applyJsonPatchExpression(expr, patch, column) {
168
+ const path = patch.path || [];
169
+ const jsonPath = buildJsonPath(path);
170
+ if (patch.op === 'remove')
171
+ return buildJsonRemoveExpression(expr, jsonPath);
172
+ return buildJsonSetExpression(expr, jsonPath, patch.value, column);
173
+ }
174
+
175
+ function buildJsonSetExpression(expr, jsonPath, value, column) {
176
+ if (engine === 'pg') {
177
+ const pathLiteral = buildPgPathLiteral(jsonPath.tokens);
178
+ const jsonValue = JSON.stringify(value === undefined ? null : value);
179
+ const sql = 'jsonb_set(' + expr.sql() + ', ' + pathLiteral + ', ?::jsonb, true)';
180
+ return newParameterized(sql, expr.parameters.concat([jsonValue]));
181
+ }
182
+ if (engine === 'mysql') {
183
+ const jsonValue = JSON.stringify(value === undefined ? null : value);
184
+ const sql = 'JSON_SET(' + expr.sql() + ', ' + jsonPath.sql + ', CAST(? AS JSON))';
185
+ return newParameterized(sql, expr.parameters.concat(jsonPath.parameters, [jsonValue]));
186
+ }
187
+ if (engine === 'sqlite') {
188
+ const jsonValue = JSON.stringify(value === undefined ? null : value);
189
+ const sql = 'json_set(' + expr.sql() + ', ' + jsonPath.sql + ', json(?))';
190
+ return newParameterized(sql, expr.parameters.concat(jsonPath.parameters, [jsonValue]));
191
+ }
192
+ if (engine === 'mssql' || engine === 'mssqlNative') {
193
+ const mssqlValue = buildMssqlJsonValue(value);
194
+ const sql = 'JSON_MODIFY(' + expr.sql() + ', ' + jsonPath.sql + ', ' + mssqlValue.sql() + ')';
195
+ return newParameterized(sql, expr.parameters.concat(jsonPath.parameters, mssqlValue.parameters));
196
+ }
197
+ if (engine === 'oracle') {
198
+ const jsonValue = JSON.stringify(value === undefined ? null : value);
199
+ const sql = 'JSON_TRANSFORM(' + expr.sql() + ', SET ' + jsonPath.sql + ' = JSON(?))';
200
+ return newParameterized(sql, expr.parameters.concat(jsonPath.parameters, [jsonValue]));
201
+ }
202
+ return column.encode(context, row[column.alias]);
203
+ }
204
+
205
+ function buildJsonRemoveExpression(expr, jsonPath) {
206
+ if (engine === 'pg') {
207
+ const pathLiteral = buildPgPathLiteral(jsonPath.tokens);
208
+ const sql = expr.sql() + ' #- ' + pathLiteral;
209
+ return newParameterized(sql, expr.parameters);
210
+ }
211
+ if (engine === 'mysql') {
212
+ const sql = 'JSON_REMOVE(' + expr.sql() + ', ' + jsonPath.sql + ')';
213
+ return newParameterized(sql, expr.parameters.concat(jsonPath.parameters));
214
+ }
215
+ if (engine === 'sqlite') {
216
+ const sql = 'json_remove(' + expr.sql() + ', ' + jsonPath.sql + ')';
217
+ return newParameterized(sql, expr.parameters.concat(jsonPath.parameters));
218
+ }
219
+ if (engine === 'mssql' || engine === 'mssqlNative') {
220
+ const sql = 'JSON_MODIFY(' + expr.sql() + ', ' + jsonPath.sql + ', NULL)';
221
+ return newParameterized(sql, expr.parameters.concat(jsonPath.parameters));
222
+ }
223
+ if (engine === 'oracle') {
224
+ const sql = 'JSON_TRANSFORM(' + expr.sql() + ', REMOVE ' + jsonPath.sql + ')';
225
+ return newParameterized(sql, expr.parameters.concat(jsonPath.parameters));
226
+ }
227
+ return expr;
228
+ }
229
+
230
+ function buildJsonExtractExpression(columnSql, jsonPath, oldValue) {
231
+ if (engine === 'pg') {
232
+ const sql = columnSql + ' #> ' + buildPgPathLiteral(jsonPath.tokens);
233
+ return newParameterized(sql);
234
+ }
235
+ if (engine === 'mysql') {
236
+ const sql = 'JSON_EXTRACT(' + columnSql + ', ' + jsonPath.sql + ')';
237
+ return newParameterized(sql, jsonPath.parameters);
238
+ }
239
+ if (engine === 'sqlite') {
240
+ const sql = 'json_extract(' + columnSql + ', ' + jsonPath.sql + ')';
241
+ return newParameterized(sql, jsonPath.parameters);
242
+ }
243
+ if (engine === 'mssql' || engine === 'mssqlNative') {
244
+ const fn = isJsonObject(oldValue) ? 'JSON_QUERY' : 'JSON_VALUE';
245
+ const sql = fn + '(' + columnSql + ', ' + jsonPath.sql + ')';
246
+ return newParameterized(sql, jsonPath.parameters);
247
+ }
248
+ if (engine === 'oracle') {
249
+ const fn = isJsonObject(oldValue) ? 'JSON_QUERY' : 'JSON_VALUE';
250
+ const sql = fn + '(' + columnSql + ', ' + jsonPath.sql + ')';
251
+ return newParameterized(sql, jsonPath.parameters);
252
+ }
253
+ return newParameterized(columnSql);
254
+ }
255
+
256
+ function buildJsonPath(pathTokens) {
257
+ const tokens = Array.isArray(pathTokens) ? pathTokens : [];
258
+ if (engine === 'pg')
259
+ return { tokens, sql: buildPgPathLiteral(tokens), parameters: [] };
260
+ if (engine === 'oracle') {
261
+ let jsonPath = '$';
262
+ for (let i = 0; i < tokens.length; i++) {
263
+ const token = String(tokens[i]);
264
+ if (/^\d+$/.test(token))
265
+ jsonPath += '[' + token + ']';
266
+ else if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(token))
267
+ jsonPath += '.' + token;
268
+ else
269
+ jsonPath += '["' + token.replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"]';
270
+ }
271
+ return { tokens, sql: '\'' + jsonPath.replace(/'/g, '\'\'') + '\'', parameters: [] };
272
+ }
273
+ let jsonPath = '$';
274
+ for (let i = 0; i < tokens.length; i++) {
275
+ const token = String(tokens[i]);
276
+ if (/^\d+$/.test(token))
277
+ jsonPath += '[' + token + ']';
278
+ else if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(token))
279
+ jsonPath += '.' + token;
280
+ else
281
+ jsonPath += '["' + token.replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"]';
282
+ }
283
+ return { tokens, sql: '?', parameters: [jsonPath] };
284
+ }
285
+
286
+ function buildPgPathLiteral(tokens) {
287
+ const parts = tokens.map(token => {
288
+ const text = String(token);
289
+ if (/^[A-Za-z0-9_]+$/.test(text))
290
+ return text;
291
+ return '"' + text.replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"';
292
+ });
293
+ return '\'{'+ parts.join(',') + '}\'';
294
+ }
295
+
296
+ function encodeJsonValue(value, column) {
297
+ if (engine === 'oracle') {
298
+ if (value === null || value === undefined)
299
+ return newParameterized('null');
300
+ if (isJsonObject(value))
301
+ return column.encode(context, value);
302
+ if (typeof value === 'boolean' || typeof value === 'number')
303
+ return newParameterized('?', [String(value)]);
304
+ return newParameterized('?', [value]);
305
+ }
306
+ if (engine === 'pg') {
307
+ const jsonValue = JSON.stringify(value === undefined ? null : value);
308
+ return newParameterized('?::jsonb', [jsonValue]);
309
+ }
310
+ if (engine === 'mysql') {
311
+ const jsonValue = JSON.stringify(value === undefined ? null : value);
312
+ return newParameterized('CAST(? AS JSON)', [jsonValue]);
313
+ }
314
+ if (engine === 'sqlite') {
315
+ if (isJsonObject(value)) {
316
+ const jsonValue = JSON.stringify(value);
317
+ return newParameterized('?', [jsonValue]);
318
+ }
319
+ if (value === null || value === undefined)
320
+ return newParameterized('null');
321
+ return newParameterized('?', [value]);
322
+ }
323
+ if (engine === 'mssql' || engine === 'mssqlNative') {
324
+ if (isJsonObject(value))
325
+ return newParameterized('JSON_QUERY(?)', [JSON.stringify(value)]);
326
+ if (value === null || value === undefined)
327
+ return newParameterized('null');
328
+ return newParameterized('?', [String(value)]);
329
+ }
330
+ return column.encode(context, value);
331
+ }
332
+
333
+ function buildMssqlJsonValue(value) {
334
+ if (isJsonObject(value))
335
+ return newParameterized('JSON_QUERY(?)', [JSON.stringify(value)]);
336
+ if (value === null || value === undefined)
337
+ return newParameterized('null');
338
+ return newParameterized('?', [value]);
339
+ }
340
+
341
+ function isJsonObject(value) {
342
+ return value && typeof value === 'object';
343
+ }
344
+
42
345
  return command;
43
346
 
44
347
 
45
348
  }
46
349
 
47
- module.exports = newUpdateCommandCore;
350
+ module.exports = newUpdateCommandCore;
@@ -1,4 +1,4 @@
1
- var executeQuery = require('./executeQuery');
1
+ var executeCommand = require('./executeCommand');
2
2
  var newPromise = require('../promise');
3
3
 
4
4
  function executeChanges(context, queries) {
@@ -11,9 +11,9 @@ function executeChanges(context, queries) {
11
11
  function execute() {
12
12
  i++;
13
13
  if (i + 1 === queries.length)
14
- return executeQuery(context, queries[i]).then(notifyListener);
14
+ return executeCommand(context, queries[i]).then(notifyListener);
15
15
  else {
16
- return executeQuery(context, queries[i]).then(notifyListener).then(execute);
16
+ return executeCommand(context, queries[i]).then(notifyListener).then(execute);
17
17
  }
18
18
  }
19
19
 
@@ -0,0 +1,8 @@
1
+ var newResolver = require('./resolveExecuteCommand');
2
+
3
+ function executeCommand(context, query) {
4
+ var resolver = newResolver(context, query);
5
+ return new Promise(resolver);
6
+ }
7
+
8
+ module.exports = executeCommand;
@@ -0,0 +1,26 @@
1
+ function negotiateNullParams(query) {
2
+ if (query && query.parameters && query.parameters.length > 0 && (query.parameters.filter(x => x === null || x === undefined).length > 0)) {
3
+ var splitted = query.sql().split('?');
4
+ var sql = '';
5
+ var parameters = [];
6
+ var lastIndex = splitted.length - 1;
7
+ for (var i = 0; i < lastIndex; i++) {
8
+ if (query.parameters[i] === null || query.parameters[i] === undefined)
9
+ sql += splitted[i] + 'null';
10
+ else {
11
+ sql += splitted[i] + '?';
12
+ parameters.push(query.parameters[i]);
13
+ }
14
+ }
15
+ sql += splitted[lastIndex];
16
+ return {
17
+ sql: () => sql,
18
+ parameters
19
+ };
20
+
21
+ }
22
+ else
23
+ return query;
24
+ }
25
+
26
+ module.exports = negotiateNullParams;
@@ -0,0 +1,28 @@
1
+ const getSessionSingleton = require('../getSessionSingleton');
2
+ const negotiateNullParams = require('./negotiateNullParams');
3
+
4
+ function resolveExecuteQuery(context, query) {
5
+ return resolve;
6
+
7
+ function resolve(success, failed) {
8
+ try {
9
+ var client = getSessionSingleton(context, 'dbClient');
10
+ query = negotiateNullParams(query);
11
+ client.executeCommand(query, onCompleted);
12
+ } catch (e) {
13
+ failed(e);
14
+ }
15
+
16
+ function onCompleted(err, rows) {
17
+ if (!err)
18
+ success(rows);
19
+ else
20
+ failed(err);
21
+ }
22
+ }
23
+
24
+ }
25
+
26
+
27
+
28
+ module.exports = resolveExecuteQuery;