summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2015-02-15 23:26:46 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2015-02-15 23:26:46 -0500
commiteffcaa4c286da355e7ee59a6e774c4cd7a0ea4f5 (patch)
tree0a68609d484d83ad31bc96050cc1dbbd8d607a0d
parent52029445b943f1d2fa1280001afe3392dd16738c (diff)
Fix null-pointer-deref crash while doing COPY IN with check constraints.
In commit bf7ca15875988a88e97302e012d7c4808bef3ea9 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.
-rw-r--r--src/backend/executor/execQual.c7
-rw-r--r--src/test/regress/expected/copy2.out35
-rw-r--r--src/test/regress/sql/copy2.sql17
3 files changed, 57 insertions, 2 deletions
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index baa02f256e3..14a85809194 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 2449eefb16d..816fa77056c 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 1961446fdb1..97d975b6303 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();