mirror of
https://github.com/honeymoose/OpenSearch.git
synced 2025-03-25 17:38:44 +00:00
SQL: Implement IFNULL variant of COALESCE (#35762)
IFNULL is a MySQL variant (also used in other DBs) which takes only 2 arguments and returns the first one that is not null. Closes: #35749
This commit is contained in:
parent
03f003733d
commit
e179bd393d
@ -44,3 +44,40 @@ include-tagged::{sql-specs}/docs.csv-spec[coalesceReturnNonNull]
|
||||
----
|
||||
include-tagged::{sql-specs}/docs.csv-spec[coalesceReturnNull]
|
||||
----
|
||||
|
||||
|
||||
[[sql-functions-conditional-ifnull]]
|
||||
==== `IFNULL`
|
||||
|
||||
.Synopsis
|
||||
[source, sql]
|
||||
----
|
||||
IFNULL ( expression<1>, expression<2> )
|
||||
----
|
||||
|
||||
*Input*:
|
||||
|
||||
<1> 1st expression
|
||||
|
||||
<2> 2nd expression
|
||||
|
||||
|
||||
*Output*: 2nd expression if 1st expression is null, otherwise 1st expression.
|
||||
|
||||
.Description
|
||||
|
||||
Variant of <<sql-functions-conditional-coalesce>> with only two arguments.
|
||||
Returns the first of its arguments that is not null.
|
||||
If all arguments are null, then it returns `null`.
|
||||
|
||||
|
||||
|
||||
["source","sql",subs="attributes,callouts,macros"]
|
||||
----
|
||||
include-tagged::{sql-specs}/docs.csv-spec[ifNullReturnFirst]
|
||||
----
|
||||
|
||||
["source","sql",subs="attributes,callouts,macros"]
|
||||
----
|
||||
include-tagged::{sql-specs}/docs.csv-spec[ifNullReturnSecond]
|
||||
----
|
||||
|
@ -20,6 +20,7 @@ STDDEV_POP |AGGREGATE
|
||||
SUM_OF_SQUARES |AGGREGATE
|
||||
VAR_POP |AGGREGATE
|
||||
COALESCE |CONDITIONAL
|
||||
IFNULL |CONDITIONAL
|
||||
DAY |SCALAR
|
||||
DAYNAME |SCALAR
|
||||
DAYOFMONTH |SCALAR
|
||||
|
@ -197,6 +197,7 @@ STDDEV_POP |AGGREGATE
|
||||
SUM_OF_SQUARES |AGGREGATE
|
||||
VAR_POP |AGGREGATE
|
||||
COALESCE |CONDITIONAL
|
||||
IFNULL |CONDITIONAL
|
||||
DAY |SCALAR
|
||||
DAYNAME |SCALAR
|
||||
DAYOFMONTH |SCALAR
|
||||
@ -1531,3 +1532,24 @@ SELECT COALESCE(null, null, null, null) AS "coalesce";
|
||||
null
|
||||
// end::coalesceReturnNull
|
||||
;
|
||||
|
||||
ifNullReturnFirst
|
||||
// tag::ifNullReturnFirst
|
||||
SELECT IFNULL('elastic', null) AS "ifnull";
|
||||
|
||||
ifnull
|
||||
---------------
|
||||
elastic
|
||||
// end::ifNullReturnFirst
|
||||
;
|
||||
|
||||
|
||||
ifNullReturnSecond
|
||||
// tag::ifNullReturnSecond
|
||||
SELECT IFNULL(null, 'search') AS "ifnull";
|
||||
|
||||
ifnull
|
||||
---------------
|
||||
search
|
||||
// end::ifNullReturnSecond
|
||||
;
|
||||
|
@ -10,3 +10,6 @@ SELECT COALESCE(null, ABS(MAX(emp_no)) + 1, 123) AS c FROM test_emp GROUP BY lan
|
||||
|
||||
coalesceWhere
|
||||
SELECT COALESCE(null, ABS(emp_no) + 1, 123) AS c FROM test_emp WHERE COALESCE(null, ABS(emp_no) + 1, 123, 321) > 100 ORDER BY emp_no NULLS FIRST LIMIT 5;
|
||||
|
||||
ifNullField
|
||||
SELECT IFNULL(null, ABS(emp_no) + 1) AS c FROM test_emp ORDER BY emp_no LIMIT 5;
|
||||
|
@ -83,6 +83,7 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.string.Space;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.string.Substring;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.string.UCase;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.conditional.Coalesce;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.conditional.IFNull;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.Mod;
|
||||
import org.elasticsearch.xpack.sql.parser.ParsingException;
|
||||
import org.elasticsearch.xpack.sql.tree.Location;
|
||||
@ -145,6 +146,7 @@ public class FunctionRegistry {
|
||||
// Scalar functions
|
||||
// conditional
|
||||
addToMap(def(Coalesce.class, Coalesce::new));
|
||||
addToMap(def(IFNull.class, IFNull::new));
|
||||
// Date
|
||||
addToMap(def(DayName.class, DayName::new, "DAYNAME"),
|
||||
def(DayOfMonth.class, DayOfMonth::new, "DAYOFMONTH", "DAY", "DOM"),
|
||||
|
@ -31,7 +31,7 @@ public class Coalesce extends ConditionalFunction {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NodeInfo<Coalesce> info() {
|
||||
protected NodeInfo<? extends Coalesce> info() {
|
||||
return NodeInfo.create(this, Coalesce::new, children());
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.xpack.sql.expression.predicate.conditional;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.tree.Location;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Variant of {@link Coalesce} with two args used by MySQL and ODBC.
|
||||
*
|
||||
* Name is `IFNull` to avoid having it registered as `IF_NULL` instead of `IFNULL`.
|
||||
*/
|
||||
public class IFNull extends Coalesce {
|
||||
|
||||
public IFNull(Location location, Expression first, Expression second) {
|
||||
super(location, Arrays.asList(first, second));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expression replaceChildren(List<Expression> newChildren) {
|
||||
return new IFNull(location(), newChildren.get(0), newChildren.get(1));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NodeInfo<IFNull> info() {
|
||||
return NodeInfo.create(this, IFNull::new, children().get(0), children().get(1));
|
||||
}
|
||||
}
|
@ -35,13 +35,14 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.math.Floor;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.string.Ascii;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.string.Repeat;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.BinaryOperator;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.nulls.IsNull;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.Range;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.conditional.Coalesce;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.conditional.IFNull;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.logical.And;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.logical.Not;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.logical.Or;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.nulls.IsNotNull;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.nulls.IsNull;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.Add;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.Div;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.Mod;
|
||||
@ -448,6 +449,22 @@ public class OptimizerTests extends ESTestCase {
|
||||
assertEquals(Literal.TRUE, e.children().get(0));
|
||||
}
|
||||
|
||||
public void testSimplifyIfNullNulls() {
|
||||
Expression e = new SimplifyCoalesce().rule(new IFNull(EMPTY, Literal.NULL, Literal.NULL));
|
||||
assertEquals(Coalesce.class, e.getClass());
|
||||
assertEquals(0, e.children().size());
|
||||
}
|
||||
|
||||
public void testSimplifyIfNullWithNullAndValue() {
|
||||
Expression e = new SimplifyCoalesce().rule(new IFNull(EMPTY, Literal.NULL, ONE));
|
||||
assertEquals(1, e.children().size());
|
||||
assertEquals(ONE, e.children().get(0));
|
||||
|
||||
e = new SimplifyCoalesce().rule(new IFNull(EMPTY, ONE, Literal.NULL));
|
||||
assertEquals(1, e.children().size());
|
||||
assertEquals(ONE, e.children().get(0));
|
||||
}
|
||||
|
||||
//
|
||||
// Logical simplifications
|
||||
//
|
||||
|
@ -25,6 +25,7 @@ import org.elasticsearch.xpack.sql.expression.gen.pipeline.BinaryPipesTests;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.processor.ConstantProcessor;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.conditional.IFNull;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.In;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.fulltext.FullTextPredicate;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.InPipe;
|
||||
@ -87,7 +88,7 @@ import static org.mockito.Mockito.mock;
|
||||
public class NodeSubclassTests<T extends B, B extends Node<B>> extends ESTestCase {
|
||||
|
||||
private static final List<Class<? extends Node<?>>> CLASSES_WITH_MIN_TWO_CHILDREN = Arrays.asList(
|
||||
In.class, InPipe.class, Percentile.class, Percentiles.class, PercentileRanks.class);
|
||||
IFNull.class, In.class, InPipe.class, Percentile.class, Percentiles.class, PercentileRanks.class);
|
||||
|
||||
private final Class<T> subclass;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user