Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
10 changes: 10 additions & 0 deletions core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ dependencies {
testImplementation(libs.guava)
testImplementation(libs.bundles.jackson)
testImplementation(libs.classgraph)
testImplementation(libs.json.schema.validator)

testImplementation(libs.junit.jupiter)
testRuntimeOnly(libs.junit.platform.launcher)
Expand Down Expand Up @@ -243,6 +244,15 @@ tasks.named<ProcessResources>("processResources") {
from("../substrait/extensions") { into("substrait/extensions") }
}

tasks.named<ProcessResources>("processTestResources") {
// Dialect schema, used to validate dialects produced by the Dialect model in tests.
from("../substrait/text") { into("substrait/text") }
// A real-world dialect to exercise parsing against.
from("../spark/spark_dialect.yaml") { into("dialect") }
// Per-section dialect fixtures published by the substrait spec.
from("../substrait/dialects/tests") { into("dialect/tests") }
}

project.configure<IdeaModel> {
module {
resourceDirs.addAll(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.substrait.dialect;

/** Permissible failure options for {@code CAST} expressions. */
public enum CastFailureOption {
/** Return null when a cast fails. */
RETURN_NULL,
/** Throw an exception when a cast fails. */
THROW_EXCEPTION
}
9 changes: 9 additions & 0 deletions core/src/main/java/io/substrait/dialect/DdlWriteType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.substrait.dialect;

/** Operable object types for {@code DDL} relations. */
public enum DdlWriteType {
/** A named DDL object. */
NAMED_OBJECT,
/** An extension-defined DDL object. */
EXTENSION_OBJECT
}
192 changes: 192 additions & 0 deletions core/src/main/java/io/substrait/dialect/Dialect.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
package io.substrait.dialect;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.immutables.value.Value;

/**
* A Substrait dialect: a description of what a target execution system supports — which types,
* relations, expressions and functions, along with per-feature configuration (join types, set
* operations, cast failure behavior, ...). The model maps the schema published at {@code
* substrait/text/dialect_schema.yaml}.
*
* <p>Build a dialect with {@link #builder()} and the sibling entry types and serialize it with
* {@link #toYaml(Dialect)}; parse one with {@link #load(String)} and friends.
*/
@JsonDeserialize(as = ImmutableDialect.class)
@JsonSerialize(as = ImmutableDialect.class)
@Value.Immutable
public abstract class Dialect {

/**
* The name of the dialect, if any.
*
* @return the optional dialect name
*/
public abstract Optional<String> name();

/**
* Free-form metadata associated with the dialect, if any.
*
* @return the optional metadata
*/
public abstract Optional<Map<String, Object>> metadata();

/**
* The dependencies referenced by the dialect, keyed by alias.
*
* @return the dependency aliases mapped to their URIs
*/
public abstract Map<String, String> dependencies();

/**
* The types supported by the dialect.
*
* @return the supported types
*/
@JsonProperty("supported_types")
public abstract List<SupportedType> supportedTypes();

/**
* The relations supported by the dialect.
*
* @return the supported relations
*/
@JsonProperty("supported_relations")
public abstract List<SupportedRelation> supportedRelations();

/**
* The expressions supported by the dialect.
*
* @return the supported expressions
*/
@JsonProperty("supported_expressions")
public abstract List<SupportedExpression> supportedExpressions();

/**
* The scalar functions supported by the dialect.
*
* @return the supported scalar functions
*/
@JsonProperty("supported_scalar_functions")
public abstract List<DialectFunction> supportedScalarFunctions();

/**
* The aggregate functions supported by the dialect.
*
* @return the supported aggregate functions
*/
@JsonProperty("supported_aggregate_functions")
public abstract List<DialectFunction> supportedAggregateFunctions();

/**
* The window functions supported by the dialect.
*
* @return the supported window functions
*/
@JsonProperty("supported_window_functions")
public abstract List<DialectFunction> supportedWindowFunctions();

/**
* The execution-behavior configuration of the dialect, if any.
*
* @return the optional execution behavior
*/
@JsonProperty("supported_execution_behavior")
public abstract Optional<ExecutionBehavior> supportedExecutionBehavior();

/**
* Creates a builder for {@link Dialect}.
*
* @return a new builder
*/
public static ImmutableDialect.Builder builder() {
return ImmutableDialect.builder();
}

/**
* Parse a dialect from YAML content.
*
* @param content the YAML content
* @return the parsed dialect
*/
public static Dialect load(String content) {
try {
return DialectJsonSupport.MAPPER.readValue(content, Dialect.class);
} catch (IOException e) {
throw new IllegalStateException(e);
}
}

/**
* Parse a dialect from a YAML stream. The caller retains ownership of the stream; it is not
* closed by this method.
*
* @param stream the YAML input stream
* @return the parsed dialect
*/
public static Dialect load(InputStream stream) {
try {
return DialectJsonSupport.MAPPER
.readerFor(Dialect.class)
.without(JsonParser.Feature.AUTO_CLOSE_SOURCE)
.readValue(stream);
} catch (IOException e) {
throw new IllegalStateException(e);
}
}

/**
* Parse a dialect from a YAML file on disk.
*
* @param path the path to the YAML file
* @return the parsed dialect
*/
public static Dialect loadFromFile(Path path) {
try {
return DialectJsonSupport.MAPPER.readValue(path.toFile(), Dialect.class);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

/**
* Parse a dialect from a classpath resource.
*
* @param resourcePath the classpath resource path
* @return the parsed dialect
*/
public static Dialect loadResource(String resourcePath) {
try (InputStream stream = Dialect.class.getResourceAsStream(resourcePath)) {
if (stream == null) {
throw new IllegalArgumentException("Dialect resource not found: " + resourcePath);
}
return load(stream);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

/**
* Serialize a dialect to YAML.
*
* @param dialect the dialect to serialize
* @return the YAML representation
*/
public static String toYaml(Dialect dialect) {
try {
return DialectJsonSupport.MAPPER.writeValueAsString(dialect);
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
}
76 changes: 76 additions & 0 deletions core/src/main/java/io/substrait/dialect/DialectFunction.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package io.substrait.dialect;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.immutables.value.Value;

/** A function supported by a dialect, referencing a dependency alias. */
@JsonDeserialize(as = ImmutableDialectFunction.class)
@JsonSerialize(as = ImmutableDialectFunction.class)
@Value.Immutable
public abstract class DialectFunction {
/**
* Dependency (alias) in which the function is declared.
*
* @return the dependency alias
*/
public abstract String source();

/**
* The name of the function as declared in the extension it is defined in.
*
* @return the function name
*/
public abstract String name();

/**
* Free-form metadata associated with the function, if any.
*
* @return the optional metadata
*/
public abstract Optional<Map<String, Object>> metadata();

/**
* System-specific metadata for the function, if any.
*
* @return the optional system metadata
*/
@JsonProperty("system_metadata")
public abstract Optional<SystemFunctionMetadata> systemMetadata();

/**
* The options required when invoking the function, if any.
*
* @return the optional required options
*/
@JsonProperty("required_options")
public abstract Optional<Map<String, Object>> requiredOptions();

/**
* One or more implementations supported by this function, identified by argument signatures.
*
* @return the supported implementation signatures
*/
@JsonProperty("supported_impls")
public abstract List<String> supportedImpls();

/**
* The variadic argument bounds of the function, if any.
*
* @return the optional variadic bounds
*/
public abstract Optional<Variadic> variadic();

/**
* Creates a builder for {@link DialectFunction}.
*
* @return a new builder
*/
public static ImmutableDialectFunction.Builder builder() {
return ImmutableDialectFunction.builder();
}
}
Loading
Loading