diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index baa02f256e3afa2cdf5df943e8a3a1317af59cc9..14a858091945b7eed733ca7bdee3ce058151e897 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -898,7 +898,9 @@ ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext, * If we can't locate the RTE, assume the column names we've got are OK. * (As of this writing, the only cases where we can't locate the RTE are * in execution of trigger WHEN clauses, and then the Var will have the - * trigger's relation's rowtype, so its names are fine.) + * trigger's relation's rowtype, so its names are fine.) Also, if the + * creator of the RTE didn't bother to fill in an eref field, assume our + * column names are OK. (This happens in COPY, and perhaps other places.) */ if (variable->vartype == RECORDOID && econtext->ecxt_estate && @@ -907,7 +909,8 @@ ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext, RangeTblEntry *rte = rt_fetch(variable->varno, econtext->ecxt_estate->es_range_table); - ExecTypeSetColNames(output_tupdesc, rte->eref->colnames); + if (rte->eref) + ExecTypeSetColNames(output_tupdesc, rte->eref->colnames); } /* Bless the tupdesc if needed, and save it in the execution state */ diff --git a/src/test/regress/expected/copy2.out b/src/test/regress/expected/copy2.out index 2449eefb16d4b8712b7e78f39ff42886a098789c..816fa77056ca53766885bf92d4b48f45b3b3a93d 100644 --- a/src/test/regress/expected/copy2.out +++ b/src/test/regress/expected/copy2.out @@ -255,6 +255,41 @@ SELECT * FROM testnull; | (4 rows) +-- test case with whole-row Var in a check constraint +create table check_con_tbl (f1 int); +create function check_con_function(check_con_tbl) returns bool as $$ +begin + raise notice 'input = %', row_to_json($1); + return $1.f1 > 0; +end $$ language plpgsql immutable; +alter table check_con_tbl add check (check_con_function(check_con_tbl.*)); +\d+ check_con_tbl + Table "public.check_con_tbl" + Column | Type | Modifiers | Storage | Stats target | Description +--------+---------+-----------+---------+--------------+------------- + f1 | integer | | plain | | +Check constraints: + "check_con_tbl_check" CHECK (check_con_function(check_con_tbl.*)) +Has OIDs: no + +copy check_con_tbl from stdin; +NOTICE: input = {"f1":1} +CONTEXT: COPY check_con_tbl, line 1: "1" +NOTICE: input = {"f1":null} +CONTEXT: COPY check_con_tbl, line 2: "\N" +copy check_con_tbl from stdin; +NOTICE: input = {"f1":0} +CONTEXT: COPY check_con_tbl, line 1: "0" +ERROR: new row for relation "check_con_tbl" violates check constraint "check_con_tbl_check" +DETAIL: Failing row contains (0). +CONTEXT: COPY check_con_tbl, line 1: "0" +select * from check_con_tbl; + f1 +---- + 1 + +(2 rows) + DROP TABLE x, y; DROP FUNCTION fn_x_before(); DROP FUNCTION fn_x_after(); diff --git a/src/test/regress/sql/copy2.sql b/src/test/regress/sql/copy2.sql index 1961446fdb1b25f5b415f747236832e3689d53a1..97d975b6303e9a92949b118ac6eab2b5dfb702aa 100644 --- a/src/test/regress/sql/copy2.sql +++ b/src/test/regress/sql/copy2.sql @@ -178,6 +178,23 @@ COPY testnull FROM stdin WITH NULL AS E'\\0'; SELECT * FROM testnull; +-- test case with whole-row Var in a check constraint +create table check_con_tbl (f1 int); +create function check_con_function(check_con_tbl) returns bool as $$ +begin + raise notice 'input = %', row_to_json($1); + return $1.f1 > 0; +end $$ language plpgsql immutable; +alter table check_con_tbl add check (check_con_function(check_con_tbl.*)); +\d+ check_con_tbl +copy check_con_tbl from stdin; +1 +\N +\. +copy check_con_tbl from stdin; +0 +\. +select * from check_con_tbl; DROP TABLE x, y; DROP FUNCTION fn_x_before();