aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser')
-rw-r--r--src/backend/parser/gram.y135
-rw-r--r--src/backend/parser/keywords.c3
-rw-r--r--src/backend/parser/parse_clause.c32
-rw-r--r--src/backend/parser/parse_coerce.c26
-rw-r--r--src/backend/parser/parse_expr.c120
5 files changed, 214 insertions, 102 deletions
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 263830244dc..e47c3f0b331 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.230 2001/06/09 23:21:54 petere Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.231 2001/06/19 22:39:11 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -315,7 +315,7 @@ static void doNegateFloat(Value *v);
SCHEMA, SCROLL, SECOND_P, SELECT, SESSION, SESSION_USER, SET, SOME, SUBSTRING,
TABLE, TEMPORARY, THEN, TIME, TIMESTAMP, TIMEZONE_HOUR,
TIMEZONE_MINUTE, TO, TRAILING, TRANSACTION, TRIM, TRUE_P,
- UNION, UNIQUE, UPDATE, USER, USING,
+ UNION, UNIQUE, UNKNOWN, UPDATE, USER, USING,
VALUES, VARCHAR, VARYING, VIEW,
WHEN, WHERE, WITH, WORK, YEAR_P, ZONE
@@ -386,7 +386,7 @@ static void doNegateFloat(Value *v);
%left Op /* multi-character ops and user-defined operators */
%nonassoc NOTNULL
%nonassoc ISNULL
-%nonassoc IS NULL_P TRUE_P FALSE_P /* sets precedence for IS NULL, etc */
+%nonassoc IS NULL_P TRUE_P FALSE_P UNKNOWN /* sets precedence for IS NULL, etc */
%left '+' '-'
%left '*' '/' '%'
%left '^'
@@ -4434,9 +4434,19 @@ a_expr: c_expr
* (like Microsoft's). Turn these into IS NULL exprs.
*/
if (exprIsNullConstant($3))
- $$ = makeA_Expr(ISNULL, NULL, $1, NULL);
+ {
+ NullTest *n = makeNode(NullTest);
+ n->arg = $1;
+ n->nulltesttype = IS_NULL;
+ $$ = (Node *)n;
+ }
else if (exprIsNullConstant($1))
- $$ = makeA_Expr(ISNULL, NULL, $3, NULL);
+ {
+ NullTest *n = makeNode(NullTest);
+ n->arg = $3;
+ n->nulltesttype = IS_NULL;
+ $$ = (Node *)n;
+ }
else
$$ = makeA_Expr(OP, "=", $1, $3);
}
@@ -4499,59 +4509,95 @@ a_expr: c_expr
n->agg_distinct = FALSE;
$$ = makeA_Expr(OP, "!~~*", $1, (Node *) n);
}
-
+ /* NullTest clause
+ * Define SQL92-style Null test clause.
+ * Allow two forms described in the standard:
+ * a IS NULL
+ * a IS NOT NULL
+ * Allow two SQL extensions
+ * a ISNULL
+ * a NOTNULL
+ * NOTE: this is not yet fully SQL-compatible, since SQL92
+ * allows a row constructor as argument, not just a scalar.
+ */
| a_expr ISNULL
- { $$ = makeA_Expr(ISNULL, NULL, $1, NULL); }
+ {
+ NullTest *n = makeNode(NullTest);
+ n->arg = $1;
+ n->nulltesttype = IS_NULL;
+ $$ = (Node *)n;
+ }
| a_expr IS NULL_P
- { $$ = makeA_Expr(ISNULL, NULL, $1, NULL); }
+ {
+ NullTest *n = makeNode(NullTest);
+ n->arg = $1;
+ n->nulltesttype = IS_NULL;
+ $$ = (Node *)n;
+ }
| a_expr NOTNULL
- { $$ = makeA_Expr(NOTNULL, NULL, $1, NULL); }
+ {
+ NullTest *n = makeNode(NullTest);
+ n->arg = $1;
+ n->nulltesttype = IS_NOT_NULL;
+ $$ = (Node *)n;
+ }
| a_expr IS NOT NULL_P
- { $$ = makeA_Expr(NOTNULL, NULL, $1, NULL); }
+ {
+ NullTest *n = makeNode(NullTest);
+ n->arg = $1;
+ n->nulltesttype = IS_NOT_NULL;
+ $$ = (Node *)n;
+ }
/* IS TRUE, IS FALSE, etc used to be function calls
* but let's make them expressions to allow the optimizer
* a chance to eliminate them if a_expr is a constant string.
* - thomas 1997-12-22
+ *
+ * Created BooleanTest Node type, and changed handling
+ * for NULL inputs
+ * - jec 2001-06-18
*/
| a_expr IS TRUE_P
{
- A_Const *n = makeNode(A_Const);
- n->val.type = T_String;
- n->val.val.str = "t";
- n->typename = makeNode(TypeName);
- n->typename->name = xlateSqlType("bool");
- n->typename->typmod = -1;
- $$ = makeA_Expr(OP, "=", $1,(Node *)n);
+ BooleanTest *b = makeNode(BooleanTest);
+ b->arg = $1;
+ b->booltesttype = IS_TRUE;
+ $$ = (Node *)b;
}
- | a_expr IS NOT FALSE_P
+ | a_expr IS NOT TRUE_P
{
- A_Const *n = makeNode(A_Const);
- n->val.type = T_String;
- n->val.val.str = "t";
- n->typename = makeNode(TypeName);
- n->typename->name = xlateSqlType("bool");
- n->typename->typmod = -1;
- $$ = makeA_Expr(OP, "=", $1,(Node *)n);
+ BooleanTest *b = makeNode(BooleanTest);
+ b->arg = $1;
+ b->booltesttype = IS_NOT_TRUE;
+ $$ = (Node *)b;
}
| a_expr IS FALSE_P
{
- A_Const *n = makeNode(A_Const);
- n->val.type = T_String;
- n->val.val.str = "f";
- n->typename = makeNode(TypeName);
- n->typename->name = xlateSqlType("bool");
- n->typename->typmod = -1;
- $$ = makeA_Expr(OP, "=", $1,(Node *)n);
+ BooleanTest *b = makeNode(BooleanTest);
+ b->arg = $1;
+ b->booltesttype = IS_FALSE;
+ $$ = (Node *)b;
}
- | a_expr IS NOT TRUE_P
+ | a_expr IS NOT FALSE_P
{
- A_Const *n = makeNode(A_Const);
- n->val.type = T_String;
- n->val.val.str = "f";
- n->typename = makeNode(TypeName);
- n->typename->name = xlateSqlType("bool");
- n->typename->typmod = -1;
- $$ = makeA_Expr(OP, "=", $1,(Node *)n);
+ BooleanTest *b = makeNode(BooleanTest);
+ b->arg = $1;
+ b->booltesttype = IS_NOT_FALSE;
+ $$ = (Node *)b;
+ }
+ | a_expr IS UNKNOWN
+ {
+ BooleanTest *b = makeNode(BooleanTest);
+ b->arg = $1;
+ b->booltesttype = IS_UNKNOWN;
+ $$ = (Node *)b;
+ }
+ | a_expr IS NOT UNKNOWN
+ {
+ BooleanTest *b = makeNode(BooleanTest);
+ b->arg = $1;
+ b->booltesttype = IS_NOT_UNKNOWN;
+ $$ = (Node *)b;
}
| a_expr BETWEEN b_expr AND b_expr %prec BETWEEN
{
@@ -5206,12 +5252,14 @@ case_expr: CASE case_arg when_clause_list case_default END_TRANS
| COALESCE '(' expr_list ')'
{
CaseExpr *c = makeNode(CaseExpr);
- CaseWhen *w;
List *l;
foreach (l,$3)
{
- w = makeNode(CaseWhen);
- w->expr = makeA_Expr(NOTNULL, NULL, lfirst(l), NULL);
+ CaseWhen *w = makeNode(CaseWhen);
+ NullTest *n = makeNode(NullTest);
+ n->arg = lfirst(l);
+ n->nulltesttype = IS_NOT_NULL;
+ w->expr = (Node *) n;
w->result = lfirst(l);
c->args = lappend(c->args, w);
}
@@ -5765,6 +5813,7 @@ ColLabel: ColId { $$ = $1; }
| TRUE_P { $$ = "true"; }
| UNION { $$ = "union"; }
| UNIQUE { $$ = "unique"; }
+ | UNKNOWN { $$ = "unknown"; }
| USER { $$ = "user"; }
| USING { $$ = "using"; }
| VACUUM { $$ = "vacuum"; }
diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c
index 6064ca8a8ff..ccdfb88a2e2 100644
--- a/src/backend/parser/keywords.c
+++ b/src/backend/parser/keywords.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.92 2001/05/08 21:06:43 petere Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.93 2001/06/19 22:39:11 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -265,6 +265,7 @@ static ScanKeyword ScanKeywords[] = {
{"type", TYPE_P},
{"union", UNION},
{"unique", UNIQUE},
+ {"unknown", UNKNOWN},
{"unlisten", UNLISTEN},
{"until", UNTIL},
{"update", UPDATE},
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 02c6a4ac8c7..585b21b0f45 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.80 2001/05/18 21:24:19 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.81 2001/06/19 22:39:11 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -286,16 +286,14 @@ transformJoinUsingClause(ParseState *pstate, List *leftVars, List *rightVars)
*/
result = transformExpr(pstate, result, EXPR_COLUMN_FIRST);
+ /*
+ * 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)
- {
-
- /*
- * This could only happen if someone defines a funny version of
- * '='
- */
elog(ERROR, "JOIN/USING clause must return type bool, not type %s",
typeidTypeName(exprType(result)));
- }
return result;
} /* transformJoinUsingClause() */
@@ -328,11 +326,10 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j,
/* This part is just like transformWhereClause() */
result = transformExpr(pstate, j->quals, EXPR_COLUMN_FIRST);
- if (exprType(result) != BOOLOID)
- {
+
+ if (! coerce_to_boolean(pstate, &result))
elog(ERROR, "JOIN/ON clause must return type bool, not type %s",
typeidTypeName(exprType(result)));
- }
pstate->p_namespace = save_namespace;
@@ -689,11 +686,11 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
/* Need COALESCE(l_colvar, r_colvar) */
CaseExpr *c = makeNode(CaseExpr);
CaseWhen *w = makeNode(CaseWhen);
- A_Expr *a = makeNode(A_Expr);
+ NullTest *n = makeNode(NullTest);
- a->oper = NOTNULL;
- a->lexpr = l_colvar;
- w->expr = (Node *) a;
+ n->arg = l_colvar;
+ n->nulltesttype = IS_NOT_NULL;
+ w->expr = (Node *) n;
w->result = l_colvar;
c->args = makeList1(w);
c->defresult = r_colvar;
@@ -777,11 +774,10 @@ transformWhereClause(ParseState *pstate, Node *clause)
qual = transformExpr(pstate, clause, EXPR_COLUMN_FIRST);
- if (exprType(qual) != BOOLOID)
- {
+ if (! coerce_to_boolean(pstate, &qual))
elog(ERROR, "WHERE clause must return type bool, not type %s",
typeidTypeName(exprType(qual)));
- }
+
return qual;
}
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 38f044217e5..283fd302407 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.57 2001/05/22 16:37:16 petere Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.58 2001/06/19 22:39:11 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -321,6 +321,30 @@ coerce_type_typmod(ParseState *pstate, Node *node,
}
+/* coerce_to_boolean()
+ * Coerce an argument of a construct that requires boolean input
+ * (AND, OR, NOT, etc).
+ *
+ * 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.)
+ */
+bool
+coerce_to_boolean(ParseState *pstate, Node **pnode)
+{
+ Oid inputTypeId = exprType(*pnode);
+ Oid targetTypeId;
+
+ if (inputTypeId == BOOLOID)
+ return true; /* no work */
+ targetTypeId = BOOLOID;
+ if (! can_coerce_type(1, &inputTypeId, &targetTypeId))
+ return false; /* fail, but let caller choose error msg */
+ *pnode = coerce_type(pstate, *pnode, inputTypeId, targetTypeId, -1);
+ return true;
+}
+
+
/* select_common_type()
* Determine the common supertype of a list of input expression types.
* This is used for determining the output type of CASE and UNION
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index a196779f44c..5fda57f5f92 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.97 2001/06/04 23:27:23 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.98 2001/06/19 22:39:11 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -167,32 +167,6 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
result = (Node *) make_op(a->opname, lexpr, rexpr);
}
break;
- case ISNULL:
- {
- Node *lexpr = transformExpr(pstate,
- a->lexpr,
- precedence);
-
- result = ParseFuncOrColumn(pstate,
- "nullvalue",
- makeList1(lexpr),
- false, false,
- precedence);
- }
- break;
- case NOTNULL:
- {
- Node *lexpr = transformExpr(pstate,
- a->lexpr,
- precedence);
-
- result = ParseFuncOrColumn(pstate,
- "nonnullvalue",
- makeList1(lexpr),
- false, false,
- precedence);
- }
- break;
case AND:
{
Node *lexpr = transformExpr(pstate,
@@ -203,13 +177,15 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
precedence);
Expr *expr = makeNode(Expr);
- if (exprType(lexpr) != BOOLOID)
+ if (! coerce_to_boolean(pstate, &lexpr))
elog(ERROR, "left-hand side of AND is type '%s', not '%s'",
- typeidTypeName(exprType(lexpr)), typeidTypeName(BOOLOID));
+ typeidTypeName(exprType(lexpr)),
+ typeidTypeName(BOOLOID));
- if (exprType(rexpr) != BOOLOID)
+ if (! coerce_to_boolean(pstate, &rexpr))
elog(ERROR, "right-hand side of AND is type '%s', not '%s'",
- typeidTypeName(exprType(rexpr)), typeidTypeName(BOOLOID));
+ typeidTypeName(exprType(rexpr)),
+ typeidTypeName(BOOLOID));
expr->typeOid = BOOLOID;
expr->opType = AND_EXPR;
@@ -227,12 +203,16 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
precedence);
Expr *expr = makeNode(Expr);
- if (exprType(lexpr) != BOOLOID)
+ if (! coerce_to_boolean(pstate, &lexpr))
elog(ERROR, "left-hand side of OR is type '%s', not '%s'",
- typeidTypeName(exprType(lexpr)), typeidTypeName(BOOLOID));
- if (exprType(rexpr) != BOOLOID)
+ typeidTypeName(exprType(lexpr)),
+ typeidTypeName(BOOLOID));
+
+ if (! coerce_to_boolean(pstate, &rexpr))
elog(ERROR, "right-hand side of OR is type '%s', not '%s'",
- typeidTypeName(exprType(rexpr)), typeidTypeName(BOOLOID));
+ typeidTypeName(exprType(rexpr)),
+ typeidTypeName(BOOLOID));
+
expr->typeOid = BOOLOID;
expr->opType = OR_EXPR;
expr->args = makeList2(lexpr, rexpr);
@@ -246,9 +226,11 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
precedence);
Expr *expr = makeNode(Expr);
- if (exprType(rexpr) != BOOLOID)
+ if (! coerce_to_boolean(pstate, &rexpr))
elog(ERROR, "argument to NOT is type '%s', not '%s'",
- typeidTypeName(exprType(rexpr)), typeidTypeName(BOOLOID));
+ typeidTypeName(exprType(rexpr)),
+ typeidTypeName(BOOLOID));
+
expr->typeOid = BOOLOID;
expr->opType = NOT_EXPR;
expr->args = makeList1(rexpr);
@@ -491,7 +473,8 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
CaseWhen *w = (CaseWhen *) expr;
w->expr = transformExpr(pstate, w->expr, precedence);
- if (exprType(w->expr) != BOOLOID)
+
+ if (! coerce_to_boolean(pstate, &w->expr))
elog(ERROR, "WHEN clause must have a boolean result");
/*
@@ -510,6 +493,59 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
break;
}
+ case T_NullTest:
+ {
+ NullTest *n = (NullTest *) expr;
+
+ n->arg = transformExpr(pstate, n->arg, precedence);
+ /* the argument can be any type, so don't coerce it */
+ result = expr;
+ break;
+ }
+
+ case T_BooleanTest:
+ {
+ BooleanTest *b = (BooleanTest *) expr;
+
+ b->arg = transformExpr(pstate, b->arg, precedence);
+
+ if (! coerce_to_boolean(pstate, &b->arg))
+ {
+ 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 */
+ }
+
+ elog(ERROR, "Argument of %s must be boolean",
+ clausename);
+ }
+ result = expr;
+ break;
+ }
+
/*
* Quietly accept node types that may be presented when we are
* called on an already-transformed tree.
@@ -669,8 +705,14 @@ exprType(Node *expr)
case T_CaseWhen:
type = exprType(((CaseWhen *) expr)->result);
break;
+ case T_NullTest:
+ type = BOOLOID;
+ break;
+ case T_BooleanTest:
+ type = BOOLOID;
+ break;
case T_Ident:
- /* is this right? */
+ /* XXX is this right? */
type = UNKNOWNOID;
break;
default: