aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/parse_target.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2009-10-31 01:41:31 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2009-10-31 01:41:31 +0000
commitfb5d05805b3f7658e47ad2c6a4631e497bb3f627 (patch)
tree74ee6327b45c313ac0ac21e92a3d61a52135cd2f /src/backend/parser/parse_target.c
parent8442317beb8fb6f180880a977c03ccae4304df25 (diff)
downloadpostgresql-fb5d05805b3f7658e47ad2c6a4631e497bb3f627.tar.gz
postgresql-fb5d05805b3f7658e47ad2c6a4631e497bb3f627.zip
Implement parser hooks for processing ColumnRef and ParamRef nodes, as per my
recent proposal. As proof of concept, remove knowledge of Params from the core parser, arranging for them to be handled entirely by parser hook functions. It turns out we need an additional hook for that --- I had forgotten about the code that handles inferring a parameter's type from context. This is a preliminary step towards letting plpgsql handle its variables through parser hooks. Additional work remains to be done to expose the facility through SPI, but I think this is all the changes needed in the core parser.
Diffstat (limited to 'src/backend/parser/parse_target.c')
-rw-r--r--src/backend/parser/parse_target.c312
1 files changed, 213 insertions, 99 deletions
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 3aff83955b8..ce3f51ca6e5 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.173 2009/10/21 20:22:38 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.174 2009/10/31 01:41:31 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -48,6 +48,10 @@ static List *ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref,
static List *ExpandAllTables(ParseState *pstate, int location);
static List *ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind,
bool targetlist);
+static List *ExpandSingleTable(ParseState *pstate, RangeTblEntry *rte,
+ int location, bool targetlist);
+static List *ExpandRowReference(ParseState *pstate, Node *expr,
+ bool targetlist);
static int FigureColnameInternal(Node *node, char **name);
@@ -879,90 +883,135 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref,
* Target item is relation.*, expand that table
*
* (e.g., SELECT emp.*, dname FROM emp, dept)
+ *
+ * Note: this code is a lot like transformColumnRef; it's tempting
+ * to call that instead and then replace the resulting whole-row Var
+ * with a list of Vars. However, that would leave us with the
+ * RTE's selectedCols bitmap showing the whole row as needing
+ * select permission, as well as the individual columns. That would
+ * be incorrect (since columns added later shouldn't need select
+ * permissions). We could try to remove the whole-row permission bit
+ * after the fact, but duplicating code is less messy.
*/
- char *schemaname;
- char *relname;
- RangeTblEntry *rte;
- int sublevels_up;
- int rtindex;
+ char *nspname = NULL;
+ char *relname = NULL;
+ RangeTblEntry *rte = NULL;
+ int levels_up;
+ enum {
+ CRSERR_NO_RTE,
+ CRSERR_WRONG_DB,
+ CRSERR_TOO_MANY
+ } crserr = CRSERR_NO_RTE;
+
+ /*
+ * Give the PreParseColumnRefHook, if any, first shot. If it returns
+ * non-null then we should use that expression.
+ */
+ if (pstate->p_pre_columnref_hook != NULL)
+ {
+ Node *node;
+
+ node = (*pstate->p_pre_columnref_hook) (pstate, cref);
+ if (node != NULL)
+ return ExpandRowReference(pstate, node, targetlist);
+ }
switch (numnames)
{
case 2:
- schemaname = NULL;
relname = strVal(linitial(fields));
+ rte = refnameRangeTblEntry(pstate, nspname, relname,
+ cref->location,
+ &levels_up);
break;
case 3:
- schemaname = strVal(linitial(fields));
+ nspname = strVal(linitial(fields));
relname = strVal(lsecond(fields));
+ rte = refnameRangeTblEntry(pstate, nspname, relname,
+ cref->location,
+ &levels_up);
break;
case 4:
- {
- char *name1 = strVal(linitial(fields));
+ {
+ char *catname = strVal(linitial(fields));
- /*
- * We check the catalog name and then ignore it.
- */
- if (strcmp(name1, get_database_name(MyDatabaseId)) != 0)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cross-database references are not implemented: %s",
- NameListToString(fields)),
- parser_errposition(pstate, cref->location)));
- schemaname = strVal(lsecond(fields));
- relname = strVal(lthird(fields));
+ /*
+ * We check the catalog name and then ignore it.
+ */
+ if (strcmp(catname, get_database_name(MyDatabaseId)) != 0)
+ {
+ crserr = CRSERR_WRONG_DB;
break;
}
+ nspname = strVal(lsecond(fields));
+ relname = strVal(lthird(fields));
+ rte = refnameRangeTblEntry(pstate, nspname, relname,
+ cref->location,
+ &levels_up);
+ break;
+ }
default:
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("improper qualified name (too many dotted names): %s",
- NameListToString(fields)),
- parser_errposition(pstate, cref->location)));
- schemaname = NULL; /* keep compiler quiet */
- relname = NULL;
+ crserr = CRSERR_TOO_MANY;
break;
}
- rte = refnameRangeTblEntry(pstate, schemaname, relname, cref->location,
- &sublevels_up);
- if (rte == NULL)
- errorMissingRTE(pstate,
- makeRangeVar(schemaname, relname, cref->location));
-
- rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up);
-
- if (targetlist)
- {
- /* expandRelAttrs handles permissions marking */
- return expandRelAttrs(pstate, rte, rtindex, sublevels_up,
- cref->location);
- }
- else
+ /*
+ * Now give the PostParseColumnRefHook, if any, a chance.
+ * We cheat a bit by passing the RangeTblEntry, not a Var,
+ * as the planned translation. (A single Var wouldn't be
+ * strictly correct anyway. This convention allows hooks
+ * that really care to know what is happening.)
+ */
+ if (pstate->p_post_columnref_hook != NULL)
{
- List *vars;
- ListCell *l;
+ Node *node;
- expandRTE(rte, rtindex, sublevels_up, cref->location, false,
- NULL, &vars);
-
- /*
- * Require read access to the table. This is normally redundant
- * with the markVarForSelectPriv calls below, but not if the table
- * has zero columns.
- */
- rte->requiredPerms |= ACL_SELECT;
-
- /* Require read access to each column */
- foreach(l, vars)
+ node = (*pstate->p_post_columnref_hook) (pstate, cref,
+ (Node *) rte);
+ if (node != NULL)
{
- Var *var = (Var *) lfirst(l);
-
- markVarForSelectPriv(pstate, var, rte);
+ if (rte != NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_COLUMN),
+ errmsg("column reference \"%s\" is ambiguous",
+ NameListToString(cref->fields)),
+ parser_errposition(pstate, cref->location)));
+ return ExpandRowReference(pstate, node, targetlist);
}
+ }
- return vars;
+ /*
+ * Throw error if no translation found.
+ */
+ if (rte == NULL)
+ {
+ switch (crserr)
+ {
+ case CRSERR_NO_RTE:
+ errorMissingRTE(pstate, makeRangeVar(nspname, relname,
+ cref->location));
+ break;
+ case CRSERR_WRONG_DB:
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cross-database references are not implemented: %s",
+ NameListToString(cref->fields)),
+ parser_errposition(pstate, cref->location)));
+ break;
+ case CRSERR_TOO_MANY:
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("improper qualified name (too many dotted names): %s",
+ NameListToString(cref->fields)),
+ parser_errposition(pstate, cref->location)));
+ break;
+ }
}
+
+ /*
+ * OK, expand the RTE into fields.
+ */
+ return ExpandSingleTable(pstate, rte, cref->location, targetlist);
}
}
@@ -1015,11 +1064,7 @@ static List *
ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind,
bool targetlist)
{
- List *result = NIL;
Node *expr;
- TupleDesc tupleDesc;
- int numAttrs;
- int i;
/* Strip off the '*' to create a reference to the rowtype object */
ind = copyObject(ind);
@@ -1029,7 +1074,102 @@ ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind,
/* And transform that */
expr = transformExpr(pstate, (Node *) ind);
+ /* Expand the rowtype expression into individual fields */
+ return ExpandRowReference(pstate, expr, targetlist);
+}
+
+/*
+ * ExpandSingleTable()
+ * Transforms foo.* into a list of expressions or targetlist entries.
+ *
+ * This handles the case where foo has been determined to be a simple
+ * reference to an RTE, so we can just generate Vars for the expressions.
+ *
+ * The referenced columns are marked as requiring SELECT access.
+ */
+static List *
+ExpandSingleTable(ParseState *pstate, RangeTblEntry *rte,
+ int location, bool targetlist)
+{
+ int sublevels_up;
+ int rtindex;
+
+ rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up);
+
+ if (targetlist)
+ {
+ /* expandRelAttrs handles permissions marking */
+ return expandRelAttrs(pstate, rte, rtindex, sublevels_up,
+ location);
+ }
+ else
+ {
+ List *vars;
+ ListCell *l;
+
+ expandRTE(rte, rtindex, sublevels_up, location, false,
+ NULL, &vars);
+
+ /*
+ * Require read access to the table. This is normally redundant
+ * with the markVarForSelectPriv calls below, but not if the table
+ * has zero columns.
+ */
+ rte->requiredPerms |= ACL_SELECT;
+
+ /* Require read access to each column */
+ foreach(l, vars)
+ {
+ Var *var = (Var *) lfirst(l);
+
+ markVarForSelectPriv(pstate, var, rte);
+ }
+
+ return vars;
+ }
+}
+
+/*
+ * ExpandRowReference()
+ * Transforms foo.* into a list of expressions or targetlist entries.
+ *
+ * This handles the case where foo is an arbitrary expression of composite
+ * type.
+ */
+static List *
+ExpandRowReference(ParseState *pstate, Node *expr,
+ bool targetlist)
+{
+ List *result = NIL;
+ TupleDesc tupleDesc;
+ int numAttrs;
+ int i;
+
/*
+ * If the rowtype expression is a whole-row Var, we can expand the fields
+ * as simple Vars. Note: if the RTE is a relation, this case leaves us
+ * with the RTE's selectedCols bitmap showing the whole row as needing
+ * select permission, as well as the individual columns. However, we can
+ * only get here for weird notations like (table.*).*, so it's not worth
+ * trying to clean up --- arguably, the permissions marking is correct
+ * anyway for such cases.
+ */
+ if (IsA(expr, Var) &&
+ ((Var *) expr)->varattno == InvalidAttrNumber)
+ {
+ Var *var = (Var *) expr;
+ RangeTblEntry *rte;
+
+ rte = GetRTEByRangeTablePosn(pstate, var->varno, var->varlevelsup);
+ return ExpandSingleTable(pstate, rte, var->location, targetlist);
+ }
+
+ /*
+ * Otherwise we have to do it the hard way. Our current implementation
+ * is to generate multiple copies of the expression and do FieldSelects.
+ * (This can be pretty inefficient if the expression involves nontrivial
+ * computation :-(.)
+ *
* 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
@@ -1053,56 +1193,30 @@ ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind,
for (i = 0; i < numAttrs; i++)
{
Form_pg_attribute att = tupleDesc->attrs[i];
- Node *fieldnode;
+ FieldSelect *fselect;
if (att->attisdropped)
continue;
- /*
- * If we got a whole-row Var from the rowtype reference, we can expand
- * the fields as simple Vars. Otherwise we must generate multiple
- * copies of the rowtype reference and do FieldSelects.
- */
- if (IsA(expr, Var) &&
- ((Var *) expr)->varattno == InvalidAttrNumber)
- {
- Var *var = (Var *) expr;
- Var *newvar;
-
- newvar = makeVar(var->varno,
- i + 1,
- att->atttypid,
- att->atttypmod,
- var->varlevelsup);
- newvar->location = var->location;
-
- fieldnode = (Node *) newvar;
- }
- else
- {
- FieldSelect *fselect = makeNode(FieldSelect);
-
- fselect->arg = (Expr *) copyObject(expr);
- fselect->fieldnum = i + 1;
- fselect->resulttype = att->atttypid;
- fselect->resulttypmod = att->atttypmod;
-
- fieldnode = (Node *) fselect;
- }
+ fselect = makeNode(FieldSelect);
+ fselect->arg = (Expr *) copyObject(expr);
+ fselect->fieldnum = i + 1;
+ fselect->resulttype = att->atttypid;
+ fselect->resulttypmod = att->atttypmod;
if (targetlist)
{
/* add TargetEntry decoration */
TargetEntry *te;
- te = makeTargetEntry((Expr *) fieldnode,
+ te = makeTargetEntry((Expr *) fselect,
(AttrNumber) pstate->p_next_resno++,
pstrdup(NameStr(att->attname)),
false);
result = lappend(result, te);
}
else
- result = lappend(result, fieldnode);
+ result = lappend(result, fselect);
}
return result;