SET search_path = social,public;

LOCK TABLE feedback_task IN EXCLUSIVE MODE;

-- Postgres doesn't allow partitioning of tables with foreign keys
--
ALTER TABLE comment DROP CONSTRAINT "comment_feedback_task_id_fkey";
ALTER TABLE commit_feedback_task DROP CONSTRAINT "commit_feedback_task_feedback_task_id_fkey";
ALTER TABLE fbapi_issue DROP CONSTRAINT "fbapi_issue_feedback_task_id_fkey";
ALTER TABLE star_trek_issue DROP CONSTRAINT "star_trek_issue_feedback_task_id_fkey";
ALTER TABLE feedback_task DROP CONSTRAINT "feedback_task_duplicate_head_id_fkey";

ALTER TABLE feedback_task RENAME TO old_feedback_task;

-----------------------------
-- INHERITANCE AND INDEXES --
-----------------------------

CREATE TABLE feedback_task (LIKE old_feedback_task INCLUDING DEFAULTS INCLUDING CONSTRAINTS);

-- These constraints are no longer valid.
-- New constraints will be created for each partition
--
ALTER TABLE feedback_task DROP CONSTRAINT bucket_constraint;
ALTER TABLE feedback_task DROP CONSTRAINT deploy_close_constraint;
ALTER TABLE feedback_task DROP CONSTRAINT outgoing_resolve_constraint;
ALTER TABLE feedback_task DROP CONSTRAINT resolve_close_constraint;

-- Create 'pending' partition
--
CREATE TABLE feedback_task_pending (
    CONSTRAINT task_pending_pkey PRIMARY KEY (id),

    CONSTRAINT check_bucket_incoming_or_deferred CHECK (bucket = 'incoming' OR bucket = 'deferred'),
    CONSTRAINT check_null_resolved_by CHECK (resolved_by IS NULL),
    CONSTRAINT check_null_resolved_at CHECK (resolved_at IS NULL),
    CONSTRAINT check_null_resolution CHECK (resolution IS NULL),
    CONSTRAINT check_null_closed_by CHECK (closed_by IS NULL),
    CONSTRAINT check_null_closed_at CHECK (closed_at IS NULL),
    CONSTRAINT check_null_deployed_at CHECK (deployed_at IS NULL)
)
INHERITS (feedback_task);

CREATE INDEX feedback_task_pending_bucket_idx ON feedback_task_pending (bucket);
CREATE INDEX feedback_task_pending_duplicate_head_id_idx ON feedback_task_pending (duplicate_head_id);
CREATE INDEX feedback_task_pending_object_id_idx ON feedback_task_pending (object_id);
CREATE INDEX feedback_task_pending_position_idx ON feedback_task_pending USING gist (position);
CREATE INDEX feedback_task_pending_type_source_workflow_idx ON feedback_task_pending (type, source, workflow);

-- Create outgoing closed partition
--
CREATE TABLE feedback_task_outgoing_closed (
    CONSTRAINT task_outgoing_closed_pkey PRIMARY KEY (id),

    CONSTRAINT check_bucket_outgoing CHECK (bucket = 'outgoing'),

    CONSTRAINT check_not_null_resolved_by CHECK (resolved_by IS NOT NULL),
    CONSTRAINT check_not_null_resolved_at CHECK (resolved_at IS NOT NULL),
    CONSTRAINT check_not_null_resolution CHECK (resolution IS NOT NULL),
    CONSTRAINT check_not_null_closed_by CHECK (closed_by IS NOT NULL),
    CONSTRAINT check_not_null_closed_at CHECK (closed_at IS NOT NULL)
)
INHERITS (feedback_task);

CREATE INDEX feedback_task_outgoing_closed_acquired_by_idx ON feedback_task_outgoing_closed (acquired_by);
CREATE INDEX feedback_task_outgoing_closed_duplicate_head_id_idx ON feedback_task_outgoing_closed (duplicate_head_id);
CREATE INDEX feedback_task_outgoing_closed_object_id_idx ON feedback_task_outgoing_closed (object_id);
CREATE INDEX feedback_task_outgoing_closed_position_idx ON feedback_task_outgoing_closed USING gist (position);
CREATE INDEX feedback_task_outgoing_closed_type_source_workflow_idx ON feedback_task_outgoing_closed (type, source, workflow);

-- Create outgoing opened partition
--
CREATE TABLE feedback_task_outgoing_opened (
    CONSTRAINT task_outgoing_opened_pkey PRIMARY KEY (id),

    CONSTRAINT check_bucket_outgoing CHECK (bucket = 'outgoing'),

    CONSTRAINT check_null_closed_by CHECK (closed_by IS NULL),
    CONSTRAINT check_null_closed_at CHECK (closed_at IS NULL),
    CONSTRAINT check_null_deployed_at CHECK (deployed_at IS NULL)
)
INHERITS (feedback_task);

CREATE INDEX feedback_task_outgoing_opened_acquired_by_idx ON feedback_task_outgoing_opened (acquired_by);
CREATE INDEX feedback_task_outgoing_opened_duplicate_head_id_idx ON feedback_task_outgoing_opened (duplicate_head_id);
CREATE INDEX feedback_task_outgoing_opened_object_id_idx ON feedback_task_outgoing_opened (object_id);
CREATE INDEX feedback_task_outgoing_opened_position_idx ON feedback_task_outgoing_opened USING gist (position);
CREATE INDEX feedback_task_outgoing_opened_type_source_workflow_idx ON feedback_task_outgoing_opened (type, source, workflow);

---------------
-- FUNCTIONS --
---------------

ALTER FUNCTION feedback_task_insert_update_trigger() RENAME TO feedback_task_calc_hash_trigger;

-- Direct insertion in parent table 'feedback_task' is not allowed
--
CREATE FUNCTION feedback_task_insert_trigger() RETURNS trigger
    LANGUAGE plpgsql
    AS $$
BEGIN
    RAISE EXCEPTION 'Not insertable table feedback_task';
END;
$$;

-- 'pending' -> 'outgoing_opened', 'pending' -> 'outgoing_closed'
--
CREATE OR REPLACE FUNCTION feedback_task_pending_update_trigger() RETURNS trigger
    LANGUAGE plpgsql
    AS $$
BEGIN
    IF (NEW.bucket = 'incoming' OR NEW.bucket = 'deferred') THEN
        RETURN NEW;
    END IF;

    DELETE FROM social.feedback_task_pending WHERE id = OLD.id;

    IF (NEW.closed_by IS NULL) THEN
        INSERT INTO social.feedback_task_outgoing_opened VALUES (NEW.*);
    ELSE
        INSERT INTO social.feedback_task_outgoing_closed VALUES (NEW.*);
    END IF;

    RETURN NULL;
END;
$$;

-- 'outgoing_opened' -> 'outgoing_closed'
--
CREATE OR REPLACE FUNCTION feedback_task_outgoing_opened_update_trigger() RETURNS trigger
    LANGUAGE plpgsql
    AS $$
BEGIN
    IF (NEW.closed_by IS NULL) THEN
        RETURN NEW;
    END IF;

    DELETE FROM social.feedback_task_outgoing_opened WHERE id = OLD.id;
    INSERT INTO social.feedback_task_outgoing_closed VALUES (NEW.*);
    RETURN NULL;
END;
$$;

-- 'outgoing_closed' -> 'outgoing_opened'
--
CREATE OR REPLACE FUNCTION feedback_task_outgoing_closed_update_trigger() RETURNS trigger
    LANGUAGE plpgsql
    AS $$
BEGIN
    IF (NEW.closed_by IS NOT NULL) THEN
        RETURN NEW;
    END IF;

    DELETE FROM social.feedback_task_outgoing_closed WHERE id = OLD.id;
    INSERT INTO social.feedback_task_outgoing_opened VALUES (NEW.*);
    RETURN NULL;
END;
$$;

--------------
-- TRIGGERS --
--------------

-- N.B. triggers execution order for the same table depends on their naming.
-- That's why there are chars '1' and '2'

DROP TRIGGER IF EXISTS feedback_task_insert_trigger ON feedback_task;
DROP TRIGGER IF EXISTS feedback_task_update_trigger ON feedback_task;

-- feedback_task
--
CREATE TRIGGER feedback_task_insert_trigger BEFORE INSERT ON feedback_task
    FOR EACH STATEMENT EXECUTE PROCEDURE feedback_task_insert_trigger();

-- feedback_task_pending
--
CREATE TRIGGER feedback_task_pending_1_update_trigger BEFORE UPDATE ON feedback_task_pending
    FOR EACH ROW EXECUTE PROCEDURE feedback_task_pending_update_trigger();

CREATE TRIGGER feedback_task_pending_2_calc_hash_trigger BEFORE INSERT OR UPDATE ON feedback_task_pending
    FOR EACH ROW EXECUTE PROCEDURE feedback_task_calc_hash_trigger();

-- feedback_task_outgoing_opened
--
CREATE TRIGGER feedback_task_outgoing_opened_1_update_trigger BEFORE UPDATE ON feedback_task_outgoing_opened
    FOR EACH ROW EXECUTE PROCEDURE feedback_task_outgoing_opened_update_trigger();

CREATE TRIGGER feedback_task_outgoing_opened_2_calc_hash_trigger BEFORE INSERT OR UPDATE ON feedback_task_outgoing_opened
    FOR EACH ROW EXECUTE PROCEDURE feedback_task_calc_hash_trigger();

-- feedback_task_outgoing_closed
--
CREATE TRIGGER feedback_task_outgoing_closed_1_update_trigger BEFORE UPDATE ON feedback_task_outgoing_closed
    FOR EACH ROW EXECUTE PROCEDURE feedback_task_outgoing_closed_update_trigger();

CREATE TRIGGER feedback_task_outgoing_closed_2_calc_hash_trigger BEFORE INSERT OR UPDATE ON feedback_task_outgoing_closed
    FOR EACH ROW EXECUTE PROCEDURE feedback_task_calc_hash_trigger();

---------------------------
-- PARTITIONS POPULATION --
---------------------------

INSERT INTO feedback_task_pending
    SELECT * FROM old_feedback_task
        WHERE bucket = 'incoming' OR bucket = 'deferred';

INSERT INTO feedback_task_outgoing_opened
    SELECT * FROM old_feedback_task
        WHERE bucket = 'outgoing' AND closed_by IS NULL;

INSERT INTO feedback_task_outgoing_closed
    SELECT * FROM old_feedback_task
        WHERE bucket = 'outgoing' AND closed_by IS NOT NULL;

--------------------
-- DROP OLD TABLE --
--------------------

ALTER SEQUENCE feedback_task_id_seq OWNED BY feedback_task.id;

DROP TABLE old_feedback_task;

SET search_path=public;
