diff --git a/internal/config/store_postgres.go b/internal/config/store_postgres.go index b8b0346c3..b1a1f4844 100644 --- a/internal/config/store_postgres.go +++ b/internal/config/store_postgres.go @@ -1810,11 +1810,17 @@ func (s *PostgresStore) GetActivePurchaseHistory(ctx context.Context, asOf time. args := []any{asOf} conds, args = appendAccountPredicate(conds, args, accountIDs, externalIDsByProvider) + // The column list MUST match scanPurchaseHistoryRow's scan destinations + // exactly (pgx errors on a field/destination count mismatch). #808 added + // offering_class, listing_id, listing_state to the scanner and every other + // purchase-history SELECT but missed this one, which broke every real-Postgres + // call here (dashboard KPIs, /api/inventory/*, analytics collector). query := fmt.Sprintf(` SELECT account_id, purchase_id, timestamp, provider, service, region, resource_type, count, term, payment, upfront_cost, monthly_cost, estimated_savings, plan_id, plan_name, ramp_step, cloud_account_id, - revocation_window_closes_at, revoked_at, revoked_via, support_case_id + revocation_window_closes_at, revoked_at, revoked_via, support_case_id, + offering_class, listing_id, listing_state FROM purchase_history WHERE %s ORDER BY timestamp DESC diff --git a/internal/config/store_postgres_pgxmock_test.go b/internal/config/store_postgres_pgxmock_test.go index 913e40943..9f66dc7e2 100644 --- a/internal/config/store_postgres_pgxmock_test.go +++ b/internal/config/store_postgres_pgxmock_test.go @@ -1046,7 +1046,7 @@ func TestPGXMock_GetActivePurchaseHistory_Unscoped(t *testing.T) { now := time.Now().Truncate(time.Second) rows := pgxmock.NewRows(purchaseHistoryCols).AddRow(purchaseHistoryRow(now, "aws", "acct-1")...) mock.ExpectQuery( - `SELECT account_id, purchase_id, .*revocation_window_closes_at, revoked_at, revoked_via, support_case_id FROM purchase_history WHERE term > 0 AND timestamp \+ make_interval\(hours => term \* 8760\) >= \$1 AND revoked_at IS NULL ORDER BY timestamp DESC$`, + `SELECT account_id, purchase_id, .*revocation_window_closes_at, revoked_at, revoked_via, support_case_id, offering_class, listing_id, listing_state FROM purchase_history WHERE term > 0 AND timestamp \+ make_interval\(hours => term \* 8760\) >= \$1 AND revoked_at IS NULL ORDER BY timestamp DESC$`, ).WithArgs(now).WillReturnRows(rows) records, err := store.GetActivePurchaseHistory(ctx, now, nil, nil)