@@ -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+
120145type colWithStats struct {
121146 col Column
122147 stats * ColumnStats
0 commit comments