Skip to content

Commit 9f43249

Browse files
committedJan 27, 2022
Prevent executing mutations twice
1 parent d3df35b commit 9f43249

File tree

4 files changed

+39
-5
lines changed

4 files changed

+39
-5
lines changed
 

‎src/interfaces-private.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -131,11 +131,17 @@ export interface _Transaction {
131131
fullCommit(): _Transaction;
132132
rollback(): _Transaction;
133133
delete(identity: symbol): void;
134+
/** Set data persisted in this transaction */
134135
set<T>(identity: symbol, data: T): T;
136+
/** Get data persisted in this transaction */
135137
get<T>(identity: symbol): T;
136138
getMap<T extends ImMap<any, any>>(identity: symbol): T;
137139
getSet<T>(identity: symbol): ImSet<T>;
138-
affectedRows: number;
140+
/** Get transient data, which will only exist within the scope of the current statement */
141+
setTransient<T>(identity: symbol, data: T): T;
142+
/** Set transient data, which will only exist within the scope of the current statement */
143+
getTransient<T>(identity: symbol): T;
144+
clearTransientData(): void;
139145
}
140146

141147
export interface Stats {

‎src/mutations/mutation-base.ts

+15-2
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,16 @@ import { buildValue } from '../expression-builder';
99

1010
type MutationStatement = InsertStatement | UpdateStatement | DeleteStatement;
1111

12+
1213
export abstract class MutationDataSourceBase<T> extends DataSourceBase<T> {
14+
public static readonly affectedRows = Symbol('affectedRows');
1315

1416
/** Perform the mutation, and returns the affected elements */
1517
protected abstract performMutation(t: _Transaction): T[];
1618

1719
private returningRows?: ArrayFilter;
1820
private returning?: _ISelection;
21+
private mutationResult = Symbol('mutationResult');
1922

2023
get columns() {
2124
return this.returning?.columns ?? [];
@@ -32,9 +35,19 @@ export abstract class MutationDataSourceBase<T> extends DataSourceBase<T> {
3235
}
3336

3437
*enumerate(t: _Transaction): Iterable<any> {
35-
const affected = this.performMutation(t);
38+
// check if this mutation has already been executed in the statement being executed
39+
// and get the result from cache to avoid re-excuting it
40+
// see unit test "can use delete result multiple times in select"
41+
let affected = t.getTransient<T[]>(this.mutationResult);
42+
if (!affected) {
43+
// execute mutation if nescessary
44+
affected = this.performMutation(t);
45+
t.setTransient(this.mutationResult, affected);
46+
}
47+
3648

37-
t.affectedRows = affected.length;
49+
// set the result count
50+
t.setTransient(MutationDataSourceBase.affectedRows, affected.length);
3851
if (!this.returning) {
3952
return;
4053
}

‎src/schema.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { OverloadResolver } from './overload-resolver';
2020
import { Deletion } from './mutations/deletion';
2121
import { Update } from './mutations/update';
2222
import { Insert } from './mutations/insert';
23+
import { MutationDataSourceBase } from './mutations/mutation-base';
2324

2425
export class DbSchema implements _ISchema, ISchema {
2526

@@ -137,6 +138,7 @@ export class DbSchema implements _ISchema, ISchema {
137138
const { checked: p, check } = this.db.options.noAstCoverageCheck
138139
? { checked: _p, check: null }
139140
: watchUse(_p);
141+
t.clearTransientData();
140142

141143
switch (p.type) {
142144
case 'start transaction':
@@ -536,7 +538,7 @@ but the resulting statement cannot be executed → Probably not a pg-mem error.`
536538
: cleanResults([...last.enumerate(t)]);
537539
return {
538540
rows,
539-
rowCount: typeof last === 'number' ? last : rows.length,
541+
rowCount: t.getTransient(MutationDataSourceBase.affectedRows) ?? rows.length,
540542
command: p.type.toUpperCase(),
541543
fields: [],
542544
location: this.locOf(p),

‎src/transaction.ts

+14-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { NotSupported, QueryError } from './interfaces';
44

55
export class Transaction implements _Transaction {
66
private origData: ImMap<symbol, any>;
7-
affectedRows: number = 0;
7+
private transientData: any = {};
88

99
static root() {
1010
return new Transaction(null, ImMap());
@@ -78,4 +78,17 @@ export class Transaction implements _Transaction {
7878
return this.parent ?? this;
7979
}
8080

81+
setTransient<T>(identity: symbol, data: T): T {
82+
this.transientData[identity] = data as any;
83+
return data;
84+
}
85+
86+
/** Set transient data, which will only exist within the scope of the current statement */
87+
getTransient<T>(identity: symbol): T {
88+
return this.transientData[identity] as T;
89+
}
90+
91+
clearTransientData(): void {
92+
this.transientData = {};
93+
}
8194
}

0 commit comments

Comments
 (0)
Please sign in to comment.