OSDN Git Service

Fix ExpandIndirectionStar to handle cases where the expression to be
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 25 Apr 2005 21:03:25 +0000 (21:03 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 25 Apr 2005 21:03:25 +0000 (21:03 +0000)
expanded is of RECORD type, eg
'select (foo).* from (select foo(f1) from t1) ss'
where foo() is a function declared with multiple OUT parameters.

src/backend/parser/parse_target.c

index d537f57..b7abfe9 100644 (file)
@@ -8,13 +8,14 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.131 2005/04/06 16:34:06 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.132 2005/04/25 21:03:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
 #include "commands/dbcommands.h"
+#include "funcapi.h"
 #include "miscadmin.h"
 #include "nodes/bitmapset.h"
 #include "nodes/makefuncs.h"
@@ -43,6 +44,8 @@ static Node *transformAssignmentIndirection(ParseState *pstate,
 static List *ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref);
 static List *ExpandAllTables(ParseState *pstate);
 static List *ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind);
+static TupleDesc expandRecordVariable(ParseState *pstate, Var *var,
+                                                                         int levelsup);
 static int     FigureColnameInternal(Node *node, char **name);
 
 
@@ -822,8 +825,23 @@ ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind)
        /* And transform that */
        expr = transformExpr(pstate, (Node *) ind);
 
-       /* Verify it's a composite type, and get the tupdesc */
-       tupleDesc = lookup_rowtype_tupdesc(exprType(expr), exprTypmod(expr));
+       /*
+        * Verify it's a composite type, and get the tupdesc.  We use
+        * get_expr_result_type() because that can handle references to
+        * functions returning anonymous record types.  If that fails,
+        * use lookup_rowtype_tupdesc(), which will almost certainly fail
+        * as well, but it will give an appropriate error message.
+        *
+        * If it's a Var of type RECORD, we have to work even harder: we have
+        * to find what the Var refers to, and pass that to get_expr_result_type.
+        * That task is handled by expandRecordVariable().
+        */
+       if (IsA(expr, Var) &&
+               ((Var *) expr)->vartype == RECORDOID)
+               tupleDesc = expandRecordVariable(pstate, (Var *) expr, 0);
+       else if (get_expr_result_type(expr, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
+               tupleDesc = lookup_rowtype_tupdesc(exprType(expr), exprTypmod(expr));
+       Assert(tupleDesc);
 
        /* Generate a list of references to the individual fields */
        numAttrs = tupleDesc->natts;
@@ -875,6 +893,136 @@ ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind)
 }
 
 /*
+ * expandRecordVariable
+ *             Get the tuple descriptor for a Var of type RECORD, if possible.
+ *
+ * Since no actual table or view column is allowed to have type RECORD, such
+ * a Var must refer to a JOIN or FUNCTION RTE or to a subquery output.  We
+ * drill down to find the ultimate defining expression and attempt to infer
+ * the tupdesc from it.  We ereport if we can't determine the tupdesc.
+ *
+ * levelsup is an extra offset to interpret the Var's varlevelsup correctly.
+ */
+static TupleDesc
+expandRecordVariable(ParseState *pstate, Var *var, int levelsup)
+{
+       TupleDesc       tupleDesc;
+       int                     netlevelsup;
+       RangeTblEntry *rte;
+       AttrNumber      attnum;
+       Node       *expr;
+
+       /* Check my caller didn't mess up */
+       Assert(IsA(var, Var));
+       Assert(var->vartype == RECORDOID);
+
+       netlevelsup = var->varlevelsup + levelsup;
+       rte = GetRTEByRangeTablePosn(pstate, var->varno, netlevelsup);
+       attnum = var->varattno;
+
+       expr = (Node *) var;            /* default if we can't drill down */
+
+       switch (rte->rtekind)
+       {
+               case RTE_RELATION:
+               case RTE_SPECIAL:
+                       /*
+                        * This case should not occur: a whole-row Var should have the
+                        * table's named rowtype, and a column of a table shouldn't have
+                        * type RECORD either.  Fall through and fail (most likely)
+                        * at the bottom.
+                        */
+                       break;
+               case 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)
+                                       elog(ERROR, "subquery %s does not have attribute %d",
+                                                rte->eref->aliasname, attnum);
+                               expr = (Node *) ste->expr;
+                               if (IsA(expr, Var))
+                               {
+                                       /*
+                                        * Recurse into the sub-select to see what its Var refers
+                                        * to.  We have to build an additional level of ParseState
+                                        * to keep in step with varlevelsup in the subselect.
+                                        */
+                                       ParseState      mypstate;
+
+                                       MemSet(&mypstate, 0, sizeof(mypstate));
+                                       mypstate.parentParseState = pstate;
+                                       mypstate.p_rtable = rte->subquery->rtable;
+                                       /* don't bother filling the rest of the fake pstate */
+
+                                       return expandRecordVariable(&mypstate, (Var *) expr, 0);
+                               }
+                               /* else fall through to inspect the expression */
+                       }
+                       break;
+               case RTE_JOIN:
+                       /* Join RTE */
+                       if (attnum == InvalidAttrNumber)
+                       {
+                               /* Whole-row reference to join, so expand the fields */
+                               List       *names,
+                                                  *vars;
+                               ListCell   *lname,
+                                                  *lvar;
+                               int                     i;
+
+                               expandRTE(GetLevelNRangeTable(pstate, netlevelsup),
+                                                 var->varno, 0, false, &names, &vars);
+
+                               tupleDesc = CreateTemplateTupleDesc(list_length(vars), false);
+                               i = 1;
+                               forboth(lname, names, lvar, vars)
+                               {
+                                       char       *label = strVal(lfirst(lname));
+                                       Node       *varnode = (Node *) lfirst(lvar);
+
+                                       TupleDescInitEntry(tupleDesc, i,
+                                                                          label,
+                                                                          exprType(varnode),
+                                                                          exprTypmod(varnode),
+                                                                          0);
+                                       i++;
+                               }
+                               Assert(lname == NULL && lvar == NULL);  /* lists same len? */
+                               return tupleDesc;
+                       }
+                       /* Else recursively inspect the alias variable */
+                       Assert(attnum > 0 && attnum <= list_length(rte->joinaliasvars));
+                       expr = (Node *) list_nth(rte->joinaliasvars, attnum - 1);
+                       if (IsA(expr, Var))
+                               return expandRecordVariable(pstate, (Var *) expr, netlevelsup);
+                       /* else fall through to inspect the expression */
+                       break;
+               case RTE_FUNCTION:
+                       expr = rte->funcexpr;
+                       /* The func expr probably can't be a Var, but check */
+                       if (IsA(expr, Var))
+                               return expandRecordVariable(pstate, (Var *) expr, netlevelsup);
+                       /* else fall through to inspect the expression */
+                       break;
+       }
+
+       /*
+        * We now have an expression we can't expand any more, so see if
+        * get_expr_result_type() can do anything with it.  If not, pass
+        * to lookup_rowtype_tupdesc() which will probably fail, but will
+        * give an appropriate error message while failing.
+        */
+       if (get_expr_result_type(expr, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
+               tupleDesc = lookup_rowtype_tupdesc(exprType(expr), exprTypmod(expr));
+
+       return tupleDesc;
+}
+
+
+/*
  * FigureColname -
  *       if the name of the resulting column is not specified in the target
  *       list, we have to guess a suitable name.  The SQL spec provides some