postgresql/src/backend/parser/parse_expr.c

2637 lines
71 KiB
C
Raw Normal View History

/*-------------------------------------------------------------------------
*
* parse_expr.c
* handle expressions in parser
*
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
2010-09-20 22:08:53 +02:00
* src/backend/parser/parse_expr.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "catalog/pg_type.h"
2003-06-27 19:07:03 +02:00
#include "commands/dbcommands.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/var.h"
#include "parser/analyze.h"
1999-07-16 07:00:38 +02:00
#include "parser/parse_coerce.h"
#include "parser/parse_collate.h"
#include "parser/parse_expr.h"
#include "parser/parse_func.h"
#include "parser/parse_oper.h"
#include "parser/parse_relation.h"
#include "parser/parse_target.h"
2000-06-15 05:33:12 +02:00
#include "parser/parse_type.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/xml.h"
bool Transform_null_equals = false;
static Node *transformExprRecurse(ParseState *pstate, Node *expr);
static Node *transformParamRef(ParseState *pstate, ParamRef *pref);
static Node *transformAExprOp(ParseState *pstate, A_Expr *a);
static Node *transformAExprAnd(ParseState *pstate, A_Expr *a);
static Node *transformAExprOr(ParseState *pstate, A_Expr *a);
static Node *transformAExprNot(ParseState *pstate, A_Expr *a);
static Node *transformAExprOpAny(ParseState *pstate, A_Expr *a);
static Node *transformAExprOpAll(ParseState *pstate, A_Expr *a);
static Node *transformAExprDistinct(ParseState *pstate, A_Expr *a);
static Node *transformAExprNullIf(ParseState *pstate, A_Expr *a);
static Node *transformAExprOf(ParseState *pstate, A_Expr *a);
static Node *transformAExprIn(ParseState *pstate, A_Expr *a);
static Node *transformFuncCall(ParseState *pstate, FuncCall *fn);
static Node *transformCaseExpr(ParseState *pstate, CaseExpr *c);
static Node *transformSubLink(ParseState *pstate, SubLink *sublink);
static Node *transformArrayExpr(ParseState *pstate, A_ArrayExpr *a,
Oid array_type, Oid element_type, int32 typmod);
static Node *transformRowExpr(ParseState *pstate, RowExpr *r);
static Node *transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c);
static Node *transformMinMaxExpr(ParseState *pstate, MinMaxExpr *m);
static Node *transformXmlExpr(ParseState *pstate, XmlExpr *x);
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, RangeTblEntry *rte,
2010-02-26 03:01:40 +01:00
int location);
static Node *transformIndirection(ParseState *pstate, Node *basenode,
List *indirection);
static Node *transformTypeCast(ParseState *pstate, TypeCast *tc);
static Node *transformCollateClause(ParseState *pstate, CollateClause *c);
static Node *make_row_comparison_op(ParseState *pstate, List *opname,
2006-10-04 02:30:14 +02:00
List *largs, List *rargs, int location);
static Node *make_row_distinct_op(ParseState *pstate, List *opname,
RowExpr *lrow, RowExpr *rrow, int location);
static Expr *make_distinct_op(ParseState *pstate, List *opname,
Node *ltree, Node *rtree, int location);
/*
* transformExpr -
* Analyze and transform expressions. Type checking and type casting is
* done here. The optimizer and the executor cannot handle the original
* (raw) expressions collected by the parse tree. Hence the transformation
* here.
*
* NOTE: there are various cases in which this routine will get applied to
* an already-transformed expression. Some examples:
* 1. At least one construct (BETWEEN/AND) puts the same nodes
* into two branches of the parse tree; hence, some nodes
* are transformed twice.
* 2. Another way it can happen is that coercion of an operator or
* function argument to the required type (via coerce_type())
* can apply transformExpr to an already-transformed subexpression.
* An example here is "SELECT count(*) + 1.0 FROM table".
* 3. CREATE TABLE t1 (LIKE t2 INCLUDING INDEXES) can pass in
* already-transformed index expressions.
* While it might be possible to eliminate these cases, the path of
* least resistance so far has been to ensure that transformExpr() does
* no damage if applied to an already-transformed tree. This is pretty
* easy for cases where the transformation replaces one node type with
* another, such as A_Const => Const; we just do nothing when handed
* a Const. More care is needed for node types that are used as both
* input and output of transformExpr; see SubLink for example.
*/
Node *
transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKind)
{
Node *result;
ParseExprKind sv_expr_kind;
/* Save and restore identity of expression type we're parsing */
Assert(exprKind != EXPR_KIND_NONE);
sv_expr_kind = pstate->p_expr_kind;
pstate->p_expr_kind = exprKind;
result = transformExprRecurse(pstate, expr);
pstate->p_expr_kind = sv_expr_kind;
return result;
}
static Node *
transformExprRecurse(ParseState *pstate, Node *expr)
{
Node *result;
if (expr == NULL)
return NULL;
/* Guard against stack overflow due to overly complex expressions */
check_stack_depth();
switch (nodeTag(expr))
{
case T_ColumnRef:
result = transformColumnRef(pstate, (ColumnRef *) expr);
break;
case T_ParamRef:
result = transformParamRef(pstate, (ParamRef *) expr);
break;
case T_A_Const:
{
A_Const *con = (A_Const *) expr;
Value *val = &con->val;
result = (Node *) make_const(pstate, val, con->location);
break;
}
case T_A_Indirection:
{
2004-08-29 07:07:03 +02:00
A_Indirection *ind = (A_Indirection *) expr;
result = transformExprRecurse(pstate, ind->arg);
result = transformIndirection(pstate, result,
ind->indirection);
break;
}
case T_A_ArrayExpr:
result = transformArrayExpr(pstate, (A_ArrayExpr *) expr,
InvalidOid, InvalidOid, -1);
break;
case T_TypeCast:
{
TypeCast *tc = (TypeCast *) expr;
/*
* If the subject of the typecast is an ARRAY[] construct and
* the target type is an array type, we invoke
* transformArrayExpr() directly so that we can pass down the
* type information. This avoids some cases where
* transformArrayExpr() might not infer the correct type.
*/
if (IsA(tc->arg, A_ArrayExpr))
{
Oid targetType;
Oid elementType;
int32 targetTypmod;
typenameTypeIdAndMod(pstate, tc->typeName,
&targetType, &targetTypmod);
2011-04-10 17:42:00 +02:00
Improve handling of domains over arrays. This patch eliminates various bizarre behaviors caused by sloppy thinking about the difference between a domain type and its underlying array type. In particular, the operation of updating one element of such an array has to be considered as yielding a value of the underlying array type, *not* a value of the domain, because there's no assurance that the domain's CHECK constraints are still satisfied. If we're intending to store the result back into a domain column, we have to re-cast to the domain type so that constraints are re-checked. For similar reasons, such a domain can't be blindly matched to an ANYARRAY polymorphic parameter, because the polymorphic function is likely to apply array-ish operations that could invalidate the domain constraints. For the moment, we just forbid such matching. We might later wish to insert an automatic downcast to the underlying array type, but such a change should also change matching of domains to ANYELEMENT for consistency. To ensure that all such logic is rechecked, this patch removes the original hack of setting a domain's pg_type.typelem field to match its base type; the typelem will always be zero instead. In those places where it's really okay to look through the domain type with no other logic changes, use the newly added get_base_element_type function in place of get_element_type. catversion bumped due to change in pg_type contents. Per bug #5717 from Richard Huxton and subsequent discussion.
2010-10-21 22:07:17 +02:00
/*
* If target is a domain over array, work with the base
* array type here. transformTypeCast below will cast the
* array type to the domain. In the usual case that the
* target is not a domain, transformTypeCast is a no-op.
*/
targetType = getBaseTypeAndTypmod(targetType,
&targetTypmod);
elementType = get_element_type(targetType);
if (OidIsValid(elementType))
{
tc = copyObject(tc);
tc->arg = transformArrayExpr(pstate,
(A_ArrayExpr *) tc->arg,
targetType,
elementType,
targetTypmod);
}
}
result = transformTypeCast(pstate, tc);
break;
}
case T_CollateClause:
result = transformCollateClause(pstate, (CollateClause *) expr);
break;
case T_A_Expr:
{
A_Expr *a = (A_Expr *) expr;
switch (a->kind)
{
case AEXPR_OP:
result = transformAExprOp(pstate, a);
break;
case AEXPR_AND:
result = transformAExprAnd(pstate, a);
break;
case AEXPR_OR:
result = transformAExprOr(pstate, a);
break;
case AEXPR_NOT:
result = transformAExprNot(pstate, a);
break;
case AEXPR_OP_ANY:
result = transformAExprOpAny(pstate, a);
break;
case AEXPR_OP_ALL:
result = transformAExprOpAll(pstate, a);
break;
case AEXPR_DISTINCT:
result = transformAExprDistinct(pstate, a);
break;
case AEXPR_NULLIF:
result = transformAExprNullIf(pstate, a);
break;
case AEXPR_OF:
result = transformAExprOf(pstate, a);
break;
case AEXPR_IN:
result = transformAExprIn(pstate, a);
break;
default:
elog(ERROR, "unrecognized A_Expr kind: %d", a->kind);
result = NULL; /* keep compiler quiet */
break;
}
break;
}
case T_FuncCall:
result = transformFuncCall(pstate, (FuncCall *) expr);
break;
case T_NamedArgExpr:
{
NamedArgExpr *na = (NamedArgExpr *) expr;
na->arg = (Expr *) transformExprRecurse(pstate, (Node *) na->arg);
result = expr;
break;
}
case T_SubLink:
result = transformSubLink(pstate, (SubLink *) expr);
break;
1998-12-04 16:34:49 +01:00
case T_CaseExpr:
result = transformCaseExpr(pstate, (CaseExpr *) expr);
break;
1998-12-04 16:34:49 +01:00
case T_RowExpr:
result = transformRowExpr(pstate, (RowExpr *) expr);
break;
case T_CoalesceExpr:
result = transformCoalesceExpr(pstate, (CoalesceExpr *) expr);
break;
case T_MinMaxExpr:
result = transformMinMaxExpr(pstate, (MinMaxExpr *) expr);
break;
case T_XmlExpr:
result = transformXmlExpr(pstate, (XmlExpr *) expr);
break;
case T_XmlSerialize:
result = transformXmlSerialize(pstate, (XmlSerialize *) expr);
break;
case T_NullTest:
{
NullTest *n = (NullTest *) expr;
n->arg = (Expr *) transformExprRecurse(pstate, (Node *) n->arg);
/* the argument can be any type, so don't coerce it */
n->argisrow = type_is_rowtype(exprType((Node *) n->arg));
result = expr;
break;
}
case T_BooleanTest:
result = transformBooleanTest(pstate, (BooleanTest *) expr);
break;
case T_CurrentOfExpr:
result = transformCurrentOfExpr(pstate, (CurrentOfExpr *) expr);
break;
2002-09-04 22:31:48 +02:00
/*********************************************
* Quietly accept node types that may be presented when we are
* called on an already-transformed tree.
*
* Do any other node types need to be accepted? For now we are
* taking a conservative approach, and only accepting node
* types that are demonstrably necessary to accept.
*********************************************/
1998-03-26 22:08:10 +01:00
case T_Var:
case T_Const:
case T_Param:
case T_Aggref:
case T_WindowFunc:
case T_ArrayRef:
case T_FuncExpr:
case T_OpExpr:
case T_DistinctExpr:
case T_NullIfExpr:
case T_ScalarArrayOpExpr:
case T_BoolExpr:
case T_FieldSelect:
case T_FieldStore:
case T_RelabelType:
case T_CoerceViaIO:
case T_ArrayCoerceExpr:
case T_ConvertRowtypeExpr:
case T_CollateExpr:
case T_CaseTestExpr:
case T_ArrayExpr:
case T_CoerceToDomain:
case T_CoerceToDomainValue:
case T_SetToDefault:
1998-03-26 22:08:10 +01:00
{
result = (Node *) expr;
break;
}
default:
/* should not reach here */
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
result = NULL; /* keep compiler quiet */
break;
}
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;
/*
* We have to split any field-selection operations apart from
2005-10-15 04:49:52 +02:00
* subscripting. Adjacent A_Indices nodes have to be treated as a single
* multidimensional subscript operation.
*/
foreach(i, indirection)
{
2004-08-29 07:07:03 +02:00
Node *n = lfirst(i);
if (IsA(n, A_Indices))
subscripts = lappend(subscripts, n);
else if (IsA(n, A_Star))
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("row expansion via \"*\" is not supported here"),
parser_errposition(pstate, location)));
}
else
{
Node *newresult;
Assert(IsA(n, String));
/* process subscripts before this field selection */
if (subscripts)
result = (Node *) transformArraySubscripts(pstate,
result,
2005-10-15 04:49:52 +02:00
exprType(result),
InvalidOid,
exprTypmod(result),
subscripts,
NULL);
subscripts = NIL;
newresult = ParseFuncOrColumn(pstate,
list_make1(n),
list_make1(result),
NIL, false, false, false,
NULL, true, location);
if (newresult == NULL)
unknown_attribute(pstate, result, strVal(n), location);
result = newresult;
}
}
/* process trailing subscripts, if any */
if (subscripts)
result = (Node *) transformArraySubscripts(pstate,
result,
exprType(result),
InvalidOid,
exprTypmod(result),
subscripts,
NULL);
return result;
}
/*
* Transform a ColumnRef.
*
* If you find yourself changing this code, see also ExpandColumnRefStar.
*/
static Node *
transformColumnRef(ParseState *pstate, ColumnRef *cref)
{
Node *node = NULL;
char *nspname = NULL;
char *relname = NULL;
char *colname = NULL;
RangeTblEntry *rte;
int levels_up;
2010-02-26 03:01:40 +01:00
enum
{
CRERR_NO_COLUMN,
CRERR_NO_RTE,
CRERR_WRONG_DB,
CRERR_TOO_MANY
} crerr = CRERR_NO_COLUMN;
/*
2010-02-26 03:01:40 +01:00
* 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:
*
* A First try to resolve as unqualified column name;
* if no luck, try to resolve as unqualified table name (A.*).
* A.B A is an unqualified table name; B is either a
* column or function name (trying column name first).
* A.B.C schema A, table B, col or func name C.
* A.B.C.D catalog A, schema B, table C, col or func D.
* A.* A is an unqualified table name; means whole-row value.
* A.B.* whole-row value of table B in schema A.
* A.B.C.* whole-row value of table C in schema B in catalog A.
*
* We do not need to cope with bare "*"; that will only be accepted by
* the grammar at the top level of a SELECT list, and transformTargetList
* will take care of it before it ever gets here. Also, "A.*" etc will
* be expanded by transformTargetList if they appear at SELECT top level,
* so here we are only going to see them as function or operator inputs.
*
* Currently, if a catalog name is given then it must equal the current
* database name; we check it here and then discard it.
*----------
*/
switch (list_length(cref->fields))
{
case 1:
2002-09-04 22:31:48 +02:00
{
Node *field1 = (Node *) linitial(cref->fields);
Assert(IsA(field1, String));
colname = strVal(field1);
2002-09-04 22:31:48 +02:00
/* Try to identify as an unqualified column */
node = colNameToVar(pstate, colname, false, cref->location);
2002-09-04 22:31:48 +02:00
if (node == NULL)
{
/*
* Not known as a column of any range-table entry.
*
* Consider the possibility that it's VALUE in a domain
* check expression. (We handle VALUE as a name, not a
* keyword, to avoid breaking a lot of applications that
* have used VALUE as a column name in the past.)
*/
if (pstate->p_value_substitute != NULL &&
strcmp(colname, "value") == 0)
{
node = (Node *) copyObject(pstate->p_value_substitute);
/*
* Try to propagate location knowledge. This should
* be extended if p_value_substitute can ever take on
* other node types.
*/
if (IsA(node, CoerceToDomainValue))
((CoerceToDomainValue *) node)->location = cref->location;
break;
}
/*
2004-08-29 07:07:03 +02:00
* Try to find the name as a relation. Note that only
2005-10-15 04:49:52 +02:00
* relations already entered into the rangetable will be
* recognized.
2002-09-04 22:31:48 +02:00
*
* This is a hack for backwards compatibility with
2005-10-15 04:49:52 +02:00
* PostQUEL-inspired syntax. The preferred form now is
* "rel.*".
2002-09-04 22:31:48 +02:00
*/
rte = refnameRangeTblEntry(pstate, NULL, colname,
cref->location,
&levels_up);
if (rte)
node = transformWholeRowRef(pstate, rte,
cref->location);
2002-09-04 22:31:48 +02:00
}
break;
}
case 2:
{
Node *field1 = (Node *) linitial(cref->fields);
Node *field2 = (Node *) lsecond(cref->fields);
Assert(IsA(field1, String));
relname = strVal(field1);
/* Locate the referenced RTE */
rte = refnameRangeTblEntry(pstate, nspname, relname,
cref->location,
&levels_up);
if (rte == NULL)
{
crerr = CRERR_NO_RTE;
break;
}
2002-09-04 22:31:48 +02:00
/* Whole-row reference? */
if (IsA(field2, A_Star))
{
node = transformWholeRowRef(pstate, rte, cref->location);
2002-09-04 22:31:48 +02:00
break;
}
Assert(IsA(field2, String));
colname = strVal(field2);
/* Try to identify as a column of the RTE */
node = scanRTEForColumn(pstate, rte, colname, cref->location);
2002-09-04 22:31:48 +02:00
if (node == NULL)
{
/* Try it as a function call on the whole row */
node = transformWholeRowRef(pstate, rte, cref->location);
2002-09-04 22:31:48 +02:00
node = ParseFuncOrColumn(pstate,
list_make1(makeString(colname)),
list_make1(node),
NIL, false, false, false,
NULL, true, cref->location);
2002-09-04 22:31:48 +02:00
}
break;
}
case 3:
{
Node *field1 = (Node *) linitial(cref->fields);
Node *field2 = (Node *) lsecond(cref->fields);
Node *field3 = (Node *) lthird(cref->fields);
Assert(IsA(field1, String));
nspname = strVal(field1);
Assert(IsA(field2, String));
relname = strVal(field2);
/* Locate the referenced RTE */
rte = refnameRangeTblEntry(pstate, nspname, relname,
cref->location,
&levels_up);
if (rte == NULL)
{
crerr = CRERR_NO_RTE;
break;
}
2002-09-04 22:31:48 +02:00
/* Whole-row reference? */
if (IsA(field3, A_Star))
2002-09-04 22:31:48 +02:00
{
node = transformWholeRowRef(pstate, rte, cref->location);
2002-09-04 22:31:48 +02:00
break;
}
Assert(IsA(field3, String));
colname = strVal(field3);
/* Try to identify as a column of the RTE */
node = scanRTEForColumn(pstate, rte, colname, cref->location);
2002-09-04 22:31:48 +02:00
if (node == NULL)
{
/* Try it as a function call on the whole row */
node = transformWholeRowRef(pstate, rte, cref->location);
2002-09-04 22:31:48 +02:00
node = ParseFuncOrColumn(pstate,
list_make1(makeString(colname)),
list_make1(node),
NIL, false, false, false,
NULL, true, cref->location);
2002-09-04 22:31:48 +02:00
}
break;
}
case 4:
{
Node *field1 = (Node *) linitial(cref->fields);
Node *field2 = (Node *) lsecond(cref->fields);
Node *field3 = (Node *) lthird(cref->fields);
Node *field4 = (Node *) lfourth(cref->fields);
char *catname;
Assert(IsA(field1, String));
catname = strVal(field1);
Assert(IsA(field2, String));
nspname = strVal(field2);
Assert(IsA(field3, String));
relname = strVal(field3);
2002-09-04 22:31:48 +02:00
/*
* We check the catalog name and then ignore it.
*/
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;
}
2002-09-04 22:31:48 +02:00
/* Whole-row reference? */
if (IsA(field4, A_Star))
2002-09-04 22:31:48 +02:00
{
node = transformWholeRowRef(pstate, rte, cref->location);
2002-09-04 22:31:48 +02:00
break;
}
Assert(IsA(field4, String));
colname = strVal(field4);
/* Try to identify as a column of the RTE */
node = scanRTEForColumn(pstate, rte, colname, cref->location);
2002-09-04 22:31:48 +02:00
if (node == NULL)
{
/* Try it as a function call on the whole row */
node = transformWholeRowRef(pstate, rte, cref->location);
2002-09-04 22:31:48 +02:00
node = ParseFuncOrColumn(pstate,
list_make1(makeString(colname)),
list_make1(node),
NIL, false, false, false,
NULL, true, cref->location);
2002-09-04 22:31:48 +02:00
}
break;
}
default:
2010-02-26 03:01:40 +01:00
crerr = CRERR_TOO_MANY; /* too many dotted names */
break;
}
/*
* 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
2010-02-26 03:01:40 +01:00
* 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.
*/
if (pstate->p_post_columnref_hook != NULL)
{
2010-02-26 03:01:40 +01:00
Node *hookresult;
hookresult = (*pstate->p_post_columnref_hook) (pstate, cref, node);
if (node == NULL)
node = hookresult;
else if (hookresult != NULL)
ereport(ERROR,
(errcode(ERRCODE_AMBIGUOUS_COLUMN),
errmsg("column reference \"%s\" is ambiguous",
NameListToString(cref->fields)),
parser_errposition(pstate, cref->location)));
}
/*
* Throw error if no translation found.
*/
if (node == NULL)
{
switch (crerr)
{
case CRERR_NO_COLUMN:
errorMissingColumn(pstate, relname, colname, 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),
2010-02-26 03:01:40 +01:00
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),
2010-02-26 03:01:40 +01:00
errmsg("improper qualified name (too many dotted names): %s",
NameListToString(cref->fields)),
parser_errposition(pstate, cref->location)));
break;
}
}
return node;
}
static Node *
transformParamRef(ParseState *pstate, ParamRef *pref)
{
Node *result;
/*
2010-02-26 03:01:40 +01:00
* 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 */
static bool
exprIsNullConstant(Node *arg)
{
if (arg && IsA(arg, A_Const))
{
2007-11-15 22:14:46 +01:00
A_Const *con = (A_Const *) arg;
if (con->val.type == T_Null)
return true;
}
return false;
}
static Node *
transformAExprOp(ParseState *pstate, A_Expr *a)
{
Node *lexpr = a->lexpr;
Node *rexpr = a->rexpr;
Node *result;
/*
2005-10-15 04:49:52 +02:00
* Special-case "foo = NULL" and "NULL = foo" for compatibility with
* standards-broken products (like Microsoft's). Turn these into IS NULL
* exprs. (If either side is a CaseTestExpr, then the expression was
* generated internally from a CASE-WHEN expression, and
* transform_null_equals does not apply.)
*/
if (Transform_null_equals &&
list_length(a->name) == 1 &&
strcmp(strVal(linitial(a->name)), "=") == 0 &&
(exprIsNullConstant(lexpr) || exprIsNullConstant(rexpr)) &&
(!IsA(lexpr, CaseTestExpr) &&!IsA(rexpr, CaseTestExpr)))
{
NullTest *n = makeNode(NullTest);
n->nulltesttype = IS_NULL;
if (exprIsNullConstant(lexpr))
n->arg = (Expr *) rexpr;
else
n->arg = (Expr *) lexpr;
result = transformExprRecurse(pstate, (Node *) n);
}
else if (lexpr && IsA(lexpr, RowExpr) &&
rexpr && IsA(rexpr, SubLink) &&
((SubLink *) rexpr)->subLinkType == EXPR_SUBLINK)
{
/*
* Convert "row op subselect" into a ROWCOMPARE sublink. Formerly the
2005-10-15 04:49:52 +02:00
* grammar did this, but now that a row construct is allowed anywhere
* in expressions, it's easier to do it here.
*/
SubLink *s = (SubLink *) rexpr;
s->subLinkType = ROWCOMPARE_SUBLINK;
s->testexpr = lexpr;
s->operName = a->name;
s->location = a->location;
result = transformExprRecurse(pstate, (Node *) s);
}
else if (lexpr && IsA(lexpr, RowExpr) &&
rexpr && IsA(rexpr, RowExpr))
{
/* "row op row" */
lexpr = transformExprRecurse(pstate, lexpr);
rexpr = transformExprRecurse(pstate, rexpr);
Assert(IsA(lexpr, RowExpr));
Assert(IsA(rexpr, RowExpr));
result = make_row_comparison_op(pstate,
a->name,
((RowExpr *) lexpr)->args,
((RowExpr *) rexpr)->args,
a->location);
}
else
{
/* Ordinary scalar operator */
lexpr = transformExprRecurse(pstate, lexpr);
rexpr = transformExprRecurse(pstate, rexpr);
result = (Node *) make_op(pstate,
a->name,
lexpr,
rexpr,
a->location);
}
return result;
}
static Node *
transformAExprAnd(ParseState *pstate, A_Expr *a)
{
Node *lexpr = transformExprRecurse(pstate, a->lexpr);
Node *rexpr = transformExprRecurse(pstate, a->rexpr);
lexpr = coerce_to_boolean(pstate, lexpr, "AND");
rexpr = coerce_to_boolean(pstate, rexpr, "AND");
return (Node *) makeBoolExpr(AND_EXPR,
list_make2(lexpr, rexpr),
a->location);
}
static Node *
transformAExprOr(ParseState *pstate, A_Expr *a)
{
Node *lexpr = transformExprRecurse(pstate, a->lexpr);
Node *rexpr = transformExprRecurse(pstate, a->rexpr);
lexpr = coerce_to_boolean(pstate, lexpr, "OR");
rexpr = coerce_to_boolean(pstate, rexpr, "OR");
return (Node *) makeBoolExpr(OR_EXPR,
list_make2(lexpr, rexpr),
a->location);
}
static Node *
transformAExprNot(ParseState *pstate, A_Expr *a)
{
Node *rexpr = transformExprRecurse(pstate, a->rexpr);
rexpr = coerce_to_boolean(pstate, rexpr, "NOT");
return (Node *) makeBoolExpr(NOT_EXPR,
list_make1(rexpr),
a->location);
}
static Node *
transformAExprOpAny(ParseState *pstate, A_Expr *a)
{
Node *lexpr = transformExprRecurse(pstate, a->lexpr);
Node *rexpr = transformExprRecurse(pstate, a->rexpr);
return (Node *) make_scalar_array_op(pstate,
a->name,
true,
lexpr,
rexpr,
a->location);
}
static Node *
transformAExprOpAll(ParseState *pstate, A_Expr *a)
{
Node *lexpr = transformExprRecurse(pstate, a->lexpr);
Node *rexpr = transformExprRecurse(pstate, a->rexpr);
return (Node *) make_scalar_array_op(pstate,
a->name,
false,
lexpr,
rexpr,
a->location);
}
static Node *
transformAExprDistinct(ParseState *pstate, A_Expr *a)
{
Node *lexpr = transformExprRecurse(pstate, a->lexpr);
Node *rexpr = transformExprRecurse(pstate, a->rexpr);
if (lexpr && IsA(lexpr, RowExpr) &&
rexpr && IsA(rexpr, RowExpr))
{
/* "row op row" */
return make_row_distinct_op(pstate, a->name,
(RowExpr *) lexpr,
(RowExpr *) rexpr,
a->location);
}
else
{
/* Ordinary scalar operator */
return (Node *) make_distinct_op(pstate,
a->name,
lexpr,
rexpr,
a->location);
}
}
static Node *
transformAExprNullIf(ParseState *pstate, A_Expr *a)
{
Node *lexpr = transformExprRecurse(pstate, a->lexpr);
Node *rexpr = transformExprRecurse(pstate, a->rexpr);
OpExpr *result;
result = (OpExpr *) make_op(pstate,
a->name,
lexpr,
rexpr,
a->location);
/*
* The comparison operator itself should yield boolean ...
*/
if (result->opresulttype != BOOLOID)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("NULLIF requires = operator to yield boolean"),
parser_errposition(pstate, a->location)));
/*
* ... but the NullIfExpr will yield the first operand's type.
*/
result->opresulttype = exprType((Node *) linitial(result->args));
/*
* We rely on NullIfExpr and OpExpr being the same struct
*/
NodeSetTag(result, T_NullIfExpr);
return (Node *) result;
}
static Node *
transformAExprOf(ParseState *pstate, A_Expr *a)
{
/*
2006-10-04 02:30:14 +02:00
* Checking an expression for match to a list of type names. Will result
* in a boolean constant node.
*/
Node *lexpr = transformExprRecurse(pstate, a->lexpr);
Const *result;
ListCell *telem;
Oid ltype,
rtype;
bool matched = false;
ltype = exprType(lexpr);
foreach(telem, (List *) a->rexpr)
{
rtype = typenameTypeId(pstate, lfirst(telem));
matched = (rtype == ltype);
if (matched)
break;
}
/*
2006-10-04 02:30:14 +02:00
* We have two forms: equals or not equals. Flip the sense of the result
2005-10-15 04:49:52 +02:00
* for not equals.
*/
if (strcmp(strVal(linitial(a->name)), "<>") == 0)
matched = (!matched);
result = (Const *) makeBoolConst(matched, false);
/* Make the result have the original input's parse location */
result->location = exprLocation((Node *) a);
return (Node *) result;
}
static Node *
transformAExprIn(ParseState *pstate, A_Expr *a)
{
Node *result = NULL;
Node *lexpr;
List *rexprs;
List *rvars;
List *rnonvars;
bool useOr;
bool haveRowExpr;
ListCell *l;
/*
* If the operator is <>, combine with AND not OR.
*/
if (strcmp(strVal(linitial(a->name)), "<>") == 0)
useOr = false;
else
useOr = true;
/*
2006-10-04 02:30:14 +02:00
* We try to generate a ScalarArrayOpExpr from IN/NOT IN, but this is only
* possible if the inputs are all scalars (no RowExprs) and there is a
* suitable array type available. If not, we fall back to a boolean
* condition tree with multiple copies of the lefthand expression. Also,
* any IN-list items that contain Vars are handled as separate boolean
* conditions, because that gives the planner more scope for optimization
* on such clauses.
*
* First step: transform all the inputs, and detect whether any are
* RowExprs or contain Vars.
*/
lexpr = transformExprRecurse(pstate, a->lexpr);
haveRowExpr = (lexpr && IsA(lexpr, RowExpr));
rexprs = rvars = rnonvars = NIL;
foreach(l, (List *) a->rexpr)
{
Node *rexpr = transformExprRecurse(pstate, lfirst(l));
haveRowExpr |= (rexpr && IsA(rexpr, RowExpr));
rexprs = lappend(rexprs, rexpr);
if (contain_vars_of_level(rexpr, 0))
rvars = lappend(rvars, rexpr);
else
rnonvars = lappend(rnonvars, rexpr);
}
/*
* ScalarArrayOpExpr is only going to be useful if there's more than one
* non-Var righthand item. Also, it won't work for RowExprs.
*/
if (!haveRowExpr && list_length(rnonvars) > 1)
{
List *allexprs;
Oid scalar_type;
Oid array_type;
/*
* Try to select a common type for the array elements. Note that
* since the LHS' type is first in the list, it will be preferred when
* there is doubt (eg, when all the RHS items are unknown literals).
*
* Note: use list_concat here not lcons, to avoid damaging rnonvars.
*/
allexprs = list_concat(list_make1(lexpr), rnonvars);
scalar_type = select_common_type(pstate, allexprs, NULL, NULL);
/* Do we have an array type to use? */
if (OidIsValid(scalar_type))
array_type = get_array_type(scalar_type);
else
array_type = InvalidOid;
if (array_type != InvalidOid)
{
/*
* OK: coerce all the right-hand non-Var inputs to the common type
* and build an ArrayExpr for them.
*/
List *aexprs;
ArrayExpr *newa;
aexprs = NIL;
foreach(l, rnonvars)
{
Node *rexpr = (Node *) lfirst(l);
rexpr = coerce_to_common_type(pstate, rexpr,
scalar_type,
"IN");
aexprs = lappend(aexprs, rexpr);
}
newa = makeNode(ArrayExpr);
newa->array_typeid = array_type;
/* array_collid will be set by parse_collate.c */
newa->element_typeid = scalar_type;
newa->elements = aexprs;
newa->multidims = false;
newa->location = -1;
result = (Node *) make_scalar_array_op(pstate,
a->name,
useOr,
lexpr,
(Node *) newa,
a->location);
/* Consider only the Vars (if any) in the loop below */
rexprs = rvars;
}
}
/*
* Must do it the hard way, ie, with a boolean expression tree.
*/
foreach(l, rexprs)
{
Node *rexpr = (Node *) lfirst(l);
Node *cmp;
if (haveRowExpr)
{
if (!IsA(lexpr, RowExpr) ||
!IsA(rexpr, RowExpr))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
2006-10-04 02:30:14 +02:00
errmsg("arguments of row IN must all be row expressions"),
parser_errposition(pstate, a->location)));
cmp = make_row_comparison_op(pstate,
a->name,
2006-10-04 02:30:14 +02:00
(List *) copyObject(((RowExpr *) lexpr)->args),
((RowExpr *) rexpr)->args,
a->location);
}
else
cmp = (Node *) make_op(pstate,
a->name,
copyObject(lexpr),
rexpr,
a->location);
cmp = coerce_to_boolean(pstate, cmp, "IN");
if (result == NULL)
result = cmp;
else
result = (Node *) makeBoolExpr(useOr ? OR_EXPR : AND_EXPR,
list_make2(result, cmp),
a->location);
}
return result;
}
static Node *
transformFuncCall(ParseState *pstate, FuncCall *fn)
{
List *targs;
ListCell *args;
/* Transform the list of arguments ... */
targs = NIL;
foreach(args, fn->args)
{
targs = lappend(targs, transformExprRecurse(pstate,
(Node *) lfirst(args)));
}
/* ... and hand off to ParseFuncOrColumn */
return ParseFuncOrColumn(pstate,
fn->funcname,
targs,
fn->agg_order,
fn->agg_star,
fn->agg_distinct,
fn->func_variadic,
fn->over,
false,
fn->location);
}
static Node *
transformCaseExpr(ParseState *pstate, CaseExpr *c)
{
CaseExpr *newc;
Node *arg;
CaseTestExpr *placeholder;
List *newargs;
List *resultexprs;
ListCell *l;
Node *defresult;
Oid ptype;
/* If we already transformed this node, do nothing */
if (OidIsValid(c->casetype))
return (Node *) c;
newc = makeNode(CaseExpr);
/* transform the test expression, if any */
arg = transformExprRecurse(pstate, (Node *) c->arg);
/* generate placeholder for test expression */
if (arg)
{
/*
2005-10-15 04:49:52 +02:00
* If test expression is an untyped literal, force it to text. We have
* to do something now because we won't be able to do this coercion on
* the placeholder. This is not as flexible as what was done in 7.4
* and before, but it's good enough to handle the sort of silly coding
* commonly seen.
*/
if (exprType(arg) == UNKNOWNOID)
arg = coerce_to_common_type(pstate, arg, TEXTOID, "CASE");
/*
* Run collation assignment on the test expression so that we know
2011-04-10 17:42:00 +02:00
* what collation to mark the placeholder with. In principle we could
* leave it to parse_collate.c to do that later, but propagating the
* result to the CaseTestExpr would be unnecessarily complicated.
*/
assign_expr_collations(pstate, arg);
placeholder = makeNode(CaseTestExpr);
placeholder->typeId = exprType(arg);
placeholder->typeMod = exprTypmod(arg);
placeholder->collation = exprCollation(arg);
}
else
placeholder = NULL;
newc->arg = (Expr *) arg;
/* transform the list of arguments */
newargs = NIL;
resultexprs = NIL;
foreach(l, c->args)
{
CaseWhen *w = (CaseWhen *) lfirst(l);
CaseWhen *neww = makeNode(CaseWhen);
Node *warg;
Assert(IsA(w, CaseWhen));
warg = (Node *) w->expr;
if (placeholder)
{
/* shorthand form was specified, so expand... */
warg = (Node *) makeSimpleA_Expr(AEXPR_OP, "=",
(Node *) placeholder,
warg,
w->location);
}
neww->expr = (Expr *) transformExprRecurse(pstate, warg);
neww->expr = (Expr *) coerce_to_boolean(pstate,
(Node *) neww->expr,
"CASE/WHEN");
warg = (Node *) w->result;
neww->result = (Expr *) transformExprRecurse(pstate, warg);
neww->location = w->location;
newargs = lappend(newargs, neww);
resultexprs = lappend(resultexprs, neww->result);
}
newc->args = newargs;
/* transform the default clause */
defresult = (Node *) c->defresult;
if (defresult == NULL)
{
A_Const *n = makeNode(A_Const);
n->val.type = T_Null;
n->location = -1;
defresult = (Node *) n;
}
newc->defresult = (Expr *) transformExprRecurse(pstate, defresult);
/*
* Note: default result is considered the most significant type in
2005-10-15 04:49:52 +02:00
* determining preferred type. This is how the code worked before, but it
* seems a little bogus to me --- tgl
*/
resultexprs = lcons(newc->defresult, resultexprs);
ptype = select_common_type(pstate, resultexprs, "CASE", NULL);
Assert(OidIsValid(ptype));
newc->casetype = ptype;
/* casecollid will be set by parse_collate.c */
/* Convert default result clause, if necessary */
newc->defresult = (Expr *)
coerce_to_common_type(pstate,
(Node *) newc->defresult,
ptype,
"CASE/ELSE");
/* Convert when-clause results, if necessary */
foreach(l, newc->args)
{
CaseWhen *w = (CaseWhen *) lfirst(l);
w->result = (Expr *)
coerce_to_common_type(pstate,
(Node *) w->result,
ptype,
"CASE/WHEN");
}
newc->location = c->location;
return (Node *) newc;
}
static Node *
transformSubLink(ParseState *pstate, SubLink *sublink)
{
Node *result = (Node *) sublink;
Query *qtree;
const char *err;
/* If we already transformed this node, do nothing */
if (IsA(sublink->subselect, Query))
return result;
/*
* Check to see if the sublink is in an invalid place within the query.
* We allow sublinks everywhere in SELECT/INSERT/UPDATE/DELETE, but
* generally not in utility statements.
*/
err = NULL;
switch (pstate->p_expr_kind)
{
case EXPR_KIND_NONE:
Assert(false); /* can't happen */
break;
case EXPR_KIND_OTHER:
/* Accept sublink here; caller must throw error if wanted */
break;
case EXPR_KIND_JOIN_ON:
case EXPR_KIND_JOIN_USING:
case EXPR_KIND_FROM_SUBSELECT:
case EXPR_KIND_FROM_FUNCTION:
case EXPR_KIND_WHERE:
case EXPR_KIND_HAVING:
case EXPR_KIND_WINDOW_PARTITION:
case EXPR_KIND_WINDOW_ORDER:
case EXPR_KIND_WINDOW_FRAME_RANGE:
case EXPR_KIND_WINDOW_FRAME_ROWS:
case EXPR_KIND_SELECT_TARGET:
case EXPR_KIND_INSERT_TARGET:
case EXPR_KIND_UPDATE_SOURCE:
case EXPR_KIND_UPDATE_TARGET:
case EXPR_KIND_GROUP_BY:
case EXPR_KIND_ORDER_BY:
case EXPR_KIND_DISTINCT_ON:
case EXPR_KIND_LIMIT:
case EXPR_KIND_OFFSET:
case EXPR_KIND_RETURNING:
case EXPR_KIND_VALUES:
/* okay */
break;
case EXPR_KIND_CHECK_CONSTRAINT:
case EXPR_KIND_DOMAIN_CHECK:
err = _("cannot use subquery in CHECK constraint");
break;
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
err = _("cannot use subquery in DEFAULT expression");
break;
case EXPR_KIND_INDEX_EXPRESSION:
err = _("cannot use subquery in index expression");
break;
case EXPR_KIND_INDEX_PREDICATE:
err = _("cannot use subquery in index predicate");
break;
case EXPR_KIND_ALTER_COL_TRANSFORM:
err = _("cannot use subquery in transform expression");
break;
case EXPR_KIND_EXECUTE_PARAMETER:
err = _("cannot use subquery in EXECUTE parameter");
break;
case EXPR_KIND_TRIGGER_WHEN:
err = _("cannot use subquery in trigger WHEN condition");
break;
/*
* There is intentionally no default: case here, so that the
* compiler will warn if we add a new ParseExprKind without
* extending this switch. If we do see an unrecognized value at
* runtime, the behavior will be the same as for EXPR_KIND_OTHER,
* which is sane anyway.
*/
}
if (err)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg_internal("%s", err),
parser_errposition(pstate, sublink->location)));
pstate->p_hasSubLinks = true;
/*
* OK, let's transform the sub-SELECT.
*/
qtree = parse_sub_analyze(sublink->subselect, pstate, NULL, false);
/*
* Check that we got something reasonable. Many of these conditions are
* impossible given restrictions of the grammar, but check 'em anyway.
*/
if (!IsA(qtree, Query) ||
qtree->commandType != CMD_SELECT ||
qtree->utilityStmt != NULL)
elog(ERROR, "unexpected non-SELECT command in SubLink");
sublink->subselect = (Node *) qtree;
if (sublink->subLinkType == EXISTS_SUBLINK)
{
/*
2006-10-04 02:30:14 +02:00
* EXISTS needs no test expression or combining operator. These fields
* should be null already, but make sure.
*/
sublink->testexpr = NULL;
sublink->operName = NIL;
}
else if (sublink->subLinkType == EXPR_SUBLINK ||
sublink->subLinkType == ARRAY_SUBLINK)
{
ListCell *tlist_item = list_head(qtree->targetList);
/*
2005-10-15 04:49:52 +02:00
* Make sure the subselect delivers a single column (ignoring resjunk
* targets).
*/
if (tlist_item == NULL ||
((TargetEntry *) lfirst(tlist_item))->resjunk)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("subquery must return a column"),
parser_errposition(pstate, sublink->location)));
while ((tlist_item = lnext(tlist_item)) != NULL)
{
if (!((TargetEntry *) lfirst(tlist_item))->resjunk)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("subquery must return only one column"),
parser_errposition(pstate, sublink->location)));
}
/*
2006-10-04 02:30:14 +02:00
* EXPR and ARRAY need no test expression or combining operator. These
* fields should be null already, but make sure.
*/
sublink->testexpr = NULL;
sublink->operName = NIL;
}
else
{
/* ALL, ANY, or ROWCOMPARE: generate row-comparing expression */
Node *lefthand;
List *left_list;
List *right_list;
ListCell *l;
/*
* Transform lefthand expression, and convert to a list
*/
lefthand = transformExprRecurse(pstate, sublink->testexpr);
if (lefthand && IsA(lefthand, RowExpr))
left_list = ((RowExpr *) lefthand)->args;
else
left_list = list_make1(lefthand);
/*
2006-10-04 02:30:14 +02:00
* Build a list of PARAM_SUBLINK nodes representing the output columns
* of the subquery.
*/
right_list = NIL;
foreach(l, qtree->targetList)
{
TargetEntry *tent = (TargetEntry *) lfirst(l);
Param *param;
if (tent->resjunk)
continue;
param = makeNode(Param);
param->paramkind = PARAM_SUBLINK;
param->paramid = tent->resno;
param->paramtype = exprType((Node *) tent->expr);
param->paramtypmod = exprTypmod((Node *) tent->expr);
param->paramcollid = exprCollation((Node *) tent->expr);
param->location = -1;
right_list = lappend(right_list, param);
}
/*
2006-10-04 02:30:14 +02:00
* We could rely on make_row_comparison_op to complain if the list
* lengths differ, but we prefer to generate a more specific error
* message.
*/
if (list_length(left_list) < list_length(right_list))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("subquery has too many columns"),
parser_errposition(pstate, sublink->location)));
if (list_length(left_list) > list_length(right_list))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("subquery has too few columns"),
parser_errposition(pstate, sublink->location)));
/*
* Identify the combining operator(s) and generate a suitable
* row-comparison expression.
*/
sublink->testexpr = make_row_comparison_op(pstate,
sublink->operName,
left_list,
right_list,
sublink->location);
}
return result;
}
/*
* transformArrayExpr
*
* If the caller specifies the target type, the resulting array will
* be of exactly that type. Otherwise we try to infer a common type
* for the elements using select_common_type().
*/
static Node *
transformArrayExpr(ParseState *pstate, A_ArrayExpr *a,
Oid array_type, Oid element_type, int32 typmod)
{
ArrayExpr *newa = makeNode(ArrayExpr);
List *newelems = NIL;
List *newcoercedelems = NIL;
ListCell *element;
Oid coerce_type;
bool coerce_hard;
/*
* Transform the element expressions
*
* Assume that the array is one-dimensional unless we find an array-type
* element expression.
*/
newa->multidims = false;
foreach(element, a->elements)
{
Node *e = (Node *) lfirst(element);
Node *newe;
/*
* If an element is itself an A_ArrayExpr, recurse directly so that we
* can pass down any target type we were given.
*/
if (IsA(e, A_ArrayExpr))
{
newe = transformArrayExpr(pstate,
(A_ArrayExpr *) e,
array_type,
element_type,
typmod);
/* we certainly have an array here */
Assert(array_type == InvalidOid || array_type == exprType(newe));
newa->multidims = true;
}
else
{
newe = transformExprRecurse(pstate, e);
/*
* Check for sub-array expressions, if we haven't already found
* one.
*/
if (!newa->multidims && type_is_array(exprType(newe)))
newa->multidims = true;
}
newelems = lappend(newelems, newe);
}
/*
* Select a target type for the elements.
*
* If we haven't been given a target array type, we must try to deduce a
* common type based on the types of the individual elements present.
*/
if (OidIsValid(array_type))
{
/* Caller must ensure array_type matches element_type */
Assert(OidIsValid(element_type));
coerce_type = (newa->multidims ? array_type : element_type);
coerce_hard = true;
}
else
{
/* Can't handle an empty array without a target type */
if (newelems == NIL)
ereport(ERROR,
(errcode(ERRCODE_INDETERMINATE_DATATYPE),
errmsg("cannot determine type of empty array"),
errhint("Explicitly cast to the desired type, "
"for example ARRAY[]::integer[]."),
parser_errposition(pstate, a->location)));
/* Select a common type for the elements */
coerce_type = select_common_type(pstate, newelems, "ARRAY", NULL);
if (newa->multidims)
{
array_type = coerce_type;
element_type = get_element_type(array_type);
if (!OidIsValid(element_type))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("could not find element type for data type %s",
format_type_be(array_type)),
parser_errposition(pstate, a->location)));
}
else
{
element_type = coerce_type;
array_type = get_array_type(element_type);
if (!OidIsValid(array_type))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("could not find array type for data type %s",
format_type_be(element_type)),
parser_errposition(pstate, a->location)));
}
coerce_hard = false;
}
/*
* Coerce elements to target type
*
* If the array has been explicitly cast, then the elements are in turn
* explicitly coerced.
*
* If the array's type was merely derived from the common type of its
* elements, then the elements are implicitly coerced to the common type.
* This is consistent with other uses of select_common_type().
*/
foreach(element, newelems)
{
Node *e = (Node *) lfirst(element);
Node *newe;
if (coerce_hard)
{
newe = coerce_to_target_type(pstate, e,
exprType(e),
coerce_type,
typmod,
COERCION_EXPLICIT,
COERCE_EXPLICIT_CAST,
-1);
if (newe == NULL)
ereport(ERROR,
(errcode(ERRCODE_CANNOT_COERCE),
errmsg("cannot cast type %s to %s",
format_type_be(exprType(e)),
format_type_be(coerce_type)),
parser_errposition(pstate, exprLocation(e))));
}
else
newe = coerce_to_common_type(pstate, e,
coerce_type,
"ARRAY");
newcoercedelems = lappend(newcoercedelems, newe);
}
newa->array_typeid = array_type;
/* array_collid will be set by parse_collate.c */
newa->element_typeid = element_type;
newa->elements = newcoercedelems;
newa->location = a->location;
return (Node *) newa;
}
static Node *
transformRowExpr(ParseState *pstate, RowExpr *r)
{
RowExpr *newr;
char fname[16];
int fnum;
ListCell *lc;
/* If we already transformed this node, do nothing */
if (OidIsValid(r->row_typeid))
return (Node *) r;
newr = makeNode(RowExpr);
/* Transform the field expressions */
newr->args = transformExpressionList(pstate, r->args, pstate->p_expr_kind);
/* Barring later casting, we consider the type RECORD */
newr->row_typeid = RECORDOID;
newr->row_format = COERCE_IMPLICIT_CAST;
/* ROW() has anonymous columns, so invent some field names */
newr->colnames = NIL;
fnum = 1;
foreach(lc, newr->args)
{
snprintf(fname, sizeof(fname), "f%d", fnum++);
newr->colnames = lappend(newr->colnames, makeString(pstrdup(fname)));
}
newr->location = r->location;
return (Node *) newr;
}
static Node *
transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c)
{
CoalesceExpr *newc = makeNode(CoalesceExpr);
List *newargs = NIL;
List *newcoercedargs = NIL;
ListCell *args;
foreach(args, c->args)
{
Node *e = (Node *) lfirst(args);
Node *newe;
newe = transformExprRecurse(pstate, e);
newargs = lappend(newargs, newe);
}
newc->coalescetype = select_common_type(pstate, newargs, "COALESCE", NULL);
/* coalescecollid will be set by parse_collate.c */
/* Convert arguments if necessary */
foreach(args, newargs)
{
Node *e = (Node *) lfirst(args);
Node *newe;
newe = coerce_to_common_type(pstate, e,
newc->coalescetype,
"COALESCE");
newcoercedargs = lappend(newcoercedargs, newe);
}
newc->args = newcoercedargs;
newc->location = c->location;
return (Node *) newc;
}
static Node *
transformMinMaxExpr(ParseState *pstate, MinMaxExpr *m)
{
MinMaxExpr *newm = makeNode(MinMaxExpr);
List *newargs = NIL;
List *newcoercedargs = NIL;
const char *funcname = (m->op == IS_GREATEST) ? "GREATEST" : "LEAST";
ListCell *args;
newm->op = m->op;
foreach(args, m->args)
{
Node *e = (Node *) lfirst(args);
Node *newe;
newe = transformExprRecurse(pstate, e);
newargs = lappend(newargs, newe);
}
newm->minmaxtype = select_common_type(pstate, newargs, funcname, NULL);
/* minmaxcollid and inputcollid will be set by parse_collate.c */
/* Convert arguments if necessary */
foreach(args, newargs)
{
Node *e = (Node *) lfirst(args);
Node *newe;
newe = coerce_to_common_type(pstate, e,
newm->minmaxtype,
funcname);
newcoercedargs = lappend(newcoercedargs, newe);
}
newm->args = newcoercedargs;
newm->location = m->location;
return (Node *) newm;
}
static Node *
transformXmlExpr(ParseState *pstate, XmlExpr *x)
{
XmlExpr *newx;
2007-11-15 22:14:46 +01:00
ListCell *lc;
int i;
/* If we already transformed this node, do nothing */
if (OidIsValid(x->type))
return (Node *) x;
newx = makeNode(XmlExpr);
newx->op = x->op;
if (x->name)
newx->name = map_sql_identifier_to_xml_name(x->name, false, false);
else
newx->name = NULL;
newx->xmloption = x->xmloption;
newx->type = XMLOID; /* this just marks the node as transformed */
newx->typmod = -1;
newx->location = x->location;
/*
2007-11-15 22:14:46 +01:00
* gram.y built the named args as a list of ResTarget. Transform each,
* and break the names out as a separate list.
*/
newx->named_args = NIL;
newx->arg_names = NIL;
foreach(lc, x->named_args)
{
2007-11-15 22:14:46 +01:00
ResTarget *r = (ResTarget *) lfirst(lc);
Node *expr;
char *argname;
Assert(IsA(r, ResTarget));
expr = transformExprRecurse(pstate, r->val);
if (r->name)
argname = map_sql_identifier_to_xml_name(r->name, false, false);
else if (IsA(r->val, ColumnRef))
argname = map_sql_identifier_to_xml_name(FigureColname(r->val),
true, false);
else
{
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
x->op == IS_XMLELEMENT
? errmsg("unnamed XML attribute value must be a column reference")
: errmsg("unnamed XML element value must be a column reference"),
parser_errposition(pstate, r->location)));
argname = NULL; /* keep compiler quiet */
}
/* reject duplicate argnames in XMLELEMENT only */
if (x->op == IS_XMLELEMENT)
{
2007-11-15 22:14:46 +01:00
ListCell *lc2;
foreach(lc2, newx->arg_names)
{
if (strcmp(argname, strVal(lfirst(lc2))) == 0)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("XML attribute name \"%s\" appears more than once",
argname),
parser_errposition(pstate, r->location)));
}
}
newx->named_args = lappend(newx->named_args, expr);
newx->arg_names = lappend(newx->arg_names, makeString(argname));
}
/* The other arguments are of varying types depending on the function */
newx->args = NIL;
i = 0;
foreach(lc, x->args)
{
Node *e = (Node *) lfirst(lc);
Node *newe;
newe = transformExprRecurse(pstate, e);
switch (x->op)
{
case IS_XMLCONCAT:
newe = coerce_to_specific_type(pstate, newe, XMLOID,
"XMLCONCAT");
break;
2007-01-12 23:09:49 +01:00
case IS_XMLELEMENT:
/* no coercion necessary */
break;
case IS_XMLFOREST:
newe = coerce_to_specific_type(pstate, newe, XMLOID,
"XMLFOREST");
break;
case IS_XMLPARSE:
if (i == 0)
newe = coerce_to_specific_type(pstate, newe, TEXTOID,
"XMLPARSE");
else
newe = coerce_to_boolean(pstate, newe, "XMLPARSE");
break;
case IS_XMLPI:
newe = coerce_to_specific_type(pstate, newe, TEXTOID,
"XMLPI");
break;
case IS_XMLROOT:
if (i == 0)
newe = coerce_to_specific_type(pstate, newe, XMLOID,
"XMLROOT");
else if (i == 1)
newe = coerce_to_specific_type(pstate, newe, TEXTOID,
"XMLROOT");
else
newe = coerce_to_specific_type(pstate, newe, INT4OID,
"XMLROOT");
break;
case IS_XMLSERIALIZE:
/* not handled here */
Assert(false);
break;
case IS_DOCUMENT:
newe = coerce_to_specific_type(pstate, newe, XMLOID,
"IS DOCUMENT");
break;
}
newx->args = lappend(newx->args, newe);
i++;
}
return (Node *) newx;
}
static Node *
transformXmlSerialize(ParseState *pstate, XmlSerialize *xs)
{
Node *result;
XmlExpr *xexpr;
Oid targetType;
int32 targetTypmod;
xexpr = makeNode(XmlExpr);
xexpr->op = IS_XMLSERIALIZE;
xexpr->args = list_make1(coerce_to_specific_type(pstate,
transformExprRecurse(pstate, xs->expr),
XMLOID,
"XMLSERIALIZE"));
typenameTypeIdAndMod(pstate, xs->typeName, &targetType, &targetTypmod);
xexpr->xmloption = xs->xmloption;
xexpr->location = xs->location;
/* We actually only need these to be able to parse back the expression. */
xexpr->type = targetType;
xexpr->typmod = targetTypmod;
/*
2007-11-15 22:14:46 +01:00
* The actual target type is determined this way. SQL allows char and
* varchar as target types. We allow anything that can be cast implicitly
* from text. This way, user-defined text-like data types automatically
* fit in.
*/
result = coerce_to_target_type(pstate, (Node *) xexpr,
TEXTOID, targetType, targetTypmod,
COERCION_IMPLICIT,
COERCE_IMPLICIT_CAST,
-1);
if (result == NULL)
ereport(ERROR,
(errcode(ERRCODE_CANNOT_COERCE),
errmsg("cannot cast XMLSERIALIZE result to %s",
format_type_be(targetType)),
parser_errposition(pstate, xexpr->location)));
return result;
}
static Node *
transformBooleanTest(ParseState *pstate, BooleanTest *b)
{
const char *clausename;
switch (b->booltesttype)
{
case IS_TRUE:
clausename = "IS TRUE";
break;
case IS_NOT_TRUE:
clausename = "IS NOT TRUE";
break;
case IS_FALSE:
clausename = "IS FALSE";
break;
case IS_NOT_FALSE:
clausename = "IS NOT FALSE";
break;
case IS_UNKNOWN:
clausename = "IS UNKNOWN";
break;
case IS_NOT_UNKNOWN:
clausename = "IS NOT UNKNOWN";
break;
default:
elog(ERROR, "unrecognized booltesttype: %d",
(int) b->booltesttype);
2005-10-15 04:49:52 +02:00
clausename = NULL; /* keep compiler quiet */
}
b->arg = (Expr *) transformExprRecurse(pstate, (Node *) b->arg);
b->arg = (Expr *) coerce_to_boolean(pstate,
(Node *) b->arg,
clausename);
return (Node *) b;
}
static Node *
transformCurrentOfExpr(ParseState *pstate, CurrentOfExpr *cexpr)
{
2007-11-15 22:14:46 +01:00
int sublevels_up;
/* CURRENT OF can only appear at top level of UPDATE/DELETE */
Assert(pstate->p_target_rangetblentry != NULL);
cexpr->cvarno = RTERangeTablePosn(pstate,
pstate->p_target_rangetblentry,
&sublevels_up);
Assert(sublevels_up == 0);
/*
* Check to see if the cursor name matches a parameter of type REFCURSOR.
2010-02-26 03:01:40 +01:00
* If so, replace the raw name reference with a parameter reference. (This
* is a hack for the convenience of plpgsql.)
*/
2010-02-26 03:01:40 +01:00
if (cexpr->cursor_name != NULL) /* in case already transformed */
{
ColumnRef *cref = makeNode(ColumnRef);
Node *node = NULL;
/* Build an unqualified ColumnRef with the given name */
cref->fields = list_make1(makeString(cexpr->cursor_name));
cref->location = -1;
/* See if there is a translation available from a parser hook */
if (pstate->p_pre_columnref_hook != NULL)
node = (*pstate->p_pre_columnref_hook) (pstate, cref);
if (node == NULL && pstate->p_post_columnref_hook != NULL)
node = (*pstate->p_post_columnref_hook) (pstate, cref, NULL);
/*
2010-02-26 03:01:40 +01:00
* XXX Should we throw an error if we get a translation that isn't a
* refcursor Param? For now it seems best to silently ignore false
* matches.
*/
if (node != NULL && IsA(node, Param))
{
2010-02-26 03:01:40 +01:00
Param *p = (Param *) node;
if (p->paramkind == PARAM_EXTERN &&
p->paramtype == REFCURSOROID)
{
/* Matches, so convert CURRENT OF to a param reference */
cexpr->cursor_name = NULL;
cexpr->cursor_param = p->paramid;
}
}
}
return (Node *) cexpr;
}
/*
* Construct a whole-row reference to represent the notation "relation.*".
*/
static Node *
transformWholeRowRef(ParseState *pstate, RangeTblEntry *rte, int location)
{
Var *result;
int vnum;
int sublevels_up;
/* Find the RTE's rangetable location */
vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
/*
* Build the appropriate referencing node. Note that if the RTE is a
* function returning scalar, we create just a plain reference to the
* function value, not a composite containing a single column. This is
* pretty inconsistent at first sight, but it's what we've done
* historically. One argument for it is that "rel" and "rel.*" mean the
* same thing for composite relations, so why not for scalar functions...
*/
result = makeWholeRowVar(rte, vnum, sublevels_up, true);
/* location is not filled in by makeWholeRowVar */
result->location = location;
/* mark relation as requiring whole-row SELECT access */
markVarForSelectPriv(pstate, result, rte);
return (Node *) result;
}
/*
* Handle an explicit CAST construct.
*
* Transform the argument, then look up the type name and apply any necessary
* coercion function(s).
*/
static Node *
transformTypeCast(ParseState *pstate, TypeCast *tc)
{
Node *result;
Node *expr = transformExprRecurse(pstate, tc->arg);
Oid inputType = exprType(expr);
Oid targetType;
int32 targetTypmod;
int location;
typenameTypeIdAndMod(pstate, tc->typeName, &targetType, &targetTypmod);
if (inputType == InvalidOid)
return expr; /* do nothing if NULL input */
/*
* Location of the coercion is preferentially the location of the :: or
* CAST symbol, but if there is none then use the location of the type
* name (this can happen in TypeName 'string' syntax, for instance).
*/
location = tc->location;
if (location < 0)
location = tc->typeName->location;
result = coerce_to_target_type(pstate, expr, inputType,
targetType, targetTypmod,
COERCION_EXPLICIT,
COERCE_EXPLICIT_CAST,
location);
if (result == NULL)
ereport(ERROR,
(errcode(ERRCODE_CANNOT_COERCE),
errmsg("cannot cast type %s to %s",
format_type_be(inputType),
format_type_be(targetType)),
parser_coercion_errposition(pstate, location, expr)));
return result;
}
/*
* Handle an explicit COLLATE clause.
*
* Transform the argument, and look up the collation name.
*/
static Node *
transformCollateClause(ParseState *pstate, CollateClause *c)
{
CollateExpr *newc;
2011-04-10 17:42:00 +02:00
Oid argtype;
newc = makeNode(CollateExpr);
newc->arg = (Expr *) transformExprRecurse(pstate, c->arg);
argtype = exprType((Node *) newc->arg);
2011-04-10 17:42:00 +02:00
Remove collation information from TypeName, where it does not belong. The initial collations patch treated a COLLATE spec as part of a TypeName, following what can only be described as brain fade on the part of the SQL committee. It's a lot more reasonable to treat COLLATE as a syntactically separate object, so that it can be added in only the productions where it actually belongs, rather than needing to reject it in a boatload of places where it doesn't belong (something the original patch mostly failed to do). In addition this change lets us meet the spec's requirement to allow COLLATE anywhere in the clauses of a ColumnDef, and it avoids unfriendly behavior for constructs such as "foo::type COLLATE collation". To do this, pull collation information out of TypeName and put it in ColumnDef instead, thus reverting most of the collation-related changes in parse_type.c's API. I made one additional structural change, which was to use a ColumnDef as an intermediate node in AT_AlterColumnType AlterTableCmd nodes. This provides enough room to get rid of the "transform" wart in AlterTableCmd too, since the ColumnDef can carry the USING expression easily enough. Also fix some other minor bugs that have crept in in the same areas, like failure to copy recently-added fields of ColumnDef in copyfuncs.c. While at it, document the formerly secret ability to specify a collation in ALTER TABLE ALTER COLUMN TYPE, ALTER TYPE ADD ATTRIBUTE, and ALTER TYPE ALTER ATTRIBUTE TYPE; and correct some misstatements about what the default collation selection will be when COLLATE is omitted. BTW, the three-parameter form of format_type() should go away too, since it just contributes to the confusion in this area; but I'll do that in a separate patch.
2011-03-10 04:38:52 +01:00
/*
2011-04-10 17:42:00 +02:00
* The unknown type is not collatable, but coerce_type() takes care of it
* separately, so we'll let it go here.
Remove collation information from TypeName, where it does not belong. The initial collations patch treated a COLLATE spec as part of a TypeName, following what can only be described as brain fade on the part of the SQL committee. It's a lot more reasonable to treat COLLATE as a syntactically separate object, so that it can be added in only the productions where it actually belongs, rather than needing to reject it in a boatload of places where it doesn't belong (something the original patch mostly failed to do). In addition this change lets us meet the spec's requirement to allow COLLATE anywhere in the clauses of a ColumnDef, and it avoids unfriendly behavior for constructs such as "foo::type COLLATE collation". To do this, pull collation information out of TypeName and put it in ColumnDef instead, thus reverting most of the collation-related changes in parse_type.c's API. I made one additional structural change, which was to use a ColumnDef as an intermediate node in AT_AlterColumnType AlterTableCmd nodes. This provides enough room to get rid of the "transform" wart in AlterTableCmd too, since the ColumnDef can carry the USING expression easily enough. Also fix some other minor bugs that have crept in in the same areas, like failure to copy recently-added fields of ColumnDef in copyfuncs.c. While at it, document the formerly secret ability to specify a collation in ALTER TABLE ALTER COLUMN TYPE, ALTER TYPE ADD ATTRIBUTE, and ALTER TYPE ALTER ATTRIBUTE TYPE; and correct some misstatements about what the default collation selection will be when COLLATE is omitted. BTW, the three-parameter form of format_type() should go away too, since it just contributes to the confusion in this area; but I'll do that in a separate patch.
2011-03-10 04:38:52 +01:00
*/
if (!type_is_collatable(argtype) && argtype != UNKNOWNOID)
ereport(ERROR,
Remove collation information from TypeName, where it does not belong. The initial collations patch treated a COLLATE spec as part of a TypeName, following what can only be described as brain fade on the part of the SQL committee. It's a lot more reasonable to treat COLLATE as a syntactically separate object, so that it can be added in only the productions where it actually belongs, rather than needing to reject it in a boatload of places where it doesn't belong (something the original patch mostly failed to do). In addition this change lets us meet the spec's requirement to allow COLLATE anywhere in the clauses of a ColumnDef, and it avoids unfriendly behavior for constructs such as "foo::type COLLATE collation". To do this, pull collation information out of TypeName and put it in ColumnDef instead, thus reverting most of the collation-related changes in parse_type.c's API. I made one additional structural change, which was to use a ColumnDef as an intermediate node in AT_AlterColumnType AlterTableCmd nodes. This provides enough room to get rid of the "transform" wart in AlterTableCmd too, since the ColumnDef can carry the USING expression easily enough. Also fix some other minor bugs that have crept in in the same areas, like failure to copy recently-added fields of ColumnDef in copyfuncs.c. While at it, document the formerly secret ability to specify a collation in ALTER TABLE ALTER COLUMN TYPE, ALTER TYPE ADD ATTRIBUTE, and ALTER TYPE ALTER ATTRIBUTE TYPE; and correct some misstatements about what the default collation selection will be when COLLATE is omitted. BTW, the three-parameter form of format_type() should go away too, since it just contributes to the confusion in this area; but I'll do that in a separate patch.
2011-03-10 04:38:52 +01:00
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("collations are not supported by type %s",
Remove collation information from TypeName, where it does not belong. The initial collations patch treated a COLLATE spec as part of a TypeName, following what can only be described as brain fade on the part of the SQL committee. It's a lot more reasonable to treat COLLATE as a syntactically separate object, so that it can be added in only the productions where it actually belongs, rather than needing to reject it in a boatload of places where it doesn't belong (something the original patch mostly failed to do). In addition this change lets us meet the spec's requirement to allow COLLATE anywhere in the clauses of a ColumnDef, and it avoids unfriendly behavior for constructs such as "foo::type COLLATE collation". To do this, pull collation information out of TypeName and put it in ColumnDef instead, thus reverting most of the collation-related changes in parse_type.c's API. I made one additional structural change, which was to use a ColumnDef as an intermediate node in AT_AlterColumnType AlterTableCmd nodes. This provides enough room to get rid of the "transform" wart in AlterTableCmd too, since the ColumnDef can carry the USING expression easily enough. Also fix some other minor bugs that have crept in in the same areas, like failure to copy recently-added fields of ColumnDef in copyfuncs.c. While at it, document the formerly secret ability to specify a collation in ALTER TABLE ALTER COLUMN TYPE, ALTER TYPE ADD ATTRIBUTE, and ALTER TYPE ALTER ATTRIBUTE TYPE; and correct some misstatements about what the default collation selection will be when COLLATE is omitted. BTW, the three-parameter form of format_type() should go away too, since it just contributes to the confusion in this area; but I'll do that in a separate patch.
2011-03-10 04:38:52 +01:00
format_type_be(argtype)),
parser_errposition(pstate, c->location)));
newc->collOid = LookupCollation(pstate, c->collname, c->location);
newc->location = c->location;
return (Node *) newc;
}
/*
* Transform a "row compare-op row" construct
*
* The inputs are lists of already-transformed expressions.
* As with coerce_type, pstate may be NULL if no special unknown-Param
* processing is wanted.
*
* The output may be a single OpExpr, an AND or OR combination of OpExprs,
* or a RowCompareExpr. In all cases it is guaranteed to return boolean.
* The AND, OR, and RowCompareExpr cases further imply things about the
* behavior of the operators (ie, they behave as =, <>, or < <= > >=).
*/
static Node *
make_row_comparison_op(ParseState *pstate, List *opname,
List *largs, List *rargs, int location)
{
RowCompareExpr *rcexpr;
RowCompareType rctype;
List *opexprs;
List *opnos;
List *opfamilies;
ListCell *l,
*r;
List **opinfo_lists;
Bitmapset *strats;
int nopers;
int i;
nopers = list_length(largs);
if (nopers != list_length(rargs))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("unequal number of entries in row expressions"),
parser_errposition(pstate, location)));
/*
2006-10-04 02:30:14 +02:00
* We can't compare zero-length rows because there is no principled basis
* for figuring out what the operator is.
*/
if (nopers == 0)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot compare rows of zero length"),
parser_errposition(pstate, location)));
/*
2006-10-04 02:30:14 +02:00
* Identify all the pairwise operators, using make_op so that behavior is
* the same as in the simple scalar case.
*/
opexprs = NIL;
forboth(l, largs, r, rargs)
{
2004-08-29 07:07:03 +02:00
Node *larg = (Node *) lfirst(l);
Node *rarg = (Node *) lfirst(r);
OpExpr *cmp;
cmp = (OpExpr *) make_op(pstate, opname, larg, rarg, location);
Assert(IsA(cmp, OpExpr));
/*
* We don't use coerce_to_boolean here because we insist on the
* operator yielding boolean directly, not via coercion. If it
* doesn't yield bool it won't be in any index opfamilies...
*/
if (cmp->opresulttype != BOOLOID)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
2006-10-04 02:30:14 +02:00
errmsg("row comparison operator must yield type boolean, "
"not type %s",
format_type_be(cmp->opresulttype)),
parser_errposition(pstate, location)));
if (expression_returns_set((Node *) cmp))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("row comparison operator must not return a set"),
parser_errposition(pstate, location)));
opexprs = lappend(opexprs, cmp);
}
/*
2006-10-04 02:30:14 +02:00
* If rows are length 1, just return the single operator. In this case we
* don't insist on identifying btree semantics for the operator (but we
* still require it to return boolean).
*/
if (nopers == 1)
return (Node *) linitial(opexprs);
/*
* Now we must determine which row comparison semantics (= <> < <= > >=)
2007-11-15 22:14:46 +01:00
* apply to this set of operators. We look for btree opfamilies
* containing the operators, and see which interpretations (strategy
* numbers) exist for each operator.
*/
opinfo_lists = (List **) palloc(nopers * sizeof(List *));
strats = NULL;
i = 0;
foreach(l, opexprs)
{
Oid opno = ((OpExpr *) lfirst(l))->opno;
2006-10-04 02:30:14 +02:00
Bitmapset *this_strats;
ListCell *j;
opinfo_lists[i] = get_op_btree_interpretation(opno);
2006-10-04 02:30:14 +02:00
/*
* convert strategy numbers into a Bitmapset to make the intersection
* calculation easy.
*/
this_strats = NULL;
foreach(j, opinfo_lists[i])
{
OpBtreeInterpretation *opinfo = lfirst(j);
this_strats = bms_add_member(this_strats, opinfo->strategy);
}
if (i == 0)
strats = this_strats;
else
strats = bms_int_members(strats, this_strats);
i++;
}
/*
* If there are multiple common interpretations, we may use any one of
* them ... this coding arbitrarily picks the lowest btree strategy
* number.
*/
i = bms_first_member(strats);
if (i < 0)
{
/* No common interpretation, so fail */
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("could not determine interpretation of row comparison operator %s",
strVal(llast(opname))),
errhint("Row comparison operators must be associated with btree operator families."),
parser_errposition(pstate, location)));
}
rctype = (RowCompareType) i;
/*
2006-10-04 02:30:14 +02:00
* For = and <> cases, we just combine the pairwise operators with AND or
* OR respectively.
*
* Note: this is presently the only place where the parser generates
2006-10-04 02:30:14 +02:00
* BoolExpr with more than two arguments. Should be OK since the rest of
* the system thinks BoolExpr is N-argument anyway.
*/
if (rctype == ROWCOMPARE_EQ)
return (Node *) makeBoolExpr(AND_EXPR, opexprs, location);
if (rctype == ROWCOMPARE_NE)
return (Node *) makeBoolExpr(OR_EXPR, opexprs, location);
/*
* Otherwise we need to choose exactly which opfamily to associate with
2006-10-04 02:30:14 +02:00
* each operator.
*/
opfamilies = NIL;
for (i = 0; i < nopers; i++)
{
Oid opfamily = InvalidOid;
ListCell *j;
foreach(j, opinfo_lists[i])
{
OpBtreeInterpretation *opinfo = lfirst(j);
if (opinfo->strategy == rctype)
{
opfamily = opinfo->opfamily_id;
break;
}
}
if (OidIsValid(opfamily))
opfamilies = lappend_oid(opfamilies, opfamily);
2007-11-15 22:14:46 +01:00
else /* should not happen */
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("could not determine interpretation of row comparison operator %s",
strVal(llast(opname))),
2006-10-04 02:30:14 +02:00
errdetail("There are multiple equally-plausible candidates."),
parser_errposition(pstate, location)));
}
/*
* Now deconstruct the OpExprs and create a RowCompareExpr.
*
* Note: can't just reuse the passed largs/rargs lists, because of
* possibility that make_op inserted coercion operations.
*/
opnos = NIL;
largs = NIL;
rargs = NIL;
foreach(l, opexprs)
{
OpExpr *cmp = (OpExpr *) lfirst(l);
opnos = lappend_oid(opnos, cmp->opno);
largs = lappend(largs, linitial(cmp->args));
rargs = lappend(rargs, lsecond(cmp->args));
}
rcexpr = makeNode(RowCompareExpr);
rcexpr->rctype = rctype;
rcexpr->opnos = opnos;
rcexpr->opfamilies = opfamilies;
2011-04-10 17:42:00 +02:00
rcexpr->inputcollids = NIL; /* assign_expr_collations will fix this */
rcexpr->largs = largs;
rcexpr->rargs = rargs;
return (Node *) rcexpr;
}
/*
* Transform a "row IS DISTINCT FROM row" construct
*
* The input RowExprs are already transformed
*/
static Node *
make_row_distinct_op(ParseState *pstate, List *opname,
RowExpr *lrow, RowExpr *rrow,
int location)
{
Node *result = NULL;
List *largs = lrow->args;
List *rargs = rrow->args;
ListCell *l,
*r;
if (list_length(largs) != list_length(rargs))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("unequal number of entries in row expressions"),
parser_errposition(pstate, location)));
forboth(l, largs, r, rargs)
{
2004-08-29 07:07:03 +02:00
Node *larg = (Node *) lfirst(l);
Node *rarg = (Node *) lfirst(r);
Node *cmp;
cmp = (Node *) make_distinct_op(pstate, opname, larg, rarg, location);
if (result == NULL)
result = cmp;
else
result = (Node *) makeBoolExpr(OR_EXPR,
list_make2(result, cmp),
location);
}
if (result == NULL)
{
/* zero-length rows? Generate constant FALSE */
result = makeBoolConst(false, false);
}
return result;
}
/*
* make the node for an IS DISTINCT FROM operator
*/
static Expr *
make_distinct_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
int location)
{
2004-08-29 07:07:03 +02:00
Expr *result;
result = make_op(pstate, opname, ltree, rtree, location);
if (((OpExpr *) result)->opresulttype != BOOLOID)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
2006-10-04 02:30:14 +02:00
errmsg("IS DISTINCT FROM requires = operator to yield boolean"),
parser_errposition(pstate, location)));
2004-08-29 07:07:03 +02:00
/*
2004-08-29 07:07:03 +02:00
* We rely on DistinctExpr and OpExpr being same struct
*/
NodeSetTag(result, T_DistinctExpr);
return result;
}
/*
* Produce a string identifying an expression by kind.
*
* Note: when practical, use a simple SQL keyword for the result. If that
* doesn't work well, check call sites to see whether custom error message
* strings are required.
*/
const char *
ParseExprKindName(ParseExprKind exprKind)
{
switch (exprKind)
{
case EXPR_KIND_NONE:
return "invalid expression context";
case EXPR_KIND_OTHER:
return "extension expression";
case EXPR_KIND_JOIN_ON:
return "JOIN/ON";
case EXPR_KIND_JOIN_USING:
return "JOIN/USING";
case EXPR_KIND_FROM_SUBSELECT:
return "sub-SELECT in FROM";
case EXPR_KIND_FROM_FUNCTION:
return "function in FROM";
case EXPR_KIND_WHERE:
return "WHERE";
case EXPR_KIND_HAVING:
return "HAVING";
case EXPR_KIND_WINDOW_PARTITION:
return "window PARTITION BY";
case EXPR_KIND_WINDOW_ORDER:
return "window ORDER BY";
case EXPR_KIND_WINDOW_FRAME_RANGE:
return "window RANGE";
case EXPR_KIND_WINDOW_FRAME_ROWS:
return "window ROWS";
case EXPR_KIND_SELECT_TARGET:
return "SELECT";
case EXPR_KIND_INSERT_TARGET:
return "INSERT";
case EXPR_KIND_UPDATE_SOURCE:
case EXPR_KIND_UPDATE_TARGET:
return "UPDATE";
case EXPR_KIND_GROUP_BY:
return "GROUP BY";
case EXPR_KIND_ORDER_BY:
return "ORDER BY";
case EXPR_KIND_DISTINCT_ON:
return "DISTINCT ON";
case EXPR_KIND_LIMIT:
return "LIMIT";
case EXPR_KIND_OFFSET:
return "OFFSET";
case EXPR_KIND_RETURNING:
return "RETURNING";
case EXPR_KIND_VALUES:
return "VALUES";
case EXPR_KIND_CHECK_CONSTRAINT:
case EXPR_KIND_DOMAIN_CHECK:
return "CHECK";
case EXPR_KIND_COLUMN_DEFAULT:
case EXPR_KIND_FUNCTION_DEFAULT:
return "DEFAULT";
case EXPR_KIND_INDEX_EXPRESSION:
return "index expression";
case EXPR_KIND_INDEX_PREDICATE:
return "index predicate";
case EXPR_KIND_ALTER_COL_TRANSFORM:
return "USING";
case EXPR_KIND_EXECUTE_PARAMETER:
return "EXECUTE";
case EXPR_KIND_TRIGGER_WHEN:
return "WHEN";
/*
* There is intentionally no default: case here, so that the
* compiler will warn if we add a new ParseExprKind without
* extending this switch. If we do see an unrecognized value at
* runtime, we'll fall through to the "unrecognized" return.
*/
}
return "unrecognized expression kind";
}