aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/parse_expr.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser/parse_expr.c')
-rw-r--r--src/backend/parser/parse_expr.c431
1 files changed, 265 insertions, 166 deletions
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 2146329ad89..1e76d3b546f 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -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);