-- Create test tables
CREATE TABLE Employee (
    EmployeeId INT NOT NULL,
    LastName VARCHAR(20) NOT NULL,
    CONSTRAINT PK_Employee PRIMARY KEY (EmployeeId));
CREATE TABLE Employee_Audit (
    EmployeeId INT NOT NULL,
    LastName VARCHAR(20) NOT NULL,
    EmpAdditionTime VARCHAR(20) NOT NULL);

-- Create trigger functions
CREATE OR REPLACE FUNCTION employee_insert_trigger_fnc()
  RETURNS trigger AS
$$
BEGIN
    INSERT INTO Employee_Audit (EmployeeId, LastName, EmpAdditionTime)
         VALUES(NEW.EmployeeId,NEW.LastName,current_date);
RETURN NEW;
END;
$$
LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION before_trigger_fnc()
  RETURNS trigger AS
$$
BEGIN
    PERFORM 'SELECT 1';
RETURN NEW;
END;
$$
LANGUAGE plpgsql;


-- Hook the insert trigger twice after each row
CREATE TRIGGER employee_insert_trigger
  AFTER INSERT
  ON Employee
  FOR EACH ROW
  EXECUTE PROCEDURE employee_insert_trigger_fnc();

CREATE TRIGGER employee_insert_trigger_2
  AFTER INSERT
  ON Employee
  FOR EACH ROW
  EXECUTE PROCEDURE employee_insert_trigger_fnc();


-- Call insert
/*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ INSERT INTO Employee VALUES(10,'Adams');

--
-- +--------------------------------------------------------------------------------------------------------+
-- | B: INSERT INTO Employee                                                                                |
-- ++----------+-+--------------+---------------------------------------------------------------------------+
--  |C: Planner| |D: ExecutorRun| G: ExecutorFinish                                                       |
--  +----------+ +--------------+--+-----------------------------------+-+--------------------------------+
--                                 |I: INSERT INTO Employee_Audit...   | |N: INSERT INTO Employee_Audit...|
--                                 +-+----------++--------------------++ +--------------------------------+
--                                   |J: Planner||K: ExecutorRun      |  |O: ExecutorRun |
--                                   +----------++--------------------+  +---------------+

-- Gather span_id, span start and span end of call with triggers
SELECT span_id AS span_b_id,
        get_epoch(span_start) as span_b_start,
        get_epoch(span_end) as span_b_end
		from pg_tracing_peek_spans where parent_id='0000000000000001' AND span_type='Insert query' \gset
SELECT span_id AS span_c_id,
        get_epoch(span_start) as span_c_start,
        get_epoch(span_end) as span_c_end
		from pg_tracing_peek_spans where parent_id=:'span_b_id' and span_type='Planner' \gset
SELECT span_id AS span_d_id,
        get_epoch(span_start) as span_d_start,
        get_epoch(span_end) as span_d_end
		from pg_tracing_peek_spans where parent_id=:'span_b_id' and span_operation='ExecutorRun' \gset

-- Executor Finish, first trigger
SELECT span_id AS span_g_id,
        get_epoch(span_start) as span_g_start,
        get_epoch(span_end) as span_g_end
		from pg_tracing_peek_spans where parent_id=:'span_b_id' and span_operation='ExecutorFinish' \gset
SELECT span_id AS span_i_id,
        get_epoch(span_start) as span_i_start,
        get_epoch(span_end) as span_i_end
		from pg_tracing_peek_spans where parent_id=:'span_g_id' and span_type='Insert query' limit 1 \gset
SELECT span_id AS span_j_id,
        get_epoch(span_start) as span_j_start,
        get_epoch(span_end) as span_j_end
		from pg_tracing_peek_spans where parent_id=:'span_i_id' and span_type='Planner' \gset
SELECT span_id AS span_k_id,
        get_epoch(span_start) as span_k_start,
        get_epoch(span_end) as span_k_end
		from pg_tracing_peek_spans where parent_id=:'span_i_id' and span_operation='ExecutorRun' \gset

-- Executor Finish, second trigger
SELECT span_id AS span_n_id,
        get_epoch(span_start) as span_n_start,
        get_epoch(span_end) as span_n_end
        from pg_tracing_peek_spans where parent_id=:'span_g_id' and span_type='Insert query' limit 1 offset 1 \gset
SELECT span_id AS span_o_id,
        get_epoch(span_start) as span_o_start,
        get_epoch(span_end) as span_o_end
        from pg_tracing_peek_spans where parent_id=:'span_n_id' and span_operation='ExecutorRun' \gset

-- Check that spans' start and end are within expection
-- Planner
SELECT :span_c_start >= :span_b_start as planner_starts_after_root_span;
-- ExecutorRun
SELECT :span_d_start >= :span_c_end as executor_run_starts_after_planner,
       :span_d_end <= :span_g_start as executor_run_ends_before_finish;
-- ExecutorFinish
SELECT :span_g_start <= :span_i_start as executor_finish_starts_before_child,
       :span_g_end >= :span_o_end as executor_finish_ends_after_child;
-- First trigger
SELECT :span_i_end >= GREATEST(:span_j_end, :span_k_end) as first_trigger_ends_after_children;
-- Second trigger
SELECT :span_o_start >= :span_i_end  as second_trigger_starts_after_first,
       :span_o_end >= :span_o_end as second_trigger_ends_after_child,
       :span_o_end <= :span_g_end as second_trigger_ends_before_executorfinish_end,
       :span_n_end <= :span_g_end as second_trigger_ends_before_root_executorfinish_end;

-- Check span_operation
SELECT span_type, span_operation, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000001';

-- Check count of query_id
SELECT count(distinct query_id) from pg_tracing_consume_spans where trace_id='00000000000000000000000000000001';

-- Test table to test before trigger
CREATE TABLE before_trigger_table (a int);

-- Hook the before trigger
CREATE TRIGGER employee_insert_trigger
  BEFORE INSERT
  ON before_trigger_table
  FOR EACH ROW
  EXECUTE PROCEDURE before_trigger_fnc();

CREATE TRIGGER employee_update_trigger
  BEFORE UPDATE
  ON before_trigger_table
  FOR EACH ROW
  EXECUTE PROCEDURE before_trigger_fnc();

CREATE TRIGGER employee_delete_trigger
  BEFORE DELETE
  ON before_trigger_table
  FOR EACH ROW
  EXECUTE PROCEDURE before_trigger_fnc();

-- Call before trigger insert
/*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ INSERT INTO before_trigger_table SELECT generate_series(1,2);
SELECT trace_id, span_type, span_operation, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000001';

-- Call before trigger update
/*dddbs='postgres.db',traceparent='00-00000000000000000000000000000002-0000000000000002-01'*/ UPDATE before_trigger_table set a=1;
SELECT trace_id, span_type, span_operation, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000002';

-- Call before trigger delete
/*dddbs='postgres.db',traceparent='00-00000000000000000000000000000003-0000000000000003-01'*/ DELETE FROM before_trigger_table where a=1;
SELECT trace_id, span_type, span_operation, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000003';

-- Check count of query_id
SELECT count(distinct query_id) from pg_tracing_consume_spans;

-- Test foreign key failure
CREATE TYPE rainbow AS ENUM ('red', 'orange', 'yellow', 'green', 'blue', 'purple');
CREATE TABLE enumtest_parent (id rainbow PRIMARY KEY);
CREATE TABLE enumtest_child (parent rainbow REFERENCES enumtest_parent);
INSERT INTO enumtest_parent VALUES ('red');
/*traceparent='00-00000000000000000000000000000004-0000000000000004-01'*/ INSERT INTO enumtest_child VALUES ('blue');
SELECT trace_id, span_type, span_operation, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000004';

-- Test before trigger with copy dml
create table copydml_test (id serial, t text);
create function qqq_trig() returns trigger as $$
begin
if tg_op in ('INSERT', 'UPDATE') then
    return new;
end if;
end
$$ language plpgsql;
create trigger qqqbef before insert or update or delete on copydml_test
    for each row execute procedure qqq_trig();
/*traceparent='00-00000000000000000000000000000005-0000000000000005-01'*/ copy (insert into copydml_test (t) values ('f') returning id) to stdout;

SELECT trace_id, span_type, span_operation, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000005';

-- Test explain on a before trigger
-- This will go through ExecutorEnd without any parent executor run
/*dddbs='postgres.db',traceparent='00-fed00000000000000000000000000006-0000000000000003-01'*/ explain INSERT INTO before_trigger_table VALUES(10);
SELECT trace_id, span_type, span_operation, lvl from peek_ordered_spans where trace_id='fed00000000000000000000000000006';

-- Cleanup
CALL reset_settings();
CALL clean_spans();
