Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 4452000

Browse files
committed
Fix constant-folding of ROW(...) IS [NOT] NULL with composite fields.
The SQL standard appears to specify that IS [NOT] NULL's tests of field nullness are non-recursive, ie, we shouldn't consider that a composite field with value ROW(NULL,NULL) is null for this purpose. ExecEvalNullTest got this right, but eval_const_expressions did not, leading to weird inconsistencies depending on whether the expression was such that the planner could apply constant folding. Also, adjust the docs to mention that IS [NOT] DISTINCT FROM NULL can be used as a substitute test if a simple null check is wanted for a rowtype argument. That motivated reordering things so that IS [NOT] DISTINCT FROM is described before IS [NOT] NULL. In HEAD, I went a bit further and added a table showing all the comparison-related predicates. Per bug #14235. Back-patch to all supported branches, since it's certainly undesirable that constant-folding should change the semantics. Report and patch by Andrew Gierth; assorted wordsmithing and revised regression test cases by me. Report: <20160708024746.1410.57282@wrigleys.postgresql.org>
1 parent c1a9542 commit 4452000

File tree

5 files changed

+250
-55
lines changed

5 files changed

+250
-55
lines changed

doc/src/sgml/func.sgml

+148-51
Original file line numberDiff line numberDiff line change
@@ -256,12 +256,111 @@
256256
<literal>3</literal>).
257257
</para>
258258

259+
<para>
260+
There are also some comparison predicates, as shown in <xref
261+
linkend="functions-comparison-pred-table">. These behave much like
262+
operators, but have special syntax mandated by the SQL standard.
263+
</para>
264+
265+
<table id="functions-comparison-pred-table">
266+
<title>Comparison Predicates</title>
267+
<tgroup cols="2">
268+
<thead>
269+
<row>
270+
<entry>Predicate</entry>
271+
<entry>Description</entry>
272+
</row>
273+
</thead>
274+
275+
<tbody>
276+
<row>
277+
<entry> <replaceable>a</> <literal>BETWEEN</> <replaceable>x</> <literal>AND</> <replaceable>y</> </entry>
278+
<entry>between</entry>
279+
</row>
280+
281+
<row>
282+
<entry> <replaceable>a</> <literal>NOT BETWEEN</> <replaceable>x</> <literal>AND</> <replaceable>y</> </entry>
283+
<entry>not between</entry>
284+
</row>
285+
286+
<row>
287+
<entry> <replaceable>a</> <literal>BETWEEN SYMMETRIC</> <replaceable>x</> <literal>AND</> <replaceable>y</> </entry>
288+
<entry>between, after sorting the comparison values</entry>
289+
</row>
290+
291+
<row>
292+
<entry> <replaceable>a</> <literal>NOT BETWEEN SYMMETRIC</> <replaceable>x</> <literal>AND</> <replaceable>y</> </entry>
293+
<entry>not between, after sorting the comparison values</entry>
294+
</row>
295+
296+
<row>
297+
<entry> <replaceable>a</> <literal>IS DISTINCT FROM</> <replaceable>b</> </entry>
298+
<entry>not equal, treating null like an ordinary value</entry>
299+
</row>
300+
301+
<row>
302+
<entry><replaceable>a</> <literal>IS NOT DISTINCT FROM</> <replaceable>b</></entry>
303+
<entry>equal, treating null like an ordinary value</entry>
304+
</row>
305+
306+
<row>
307+
<entry> <replaceable>expression</> <literal>IS NULL</> </entry>
308+
<entry>is null</entry>
309+
</row>
310+
311+
<row>
312+
<entry> <replaceable>expression</> <literal>IS NOT NULL</> </entry>
313+
<entry>is not null</entry>
314+
</row>
315+
316+
<row>
317+
<entry> <replaceable>expression</> <literal>ISNULL</> </entry>
318+
<entry>is null (nonstandard syntax)</entry>
319+
</row>
320+
321+
<row>
322+
<entry> <replaceable>expression</> <literal>NOTNULL</> </entry>
323+
<entry>is not null (nonstandard syntax)</entry>
324+
</row>
325+
326+
<row>
327+
<entry> <replaceable>boolean_expression</> <literal>IS TRUE</> </entry>
328+
<entry>is true</entry>
329+
</row>
330+
331+
<row>
332+
<entry> <replaceable>boolean_expression</> <literal>IS NOT TRUE</> </entry>
333+
<entry>is false or unknown</entry>
334+
</row>
335+
336+
<row>
337+
<entry> <replaceable>boolean_expression</> <literal>IS FALSE</> </entry>
338+
<entry>is false</entry>
339+
</row>
340+
341+
<row>
342+
<entry> <replaceable>boolean_expression</> <literal>IS NOT FALSE</> </entry>
343+
<entry>is true or unknown</entry>
344+
</row>
345+
346+
<row>
347+
<entry> <replaceable>boolean_expression</> <literal>IS UNKNOWN</> </entry>
348+
<entry>is unknown</entry>
349+
</row>
350+
351+
<row>
352+
<entry> <replaceable>boolean_expression</> <literal>IS NOT UNKNOWN</> </entry>
353+
<entry>is true or false</entry>
354+
</row>
355+
</tbody>
356+
</tgroup>
357+
</table>
358+
259359
<para>
260360
<indexterm>
261361
<primary>BETWEEN</primary>
262362
</indexterm>
263-
In addition to the comparison operators, the special
264-
<token>BETWEEN</token> construct is available:
363+
The <token>BETWEEN</token> predicate simplifies range tests:
265364
<synopsis>
266365
<replaceable>a</replaceable> BETWEEN <replaceable>x</replaceable> AND <replaceable>y</replaceable>
267366
</synopsis>
@@ -282,13 +381,39 @@
282381
<indexterm>
283382
<primary>BETWEEN SYMMETRIC</primary>
284383
</indexterm>
285-
<literal>BETWEEN SYMMETRIC</> is the same as <literal>BETWEEN</>
384+
<literal>BETWEEN SYMMETRIC</> is like <literal>BETWEEN</>
286385
except there is no requirement that the argument to the left of
287386
<literal>AND</> be less than or equal to the argument on the right.
288387
If it is not, those two arguments are automatically swapped, so that
289388
a nonempty range is always implied.
290389
</para>
291390

391+
<para>
392+
<indexterm>
393+
<primary>IS DISTINCT FROM</primary>
394+
</indexterm>
395+
<indexterm>
396+
<primary>IS NOT DISTINCT FROM</primary>
397+
</indexterm>
398+
Ordinary comparison operators yield null (signifying <quote>unknown</>),
399+
not true or false, when either input is null. For example,
400+
<literal>7 = NULL</> yields null, as does <literal>7 &lt;&gt; NULL</>. When
401+
this behavior is not suitable, use the
402+
<literal>IS <optional> NOT </> DISTINCT FROM</literal> predicates:
403+
<synopsis>
404+
<replaceable>a</replaceable> IS DISTINCT FROM <replaceable>b</replaceable>
405+
<replaceable>a</replaceable> IS NOT DISTINCT FROM <replaceable>b</replaceable>
406+
</synopsis>
407+
For non-null inputs, <literal>IS DISTINCT FROM</literal> is
408+
the same as the <literal>&lt;&gt;</> operator. However, if both
409+
inputs are null it returns false, and if only one input is
410+
null it returns true. Similarly, <literal>IS NOT DISTINCT
411+
FROM</literal> is identical to <literal>=</literal> for non-null
412+
inputs, but it returns true when both inputs are null, and false when only
413+
one input is null. Thus, these predicates effectively act as though null
414+
were a normal data value, rather than <quote>unknown</>.
415+
</para>
416+
292417
<para>
293418
<indexterm>
294419
<primary>IS NULL</primary>
@@ -302,12 +427,12 @@
302427
<indexterm>
303428
<primary>NOTNULL</primary>
304429
</indexterm>
305-
To check whether a value is or is not null, use the constructs:
430+
To check whether a value is or is not null, use the predicates:
306431
<synopsis>
307432
<replaceable>expression</replaceable> IS NULL
308433
<replaceable>expression</replaceable> IS NOT NULL
309434
</synopsis>
310-
or the equivalent, but nonstandard, constructs:
435+
or the equivalent, but nonstandard, predicates:
311436
<synopsis>
312437
<replaceable>expression</replaceable> ISNULL
313438
<replaceable>expression</replaceable> NOTNULL
@@ -320,8 +445,7 @@
320445
<literal><replaceable>expression</replaceable> = NULL</literal>
321446
because <literal>NULL</> is not <quote>equal to</quote>
322447
<literal>NULL</>. (The null value represents an unknown value,
323-
and it is not known whether two unknown values are equal.) This
324-
behavior conforms to the SQL standard.
448+
and it is not known whether two unknown values are equal.)
325449
</para>
326450

327451
<tip>
@@ -338,47 +462,20 @@
338462
</para>
339463
</tip>
340464

341-
<note>
342465
<para>
343466
If the <replaceable>expression</replaceable> is row-valued, then
344467
<literal>IS NULL</> is true when the row expression itself is null
345468
or when all the row's fields are null, while
346469
<literal>IS NOT NULL</> is true when the row expression itself is non-null
347470
and all the row's fields are non-null. Because of this behavior,
348471
<literal>IS NULL</> and <literal>IS NOT NULL</> do not always return
349-
inverse results for row-valued expressions, i.e., a row-valued
350-
expression that contains both NULL and non-null values will return false
351-
for both tests.
352-
This definition conforms to the SQL standard, and is a change from the
353-
inconsistent behavior exhibited by <productname>PostgreSQL</productname>
354-
versions prior to 8.2.
355-
</para>
356-
</note>
357-
358-
<para>
359-
<indexterm>
360-
<primary>IS DISTINCT FROM</primary>
361-
</indexterm>
362-
<indexterm>
363-
<primary>IS NOT DISTINCT FROM</primary>
364-
</indexterm>
365-
Ordinary comparison operators yield null (signifying <quote>unknown</>),
366-
not true or false, when either input is null. For example,
367-
<literal>7 = NULL</> yields null, as does <literal>7 &lt;&gt; NULL</>. When
368-
this behavior is not suitable, use the
369-
<literal>IS <optional> NOT </> DISTINCT FROM</literal> constructs:
370-
<synopsis>
371-
<replaceable>expression</replaceable> IS DISTINCT FROM <replaceable>expression</replaceable>
372-
<replaceable>expression</replaceable> IS NOT DISTINCT FROM <replaceable>expression</replaceable>
373-
</synopsis>
374-
For non-null inputs, <literal>IS DISTINCT FROM</literal> is
375-
the same as the <literal>&lt;&gt;</> operator. However, if both
376-
inputs are null it returns false, and if only one input is
377-
null it returns true. Similarly, <literal>IS NOT DISTINCT
378-
FROM</literal> is identical to <literal>=</literal> for non-null
379-
inputs, but it returns true when both inputs are null, and false when only
380-
one input is null. Thus, these constructs effectively act as though null
381-
were a normal data value, rather than <quote>unknown</>.
472+
inverse results for row-valued expressions; in particular, a row-valued
473+
expression that contains both null and non-null fields will return false
474+
for both tests. In some cases, it may be preferable to
475+
write <replaceable>row</replaceable> <literal>IS DISTINCT FROM NULL</>
476+
or <replaceable>row</replaceable> <literal>IS NOT DISTINCT FROM NULL</>,
477+
which will simply check whether the overall row value is null without any
478+
additional tests on the row fields.
382479
</para>
383480

384481
<para>
@@ -400,14 +497,14 @@
400497
<indexterm>
401498
<primary>IS NOT UNKNOWN</primary>
402499
</indexterm>
403-
Boolean values can also be tested using the constructs
500+
Boolean values can also be tested using the predicates
404501
<synopsis>
405-
<replaceable>expression</replaceable> IS TRUE
406-
<replaceable>expression</replaceable> IS NOT TRUE
407-
<replaceable>expression</replaceable> IS FALSE
408-
<replaceable>expression</replaceable> IS NOT FALSE
409-
<replaceable>expression</replaceable> IS UNKNOWN
410-
<replaceable>expression</replaceable> IS NOT UNKNOWN
502+
<replaceable>boolean_expression</replaceable> IS TRUE
503+
<replaceable>boolean_expression</replaceable> IS NOT TRUE
504+
<replaceable>boolean_expression</replaceable> IS FALSE
505+
<replaceable>boolean_expression</replaceable> IS NOT FALSE
506+
<replaceable>boolean_expression</replaceable> IS UNKNOWN
507+
<replaceable>boolean_expression</replaceable> IS NOT UNKNOWN
411508
</synopsis>
412509
These will always return true or false, never a null value, even when the
413510
operand is null.
@@ -427,7 +524,7 @@
427524
<primary>IS NOT OF</primary>
428525
</indexterm>
429526
It is possible to check the data type of an expression using the
430-
constructs
527+
predicates
431528
<synopsis>
432529
<replaceable>expression</replaceable> IS OF (typename, ...)
433530
<replaceable>expression</replaceable> IS NOT OF (typename, ...)
@@ -461,7 +558,7 @@
461558
</indexterm>
462559
<literal>num_nonnulls(VARIADIC "any")</literal>
463560
</entry>
464-
<entry>returns the number of non-NULL arguments</entry>
561+
<entry>returns the number of non-null arguments</entry>
465562
<entry><literal>num_nonnulls(1, NULL, 2)</literal></entry>
466563
<entry><literal>2</literal></entry>
467564
</row>
@@ -472,7 +569,7 @@
472569
</indexterm>
473570
<literal>num_nulls(VARIADIC "any")</literal>
474571
</entry>
475-
<entry>returns the number of NULL arguments</entry>
572+
<entry>returns the number of null arguments</entry>
476573
<entry><literal>num_nulls(1, NULL, 2)</literal></entry>
477574
<entry><literal>1</literal></entry>
478575
</row>

src/backend/executor/execQual.c

+15
Original file line numberDiff line numberDiff line change
@@ -3815,6 +3815,21 @@ ExecEvalNullTest(NullTestState *nstate,
38153815

38163816
if (ntest->argisrow && !(*isNull))
38173817
{
3818+
/*
3819+
* The SQL standard defines IS [NOT] NULL for a non-null rowtype
3820+
* argument as:
3821+
*
3822+
* "R IS NULL" is true if every field is the null value.
3823+
*
3824+
* "R IS NOT NULL" is true if no field is the null value.
3825+
*
3826+
* This definition is (apparently intentionally) not recursive; so our
3827+
* tests on the fields are primitive attisnull tests, not recursive
3828+
* checks to see if they are all-nulls or no-nulls rowtypes.
3829+
*
3830+
* The standard does not consider the possibility of zero-field rows,
3831+
* but here we consider them to vacuously satisfy both predicates.
3832+
*/
38183833
HeapTupleHeader tuple;
38193834
Oid tupType;
38203835
int32 tupTypmod;

src/backend/optimizer/util/clauses.c

+9-4
Original file line numberDiff line numberDiff line change
@@ -3273,7 +3273,7 @@ eval_const_expressions_mutator(Node *node,
32733273

32743274
arg = eval_const_expressions_mutator((Node *) ntest->arg,
32753275
context);
3276-
if (arg && IsA(arg, RowExpr))
3276+
if (ntest->argisrow && arg && IsA(arg, RowExpr))
32773277
{
32783278
/*
32793279
* We break ROW(...) IS [NOT] NULL into separate tests on
@@ -3285,8 +3285,6 @@ eval_const_expressions_mutator(Node *node,
32853285
List *newargs = NIL;
32863286
ListCell *l;
32873287

3288-
Assert(ntest->argisrow);
3289-
32903288
foreach(l, rarg->args)
32913289
{
32923290
Node *relem = (Node *) lfirst(l);
@@ -3305,10 +3303,17 @@ eval_const_expressions_mutator(Node *node,
33053303
return makeBoolConst(false, false);
33063304
continue;
33073305
}
3306+
3307+
/*
3308+
* Else, make a scalar (argisrow == false) NullTest
3309+
* for this field. Scalar semantics are required
3310+
* because IS [NOT] NULL doesn't recurse; see comments
3311+
* in ExecEvalNullTest().
3312+
*/
33083313
newntest = makeNode(NullTest);
33093314
newntest->arg = (Expr *) relem;
33103315
newntest->nulltesttype = ntest->nulltesttype;
3311-
newntest->argisrow = type_is_rowtype(exprType(relem));
3316+
newntest->argisrow = false;
33123317
newntest->location = ntest->location;
33133318
newargs = lappend(newargs, newntest);
33143319
}

0 commit comments

Comments
 (0)