Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit4452000

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 fieldnullness are non-recursive, ie, we shouldn't consider that a compositefield 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 expressionwas such that the planner could apply constant folding.Also, adjust the docs to mention that IS [NOT] DISTINCT FROM NULL can beused as a substitute test if a simple null check is wanted for a rowtypeargument. That motivated reordering things so that IS [NOT] DISTINCT FROMis described before IS [NOT] NULL. In HEAD, I went a bit further and addeda table showing all the comparison-related predicates.Per bug #14235. Back-patch to all supported branches, since it's certainlyundesirable that constant-folding should change the semantics.Report and patch by Andrew Gierth; assorted wordsmithing and revisedregression test cases by me.Report: <20160708024746.1410.57282@wrigleys.postgresql.org>
1 parentc1a9542 commit4452000

File tree

5 files changed

+250
-55
lines changed

5 files changed

+250
-55
lines changed

‎doc/src/sgml/func.sgml

Lines changed: 148 additions & 51 deletions
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</> isthe same as <literal>BETWEEN</>
384+
<literal>BETWEEN SYMMETRIC</> islike <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 theconstructs:
430+
To check whether a value is or is not null, use thepredicates:
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 theconstructs
500+
Boolean values can also be tested using thepredicates
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 ofNULL arguments</entry>
572+
<entry>returns the number ofnull 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

Lines changed: 15 additions & 0 deletions
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
HeapTupleHeadertuple;
38193834
OidtupType;
38203835
int32tupTypmod;

‎src/backend/optimizer/util/clauses.c

Lines changed: 9 additions & 4 deletions
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
returnmakeBoolConst(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)

[8]ページ先頭

©2009-2025 Movatter.jp