-- v0.85.0 — scheduled refresh deadlines and typed outcomes

ALTER TABLE pgtrickle.pgt_refresh_history
    ADD COLUMN IF NOT EXISTS error_code text,
    ADD COLUMN IF NOT EXISTS error_sqlstate text,
    ADD COLUMN IF NOT EXISTS retryable boolean;

ALTER TABLE pgtrickle.pgt_scheduler_jobs
    ADD COLUMN IF NOT EXISTS outcome_code text,
    ADD COLUMN IF NOT EXISTS outcome_sqlstate text,
    ADD COLUMN IF NOT EXISTS worker_slot_generation bigint;

ALTER TABLE pgtrickle.pgt_stream_tables
    ADD COLUMN IF NOT EXISTS self_heal_work_mem_percent smallint NOT NULL DEFAULT 100,
    ADD COLUMN IF NOT EXISTS self_heal_lock_backoff_exponent smallint NOT NULL DEFAULT 0,
    ADD COLUMN IF NOT EXISTS self_heal_success_streak smallint NOT NULL DEFAULT 0,
    ADD COLUMN IF NOT EXISTS last_error_code text,
    ADD COLUMN IF NOT EXISTS last_error_retryable boolean;

DO $$
BEGIN
    IF NOT EXISTS (
        SELECT 1 FROM pg_catalog.pg_constraint
        WHERE conrelid = 'pgtrickle.pgt_refresh_history'::regclass
          AND conname = 'pgt_refresh_history_error_code_check'
    ) THEN
        ALTER TABLE pgtrickle.pgt_refresh_history
            ADD CONSTRAINT pgt_refresh_history_error_code_check
            CHECK (error_code IS NULL OR error_code IN
                ('LOCK_TIMEOUT', 'STATEMENT_TIMEOUT', 'DEADLOCK',
                 'SERIALIZATION', 'OUT_OF_MEMORY', 'CANCELLED',
                 'PERMANENT', 'UNKNOWN_RETRYABLE'));
    END IF;
    IF NOT EXISTS (
        SELECT 1 FROM pg_catalog.pg_constraint
        WHERE conrelid = 'pgtrickle.pgt_scheduler_jobs'::regclass
          AND conname = 'pgt_scheduler_jobs_outcome_code_check'
    ) THEN
        ALTER TABLE pgtrickle.pgt_scheduler_jobs
            ADD CONSTRAINT pgt_scheduler_jobs_outcome_code_check
            CHECK (outcome_code IS NULL OR outcome_code IN
                ('LOCK_TIMEOUT', 'STATEMENT_TIMEOUT', 'DEADLOCK',
                 'SERIALIZATION', 'OUT_OF_MEMORY', 'CANCELLED',
                 'PERMANENT', 'UNKNOWN_RETRYABLE'));
    END IF;
    IF NOT EXISTS (
        SELECT 1 FROM pg_catalog.pg_constraint
        WHERE conrelid = 'pgtrickle.pgt_stream_tables'::regclass
          AND conname = 'pgt_stream_tables_self_heal_work_mem_percent_check'
    ) THEN
        ALTER TABLE pgtrickle.pgt_stream_tables
            ADD CONSTRAINT pgt_stream_tables_self_heal_work_mem_percent_check
            CHECK (self_heal_work_mem_percent BETWEEN 25 AND 100);
    END IF;
    IF NOT EXISTS (
        SELECT 1 FROM pg_catalog.pg_constraint
        WHERE conrelid = 'pgtrickle.pgt_stream_tables'::regclass
          AND conname = 'pgt_stream_tables_self_heal_lock_backoff_check'
    ) THEN
        ALTER TABLE pgtrickle.pgt_stream_tables
            ADD CONSTRAINT pgt_stream_tables_self_heal_lock_backoff_check
            CHECK (self_heal_lock_backoff_exponent BETWEEN 0 AND 6);
    END IF;
    IF NOT EXISTS (
        SELECT 1 FROM pg_catalog.pg_constraint
        WHERE conrelid = 'pgtrickle.pgt_stream_tables'::regclass
          AND conname = 'pgt_stream_tables_self_heal_success_streak_check'
    ) THEN
        ALTER TABLE pgtrickle.pgt_stream_tables
            ADD CONSTRAINT pgt_stream_tables_self_heal_success_streak_check
            CHECK (self_heal_success_streak BETWEEN 0 AND 3);
    END IF;
    IF NOT EXISTS (
        SELECT 1 FROM pg_catalog.pg_constraint
        WHERE conrelid = 'pgtrickle.pgt_stream_tables'::regclass
          AND conname = 'pgt_stream_tables_last_error_code_check'
    ) THEN
        ALTER TABLE pgtrickle.pgt_stream_tables
            ADD CONSTRAINT pgt_stream_tables_last_error_code_check
            CHECK (last_error_code IS NULL OR last_error_code IN
                ('LOCK_TIMEOUT', 'STATEMENT_TIMEOUT', 'DEADLOCK',
                 'SERIALIZATION', 'OUT_OF_MEMORY', 'CANCELLED',
                 'PERMANENT', 'UNKNOWN_RETRYABLE'));
    END IF;
END
$$;

CREATE INDEX IF NOT EXISTS idx_sched_jobs_terminal_finished
    ON pgtrickle.pgt_scheduler_jobs (finished_at, job_id)
    WHERE status IN ('SUCCEEDED', 'RETRYABLE_FAILED', 'PERMANENT_FAILED', 'CANCELLED');

CREATE INDEX IF NOT EXISTS idx_hist_start_time
    ON pgtrickle.pgt_refresh_history (start_time, refresh_id);

INSERT INTO pgtrickle.pgt_schema_version (version, description)
VALUES ('0.85.0', 'Scheduler and resource resilience gate')
ON CONFLICT (version) DO NOTHING;

CREATE OR REPLACE FUNCTION pgtrickle."resume_after_drain"() RETURNS bool
STRICT
LANGUAGE c /* Rust */
AS 'MODULE_PATHNAME', 'resume_after_drain_wrapper';

REVOKE EXECUTE ON FUNCTION pgtrickle.resume_after_drain() FROM PUBLIC;
