]> granicus.if.org Git - postgresql/commitdiff
Implement parser hooks for processing ColumnRef and ParamRef nodes, as per my
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 31 Oct 2009 01:41:31 +0000 (01:41 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 31 Oct 2009 01:41:31 +0000 (01:41 +0000)
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.

15 files changed:
src/backend/catalog/namespace.c
src/backend/parser/Makefile
src/backend/parser/README
src/backend/parser/analyze.c
src/backend/parser/parse_coerce.c
src/backend/parser/parse_expr.c
src/backend/parser/parse_func.c
src/backend/parser/parse_node.c
src/backend/parser/parse_param.c [new file with mode: 0644]
src/backend/parser/parse_relation.c
src/backend/parser/parse_target.c
src/include/catalog/namespace.h
src/include/parser/parse_node.h
src/include/parser/parse_param.h [new file with mode: 0644]
src/include/parser/parse_relation.h

index 2c327d45f15c3235bdadfe889e5a6e1fd2bedcba..0c361557a9ade0bef5f4ef13bec74ca291cd0e8d 100644 (file)
@@ -13,7 +13,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.119 2009/10/08 02:39:17 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.120 2009/10/31 01:41:31 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2289,6 +2289,38 @@ DeconstructQualifiedName(List *names,
        *objname_p = objname;
 }
 
+/*
+ * LookupNamespaceNoError
+ *             Look up a schema name.
+ *
+ * Returns the namespace OID, or InvalidOid if not found.
+ *
+ * Note this does NOT perform any permissions check --- callers are
+ * responsible for being sure that an appropriate check is made.
+ * In the majority of cases LookupExplicitNamespace is preferable.
+ */
+Oid
+LookupNamespaceNoError(const char *nspname)
+{
+       /* check for pg_temp alias */
+       if (strcmp(nspname, "pg_temp") == 0)
+       {
+               if (OidIsValid(myTempNamespace))
+                       return myTempNamespace;
+
+               /*
+                * Since this is used only for looking up existing objects, there is
+                * no point in trying to initialize the temp namespace here; and doing
+                * so might create problems for some callers. Just report "not found".
+                */
+               return InvalidOid;
+       }
+
+       return GetSysCacheOid(NAMESPACENAME,
+                                                 CStringGetDatum(nspname),
+                                                 0, 0, 0);
+}
+
 /*
  * LookupExplicitNamespace
  *             Process an explicitly-specified schema name: look up the schema
@@ -2336,8 +2368,8 @@ LookupExplicitNamespace(const char *nspname)
  * LookupCreationNamespace
  *             Look up the schema and verify we have CREATE rights on it.
  *
- * This is just like LookupExplicitNamespace except for the permission check,
- * and that we are willing to create pg_temp if needed.
+ * This is just like LookupExplicitNamespace except for the different
+ * permission check, and that we are willing to create pg_temp if needed.
  *
  * Note: calling this may result in a CommandCounterIncrement operation,
  * if we have to create or clean out the temp namespace.
index 1ef3bb24db1a2e85704c2d308b169a0e74a4d4c8..9e58d83dfe4f1a487d64c032f156b1935b8a182b 100644 (file)
@@ -2,7 +2,7 @@
 #
 # Makefile for parser
 #
-# $PostgreSQL: pgsql/src/backend/parser/Makefile,v 1.51 2009/08/28 20:26:19 petere Exp $
+# $PostgreSQL: pgsql/src/backend/parser/Makefile,v 1.52 2009/10/31 01:41:31 tgl Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -12,9 +12,10 @@ include $(top_builddir)/src/Makefile.global
 
 override CPPFLAGS := -I$(srcdir) $(CPPFLAGS)
 
-OBJS= analyze.o gram.o keywords.o parser.o parse_agg.o parse_cte.o parse_clause.o \
-      parse_expr.o parse_func.o parse_node.o parse_oper.o parse_relation.o \
-      parse_type.o parse_coerce.o parse_target.o parse_utilcmd.o scansup.o kwlookup.o
+OBJS= analyze.o gram.o keywords.o kwlookup.o parser.o \
+      parse_agg.o parse_clause.o parse_coerce.o parse_cte.o parse_expr.o \
+      parse_func.o parse_node.o parse_oper.o parse_param.o parse_relation.o \
+      parse_target.o parse_type.o parse_utilcmd.o scansup.o
 
 FLEXFLAGS = -CF
 
index 36b6aeb7df7f6873b82e3ce36ef10a8c021fbbf5..10a5be652c17a9e9d087a6534c030e1400a238e4 100644 (file)
@@ -1,4 +1,4 @@
-$PostgreSQL: pgsql/src/backend/parser/README,v 1.10 2008/04/09 01:00:46 momjian Exp $
+$PostgreSQL: pgsql/src/backend/parser/README,v 1.11 2009/10/31 01:41:31 tgl Exp $
 
 Parser
 ======
@@ -10,17 +10,20 @@ to the optimizer and then executor.
 parser.c       things start here
 scan.l         break query into tokens
 scansup.c      handle escapes in input strings
-keywords.c     turn keywords into specific tokens
-gram.y         parse the tokens and fill query-type-specific structures
+kwlookup.c     turn keywords into specific tokens
+keywords.c     table of standard keywords (passed to kwlookup.c)
+gram.y         parse the tokens and produce a "raw" parse tree
 analyze.c      top level of parse analysis for optimizable queries
+parse_agg.c    handle aggregates, like SUM(col1),  AVG(col2), ...
 parse_clause.c handle clauses like WHERE, ORDER BY, GROUP BY, ...
 parse_coerce.c handle coercing expressions to different data types
+parse_cte.c    handle Common Table Expressions (WITH clauses)
 parse_expr.c   handle expressions like col, col + 3, x = 3 or x = 4
-parse_oper.c   handle operators in expressions
-parse_agg.c    handle aggregates, like SUM(col1),  AVG(col2), ...
 parse_func.c   handle functions, table.column and column identifiers
 parse_node.c   create nodes for various structures
-parse_target.c handle the result list of the query
+parse_oper.c   handle operators in expressions
+parse_param.c  handle Params (for the cases used in the core backend)
 parse_relation.c support routines for tables and column handling
+parse_target.c handle the result list of the query
 parse_type.c   support routines for data type handling
 parse_utilcmd.c        parse analysis for utility commands (done at execution time)
index c89c7c82a08b0b33141a58703797a4a8a1d9ff61..49ec8481846abd71a1e366d04696b67718a71cb0 100644 (file)
@@ -17,7 +17,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *     $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.395 2009/10/28 14:55:43 tgl Exp $
+ *     $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.396 2009/10/31 01:41:31 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -35,6 +35,7 @@
 #include "parser/parse_coerce.h"
 #include "parser/parse_cte.h"
 #include "parser/parse_oper.h"
+#include "parser/parse_param.h"
 #include "parser/parse_relation.h"
 #include "parser/parse_target.h"
 #include "parser/parsetree.h"
@@ -62,7 +63,6 @@ static Query *transformExplainStmt(ParseState *pstate,
                                         ExplainStmt *stmt);
 static void transformLockingClause(ParseState *pstate, Query *qry,
                                                                   LockingClause *lc, bool pushedDown);
-static bool check_parameter_resolution_walker(Node *node, ParseState *pstate);
 
 
 /*
@@ -86,9 +86,9 @@ parse_analyze(Node *parseTree, const char *sourceText,
        Assert(sourceText != NULL); /* required as of 8.4 */
 
        pstate->p_sourcetext = sourceText;
-       pstate->p_paramtypes = paramTypes;
-       pstate->p_numparams = numParams;
-       pstate->p_variableparams = false;
+
+       if (numParams > 0)
+               parse_fixed_parameters(pstate, paramTypes, numParams);
 
        query = transformStmt(pstate, parseTree);
 
@@ -114,18 +114,13 @@ parse_analyze_varparams(Node *parseTree, const char *sourceText,
        Assert(sourceText != NULL); /* required as of 8.4 */
 
        pstate->p_sourcetext = sourceText;
-       pstate->p_paramtypes = *paramTypes;
-       pstate->p_numparams = *numParams;
-       pstate->p_variableparams = true;
+
+       parse_variable_parameters(pstate, paramTypes, numParams);
 
        query = transformStmt(pstate, parseTree);
 
        /* make sure all is well with parameter types */
-       if (pstate->p_numparams > 0)
-               check_parameter_resolution_walker((Node *) query, pstate);
-
-       *paramTypes = pstate->p_paramtypes;
-       *numParams = pstate->p_numparams;
+       check_variable_parameters(pstate, query);
 
        free_parsestate(pstate);
 
@@ -1982,7 +1977,7 @@ transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt)
  *
  * EXPLAIN is just like other utility statements in that we emit it as a
  * CMD_UTILITY Query node with no transformation of the raw parse tree.
- * However, if p_variableparams is set, it could be that the client is
+ * However, if p_coerce_param_hook is set, it could be that the client is
  * expecting us to resolve parameter types in something like
  *             EXPLAIN SELECT * FROM tab WHERE col = $1
  * To deal with such cases, we run parse analysis and throw away the result;
@@ -1996,7 +1991,7 @@ transformExplainStmt(ParseState *pstate, ExplainStmt *stmt)
 {
        Query      *result;
 
-       if (pstate->p_variableparams)
+       if (pstate->p_coerce_param_hook != NULL)
        {
                /* Since parse analysis scribbles on its input, copy the tree first! */
                (void) transformStmt(pstate, copyObject(stmt->query));
@@ -2239,50 +2234,3 @@ applyLockingClause(Query *qry, Index rtindex,
        rc->pushedDown = pushedDown;
        qry->rowMarks = lappend(qry->rowMarks, rc);
 }
-
-
-/*
- * Traverse a fully-analyzed tree to verify that parameter symbols
- * match their types.  We need this because some Params might still
- * be UNKNOWN, if there wasn't anything to force their coercion,
- * and yet other instances seen later might have gotten coerced.
- */
-static bool
-check_parameter_resolution_walker(Node *node, ParseState *pstate)
-{
-       if (node == NULL)
-               return false;
-       if (IsA(node, Param))
-       {
-               Param      *param = (Param *) node;
-
-               if (param->paramkind == PARAM_EXTERN)
-               {
-                       int                     paramno = param->paramid;
-
-                       if (paramno <= 0 || /* shouldn't happen, but... */
-                               paramno > pstate->p_numparams)
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_UNDEFINED_PARAMETER),
-                                                errmsg("there is no parameter $%d", paramno),
-                                                parser_errposition(pstate, param->location)));
-
-                       if (param->paramtype != pstate->p_paramtypes[paramno - 1])
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_AMBIGUOUS_PARAMETER),
-                                        errmsg("could not determine data type of parameter $%d",
-                                                       paramno),
-                                                parser_errposition(pstate, param->location)));
-               }
-               return false;
-       }
-       if (IsA(node, Query))
-       {
-               /* Recurse into RTE subquery or not-yet-planned sublink subquery */
-               return query_tree_walker((Query *) node,
-                                                                check_parameter_resolution_walker,
-                                                                (void *) pstate, 0);
-       }
-       return expression_tree_walker(node, check_parameter_resolution_walker,
-                                                                 (void *) pstate);
-}
index 0aec4a850e26a36ce023aa1677b2e2361cb53a93..7f5f462d175dc40ed5e76222ac6c2335bb597072 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.177 2009/06/11 14:49:00 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.178 2009/10/31 01:41:31 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -259,69 +259,21 @@ coerce_type(ParseState *pstate, Node *node,
 
                return result;
        }
-       if (inputTypeId == UNKNOWNOID && IsA(node, Param) &&
-               ((Param *) node)->paramkind == PARAM_EXTERN &&
-               pstate != NULL && pstate->p_variableparams)
+       if (IsA(node, Param) &&
+               pstate != NULL && pstate->p_coerce_param_hook != NULL)
        {
                /*
-                * Input is a Param of previously undetermined type, and we want to
-                * update our knowledge of the Param's type.  Find the topmost
-                * ParseState and update the state.
+                * Allow the CoerceParamHook to decide what happens.  It can return
+                * a transformed node (very possibly the same Param node), or return
+                * NULL to indicate we should proceed with normal coercion.
                 */
-               Param      *param = (Param *) node;
-               int                     paramno = param->paramid;
-               ParseState *toppstate;
-
-               toppstate = pstate;
-               while (toppstate->parentParseState != NULL)
-                       toppstate = toppstate->parentParseState;
-
-               if (paramno <= 0 ||             /* shouldn't happen, but... */
-                       paramno > toppstate->p_numparams)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_UNDEFINED_PARAMETER),
-                                        errmsg("there is no parameter $%d", paramno),
-                                        parser_errposition(pstate, param->location)));
-
-               if (toppstate->p_paramtypes[paramno - 1] == UNKNOWNOID)
-               {
-                       /* We've successfully resolved the type */
-                       toppstate->p_paramtypes[paramno - 1] = targetTypeId;
-               }
-               else if (toppstate->p_paramtypes[paramno - 1] == targetTypeId)
-               {
-                       /* We previously resolved the type, and it matches */
-               }
-               else
-               {
-                       /* Ooops */
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_AMBIGUOUS_PARAMETER),
-                                        errmsg("inconsistent types deduced for parameter $%d",
-                                                       paramno),
-                                        errdetail("%s versus %s",
-                                               format_type_be(toppstate->p_paramtypes[paramno - 1]),
-                                                          format_type_be(targetTypeId)),
-                                        parser_errposition(pstate, param->location)));
-               }
-
-               param->paramtype = targetTypeId;
-
-               /*
-                * Note: it is tempting here to set the Param's paramtypmod to
-                * targetTypeMod, but that is probably unwise because we have no
-                * infrastructure that enforces that the value delivered for a Param
-                * will match any particular typmod.  Leaving it -1 ensures that a
-                * run-time length check/coercion will occur if needed.
-                */
-               param->paramtypmod = -1;
-
-               /* Use the leftmost of the param's and coercion's locations */
-               if (location >= 0 &&
-                       (param->location < 0 || location < param->location))
-                       param->location = location;
-
-               return (Node *) param;
+               result = (*pstate->p_coerce_param_hook) (pstate,
+                                                                                                (Param *) node,
+                                                                                                targetTypeId,
+                                                                                                targetTypeMod,
+                                                                                                location);
+               if (result)
+                       return result;
        }
        pathtype = find_coercion_pathway(targetTypeId, inputTypeId, ccontext,
                                                                         &funcId);
index 2146329ad89693999ba2624e5cafdf6ad00e1be2..1e76d3b546fc74909dc19f90db5f9f7ae93f1cf9 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.246 2009/10/27 17:11:18 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.247 2009/10/31 01:41:31 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -60,8 +60,8 @@ static Node *transformXmlSerialize(ParseState *pstate, XmlSerialize *xs);
 static Node *transformBooleanTest(ParseState *pstate, BooleanTest *b);
 static Node *transformCurrentOfExpr(ParseState *pstate, CurrentOfExpr *cexpr);
 static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref);
-static Node *transformWholeRowRef(ParseState *pstate, char *schemaname,
-                                        char *relname, int location);
+static Node *transformWholeRowRef(ParseState *pstate, RangeTblEntry *rte,
+                                                                 int location);
 static Node *transformIndirection(ParseState *pstate, Node *basenode,
                                         List *indirection);
 static Node *transformTypeCast(ParseState *pstate, TypeCast *tc);
@@ -327,11 +327,64 @@ transformExpr(ParseState *pstate, Node *expr)
        return result;
 }
 
+/*
+ * helper routine for delivering "column does not exist" error message
+ *
+ * (Usually we don't have to work this hard, but the general case of field
+ * selection from an arbitrary node needs it.)
+ */
+static void
+unknown_attribute(ParseState *pstate, Node *relref, char *attname,
+                                 int location)
+{
+       RangeTblEntry *rte;
+
+       if (IsA(relref, Var) &&
+               ((Var *) relref)->varattno == InvalidAttrNumber)
+       {
+               /* Reference the RTE by alias not by actual table name */
+               rte = GetRTEByRangeTablePosn(pstate,
+                                                                        ((Var *) relref)->varno,
+                                                                        ((Var *) relref)->varlevelsup);
+               ereport(ERROR,
+                               (errcode(ERRCODE_UNDEFINED_COLUMN),
+                                errmsg("column %s.%s does not exist",
+                                               rte->eref->aliasname, attname),
+                                parser_errposition(pstate, location)));
+       }
+       else
+       {
+               /* Have to do it by reference to the type of the expression */
+               Oid                     relTypeId = exprType(relref);
+
+               if (ISCOMPLEX(relTypeId))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_UNDEFINED_COLUMN),
+                                        errmsg("column \"%s\" not found in data type %s",
+                                                       attname, format_type_be(relTypeId)),
+                                        parser_errposition(pstate, location)));
+               else if (relTypeId == RECORDOID)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_UNDEFINED_COLUMN),
+                          errmsg("could not identify column \"%s\" in record data type",
+                                         attname),
+                                        parser_errposition(pstate, location)));
+               else
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                                        errmsg("column notation .%s applied to type %s, "
+                                                       "which is not a composite type",
+                                                       attname, format_type_be(relTypeId)),
+                                        parser_errposition(pstate, location)));
+       }
+}
+
 static Node *
 transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
 {
        Node       *result = basenode;
        List       *subscripts = NIL;
+       int                     location = exprLocation(basenode);
        ListCell   *i;
 
        /*
@@ -350,10 +403,12 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
                        ereport(ERROR,
                                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                         errmsg("row expansion via \"*\" is not supported here"),
-                                        parser_errposition(pstate, exprLocation(basenode))));
+                                        parser_errposition(pstate, location)));
                }
                else
                {
+                       Node       *newresult;
+
                        Assert(IsA(n, String));
 
                        /* process subscripts before this field selection */
@@ -367,11 +422,14 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
                                                                                                                   NULL);
                        subscripts = NIL;
 
-                       result = ParseFuncOrColumn(pstate,
-                                                                          list_make1(n),
-                                                                          list_make1(result),
-                                                                          false, false, false,
-                                                                          NULL, true, -1);
+                       newresult = ParseFuncOrColumn(pstate,
+                                                                                 list_make1(n),
+                                                                                 list_make1(result),
+                                                                                 false, false, false,
+                                                                                 NULL, true, location);
+                       if (newresult == NULL)
+                               unknown_attribute(pstate, result, strVal(n), location);
+                       result = newresult;
                }
        }
        /* process trailing subscripts, if any */
@@ -387,12 +445,37 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
        return result;
 }
 
+/*
+ * Transform a ColumnRef.
+ *
+ * If you find yourself changing this code, see also ExpandColumnRefStar.
+ */
 static Node *
 transformColumnRef(ParseState *pstate, ColumnRef *cref)
 {
-       int                     numnames = list_length(cref->fields);
-       Node       *node;
+       Node       *node = NULL;
+       char       *nspname = NULL;
+       char       *relname = NULL;
+       char       *colname = NULL;
+       RangeTblEntry *rte;
        int                     levels_up;
+       enum {
+               CRERR_NO_COLUMN,
+               CRERR_NO_RTE,
+               CRERR_WRONG_DB,
+               CRERR_TOO_MANY
+       }                       crerr = CRERR_NO_COLUMN;
+
+       /*
+        * Give the PreParseColumnRefHook, if any, first shot.  If it returns
+        * non-null then that's all, folks.
+        */
+       if (pstate->p_pre_columnref_hook != NULL)
+       {
+               node = (*pstate->p_pre_columnref_hook) (pstate, cref);
+               if (node != NULL)
+                       return node;
+       }
 
        /*----------
         * The allowed syntaxes are:
@@ -417,18 +500,17 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
         * database name; we check it here and then discard it.
         *----------
         */
-       switch (numnames)
+       switch (list_length(cref->fields))
        {
                case 1:
                        {
                                Node       *field1 = (Node *) linitial(cref->fields);
-                               char       *name1;
 
                                Assert(IsA(field1, String));
-                               name1 = strVal(field1);
+                               colname = strVal(field1);
 
                                /* Try to identify as an unqualified column */
-                               node = colNameToVar(pstate, name1, false, cref->location);
+                               node = colNameToVar(pstate, colname, false, cref->location);
 
                                if (node == NULL)
                                {
@@ -441,7 +523,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
                                         * have used VALUE as a column name in the past.)
                                         */
                                        if (pstate->p_value_substitute != NULL &&
-                                               strcmp(name1, "value") == 0)
+                                               strcmp(colname, "value") == 0)
                                        {
                                                node = (Node *) copyObject(pstate->p_value_substitute);
 
@@ -464,17 +546,12 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
                                         * PostQUEL-inspired syntax.  The preferred form now is
                                         * "rel.*".
                                         */
-                                       if (refnameRangeTblEntry(pstate, NULL, name1,
-                                                                                        cref->location,
-                                                                                        &levels_up) != NULL)
-                                               node = transformWholeRowRef(pstate, NULL, name1,
+                                       rte = refnameRangeTblEntry(pstate, NULL, colname,
+                                                                                          cref->location,
+                                                                                          &levels_up);
+                                       if (rte)
+                                               node = transformWholeRowRef(pstate, rte,
                                                                                                        cref->location);
-                                       else
-                                               ereport(ERROR,
-                                                               (errcode(ERRCODE_UNDEFINED_COLUMN),
-                                                                errmsg("column \"%s\" does not exist",
-                                                                               name1),
-                                                                parser_errposition(pstate, cref->location)));
                                }
                                break;
                        }
@@ -482,36 +559,38 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
                        {
                                Node       *field1 = (Node *) linitial(cref->fields);
                                Node       *field2 = (Node *) lsecond(cref->fields);
-                               char       *name1;
-                               char       *name2;
 
                                Assert(IsA(field1, String));
-                               name1 = strVal(field1);
+                               relname = strVal(field1);
+
+                               /* Locate the referenced RTE */
+                               rte = refnameRangeTblEntry(pstate, nspname, relname,
+                                                                                  cref->location,
+                                                                                  &levels_up);
+                               if (rte == NULL)
+                               {
+                                       crerr = CRERR_NO_RTE;
+                                       break;
+                               }
 
                                /* Whole-row reference? */
                                if (IsA(field2, A_Star))
                                {
-                                       node = transformWholeRowRef(pstate, NULL, name1,
-                                                                                               cref->location);
+                                       node = transformWholeRowRef(pstate, rte, cref->location);
                                        break;
                                }
 
                                Assert(IsA(field2, String));
-                               name2 = strVal(field2);
+                               colname = strVal(field2);
 
-                               /* Try to identify as a once-qualified column */
-                               node = qualifiedNameToVar(pstate, NULL, name1, name2,
-                                                                                 cref->location);
+                               /* Try to identify as a column of the RTE */
+                               node = scanRTEForColumn(pstate, rte, colname, cref->location);
                                if (node == NULL)
                                {
-                                       /*
-                                        * Not known as a column of any range-table entry, so try
-                                        * it as a function call.
-                                        */
-                                       node = transformWholeRowRef(pstate, NULL, name1,
-                                                                                               cref->location);
+                                       /* Try it as a function call on the whole row */
+                                       node = transformWholeRowRef(pstate, rte, cref->location);
                                        node = ParseFuncOrColumn(pstate,
-                                                                                        list_make1(makeString(name2)),
+                                                                                        list_make1(makeString(colname)),
                                                                                         list_make1(node),
                                                                                         false, false, false,
                                                                                         NULL, true, cref->location);
@@ -523,36 +602,40 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
                                Node       *field1 = (Node *) linitial(cref->fields);
                                Node       *field2 = (Node *) lsecond(cref->fields);
                                Node       *field3 = (Node *) lthird(cref->fields);
-                               char       *name1;
-                               char       *name2;
-                               char       *name3;
 
                                Assert(IsA(field1, String));
-                               name1 = strVal(field1);
+                               nspname = strVal(field1);
                                Assert(IsA(field2, String));
-                               name2 = strVal(field2);
+                               relname = strVal(field2);
+
+                               /* Locate the referenced RTE */
+                               rte = refnameRangeTblEntry(pstate, nspname, relname,
+                                                                                  cref->location,
+                                                                                  &levels_up);
+                               if (rte == NULL)
+                               {
+                                       crerr = CRERR_NO_RTE;
+                                       break;
+                               }
 
                                /* Whole-row reference? */
                                if (IsA(field3, A_Star))
                                {
-                                       node = transformWholeRowRef(pstate, name1, name2,
-                                                                                               cref->location);
+                                       node = transformWholeRowRef(pstate, rte, cref->location);
                                        break;
                                }
 
                                Assert(IsA(field3, String));
-                               name3 = strVal(field3);
+                               colname = strVal(field3);
 
-                               /* Try to identify as a twice-qualified column */
-                               node = qualifiedNameToVar(pstate, name1, name2, name3,
-                                                                                 cref->location);
+                               /* Try to identify as a column of the RTE */
+                               node = scanRTEForColumn(pstate, rte, colname, cref->location);
                                if (node == NULL)
                                {
-                                       /* Try it as a function call */
-                                       node = transformWholeRowRef(pstate, name1, name2,
-                                                                                               cref->location);
+                                       /* Try it as a function call on the whole row */
+                                       node = transformWholeRowRef(pstate, rte, cref->location);
                                        node = ParseFuncOrColumn(pstate,
-                                                                                        list_make1(makeString(name3)),
+                                                                                        list_make1(makeString(colname)),
                                                                                         list_make1(node),
                                                                                         false, false, false,
                                                                                         NULL, true, cref->location);
@@ -565,49 +648,52 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
                                Node       *field2 = (Node *) lsecond(cref->fields);
                                Node       *field3 = (Node *) lthird(cref->fields);
                                Node       *field4 = (Node *) lfourth(cref->fields);
-                               char       *name1;
-                               char       *name2;
-                               char       *name3;
-                               char       *name4;
+                               char       *catname;
 
                                Assert(IsA(field1, String));
-                               name1 = strVal(field1);
+                               catname = strVal(field1);
                                Assert(IsA(field2, String));
-                               name2 = strVal(field2);
+                               nspname = strVal(field2);
                                Assert(IsA(field3, String));
-                               name3 = strVal(field3);
+                               relname = strVal(field3);
 
                                /*
                                 * 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(cref->fields)),
-                                                        parser_errposition(pstate, cref->location)));
+                               if (strcmp(catname, get_database_name(MyDatabaseId)) != 0)
+                               {
+                                       crerr = CRERR_WRONG_DB;
+                                       break;
+                               }
+
+                               /* Locate the referenced RTE */
+                               rte = refnameRangeTblEntry(pstate, nspname, relname,
+                                                                                  cref->location,
+                                                                                  &levels_up);
+                               if (rte == NULL)
+                               {
+                                       crerr = CRERR_NO_RTE;
+                                       break;
+                               }
 
                                /* Whole-row reference? */
                                if (IsA(field4, A_Star))
                                {
-                                       node = transformWholeRowRef(pstate, name2, name3,
-                                                                                               cref->location);
+                                       node = transformWholeRowRef(pstate, rte, cref->location);
                                        break;
                                }
 
                                Assert(IsA(field4, String));
-                               name4 = strVal(field4);
+                               colname = strVal(field4);
 
-                               /* Try to identify as a twice-qualified column */
-                               node = qualifiedNameToVar(pstate, name2, name3, name4,
-                                                                                 cref->location);
+                               /* Try to identify as a column of the RTE */
+                               node = scanRTEForColumn(pstate, rte, colname, cref->location);
                                if (node == NULL)
                                {
-                                       /* Try it as a function call */
-                                       node = transformWholeRowRef(pstate, name2, name3,
-                                                                                               cref->location);
+                                       /* Try it as a function call on the whole row */
+                                       node = transformWholeRowRef(pstate, rte, cref->location);
                                        node = ParseFuncOrColumn(pstate,
-                                                                                        list_make1(makeString(name4)),
+                                                                                        list_make1(makeString(colname)),
                                                                                         list_make1(node),
                                                                                         false, false, false,
                                                                                         NULL, true, cref->location);
@@ -615,86 +701,101 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
                                break;
                        }
                default:
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_SYNTAX_ERROR),
-                               errmsg("improper qualified name (too many dotted names): %s",
-                                          NameListToString(cref->fields)),
-                                        parser_errposition(pstate, cref->location)));
-                       node = NULL;            /* keep compiler quiet */
+                       crerr = CRERR_TOO_MANY;                 /* too many dotted names */
                        break;
        }
 
-       return node;
-}
-
-/*
- * Locate the parameter type info for the given parameter number, and
- * return a pointer to it.
- */
-static Oid *
-find_param_type(ParseState *pstate, int paramno, int location)
-{
-       Oid                *result;
-
        /*
-        * Find topmost ParseState, which is where paramtype info lives.
+        * Now give the PostParseColumnRefHook, if any, a chance.  We pass the
+        * translation-so-far so that it can throw an error if it wishes in the
+        * case that it has a conflicting interpretation of the ColumnRef.
+        * (If it just translates anyway, we'll throw an error, because we can't
+        * undo whatever effects the preceding steps may have had on the pstate.)
+        * If it returns NULL, use the standard translation, or throw a suitable
+        * error if there is none.
         */
-       while (pstate->parentParseState != NULL)
-               pstate = pstate->parentParseState;
-
-       /* Check parameter number is in range */
-       if (paramno <= 0)                       /* probably can't happen? */
-               ereport(ERROR,
-                               (errcode(ERRCODE_UNDEFINED_PARAMETER),
-                                errmsg("there is no parameter $%d", paramno),
-                                parser_errposition(pstate, location)));
-       if (paramno > pstate->p_numparams)
+       if (pstate->p_post_columnref_hook != NULL)
        {
-               if (!pstate->p_variableparams)
+               Node   *hookresult;
+
+               hookresult = (*pstate->p_post_columnref_hook) (pstate, cref, node);
+               if (node == NULL)
+                       node = hookresult;
+               else if (hookresult != NULL)
                        ereport(ERROR,
-                                       (errcode(ERRCODE_UNDEFINED_PARAMETER),
-                                        errmsg("there is no parameter $%d", paramno),
-                                        parser_errposition(pstate, location)));
-               /* Okay to enlarge param array */
-               if (pstate->p_paramtypes)
-                       pstate->p_paramtypes = (Oid *) repalloc(pstate->p_paramtypes,
-                                                                                                       paramno * sizeof(Oid));
-               else
-                       pstate->p_paramtypes = (Oid *) palloc(paramno * sizeof(Oid));
-               /* Zero out the previously-unreferenced slots */
-               MemSet(pstate->p_paramtypes + pstate->p_numparams,
-                          0,
-                          (paramno - pstate->p_numparams) * sizeof(Oid));
-               pstate->p_numparams = paramno;
+                                       (errcode(ERRCODE_AMBIGUOUS_COLUMN),
+                                        errmsg("column reference \"%s\" is ambiguous",
+                                                       NameListToString(cref->fields)),
+                                        parser_errposition(pstate, cref->location)));
        }
 
-       result = &pstate->p_paramtypes[paramno - 1];
-
-       if (pstate->p_variableparams)
+       /*
+        * Throw error if no translation found.
+        */
+       if (node == NULL)
        {
-               /* If not seen before, initialize to UNKNOWN type */
-               if (*result == InvalidOid)
-                       *result = UNKNOWNOID;
+               switch (crerr)
+               {
+                       case CRERR_NO_COLUMN:
+                               if (relname)
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_UNDEFINED_COLUMN),
+                                                        errmsg("column %s.%s does not exist",
+                                                                       relname, colname),
+                                                        parser_errposition(pstate, cref->location)));
+
+                               else
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_UNDEFINED_COLUMN),
+                                                        errmsg("column \"%s\" does not exist",
+                                                                       colname),
+                                                        parser_errposition(pstate, cref->location)));
+                               break;
+                       case CRERR_NO_RTE:
+                               errorMissingRTE(pstate, makeRangeVar(nspname, relname,
+                                                                                                        cref->location));
+                               break;
+                       case CRERR_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 CRERR_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;
+               }
        }
 
-       return result;
+       return node;
 }
 
 static Node *
 transformParamRef(ParseState *pstate, ParamRef *pref)
 {
-       int                     paramno = pref->number;
-       Oid                *pptype = find_param_type(pstate, paramno, pref->location);
-       Param      *param;
-
-       param = makeNode(Param);
-       param->paramkind = PARAM_EXTERN;
-       param->paramid = paramno;
-       param->paramtype = *pptype;
-       param->paramtypmod = -1;
-       param->location = pref->location;
-
-       return (Node *) param;
+       Node       *result;
+
+       /*
+        * The core parser knows nothing about Params.  If a hook is supplied,
+        * call it.  If not, or if the hook returns NULL, throw a generic error.
+        */
+       if (pstate->p_paramref_hook != NULL)
+               result = (*pstate->p_paramref_hook) (pstate, pref);
+       else
+               result = NULL;
+
+       if (result == NULL)
+               ereport(ERROR,
+                               (errcode(ERRCODE_UNDEFINED_PARAMETER),
+                                errmsg("there is no parameter $%d", pref->number),
+                                parser_errposition(pstate, pref->location)));
+
+       return result;
 }
 
 /* Test whether an a_expr is a plain NULL constant or not */
@@ -1861,26 +1962,33 @@ transformCurrentOfExpr(ParseState *pstate, CurrentOfExpr *cexpr)
                                                                          &sublevels_up);
        Assert(sublevels_up == 0);
 
-       /* If a parameter is used, it must be of type REFCURSOR */
+       /*
+        * If a parameter is used, it must be of type REFCURSOR.  To verify
+        * that the parameter hooks think so, build a dummy ParamRef and
+        * transform it.
+        */
        if (cexpr->cursor_name == NULL)
        {
-               Oid                *pptype = find_param_type(pstate, cexpr->cursor_param, -1);
-
-               if (pstate->p_variableparams && *pptype == UNKNOWNOID)
-               {
-                       /* resolve unknown param type as REFCURSOR */
-                       *pptype = REFCURSOROID;
-               }
-               else if (*pptype != REFCURSOROID)
-               {
+               ParamRef *p = makeNode(ParamRef);
+               Node   *n;
+
+               p->number = cexpr->cursor_param;
+               p->location = -1;
+               n = transformParamRef(pstate, p);
+               /* Allow the parameter type to be inferred if it's unknown */
+               if (exprType(n) == UNKNOWNOID)
+                       n = coerce_type(pstate, n, UNKNOWNOID,
+                                                       REFCURSOROID, -1,
+                                                       COERCION_IMPLICIT, COERCE_IMPLICIT_CAST,
+                                                       -1);
+               if (exprType(n) != REFCURSOROID)
                        ereport(ERROR,
                                        (errcode(ERRCODE_AMBIGUOUS_PARAMETER),
                                         errmsg("inconsistent types deduced for parameter $%d",
                                                        cexpr->cursor_param),
                                         errdetail("%s versus %s",
-                                                          format_type_be(*pptype),
+                                                          format_type_be(exprType(n)),
                                                           format_type_be(REFCURSOROID))));
-               }
        }
 
        return (Node *) cexpr;
@@ -1896,23 +2004,14 @@ transformCurrentOfExpr(ParseState *pstate, CurrentOfExpr *cexpr)
  * a rowtype; either a named composite type, or RECORD.
  */
 static Node *
-transformWholeRowRef(ParseState *pstate, char *schemaname, char *relname,
-                                        int location)
+transformWholeRowRef(ParseState *pstate, RangeTblEntry *rte, int location)
 {
        Var                *result;
-       RangeTblEntry *rte;
        int                     vnum;
        int                     sublevels_up;
        Oid                     toid;
 
-       /* Look up the referenced RTE, failing if not present */
-
-       rte = refnameRangeTblEntry(pstate, schemaname, relname, location,
-                                                          &sublevels_up);
-
-       if (rte == NULL)
-               errorMissingRTE(pstate,
-                                               makeRangeVar(schemaname, relname, location));
+       /* Find the RTE's rangetable location */
 
        vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
 
index e752dd8d1ec19d3d2911a9d668bc35403522176b..d86bd1732f9c095889e415361825cd320a346c49 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.217 2009/10/08 02:39:23 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.218 2009/10/31 01:41:31 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -33,8 +33,6 @@
 static Oid     FuncNameAsType(List *funcname);
 static Node *ParseComplexProjection(ParseState *pstate, char *funcname,
                                           Node *first_arg, int location);
-static void unknown_attribute(ParseState *pstate, Node *relref, char *attname,
-                                 int location);
 
 
 /*
@@ -53,6 +51,8 @@ static void unknown_attribute(ParseState *pstate, Node *relref, char *attname,
  *     not to affect the semantics.  When is_column is true, we should have
  *     a single argument (the putative table), unqualified function name
  *     equal to the column name, and no aggregate or variadic decoration.
+ *     Also, when is_column is true, we return NULL on failure rather than
+ *     reporting a no-such-function error.
  *
  *     The argument expressions (in fargs) must have been transformed already.
  */
@@ -253,16 +253,11 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
                /*
                 * Oops.  Time to die.
                 *
-                * If we are dealing with the attribute notation rel.function, give an
-                * error message that is appropriate for that case.
+                * If we are dealing with the attribute notation rel.function,
+                * let the caller handle failure.
                 */
                if (is_column)
-               {
-                       Assert(nargs == 1);
-                       Assert(list_length(funcname) == 1);
-                       unknown_attribute(pstate, first_arg, strVal(linitial(funcname)),
-                                                         location);
-               }
+                       return NULL;
 
                /*
                 * Else generate a detailed complaint for a function
@@ -1343,55 +1338,6 @@ ParseComplexProjection(ParseState *pstate, char *funcname, Node *first_arg,
        return NULL;                            /* funcname does not match any column */
 }
 
-/*
- * helper routine for delivering "column does not exist" error message
- */
-static void
-unknown_attribute(ParseState *pstate, Node *relref, char *attname,
-                                 int location)
-{
-       RangeTblEntry *rte;
-
-       if (IsA(relref, Var) &&
-               ((Var *) relref)->varattno == InvalidAttrNumber)
-       {
-               /* Reference the RTE by alias not by actual table name */
-               rte = GetRTEByRangeTablePosn(pstate,
-                                                                        ((Var *) relref)->varno,
-                                                                        ((Var *) relref)->varlevelsup);
-               ereport(ERROR,
-                               (errcode(ERRCODE_UNDEFINED_COLUMN),
-                                errmsg("column %s.%s does not exist",
-                                               rte->eref->aliasname, attname),
-                                parser_errposition(pstate, location)));
-       }
-       else
-       {
-               /* Have to do it by reference to the type of the expression */
-               Oid                     relTypeId = exprType(relref);
-
-               if (ISCOMPLEX(relTypeId))
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_UNDEFINED_COLUMN),
-                                        errmsg("column \"%s\" not found in data type %s",
-                                                       attname, format_type_be(relTypeId)),
-                                        parser_errposition(pstate, location)));
-               else if (relTypeId == RECORDOID)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_UNDEFINED_COLUMN),
-                          errmsg("could not identify column \"%s\" in record data type",
-                                         attname),
-                                        parser_errposition(pstate, location)));
-               else
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-                                        errmsg("column notation .%s applied to type %s, "
-                                                       "which is not a composite type",
-                                                       attname, format_type_be(relTypeId)),
-                                        parser_errposition(pstate, location)));
-       }
-}
-
 /*
  * funcname_signature_string
  *             Build a string representing a function name, including arg types.
index f775850e049cd22d6e130bf827ae995dce222806..0dea02c71b51af7ff163dfb617453d6c87e59f73 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/parse_node.c,v 1.105 2009/06/11 14:49:00 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/parser/parse_node.c,v 1.106 2009/10/31 01:41:31 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,7 +53,12 @@ make_parsestate(ParseState *parentParseState)
        if (parentParseState)
        {
                pstate->p_sourcetext = parentParseState->p_sourcetext;
-               pstate->p_variableparams = parentParseState->p_variableparams;
+               /* all hooks are copied from parent */
+               pstate->p_pre_columnref_hook = parentParseState->p_pre_columnref_hook;
+               pstate->p_post_columnref_hook = parentParseState->p_post_columnref_hook;
+               pstate->p_paramref_hook = parentParseState->p_paramref_hook;
+               pstate->p_coerce_param_hook = parentParseState->p_coerce_param_hook;
+               pstate->p_ref_hook_state = parentParseState->p_ref_hook_state;
        }
 
        return pstate;
diff --git a/src/backend/parser/parse_param.c b/src/backend/parser/parse_param.c
new file mode 100644 (file)
index 0000000..1c39970
--- /dev/null
@@ -0,0 +1,307 @@
+/*-------------------------------------------------------------------------
+ *
+ * parse_param.c
+ *       handle parameters in parser
+ *
+ * This code covers two cases that are used within the core backend:
+ *             * a fixed list of parameters with known types
+ *             * an expandable list of parameters whose types can optionally
+ *               be determined from context
+ * In both cases, only explicit $n references (ParamRef nodes) are supported.
+ *
+ * Note that other approaches to parameters are possible using the parser
+ * hooks defined in ParseState.
+ *
+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *       $PostgreSQL: pgsql/src/backend/parser/parse_param.c,v 2.1 2009/10/31 01:41:31 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include <limits.h>
+
+#include "catalog/pg_type.h"
+#include "nodes/nodeFuncs.h"
+#include "parser/parse_param.h"
+#include "utils/builtins.h"
+
+
+typedef struct FixedParamState
+{
+       Oid                *paramTypes;         /* array of parameter type OIDs */
+       int                     numParams;              /* number of array entries */
+} FixedParamState;
+
+/*
+ * In the varparams case, the caller-supplied OID array (if any) can be
+ * re-palloc'd larger at need.  A zero array entry means that parameter number
+ * hasn't been seen, while UNKNOWNOID means the parameter has been used but
+ * its type is not yet known.
+ */
+typedef struct VarParamState
+{
+       Oid               **paramTypes;         /* array of parameter type OIDs */
+       int                *numParams;          /* number of array entries */
+} VarParamState;
+
+static Node *fixed_paramref_hook(ParseState *pstate, ParamRef *pref);
+static Node *variable_paramref_hook(ParseState *pstate, ParamRef *pref);
+static Node *variable_coerce_param_hook(ParseState *pstate, Param *param,
+                                                                               Oid targetTypeId, int32 targetTypeMod,
+                                                                               int location);
+static bool check_parameter_resolution_walker(Node *node, ParseState *pstate);
+
+
+/*
+ * Set up to process a query containing references to fixed parameters.
+ */
+void
+parse_fixed_parameters(ParseState *pstate,
+                                          Oid *paramTypes, int numParams)
+{
+       FixedParamState *parstate = palloc(sizeof(FixedParamState));
+
+       parstate->paramTypes = paramTypes;
+       parstate->numParams = numParams;
+       pstate->p_ref_hook_state = (void *) parstate;
+       pstate->p_paramref_hook = fixed_paramref_hook;
+       /* no need to use p_coerce_param_hook */
+}
+
+/*
+ * Set up to process a query containing references to variable parameters.
+ */
+void
+parse_variable_parameters(ParseState *pstate,
+                                                 Oid **paramTypes, int *numParams)
+{
+       VarParamState *parstate = palloc(sizeof(VarParamState));
+
+       parstate->paramTypes = paramTypes;
+       parstate->numParams = numParams;
+       pstate->p_ref_hook_state = (void *) parstate;
+       pstate->p_paramref_hook = variable_paramref_hook;
+       pstate->p_coerce_param_hook = variable_coerce_param_hook;
+}
+
+/*
+ * Transform a ParamRef using fixed parameter types.
+ */
+static Node *
+fixed_paramref_hook(ParseState *pstate, ParamRef *pref)
+{
+       FixedParamState *parstate = (FixedParamState *) pstate->p_ref_hook_state;
+       int                     paramno = pref->number;
+       Param      *param;
+
+       /* Check parameter number is in range */
+       if (paramno <= 0 || paramno > parstate->numParams)
+               ereport(ERROR,
+                               (errcode(ERRCODE_UNDEFINED_PARAMETER),
+                                errmsg("there is no parameter $%d", paramno),
+                                parser_errposition(pstate, pref->location)));
+
+       param = makeNode(Param);
+       param->paramkind = PARAM_EXTERN;
+       param->paramid = paramno;
+       param->paramtype = parstate->paramTypes[paramno - 1];
+       param->paramtypmod = -1;
+       param->location = pref->location;
+
+       return (Node *) param;
+}
+
+/*
+ * Transform a ParamRef using variable parameter types.
+ *
+ * The only difference here is we must enlarge the parameter type array
+ * as needed.
+ */
+static Node *
+variable_paramref_hook(ParseState *pstate, ParamRef *pref)
+{
+       VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
+       int                     paramno = pref->number;
+       Oid                *pptype;
+       Param      *param;
+
+       /* Check parameter number is in range */
+       if (paramno <= 0 || paramno > INT_MAX / sizeof(Oid))
+               ereport(ERROR,
+                               (errcode(ERRCODE_UNDEFINED_PARAMETER),
+                                errmsg("there is no parameter $%d", paramno),
+                                parser_errposition(pstate, pref->location)));
+       if (paramno > *parstate->numParams)
+       {
+               /* Need to enlarge param array */
+               if (*parstate->paramTypes)
+                       *parstate->paramTypes = (Oid *) repalloc(*parstate->paramTypes,
+                                                                                                        paramno * sizeof(Oid));
+               else
+                       *parstate->paramTypes = (Oid *) palloc(paramno * sizeof(Oid));
+               /* Zero out the previously-unreferenced slots */
+               MemSet(*parstate->paramTypes + *parstate->numParams,
+                          0,
+                          (paramno - *parstate->numParams) * sizeof(Oid));
+               *parstate->numParams = paramno;
+       }
+
+       /* Locate param's slot in array */
+       pptype = &(*parstate->paramTypes)[paramno - 1];
+
+       /* If not seen before, initialize to UNKNOWN type */
+       if (*pptype == InvalidOid)
+               *pptype = UNKNOWNOID;
+
+       param = makeNode(Param);
+       param->paramkind = PARAM_EXTERN;
+       param->paramid = paramno;
+       param->paramtype = *pptype;
+       param->paramtypmod = -1;
+       param->location = pref->location;
+
+       return (Node *) param;
+}
+
+/*
+ * Coerce a Param to a query-requested datatype, in the varparams case.
+ */
+static Node *
+variable_coerce_param_hook(ParseState *pstate, Param *param,
+                                                  Oid targetTypeId, int32 targetTypeMod,
+                                                  int location)
+{
+       if (param->paramkind == PARAM_EXTERN && param->paramtype == UNKNOWNOID)
+       {
+               /*
+                * Input is a Param of previously undetermined type, and we want to
+                * update our knowledge of the Param's type.
+                */
+               VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
+               Oid                *paramTypes = *parstate->paramTypes;
+               int                     paramno = param->paramid;
+
+               if (paramno <= 0 ||             /* shouldn't happen, but... */
+                       paramno > *parstate->numParams)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_UNDEFINED_PARAMETER),
+                                        errmsg("there is no parameter $%d", paramno),
+                                        parser_errposition(pstate, param->location)));
+
+               if (paramTypes[paramno - 1] == UNKNOWNOID)
+               {
+                       /* We've successfully resolved the type */
+                       paramTypes[paramno - 1] = targetTypeId;
+               }
+               else if (paramTypes[paramno - 1] == targetTypeId)
+               {
+                       /* We previously resolved the type, and it matches */
+               }
+               else
+               {
+                       /* Ooops */
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_AMBIGUOUS_PARAMETER),
+                                        errmsg("inconsistent types deduced for parameter $%d",
+                                                       paramno),
+                                        errdetail("%s versus %s",
+                                                          format_type_be(paramTypes[paramno - 1]),
+                                                          format_type_be(targetTypeId)),
+                                        parser_errposition(pstate, param->location)));
+               }
+
+               param->paramtype = targetTypeId;
+
+               /*
+                * Note: it is tempting here to set the Param's paramtypmod to
+                * targetTypeMod, but that is probably unwise because we have no
+                * infrastructure that enforces that the value delivered for a Param
+                * will match any particular typmod.  Leaving it -1 ensures that a
+                * run-time length check/coercion will occur if needed.
+                */
+               param->paramtypmod = -1;
+
+               /* Use the leftmost of the param's and coercion's locations */
+               if (location >= 0 &&
+                       (param->location < 0 || location < param->location))
+                       param->location = location;
+
+               return (Node *) param;
+       }
+
+       /* Else signal to proceed with normal coercion */
+       return NULL;
+}
+
+/*
+ * Check for consistent assignment of variable parameters after completion
+ * of parsing with parse_variable_parameters.
+ *
+ * Note: this code intentionally does not check that all parameter positions
+ * were used, nor that all got non-UNKNOWN types assigned.  Caller of parser
+ * should enforce that if it's important.
+ */
+void
+check_variable_parameters(ParseState *pstate, Query *query)
+{
+       VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
+
+       /* If numParams is zero then no Params were generated, so no work */
+       if (*parstate->numParams > 0)
+               (void) query_tree_walker(query,
+                                                                check_parameter_resolution_walker,
+                                                                (void *) pstate, 0);
+}
+
+/*
+ * Traverse a fully-analyzed tree to verify that parameter symbols
+ * match their types.  We need this because some Params might still
+ * be UNKNOWN, if there wasn't anything to force their coercion,
+ * and yet other instances seen later might have gotten coerced.
+ */
+static bool
+check_parameter_resolution_walker(Node *node, ParseState *pstate)
+{
+       if (node == NULL)
+               return false;
+       if (IsA(node, Param))
+       {
+               Param      *param = (Param *) node;
+
+               if (param->paramkind == PARAM_EXTERN)
+               {
+                       VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
+                       int                     paramno = param->paramid;
+
+                       if (paramno <= 0 || /* shouldn't happen, but... */
+                               paramno > *parstate->numParams)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_UNDEFINED_PARAMETER),
+                                                errmsg("there is no parameter $%d", paramno),
+                                                parser_errposition(pstate, param->location)));
+
+                       if (param->paramtype != (*parstate->paramTypes)[paramno - 1])
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_AMBIGUOUS_PARAMETER),
+                                        errmsg("could not determine data type of parameter $%d",
+                                                       paramno),
+                                                parser_errposition(pstate, param->location)));
+               }
+               return false;
+       }
+       if (IsA(node, Query))
+       {
+               /* Recurse into RTE subquery or not-yet-planned sublink subquery */
+               return query_tree_walker((Query *) node,
+                                                                check_parameter_resolution_walker,
+                                                                (void *) pstate, 0);
+       }
+       return expression_tree_walker(node, check_parameter_resolution_walker,
+                                                                 (void *) pstate);
+}
index 9b35577ec3c8e2606b398495880b566ec7ecce83..184bc7c47a099710e59b9499c2c6a73f3b88d6ab 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.146 2009/10/27 17:11:18 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.147 2009/10/31 01:41:31 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -86,7 +86,17 @@ refnameRangeTblEntry(ParseState *pstate,
        {
                Oid                     namespaceId;
 
-               namespaceId = LookupExplicitNamespace(schemaname);
+               /*
+                * We can use LookupNamespaceNoError() here because we are only
+                * interested in finding existing RTEs.  Checking USAGE permission
+                * on the schema is unnecessary since it would have already been
+                * checked when the RTE was made.  Furthermore, we want to report
+                * "RTE not found", not "no permissions for schema", if the name
+                * happens to match a schema name the user hasn't got access to.
+                */
+               namespaceId = LookupNamespaceNoError(schemaname);
+               if (!OidIsValid(relId))
+                       return NULL;
                relId = get_relname_relid(refname, namespaceId);
                if (!OidIsValid(relId))
                        return NULL;
@@ -555,32 +565,6 @@ colNameToVar(ParseState *pstate, char *colname, bool localonly,
        return result;
 }
 
-/*
- * qualifiedNameToVar
- *       Search for a qualified column name: either refname.colname or
- *       schemaname.relname.colname.
- *
- *       If found, return the appropriate Var node.
- *       If not found, return NULL.  If the name proves ambiguous, raise error.
- */
-Node *
-qualifiedNameToVar(ParseState *pstate,
-                                  char *schemaname,
-                                  char *refname,
-                                  char *colname,
-                                  int location)
-{
-       RangeTblEntry *rte;
-       int                     sublevels_up;
-
-       rte = refnameRangeTblEntry(pstate, schemaname, refname, location,
-                                                          &sublevels_up);
-       if (rte == NULL)
-               return NULL;
-
-       return scanRTEForColumn(pstate, rte, colname, location);
-}
-
 /*
  * markRTEForSelectPriv
  *        Mark the specified column of an RTE as requiring SELECT privilege
@@ -2389,7 +2373,8 @@ errorMissingRTE(ParseState *pstate, RangeVar *relation)
 
        /*
         * Check to see if there are any potential matches in the query's
-        * rangetable.
+        * rangetable.  (Note: cases involving a bad schema name in the
+        * RangeVar will throw error immediately here.  That seems OK.)
         */
        rte = searchRangeTable(pstate, relation);
 
index 3aff83955b8ffca19dd80de947314b03d8e77d58..ce3f51ca6e5c0009ca054b6c4432506236f062ac 100644 (file)
@@ -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;
index 2c2b88951a354b834a2685acb918b10eaa4b6da1..55eed83345fc7a7a2bd37e8ca322bbdc897b3ab7 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/namespace.h,v 1.60 2009/10/08 02:39:23 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/namespace.h,v 1.61 2009/10/31 01:41:31 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -89,6 +89,7 @@ extern bool TSConfigIsVisible(Oid cfgid);
 extern void DeconstructQualifiedName(List *names,
                                                 char **nspname_p,
                                                 char **objname_p);
+extern Oid     LookupNamespaceNoError(const char *nspname);
 extern Oid     LookupExplicitNamespace(const char *nspname);
 
 extern Oid     LookupCreationNamespace(const char *nspname);
index 5eb446f12042b695b16f1dd8afec56126b0215b0..27aa7f2c6126dea53eb4f427841f3359e9fa022b 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/parser/parse_node.h,v 1.65 2009/10/27 17:11:18 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/parser/parse_node.h,v 1.66 2009/10/31 01:41:31 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "nodes/parsenodes.h"
 #include "utils/relcache.h"
 
+
+/*
+ * Function signatures for parser hooks
+ */
+typedef struct ParseState ParseState;
+
+typedef Node * (*PreParseColumnRefHook) (ParseState *pstate, ColumnRef *cref);
+typedef Node * (*PostParseColumnRefHook) (ParseState *pstate, ColumnRef *cref, Node *var);
+typedef Node * (*ParseParamRefHook) (ParseState *pstate, ParamRef *pref);
+typedef Node * (*CoerceParamHook) (ParseState *pstate, Param *param,
+                                                                  Oid targetTypeId, int32 targetTypeMod,
+                                                                  int location);
+
+
 /*
  * State information used during parse analysis
  *
  * afterwards (so that any resjunk tlist items needed for the sort/group
  * clauses end up at the end of the query tlist).  A WindowDef's location in
  * this list, counting from 1, is the winref number to use to reference it.
- *
- * p_paramtypes: an array of p_numparams type OIDs for $n parameter symbols
- * (zeroth entry in array corresponds to $1).  If p_variableparams is true, the
- * set of param types is not predetermined; in that case, a zero array entry
- * means that parameter number hasn't been seen, and UNKNOWNOID means the
- * parameter has been used but its type is not yet known.  NOTE: in a stack
- * of ParseStates, only the topmost ParseState contains paramtype info; but
- * we copy the p_variableparams flag down to the child nodes for speed in
- * coerce_type.
  */
-typedef struct ParseState
+struct ParseState
 {
        struct ParseState *parentParseState;            /* stack link */
        const char *p_sourcetext;       /* source text, or NULL if not available */
@@ -92,12 +97,9 @@ typedef struct ParseState
        List       *p_future_ctes;      /* common table exprs not yet in namespace */
        CommonTableExpr *p_parent_cte;          /* this query's containing CTE */
        List       *p_windowdefs;       /* raw representations of window clauses */
-       Oid                *p_paramtypes;       /* OIDs of types for $n parameter symbols */
-       int                     p_numparams;    /* allocated size of p_paramtypes[] */
        int                     p_next_resno;   /* next targetlist resno to assign */
        List       *p_locking_clause;           /* raw FOR UPDATE/FOR SHARE info */
        Node       *p_value_substitute;         /* what to replace VALUE with, if any */
-       bool            p_variableparams;
        bool            p_hasAggs;
        bool            p_hasWindowFuncs;
        bool            p_hasSubLinks;
@@ -106,7 +108,17 @@ typedef struct ParseState
        bool            p_locked_from_parent;
        Relation        p_target_relation;
        RangeTblEntry *p_target_rangetblentry;
-} ParseState;
+
+       /*
+        * Optional hook functions for parser callbacks.  These are null unless
+        * set up by the caller of make_parsestate.
+        */
+       PreParseColumnRefHook p_pre_columnref_hook;
+       PostParseColumnRefHook p_post_columnref_hook;
+       ParseParamRefHook p_paramref_hook;
+       CoerceParamHook p_coerce_param_hook;
+       void       *p_ref_hook_state;   /* common passthrough link for above */
+};
 
 /* Support for parser_errposition_callback function */
 typedef struct ParseCallbackState
diff --git a/src/include/parser/parse_param.h b/src/include/parser/parse_param.h
new file mode 100644 (file)
index 0000000..1423cde
--- /dev/null
@@ -0,0 +1,24 @@
+/*-------------------------------------------------------------------------
+ *
+ * parse_param.h
+ *       handle parameters in parser
+ *
+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $PostgreSQL: pgsql/src/include/parser/parse_param.h,v 1.1 2009/10/31 01:41:31 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PARSE_PARAM_H
+#define PARSE_PARAM_H
+
+#include "parser/parse_node.h"
+
+extern void parse_fixed_parameters(ParseState *pstate,
+                                                                  Oid *paramTypes, int numParams);
+extern void parse_variable_parameters(ParseState *pstate,
+                                                                         Oid **paramTypes, int *numParams);
+extern void check_variable_parameters(ParseState *pstate, Query *query);
+
+#endif   /* PARSE_PARAM_H */
index 96b60de480e6ecea2b4bc3c436f02e9dcc547ed3..006dfa75f064ac1d93b3423102bd348688e193bd 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/parser/parse_relation.h,v 1.66 2009/10/27 17:11:18 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/parser/parse_relation.h,v 1.67 2009/10/31 01:41:31 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -38,11 +38,6 @@ extern Node *scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte,
                                 char *colname, int location);
 extern Node *colNameToVar(ParseState *pstate, char *colname, bool localonly,
                         int location);
-extern Node *qualifiedNameToVar(ParseState *pstate,
-                                  char *schemaname,
-                                  char *refname,
-                                  char *colname,
-                                  int location);
 extern void markVarForSelectPriv(ParseState *pstate, Var *var,
                                         RangeTblEntry *rte);
 extern Relation parserOpenTable(ParseState *pstate, const RangeVar *relation,