fix: compute expires_at in SQL instead of generated column

TIMESTAMPTZ + INTERVAL is STABLE (depends on timezone), not IMMUTABLE,
so PostgreSQL rejects it in GENERATED ALWAYS AS STORED columns.
Fix: remove generated column and compute expires_at inline in each query
as (added_at + storage_days * INTERVAL '1 day').

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
dbastrikin
2026-02-21 23:24:42 +02:00
parent b9b9e9fe11
commit 288bb1c375
2 changed files with 8 additions and 6 deletions

View File

@@ -23,7 +23,10 @@ func NewRepository(pool *pgxpool.Pool) *Repository {
return &Repository{pool: pool}
}
const selectCols = `id, user_id, mapping_id, name, quantity, unit, category, storage_days, added_at, expires_at`
// expires_at is computed in SQL because TIMESTAMPTZ + INTERVAL is STABLE (not IMMUTABLE),
// which prevents it from being used as a stored generated column.
const selectCols = `id, user_id, mapping_id, name, quantity, unit, category, storage_days, added_at,
(added_at + storage_days * INTERVAL '1 day') AS expires_at`
// List returns all products for a user, sorted by expires_at ASC.
func (r *Repository) List(ctx context.Context, userID string) ([]*Product, error) {

View File

@@ -8,13 +8,12 @@ CREATE TABLE products (
unit TEXT NOT NULL DEFAULT 'pcs',
category TEXT,
storage_days INT NOT NULL DEFAULT 7,
added_at TIMESTAMPTZ NOT NULL DEFAULT now(),
expires_at TIMESTAMPTZ GENERATED ALWAYS AS
(added_at + (storage_days || ' days')::INTERVAL) STORED
added_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- expires_at is computed as (added_at + storage_days * INTERVAL '1 day') in queries.
-- A stored generated column cannot be used because timestamptz + interval is STABLE, not IMMUTABLE.
CREATE INDEX idx_products_user_id ON products(user_id);
CREATE INDEX idx_products_expires_at ON products(user_id, expires_at);
-- +goose Down
DROP TABLE products;