]> granicus.if.org Git - postgresql/commitdiff
Give a suitable HINT when an INSERT's data source is a RowExpr containing
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 18 Sep 2010 18:37:01 +0000 (18:37 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 18 Sep 2010 18:37:01 +0000 (18:37 +0000)
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.

src/backend/parser/analyze.c

index 62d8ad5d7149338b24fac4a445f5c41c6d741a31..a1ad3e20e5bf90dfbe46e1a6d969f09d6bb868d6 100644 (file)
@@ -17,7 +17,7 @@
  * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *     $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.403 2010/08/27 20:30:08 petere Exp $
+ *     $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.404 2010/09/18 18:37:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -47,6 +47,7 @@ static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt);
 static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt);
 static List *transformInsertRow(ParseState *pstate, List *exprlist,
                                   List *stmtcols, List *icolumns, List *attrnos);
+static int     count_rowexpr_columns(ParseState *pstate, Node *expr);
 static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt);
 static Query *transformValuesClause(ParseState *pstate, SelectStmt *stmt);
 static Query *transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt);
@@ -726,12 +727,27 @@ transformInsertRow(ParseState *pstate, List *exprlist,
                                                                                                  list_length(icolumns))))));
        if (stmtcols != NIL &&
                list_length(exprlist) < list_length(icolumns))
+       {
+               /*
+                * We can get here for cases like INSERT ... SELECT (a,b,c) FROM ...
+                * where the user accidentally created a RowExpr instead of separate
+                * columns.  Add a suitable hint if that seems to be the problem,
+                * because the main error message is quite misleading for this case.
+                * (If there's no stmtcols, you'll get something about data type
+                * mismatch, which is less misleading so we don't worry about giving
+                * a hint in that case.)
+                */
                ereport(ERROR,
                                (errcode(ERRCODE_SYNTAX_ERROR),
                                 errmsg("INSERT has more target columns than expressions"),
+                                ((list_length(exprlist) == 1 &&
+                                  count_rowexpr_columns(pstate, linitial(exprlist)) ==
+                                  list_length(icolumns)) ?
+                                 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),
                                 parser_errposition(pstate,
                                                                        exprLocation(list_nth(icolumns,
                                                                                                  list_length(exprlist))))));
+       }
 
        /*
         * Prepare columns for assignment to target table.
@@ -762,6 +778,49 @@ transformInsertRow(ParseState *pstate, List *exprlist,
        return result;
 }
 
+/*
+ * count_rowexpr_columns -
+ *       get number of columns contained in a ROW() expression;
+ *       return -1 if expression isn't a RowExpr or a Var referencing one.
+ *
+ * This is currently used only for hint purposes, so we aren't terribly
+ * tense about recognizing all possible cases.  The Var case is interesting
+ * because that's what we'll get in the INSERT ... SELECT (...) case.
+ */
+static int
+count_rowexpr_columns(ParseState *pstate, Node *expr)
+{
+       if (expr == NULL)
+               return -1;
+       if (IsA(expr, RowExpr))
+               return list_length(((RowExpr *) expr)->args);
+       if (IsA(expr, Var))
+       {
+               Var                *var = (Var *) expr;
+               AttrNumber      attnum = var->varattno;
+
+               if (attnum > 0 && var->vartype == RECORDOID)
+               {
+                       RangeTblEntry *rte;
+
+                       rte = GetRTEByRangeTablePosn(pstate, var->varno, var->varlevelsup);
+                       if (rte->rtekind == RTE_SUBQUERY)
+                       {
+                               /* Subselect-in-FROM: examine sub-select's output expr */
+                               TargetEntry *ste = get_tle_by_resno(rte->subquery->targetList,
+                                                                                                       attnum);
+
+                               if (ste == NULL || ste->resjunk)
+                                       return -1;
+                               expr = (Node *) ste->expr;
+                               if (IsA(expr, RowExpr))
+                                       return list_length(((RowExpr *) expr)->args);
+                       }
+               }
+       }
+       return -1;
+}
+
 
 /*
  * transformSelectStmt -