Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ bigquery = ["ibis-framework[bigquery]>=11.0.0"]
pyspark = ["setuptools", "ibis-framework[pyspark]>=11.0.0"]
clickhouse = ["ibis-framework[clickhouse]>=11.0.0"]
databricks = ["databricks-sql-connector>=4", "ibis-framework[databricks]>=11.0.0"]
singlestoredb = ["singlestoredb>=1.0", "ibis-framework[singlestoredb]>=11.0.0"]
exasol = ["pyexasol>=0.25.2", "ibis-framework[exasol]>=11.0.0"]
impala = ["impyla>=0.17", "ibis-framework[impala]>=11.0.0"]
materialize = ["psycopg>=3.2.0", "ibis-framework[materialize]>=11.0.0"]
risingwave = ["psycopg2>=2.8.4", "ibis-framework[risingwave]>=11.0.0"]
druid = ["pydruid>=0.6.7", "ibis-framework[druid]>=11.0.0"]
trino = ["ibis-framework[trino]>=11.0.0"]


Expand Down Expand Up @@ -94,7 +100,3 @@ tests = ["tests", "*/mountainash-data/tests"]
[tool.coverage.report]
exclude_lines = ["no cov", "if __name__ == .__main__:", "if TYPE_CHECKING:"]


[tool.pyright]
venvPath = "/home/nathanielramm/git/mountainash-ide/mountainash-dev-local/.venv"
venv = ".venv"
207 changes: 207 additions & 0 deletions src/mountainash_data/backends/ibis/dialects/_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,177 @@ def _build_databricks_connection(**config: t.Any) -> t.Any:
return ibis.databricks.connect(**kwargs)


def _build_singlestoredb_connection(**config: t.Any) -> t.Any:
"""Build a SingleStoreDB ibis connection.

Uses ibis.singlestoredb.connect() with kwargs: host, port, user, password,
database, driver, autocommit, local_infile.
"""
import ibis

host = config.get("host", "localhost")
port = config.get("port", 3306)
user = config.get("user", config.get("username", None))
password = config.get("password", None)
database = config.get("database", None)
driver = config.get("driver", None)
autocommit = config.get("autocommit", True)
local_infile = config.get("local_infile", True)

known = {"host", "port", "user", "username", "password", "database",
"driver", "autocommit", "local_infile", "connection_string"}
extra = {k: v for k, v in config.items() if k not in known}

kwargs: dict[str, t.Any] = {
"host": host,
"port": port,
"autocommit": autocommit,
"local_infile": local_infile,
}
if user is not None:
kwargs["user"] = user
if password is not None:
kwargs["password"] = password
if database is not None:
kwargs["database"] = database
if driver is not None:
kwargs["driver"] = driver

kwargs.update(extra)
return ibis.singlestoredb.connect(**kwargs)


def _build_exasol_connection(**config: t.Any) -> t.Any:
"""Build an Exasol ibis connection."""
import ibis

user = config.get("user", config.get("username", None))
password = config.get("password", None)
host = config.get("host", "localhost")
port = config.get("port", 8563)
timezone = config.get("timezone", "UTC")

known = {"host", "port", "user", "username", "password", "timezone",
"connection_string"}
extra = {k: v for k, v in config.items() if k not in known}

kwargs: dict[str, t.Any] = {"host": host, "port": port, "timezone": timezone}
if user is not None:
kwargs["user"] = user
if password is not None:
kwargs["password"] = password
kwargs.update(extra)
return ibis.exasol.connect(**kwargs)


def _build_impala_connection(**config: t.Any) -> t.Any:
"""Build an Impala ibis connection."""
import ibis

host = config.get("host", "localhost")
port = config.get("port", 21050)
database = config.get("database", "default")
timeout = config.get("timeout", 45)
use_ssl = config.get("use_ssl", False)
ca_cert = config.get("ca_cert", None)
user = config.get("user", config.get("username", None))
password = config.get("password", None)
auth_mechanism = config.get("auth_mechanism", "NOSASL")
kerberos_service_name = config.get("kerberos_service_name", "impala")

known = {"host", "port", "database", "timeout", "use_ssl", "ca_cert",
"user", "username", "password", "auth_mechanism",
"kerberos_service_name", "connection_string"}
extra = {k: v for k, v in config.items() if k not in known}

kwargs: dict[str, t.Any] = {
"host": host, "port": port, "database": database,
"timeout": timeout, "use_ssl": use_ssl,
"auth_mechanism": auth_mechanism,
"kerberos_service_name": kerberos_service_name,
}
if ca_cert is not None:
kwargs["ca_cert"] = ca_cert
if user is not None:
kwargs["user"] = user
if password is not None:
kwargs["password"] = password
kwargs.update(extra)
return ibis.impala.connect(**kwargs)


def _build_materialize_connection(**config: t.Any) -> t.Any:
"""Build a Materialize ibis connection."""
import ibis

host = config.get("host", None)
port = config.get("port", 6875)
user = config.get("user", config.get("username", None))
password = config.get("password", None)
database = config.get("database", None)
schema = config.get("schema", None)
autocommit = config.get("autocommit", True)
cluster = config.get("cluster", None)

known = {"host", "port", "user", "username", "password", "database",
"schema", "autocommit", "cluster", "connection_string"}
extra = {k: v for k, v in config.items() if k not in known}

kwargs: dict[str, t.Any] = {"port": port, "autocommit": autocommit}
if host is not None:
kwargs["host"] = host
if user is not None:
kwargs["user"] = user
if password is not None:
kwargs["password"] = password
if database is not None:
kwargs["database"] = database
if schema is not None:
kwargs["schema"] = schema
if cluster is not None:
kwargs["cluster"] = cluster
kwargs.update(extra)
return ibis.materialize.connect(**kwargs)


def _build_risingwave_connection(**config: t.Any) -> t.Any:
"""Build a RisingWave ibis connection."""
import ibis

host = config.get("host", None)
port = config.get("port", 5432)
user = config.get("user", config.get("username", None))
password = config.get("password", None)
database = config.get("database", None)
schema = config.get("schema", None)

known = {"host", "port", "user", "username", "password", "database",
"schema", "connection_string"}
extra = {k: v for k, v in config.items() if k not in known}

kwargs: dict[str, t.Any] = {"port": port}
if host is not None:
kwargs["host"] = host
if user is not None:
kwargs["user"] = user
if password is not None:
kwargs["password"] = password
if database is not None:
kwargs["database"] = database
if schema is not None:
kwargs["schema"] = schema
kwargs.update(extra)
return ibis.risingwave.connect(**kwargs)


def _build_druid_connection(**config: t.Any) -> t.Any:
"""Build a Druid ibis connection."""
import ibis

extra = {k: v for k, v in config.items() if k != "connection_string"}
return ibis.druid.connect(**extra)


def _build_pyspark_connection(**config: t.Any) -> t.Any:
"""Build a PySpark ibis connection.

Expand Down Expand Up @@ -567,6 +738,42 @@ def _build_pyspark_connection(**config: t.Any) -> t.Any:
connection_string_scheme="",
connection_builder=_build_databricks_connection,
),
"singlestoredb": DialectSpec(
ibis_backend_name="singlestoredb",
connection_mode=_KWARGS,
connection_string_scheme="singlestoredb://",
connection_builder=_build_singlestoredb_connection,
),
"exasol": DialectSpec(
ibis_backend_name="exasol",
connection_mode=_KWARGS,
connection_string_scheme="exasol://",
connection_builder=_build_exasol_connection,
),
"impala": DialectSpec(
ibis_backend_name="impala",
connection_mode=_KWARGS,
connection_string_scheme="impala://",
connection_builder=_build_impala_connection,
),
"materialize": DialectSpec(
ibis_backend_name="materialize",
connection_mode=_KWARGS,
connection_string_scheme="materialize://",
connection_builder=_build_materialize_connection,
),
"risingwave": DialectSpec(
ibis_backend_name="risingwave",
connection_mode=_KWARGS,
connection_string_scheme="risingwave://",
connection_builder=_build_risingwave_connection,
),
"druid": DialectSpec(
ibis_backend_name="druid",
connection_mode=_KWARGS,
connection_string_scheme="druid://",
connection_builder=_build_druid_connection,
),
"pyspark": DialectSpec(
ibis_backend_name="pyspark",
connection_mode=_CONNECTION_STRING,
Expand Down
18 changes: 18 additions & 0 deletions src/mountainash_data/core/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ class CONST_DB_PROVIDER_TYPE(Enum):
ORACLE = auto()
CLICKHOUSE = auto()
DATABRICKS = auto()
SINGLESTOREDB = auto()
EXASOL = auto()
IMPALA = auto()
MATERIALIZE = auto()
RISINGWAVE = auto()
DRUID = auto()
PYSPARK = auto()


Expand Down Expand Up @@ -112,6 +118,12 @@ class CONST_DB_BACKEND(StrEnum):
MYSQL = "MYSQL"
CLICKHOUSE = "CLICKHOUSE"
MOTHERDUCK = "MOTHERDUCK"
SINGLESTOREDB = "SINGLESTOREDB"
EXASOL = "EXASOL"
IMPALA = "IMPALA"
MATERIALIZE = "MATERIALIZE"
RISINGWAVE = "RISINGWAVE"
DRUID = "DRUID"
PYICEBERG = "PYICEBERG"
# POLARS = "POLARS"
# PANDAS = "PANDAS"
Expand Down Expand Up @@ -147,6 +159,12 @@ class CONST_DB_BACKEND_IBIS_PREFIX(StrEnum):
MSSQL = "mssql:"
CLICKHOUSE = "clickhouse:"
MYSQL = "mysql:"
SINGLESTOREDB = "singlestoredb:"
EXASOL = "exasol:"
IMPALA = "impala:"
MATERIALIZE = "materialize:"
RISINGWAVE = "risingwave:"
DRUID = "druid:"

class CONST_DB_BACKEND_CAPABILITIES(Enum):
"""
Expand Down
14 changes: 12 additions & 2 deletions src/mountainash_data/core/settings/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,18 @@ class body is a two-line shell (``__descriptor__`` + ``__adapter__``).
from .clickhouse import ClickHouseAuthSettings
from .databricks import DatabricksAuthSettings
from .mysql import MySQLAuthSettings
from .singlestoredb import SingleStoreDBAuthSettings
from .mssql import MSSQLAuthSettings
from .snowflake import SnowflakeAuthSettings
from .bigquery import BigQueryAuthSettings
from .redshift import RedshiftAuthSettings
from .pyspark import PySparkAuthSettings
from .trino import TrinoAuthSettings
from .exasol import ExasolAuthSettings
from .impala import ImpalaAuthSettings
from .materialize import MaterializeAuthSettings
from .risingwave import RisingWaveAuthSettings
from .druid import DruidAuthSettings
from .pyiceberg_rest import PyIcebergRestAuthSettings

__all__ = [
Expand All @@ -61,7 +67,11 @@ class body is a two-line shell (``__descriptor__`` + ``__adapter__``).
# backends
"SQLiteAuthSettings", "DuckDBAuthSettings", "MotherDuckAuthSettings",
"PostgreSQLAuthSettings", "ClickHouseAuthSettings",
"DatabricksAuthSettings", "MySQLAuthSettings", "MSSQLAuthSettings",
"DatabricksAuthSettings", "MySQLAuthSettings", "SingleStoreDBAuthSettings",
"MSSQLAuthSettings",
"SnowflakeAuthSettings", "BigQueryAuthSettings", "RedshiftAuthSettings",
"PySparkAuthSettings", "TrinoAuthSettings", "PyIcebergRestAuthSettings",
"PySparkAuthSettings", "TrinoAuthSettings",
"ExasolAuthSettings", "ImpalaAuthSettings", "MaterializeAuthSettings",
"RisingWaveAuthSettings", "DruidAuthSettings",
"PyIcebergRestAuthSettings",
]
40 changes: 40 additions & 0 deletions src/mountainash_data/core/settings/druid.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"""Druid backend settings.

Driver: https://github.com/druid-io/pydruid
Ibis: ``ibis.druid.connect(**kwargs)``

Druid's ibis backend accepts fully dynamic kwargs passed through to pydruid.
Core parameters are host, port, and path for the Druid broker endpoint.
"""

from __future__ import annotations

from ..constants import CONST_DB_PROVIDER_TYPE
from mountainash_settings.auth import NoAuth, PasswordAuth
from .descriptor import BackendDescriptor, ParameterSpec
from .profile import ConnectionProfile
from .registry import register


DRUID_DESCRIPTOR = BackendDescriptor(
name="druid",
provider_type=CONST_DB_PROVIDER_TYPE.DRUID,
default_port=8082,
connection_string_scheme="druid://",
ibis_dialect="druid",
auth_modes=[PasswordAuth, NoAuth],
parameters=[
ParameterSpec(name="HOST", type=str, tier="core", driver_key="host"),
ParameterSpec(name="PORT", type=int, tier="core", default=8082,
driver_key="port"),
ParameterSpec(name="ENDPOINT_PATH", type=str, tier="core",
default="/druid/v2/sql", driver_key="path"),
ParameterSpec(name="SCHEME", type=str, tier="core",
default="http", driver_key="scheme"),
],
)


@register(DRUID_DESCRIPTOR)
class DruidAuthSettings(ConnectionProfile):
__descriptor__ = DRUID_DESCRIPTOR
36 changes: 36 additions & 0 deletions src/mountainash_data/core/settings/exasol.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"""Exasol backend settings.

Driver: https://github.com/exasol/pyexasol
Ibis: ``ibis.exasol.connect(user, password, host, port, timezone,
websocket_sslopt, **kwargs)``
"""

from __future__ import annotations

from ..constants import CONST_DB_PROVIDER_TYPE
from mountainash_settings.auth import PasswordAuth
from .descriptor import BackendDescriptor, ParameterSpec
from .profile import ConnectionProfile
from .registry import register


EXASOL_DESCRIPTOR = BackendDescriptor(
name="exasol",
provider_type=CONST_DB_PROVIDER_TYPE.EXASOL,
default_port=8563,
connection_string_scheme="exasol://",
ibis_dialect="exasol",
auth_modes=[PasswordAuth],
parameters=[
ParameterSpec(name="HOST", type=str, tier="core", driver_key="host"),
ParameterSpec(name="PORT", type=int, tier="core", default=8563,
driver_key="port"),
ParameterSpec(name="TIMEZONE", type=str, tier="core",
default="UTC", driver_key="timezone"),
],
)


@register(EXASOL_DESCRIPTOR)
class ExasolAuthSettings(ConnectionProfile):
__descriptor__ = EXASOL_DESCRIPTOR
Loading
Loading