This is an automated email from the ASF dual-hosted git repository.

rusackas pushed a commit to branch feat/add-phoenix-iotdb-engine-specs
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 1b2b5eec238affcc39ec7d5b3de7532e50b2fc76
Author: Evan Rusackas <[email protected]>
AuthorDate: Sat Jan 31 14:49:30 2026 +0100

    feat(db_engine_specs): add Apache Phoenix and Apache IoTDB engine specs
    
    Add engine specs for two ASF database projects:
    
    - Apache Phoenix: SQL layer over Apache HBase, using phoenixdb driver
      (phoenix:// dialect, default port 8765)
    - Apache IoTDB: Time series database for IoT data, using apache-iotdb
      driver (iotdb:// dialect, default port 6667)
    
    Both include metadata for docs generation, logos, and connection info.
    
    Co-Authored-By: Claude Opus 4.5 <[email protected]>
---
 docs/static/img/databases/apache-iotdb.svg   | Bin 0 -> 4646 bytes
 docs/static/img/databases/apache-phoenix.png | Bin 0 -> 9552 bytes
 superset/db_engine_specs/iotdb.py            |  57 +++++++++++++++++
 superset/db_engine_specs/phoenix.py          |  90 +++++++++++++++++++++++++++
 4 files changed, 147 insertions(+)

diff --git a/docs/static/img/databases/apache-iotdb.svg 
b/docs/static/img/databases/apache-iotdb.svg
new file mode 100644
index 0000000000..d742630737
Binary files /dev/null and b/docs/static/img/databases/apache-iotdb.svg differ
diff --git a/docs/static/img/databases/apache-phoenix.png 
b/docs/static/img/databases/apache-phoenix.png
new file mode 100644
index 0000000000..d5f32ec9fd
Binary files /dev/null and b/docs/static/img/databases/apache-phoenix.png differ
diff --git a/superset/db_engine_specs/iotdb.py 
b/superset/db_engine_specs/iotdb.py
new file mode 100644
index 0000000000..69049c05a4
--- /dev/null
+++ b/superset/db_engine_specs/iotdb.py
@@ -0,0 +1,57 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License 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.
+from superset.db_engine_specs.base import BaseEngineSpec, DatabaseCategory
+
+
+class IoTDBEngineSpec(BaseEngineSpec):  # pylint: disable=abstract-method
+    """Dialect for Apache IoTDB"""
+
+    engine = "iotdb"
+    engine_name = "Apache IoTDB"
+
+    metadata = {
+        "description": (
+            "Apache IoTDB is a time series database designed for IoT data, "
+            "with efficient storage and query capabilities for massive "
+            "time series data."
+        ),
+        "logo": "apache-iotdb.svg",
+        "homepage_url": "https://iotdb.apache.org/";,
+        "categories": [
+            DatabaseCategory.APACHE_PROJECTS,
+            DatabaseCategory.TIME_SERIES,
+            DatabaseCategory.OPEN_SOURCE,
+        ],
+        "pypi_packages": ["apache-iotdb"],
+        "connection_string": 
("iotdb://{username}:{password}@{hostname}:{port}/"),
+        "default_port": 6667,
+        "parameters": {
+            "username": "Database username (default: root)",
+            "password": "Database password (default: root)",
+            "hostname": "IP address or hostname",
+            "port": "Default 6667",
+        },
+        "notes": (
+            "The IoTDB SQLAlchemy dialect was written to integrate with "
+            "Apache Superset. IoTDB uses a hierarchical data model, which "
+            "is reorganized into a relational model for SQL queries."
+        ),
+    }
+
+    _time_grain_expressions = {
+        None: "{col}",
+    }
diff --git a/superset/db_engine_specs/phoenix.py 
b/superset/db_engine_specs/phoenix.py
new file mode 100644
index 0000000000..85df239613
--- /dev/null
+++ b/superset/db_engine_specs/phoenix.py
@@ -0,0 +1,90 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License 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.
+from datetime import datetime
+from typing import Any, Optional
+
+from sqlalchemy import types
+
+from superset.constants import TimeGrain
+from superset.db_engine_specs.base import BaseEngineSpec, DatabaseCategory
+
+
+class PhoenixEngineSpec(BaseEngineSpec):  # pylint: disable=abstract-method
+    """Dialect for Apache Phoenix"""
+
+    engine = "phoenix"
+    engine_name = "Apache Phoenix"
+
+    metadata = {
+        "description": (
+            "Apache Phoenix is a relational database layer over Apache HBase, "
+            "providing low-latency SQL queries over HBase data."
+        ),
+        "logo": "apache-phoenix.png",
+        "homepage_url": "https://phoenix.apache.org/";,
+        "categories": [
+            DatabaseCategory.APACHE_PROJECTS,
+            DatabaseCategory.ANALYTICAL_DATABASES,
+            DatabaseCategory.OPEN_SOURCE,
+        ],
+        "pypi_packages": ["phoenixdb"],
+        "connection_string": "phoenix://{hostname}:{port}/",
+        "default_port": 8765,
+        "notes": (
+            "Phoenix provides a SQL interface to Apache HBase. "
+            "The phoenixdb driver connects via the Phoenix Query Server "
+            "and supports a subset of SQLAlchemy."
+        ),
+    }
+
+    _time_grain_expressions = {
+        None: "{col}",
+        TimeGrain.SECOND: (
+            "CAST(DATE_TRUNC('SECOND', CAST({col} AS TIMESTAMP)) AS TIMESTAMP)"
+        ),
+        TimeGrain.MINUTE: (
+            "CAST(DATE_TRUNC('MINUTE', CAST({col} AS TIMESTAMP)) AS TIMESTAMP)"
+        ),
+        TimeGrain.HOUR: (
+            "CAST(DATE_TRUNC('HOUR', CAST({col} AS TIMESTAMP)) AS TIMESTAMP)"
+        ),
+        TimeGrain.DAY: "CAST(DATE_TRUNC('DAY', CAST({col} AS TIMESTAMP)) AS 
DATE)",
+        TimeGrain.WEEK: "CAST(DATE_TRUNC('WEEK', CAST({col} AS TIMESTAMP)) AS 
DATE)",
+        TimeGrain.MONTH: (
+            "CAST(DATE_TRUNC('MONTH', CAST({col} AS TIMESTAMP)) AS DATE)"
+        ),
+        TimeGrain.QUARTER: (
+            "CAST(DATE_TRUNC('QUARTER', CAST({col} AS TIMESTAMP)) AS DATE)"
+        ),
+        TimeGrain.YEAR: "CAST(DATE_TRUNC('YEAR', CAST({col} AS TIMESTAMP)) AS 
DATE)",
+    }
+
+    @classmethod
+    def convert_dttm(
+        cls,
+        target_type: str,
+        dttm: datetime,
+        db_extra: Optional[dict[str, Any]] = None,
+    ) -> Optional[str]:
+        sqla_type = cls.get_sqla_column_type(target_type)
+
+        if isinstance(sqla_type, types.Date):
+            return f"TO_DATE('{dttm.date().isoformat()}', 'yyyy-MM-dd')"
+        if isinstance(sqla_type, (types.DateTime, types.TIMESTAMP)):
+            datetime_formatted = dttm.isoformat(sep=" ", timespec="seconds")
+            return f"TO_TIMESTAMP('{datetime_formatted}', 'yyyy-MM-dd 
HH:mm:ss')"
+        return None

Reply via email to