Skip to content

Commit effcaa4

Browse files
committed
Fix null-pointer-deref crash while doing COPY IN with check constraints.
In commit bf7ca15 I introduced an assumption that an RTE referenced by a whole-row Var must have a valid eref field. This is false for RTEs constructed by DoCopy, and there are other places taking similar shortcuts. Perhaps we should make all those places go through addRangeTableEntryForRelation or its siblings instead of having ad-hoc logic, but the most reliable fix seems to be to make the new code in ExecEvalWholeRowVar cope if there's no eref. We can reasonably assume that there's no need to insert column aliases if no aliases were provided. Add a regression test case covering this, and also verifying that a sane column name is in fact available in this situation. Although the known case only crashes in 9.4 and HEAD, it seems prudent to back-patch the code change to 9.2, since all the ingredients for a similar failure exist in the variant patch applied to 9.3 and 9.2. Per report from Jean-Pierre Pelletier.
1 parent 5202944 commit effcaa4

File tree

3 files changed

+57
-2
lines changed

3 files changed

+57
-2
lines changed

src/backend/executor/execQual.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -898,7 +898,9 @@ ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext,
898898
* If we can't locate the RTE, assume the column names we've got are OK.
899899
* (As of this writing, the only cases where we can't locate the RTE are
900900
* in execution of trigger WHEN clauses, and then the Var will have the
901-
* trigger's relation's rowtype, so its names are fine.)
901+
* trigger's relation's rowtype, so its names are fine.) Also, if the
902+
* creator of the RTE didn't bother to fill in an eref field, assume our
903+
* column names are OK. (This happens in COPY, and perhaps other places.)
902904
*/
903905
if (variable->vartype == RECORDOID &&
904906
econtext->ecxt_estate &&
@@ -907,7 +909,8 @@ ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext,
907909
RangeTblEntry *rte = rt_fetch(variable->varno,
908910
econtext->ecxt_estate->es_range_table);
909911

910-
ExecTypeSetColNames(output_tupdesc, rte->eref->colnames);
912+
if (rte->eref)
913+
ExecTypeSetColNames(output_tupdesc, rte->eref->colnames);
911914
}
912915

913916
/* Bless the tupdesc if needed, and save it in the execution state */

src/test/regress/expected/copy2.out

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,41 @@ SELECT * FROM testnull;
255255
|
256256
(4 rows)
257257

258+
-- test case with whole-row Var in a check constraint
259+
create table check_con_tbl (f1 int);
260+
create function check_con_function(check_con_tbl) returns bool as $$
261+
begin
262+
raise notice 'input = %', row_to_json($1);
263+
return $1.f1 > 0;
264+
end $$ language plpgsql immutable;
265+
alter table check_con_tbl add check (check_con_function(check_con_tbl.*));
266+
\d+ check_con_tbl
267+
Table "public.check_con_tbl"
268+
Column | Type | Modifiers | Storage | Stats target | Description
269+
--------+---------+-----------+---------+--------------+-------------
270+
f1 | integer | | plain | |
271+
Check constraints:
272+
"check_con_tbl_check" CHECK (check_con_function(check_con_tbl.*))
273+
Has OIDs: no
274+
275+
copy check_con_tbl from stdin;
276+
NOTICE: input = {"f1":1}
277+
CONTEXT: COPY check_con_tbl, line 1: "1"
278+
NOTICE: input = {"f1":null}
279+
CONTEXT: COPY check_con_tbl, line 2: "\N"
280+
copy check_con_tbl from stdin;
281+
NOTICE: input = {"f1":0}
282+
CONTEXT: COPY check_con_tbl, line 1: "0"
283+
ERROR: new row for relation "check_con_tbl" violates check constraint "check_con_tbl_check"
284+
DETAIL: Failing row contains (0).
285+
CONTEXT: COPY check_con_tbl, line 1: "0"
286+
select * from check_con_tbl;
287+
f1
288+
----
289+
1
290+
291+
(2 rows)
292+
258293
DROP TABLE x, y;
259294
DROP FUNCTION fn_x_before();
260295
DROP FUNCTION fn_x_after();

src/test/regress/sql/copy2.sql

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,23 @@ COPY testnull FROM stdin WITH NULL AS E'\\0';
178178

179179
SELECT * FROM testnull;
180180

181+
-- test case with whole-row Var in a check constraint
182+
create table check_con_tbl (f1 int);
183+
create function check_con_function(check_con_tbl) returns bool as $$
184+
begin
185+
raise notice 'input = %', row_to_json($1);
186+
return $1.f1 > 0;
187+
end $$ language plpgsql immutable;
188+
alter table check_con_tbl add check (check_con_function(check_con_tbl.*));
189+
\d+ check_con_tbl
190+
copy check_con_tbl from stdin;
191+
1
192+
\N
193+
\.
194+
copy check_con_tbl from stdin;
195+
0
196+
\.
197+
select * from check_con_tbl;
181198

182199
DROP TABLE x, y;
183200
DROP FUNCTION fn_x_before();

0 commit comments

Comments
 (0)