From f036adbc4ffca09009a90419afc53ec48e40f15f Mon Sep 17 00:00:00 2001 From: miomiocat <284487410@qq.com> Date: Tue, 23 May 2023 22:33:27 +0800 Subject: [PATCH] feat: Add StarRocks support (#23209) Signed-off-by: miomiocat <284487410@qq.com> Co-authored-by: Ville Brofeldt --- README.md | 1 + .../databases/installing-database-drivers.mdx | 1 + docs/docs/databases/starrocks.mdx | 26 ++ docs/src/resources/data.js | 5 + docs/static/img/databases/starrocks.png | Bin 0 -> 14896 bytes setup.py | 1 + .../src/assets/images/starrocks.png | Bin 0 -> 14896 bytes superset/db_engine_specs/starrocks.py | 222 ++++++++++++++++++ .../db_engine_specs/test_starrocks.py | 111 +++++++++ 9 files changed, 367 insertions(+) create mode 100644 docs/docs/databases/starrocks.mdx create mode 100644 docs/static/img/databases/starrocks.png create mode 100644 superset-frontend/src/assets/images/starrocks.png create mode 100644 superset/db_engine_specs/starrocks.py create mode 100644 tests/unit_tests/db_engine_specs/test_starrocks.py diff --git a/README.md b/README.md index 0cadd1ead..8c13452d1 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,7 @@ Here are some of the major database solutions that are supported: pinot teradata yugabyte + starrocks

**A more comprehensive list of supported databases** along with the configuration instructions can be found [here](https://superset.apache.org/docs/databases/installing-database-drivers). diff --git a/docs/docs/databases/installing-database-drivers.mdx b/docs/docs/databases/installing-database-drivers.mdx index ee1b38533..86b1ecd19 100644 --- a/docs/docs/databases/installing-database-drivers.mdx +++ b/docs/docs/databases/installing-database-drivers.mdx @@ -52,6 +52,7 @@ A list of some of the recommended packages. | [Trino](/docs/databases/trino) | `pip install trino` | `trino://{username}:{password}@{hostname}:{port}/{catalog}` | | [Presto](/docs/databases/presto) | `pip install pyhive` | `presto://` | | [SAP Hana](/docs/databases/hana) | `pip install hdbcli sqlalchemy-hana or pip install apache-superset[hana]` | `hana://{username}:{password}@{host}:{port}` | +| [StarRocks](/docs/databases/starrocks) | `pip install starrocks` | `starrocks://:@:/.` | | [Snowflake](/docs/databases/snowflake) | `pip install snowflake-sqlalchemy` | `snowflake://{user}:{password}@{account}.{region}/{database}?role={role}&warehouse={warehouse}` | | SQLite | No additional library needed | `sqlite://` | | [SQL Server](/docs/databases/sql-server) | `pip install pymssql` | `mssql+pymssql://` | diff --git a/docs/docs/databases/starrocks.mdx b/docs/docs/databases/starrocks.mdx new file mode 100644 index 000000000..9572437f4 --- /dev/null +++ b/docs/docs/databases/starrocks.mdx @@ -0,0 +1,26 @@ +--- +title: StarRocks +hide_title: true +sidebar_position: 5 +version: 1 +--- + +## StarRocks + +The [sqlalchemy-starrocks](https://pypi.org/project/starrocks/) library is the recommended +way to connect to StarRocks through SQLAlchemy. + +You'll need to the following setting values to form the connection string: + +- **User**: User Name +- **Password**: DBPassword +- **Host**: StarRocks FE Host +- **Catalog**: Catalog Name +- **Database**: Database Name +- **Port**: StarRocks FE port + +Here's what the connection string looks like: + +``` +starrocks://:@:/. +``` diff --git a/docs/src/resources/data.js b/docs/src/resources/data.js index 49bc554a4..79b12017d 100644 --- a/docs/src/resources/data.js +++ b/docs/src/resources/data.js @@ -152,5 +152,10 @@ export const Databases = [ title: 'YugabyteDB', href: "www.yugabyte.com", imgName: 'yugabyte.png' + }, + { + title: 'StarRocks', + href: "www.starrocks.io", + imgName: 'starrocks.png' } ]; diff --git a/docs/static/img/databases/starrocks.png b/docs/static/img/databases/starrocks.png new file mode 100644 index 0000000000000000000000000000000000000000..6868371d7eace8ea5b3327062f8bcf96454a8af3 GIT binary patch literal 14896 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 literal 0 HcmV?d00001 diff --git a/setup.py b/setup.py index 6bad485f2..41f7e11e3 100644 --- a/setup.py +++ b/setup.py @@ -189,6 +189,7 @@ setup( "thumbnails": ["Pillow>=9.5.0, <10.0.0"], "vertica": ["sqlalchemy-vertica-python>=0.5.9, < 0.6"], "netezza": ["nzalchemy>=11.0.2"], + "starrocks": ["starrocks>=1.0.0"], }, python_requires="~=3.9", author="Apache Software Foundation", diff --git a/superset-frontend/src/assets/images/starrocks.png b/superset-frontend/src/assets/images/starrocks.png new file mode 100644 index 0000000000000000000000000000000000000000..6868371d7eace8ea5b3327062f8bcf96454a8af3 GIT binary patch literal 14896 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 literal 0 HcmV?d00001 diff --git a/superset/db_engine_specs/starrocks.py b/superset/db_engine_specs/starrocks.py new file mode 100644 index 000000000..f687fdbdb --- /dev/null +++ b/superset/db_engine_specs/starrocks.py @@ -0,0 +1,222 @@ +# 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. + +import logging +import re +from typing import Any, Dict, List, Optional, Pattern, Tuple, Type +from urllib import parse + +from flask_babel import gettext as __ +from sqlalchemy import Integer, Numeric, types +from sqlalchemy.engine import Inspector +from sqlalchemy.engine.result import Row as ResultRow +from sqlalchemy.engine.url import URL +from sqlalchemy.sql.type_api import TypeEngine + +from superset.db_engine_specs.mysql import MySQLEngineSpec +from superset.errors import SupersetErrorType +from superset.utils.core import GenericDataType + +# Regular expressions to catch custom errors +CONNECTION_ACCESS_DENIED_REGEX = re.compile( + "Access denied for user '(?P.*?)'" +) +CONNECTION_UNKNOWN_DATABASE_REGEX = re.compile("Unknown database '(?P.*?)'") + +logger = logging.getLogger(__name__) + + +class TINYINT(Integer): # pylint: disable=no-init + __visit_name__ = "TINYINT" + + +class DOUBLE(Numeric): # pylint: disable=no-init + __visit_name__ = "DOUBLE" + + +class ARRAY(TypeEngine): # pylint: disable=no-init + __visit_name__ = "ARRAY" + + @property + def python_type(self) -> Optional[Type[List[Any]]]: + return list + + +class MAP(TypeEngine): # pylint: disable=no-init + __visit_name__ = "MAP" + + @property + def python_type(self) -> Optional[Type[Dict[Any, Any]]]: + return dict + + +class STRUCT(TypeEngine): # pylint: disable=no-init + __visit_name__ = "STRUCT" + + @property + def python_type(self) -> Optional[Type[Any]]: + return None + + +class StarRocksEngineSpec(MySQLEngineSpec): + engine = "starrocks" + engine_name = "StarRocks" + + default_driver = "starrocks" + sqlalchemy_uri_placeholder = ( + "starrocks://user:password@host:port/catalog.db[?key=value&key=value...]" + ) + + column_type_mappings = ( # type: ignore + ( + re.compile(r"^tinyint", re.IGNORECASE), + TINYINT(), + GenericDataType.NUMERIC, + ), + ( + re.compile(r"^decimal.*", re.IGNORECASE), + types.DECIMAL(), + GenericDataType.NUMERIC, + ), + ( + re.compile(r"^double", re.IGNORECASE), + DOUBLE(), + GenericDataType.NUMERIC, + ), + ( + re.compile(r"^varchar(\((\d+)\))*$", re.IGNORECASE), + types.VARCHAR(), + GenericDataType.STRING, + ), + ( + re.compile(r"^char(\((\d+)\))*$", re.IGNORECASE), + types.CHAR(), + GenericDataType.STRING, + ), + ( + re.compile(r"^binary.*", re.IGNORECASE), + types.String(), + GenericDataType.STRING, + ), + (re.compile(r"^array.*", re.IGNORECASE), ARRAY(), GenericDataType.STRING), + (re.compile(r"^map.*", re.IGNORECASE), MAP(), GenericDataType.STRING), + (re.compile(r"^struct.*", re.IGNORECASE), STRUCT(), GenericDataType.STRING), + ) + + custom_errors: Dict[Pattern[str], Tuple[str, SupersetErrorType, Dict[str, Any]]] = { + CONNECTION_ACCESS_DENIED_REGEX: ( + __('Either the username "%(username)s" or the password is incorrect.'), + SupersetErrorType.CONNECTION_ACCESS_DENIED_ERROR, + {"invalid": ["username", "password"]}, + ), + CONNECTION_UNKNOWN_DATABASE_REGEX: ( + __('Unable to connect to database "%(database)s".'), + SupersetErrorType.CONNECTION_UNKNOWN_DATABASE_ERROR, + {"invalid": ["database"]}, + ), + } + + @classmethod + def adjust_engine_params( + cls, + uri: URL, + connect_args: Dict[str, Any], + catalog: Optional[str] = None, + schema: Optional[str] = None, + ) -> Tuple[URL, Dict[str, Any]]: + database = uri.database + if schema and database: + schema = parse.quote(schema, safe="") + if "." in database: + database = database.split(".")[0] + "." + schema + else: + database += "." + schema + uri = uri.set(database=database) + + return uri, connect_args + + @classmethod + def get_columns( + cls, inspector: Inspector, table_name: str, schema: Optional[str] + ) -> List[Dict[str, Any]]: + columns = cls._show_columns(inspector, table_name, schema) + result: List[Dict[str, Any]] = [] + for column in columns: + column_spec = cls.get_column_spec(column.Type) + column_type = column_spec.sqla_type if column_spec else None + if column_type is None: + column_type = types.String() + logger.info( + "Did not recognize starrocks type %s of column %s", + str(column.Type), + str(column.Field), + ) + column_info = cls._create_column_info(column.Field, column_type) + column_info["nullable"] = getattr(column, "Null", True) + column_info["default"] = None + result.append(column_info) + return result + + @classmethod + def _show_columns( + cls, inspector: Inspector, table_name: str, schema: Optional[str] + ) -> List[ResultRow]: + """ + Show starrocks column names + :param inspector: object that performs database schema inspection + :param table_name: table name + :param schema: schema name + :return: list of column objects + """ + quote = inspector.engine.dialect.identifier_preparer.quote_identifier + full_table = quote(table_name) + if schema: + full_table = "{}.{}".format(quote(schema), full_table) + return inspector.bind.execute(f"SHOW COLUMNS FROM {full_table}").fetchall() + + @classmethod + def _create_column_info( + cls, name: str, data_type: types.TypeEngine + ) -> Dict[str, Any]: + """ + Create column info object + :param name: column name + :param data_type: column data type + :return: column info object + """ + return {"name": name, "type": f"{data_type}"} + + @classmethod + def get_schema_from_engine_params( + cls, + sqlalchemy_uri: URL, + connect_args: Dict[str, Any], + ) -> Optional[str]: + """ + Return the configured schema. + + For StarRocks the SQLAlchemy URI looks like this: + + starrocks://localhost:9030/catalog.schema + + """ + database = sqlalchemy_uri.database.strip("/") + + if "." not in database: + return None + + return parse.unquote(database.split(".")[1]) diff --git a/tests/unit_tests/db_engine_specs/test_starrocks.py b/tests/unit_tests/db_engine_specs/test_starrocks.py new file mode 100644 index 000000000..7812a1683 --- /dev/null +++ b/tests/unit_tests/db_engine_specs/test_starrocks.py @@ -0,0 +1,111 @@ +# 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 typing import Any, Dict, Optional, Type + +import pytest +from sqlalchemy import types +from sqlalchemy.engine.url import make_url + +from superset.db_engine_specs.starrocks import ARRAY, DOUBLE, MAP, STRUCT, TINYINT +from superset.utils.core import GenericDataType +from tests.unit_tests.db_engine_specs.utils import assert_column_spec + + +@pytest.mark.parametrize( + "native_type,sqla_type,attrs,generic_type,is_dttm", + [ + # Numeric + ("TINYINT", TINYINT, None, GenericDataType.NUMERIC, False), + ("DECIMAL", types.DECIMAL, None, GenericDataType.NUMERIC, False), + ("DOUBLE", DOUBLE, None, GenericDataType.NUMERIC, False), + # String + ("CHAR", types.CHAR, None, GenericDataType.STRING, False), + ("VARCHAR", types.VARCHAR, None, GenericDataType.STRING, False), + ("BINARY", types.String, None, GenericDataType.STRING, False), + # Complex type + ("ARRAY", ARRAY, None, GenericDataType.STRING, False), + ("MAP", MAP, None, GenericDataType.STRING, False), + ("STRUCT", STRUCT, None, GenericDataType.STRING, False), + ], +) +def test_get_column_spec( + native_type: str, + sqla_type: Type[types.TypeEngine], + attrs: Optional[Dict[str, Any]], + generic_type: GenericDataType, + is_dttm: bool, +) -> None: + from superset.db_engine_specs.starrocks import StarRocksEngineSpec as spec + + assert_column_spec(spec, native_type, sqla_type, attrs, generic_type, is_dttm) + + +@pytest.mark.parametrize( + "sqlalchemy_uri,connect_args,return_schema,return_connect_args", + [ + ( + "starrocks://user:password@host/db1", + {"param1": "some_value"}, + "db1", + {"param1": "some_value"}, + ), + ( + "starrocks://user:password@host/catalog1.db1", + {"param1": "some_value"}, + "catalog1.db1", + {"param1": "some_value"}, + ), + ], +) +def test_adjust_engine_params( + sqlalchemy_uri: str, + connect_args: Dict[str, Any], + return_schema: str, + return_connect_args: Dict[str, Any], +) -> None: + from superset.db_engine_specs.starrocks import StarRocksEngineSpec + + url = make_url(sqlalchemy_uri) + returned_url, returned_connect_args = StarRocksEngineSpec.adjust_engine_params( + url, connect_args + ) + assert returned_url.database == return_schema + assert returned_connect_args == return_connect_args + + +def test_get_schema_from_engine_params() -> None: + """ + Test the ``get_schema_from_engine_params`` method. + """ + from superset.db_engine_specs.starrocks import StarRocksEngineSpec + + assert ( + StarRocksEngineSpec.get_schema_from_engine_params( + make_url("starrocks://localhost:9030/hive.default"), + {}, + ) + == "default" + ) + + assert ( + StarRocksEngineSpec.get_schema_from_engine_params( + make_url("starrocks://localhost:9030/hive"), + {}, + ) + is None + )