Skip to content

Commit 6112d21

Browse files
committed
fix: isolate each injection in a savepoint
1 parent 22c7272 commit 6112d21

1 file changed

Lines changed: 31 additions & 6 deletions

File tree

internal/schema/inject.go

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@ func InjectStats(ctx context.Context, pool *pgxpool.Pool, a *AnnotatedSchema, pg
4747
qual := t.Qual()
4848

4949
if sz := a.SizingFor(qual); sz != nil {
50-
if err := injectRelationStats(ctx, tx, pgMajor, t.Schema, t.Name, sz.Relpages, sz.Reltuples); err != nil {
50+
if err := runSavepoint(ctx, tx, func(tx pgx.Tx) error {
51+
return injectRelationStats(ctx, tx, pgMajor, t.Schema, t.Name, sz.Relpages, sz.Reltuples)
52+
}); err != nil {
5153
result.warn("table %s.%s: %v", t.Schema, t.Name, err)
5254
} else {
5355
result.TablesUpdated++
@@ -59,7 +61,9 @@ func InjectStats(ctx context.Context, pool *pgxpool.Pool, a *AnnotatedSchema, pg
5961
if isz == nil {
6062
continue
6163
}
62-
if err := injectRelationStats(ctx, tx, pgMajor, t.Schema, idx.Name, isz.Relpages, isz.Reltuples); err != nil {
64+
if err := runSavepoint(ctx, tx, func(tx pgx.Tx) error {
65+
return injectRelationStats(ctx, tx, pgMajor, t.Schema, idx.Name, isz.Relpages, isz.Reltuples)
66+
}); err != nil {
6367
result.warn("index %s.%s: %v", t.Schema, idx.Name, err)
6468
} else {
6569
result.IndexesUpdated++
@@ -73,7 +77,9 @@ func InjectStats(ctx context.Context, pool *pgxpool.Pool, a *AnnotatedSchema, pg
7377

7478
if pgMajor >= 18 {
7579
for _, cs := range colsWithStats {
76-
if err := injectColumnStatsPG18(ctx, tx, pgMajor, t.Schema, t.Name, cs.col, cs.stats); err != nil {
80+
if err := runSavepoint(ctx, tx, func(tx pgx.Tx) error {
81+
return injectColumnStatsPG18(ctx, tx, pgMajor, t.Schema, t.Name, cs.col, cs.stats)
82+
}); err != nil {
7783
result.warn("column %s.%s.%s: %v", t.Schema, t.Name, cs.col.Name, err)
7884
} else {
7985
result.ColumnsUpdated++
@@ -84,8 +90,12 @@ func InjectStats(ctx context.Context, pool *pgxpool.Pool, a *AnnotatedSchema, pg
8490
for i, cs := range colsWithStats {
8591
names[i] = cs.col
8692
}
87-
meta, err := batchLookupColumnMeta(ctx, tx, t.Schema, t.Name, names)
88-
if err != nil {
93+
var meta map[string]columnMeta
94+
if err := runSavepoint(ctx, tx, func(tx pgx.Tx) error {
95+
var e error
96+
meta, e = batchLookupColumnMeta(ctx, tx, t.Schema, t.Name, names)
97+
return e
98+
}); err != nil {
8999
result.warn("column metadata lookup %s.%s: %v", t.Schema, t.Name, err)
90100
continue
91101
}
@@ -95,7 +105,9 @@ func InjectStats(ctx context.Context, pool *pgxpool.Pool, a *AnnotatedSchema, pg
95105
result.warn("column %s.%s.%s: not found in target database", t.Schema, t.Name, cs.col.Name)
96106
continue
97107
}
98-
if err := injectColumnStatsLegacy(ctx, tx, cm, cs.stats); err != nil {
108+
if err := runSavepoint(ctx, tx, func(tx pgx.Tx) error {
109+
return injectColumnStatsLegacy(ctx, tx, cm, cs.stats)
110+
}); err != nil {
99111
result.warn("column %s.%s.%s: %v", t.Schema, t.Name, cs.col.Name, err)
100112
} else {
101113
result.ColumnsUpdated++
@@ -117,6 +129,19 @@ func InjectStats(ctx context.Context, pool *pgxpool.Pool, a *AnnotatedSchema, pg
117129
return result, nil
118130
}
119131

132+
// savepoint per injection: one failed Exec else aborts the whole pgx tx (25P02).
133+
func runSavepoint(ctx context.Context, tx pgx.Tx, fn func(pgx.Tx) error) error {
134+
sp, err := tx.Begin(ctx)
135+
if err != nil {
136+
return err
137+
}
138+
if err := fn(sp); err != nil {
139+
_ = sp.Rollback(ctx)
140+
return err
141+
}
142+
return sp.Commit(ctx)
143+
}
144+
120145
type colWithStats struct {
121146
col Column
122147
stats *ColumnStats

0 commit comments

Comments
 (0)