slonik
Advanced tools
Comparing version 40.2.6 to 41.0.0
@@ -6,2 +6,3 @@ "use strict"; | ||
const constants_1 = require("../constants"); | ||
const transactionContext_1 = require("../contexts/transactionContext"); | ||
const errors_1 = require("../errors"); | ||
@@ -64,32 +65,37 @@ const state_1 = require("../state"); | ||
const transaction = async (parentLog, connection, clientConfiguration, handler, transactionRetryLimit) => { | ||
const poolClientState = (0, state_1.getPoolClientState)(connection); | ||
if (poolClientState.transactionDepth !== null) { | ||
throw new errors_1.UnexpectedStateError('Cannot use the same connection to start a new transaction before completing the last transaction.'); | ||
} | ||
poolClientState.transactionDepth = 0; | ||
poolClientState.transactionId = (0, createUid_1.createUid)(); | ||
const log = parentLog.child({ | ||
transactionId: poolClientState.transactionId, | ||
}); | ||
try { | ||
return await execTransaction(log, connection, clientConfiguration, handler); | ||
} | ||
catch (error) { | ||
const transactionRetryLimitToUse = transactionRetryLimit ?? clientConfiguration.transactionRetryLimit; | ||
const shouldRetry = typeof error.code === 'string' && | ||
error.code.startsWith(constants_1.TRANSACTION_ROLLBACK_ERROR_PREFIX) && | ||
transactionRetryLimitToUse > 0; | ||
if (shouldRetry) { | ||
return await retryTransaction(log, connection, clientConfiguration, handler, transactionRetryLimit); | ||
const transactionId = (0, createUid_1.createUid)(); | ||
return transactionContext_1.transactionContext.run({ | ||
transactionId, | ||
}, async () => { | ||
const poolClientState = (0, state_1.getPoolClientState)(connection); | ||
if (poolClientState.transactionDepth !== null) { | ||
throw new errors_1.UnexpectedStateError('Cannot use the same connection to start a new transaction before completing the last transaction.'); | ||
} | ||
else { | ||
throw error; | ||
poolClientState.transactionDepth = 0; | ||
poolClientState.transactionId = transactionId; | ||
const log = parentLog.child({ | ||
transactionId: poolClientState.transactionId, | ||
}); | ||
try { | ||
return await execTransaction(log, connection, clientConfiguration, handler); | ||
} | ||
} | ||
finally { | ||
poolClientState.transactionDepth = null; | ||
poolClientState.transactionId = null; | ||
} | ||
catch (error) { | ||
const transactionRetryLimitToUse = transactionRetryLimit ?? clientConfiguration.transactionRetryLimit; | ||
const shouldRetry = typeof error.code === 'string' && | ||
error.code.startsWith(constants_1.TRANSACTION_ROLLBACK_ERROR_PREFIX) && | ||
transactionRetryLimitToUse > 0; | ||
if (shouldRetry) { | ||
return await retryTransaction(log, connection, clientConfiguration, handler, transactionRetryLimit); | ||
} | ||
else { | ||
throw error; | ||
} | ||
} | ||
finally { | ||
poolClientState.transactionDepth = null; | ||
poolClientState.transactionId = null; | ||
} | ||
}); | ||
}; | ||
exports.transaction = transaction; | ||
//# sourceMappingURL=transaction.js.map |
@@ -20,2 +20,5 @@ import { type PrimitiveValueExpression, type Query, type QueryResultRow } from './types'; | ||
} | ||
export declare class UnexpectedForeignConnectionError extends SlonikError { | ||
constructor(); | ||
} | ||
export declare class ConnectionError extends SlonikError { | ||
@@ -22,0 +25,0 @@ } |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.CheckIntegrityConstraintViolationError = exports.UniqueIntegrityConstraintViolationError = exports.ForeignKeyIntegrityConstraintViolationError = exports.NotNullIntegrityConstraintViolationError = exports.IntegrityConstraintViolationError = exports.SchemaValidationError = exports.DataIntegrityError = exports.NotFoundError = exports.TupleMovedToAnotherPartitionError = exports.BackendTerminatedError = exports.BackendTerminatedUnexpectedlyError = exports.IdleTransactionTimeoutError = exports.StatementTimeoutError = exports.StatementCancelledError = exports.ConnectionError = exports.UnexpectedStateError = exports.InputSyntaxError = exports.InvalidInputError = exports.InvalidConfigurationError = exports.SlonikError = void 0; | ||
exports.CheckIntegrityConstraintViolationError = exports.UniqueIntegrityConstraintViolationError = exports.ForeignKeyIntegrityConstraintViolationError = exports.NotNullIntegrityConstraintViolationError = exports.IntegrityConstraintViolationError = exports.SchemaValidationError = exports.DataIntegrityError = exports.NotFoundError = exports.TupleMovedToAnotherPartitionError = exports.BackendTerminatedError = exports.BackendTerminatedUnexpectedlyError = exports.IdleTransactionTimeoutError = exports.StatementTimeoutError = exports.StatementCancelledError = exports.ConnectionError = exports.UnexpectedForeignConnectionError = exports.UnexpectedStateError = exports.InputSyntaxError = exports.InvalidInputError = exports.InvalidConfigurationError = exports.SlonikError = void 0; | ||
class SlonikError extends Error { | ||
@@ -30,2 +30,8 @@ constructor(message, options) { | ||
exports.UnexpectedStateError = UnexpectedStateError; | ||
class UnexpectedForeignConnectionError extends SlonikError { | ||
constructor() { | ||
super('Cannot run a query inside a transaction using a foreign connection.'); | ||
} | ||
} | ||
exports.UnexpectedForeignConnectionError = UnexpectedForeignConnectionError; | ||
class ConnectionError extends SlonikError { | ||
@@ -32,0 +38,0 @@ } |
@@ -33,2 +33,11 @@ "use strict"; | ||
const createIntegrationTests = (test, driverFactory) => { | ||
test('does not allow to reference a non-transaction connection inside of a transaction', async (t) => { | ||
const pool = await (0, __1.createPool)(t.context.dsn, { | ||
driverFactory, | ||
}); | ||
const error = await t.throwsAsync(pool.transaction(async () => { | ||
await pool.query(__1.sql.unsafe `SELECT 1`); | ||
})); | ||
t.true(error instanceof __1.UnexpectedForeignConnectionError); | ||
}); | ||
test('streams data', async (t) => { | ||
@@ -1377,5 +1386,5 @@ const pool = await (0, __1.createPool)(t.context.dsn, { | ||
}); | ||
t.is(await pool.transaction(async () => { | ||
t.is(await pool.transaction(async (transaction) => { | ||
await (0, promises_1.setTimeout)(200); | ||
return await pool.oneFirst(__1.sql.unsafe ` | ||
return await transaction.oneFirst(__1.sql.unsafe ` | ||
SELECT 1; | ||
@@ -1382,0 +1391,0 @@ `); |
@@ -35,3 +35,3 @@ /// <reference types="node" /> | ||
}; | ||
export { BackendTerminatedError, BackendTerminatedUnexpectedlyError, CheckIntegrityConstraintViolationError, ConnectionError, DataIntegrityError, ForeignKeyIntegrityConstraintViolationError, IdleTransactionTimeoutError, InputSyntaxError, IntegrityConstraintViolationError, InvalidConfigurationError, InvalidInputError, NotFoundError, NotNullIntegrityConstraintViolationError, SchemaValidationError, SlonikError, StatementCancelledError, StatementTimeoutError, TupleMovedToAnotherPartitionError, UnexpectedStateError, UniqueIntegrityConstraintViolationError, } from './errors'; | ||
export { BackendTerminatedError, BackendTerminatedUnexpectedlyError, CheckIntegrityConstraintViolationError, ConnectionError, DataIntegrityError, ForeignKeyIntegrityConstraintViolationError, IdleTransactionTimeoutError, InputSyntaxError, IntegrityConstraintViolationError, InvalidConfigurationError, InvalidInputError, NotFoundError, NotNullIntegrityConstraintViolationError, SchemaValidationError, SlonikError, StatementCancelledError, StatementTimeoutError, TupleMovedToAnotherPartitionError, UnexpectedForeignConnectionError, UnexpectedStateError, UniqueIntegrityConstraintViolationError, } from './errors'; | ||
export { createPool } from './factories/createPool'; | ||
@@ -38,0 +38,0 @@ export { createSqlTag } from './factories/createSqlTag'; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.stringifyDsn = exports.parseDsn = exports.isSqlToken = exports.createTimestampWithTimeZoneTypeParser = exports.createTimestampTypeParser = exports.createNumericTypeParser = exports.createIntervalTypeParser = exports.createDateTypeParser = exports.createBigintTypeParser = exports.createTypeParserPreset = exports.createSqlTokenSqlFragment = exports.createSqlTag = exports.createPool = exports.UniqueIntegrityConstraintViolationError = exports.UnexpectedStateError = exports.TupleMovedToAnotherPartitionError = exports.StatementTimeoutError = exports.StatementCancelledError = exports.SlonikError = exports.SchemaValidationError = exports.NotNullIntegrityConstraintViolationError = exports.NotFoundError = exports.InvalidInputError = exports.InvalidConfigurationError = exports.IntegrityConstraintViolationError = exports.InputSyntaxError = exports.IdleTransactionTimeoutError = exports.ForeignKeyIntegrityConstraintViolationError = exports.DataIntegrityError = exports.ConnectionError = exports.CheckIntegrityConstraintViolationError = exports.BackendTerminatedUnexpectedlyError = exports.BackendTerminatedError = exports.sql = exports.createDriverFactory = void 0; | ||
exports.stringifyDsn = exports.parseDsn = exports.isSqlToken = exports.createTimestampWithTimeZoneTypeParser = exports.createTimestampTypeParser = exports.createNumericTypeParser = exports.createIntervalTypeParser = exports.createDateTypeParser = exports.createBigintTypeParser = exports.createTypeParserPreset = exports.createSqlTokenSqlFragment = exports.createSqlTag = exports.createPool = exports.UniqueIntegrityConstraintViolationError = exports.UnexpectedStateError = exports.UnexpectedForeignConnectionError = exports.TupleMovedToAnotherPartitionError = exports.StatementTimeoutError = exports.StatementCancelledError = exports.SlonikError = exports.SchemaValidationError = exports.NotNullIntegrityConstraintViolationError = exports.NotFoundError = exports.InvalidInputError = exports.InvalidConfigurationError = exports.IntegrityConstraintViolationError = exports.InputSyntaxError = exports.IdleTransactionTimeoutError = exports.ForeignKeyIntegrityConstraintViolationError = exports.DataIntegrityError = exports.ConnectionError = exports.CheckIntegrityConstraintViolationError = exports.BackendTerminatedUnexpectedlyError = exports.BackendTerminatedError = exports.sql = exports.createDriverFactory = void 0; | ||
const createSqlTag_1 = require("./factories/createSqlTag"); | ||
@@ -27,2 +27,3 @@ var createDriverFactory_1 = require("./factories/createDriverFactory"); | ||
Object.defineProperty(exports, "TupleMovedToAnotherPartitionError", { enumerable: true, get: function () { return errors_1.TupleMovedToAnotherPartitionError; } }); | ||
Object.defineProperty(exports, "UnexpectedForeignConnectionError", { enumerable: true, get: function () { return errors_1.UnexpectedForeignConnectionError; } }); | ||
Object.defineProperty(exports, "UnexpectedStateError", { enumerable: true, get: function () { return errors_1.UnexpectedStateError; } }); | ||
@@ -29,0 +30,0 @@ Object.defineProperty(exports, "UniqueIntegrityConstraintViolationError", { enumerable: true, get: function () { return errors_1.UniqueIntegrityConstraintViolationError; } }); |
@@ -5,2 +5,3 @@ "use strict"; | ||
const constants_1 = require("../constants"); | ||
const transactionContext_1 = require("../contexts/transactionContext"); | ||
const errors_1 = require("../errors"); | ||
@@ -60,2 +61,7 @@ const state_1 = require("../state"); | ||
} | ||
const transactionStore = transactionContext_1.transactionContext.getStore(); | ||
if (transactionStore?.transactionId && | ||
transactionStore.transactionId !== poolClientState.transactionId) { | ||
throw new errors_1.UnexpectedForeignConnectionError(); | ||
} | ||
const queryInputTime = process.hrtime.bigint(); | ||
@@ -62,0 +68,0 @@ let stackTrace = null; |
@@ -95,3 +95,3 @@ { | ||
"types": "./dist/index.d.ts", | ||
"version": "40.2.6" | ||
"version": "41.0.0" | ||
} |
import { bindTransactionConnection } from '../binders/bindTransactionConnection'; | ||
import { TRANSACTION_ROLLBACK_ERROR_PREFIX } from '../constants'; | ||
import { transactionContext } from '../contexts/transactionContext'; | ||
import { BackendTerminatedError, UnexpectedStateError } from '../errors'; | ||
@@ -119,43 +120,57 @@ import { getPoolClientState } from '../state'; | ||
) => { | ||
const poolClientState = getPoolClientState(connection); | ||
const transactionId = createUid(); | ||
if (poolClientState.transactionDepth !== null) { | ||
throw new UnexpectedStateError( | ||
'Cannot use the same connection to start a new transaction before completing the last transaction.', | ||
); | ||
} | ||
return transactionContext.run( | ||
{ | ||
transactionId, | ||
}, | ||
async () => { | ||
const poolClientState = getPoolClientState(connection); | ||
poolClientState.transactionDepth = 0; | ||
poolClientState.transactionId = createUid(); | ||
if (poolClientState.transactionDepth !== null) { | ||
throw new UnexpectedStateError( | ||
'Cannot use the same connection to start a new transaction before completing the last transaction.', | ||
); | ||
} | ||
const log = parentLog.child({ | ||
transactionId: poolClientState.transactionId, | ||
}); | ||
poolClientState.transactionDepth = 0; | ||
poolClientState.transactionId = transactionId; | ||
try { | ||
return await execTransaction(log, connection, clientConfiguration, handler); | ||
} catch (error) { | ||
const transactionRetryLimitToUse = | ||
transactionRetryLimit ?? clientConfiguration.transactionRetryLimit; | ||
const log = parentLog.child({ | ||
transactionId: poolClientState.transactionId, | ||
}); | ||
const shouldRetry = | ||
typeof error.code === 'string' && | ||
error.code.startsWith(TRANSACTION_ROLLBACK_ERROR_PREFIX) && | ||
transactionRetryLimitToUse > 0; | ||
try { | ||
return await execTransaction( | ||
log, | ||
connection, | ||
clientConfiguration, | ||
handler, | ||
); | ||
} catch (error) { | ||
const transactionRetryLimitToUse = | ||
transactionRetryLimit ?? clientConfiguration.transactionRetryLimit; | ||
if (shouldRetry) { | ||
return await retryTransaction( | ||
log, | ||
connection, | ||
clientConfiguration, | ||
handler, | ||
transactionRetryLimit, | ||
); | ||
} else { | ||
throw error; | ||
} | ||
} finally { | ||
poolClientState.transactionDepth = null; | ||
poolClientState.transactionId = null; | ||
} | ||
const shouldRetry = | ||
typeof error.code === 'string' && | ||
error.code.startsWith(TRANSACTION_ROLLBACK_ERROR_PREFIX) && | ||
transactionRetryLimitToUse > 0; | ||
if (shouldRetry) { | ||
return await retryTransaction( | ||
log, | ||
connection, | ||
clientConfiguration, | ||
handler, | ||
transactionRetryLimit, | ||
); | ||
} else { | ||
throw error; | ||
} | ||
} finally { | ||
poolClientState.transactionDepth = null; | ||
poolClientState.transactionId = null; | ||
} | ||
}, | ||
); | ||
}; |
@@ -39,2 +39,10 @@ import { | ||
export class UnexpectedForeignConnectionError extends SlonikError { | ||
public constructor() { | ||
super( | ||
'Cannot run a query inside a transaction using a foreign connection.', | ||
); | ||
} | ||
} | ||
export class ConnectionError extends SlonikError {} | ||
@@ -41,0 +49,0 @@ |
@@ -19,2 +19,3 @@ /* eslint-disable id-length */ | ||
TupleMovedToAnotherPartitionError, | ||
UnexpectedForeignConnectionError, | ||
UnexpectedStateError, | ||
@@ -35,2 +36,16 @@ UniqueIntegrityConstraintViolationError, | ||
) => { | ||
test('does not allow to reference a non-transaction connection inside of a transaction', async (t) => { | ||
const pool = await createPool(t.context.dsn, { | ||
driverFactory, | ||
}); | ||
const error = await t.throwsAsync( | ||
pool.transaction(async () => { | ||
await pool.query(sql.unsafe`SELECT 1`); | ||
}), | ||
); | ||
t.true(error instanceof UnexpectedForeignConnectionError); | ||
}); | ||
test('streams data', async (t) => { | ||
@@ -1826,6 +1841,6 @@ const pool = await createPool(t.context.dsn, { | ||
t.is( | ||
await pool.transaction(async () => { | ||
await pool.transaction(async (transaction) => { | ||
await delay(200); | ||
return await pool.oneFirst(sql.unsafe` | ||
return await transaction.oneFirst(sql.unsafe` | ||
SELECT 1; | ||
@@ -1832,0 +1847,0 @@ `); |
@@ -39,2 +39,3 @@ import { createSqlTag } from './factories/createSqlTag'; | ||
TupleMovedToAnotherPartitionError, | ||
UnexpectedForeignConnectionError, | ||
UnexpectedStateError, | ||
@@ -41,0 +42,0 @@ UniqueIntegrityConstraintViolationError, |
import { TRANSACTION_ROLLBACK_ERROR_PREFIX } from '../constants'; | ||
import { transactionContext } from '../contexts/transactionContext'; | ||
import { | ||
@@ -8,2 +9,3 @@ BackendTerminatedError, | ||
TupleMovedToAnotherPartitionError, | ||
UnexpectedForeignConnectionError, | ||
UnexpectedStateError, | ||
@@ -142,2 +144,11 @@ } from '../errors'; | ||
const transactionStore = transactionContext.getStore(); | ||
if ( | ||
transactionStore?.transactionId && | ||
transactionStore.transactionId !== poolClientState.transactionId | ||
) { | ||
throw new UnexpectedForeignConnectionError(); | ||
} | ||
const queryInputTime = process.hrtime.bigint(); | ||
@@ -144,0 +155,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
1053753
604
18803
2806