-- Restore migration 102's rollup window function verbatim, then drop the -- columns. Order matters: the function must stop referencing the columns -- before they go away. CREATE OR REPLACE FUNCTION rollup_task_usage_hourly_window( p_from TIMESTAMPTZ, p_to TIMESTAMPTZ ) RETURNS BIGINT LANGUAGE plpgsql AS $$ DECLARE v_rows BIGINT; BEGIN IF p_from >= p_to THEN RETURN 0; END IF; WITH dirty_from_updates AS ( SELECT DISTINCT task_usage_hour_bucket(tu.created_at) AS bucket_hour, a.workspace_id AS workspace_id, atq.runtime_id AS runtime_id, atq.agent_id AS agent_id, i.project_id AS project_id, tu.provider AS provider, tu.model AS model FROM task_usage tu JOIN agent_task_queue atq ON atq.id = tu.task_id JOIN agent a ON a.id = atq.agent_id LEFT JOIN issue i ON i.id = atq.issue_id WHERE atq.runtime_id IS NOT NULL AND ( (tu.updated_at >= p_from AND tu.updated_at < p_to) OR (tu.updated_at IS NULL AND tu.created_at >= p_from AND tu.created_at < p_to) ) ), dirty_from_queue AS ( SELECT bucket_hour, workspace_id, runtime_id, agent_id, project_id, provider, model FROM task_usage_hourly_dirty WHERE enqueued_at < p_to ), dirty_keys AS ( SELECT * FROM dirty_from_updates UNION SELECT * FROM dirty_from_queue ), recomputed AS ( SELECT dk.bucket_hour, dk.workspace_id, dk.runtime_id, dk.agent_id, dk.project_id, dk.provider, dk.model, SUM(tu.input_tokens)::bigint AS input_tokens, SUM(tu.output_tokens)::bigint AS output_tokens, SUM(tu.cache_read_tokens)::bigint AS cache_read_tokens, SUM(tu.cache_write_tokens)::bigint AS cache_write_tokens, COUNT(DISTINCT tu.task_id)::bigint AS task_count, COUNT(*)::bigint AS event_count FROM dirty_keys dk JOIN agent_task_queue atq ON atq.runtime_id = dk.runtime_id AND atq.agent_id = dk.agent_id JOIN agent a ON a.id = atq.agent_id AND a.workspace_id = dk.workspace_id LEFT JOIN issue i ON i.id = atq.issue_id JOIN task_usage tu ON tu.task_id = atq.id AND tu.provider = dk.provider AND tu.model = dk.model AND task_usage_hour_bucket(tu.created_at) = dk.bucket_hour WHERE (i.project_id IS NOT DISTINCT FROM dk.project_id) GROUP BY 1, 2, 3, 4, 5, 6, 7 ), upserted AS ( INSERT INTO task_usage_hourly AS d ( bucket_hour, workspace_id, runtime_id, agent_id, project_id, provider, model, input_tokens, output_tokens, cache_read_tokens, cache_write_tokens, task_count, event_count ) SELECT bucket_hour, workspace_id, runtime_id, agent_id, project_id, provider, model, input_tokens, output_tokens, cache_read_tokens, cache_write_tokens, task_count, event_count FROM recomputed ON CONFLICT ON CONSTRAINT uq_task_usage_hourly_key DO UPDATE SET input_tokens = EXCLUDED.input_tokens, output_tokens = EXCLUDED.output_tokens, cache_read_tokens = EXCLUDED.cache_read_tokens, cache_write_tokens = EXCLUDED.cache_write_tokens, task_count = EXCLUDED.task_count, event_count = EXCLUDED.event_count, updated_at = now() RETURNING 1 ), deleted_empty AS ( DELETE FROM task_usage_hourly d USING dirty_keys dk WHERE d.bucket_hour = dk.bucket_hour AND d.workspace_id = dk.workspace_id AND d.runtime_id = dk.runtime_id AND d.agent_id = dk.agent_id AND d.project_id IS NOT DISTINCT FROM dk.project_id AND d.provider = dk.provider AND d.model = dk.model AND NOT EXISTS ( SELECT 1 FROM recomputed r WHERE r.bucket_hour = dk.bucket_hour AND r.workspace_id = dk.workspace_id AND r.runtime_id = dk.runtime_id AND r.agent_id = dk.agent_id AND r.project_id IS NOT DISTINCT FROM dk.project_id AND r.provider = dk.provider AND r.model = dk.model ) RETURNING 1 ) SELECT (SELECT COUNT(*) FROM upserted) + (SELECT COUNT(*) FROM deleted_empty) INTO v_rows; DELETE FROM task_usage_hourly_dirty WHERE enqueued_at < p_to; RETURN v_rows; END; $$; ALTER TABLE task_usage_hourly DROP COLUMN IF EXISTS uncosted_cache_write_tokens, DROP COLUMN IF EXISTS uncosted_cache_read_tokens, DROP COLUMN IF EXISTS uncosted_output_tokens, DROP COLUMN IF EXISTS uncosted_input_tokens, DROP COLUMN IF EXISTS cost_usd_ticks; ALTER TABLE task_usage DROP COLUMN IF EXISTS cost_usd_ticks;