@ductape/sdk 0.4.4 → 0.4.6
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/dist/context/execution-context.d.ts +6 -0
- package/dist/context/execution-context.js +1 -1
- package/dist/context/execution-context.js.map +1 -1
- package/dist/database/adapters/dynamodb.adapter.d.ts +2 -0
- package/dist/database/adapters/dynamodb.adapter.js +18 -1
- package/dist/database/adapters/dynamodb.adapter.js.map +1 -1
- package/dist/database/adapters/mongodb.adapter.d.ts +4 -0
- package/dist/database/adapters/mongodb.adapter.js +58 -14
- package/dist/database/adapters/mongodb.adapter.js.map +1 -1
- package/dist/database/adapters/mysql.adapter.d.ts +2 -0
- package/dist/database/adapters/mysql.adapter.js +90 -15
- package/dist/database/adapters/mysql.adapter.js.map +1 -1
- package/dist/database/adapters/postgresql.adapter.d.ts +2 -0
- package/dist/database/adapters/postgresql.adapter.js +24 -4
- package/dist/database/adapters/postgresql.adapter.js.map +1 -1
- package/dist/database/atomic-batch.d.ts +58 -0
- package/dist/database/atomic-batch.js +183 -0
- package/dist/database/atomic-batch.js.map +1 -0
- package/dist/database/atomic-pipeline.d.ts +27 -0
- package/dist/database/atomic-pipeline.js +118 -0
- package/dist/database/atomic-pipeline.js.map +1 -0
- package/dist/database/databases.service.d.ts +9 -0
- package/dist/database/databases.service.js +234 -3
- package/dist/database/databases.service.js.map +1 -1
- package/dist/database/dynamodb-atomic-batch.d.ts +8 -0
- package/dist/database/dynamodb-atomic-batch.js +153 -0
- package/dist/database/dynamodb-atomic-batch.js.map +1 -0
- package/dist/database/index.d.ts +2 -0
- package/dist/database/index.js +4 -2
- package/dist/database/index.js.map +1 -1
- package/dist/database/sql-context.d.ts +17 -0
- package/dist/database/sql-context.js +51 -0
- package/dist/database/sql-context.js.map +1 -0
- package/dist/database/transactions/transaction-manager.d.ts +5 -1
- package/dist/database/transactions/transaction-manager.js +13 -2
- package/dist/database/transactions/transaction-manager.js.map +1 -1
- package/dist/database/types/index.d.ts +1 -0
- package/dist/database/types/pipeline.interface.d.ts +19 -0
- package/dist/database/types/pipeline.interface.js +22 -0
- package/dist/database/types/pipeline.interface.js.map +1 -0
- package/dist/index.d.ts +9 -1
- package/dist/index.js +17 -2
- package/dist/index.js.map +1 -1
- package/dist/logs/logs.service.js +10 -0
- package/dist/logs/logs.service.js.map +1 -1
- package/dist/processor/services/processor.service.d.ts +2 -2
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -41,17 +41,38 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
41
41
|
return result;
|
|
42
42
|
};
|
|
43
43
|
})();
|
|
44
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
45
|
+
var t = {};
|
|
46
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
47
|
+
t[p] = s[p];
|
|
48
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
49
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
50
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
51
|
+
t[p[i]] = s[p[i]];
|
|
52
|
+
}
|
|
53
|
+
return t;
|
|
54
|
+
};
|
|
44
55
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
45
56
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
46
57
|
};
|
|
47
58
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
59
|
exports.databaseService = exports.DatabaseConnection = exports.DatabaseService = void 0;
|
|
49
60
|
const enums_1 = require("./types/enums");
|
|
61
|
+
const pipeline_interface_1 = require("./types/pipeline.interface");
|
|
62
|
+
const atomic_pipeline_1 = require("./atomic-pipeline");
|
|
63
|
+
const sql_context_1 = require("./sql-context");
|
|
50
64
|
const presave_processor_1 = require("./presave/presave-processor");
|
|
65
|
+
const atomic_batch_1 = require("./atomic-batch");
|
|
66
|
+
const dynamodb_adapter_1 = require("./adapters/dynamodb.adapter");
|
|
67
|
+
const transaction_manager_1 = require("./transactions/transaction-manager");
|
|
68
|
+
const mongodb_adapter_1 = require("./adapters/mongodb.adapter");
|
|
69
|
+
const postgresql_adapter_1 = require("./adapters/postgresql.adapter");
|
|
70
|
+
const mysql_adapter_1 = require("./adapters/mysql.adapter");
|
|
71
|
+
const context_1 = require("../context");
|
|
51
72
|
const adapter_factory_1 = require("./adapters/adapter.factory");
|
|
52
73
|
const query_builder_1 = require("./operators/query-builder");
|
|
53
74
|
const aggregation_builder_1 = require("./operators/aggregation-builder");
|
|
54
|
-
const
|
|
75
|
+
const transaction_manager_2 = require("./transactions/transaction-manager");
|
|
55
76
|
const schema_manager_1 = require("./schema/schema-manager");
|
|
56
77
|
const migration_engine_1 = require("./migrations/migration-engine");
|
|
57
78
|
const action_manager_1 = require("./actions/action-manager");
|
|
@@ -94,6 +115,7 @@ class DatabaseService {
|
|
|
94
115
|
this.adapters = new Map();
|
|
95
116
|
this.connectionContexts = new Map();
|
|
96
117
|
this.currentContext = null;
|
|
118
|
+
this.transactionScopes = new WeakMap();
|
|
97
119
|
this.schemaManagers = new Map();
|
|
98
120
|
this.migrationEngines = new Map();
|
|
99
121
|
/** Service configuration */
|
|
@@ -116,7 +138,7 @@ class DatabaseService {
|
|
|
116
138
|
this.config = config || null;
|
|
117
139
|
this.preInitializedProductBuilder = config === null || config === void 0 ? void 0 : config.preInitializedProductBuilder;
|
|
118
140
|
this.adapterFactory = new adapter_factory_1.AdapterFactory();
|
|
119
|
-
this.transactionManager = new
|
|
141
|
+
this.transactionManager = new transaction_manager_2.TransactionManager();
|
|
120
142
|
this.actionManager = new action_manager_1.ActionManager(this);
|
|
121
143
|
this._privateKey = (config === null || config === void 0 ? void 0 : config.private_key) || '';
|
|
122
144
|
if ((config === null || config === void 0 ? void 0 : config.default_product) || (config === null || config === void 0 ? void 0 : config.default_env)) {
|
|
@@ -1356,6 +1378,9 @@ class DatabaseService {
|
|
|
1356
1378
|
*/
|
|
1357
1379
|
async raw(options) {
|
|
1358
1380
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
1381
|
+
// A cached response cannot represent this transaction's snapshot or own writes.
|
|
1382
|
+
if (options.transaction && options.cache)
|
|
1383
|
+
options = Object.assign(Object.assign({}, options), { cache: undefined });
|
|
1359
1384
|
const process_id = (0, processor_utils_1.generateObjectId)();
|
|
1360
1385
|
const start = Date.now();
|
|
1361
1386
|
this.initializeLogService();
|
|
@@ -1860,6 +1885,104 @@ class DatabaseService {
|
|
|
1860
1885
|
* where: { status: 'completed' },
|
|
1861
1886
|
* });
|
|
1862
1887
|
*/
|
|
1888
|
+
async atomicPipeline(options) {
|
|
1889
|
+
const { context: _context } = options, batchInput = __rest(options, ["context"]);
|
|
1890
|
+
(0, atomic_batch_1.validateAtomicBatch)(batchInput);
|
|
1891
|
+
// Retain only the opaque session and transaction by identity across awaits.
|
|
1892
|
+
options = Object.assign(Object.assign({}, options), { context: JSON.parse(JSON.stringify(options.context)), operations: JSON.parse(JSON.stringify(options.operations)) });
|
|
1893
|
+
return (0, atomic_pipeline_1.executeAtomicPipeline)(options, {
|
|
1894
|
+
preflight: async (scope) => {
|
|
1895
|
+
try {
|
|
1896
|
+
await this.ensureAdapterFor(scope);
|
|
1897
|
+
const adapter = this.getAdapter(scope);
|
|
1898
|
+
if (!(adapter instanceof mongodb_adapter_1.MongoDBAdapter || adapter instanceof postgresql_adapter_1.PostgreSQLAdapter || adapter instanceof mysql_adapter_1.MySQLAdapter))
|
|
1899
|
+
throw new Error('Adapter cannot provide a transactional context pipeline');
|
|
1900
|
+
if ((adapter instanceof mongodb_adapter_1.MongoDBAdapter) === !!scope.context.sql)
|
|
1901
|
+
throw new Error('Context dialect does not match adapter');
|
|
1902
|
+
if (scope.transaction && (!(scope.transaction instanceof transaction_manager_1.Transaction) || !scope.transaction.belongsTo(adapter)
|
|
1903
|
+
|| !scope.transaction.isActive() || this.transactionScopes.get(scope.transaction) !== JSON.stringify([scope.product, scope.env, scope.database])))
|
|
1904
|
+
throw new Error('Transaction scope mismatch');
|
|
1905
|
+
if (adapter instanceof mongodb_adapter_1.MongoDBAdapter)
|
|
1906
|
+
await adapter.assertAtomicBatchSupport([...new Set([scope.context.table, ...scope.operations.map(op => op.table)])]);
|
|
1907
|
+
else if (scope.transaction && scope.transaction.isolationLevel !== enums_1.IsolationLevel.SERIALIZABLE)
|
|
1908
|
+
throw new Error('SQL pipeline requires SERIALIZABLE isolation');
|
|
1909
|
+
}
|
|
1910
|
+
catch (_a) {
|
|
1911
|
+
throw new atomic_batch_1.AtomicBatchError('BATCH_PREFLIGHT_FAILED', 'not_started');
|
|
1912
|
+
}
|
|
1913
|
+
},
|
|
1914
|
+
begin: () => this.beginTransaction({ product: options.product, env: options.env, database: options.database, isolationLevel: enums_1.IsolationLevel.SERIALIZABLE }),
|
|
1915
|
+
read: async (transaction) => {
|
|
1916
|
+
if (!options.context.sql)
|
|
1917
|
+
return this.aggregatePipeline({ product: options.product, env: options.env, database: options.database,
|
|
1918
|
+
session: options.session, transaction, table: options.context.table, pipeline: options.context.pipeline, maxTimeMS: options.context.maxTimeMS });
|
|
1919
|
+
const adapter = this.getAdapter(options);
|
|
1920
|
+
await adapter.assertAtomicBatchSupport([...new Set([options.context.table, ...(options.context.sql.joins || []).map(join => join.table), ...options.operations.map(op => op.table)])], transaction);
|
|
1921
|
+
let context = options.context.sql;
|
|
1922
|
+
if (options.session) {
|
|
1923
|
+
const builder = await this.getProductBuilder(options.product);
|
|
1924
|
+
const key = builder.fetchPrivateKey();
|
|
1925
|
+
if (!key)
|
|
1926
|
+
throw new Error('Cannot verify pipeline session');
|
|
1927
|
+
const { processSessionForExecution } = await Promise.resolve().then(() => __importStar(require('../sessions')));
|
|
1928
|
+
const result = await processSessionForExecution(options.session, key, context, options.env, { fetchSessionSelector: async (tag) => { var _a; return (_a = (await builder.fetchSession(tag))) === null || _a === void 0 ? void 0 : _a.selector; } });
|
|
1929
|
+
if (result.error)
|
|
1930
|
+
throw new Error('Pipeline session validation failed');
|
|
1931
|
+
context = result.input;
|
|
1932
|
+
}
|
|
1933
|
+
const query = (0, sql_context_1.compileSqlContext)(options.context.table, context, adapter instanceof postgresql_adapter_1.PostgreSQLAdapter);
|
|
1934
|
+
return adapter.raw(Object.assign(Object.assign({}, query), { table: options.context.table, transaction }));
|
|
1935
|
+
},
|
|
1936
|
+
write: batch => this.atomicBatch(batch),
|
|
1937
|
+
});
|
|
1938
|
+
}
|
|
1939
|
+
async aggregatePipeline(options) {
|
|
1940
|
+
var _a, _b, _c;
|
|
1941
|
+
for (const key of ['product', 'env', 'database', 'table']) {
|
|
1942
|
+
if (typeof options[key] !== 'string' || !options[key].trim())
|
|
1943
|
+
throw new Error(`aggregatePipeline requires ${key}`);
|
|
1944
|
+
}
|
|
1945
|
+
(0, pipeline_interface_1.validateReadPipeline)(options.pipeline);
|
|
1946
|
+
await this.ensureAdapterFor(options);
|
|
1947
|
+
const adapter = this.getAdapter(options);
|
|
1948
|
+
if (!(adapter instanceof mongodb_adapter_1.MongoDBAdapter))
|
|
1949
|
+
throw new Error('aggregatePipeline requires MongoDB');
|
|
1950
|
+
const transaction = options.transaction;
|
|
1951
|
+
if (transaction && (!(transaction instanceof transaction_manager_1.Transaction) || !transaction.belongsTo(adapter) || !transaction.isActive()
|
|
1952
|
+
|| this.transactionScopes.get(transaction) !== JSON.stringify([options.product, options.env, options.database]))) {
|
|
1953
|
+
throw new Error('Pipeline transaction does not belong to this product/environment/database');
|
|
1954
|
+
}
|
|
1955
|
+
let resolved = options;
|
|
1956
|
+
let sessionLogFields = {};
|
|
1957
|
+
if (options.session) {
|
|
1958
|
+
const builder = await this.getProductBuilder(options.product);
|
|
1959
|
+
const privateKey = builder.fetchPrivateKey();
|
|
1960
|
+
if (!privateKey)
|
|
1961
|
+
throw new Error('Cannot verify pipeline session');
|
|
1962
|
+
const { processSessionForExecution } = await Promise.resolve().then(() => __importStar(require('../sessions')));
|
|
1963
|
+
// Never recursively transform the driver session or replace the original opaque token.
|
|
1964
|
+
const { transaction: _transaction } = options, input = __rest(options, ["transaction"]);
|
|
1965
|
+
const result = await processSessionForExecution(options.session, privateKey, input, options.env, {
|
|
1966
|
+
fetchSessionSelector: async (tag) => { var _a; return (_a = (await builder.fetchSession(tag))) === null || _a === void 0 ? void 0 : _a.selector; },
|
|
1967
|
+
});
|
|
1968
|
+
if (result.error)
|
|
1969
|
+
throw new Error('Pipeline session validation failed');
|
|
1970
|
+
resolved = Object.assign(Object.assign({}, options), { pipeline: result.input.pipeline, transaction, session: options.session });
|
|
1971
|
+
sessionLogFields = result.logFields;
|
|
1972
|
+
}
|
|
1973
|
+
this.initializeLogService();
|
|
1974
|
+
const start = Date.now();
|
|
1975
|
+
let successful = false;
|
|
1976
|
+
try {
|
|
1977
|
+
const result = await adapter.aggregatePipeline(resolved);
|
|
1978
|
+
successful = true;
|
|
1979
|
+
return result;
|
|
1980
|
+
}
|
|
1981
|
+
finally {
|
|
1982
|
+
(_a = this.logService) === null || _a === void 0 ? void 0 : _a.add(Object.assign(Object.assign({ product_tag: options.product, workspace_id: ((_b = this.config) === null || _b === void 0 ? void 0 : _b.workspace_id) || '', env: options.env, app_env: options.env, process_id: (0, processor_utils_1.generateObjectId)(), type: logs_types_1.LogEventTypes.DATABASE, parent_tag: options.database, child_tag: 'aggregatePipeline' }, sessionLogFields), { start, end: Date.now(), message: `Database pipeline - ${successful ? 'success' : 'failed'}`, status: successful ? logs_types_1.LogEventStatus.SUCCESS : logs_types_1.LogEventStatus.FAIL, successful_execution: successful, failed_execution: !successful, data: { table: options.table, stages: options.pipeline.length } }));
|
|
1983
|
+
void ((_c = this.logService) === null || _c === void 0 ? void 0 : _c.publish().catch(() => { }));
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1863
1986
|
async aggregate(options) {
|
|
1864
1987
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
1865
1988
|
const process_id = (0, processor_utils_1.generateObjectId)();
|
|
@@ -1973,7 +2096,109 @@ class DatabaseService {
|
|
|
1973
2096
|
async beginTransaction(options) {
|
|
1974
2097
|
await this.ensureAdapterFor(options);
|
|
1975
2098
|
const adapter = this.getAdapter(options);
|
|
1976
|
-
|
|
2099
|
+
const transaction = await this.transactionManager.beginTransaction(adapter, options);
|
|
2100
|
+
this.transactionScopes.set(transaction, JSON.stringify([options.product, options.env, options.database]));
|
|
2101
|
+
return transaction;
|
|
2102
|
+
}
|
|
2103
|
+
/** Bounded ACID batch. Unsupported adapters and configured triggers fail closed. */
|
|
2104
|
+
async atomicBatch(options) {
|
|
2105
|
+
var _a;
|
|
2106
|
+
const logs = [];
|
|
2107
|
+
const batch = { id: (0, processor_utils_1.generateObjectId)(), logs, system: !!options.system };
|
|
2108
|
+
let outcome = 'not_started';
|
|
2109
|
+
try {
|
|
2110
|
+
const result = await (0, context_1.runWithExecutionContext)((0, context_1.createExecutionContext)({ database_batch: batch }), () => (0, atomic_batch_1.executeAtomicBatch)({
|
|
2111
|
+
preflight: async (scope) => {
|
|
2112
|
+
var _a;
|
|
2113
|
+
await this.ensureAdapterFor(scope);
|
|
2114
|
+
if (((_a = this.currentContext) === null || _a === void 0 ? void 0 : _a.product) && this.currentContext.product !== scope.product) {
|
|
2115
|
+
throw new Error('Atomic batch product must match the active database context');
|
|
2116
|
+
}
|
|
2117
|
+
if (scope.session && !(await this.getProductBuilder(scope.product)).fetchPrivateKey()) {
|
|
2118
|
+
throw new Error('Atomic batch cannot verify the provided user session');
|
|
2119
|
+
}
|
|
2120
|
+
const adapter = this.getAdapter(scope);
|
|
2121
|
+
if (!(adapter instanceof mongodb_adapter_1.MongoDBAdapter || adapter instanceof postgresql_adapter_1.PostgreSQLAdapter || adapter instanceof mysql_adapter_1.MySQLAdapter || adapter instanceof dynamodb_adapter_1.DynamoDBAdapter)) {
|
|
2122
|
+
throw new Error('This adapter cannot provide the required multi-table ACID contract; Cassandra logged batches are not a substitute');
|
|
2123
|
+
}
|
|
2124
|
+
if (scope.transaction && (!(scope.transaction instanceof transaction_manager_1.Transaction) || !scope.transaction.belongsTo(adapter) || !scope.transaction.isActive()
|
|
2125
|
+
|| this.transactionScopes.get(scope.transaction) !== JSON.stringify([scope.product, scope.env, scope.database]))) {
|
|
2126
|
+
throw new Error('Atomic batch transaction does not belong to the active database connection');
|
|
2127
|
+
}
|
|
2128
|
+
if (scope.transaction && (adapter instanceof postgresql_adapter_1.PostgreSQLAdapter || adapter instanceof mysql_adapter_1.MySQLAdapter)
|
|
2129
|
+
&& scope.transaction.isolationLevel !== enums_1.IsolationLevel.SERIALIZABLE) {
|
|
2130
|
+
throw new Error('Borrowed SQL batch transactions must use SERIALIZABLE isolation');
|
|
2131
|
+
}
|
|
2132
|
+
const triggers = await this.fetchTriggers(scope.product, scope.database);
|
|
2133
|
+
if (triggers.length)
|
|
2134
|
+
throw new Error('Atomic batch does not yet support configured triggers; use a transaction-local outbox');
|
|
2135
|
+
if (adapter instanceof mongodb_adapter_1.MongoDBAdapter)
|
|
2136
|
+
await adapter.assertAtomicBatchSupport([...new Set(scope.operations.map(op => op.table))]);
|
|
2137
|
+
},
|
|
2138
|
+
executeNative: async (scope) => {
|
|
2139
|
+
var _a, _b, _c, _d;
|
|
2140
|
+
const adapter = this.getAdapter(scope);
|
|
2141
|
+
if (!(adapter instanceof dynamodb_adapter_1.DynamoDBAdapter))
|
|
2142
|
+
return undefined;
|
|
2143
|
+
let resolved = scope;
|
|
2144
|
+
let sessionLogFields = {};
|
|
2145
|
+
if (scope.session) {
|
|
2146
|
+
const builder = await this.getProductBuilder(scope.product);
|
|
2147
|
+
const { processSessionForExecution } = await Promise.resolve().then(() => __importStar(require('../sessions')));
|
|
2148
|
+
const result = await processSessionForExecution(scope.session, builder.fetchPrivateKey(), scope, scope.env, {
|
|
2149
|
+
fetchSessionSelector: async (tag) => { var _a; return (_a = (await builder.fetchSession(tag))) === null || _a === void 0 ? void 0 : _a.selector; },
|
|
2150
|
+
});
|
|
2151
|
+
if (result.error)
|
|
2152
|
+
throw new atomic_batch_1.AtomicBatchError('BATCH_VALIDATION', 'not_started');
|
|
2153
|
+
resolved = Object.assign(Object.assign({}, scope), { operations: result.input.operations });
|
|
2154
|
+
sessionLogFields = result.logFields;
|
|
2155
|
+
}
|
|
2156
|
+
this.initializeLogService();
|
|
2157
|
+
const start = Date.now();
|
|
2158
|
+
try {
|
|
2159
|
+
const result = await adapter.atomicBatch(resolved);
|
|
2160
|
+
(_a = this.logService) === null || _a === void 0 ? void 0 : _a.add(Object.assign({ product_tag: scope.product, workspace_id: ((_b = this.config) === null || _b === void 0 ? void 0 : _b.workspace_id) || '', env: scope.env, app_env: scope.env, process_id: result.transactionId, type: logs_types_1.LogEventTypes.DATABASE, parent_tag: scope.database, child_tag: 'atomicBatch', start, end: Date.now(), message: 'Database atomic batch - success', status: logs_types_1.LogEventStatus.SUCCESS, successful_execution: true, data: { operations: scope.operations.map(op => ({ name: op.name, operation: op.operation, table: op.table })) } }, sessionLogFields));
|
|
2161
|
+
return result;
|
|
2162
|
+
}
|
|
2163
|
+
catch (error) {
|
|
2164
|
+
(_c = this.logService) === null || _c === void 0 ? void 0 : _c.add(Object.assign({ product_tag: scope.product, workspace_id: ((_d = this.config) === null || _d === void 0 ? void 0 : _d.workspace_id) || '', env: scope.env, app_env: scope.env, process_id: batch.id, type: logs_types_1.LogEventTypes.DATABASE, parent_tag: scope.database, child_tag: 'atomicBatch', start, end: Date.now(), message: 'Database atomic batch - failed', status: logs_types_1.LogEventStatus.FAIL, failed_execution: true, data: { outcome: error instanceof atomic_batch_1.AtomicBatchError ? error.outcome : 'not_started' } }, sessionLogFields));
|
|
2165
|
+
throw error;
|
|
2166
|
+
}
|
|
2167
|
+
},
|
|
2168
|
+
validateTransaction: async (scope, transaction) => {
|
|
2169
|
+
const adapter = this.getAdapter(scope);
|
|
2170
|
+
if (adapter instanceof postgresql_adapter_1.PostgreSQLAdapter || adapter instanceof mysql_adapter_1.MySQLAdapter) {
|
|
2171
|
+
await adapter.assertAtomicBatchSupport([...new Set(scope.operations.map(op => op.table))], transaction);
|
|
2172
|
+
}
|
|
2173
|
+
},
|
|
2174
|
+
begin: (scope) => this.beginTransaction({ product: scope.product, env: scope.env, database: scope.database, isolationLevel: enums_1.IsolationLevel.SERIALIZABLE }),
|
|
2175
|
+
execute: async (op, scope, transaction) => {
|
|
2176
|
+
const input = { product: scope.product, env: scope.env, database: scope.database, session: scope.session,
|
|
2177
|
+
table: op.table, transaction, returning: op.returning };
|
|
2178
|
+
switch (op.operation) {
|
|
2179
|
+
case 'insert': return this.insert(Object.assign(Object.assign({}, input), { data: op.data }));
|
|
2180
|
+
case 'update': return this.update(Object.assign(Object.assign({}, input), { where: op.where, data: op.data }));
|
|
2181
|
+
case 'delete': return this.delete(Object.assign(Object.assign({}, input), { where: op.where }));
|
|
2182
|
+
case 'upsert': return this.upsert(Object.assign(Object.assign({}, input), { data: op.data, conflictKeys: op.conflictKeys }));
|
|
2183
|
+
}
|
|
2184
|
+
},
|
|
2185
|
+
}, options));
|
|
2186
|
+
outcome = result.status;
|
|
2187
|
+
return result;
|
|
2188
|
+
}
|
|
2189
|
+
catch (error) {
|
|
2190
|
+
outcome = error instanceof atomic_batch_1.AtomicBatchError ? error.outcome : 'not_started';
|
|
2191
|
+
throw error;
|
|
2192
|
+
}
|
|
2193
|
+
finally {
|
|
2194
|
+
for (const emit of logs) {
|
|
2195
|
+
try {
|
|
2196
|
+
emit(outcome);
|
|
2197
|
+
}
|
|
2198
|
+
catch ( /* Telemetry cannot change transaction outcome. */_b) { /* Telemetry cannot change transaction outcome. */ }
|
|
2199
|
+
}
|
|
2200
|
+
void ((_a = this.logService) === null || _a === void 0 ? void 0 : _a.publish().catch(() => { }));
|
|
2201
|
+
}
|
|
1977
2202
|
}
|
|
1978
2203
|
// ==================== SCHEMA OPERATIONS ====================
|
|
1979
2204
|
/**
|
|
@@ -2839,6 +3064,9 @@ class DatabaseService {
|
|
|
2839
3064
|
return await fn(adapter);
|
|
2840
3065
|
}
|
|
2841
3066
|
catch (error) {
|
|
3067
|
+
// An uncertain transactional write must never be replayed on a fresh connection.
|
|
3068
|
+
if (options.transaction)
|
|
3069
|
+
throw error;
|
|
2842
3070
|
if (!this.isConnectionError(error))
|
|
2843
3071
|
throw error;
|
|
2844
3072
|
await this.connect({
|
|
@@ -3467,6 +3695,9 @@ class DatabaseConnection {
|
|
|
3467
3695
|
aggregate(options) {
|
|
3468
3696
|
return this.service.aggregate(Object.assign(Object.assign({}, options), { database: this._database, env: this._env }));
|
|
3469
3697
|
}
|
|
3698
|
+
aggregatePipeline(options) {
|
|
3699
|
+
return this.service.aggregatePipeline(Object.assign(Object.assign({}, options), { product: this._product, database: this._database, env: this._env }));
|
|
3700
|
+
}
|
|
3470
3701
|
groupBy(options) {
|
|
3471
3702
|
return this.service.groupBy(Object.assign(Object.assign({}, options), { database: this._database, env: this._env }));
|
|
3472
3703
|
}
|