From 975e70429e468173d249d8cdd5f5610d98c44f2a Mon Sep 17 00:00:00 2001 From: Nicholas Molnar <65710+neekolas@users.noreply.github.com> Date: Wed, 26 Feb 2025 12:34:53 -0500 Subject: [PATCH] Add payers table (#535) ### TL;DR Added a new `payers` table and associated query functionality to track unique payer addresses. I decided to make this table normalized since we are going to reference it from a lot of places and a 42 byte address is a lot more to store than a 32 bit int. ### What changed? - Created a new `payers` table with `id` and `address` columns - Added `FindOrCreatePayer` query to handle upsert operations for payer addresses - Implemented `Payer` model struct with corresponding fields - Added migration files for creating and dropping the payers table ### How to test? 1. Run database migrations 2. Execute the test suite, specifically `TestFindOrCreatePayer` 3. Verify that: - New payer addresses are assigned unique IDs - Duplicate addresses return the existing ID - Different addresses receive different IDs ### Why make this change? To maintain a normalized database structure for payer addresses, ensuring each unique address is stored only once and can be referenced by ID throughout the system. This prevents duplicate storage of addresses and enables efficient querying of payer-related data. ## Summary by CodeRabbit - **New Features** - Introduced enhanced management of payer records that automatically creates new entries or updates existing ones, ensuring data integrity. - **Chores** - Added database migration scripts to establish the new payer records table and support rollback processes. - **Tests** - Included comprehensive tests verifying the reliability of the new payer record management functionality. --- pkg/db/queries.sql | 9 ++++++++ pkg/db/queries/models.go | 5 ++++ pkg/db/queries/queries.sql.go | 17 ++++++++++++++ pkg/db/queries_test.go | 23 +++++++++++++++++++ .../00005_add-payers-table.down.sql | 2 ++ pkg/migrations/00005_add-payers-table.up.sql | 5 ++++ 6 files changed, 61 insertions(+) create mode 100644 pkg/migrations/00005_add-payers-table.down.sql create mode 100644 pkg/migrations/00005_add-payers-table.up.sql diff --git a/pkg/db/queries.sql b/pkg/db/queries.sql index 7311d76e..dc06efe8 100644 --- a/pkg/db/queries.sql +++ b/pkg/db/queries.sql @@ -171,3 +171,12 @@ FROM ( WHERE bm.block_number = locked_rows.block_number; +-- name: FindOrCreatePayer :one +INSERT INTO payers(address) + VALUES (@address) +ON CONFLICT (address) + DO UPDATE SET + address = @address + RETURNING + id; + diff --git a/pkg/db/queries/models.go b/pkg/db/queries/models.go index ae4f9436..31a7cc22 100644 --- a/pkg/db/queries/models.go +++ b/pkg/db/queries/models.go @@ -44,6 +44,11 @@ type NodeInfo struct { SingletonID int16 } +type Payer struct { + ID int32 + Address string +} + type StagedOriginatorEnvelope struct { ID int64 OriginatorTime time.Time diff --git a/pkg/db/queries/queries.sql.go b/pkg/db/queries/queries.sql.go index 71f0fcca..428df05c 100644 --- a/pkg/db/queries/queries.sql.go +++ b/pkg/db/queries/queries.sql.go @@ -25,6 +25,23 @@ func (q *Queries) DeleteStagedOriginatorEnvelope(ctx context.Context, id int64) return result.RowsAffected() } +const findOrCreatePayer = `-- name: FindOrCreatePayer :one +INSERT INTO payers(address) + VALUES ($1) +ON CONFLICT (address) + DO UPDATE SET + address = $1 + RETURNING + id +` + +func (q *Queries) FindOrCreatePayer(ctx context.Context, address string) (int32, error) { + row := q.db.QueryRowContext(ctx, findOrCreatePayer, address) + var id int32 + err := row.Scan(&id) + return id, err +} + const getAddressLogs = `-- name: GetAddressLogs :many SELECT a.address, diff --git a/pkg/db/queries_test.go b/pkg/db/queries_test.go index 390a4f95..5948ac33 100644 --- a/pkg/db/queries_test.go +++ b/pkg/db/queries_test.go @@ -135,3 +135,26 @@ func TestRevokeAddressLog(t *testing.T) { require.NotNil(t, addressLog) require.Equal(t, addressLog.AssociationSequenceID.Int64, int64(3)) } + +func TestFindOrCreatePayer(t *testing.T) { + ctx := context.Background() + db, _, cleanup := testutils.NewDB(t, ctx) + defer cleanup() + + querier := queries.New(db) + + address1 := testutils.RandomString(42) + address2 := testutils.RandomString(42) + + id1, err := querier.FindOrCreatePayer(ctx, address1) + require.NoError(t, err) + + id2, err := querier.FindOrCreatePayer(ctx, address2) + require.NoError(t, err) + + require.NotEqual(t, id1, id2) + + reinsertId, err := querier.FindOrCreatePayer(ctx, address1) + require.NoError(t, err) + require.Equal(t, id1, reinsertId) +} diff --git a/pkg/migrations/00005_add-payers-table.down.sql b/pkg/migrations/00005_add-payers-table.down.sql new file mode 100644 index 00000000..e6594193 --- /dev/null +++ b/pkg/migrations/00005_add-payers-table.down.sql @@ -0,0 +1,2 @@ +DROP TABLE IF EXISTS payers; + diff --git a/pkg/migrations/00005_add-payers-table.up.sql b/pkg/migrations/00005_add-payers-table.up.sql new file mode 100644 index 00000000..ab2d0cc5 --- /dev/null +++ b/pkg/migrations/00005_add-payers-table.up.sql @@ -0,0 +1,5 @@ +CREATE TABLE payers( + id SERIAL PRIMARY KEY, + address TEXT NOT NULL UNIQUE +); +