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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy