Commit a341a96c authored by Neil Conway's avatar Neil Conway

Refactor transformExpr() by creating separate functions for most of the

expression types.
parent a73198ac
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.179 2005/01/12 17:32:36 tgl Exp $
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.180 2005/01/19 23:45:24 neilc Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -37,9 +37,27 @@
bool Transform_null_equals = false;
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 *transformFuncCall(ParseState *pstate, FuncCall *fn);
static Node *transformCaseExpr(ParseState *pstate, CaseExpr *c);
static Node *transformSubLink(ParseState *pstate, SubLink *sublink);
static Node *transformArrayExpr(ParseState *pstate, ArrayExpr *a);
static Node *transformRowExpr(ParseState *pstate, RowExpr *r);
static Node *transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c);
static Node *transformBooleanTest(ParseState *pstate, BooleanTest *b);
static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref);
static Node *transformWholeRowRef(ParseState *pstate, char *schemaname,
char *relname);
static Node *transformBooleanTest(ParseState *pstate, BooleanTest *b);
static Node *transformIndirection(ParseState *pstate, Node *basenode,
List *indirection);
static Node *typecast_expression(ParseState *pstate, Node *expr,
......@@ -90,65 +108,13 @@ transformExpr(ParseState *pstate, Node *expr)
switch (nodeTag(expr))
{
case T_ColumnRef:
{
result = transformColumnRef(pstate, (ColumnRef *) expr);
break;
}
case T_ParamRef:
{
ParamRef *pref = (ParamRef *) expr;
int paramno = pref->number;
ParseState *toppstate;
Param *param;
/*
* Find topmost ParseState, which is where paramtype info
* lives.
*/
toppstate = pstate;
while (toppstate->parentParseState != NULL)
toppstate = toppstate->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)));
if (paramno > toppstate->p_numparams)
{
if (!toppstate->p_variableparams)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_PARAMETER),
errmsg("there is no parameter $%d",
paramno)));
/* Okay to enlarge param array */
if (toppstate->p_paramtypes)
toppstate->p_paramtypes =
(Oid *) repalloc(toppstate->p_paramtypes,
paramno * sizeof(Oid));
else
toppstate->p_paramtypes =
(Oid *) palloc(paramno * sizeof(Oid));
/* Zero out the previously-unreferenced slots */
MemSet(toppstate->p_paramtypes + toppstate->p_numparams,
0,
(paramno - toppstate->p_numparams) * sizeof(Oid));
toppstate->p_numparams = paramno;
}
if (toppstate->p_variableparams)
{
/* If not seen before, initialize to UNKNOWN type */
if (toppstate->p_paramtypes[paramno - 1] == InvalidOid)
toppstate->p_paramtypes[paramno - 1] = UNKNOWNOID;
}
param = makeNode(Param);
param->paramkind = PARAM_NUM;
param->paramid = (AttrNumber) paramno;
param->paramtype = toppstate->p_paramtypes[paramno - 1];
result = (Node *) param;
case T_ParamRef:
result = transformParamRef(pstate, (ParamRef *) expr);
break;
}
case T_A_Const:
{
A_Const *con = (A_Const *) expr;
......@@ -160,6 +126,7 @@ transformExpr(ParseState *pstate, Node *expr)
con->typename);
break;
}
case T_A_Indirection:
{
A_Indirection *ind = (A_Indirection *) expr;
......@@ -169,6 +136,7 @@ transformExpr(ParseState *pstate, Node *expr)
ind->indirection);
break;
}
case T_TypeCast:
{
TypeCast *tc = (TypeCast *) expr;
......@@ -177,6 +145,7 @@ transformExpr(ParseState *pstate, Node *expr)
result = typecast_expression(pstate, arg, tc->typename);
break;
}
case T_A_Expr:
{
A_Expr *a = (A_Expr *) expr;
......@@ -184,449 +153,652 @@ transformExpr(ParseState *pstate, Node *expr)
switch (a->kind)
{
case AEXPR_OP:
{
Node *lexpr = a->lexpr;
Node *rexpr = a->rexpr;
/*
* Special-case "foo = NULL" and "NULL = foo"
* for compatibility with standards-broken
* products (like Microsoft's). Turn these
* into IS NULL exprs.
*/
if (Transform_null_equals &&
list_length(a->name) == 1 &&
strcmp(strVal(linitial(a->name)), "=") == 0 &&
(exprIsNullConstant(lexpr) ||
exprIsNullConstant(rexpr)))
{
NullTest *n = makeNode(NullTest);
n->nulltesttype = IS_NULL;
if (exprIsNullConstant(lexpr))
n->arg = (Expr *) rexpr;
else
n->arg = (Expr *) lexpr;
result = transformExpr(pstate,
(Node *) n);
}
else if (lexpr && IsA(lexpr, RowExpr) &&
rexpr && IsA(rexpr, SubLink) &&
((SubLink *) rexpr)->subLinkType == EXPR_SUBLINK)
{
/*
* Convert "row op subselect" into a
* MULTIEXPR sublink. Formerly the
* 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 = MULTIEXPR_SUBLINK;
s->lefthand = ((RowExpr *) lexpr)->args;
s->operName = a->name;
result = transformExpr(pstate, (Node *) s);
}
else if (lexpr && IsA(lexpr, RowExpr) &&
rexpr && IsA(rexpr, RowExpr))
{
/* "row op row" */
result = make_row_op(pstate, a->name,
lexpr, rexpr);
}
else
{
/* Ordinary scalar operator */
lexpr = transformExpr(pstate, lexpr);
rexpr = transformExpr(pstate, rexpr);
result = (Node *) make_op(pstate,
a->name,
lexpr,
rexpr);
}
}
result = transformAExprOp(pstate, a);
break;
case AEXPR_AND:
{
Node *lexpr = transformExpr(pstate,
a->lexpr);
Node *rexpr = transformExpr(pstate,
a->rexpr);
lexpr = coerce_to_boolean(pstate, lexpr, "AND");
rexpr = coerce_to_boolean(pstate, rexpr, "AND");
result = (Node *) makeBoolExpr(AND_EXPR,
list_make2(lexpr,
rexpr));
}
result = transformAExprAnd(pstate, a);
break;
case AEXPR_OR:
{
Node *lexpr = transformExpr(pstate,
a->lexpr);
Node *rexpr = transformExpr(pstate,
a->rexpr);
lexpr = coerce_to_boolean(pstate, lexpr, "OR");
rexpr = coerce_to_boolean(pstate, rexpr, "OR");
result = (Node *) makeBoolExpr(OR_EXPR,
list_make2(lexpr,
rexpr));
}
result = transformAExprOr(pstate, a);
break;
case AEXPR_NOT:
{
Node *rexpr = transformExpr(pstate,
a->rexpr);
rexpr = coerce_to_boolean(pstate, rexpr, "NOT");
result = (Node *) makeBoolExpr(NOT_EXPR,
list_make1(rexpr));
}
result = transformAExprNot(pstate, a);
break;
case AEXPR_OP_ANY:
{
Node *lexpr = transformExpr(pstate,
a->lexpr);
Node *rexpr = transformExpr(pstate,
a->rexpr);
result = (Node *) make_scalar_array_op(pstate,
a->name,
true,
lexpr,
rexpr);
}
result = transformAExprOpAny(pstate, a);
break;
case AEXPR_OP_ALL:
{
Node *lexpr = transformExpr(pstate,
a->lexpr);
Node *rexpr = transformExpr(pstate,
a->rexpr);
result = (Node *) make_scalar_array_op(pstate,
a->name,
false,
lexpr,
rexpr);
}
result = transformAExprOpAll(pstate, a);
break;
case AEXPR_DISTINCT:
{
Node *lexpr = a->lexpr;
Node *rexpr = a->rexpr;
if (lexpr && IsA(lexpr, RowExpr) &&
rexpr && IsA(rexpr, RowExpr))
{
/* "row op row" */
result = make_row_distinct_op(pstate, a->name,
lexpr, rexpr);
}
else
{
/* Ordinary scalar operator */
lexpr = transformExpr(pstate, lexpr);
rexpr = transformExpr(pstate, rexpr);
result = (Node *) make_distinct_op(pstate,
a->name,
lexpr,
rexpr);
}
}
result = transformAExprDistinct(pstate, a);
break;
case AEXPR_NULLIF:
{
Node *lexpr = transformExpr(pstate,
a->lexpr);
Node *rexpr = transformExpr(pstate,
a->rexpr);
result = (Node *) make_op(pstate,
a->name,
lexpr,
rexpr);
if (((OpExpr *) result)->opresulttype != BOOLOID)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("NULLIF requires = operator to yield boolean")));
/*
* We rely on NullIfExpr and OpExpr being same
* struct
*/
NodeSetTag(result, T_NullIfExpr);
}
result = transformAExprNullIf(pstate, a);
break;
case AEXPR_OF:
{
/*
* Checking an expression for match to type.
* Will result in a boolean constant node.
*/
ListCell *telem;
A_Const *n;
Oid ltype,
rtype;
bool matched = FALSE;
Node *lexpr = transformExpr(pstate,
a->lexpr);
ltype = exprType(lexpr);
foreach(telem, (List *) a->rexpr)
{
rtype = LookupTypeName(lfirst(telem));
matched = (rtype == ltype);
if (matched)
result = transformAExprOf(pstate, a);
break;
default:
elog(ERROR, "unrecognized A_Expr kind: %d", a->kind);
}
break;
}
/*
* Expect two forms: equals or not equals.
* Flip the sense of the result for not
* equals.
*/
if (strcmp(strVal(linitial(a->name)), "!=") == 0)
matched = (!matched);
case T_FuncCall:
result = transformFuncCall(pstate, (FuncCall *) expr);
break;
n = makeNode(A_Const);
n->val.type = T_String;
n->val.val.str = (matched ? "t" : "f");
n->typename = SystemTypeName("bool");
case T_SubLink:
result = transformSubLink(pstate, (SubLink *) expr);
break;
result = transformExpr(pstate, (Node *) n);
}
case T_CaseExpr:
result = transformCaseExpr(pstate, (CaseExpr *) expr);
break;
}
case T_ArrayExpr:
result = transformArrayExpr(pstate, (ArrayExpr *) expr);
break;
}
case T_FuncCall:
{
FuncCall *fn = (FuncCall *) expr;
List *targs;
ListCell *args;
/*
* Transform the list of arguments. We use a shallow list
* copy and then transform-in-place to avoid O(N^2)
* behavior from repeated lappend's.
*
* XXX: repeated lappend() would no longer result in O(n^2)
* behavior; worth reconsidering this design?
*/
targs = list_copy(fn->args);
foreach(args, targs)
{
lfirst(args) = transformExpr(pstate,
(Node *) lfirst(args));
}
result = ParseFuncOrColumn(pstate,
fn->funcname,
targs,
fn->agg_star,
fn->agg_distinct,
false);
case T_RowExpr:
result = transformRowExpr(pstate, (RowExpr *) expr);
break;
}
case T_SubLink:
{
SubLink *sublink = (SubLink *) expr;
List *qtrees;
Query *qtree;
/* If we already transformed this node, do nothing */
if (IsA(sublink->subselect, Query))
case T_CoalesceExpr:
result = transformCoalesceExpr(pstate, (CoalesceExpr *) expr);
break;
case T_NullTest:
{
NullTest *n = (NullTest *) expr;
n->arg = (Expr *) transformExpr(pstate, (Node *) n->arg);
/* the argument can be any type, so don't coerce it */
result = expr;
break;
}
pstate->p_hasSubLinks = true;
qtrees = parse_sub_analyze(sublink->subselect, pstate);
if (list_length(qtrees) != 1)
elog(ERROR, "bad query in sub-select");
qtree = (Query *) linitial(qtrees);
if (qtree->commandType != CMD_SELECT ||
qtree->resultRelation != 0)
elog(ERROR, "bad query in sub-select");
sublink->subselect = (Node *) qtree;
if (sublink->subLinkType == EXISTS_SUBLINK)
case T_BooleanTest:
result = transformBooleanTest(pstate, (BooleanTest *) expr);
break;
/*********************************************
* 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.
*********************************************/
case T_Var:
case T_Const:
case T_Param:
case T_Aggref:
case T_ArrayRef:
case T_FuncExpr:
case T_OpExpr:
case T_DistinctExpr:
case T_ScalarArrayOpExpr:
case T_NullIfExpr:
case T_BoolExpr:
case T_FieldSelect:
case T_FieldStore:
case T_RelabelType:
case T_ConvertRowtypeExpr:
case T_CaseTestExpr:
case T_CoerceToDomain:
case T_CoerceToDomainValue:
case T_SetToDefault:
{
result = (Node *) expr;
break;
}
default:
/* should not reach here */
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
break;
}
return result;
}
static Node *
transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
{
Node *result = basenode;
List *subscripts = NIL;
ListCell *i;
/*
* EXISTS needs no lefthand or combining operator.
* These fields should be NIL already, but make sure.
* We have to split any field-selection operations apart from
* subscripting. Adjacent A_Indices nodes have to be treated as a
* single multidimensional subscript operation.
*/
sublink->lefthand = NIL;
sublink->operName = NIL;
sublink->operOids = NIL;
sublink->useOr = FALSE;
foreach(i, indirection)
{
Node *n = lfirst(i);
if (IsA(n, A_Indices))
subscripts = lappend(subscripts, n);
else
{
Assert(IsA(n, String));
/* process subscripts before this field selection */
if (subscripts)
result = (Node *) transformArraySubscripts(pstate,
result,
exprType(result),
InvalidOid,
-1,
subscripts,
NULL);
subscripts = NIL;
result = ParseFuncOrColumn(pstate,
list_make1(n),
list_make1(result),
false, false, true);
}
else if (sublink->subLinkType == EXPR_SUBLINK ||
sublink->subLinkType == ARRAY_SUBLINK)
}
/* process trailing subscripts, if any */
if (subscripts)
result = (Node *) transformArraySubscripts(pstate,
result,
exprType(result),
InvalidOid,
-1,
subscripts,
NULL);
return result;
}
static Node *
transformColumnRef(ParseState *pstate, ColumnRef *cref)
{
int numnames = list_length(cref->fields);
Node *node;
int levels_up;
/*----------
* 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 (numnames)
{
ListCell *tlist_item = list_head(qtree->targetList);
case 1:
{
char *name = strVal(linitial(cref->fields));
/* Try to identify as an unqualified column */
node = colNameToVar(pstate, name, false);
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(name, "value") == 0)
{
node = (Node *) copyObject(pstate->p_value_substitute);
break;
}
/*
* Try to find the name as a relation. Note that only
* relations already entered into the rangetable will
* be recognized.
*
* This is a hack for backwards compatibility with
* PostQUEL-inspired syntax. The preferred form now
* is "rel.*".
*/
if (refnameRangeTblEntry(pstate, NULL, name,
&levels_up) != NULL)
node = transformWholeRowRef(pstate, NULL, name);
else
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("column \"%s\" does not exist",
name)));
}
break;
}
case 2:
{
char *name1 = strVal(linitial(cref->fields));
char *name2 = strVal(lsecond(cref->fields));
/* Whole-row reference? */
if (strcmp(name2, "*") == 0)
{
node = transformWholeRowRef(pstate, NULL, name1);
break;
}
/* Try to identify as a once-qualified column */
node = qualifiedNameToVar(pstate, NULL, name1, name2, true);
if (node == NULL)
{
/*
* Not known as a column of any range-table entry, so
* try it as a function call. Here, we will create an
* implicit RTE for tables not already entered.
*/
node = transformWholeRowRef(pstate, NULL, name1);
node = ParseFuncOrColumn(pstate,
list_make1(makeString(name2)),
list_make1(node),
false, false, true);
}
break;
}
case 3:
{
char *name1 = strVal(linitial(cref->fields));
char *name2 = strVal(lsecond(cref->fields));
char *name3 = strVal(lthird(cref->fields));
/* Whole-row reference? */
if (strcmp(name3, "*") == 0)
{
node = transformWholeRowRef(pstate, name1, name2);
break;
}
/* Try to identify as a twice-qualified column */
node = qualifiedNameToVar(pstate, name1, name2, name3, true);
if (node == NULL)
{
/* Try it as a function call */
node = transformWholeRowRef(pstate, name1, name2);
node = ParseFuncOrColumn(pstate,
list_make1(makeString(name3)),
list_make1(node),
false, false, true);
}
break;
}
case 4:
{
char *name1 = strVal(linitial(cref->fields));
char *name2 = strVal(lsecond(cref->fields));
char *name3 = strVal(lthird(cref->fields));
char *name4 = strVal(lfourth(cref->fields));
/*
* We check the catalog name and then ignore it.
*/
if (strcmp(name1, get_database_name(MyDatabaseId)) != 0)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cross-database references are not implemented: %s",
NameListToString(cref->fields))));
/* Whole-row reference? */
if (strcmp(name4, "*") == 0)
{
node = transformWholeRowRef(pstate, name2, name3);
break;
}
/* Try to identify as a twice-qualified column */
node = qualifiedNameToVar(pstate, name2, name3, name4, true);
if (node == NULL)
{
/* Try it as a function call */
node = transformWholeRowRef(pstate, name2, name3);
node = ParseFuncOrColumn(pstate,
list_make1(makeString(name4)),
list_make1(node),
false, false, true);
}
break;
}
default:
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("improper qualified name (too many dotted names): %s",
NameListToString(cref->fields))));
node = NULL; /* keep compiler quiet */
break;
}
return node;
}
static Node *
transformParamRef(ParseState *pstate, ParamRef *pref)
{
int paramno = pref->number;
ParseState *toppstate;
Param *param;
/*
* Make sure the subselect delivers a single column
* (ignoring resjunk targets).
* Find topmost ParseState, which is where paramtype info lives.
*/
if (tlist_item == NULL ||
((TargetEntry *) lfirst(tlist_item))->resdom->resjunk)
toppstate = pstate;
while (toppstate->parentParseState != NULL)
toppstate = toppstate->parentParseState;
/* Check parameter number is in range */
if (paramno <= 0) /* probably can't happen? */
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("subquery must return a column")));
while ((tlist_item = lnext(tlist_item)) != NULL)
(errcode(ERRCODE_UNDEFINED_PARAMETER),
errmsg("there is no parameter $%d", paramno)));
if (paramno > toppstate->p_numparams)
{
if (!((TargetEntry *) lfirst(tlist_item))->resdom->resjunk)
if (!toppstate->p_variableparams)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("subquery must return only one column")));
(errcode(ERRCODE_UNDEFINED_PARAMETER),
errmsg("there is no parameter $%d",
paramno)));
/* Okay to enlarge param array */
if (toppstate->p_paramtypes)
toppstate->p_paramtypes =
(Oid *) repalloc(toppstate->p_paramtypes,
paramno * sizeof(Oid));
else
toppstate->p_paramtypes =
(Oid *) palloc(paramno * sizeof(Oid));
/* Zero out the previously-unreferenced slots */
MemSet(toppstate->p_paramtypes + toppstate->p_numparams,
0,
(paramno - toppstate->p_numparams) * sizeof(Oid));
toppstate->p_numparams = paramno;
}
if (toppstate->p_variableparams)
{
/* If not seen before, initialize to UNKNOWN type */
if (toppstate->p_paramtypes[paramno - 1] == InvalidOid)
toppstate->p_paramtypes[paramno - 1] = UNKNOWNOID;
}
param = makeNode(Param);
param->paramkind = PARAM_NUM;
param->paramid = (AttrNumber) paramno;
param->paramtype = toppstate->p_paramtypes[paramno - 1];
return (Node *) param;
}
static Node *
transformAExprOp(ParseState *pstate, A_Expr *a)
{
Node *lexpr = a->lexpr;
Node *rexpr = a->rexpr;
Node *result;
/*
* EXPR and ARRAY need no lefthand or combining
* operator. These fields should be NIL already, but
* make sure.
* Special-case "foo = NULL" and "NULL = foo" for compatibility
* with standards-broken products (like Microsoft's). Turn these
* into IS NULL exprs.
*/
sublink->lefthand = NIL;
sublink->operName = NIL;
sublink->operOids = NIL;
sublink->useOr = FALSE;
}
else
if (Transform_null_equals &&
list_length(a->name) == 1 &&
strcmp(strVal(linitial(a->name)), "=") == 0 &&
(exprIsNullConstant(lexpr) || exprIsNullConstant(rexpr)))
{
/* ALL, ANY, or MULTIEXPR: generate operator list */
List *left_list = sublink->lefthand;
List *right_list = qtree->targetList;
int row_length = list_length(left_list);
bool needNot = false;
List *op = sublink->operName;
char *opname = strVal(llast(op));
ListCell *l;
ListCell *ll_item;
NullTest *n = makeNode(NullTest);
/* transform lefthand expressions */
foreach(l, left_list)
lfirst(l) = transformExpr(pstate, lfirst(l));
n->nulltesttype = IS_NULL;
if (exprIsNullConstant(lexpr))
n->arg = (Expr *) rexpr;
else
n->arg = (Expr *) lexpr;
result = transformExpr(pstate, (Node *) n);
}
else if (lexpr && IsA(lexpr, RowExpr) &&
rexpr && IsA(rexpr, SubLink) &&
((SubLink *) rexpr)->subLinkType == EXPR_SUBLINK)
{
/*
* If the expression is "<> ALL" (with unqualified
* opname) then convert it to "NOT IN". This is a
* hack to improve efficiency of expressions output by
* pre-7.4 Postgres.
* Convert "row op subselect" into a MULTIEXPR sublink.
* Formerly the grammar did this, but now that a row construct
* is allowed anywhere in expressions, it's easier to do it
* here.
*/
if (sublink->subLinkType == ALL_SUBLINK &&
list_length(op) == 1 && strcmp(opname, "<>") == 0)
SubLink *s = (SubLink *) rexpr;
s->subLinkType = MULTIEXPR_SUBLINK;
s->lefthand = ((RowExpr *) lexpr)->args;
s->operName = a->name;
result = transformExpr(pstate, (Node *) s);
}
else if (lexpr && IsA(lexpr, RowExpr) &&
rexpr && IsA(rexpr, RowExpr))
{
sublink->subLinkType = ANY_SUBLINK;
opname = pstrdup("=");
op = list_make1(makeString(opname));
sublink->operName = op;
needNot = true;
/* "row op row" */
result = make_row_op(pstate, a->name, lexpr, rexpr);
}
/* Set useOr if op is "<>" (possibly qualified) */
if (strcmp(opname, "<>") == 0)
sublink->useOr = TRUE;
else
sublink->useOr = FALSE;
{
/* Ordinary scalar operator */
lexpr = transformExpr(pstate, lexpr);
rexpr = transformExpr(pstate, rexpr);
/* Combining operators other than =/<> is dubious... */
if (row_length != 1 &&
strcmp(opname, "=") != 0 &&
strcmp(opname, "<>") != 0)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("row comparison cannot use operator %s",
opname)));
result = (Node *) make_op(pstate,
a->name,
lexpr,
rexpr);
}
/*
* To build the list of combining operator OIDs, we
* must scan subquery's targetlist to find values that
* will be matched against lefthand values. We need
* to ignore resjunk targets, so doing the outer
* iteration over right_list is easier than doing it
* over left_list.
*/
sublink->operOids = NIL;
return result;
}
ll_item = list_head(left_list);
foreach(l, right_list)
static Node *
transformAExprAnd(ParseState *pstate, A_Expr *a)
{
Node *lexpr = transformExpr(pstate, a->lexpr);
Node *rexpr = transformExpr(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));
}
static Node *
transformAExprOr(ParseState *pstate, A_Expr *a)
{
Node *lexpr = transformExpr(pstate, a->lexpr);
Node *rexpr = transformExpr(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));
}
static Node *
transformAExprNot(ParseState *pstate, A_Expr *a)
{
Node *rexpr = transformExpr(pstate, a->rexpr);
rexpr = coerce_to_boolean(pstate, rexpr, "NOT");
return (Node *) makeBoolExpr(NOT_EXPR,
list_make1(rexpr));
}
static Node *
transformAExprOpAny(ParseState *pstate, A_Expr *a)
{
Node *lexpr = transformExpr(pstate, a->lexpr);
Node *rexpr = transformExpr(pstate, a->rexpr);
return (Node *) make_scalar_array_op(pstate,
a->name,
true,
lexpr,
rexpr);
}
static Node *
transformAExprOpAll(ParseState *pstate, A_Expr *a)
{
Node *lexpr = transformExpr(pstate, a->lexpr);
Node *rexpr = transformExpr(pstate, a->rexpr);
return (Node *) make_scalar_array_op(pstate,
a->name,
false,
lexpr,
rexpr);
}
static Node *
transformAExprDistinct(ParseState *pstate, A_Expr *a)
{
Node *lexpr = a->lexpr;
Node *rexpr = a->rexpr;
if (lexpr && IsA(lexpr, RowExpr) &&
rexpr && IsA(rexpr, RowExpr))
{
TargetEntry *tent = (TargetEntry *) lfirst(l);
Node *lexpr;
Operator optup;
Form_pg_operator opform;
/* "row op row" */
return make_row_distinct_op(pstate, a->name,
lexpr, rexpr);
}
else
{
/* Ordinary scalar operator */
lexpr = transformExpr(pstate, lexpr);
rexpr = transformExpr(pstate, rexpr);
if (tent->resdom->resjunk)
continue;
return (Node *) make_distinct_op(pstate,
a->name,
lexpr,
rexpr);
}
}
if (ll_item == NULL)
static Node *
transformAExprNullIf(ParseState *pstate, A_Expr *a)
{
Node *lexpr = transformExpr(pstate, a->lexpr);
Node *rexpr = transformExpr(pstate, a->rexpr);
Node *result;
result = (Node *) make_op(pstate,
a->name,
lexpr,
rexpr);
if (((OpExpr *) result)->opresulttype != BOOLOID)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("subquery has too many columns")));
lexpr = lfirst(ll_item);
ll_item = lnext(ll_item);
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("NULLIF requires = operator to yield boolean")));
/*
* It's OK to use oper() not compatible_oper()
* here, because make_subplan() will insert type
* coercion calls if needed.
* We rely on NullIfExpr and OpExpr being the same struct
*/
optup = oper(op,
exprType(lexpr),
exprType((Node *) tent->expr),
false);
opform = (Form_pg_operator) GETSTRUCT(optup);
NodeSetTag(result, T_NullIfExpr);
if (opform->oprresult != BOOLOID)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("operator %s must return type boolean, not type %s",
opname,
format_type_be(opform->oprresult)),
errhint("The operator of a quantified predicate subquery must return type boolean.")));
return result;
}
static Node *
transformAExprOf(ParseState *pstate, A_Expr *a)
{
/*
* Checking an expression for match to type. Will result in a
* boolean constant node.
*/
ListCell *telem;
A_Const *n;
Oid ltype,
rtype;
bool matched = false;
Node *lexpr = transformExpr(pstate, a->lexpr);
ltype = exprType(lexpr);
foreach(telem, (List *) a->rexpr)
{
rtype = LookupTypeName(lfirst(telem));
matched = (rtype == ltype);
if (matched)
break;
}
if (get_func_retset(opform->oprcode))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("operator %s must not return a set",
opname),
errhint("The operator of a quantified predicate subquery must return type boolean.")));
/*
* Expect two forms: equals or not equals. Flip the sense of the
* result for not equals.
*/
if (strcmp(strVal(linitial(a->name)), "!=") == 0)
matched = (!matched);
sublink->operOids = lappend_oid(sublink->operOids,
oprid(optup));
n = makeNode(A_Const);
n->val.type = T_String;
n->val.val.str = (matched ? "t" : "f");
n->typename = SystemTypeName("bool");
ReleaseSysCache(optup);
}
if (ll_item != NULL)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("subquery has too few columns")));
return transformExpr(pstate, (Node *) n);
}
if (needNot)
static Node *
transformFuncCall(ParseState *pstate, FuncCall *fn)
{
List *targs;
ListCell *args;
/*
* Transform the list of arguments. We use a shallow list copy
* and then transform-in-place to avoid O(N^2) behavior from
* repeated lappend's.
*
* XXX: repeated lappend() would no longer result in O(n^2)
* behavior; worth reconsidering this design?
*/
targs = list_copy(fn->args);
foreach(args, targs)
{
expr = coerce_to_boolean(pstate, expr, "NOT");
expr = (Node *) makeBoolExpr(NOT_EXPR,
list_make1(expr));
}
}
result = (Node *) expr;
break;
lfirst(args) = transformExpr(pstate,
(Node *) lfirst(args));
}
case T_CaseExpr:
{
CaseExpr *c = (CaseExpr *) expr;
return ParseFuncOrColumn(pstate,
fn->funcname,
targs,
fn->agg_star,
fn->agg_distinct,
false);
}
static Node *
transformCaseExpr(ParseState *pstate, CaseExpr *c)
{
CaseExpr *newc;
Node *arg;
CaseTestExpr *placeholder;
......@@ -638,10 +810,8 @@ transformExpr(ParseState *pstate, Node *expr)
/* If we already transformed this node, do nothing */
if (OidIsValid(c->casetype))
{
result = expr;
break;
}
return (Node *) c;
newc = makeNode(CaseExpr);
/* transform the test expression, if any */
......@@ -651,16 +821,15 @@ transformExpr(ParseState *pstate, Node *expr)
if (arg)
{
/*
* 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 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");
arg = coerce_to_common_type(pstate, arg, TEXTOID, "CASE");
placeholder = makeNode(CaseTestExpr);
placeholder->typeId = exprType(arg);
placeholder->typeMod = exprTypmod(arg);
......@@ -716,491 +885,388 @@ transformExpr(ParseState *pstate, Node *expr)
newc->defresult = (Expr *) transformExpr(pstate, defresult);
/*
* Note: default result is considered the most significant
* type in determining preferred type. This is how the
* code worked before, but it seems a little bogus to me
* Note: default result is considered the most significant type in
* determining preferred type. This is how the code worked before,
* but it seems a little bogus to me
* --- tgl
*/
typeids = lcons_oid(exprType((Node *) newc->defresult), typeids);
ptype = select_common_type(typeids, "CASE");
Assert(OidIsValid(ptype));
newc->casetype = ptype;
/* 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");
}
result = (Node *) newc;
break;
}
case T_ArrayExpr:
{
ArrayExpr *a = (ArrayExpr *) expr;
ArrayExpr *newa = makeNode(ArrayExpr);
List *newelems = NIL;
List *newcoercedelems = NIL;
List *typeids = NIL;
ListCell *element;
Oid array_type;
Oid element_type;
/* Transform the element expressions */
foreach(element, a->elements)
{
Node *e = (Node *) lfirst(element);
Node *newe;
newe = transformExpr(pstate, e);
newelems = lappend(newelems, newe);
typeids = lappend_oid(typeids, exprType(newe));
}
/* Select a common type for the elements */
element_type = select_common_type(typeids, "ARRAY");
/* Coerce arguments to common type if necessary */
foreach(element, newelems)
{
Node *e = (Node *) lfirst(element);
Node *newe;
newe = coerce_to_common_type(pstate, e,
element_type,
"ARRAY");
newcoercedelems = lappend(newcoercedelems, newe);
}
/* Do we have an array type to use? */
array_type = get_array_type(element_type);
if (array_type != InvalidOid)
{
/* Elements are presumably of scalar type */
newa->multidims = false;
}
else
{
/* Must be nested array expressions */
newa->multidims = true;
array_type = element_type;
element_type = get_element_type(array_type);
if (!OidIsValid(element_type))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("could not find array type for data type %s",
format_type_be(array_type))));
}
newa->array_typeid = array_type;
newa->element_typeid = element_type;
newa->elements = newcoercedelems;
result = (Node *) newa;
break;
}
case T_RowExpr:
{
RowExpr *r = (RowExpr *) expr;
RowExpr *newr = makeNode(RowExpr);
List *newargs = NIL;
ListCell *arg;
/* Transform the field expressions */
foreach(arg, r->args)
{
Node *e = (Node *) lfirst(arg);
Node *newe;
newe = transformExpr(pstate, e);
newargs = lappend(newargs, newe);
}
newr->args = newargs;
/* Barring later casting, we consider the type RECORD */
newr->row_typeid = RECORDOID;
newr->row_format = COERCE_IMPLICIT_CAST;
result = (Node *) newr;
break;
}
case T_CoalesceExpr:
{
CoalesceExpr *c = (CoalesceExpr *) expr;
CoalesceExpr *newc = makeNode(CoalesceExpr);
List *newargs = NIL;
List *newcoercedargs = NIL;
List *typeids = NIL;
ListCell *args;
foreach(args, c->args)
{
Node *e = (Node *) lfirst(args);
Node *newe;
newe = transformExpr(pstate, e);
newargs = lappend(newargs, newe);
typeids = lappend_oid(typeids, exprType(newe));
}
newc->coalescetype = select_common_type(typeids, "COALESCE");
/* 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;
result = (Node *) newc;
break;
}
case T_NullTest:
{
NullTest *n = (NullTest *) expr;
n->arg = (Expr *) transformExpr(pstate, (Node *) n->arg);
/* the argument can be any type, so don't coerce it */
result = expr;
break;
}
case T_BooleanTest:
{
BooleanTest *b = (BooleanTest *) expr;
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);
clausename = NULL; /* keep compiler quiet */
}
b->arg = (Expr *) transformExpr(pstate, (Node *) b->arg);
b->arg = (Expr *) coerce_to_boolean(pstate,
(Node *) b->arg,
clausename);
typeids = lcons_oid(exprType((Node *) newc->defresult), typeids);
result = expr;
break;
}
ptype = select_common_type(typeids, "CASE");
Assert(OidIsValid(ptype));
newc->casetype = ptype;
/*********************************************
* 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.
*********************************************/
case T_Var:
case T_Const:
case T_Param:
case T_Aggref:
case T_ArrayRef:
case T_FuncExpr:
case T_OpExpr:
case T_DistinctExpr:
case T_ScalarArrayOpExpr:
case T_NullIfExpr:
case T_BoolExpr:
case T_FieldSelect:
case T_FieldStore:
case T_RelabelType:
case T_ConvertRowtypeExpr:
case T_CaseTestExpr:
case T_CoerceToDomain:
case T_CoerceToDomainValue:
case T_SetToDefault:
/* 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)
{
result = (Node *) expr;
break;
}
CaseWhen *w = (CaseWhen *) lfirst(l);
default:
/* should not reach here */
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
break;
w->result = (Expr *)
coerce_to_common_type(pstate,
(Node *) w->result,
ptype,
"CASE/WHEN");
}
return result;
return (Node *) newc;
}
static Node *
transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
transformSubLink(ParseState *pstate, SubLink *sublink)
{
Node *result = basenode;
List *subscripts = NIL;
ListCell *i;
List *qtrees;
Query *qtree;
Node *result = (Node *) sublink;
/* If we already transformed this node, do nothing */
if (IsA(sublink->subselect, Query))
return result;
pstate->p_hasSubLinks = true;
qtrees = parse_sub_analyze(sublink->subselect, pstate);
if (list_length(qtrees) != 1)
elog(ERROR, "bad query in sub-select");
qtree = (Query *) linitial(qtrees);
if (qtree->commandType != CMD_SELECT ||
qtree->resultRelation != 0)
elog(ERROR, "bad query in sub-select");
sublink->subselect = (Node *) qtree;
if (sublink->subLinkType == EXISTS_SUBLINK)
{
/*
* We have to split any field-selection operations apart from
* subscripting. Adjacent A_Indices nodes have to be treated as a
* single multidimensional subscript operation.
* EXISTS needs no lefthand or combining operator. These
* fields should be NIL already, but make sure.
*/
foreach(i, indirection)
sublink->lefthand = NIL;
sublink->operName = NIL;
sublink->operOids = NIL;
sublink->useOr = FALSE;
}
else if (sublink->subLinkType == EXPR_SUBLINK ||
sublink->subLinkType == ARRAY_SUBLINK)
{
Node *n = lfirst(i);
ListCell *tlist_item = list_head(qtree->targetList);
if (IsA(n, A_Indices))
subscripts = lappend(subscripts, n);
/*
* Make sure the subselect delivers a single column (ignoring
* resjunk targets).
*/
if (tlist_item == NULL ||
((TargetEntry *) lfirst(tlist_item))->resdom->resjunk)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("subquery must return a column")));
while ((tlist_item = lnext(tlist_item)) != NULL)
{
if (!((TargetEntry *) lfirst(tlist_item))->resdom->resjunk)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("subquery must return only one column")));
}
/*
* EXPR and ARRAY need no lefthand or combining
* operator. These fields should be NIL already, but make
* sure.
*/
sublink->lefthand = NIL;
sublink->operName = NIL;
sublink->operOids = NIL;
sublink->useOr = FALSE;
}
else
{
Assert(IsA(n, String));
/* ALL, ANY, or MULTIEXPR: generate operator list */
List *left_list = sublink->lefthand;
List *right_list = qtree->targetList;
int row_length = list_length(left_list);
bool needNot = false;
List *op = sublink->operName;
char *opname = strVal(llast(op));
ListCell *l;
ListCell *ll_item;
/* process subscripts before this field selection */
if (subscripts)
result = (Node *) transformArraySubscripts(pstate,
result,
exprType(result),
InvalidOid,
-1,
subscripts,
NULL);
subscripts = NIL;
/* transform lefthand expressions */
foreach(l, left_list)
lfirst(l) = transformExpr(pstate, lfirst(l));
result = ParseFuncOrColumn(pstate,
list_make1(n),
list_make1(result),
false, false, true);
}
/*
* If the expression is "<> ALL" (with unqualified opname)
* then convert it to "NOT IN". This is a hack to improve
* efficiency of expressions output by pre-7.4 Postgres.
*/
if (sublink->subLinkType == ALL_SUBLINK &&
list_length(op) == 1 && strcmp(opname, "<>") == 0)
{
sublink->subLinkType = ANY_SUBLINK;
opname = pstrdup("=");
op = list_make1(makeString(opname));
sublink->operName = op;
needNot = true;
}
/* process trailing subscripts, if any */
if (subscripts)
result = (Node *) transformArraySubscripts(pstate,
result,
exprType(result),
InvalidOid,
-1,
subscripts,
NULL);
return result;
}
/* Set useOr if op is "<>" (possibly qualified) */
if (strcmp(opname, "<>") == 0)
sublink->useOr = TRUE;
else
sublink->useOr = FALSE;
static Node *
transformColumnRef(ParseState *pstate, ColumnRef *cref)
{
int numnames = list_length(cref->fields);
Node *node;
int levels_up;
/* Combining operators other than =/<> is dubious... */
if (row_length != 1 &&
strcmp(opname, "=") != 0 &&
strcmp(opname, "<>") != 0)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("row comparison cannot use operator %s",
opname)));
/*----------
* 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.
*----------
/*
* To build the list of combining operator OIDs, we must scan
* subquery's targetlist to find values that will be matched
* against lefthand values. We need to ignore resjunk
* targets, so doing the outer iteration over right_list is
* easier than doing it over left_list.
*/
switch (numnames)
{
case 1:
sublink->operOids = NIL;
ll_item = list_head(left_list);
foreach(l, right_list)
{
char *name = strVal(linitial(cref->fields));
TargetEntry *tent = (TargetEntry *) lfirst(l);
Node *lexpr;
Operator optup;
Form_pg_operator opform;
/* Try to identify as an unqualified column */
node = colNameToVar(pstate, name, false);
if (tent->resdom->resjunk)
continue;
if (ll_item == NULL)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("subquery has too many columns")));
lexpr = lfirst(ll_item);
ll_item = lnext(ll_item);
/*
* It's OK to use oper() not compatible_oper() here,
* because make_subplan() will insert type coercion calls
* if needed.
*/
optup = oper(op,
exprType(lexpr),
exprType((Node *) tent->expr),
false);
opform = (Form_pg_operator) GETSTRUCT(optup);
if (opform->oprresult != BOOLOID)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("operator %s must return type boolean, not type %s",
opname,
format_type_be(opform->oprresult)),
errhint("The operator of a quantified predicate subquery must return type boolean.")));
if (get_func_retset(opform->oprcode))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("operator %s must not return a set",
opname),
errhint("The operator of a quantified predicate subquery must return type boolean.")));
sublink->operOids = lappend_oid(sublink->operOids,
oprid(optup));
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(name, "value") == 0)
{
node = (Node *) copyObject(pstate->p_value_substitute);
break;
ReleaseSysCache(optup);
}
/*
* Try to find the name as a relation. Note that only
* relations already entered into the rangetable will
* be recognized.
*
* This is a hack for backwards compatibility with
* PostQUEL-inspired syntax. The preferred form now
* is "rel.*".
*/
if (refnameRangeTblEntry(pstate, NULL, name,
&levels_up) != NULL)
node = transformWholeRowRef(pstate, NULL, name);
else
if (ll_item != NULL)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("column \"%s\" does not exist",
name)));
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("subquery has too few columns")));
if (needNot)
{
result = coerce_to_boolean(pstate, result, "NOT");
result = (Node *) makeBoolExpr(NOT_EXPR,
list_make1(result));
}
break;
}
case 2:
{
char *name1 = strVal(linitial(cref->fields));
char *name2 = strVal(lsecond(cref->fields));
/* Whole-row reference? */
if (strcmp(name2, "*") == 0)
return result;
}
static Node *
transformArrayExpr(ParseState *pstate, ArrayExpr *a)
{
ArrayExpr *newa = makeNode(ArrayExpr);
List *newelems = NIL;
List *newcoercedelems = NIL;
List *typeids = NIL;
ListCell *element;
Oid array_type;
Oid element_type;
/* Transform the element expressions */
foreach(element, a->elements)
{
node = transformWholeRowRef(pstate, NULL, name1);
break;
Node *e = (Node *) lfirst(element);
Node *newe;
newe = transformExpr(pstate, e);
newelems = lappend(newelems, newe);
typeids = lappend_oid(typeids, exprType(newe));
}
/* Try to identify as a once-qualified column */
node = qualifiedNameToVar(pstate, NULL, name1, name2, true);
if (node == NULL)
/* Select a common type for the elements */
element_type = select_common_type(typeids, "ARRAY");
/* Coerce arguments to common type if necessary */
foreach(element, newelems)
{
/*
* Not known as a column of any range-table entry, so
* try it as a function call. Here, we will create an
* implicit RTE for tables not already entered.
*/
node = transformWholeRowRef(pstate, NULL, name1);
node = ParseFuncOrColumn(pstate,
list_make1(makeString(name2)),
list_make1(node),
false, false, true);
Node *e = (Node *) lfirst(element);
Node *newe;
newe = coerce_to_common_type(pstate, e,
element_type,
"ARRAY");
newcoercedelems = lappend(newcoercedelems, newe);
}
break;
/* Do we have an array type to use? */
array_type = get_array_type(element_type);
if (array_type != InvalidOid)
{
/* Elements are presumably of scalar type */
newa->multidims = false;
}
case 3:
else
{
char *name1 = strVal(linitial(cref->fields));
char *name2 = strVal(lsecond(cref->fields));
char *name3 = strVal(lthird(cref->fields));
/* Must be nested array expressions */
newa->multidims = true;
/* Whole-row reference? */
if (strcmp(name3, "*") == 0)
{
node = transformWholeRowRef(pstate, name1, name2);
break;
array_type = element_type;
element_type = get_element_type(array_type);
if (!OidIsValid(element_type))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("could not find array type for data type %s",
format_type_be(array_type))));
}
/* Try to identify as a twice-qualified column */
node = qualifiedNameToVar(pstate, name1, name2, name3, true);
if (node == NULL)
newa->array_typeid = array_type;
newa->element_typeid = element_type;
newa->elements = newcoercedelems;
return (Node *) newa;
}
static Node *
transformRowExpr(ParseState *pstate, RowExpr *r)
{
RowExpr *newr = makeNode(RowExpr);
List *newargs = NIL;
ListCell *arg;
/* Transform the field expressions */
foreach(arg, r->args)
{
/* Try it as a function call */
node = transformWholeRowRef(pstate, name1, name2);
node = ParseFuncOrColumn(pstate,
list_make1(makeString(name3)),
list_make1(node),
false, false, true);
}
break;
Node *e = (Node *) lfirst(arg);
Node *newe;
newe = transformExpr(pstate, e);
newargs = lappend(newargs, newe);
}
case 4:
{
char *name1 = strVal(linitial(cref->fields));
char *name2 = strVal(lsecond(cref->fields));
char *name3 = strVal(lthird(cref->fields));
char *name4 = strVal(lfourth(cref->fields));
newr->args = newargs;
/*
* 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))));
/* Barring later casting, we consider the type RECORD */
newr->row_typeid = RECORDOID;
newr->row_format = COERCE_IMPLICIT_CAST;
/* Whole-row reference? */
if (strcmp(name4, "*") == 0)
return (Node *) newr;
}
static Node *
transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c)
{
CoalesceExpr *newc = makeNode(CoalesceExpr);
List *newargs = NIL;
List *newcoercedargs = NIL;
List *typeids = NIL;
ListCell *args;
foreach(args, c->args)
{
node = transformWholeRowRef(pstate, name2, name3);
break;
Node *e = (Node *) lfirst(args);
Node *newe;
newe = transformExpr(pstate, e);
newargs = lappend(newargs, newe);
typeids = lappend_oid(typeids, exprType(newe));
}
/* Try to identify as a twice-qualified column */
node = qualifiedNameToVar(pstate, name2, name3, name4, true);
if (node == NULL)
newc->coalescetype = select_common_type(typeids, "COALESCE");
/* Convert arguments if necessary */
foreach(args, newargs)
{
/* Try it as a function call */
node = transformWholeRowRef(pstate, name2, name3);
node = ParseFuncOrColumn(pstate,
list_make1(makeString(name4)),
list_make1(node),
false, false, true);
Node *e = (Node *) lfirst(args);
Node *newe;
newe = coerce_to_common_type(pstate, e,
newc->coalescetype,
"COALESCE");
newcoercedargs = lappend(newcoercedargs, newe);
}
newc->args = newcoercedargs;
return (Node *) newc;
}
static Node *
transformBooleanTest(ParseState *pstate, BooleanTest *b)
{
const char *clausename;
switch (b->booltesttype)
{
case IS_TRUE:
clausename = "IS TRUE";
break;
}
default:
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("improper qualified name (too many dotted names): %s",
NameListToString(cref->fields))));
node = NULL; /* keep compiler quiet */
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);
clausename = NULL; /* keep compiler quiet */
}
return node;
b->arg = (Expr *) transformExpr(pstate, (Node *) b->arg);
b->arg = (Expr *) coerce_to_boolean(pstate,
(Node *) b->arg,
clausename);
return (Node *) b;
}
/*
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment