Skip to content

Commit af0161e

Browse files
committed
Give a suitable HINT when an INSERT's data source is a RowExpr containing
the same number of columns expected by the insert. This suggests that there were extra parentheses that converted the intended column list into a row expression. Original patch by Marko Tiikkaja, rather heavily editorialized by me.
1 parent 48f0a28 commit af0161e

File tree

1 file changed

+60
-1
lines changed

1 file changed

+60
-1
lines changed

src/backend/parser/analyze.c

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
1818
* Portions Copyright (c) 1994, Regents of the University of California
1919
*
20-
* $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.403 2010/08/27 20:30:08 petere Exp $
20+
* $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.404 2010/09/18 18:37:01 tgl Exp $
2121
*
2222
*-------------------------------------------------------------------------
2323
*/
@@ -47,6 +47,7 @@ static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt);
4747
static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt);
4848
static List *transformInsertRow(ParseState *pstate, List *exprlist,
4949
List *stmtcols, List *icolumns, List *attrnos);
50+
static int count_rowexpr_columns(ParseState *pstate, Node *expr);
5051
static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt);
5152
static Query *transformValuesClause(ParseState *pstate, SelectStmt *stmt);
5253
static Query *transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt);
@@ -726,12 +727,27 @@ transformInsertRow(ParseState *pstate, List *exprlist,
726727
list_length(icolumns))))));
727728
if (stmtcols != NIL &&
728729
list_length(exprlist) < list_length(icolumns))
730+
{
731+
/*
732+
* We can get here for cases like INSERT ... SELECT (a,b,c) FROM ...
733+
* where the user accidentally created a RowExpr instead of separate
734+
* columns. Add a suitable hint if that seems to be the problem,
735+
* because the main error message is quite misleading for this case.
736+
* (If there's no stmtcols, you'll get something about data type
737+
* mismatch, which is less misleading so we don't worry about giving
738+
* a hint in that case.)
739+
*/
729740
ereport(ERROR,
730741
(errcode(ERRCODE_SYNTAX_ERROR),
731742
errmsg("INSERT has more target columns than expressions"),
743+
((list_length(exprlist) == 1 &&
744+
count_rowexpr_columns(pstate, linitial(exprlist)) ==
745+
list_length(icolumns)) ?
746+
errhint("The insertion source is a row expression containing the same number of columns expected by the INSERT. Did you accidentally use extra parentheses?") : 0),
732747
parser_errposition(pstate,
733748
exprLocation(list_nth(icolumns,
734749
list_length(exprlist))))));
750+
}
735751

736752
/*
737753
* Prepare columns for assignment to target table.
@@ -762,6 +778,49 @@ transformInsertRow(ParseState *pstate, List *exprlist,
762778
return result;
763779
}
764780

781+
/*
782+
* count_rowexpr_columns -
783+
* get number of columns contained in a ROW() expression;
784+
* return -1 if expression isn't a RowExpr or a Var referencing one.
785+
*
786+
* This is currently used only for hint purposes, so we aren't terribly
787+
* tense about recognizing all possible cases. The Var case is interesting
788+
* because that's what we'll get in the INSERT ... SELECT (...) case.
789+
*/
790+
static int
791+
count_rowexpr_columns(ParseState *pstate, Node *expr)
792+
{
793+
if (expr == NULL)
794+
return -1;
795+
if (IsA(expr, RowExpr))
796+
return list_length(((RowExpr *) expr)->args);
797+
if (IsA(expr, Var))
798+
{
799+
Var *var = (Var *) expr;
800+
AttrNumber attnum = var->varattno;
801+
802+
if (attnum > 0 && var->vartype == RECORDOID)
803+
{
804+
RangeTblEntry *rte;
805+
806+
rte = GetRTEByRangeTablePosn(pstate, var->varno, var->varlevelsup);
807+
if (rte->rtekind == RTE_SUBQUERY)
808+
{
809+
/* Subselect-in-FROM: examine sub-select's output expr */
810+
TargetEntry *ste = get_tle_by_resno(rte->subquery->targetList,
811+
attnum);
812+
813+
if (ste == NULL || ste->resjunk)
814+
return -1;
815+
expr = (Node *) ste->expr;
816+
if (IsA(expr, RowExpr))
817+
return list_length(((RowExpr *) expr)->args);
818+
}
819+
}
820+
}
821+
return -1;
822+
}
823+
765824

766825
/*
767826
* transformSelectStmt -

0 commit comments

Comments
 (0)