If user inserts a new row which has a text or jsonb column with value "null" as string, (i.e. not actual NULL), it is deserialized as actual NULL, effectively corrupting the data.
Steps to reproduce:
Before clone --follow:
During clone --follow:
INSERT INTO t(c) VALUES("null");
On source:
SELECT * FROM t;
c
------
null
(1 row)
On target:
SELECT * FROM t;
c
------
(1 row)
The test_decoding output seems correct:
{"action":"I","xid":"0","lsn":"0/5E0000A8","timestamp":"2025-10-09 20:00:59.240677+0000","message":"table public.t: INSERT: c[text]:'null'"}
For actual NULL, the test_decoding output is as follows:
The test_decoding output seems correct:
{"action":"I","xid":"0","lsn":"0/5E0000A8","timestamp":"2025-10-09 20:00:59.240677+0000","message":"table public.t: INSERT: c[text]:null"}
Howewe, they both generate the same SQL:
PREPARE a75e754c AS INSERT INTO public.t (c) overriding system value VALUES ($1);
EXECUTE a75e754c[null];
It seems the issue is combination of these two pieces of code:
- First we ignore the quotes while reading the test_decoding output here,
- Then we set isNull to true if the value we read equals to "null" here
If user inserts a new row which has a text or jsonb column with value "null" as string, (i.e. not actual
NULL), it is deserialized as actualNULL, effectively corrupting the data.Steps to reproduce:
Before
clone --follow:During
clone --follow:On source:
On target:
The test_decoding output seems correct:
For actual
NULL, the test_decoding output is as follows:Howewe, they both generate the same SQL:
It seems the issue is combination of these two pieces of code: