pqb 0.40.6 → 0.40.8
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/index.d.ts +123 -2
- package/dist/index.js +27 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +27 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -5022,8 +5022,129 @@ declare class AfterCommitError extends OrchidOrmError {
|
|
|
5022
5022
|
}
|
|
5023
5023
|
declare const _afterCommitError: (result: unknown, hookResults: AfterCommitErrorResult[], catchAfterCommitError: ((error: AfterCommitError) => void) | undefined) => void;
|
|
5024
5024
|
declare class Transaction {
|
|
5025
|
-
|
|
5026
|
-
|
|
5025
|
+
/**
|
|
5026
|
+
* In Orchid ORM the method is `$transaction`, when using `pqb` on its own it is `transaction`.
|
|
5027
|
+
*
|
|
5028
|
+
* `COMMIT` happens automatically after the callback was successfully resolved, and `ROLLBACK` is done automatically if the callback fails.
|
|
5029
|
+
*
|
|
5030
|
+
* Let's consider the case of transferring money from one user to another:
|
|
5031
|
+
*
|
|
5032
|
+
* ```ts
|
|
5033
|
+
* export const transferMoney = async (
|
|
5034
|
+
* fromId: number,
|
|
5035
|
+
* toId: number,
|
|
5036
|
+
* amount: number,
|
|
5037
|
+
* ) => {
|
|
5038
|
+
* try {
|
|
5039
|
+
* // db.$transaction returns data that is returned from the callback
|
|
5040
|
+
* // result here is senderRemainder
|
|
5041
|
+
* const result = await db.$transaction(async () => {
|
|
5042
|
+
* const sender = await db.user.find(fromId);
|
|
5043
|
+
* const senderRemainder = sender.balance - amount;
|
|
5044
|
+
* if (senderRemainder < 0) {
|
|
5045
|
+
* throw new Error('Sender does not have enough money');
|
|
5046
|
+
* }
|
|
5047
|
+
*
|
|
5048
|
+
* await db.user.find(fromId).decrement({
|
|
5049
|
+
* balance: amount,
|
|
5050
|
+
* });
|
|
5051
|
+
* await db.user.find(toId).increment({
|
|
5052
|
+
* balance: amount,
|
|
5053
|
+
* });
|
|
5054
|
+
*
|
|
5055
|
+
* return senderRemainder;
|
|
5056
|
+
* });
|
|
5057
|
+
* } catch (error) {
|
|
5058
|
+
* // handle transaction error
|
|
5059
|
+
* }
|
|
5060
|
+
* };
|
|
5061
|
+
* ```
|
|
5062
|
+
*
|
|
5063
|
+
* It performs 3 queries in a single transaction: load sender record, decrement sender's balance, increment receiver's balance.
|
|
5064
|
+
*
|
|
5065
|
+
* If sender or receiver record doesn't exist, it will throw `NotFound` error, and there is an error thrown when sender's balance is too low.
|
|
5066
|
+
* In such case, the transaction will be rolled back and no changes will be applied to the database.
|
|
5067
|
+
*
|
|
5068
|
+
* Internally, ORM relies on [AsyncLocalStorage](https://nodejs.org/api/async_context.html#class-asynclocalstorage) feature of node.js,
|
|
5069
|
+
* it allows passing the transaction object implicitly. So that any query that is done inside of callback, will run inside a transaction.
|
|
5070
|
+
*
|
|
5071
|
+
* ## nested transactions
|
|
5072
|
+
*
|
|
5073
|
+
* Transactions can be nested one in another.
|
|
5074
|
+
* The top level transaction is the real one,
|
|
5075
|
+
* and the nested ones are emulated with [savepoint](https://www.postgresql.org/docs/current/sql-savepoint.html) instead of `BEGIN`
|
|
5076
|
+
* and [release savepoint](https://www.postgresql.org/docs/current/sql-release-savepoint.html) instead of `COMMIT`.
|
|
5077
|
+
*
|
|
5078
|
+
* Use [ensureTransaction](#ensuretransaction) to run all queries in a single transaction.
|
|
5079
|
+
*
|
|
5080
|
+
* ```ts
|
|
5081
|
+
* const result = await db.$transaction(async () => {
|
|
5082
|
+
* await db.table.create(...one);
|
|
5083
|
+
*
|
|
5084
|
+
* const result = await db.$transaction(async () => {
|
|
5085
|
+
* await db.table.create(...two);
|
|
5086
|
+
* return 123;
|
|
5087
|
+
* });
|
|
5088
|
+
*
|
|
5089
|
+
* await db.table.create(...three);
|
|
5090
|
+
*
|
|
5091
|
+
* return result;
|
|
5092
|
+
* });
|
|
5093
|
+
*
|
|
5094
|
+
* // result is returned from the inner transaction
|
|
5095
|
+
* result === 123;
|
|
5096
|
+
* ```
|
|
5097
|
+
*
|
|
5098
|
+
* If the inner transaction throws an error, and it is caught by `try/catch` of outer transaction,
|
|
5099
|
+
* it performs [rollback to savepoint](https://www.postgresql.org/docs/current/sql-rollback-to.html)
|
|
5100
|
+
* and the outer transaction can continue:
|
|
5101
|
+
*
|
|
5102
|
+
* ```ts
|
|
5103
|
+
* class CustomError extends Error {}
|
|
5104
|
+
*
|
|
5105
|
+
* await db.$transaction(async () => {
|
|
5106
|
+
* try {
|
|
5107
|
+
* await db.$transaction(async () => {
|
|
5108
|
+
* throw new CustomError();
|
|
5109
|
+
* });
|
|
5110
|
+
* } catch (err) {
|
|
5111
|
+
* if (err instanceof CustomError) {
|
|
5112
|
+
* // ignore this error
|
|
5113
|
+
* return;
|
|
5114
|
+
* }
|
|
5115
|
+
* throw err;
|
|
5116
|
+
* }
|
|
5117
|
+
*
|
|
5118
|
+
* // this transaction can continue
|
|
5119
|
+
* await db.table.create(...data);
|
|
5120
|
+
* });
|
|
5121
|
+
* ```
|
|
5122
|
+
*
|
|
5123
|
+
* If the error in the inner transaction is not caught, all nested transactions are rolled back and aborted.
|
|
5124
|
+
*/
|
|
5125
|
+
transaction<Result>(this: PickQueryQAndInternal, cb: () => Promise<Result>): Promise<Result>;
|
|
5126
|
+
transaction<Result>(this: PickQueryQAndInternal, options: IsolationLevel | TransactionOptions, cb: () => Promise<Result>): Promise<Result>;
|
|
5127
|
+
/**
|
|
5128
|
+
* Use the `$ensureTransaction` when you want to ensure the sequence of queries is running in a transaction, but there is no need for Postgres [savepoints](https://www.postgresql.org/docs/current/sql-savepoint.html).
|
|
5129
|
+
*
|
|
5130
|
+
* ```ts
|
|
5131
|
+
* async function updateUserBalance(userId: string, amount: number) {
|
|
5132
|
+
* await db.$ensureTransaction(async () => {
|
|
5133
|
+
* await db.transfer.create({ userId, amount })
|
|
5134
|
+
* await db.user.find(userId).increment({ balance: amount })
|
|
5135
|
+
* })
|
|
5136
|
+
* }
|
|
5137
|
+
*
|
|
5138
|
+
* async function saveDeposit(userId: string, deposit: { ... }) {
|
|
5139
|
+
* await db.$ensureTransaction(async () => {
|
|
5140
|
+
* await db.deposit.create(deposit)
|
|
5141
|
+
* // transaction in updateUserBalance won't be started
|
|
5142
|
+
* await updateUserBalance(userId, deposit.amount)
|
|
5143
|
+
* })
|
|
5144
|
+
* }
|
|
5145
|
+
* ```
|
|
5146
|
+
*/
|
|
5147
|
+
ensureTransaction<Result>(this: PickQueryQAndInternal, cb: () => Promise<Result>): Promise<Result>;
|
|
5027
5148
|
}
|
|
5028
5149
|
|
|
5029
5150
|
type AfterHook<Select extends PropertyKey[], Shape extends QueryColumns> = QueryAfterHook<{
|
package/dist/index.js
CHANGED
|
@@ -3195,7 +3195,7 @@ const escape = (value, migration, nested) => {
|
|
|
3195
3195
|
else if (value instanceof Date)
|
|
3196
3196
|
return `'${value.toISOString()}'`;
|
|
3197
3197
|
else if (Array.isArray(value))
|
|
3198
|
-
return migration && nested && !value.length ? "" : (migration ? "{" : nested ? "[" : "ARRAY[") + value.map((el) => escape(el, migration, true)).join(",") + (migration ? "}" : "]");
|
|
3198
|
+
return migration && nested && !value.length ? "" : (migration ? nested ? "{" : "'{" : nested ? "[" : "ARRAY[") + value.map((el) => escape(el, migration, true)).join(",") + (migration ? nested ? "}" : "}'" : "]");
|
|
3199
3199
|
else if (value === null || value === void 0)
|
|
3200
3200
|
return "NULL";
|
|
3201
3201
|
else
|
|
@@ -3397,6 +3397,32 @@ class Transaction {
|
|
|
3397
3397
|
}
|
|
3398
3398
|
}
|
|
3399
3399
|
}
|
|
3400
|
+
/**
|
|
3401
|
+
* Use the `$ensureTransaction` when you want to ensure the sequence of queries is running in a transaction, but there is no need for Postgres [savepoints](https://www.postgresql.org/docs/current/sql-savepoint.html).
|
|
3402
|
+
*
|
|
3403
|
+
* ```ts
|
|
3404
|
+
* async function updateUserBalance(userId: string, amount: number) {
|
|
3405
|
+
* await db.$ensureTransaction(async () => {
|
|
3406
|
+
* await db.transfer.create({ userId, amount })
|
|
3407
|
+
* await db.user.find(userId).increment({ balance: amount })
|
|
3408
|
+
* })
|
|
3409
|
+
* }
|
|
3410
|
+
*
|
|
3411
|
+
* async function saveDeposit(userId: string, deposit: { ... }) {
|
|
3412
|
+
* await db.$ensureTransaction(async () => {
|
|
3413
|
+
* await db.deposit.create(deposit)
|
|
3414
|
+
* // transaction in updateUserBalance won't be started
|
|
3415
|
+
* await updateUserBalance(userId, deposit.amount)
|
|
3416
|
+
* })
|
|
3417
|
+
* }
|
|
3418
|
+
* ```
|
|
3419
|
+
*/
|
|
3420
|
+
ensureTransaction(cb) {
|
|
3421
|
+
const trx = this.internal.transactionStorage.getStore();
|
|
3422
|
+
if (trx)
|
|
3423
|
+
return cb();
|
|
3424
|
+
return Transaction.prototype.transaction.call(this, cb);
|
|
3425
|
+
}
|
|
3400
3426
|
}
|
|
3401
3427
|
const runAfterCommit = async (afterCommit, result) => {
|
|
3402
3428
|
if (afterCommit) {
|