From a1ee06625c268a10ce572e72af07ac082626eeb0 Mon Sep 17 00:00:00 2001 From: Peter Eisentraut <peter_e@gmx.net> Date: Thu, 20 Sep 2001 14:20:28 +0000 Subject: [PATCH] Provide tunable knob for x = NULL -> x IS NULL transformation, default to off. --- doc/src/sgml/func.sgml | 24 ++++++++++++------ doc/src/sgml/runtime.sgml | 45 ++++++++++++++++++++++++++++++++- src/backend/parser/gram.y | 29 +++------------------ src/backend/parser/parse_expr.c | 42 ++++++++++++++++++++++-------- src/backend/utils/misc/guc.c | 6 ++--- src/include/parser/gramparse.h | 3 ++- src/include/parser/parse_expr.h | 3 ++- 7 files changed, 101 insertions(+), 51 deletions(-) diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 89a9bd1f8e..0dc19768c1 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -1,4 +1,4 @@ -<!-- $Header: /cvsroot/pgsql/doc/src/sgml/func.sgml,v 1.71 2001/09/10 02:46:18 ishii Exp $ --> +<!-- $Header: /cvsroot/pgsql/doc/src/sgml/func.sgml,v 1.72 2001/09/20 14:20:26 petere Exp $ --> <chapter id="functions"> <title>Functions and Operators</title> @@ -266,16 +266,24 @@ Do <emphasis>not</emphasis> use <literal><replaceable>expression</replaceable> = NULL</literal> because NULL is not <quote>equal to</quote> NULL. (NULL represents - an unknown value, so it is not known whether two unknown values are - equal.) <productname>Postgres</productname> presently converts - <literal>x = NULL</literal> clauses to <literal>x IS NULL</literal> to - allow some broken client applications (such as - <productname>Microsoft Access</productname>) 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.) </para> <para> - Boolean values can be tested using the constructs + Some applications may (incorrectly) require that + <literal><replaceable>expression</replaceable> = NULL</literal> + returns true if <replaceable>expression</replaceable> evaluates to + the NULL value. To support these applications, the run-time option + <varname>transform_null_equals</varname> can be turned on (e.g., + <literal>SET transform_null_equals TO ON;</literal>). + <productname>PostgreSQL</productname> would then convert <literal>x + = NULL</literal> clauses to <literal>x IS NULL</literal>. This was + the default behavior in releases 6.5 through 7.1. + </para> + + <para> + Boolean values can also be tested using the constructs <synopsis> <replaceable>expression</replaceable> IS TRUE <replaceable>expression</replaceable> IS NOT TRUE diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml index 2f27788334..eef78e17a3 100644 --- a/doc/src/sgml/runtime.sgml +++ b/doc/src/sgml/runtime.sgml @@ -1,5 +1,5 @@ <!-- -$Header: /cvsroot/pgsql/doc/src/sgml/runtime.sgml,v 1.80 2001/09/16 16:11:09 petere Exp $ +$Header: /cvsroot/pgsql/doc/src/sgml/runtime.sgml,v 1.81 2001/09/20 14:20:27 petere Exp $ --> <Chapter Id="runtime"> @@ -1201,6 +1201,49 @@ dynamic_library_path = '/usr/local/lib/postgresql:/home/my_project/lib:$libdir' </listitem> </varlistentry> + <varlistentry> + <term><varname>TRANSFORM_NULL_EQUALS</varname> (<type>boolean</type>)</term> + <listitem> + <para> + When turned on, expressions of the form + <literal><replaceable>expr</> = NULL</literal> (or + <literal>NULL = <replaceable>expr</></literal>) are treated as + <literal><replaceable>expr</> IS NULL</literal>, that is, they + return true if <replaceable>expr</> evaluates to the NULL + value, and false otherwise. The correct behavior of + <literal><replaceable>expr</> = NULL</literal> is to always + return NULL (unknown). Therefore this option defaults to off. + </para> + + <para> + However, filtered forms in <productname>Microsoft + Access</productname> generate queries that appear to use + <literal><replaceable>expr</> = NULL</literal> 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 <literal><replaceable>expr</> = NULL</literal> 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. + </para> + + <para> + Note that this option only affects the literal <literal>=</> + operator, not other comparison operators or other expressions + that are computationally equivalent to some expression + involving the equals operator (such as <literal>IN</literal>). + Thus, this option is not a general fix for bad programming. + </para> + + <para> + Refer to the <citetitle>User's Guide</citetitle> for related + information. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><varname>PORT</varname> (<type>integer</type>)</term> <listitem> diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index c1e331cf81..24407902bd 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 7639fd2db5..badbe60f75 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); - - result = (Node *) make_op(a->opname, lexpr, 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 && strcmp(a->opname, "=")==0 + && (exprIsNullConstant(a->lexpr) || exprIsNullConstant(a->rexpr))) + { + NullTest *n = makeNode(NullTest); + n->nulltesttype = IS_NULL; + + 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 5af9fc67ab..ebb7745347 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 <peter_e@gmx.net>. @@ -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 88c00ce9f3..ad045dd4b6 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 4dfc2367fd..c51bf7cc04 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); -- 2.24.1