-- Add inverse_of column to questions table -- This allows tracking when questions are inverses of each other -- (e.g., "Do you want to give X?" and "Do you want to receive X?") -- The "primary" question has inverse_of = NULL -- The "secondary" (inverse) question points to its primary -- Add the columns ALTER TABLE questions ADD COLUMN inverse_of UUID REFERENCES questions(id) ON DELETE SET NULL; ALTER TABLE questions ADD COLUMN deleted_at TIMESTAMPTZ; -- Add comments to document the columns COMMENT ON COLUMN questions.inverse_of IS 'References the primary question that this question is an inverse of. Used to track question pairs (e.g., give/receive). Primary questions have inverse_of = NULL, secondary questions point to their primary.'; COMMENT ON COLUMN questions.deleted_at IS 'Soft delete timestamp. NULL means the question is active.'; -- Create an index for efficient lookups CREATE INDEX idx_questions_inverse_of ON questions(inverse_of) WHERE inverse_of IS NOT NULL; -- Create a function to get unique question count for a pack -- Unique count = questions that are NOT inverses of another question (i.e., inverse_of IS NULL) CREATE OR REPLACE FUNCTION get_pack_unique_question_count(pack_uuid UUID) RETURNS INTEGER LANGUAGE SQL STABLE AS $$ SELECT COUNT(*)::INTEGER FROM questions WHERE pack_id = pack_uuid AND inverse_of IS NULL AND deleted_at IS NULL; $$; -- Grant execute permission to authenticated users GRANT EXECUTE ON FUNCTION get_pack_unique_question_count(UUID) TO authenticated; -- Create a view to get pack stats including both total and unique counts CREATE OR REPLACE VIEW pack_question_stats AS SELECT pack_id, COUNT(*) FILTER (WHERE deleted_at IS NULL) AS total_questions, COUNT(*) FILTER (WHERE inverse_of IS NULL AND deleted_at IS NULL) AS unique_questions, COUNT(*) FILTER (WHERE inverse_of IS NOT NULL AND deleted_at IS NULL) AS inverse_questions FROM questions GROUP BY pack_id; -- Grant select on the view GRANT SELECT ON pack_question_stats TO authenticated;