From de14199441542e8c591533b86accf2cef12d5607 Mon Sep 17 00:00:00 2001 From: "R. C. Howell" Date: Mon, 24 Jun 2024 09:49:57 -0700 Subject: [PATCH 1/9] Reset partiql-spi closer to main branch --- .../main/kotlin/org/partiql/planner/catalog/Name.kt | 13 +++++++++++++ .../kotlin/org/partiql/spi/connector/Connector.kt | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/catalog/Name.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/catalog/Name.kt index 747ee87618..5f1d029f55 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/catalog/Name.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/catalog/Name.kt @@ -1,4 +1,17 @@ package org.partiql.planner.catalog +/* + * Copyright Amazon.com, Inc. or its affiliates. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at: + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + */ import java.util.Spliterator import java.util.function.Consumer diff --git a/partiql-spi/src/main/kotlin/org/partiql/spi/connector/Connector.kt b/partiql-spi/src/main/kotlin/org/partiql/spi/connector/Connector.kt index a7b0ab6ba5..ac2abba834 100644 --- a/partiql-spi/src/main/kotlin/org/partiql/spi/connector/Connector.kt +++ b/partiql-spi/src/main/kotlin/org/partiql/spi/connector/Connector.kt @@ -18,7 +18,7 @@ import com.amazon.ionelement.api.StructElement import org.partiql.eval.bindings.Bindings /** - * A mechanism by which PartiQL can access bindings and metadata for a namespace (catalog). + * A mechanism by which PartiQL can access bindings and catalog metadata. */ public interface Connector { From e9cd4fd61548b6ceea5be8a828a7c6f8d0721ad2 Mon Sep 17 00:00:00 2001 From: "R. C. Howell" Date: Thu, 27 Jun 2024 10:31:44 -0700 Subject: [PATCH 2/9] Adds v1 connector interfaces over bindings and metadata --- .../org/partiql/planner/catalog/Name.kt | 37 -------- .../org/partiql/planner/metadata/Metadata.kt | 12 +++ .../org/partiql/planner/metadata/Name.kt | 26 ++++++ .../org/partiql/planner/metadata/Namespace.kt | 61 +++++++++++++ .../org/partiql/planner/metadata/Routine.kt | 91 +++++++++++++++++++ .../org/partiql/planner/metadata/Table.kt | 40 ++++++++ 6 files changed, 230 insertions(+), 37 deletions(-) delete mode 100644 partiql-planner/src/main/kotlin/org/partiql/planner/catalog/Name.kt create mode 100644 partiql-planner/src/main/kotlin/org/partiql/planner/metadata/Metadata.kt create mode 100644 partiql-planner/src/main/kotlin/org/partiql/planner/metadata/Name.kt create mode 100644 partiql-planner/src/main/kotlin/org/partiql/planner/metadata/Namespace.kt create mode 100644 partiql-planner/src/main/kotlin/org/partiql/planner/metadata/Routine.kt create mode 100644 partiql-planner/src/main/kotlin/org/partiql/planner/metadata/Table.kt diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/catalog/Name.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/catalog/Name.kt deleted file mode 100644 index 5f1d029f55..0000000000 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/catalog/Name.kt +++ /dev/null @@ -1,37 +0,0 @@ -package org.partiql.planner.catalog -/* - * Copyright Amazon.com, Inc. or its affiliates. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at: - * - * http://aws.amazon.com/apache2.0/ - * - * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific - * language governing permissions and limitations under the License. - */ - -import java.util.Spliterator -import java.util.function.Consumer - -/** - * Thin wrapper over a list of strings. - */ -public data class Name(public val steps: List) : Iterable { - - public companion object { - - @JvmStatic - public fun of(vararg steps: String): Name = Name(steps.toList()) - } - - public operator fun get(index: Int): String = steps[index] - - override fun forEach(action: Consumer?): Unit = steps.forEach(action) - - override fun iterator(): Iterator = steps.iterator() - - override fun spliterator(): Spliterator = steps.spliterator() -} diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/metadata/Metadata.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/metadata/Metadata.kt new file mode 100644 index 0000000000..b525c24875 --- /dev/null +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/metadata/Metadata.kt @@ -0,0 +1,12 @@ +package org.partiql.planner.metadata + +/** + * Top-level metadata interface for access to object descriptors. + */ +public interface Metadata { + + /** + * The root namespace. + */ + public fun getNamespace(): Namespace +} diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/metadata/Name.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/metadata/Name.kt new file mode 100644 index 0000000000..9cf27077ab --- /dev/null +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/metadata/Name.kt @@ -0,0 +1,26 @@ +package org.partiql.planner.metadata + +import java.util.Spliterator +import java.util.function.Consumer + +/** + * Thin wrapper over a list of strings. + * + * @property steps + */ +public data class Name(public val steps: List) : Iterable { + + public companion object { + + @JvmStatic + public fun of(vararg steps: String): Name = Name(steps.toList()) + } + + public operator fun get(index: Int): String = steps[index] + + override fun forEach(action: Consumer?): Unit = steps.forEach(action) + + override fun iterator(): Iterator = steps.iterator() + + override fun spliterator(): Spliterator = steps.spliterator() +} \ No newline at end of file diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/metadata/Namespace.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/metadata/Namespace.kt new file mode 100644 index 0000000000..750c43eba0 --- /dev/null +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/metadata/Namespace.kt @@ -0,0 +1,61 @@ +package org.partiql.planner.metadata + +/** + * A [Namespace] is a namespace for the following objects; top-level namespaces are called "Catalogs". + * + * . Tables + * . Base + * . View + * . Index + * . Routines + * . Functions + * . Procedures + * . Namespaces + * . Types + * + * See, https://github.com/apache/calcite/blob/main/core/src/main/java/org/apache/calcite/schema/Schema.java + */ +public interface Namespace { + + /** + * The [Namespace] name. + */ + public fun getName(): String + + /** + * Get a table by name. + * + * @param name The case-sensitive [Table] name. + * @return The [Table] or null if not found. + */ + public fun getTable(name: String): Table? = null + + /** + * Get a function's variants by name. + * + * @param name The case-sensitive [Routine] name. + * @return A collection of all [Routine]s in the current namespace with this name. + */ + public fun getFunctions(name: String): Collection = DEFAULT_FUNCTIONS + + /** + * Get a sub-namespace by name. + * + * @param name + * @return + */ + public fun getNamespace(name: String): Namespace? = null + + /** + * Get all sub-namespaces. + */ + public fun getNamespaces(): Collection = DEFAULT_SCOPES + + /** + * Memoized defaults. + */ + private companion object { + val DEFAULT_FUNCTIONS = emptyList() + val DEFAULT_SCOPES = emptyList() + } +} diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/metadata/Routine.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/metadata/Routine.kt new file mode 100644 index 0000000000..a58453aa9a --- /dev/null +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/metadata/Routine.kt @@ -0,0 +1,91 @@ +package org.partiql.planner.metadata + +import org.partiql.types.PType + +/** + * A [Routine] is a PartiQL-routine callable from an expression context. + */ +public sealed interface Routine { + + /** + * The function name. Required. + */ + public fun getName(): String + + /** + * The formal argument definitions. Optional. + */ + public fun getParameters(): Array = DEFAULT_PARAMETERS + + /** + * The function return type. Required. + */ + public fun getReturnType(): PType.Kind + + /** + * Represents an SQL row-value expression call. + */ + public interface Operator : Routine { + public fun getSymbol(): String + public fun getLHS(): PType.Kind? + public fun getRHS(): PType.Kind + } + + /** + * Represents an SQL row-value expression call. + */ + public interface Scalar : Routine { + + /** + * Additional function properties useful for planning. Optional. + */ + public fun getProperties(): Properties = DEFAULT_PROPERTIES + } + + /** + * Represents an SQL table-value expression call. + */ + public interface Aggregation : Routine + + /** + * [Parameter] is a formal argument's definition. + * + * @property name + * @property type + */ + public data class Parameter( + @JvmField public val name: String, + @JvmField public val type: PType.Kind, + ) + + /** + * PartiQL-function properties. + * + * @property isNullCall + */ + public data class Properties( + @JvmField public val isNullCall: Boolean = false, + ) + + /** + * Memoized defaults. + */ + public companion object { + + private val DEFAULT_PARAMETERS = emptyArray() + private val DEFAULT_PROPERTIES = Properties(isNullCall = true) + + @JvmOverloads + public fun scalar( + name: String, + parameters: Collection, + returnType: PType.Kind, + properties: Properties = DEFAULT_PROPERTIES, + ): Scalar = object : Scalar { + override fun getName(): String = name + override fun getParameters(): Array = parameters.toTypedArray() + override fun getReturnType(): PType.Kind = returnType + override fun getProperties(): Properties = properties + } + } +} diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/metadata/Table.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/metadata/Table.kt new file mode 100644 index 0000000000..8022f014d9 --- /dev/null +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/metadata/Table.kt @@ -0,0 +1,40 @@ +package org.partiql.planner.metadata + +import org.partiql.types.PType + +/** + * In PartiQL, a [Table] can take on any shape and is not necessarily rows+columns. + * + * From Calcite, + * + * ''' + * Note that a table does not know its name. It is in fact possible for + * a table to be used more than once, perhaps under multiple names or under + * multiple schemas. (Compare with the i-node concept + * in the UNIX filesystem.) + * ''' + * + * See, https://github.com/apache/calcite/blob/main/core/src/main/java/org/apache/calcite/schema/Table.java + */ +public interface Table { + + /** + * The table's kind. + */ + public fun getKind(): Kind = Kind.TABLE + + /** + * The table's schema. + */ + public fun getSchema(): PType = PType.typeDynamic() + + /** + * A [Table] can be one of several [Kind]s. + */ + public enum class Kind { + TABLE, + VIEW, + INDEX, + OTHER, + } +} \ No newline at end of file From bfe02ed619fe2cafe2d34b336f826c66b9cfd2d2 Mon Sep 17 00:00:00 2001 From: "R. C. Howell" Date: Thu, 27 Jun 2024 11:57:24 -0700 Subject: [PATCH 3/9] Updates memory plugin to v1 interfaces --- .../org/partiql/spi/connector/Connector.kt | 4 +- plugins/partiql-memory/README.md | 45 ++-- .../partiql/plugins/memory/MemoryBindings.kt | 15 -- .../partiql/plugins/memory/MemoryCatalog.kt | 225 ++++-------------- .../plugins/memory/MemoryCatalogBuilder.kt | 81 ------- .../partiql/plugins/memory/MemoryConnector.kt | 42 ++-- .../partiql/plugins/memory/MemoryMetadata.kt | 24 -- .../partiql/plugins/memory/MemoryNamespace.kt | 39 +++ .../partiql/plugins/memory/MemoryObject.kt | 32 --- .../partiql/plugins/memory/MemoryPlugin.kt | 5 +- .../org/partiql/plugins/memory/MemoryTable.kt | 55 +++++ .../plugins/memory/MemoryCatalogTest.kt | 111 --------- 12 files changed, 172 insertions(+), 506 deletions(-) delete mode 100644 plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryBindings.kt delete mode 100644 plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryCatalogBuilder.kt delete mode 100644 plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryMetadata.kt create mode 100644 plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryNamespace.kt delete mode 100644 plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryObject.kt create mode 100644 plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryTable.kt delete mode 100644 plugins/partiql-memory/src/test/kotlin/org/partiql/plugins/memory/MemoryCatalogTest.kt diff --git a/partiql-spi/src/main/kotlin/org/partiql/spi/connector/Connector.kt b/partiql-spi/src/main/kotlin/org/partiql/spi/connector/Connector.kt index ac2abba834..4fc51c7b8b 100644 --- a/partiql-spi/src/main/kotlin/org/partiql/spi/connector/Connector.kt +++ b/partiql-spi/src/main/kotlin/org/partiql/spi/connector/Connector.kt @@ -15,7 +15,9 @@ package org.partiql.spi.connector import com.amazon.ionelement.api.StructElement +import com.amazon.ionelement.api.emptyIonStruct import org.partiql.eval.bindings.Bindings +import org.partiql.planner.metadata.Metadata /** * A mechanism by which PartiQL can access bindings and catalog metadata. @@ -48,6 +50,6 @@ public interface Connector { * @param config * @return */ - public fun create(config: StructElement? = null): Connector + public fun create(config: StructElement = emptyIonStruct()): Connector } } diff --git a/plugins/partiql-memory/README.md b/plugins/partiql-memory/README.md index e26a74f27a..c3633cf4fa 100644 --- a/plugins/partiql-memory/README.md +++ b/plugins/partiql-memory/README.md @@ -1,36 +1,19 @@ -# PartiQL In-Memory Plugin +# PartiQL Memory Plugin -This is a PartiQL plugin for in-memory DB. The primary purpose of this plugin is for testing. +This is a plugin for defining namespaces and tables in memory. -## Provider - -The plugin is backed by a catalog provider. This enables use to easily modify a catalog for testing. - -```kotlin -val provider = MemoryCatalog.Provider() -provider[catalogName] = MemoryCatalog.of( - t1 to StaticType.INT2, - ... -) -``` - -## Catalog path - -The in-memory connector can handle arbitrary depth catalog path: +## Usage ```kotlin -val provider = MemoryCatalog.Provider() -provider[catalogName] = MemoryCatalog.of( - "schema.tbl" to StaticType.INT2, -) -``` - -The full path is `catalogName.schema.tbl` - -The lookup logic is identical to localPlugin. - +// define the data and types. +val catalog = MemoryCatalog.builder() + .name("hello") + .defineTable("pi", MemoryTable( + type = PType.typeFloat32(), + data = ionFloat(3.14), + )) + .build() + +// create a connector to be used in a session +val connector = MemoryConnector.from(catalog) ``` -|_ catalogName - |_ schema - |_ tbl.ion -``` \ No newline at end of file diff --git a/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryBindings.kt b/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryBindings.kt deleted file mode 100644 index 24cec718cd..0000000000 --- a/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryBindings.kt +++ /dev/null @@ -1,15 +0,0 @@ -package org.partiql.plugins.memory - -import org.partiql.spi.connector.ConnectorBindings -import org.partiql.spi.connector.ConnectorPath -import org.partiql.value.PartiQLValue -import org.partiql.value.PartiQLValueExperimental -import org.partiql.value.missingValue - -@OptIn(PartiQLValueExperimental::class) -public class MemoryBindings(private val catalog: MemoryCatalog) : ConnectorBindings { - - override fun getValue(path: ConnectorPath): PartiQLValue { - return catalog.get(path)?.getValue() ?: missingValue() - } -} diff --git a/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryCatalog.kt b/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryCatalog.kt index 426b65d496..3f7a2c2479 100644 --- a/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryCatalog.kt +++ b/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryCatalog.kt @@ -15,207 +15,70 @@ package org.partiql.plugins.memory -import org.partiql.spi.BindingName -import org.partiql.spi.BindingPath -import org.partiql.spi.connector.ConnectorFnProvider -import org.partiql.spi.connector.ConnectorHandle -import org.partiql.spi.connector.ConnectorPath -import org.partiql.spi.connector.sql.SqlFnProvider -import org.partiql.spi.connector.sql.info.InfoSchema -import org.partiql.spi.fn.FnExperimental +import org.partiql.eval.bindings.Binding +import org.partiql.eval.bindings.Bindings +import org.partiql.planner.metadata.Metadata +import org.partiql.planner.metadata.Namespace /** - * A basic catalog implementation used in testing. + * A basic connector implementation used in testing. * - * Note, this treats both delimited (quoted) and simple (unquoted) identifiers as case-sensitive. + * TODO incorporate the nested namespaces possible in the current v1 memory plugin. + * I'm keeping this simple for now before APIs stabilize. * - * @property name + * https://github.com/partiql/partiql-lang-kotlin/commit/f8cbeb83a8d4ba8f5218b9db016e0661d778441e */ -public class MemoryCatalog(public val name: String, public val infoSchema: InfoSchema) { - - @OptIn(FnExperimental::class) - public fun getFunctions(): ConnectorFnProvider = SqlFnProvider(infoSchema.functions) - - private val root: Tree.Dir = Tree.Dir(name) - - /** - * Inserts the `obj` at the given path, creating any non-existent intermediate directories as necessary. - * - * @param path Catalog absolute path. - * @param obj Object to insert. - */ - public fun insert(path: BindingPath, obj: MemoryObject) { - val dir = path.steps.dropLast(1) - val binding = path.steps.last() - var curr: Tree.Dir = root - // create any non-existent intermediate directories - dir.forEach { - curr = curr.mkdir(it.name) - } - // insert entity in current dir - curr.insert( - binding.name, - ConnectorHandle.Obj( - path = ConnectorPath(path.steps.map { it.name }), - entity = obj, - ) - ) - } +public class MemoryCatalog private constructor( + private val name: String, + private val tables: Map, +) { - /** - * Finds a [MemoryObject] in the catalog, returning `null` if it does not exist. - * - * 1) If multiple paths are found, return the longest match. - * 2) If the path is ambiguous, this will throw an error. - * - * This follows the scoping rules in section 10 of the PartiQL Specification. - * - * @param path - * @return - */ - public fun find(path: BindingPath): ConnectorHandle.Obj? { - var currItems = listOf() - var currDirs = listOf(root) - for (name in path.steps) { - val nextItems = mutableListOf() - val nextDirs = mutableListOf() - currDirs.flatMap { it.find(name) }.forEach { - when (it) { - is Tree.Dir -> nextDirs.add(it) - is Tree.Item -> nextItems.add(it) - } - } - currItems = if (nextItems.isEmpty()) currItems else nextItems - currDirs = if (nextDirs.isEmpty()) break else nextDirs - } - return when (currItems.size) { - 0 -> null - 1 -> currItems.first().obj - else -> error("Ambiguous binding $path, found multiple matching bindings") - } - } + // these could be anonymous, but I thought it was cleaner to separate. + private val bindings = MBindings() + private val metadata = MMetadata() - /** - * Gets a [MemoryObject] in the catalog, returning `null` if it does not exist. - * - * @param path - * @return - */ - public fun get(path: ConnectorPath): MemoryObject? { - var curr: Tree.Dir = root - for (i in path.steps.indices) { - val next = curr.get(path.steps[i]) ?: break - when (next) { - is Tree.Dir -> curr = next - is Tree.Item -> { - if (i == path.steps.size - 1) { - return next.obj.entity as? MemoryObject - } - break - } - } - } - return null - } + public fun getBindings(): Bindings = bindings + public fun getMetadata(): Metadata = metadata - public companion object { + public class Builder internal constructor() { - @JvmStatic - public fun builder(): MemoryCatalogBuilder = MemoryCatalogBuilder() + private var name: String? = null + private var tables: MutableMap = mutableMapOf() - @JvmStatic - public fun SQL(): MemoryCatalogBuilder = MemoryCatalogBuilder().info(InfoSchema.default()) + public fun name(name: String): Builder = apply { this.name = name } + + public fun defineTable(name: String, table: MemoryTable): Builder = apply { tables[name] = table } + + public fun build(): MemoryCatalog = MemoryCatalog(name!!, tables) + } + + public companion object { @JvmStatic - public fun PartiQL(): MemoryCatalogBuilder = MemoryCatalogBuilder().info(InfoSchema.ext()) + public fun builder(): Builder = Builder() } - private sealed interface Tree { + private inner class MBindings : Bindings { + override fun getBindings(name: String): Bindings? = null + override fun getBinding(name: String): Binding? = tables[name] + } - /** - * The catalog entry's case-sensitive binding name. - */ - val name: String + private inner class MMetadata : Metadata { /** - * Dir is similar to an SQL Schema as well as a Unix directory. - * - * @property name + * Build a root namespace. */ - class Dir(override val name: String) : Tree { - - private val children: MutableMap = mutableMapOf() - - /** - * Creates a directory, returning the new directory. - * - * 1) If a subdirectory with this name already exists, no action. - * 2) If an entity with this name already exists, error. - * - * @param name - * @return - */ - fun mkdir(name: String): Dir { - var child = children[name] - if (child is Item) { - error("File exists: `$name`") - } - if (child == null) { - child = Dir(name) - children[name] = child + private val root = MemoryNamespace.builder() + .name(name) + .apply { + for (table in tables) { + val n = table.key + val t = table.value + defineTable(n, t) } - return child as Dir } + .build() - /** - * Inserts an entity in this directory, return the new entity. - * - * 1) If an entity with this name already exists, overwrite. - * 2) If a subdirectory with this name already exists, error. - * - * @param name - * @param obj - * @return - */ - fun insert(name: String, obj: ConnectorHandle.Obj): Item { - if (children[name] is Dir) { - error("Directory exists: `$name`") - } - val child = Item(name, obj) - children[name] = child - return child - } - - /** - * List directory contents. - * - * @return - */ - fun ls(): Collection = children.values - - /** - * Find all directory entries by binding naming. - * - * @param name - * @return - */ - fun find(name: BindingName): List = ls().filter { name.matches(it.name) } - - /** - * Get all directory entries by name. - * - * @param name - * @return - */ - fun get(name: String): Tree? = children[name] - } - - /** - * Item represents a type-annotated global binding in a catalog. - * - * @property name - * @property obj - */ - class Item(override val name: String, val obj: ConnectorHandle.Obj) : Tree + override fun getNamespace(): Namespace = root } } diff --git a/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryCatalogBuilder.kt b/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryCatalogBuilder.kt deleted file mode 100644 index 15d6e06c58..0000000000 --- a/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryCatalogBuilder.kt +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package org.partiql.plugins.memory - -import com.amazon.ionelement.api.IonElement -import org.partiql.spi.BindingCase -import org.partiql.spi.BindingName -import org.partiql.spi.BindingPath -import org.partiql.spi.connector.ConnectorPath -import org.partiql.spi.connector.sql.info.InfoSchema -import org.partiql.spi.fn.Agg -import org.partiql.spi.fn.Fn -import org.partiql.spi.fn.FnExperimental -import org.partiql.spi.fn.Index -import org.partiql.types.StaticType -import org.partiql.value.PartiQLValueExperimental -import org.partiql.value.io.PartiQLValueIonReaderBuilder - -/** - * Utility class for creating a MemoryCatalog. - */ -public class MemoryCatalogBuilder { - - private var _name: String? = null - private var _info: InfoSchema? = null - private var _items: MutableList> = mutableListOf() - - public fun name(name: String): MemoryCatalogBuilder = this.apply { this._name = name } - - public fun info(info: InfoSchema): MemoryCatalogBuilder = this.apply { this._info = info } - - /** - * This is a simple `dot` delimited utility for adding type definitions. - * - * At some point, this will support adding values as well as paths. - * - * @param name - * @param type - */ - @OptIn(PartiQLValueExperimental::class) - @JvmOverloads - public fun define(name: String, type: StaticType = StaticType.ANY, value: IonElement? = null): MemoryCatalogBuilder = this.apply { - val path = BindingPath(name.split(".").map { BindingName(it, BindingCase.SENSITIVE) }) - val pValue = value?.let { elt -> - PartiQLValueIonReaderBuilder.standard().build(elt).read() - } - val obj = MemoryObject(type, value = pValue) - _items.add(path to obj) - } - - @OptIn(FnExperimental::class) - public fun build(): MemoryCatalog { - val name = _name ?: error("MemoryCatalog must have a name") - val info = _info ?: InfoSchema( - object : Index { - override fun get(path: List): List = emptyList() - override fun get(path: ConnectorPath, specific: String): Fn? = null - }, - object : Index { - override fun get(path: List): List = emptyList() - override fun get(path: ConnectorPath, specific: String): Agg? = null - } - ) - val catalog = MemoryCatalog(name, info) - for (item in _items) { catalog.insert(item.first, item.second) } - return catalog - } -} diff --git a/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryConnector.kt b/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryConnector.kt index ea984ae2e8..abb90646fb 100644 --- a/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryConnector.kt +++ b/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryConnector.kt @@ -16,45 +16,33 @@ package org.partiql.plugins.memory import com.amazon.ionelement.api.StructElement +import org.partiql.eval.bindings.Bindings +import org.partiql.planner.metadata.Metadata import org.partiql.spi.connector.Connector -import org.partiql.spi.connector.ConnectorBindings -import org.partiql.spi.connector.ConnectorFnProvider -import org.partiql.spi.connector.ConnectorSession -import org.partiql.spi.connector.sql.SqlConnector -import org.partiql.spi.connector.sql.SqlMetadata -import org.partiql.spi.fn.FnExperimental /** * This is a plugin used for testing and is not a versioned API per semver. */ -public class MemoryConnector(private val catalog: MemoryCatalog) : SqlConnector() { +public class MemoryConnector private constructor(private val catalog: MemoryCatalog) : Connector { - private val bindings = MemoryBindings(catalog) - - override fun getBindings(): ConnectorBindings = bindings + public companion object { - override fun getMetadata(session: ConnectorSession): SqlMetadata = MemoryMetadata(catalog, session, catalog.infoSchema) + @JvmStatic + public fun from(catalog: MemoryCatalog): MemoryConnector = MemoryConnector(catalog) + } - @OptIn(FnExperimental::class) - override fun getFunctions(): ConnectorFnProvider = catalog.getFunctions() + override fun getBindings(): Bindings = catalog.getBindings() + override fun getMetadata(): Metadata = catalog.getMetadata() - internal class Factory(private val catalogs: List) : Connector.Factory { + /** + * For use with ServiceLoader to instantiate a connector from an Ion config. + */ + internal class Factory : Connector.Factory { override val name: String = "memory" - override fun create(catalogName: String, config: StructElement?): MemoryConnector { - val catalog = catalogs.firstOrNull { it.name == catalogName } - ?: error("Catalog $catalogName is not registered in the MemoryPlugin") - return MemoryConnector(catalog) + override fun create(config: StructElement): Connector { + TODO("Instantiation of a MemoryConnector via the factory is currently not supported") } } - - public companion object { - - /** - * A connector whose catalogs holds no binding and all SQL-92 function and PartiQL-Builtin - */ - @JvmStatic - public fun partiQL(): MemoryConnector = MemoryConnector(MemoryCatalog.PartiQL().name("default").build()) - } } diff --git a/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryMetadata.kt b/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryMetadata.kt deleted file mode 100644 index 2818d85803..0000000000 --- a/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryMetadata.kt +++ /dev/null @@ -1,24 +0,0 @@ -package org.partiql.plugins.memory - -import org.partiql.spi.BindingPath -import org.partiql.spi.connector.ConnectorHandle -import org.partiql.spi.connector.ConnectorSession -import org.partiql.spi.connector.sql.SqlMetadata -import org.partiql.spi.connector.sql.info.InfoSchema -import org.partiql.spi.fn.FnExperimental - -internal class MemoryMetadata( - private val catalog: MemoryCatalog, - session: ConnectorSession, - info: InfoSchema, -) : SqlMetadata(session, info) { - - override fun getObject(path: BindingPath): ConnectorHandle.Obj? { - return super.getObject(path) ?: catalog.find(path) - } - - @FnExperimental - override fun getFunction(path: BindingPath): ConnectorHandle.Fn? { - return super.getFunction(path) - } -} diff --git a/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryNamespace.kt b/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryNamespace.kt new file mode 100644 index 0000000000..b1aeb5ef6a --- /dev/null +++ b/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryNamespace.kt @@ -0,0 +1,39 @@ +package org.partiql.plugins.memory + +import org.partiql.planner.metadata.Namespace +import org.partiql.planner.metadata.Table + +/** + * Namespace implementation. + */ +public class MemoryNamespace private constructor( + private val name: String, + private val tables: Map, + private val namespaces: Map, +) : Namespace { + override fun getName(): String = name + override fun getTable(name: String): Table? = tables[name] + override fun getNamespace(name: String): Namespace? = namespaces[name] + override fun getNamespaces(): Collection = namespaces.values + + public class Builder internal constructor() { + + private var name: String? = null + private var tables: MutableMap = mutableMapOf() + private var namespaces: MutableMap = mutableMapOf() + + public fun name(name: String): Builder = apply { this.name = name } + + public fun defineTable(name: String, table: MemoryTable): Builder = apply { tables[name] = table } + + public fun defineNamespace(namespace: MemoryNamespace): Builder = apply { namespaces[namespace.getName()] = namespace } + + public fun build(): MemoryNamespace = MemoryNamespace(name!!, tables, namespaces) + } + + public companion object { + + @JvmStatic + public fun builder(): Builder = Builder() + } +} diff --git a/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryObject.kt b/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryObject.kt deleted file mode 100644 index 881ec291aa..0000000000 --- a/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryObject.kt +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package org.partiql.plugins.memory - -import org.partiql.spi.connector.ConnectorObject -import org.partiql.types.StaticType -import org.partiql.value.PartiQLValue -import org.partiql.value.PartiQLValueExperimental - -@OptIn(PartiQLValueExperimental::class) -public class MemoryObject( - private val type: StaticType, - private val value: PartiQLValue? = null, -) : ConnectorObject { - - public fun getValue(): PartiQLValue? = value - - override fun getType(): StaticType = type -} diff --git a/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryPlugin.kt b/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryPlugin.kt index d282dd1be8..21d4cba488 100644 --- a/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryPlugin.kt +++ b/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryPlugin.kt @@ -18,7 +18,6 @@ package org.partiql.plugins.memory import org.partiql.spi.Plugin import org.partiql.spi.connector.Connector -public class MemoryPlugin(catalogs: List) : Plugin { - - override val factory: Connector.Factory = MemoryConnector.Factory(catalogs) +public object MemoryPlugin : Plugin { + override val factory: Connector.Factory = MemoryConnector.Factory() } diff --git a/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryTable.kt b/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryTable.kt new file mode 100644 index 0000000000..a3a2f6c7e1 --- /dev/null +++ b/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryTable.kt @@ -0,0 +1,55 @@ +package org.partiql.plugins.memory + +import org.partiql.eval.bindings.Binding +import org.partiql.eval.value.Datum +import org.partiql.planner.metadata.Table +import org.partiql.types.PType + +public class MemoryTable private constructor( + private val type: PType, + private val datum: Datum, +) : Table, Binding { + + override fun getKind(): Table.Kind = Table.Kind.TABLE + override fun getSchema(): PType = type + override fun getDatum(): Datum = datum + + public companion object { + + /** + * Create an empty table with dynamic schema. + */ + @JvmStatic + public fun empty(): MemoryTable = MemoryTable( + type = PType.typeDynamic(), + datum = Datum.nullValue(), + ) + + /** + * Create an empty table with known schema. + */ + @JvmStatic + public fun empty(schema: PType): MemoryTable = MemoryTable( + type = schema, + datum = Datum.nullValue(), + ) + + /** + * Create a table from a Datum with dynamic schema. + */ + @JvmStatic + public fun of(value: Datum): MemoryTable = MemoryTable( + type = PType.typeDynamic(), + datum = value, + ) + + /** + * Create a table from a Datum with known schema. + */ + @JvmStatic + public fun of(value: Datum, schema: PType): MemoryTable = MemoryTable( + type = schema, + datum = value, + ) + } +} diff --git a/plugins/partiql-memory/src/test/kotlin/org/partiql/plugins/memory/MemoryCatalogTest.kt b/plugins/partiql-memory/src/test/kotlin/org/partiql/plugins/memory/MemoryCatalogTest.kt deleted file mode 100644 index 210b7779ac..0000000000 --- a/plugins/partiql-memory/src/test/kotlin/org/partiql/plugins/memory/MemoryCatalogTest.kt +++ /dev/null @@ -1,111 +0,0 @@ -package org.partiql.plugins.memory - -import org.junit.jupiter.api.Test -import org.partiql.spi.BindingCase -import org.partiql.spi.BindingName -import org.partiql.spi.BindingPath -import org.partiql.spi.connector.ConnectorPath -import org.partiql.types.BagType -import org.partiql.types.StaticType -import org.partiql.types.StructType - -class MemoryCatalogTest { - - companion object { - - private val catalog = MemoryCatalog.builder() - .name("test") - .define("a", StaticType.INT2) - .define( - "struct", - StructType( - fields = listOf(StructType.Field("a", StaticType.INT2)) - ) - ) - .define( - "schema.tbl", - BagType( - StructType( - fields = listOf(StructType.Field("a", StaticType.INT2)) - ) - ) - ) - .build() - } - - @Test - fun getValue() { - val requested = BindingPath( - listOf( - BindingName("a", BindingCase.INSENSITIVE) - ) - ) - val expected = StaticType.INT2 - val handle = catalog.find(requested) - val descriptor = handle!!.entity.getType() - assert(requested.matches(handle.path)) - assert(expected == descriptor) - } - - @Test - fun getCaseSensitiveValueShouldFail() { - val requested = BindingPath( - listOf( - BindingName("A", BindingCase.SENSITIVE) - ) - ) - val handle = catalog.find(requested) - assert(null == handle) - } - - @Test - fun accessStruct() { - val requested = BindingPath( - listOf( - BindingName("struct", BindingCase.INSENSITIVE), - BindingName("a", BindingCase.INSENSITIVE) - ) - ) - val handle = catalog.find(requested) - val descriptor = handle!!.entity.getType() - val expectConnectorPath = ConnectorPath.of("struct") - val expectedObjectType = StructType(fields = listOf(StructType.Field("a", StaticType.INT2))) - - assert(expectConnectorPath == handle.path) - assert(expectedObjectType == descriptor) - } - - @Test - fun pathNavigationSuccess() { - val requested = BindingPath( - listOf( - BindingName("schema", BindingCase.INSENSITIVE), - BindingName("tbl", BindingCase.INSENSITIVE) - ) - ) - val handle = catalog.find(requested) - val descriptor = handle!!.entity.getType() - val expectedObjectType = BagType(StructType(fields = listOf(StructType.Field("a", StaticType.INT2)))) - - assert(requested.matches(handle.path)) - assert(expectedObjectType == descriptor) - } - - @Test - fun pathNavigationSuccess2() { - val requested = BindingPath( - listOf( - BindingName("schema", BindingCase.INSENSITIVE), - BindingName("tbl", BindingCase.INSENSITIVE), - BindingName("a", BindingCase.INSENSITIVE) - ) - ) - val handle = catalog.find(requested) - val descriptor = handle!!.entity.getType() - val expectedObjectType = BagType(StructType(fields = listOf(StructType.Field("a", StaticType.INT2)))) - val expectConnectorPath = ConnectorPath.of("schema", "tbl") - - assert(expectConnectorPath == handle.path) - assert(expectedObjectType == descriptor) - } -} From 5c27898e5940a864df9f7cbc3085b2356925b8c3 Mon Sep 17 00:00:00 2001 From: "R. C. Howell" Date: Thu, 27 Jun 2024 13:18:16 -0700 Subject: [PATCH 4/9] Delete uneccessary files --- .../internal/fn/scalar/FnDateAddYear.kt | 188 ------------------ .../plugin/internal/fn/scalar/FnIsChar.kt | 69 ------- 2 files changed, 257 deletions(-) delete mode 100644 plugins/partiql-plugin/src/main/kotlin/org/partiql/plugin/internal/fn/scalar/FnDateAddYear.kt delete mode 100644 plugins/partiql-plugin/src/main/kotlin/org/partiql/plugin/internal/fn/scalar/FnIsChar.kt diff --git a/plugins/partiql-plugin/src/main/kotlin/org/partiql/plugin/internal/fn/scalar/FnDateAddYear.kt b/plugins/partiql-plugin/src/main/kotlin/org/partiql/plugin/internal/fn/scalar/FnDateAddYear.kt deleted file mode 100644 index c8b736c236..0000000000 --- a/plugins/partiql-plugin/src/main/kotlin/org/partiql/plugin/internal/fn/scalar/FnDateAddYear.kt +++ /dev/null @@ -1,188 +0,0 @@ -// ktlint-disable filename -@file:Suppress("ClassName") - -package org.partiql.plugin.internal.fn.scalar - -import org.partiql.errors.DataException -import org.partiql.errors.TypeCheckException -import org.partiql.spi.function.PartiQLFunction -import org.partiql.spi.function.PartiQLFunctionExperimental -import org.partiql.types.function.FunctionParameter -import org.partiql.types.function.FunctionSignature -import org.partiql.value.DateValue -import org.partiql.value.Int32Value -import org.partiql.value.Int64Value -import org.partiql.value.IntValue -import org.partiql.value.PartiQLValue -import org.partiql.value.PartiQLValueExperimental -import org.partiql.value.PartiQLValueType.DATE -import org.partiql.value.PartiQLValueType.INT -import org.partiql.value.PartiQLValueType.INT32 -import org.partiql.value.PartiQLValueType.INT64 -import org.partiql.value.PartiQLValueType.TIMESTAMP -import org.partiql.value.TimestampValue -import org.partiql.value.check -import org.partiql.value.dateValue -import org.partiql.value.timestampValue - -@OptIn(PartiQLValueExperimental::class, PartiQLFunctionExperimental::class) -internal object Fn_DATE_ADD_YEAR__INT32_DATE__DATE : PartiQLFunction.Scalar { - - override val signature = FunctionSignature.Scalar( - name = "date_add_year", - returns = DATE, - parameters = listOf( - FunctionParameter("interval", INT32), - FunctionParameter("datetime", DATE), - ), - isNullCall = true, - isNullable = false, - ) - - override fun invoke(args: Array): PartiQLValue { - val interval = args[0].check() - val datetime = args[1].check() - return if (datetime.value == null || interval.value == null) { - dateValue(null) - } else { - val datetimeValue = datetime.value!! - val intervalValue = interval.toInt64().value!! - dateValue(datetimeValue.plusYears(intervalValue)) - } - } -} - -@OptIn(PartiQLValueExperimental::class, PartiQLFunctionExperimental::class) -internal object Fn_DATE_ADD_YEAR__INT64_DATE__DATE : PartiQLFunction.Scalar { - - override val signature = FunctionSignature.Scalar( - name = "date_add_year", - returns = DATE, - parameters = listOf( - FunctionParameter("interval", INT64), - FunctionParameter("datetime", DATE), - ), - isNullCall = true, - isNullable = false, - ) - - override fun invoke(args: Array): PartiQLValue { - val interval = args[0].check() - val datetime = args[1].check() - return if (datetime.value == null || interval.value == null) { - dateValue(null) - } else { - val datetimeValue = datetime.value!! - val intervalValue = interval.value!! - dateValue(datetimeValue.plusYears(intervalValue)) - } - } -} - -@OptIn(PartiQLValueExperimental::class, PartiQLFunctionExperimental::class) -internal object Fn_DATE_ADD_YEAR__INT_DATE__DATE : PartiQLFunction.Scalar { - - override val signature = FunctionSignature.Scalar( - name = "date_add_year", - returns = DATE, - parameters = listOf( - FunctionParameter("interval", INT), - FunctionParameter("datetime", DATE), - ), - isNullCall = true, - isNullable = false, - ) - - override fun invoke(args: Array): PartiQLValue { - val interval = args[0].check() - val datetime = args[1].check() - return if (datetime.value == null || interval.value == null) { - dateValue(null) - } else { - val datetimeValue = datetime.value!! - val intervalValue = try { interval.toInt64().value!! } catch (e: DataException) { throw TypeCheckException() } - dateValue(datetimeValue.plusYears(intervalValue)) - } - } -} - -@OptIn(PartiQLValueExperimental::class, PartiQLFunctionExperimental::class) -internal object Fn_DATE_ADD_YEAR__INT32_TIMESTAMP__TIMESTAMP : PartiQLFunction.Scalar { - - override val signature = FunctionSignature.Scalar( - name = "date_add_year", - returns = TIMESTAMP, - parameters = listOf( - FunctionParameter("interval", INT32), - FunctionParameter("datetime", TIMESTAMP), - ), - isNullCall = true, - isNullable = false, - ) - - override fun invoke(args: Array): PartiQLValue { - val interval = args[0].check() - val datetime = args[1].check() - return if (datetime.value == null || interval.value == null) { - timestampValue(null) - } else { - val datetimeValue = datetime.value!! - val intervalValue = interval.toInt64().value!! - timestampValue(datetimeValue.plusYears(intervalValue)) - } - } -} - -@OptIn(PartiQLValueExperimental::class, PartiQLFunctionExperimental::class) -internal object Fn_DATE_ADD_YEAR__INT64_TIMESTAMP__TIMESTAMP : PartiQLFunction.Scalar { - - override val signature = FunctionSignature.Scalar( - name = "date_add_year", - returns = TIMESTAMP, - parameters = listOf( - FunctionParameter("interval", INT64), - FunctionParameter("datetime", TIMESTAMP), - ), - isNullCall = true, - isNullable = false, - ) - - override fun invoke(args: Array): PartiQLValue { - val interval = args[0].check() - val datetime = args[1].check() - return if (datetime.value == null || interval.value == null) { - timestampValue(null) - } else { - val datetimeValue = datetime.value!! - val intervalValue = interval.value!! - timestampValue(datetimeValue.plusYears(intervalValue)) - } - } -} - -@OptIn(PartiQLValueExperimental::class, PartiQLFunctionExperimental::class) -internal object Fn_DATE_ADD_YEAR__INT_TIMESTAMP__TIMESTAMP : PartiQLFunction.Scalar { - - override val signature = FunctionSignature.Scalar( - name = "date_add_year", - returns = TIMESTAMP, - parameters = listOf( - FunctionParameter("interval", INT), - FunctionParameter("datetime", TIMESTAMP), - ), - isNullCall = true, - isNullable = false, - ) - - override fun invoke(args: Array): PartiQLValue { - val interval = args[0].check() - val datetime = args[1].check() - return if (datetime.value == null || interval.value == null) { - timestampValue(null) - } else { - val datetimeValue = datetime.value!! - val intervalValue = try { interval.toInt64().value!! } catch (e: DataException) { throw TypeCheckException() } - timestampValue(datetimeValue.plusYears(intervalValue)) - } - } -} diff --git a/plugins/partiql-plugin/src/main/kotlin/org/partiql/plugin/internal/fn/scalar/FnIsChar.kt b/plugins/partiql-plugin/src/main/kotlin/org/partiql/plugin/internal/fn/scalar/FnIsChar.kt deleted file mode 100644 index ae010524e2..0000000000 --- a/plugins/partiql-plugin/src/main/kotlin/org/partiql/plugin/internal/fn/scalar/FnIsChar.kt +++ /dev/null @@ -1,69 +0,0 @@ -// ktlint-disable filename -@file:Suppress("ClassName") - -package org.partiql.plugin.internal.fn.scalar - -import org.partiql.errors.TypeCheckException -import org.partiql.spi.function.PartiQLFunction -import org.partiql.spi.function.PartiQLFunctionExperimental -import org.partiql.types.function.FunctionParameter -import org.partiql.types.function.FunctionSignature -import org.partiql.value.CharValue -import org.partiql.value.Int32Value -import org.partiql.value.PartiQLValue -import org.partiql.value.PartiQLValueExperimental -import org.partiql.value.PartiQLValueType.ANY -import org.partiql.value.PartiQLValueType.BOOL -import org.partiql.value.PartiQLValueType.INT32 -import org.partiql.value.StringValue -import org.partiql.value.boolValue -import org.partiql.value.check - -@OptIn(PartiQLValueExperimental::class, PartiQLFunctionExperimental::class) -internal object Fn_IS_CHAR__ANY__BOOL : PartiQLFunction.Scalar { - - override val signature = FunctionSignature.Scalar( - name = "is_char", - returns = BOOL, - parameters = listOf(FunctionParameter("value", ANY)), - isNullCall = true, - isNullable = false, - ) - - override fun invoke(args: Array): PartiQLValue { - val arg = args[0] - return if (arg.isNull) { - boolValue(null) - } else { - boolValue(arg is CharValue) - } - } -} - -@OptIn(PartiQLValueExperimental::class, PartiQLFunctionExperimental::class) -internal object Fn_IS_CHAR__INT32_ANY__BOOL : PartiQLFunction.Scalar { - - override val signature = FunctionSignature.Scalar( - name = "is_char", - returns = BOOL, - parameters = listOf( - FunctionParameter("length", INT32), - FunctionParameter("value", ANY), - ), - isNullCall = true, - isNullable = false, - ) - - override fun invoke(args: Array): PartiQLValue { - val length = args[0].check().value - if (length == null || length < 0) { - throw TypeCheckException() - } - val v = args[1] - return when { - v.isNull -> boolValue(null) - v !is StringValue -> boolValue(false) - else -> boolValue(v.value!!.length == length) - } - } -} From e33291566ef546d6ecf4a5afaffd50dafe6b3e2d Mon Sep 17 00:00:00 2001 From: "R. C. Howell" Date: Thu, 27 Jun 2024 14:29:23 -0700 Subject: [PATCH 5/9] Updates local plugin --- .../partiql/plugins/local/LocalBindings.kt | 11 ++ .../org/partiql/plugins/local/LocalCatalog.kt | 109 ------------- .../partiql/plugins/local/LocalConnector.kt | 92 ++--------- .../partiql/plugins/local/LocalNamespace.kt | 42 +++++ .../org/partiql/plugins/local/LocalObject.kt | 31 ---- .../org/partiql/plugins/local/LocalTable.kt | 43 +++++ .../local/LocalConnectorMetadataTests.kt | 150 ------------------ .../resources/catalogs/local/data/records.ion | 17 -- .../resources/catalogs/local/data/struct.ion | 31 ---- 9 files changed, 112 insertions(+), 414 deletions(-) create mode 100644 plugins/partiql-local/src/main/kotlin/org/partiql/plugins/local/LocalBindings.kt delete mode 100644 plugins/partiql-local/src/main/kotlin/org/partiql/plugins/local/LocalCatalog.kt create mode 100644 plugins/partiql-local/src/main/kotlin/org/partiql/plugins/local/LocalNamespace.kt delete mode 100644 plugins/partiql-local/src/main/kotlin/org/partiql/plugins/local/LocalObject.kt create mode 100644 plugins/partiql-local/src/main/kotlin/org/partiql/plugins/local/LocalTable.kt delete mode 100644 plugins/partiql-local/src/test/kotlin/org/partiql/plugins/local/LocalConnectorMetadataTests.kt delete mode 100644 plugins/partiql-local/src/test/resources/catalogs/local/data/records.ion delete mode 100644 plugins/partiql-local/src/test/resources/catalogs/local/data/struct.ion diff --git a/plugins/partiql-local/src/main/kotlin/org/partiql/plugins/local/LocalBindings.kt b/plugins/partiql-local/src/main/kotlin/org/partiql/plugins/local/LocalBindings.kt new file mode 100644 index 0000000000..9d8c6d8831 --- /dev/null +++ b/plugins/partiql-local/src/main/kotlin/org/partiql/plugins/local/LocalBindings.kt @@ -0,0 +1,11 @@ +package org.partiql.plugins.local + +import org.partiql.eval.bindings.Binding +import org.partiql.eval.bindings.Bindings + +internal object LocalBindings : Bindings { + + override fun getBindings(name: String): Bindings? = null + + override fun getBinding(name: String): Binding? = null +} diff --git a/plugins/partiql-local/src/main/kotlin/org/partiql/plugins/local/LocalCatalog.kt b/plugins/partiql-local/src/main/kotlin/org/partiql/plugins/local/LocalCatalog.kt deleted file mode 100644 index 207b5b9a25..0000000000 --- a/plugins/partiql-local/src/main/kotlin/org/partiql/plugins/local/LocalCatalog.kt +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at: - * - * http://aws.amazon.com/apache2.0/ - * - * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific - * language governing permissions and limitations under the License. - */ - -package org.partiql.plugins.local - -import com.amazon.ionelement.api.loadSingleElement -import org.partiql.spi.BindingCase -import org.partiql.spi.BindingName -import org.partiql.spi.BindingPath -import org.partiql.types.StaticType -import java.io.File -import java.nio.file.Path - -private sealed class FsTree(val name: String) { - - // "Directory" node - class D(name: String, val children: List) : FsTree(name) - - // Type node - class T(name: String, val type: StaticType) : FsTree(name) -} - -/** - * Build a memoized catalog tree from local schema definitions. - */ -internal class LocalCatalog private constructor(private val root: FsTree.D) { - - /** - * Search the tree for the type. - */ - public fun lookup(path: BindingPath): LocalObject? { - val match = mutableListOf() - var curr: FsTree? = root - for (step in path.steps) { - if (curr == null) return null - when (curr) { - is FsTree.T -> break - is FsTree.D -> { - curr = curr.children.firstOrNull { step.matches(it.name) } - if (curr != null) match.add(curr.name) - } - } - } - // All steps matched and we're at a leaf - if (curr is FsTree.T) { - return LocalObject(match, curr.type) - } - return null - } - - /** - * Provide a list of all objects in this catalog. - */ - public fun listObjects(): List = sequence { search(emptyList(), root) }.toList() - - private suspend fun SequenceScope.search(acc: List, node: FsTree) = - when (node) { - is FsTree.D -> search(acc, node) - is FsTree.T -> search(acc, node) - } - - private suspend fun SequenceScope.search(acc: List, node: FsTree.D) { - val steps = acc + BindingName(node.name, BindingCase.INSENSITIVE) - for (child in node.children) { - search(steps, child) - } - } - - private suspend fun SequenceScope.search(acc: List, node: FsTree.T) { - val steps = acc + BindingName(node.name, BindingCase.INSENSITIVE) - this.yield(BindingPath(steps)) - } - - companion object { - - /** - * Builds a FsTree from the given root. - */ - public fun load(root: Path): LocalCatalog = LocalCatalog(root.toFile().tree() as FsTree.D) - - private fun File.tree(): FsTree = when (this.isDirectory) { - true -> d() - else -> t() - } - - private fun File.d(): FsTree.D { - val children = listFiles()!!.map { it.tree() } - return FsTree.D(name, children) - } - - private fun File.t(): FsTree.T { - val text = readText() - val ion = loadSingleElement(text) - val type = ion.toStaticType() - return FsTree.T(nameWithoutExtension, type) - } - } -} diff --git a/plugins/partiql-local/src/main/kotlin/org/partiql/plugins/local/LocalConnector.kt b/plugins/partiql-local/src/main/kotlin/org/partiql/plugins/local/LocalConnector.kt index 9072c659c8..9f25564272 100644 --- a/plugins/partiql-local/src/main/kotlin/org/partiql/plugins/local/LocalConnector.kt +++ b/plugins/partiql-local/src/main/kotlin/org/partiql/plugins/local/LocalConnector.kt @@ -15,18 +15,13 @@ package org.partiql.plugins.local import com.amazon.ionelement.api.StructElement -import org.partiql.spi.BindingPath +import org.partiql.eval.bindings.Bindings +import org.partiql.planner.metadata.Metadata +import org.partiql.planner.metadata.Namespace import org.partiql.spi.connector.Connector -import org.partiql.spi.connector.ConnectorAggProvider -import org.partiql.spi.connector.ConnectorBindings -import org.partiql.spi.connector.ConnectorFnProvider -import org.partiql.spi.connector.ConnectorHandle -import org.partiql.spi.connector.ConnectorMetadata -import org.partiql.spi.connector.ConnectorPath -import org.partiql.spi.connector.ConnectorSession -import org.partiql.spi.fn.FnExperimental import java.nio.file.Path import java.nio.file.Paths +import kotlin.io.path.isDirectory import kotlin.io.path.notExists /** @@ -40,88 +35,33 @@ import kotlin.io.path.notExists * root: "/Users/me/some/root/directory" * } * ``` - * - * @property catalogRoot Catalog root path - * @property catalogName Catalog name - * @property config Catalog configuration */ -public class LocalConnector( - private val catalogRoot: Path, - private val catalogName: String, - private val config: StructElement, -) : Connector { +public class LocalConnector(private val root: Path) : Connector { public companion object { public const val CONNECTOR_NAME: String = "local" public const val ROOT_KEY: String = "root" } - private val metadata = Metadata(catalogRoot) - - // not yet defined in SPI - public fun listObjects(): List = metadata.listObjects() - - override fun getMetadata(session: ConnectorSession): ConnectorMetadata = metadata - - override fun getBindings(): ConnectorBindings { - TODO("Not yet implemented") - } - - @FnExperimental - override fun getFunctions(): ConnectorFnProvider { - TODO("Not yet implemented") - } + override fun getBindings(): Bindings = LocalBindings - @FnExperimental - override fun getAggregations(): ConnectorAggProvider { - TODO("Not yet implemented") + override fun getMetadata(): Metadata = object : Metadata { + override fun getNamespace(): Namespace = LocalNamespace(root) } internal class Factory : Connector.Factory { - private val default: Path = Paths.get(System.getProperty("user.home")).resolve(".partiql/local") - override val name: String = CONNECTOR_NAME - override fun create(catalogName: String, config: StructElement?): Connector { - assert(config != null) { "Local plugin requires non-null config" } - val root = config!!.getOptional(ROOT_KEY)?.stringValueOrNull?.let { Paths.get(it) } - val catalogRoot = root ?: default - if (catalogRoot.notExists()) { - error("Invalid catalog `$catalogRoot`") + override fun create(config: StructElement): Connector { + val root = config.getOptional(ROOT_KEY)?.stringValueOrNull?.let { Paths.get(it) } + if (root == null) { + error("Root cannot be null") } - return LocalConnector(catalogRoot, catalogName, config) - } - } - - public class Metadata(root: Path) : ConnectorMetadata { - - /** - * TODO watch root for changes and rebuild catalog if needed. - */ - // private val watcher = FileSystems.getDefault().newWatchService() - - /** - * Cached catalog - */ - private var catalog = LocalCatalog.load(root) - - override fun getObject(path: BindingPath): ConnectorHandle.Obj? { - val value = catalog.lookup(path) ?: return null - return ConnectorHandle.Obj( - path = ConnectorPath(value.path), - entity = value, - ) - } - - @FnExperimental - override fun getFunction(path: BindingPath): ConnectorHandle.Fn? { - TODO("Not yet implemented") + if (root.notExists() || !root.isDirectory()) { + error("Invalid catalog `$root`") + } + return LocalConnector(root) } - - @FnExperimental - override fun getAggregation(path: BindingPath): ConnectorHandle.Agg? = null - - internal fun listObjects(): List = catalog.listObjects() } } diff --git a/plugins/partiql-local/src/main/kotlin/org/partiql/plugins/local/LocalNamespace.kt b/plugins/partiql-local/src/main/kotlin/org/partiql/plugins/local/LocalNamespace.kt new file mode 100644 index 0000000000..edc66811df --- /dev/null +++ b/plugins/partiql-local/src/main/kotlin/org/partiql/plugins/local/LocalNamespace.kt @@ -0,0 +1,42 @@ +package org.partiql.plugins.local + +import org.partiql.planner.metadata.Namespace +import org.partiql.planner.metadata.Table +import java.nio.file.Path +import kotlin.io.path.isDirectory +import kotlin.io.path.isRegularFile +import kotlin.io.path.nameWithoutExtension + +/** + * Thin wrapper over a directory. + * - subdir -> namespace + * - file -> table + */ +public class LocalNamespace(private val path: Path) : Namespace { + + init { + assert(path.isDirectory()) { "LocalNamespace must be a directory" } + } + + override fun getName(): String = path.nameWithoutExtension + + override fun getNamespaces(): Collection { + return path.toFile().listFiles { f, _ -> f.isDirectory }!!.map { LocalNamespace(it.toPath()) } + } + + override fun getNamespace(name: String): Namespace? { + val p = path.resolve(name) + return when (p.isDirectory()) { + true -> LocalNamespace(p) + else -> null + } + } + + override fun getTable(name: String): Table? { + val p = path.resolve(name) + return when (p.isRegularFile()) { + true -> LocalTable(p) + else -> null + } + } +} diff --git a/plugins/partiql-local/src/main/kotlin/org/partiql/plugins/local/LocalObject.kt b/plugins/partiql-local/src/main/kotlin/org/partiql/plugins/local/LocalObject.kt deleted file mode 100644 index dda6033022..0000000000 --- a/plugins/partiql-local/src/main/kotlin/org/partiql/plugins/local/LocalObject.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at: - * - * http://aws.amazon.com/apache2.0/ - * - * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific - * language governing permissions and limitations under the License. - */ - -package org.partiql.plugins.local - -import org.partiql.spi.connector.ConnectorObject -import org.partiql.types.StaticType - -/** - * Associate a resolved path with a [StaticType] - * - * @property path - * @property type - */ -internal class LocalObject( - val path: List, - private val type: StaticType, -) : ConnectorObject { - override fun getType(): StaticType = type -} diff --git a/plugins/partiql-local/src/main/kotlin/org/partiql/plugins/local/LocalTable.kt b/plugins/partiql-local/src/main/kotlin/org/partiql/plugins/local/LocalTable.kt new file mode 100644 index 0000000000..a63284215f --- /dev/null +++ b/plugins/partiql-local/src/main/kotlin/org/partiql/plugins/local/LocalTable.kt @@ -0,0 +1,43 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at: + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + */ + +package org.partiql.plugins.local + +import com.amazon.ion.system.IonReaderBuilder +import com.amazon.ionelement.api.loadSingleElement +import org.partiql.eval.bindings.Binding +import org.partiql.eval.value.Datum +import org.partiql.planner.metadata.Table +import org.partiql.types.PType +import org.partiql.types.StaticType +import java.nio.file.Path +import kotlin.io.path.reader + +/** + * Associate a resolved path with a [StaticType] + */ +internal class LocalTable(private val path: Path) : Table, Binding { + + override fun getKind(): Table.Kind = Table.Kind.TABLE + + override fun getSchema(): PType { + val reader = IonReaderBuilder.standard().build(path.reader()) + val element = loadSingleElement(reader) + val staticType = element.toStaticType() + return PType.fromStaticType(staticType) + } + + // TODO for now files are `type` only. + override fun getDatum(): Datum = Datum.nullValue() +} diff --git a/plugins/partiql-local/src/test/kotlin/org/partiql/plugins/local/LocalConnectorMetadataTests.kt b/plugins/partiql-local/src/test/kotlin/org/partiql/plugins/local/LocalConnectorMetadataTests.kt deleted file mode 100644 index 11397f3348..0000000000 --- a/plugins/partiql-local/src/test/kotlin/org/partiql/plugins/local/LocalConnectorMetadataTests.kt +++ /dev/null @@ -1,150 +0,0 @@ -package org.partiql.plugins.local - -import org.junit.jupiter.api.Test -import org.partiql.spi.BindingCase -import org.partiql.spi.BindingName -import org.partiql.spi.BindingPath -import org.partiql.spi.connector.ConnectorPath -import org.partiql.types.BagType -import org.partiql.types.IntType -import org.partiql.types.StaticType -import org.partiql.types.StructType -import org.partiql.types.TupleConstraint -import java.nio.file.Paths -import kotlin.test.assertEquals - -class LocalConnectorMetadataTests { - - private val catalogUrl = - LocalConnectorMetadataTests::class.java.classLoader.getResource("catalogs/local") ?: error("Couldn't be found") - - private val metadata = LocalConnector.Metadata(Paths.get(catalogUrl.path)) - - @Test - fun getTable() { - // Prepare - val requested = BindingPath( - listOf( - BindingName("data", BindingCase.INSENSITIVE), - BindingName("records", BindingCase.INSENSITIVE), - ) - ) - val expected = BagType( - StructType( - fields = mapOf( - "id" to StaticType.INT, - "path" to StaticType.STRING - ), - contentClosed = true, - constraints = setOf(TupleConstraint.Ordered, TupleConstraint.Open(false)) - ) - ) - - // Act - val handle = metadata.getObject(requested)!! - val descriptor = handle.entity.getType() - - // Assert - assert(requested.matches(handle.path)) - assert(expected == descriptor) { - buildString { - appendLine("Expected: $expected") - appendLine("Actual: $descriptor") - } - } - } - - @Test - fun getStruct() { - // Prepare - val requested = BindingPath( - listOf( - BindingName("data", BindingCase.INSENSITIVE), - BindingName("struct", BindingCase.INSENSITIVE), - BindingName("nested", BindingCase.INSENSITIVE), - ) - ) - val expectedPath = ConnectorPath.of("data", "struct") - val expected = - StructType( - contentClosed = true, - fields = mapOf( - "id" to IntType(), - "nested" to StructType( - contentClosed = true, - fields = mapOf( - "nested_id" to IntType() - ), - constraints = setOf( - TupleConstraint.Open(false), - TupleConstraint.UniqueAttrs(true), - TupleConstraint.Ordered - ) - ) - ), - constraints = setOf( - TupleConstraint.Open(false), - TupleConstraint.UniqueAttrs(true), - TupleConstraint.Ordered - ) - ) - - // Act - val handle = metadata.getObject(requested)!! - val descriptor = handle.entity.getType() - - // Assert - assertEquals(expectedPath, handle.path) - assert(expected == descriptor) { - buildString { - appendLine("Expected: $expected") - appendLine("Actual: $descriptor") - } - } - } - - @Test - fun failToFindObject() { - // Prepare - val requested = BindingPath( - listOf( - BindingName("data", BindingCase.INSENSITIVE), - BindingName("unknown", BindingCase.INSENSITIVE), - ) - ) - - // Act - val handle = metadata.getObject(requested) - assertEquals(null, handle) - } - - @Test - fun failToFindSchema() { - // Prepare - val requested = BindingPath( - listOf( - BindingName("unknown", BindingCase.INSENSITIVE), - BindingName("records", BindingCase.INSENSITIVE), - ) - ) - - // Act - val handle = metadata.getObject(requested) - assertEquals(null, handle) - } - - @Test - fun failToFindCaseSensitiveObject() { - // Prepare - val requested = BindingPath( - listOf( - BindingName("data", BindingCase.INSENSITIVE), - BindingName("RECORDS", BindingCase.SENSITIVE), - ) - ) - - // Act - val handle = metadata.getObject(requested) - assertEquals(null, handle) - } -} diff --git a/plugins/partiql-local/src/test/resources/catalogs/local/data/records.ion b/plugins/partiql-local/src/test/resources/catalogs/local/data/records.ion deleted file mode 100644 index 884915fa5d..0000000000 --- a/plugins/partiql-local/src/test/resources/catalogs/local/data/records.ion +++ /dev/null @@ -1,17 +0,0 @@ -{ - type: "bag", - items: { - type: "struct", - constraints: [ ordered, closed ], - fields: [ - { - name: "id", - type: "int", - }, - { - name: "path", - type: "string", - }, - ], - }, -} diff --git a/plugins/partiql-local/src/test/resources/catalogs/local/data/struct.ion b/plugins/partiql-local/src/test/resources/catalogs/local/data/struct.ion deleted file mode 100644 index cba96f5e2d..0000000000 --- a/plugins/partiql-local/src/test/resources/catalogs/local/data/struct.ion +++ /dev/null @@ -1,31 +0,0 @@ -{ - type: "struct", - constraints: [ - closed, - unique, - ordered - ], - fields: [ - { - name: "id", - type: "int", - }, - { - name: "nested", - type: { - type: "struct", - constraints: [ - closed, - unique, - ordered - ], - fields: [ - { - name: "nested_id", - type: "int", - }, - ], - }, - }, - ], -} From 40178949f4b468d9c308333aaed9195b6d2ca653 Mon Sep 17 00:00:00 2001 From: "R. C. Howell" Date: Mon, 8 Jul 2024 13:49:05 -0700 Subject: [PATCH 6/9] Make Catalog the primary interface --- .../planner/{metadata => catalog}/Name.kt | 6 +- .../org/partiql/planner/metadata/Metadata.kt | 12 --- .../org/partiql/planner/metadata/Namespace.kt | 61 ------------- .../org/partiql/planner/metadata/Routine.kt | 91 ------------------- .../org/partiql/planner/metadata/Table.kt | 40 -------- 5 files changed, 2 insertions(+), 208 deletions(-) rename partiql-planner/src/main/kotlin/org/partiql/planner/{metadata => catalog}/Name.kt (91%) delete mode 100644 partiql-planner/src/main/kotlin/org/partiql/planner/metadata/Metadata.kt delete mode 100644 partiql-planner/src/main/kotlin/org/partiql/planner/metadata/Namespace.kt delete mode 100644 partiql-planner/src/main/kotlin/org/partiql/planner/metadata/Routine.kt delete mode 100644 partiql-planner/src/main/kotlin/org/partiql/planner/metadata/Table.kt diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/metadata/Name.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/catalog/Name.kt similarity index 91% rename from partiql-planner/src/main/kotlin/org/partiql/planner/metadata/Name.kt rename to partiql-planner/src/main/kotlin/org/partiql/planner/catalog/Name.kt index 9cf27077ab..747ee87618 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/metadata/Name.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/catalog/Name.kt @@ -1,12 +1,10 @@ -package org.partiql.planner.metadata +package org.partiql.planner.catalog import java.util.Spliterator import java.util.function.Consumer /** * Thin wrapper over a list of strings. - * - * @property steps */ public data class Name(public val steps: List) : Iterable { @@ -23,4 +21,4 @@ public data class Name(public val steps: List) : Iterable { override fun iterator(): Iterator = steps.iterator() override fun spliterator(): Spliterator = steps.spliterator() -} \ No newline at end of file +} diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/metadata/Metadata.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/metadata/Metadata.kt deleted file mode 100644 index b525c24875..0000000000 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/metadata/Metadata.kt +++ /dev/null @@ -1,12 +0,0 @@ -package org.partiql.planner.metadata - -/** - * Top-level metadata interface for access to object descriptors. - */ -public interface Metadata { - - /** - * The root namespace. - */ - public fun getNamespace(): Namespace -} diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/metadata/Namespace.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/metadata/Namespace.kt deleted file mode 100644 index 750c43eba0..0000000000 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/metadata/Namespace.kt +++ /dev/null @@ -1,61 +0,0 @@ -package org.partiql.planner.metadata - -/** - * A [Namespace] is a namespace for the following objects; top-level namespaces are called "Catalogs". - * - * . Tables - * . Base - * . View - * . Index - * . Routines - * . Functions - * . Procedures - * . Namespaces - * . Types - * - * See, https://github.com/apache/calcite/blob/main/core/src/main/java/org/apache/calcite/schema/Schema.java - */ -public interface Namespace { - - /** - * The [Namespace] name. - */ - public fun getName(): String - - /** - * Get a table by name. - * - * @param name The case-sensitive [Table] name. - * @return The [Table] or null if not found. - */ - public fun getTable(name: String): Table? = null - - /** - * Get a function's variants by name. - * - * @param name The case-sensitive [Routine] name. - * @return A collection of all [Routine]s in the current namespace with this name. - */ - public fun getFunctions(name: String): Collection = DEFAULT_FUNCTIONS - - /** - * Get a sub-namespace by name. - * - * @param name - * @return - */ - public fun getNamespace(name: String): Namespace? = null - - /** - * Get all sub-namespaces. - */ - public fun getNamespaces(): Collection = DEFAULT_SCOPES - - /** - * Memoized defaults. - */ - private companion object { - val DEFAULT_FUNCTIONS = emptyList() - val DEFAULT_SCOPES = emptyList() - } -} diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/metadata/Routine.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/metadata/Routine.kt deleted file mode 100644 index a58453aa9a..0000000000 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/metadata/Routine.kt +++ /dev/null @@ -1,91 +0,0 @@ -package org.partiql.planner.metadata - -import org.partiql.types.PType - -/** - * A [Routine] is a PartiQL-routine callable from an expression context. - */ -public sealed interface Routine { - - /** - * The function name. Required. - */ - public fun getName(): String - - /** - * The formal argument definitions. Optional. - */ - public fun getParameters(): Array = DEFAULT_PARAMETERS - - /** - * The function return type. Required. - */ - public fun getReturnType(): PType.Kind - - /** - * Represents an SQL row-value expression call. - */ - public interface Operator : Routine { - public fun getSymbol(): String - public fun getLHS(): PType.Kind? - public fun getRHS(): PType.Kind - } - - /** - * Represents an SQL row-value expression call. - */ - public interface Scalar : Routine { - - /** - * Additional function properties useful for planning. Optional. - */ - public fun getProperties(): Properties = DEFAULT_PROPERTIES - } - - /** - * Represents an SQL table-value expression call. - */ - public interface Aggregation : Routine - - /** - * [Parameter] is a formal argument's definition. - * - * @property name - * @property type - */ - public data class Parameter( - @JvmField public val name: String, - @JvmField public val type: PType.Kind, - ) - - /** - * PartiQL-function properties. - * - * @property isNullCall - */ - public data class Properties( - @JvmField public val isNullCall: Boolean = false, - ) - - /** - * Memoized defaults. - */ - public companion object { - - private val DEFAULT_PARAMETERS = emptyArray() - private val DEFAULT_PROPERTIES = Properties(isNullCall = true) - - @JvmOverloads - public fun scalar( - name: String, - parameters: Collection, - returnType: PType.Kind, - properties: Properties = DEFAULT_PROPERTIES, - ): Scalar = object : Scalar { - override fun getName(): String = name - override fun getParameters(): Array = parameters.toTypedArray() - override fun getReturnType(): PType.Kind = returnType - override fun getProperties(): Properties = properties - } - } -} diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/metadata/Table.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/metadata/Table.kt deleted file mode 100644 index 8022f014d9..0000000000 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/metadata/Table.kt +++ /dev/null @@ -1,40 +0,0 @@ -package org.partiql.planner.metadata - -import org.partiql.types.PType - -/** - * In PartiQL, a [Table] can take on any shape and is not necessarily rows+columns. - * - * From Calcite, - * - * ''' - * Note that a table does not know its name. It is in fact possible for - * a table to be used more than once, perhaps under multiple names or under - * multiple schemas. (Compare with the i-node concept - * in the UNIX filesystem.) - * ''' - * - * See, https://github.com/apache/calcite/blob/main/core/src/main/java/org/apache/calcite/schema/Table.java - */ -public interface Table { - - /** - * The table's kind. - */ - public fun getKind(): Kind = Kind.TABLE - - /** - * The table's schema. - */ - public fun getSchema(): PType = PType.typeDynamic() - - /** - * A [Table] can be one of several [Kind]s. - */ - public enum class Kind { - TABLE, - VIEW, - INDEX, - OTHER, - } -} \ No newline at end of file From f7c1424448b4c466e0fcdeb4bf6e67efea65daa9 Mon Sep 17 00:00:00 2001 From: "R. C. Howell" Date: Mon, 8 Jul 2024 14:48:26 -0700 Subject: [PATCH 7/9] Updates MemoryConnector --- .../org/partiql/planner/catalog/Catalog.kt | 10 +-- .../org/partiql/planner/catalog/Name.kt | 2 + .../partiql/plugins/memory/MemoryCatalog.kt | 84 ------------------- .../partiql/plugins/memory/MemoryConnector.kt | 64 ++++++++++++-- .../partiql/plugins/memory/MemoryNamespace.kt | 39 --------- .../org/partiql/plugins/memory/MemoryTable.kt | 17 ++-- .../plugins/memory/MemoryConnectorTest.kt | 19 +++++ 7 files changed, 92 insertions(+), 143 deletions(-) delete mode 100644 plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryCatalog.kt delete mode 100644 plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryNamespace.kt create mode 100644 plugins/partiql-memory/src/test/kotlin/org/partiql/plugins/memory/MemoryConnectorTest.kt diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/catalog/Catalog.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/catalog/Catalog.kt index d14999cab1..cff4f3e271 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/catalog/Catalog.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/catalog/Catalog.kt @@ -25,14 +25,14 @@ public interface Catalog { /** * List top-level tables. */ - public fun listTables(): Name? = null + public fun listTables(): Collection = emptyList() /** * List all tables under this namespace. * * @param namespace */ - public fun listTables(namespace: Namespace): List = emptyList() + public fun listTables(namespace: Namespace): Collection = emptyList() /** * List top-level namespaces from the catalog. @@ -44,13 +44,13 @@ public interface Catalog { * * @param namespace */ - public fun listNamespaces(namespace: Namespace) + public fun listNamespaces(namespace: Namespace): Collection = emptyList() /** - * Get a function's variants by name. + * Get a routine's variants by name. * * @param name The case-sensitive [Routine] name. * @return A collection of all [Routine]s in the current namespace with this name. */ - public fun getFunctions(name: Name): Collection = emptyList() + public fun getRoutines(name: Name): Collection = emptyList() } diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/catalog/Name.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/catalog/Name.kt index 747ee87618..30235da7e8 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/catalog/Name.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/catalog/Name.kt @@ -14,6 +14,8 @@ public data class Name(public val steps: List) : Iterable { public fun of(vararg steps: String): Name = Name(steps.toList()) } + public fun getLength(): Int = steps.size + public operator fun get(index: Int): String = steps[index] override fun forEach(action: Consumer?): Unit = steps.forEach(action) diff --git a/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryCatalog.kt b/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryCatalog.kt deleted file mode 100644 index 3f7a2c2479..0000000000 --- a/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryCatalog.kt +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package org.partiql.plugins.memory - -import org.partiql.eval.bindings.Binding -import org.partiql.eval.bindings.Bindings -import org.partiql.planner.metadata.Metadata -import org.partiql.planner.metadata.Namespace - -/** - * A basic connector implementation used in testing. - * - * TODO incorporate the nested namespaces possible in the current v1 memory plugin. - * I'm keeping this simple for now before APIs stabilize. - * - * https://github.com/partiql/partiql-lang-kotlin/commit/f8cbeb83a8d4ba8f5218b9db016e0661d778441e - */ -public class MemoryCatalog private constructor( - private val name: String, - private val tables: Map, -) { - - // these could be anonymous, but I thought it was cleaner to separate. - private val bindings = MBindings() - private val metadata = MMetadata() - - public fun getBindings(): Bindings = bindings - public fun getMetadata(): Metadata = metadata - - public class Builder internal constructor() { - - private var name: String? = null - private var tables: MutableMap = mutableMapOf() - - public fun name(name: String): Builder = apply { this.name = name } - - public fun defineTable(name: String, table: MemoryTable): Builder = apply { tables[name] = table } - - public fun build(): MemoryCatalog = MemoryCatalog(name!!, tables) - } - - public companion object { - - @JvmStatic - public fun builder(): Builder = Builder() - } - - private inner class MBindings : Bindings { - override fun getBindings(name: String): Bindings? = null - override fun getBinding(name: String): Binding? = tables[name] - } - - private inner class MMetadata : Metadata { - - /** - * Build a root namespace. - */ - private val root = MemoryNamespace.builder() - .name(name) - .apply { - for (table in tables) { - val n = table.key - val t = table.value - defineTable(n, t) - } - } - .build() - - override fun getNamespace(): Namespace = root - } -} diff --git a/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryConnector.kt b/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryConnector.kt index abb90646fb..48e76c9957 100644 --- a/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryConnector.kt +++ b/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryConnector.kt @@ -16,23 +16,24 @@ package org.partiql.plugins.memory import com.amazon.ionelement.api.StructElement +import org.partiql.eval.bindings.Binding import org.partiql.eval.bindings.Bindings -import org.partiql.planner.metadata.Metadata +import org.partiql.planner.catalog.Catalog +import org.partiql.planner.catalog.Name +import org.partiql.planner.catalog.Table import org.partiql.spi.connector.Connector /** * This is a plugin used for testing and is not a versioned API per semver. */ -public class MemoryConnector private constructor(private val catalog: MemoryCatalog) : Connector { +public class MemoryConnector private constructor( + private val name: String, + private val tables: Map, +) : Connector { - public companion object { - - @JvmStatic - public fun from(catalog: MemoryCatalog): MemoryConnector = MemoryConnector(catalog) - } + override fun getBindings(): Bindings = bindings - override fun getBindings(): Bindings = catalog.getBindings() - override fun getMetadata(): Metadata = catalog.getMetadata() + override fun getCatalog(): Catalog = catalog /** * For use with ServiceLoader to instantiate a connector from an Ion config. @@ -45,4 +46,49 @@ public class MemoryConnector private constructor(private val catalog: MemoryCata TODO("Instantiation of a MemoryConnector via the factory is currently not supported") } } + + public companion object { + + @JvmStatic + public fun builder(): Builder = Builder() + + public class Builder internal constructor() { + + private var name: String? = null + private var tables: MutableMap = mutableMapOf() + + public fun name(name: String): Builder = apply { this.name = name } + + public fun createTable(table: MemoryTable): Builder = apply { tables[table.getName()] = table } + + public fun build(): MemoryConnector = MemoryConnector(name!!, tables) + } + } + + /** + * Implement [Bindings] over the tables map. + */ + private val bindings = object : Bindings { + override fun getBindings(name: String): Bindings? = null + override fun getBinding(name: String): Binding? = tables[name] + } + + /** + * Implement [Catalog] over the tables map. + */ + private val catalog = object : Catalog { + + override fun getName(): String = name + + override fun getTable(name: Name): Table? { + if (name.getLength() > 1) { + error("MemoryCatalog does not support namespaces") + } + return tables[name[0]] + } + + override fun listTables(): Collection { + return tables.keys.map { Name.of(it) } + } + } } diff --git a/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryNamespace.kt b/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryNamespace.kt deleted file mode 100644 index b1aeb5ef6a..0000000000 --- a/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryNamespace.kt +++ /dev/null @@ -1,39 +0,0 @@ -package org.partiql.plugins.memory - -import org.partiql.planner.metadata.Namespace -import org.partiql.planner.metadata.Table - -/** - * Namespace implementation. - */ -public class MemoryNamespace private constructor( - private val name: String, - private val tables: Map, - private val namespaces: Map, -) : Namespace { - override fun getName(): String = name - override fun getTable(name: String): Table? = tables[name] - override fun getNamespace(name: String): Namespace? = namespaces[name] - override fun getNamespaces(): Collection = namespaces.values - - public class Builder internal constructor() { - - private var name: String? = null - private var tables: MutableMap = mutableMapOf() - private var namespaces: MutableMap = mutableMapOf() - - public fun name(name: String): Builder = apply { this.name = name } - - public fun defineTable(name: String, table: MemoryTable): Builder = apply { tables[name] = table } - - public fun defineNamespace(namespace: MemoryNamespace): Builder = apply { namespaces[namespace.getName()] = namespace } - - public fun build(): MemoryNamespace = MemoryNamespace(name!!, tables, namespaces) - } - - public companion object { - - @JvmStatic - public fun builder(): Builder = Builder() - } -} diff --git a/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryTable.kt b/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryTable.kt index a3a2f6c7e1..6872cc3c5e 100644 --- a/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryTable.kt +++ b/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryTable.kt @@ -2,15 +2,16 @@ package org.partiql.plugins.memory import org.partiql.eval.bindings.Binding import org.partiql.eval.value.Datum -import org.partiql.planner.metadata.Table +import org.partiql.planner.catalog.Table import org.partiql.types.PType public class MemoryTable private constructor( + private val name: String, private val type: PType, private val datum: Datum, ) : Table, Binding { - override fun getKind(): Table.Kind = Table.Kind.TABLE + override fun getName(): String = name override fun getSchema(): PType = type override fun getDatum(): Datum = datum @@ -20,7 +21,8 @@ public class MemoryTable private constructor( * Create an empty table with dynamic schema. */ @JvmStatic - public fun empty(): MemoryTable = MemoryTable( + public fun empty(name: String): MemoryTable = MemoryTable( + name = name, type = PType.typeDynamic(), datum = Datum.nullValue(), ) @@ -29,7 +31,8 @@ public class MemoryTable private constructor( * Create an empty table with known schema. */ @JvmStatic - public fun empty(schema: PType): MemoryTable = MemoryTable( + public fun empty(name: String, schema: PType): MemoryTable = MemoryTable( + name = name, type = schema, datum = Datum.nullValue(), ) @@ -38,7 +41,8 @@ public class MemoryTable private constructor( * Create a table from a Datum with dynamic schema. */ @JvmStatic - public fun of(value: Datum): MemoryTable = MemoryTable( + public fun of(name: String, value: Datum): MemoryTable = MemoryTable( + name = name, type = PType.typeDynamic(), datum = value, ) @@ -47,7 +51,8 @@ public class MemoryTable private constructor( * Create a table from a Datum with known schema. */ @JvmStatic - public fun of(value: Datum, schema: PType): MemoryTable = MemoryTable( + public fun of(name: String, value: Datum, schema: PType): MemoryTable = MemoryTable( + name = name, type = schema, datum = value, ) diff --git a/plugins/partiql-memory/src/test/kotlin/org/partiql/plugins/memory/MemoryConnectorTest.kt b/plugins/partiql-memory/src/test/kotlin/org/partiql/plugins/memory/MemoryConnectorTest.kt new file mode 100644 index 0000000000..00e4b86d3b --- /dev/null +++ b/plugins/partiql-memory/src/test/kotlin/org/partiql/plugins/memory/MemoryConnectorTest.kt @@ -0,0 +1,19 @@ +package org.partiql.plugins.memory + +import org.junit.jupiter.api.Test + +class MemoryConnectorTest { + + @Test + fun sanity() { + val connector = MemoryConnector.builder() + .name("default") + .createTable(MemoryTable.empty("a")) + .createTable(MemoryTable.empty("b")) + .createTable(MemoryTable.empty("c")) + .build() + val catalog = connector.getCatalog() + assert(catalog.listTables().size == 3) + assert(catalog.listNamespaces().size == 0) + } +} From b81c80336dd5173069627a7816cedfdd3db3716d Mon Sep 17 00:00:00 2001 From: "R. C. Howell" Date: Mon, 8 Jul 2024 16:14:20 -0700 Subject: [PATCH 8/9] Updates partiql-local plugin --- .../plugins/local/LocalConnectorTest.kt | 11 +++ .../org/partiql/planner/catalog/Catalog.kt | 4 +- .../org/partiql/planner/catalog/Name.kt | 32 ++++---- .../org/partiql/planner/catalog/Namespace.kt | 9 +++ .../org/partiql/plugins/local/LocalCatalog.kt | 75 +++++++++++++++++++ .../partiql/plugins/local/LocalConnector.kt | 34 +++++++-- .../partiql/plugins/local/LocalNamespace.kt | 42 ----------- .../org/partiql/plugins/local/LocalTable.kt | 16 +++- .../partiql/plugins/memory/MemoryConnector.kt | 4 +- 9 files changed, 156 insertions(+), 71 deletions(-) create mode 100644 partiql-cli/src/test/kotlin/org/partiql/plugins/local/LocalConnectorTest.kt create mode 100644 plugins/partiql-local/src/main/kotlin/org/partiql/plugins/local/LocalCatalog.kt delete mode 100644 plugins/partiql-local/src/main/kotlin/org/partiql/plugins/local/LocalNamespace.kt diff --git a/partiql-cli/src/test/kotlin/org/partiql/plugins/local/LocalConnectorTest.kt b/partiql-cli/src/test/kotlin/org/partiql/plugins/local/LocalConnectorTest.kt new file mode 100644 index 0000000000..c18c1abfcb --- /dev/null +++ b/partiql-cli/src/test/kotlin/org/partiql/plugins/local/LocalConnectorTest.kt @@ -0,0 +1,11 @@ +package org.partiql.plugins.local + +import org.junit.jupiter.api.Test + +class LocalConnectorTest { + + @Test + fun sanity() { + TODO("Not yet implemented") + } +} diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/catalog/Catalog.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/catalog/Catalog.kt index cff4f3e271..8d2c3033ae 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/catalog/Catalog.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/catalog/Catalog.kt @@ -25,7 +25,7 @@ public interface Catalog { /** * List top-level tables. */ - public fun listTables(): Collection = emptyList() + public fun listTables(): Collection = listTables(Namespace.empty()) /** * List all tables under this namespace. @@ -37,7 +37,7 @@ public interface Catalog { /** * List top-level namespaces from the catalog. */ - public fun listNamespaces(): Collection = emptyList() + public fun listNamespaces(): Collection = listNamespaces(Namespace.empty()) /** * List all child namespaces from the namespace. diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/catalog/Name.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/catalog/Name.kt index 30235da7e8..3478018045 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/catalog/Name.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/catalog/Name.kt @@ -1,26 +1,28 @@ package org.partiql.planner.catalog -import java.util.Spliterator -import java.util.function.Consumer - /** * Thin wrapper over a list of strings. */ -public data class Name(public val steps: List) : Iterable { - - public companion object { +public data class Name( + private val namespace: Namespace, + private val name: String, +) { - @JvmStatic - public fun of(vararg steps: String): Name = Name(steps.toList()) - } + public fun getNamespace(): Namespace = namespace - public fun getLength(): Int = steps.size + public fun hasNamespace(): Boolean = !namespace.isEmpty() - public operator fun get(index: Int): String = steps[index] + public fun getName(): String = name - override fun forEach(action: Consumer?): Unit = steps.forEach(action) - - override fun iterator(): Iterator = steps.iterator() + public companion object { - override fun spliterator(): Spliterator = steps.spliterator() + @JvmStatic + public fun of(vararg names: String): Name { + assert(names.size > 1) { "Cannot create an empty" } + return Name( + namespace = Namespace.of(*names.drop(1).toTypedArray()), + name = names.last(), + ) + } + } } diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/catalog/Namespace.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/catalog/Namespace.kt index b77e3aac31..fbbb603d04 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/catalog/Namespace.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/catalog/Namespace.kt @@ -67,11 +67,20 @@ public class Namespace private constructor( public fun empty(): Namespace = EMPTY + @JvmStatic public fun of(vararg levels: String): Namespace { if (levels.isEmpty()) { return empty() } return Namespace(arrayOf(*levels)) } + + @JvmStatic + public fun of(levels: Collection): Namespace { + if (levels.isEmpty()) { + return empty() + } + return Namespace(levels.toTypedArray()) + } } } diff --git a/plugins/partiql-local/src/main/kotlin/org/partiql/plugins/local/LocalCatalog.kt b/plugins/partiql-local/src/main/kotlin/org/partiql/plugins/local/LocalCatalog.kt new file mode 100644 index 0000000000..4345398ac6 --- /dev/null +++ b/plugins/partiql-local/src/main/kotlin/org/partiql/plugins/local/LocalCatalog.kt @@ -0,0 +1,75 @@ +package org.partiql.plugins.local + +import org.partiql.planner.catalog.Catalog +import org.partiql.planner.catalog.Name +import org.partiql.planner.catalog.Namespace +import org.partiql.planner.catalog.Routine +import org.partiql.planner.catalog.Table +import java.nio.file.Path +import kotlin.io.path.isDirectory +import kotlin.io.path.notExists + +/** + * Implementation of [Catalog] where dirs are namespaces and files are table metadata. + */ +internal class LocalCatalog( + private val name: String, + private val root: Path, +) : Catalog { + + private companion object { + private const val EXT = ".ion" + } + + init { + assert(root.isDirectory()) { "LocalNamespace must be a directory" } + } + + override fun getName(): String { + return name + } + + override fun getTable(name: Name): Table? { + val path = toPath(name.getNamespace()).resolve(name.getName() + EXT) + if (path.notExists() || !path.isDirectory()) { + return null + } + return LocalTable(name.getName(), path) + } + + override fun listTables(namespace: Namespace): Collection { + val path = toPath(namespace) + if (path.notExists()) { + // throw exception? + return emptyList() + } + return super.listTables(namespace) + } + + override fun listNamespaces(namespace: Namespace): Collection { + val path = toPath(namespace) + if (path.notExists() || path.isDirectory()) { + // throw exception? + return emptyList() + } + // List all child directories + return path.toFile() + .listFiles()!! + .filter { it.isDirectory } + .map { toNamespace(it.toPath()) } + } + + override fun getRoutines(name: Name): Collection = emptyList() + + private fun toPath(namespace: Namespace): Path { + var curr = root + for (level in namespace) { + curr = curr.resolve(level) + } + return curr + } + + private fun toNamespace(path: Path): Namespace { + return Namespace.of(path.relativize(root).map { it.toString() }) + } +} diff --git a/plugins/partiql-local/src/main/kotlin/org/partiql/plugins/local/LocalConnector.kt b/plugins/partiql-local/src/main/kotlin/org/partiql/plugins/local/LocalConnector.kt index 9f25564272..302355fdec 100644 --- a/plugins/partiql-local/src/main/kotlin/org/partiql/plugins/local/LocalConnector.kt +++ b/plugins/partiql-local/src/main/kotlin/org/partiql/plugins/local/LocalConnector.kt @@ -16,8 +16,7 @@ package org.partiql.plugins.local import com.amazon.ionelement.api.StructElement import org.partiql.eval.bindings.Bindings -import org.partiql.planner.metadata.Metadata -import org.partiql.planner.metadata.Namespace +import org.partiql.planner.catalog.Catalog import org.partiql.spi.connector.Connector import java.nio.file.Path import java.nio.file.Paths @@ -36,18 +35,39 @@ import kotlin.io.path.notExists * } * ``` */ -public class LocalConnector(private val root: Path) : Connector { +public class LocalConnector private constructor( + private val name: String, + private val root: Path, +) : Connector { + + private val catalog = LocalCatalog(name, root) public companion object { + public const val CONNECTOR_NAME: String = "local" + public const val ROOT_KEY: String = "root" + + @JvmStatic + public fun builder(): Builder = Builder() + + public class Builder internal constructor() { + + private var name: String? = null + + private var root: Path? = null + + public fun name(name: String): Builder = apply { this.name = name } + + public fun root(root: Path): Builder = apply { this.root = root } + + public fun build(): LocalConnector = LocalConnector(name!!, root!!) + } } override fun getBindings(): Bindings = LocalBindings - override fun getMetadata(): Metadata = object : Metadata { - override fun getNamespace(): Namespace = LocalNamespace(root) - } + override fun getCatalog(): Catalog = catalog internal class Factory : Connector.Factory { @@ -61,7 +81,7 @@ public class LocalConnector(private val root: Path) : Connector { if (root.notExists() || !root.isDirectory()) { error("Invalid catalog `$root`") } - return LocalConnector(root) + return LocalConnector("default", root) } } } diff --git a/plugins/partiql-local/src/main/kotlin/org/partiql/plugins/local/LocalNamespace.kt b/plugins/partiql-local/src/main/kotlin/org/partiql/plugins/local/LocalNamespace.kt deleted file mode 100644 index edc66811df..0000000000 --- a/plugins/partiql-local/src/main/kotlin/org/partiql/plugins/local/LocalNamespace.kt +++ /dev/null @@ -1,42 +0,0 @@ -package org.partiql.plugins.local - -import org.partiql.planner.metadata.Namespace -import org.partiql.planner.metadata.Table -import java.nio.file.Path -import kotlin.io.path.isDirectory -import kotlin.io.path.isRegularFile -import kotlin.io.path.nameWithoutExtension - -/** - * Thin wrapper over a directory. - * - subdir -> namespace - * - file -> table - */ -public class LocalNamespace(private val path: Path) : Namespace { - - init { - assert(path.isDirectory()) { "LocalNamespace must be a directory" } - } - - override fun getName(): String = path.nameWithoutExtension - - override fun getNamespaces(): Collection { - return path.toFile().listFiles { f, _ -> f.isDirectory }!!.map { LocalNamespace(it.toPath()) } - } - - override fun getNamespace(name: String): Namespace? { - val p = path.resolve(name) - return when (p.isDirectory()) { - true -> LocalNamespace(p) - else -> null - } - } - - override fun getTable(name: String): Table? { - val p = path.resolve(name) - return when (p.isRegularFile()) { - true -> LocalTable(p) - else -> null - } - } -} diff --git a/plugins/partiql-local/src/main/kotlin/org/partiql/plugins/local/LocalTable.kt b/plugins/partiql-local/src/main/kotlin/org/partiql/plugins/local/LocalTable.kt index a63284215f..fa589265c6 100644 --- a/plugins/partiql-local/src/main/kotlin/org/partiql/plugins/local/LocalTable.kt +++ b/plugins/partiql-local/src/main/kotlin/org/partiql/plugins/local/LocalTable.kt @@ -18,18 +18,28 @@ import com.amazon.ion.system.IonReaderBuilder import com.amazon.ionelement.api.loadSingleElement import org.partiql.eval.bindings.Binding import org.partiql.eval.value.Datum -import org.partiql.planner.metadata.Table +import org.partiql.planner.catalog.Table import org.partiql.types.PType import org.partiql.types.StaticType import java.nio.file.Path +import kotlin.io.path.isDirectory import kotlin.io.path.reader /** * Associate a resolved path with a [StaticType] */ -internal class LocalTable(private val path: Path) : Table, Binding { +internal class LocalTable( + private val name: String, + private val path: Path, +) : Table, Binding { - override fun getKind(): Table.Kind = Table.Kind.TABLE + init { + assert(!path.isDirectory()) { "LocalTable path must be a file." } + } + + override fun getName(): String { + return name + } override fun getSchema(): PType { val reader = IonReaderBuilder.standard().build(path.reader()) diff --git a/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryConnector.kt b/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryConnector.kt index 48e76c9957..48276a5f60 100644 --- a/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryConnector.kt +++ b/plugins/partiql-memory/src/main/kotlin/org/partiql/plugins/memory/MemoryConnector.kt @@ -81,10 +81,10 @@ public class MemoryConnector private constructor( override fun getName(): String = name override fun getTable(name: Name): Table? { - if (name.getLength() > 1) { + if (name.hasNamespace()) { error("MemoryCatalog does not support namespaces") } - return tables[name[0]] + return tables[name.getName()] } override fun listTables(): Collection { From 7adc8a31b8493054996e3e03a30846746793679b Mon Sep 17 00:00:00 2001 From: "R. C. Howell" Date: Tue, 9 Jul 2024 09:33:28 -0700 Subject: [PATCH 9/9] Fix Connector.kt --- .../src/main/kotlin/org/partiql/spi/connector/Connector.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/partiql-spi/src/main/kotlin/org/partiql/spi/connector/Connector.kt b/partiql-spi/src/main/kotlin/org/partiql/spi/connector/Connector.kt index 4fc51c7b8b..10b5a72f48 100644 --- a/partiql-spi/src/main/kotlin/org/partiql/spi/connector/Connector.kt +++ b/partiql-spi/src/main/kotlin/org/partiql/spi/connector/Connector.kt @@ -17,7 +17,7 @@ package org.partiql.spi.connector import com.amazon.ionelement.api.StructElement import com.amazon.ionelement.api.emptyIonStruct import org.partiql.eval.bindings.Bindings -import org.partiql.planner.metadata.Metadata +import org.partiql.planner.catalog.Catalog /** * A mechanism by which PartiQL can access bindings and catalog metadata. @@ -30,9 +30,9 @@ public interface Connector { public fun getBindings(): Bindings /** - * Returns the root namespace of this catalog. + * Returns a [Catalog] which the planner uses to load catalog metadata. */ - public fun getMetadata(): org.partiql.planner.catalog.Catalog + public fun getCatalog(): Catalog /** * A Plugin leverages a [Factory] to produce a [Connector] which is used for binding and metadata access.