\set ECHO none
\pset format unaligned

-- HAVING on array_agg() against a constant array: the general possible-worlds
-- pipeline (no aggregate-specific optimization), with elements compared as text
-- so any element type works. Exact probabilities, all atoms independent at
-- p = 0.5:  A = {1='a', 2='b'}, B = {3='c'}.

CREATE TABLE haa(g text, v int, nm text);
INSERT INTO haa VALUES ('A',1,'a'), ('A',2,'b'), ('B',3,'c');
SELECT add_provenance('haa');
DO $$ BEGIN PERFORM set_prob(provenance(), 0.5) FROM haa; END $$;

-- array_agg(v ORDER BY v) = ARRAY[1,2]: both A-rows present.    A 0.25  B 0
CREATE TABLE r1 AS SELECT g, round(probability_evaluate(provenance())::numeric,4) AS p
  FROM haa GROUP BY g HAVING array_agg(v ORDER BY v) = ARRAY[1,2];
SELECT remove_provenance('r1'); SELECT 'int =[1,2]' AS q, g, p FROM r1 ORDER BY g;

-- = ARRAY[1]: row v=1 present, v=2 absent.                      A 0.25  B 0
CREATE TABLE r2 AS SELECT g, round(probability_evaluate(provenance())::numeric,4) AS p
  FROM haa GROUP BY g HAVING array_agg(v ORDER BY v) = ARRAY[1];
SELECT remove_provenance('r2'); SELECT 'int =[1]' AS q, g, p FROM r2 ORDER BY g;

-- <> ARRAY[1,2]: any non-empty world that is not exactly [1,2]. A 0.5   B 0.5
CREATE TABLE r3 AS SELECT g, round(probability_evaluate(provenance())::numeric,4) AS p
  FROM haa GROUP BY g HAVING array_agg(v ORDER BY v) <> ARRAY[1,2];
SELECT remove_provenance('r3'); SELECT 'int <>[1,2]' AS q, g, p FROM r3 ORDER BY g;

-- text elements compared the same way.                          A 0.25  B 0
CREATE TABLE r4 AS SELECT g, round(probability_evaluate(provenance())::numeric,4) AS p
  FROM haa GROUP BY g HAVING array_agg(nm ORDER BY nm) = ARRAY['a','b'];
SELECT remove_provenance('r4'); SELECT 'text =[a,b]' AS q, g, p FROM r4 ORDER BY g;

-- ORDER BY a different column, descending: the comparison must follow the
-- aggregate's ORDER BY, not the plan's row order (which only coincides when
-- PostgreSQL >= 16 pre-sorts the ordered-aggregate input).      A 0.25  B 0
CREATE TABLE r8 AS SELECT g, round(probability_evaluate(provenance())::numeric,4) AS p
  FROM haa GROUP BY g HAVING array_agg(v ORDER BY nm DESC) = ARRAY[2,1];
SELECT remove_provenance('r8'); SELECT 'int desc=[2,1]' AS q, g, p FROM r8 ORDER BY g;

-- Boolean elements: PostgreSQL renders a scalar bool as 'true'/'false' but a
-- bool array element as 't'/'f'; the array_agg comparison reconciles the two so
-- the value-as-text match works.  hb: A = {true, false}, B = {true}, p = 0.5.
CREATE TABLE hb(g text, flag boolean);
INSERT INTO hb VALUES ('A', true), ('A', false), ('B', true);
SELECT add_provenance('hb');
DO $$ BEGIN PERFORM set_prob(provenance(), 0.5) FROM hb; END $$;

-- = ARRAY[true]: only the true row present.                      A 0.25  B 0.5
CREATE TABLE r5 AS SELECT g, round(probability_evaluate(provenance())::numeric,4) AS p
  FROM hb GROUP BY g HAVING array_agg(flag ORDER BY flag) = ARRAY[true];
SELECT remove_provenance('r5'); SELECT 'bool =[t]' AS q, g, p FROM r5 ORDER BY g;

-- = ARRAY[false,true]: both A-rows present (ordered).            A 0.25  B 0
CREATE TABLE r6 AS SELECT g, round(probability_evaluate(provenance())::numeric,4) AS p
  FROM hb GROUP BY g HAVING array_agg(flag ORDER BY flag) = ARRAY[false,true];
SELECT remove_provenance('r6'); SELECT 'bool =[f,t]' AS q, g, p FROM r6 ORDER BY g;

-- <> ARRAY[true]: non-empty and not exactly [true].              A 0.5   B 0
CREATE TABLE r7 AS SELECT g, round(probability_evaluate(provenance())::numeric,4) AS p
  FROM hb GROUP BY g HAVING array_agg(flag ORDER BY flag) <> ARRAY[true];
SELECT remove_provenance('r7'); SELECT 'bool <>[t]' AS q, g, p FROM r7 ORDER BY g;

DROP TABLE r1; DROP TABLE r2; DROP TABLE r3; DROP TABLE r4;
DROP TABLE r5; DROP TABLE r6; DROP TABLE r7; DROP TABLE r8;
DROP TABLE haa; DROP TABLE hb;
