diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 89a9bd1f8eb..0dc19768c1c 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -1,4 +1,4 @@ - + Functions and Operators @@ -266,16 +266,24 @@ Do not use expression = NULL because NULL is not equal to NULL. (NULL represents - an unknown value, so it is not known whether two unknown values are - equal.) Postgres presently converts - x = NULL clauses to x IS NULL to - allow some broken client applications (such as - Microsoft Access) to work, but this may - be discontinued in a future release. + an unknown value, and it is not known whether two unknown values are + equal.) - Boolean values can be tested using the constructs + Some applications may (incorrectly) require that + expression = NULL + returns true if expression evaluates to + the NULL value. To support these applications, the run-time option + transform_null_equals can be turned on (e.g., + SET transform_null_equals TO ON;). + PostgreSQL would then convert x + = NULL clauses to x IS NULL. This was + the default behavior in releases 6.5 through 7.1. + + + + Boolean values can also be tested using the constructs expression IS TRUE expression IS NOT TRUE diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml index 2f277883346..eef78e17a35 100644 --- a/doc/src/sgml/runtime.sgml +++ b/doc/src/sgml/runtime.sgml @@ -1,5 +1,5 @@ @@ -1201,6 +1201,49 @@ dynamic_library_path = '/usr/local/lib/postgresql:/home/my_project/lib:$libdir' + + TRANSFORM_NULL_EQUALS (boolean) + + + When turned on, expressions of the form + expr = NULL (or + NULL = expr) are treated as + expr IS NULL, that is, they + return true if expr evaluates to the NULL + value, and false otherwise. The correct behavior of + expr = NULL is to always + return NULL (unknown). Therefore this option defaults to off. + + + + However, filtered forms in Microsoft + Access generate queries that appear to use + expr = NULL to test for + NULLs, so if you use that interface to access the database you + might want to turn this option on. Since expressions of the + form expr = NULL always + return NULL (using the correct interpretation) they are not + very useful and do not appear often in normal applications, so + this option does little harm in practice. But new users are + frequently confused about the semantics of expressions + involving NULL, so we do not turn this option on by default. + + + + Note that this option only affects the literal = + operator, not other comparison operators or other expressions + that are computationally equivalent to some expression + involving the equals operator (such as IN). + Thus, this option is not a general fix for bad programming. + + + + Refer to the User's Guide for related + information. + + + + PORT (integer) diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index c1e331cf819..24407902bd3 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.251 2001/09/18 01:59:06 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.252 2001/09/20 14:20:27 petere Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -89,7 +89,6 @@ static void insertSelectOptions(SelectStmt *stmt, List *sortClause, List *forUpdate, Node *limitOffset, Node *limitCount); static Node *makeSetOp(SetOperation op, bool all, Node *larg, Node *rarg); -static bool exprIsNullConstant(Node *arg); static Node *doNegate(Node *n); static void doNegateFloat(Value *v); @@ -4465,29 +4464,7 @@ a_expr: c_expr | a_expr '>' a_expr { $$ = makeA_Expr(OP, ">", $1, $3); } | a_expr '=' a_expr - { - /* - * Special-case "foo = NULL" and "NULL = foo" for - * compatibility with standards-broken products - * (like Microsoft's). Turn these into IS NULL exprs. - */ - if (exprIsNullConstant($3)) - { - NullTest *n = makeNode(NullTest); - n->arg = $1; - n->nulltesttype = IS_NULL; - $$ = (Node *)n; - } - else if (exprIsNullConstant($1)) - { - NullTest *n = makeNode(NullTest); - n->arg = $3; - n->nulltesttype = IS_NULL; - $$ = (Node *)n; - } - else - $$ = makeA_Expr(OP, "=", $1, $3); - } + { $$ = makeA_Expr(OP, "=", $1, $3); } | a_expr Op a_expr { $$ = makeA_Expr(OP, $2, $1, $3); } @@ -6137,7 +6114,7 @@ Oid param_type(int t) /* * Test whether an a_expr is a plain NULL constant or not. */ -static bool +bool exprIsNullConstant(Node *arg) { if (arg && IsA(arg, A_Const)) diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 7639fd2db5c..badbe60f75a 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.99 2001/08/09 18:28:17 petere Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.100 2001/09/20 14:20:27 petere Exp $ * *------------------------------------------------------------------------- */ @@ -34,9 +34,10 @@ int max_expr_depth = DEFAULT_MAX_EXPR_DEPTH; - static int expr_depth_counter = 0; +bool Transform_null_equals = false; + static Node *parser_typecast_constant(Value *expr, TypeName *typename); static Node *parser_typecast_expression(ParseState *pstate, Node *expr, TypeName *typename); @@ -157,14 +158,35 @@ transformExpr(ParseState *pstate, Node *expr, int precedence) { case OP: { - Node *lexpr = transformExpr(pstate, - a->lexpr, - precedence); - Node *rexpr = transformExpr(pstate, - a->rexpr, - precedence); + /* + * 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 && strcmp(a->opname, "=")==0 + && (exprIsNullConstant(a->lexpr) || exprIsNullConstant(a->rexpr))) + { + NullTest *n = makeNode(NullTest); + n->nulltesttype = IS_NULL; - result = (Node *) make_op(a->opname, lexpr, rexpr); + if (exprIsNullConstant(a->lexpr)) + n->arg = a->rexpr; + else + n->arg = a->lexpr; + + result = transformExpr(pstate, n, precedence); + } + else + { + Node *lexpr = transformExpr(pstate, + a->lexpr, + precedence); + Node *rexpr = transformExpr(pstate, + a->rexpr, + precedence); + + result = (Node *) make_op(a->opname, lexpr, rexpr); + } } break; case AND: diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 5af9fc67ab2..ebb7745347e 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -4,7 +4,7 @@ * Support for grand unified configuration scheme, including SET * command, configuration file, and command line options. * - * $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.48 2001/09/12 14:06:37 petere Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.49 2001/09/20 14:20:27 petere Exp $ * * Copyright 2000 by PostgreSQL Global Development Group * Written by Peter Eisentraut . @@ -247,12 +247,10 @@ static struct config_bool {"show_source_port", PGC_SIGHUP, &ShowPortNumber, false, NULL}, {"sql_inheritance", PGC_USERSET, &SQL_inheritance, true, NULL}, - {"australian_timezones", PGC_USERSET, &Australian_timezones, false, ClearDateCache}, - {"fixbtree", PGC_POSTMASTER, &FixBTree, true, NULL}, - {"password_encryption", PGC_USERSET, &Password_encryption, false, NULL}, + {"transform_null_equals", PGC_USERSET, &Transform_null_equals, false, NULL}, {NULL, 0, NULL, false, NULL} }; diff --git a/src/include/parser/gramparse.h b/src/include/parser/gramparse.h index 88c00ce9f37..ad045dd4b60 100644 --- a/src/include/parser/gramparse.h +++ b/src/include/parser/gramparse.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: gramparse.h,v 1.15 2001/02/09 03:26:27 tgl Exp $ + * $Id: gramparse.h,v 1.16 2001/09/20 14:20:28 petere Exp $ * *------------------------------------------------------------------------- */ @@ -29,5 +29,6 @@ extern Oid param_type(int t); extern int yyparse(void); extern char *xlateSqlFunc(char *name); extern char *xlateSqlType(char *name); +bool exprIsNullConstant(Node *arg); #endif /* GRAMPARSE_H */ diff --git a/src/include/parser/parse_expr.h b/src/include/parser/parse_expr.h index 4dfc2367fd5..c51bf7cc044 100644 --- a/src/include/parser/parse_expr.h +++ b/src/include/parser/parse_expr.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: parse_expr.h,v 1.21 2001/01/24 19:43:27 momjian Exp $ + * $Id: parse_expr.h,v 1.22 2001/09/20 14:20:28 petere Exp $ * *------------------------------------------------------------------------- */ @@ -20,6 +20,7 @@ #define EXPR_RELATION_FIRST 2 extern int max_expr_depth; +extern bool Transform_null_equals; extern Node *transformExpr(ParseState *pstate, Node *expr, int precedence); extern Oid exprType(Node *expr);