fix(shop): resolve ambiguous column and missing unique constraint in RecordPurchase

The ON CONFLICT upsert referenced unqualified "bought" which PostgreSQL
rejected as ambiguous, and the table lacked the UNIQUE constraint needed
for ON CONFLICT. Adds a unique index on (character_id, shop_item_id) via
migration 0003 and qualifies the column as shop_items_bought.bought.
This commit is contained in:
Houmgaor
2026-02-24 17:06:38 +01:00
parent f9d4252860
commit d9f90e3b46
4 changed files with 34 additions and 9 deletions

View File

@@ -28,8 +28,7 @@ func (r *ShopRepository) GetShopItems(shopType uint8, shopID uint32, charID uint
func (r *ShopRepository) RecordPurchase(charID, shopItemID, quantity uint32) error {
_, err := r.db.Exec(`INSERT INTO shop_items_bought (character_id, shop_item_id, bought)
VALUES ($1,$2,$3) ON CONFLICT (character_id, shop_item_id)
DO UPDATE SET bought = bought + $3
WHERE EXCLUDED.character_id=$1 AND EXCLUDED.shop_item_id=$2
DO UPDATE SET bought = shop_items_bought.bought + $3
`, charID, shopItemID, quantity)
return err
}

View File

@@ -57,14 +57,32 @@ func TestRepoShopGetShopItems(t *testing.T) {
}
}
func TestRepoShopRecordPurchaseAmbiguousColumn(t *testing.T) {
repo, _, charID := setupShopRepo(t)
func TestRepoShopRecordPurchaseInsertAndUpdate(t *testing.T) {
repo, db, charID := setupShopRepo(t)
// RecordPurchase uses ON CONFLICT with unqualified "bought" column reference,
// which PostgreSQL rejects as ambiguous. This test documents the existing bug.
err := repo.RecordPurchase(charID, 1, 3)
if err == nil {
t.Fatal("Expected error from ambiguous column reference in RecordPurchase SQL, but got nil")
// First purchase inserts a new row
if err := repo.RecordPurchase(charID, 1, 3); err != nil {
t.Fatalf("RecordPurchase (insert) failed: %v", err)
}
var bought int
if err := db.QueryRow("SELECT bought FROM shop_items_bought WHERE character_id=$1 AND shop_item_id=$2", charID, 1).Scan(&bought); err != nil {
t.Fatalf("Verification query failed: %v", err)
}
if bought != 3 {
t.Errorf("Expected bought=3, got: %d", bought)
}
// Second purchase updates (adds to) the existing row
if err := repo.RecordPurchase(charID, 1, 2); err != nil {
t.Fatalf("RecordPurchase (update) failed: %v", err)
}
if err := db.QueryRow("SELECT bought FROM shop_items_bought WHERE character_id=$1 AND shop_item_id=$2", charID, 1).Scan(&bought); err != nil {
t.Fatalf("Verification query failed: %v", err)
}
if bought != 5 {
t.Errorf("Expected bought=5 (3+2), got: %d", bought)
}
}

View File

@@ -1220,6 +1220,9 @@ CREATE TABLE public.shop_items_bought (
bought integer
);
CREATE UNIQUE INDEX IF NOT EXISTS shop_items_bought_character_item_unique
ON public.shop_items_bought (character_id, shop_item_id);
--
-- Name: shop_items_id_seq; Type: SEQUENCE; Schema: public; Owner: -

View File

@@ -0,0 +1,5 @@
-- Add unique constraint required for ON CONFLICT upsert in RecordPurchase.
-- Uses CREATE UNIQUE INDEX which supports IF NOT EXISTS, avoiding errors
-- when the baseline schema (0001) already includes the constraint.
CREATE UNIQUE INDEX IF NOT EXISTS shop_items_bought_character_item_unique
ON public.shop_items_bought (character_id, shop_item_id);