--
-- array_interop.sql
--     Tests for to_icollection (array → icollection) and
--     to_array (icollection → array) conversions, implicit casts,
--     and round-trip behavior.
--

-- ============================================================
-- to_icollection: basic conversions
-- ============================================================

-- int array
DO $$
DECLARE
  v icollection;
BEGIN
  RAISE NOTICE 'Test 1: int array';
  v := to_icollection(ARRAY[10, 20, 30]);
  RAISE NOTICE 'count: %', count(v);
  RAISE NOTICE 'v[1]: %', v[1]::int;
  RAISE NOTICE 'v[2]: %', v[2]::int;
  RAISE NOTICE 'v[3]: %', v[3]::int;
END
$$;

-- text array
DO $$
DECLARE
  v icollection;
BEGIN
  RAISE NOTICE 'Test 2: text array';
  v := to_icollection(ARRAY['a', 'b', 'c']);
  RAISE NOTICE 'count: %', count(v);
  RAISE NOTICE 'v[1]: %', v[1]::text;
  RAISE NOTICE 'v[2]: %', v[2]::text;
  RAISE NOTICE 'v[3]: %', v[3]::text;
END
$$;

-- bigint array
DO $$
DECLARE
  v icollection;
BEGIN
  RAISE NOTICE 'Test 3: bigint array';
  v := to_icollection(ARRAY[100000000000, 200000000000]::bigint[]);
  RAISE NOTICE 'v[1]: %', v[1]::bigint;
  RAISE NOTICE 'v[2]: %', v[2]::bigint;
END
$$;

-- boolean array
DO $$
DECLARE
  v icollection;
BEGIN
  RAISE NOTICE 'Test 4: boolean array';
  v := to_icollection(ARRAY[true, false, true]);
  RAISE NOTICE 'v[1]: %', v[1]::boolean;
  RAISE NOTICE 'v[2]: %', v[2]::boolean;
  RAISE NOTICE 'v[3]: %', v[3]::boolean;
END
$$;

-- float8 array
DO $$
DECLARE
  v icollection;
BEGIN
  RAISE NOTICE 'Test 5: float8 array';
  v := to_icollection(ARRAY[1.1, 2.2, 3.3]::float8[]);
  RAISE NOTICE 'v[1]: %', v[1]::float8;
  RAISE NOTICE 'v[2]: %', v[2]::float8;
  RAISE NOTICE 'v[3]: %', v[3]::float8;
END
$$;

-- ============================================================
-- to_icollection: edge cases
-- ============================================================

-- empty array
DO $$
DECLARE
  v icollection;
BEGIN
  RAISE NOTICE 'Test 6: empty array';
  v := to_icollection(ARRAY[]::int[]);
  RAISE NOTICE 'count: %', count(v);
END
$$;

-- NULL input
DO $$
DECLARE
  v icollection;
BEGIN
  RAISE NOTICE 'Test 7: NULL input';
  v := to_icollection(NULL::int[]);
  RAISE NOTICE 'is null: %', v IS NULL;
END
$$;

-- array with NULL elements
DO $$
DECLARE
  v icollection;
BEGIN
  RAISE NOTICE 'Test 8: array with NULLs';
  v := to_icollection(ARRAY[1, NULL, 3]::int[]);
  RAISE NOTICE 'count: %', count(v);
  RAISE NOTICE 'v[1]: %', v[1]::int;
  RAISE NOTICE 'v[2] is null: %', NOT exist(v, 2) OR v[2]::text IS NULL;
  RAISE NOTICE 'v[3]: %', v[3]::int;
END
$$;

-- single element
DO $$
DECLARE
  v icollection;
BEGIN
  RAISE NOTICE 'Test 9: single element';
  v := to_icollection(ARRAY[42]);
  RAISE NOTICE 'count: %', count(v);
  RAISE NOTICE 'v[1]: %', v[1]::int;
END
$$;

-- multidimensional array rejected
DO $$
DECLARE
  v icollection;
BEGIN
  RAISE NOTICE 'Test 10: multidimensional rejected';
  v := to_icollection(ARRAY[[1,2],[3,4]]);
  RAISE NOTICE 'should not reach here';
EXCEPTION WHEN feature_not_supported THEN
  RAISE NOTICE 'caught: %', SQLERRM;
END
$$;

-- ============================================================
-- to_array: basic conversions
-- ============================================================

-- dense keys, default text[] return
DO $$
DECLARE
  v icollection;
BEGIN
  RAISE NOTICE 'Test 11: to_array dense';
  v := add(v, 1, 10);
  v := add(v, 2, 20);
  v := add(v, 3, 30);
  RAISE NOTICE 'result: %', to_array(v);
END
$$;

-- typed return via anyelement overload
DO $$
DECLARE
  v icollection;
  arr int[];
BEGIN
  RAISE NOTICE 'Test 12: to_array typed';
  v := add(v, 1, 10);
  v := add(v, 2, 20);
  v := add(v, 3, 30);
  arr := to_array(v, 0);
  RAISE NOTICE 'result: %', arr;
END
$$;

-- gaps become NULLs
DO $$
DECLARE
  v icollection;
BEGIN
  RAISE NOTICE 'Test 13: to_array with gaps';
  v := add(v, 1, 100);
  v := add(v, 3, 300);
  RAISE NOTICE 'result: %', to_array(v);
END
$$;

-- empty icollection
DO $$
DECLARE
  v icollection;
BEGIN
  RAISE NOTICE 'Test 14: to_array empty';
  RAISE NOTICE 'result: %', to_array(v);
END
$$;

-- NULL input
SELECT to_array(NULL::icollection);

-- NULL values preserved
DO $$
DECLARE
  v icollection;
BEGIN
  RAISE NOTICE 'Test 15: to_array with null values';
  v := add(v, 1, 10);
  v := add(v, 2, NULL::int);
  v := add(v, 3, 30);
  RAISE NOTICE 'result: %', to_array(v);
END
$$;

-- array lower bound matches min key
DO $$
DECLARE
  v icollection;
  arr text[];
BEGIN
  RAISE NOTICE 'Test 16: to_array preserves key as lower bound';
  v := add(v, 5, 'five');
  v := add(v, 7, 'seven');
  arr := to_array(v);
  RAISE NOTICE 'arr[5]: %', arr[5];
  RAISE NOTICE 'arr[6]: %', arr[6];
  RAISE NOTICE 'arr[7]: %', arr[7];
END
$$;

-- to_array does not mutate iteration order
DO $$
DECLARE
  v icollection;
  arr text[];
BEGIN
  RAISE NOTICE 'Test 17: to_array preserves iteration order';
  v := add(v, 3, 'three');
  v := add(v, 1, 'one');
  v := add(v, 2, 'two');
  v := first(v);
  RAISE NOTICE 'before: first key = %', key(v);
  arr := to_array(v);
  v := first(v);
  RAISE NOTICE 'after:  first key = %', key(v);
END
$$;

-- ============================================================
-- Round-trip tests
-- ============================================================

-- int round-trip
SELECT to_array(to_icollection(ARRAY[1, 2, 3]), 0);

-- text round-trip
SELECT to_array(to_icollection(ARRAY['x', 'y', 'z']), ''::text);

-- round-trip with NULLs
SELECT to_array(to_icollection(ARRAY[10, NULL, 30]::int[]), 0);

-- ============================================================
-- Implicit cast tests
-- ============================================================

-- int[] implicit cast
DO $$
DECLARE
  v icollection;
BEGIN
  RAISE NOTICE 'Test 18: implicit cast int[]';
  v := ARRAY[42, 99];
  RAISE NOTICE 'v[1]: %', v[1]::int;
  RAISE NOTICE 'v[2]: %', v[2]::int;
END
$$;

-- text[] implicit cast
DO $$
DECLARE
  v icollection;
BEGIN
  RAISE NOTICE 'Test 19: implicit cast text[]';
  v := ARRAY['hello', 'world'];
  RAISE NOTICE 'v[1]: %', v[1]::text;
  RAISE NOTICE 'v[2]: %', v[2]::text;
END
$$;

-- bigint[] implicit cast
DO $$
DECLARE
  v icollection;
BEGIN
  RAISE NOTICE 'Test 20: implicit cast bigint[]';
  v := ARRAY[1, 2, 3]::bigint[];
  RAISE NOTICE 'count: %', count(v);
END
$$;

-- ============================================================
-- BULK COLLECT pattern
-- ============================================================

CREATE TEMP TABLE bulk_test(id int, name text);
INSERT INTO bulk_test VALUES (101, 'Alice'), (202, 'Bob'), (303, 'Carol');

-- BULK COLLECT INTO with int column
DO $$
DECLARE
  v icollection;
BEGIN
  RAISE NOTICE 'Test 21: BULK COLLECT int';
  v := to_icollection(ARRAY(SELECT id FROM bulk_test ORDER BY id));
  RAISE NOTICE 'count: %', count(v);
  RAISE NOTICE 'v[1]: %', v[1]::int;
  RAISE NOTICE 'v[2]: %', v[2]::int;
  RAISE NOTICE 'v[3]: %', v[3]::int;
END
$$;

-- BULK COLLECT INTO with text column
DO $$
DECLARE
  v icollection;
BEGIN
  RAISE NOTICE 'Test 22: BULK COLLECT text';
  v := to_icollection(ARRAY(SELECT name FROM bulk_test ORDER BY id));
  RAISE NOTICE 'count: %', count(v);
  RAISE NOTICE 'v[1]: %', v[1]::text;
  RAISE NOTICE 'v[2]: %', v[2]::text;
  RAISE NOTICE 'v[3]: %', v[3]::text;
END
$$;

-- BULK COLLECT round-trip back to array
DO $$
DECLARE
  v icollection;
  arr int[];
BEGIN
  RAISE NOTICE 'Test 23: BULK COLLECT round-trip';
  v := to_icollection(ARRAY(SELECT id FROM bulk_test ORDER BY id));
  arr := to_array(v, 0);
  RAISE NOTICE 'result: %', arr;
END
$$;

DROP TABLE bulk_test;

-- ============================================================
-- Cross-type conversion tests
-- ============================================================

-- date array -> icollection preserves date type
DO $$
DECLARE
  v icollection;
BEGIN
  RAISE NOTICE 'Test 24: date array conversion';
  v := to_icollection(ARRAY['2024-01-01','2024-06-15']::date[]);
  RAISE NOTICE 'value_type: %', value_type(v);
  RAISE NOTICE 'v[1]: %', v[1]::text;
  RAISE NOTICE 'v[2]: %', v[2]::text;
END
$$;

-- date icollection -> access as incompatible type errors
DO $$
DECLARE
  v icollection;
BEGIN
  RAISE NOTICE 'Test 25: date accessed as int errors';
  v := to_icollection(ARRAY['2024-01-01']::date[]);
  RAISE NOTICE 'v[1] as int: %', v[1]::int;
EXCEPTION WHEN others THEN
  RAISE NOTICE 'caught: %', SQLERRM;
END
$$;

-- numeric array -> icollection, access as int errors (lossy)
DO $$
DECLARE
  v icollection;
BEGIN
  RAISE NOTICE 'Test 26: numeric accessed as int errors';
  v := to_icollection(ARRAY[1.5, 2.7]::numeric[]);
  RAISE NOTICE 'value_type: %', value_type(v);
  RAISE NOTICE 'v[1] as int: %', v[1]::int;
EXCEPTION WHEN others THEN
  RAISE NOTICE 'caught: %', SQLERRM;
END
$$;

-- int icollection -> to_array as text[] (widening coercion works)
DO $$
DECLARE
  v icollection;
  arr text[];
BEGIN
  RAISE NOTICE 'Test 27: int icollection to text array';
  v := add(v, 1, 42);
  v := add(v, 2, 99);
  arr := to_array(v, ''::text);
  RAISE NOTICE 'result: %', arr;
END
$$;

-- date round-trip: date[] -> icollection -> date[]
DO $$
DECLARE
  v icollection;
  arr date[];
BEGIN
  RAISE NOTICE 'Test 28: date round-trip';
  v := to_icollection(ARRAY['2024-01-01','2024-06-15']::date[]);
  arr := to_array(v, '2000-01-01'::date);
  RAISE NOTICE 'result: %', arr;
END
$$;

-- int[] assigned to typed icollection('text') errors
DO $$
DECLARE
  v icollection('text');
BEGIN
  RAISE NOTICE 'Test 29: int[] to icollection(text) errors';
  v := ARRAY[1, 2, 3];
  RAISE NOTICE 'should not reach here';
EXCEPTION WHEN datatype_mismatch THEN
  RAISE NOTICE 'caught: %', SQLERRM;
END
$$;

-- timestamp array conversion
DO $$
DECLARE
  v icollection;
BEGIN
  RAISE NOTICE 'Test 30: timestamp array';
  v := to_icollection(ARRAY['2024-01-01 12:00:00','2024-06-15 18:30:00']::timestamp[]);
  RAISE NOTICE 'value_type: %', value_type(v);
  RAISE NOTICE 'v[1]: %', v[1]::text;
  RAISE NOTICE 'v[2]: %', v[2]::text;
END
$$;

-- date[] has no implicit cast (no overload), requires explicit call
DO $$
DECLARE
  v icollection;
BEGIN
  RAISE NOTICE 'Test 31: date[] explicit conversion';
  v := to_icollection(ARRAY['2024-03-15']::date[]);
  RAISE NOTICE 'count: %', count(v);
  RAISE NOTICE 'value_type: %', value_type(v);
END
$$;

-- ============================================================
-- Overflow and boundary tests for to_array
-- ============================================================

-- key exceeding int32 range
DO $$
DECLARE v icollection;
BEGIN
  RAISE NOTICE 'Test 32: key > INT_MAX rejected';
  v := add(v, 3000000000::bigint, 'big');
  RAISE NOTICE 'to_array: %', to_array(v);
EXCEPTION WHEN program_limit_exceeded THEN
  RAISE NOTICE 'caught: %', SQLERRM;
END
$$;

-- negative key below int32 range
DO $$
DECLARE v icollection;
BEGIN
  RAISE NOTICE 'Test 33: key < INT_MIN rejected';
  v := add(v, -3000000000::bigint, 'neg');
  RAISE NOTICE 'to_array: %', to_array(v);
EXCEPTION WHEN program_limit_exceeded THEN
  RAISE NOTICE 'caught: %', SQLERRM;
END
$$;

-- huge sparse range exceeds allocation limit
DO $$
DECLARE v icollection;
BEGIN
  RAISE NOTICE 'Test 34: sparse range rejected';
  v := add(v, 1, 'lo');
  v := add(v, 2000000000, 'hi');
  RAISE NOTICE 'to_array: %', to_array(v);
EXCEPTION WHEN program_limit_exceeded THEN
  RAISE NOTICE 'caught: %', SQLERRM;
END
$$;

-- negative keys within int32 range work
DO $$
DECLARE v icollection;
BEGIN
  RAISE NOTICE 'Test 35: negative keys work';
  v := add(v, -2, 'neg2');
  v := add(v, 0, 'zero');
  v := add(v, 2, 'pos2');
  RAISE NOTICE 'result: %', to_array(v);
END
$$;

-- single element with large valid key
DO $$
DECLARE v icollection;
BEGIN
  RAISE NOTICE 'Test 36: single large key';
  v := add(v, 2000000000, 'big');
  RAISE NOTICE 'result: %', to_array(v);
END
$$;

-- ============================================================
-- Post-conversion mutation tests
-- Verify the icollection built from an array has correct
-- internal metadata by exercising all operations on it.
-- ============================================================

-- add to a converted icollection (tests value_type compatibility)
DO $$
DECLARE
  v icollection;
BEGIN
  RAISE NOTICE 'Test 37: add after from_array';
  v := to_icollection(ARRAY[10, 20, 30]);
  v := add(v, 4, 40);
  RAISE NOTICE 'count: %', count(v);
  RAISE NOTICE 'v[4]: %', v[4]::int;
END
$$;

-- add incompatible type to converted icollection (tests value_type enforcement)
DO $$
DECLARE
  v icollection;
BEGIN
  RAISE NOTICE 'Test 38: add incompatible type errors';
  v := to_icollection(ARRAY[10, 20, 30]);
  v := add(v, 4, 'text_value'::text);
EXCEPTION WHEN datatype_mismatch THEN
  RAISE NOTICE 'caught: %', SQLERRM;
END
$$;

-- delete from converted icollection
DO $$
DECLARE
  v icollection;
BEGIN
  RAISE NOTICE 'Test 39: delete after from_array';
  v := to_icollection(ARRAY[10, 20, 30]);
  v := delete(v, 2);
  RAISE NOTICE 'count: %', count(v);
  RAISE NOTICE 'exist(2): %', exist(v, 2);
  RAISE NOTICE 'v[1]: %', v[1]::int;
  RAISE NOTICE 'v[3]: %', v[3]::int;
END
$$;

-- delete_all from converted icollection
DO $$
DECLARE
  v icollection;
BEGIN
  RAISE NOTICE 'Test 40: delete_all after from_array';
  v := to_icollection(ARRAY[10, 20, 30]);
  v := delete(v);
  RAISE NOTICE 'count: %', count(v);
END
$$;

-- sort converted icollection
DO $$
DECLARE
  v icollection;
BEGIN
  RAISE NOTICE 'Test 41: sort after from_array';
  v := to_icollection(ARRAY[30, 10, 20]);
  v := sort(v);
  RAISE NOTICE 'first key: %', key(v);
  v := last(v);
  RAISE NOTICE 'last key: %', key(v);
END
$$;

-- copy converted icollection
DO $$
DECLARE
  v icollection;
  c icollection;
BEGIN
  RAISE NOTICE 'Test 42: copy after from_array';
  v := to_icollection(ARRAY[10, 20, 30]);
  c := copy(v);
  RAISE NOTICE 'copy count: %', count(c);
  RAISE NOTICE 'c[1]: %', c[1]::int;
  RAISE NOTICE 'c[3]: %', c[3]::int;
  -- mutate original, copy should be independent
  v := delete(v, 1);
  RAISE NOTICE 'orig count: %', count(v);
  RAISE NOTICE 'copy count: %', count(c);
END
$$;

-- full iteration over converted icollection
DO $$
DECLARE
  v icollection;
  k bigint;
BEGIN
  RAISE NOTICE 'Test 43: full iteration after from_array';
  v := to_icollection(ARRAY[100, 200, 300]);
  k := first_key(v);
  WHILE k IS NOT NULL LOOP
    RAISE NOTICE 'k=% v=%', k, find(v, k)::int;
    k := next_key(v, k);
  END LOOP;
END
$$;

-- iterator-based iteration
DO $$
DECLARE
  v icollection;
BEGIN
  RAISE NOTICE 'Test 44: iterator after from_array';
  v := to_icollection(ARRAY['alpha', 'bravo', 'charlie']);
  v := first(v);
  WHILE NOT isnull(v) LOOP
    RAISE NOTICE 'k=% v=%', key(v), value(v);
    v := next(v);
  END LOOP;
END
$$;

-- value_type preserved correctly
DO $$
DECLARE
  v icollection;
BEGIN
  RAISE NOTICE 'Test 45: value_type after from_array';
  v := to_icollection(ARRAY[1.5, 2.5]::numeric[]);
  RAISE NOTICE 'type: %', value_type(v);
  v := to_icollection(ARRAY[true, false]);
  RAISE NOTICE 'type: %', value_type(v);
  v := to_icollection(ARRAY['2024-01-01']::date[]);
  RAISE NOTICE 'type: %', value_type(v);
END
$$;

-- to_table on converted icollection (tests flatten path)
DO $$
DECLARE
  v icollection;
  r record;
BEGIN
  RAISE NOTICE 'Test 46: to_table after from_array';
  v := to_icollection(ARRAY[10, 20, 30]);
  FOR r IN SELECT * FROM to_table(v, 0) LOOP
    RAISE NOTICE 'k=% v=%', r.key, r.value;
  END LOOP;
END
$$;

-- persist to table and read back (tests flatten/expand round-trip)
DO $$
DECLARE
  v icollection;
  v2 icollection;
BEGIN
  RAISE NOTICE 'Test 47: persist after from_array';
  v := to_icollection(ARRAY[10, 20, 30]);
  CREATE TEMP TABLE _test_persist(data icollection) ON COMMIT DROP;
  INSERT INTO _test_persist VALUES (v);
  SELECT data INTO v2 FROM _test_persist;
  RAISE NOTICE 'count: %', count(v2);
  RAISE NOTICE 'v2[1]: %', v2[1]::int;
  RAISE NOTICE 'v2[2]: %', v2[2]::int;
  RAISE NOTICE 'v2[3]: %', v2[3]::int;
  RAISE NOTICE 'type: %', value_type(v2);
  DROP TABLE _test_persist;
END
$$;

-- text array: persist and read back (variable-length type)
DO $$
DECLARE
  v icollection;
  v2 icollection;
BEGIN
  RAISE NOTICE 'Test 48: persist text after from_array';
  v := to_icollection(ARRAY['hello', 'world']);
  CREATE TEMP TABLE _test_persist2(data icollection) ON COMMIT DROP;
  INSERT INTO _test_persist2 VALUES (v);
  SELECT data INTO v2 FROM _test_persist2;
  RAISE NOTICE 'count: %', count(v2);
  RAISE NOTICE 'v2[1]: %', v2[1]::text;
  RAISE NOTICE 'v2[2]: %', v2[2]::text;
  DROP TABLE _test_persist2;
END
$$;

-- convert from array with NULLs, then add/delete/iterate
DO $$
DECLARE
  v icollection;
BEGIN
  RAISE NOTICE 'Test 49: mutate after from_array with NULLs';
  v := to_icollection(ARRAY[1, NULL, 3]::int[]);
  v := add(v, 2, 22);
  RAISE NOTICE 'v[2] after overwrite: %', v[2]::int;
  v := delete(v, 1);
  RAISE NOTICE 'count: %', count(v);
  v := first(v);
  WHILE NOT isnull(v) LOOP
    RAISE NOTICE 'k=% v=%', key(v), value(v);
    v := next(v);
  END LOOP;
END
$$;

-- delete_range on converted icollection
DO $$
DECLARE
  v icollection;
BEGIN
  RAISE NOTICE 'Test 50: delete_range after from_array';
  v := to_icollection(ARRAY[10, 20, 30, 40, 50]);
  v := delete(v, 2::bigint, 4::bigint);
  RAISE NOTICE 'count: %', count(v);
  RAISE NOTICE 'v[1]: %', v[1]::int;
  RAISE NOTICE 'v[5]: %', v[5]::int;
END
$$;

-- persist with NULL values (flatten crash fix 6627a69)
DO $$
DECLARE
  v icollection;
  v2 icollection;
BEGIN
  RAISE NOTICE 'Test 51: persist with NULLs after from_array';
  v := to_icollection(ARRAY[10, NULL, 30]::int[]);
  CREATE TEMP TABLE _test_persist3(data icollection) ON COMMIT DROP;
  INSERT INTO _test_persist3 VALUES (v);
  SELECT data INTO v2 FROM _test_persist3;
  RAISE NOTICE 'count: %', count(v2);
  RAISE NOTICE 'v2[1]: %', v2[1]::int;
  RAISE NOTICE 'v2[2] null: %', v2[2]::text IS NULL;
  RAISE NOTICE 'v2[3]: %', v2[3]::int;
  DROP TABLE _test_persist3;
END
$$;

-- subscript assign on converted icollection (fix fa3d563)
DO $$
DECLARE
  v icollection('int');
BEGIN
  RAISE NOTICE 'Test 52: subscript assign after from_array';
  v := to_icollection(ARRAY[10, 20, 30]);
  v[2] := 99;
  RAISE NOTICE 'v[2]: %', v[2]::int;
  v[4] := 40;
  RAISE NOTICE 'v[4]: %', v[4]::int;
  RAISE NOTICE 'count: %', count(v);
END
$$;

-- subscript assign wrong type on converted icollection
DO $$
DECLARE
  v icollection('int');
BEGIN
  RAISE NOTICE 'Test 53: subscript assign wrong type errors';
  v := to_icollection(ARRAY[10, 20, 30]);
  v[1] := 'not_an_int'::text;
EXCEPTION WHEN others THEN
  RAISE NOTICE 'caught: %', SQLERRM;
END
$$;

-- INOUT parameter with converted icollection (fix 6a046e9)
CREATE OR REPLACE FUNCTION _test_inout(INOUT v icollection, key bigint, val int)
AS $$
BEGIN
  v := add(v, key, val);
END;
$$ LANGUAGE plpgsql;

DO $$
DECLARE
  v icollection;
BEGIN
  RAISE NOTICE 'Test 54: INOUT with from_array';
  v := to_icollection(ARRAY[10, 20, 30]);
  v := _test_inout(v, 4, 40);
  v := _test_inout(v, 5, 50);
  RAISE NOTICE 'count: %', count(v);
  RAISE NOTICE 'v[4]: %', v[4]::int;
  RAISE NOTICE 'v[5]: %', v[5]::int;
END
$$;

-- INOUT stress: multiple calls to exercise expanded object copy (fix 6a046e9)
DO $$
DECLARE
  v icollection;
  i int;
BEGIN
  RAISE NOTICE 'Test 55: INOUT stress with from_array';
  v := to_icollection(ARRAY[1, 2, 3]);
  FOR i IN 4..50 LOOP
    v := _test_inout(v, i, i * 10);
  END LOOP;
  RAISE NOTICE 'count: %', count(v);
  RAISE NOTICE 'v[50]: %', v[50]::int;
END
$$;

DROP FUNCTION _test_inout;

-- ============================================================
-- to_array subscript access tests (crash fix: type mismatch
-- between declared text[] return and actual element type)
-- ============================================================

-- int array: subscript access on single-arg to_array (returns text[])
SELECT (to_array(to_icollection(ARRAY[12,0,0,0]::int[])))[1];
SELECT (to_array(to_icollection(ARRAY[12,0,0,0]::int[])))[2];
SELECT (to_array(to_icollection(ARRAY[12,0,0,0]::int[])))[4];

-- int array: subscript on polymorphic overload (returns int[])
SELECT (to_array(to_icollection(ARRAY[12,0,0,0]::int[]), 0))[1];
SELECT (to_array(to_icollection(ARRAY[12,0,0,0]::int[]), 0))[2];

-- bigint array: subscript on single-arg to_array
SELECT (to_array(to_icollection(ARRAY[100000000000, 200000000000]::bigint[])))[1];

-- bigint array: subscript on polymorphic overload
SELECT (to_array(to_icollection(ARRAY[100000000000, 200000000000]::bigint[]), 0::bigint))[2];

-- boolean: subscript on single-arg to_array
SELECT (to_array(to_icollection(ARRAY[true, false, true])))[2];

-- float8: subscript on single-arg to_array
SELECT (to_array(to_icollection(ARRAY[1.1, 2.2, 3.3]::float8[])))[3];

-- text array: subscript (no conversion needed, already text)
SELECT (to_array(to_icollection(ARRAY['hello','world'])))[1];

-- verify pg_typeof for single-arg overload subscript is text
SELECT pg_typeof((to_array(to_icollection(ARRAY[42,99]::int[])))[1]);

-- verify pg_typeof for polymorphic overload subscript is integer
SELECT pg_typeof((to_array(to_icollection(ARRAY[42,99]::int[]), 0))[1]);

-- subscript with gaps (NULL elements from sparse keys)
DO $$
DECLARE
  v icollection;
  arr text[];
BEGIN
  RAISE NOTICE 'Test 56: subscript with gaps';
  v := add(v, 1, 100);
  v := add(v, 3, 300);
  arr := to_array(v);
  RAISE NOTICE 'arr[1]: %', arr[1];
  RAISE NOTICE 'arr[2]: %', arr[2];
  RAISE NOTICE 'arr[3]: %', arr[3];
END
$$;

-- subscript on to_array with zero values (original crash case)
DO $$
DECLARE
  arr text[];
BEGIN
  RAISE NOTICE 'Test 57: subscript zero values';
  arr := to_array(to_icollection(ARRAY[0,0,0]::int[]));
  RAISE NOTICE 'arr[1]: %', arr[1];
  RAISE NOTICE 'arr[2]: %', arr[2];
  RAISE NOTICE 'arr[3]: %', arr[3];
END
$$;

-- subscript on polymorphic overload with zero values
DO $$
DECLARE
  arr int[];
BEGIN
  RAISE NOTICE 'Test 58: polymorphic subscript zero values';
  arr := to_array(to_icollection(ARRAY[0,0,0]::int[]), 0);
  RAISE NOTICE 'arr[1]: %', arr[1];
  RAISE NOTICE 'arr[2]: %', arr[2];
  RAISE NOTICE 'arr[3]: %', arr[3];
END
$$;

-- date: subscript on single-arg to_array (must convert to text)
SELECT (to_array(to_icollection(ARRAY['2024-01-01','2024-06-15']::date[])))[1];

-- numeric: subscript on single-arg to_array
SELECT (to_array(to_icollection(ARRAY[3.14159, 2.71828]::numeric[])))[2];

-- ============================================================
-- to_array: typed NULL as the element-type carrier (no STRICT)
-- ============================================================

-- A typed NULL selects the array element type without a real value
DO $$
DECLARE
  v icollection;
  arr int[];
BEGIN
  RAISE NOTICE 'Test 59: to_array typed NULL carrier';
  v := add(v, 1, 10);
  v := add(v, 2, 20);
  v := add(v, 3, 30);
  arr := to_array(v, NULL::int);
  RAISE NOTICE 'result: %', arr;
END
$$;

-- Works for a complex composite element type without building a dummy value
CREATE TYPE array_interop_rec AS (a int, b text);
DO $$
DECLARE
  v icollection;
  arr array_interop_rec[];
BEGIN
  RAISE NOTICE 'Test 60: to_array typed NULL for composite element';
  v := add(v, 1, ROW(10, 'ten')::array_interop_rec);
  v := add(v, 2, ROW(20, 'twenty')::array_interop_rec);
  arr := to_array(v, NULL::array_interop_rec);
  RAISE NOTICE 'result: %', arr;
END
$$;
DROP TYPE array_interop_rec;

-- A typed NULL against a NULL collection still returns NULL (arg 0 guard)
SELECT to_array(NULL::icollection, NULL::int) IS NULL AS is_null;
