Skip to content
Open
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
import org.opensearch.sql.ast.tree.Bin;
import org.opensearch.sql.ast.tree.Chart;
import org.opensearch.sql.ast.tree.CloseCursor;
import org.opensearch.sql.ast.tree.Convert;
import org.opensearch.sql.ast.tree.Dedupe;
import org.opensearch.sql.ast.tree.Eval;
import org.opensearch.sql.ast.tree.Expand;
Expand Down Expand Up @@ -524,6 +525,11 @@ public LogicalPlan visitEval(Eval node, AnalysisContext context) {
return new LogicalEval(child, expressionsBuilder.build());
}

@Override
public LogicalPlan visitConvert(Convert node, AnalysisContext context) {
throw getOnlyForCalciteException("convert");
}

@Override
public LogicalPlan visitAddTotals(AddTotals node, AnalysisContext context) {
throw getOnlyForCalciteException("addtotals");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
import org.opensearch.sql.ast.tree.Bin;
import org.opensearch.sql.ast.tree.Chart;
import org.opensearch.sql.ast.tree.CloseCursor;
import org.opensearch.sql.ast.tree.Convert;
import org.opensearch.sql.ast.tree.Dedupe;
import org.opensearch.sql.ast.tree.Eval;
import org.opensearch.sql.ast.tree.Expand;
Expand Down Expand Up @@ -410,6 +411,10 @@ public T visitFillNull(FillNull fillNull, C context) {
return visitChildren(fillNull, context);
}

public T visitConvert(Convert node, C context) {
return visitChildren(node, context);
}

public T visitPatterns(Patterns patterns, C context) {
return visitChildren(patterns, context);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
import org.opensearch.sql.ast.tree.AppendPipe;
import org.opensearch.sql.ast.tree.Bin;
import org.opensearch.sql.ast.tree.Chart;
import org.opensearch.sql.ast.tree.Convert;
import org.opensearch.sql.ast.tree.ConvertFunction;
import org.opensearch.sql.ast.tree.Dedupe;
import org.opensearch.sql.ast.tree.Eval;
import org.opensearch.sql.ast.tree.Expand;
Expand Down Expand Up @@ -605,6 +607,34 @@ public Node visitExpand(Expand node, FieldResolutionContext context) {
return node;
}

@Override
public Node visitConvert(Convert node, FieldResolutionContext context) {
Set<String> inputFields = new HashSet<>();
Set<String> outputFields = new HashSet<>();

for (ConvertFunction convertFunc : node.getConvertFunctions()) {
List<String> fieldList = convertFunc.getFieldList();
inputFields.addAll(fieldList);

if (convertFunc.getAsField() != null) {
outputFields.add(convertFunc.getAsField());
} else {
outputFields.addAll(fieldList);
}
}

FieldResolutionResult currentReq = context.getCurrentRequirements();
Set<String> upstreamRequiredFields = new HashSet<>(currentReq.getRegularFields());
upstreamRequiredFields.removeAll(outputFields);
upstreamRequiredFields.addAll(inputFields);

context.pushRequirements(
new FieldResolutionResult(upstreamRequiredFields, currentReq.getWildcard()));
visitChildren(node, context);
context.popRequirements();
return node;
}

private Set<String> extractFieldsFromAggregation(UnresolvedExpression expr) {
Set<String> fields = new HashSet<>();
if (expr instanceof Alias alias) {
Expand Down
43 changes: 43 additions & 0 deletions core/src/main/java/org/opensearch/sql/ast/tree/Convert.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

package org.opensearch.sql.ast.tree;

import com.google.common.collect.ImmutableList;
import java.util.List;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import org.opensearch.sql.ast.AbstractNodeVisitor;

/** AST node representing the Convert command. */
@Getter
@Setter
@ToString
@EqualsAndHashCode(callSuper = false)
@RequiredArgsConstructor
public class Convert extends UnresolvedPlan {
private final String timeformat;
private final List<ConvertFunction> convertFunctions;
private UnresolvedPlan child;

@Override
public Convert attach(UnresolvedPlan child) {
this.child = child;
return this;
}

@Override
public List<UnresolvedPlan> getChild() {
return this.child == null ? ImmutableList.of() : ImmutableList.of(this.child);
}

@Override
public <T, C> T accept(AbstractNodeVisitor<T, C> nodeVisitor, C context) {
return nodeVisitor.visitConvert(this, context);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

package org.opensearch.sql.ast.tree;

import java.util.List;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.ToString;

/** Represents a single conversion function within a convert command. */
@Getter
@ToString
@EqualsAndHashCode
@RequiredArgsConstructor
public class ConvertFunction {
private final String functionName;
private final List<String> fieldList;
private final String asField;
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@
import org.opensearch.sql.ast.tree.Bin;
import org.opensearch.sql.ast.tree.Chart;
import org.opensearch.sql.ast.tree.CloseCursor;
import org.opensearch.sql.ast.tree.Convert;
import org.opensearch.sql.ast.tree.ConvertFunction;
import org.opensearch.sql.ast.tree.Dedupe;
import org.opensearch.sql.ast.tree.Eval;
import org.opensearch.sql.ast.tree.Expand;
Expand Down Expand Up @@ -928,6 +930,90 @@ public RelNode visitEval(Eval node, CalcitePlanContext context) {
return context.relBuilder.peek();
}

@Override
public RelNode visitConvert(Convert node, CalcitePlanContext context) {
visitChildren(node, context);

if (node.getConvertFunctions() == null || node.getConvertFunctions().isEmpty()) {
return context.relBuilder.peek();
}

java.util.Map<String, RexNode> replacements = new java.util.HashMap<>();
List<Pair<String, RexNode>> additions = new ArrayList<>();
Set<String> seenFields = new HashSet<>();

for (ConvertFunction convertFunc : node.getConvertFunctions()) {
processConversionFunction(convertFunc, replacements, additions, seenFields, context);
}

return buildProjectionWithConversions(replacements, additions, context);
}

private void processConversionFunction(
ConvertFunction convertFunc,
java.util.Map<String, RexNode> replacements,
List<Pair<String, RexNode>> additions,
Set<String> seenFields,
CalcitePlanContext context) {
String functionName = convertFunc.getFunctionName();
List<String> fieldList = convertFunc.getFieldList();
String asField = convertFunc.getAsField();

if (fieldList.size() != 1) {
throw new SemanticCheckException("Convert function must operate on exactly one field");
}

String fieldName = fieldList.get(0);

if (seenFields.contains(fieldName)) {
throw new SemanticCheckException(
String.format("Field '%s' cannot be converted more than once", fieldName));
}
seenFields.add(fieldName);

RexNode field = context.relBuilder.field(fieldName);
RexNode convertCall = PPLFuncImpTable.INSTANCE.resolve(context.rexBuilder, functionName, field);

if (asField != null) {
additions.add(Pair.of(asField, context.relBuilder.alias(convertCall, asField)));
} else {
replacements.put(fieldName, context.relBuilder.alias(convertCall, fieldName));
}
}

private RelNode buildProjectionWithConversions(
java.util.Map<String, RexNode> replacements,
List<Pair<String, RexNode>> additions,
CalcitePlanContext context) {
List<String> originalFields = context.relBuilder.peek().getRowType().getFieldNames();
List<RexNode> projectList = new ArrayList<>();

for (String fieldName : originalFields) {
projectList.add(replacements.getOrDefault(fieldName, context.relBuilder.field(fieldName)));
}

Set<String> addedAsNames = new HashSet<>();

for (Pair<String, RexNode> addition : additions) {
String asName = addition.getLeft();

if (originalFields.contains(asName)) {
throw new SemanticCheckException(
String.format("AS name '%s' conflicts with existing field", asName));
}

if (!addedAsNames.add(asName)) {
throw new SemanticCheckException(
String.format("AS name '%s' is used multiple times in convert", asName));
}

projectList.add(addition.getRight());
}

context.relBuilder.project(projectList);
return context.relBuilder.peek();
}

private void projectPlusOverriding(
List<RexNode> newFields, List<String> newNames, CalcitePlanContext context) {
List<String> originalFieldNames = context.relBuilder.peek().getRowType().getFieldNames();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,18 @@ public enum BuiltinFunctionName {

INTERVAL(FunctionName.of("interval")),

/** PPL Convert Command Functions. */
AUTO(FunctionName.of("auto")),
NUM(FunctionName.of("num")),
CTIME(FunctionName.of("ctime")),
MKTIME(FunctionName.of("mktime")),
DUR2SEC(FunctionName.of("dur2sec")),
MEMK(FunctionName.of("memk")),
MSTIME(FunctionName.of("mstime")),
RMUNIT(FunctionName.of("rmunit")),
RMCOMMA(FunctionName.of("rmcomma")),
NONE(FunctionName.of("none")),

/** Data Type Convert Function. */
CAST_TO_STRING(FunctionName.of("cast_to_string")),
CAST_TO_BYTE(FunctionName.of("cast_to_byte")),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,18 @@
import org.opensearch.sql.expression.function.jsonUDF.JsonFunctionImpl;
import org.opensearch.sql.expression.function.jsonUDF.JsonKeysFunctionImpl;
import org.opensearch.sql.expression.function.jsonUDF.JsonSetFunctionImpl;
import org.opensearch.sql.expression.function.udf.AutoConvertFunction;
import org.opensearch.sql.expression.function.udf.CryptographicFunction;
import org.opensearch.sql.expression.function.udf.MemkConvertFunction;
import org.opensearch.sql.expression.function.udf.NoneConvertFunction;
import org.opensearch.sql.expression.function.udf.NumConvertFunction;
import org.opensearch.sql.expression.function.udf.ParseFunction;
import org.opensearch.sql.expression.function.udf.RelevanceQueryFunction;
import org.opensearch.sql.expression.function.udf.RexExtractFunction;
import org.opensearch.sql.expression.function.udf.RexExtractMultiFunction;
import org.opensearch.sql.expression.function.udf.RexOffsetFunction;
import org.opensearch.sql.expression.function.udf.RmcommaConvertFunction;
import org.opensearch.sql.expression.function.udf.RmunitConvertFunction;
import org.opensearch.sql.expression.function.udf.SpanFunction;
import org.opensearch.sql.expression.function.udf.ToNumberFunction;
import org.opensearch.sql.expression.function.udf.ToStringFunction;
Expand Down Expand Up @@ -421,6 +427,15 @@ public class PPLBuiltinOperators extends ReflectiveSqlOperatorTable {
new NumberToStringFunction().toUDF("NUMBER_TO_STRING");
public static final SqlOperator TONUMBER = new ToNumberFunction().toUDF("TONUMBER");
public static final SqlOperator TOSTRING = new ToStringFunction().toUDF("TOSTRING");

// PPL Convert command functions
public static final SqlOperator AUTO = new AutoConvertFunction().toUDF("AUTO");
public static final SqlOperator NUM = new NumConvertFunction().toUDF("NUM");
public static final SqlOperator RMCOMMA = new RmcommaConvertFunction().toUDF("RMCOMMA");
public static final SqlOperator RMUNIT = new RmunitConvertFunction().toUDF("RMUNIT");
public static final SqlOperator MEMK = new MemkConvertFunction().toUDF("MEMK");
public static final SqlOperator NONE = new NoneConvertFunction().toUDF("NONE");

public static final SqlOperator WIDTH_BUCKET =
new org.opensearch.sql.expression.function.udf.binning.WidthBucketFunction()
.toUDF("WIDTH_BUCKET");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import static org.opensearch.sql.expression.function.BuiltinFunctionName.ASIN;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.ATAN;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.ATAN2;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.AUTO;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.AVG;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.CBRT;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.CEIL;
Expand Down Expand Up @@ -136,6 +137,7 @@
import static org.opensearch.sql.expression.function.BuiltinFunctionName.MAX;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.MD5;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.MEDIAN;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.MEMK;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.MICROSECOND;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.MIN;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.MINSPAN_BUCKET;
Expand All @@ -158,10 +160,12 @@
import static org.opensearch.sql.expression.function.BuiltinFunctionName.MVJOIN;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.MVMAP;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.MVZIP;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.NONE;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.NOT;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.NOTEQUAL;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.NOW;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.NULLIF;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.NUM;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.OR;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.PERCENTILE_APPROX;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.PERIOD_ADD;
Expand All @@ -185,6 +189,8 @@
import static org.opensearch.sql.expression.function.BuiltinFunctionName.REX_OFFSET;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.RIGHT;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.RINT;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.RMCOMMA;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.RMUNIT;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.ROUND;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.RTRIM;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.SCALAR_MAX;
Expand Down Expand Up @@ -983,6 +989,15 @@ void populate() {
registerOperator(INTERNAL_PATTERN_PARSER, PPLBuiltinOperators.PATTERN_PARSER);
registerOperator(TONUMBER, PPLBuiltinOperators.TONUMBER);
registerOperator(TOSTRING, PPLBuiltinOperators.TOSTRING);

// Register PPL Convert command functions
registerOperator(AUTO, PPLBuiltinOperators.AUTO);
registerOperator(NUM, PPLBuiltinOperators.NUM);
registerOperator(RMCOMMA, PPLBuiltinOperators.RMCOMMA);
registerOperator(RMUNIT, PPLBuiltinOperators.RMUNIT);
registerOperator(MEMK, PPLBuiltinOperators.MEMK);
registerOperator(NONE, PPLBuiltinOperators.NONE);

register(
TOSTRING,
(FunctionImp1)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

package org.opensearch.sql.expression.function.udf;

/** PPL auto() conversion function. */
public class AutoConvertFunction extends BaseConversionUDF {

public AutoConvertFunction() {
super("autoConvert");
}
}
Loading
Loading