@@ -9,10 +9,12 @@ import (
99 "math"
1010 "math/big"
1111 "strconv"
12+ "sync/atomic"
1213 "testing"
1314 "time"
1415
1516 "github.com/ethereum/go-ethereum/common"
17+ "github.com/jmoiron/sqlx"
1618 pkgerrors "github.com/pkg/errors"
1719 "github.com/stretchr/testify/assert"
1820 "github.com/stretchr/testify/mock"
@@ -2463,3 +2465,47 @@ func TestSelectLatestFinalizedBlock(t *testing.T) {
24632465 require .Equal (t , common .HexToHash ("0x1231" ), result .BlockHash )
24642466 })
24652467}
2468+
2469+ // execCountingSQLxDB wraps *sqlx.DB as a [sqlutil.DataSource] and counts how many
2470+ // ExecContext calls hit the pool object directly. Executions on a [*sqlx.Tx]
2471+ // returned from BeginTxx do not increment this counter.
2472+ //
2473+ // This guards a regression where [logpoller.DSORM.DeleteLogsAndBlocksAfter]
2474+ // used the outer ORM's DataSource inside a [logpoller.DSORM.Transact] callback
2475+ // instead of the transactional ORM's DataSource, so the two DELETEs were not
2476+ // part of the same database transaction.
2477+ type execCountingSQLxDB struct {
2478+ * sqlx.DB
2479+
2480+ poolExecContext atomic.Int32
2481+ }
2482+
2483+ func (d * execCountingSQLxDB ) PoolExecContextCount () int32 {
2484+ return d .poolExecContext .Load ()
2485+ }
2486+
2487+ func (d * execCountingSQLxDB ) ExecContext (ctx context.Context , query string , args ... any ) (sql.Result , error ) {
2488+ d .poolExecContext .Add (1 )
2489+ return d .DB .ExecContext (ctx , query , args ... )
2490+ }
2491+
2492+ var _ sqlutil.DataSource = (* execCountingSQLxDB )(nil )
2493+
2494+ func TestDSORM_DeleteLogsAndBlocksAfter_usesTransactionalDataSource (t * testing.T ) {
2495+ t .Parallel ()
2496+ testutils .SkipShortDB (t )
2497+
2498+ ctx := testutils .Context (t )
2499+ base := testutils .NewSqlxDB (t )
2500+ require .NotNil (t , base )
2501+ wrapped := & execCountingSQLxDB {DB : base }
2502+
2503+ lggr := logger .Test (t )
2504+ orm := logpoller .NewORM (testutils .NewRandomEVMChainID (), wrapped , lggr )
2505+
2506+ require .NoError (t , orm .DeleteLogsAndBlocksAfter (ctx , 1 ))
2507+
2508+ require .Zero (t , wrapped .PoolExecContextCount (),
2509+ "DeleteLogsAndBlocksAfter must run DELETEs on the transaction DataSource (orm.ds), not the outer pool; " +
2510+ "otherwise the two deletes are not atomic and this counter would be 2" )
2511+ }
0 commit comments