reproducer.sql

application/sql

Filename: reproducer.sql
Type: application/sql
Part: 0
Message: Query result differences between PostgreSQL 17 vs 16
-- Cleanup preexisting tables
DROP TABLE IF EXISTS rc4;
DROP TABLE IF EXISTS rc3;
DROP TABLE IF EXISTS rc2;
DROP TABLE IF EXISTS rc1;
DROP FUNCTION IF EXISTS rc_select;

-- Generate table 1 + varchar index. Seed with dummy data.
CREATE TABLE rc1(
    id SERIAL PRIMARY KEY,
    description VARCHAR NOT NULL
);

INSERT INTO rc1(description)
SELECT left(md5(random()::text), 10) FROM generate_series(1, 1000) s(i);

CREATE UNIQUE INDEX rc1_desc_uniq ON rc1(description);

-- Generate table 2 references table 1 via varchar index. Seed with dummy data for subset of rows
CREATE TABLE rc2(
    id SERIAL PRIMARY KEY,
    rc1_reference VARCHAR REFERENCES rc1(description) NOT NULL,
    rc2_value VARCHAR NOT NULL
);

WITH ids AS (
    SELECT ((random() * 250)::int % 250) + 1 as id
             FROM generate_series(1, 250) s(i)
)
INSERT INTO rc2(rc1_reference, rc2_value)
SELECT rc1.description,left(md5(random()::text), 10) FROM ids JOIN rc1 ON ids.id = rc1.id;

-- Generate table 3 references table 2, seed with dummy data for subset of rows
CREATE TABLE rc3(
                    id SERIAL PRIMARY KEY,
                    rc2_reference INT REFERENCES rc2(id) NOT NULL,
                    rc3_value VARCHAR
);

WITH ids AS (
    SELECT ((random() * 250)::int % 250) + 1 as id
    FROM generate_series(1, 250) s(i)
)
INSERT INTO rc3(rc2_reference, rc3_value)
SELECT rc2.id,left(md5(random()::text), 10) FROM ids JOIN rc2 ON ids.id = rc2.id;

-- Generate table 4 references table 3, seed with dummy data for subset of rows
CREATE TABLE rc4(
                    id SERIAL PRIMARY KEY,
                    rc3_reference INT REFERENCES rc3(id) NOT NULL,
                    rc4_value VARCHAR
);

INSERT INTO rc4(rc3_reference, rc4_value)
SELECT (i % 250) + 1, left(md5(random()::text), 10) FROM generate_series(1, 5000) s(i);

-- Construct a function for lateral joins between table 3 and table 4, highlight scenarios where we hit the bug
CREATE OR REPLACE FUNCTION rc_select(in_id integer DEFAULT NULL::integer)
    RETURNS TABLE(input_id varchar, id integer, value_desc character varying)
    LANGUAGE 'plpgsql'
AS $BODY$
BEGIN
    RETURN QUERY
        SELECT COALESCE(in_id::varchar, 'BUG HIT'), rc4.id, rc4.rc4_value
        FROM rc4
        WHERE (in_id IS NULL OR in_id = rc4.rc3_reference);
END;
$BODY$;

-- Sample query to hit bug. Dump to CSV/file and query for rows with "BUG HIT".
--
-- SELECT * FROM rc1
--     LEFT JOIN rc2 ON rc2.rc1_reference = rc1.description
--     LEFT JOIN rc3 ON rc2.id = rc3.rc2_reference
--     LEFT JOIN LATERAL rc_select(rc3.id) ON rc3.id IS NOT NULL;