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:
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user