mirror of
https://github.com/postgres/postgres.git
synced 2025-08-22 21:53:06 +03:00
Get rid of long-since-vestigial Iter node type, in favor of adding a
returns-set boolean field in Func and Oper nodes. This allows cleaner, more reliable tests for expressions returning sets in the planner and parser. For example, a WHERE clause returning a set is now detected and complained of in the parser, not only at runtime.
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.91 2002/05/12 20:10:04 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.92 2002/05/12 23:43:03 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -285,14 +285,7 @@ transformJoinUsingClause(ParseState *pstate, List *leftVars, List *rightVars)
|
||||
*/
|
||||
result = transformExpr(pstate, result);
|
||||
|
||||
/*
|
||||
* We expect the result to yield bool directly, otherwise complain. We
|
||||
* could try coerce_to_boolean() here, but it seems likely that an "="
|
||||
* operator that doesn't return bool is wrong anyway.
|
||||
*/
|
||||
if (exprType(result) != BOOLOID)
|
||||
elog(ERROR, "JOIN/USING clause must return type boolean, not type %s",
|
||||
format_type_be(exprType(result)));
|
||||
result = coerce_to_boolean(result, "JOIN/USING");
|
||||
|
||||
return result;
|
||||
} /* transformJoinUsingClause() */
|
||||
@@ -326,9 +319,7 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j,
|
||||
/* This part is just like transformWhereClause() */
|
||||
result = transformExpr(pstate, j->quals);
|
||||
|
||||
if (!coerce_to_boolean(pstate, &result))
|
||||
elog(ERROR, "JOIN/ON clause must return type boolean, not type %s",
|
||||
format_type_be(exprType(result)));
|
||||
result = coerce_to_boolean(result, "JOIN/ON");
|
||||
|
||||
pstate->p_namespace = save_namespace;
|
||||
|
||||
@@ -486,14 +477,7 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
|
||||
elog(ERROR, "cannot use subselect in FROM function expression");
|
||||
|
||||
/*
|
||||
* Remove any Iter nodes added by parse_func.c. We oughta get rid of
|
||||
* Iter completely ...
|
||||
*/
|
||||
while (funcexpr && IsA(funcexpr, Iter))
|
||||
funcexpr = ((Iter *) funcexpr)->iterexpr;
|
||||
|
||||
/*
|
||||
* Insist we now have a bare function call (explain.c is the only place
|
||||
* Insist we have a bare function call (explain.c is the only place
|
||||
* that depends on this, I think). If this fails, it's probably because
|
||||
* transformExpr interpreted the function notation as a type coercion.
|
||||
*/
|
||||
@@ -947,9 +931,7 @@ transformWhereClause(ParseState *pstate, Node *clause)
|
||||
|
||||
qual = transformExpr(pstate, clause);
|
||||
|
||||
if (!coerce_to_boolean(pstate, &qual))
|
||||
elog(ERROR, "WHERE clause must return type boolean, not type %s",
|
||||
format_type_be(exprType(qual)));
|
||||
qual = coerce_to_boolean(qual, "WHERE");
|
||||
|
||||
return qual;
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.71 2002/04/25 02:56:55 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.72 2002/05/12 23:43:03 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -313,26 +313,37 @@ coerce_type_typmod(ParseState *pstate, Node *node,
|
||||
|
||||
/* coerce_to_boolean()
|
||||
* Coerce an argument of a construct that requires boolean input
|
||||
* (AND, OR, NOT, etc).
|
||||
* (AND, OR, NOT, etc). Also check that input is not a set.
|
||||
*
|
||||
* If successful, update *pnode to be the transformed argument (if any
|
||||
* transformation is needed), and return TRUE. If fail, return FALSE.
|
||||
* (The caller must check for FALSE and emit a suitable error message.)
|
||||
* Returns the possibly-transformed node tree.
|
||||
*/
|
||||
bool
|
||||
coerce_to_boolean(ParseState *pstate, Node **pnode)
|
||||
Node *
|
||||
coerce_to_boolean(Node *node, const char *constructName)
|
||||
{
|
||||
Oid inputTypeId = exprType(*pnode);
|
||||
Oid inputTypeId = exprType(node);
|
||||
Oid targetTypeId;
|
||||
|
||||
if (inputTypeId == BOOLOID)
|
||||
return true; /* no work */
|
||||
targetTypeId = BOOLOID;
|
||||
if (!can_coerce_type(1, &inputTypeId, &targetTypeId, false))
|
||||
return false; /* fail, but let caller choose error msg */
|
||||
*pnode = coerce_type(pstate, *pnode, inputTypeId, targetTypeId, -1,
|
||||
false);
|
||||
return true;
|
||||
if (inputTypeId != BOOLOID)
|
||||
{
|
||||
targetTypeId = BOOLOID;
|
||||
if (!can_coerce_type(1, &inputTypeId, &targetTypeId, false))
|
||||
{
|
||||
/* translator: first %s is name of a SQL construct, eg WHERE */
|
||||
elog(ERROR, "Argument of %s must be type boolean, not type %s",
|
||||
constructName, format_type_be(inputTypeId));
|
||||
}
|
||||
node = coerce_type(NULL, node, inputTypeId, targetTypeId, -1,
|
||||
false);
|
||||
}
|
||||
|
||||
if (expression_returns_set(node))
|
||||
{
|
||||
/* translator: %s is name of a SQL construct, eg WHERE */
|
||||
elog(ERROR, "Argument of %s must not be a set function",
|
||||
constructName);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
@@ -782,7 +793,8 @@ build_func_call(Oid funcid, Oid rettype, List *args)
|
||||
|
||||
funcnode = makeNode(Func);
|
||||
funcnode->funcid = funcid;
|
||||
funcnode->functype = rettype;
|
||||
funcnode->funcresulttype = rettype;
|
||||
funcnode->funcretset = false; /* only possible case here */
|
||||
funcnode->func_fcache = NULL;
|
||||
|
||||
expr = makeNode(Expr);
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.116 2002/04/28 00:49:12 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.117 2002/05/12 23:43:03 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "parser/parse_target.h"
|
||||
#include "parser/parse_type.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/lsyscache.h"
|
||||
#include "utils/syscache.h"
|
||||
|
||||
|
||||
@@ -230,15 +231,8 @@ transformExpr(ParseState *pstate, Node *expr)
|
||||
a->rexpr);
|
||||
Expr *expr = makeNode(Expr);
|
||||
|
||||
if (!coerce_to_boolean(pstate, &lexpr))
|
||||
elog(ERROR, "left-hand side of AND is type '%s', not '%s'",
|
||||
format_type_be(exprType(lexpr)),
|
||||
format_type_be(BOOLOID));
|
||||
|
||||
if (!coerce_to_boolean(pstate, &rexpr))
|
||||
elog(ERROR, "right-hand side of AND is type '%s', not '%s'",
|
||||
format_type_be(exprType(rexpr)),
|
||||
format_type_be(BOOLOID));
|
||||
lexpr = coerce_to_boolean(lexpr, "AND");
|
||||
rexpr = coerce_to_boolean(rexpr, "AND");
|
||||
|
||||
expr->typeOid = BOOLOID;
|
||||
expr->opType = AND_EXPR;
|
||||
@@ -254,15 +248,8 @@ transformExpr(ParseState *pstate, Node *expr)
|
||||
a->rexpr);
|
||||
Expr *expr = makeNode(Expr);
|
||||
|
||||
if (!coerce_to_boolean(pstate, &lexpr))
|
||||
elog(ERROR, "left-hand side of OR is type '%s', not '%s'",
|
||||
format_type_be(exprType(lexpr)),
|
||||
format_type_be(BOOLOID));
|
||||
|
||||
if (!coerce_to_boolean(pstate, &rexpr))
|
||||
elog(ERROR, "right-hand side of OR is type '%s', not '%s'",
|
||||
format_type_be(exprType(rexpr)),
|
||||
format_type_be(BOOLOID));
|
||||
lexpr = coerce_to_boolean(lexpr, "OR");
|
||||
rexpr = coerce_to_boolean(rexpr, "OR");
|
||||
|
||||
expr->typeOid = BOOLOID;
|
||||
expr->opType = OR_EXPR;
|
||||
@@ -276,10 +263,7 @@ transformExpr(ParseState *pstate, Node *expr)
|
||||
a->rexpr);
|
||||
Expr *expr = makeNode(Expr);
|
||||
|
||||
if (!coerce_to_boolean(pstate, &rexpr))
|
||||
elog(ERROR, "argument to NOT is type '%s', not '%s'",
|
||||
format_type_be(exprType(rexpr)),
|
||||
format_type_be(BOOLOID));
|
||||
rexpr = coerce_to_boolean(rexpr, "NOT");
|
||||
|
||||
expr->typeOid = BOOLOID;
|
||||
expr->opType = NOT_EXPR;
|
||||
@@ -426,9 +410,15 @@ transformExpr(ParseState *pstate, Node *expr)
|
||||
opname, typeidTypeName(opform->oprresult),
|
||||
typeidTypeName(BOOLOID));
|
||||
|
||||
if (get_func_retset(opform->oprcode))
|
||||
elog(ERROR, "'%s' must not return a set"
|
||||
" to be used with quantified predicate subquery",
|
||||
opname);
|
||||
|
||||
newop = makeOper(oprid(optup), /* opno */
|
||||
InvalidOid, /* opid */
|
||||
opform->oprresult);
|
||||
opform->oprresult,
|
||||
false);
|
||||
sublink->oper = lappend(sublink->oper, newop);
|
||||
ReleaseSysCache(optup);
|
||||
}
|
||||
@@ -467,8 +457,7 @@ transformExpr(ParseState *pstate, Node *expr)
|
||||
}
|
||||
neww->expr = transformExpr(pstate, warg);
|
||||
|
||||
if (!coerce_to_boolean(pstate, &neww->expr))
|
||||
elog(ERROR, "WHEN clause must have a boolean result");
|
||||
neww->expr = coerce_to_boolean(neww->expr, "CASE/WHEN");
|
||||
|
||||
/*
|
||||
* result is NULL for NULLIF() construct - thomas
|
||||
@@ -553,42 +542,38 @@ transformExpr(ParseState *pstate, Node *expr)
|
||||
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, "transformExpr: unexpected booltesttype %d",
|
||||
(int) b->booltesttype);
|
||||
clausename = NULL; /* keep compiler quiet */
|
||||
}
|
||||
|
||||
b->arg = transformExpr(pstate, b->arg);
|
||||
|
||||
if (!coerce_to_boolean(pstate, &b->arg))
|
||||
{
|
||||
const char *clausename;
|
||||
b->arg = coerce_to_boolean(b->arg, 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, "transformExpr: unexpected booltesttype %d",
|
||||
(int) b->booltesttype);
|
||||
clausename = NULL; /* keep compiler quiet */
|
||||
}
|
||||
|
||||
elog(ERROR, "Argument of %s must be boolean",
|
||||
clausename);
|
||||
}
|
||||
result = expr;
|
||||
break;
|
||||
}
|
||||
@@ -833,12 +818,6 @@ exprType(Node *expr)
|
||||
|
||||
switch (nodeTag(expr))
|
||||
{
|
||||
case T_Func:
|
||||
type = ((Func *) expr)->functype;
|
||||
break;
|
||||
case T_Iter:
|
||||
type = ((Iter *) expr)->itertype;
|
||||
break;
|
||||
case T_Var:
|
||||
type = ((Var *) expr)->vartype;
|
||||
break;
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.128 2002/05/12 20:10:04 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.129 2002/05/12 23:43:03 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -280,7 +280,8 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
|
||||
Func *funcnode = makeNode(Func);
|
||||
|
||||
funcnode->funcid = funcid;
|
||||
funcnode->functype = rettype;
|
||||
funcnode->funcresulttype = rettype;
|
||||
funcnode->funcretset = retset;
|
||||
funcnode->func_fcache = NULL;
|
||||
|
||||
expr->typeOid = rettype;
|
||||
@@ -289,21 +290,6 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
|
||||
expr->args = fargs;
|
||||
|
||||
retval = (Node *) expr;
|
||||
|
||||
/*
|
||||
* if the function returns a set of values, then we need to iterate
|
||||
* over all the returned values in the executor, so we stick an iter
|
||||
* node here. if it returns a singleton, then we don't need the iter
|
||||
* node.
|
||||
*/
|
||||
if (retset)
|
||||
{
|
||||
Iter *iter = makeNode(Iter);
|
||||
|
||||
iter->itertype = rettype;
|
||||
iter->iterexpr = retval;
|
||||
retval = (Node *) iter;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1186,26 +1172,6 @@ ParseComplexProjection(ParseState *pstate,
|
||||
*/
|
||||
switch (nodeTag(first_arg))
|
||||
{
|
||||
case T_Iter:
|
||||
{
|
||||
Iter *iter = (Iter *) first_arg;
|
||||
|
||||
/*
|
||||
* If it's an Iter, we stick the FieldSelect
|
||||
* *inside* the Iter --- this is klugy, but necessary
|
||||
* because ExecTargetList() currently does the right thing
|
||||
* only when the Iter node is at the top level of a
|
||||
* targetlist item.
|
||||
*
|
||||
* XXX Iter should go away altogether...
|
||||
*/
|
||||
fselect = setup_field_select(iter->iterexpr,
|
||||
funcname, argrelid);
|
||||
iter->iterexpr = (Node *) fselect;
|
||||
iter->itertype = fselect->resulttype;
|
||||
return (Node *) iter;
|
||||
break;
|
||||
}
|
||||
case T_Var:
|
||||
{
|
||||
Var *var = (Var *) first_arg;
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.64 2002/05/01 19:26:07 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.65 2002/05/12 23:43:03 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -135,7 +135,8 @@ make_op(List *opname, Node *ltree, Node *rtree)
|
||||
|
||||
newop = makeOper(oprid(tup), /* opno */
|
||||
InvalidOid, /* opid */
|
||||
opform->oprresult); /* operator result type */
|
||||
opform->oprresult, /* opresulttype */
|
||||
get_func_retset(opform->oprcode)); /* opretset */
|
||||
|
||||
result = makeNode(Expr);
|
||||
result->typeOid = opform->oprresult;
|
||||
|
Reference in New Issue
Block a user