Skip to content
Open
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

package org.opensearch.sql.common.utils;

import java.util.Collection;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

/**
* Utility class for debugging operations. This class is only for debugging purpose, and not
* intended to be used in production code.
*/
public class DebugUtils {
// Update this to true while you are debugging. (Safe guard to avoid usage in production code. )
private static final boolean IS_DEBUG = false;
private static final Logger logger = LogManager.getLogger(DebugUtils.class);

public static <T> T debug(T obj, String message) {
verifyDebug();
print("### %s: %s (at %s)", message, stringify(obj), getCalledFrom(1));
return obj;
}

public static <T> T debug(T obj) {
verifyDebug();
print("### %s (at %s)", stringify(obj), getCalledFrom(1));
return obj;
}

private static void verifyDebug() {
if (!IS_DEBUG) {
throw new RuntimeException("DebugUtils can be used only for local debugging.");
}
}

private static void print(String format, Object... args) {
logger.info(String.format(format, args));
}

private static String getCalledFrom(int pos) {
RuntimeException e = new RuntimeException();
StackTraceElement item = e.getStackTrace()[pos + 1];
return item.getClassName() + "." + item.getMethodName() + ":" + item.getLineNumber();
}

private static String stringify(Collection<?> items) {
if (items == null) {
return "null";
}

if (items.isEmpty()) {
return "()";
}

String result = items.stream().map(i -> stringify(i)).collect(Collectors.joining(","));

return "(" + result + ")";
}

private static String stringify(Map<?, ?> map) {
if (map == null) {
return "[[null]]";
}

if (map.isEmpty()) {
return "[[EMPTY]]";
}

String result =
map.entrySet().stream()
.map(entry -> entry.getKey() + ": " + stringify(entry.getValue()))
.collect(Collectors.joining(","));
return "{" + result + "}";
}

private static String stringify(Object obj) {
if (obj instanceof Collection) {
return stringify((Collection) obj);
} else if (obj instanceof Map) {
return stringify((Map) obj);
}
return String.valueOf(obj);
}
}
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.common.utils;

import static org.junit.Assert.assertThrows;

import org.junit.Test;

public class DebugUtilsTest {

@Test
public void testDebugThrowsRuntimeException() {
assertThrows(RuntimeException.class, () -> DebugUtils.debug("test", "test message"));
}

@Test
public void testDebugWithoutMessageThrowsRuntimeException() {
assertThrows(RuntimeException.class, () -> DebugUtils.debug("test"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
import org.opensearch.sql.ast.tree.Values;
import org.opensearch.sql.ast.tree.Window;
import org.opensearch.sql.common.antlr.SyntaxCheckException;
import org.opensearch.sql.common.patterns.PatternUtils;
import org.opensearch.sql.data.model.ExprMissingValue;
import org.opensearch.sql.data.type.ExprCoreType;
import org.opensearch.sql.datasource.DataSourceService;
Expand Down Expand Up @@ -953,10 +954,10 @@ private Aggregation analyzePatternsAgg(Patterns node) {
List<UnresolvedExpression> aggExprs =
Stream.of(
new Alias(
"pattern_count",
PatternUtils.PATTERN_COUNT,
new AggregateFunction(BuiltinFunctionName.COUNT.name(), AllFields.of())),
new Alias(
"sample_logs",
PatternUtils.SAMPLE_LOGS,
new AggregateFunction(
BuiltinFunctionName.TAKE.name(),
node.getSourceField(),
Expand Down
39 changes: 39 additions & 0 deletions core/src/main/java/org/opensearch/sql/ast/AstNodeUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

package org.opensearch.sql.ast;

import lombok.experimental.UtilityClass;
import org.opensearch.sql.ast.expression.Let;
import org.opensearch.sql.ast.expression.subquery.SubqueryExpression;

/** Utility class for AST node operations shared among visitor classes. */
@UtilityClass
public class AstNodeUtils {

/**
* Checks if an AST node contains a subquery expression.
*
* @param expr The AST node to check
* @return true if the node or any of its children contains a subquery expression
*/
public static boolean containsSubqueryExpression(Node expr) {
if (expr == null) {
return false;
}
if (expr instanceof SubqueryExpression) {
return true;
}
if (expr instanceof Let l) {
return containsSubqueryExpression(l.getExpression());
}
for (Node child : expr.getChild()) {
if (containsSubqueryExpression(child)) {
return true;
}
}
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

package org.opensearch.sql.ast.analysis;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.Getter;
import org.opensearch.sql.ast.tree.Relation;
import org.opensearch.sql.ast.tree.UnresolvedPlan;

/** Context for field resolution using stack-based traversal. */
public class FieldResolutionContext {

@Getter private final Map<UnresolvedPlan, FieldResolutionResult> results;
private final Deque<FieldResolutionResult> requirementsStack;

public FieldResolutionContext() {
this.results = new IdentityHashMap<>();
this.requirementsStack = new ArrayDeque<>();
this.requirementsStack.push(new FieldResolutionResult(Set.of(), "*"));
}

public void pushRequirements(FieldResolutionResult result) {
requirementsStack.push(result);
}

public FieldResolutionResult popRequirements() {
return requirementsStack.pop();
}

public FieldResolutionResult getCurrentRequirements() {
if (requirementsStack.isEmpty()) {
throw new RuntimeException("empty stack");
} else {
return requirementsStack.peek();
}
}

public void setResult(UnresolvedPlan relation, FieldResolutionResult result) {
results.put(relation, result);
}

public Set<Relation> getRelations() {
return results.keySet().stream()
.filter(k -> k instanceof Relation)
.map(k -> (Relation) k)
.collect(Collectors.toSet());
}

public static String mergeWildcardPatterns(Set<String> patterns) {
if (patterns == null || patterns.isEmpty()) {
return null;
}
if (patterns.size() == 1) {
return patterns.iterator().next();
}
return String.join(" | ", patterns.stream().sorted().toList());
}

@Override
public String toString() {
return "FieldResolutionContext{relationResults=" + results + "}";
}
}
Loading
Loading