mirror of
https://github.com/postgres/postgres.git
synced 2025-07-30 11:03:19 +03:00
Provide tunable knob for x = NULL -> x IS NULL transformation, default to off.
This commit is contained in:
@ -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">
|
<chapter id="functions">
|
||||||
<title>Functions and Operators</title>
|
<title>Functions and Operators</title>
|
||||||
@ -266,16 +266,24 @@
|
|||||||
Do <emphasis>not</emphasis> use
|
Do <emphasis>not</emphasis> use
|
||||||
<literal><replaceable>expression</replaceable> = NULL</literal>
|
<literal><replaceable>expression</replaceable> = NULL</literal>
|
||||||
because NULL is not <quote>equal to</quote> NULL. (NULL represents
|
because NULL is not <quote>equal to</quote> NULL. (NULL represents
|
||||||
an unknown value, so it is not known whether two unknown values are
|
an unknown value, and it is not known whether two unknown values are
|
||||||
equal.) <productname>Postgres</productname> presently converts
|
equal.)
|
||||||
<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.
|
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<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>
|
<synopsis>
|
||||||
<replaceable>expression</replaceable> IS TRUE
|
<replaceable>expression</replaceable> IS TRUE
|
||||||
<replaceable>expression</replaceable> IS NOT TRUE
|
<replaceable>expression</replaceable> IS NOT TRUE
|
||||||
|
@ -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">
|
<Chapter Id="runtime">
|
||||||
@ -1201,6 +1201,49 @@ dynamic_library_path = '/usr/local/lib/postgresql:/home/my_project/lib:$libdir'
|
|||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</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>
|
<varlistentry>
|
||||||
<term><varname>PORT</varname> (<type>integer</type>)</term>
|
<term><varname>PORT</varname> (<type>integer</type>)</term>
|
||||||
<listitem>
|
<listitem>
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* HISTORY
|
||||||
* AUTHOR DATE MAJOR EVENT
|
* AUTHOR DATE MAJOR EVENT
|
||||||
@ -89,7 +89,6 @@ static void insertSelectOptions(SelectStmt *stmt,
|
|||||||
List *sortClause, List *forUpdate,
|
List *sortClause, List *forUpdate,
|
||||||
Node *limitOffset, Node *limitCount);
|
Node *limitOffset, Node *limitCount);
|
||||||
static Node *makeSetOp(SetOperation op, bool all, Node *larg, Node *rarg);
|
static Node *makeSetOp(SetOperation op, bool all, Node *larg, Node *rarg);
|
||||||
static bool exprIsNullConstant(Node *arg);
|
|
||||||
static Node *doNegate(Node *n);
|
static Node *doNegate(Node *n);
|
||||||
static void doNegateFloat(Value *v);
|
static void doNegateFloat(Value *v);
|
||||||
|
|
||||||
@ -4465,29 +4464,7 @@ a_expr: c_expr
|
|||||||
| a_expr '>' a_expr
|
| a_expr '>' a_expr
|
||||||
{ $$ = makeA_Expr(OP, ">", $1, $3); }
|
{ $$ = makeA_Expr(OP, ">", $1, $3); }
|
||||||
| a_expr '=' a_expr
|
| a_expr '=' a_expr
|
||||||
{
|
{ $$ = makeA_Expr(OP, "=", $1, $3); }
|
||||||
/*
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
| a_expr Op a_expr
|
| a_expr Op a_expr
|
||||||
{ $$ = makeA_Expr(OP, $2, $1, $3); }
|
{ $$ = 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.
|
* Test whether an a_expr is a plain NULL constant or not.
|
||||||
*/
|
*/
|
||||||
static bool
|
bool
|
||||||
exprIsNullConstant(Node *arg)
|
exprIsNullConstant(Node *arg)
|
||||||
{
|
{
|
||||||
if (arg && IsA(arg, A_Const))
|
if (arg && IsA(arg, A_Const))
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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;
|
int max_expr_depth = DEFAULT_MAX_EXPR_DEPTH;
|
||||||
|
|
||||||
static int expr_depth_counter = 0;
|
static int expr_depth_counter = 0;
|
||||||
|
|
||||||
|
bool Transform_null_equals = false;
|
||||||
|
|
||||||
static Node *parser_typecast_constant(Value *expr, TypeName *typename);
|
static Node *parser_typecast_constant(Value *expr, TypeName *typename);
|
||||||
static Node *parser_typecast_expression(ParseState *pstate,
|
static Node *parser_typecast_expression(ParseState *pstate,
|
||||||
Node *expr, TypeName *typename);
|
Node *expr, TypeName *typename);
|
||||||
@ -157,14 +158,35 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
|
|||||||
{
|
{
|
||||||
case OP:
|
case OP:
|
||||||
{
|
{
|
||||||
Node *lexpr = transformExpr(pstate,
|
/*
|
||||||
a->lexpr,
|
* Special-case "foo = NULL" and "NULL = foo" for
|
||||||
precedence);
|
* compatibility with standards-broken products
|
||||||
Node *rexpr = transformExpr(pstate,
|
* (like Microsoft's). Turn these into IS NULL exprs.
|
||||||
a->rexpr,
|
*/
|
||||||
precedence);
|
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;
|
break;
|
||||||
case AND:
|
case AND:
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
* Support for grand unified configuration scheme, including SET
|
* Support for grand unified configuration scheme, including SET
|
||||||
* command, configuration file, and command line options.
|
* 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
|
* Copyright 2000 by PostgreSQL Global Development Group
|
||||||
* Written by Peter Eisentraut <peter_e@gmx.net>.
|
* Written by Peter Eisentraut <peter_e@gmx.net>.
|
||||||
@ -247,12 +247,10 @@ static struct config_bool
|
|||||||
{"show_source_port", PGC_SIGHUP, &ShowPortNumber, false, NULL},
|
{"show_source_port", PGC_SIGHUP, &ShowPortNumber, false, NULL},
|
||||||
|
|
||||||
{"sql_inheritance", PGC_USERSET, &SQL_inheritance, true, NULL},
|
{"sql_inheritance", PGC_USERSET, &SQL_inheritance, true, NULL},
|
||||||
|
|
||||||
{"australian_timezones", PGC_USERSET, &Australian_timezones, false, ClearDateCache},
|
{"australian_timezones", PGC_USERSET, &Australian_timezones, false, ClearDateCache},
|
||||||
|
|
||||||
{"fixbtree", PGC_POSTMASTER, &FixBTree, true, NULL},
|
{"fixbtree", PGC_POSTMASTER, &FixBTree, true, NULL},
|
||||||
|
|
||||||
{"password_encryption", PGC_USERSET, &Password_encryption, false, NULL},
|
{"password_encryption", PGC_USERSET, &Password_encryption, false, NULL},
|
||||||
|
{"transform_null_equals", PGC_USERSET, &Transform_null_equals, false, NULL},
|
||||||
|
|
||||||
{NULL, 0, NULL, false, NULL}
|
{NULL, 0, NULL, false, NULL}
|
||||||
};
|
};
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 int yyparse(void);
|
||||||
extern char *xlateSqlFunc(char *name);
|
extern char *xlateSqlFunc(char *name);
|
||||||
extern char *xlateSqlType(char *name);
|
extern char *xlateSqlType(char *name);
|
||||||
|
bool exprIsNullConstant(Node *arg);
|
||||||
|
|
||||||
#endif /* GRAMPARSE_H */
|
#endif /* GRAMPARSE_H */
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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
|
#define EXPR_RELATION_FIRST 2
|
||||||
|
|
||||||
extern int max_expr_depth;
|
extern int max_expr_depth;
|
||||||
|
extern bool Transform_null_equals;
|
||||||
|
|
||||||
extern Node *transformExpr(ParseState *pstate, Node *expr, int precedence);
|
extern Node *transformExpr(ParseState *pstate, Node *expr, int precedence);
|
||||||
extern Oid exprType(Node *expr);
|
extern Oid exprType(Node *expr);
|
||||||
|
Reference in New Issue
Block a user