Skip to content

Commit 68e0359

Browse files
Marios Trivyzasmatriv
authored andcommitted
SQL: Fix null handling for AND and OR in SELECT (#35277)
Override `process()` in `BinaryLogicProcessor` which doesn't immediately return null if left or right argument is null, which is the behaviour of `process()` of the parent class `BinaryProcessor`. Also, add more tests for `AND` and `OR` in SELECT clause with literal. Fixes: #35240
1 parent c94d02a commit 68e0359

File tree

3 files changed

+115
-1
lines changed

3 files changed

+115
-1
lines changed

x-pack/plugin/sql/qa/src/main/resources/select.csv-spec

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//
22
// SELECT with = and !=
33
//
4+
// Need to CAST as STRING since for boolean types jdbc CSV translates null -> false
45
equalsSelectClause
56
SELECT CAST(4 = 4 AS STRING), CAST(NOT 4 = 4 AS STRING), CAST(3 = 4 AS STRING), CAST(NOT 3 = 4 AS STRING), CAST(1 = null AS STRING), CAST(NOT null = 1 AS STRING);
67

@@ -40,6 +41,37 @@ null |null
4041
;
4142

4243

44+
//
45+
// SELECT with OR and AND and NULL handling
46+
//
47+
// Need to CAST as STRING since for boolean types jdbc CSV translates null -> false
48+
selectWithOrAndNullHandling
49+
SELECT CAST(true OR null AS STRING), CAST(null OR true AS STRING), CAST(false OR null AS STRING), CAST(null OR false AS STRING), CAST(null OR null AS STRING);
50+
51+
CAST(true OR null AS VARCHAR):s | CAST(null OR true AS VARCHAR):s | CAST(false OR null AS VARCHAR):s | CAST(null OR false AS VARCHAR):s | CAST(null OR null AS VARCHAR):s
52+
----------------------------------+----------------------------------+-----------------------------------+-----------------------------------+---------------------------------
53+
true |true |null |null |null
54+
;
55+
56+
selectWithAndAndNullHandling
57+
SELECT CAST(true AND null AS STRING), CAST(null AND true AS STRING), CAST(false AND null AS STRING), CAST(null AND false AS STRING), CAST(null AND null AS STRING);
58+
59+
CAST(true AND null AS VARCHAR):s | CAST(null AND true AS VARCHAR):s | CAST(false AND null AS VARCHAR):s | CAST(null AND false AS VARCHAR):s | CAST(null AND null AS VARCHAR):s
60+
-----------------------------------+-----------------------------------+------------------------------------+------------------------------------+----------------------------------
61+
null |null |false |false |null
62+
;
63+
64+
selectWithOrAndAndAndNullHandling_WithTableColumns
65+
SELECT CAST(languages = 2 OR null AS STRING), CAST(languages = 2 AND null AS STRING) FROM test_emp WHERE emp_no BETWEEN 10018 AND 10020 ORDER BY emp_no;
66+
67+
CAST(((languages) == 2) OR null AS VARCHAR):s | CAST(((languages) == 2) AND null AS VARCHAR):s
68+
-----------------------------------------------+------------------------------------------------
69+
true |null
70+
null |false
71+
null |null
72+
;
73+
74+
4375
//
4476
// SELECT with IN
4577
//

x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/logical/BinaryLogicProcessor.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,18 @@ public String getWriteableName() {
8484

8585
@Override
8686
protected void checkParameter(Object param) {
87-
if (!(param instanceof Boolean)) {
87+
if (param != null && !(param instanceof Boolean)) {
8888
throw new SqlIllegalArgumentException("A boolean is required; received {}", param);
8989
}
9090
}
91+
92+
@Override
93+
public Object process(Object input) {
94+
Object l = left().process(input);
95+
checkParameter(l);
96+
Object r = right().process(input);
97+
checkParameter(r);
98+
99+
return doProcess(l, r);
100+
}
91101
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
package org.elasticsearch.xpack.sql.expression.predicate.logical;
7+
8+
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
9+
import org.elasticsearch.common.io.stream.Writeable.Reader;
10+
import org.elasticsearch.test.AbstractWireSerializingTestCase;
11+
import org.elasticsearch.xpack.sql.expression.function.scalar.Processors;
12+
import org.elasticsearch.xpack.sql.expression.gen.processor.ConstantProcessor;
13+
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
14+
15+
public class BinaryLogicProcessorTests extends AbstractWireSerializingTestCase<BinaryLogicProcessor> {
16+
17+
private static final Processor FALSE = new ConstantProcessor(false);
18+
private static final Processor TRUE = new ConstantProcessor(true);
19+
private static final Processor NULL = new ConstantProcessor((Object) null);
20+
21+
public static BinaryLogicProcessor randomProcessor() {
22+
return new BinaryLogicProcessor(
23+
new ConstantProcessor(randomFrom(Boolean.FALSE, Boolean.TRUE, null)),
24+
new ConstantProcessor(randomFrom(Boolean.FALSE, Boolean.TRUE, null)),
25+
randomFrom(BinaryLogicProcessor.BinaryLogicOperation.values()));
26+
}
27+
28+
@Override
29+
protected BinaryLogicProcessor createTestInstance() {
30+
return randomProcessor();
31+
}
32+
33+
@Override
34+
protected Reader<BinaryLogicProcessor> instanceReader() {
35+
return BinaryLogicProcessor::new;
36+
}
37+
38+
@Override
39+
protected NamedWriteableRegistry getNamedWriteableRegistry() {
40+
return new NamedWriteableRegistry(Processors.getNamedWriteables());
41+
}
42+
43+
public void testOR() {
44+
assertEquals(true, new BinaryLogicProcessor(TRUE, FALSE, BinaryLogicProcessor.BinaryLogicOperation.OR).process(null));
45+
assertEquals(true, new BinaryLogicProcessor(FALSE, TRUE, BinaryLogicProcessor.BinaryLogicOperation.OR).process(null));
46+
assertEquals(false, new BinaryLogicProcessor(FALSE, FALSE, BinaryLogicProcessor.BinaryLogicOperation.OR).process(null));
47+
assertEquals(true, new BinaryLogicProcessor(TRUE, TRUE, BinaryLogicProcessor.BinaryLogicOperation.OR).process(null));
48+
}
49+
50+
public void testORNullHandling() {
51+
assertEquals(true, new BinaryLogicProcessor(TRUE, NULL, BinaryLogicProcessor.BinaryLogicOperation.OR).process(null));
52+
assertEquals(true, new BinaryLogicProcessor(NULL, TRUE, BinaryLogicProcessor.BinaryLogicOperation.OR).process(null));
53+
assertNull(new BinaryLogicProcessor(FALSE, NULL, BinaryLogicProcessor.BinaryLogicOperation.OR).process(null));
54+
assertNull(new BinaryLogicProcessor(NULL, FALSE, BinaryLogicProcessor.BinaryLogicOperation.OR).process(null));
55+
assertNull(new BinaryLogicProcessor(NULL, NULL, BinaryLogicProcessor.BinaryLogicOperation.OR).process(null));
56+
}
57+
58+
public void testAnd() {
59+
assertEquals(false, new BinaryLogicProcessor(TRUE, FALSE, BinaryLogicProcessor.BinaryLogicOperation.AND).process(null));
60+
assertEquals(false, new BinaryLogicProcessor(FALSE, TRUE, BinaryLogicProcessor.BinaryLogicOperation.AND).process(null));
61+
assertEquals(false, new BinaryLogicProcessor(FALSE, FALSE, BinaryLogicProcessor.BinaryLogicOperation.AND).process(null));
62+
assertEquals(true, new BinaryLogicProcessor(TRUE, TRUE, BinaryLogicProcessor.BinaryLogicOperation.AND).process(null));
63+
}
64+
65+
public void testAndNullHandling() {
66+
assertNull(new BinaryLogicProcessor(TRUE, NULL, BinaryLogicProcessor.BinaryLogicOperation.AND).process(null));
67+
assertNull(new BinaryLogicProcessor(NULL, TRUE, BinaryLogicProcessor.BinaryLogicOperation.AND).process(null));
68+
assertEquals(false, new BinaryLogicProcessor(FALSE, NULL, BinaryLogicProcessor.BinaryLogicOperation.AND).process(null));
69+
assertEquals(false, new BinaryLogicProcessor(NULL, FALSE, BinaryLogicProcessor.BinaryLogicOperation.AND).process(null));
70+
assertNull(new BinaryLogicProcessor(NULL, NULL, BinaryLogicProcessor.BinaryLogicOperation.AND).process(null));
71+
}
72+
}

0 commit comments

Comments
 (0)