CREATE SERVER arr_svr FOREIGN DATA WRAPPER clickhouse_fdw OPTIONS(dbname 'arr_test');
CREATE USER MAPPING FOR CURRENT_USER SERVER arr_svr;

SELECT clickhouse_raw_query('DROP DATABASE IF EXISTS arr_test');
SELECT clickhouse_raw_query('CREATE DATABASE arr_test');
SELECT clickhouse_raw_query($$
    CREATE TABLE arr_test.t1 (
        id   Int32,
        vals Array(Int32),
        tags Array(String),
        list String
    ) ENGINE = MergeTree ORDER BY id
$$);
SELECT clickhouse_raw_query($$
    INSERT INTO arr_test.t1 VALUES
        (1, [10,20,30], ['a','b','c'], 'aa-bb-cc'),
        (2, [40,50],    ['d','e'], 'x//z'),
        (3, [60],       ['f'], 'Edit -> Insert -> Line Break')
$$);

CREATE SCHEMA arr_test;
IMPORT FOREIGN SCHEMA arr_test FROM SERVER arr_svr INTO arr_test;
SET search_path = arr_test, public;

-- array_cat → arrayConcat
EXPLAIN (VERBOSE, COSTS OFF)
SELECT * FROM t1 WHERE array_cat(vals, ARRAY[99]) = ARRAY[10,20,30,99];
SELECT * FROM t1 WHERE array_cat(vals, ARRAY[99]) = ARRAY[10,20,30,99];

-- array_append → arrayPushBack
EXPLAIN (VERBOSE, COSTS OFF)
SELECT * FROM t1 WHERE array_append(vals, 99) = ARRAY[10,20,30,99];
SELECT * FROM t1 WHERE array_append(vals, 99) = ARRAY[10,20,30,99];

-- array_remove → arrayRemove (CH 26+, EXPLAIN only)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT * FROM t1 WHERE array_remove(vals, 20) = ARRAY[10,30];

\unset ECHO
-- Use a DO block to test arrayRemove on 26+ only.
DO $$
DECLARE
	chv int[] := regexp_matches(clickhouse_raw_query('SELECT version()'), '^(\d+)\.(\d+)')::int[];
    result record;
BEGIN
    IF chv[1] >= 26 THEN
		FOR result IN SELECT * FROM t1 WHERE array_remove(vals, 20) = ARRAY[10,30] LOOP
			RAISE NOTICE '%', result;
		END LOOP;
    ELSE
		-- Fake it on earlier versions.
		RAISE NOTICE '(1,"{10,20,30}","{a,b,c}",aa-bb-cc)';
    END IF;
END;
$$;
\set ECHO all

-- array_to_string → arrayStringConcat
EXPLAIN (VERBOSE, COSTS OFF)
SELECT * FROM t1 WHERE array_to_string(tags, ',') = 'a,b,c';
SELECT * FROM t1 WHERE array_to_string(tags, ',') = 'a,b,c';

-- cardinality → length
EXPLAIN (VERBOSE, COSTS OFF)
SELECT * FROM t1 WHERE cardinality(vals) = 3;
SELECT * FROM t1 WHERE cardinality(vals) = 3;

-- array_position → indexOf
EXPLAIN (VERBOSE, COSTS OFF)
SELECT * FROM t1 WHERE array_position(vals, 20) = 2;
SELECT * FROM t1 WHERE array_position(vals, 20) = 2;

-- array_length → length (drops dimension arg)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT * FROM t1 WHERE array_length(vals, 1) = 2;
SELECT * FROM t1 WHERE array_length(vals, 1) = 2;

-- array_prepend → arrayPushFront (args reversed)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT * FROM t1 WHERE array_prepend(99, vals) = ARRAY[99,10,20,30];
SELECT * FROM t1 WHERE array_prepend(99, vals) = ARRAY[99,10,20,30];

-- string_to_array → splitByString
EXPLAIN (VERBOSE, COSTS OFF)
SELECT * FROM t1 WHERE string_to_array(list, '-') = ARRAY['aa','bb','cc'];
SELECT * FROM t1 WHERE string_to_array(list, '-') = ARRAY['aa','bb','cc'];
EXPLAIN (VERBOSE, COSTS OFF)
SELECT * FROM t1 WHERE string_to_array(list, ' -> ') = ARRAY['aa','bb','cc'];
SELECT * FROM t1 WHERE string_to_array(list, ' -> ') = ARRAY['Edit','Insert', 'Line Break'];

-- split_part → splitByString
EXPLAIN (VERBOSE, COSTS OFF)
SELECT * FROM t1 WHERE split_part(list, '-', 2) = 'bb';
SELECT * FROM t1 WHERE split_part(list, '-', 2) = 'bb';
EXPLAIN (VERBOSE, COSTS OFF)
SELECT * FROM t1 WHERE split_part(list, '-', -1) = 'cc';
SELECT * FROM t1 WHERE split_part(list, '-', -1) = 'cc';

\unset ECHO
-- Use DO to test functions available in Postgres 14+
-- trim_array → arrayResize(arr, length(arr) - n)
DO $$
DECLARE
    rec record;
	output JSONB;
BEGIN
    IF current_setting('server_version_num')::int >= 140000 THEN
        EXECUTE 'EXPLAIN (VERBOSE, FORMAT JSON) SELECT * FROM t1 WHERE trim_array(vals, 1) = ARRAY[10,20]' INTO output;
        RAISE NOTICE 'trim_array PUSHED DOWN: %', jsonb_path_query(
            output, '$[0].Plan'
        )->>'Remote SQL' = 'SELECT id, vals, tags, list FROM arr_test.t1 WHERE ((arrayResize(vals, length(vals) - 1) = [10,20]))';
        FOR rec IN EXECUTE 'SELECT * FROM t1 WHERE trim_array(vals, 1) = ARRAY[10,20]' LOOP
            RAISE NOTICE '%', rec;
        END LOOP;
    ELSE
        -- Fake it on earlier versions.
        RAISE NOTICE 'trim_array PUSHED DOWN: t';
        RAISE NOTICE '(1,"{10,20,30}","{a,b,c}",aa-bb-cc)';
    END IF;
END;
$$;

-- Use DO to test functions available in Postgres 18+
DO $$
DECLARE
    rec record;
    tc jsonb;
    tests jsonb[] := ARRAY[
        -- PG18+: array_reverse → arrayReverse
        $_${
          "func": "array_reverse",
          "where": "array_reverse(vals) = ARRAY[30,20,10]",
          "push": "((arrayReverse(vals) = [30,20,10]))"
        }$_$,
        -- PG18+: array_sort → arraySort
        $_${
          "func": "array_sort",
          "where": "array_sort(vals, true) = vals",
          "push": "((arraySort(vals) = vals))"
        }$_$,
        -- PG18+ unshippable: 'array_sort(vals, dynamic) = ARRAY[30,20,10]'
        -- Keep before array_sort(vals, const) to ensure function names not
        -- replaced.
        $_${
          "func": "array_sort(x, dynamic)",
          "where": "array_sort(vals, (select true)) = ARRAY[30,20,10]"
        }$_$,
        -- PG18+ 'array_sort(vals, const) = ARRAY[30,20,10]'
        $_${
          "func": "array_sort(x, true)",
          "where": "array_sort(vals, true) = ARRAY[30,20,10]",
          "push": "((arrayReverseSort(vals) = [30,20,10]))"
        }$_$,
        -- PG18+ unshippable: 'array_sort(vals, true, true) = ARRAY[30,20,10]'
        $_${
          "func": "array_sort(x, true, true)",
          "where": "array_sort(vals, true, true) = ARRAY[30,20,10]"
        }$_$
    ];
	output JSONB;
BEGIN
    IF current_setting('server_version_num')::int >= 180000 THEN
        FOREACH tc IN ARRAY tests LOOP
            EXECUTE format('EXPLAIN (VERBOSE, FORMAT JSON) SELECT * FROM t1 WHERE %s', tc->>'where') INTO output;
            If tc ? 'push' THEN
                RAISE NOTICE '% PUSHED DOWN: %', tc->>'func', jsonb_path_query(
                    output, '$[0].Plan'
                )->>'Remote SQL' = format('SELECT id, vals, tags, list FROM arr_test.t1 WHERE %s', tc->>'push');
            ELSE
                RAISE NOTICE '% NOT PUSHED DOWN: %', tc->>'func', jsonb_path_query(
                    output, '$[0].Plan'
                )->>'Remote SQL' = 'SELECT id, vals, tags, list FROM arr_test.t1';
            END IF;
            FOR rec IN EXECUTE format('SELECT * FROM t1 WHERE %s', tc->>'where') LOOP
                RAISE NOTICE '%', rec;
            END LOOP;
        END LOOP;
    ELSE
        -- Fake it for earlier versions.
        RAISE NOTICE 'array_reverse PUSHED DOWN: t';
        RAISE NOTICE '(1,"{10,20,30}","{a,b,c}",aa-bb-cc)';
        RAISE NOTICE 'array_sort PUSHED DOWN: f';
        RAISE NOTICE '(3,{60},{f},"Edit -> Insert -> Line Break")';
        RAISE NOTICE 'array_sort(x, dynamic) NOT PUSHED DOWN: t';
        RAISE NOTICE '(1,"{10,20,30}","{a,b,c}",aa-bb-cc)';
        RAISE NOTICE 'array_sort(x, true) PUSHED DOWN: t';
        RAISE NOTICE '(1,"{10,20,30}","{a,b,c}",aa-bb-cc)';
        RAISE NOTICE 'array_sort(x, true, true) NOT PUSHED DOWN: t';
        RAISE NOTICE '(1,"{10,20,30}","{a,b,c}",aa-bb-cc)';
    END IF;
END;
$$;
\set ECHO all

-- Operators: @> → hasAll
EXPLAIN (VERBOSE, COSTS OFF)
SELECT * FROM t1 WHERE vals @> ARRAY[10];
SELECT * FROM t1 WHERE vals @> ARRAY[10] ORDER BY id;

-- Operators: <@ → hasAll (reversed)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT * FROM t1 WHERE vals <@ ARRAY[10,20,30];
SELECT * FROM t1 WHERE vals <@ ARRAY[10,20,30] ORDER BY id;

-- Operators: && → hasAny
EXPLAIN (VERBOSE, COSTS OFF)
SELECT * FROM t1 WHERE vals && ARRAY[20,40];
SELECT * FROM t1 WHERE vals && ARRAY[20,40] ORDER BY id;

-- Subscript pushdown
EXPLAIN (VERBOSE, COSTS OFF)
SELECT * FROM t1 WHERE vals[1] = 10;
SELECT * FROM t1 WHERE vals[1] = 10;

-- Slicing pushdown
EXPLAIN (VERBOSE, COSTS OFF)
SELECT * FROM t1 WHERE vals[1:2] = ARRAY[10,20];
SELECT * FROM t1 WHERE vals[1:2] = ARRAY[10,20];

EXPLAIN (VERBOSE, COSTS OFF)
SELECT * FROM t1 WHERE vals[:2] = ARRAY[10,20];
SELECT * FROM t1 WHERE vals[:2] = ARRAY[10,20];

EXPLAIN (VERBOSE, COSTS OFF)
SELECT * FROM t1 WHERE vals[2:] = ARRAY[20,30];
SELECT * FROM t1 WHERE vals[2:] = ARRAY[20,30];

EXPLAIN (VERBOSE, COSTS OFF)
SELECT * FROM t1 WHERE vals[:] = ARRAY[10,20,30];
SELECT * FROM t1 WHERE vals[:] = ARRAY[10,20,30];

-- Unshippable (function NOT in Remote SQL)
EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM t1 WHERE array_dims(vals) = '[1:3]';
EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM t1 WHERE array_ndims(vals) = 1;
EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM t1 WHERE array_lower(vals, 1) = 1;
EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM t1 WHERE array_upper(vals, 1) = 3;
EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM t1 WHERE array_replace(vals, 20, 99) = ARRAY[10,99,30];
EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM t1 WHERE array_positions(vals, 20) = ARRAY[2];
EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM t1 WHERE array_to_string(vals, ',', '*') = '10,20,30';
EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM t1 WHERE array_fill(7, ARRAY[2], vals) = '[60:61]={7,7}'::int[];
EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM t1 WHERE string_to_array(list, '/', 'nil') = ARRAY['x','','z'];

DROP USER MAPPING FOR CURRENT_USER SERVER arr_svr;
SELECT clickhouse_raw_query('DROP DATABASE arr_test');
DROP SERVER arr_svr CASCADE;
