From a7fe6e644b750b1a07294b8104d0fc0ab67287c1 Mon Sep 17 00:00:00 2001 From: lukasz-soszynski-eliatra <110241464+lukasz-soszynski-eliatra@users.noreply.github.com> Date: Thu, 19 Sep 2024 18:32:43 +0200 Subject: [PATCH] Isempty (#676) * isempty implementation Signed-off-by: Kacper Trochimiak * isempty implementation v2 Signed-off-by: Kacper Trochimiak * Is empty function returns correct column list. Signed-off-by: Lukasz Soszynski * Tests for is empty function Signed-off-by: Lukasz Soszynski * Test and documentation related to the isempty function Signed-off-by: Lukasz Soszynski --------- Signed-off-by: Kacper Trochimiak Signed-off-by: Lukasz Soszynski Co-authored-by: Kacper Trochimiak --- .../FlintSparkPPLBuiltinFunctionITSuite.scala | 97 ++++++++++++++++++- ppl-spark-integration/README.md | 4 +- .../src/main/antlr4/OpenSearchPPLLexer.g4 | 1 + .../src/main/antlr4/OpenSearchPPLParser.g4 | 6 ++ .../sql/ast/AbstractNodeVisitor.java | 5 + .../opensearch/sql/ast/expression/Case.java | 12 +++ .../sql/ast/expression/IsEmpty.java | 35 +++++++ .../opensearch/sql/ast/expression/When.java | 8 ++ .../sql/ppl/CatalystPlanContext.java | 5 + .../sql/ppl/CatalystQueryPlanVisitor.java | 51 ++++++++-- .../sql/ppl/parser/AstExpressionBuilder.java | 21 +++- .../ppl/utils/BuiltinFunctionTranslator.java | 22 +++-- 12 files changed, 242 insertions(+), 25 deletions(-) create mode 100644 ppl-spark-integration/src/main/java/org/opensearch/sql/ast/expression/IsEmpty.java diff --git a/integ-test/src/integration/scala/org/opensearch/flint/spark/ppl/FlintSparkPPLBuiltinFunctionITSuite.scala b/integ-test/src/integration/scala/org/opensearch/flint/spark/ppl/FlintSparkPPLBuiltinFunctionITSuite.scala index bc155c023..be894ad49 100644 --- a/integ-test/src/integration/scala/org/opensearch/flint/spark/ppl/FlintSparkPPLBuiltinFunctionITSuite.scala +++ b/integ-test/src/integration/scala/org/opensearch/flint/spark/ppl/FlintSparkPPLBuiltinFunctionITSuite.scala @@ -10,9 +10,9 @@ import java.sql.{Date, Time, Timestamp} import org.opensearch.sql.ppl.utils.DataTypeTransformer.seq import org.apache.spark.sql.{AnalysisException, QueryTest, Row} -import org.apache.spark.sql.catalyst.analysis.{UnresolvedAttribute, UnresolvedFunction, UnresolvedRelation} -import org.apache.spark.sql.catalyst.expressions.{EqualTo, GreaterThan, Literal} -import org.apache.spark.sql.catalyst.plans.logical.{Filter, LogicalPlan, Project} +import org.apache.spark.sql.catalyst.analysis.{UnresolvedAttribute, UnresolvedFunction, UnresolvedRelation, UnresolvedStar} +import org.apache.spark.sql.catalyst.expressions.{Alias, CaseWhen, EqualTo, GreaterThan, Literal, NamedExpression} +import org.apache.spark.sql.catalyst.plans.logical.{Filter, GlobalLimit, LocalLimit, LogicalPlan, Project} import org.apache.spark.sql.streaming.StreamTest import org.apache.spark.sql.types.DoubleType @@ -25,6 +25,7 @@ class FlintSparkPPLBuiltinFunctionITSuite /** Test table and index name */ private val testTable = "spark_catalog.default.flint_ppl_test" private val testNullTable = "spark_catalog.default.flint_ppl_test_null" + private val testTextSizeTable = "spark_catalog.default.flint_ppl_text_size" override def beforeAll(): Unit = { super.beforeAll() @@ -270,6 +271,96 @@ class FlintSparkPPLBuiltinFunctionITSuite comparePlans(logicalPlan, expectedPlan, checkAnalysis = false) } + test("test string functions - isempty eval") { + val frame = sql(s""" + | source = $testNullTable | head 1 | eval a = isempty('full'), b = isempty(''), c = isempty(' ') | fields a, b, c + | """.stripMargin) + + val results: Array[Row] = frame.collect() + val expectedResults: Array[Row] = Array(Row(false, true, true)) + assert(results.sameElements(expectedResults)) + + val logicalPlan: LogicalPlan = frame.queryExecution.logical + val table = UnresolvedRelation(Seq("spark_catalog", "default", "flint_ppl_test_null")) + val localLimit = LocalLimit(Literal(1), table) + val globalLimit = GlobalLimit(Literal(1), localLimit) + + // val projectList = Seq(UnresolvedStar(None)) + + val caseOne = CaseWhen( + Seq( + ( + EqualTo( + UnresolvedFunction( + "length", + Seq(UnresolvedFunction("trim", Seq(Literal("full")), isDistinct = false)), + isDistinct = false), + Literal(0)), + Literal(true))), + Literal(false)) + val aliasOne = Alias(caseOne, "a")() + + val caseTwo = CaseWhen( + Seq( + ( + EqualTo( + UnresolvedFunction( + "length", + Seq(UnresolvedFunction("trim", Seq(Literal("")), isDistinct = false)), + isDistinct = false), + Literal(0)), + Literal(true))), + Literal(false)) + val aliasTwo = Alias(caseTwo, "b")() + + val caseThree = CaseWhen( + Seq( + ( + EqualTo( + UnresolvedFunction( + "length", + Seq(UnresolvedFunction("trim", Seq(Literal(" ")), isDistinct = false)), + isDistinct = false), + Literal(0)), + Literal(true))), + Literal(false)) + val aliasThree = Alias(caseThree, "c")() + + val projectList = Seq(UnresolvedStar(None), aliasOne, aliasTwo, aliasThree) + val innerProject = Project(projectList, globalLimit) + + val expectedPlan = Project( + Seq(UnresolvedAttribute("a"), UnresolvedAttribute("b"), UnresolvedAttribute("c")), + innerProject) + comparePlans(logicalPlan, expectedPlan, checkAnalysis = false) + } + + test("test string functions - isempty where") { + val frame = sql(s""" + | source = $testNullTable | where isempty('I am not empty'); + | """.stripMargin) + val results: Array[Row] = frame.collect() + assert(results.length == 0) + + val logicalPlan: LogicalPlan = frame.queryExecution.logical + + val table = UnresolvedRelation(Seq("spark_catalog", "default", "flint_ppl_test_null")) + val caseIsEmpty = CaseWhen( + Seq( + ( + EqualTo( + UnresolvedFunction( + "length", + Seq(UnresolvedFunction("trim", Seq(Literal("I am not empty")), isDistinct = false)), + isDistinct = false), + Literal(0)), + Literal(true))), + Literal(false)) + val filterPlan = Filter(caseIsEmpty, table) + val expectedPlan = Project(Seq(UnresolvedStar(None)), filterPlan) + comparePlans(logicalPlan, expectedPlan, checkAnalysis = false) + } + test("test math functions - abs") { val frame = sql(s""" | source = $testTable |where age = abs(-30) | fields name, age diff --git a/ppl-spark-integration/README.md b/ppl-spark-integration/README.md index 86abfc8da..0c83abd97 100644 --- a/ppl-spark-integration/README.md +++ b/ppl-spark-integration/README.md @@ -244,6 +244,7 @@ See the next samples of PPL queries : - `source = table | where c = 'test' | fields a,b,c | head 3` - `source = table | where ispresent(b)` - `source = table | where isnull(coalesce(a, b)) | fields a,b,c | head 3` + - `source = table | where isempty(a)` **Filters With Logical Conditions** - `source = table | where c = 'test' AND a = 1 | fields a,b,c` @@ -262,7 +263,8 @@ Assumptions: `a`, `b`, `c` are existing fields in `table` - `source = table | eval f = a * 2, h = f * 2 | fields a,f,h` - `source = table | eval f = a * 2, h = b | stats avg(f) by h` - `source = table | eval f = ispresent(a)` - - `source = table | eval r = coalesce(a, b, c) | fields r + - `source = table | eval r = coalesce(a, b, c) | fields r` + - `source = table | eval e = isempty(a) | fields e` Limitation: Overriding existing field is unsupported, following queries throw exceptions with "Reference 'a' is ambiguous" - `source = table | eval a = 10 | fields a,b,c` diff --git a/ppl-spark-integration/src/main/antlr4/OpenSearchPPLLexer.g4 b/ppl-spark-integration/src/main/antlr4/OpenSearchPPLLexer.g4 index 27068f000..0fcb069c5 100644 --- a/ppl-spark-integration/src/main/antlr4/OpenSearchPPLLexer.g4 +++ b/ppl-spark-integration/src/main/antlr4/OpenSearchPPLLexer.g4 @@ -338,6 +338,7 @@ LOCATE: 'LOCATE'; REPLACE: 'REPLACE'; REVERSE: 'REVERSE'; CAST: 'CAST'; +ISEMPTY: 'ISEMPTY'; // BOOL FUNCTIONS LIKE: 'LIKE'; diff --git a/ppl-spark-integration/src/main/antlr4/OpenSearchPPLParser.g4 b/ppl-spark-integration/src/main/antlr4/OpenSearchPPLParser.g4 index f4c1c919f..e7d7321a2 100644 --- a/ppl-spark-integration/src/main/antlr4/OpenSearchPPLParser.g4 +++ b/ppl-spark-integration/src/main/antlr4/OpenSearchPPLParser.g4 @@ -299,6 +299,7 @@ logicalExpression | left = logicalExpression (AND)? right = logicalExpression # logicalAnd | left = logicalExpression XOR right = logicalExpression # logicalXor | booleanExpression # booleanExpr + | isEmptyExpression # isEmptyExpr ; comparisonExpression @@ -328,6 +329,10 @@ booleanExpression : booleanFunctionCall ; + isEmptyExpression + : ISEMPTY LT_PRTHS functionArg RT_PRTHS + ; + relevanceExpression : singleFieldRelevanceFunction | multiFieldRelevanceFunction @@ -688,6 +693,7 @@ textFunctionName | LOCATE | REPLACE | REVERSE + | ISEMPTY ; positionFunctionName diff --git a/ppl-spark-integration/src/main/java/org/opensearch/sql/ast/AbstractNodeVisitor.java b/ppl-spark-integration/src/main/java/org/opensearch/sql/ast/AbstractNodeVisitor.java index 214e468fb..601ebd540 100644 --- a/ppl-spark-integration/src/main/java/org/opensearch/sql/ast/AbstractNodeVisitor.java +++ b/ppl-spark-integration/src/main/java/org/opensearch/sql/ast/AbstractNodeVisitor.java @@ -20,6 +20,7 @@ import org.opensearch.sql.ast.expression.Function; import org.opensearch.sql.ast.expression.In; import org.opensearch.sql.ast.expression.Interval; +import org.opensearch.sql.ast.expression.IsEmpty; import org.opensearch.sql.ast.expression.Let; import org.opensearch.sql.ast.expression.Literal; import org.opensearch.sql.ast.expression.Map; @@ -166,6 +167,10 @@ public T visitFunction(Function node, C context) { return visitChildren(node, context); } + public T visitIsEmpty(IsEmpty node, C context) { + return visitChildren(node, context); + } + public T visitWindowFunction(WindowFunction node, C context) { return visitChildren(node, context); } diff --git a/ppl-spark-integration/src/main/java/org/opensearch/sql/ast/expression/Case.java b/ppl-spark-integration/src/main/java/org/opensearch/sql/ast/expression/Case.java index 265db3ba7..2419cf744 100644 --- a/ppl-spark-integration/src/main/java/org/opensearch/sql/ast/expression/Case.java +++ b/ppl-spark-integration/src/main/java/org/opensearch/sql/ast/expression/Case.java @@ -32,6 +32,18 @@ public Case(UnresolvedExpression caseValue, List whenClauses, UnresolvedEx this.elseClause = elseClause; } + public UnresolvedExpression getCaseValue() { + return caseValue; + } + + public List getWhenClauses() { + return whenClauses; + } + + public UnresolvedExpression getElseClause() { + return elseClause; + } + @Override public List getChild() { ImmutableList.Builder children = ImmutableList.builder(); diff --git a/ppl-spark-integration/src/main/java/org/opensearch/sql/ast/expression/IsEmpty.java b/ppl-spark-integration/src/main/java/org/opensearch/sql/ast/expression/IsEmpty.java new file mode 100644 index 000000000..9b2e368a2 --- /dev/null +++ b/ppl-spark-integration/src/main/java/org/opensearch/sql/ast/expression/IsEmpty.java @@ -0,0 +1,35 @@ +package org.opensearch.sql.ast.expression; + +import com.google.common.collect.ImmutableList; +import org.opensearch.sql.ast.AbstractNodeVisitor; + +import java.util.List; + +public class IsEmpty extends UnresolvedExpression { + private Case caseValue; + + public IsEmpty(Case caseValue) { + this.caseValue = caseValue; + } + + @Override + public List getChild() { + return ImmutableList.of(this.caseValue); + } + + @Override + public R accept(AbstractNodeVisitor nodeVisitor, C context) { + return nodeVisitor.visitIsEmpty(this, context); + } + + public Case getCaseValue() { + return caseValue; + } + + @Override + public String toString() { + return String.format( + "isempty(%s)", + caseValue.toString()); + } +} diff --git a/ppl-spark-integration/src/main/java/org/opensearch/sql/ast/expression/When.java b/ppl-spark-integration/src/main/java/org/opensearch/sql/ast/expression/When.java index 9341f6c2e..455722e29 100644 --- a/ppl-spark-integration/src/main/java/org/opensearch/sql/ast/expression/When.java +++ b/ppl-spark-integration/src/main/java/org/opensearch/sql/ast/expression/When.java @@ -25,6 +25,14 @@ public When(UnresolvedExpression condition, UnresolvedExpression result) { this.result = result; } + public UnresolvedExpression getCondition() { + return condition; + } + + public UnresolvedExpression getResult() { + return result; + } + @Override public List getChild() { return ImmutableList.of(condition, result); diff --git a/ppl-spark-integration/src/main/java/org/opensearch/sql/ppl/CatalystPlanContext.java b/ppl-spark-integration/src/main/java/org/opensearch/sql/ppl/CatalystPlanContext.java index 003dc625f..46a016d1a 100644 --- a/ppl-spark-integration/src/main/java/org/opensearch/sql/ppl/CatalystPlanContext.java +++ b/ppl-spark-integration/src/main/java/org/opensearch/sql/ppl/CatalystPlanContext.java @@ -105,6 +105,11 @@ public Stack getNamedParseExpressions() { return namedParseExpressions; } + public void setNamedParseExpressions(Stack namedParseExpressions) { + this.namedParseExpressions.clear(); + this.namedParseExpressions.addAll(namedParseExpressions); + } + public Optional popNamedParseExpressions() { return namedParseExpressions.isEmpty() ? Optional.empty() : Optional.of(namedParseExpressions.pop()); } diff --git a/ppl-spark-integration/src/main/java/org/opensearch/sql/ppl/CatalystQueryPlanVisitor.java b/ppl-spark-integration/src/main/java/org/opensearch/sql/ppl/CatalystQueryPlanVisitor.java index 10a18f39c..2aa99cd67 100644 --- a/ppl-spark-integration/src/main/java/org/opensearch/sql/ppl/CatalystQueryPlanVisitor.java +++ b/ppl-spark-integration/src/main/java/org/opensearch/sql/ppl/CatalystQueryPlanVisitor.java @@ -10,6 +10,7 @@ import org.apache.spark.sql.catalyst.analysis.UnresolvedRelation; import org.apache.spark.sql.catalyst.analysis.UnresolvedStar$; import org.apache.spark.sql.catalyst.expressions.Ascending$; +import org.apache.spark.sql.catalyst.expressions.CaseWhen; import org.apache.spark.sql.catalyst.expressions.Descending$; import org.apache.spark.sql.catalyst.expressions.Expression; import org.apache.spark.sql.catalyst.expressions.NamedExpression; @@ -37,6 +38,7 @@ import org.opensearch.sql.ast.expression.Function; import org.opensearch.sql.ast.expression.In; import org.opensearch.sql.ast.expression.Interval; +import org.opensearch.sql.ast.expression.IsEmpty; import org.opensearch.sql.ast.expression.Let; import org.opensearch.sql.ast.expression.Literal; import org.opensearch.sql.ast.expression.Not; @@ -45,6 +47,7 @@ import org.opensearch.sql.ast.expression.QualifiedName; import org.opensearch.sql.ast.expression.Span; import org.opensearch.sql.ast.expression.UnresolvedExpression; +import org.opensearch.sql.ast.expression.When; import org.opensearch.sql.ast.expression.WindowFunction; import org.opensearch.sql.ast.expression.Xor; import org.opensearch.sql.ast.statement.Explain; @@ -74,17 +77,16 @@ import org.opensearch.sql.ppl.utils.SortUtils; import scala.Option; import scala.Option$; +import scala.Tuple2; import scala.collection.Seq; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.Optional; +import java.util.*; import java.util.function.BiFunction; import java.util.stream.Collectors; import static java.util.Collections.emptyList; import static java.util.List.of; +import static org.opensearch.sql.expression.function.BuiltinFunctionName.EQUAL; import static org.opensearch.sql.ppl.CatalystPlanContext.findRelation; import static org.opensearch.sql.ppl.utils.DataTypeTransformer.seq; import static org.opensearch.sql.ppl.utils.DataTypeTransformer.translate; @@ -351,11 +353,6 @@ public LogicalPlan visitIn(In node, CatalystPlanContext context) { throw new IllegalStateException("Not Supported operation : In"); } - @Override - public LogicalPlan visitCase(Case node, CatalystPlanContext context) { - throw new IllegalStateException("Not Supported operation : Case"); - } - @Override public LogicalPlan visitRareTopN(RareTopN node, CatalystPlanContext context) { throw new IllegalStateException("Not Supported operation : RareTopN"); @@ -580,6 +577,18 @@ public Expression visitFunction(Function node, CatalystPlanContext context) { return context.getNamedParseExpressions().push(function); } + @Override + public Expression visitIsEmpty(IsEmpty node, CatalystPlanContext context) { + Stack namedParseExpressions = new Stack<>(); + namedParseExpressions.addAll(context.getNamedParseExpressions()); + Expression expression = visitCase(node.getCaseValue(), context); + namedParseExpressions.add(expression); + context.setNamedParseExpressions(namedParseExpressions); + return expression; + } + + + @Override public Expression visitInterval(Interval node, CatalystPlanContext context) { throw new IllegalStateException("Not Supported operation : Interval"); @@ -602,7 +611,29 @@ public Expression visitKmeans(Kmeans node, CatalystPlanContext context) { @Override public Expression visitCase(Case node, CatalystPlanContext context) { - throw new IllegalStateException("Not Supported operation : Case"); + analyze(node.getElseClause(), context); + Expression elseValue = context.getNamedParseExpressions().pop(); + List> whens = new ArrayList<>(); + for (When when : node.getWhenClauses()) { + if (node.getCaseValue() == null) { + whens.add( + new Tuple2<>( + analyze(when.getCondition(), context), + analyze(when.getResult(), context) + ) + ); + } else { + // Merge case value and condition (compare value) into a single equal condition + Compare compare = new Compare(EQUAL.getName().getFunctionName(), node.getCaseValue(), when.getCondition()); + whens.add( + new Tuple2<>( + analyze(compare, context), analyze(when.getResult(), context) + ) + ); + } + context.retainAllNamedParseExpressions(e -> e); + } + return context.getNamedParseExpressions().push(new CaseWhen(seq(whens), Option.apply(elseValue))); } @Override diff --git a/ppl-spark-integration/src/main/java/org/opensearch/sql/ppl/parser/AstExpressionBuilder.java b/ppl-spark-integration/src/main/java/org/opensearch/sql/ppl/parser/AstExpressionBuilder.java index 44b9cfd0a..cf641bde8 100644 --- a/ppl-spark-integration/src/main/java/org/opensearch/sql/ppl/parser/AstExpressionBuilder.java +++ b/ppl-spark-integration/src/main/java/org/opensearch/sql/ppl/parser/AstExpressionBuilder.java @@ -16,6 +16,7 @@ import org.opensearch.sql.ast.expression.AllFields; import org.opensearch.sql.ast.expression.And; import org.opensearch.sql.ast.expression.Argument; +import org.opensearch.sql.ast.expression.Case; import org.opensearch.sql.ast.expression.Compare; import org.opensearch.sql.ast.expression.DataType; import org.opensearch.sql.ast.expression.EqualTo; @@ -23,18 +24,18 @@ import org.opensearch.sql.ast.expression.Function; import org.opensearch.sql.ast.expression.Interval; import org.opensearch.sql.ast.expression.IntervalUnit; +import org.opensearch.sql.ast.expression.IsEmpty; import org.opensearch.sql.ast.expression.Let; import org.opensearch.sql.ast.expression.Literal; import org.opensearch.sql.ast.expression.Not; import org.opensearch.sql.ast.expression.Or; -import org.opensearch.sql.ast.expression.ParseMethod; import org.opensearch.sql.ast.expression.QualifiedName; import org.opensearch.sql.ast.expression.Span; import org.opensearch.sql.ast.expression.SpanUnit; import org.opensearch.sql.ast.expression.UnresolvedArgument; import org.opensearch.sql.ast.expression.UnresolvedExpression; +import org.opensearch.sql.ast.expression.When; import org.opensearch.sql.ast.expression.Xor; -import org.opensearch.sql.ast.tree.Parse; import org.opensearch.sql.common.utils.StringUtils; import org.opensearch.sql.ppl.utils.ArgumentFactory; @@ -45,9 +46,12 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import static org.opensearch.sql.expression.function.BuiltinFunctionName.EQUAL; import static org.opensearch.sql.expression.function.BuiltinFunctionName.IS_NOT_NULL; import static org.opensearch.sql.expression.function.BuiltinFunctionName.IS_NULL; +import static org.opensearch.sql.expression.function.BuiltinFunctionName.LENGTH; import static org.opensearch.sql.expression.function.BuiltinFunctionName.POSITION; +import static org.opensearch.sql.expression.function.BuiltinFunctionName.TRIM; /** @@ -195,6 +199,19 @@ public UnresolvedExpression visitBooleanFunctionCall(OpenSearchPPLParser.Boolean ctx.functionArgs().functionArg()); } + @Override + public UnresolvedExpression visitIsEmptyExpression(OpenSearchPPLParser.IsEmptyExpressionContext ctx) { + Function trimFunction = new Function(TRIM.getName().getFunctionName(), Collections.singletonList(this.visitFunctionArg(ctx.functionArg()))); + Function lengthFunction = new Function(LENGTH.getName().getFunctionName(), Collections.singletonList(trimFunction)); + Compare lengthEqualsZero = new Compare(EQUAL.getName().getFunctionName(), lengthFunction, new Literal(0, DataType.INTEGER)); + Literal whenCompareValue = new Literal(0, DataType.INTEGER); + Literal isEmptyFalse = new Literal(false, DataType.BOOLEAN); + Literal isEmptyTrue = new Literal(true, DataType.BOOLEAN); + When when = new When(whenCompareValue, isEmptyTrue); + Case caseWhen = new Case(lengthFunction, Collections.singletonList(when), isEmptyFalse); + return new IsEmpty(caseWhen); + } + /** * Eval function. */ diff --git a/ppl-spark-integration/src/main/java/org/opensearch/sql/ppl/utils/BuiltinFunctionTranslator.java b/ppl-spark-integration/src/main/java/org/opensearch/sql/ppl/utils/BuiltinFunctionTranslator.java index 230fa1dad..d817305a9 100644 --- a/ppl-spark-integration/src/main/java/org/opensearch/sql/ppl/utils/BuiltinFunctionTranslator.java +++ b/ppl-spark-integration/src/main/java/org/opensearch/sql/ppl/utils/BuiltinFunctionTranslator.java @@ -14,26 +14,28 @@ import java.util.Map; import static org.opensearch.sql.expression.function.BuiltinFunctionName.ADD; +import static org.opensearch.sql.expression.function.BuiltinFunctionName.ADDDATE; +import static org.opensearch.sql.expression.function.BuiltinFunctionName.DATEDIFF; +import static org.opensearch.sql.expression.function.BuiltinFunctionName.DAY_OF_MONTH; import static org.opensearch.sql.expression.function.BuiltinFunctionName.COALESCE; import static org.opensearch.sql.expression.function.BuiltinFunctionName.SUBTRACT; import static org.opensearch.sql.expression.function.BuiltinFunctionName.MULTIPLY; import static org.opensearch.sql.expression.function.BuiltinFunctionName.DIVIDE; import static org.opensearch.sql.expression.function.BuiltinFunctionName.MODULUS; import static org.opensearch.sql.expression.function.BuiltinFunctionName.DAY_OF_WEEK; -import static org.opensearch.sql.expression.function.BuiltinFunctionName.DAY_OF_MONTH; import static org.opensearch.sql.expression.function.BuiltinFunctionName.DAY_OF_YEAR; -import static org.opensearch.sql.expression.function.BuiltinFunctionName.WEEK_OF_YEAR; -import static org.opensearch.sql.expression.function.BuiltinFunctionName.WEEK; -import static org.opensearch.sql.expression.function.BuiltinFunctionName.MONTH_OF_YEAR; import static org.opensearch.sql.expression.function.BuiltinFunctionName.HOUR_OF_DAY; +import static org.opensearch.sql.expression.function.BuiltinFunctionName.IS_NOT_NULL; +import static org.opensearch.sql.expression.function.BuiltinFunctionName.IS_NULL; +import static org.opensearch.sql.expression.function.BuiltinFunctionName.LENGTH; +import static org.opensearch.sql.expression.function.BuiltinFunctionName.LOCALTIME; import static org.opensearch.sql.expression.function.BuiltinFunctionName.MINUTE_OF_HOUR; +import static org.opensearch.sql.expression.function.BuiltinFunctionName.MONTH_OF_YEAR; import static org.opensearch.sql.expression.function.BuiltinFunctionName.SECOND_OF_MINUTE; import static org.opensearch.sql.expression.function.BuiltinFunctionName.SUBDATE; -import static org.opensearch.sql.expression.function.BuiltinFunctionName.ADDDATE; -import static org.opensearch.sql.expression.function.BuiltinFunctionName.DATEDIFF; -import static org.opensearch.sql.expression.function.BuiltinFunctionName.LOCALTIME; -import static org.opensearch.sql.expression.function.BuiltinFunctionName.IS_NULL; -import static org.opensearch.sql.expression.function.BuiltinFunctionName.IS_NOT_NULL; +import static org.opensearch.sql.expression.function.BuiltinFunctionName.TRIM; +import static org.opensearch.sql.expression.function.BuiltinFunctionName.WEEK; +import static org.opensearch.sql.expression.function.BuiltinFunctionName.WEEK_OF_YEAR; import static org.opensearch.sql.ppl.utils.DataTypeTransformer.seq; import static scala.Option.empty; @@ -69,6 +71,8 @@ public interface BuiltinFunctionTranslator { .put(IS_NOT_NULL, "isnotnull") .put(BuiltinFunctionName.ISPRESENT, "isnotnull") .put(COALESCE, "coalesce") + .put(LENGTH, "length") + .put(TRIM, "trim") .build(); static Expression builtinFunction(org.opensearch.sql.ast.expression.Function function, List args) {