aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor/nodeModifyTable.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2025-04-15 12:08:34 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2025-04-15 12:08:34 -0400
commit7c872849407730fa01e2c13b2d47483bc3ff6e7e (patch)
tree369a11b392521cbcaeb062483dfc1f4c57524ead /src/backend/executor/nodeModifyTable.c
parentf840f8ee304cb756638fd82254e3d00150a9b409 (diff)
downloadpostgresql-7c872849407730fa01e2c13b2d47483bc3ff6e7e.tar.gz
postgresql-7c872849407730fa01e2c13b2d47483bc3ff6e7e.zip
Fix failure for generated column with a not-null domain constraint.
If a GENERATED column is declared to have a domain data type where the domain's constraints disallow null values, INSERT commands failed because we built a targetlist that included coercing a null constant to the domain's type. The failure occurred even when the generated value would have been perfectly OK. This is adjacent to the issues fixed in 0da39aa76, but we didn't notice for lack of testing a domain with such a constraint. We aren't going to use the result of the targetlist entry for the generated column --- ExecComputeStoredGenerated will overwrite it. So it's not really necessary that it have the exact datatype of the generated column. This patch fixes the problem by changing the targetlist entry to be a null Const of the domain's base type, which should be sufficiently legal. (We do have to tweak ExecCheckPlanOutput to accept the situation, though.) This has been broken since we implemented generated columns. However, this patch only applies easily as far back as v14, partly because I (tgl) only carried 0da39aa76 back that far, but mostly because v14 significantly refactored the handling of INSERT/UPDATE targetlists. Given the lack of field complaints and the short remaining support lifetime of v13, I judge the cost-benefit ratio not good for devising a version that would work in v13. Reported-by: jian he <jian.universality@gmail.com> Author: jian he <jian.universality@gmail.com> Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us> Discussion: https://postgr.es/m/CACJufxG59tip2+9h=rEv-ykOFjt0cbsPVchhi0RTij8bABBA0Q@mail.gmail.com Backpatch-through: 14
Diffstat (limited to 'src/backend/executor/nodeModifyTable.c')
-rw-r--r--src/backend/executor/nodeModifyTable.c44
1 files changed, 32 insertions, 12 deletions
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 309e27f8b5f..333cbf78343 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -212,33 +212,53 @@ ExecCheckPlanOutput(Relation resultRel, List *targetList)
attr = TupleDescAttr(resultDesc, attno);
attno++;
- if (!attr->attisdropped)
+ /*
+ * Special cases here should match planner's expand_insert_targetlist.
+ */
+ if (attr->attisdropped)
{
- /* Normal case: demand type match */
- if (exprType((Node *) tle->expr) != attr->atttypid)
+ /*
+ * For a dropped column, we can't check atttypid (it's likely 0).
+ * In any case the planner has most likely inserted an INT4 null.
+ * What we insist on is just *some* NULL constant.
+ */
+ if (!IsA(tle->expr, Const) ||
+ !((Const *) tle->expr)->constisnull)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("table row type and query-specified row type do not match"),
- errdetail("Table has type %s at ordinal position %d, but query expects %s.",
- format_type_be(attr->atttypid),
- attno,
- format_type_be(exprType((Node *) tle->expr)))));
+ errdetail("Query provides a value for a dropped column at ordinal position %d.",
+ attno)));
}
- else
+ else if (attr->attgenerated)
{
/*
- * For a dropped column, we can't check atttypid (it's likely 0).
- * In any case the planner has most likely inserted an INT4 null.
- * What we insist on is just *some* NULL constant.
+ * For a generated column, the planner will have inserted a null
+ * of the column's base type (to avoid possibly failing on domain
+ * not-null constraints). It doesn't seem worth insisting on that
+ * exact type though, since a null value is type-independent. As
+ * above, just insist on *some* NULL constant.
*/
if (!IsA(tle->expr, Const) ||
!((Const *) tle->expr)->constisnull)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("table row type and query-specified row type do not match"),
- errdetail("Query provides a value for a dropped column at ordinal position %d.",
+ errdetail("Query provides a value for a generated column at ordinal position %d.",
attno)));
}
+ else
+ {
+ /* Normal case: demand type match */
+ if (exprType((Node *) tle->expr) != attr->atttypid)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("table row type and query-specified row type do not match"),
+ errdetail("Table has type %s at ordinal position %d, but query expects %s.",
+ format_type_be(attr->atttypid),
+ attno,
+ format_type_be(exprType((Node *) tle->expr)))));
+ }
}
if (attno != resultDesc->natts)
ereport(ERROR,