Skip to content

Commit

Permalink
Adds catalog interfaces to partiql-planner
Browse files Browse the repository at this point in the history
  • Loading branch information
RCHowell committed Jul 23, 2024
1 parent 73db367 commit 9477d3c
Show file tree
Hide file tree
Showing 11 changed files with 1,475 additions and 0 deletions.
284 changes: 284 additions & 0 deletions partiql-planner/api/partiql-planner.api

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package org.partiql.planner.catalog

/**
* Catalog interface for access to tables and routines.
*
* Related
* - Iceberg — https://github.com/apache/iceberg/blob/main/api/src/main/java/org/apache/iceberg/catalog/Catalog.java
* - Calcite — https://github.com/apache/calcite/blob/main/core/src/main/java/org/apache/calcite/schema/Schema.java
*/
public interface Catalog {

/**
* Returns the catalog name.
*/
public fun getName(): String

/**
* Get a table by name.
*/
public fun getTable(session: Session, name: Name): Table? = null

/**
* Given an [Identifier], returns a [Table.Handle] that corresponds to the longest-available requested path.
*
* For example, given a table named "Table" located within Catalog "AWS" and Namespace "a".b"."c", a user could
* call [getTableHandle] with the identifier "a"."b"."c"."Table". The returned [Table.Handle] will contain the table
* representation and the matching path: "a"."b"."c"."Table"
*
* As another example, consider a table within a [Namespace] that may be a struct with nested attributes.
* A user could call [getTableHandle] with the identifier "a"."b"."c"."Table"."x". In the Namespace, only table
* "Table" exists. Therefore, this method will return a [Table.Handle] with the "Table" representation and the
* matching path: "a"."b"."c"."Table".
*
* IMPORTANT: The returned [Table.Handle.name] must be correct for correct evaluation.
*
* If the [Identifier] does not correspond to an existing [Table], implementers should return null.
*/
public fun getTableHandle(session: Session, identifier: Identifier): Table.Handle? = null

/**
* List top-level tables.
*/
public fun listTables(session: Session): Collection<Name> = listTables(session, Namespace.empty())

/**
* List all tables under this namespace.
*/
public fun listTables(session: Session, namespace: Namespace): Collection<Name> = emptyList()

/**
* List top-level namespaces from the catalog.
*/
public fun listNamespaces(session: Session): Collection<Namespace> = listNamespaces(session, Namespace.empty())

/**
* List all child namespaces from the namespace.
*
* @param namespace
*/
public fun listNamespaces(session: Session, namespace: Namespace): Collection<Namespace> = emptyList()

/**
* Get a routine's variants by name; the default implementation is backed by the SQL-99 builtins.
*/
public fun getFunctions(session: Session, name: Name): Collection<Function> {
// if (name.hasNamespace()) {
// error("The default catalog implementation does not support namespaced functions, found: $name")
// }
// return SqlFunctions.getFunctions(name.getName())
error("Catalog functions are not implemented.")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package org.partiql.planner.catalog

/**
* Catalogs is used to provide the default catalog and possibly others by name.
*/
public interface Catalogs {

/**
* Returns the default catalog. Required.
*/
public fun default(): Catalog

/**
* Returns a catalog by name (single identifier).
*/
public fun get(name: String, ignoreCase: Boolean = false): Catalog? {
val default = default()
return if (name.equals(default.getName(), ignoreCase)) {
default
} else {
null
}
}

/**
* Returns a list of all available catalogs.
*/
public fun list(): Collection<Catalog> = listOf(default())

/**
* Factory methods and builder.
*/
public companion object {

@JvmStatic
public fun of(vararg catalogs: Catalog): Catalogs = of(catalogs.toList())

@JvmStatic
public fun of(catalogs: Collection<Catalog>): Catalogs {
if (catalogs.isEmpty()) {
error("Cannot create `Catalogs` with empty catalogs list.")
}
return builder().apply { catalogs.forEach { add(it) } }.build()
}

@JvmStatic
public fun builder(): Builder = Builder()
}

/**
* Java-style builder for a default [Catalogs] implementation.
*/
public class Builder {

private var default: Catalog? = null
private val catalogs = mutableMapOf<String, Catalog>()

/**
* Sets the default catalog.
*/
public fun default(default: Catalog): Builder = this.apply {
this.default = default
catalogs[default.getName()] = default
}

/**
* Adds this catalog, overwriting any existing one with the same name.
*/
public fun add(catalog: Catalog): Builder = this.apply {
if (default == null) {
this.default = catalog
}
catalogs[catalog.getName()] = catalog
}

public fun build(): Catalogs {

val default = default ?: error("Default catalog is required")

return object : Catalogs {

override fun default(): Catalog = default

override fun get(name: String, ignoreCase: Boolean): Catalog? {
if (ignoreCase) {
// search
var match: Catalog? = null
for (catalog in list()) {
if (catalog.getName().equals(name, ignoreCase = true)) {
if (match != null) {
// TODO exceptions for ambiguous catalog name lookup
error("Catalog name is ambiguous, found more than one match.")
} else {
match = catalog
}
}
}
return match
}
// lookup
return catalogs[name]
}

override fun list(): Collection<Catalog> = catalogs.values
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package org.partiql.planner.catalog

import org.partiql.planner.internal.SqlTypes
import org.partiql.types.PType

/**
* A [Function] is a PartiQL-routine callable from an expression context.
*/
public sealed interface Function {

/**
* The routine name. Required.
*/
public fun getName(): String

/**
* The formal argument definitions. Optional.
*/
public fun getParameters(): Array<Parameter> = DEFAULT_PARAMETERS

/**
* The function return type. Required.
*/
public fun getReturnType(): PType.Kind

/**
* Compute a [PType] from the given arguments.
*/
public fun computeReturnType(args: List<PType>): PType = SqlTypes.from(getReturnType())

/**
* !! DO NOT OVERRIDE !!
*/
public fun getSpecific(): String

/**
* Represents an SQL row-value expression call.
*/
public interface Scalar : Function {

/**
* SQL NULL CALL -> RETURNS NULL ON NULL INPUT
*/
public fun isNullCall(): Boolean = true

/**
* !! DO NOT OVERRIDE !!
*/
public override fun getSpecific(): String {
val name = getName().uppercase()
val parameters = getParameters().joinToString("__") { it.type.name }
val returnType = getReturnType().name
return "FN_${name}___${parameters}___$returnType"
}
}

/**
* Represents an SQL table-value expression call.
*/
public interface Aggregation : Function {

public fun isDecomposable(): Boolean = true

/**
* !! DO NOT OVERRIDE !!
*/
public override fun getSpecific(): String {
val name = getName()
val parameters = getParameters().joinToString("__") { it.type.name }
val returnType = getReturnType().name
return "AGG_${name}___${parameters}___$returnType"
}
}

/**
* [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,
)

/**
* Memoized defaults.
*/
public companion object {

private val DEFAULT_PARAMETERS = emptyArray<Parameter>()

@JvmStatic
public fun scalar(
name: String,
parameters: Collection<Parameter>,
returnType: PType.Kind,
): Scalar = object : Scalar {
override fun getName(): String = name
override fun getParameters(): Array<Parameter> = parameters.toTypedArray()
override fun getReturnType(): PType.Kind = returnType
}

@JvmStatic
public fun aggregation(
name: String,
parameters: Collection<Parameter>,
returnType: PType.Kind,
): Aggregation = object : Aggregation {
override fun getName(): String = name
override fun getParameters(): Array<Parameter> = parameters.toTypedArray()
override fun getReturnType(): PType.Kind = returnType
}
}
}
Loading

0 comments on commit 9477d3c

Please sign in to comment.