summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Eisentraut2001-09-20 14:20:28 +0000
committerPeter Eisentraut2001-09-20 14:20:28 +0000
commita1ee06625c268a10ce572e72af07ac082626eeb0 (patch)
treed7baf560585c0ccd83ae0e4d374c62bf08e27103
parentfd5e95971e7a2bc32fcb7f2b39798c51648a225f (diff)
Provide tunable knob for x = NULL -> x IS NULL transformation, default to off.
-rw-r--r--doc/src/sgml/func.sgml24
-rw-r--r--doc/src/sgml/runtime.sgml45
-rw-r--r--src/backend/parser/gram.y29
-rw-r--r--src/backend/parser/parse_expr.c42
-rw-r--r--src/backend/utils/misc/guc.c6
-rw-r--r--src/include/parser/gramparse.h3
-rw-r--r--src/include/parser/parse_expr.h3
7 files changed, 101 insertions, 51 deletions
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 @@
-<!-- $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 2f277883346..eef78e17a35 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">
@@ -1202,6 +1202,49 @@ dynamic_library_path = '/usr/local/lib/postgresql:/home/my_project/lib:$libdir'
</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>
<para>
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);
-
- 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 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 <[email protected]>.
@@ -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);