\set ECHO none
\pset format unaligned

-- Exercises the ProcessUtility CTAS lineage hook: when a
-- CREATE TABLE AS / CREATE MATERIALIZED VIEW / SELECT INTO inner
-- SELECT projects a provsql column verbatim from a tracked source,
-- the hook populates the new relation's table_info (with the
-- inherited TID/BID kind and BID block-key alignment when the key
-- columns survive the projection) and ancestor set (transitive
-- union of source ancestors), and installs the provenance_guard
-- trigger so subsequent INSERT / UPDATE OF provsql still flip the
-- table to OPAQUE.
--
-- Output is shaped to be OID-stable: we compare ancestor lists
-- against the source relation's OID rather than printing raw OIDs.

CREATE TABLE ci_src_a(x int, y int);
INSERT INTO ci_src_a VALUES (1, 10), (2, 20), (3, 30);
SELECT add_provenance('ci_src_a');

CREATE TABLE ci_src_b(x int, lbl text);
INSERT INTO ci_src_b VALUES (1, 'b1'), (2, 'b2');
SELECT add_provenance('ci_src_b');

CREATE TABLE ci_bid(k int, v int);
INSERT INTO ci_bid VALUES (1, 100), (1, 101), (2, 200);
SELECT repair_key('ci_bid', 'k');

-- ---------------------------------------------------------------
-- (1) Single-source TID CTAS that projects the source's provsql:
--     inherits kind=TID, ancestors={ci_src_a}.
-- ---------------------------------------------------------------
CREATE TABLE ci_t1 AS SELECT x, provsql FROM ci_src_a;
SELECT (get_table_info('ci_t1'::regclass::oid)).kind  AS t1_kind,
       get_ancestors('ci_t1'::regclass::oid)
         = ARRAY['ci_src_a'::regclass::oid]           AS t1_ancestors_correct;

-- ---------------------------------------------------------------
-- (2) Multi-source TID CTAS (join over two disjoint TID sources)
--     projecting one source's provsql: classifier promotes to TID
--     (every source TID, ancestor sets {ci_src_a} and {ci_src_b}
--     pairwise disjoint).  The CTAS hook fires and records
--     kind=TID, ancestors=union({ci_src_a, ci_src_b}).
-- ---------------------------------------------------------------
CREATE TABLE ci_t2 AS
  SELECT a.x, a.provsql FROM ci_src_a a, ci_src_b b WHERE a.x = b.x;
SELECT (get_table_info('ci_t2'::regclass::oid)).kind  AS t2_kind,
       (SELECT array_agg(o ORDER BY o)
          FROM unnest(get_ancestors('ci_t2'::regclass::oid)) o)
       = (SELECT array_agg(o ORDER BY o)
            FROM unnest(ARRAY['ci_src_a'::regclass::oid,
                              'ci_src_b'::regclass::oid]) o)
                                                       AS t2_ancestors_correct;

-- ---------------------------------------------------------------
-- (3) CTAS without projecting provsql: the new relation has no
--     provsql column, so the hook skips and no metadata is recorded.
-- ---------------------------------------------------------------
CREATE TABLE ci_t3 AS SELECT x, y FROM ci_src_a;
SELECT get_table_info('ci_t3'::regclass::oid) IS NULL AS t3_no_metadata,
       get_ancestors('ci_t3'::regclass::oid) IS NULL  AS t3_no_ancestry;

-- ---------------------------------------------------------------
-- (4) BID source: target list keeps the block-key column k.
--     Inherits kind=BID with the new attno of k as block_key.
--     Original k is attno 1 in ci_bid; in the new ci_t4 it lands
--     at attno 1 too (we project k first), so block_key = {1}.
-- ---------------------------------------------------------------
CREATE TABLE ci_t4 AS SELECT k, provsql FROM ci_bid;
SELECT (get_table_info('ci_t4'::regclass::oid)).kind       AS t4_kind,
       (get_table_info('ci_t4'::regclass::oid)).block_key  AS t4_block_key,
       get_ancestors('ci_t4'::regclass::oid)
         = ARRAY['ci_bid'::regclass::oid]                  AS t4_ancestors_correct;

-- (5) BID source: target list DROPS the block-key column.  The
--     classifier's BID-projection check (the n_meta == 1 branch
--     resolves outer Vars through subqueries back to base columns)
--     now reports OPAQUE for this case, so the CTAS hook skips
--     and no metadata is recorded for the new relation.  The
--     resulting ci_t5 is untracked -- safer than the previous
--     "demote to TID" behaviour, which advertised independence
--     the rows did not actually have under any preserved column.
-- ---------------------------------------------------------------
CREATE TABLE ci_t5 AS SELECT v, provsql FROM ci_bid;
SELECT get_table_info('ci_t5'::regclass::oid) IS NULL  AS t5_no_metadata,
       get_ancestors('ci_t5'::regclass::oid)  IS NULL  AS t5_no_ancestry;

-- ---------------------------------------------------------------
-- (6) SELECT INTO: PG's parser transforms this to CreateTableAsStmt;
--     the hook fires identically.
-- ---------------------------------------------------------------
SELECT x, provsql INTO ci_t6 FROM ci_src_a;
SELECT (get_table_info('ci_t6'::regclass::oid)).kind  AS t6_kind,
       get_ancestors('ci_t6'::regclass::oid)
         = ARRAY['ci_src_a'::regclass::oid]           AS t6_ancestors_correct;

-- ---------------------------------------------------------------
-- (7) CREATE MATERIALIZED VIEW: same statement node (CreateTableAsStmt
--     with objtype=OBJECT_MATVIEW), hook fires identically.
-- ---------------------------------------------------------------
CREATE MATERIALIZED VIEW ci_mv1 AS SELECT x, provsql FROM ci_src_a;
SELECT (get_table_info('ci_mv1'::regclass::oid)).kind  AS mv_kind,
       get_ancestors('ci_mv1'::regclass::oid)
         = ARRAY['ci_src_a'::regclass::oid]            AS mv_ancestors_correct;
DROP MATERIALIZED VIEW ci_mv1;

-- ---------------------------------------------------------------
-- (8) CTAS WITH NO DATA: structure-only, inner SELECT not executed.
--     The hook still fires (the new relation has a provsql column
--     by virtue of the TLE shape), so the metadata is recorded and
--     subsequent INSERTs will exercise provenance_guard.
-- ---------------------------------------------------------------
CREATE TABLE ci_t8 AS SELECT x, provsql FROM ci_src_a WITH NO DATA;
SELECT (get_table_info('ci_t8'::regclass::oid)).kind  AS t8_kind,
       get_ancestors('ci_t8'::regclass::oid)
         = ARRAY['ci_src_a'::regclass::oid]           AS t8_ancestors_correct;

-- ---------------------------------------------------------------
-- (9) provenance_guard installation: confirm the trigger exists on
--     a hook-tracked CTAS, and that supplying a user UUID on INSERT
--     flips the table to OPAQUE (the same guard semantics as
--     add_provenance'd tables).
-- ---------------------------------------------------------------
SELECT tgname FROM pg_trigger
 WHERE tgrelid = 'ci_t1'::regclass
   AND NOT tgisinternal
 ORDER BY tgname;
INSERT INTO ci_t1(x, provsql) VALUES (4, public.uuid_generate_v4());
SELECT (get_table_info('ci_t1'::regclass::oid)).kind  AS t1_kind_after_user_insert;

-- ---------------------------------------------------------------
-- (10) DROP TABLE fires cleanup_table_info, ancestry goes with it.
-- ---------------------------------------------------------------
SELECT 'ci_t4'::regclass::oid AS ci_t4_oid \gset
DROP TABLE ci_t4;
SELECT get_table_info(:ci_t4_oid) IS NULL AS t4_cleaned_up,
       get_ancestors(:ci_t4_oid) IS NULL  AS t4_ancestry_cleaned_up;

DROP TABLE ci_t1;
DROP TABLE ci_t2;
DROP TABLE ci_t3;
DROP TABLE ci_t5;
DROP TABLE ci_t6;
DROP TABLE ci_t8;
DROP TABLE ci_src_a;
DROP TABLE ci_src_b;
DROP TABLE ci_bid;
