diff options
Diffstat (limited to 'src/backend/parser')
-rw-r--r-- | src/backend/parser/gram.y | 135 | ||||
-rw-r--r-- | src/backend/parser/keywords.c | 3 | ||||
-rw-r--r-- | src/backend/parser/parse_clause.c | 32 | ||||
-rw-r--r-- | src/backend/parser/parse_coerce.c | 26 | ||||
-rw-r--r-- | src/backend/parser/parse_expr.c | 120 |
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: |