Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge physical plan evaluator and basic planner to main #612

Merged
merged 17 commits into from
May 27, 2022
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
26 changes: 23 additions & 3 deletions docs/user/BuiltInFunctions.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,6 @@ CAST(<<'a', 'b'>> AS bag) -- <<'a', 'b'>> (REPL does not display << >> and comma

### CHAR_LENGTH, CHARACTER_LENGTH



Counts the number of characters in the specified string, where 'character' is defined as a single unicode code point.

*Note:* `CHAR_LENGTH` and `CHARACTER_LENGTH` are synonyms.
Expand Down Expand Up @@ -455,9 +453,31 @@ EXTRACT(TIMEZONE_MINUTE FROM TIME WITH TIME ZONE '23:12:59-08:30') -- -30
```
*Note* that `timezone_hour` and `timezone_minute` are **not supported** for `DATE` and `TIME` (without time zone) type.

### `FILTER_DISTINCT`

Signature
: `FILTER_DISTINCT: Container -> Bag`

Header
: `FILTER_DISTINCT(c)`

Purpose
: Returns a bag of distinct values contained within a bag, list, sexp, or struct. If the container is a struct,
the field names are not considered.

Examples
:

```sql
FILTER_DISTINCT([0, 0, 1]) -- <<0, 1>>
FILTER_DISTINCT(<<0, 0, 1>>) -- <<0, 1>>
FILTER_DISTINCT(SEXP(0, 0, 1)) -- <<0, 1>>
FILTER_DISTINCT({'a': 0, 'b': 0, 'c': 1}) -- <<0, 1>>
```

### LOWER

Given a string convert all upper case characters to lower case characters.
Given a string convert all upper case characters to lower case characters.

Signature
: `LOWER: String -> String`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class ParserErrorExample(out: PrintStream) : Example(out) {

throw Exception("ParserException was not thrown")
} catch (e: ParserException) {
val errorContext = e.errorContext!!
val errorContext = e.errorContext

val errorInformation = "errorCode: ${e.errorCode}" +
"\nLINE_NUMBER: ${errorContext[Property.LINE_NUMBER]}" +
Expand Down
309 changes: 309 additions & 0 deletions lang/resources/org/partiql/type-domains/partiql.ion

Large diffs are not rendered by default.

11 changes: 8 additions & 3 deletions lang/src/org/partiql/lang/CompilerPipeline.kt
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import org.partiql.lang.types.StaticType
import org.partiql.lang.util.interruptibleFold

/**
* Contains all of the information needed for processing steps.
* Contains all information needed for processing steps.
*/
data class StepContext(
/** The instance of [ExprValueFactory] that is used by the pipeline. */
Expand Down Expand Up @@ -102,6 +102,11 @@ interface CompilerPipeline {
*/
val procedures: @JvmSuppressWildcards Map<String, StoredProcedure>

/**
* The configured global type bindings.
*/
val globalTypeBindings: Bindings<StaticType>?

/** Compiles the specified PartiQL query using the configured parser. */
fun compile(query: String): Expression

Expand Down Expand Up @@ -205,7 +210,7 @@ interface CompilerPipeline {
fun build(): CompilerPipeline {
val compileOptionsToUse = compileOptions ?: CompileOptions.standard()

when (compileOptionsToUse.thunkReturnTypeAssertions) {
when (compileOptionsToUse.thunkOptions.thunkReturnTypeAssertions) {
ThunkReturnTypeAssertions.DISABLED -> { /* intentionally blank */ }
ThunkReturnTypeAssertions.ENABLED -> {
check(this.globalTypeBindings != null) {
Expand Down Expand Up @@ -244,7 +249,7 @@ internal class CompilerPipelineImpl(
override val customDataTypes: List<CustomType>,
override val procedures: Map<String, StoredProcedure>,
private val preProcessingSteps: List<ProcessingStep>,
private val globalTypeBindings: Bindings<StaticType>?
override val globalTypeBindings: Bindings<StaticType>?
) : CompilerPipeline {

private val compiler = EvaluatingCompiler(
Expand Down
21 changes: 12 additions & 9 deletions lang/src/org/partiql/lang/SqlException.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import org.partiql.lang.errors.ErrorCode
import org.partiql.lang.errors.Property
import org.partiql.lang.errors.PropertyValueMap
import org.partiql.lang.errors.UNKNOWN
import org.partiql.lang.util.propertyValueMapOf

/**
* General exception class for the interpreter.
Expand All @@ -33,20 +34,18 @@ import org.partiql.lang.errors.UNKNOWN
*
* @param message the message for this exception
* @param errorCode the error code for this exception
* @param propertyValueMap context for this error
* @param errorContextArg context for this error, includes details like line & character offsets, among others.
* TODO: https://github.com/partiql/partiql-lang-kotlin/issues/616
* @param cause for this exception
*
* @constructor a custom error [message], the [errorCode], error context as a [propertyValueMap] and optional [cause] creates an
* [SqlException]. This is the constructor for the second configuration explained above.
*
*/
open class SqlException(
override var message: String,
val errorCode: ErrorCode,
val errorContext: PropertyValueMap? = null,
errorContextArg: PropertyValueMap? = null,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be nullable (i.e. what does a nullable errorContextArg define)? The errorContext definition on L49 could be redundant since a null errorContextArg will default to an empty property value map.

Copy link
Member Author

@dlurton dlurton May 27, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's nullable because it always has been nullable. Changing this to an argument and defaulting the errorContext property to an empty map if errorContextArg is null was a way of simplifying error handling code that reads context properties without changing all of the call sites of this constructor (a few direct, but many indirect) to conform its new signature, which would have resulted in many more noisy diffs in this series of PRs. If you want, I can undo this change in another commit against this PR, but I would consider making this argument non-nullable to be out of scope of this work.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok got it. I don't think it needs to be addressed in this PR. Could you make an issue and link here regarding making errorContextArg non-nullable?

cause: Throwable? = null
) :
RuntimeException(message, cause) {
) : RuntimeException(message, cause) {

val errorContext: PropertyValueMap = errorContextArg ?: propertyValueMapOf()

/**
* Indicates if this exception is due to an internal error or not.
Expand Down Expand Up @@ -81,7 +80,7 @@ open class SqlException(
*
* * ErrorCategory is one of `Lexer Error`, `Parser Error`, `Runtime Error`
* * ErrorLocation is the line and column where the error occurred
* * Errormessatge is the **generated** error message
* * ErrorMessage is the **generated** error message
*
*
* TODO: Prepend to the auto-generated message the file name.
Expand All @@ -90,6 +89,10 @@ open class SqlException(
fun generateMessage(): String =
"${errorCategory(errorCode)}: ${errorLocation(errorContext)}: ${errorMessage(errorCode, errorContext)}"

/** Same as [generateMessage] but without the location. */
fun generateMessageNoLocation(): String =
"${errorCategory(errorCode)}: ${errorMessage(errorCode, errorContext)}"

private fun errorMessage(errorCode: ErrorCode?, propertyValueMap: PropertyValueMap?): String =
errorCode?.getErrorMessage(propertyValueMap) ?: UNKNOWN

Expand Down
2 changes: 1 addition & 1 deletion lang/src/org/partiql/lang/ast/passes/SemanticException.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class SemanticException(
constructor(err: Problem, cause: Throwable? = null) :
this(
message = "",
errorCode = ErrorCode.SEMANTIC_INFERENCER_ERROR,
errorCode = ErrorCode.SEMANTIC_PROBLEM,
errorContext = propertyValueMapOf(
Property.LINE_NUMBER to err.sourceLocation.lineNum,
Property.COLUMN_NUMBER to err.sourceLocation.charOffset,
Expand Down
38 changes: 26 additions & 12 deletions lang/src/org/partiql/lang/domains/util.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.partiql.lang.domains

import com.amazon.ionelement.api.IonElement
import com.amazon.ionelement.api.MetaContainer
import com.amazon.ionelement.api.emptyMetaContainer
import com.amazon.ionelement.api.metaContainerOf
Expand All @@ -14,6 +15,19 @@ import org.partiql.lang.eval.BindingCase
fun PartiqlAst.Builder.id(name: String) =
id(name, caseInsensitive(), unqualified())

// TODO: once https://github.com/partiql/partiql-ir-generator/issues/6 has been completed, we can delete this.
fun PartiqlLogical.Builder.id(name: String) =
id(name, caseInsensitive(), unqualified())

// TODO: once https://github.com/partiql/partiql-ir-generator/issues/6 has been completed, we can delete this.
fun PartiqlLogical.Builder.pathExpr(exp: PartiqlLogical.Expr) =
pathExpr(exp, caseInsensitive())

// Workaround for a bug in PIG that is fixed in its next release:
// https://github.com/partiql/partiql-ir-generator/issues/41
fun List<IonElement>.asAnyElement() =
this.map { it.asAnyElement() }

val MetaContainer.staticType: StaticTypeMeta? get() = this[StaticTypeMeta.TAG] as StaticTypeMeta?

/** Constructs a container with the specified metas. */
Expand Down Expand Up @@ -60,17 +74,17 @@ fun PartiqlAst.CaseSensitivity.toBindingCase(): BindingCase = when (this) {
}

/**
* Returns the [SourceLocationMeta] as an error context if the [SourceLocationMeta.TAG] exists in the passed
* [metaContainer]. Otherwise, returns an empty map.
* Converts a [PartiqlLogical.CaseSensitivity] to a [BindingCase].
*/
fun errorContextFrom(metaContainer: MetaContainer?): PropertyValueMap {
if (metaContainer == null) {
return PropertyValueMap()
}
val location = metaContainer[SourceLocationMeta.TAG] as? SourceLocationMeta
return if (location != null) {
org.partiql.lang.eval.errorContextFrom(location)
} else {
PropertyValueMap()
}
fun PartiqlLogical.CaseSensitivity.toBindingCase(): BindingCase = when (this) {
is PartiqlLogical.CaseSensitivity.CaseInsensitive -> BindingCase.INSENSITIVE
is PartiqlLogical.CaseSensitivity.CaseSensitive -> BindingCase.SENSITIVE
}

/**
* Converts a [PartiqlLogical.CaseSensitivity] to a [BindingCase].
*/
fun PartiqlPhysical.CaseSensitivity.toBindingCase(): BindingCase = when (this) {
is PartiqlPhysical.CaseSensitivity.CaseInsensitive -> BindingCase.INSENSITIVE
is PartiqlPhysical.CaseSensitivity.CaseSensitive -> BindingCase.SENSITIVE
}
8 changes: 1 addition & 7 deletions lang/src/org/partiql/lang/errors/ErrorCode.kt
Original file line number Diff line number Diff line change
Expand Up @@ -647,7 +647,7 @@ enum class ErrorCode(
"got: ${errorContext?.get(Property.ACTUAL_ARGUMENT_TYPES) ?: UNKNOWN}"
},

SEMANTIC_INFERENCER_ERROR(
SEMANTIC_PROBLEM(
ErrorCategory.SEMANTIC,
LOCATION + setOf(Property.MESSAGE),
""
Expand Down Expand Up @@ -980,12 +980,6 @@ enum class ErrorCode(
ErrorBehaviorInPermissiveMode.RETURN_MISSING
),

EVALUATOR_SQL_EXCEPTION(
ErrorCategory.EVALUATOR,
LOCATION,
"SQL exception"
),

EVALUATOR_COUNT_START_NOT_ALLOWED(
ErrorCategory.EVALUATOR,
LOCATION,
Expand Down
6 changes: 3 additions & 3 deletions lang/src/org/partiql/lang/eval/CompileOptions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -141,14 +141,14 @@ enum class ThunkReturnTypeAssertions {
* @param defaultTimezoneOffset Default timezone offset to be used when TIME WITH TIME ZONE does not explicitly
* specify the time zone. Defaults to [ZoneOffset.UTC]
*/
@Suppress("DataClassPrivateConstructor")
data class CompileOptions private constructor (
val undefinedVariable: UndefinedVariableBehavior,
val projectionIteration: ProjectionIterationBehavior = ProjectionIterationBehavior.FILTER_MISSING,
val visitorTransformMode: VisitorTransformMode = VisitorTransformMode.DEFAULT,
val thunkOptions: ThunkOptions = ThunkOptions.standard(),
val typingMode: TypingMode = TypingMode.LEGACY,
val typedOpBehavior: TypedOpBehavior = TypedOpBehavior.LEGACY,
val thunkReturnTypeAssertions: ThunkReturnTypeAssertions = ThunkReturnTypeAssertions.DISABLED,
val defaultTimezoneOffset: ZoneOffset = ZoneOffset.UTC
) {

Expand Down Expand Up @@ -177,7 +177,7 @@ data class CompileOptions private constructor (
fun build(options: CompileOptions, block: Builder.() -> Unit) = Builder(options).apply(block).build()

/**
* Creates a [CompileOptions] instance with the standard values.
* Creates a [CompileOptions] instance with the standard values for use by the legacy AST compiler.
*/
@JvmStatic
fun standard() = Builder().build()
Expand All @@ -194,7 +194,7 @@ data class CompileOptions private constructor (
fun typingMode(value: TypingMode) = set { copy(typingMode = value) }
fun typedOpBehavior(value: TypedOpBehavior) = set { copy(typedOpBehavior = value) }
fun thunkOptions(value: ThunkOptions) = set { copy(thunkOptions = value) }
fun evaluationTimeTypeChecks(value: ThunkReturnTypeAssertions) = set { copy(thunkReturnTypeAssertions = value) }
fun thunkOptions(build: ThunkOptions.Builder.() -> Unit) = set { copy(thunkOptions = ThunkOptions.build(build)) }
fun defaultTimezoneOffset(value: ZoneOffset) = set { copy(defaultTimezoneOffset = value) }

private inline fun set(block: CompileOptions.() -> CompileOptions): Builder {
Expand Down
23 changes: 21 additions & 2 deletions lang/src/org/partiql/lang/eval/EvaluatingCompiler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,25 @@ import java.util.TreeSet
import java.util.regex.Pattern
import kotlin.Comparator

/**
* A thunk with no parameters other than the current environment.
*
* See https://en.wikipedia.org/wiki/Thunk
*
* This name was chosen because it is a thunk that accepts an instance of `Environment`.
*/
private typealias ThunkEnv = Thunk<Environment>

/**
* A thunk taking a single [T] argument and the current environment.
*
* See https://en.wikipedia.org/wiki/Thunk
*
* This name was chosen because it is a thunk that accepts an instance of `Environment` and an [ExprValue] as
* its arguments.
*/
private typealias ThunkEnvValue<T> = ThunkValue<Environment, T>

/**
* A basic compiler that converts an instance of [PartiqlAst] to an [Expression].
*
Expand Down Expand Up @@ -115,7 +134,7 @@ internal class EvaluatingCompiler(
private val compileOptions: CompileOptions = CompileOptions.standard()
) {
private val errorSignaler = compileOptions.typingMode.createErrorSignaler(valueFactory)
private val thunkFactory = compileOptions.typingMode.createThunkFactory(compileOptions, valueFactory)
private val thunkFactory = compileOptions.typingMode.createThunkFactory<Environment>(compileOptions.thunkOptions, valueFactory)

private val compilationContextStack = Stack<CompilationContext>()

Expand Down Expand Up @@ -3048,7 +3067,7 @@ private class SingleProjectionElement(val name: ExprValue, val thunk: ThunkEnv)
*/
private class MultipleProjectionElement(val thunks: List<ThunkEnv>) : ProjectionElement()

private val MetaContainer.sourceLocationMeta get() = this[SourceLocationMeta.TAG] as? SourceLocationMeta
internal val MetaContainer.sourceLocationMeta get() = this[SourceLocationMeta.TAG] as? SourceLocationMeta

private fun StaticType.getTypes() = when (val flattened = this.flatten()) {
is AnyOfType -> flattened.types
Expand Down
4 changes: 4 additions & 0 deletions lang/src/org/partiql/lang/eval/Exceptions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ fun fillErrorContext(errorContext: PropertyValueMap, metaContainer: MetaContaine
}
}

/**
* Returns the [SourceLocationMeta] as an error context if the [SourceLocationMeta.TAG] exists in the passed
* [metaContainer]. Otherwise, returns an empty map.
*/
fun errorContextFrom(metaContainer: MetaContainer?): PropertyValueMap {
if (metaContainer == null) {
return PropertyValueMap()
Expand Down
Loading