* 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 $
*
*-------------------------------------------------------------------------
*/
*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
* 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.
#
# 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 $
#
#-------------------------------------------------------------------------
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
-$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
======
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)
* 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 $
*
*-------------------------------------------------------------------------
*/
#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"
ExplainStmt *stmt);
static void transformLockingClause(ParseState *pstate, Query *qry,
LockingClause *lc, bool pushedDown);
-static bool check_parameter_resolution_walker(Node *node, ParseState *pstate);
/*
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);
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);
*
* 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;
{
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));
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);
-}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
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;
/*
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 */
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 */
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:
* 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)
{
* 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);
* 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;
}
{
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);
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);
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);
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 */
&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;
* 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);
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
/*
* 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.
*/
/*
* 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
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.
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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;
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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);
+}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
{
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;
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
/*
* 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);
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
* 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);
}
}
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);
/* 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
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;
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
* 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 */
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;
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
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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 */
* 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 $
*
*-------------------------------------------------------------------------
*/
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,