diff --git a/.github/workflows/benchmark-main.yml b/.github/workflows/benchmark-main.yml index e038446cbb..0e32323c76 100644 --- a/.github/workflows/benchmark-main.yml +++ b/.github/workflows/benchmark-main.yml @@ -7,10 +7,8 @@ name: JMH Benchmarks (Main) -on: - push: - branches: - - main +# TODO: Make this run on pushes to main once appropriate benchmarks have been written. +on: [workflow_dispatch] env: JMH_RESULTS_PATH_FROM_ROOT: partiql-lang/build/reports/jmh/results.json diff --git a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/Env.kt b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/Env.kt index da8e6ed8fb..510c00d864 100644 --- a/partiql-planner/src/main/kotlin/org/partiql/planner/internal/Env.kt +++ b/partiql-planner/src/main/kotlin/org/partiql/planner/internal/Env.kt @@ -58,12 +58,12 @@ internal class Env(private val session: Session) { // 1. Search in current catalog and namespace var catalog = default var path = resolve(identifier) - var table = catalog.getTable(session, path) + var table = catalog.resolveTable(session, path)?.let { catalog.getTable(session, it) } // 2. Lookup as a schema-qualified identifier. if (table == null && identifier.hasQualifier()) { path = identifier - table = catalog.getTable(session, path) + table = catalog.resolveTable(session, path)?.let { catalog.getTable(session, it) } } // 3. Lookup as a catalog-qualified identifier @@ -73,7 +73,7 @@ internal class Env(private val session: Session) { val tail = parts.drop(1) catalog = catalogs.getCatalog(head.getText(), ignoreCase = head.isRegular()) ?: return null path = Identifier.of(tail) - table = catalog.getTable(session, path) + table = catalog.resolveTable(session, path)?.let { catalog.getTable(session, it) } } // !! NOT FOUND !! diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/TestCatalog.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/TestCatalog.kt index 39075fc60b..960c14a661 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/internal/TestCatalog.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/internal/TestCatalog.kt @@ -20,15 +20,19 @@ public class TestCatalog private constructor( override fun getName(): String = name override fun getTable(session: Session, name: Name): Table? { - return null + var curr: Tree = root + for (part in name) { + curr = curr.get(Identifier.delimited(part).first()) ?: break + } + return curr.table } - override fun getTable(session: Session, identifier: Identifier): Table? { + override fun resolveTable(session: Session, identifier: Identifier): Name? { var curr: Tree = root for (part in identifier) { curr = curr.get(part) ?: break } - return curr.table + return curr.table?.getName() } private class Tree( diff --git a/partiql-planner/src/test/kotlin/org/partiql/planner/plugins/local/LocalCatalog.kt b/partiql-planner/src/test/kotlin/org/partiql/planner/plugins/local/LocalCatalog.kt index 70ecea4984..c1cd967db4 100644 --- a/partiql-planner/src/test/kotlin/org/partiql/planner/plugins/local/LocalCatalog.kt +++ b/partiql-planner/src/test/kotlin/org/partiql/planner/plugins/local/LocalCatalog.kt @@ -38,7 +38,7 @@ public class LocalCatalog internal constructor( /** * TODO this doesn't handle ambiguous binding errors or back-tracking for longest prefix searching. */ - override fun getTable(session: Session, identifier: Identifier): Table? { + override fun resolveTable(session: Session, identifier: Identifier): Name? { val matched = mutableListOf() var curr = root for (part in identifier) { @@ -62,8 +62,7 @@ public class LocalCatalog internal constructor( return null } // Remove the extension - val name = Name.of(matched) - return LocalTable(name, path) + return Name.of(matched) } // TODO preserving this logic if catalog regains the listing APIs. diff --git a/partiql-spi/api/partiql-spi.api b/partiql-spi/api/partiql-spi.api index 7b9571a09f..4d308d4b52 100644 --- a/partiql-spi/api/partiql-spi.api +++ b/partiql-spi/api/partiql-spi.api @@ -73,8 +73,8 @@ public abstract interface class org/partiql/spi/catalog/Catalog { public abstract fun getAggregations (Lorg/partiql/spi/catalog/Session;Ljava/lang/String;)Ljava/util/Collection; public abstract fun getFunctions (Lorg/partiql/spi/catalog/Session;Ljava/lang/String;)Ljava/util/Collection; public abstract fun getName ()Ljava/lang/String; - public abstract fun getTable (Lorg/partiql/spi/catalog/Session;Lorg/partiql/spi/catalog/Identifier;)Lorg/partiql/spi/catalog/Table; public abstract fun getTable (Lorg/partiql/spi/catalog/Session;Lorg/partiql/spi/catalog/Name;)Lorg/partiql/spi/catalog/Table; + public abstract fun resolveTable (Lorg/partiql/spi/catalog/Session;Lorg/partiql/spi/catalog/Identifier;)Lorg/partiql/spi/catalog/Name; } public final class org/partiql/spi/catalog/Catalog$Builder { @@ -90,14 +90,13 @@ public final class org/partiql/spi/catalog/Catalog$Companion { public final class org/partiql/spi/catalog/Catalog$DefaultImpls { public static fun getAggregations (Lorg/partiql/spi/catalog/Catalog;Lorg/partiql/spi/catalog/Session;Ljava/lang/String;)Ljava/util/Collection; public static fun getFunctions (Lorg/partiql/spi/catalog/Catalog;Lorg/partiql/spi/catalog/Session;Ljava/lang/String;)Ljava/util/Collection; - public static fun getTable (Lorg/partiql/spi/catalog/Catalog;Lorg/partiql/spi/catalog/Session;Lorg/partiql/spi/catalog/Identifier;)Lorg/partiql/spi/catalog/Table; public static fun getTable (Lorg/partiql/spi/catalog/Catalog;Lorg/partiql/spi/catalog/Session;Lorg/partiql/spi/catalog/Name;)Lorg/partiql/spi/catalog/Table; + public static fun resolveTable (Lorg/partiql/spi/catalog/Catalog;Lorg/partiql/spi/catalog/Session;Lorg/partiql/spi/catalog/Identifier;)Lorg/partiql/spi/catalog/Name; } public abstract interface class org/partiql/spi/catalog/Catalogs { public static final field Companion Lorg/partiql/spi/catalog/Catalogs$Companion; public static fun builder ()Lorg/partiql/spi/catalog/Catalogs$Builder; - public static fun empty ()Lorg/partiql/spi/catalog/Catalogs; public abstract fun getCatalog (Ljava/lang/String;Z)Lorg/partiql/spi/catalog/Catalog; public static fun of (Ljava/util/Collection;)Lorg/partiql/spi/catalog/Catalogs; public static fun of ([Lorg/partiql/spi/catalog/Catalog;)Lorg/partiql/spi/catalog/Catalogs; @@ -111,7 +110,6 @@ public final class org/partiql/spi/catalog/Catalogs$Builder { public final class org/partiql/spi/catalog/Catalogs$Companion { public final fun builder ()Lorg/partiql/spi/catalog/Catalogs$Builder; - public final fun empty ()Lorg/partiql/spi/catalog/Catalogs; public final fun of (Ljava/util/Collection;)Lorg/partiql/spi/catalog/Catalogs; public final fun of ([Lorg/partiql/spi/catalog/Catalog;)Lorg/partiql/spi/catalog/Catalogs; } @@ -126,7 +124,7 @@ public final class org/partiql/spi/catalog/Identifier : java/lang/Iterable, kotl public final fun append (Lorg/partiql/spi/catalog/Identifier;)Lorg/partiql/spi/catalog/Identifier; public final fun append ([Lorg/partiql/spi/catalog/Identifier$Part;)Lorg/partiql/spi/catalog/Identifier; public static final fun delimited (Ljava/lang/String;)Lorg/partiql/spi/catalog/Identifier; - public static final fun delimited (Ljava/util/Collection;)Lorg/partiql/spi/catalog/Identifier; + public static final fun delimited (Ljava/util/List;)Lorg/partiql/spi/catalog/Identifier; public static final fun delimited ([Ljava/lang/String;)Lorg/partiql/spi/catalog/Identifier; public fun equals (Ljava/lang/Object;)Z public fun forEach (Ljava/util/function/Consumer;)V @@ -138,10 +136,10 @@ public final class org/partiql/spi/catalog/Identifier : java/lang/Iterable, kotl public fun iterator ()Ljava/util/Iterator; public final fun matches (Ljava/lang/String;Z)Z public static synthetic fun matches$default (Lorg/partiql/spi/catalog/Identifier;Ljava/lang/String;ZILjava/lang/Object;)Z - public static final fun of (Ljava/util/Collection;)Lorg/partiql/spi/catalog/Identifier; + public static final fun of (Ljava/util/List;)Lorg/partiql/spi/catalog/Identifier; public static final fun of ([Lorg/partiql/spi/catalog/Identifier$Part;)Lorg/partiql/spi/catalog/Identifier; public static final fun regular (Ljava/lang/String;)Lorg/partiql/spi/catalog/Identifier; - public static final fun regular (Ljava/util/Collection;)Lorg/partiql/spi/catalog/Identifier; + public static final fun regular (Ljava/util/List;)Lorg/partiql/spi/catalog/Identifier; public static final fun regular ([Ljava/lang/String;)Lorg/partiql/spi/catalog/Identifier; public fun spliterator ()Ljava/util/Spliterator; public fun toString ()Ljava/lang/String; @@ -149,12 +147,12 @@ public final class org/partiql/spi/catalog/Identifier : java/lang/Iterable, kotl public final class org/partiql/spi/catalog/Identifier$Companion { public final fun delimited (Ljava/lang/String;)Lorg/partiql/spi/catalog/Identifier; - public final fun delimited (Ljava/util/Collection;)Lorg/partiql/spi/catalog/Identifier; + public final fun delimited (Ljava/util/List;)Lorg/partiql/spi/catalog/Identifier; public final fun delimited ([Ljava/lang/String;)Lorg/partiql/spi/catalog/Identifier; - public final fun of (Ljava/util/Collection;)Lorg/partiql/spi/catalog/Identifier; + public final fun of (Ljava/util/List;)Lorg/partiql/spi/catalog/Identifier; public final fun of ([Lorg/partiql/spi/catalog/Identifier$Part;)Lorg/partiql/spi/catalog/Identifier; public final fun regular (Ljava/lang/String;)Lorg/partiql/spi/catalog/Identifier; - public final fun regular (Ljava/util/Collection;)Lorg/partiql/spi/catalog/Identifier; + public final fun regular (Ljava/util/List;)Lorg/partiql/spi/catalog/Identifier; public final fun regular ([Ljava/lang/String;)Lorg/partiql/spi/catalog/Identifier; } @@ -186,14 +184,14 @@ public final class org/partiql/spi/catalog/Name : java/lang/Iterable, kotlin/jvm public final fun hasNamespace ()Z public fun hashCode ()I public fun iterator ()Ljava/util/Iterator; - public static final fun of (Ljava/util/Collection;)Lorg/partiql/spi/catalog/Name; + public static final fun of (Ljava/util/List;)Lorg/partiql/spi/catalog/Name; public static final fun of ([Ljava/lang/String;)Lorg/partiql/spi/catalog/Name; public fun spliterator ()Ljava/util/Spliterator; public fun toString ()Ljava/lang/String; } public final class org/partiql/spi/catalog/Name$Companion { - public final fun of (Ljava/util/Collection;)Lorg/partiql/spi/catalog/Name; + public final fun of (Ljava/util/List;)Lorg/partiql/spi/catalog/Name; public final fun of ([Ljava/lang/String;)Lorg/partiql/spi/catalog/Name; } @@ -210,7 +208,7 @@ public final class org/partiql/spi/catalog/Namespace : java/lang/Iterable, kotli public fun hashCode ()I public final fun isEmpty ()Z public fun iterator ()Ljava/util/Iterator; - public static final fun of (Ljava/util/Collection;)Lorg/partiql/spi/catalog/Namespace; + public static final fun of (Ljava/util/List;)Lorg/partiql/spi/catalog/Namespace; public static final fun of ([Ljava/lang/String;)Lorg/partiql/spi/catalog/Namespace; public fun spliterator ()Ljava/util/Spliterator; public fun toString ()Ljava/lang/String; @@ -218,7 +216,7 @@ public final class org/partiql/spi/catalog/Namespace : java/lang/Iterable, kotli public final class org/partiql/spi/catalog/Namespace$Companion { public final fun empty ()Lorg/partiql/spi/catalog/Namespace; - public final fun of (Ljava/util/Collection;)Lorg/partiql/spi/catalog/Namespace; + public final fun of (Ljava/util/List;)Lorg/partiql/spi/catalog/Namespace; public final fun of ([Ljava/lang/String;)Lorg/partiql/spi/catalog/Namespace; } @@ -257,10 +255,11 @@ public final class org/partiql/spi/catalog/Session$Builder { public final fun catalog (Ljava/lang/String;)Lorg/partiql/spi/catalog/Session$Builder; public final fun catalogs ([Lorg/partiql/spi/catalog/Catalog;)Lorg/partiql/spi/catalog/Session$Builder; public final fun identity (Ljava/lang/String;)Lorg/partiql/spi/catalog/Session$Builder; - public final fun namespace (Ljava/util/Collection;)Lorg/partiql/spi/catalog/Session$Builder; + public final fun namespace (Ljava/util/List;)Lorg/partiql/spi/catalog/Session$Builder; public final fun namespace (Lorg/partiql/spi/catalog/Namespace;)Lorg/partiql/spi/catalog/Session$Builder; public final fun namespace ([Ljava/lang/String;)Lorg/partiql/spi/catalog/Session$Builder; public final fun property (Ljava/lang/String;Ljava/lang/String;)Lorg/partiql/spi/catalog/Session$Builder; + public final fun system (Lorg/partiql/spi/catalog/Catalog;)Lorg/partiql/spi/catalog/Session$Builder; } public final class org/partiql/spi/catalog/Session$Companion { diff --git a/partiql-spi/src/main/java/org/partiql/spi/catalog/System.java b/partiql-spi/src/main/java/org/partiql/spi/catalog/System.java new file mode 100644 index 0000000000..a1a6a3b461 --- /dev/null +++ b/partiql-spi/src/main/java/org/partiql/spi/catalog/System.java @@ -0,0 +1,60 @@ +package org.partiql.spi.catalog; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.partiql.spi.function.Aggregation; +import org.partiql.spi.function.Builtins; +import org.partiql.spi.function.Function; + +import java.util.Collection; + +/** + *

+ * This package-private class implements the PartiQL System Catalog. + *

+ *

+ * It provides the implementation for the PartiQL System Catalog, which is a built-in catalog + * that provides access to the PartiQL language and its built-in functions and aggregations. + *

+ * @see Session.Builder + */ +final class System implements Catalog { + + @NotNull + private static final String NAME = "$system"; + + /** + * This is a package-private singleton. + */ + static System INSTANCE = new System(); + + @NotNull + @Override + public String getName() { + return NAME; + } + + @Nullable + @Override + public Table getTable(@NotNull Session session, @NotNull Name name) { + return null; + } + + @Nullable + @Override + public Name resolveTable(@NotNull Session session, @NotNull Identifier identifier) { + return null; + } + + @NotNull + @Override + public Collection getFunctions(@NotNull Session session, @NotNull String name) { + return Builtins.INSTANCE.getFunctions(name); + } + + @NotNull + @Override + public Collection getAggregations(@NotNull Session session, @NotNull String name) { + return Builtins.INSTANCE.getAggregations(name); + } +} diff --git a/partiql-spi/src/main/kotlin/org/partiql/spi/catalog/Catalog.kt b/partiql-spi/src/main/kotlin/org/partiql/spi/catalog/Catalog.kt index d7233390bb..19a6bcb7eb 100644 --- a/partiql-spi/src/main/kotlin/org/partiql/spi/catalog/Catalog.kt +++ b/partiql-spi/src/main/kotlin/org/partiql/spi/catalog/Catalog.kt @@ -41,7 +41,7 @@ public interface Catalog { * 2. Invoke getTable("a"."b"."c"."Example"."x") * 3. The implementation MUST match "a"."b"."c"."Example" to a.b.c.Example (note "x" does not match a table) */ - public fun getTable(session: Session, identifier: Identifier): Table? = null + public fun resolveTable(session: Session, identifier: Identifier): Name? = null /** * Returns a collection of scalar functions in this catalog with the given name, or an empty list if none. diff --git a/partiql-spi/src/main/kotlin/org/partiql/spi/catalog/Catalogs.kt b/partiql-spi/src/main/kotlin/org/partiql/spi/catalog/Catalogs.kt index 72d90ed260..32426ea1cd 100644 --- a/partiql-spi/src/main/kotlin/org/partiql/spi/catalog/Catalogs.kt +++ b/partiql-spi/src/main/kotlin/org/partiql/spi/catalog/Catalogs.kt @@ -25,20 +25,6 @@ public interface Catalogs { return builder().apply { catalogs.forEach { add(it) } }.build() } - /** - * A catalog provider that always returns an empty catalog. - */ - @JvmStatic - public fun empty(): Catalogs = object : Catalogs { - private val EMPTY = object : Catalog { - override fun getName(): String = "empty" - override fun getTable(session: Session, name: Name): Table? = null - override fun getTable(session: Session, identifier: Identifier): Table? = null - } - - override fun getCatalog(name: String, ignoreCase: Boolean): Catalog = EMPTY - } - @JvmStatic public fun builder(): Builder = Builder() } diff --git a/partiql-spi/src/main/kotlin/org/partiql/spi/catalog/Identifier.kt b/partiql-spi/src/main/kotlin/org/partiql/spi/catalog/Identifier.kt index fd9c7a93dc..bacba281e4 100644 --- a/partiql-spi/src/main/kotlin/org/partiql/spi/catalog/Identifier.kt +++ b/partiql-spi/src/main/kotlin/org/partiql/spi/catalog/Identifier.kt @@ -30,7 +30,7 @@ public class Identifier private constructor( public fun hasQualifier(): Boolean = qualifier.isNotEmpty() /** - * Returns a collection of the identifier parts. + * Returns an ordered collection of the identifier parts. */ public fun getParts(): List { return listOf(*qualifier) + identifier @@ -195,7 +195,7 @@ public class Identifier private constructor( public fun regular(vararg parts: String): Identifier = regular(parts.toList()) @JvmStatic - public fun regular(parts: Collection): Identifier { + public fun regular(parts: List): Identifier { if (parts.isEmpty()) { error("Cannot create an identifier with no parts") } @@ -211,7 +211,7 @@ public class Identifier private constructor( public fun delimited(vararg parts: String): Identifier = delimited(parts.toList()) @JvmStatic - public fun delimited(parts: Collection): Identifier { + public fun delimited(parts: List): Identifier { if (parts.isEmpty()) { error("Cannot create an identifier with no parts") } @@ -224,7 +224,7 @@ public class Identifier private constructor( public fun of(vararg parts: Part): Identifier = of(parts.toList()) @JvmStatic - public fun of(parts: Collection): Identifier { + public fun of(parts: List): Identifier { if (parts.isEmpty()) { error("Cannot create an identifier with no parts") } diff --git a/partiql-spi/src/main/kotlin/org/partiql/spi/catalog/Name.kt b/partiql-spi/src/main/kotlin/org/partiql/spi/catalog/Name.kt index a089be9f13..5611efd44e 100644 --- a/partiql-spi/src/main/kotlin/org/partiql/spi/catalog/Name.kt +++ b/partiql-spi/src/main/kotlin/org/partiql/spi/catalog/Name.kt @@ -88,10 +88,10 @@ public class Name( public fun of(vararg names: String): Name = of(names.toList()) /** - * Construct a name from a collection of strings. + * Construct a name from an ordered collection of strings. */ @JvmStatic - public fun of(names: Collection): Name { + public fun of(names: List): Name { assert(names.size > 0) { "Cannot create an empty name" } val namespace = Namespace.of(names.take(names.size - 1)) val name = names.last() diff --git a/partiql-spi/src/main/kotlin/org/partiql/spi/catalog/Namespace.kt b/partiql-spi/src/main/kotlin/org/partiql/spi/catalog/Namespace.kt index 170bf5b989..ee09c06759 100644 --- a/partiql-spi/src/main/kotlin/org/partiql/spi/catalog/Namespace.kt +++ b/partiql-spi/src/main/kotlin/org/partiql/spi/catalog/Namespace.kt @@ -92,7 +92,7 @@ public class Namespace private constructor( } @JvmStatic - public fun of(levels: Collection): Namespace { + public fun of(levels: List): Namespace { if (levels.isEmpty()) { return empty() } diff --git a/partiql-spi/src/main/kotlin/org/partiql/spi/catalog/Session.kt b/partiql-spi/src/main/kotlin/org/partiql/spi/catalog/Session.kt index fd114a82f1..42c633bdc7 100644 --- a/partiql-spi/src/main/kotlin/org/partiql/spi/catalog/Session.kt +++ b/partiql-spi/src/main/kotlin/org/partiql/spi/catalog/Session.kt @@ -46,11 +46,8 @@ public interface Session { * Returns a [Session] with only the "empty" catalog implementation. */ @JvmStatic - public fun empty(): Session = object : Session { - override fun getIdentity(): String = "unknown" - override fun getCatalog(): String = "empty" - override fun getCatalogs(): Catalogs = Catalogs.empty() - override fun getNamespace(): Namespace = Namespace.empty() + public fun empty(): Session { + return builder().catalog(System.INSTANCE.getName()).catalogs().build() } @JvmStatic @@ -64,6 +61,7 @@ public interface Session { private var identity: String = "unknown" private var catalog: String? = null + private var system: Catalog = System.INSTANCE private var catalogs: Catalogs.Builder = Catalogs.builder() private var namespace: Namespace = Namespace.empty() private var properties: MutableMap = mutableMapOf() @@ -88,7 +86,7 @@ public interface Session { return this } - public fun namespace(levels: Collection): Builder { + public fun namespace(levels: List): Builder { this.namespace = Namespace.of(levels) return this } @@ -98,6 +96,16 @@ public interface Session { return this } + /** + * Adds and designates a catalog to always be on the SQL-Path. This [catalog] provides all built-in functions + * to the system at hand. + * If this is never invoked, a default system catalog is provided. + */ + public fun system(catalog: Catalog): Builder { + this.system = catalog + return this + } + /** * Adds catalogs to this session. */ @@ -110,16 +118,24 @@ public interface Session { public fun build(): Session = object : Session { - private val _catalogs = catalogs.build() + private val _catalogs: Catalogs + private val systemCatalogNamespace: Namespace = Namespace.of(system.getName()) init { require(catalog != null) { "Session catalog must be set" } + catalogs.add(system) + _catalogs = catalogs.build() } override fun getIdentity(): String = identity override fun getCatalog(): String = catalog!! override fun getCatalogs(): Catalogs = _catalogs override fun getNamespace(): Namespace = namespace + + override fun getPath(): Path { + val currentNamespace = getNamespace() + return Path.of(currentNamespace, systemCatalogNamespace) + } } } } diff --git a/partiql-spi/src/main/kotlin/org/partiql/spi/catalog/impl/StandardCatalog.kt b/partiql-spi/src/main/kotlin/org/partiql/spi/catalog/impl/StandardCatalog.kt index d2f6d42b1b..83a43763ca 100644 --- a/partiql-spi/src/main/kotlin/org/partiql/spi/catalog/impl/StandardCatalog.kt +++ b/partiql-spi/src/main/kotlin/org/partiql/spi/catalog/impl/StandardCatalog.kt @@ -26,14 +26,14 @@ internal class StandardCatalog( /** * TODO implement "longest match" on identifier searching. */ - override fun getTable(session: Session, identifier: Identifier): Table? { + override fun resolveTable(session: Session, identifier: Identifier): Name? { // TODO memory connector does not handle qualified identifiers and longest match val first = identifier.first() - for ((name, table) in tables) { + for ((name, _) in tables) { val str = name.getName() // only use single identifiers for now if (first.matches(str)) { // TODO emit errors on ambiguous table names - return table + return name } } return null