You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@superset.apache.org by "AdalbertMemSQL (via GitHub)" <gi...@apache.org> on 2025/03/27 15:05:36 UTC
[PR] feat(database): add SingleStore engine specification [superset]
AdalbertMemSQL opened a new pull request, #32887:
URL: https://github.com/apache/superset/pull/32887
### SUMMARY
This PR introduces support for SingleStore by adding a dedicated engine specification. This enables users to connect and interact with SingleStore databases in Superset with appropriate metadata handling, syntax, and behavior tailored to SingleStore.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org
Re: [PR] feat(database): add SingleStore engine specification [superset]
Posted by "AdalbertMemSQL (via GitHub)" <gi...@apache.org>.
AdalbertMemSQL commented on code in PR #32887:
URL: https://github.com/apache/superset/pull/32887#discussion_r2028528110
##########
superset/db_engine_specs/singlestore.py:
##########
@@ -0,0 +1,558 @@
+# 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 datetime import datetime
+from typing import Any, Optional
+from urllib import parse
+
+from flask import current_app
+from sqlalchemy import types
+from sqlalchemy.engine import URL
+
+from superset.constants import TimeGrain
+from superset.db_engine_specs.base import BaseEngineSpec, ColumnTypeMapping, LimitMethod
+from superset.models.core import Database
+from superset.models.sql_lab import Query
+from superset.utils.core import GenericDataType
+
+logger = logging.getLogger(__name__)
+
+
+class SingleStoreSpec(BaseEngineSpec):
Review Comment:
Thanks for the suggestion. Added this.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org
Re: [PR] feat(database): add SingleStore engine specification [superset]
Posted by "rusackas (via GitHub)" <gi...@apache.org>.
rusackas commented on PR #32887:
URL: https://github.com/apache/superset/pull/32887#issuecomment-2772930984
Thanks! Looks like it needs a minor rebase, but we'll try to review soon!
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org
Re: [PR] feat(database): add SingleStore engine specification [superset]
Posted by "betodealmeida (via GitHub)" <gi...@apache.org>.
betodealmeida commented on code in PR #32887:
URL: https://github.com/apache/superset/pull/32887#discussion_r2027249947
##########
superset/db_engine_specs/singlestore.py:
##########
@@ -0,0 +1,558 @@
+# 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 datetime import datetime
+from typing import Any, Optional
+from urllib import parse
+
+from flask import current_app
+from sqlalchemy import types
+from sqlalchemy.engine import URL
+
+from superset.constants import TimeGrain
+from superset.db_engine_specs.base import BaseEngineSpec, ColumnTypeMapping, LimitMethod
+from superset.models.core import Database
+from superset.models.sql_lab import Query
+from superset.utils.core import GenericDataType
+
+logger = logging.getLogger(__name__)
+
+
+class SingleStoreSpec(BaseEngineSpec):
Review Comment:
If you also inherit from `superset.db_engine_specs.base.BasicParametersMixin` you'll get a nicer configuration UI when adding/editing the database.
##########
superset/db_engine_specs/singlestore.py:
##########
@@ -0,0 +1,558 @@
+# 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 datetime import datetime
+from typing import Any, Optional
+from urllib import parse
+
+from flask import current_app
+from sqlalchemy import types
+from sqlalchemy.engine import URL
+
+from superset.constants import TimeGrain
+from superset.db_engine_specs.base import BaseEngineSpec, ColumnTypeMapping, LimitMethod
+from superset.models.core import Database
+from superset.models.sql_lab import Query
+from superset.utils.core import GenericDataType
+
+logger = logging.getLogger(__name__)
+
+
+class SingleStoreSpec(BaseEngineSpec):
+ engine_name = "SingleStore"
+
+ engine = "singlestoredb"
+ drivers = {"singlestoredb": "SingleStore Python client"}
+ default_driver = "singlestoredb"
+
+ limit_method = LimitMethod.FORCE_LIMIT
+ allows_joins = True
+ allows_subqueries = True
+ allows_alias_in_select = True
+ allows_alias_in_orderby = True
+ time_groupby_inline = False
+ allows_alias_to_source_column = True
+ allows_hidden_orderby_agg = True
+ allows_hidden_cc_in_orderby = False
+ allows_cte_in_subquery = True
+ allow_limit_clause = True
+ max_column_name_length = 256
+ allows_sql_comments = True
+ allows_escaped_colons = True
+ supports_file_upload = True
+ supports_dynamic_schema = True
+ disable_ssh_tunneling = False
+
+ sqlalchemy_uri_placeholder = (
+ "singlestoredb://{username}:{password}@{host}:{port}/{database}"
+ )
+
+ _time_grain_expressions = {
+ None: "{col}",
+ TimeGrain.SECOND: "DATE_TRUNC('second', {col})",
+ TimeGrain.MINUTE: "DATE_TRUNC('minute', {col})",
+ TimeGrain.HOUR: "DATE_TRUNC('hour', {col})",
+ TimeGrain.DAY: "DATE_TRUNC('day', {col})",
+ TimeGrain.WEEK: "DATE_TRUNC('week', {col})",
+ TimeGrain.MONTH: "DATE_TRUNC('month', {col})",
+ TimeGrain.QUARTER: "DATE_TRUNC('quarter', {col})",
+ TimeGrain.YEAR: "DATE_TRUNC('year', {col})",
+ }
+
+ column_type_mappings: tuple[ColumnTypeMapping, ...] = (
+ (
+ re.compile(r"^tinyint", re.IGNORECASE),
+ types.SmallInteger(),
+ GenericDataType.NUMERIC,
+ ),
+ (
+ re.compile(r"^mediumint", re.IGNORECASE),
+ types.Integer(),
+ GenericDataType.NUMERIC,
+ ),
+ (
+ re.compile(r"^year", re.IGNORECASE),
+ types.Integer(),
+ GenericDataType.NUMERIC,
+ ),
+ (
+ re.compile(r"^bit", re.IGNORECASE),
+ types.LargeBinary(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^(var)?binary", re.IGNORECASE),
+ types.LargeBinary(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^(tiny|medium|long)?blob", re.IGNORECASE),
+ types.LargeBinary(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^json", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^bson", re.IGNORECASE),
+ types.LargeBinary(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^geographypoint", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^geography", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^vector", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^enum", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^set", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ )
+
+ @classmethod
+ def get_function_names(
+ cls,
+ database: Database,
+ ) -> list[str]:
+ """
+ Get a list of function names that are able to be called on the database.
+ Used for SQL Lab autocomplete.
+
+ :param database: The database to get functions for
+ :return: A list of function names usable in the database
+ """
+
+ functions = [
+ "ABS",
+ "ACOS",
+ "ADDTIME",
+ "AES_DECRYPT",
+ "AES_ENCRYPT",
+ "AGGREGATOR_ID",
+ "ANY_VALUE",
+ "APPROX_COUNT_DISTINCT",
+ "APPROX_COUNT_DISTINCT_ACCUMULATE",
+ "APPROX_COUNT_DISTINCT_COMBINE",
+ "APPROX_COUNT_DISTINCT_ESTIMATE",
+ "APPROX_GEOGRAPHY_INTERSECTS",
+ "APPROX_PERCENTILE",
+ "ASCII",
+ "ASIN",
+ "ATAN",
+ "ATAN2",
+ "ATAN",
+ "AVG",
+ "BETWEEN",
+ "NOT",
+ "BIN",
+ "BIN_TO_UUID",
+ "BINARY",
+ "BIT_AND",
+ "BIT_COUNT",
+ "BIT_OR",
+ "BIT_XOR",
+ "BM25",
+ "BSON_ARRAY_CONTAINS_BSON",
+ "BSON_ARRAY_PUSH",
+ "BSON_ARRAY_SLICE",
+ "BSON_BUILD_ARRAY",
+ "BSON_BUILD_OBJECT",
+ "BSON_COMPARE",
+ "BSON_EXTRACT_BIGINT",
+ "BSON_EXTRACT_BOOL",
+ "BSON_EXTRACT_BSON",
+ "BSON_EXTRACT_DATETIME",
+ "BSON_EXTRACT_DOUBLE",
+ "BSON_EXTRACT_STRING",
+ "BSON_GET_TYPE",
+ "BSON_INCLUDE_MASK",
+ "BSON_EXCLUDE_MASK",
+ "BSON_LENGTH",
+ "BSON_MATCH_ANY",
+ "BSON_MATCH_ANY_EXISTS",
+ "BSON_MERGE",
+ "BSON_NORMALIZE",
+ "BSON_NORMALIZE_ASC",
+ "BSON_NORMALIZE_DESC",
+ "BSON_NORMALIZE_DESC",
+ "BSON_SET_BSON",
+ "BSON_UNWIND",
+ "CASE",
+ "CEIL",
+ "CHAR",
+ "CHARACTER_LENGTH",
+ "CHARSET",
+ "COALESCE",
+ "CONCAT",
+ "CONCAT_WS",
+ "CONNECTION_ID",
+ "CONV",
+ "CAST",
+ "CONVERT",
+ "CONVERT_TZ",
+ "COS",
+ "COT",
+ "COUNT",
+ "CRC32",
+ "CURRENT_DATE",
+ "CURDATE",
+ "CURRENT_TIME",
+ "CURTIME",
+ "CURRENT_TIMESTAMP",
+ "DATABASE",
+ "DATE",
+ "DATE_ADD",
+ "DATE_FORMAT",
+ "DATE_SUB",
+ "DATE_TRUNC",
+ "DATEDIFF",
+ "DAY",
+ "DAYNAME",
+ "DAYOFWEEK",
+ "DAYOFYEAR",
+ "DECODE",
+ "DEGREES",
+ "DENSE_RANK",
+ "DOT_PRODUCT",
+ "ELT",
+ "ESTIMATED_QUERY_LEAF_MEMORY",
+ "ESTIMATED_QUERY_RUNTIME",
+ "EUCLIDEAN_DISTANCE",
+ "EXP",
+ "EXTRACT",
+ "FIELD",
+ "FIRST",
+ "FIRST_VALUE",
+ "FLOOR",
+ "FORMAT",
+ "FOUND_ROWS",
+ "FROM_BASE64",
+ "FROM_DAYS",
+ "FROM_UNIXTIME",
+ "GEOGRAPHY_AREA",
+ "GEOGRAPHY_CONTAINS",
+ "GEOGRAPHY_DISTANCE",
+ "GEOGRAPHY_INTERSECTS",
+ "GEOGRAPHY_LATITUDE",
+ "GEOGRAPHY_LENGTH",
+ "GEOGRAPHY_LONGITUDE",
+ "GEOGRAPHY_POINT",
+ "GEOGRAPHY_WITHIN_DISTANCE",
+ "GET_FORMAT",
+ "GREATEST",
+ "GROUP_CONCAT",
+ "HEX",
+ "HIGHLIGHT",
+ "HOUR",
+ "IF",
+ "IN",
+ "INET_ATON",
+ "INET_NTOA",
+ "INET6_ATON",
+ "INET6_NTOA",
+ "INITCAP",
+ "INSTR",
+ "IS_BSON_NULL",
+ "IS_UUID",
+ "ISNULL",
+ "ISNUMERIC",
+ "JSON_AGG",
+ "JSON_ARRAY_CONTAINS_DOUBLE",
+ "JSON_ARRAY_CONTAINS_STRING",
+ "JSON_ARRAY_CONTAINS_JSON",
+ "JSON_ARRAY_PACK",
+ "JSON_ARRAY_PUSH_DOUBLE",
+ "JSON_ARRAY_PUSH_STRING",
+ "JSON_ARRAY_PUSH_JSON",
+ "JSON_ARRAY_UNPACK",
+ "JSON_BUILD_ARRAY",
+ "JSON_BUILD_OBJECT",
+ "JSON_DELETE_KEY",
+ "JSON_EXTRACT_DOUBLE",
+ "JSON_EXTRACT_STRING",
+ "JSON_EXTRACT_JSON",
+ "JSON_EXTRACT_BIGINT",
+ "JSON_GET_TYPE",
+ "JSON_KEYS",
+ "JSON_LENGTH",
+ "JSON_MATCH_ANY",
+ "JSON_MERGE_PATCH",
+ "JSON_PRETTY",
+ "JSON_SET_DOUBLE",
+ "JSON_SET_STRING",
+ "JSON_SET_JSON",
+ "JSON_SPLICE_DOUBLE",
+ "JSON_SPLICE_STRING",
+ "JSON_SPLICE_JSON",
+ "JSON_TO_ARRAY",
+ "LAG",
+ "LAST",
+ "LAST_DAY",
+ "LAST_INSERT_ID",
+ "LAST_VALUE",
+ "LCASE",
+ "LEAD",
+ "LEAST",
+ "LEFT",
+ "LENGTH",
+ "LIKE",
+ "LN",
+ "LOCALTIMESTAMP",
+ "LOCATE",
+ "LOG",
+ "LOG10",
+ "LOG2",
+ "LPAD",
+ "LTRIM",
+ "MATCH",
+ "MAX",
+ "MD5",
+ "MEDIAN",
+ "MICROSECOND",
+ "MIN",
+ "MINUTE",
+ "MOD",
+ "MONTH",
+ "MONTHNAME",
+ "MONTHS_BETWEEN",
+ "NOPARAM",
+ "NOW",
+ "NTH_VALUE",
+ "NTILE",
+ "NULLIF",
+ "NVL",
+ "IFNULL",
+ "PERCENT_RANK",
+ "PERCENTILE_CONT",
+ "MEDIAN",
+ "PERCENTILE_DISC",
+ "PI",
+ "POW",
+ "QUARTER",
+ "QUOTE",
+ "RADIANS",
+ "RAND",
+ "RANK",
+ "REDUCE",
+ "REGEXP_INSTR",
+ "REGEXP_MATCH",
+ "REGEXP_REPLACE",
+ "REGEXP_SUBSTR",
+ "REPLACE",
+ "REVERSE",
+ "RIGHT",
+ "RLIKE",
+ "REGEXP",
+ "ROUND",
+ "ROW_COUNT",
+ "ROW_NUMBER",
+ "RPAD",
+ "RTRIM",
+ "SCALAR_VECTOR_MUL",
+ "SEC_TO_TIME",
+ "SECOND",
+ "SECRET",
+ "SET",
+ "SHA1",
+ "SHA2",
+ "SIGMOID",
+ "SIGN",
+ "SIN",
+ "SLEEP",
+ "SPLIT",
+ "SQRT",
+ "STD",
+ "STDDEV",
+ "STDDEV_POP",
+ "STDDEV_SAMP",
+ "STR_TO_DATE",
+ "strcmp",
+ "STRING_BYTES",
+ "SUBSTRING",
+ "SUBSTRING_INDEX",
+ "SUM",
+ "SYS_GUID",
+ "UUID",
+ "TAN",
+ "TIME",
+ "TIME_BUCKET",
+ "TIME_FORMAT",
+ "TIME_TO_SEC",
+ "TIMEDIFF",
+ "TIMESTAMP",
+ "TIMESTAMPADD",
+ "TIMESTAMPDIFF",
+ "TO_BASE64",
+ "TO_CHAR",
+ "TO_DATE",
+ "TO_DAYS",
+ "TO_JSON",
+ "TO_NUMBER",
+ "TO_SECONDS",
+ "TO_TIMESTAMP",
+ "TRIM",
+ "TRUNC",
+ "TRUNCATE",
+ "UCASE",
+ "UNHEX",
+ "UNIX_TIMESTAMP",
+ "USER",
+ "UTC_DATE",
+ "UTC_TIME",
+ "UTC_TIMESTAMP",
+ "UUID_TO_BIN",
+ "VARIANCE",
+ "VAR_SAMP",
+ "VECTOR_ADD",
+ "VECTOR_ELEMENTS_SUM",
+ "VECTOR_KTH_ELEMENT",
+ "VECTOR_MUL",
+ "VECTOR_NUM_ELEMENTS",
+ "VECTOR_SORT",
+ "VECTOR_SUB",
+ "VECTOR_SUBVECTOR",
+ "VECTOR_SUM",
+ "WEEK",
+ "WEEKDAY",
+ "YEAR",
+ ]
+
+ if (database_name := cls.get_default_schema(database, None)) is not None:
+ df = database.get_df(
+ f"SHOW FUNCTIONS IN `{database_name.replace('`', '``')}`"
+ )
+
+ function_name_col = f"Functions_in_{database_name}"
+ if function_name_col in df:
+ functions += df[function_name_col].tolist()
+ else:
+ columns = df.columns.values.tolist()
+ logger.error(
+ "Payload from `SHOW FUNCTIONS` has the incorrect format. "
+ "Expected column `%s`, found: %s.",
+ function_name_col,
+ ", ".join(columns),
+ exc_info=True,
+ )
+
+ return functions
+
+ @classmethod
+ def epoch_to_dttm(cls) -> str:
+ return "from_unixtime({col})"
+
+ @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"CAST('{dttm.date().isoformat()}' AS DATE)"
+ if isinstance(sqla_type, types.TIMESTAMP):
+ return f"""('{dttm.isoformat(sep=" ", timespec="microseconds")}' :> TIMESTAMP(6))""" # noqa: E501
+ if isinstance(sqla_type, types.DateTime):
+ return f"""CAST('{dttm.isoformat(sep=" ", timespec="microseconds")}' AS DATETIME(6))""" # noqa: E501
+ if isinstance(sqla_type, types.Time):
+ return f"""CAST('{dttm.strftime("%H:%M:%S.%f")}' AS TIME(6))"""
+
+ return None
+
+ @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]]:
+ if schema:
+ uri = uri.set(database=parse.quote(schema, safe=""))
+
+ connect_args.setdefault(
+ "conn_attrs",
+ {
+ "_connector_name": "SingleStore Superset Database Engine",
+ "_connector_version": current_app.config.get("VERSION_STRING", "dev"),
+ "_product_version": current_app.config.get("VERSION_STRING", "dev"),
+ },
+ )
+ return uri, connect_args
+
+ @classmethod
+ def get_schema_from_engine_params(
+ cls,
+ sqlalchemy_uri: URL,
+ connect_args: dict[str, Any],
+ ) -> Optional[str]:
+ """
+ Return the configured schema.
+
+ A MySQL database is a SQLAlchemy schema.
+ """
+ return parse.unquote(sqlalchemy_uri.database)
+
+ @classmethod
+ def get_cancel_query_id(cls, cursor: Any, query: Query) -> Optional[str]:
+ """
+ Get SingleStore connection ID and aggregator ID that will be used to cancel all
+ other running queries in the same connection.
+
+ :param cursor: Cursor instance in which the query will be executed
+ :param query: Query instance
+ :return: SingleStore connection ID and aggregator ID
+ """
+ cursor.execute("SELECT CONNECTION_ID(), AGGREGATOR_ID()")
+ row = cursor.fetchone()
+ return " ".join(str(item) for item in row)
+
+ @classmethod
+ def cancel_query(cls, cursor: Any, query: Query, cancel_query_id: str) -> bool:
Review Comment:
Nice!
##########
superset/db_engine_specs/singlestore.py:
##########
@@ -0,0 +1,558 @@
+# 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 datetime import datetime
+from typing import Any, Optional
+from urllib import parse
+
+from flask import current_app
+from sqlalchemy import types
+from sqlalchemy.engine import URL
+
+from superset.constants import TimeGrain
+from superset.db_engine_specs.base import BaseEngineSpec, ColumnTypeMapping, LimitMethod
+from superset.models.core import Database
+from superset.models.sql_lab import Query
+from superset.utils.core import GenericDataType
+
+logger = logging.getLogger(__name__)
+
+
+class SingleStoreSpec(BaseEngineSpec):
+ engine_name = "SingleStore"
+
+ engine = "singlestoredb"
+ drivers = {"singlestoredb": "SingleStore Python client"}
+ default_driver = "singlestoredb"
+
+ limit_method = LimitMethod.FORCE_LIMIT
+ allows_joins = True
+ allows_subqueries = True
+ allows_alias_in_select = True
+ allows_alias_in_orderby = True
+ time_groupby_inline = False
+ allows_alias_to_source_column = True
+ allows_hidden_orderby_agg = True
+ allows_hidden_cc_in_orderby = False
+ allows_cte_in_subquery = True
+ allow_limit_clause = True
+ max_column_name_length = 256
+ allows_sql_comments = True
+ allows_escaped_colons = True
+ supports_file_upload = True
+ supports_dynamic_schema = True
+ disable_ssh_tunneling = False
+
+ sqlalchemy_uri_placeholder = (
+ "singlestoredb://{username}:{password}@{host}:{port}/{database}"
+ )
+
+ _time_grain_expressions = {
+ None: "{col}",
+ TimeGrain.SECOND: "DATE_TRUNC('second', {col})",
+ TimeGrain.MINUTE: "DATE_TRUNC('minute', {col})",
+ TimeGrain.HOUR: "DATE_TRUNC('hour', {col})",
+ TimeGrain.DAY: "DATE_TRUNC('day', {col})",
+ TimeGrain.WEEK: "DATE_TRUNC('week', {col})",
+ TimeGrain.MONTH: "DATE_TRUNC('month', {col})",
+ TimeGrain.QUARTER: "DATE_TRUNC('quarter', {col})",
+ TimeGrain.YEAR: "DATE_TRUNC('year', {col})",
+ }
+
+ column_type_mappings: tuple[ColumnTypeMapping, ...] = (
+ (
+ re.compile(r"^tinyint", re.IGNORECASE),
+ types.SmallInteger(),
+ GenericDataType.NUMERIC,
+ ),
+ (
+ re.compile(r"^mediumint", re.IGNORECASE),
+ types.Integer(),
+ GenericDataType.NUMERIC,
+ ),
+ (
+ re.compile(r"^year", re.IGNORECASE),
+ types.Integer(),
+ GenericDataType.NUMERIC,
+ ),
+ (
+ re.compile(r"^bit", re.IGNORECASE),
+ types.LargeBinary(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^(var)?binary", re.IGNORECASE),
+ types.LargeBinary(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^(tiny|medium|long)?blob", re.IGNORECASE),
+ types.LargeBinary(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^json", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^bson", re.IGNORECASE),
+ types.LargeBinary(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^geographypoint", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^geography", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^vector", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^enum", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^set", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ )
+
+ @classmethod
+ def get_function_names(
+ cls,
+ database: Database,
+ ) -> list[str]:
+ """
+ Get a list of function names that are able to be called on the database.
+ Used for SQL Lab autocomplete.
+
+ :param database: The database to get functions for
+ :return: A list of function names usable in the database
+ """
+
+ functions = [
+ "ABS",
+ "ACOS",
+ "ADDTIME",
+ "AES_DECRYPT",
+ "AES_ENCRYPT",
+ "AGGREGATOR_ID",
+ "ANY_VALUE",
+ "APPROX_COUNT_DISTINCT",
+ "APPROX_COUNT_DISTINCT_ACCUMULATE",
+ "APPROX_COUNT_DISTINCT_COMBINE",
+ "APPROX_COUNT_DISTINCT_ESTIMATE",
+ "APPROX_GEOGRAPHY_INTERSECTS",
+ "APPROX_PERCENTILE",
+ "ASCII",
+ "ASIN",
+ "ATAN",
+ "ATAN2",
+ "ATAN",
+ "AVG",
+ "BETWEEN",
+ "NOT",
+ "BIN",
+ "BIN_TO_UUID",
+ "BINARY",
+ "BIT_AND",
+ "BIT_COUNT",
+ "BIT_OR",
+ "BIT_XOR",
+ "BM25",
+ "BSON_ARRAY_CONTAINS_BSON",
+ "BSON_ARRAY_PUSH",
+ "BSON_ARRAY_SLICE",
+ "BSON_BUILD_ARRAY",
+ "BSON_BUILD_OBJECT",
+ "BSON_COMPARE",
+ "BSON_EXTRACT_BIGINT",
+ "BSON_EXTRACT_BOOL",
+ "BSON_EXTRACT_BSON",
+ "BSON_EXTRACT_DATETIME",
+ "BSON_EXTRACT_DOUBLE",
+ "BSON_EXTRACT_STRING",
+ "BSON_GET_TYPE",
+ "BSON_INCLUDE_MASK",
+ "BSON_EXCLUDE_MASK",
+ "BSON_LENGTH",
+ "BSON_MATCH_ANY",
+ "BSON_MATCH_ANY_EXISTS",
+ "BSON_MERGE",
+ "BSON_NORMALIZE",
+ "BSON_NORMALIZE_ASC",
+ "BSON_NORMALIZE_DESC",
+ "BSON_NORMALIZE_DESC",
+ "BSON_SET_BSON",
+ "BSON_UNWIND",
+ "CASE",
+ "CEIL",
+ "CHAR",
+ "CHARACTER_LENGTH",
+ "CHARSET",
+ "COALESCE",
+ "CONCAT",
+ "CONCAT_WS",
+ "CONNECTION_ID",
+ "CONV",
+ "CAST",
+ "CONVERT",
+ "CONVERT_TZ",
+ "COS",
+ "COT",
+ "COUNT",
+ "CRC32",
+ "CURRENT_DATE",
+ "CURDATE",
+ "CURRENT_TIME",
+ "CURTIME",
+ "CURRENT_TIMESTAMP",
+ "DATABASE",
+ "DATE",
+ "DATE_ADD",
+ "DATE_FORMAT",
+ "DATE_SUB",
+ "DATE_TRUNC",
+ "DATEDIFF",
+ "DAY",
+ "DAYNAME",
+ "DAYOFWEEK",
+ "DAYOFYEAR",
+ "DECODE",
+ "DEGREES",
+ "DENSE_RANK",
+ "DOT_PRODUCT",
+ "ELT",
+ "ESTIMATED_QUERY_LEAF_MEMORY",
+ "ESTIMATED_QUERY_RUNTIME",
+ "EUCLIDEAN_DISTANCE",
+ "EXP",
+ "EXTRACT",
+ "FIELD",
+ "FIRST",
+ "FIRST_VALUE",
+ "FLOOR",
+ "FORMAT",
+ "FOUND_ROWS",
+ "FROM_BASE64",
+ "FROM_DAYS",
+ "FROM_UNIXTIME",
+ "GEOGRAPHY_AREA",
+ "GEOGRAPHY_CONTAINS",
+ "GEOGRAPHY_DISTANCE",
+ "GEOGRAPHY_INTERSECTS",
+ "GEOGRAPHY_LATITUDE",
+ "GEOGRAPHY_LENGTH",
+ "GEOGRAPHY_LONGITUDE",
+ "GEOGRAPHY_POINT",
+ "GEOGRAPHY_WITHIN_DISTANCE",
+ "GET_FORMAT",
+ "GREATEST",
+ "GROUP_CONCAT",
+ "HEX",
+ "HIGHLIGHT",
+ "HOUR",
+ "IF",
+ "IN",
+ "INET_ATON",
+ "INET_NTOA",
+ "INET6_ATON",
+ "INET6_NTOA",
+ "INITCAP",
+ "INSTR",
+ "IS_BSON_NULL",
+ "IS_UUID",
+ "ISNULL",
+ "ISNUMERIC",
+ "JSON_AGG",
+ "JSON_ARRAY_CONTAINS_DOUBLE",
+ "JSON_ARRAY_CONTAINS_STRING",
+ "JSON_ARRAY_CONTAINS_JSON",
+ "JSON_ARRAY_PACK",
+ "JSON_ARRAY_PUSH_DOUBLE",
+ "JSON_ARRAY_PUSH_STRING",
+ "JSON_ARRAY_PUSH_JSON",
+ "JSON_ARRAY_UNPACK",
+ "JSON_BUILD_ARRAY",
+ "JSON_BUILD_OBJECT",
+ "JSON_DELETE_KEY",
+ "JSON_EXTRACT_DOUBLE",
+ "JSON_EXTRACT_STRING",
+ "JSON_EXTRACT_JSON",
+ "JSON_EXTRACT_BIGINT",
+ "JSON_GET_TYPE",
+ "JSON_KEYS",
+ "JSON_LENGTH",
+ "JSON_MATCH_ANY",
+ "JSON_MERGE_PATCH",
+ "JSON_PRETTY",
+ "JSON_SET_DOUBLE",
+ "JSON_SET_STRING",
+ "JSON_SET_JSON",
+ "JSON_SPLICE_DOUBLE",
+ "JSON_SPLICE_STRING",
+ "JSON_SPLICE_JSON",
+ "JSON_TO_ARRAY",
+ "LAG",
+ "LAST",
+ "LAST_DAY",
+ "LAST_INSERT_ID",
+ "LAST_VALUE",
+ "LCASE",
+ "LEAD",
+ "LEAST",
+ "LEFT",
+ "LENGTH",
+ "LIKE",
+ "LN",
+ "LOCALTIMESTAMP",
+ "LOCATE",
+ "LOG",
+ "LOG10",
+ "LOG2",
+ "LPAD",
+ "LTRIM",
+ "MATCH",
+ "MAX",
+ "MD5",
+ "MEDIAN",
+ "MICROSECOND",
+ "MIN",
+ "MINUTE",
+ "MOD",
+ "MONTH",
+ "MONTHNAME",
+ "MONTHS_BETWEEN",
+ "NOPARAM",
+ "NOW",
+ "NTH_VALUE",
+ "NTILE",
+ "NULLIF",
+ "NVL",
+ "IFNULL",
+ "PERCENT_RANK",
+ "PERCENTILE_CONT",
+ "MEDIAN",
+ "PERCENTILE_DISC",
+ "PI",
+ "POW",
+ "QUARTER",
+ "QUOTE",
+ "RADIANS",
+ "RAND",
+ "RANK",
+ "REDUCE",
+ "REGEXP_INSTR",
+ "REGEXP_MATCH",
+ "REGEXP_REPLACE",
+ "REGEXP_SUBSTR",
+ "REPLACE",
+ "REVERSE",
+ "RIGHT",
+ "RLIKE",
+ "REGEXP",
+ "ROUND",
+ "ROW_COUNT",
+ "ROW_NUMBER",
+ "RPAD",
+ "RTRIM",
+ "SCALAR_VECTOR_MUL",
+ "SEC_TO_TIME",
+ "SECOND",
+ "SECRET",
+ "SET",
+ "SHA1",
+ "SHA2",
+ "SIGMOID",
+ "SIGN",
+ "SIN",
+ "SLEEP",
+ "SPLIT",
+ "SQRT",
+ "STD",
+ "STDDEV",
+ "STDDEV_POP",
+ "STDDEV_SAMP",
+ "STR_TO_DATE",
+ "strcmp",
+ "STRING_BYTES",
+ "SUBSTRING",
+ "SUBSTRING_INDEX",
+ "SUM",
+ "SYS_GUID",
+ "UUID",
+ "TAN",
+ "TIME",
+ "TIME_BUCKET",
+ "TIME_FORMAT",
+ "TIME_TO_SEC",
+ "TIMEDIFF",
+ "TIMESTAMP",
+ "TIMESTAMPADD",
+ "TIMESTAMPDIFF",
+ "TO_BASE64",
+ "TO_CHAR",
+ "TO_DATE",
+ "TO_DAYS",
+ "TO_JSON",
+ "TO_NUMBER",
+ "TO_SECONDS",
+ "TO_TIMESTAMP",
+ "TRIM",
+ "TRUNC",
+ "TRUNCATE",
+ "UCASE",
+ "UNHEX",
+ "UNIX_TIMESTAMP",
+ "USER",
+ "UTC_DATE",
+ "UTC_TIME",
+ "UTC_TIMESTAMP",
+ "UUID_TO_BIN",
+ "VARIANCE",
+ "VAR_SAMP",
+ "VECTOR_ADD",
+ "VECTOR_ELEMENTS_SUM",
+ "VECTOR_KTH_ELEMENT",
+ "VECTOR_MUL",
+ "VECTOR_NUM_ELEMENTS",
+ "VECTOR_SORT",
+ "VECTOR_SUB",
+ "VECTOR_SUBVECTOR",
+ "VECTOR_SUM",
+ "WEEK",
+ "WEEKDAY",
+ "YEAR",
+ ]
+
+ if (database_name := cls.get_default_schema(database, None)) is not None:
+ df = database.get_df(
+ f"SHOW FUNCTIONS IN `{database_name.replace('`', '``')}`"
+ )
+
+ function_name_col = f"Functions_in_{database_name}"
+ if function_name_col in df:
+ functions += df[function_name_col].tolist()
Review Comment:
Also, we could access the dataframe by index, then we don't have to worry about the column name:
```python
functions.update(df.iloc[:, 0].tolist())
```
##########
superset/db_engine_specs/singlestore.py:
##########
@@ -0,0 +1,558 @@
+# 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 datetime import datetime
+from typing import Any, Optional
+from urllib import parse
+
+from flask import current_app
+from sqlalchemy import types
+from sqlalchemy.engine import URL
+
+from superset.constants import TimeGrain
+from superset.db_engine_specs.base import BaseEngineSpec, ColumnTypeMapping, LimitMethod
+from superset.models.core import Database
+from superset.models.sql_lab import Query
+from superset.utils.core import GenericDataType
+
+logger = logging.getLogger(__name__)
+
+
+class SingleStoreSpec(BaseEngineSpec):
+ engine_name = "SingleStore"
+
+ engine = "singlestoredb"
+ drivers = {"singlestoredb": "SingleStore Python client"}
+ default_driver = "singlestoredb"
+
+ limit_method = LimitMethod.FORCE_LIMIT
+ allows_joins = True
+ allows_subqueries = True
+ allows_alias_in_select = True
+ allows_alias_in_orderby = True
+ time_groupby_inline = False
+ allows_alias_to_source_column = True
+ allows_hidden_orderby_agg = True
+ allows_hidden_cc_in_orderby = False
+ allows_cte_in_subquery = True
+ allow_limit_clause = True
+ max_column_name_length = 256
+ allows_sql_comments = True
+ allows_escaped_colons = True
+ supports_file_upload = True
+ supports_dynamic_schema = True
+ disable_ssh_tunneling = False
+
+ sqlalchemy_uri_placeholder = (
+ "singlestoredb://{username}:{password}@{host}:{port}/{database}"
+ )
+
+ _time_grain_expressions = {
+ None: "{col}",
+ TimeGrain.SECOND: "DATE_TRUNC('second', {col})",
+ TimeGrain.MINUTE: "DATE_TRUNC('minute', {col})",
+ TimeGrain.HOUR: "DATE_TRUNC('hour', {col})",
+ TimeGrain.DAY: "DATE_TRUNC('day', {col})",
+ TimeGrain.WEEK: "DATE_TRUNC('week', {col})",
+ TimeGrain.MONTH: "DATE_TRUNC('month', {col})",
+ TimeGrain.QUARTER: "DATE_TRUNC('quarter', {col})",
+ TimeGrain.YEAR: "DATE_TRUNC('year', {col})",
+ }
+
+ column_type_mappings: tuple[ColumnTypeMapping, ...] = (
+ (
+ re.compile(r"^tinyint", re.IGNORECASE),
+ types.SmallInteger(),
+ GenericDataType.NUMERIC,
+ ),
+ (
+ re.compile(r"^mediumint", re.IGNORECASE),
+ types.Integer(),
+ GenericDataType.NUMERIC,
+ ),
+ (
+ re.compile(r"^year", re.IGNORECASE),
+ types.Integer(),
+ GenericDataType.NUMERIC,
+ ),
+ (
+ re.compile(r"^bit", re.IGNORECASE),
+ types.LargeBinary(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^(var)?binary", re.IGNORECASE),
+ types.LargeBinary(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^(tiny|medium|long)?blob", re.IGNORECASE),
+ types.LargeBinary(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^json", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^bson", re.IGNORECASE),
+ types.LargeBinary(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^geographypoint", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^geography", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^vector", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^enum", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^set", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ )
+
+ @classmethod
+ def get_function_names(
+ cls,
+ database: Database,
+ ) -> list[str]:
+ """
+ Get a list of function names that are able to be called on the database.
+ Used for SQL Lab autocomplete.
+
+ :param database: The database to get functions for
+ :return: A list of function names usable in the database
+ """
+
+ functions = [
+ "ABS",
+ "ACOS",
+ "ADDTIME",
+ "AES_DECRYPT",
+ "AES_ENCRYPT",
+ "AGGREGATOR_ID",
+ "ANY_VALUE",
+ "APPROX_COUNT_DISTINCT",
+ "APPROX_COUNT_DISTINCT_ACCUMULATE",
+ "APPROX_COUNT_DISTINCT_COMBINE",
+ "APPROX_COUNT_DISTINCT_ESTIMATE",
+ "APPROX_GEOGRAPHY_INTERSECTS",
+ "APPROX_PERCENTILE",
+ "ASCII",
+ "ASIN",
+ "ATAN",
+ "ATAN2",
+ "ATAN",
+ "AVG",
+ "BETWEEN",
+ "NOT",
+ "BIN",
+ "BIN_TO_UUID",
+ "BINARY",
+ "BIT_AND",
+ "BIT_COUNT",
+ "BIT_OR",
+ "BIT_XOR",
+ "BM25",
+ "BSON_ARRAY_CONTAINS_BSON",
+ "BSON_ARRAY_PUSH",
+ "BSON_ARRAY_SLICE",
+ "BSON_BUILD_ARRAY",
+ "BSON_BUILD_OBJECT",
+ "BSON_COMPARE",
+ "BSON_EXTRACT_BIGINT",
+ "BSON_EXTRACT_BOOL",
+ "BSON_EXTRACT_BSON",
+ "BSON_EXTRACT_DATETIME",
+ "BSON_EXTRACT_DOUBLE",
+ "BSON_EXTRACT_STRING",
+ "BSON_GET_TYPE",
+ "BSON_INCLUDE_MASK",
+ "BSON_EXCLUDE_MASK",
+ "BSON_LENGTH",
+ "BSON_MATCH_ANY",
+ "BSON_MATCH_ANY_EXISTS",
+ "BSON_MERGE",
+ "BSON_NORMALIZE",
+ "BSON_NORMALIZE_ASC",
+ "BSON_NORMALIZE_DESC",
+ "BSON_NORMALIZE_DESC",
+ "BSON_SET_BSON",
+ "BSON_UNWIND",
+ "CASE",
+ "CEIL",
+ "CHAR",
+ "CHARACTER_LENGTH",
+ "CHARSET",
+ "COALESCE",
+ "CONCAT",
+ "CONCAT_WS",
+ "CONNECTION_ID",
+ "CONV",
+ "CAST",
+ "CONVERT",
+ "CONVERT_TZ",
+ "COS",
+ "COT",
+ "COUNT",
+ "CRC32",
+ "CURRENT_DATE",
+ "CURDATE",
+ "CURRENT_TIME",
+ "CURTIME",
+ "CURRENT_TIMESTAMP",
+ "DATABASE",
+ "DATE",
+ "DATE_ADD",
+ "DATE_FORMAT",
+ "DATE_SUB",
+ "DATE_TRUNC",
+ "DATEDIFF",
+ "DAY",
+ "DAYNAME",
+ "DAYOFWEEK",
+ "DAYOFYEAR",
+ "DECODE",
+ "DEGREES",
+ "DENSE_RANK",
+ "DOT_PRODUCT",
+ "ELT",
+ "ESTIMATED_QUERY_LEAF_MEMORY",
+ "ESTIMATED_QUERY_RUNTIME",
+ "EUCLIDEAN_DISTANCE",
+ "EXP",
+ "EXTRACT",
+ "FIELD",
+ "FIRST",
+ "FIRST_VALUE",
+ "FLOOR",
+ "FORMAT",
+ "FOUND_ROWS",
+ "FROM_BASE64",
+ "FROM_DAYS",
+ "FROM_UNIXTIME",
+ "GEOGRAPHY_AREA",
+ "GEOGRAPHY_CONTAINS",
+ "GEOGRAPHY_DISTANCE",
+ "GEOGRAPHY_INTERSECTS",
+ "GEOGRAPHY_LATITUDE",
+ "GEOGRAPHY_LENGTH",
+ "GEOGRAPHY_LONGITUDE",
+ "GEOGRAPHY_POINT",
+ "GEOGRAPHY_WITHIN_DISTANCE",
+ "GET_FORMAT",
+ "GREATEST",
+ "GROUP_CONCAT",
+ "HEX",
+ "HIGHLIGHT",
+ "HOUR",
+ "IF",
+ "IN",
+ "INET_ATON",
+ "INET_NTOA",
+ "INET6_ATON",
+ "INET6_NTOA",
+ "INITCAP",
+ "INSTR",
+ "IS_BSON_NULL",
+ "IS_UUID",
+ "ISNULL",
+ "ISNUMERIC",
+ "JSON_AGG",
+ "JSON_ARRAY_CONTAINS_DOUBLE",
+ "JSON_ARRAY_CONTAINS_STRING",
+ "JSON_ARRAY_CONTAINS_JSON",
+ "JSON_ARRAY_PACK",
+ "JSON_ARRAY_PUSH_DOUBLE",
+ "JSON_ARRAY_PUSH_STRING",
+ "JSON_ARRAY_PUSH_JSON",
+ "JSON_ARRAY_UNPACK",
+ "JSON_BUILD_ARRAY",
+ "JSON_BUILD_OBJECT",
+ "JSON_DELETE_KEY",
+ "JSON_EXTRACT_DOUBLE",
+ "JSON_EXTRACT_STRING",
+ "JSON_EXTRACT_JSON",
+ "JSON_EXTRACT_BIGINT",
+ "JSON_GET_TYPE",
+ "JSON_KEYS",
+ "JSON_LENGTH",
+ "JSON_MATCH_ANY",
+ "JSON_MERGE_PATCH",
+ "JSON_PRETTY",
+ "JSON_SET_DOUBLE",
+ "JSON_SET_STRING",
+ "JSON_SET_JSON",
+ "JSON_SPLICE_DOUBLE",
+ "JSON_SPLICE_STRING",
+ "JSON_SPLICE_JSON",
+ "JSON_TO_ARRAY",
+ "LAG",
+ "LAST",
+ "LAST_DAY",
+ "LAST_INSERT_ID",
+ "LAST_VALUE",
+ "LCASE",
+ "LEAD",
+ "LEAST",
+ "LEFT",
+ "LENGTH",
+ "LIKE",
+ "LN",
+ "LOCALTIMESTAMP",
+ "LOCATE",
+ "LOG",
+ "LOG10",
+ "LOG2",
+ "LPAD",
+ "LTRIM",
+ "MATCH",
+ "MAX",
+ "MD5",
+ "MEDIAN",
+ "MICROSECOND",
+ "MIN",
+ "MINUTE",
+ "MOD",
+ "MONTH",
+ "MONTHNAME",
+ "MONTHS_BETWEEN",
+ "NOPARAM",
+ "NOW",
+ "NTH_VALUE",
+ "NTILE",
+ "NULLIF",
+ "NVL",
+ "IFNULL",
+ "PERCENT_RANK",
+ "PERCENTILE_CONT",
+ "MEDIAN",
+ "PERCENTILE_DISC",
+ "PI",
+ "POW",
+ "QUARTER",
+ "QUOTE",
+ "RADIANS",
+ "RAND",
+ "RANK",
+ "REDUCE",
+ "REGEXP_INSTR",
+ "REGEXP_MATCH",
+ "REGEXP_REPLACE",
+ "REGEXP_SUBSTR",
+ "REPLACE",
+ "REVERSE",
+ "RIGHT",
+ "RLIKE",
+ "REGEXP",
+ "ROUND",
+ "ROW_COUNT",
+ "ROW_NUMBER",
+ "RPAD",
+ "RTRIM",
+ "SCALAR_VECTOR_MUL",
+ "SEC_TO_TIME",
+ "SECOND",
+ "SECRET",
+ "SET",
+ "SHA1",
+ "SHA2",
+ "SIGMOID",
+ "SIGN",
+ "SIN",
+ "SLEEP",
+ "SPLIT",
+ "SQRT",
+ "STD",
+ "STDDEV",
+ "STDDEV_POP",
+ "STDDEV_SAMP",
+ "STR_TO_DATE",
+ "strcmp",
+ "STRING_BYTES",
+ "SUBSTRING",
+ "SUBSTRING_INDEX",
+ "SUM",
+ "SYS_GUID",
+ "UUID",
+ "TAN",
+ "TIME",
+ "TIME_BUCKET",
+ "TIME_FORMAT",
+ "TIME_TO_SEC",
+ "TIMEDIFF",
+ "TIMESTAMP",
+ "TIMESTAMPADD",
+ "TIMESTAMPDIFF",
+ "TO_BASE64",
+ "TO_CHAR",
+ "TO_DATE",
+ "TO_DAYS",
+ "TO_JSON",
+ "TO_NUMBER",
+ "TO_SECONDS",
+ "TO_TIMESTAMP",
+ "TRIM",
+ "TRUNC",
+ "TRUNCATE",
+ "UCASE",
+ "UNHEX",
+ "UNIX_TIMESTAMP",
+ "USER",
+ "UTC_DATE",
+ "UTC_TIME",
+ "UTC_TIMESTAMP",
+ "UUID_TO_BIN",
+ "VARIANCE",
+ "VAR_SAMP",
+ "VECTOR_ADD",
+ "VECTOR_ELEMENTS_SUM",
+ "VECTOR_KTH_ELEMENT",
+ "VECTOR_MUL",
+ "VECTOR_NUM_ELEMENTS",
+ "VECTOR_SORT",
+ "VECTOR_SUB",
+ "VECTOR_SUBVECTOR",
+ "VECTOR_SUM",
+ "WEEK",
+ "WEEKDAY",
+ "YEAR",
+ ]
+
+ if (database_name := cls.get_default_schema(database, None)) is not None:
+ df = database.get_df(
+ f"SHOW FUNCTIONS IN `{database_name.replace('`', '``')}`"
+ )
+
+ function_name_col = f"Functions_in_{database_name}"
+ if function_name_col in df:
+ functions += df[function_name_col].tolist()
Review Comment:
It's better to make `functions` a `set[str]`, then here you can:
```python
functions.update(df[function_name_col].tolist()) # .tolist() might not be necessary
```
Then return `sorted(functions)`. This way you remove duplicates.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org
Re: [PR] feat(database): add SingleStore engine specification [superset]
Posted by "rusackas (via GitHub)" <gi...@apache.org>.
rusackas merged PR #32887:
URL: https://github.com/apache/superset/pull/32887
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org
Re: [PR] feat(database): add SingleStore engine specification [superset]
Posted by "rusackas (via GitHub)" <gi...@apache.org>.
rusackas commented on code in PR #32887:
URL: https://github.com/apache/superset/pull/32887#discussion_r2121671462
##########
docs/docs/configuration/databases.mdx:
##########
@@ -72,7 +72,7 @@ are compatible with Superset.
| [PostgreSQL](/docs/configuration/databases#postgres) | `pip install psycopg2` | `postgresql://<UserName>:<DBPassword>@<Database Host>/<Database Name>` |
| [Presto](/docs/configuration/databases#presto) | `pip install pyhive` | `presto://{username}:{password}@{hostname}:{port}/{database}` |
| [Rockset](/docs/configuration/databases#rockset) | `pip install rockset-sqlalchemy` | `rockset://<api_key>:@<api_server>` |
-| [SAP Hana](/docs/configuration/databases#hana) | `pip install hdbcli sqlalchemy-hana` or `pip install apache_superset[hana]` | `hana://{username}:{password}@{host}:{port}` |
+| [SingleStore](/docs/configuration/databases#singlestore) | `pip install sqlalchemy-singlestoredb` | `singlestoredb://{username}:{password}@{host}:{port}/{database}` |
Review Comment:
Assuming we don't want to delete SAP Hana, but rather just _add_ SingleStore.
```suggestion
| [SAP Hana](/docs/configuration/databases#hana) | `pip install hdbcli sqlalchemy-hana` or `pip install apache_superset[hana]` | `hana://{username}:{password}@{host}:{port}` |
| [SingleStore](/docs/configuration/databases#singlestore) | `pip install sqlalchemy-singlestoredb` | `singlestoredb://{username}:{password}@{host}:{port}/{database}` |
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org
Re: [PR] feat(database): add SingleStore engine specification [superset]
Posted by "AdalbertMemSQL (via GitHub)" <gi...@apache.org>.
AdalbertMemSQL commented on PR #32887:
URL: https://github.com/apache/superset/pull/32887#issuecomment-2820549477
Hi @rusackas, @betodealmeida,
Just following up on this PR — I’ve incorporated the requested changes and pushed the updates.
Would you mind taking another look when you have a moment? Let me know if anything else is needed on my end.
Thanks again!
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org
Re: [PR] feat(database): add SingleStore engine specification [superset]
Posted by "rusackas (via GitHub)" <gi...@apache.org>.
rusackas commented on code in PR #32887:
URL: https://github.com/apache/superset/pull/32887#discussion_r2121672799
##########
docs/docs/configuration/databases.mdx:
##########
@@ -72,7 +72,7 @@ are compatible with Superset.
| [PostgreSQL](/docs/configuration/databases#postgres) | `pip install psycopg2` | `postgresql://<UserName>:<DBPassword>@<Database Host>/<Database Name>` |
| [Presto](/docs/configuration/databases#presto) | `pip install pyhive` | `presto://{username}:{password}@{hostname}:{port}/{database}` |
| [Rockset](/docs/configuration/databases#rockset) | `pip install rockset-sqlalchemy` | `rockset://<api_key>:@<api_server>` |
-| [SAP Hana](/docs/configuration/databases#hana) | `pip install hdbcli sqlalchemy-hana` or `pip install apache_superset[hana]` | `hana://{username}:{password}@{host}:{port}` |
+| [SingleStore](/docs/configuration/databases#singlestore) | `pip install sqlalchemy-singlestoredb` | `singlestoredb://{username}:{password}@{host}:{port}/{database}` |
Review Comment:
I don't think I got the formatting quite right in my copypaste there ;)
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org
Re: [PR] feat(database): add SingleStore engine specification [superset]
Posted by "korbit-ai[bot] (via GitHub)" <gi...@apache.org>.
korbit-ai[bot] commented on code in PR #32887:
URL: https://github.com/apache/superset/pull/32887#discussion_r2016896536
##########
superset/db_engine_specs/singlestore.py:
##########
@@ -0,0 +1,536 @@
+import logging
+import re
+from datetime import datetime
+from typing import Any, Optional
+from urllib import parse
+
+from sqlalchemy import types
+from sqlalchemy.engine import URL
+
+from superset.constants import TimeGrain
+from superset.db_engine_specs.base import BaseEngineSpec, ColumnTypeMapping, LimitMethod
+from superset.models.core import Database
+from superset.models.sql_lab import Query
+from superset.utils.core import GenericDataType
+
+logger = logging.getLogger(__name__)
+
+
+class SingleStoreSpec(BaseEngineSpec):
+ engine_name = "SingleStore"
+
+ engine = "singlestoredb"
+ drivers = {"singlestoredb": "SingleStore Python client"}
+ default_driver = "singlestoredb"
+
+ limit_method = LimitMethod.FORCE_LIMIT
+ allows_joins = True
+ allows_subqueries = True
+ allows_alias_in_select = True
+ allows_alias_in_orderby = True
+ time_groupby_inline = False
+ allows_alias_to_source_column = True
+ allows_hidden_orderby_agg = True
+ allows_hidden_cc_in_orderby = False
+ allows_cte_in_subquery = True
+ allow_limit_clause = True
+ max_column_name_length = 256
+ allows_sql_comments = True
+ allows_escaped_colons = True
+ supports_file_upload = True
+ supports_dynamic_schema = True
+ disable_ssh_tunneling = False
+
+ sqlalchemy_uri_placeholder = (
+ "singlestoredb://{username}:{password}@{host}:{port}/{database}"
+ )
+
+ _time_grain_expressions = {
+ None: "{col}",
+ TimeGrain.SECOND: "DATE_TRUNC('second', {col})",
+ TimeGrain.MINUTE: "DATE_TRUNC('minute', {col})",
+ TimeGrain.HOUR: "DATE_TRUNC('hour', {col})",
+ TimeGrain.DAY: "DATE_TRUNC('day', {col})",
+ TimeGrain.WEEK: "DATE_TRUNC('week', {col})",
+ TimeGrain.MONTH: "DATE_TRUNC('month', {col})",
+ TimeGrain.QUARTER: "DATE_TRUNC('quarter', {col})",
+ TimeGrain.YEAR: "DATE_TRUNC('year', {col})",
+ }
+
+ column_type_mappings: tuple[ColumnTypeMapping, ...] = (
+ (
+ re.compile(r"^tinyint", re.IGNORECASE),
+ types.SmallInteger(),
+ GenericDataType.NUMERIC,
+ ),
+ (
+ re.compile(r"^mediumint", re.IGNORECASE),
+ types.Integer(),
+ GenericDataType.NUMERIC,
+ ),
+ (
+ re.compile(r"^year", re.IGNORECASE),
+ types.Integer(),
+ GenericDataType.NUMERIC,
+ ),
+ (
+ re.compile(r"^bit", re.IGNORECASE),
+ types.LargeBinary(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^(var)?binary", re.IGNORECASE),
+ types.LargeBinary(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^(tiny|medium|long)?blob", re.IGNORECASE),
+ types.LargeBinary(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^json", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^bson", re.IGNORECASE),
+ types.LargeBinary(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^geographypoint", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^geography", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^vector", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^enum", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^set", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ )
+
+ @classmethod
+ def get_function_names(
+ cls,
+ database: Database,
+ ) -> list[str]:
+ """
+ Get a list of function names that are able to be called on the database.
+ Used for SQL Lab autocomplete.
+
+ :param database: The database to get functions for
+ :return: A list of function names usable in the database
+ """
+
+ functions = [
+ "ABS",
+ "ACOS",
+ "ADDTIME",
+ "AES_DECRYPT",
+ "AES_ENCRYPT",
+ "AGGREGATOR_ID",
+ "ANY_VALUE",
+ "APPROX_COUNT_DISTINCT",
+ "APPROX_COUNT_DISTINCT_ACCUMULATE",
+ "APPROX_COUNT_DISTINCT_COMBINE",
+ "APPROX_COUNT_DISTINCT_ESTIMATE",
+ "APPROX_GEOGRAPHY_INTERSECTS",
+ "APPROX_PERCENTILE",
+ "ASCII",
+ "ASIN",
+ "ATAN",
+ "ATAN2",
+ "ATAN",
+ "AVG",
+ "BETWEEN",
+ "NOT",
+ "BIN",
+ "BIN_TO_UUID",
+ "BINARY",
+ "BIT_AND",
+ "BIT_COUNT",
+ "BIT_OR",
+ "BIT_XOR",
+ "BM25",
+ "BSON_ARRAY_CONTAINS_BSON",
+ "BSON_ARRAY_PUSH",
+ "BSON_ARRAY_SLICE",
+ "BSON_BUILD_ARRAY",
+ "BSON_BUILD_OBJECT",
+ "BSON_COMPARE",
+ "BSON_EXTRACT_BIGINT",
+ "BSON_EXTRACT_BOOL",
+ "BSON_EXTRACT_BSON",
+ "BSON_EXTRACT_DATETIME",
+ "BSON_EXTRACT_DOUBLE",
+ "BSON_EXTRACT_STRING",
+ "BSON_GET_TYPE",
+ "BSON_INCLUDE_MASK",
+ "BSON_EXCLUDE_MASK",
+ "BSON_LENGTH",
+ "BSON_MATCH_ANY",
+ "BSON_MATCH_ANY_EXISTS",
+ "BSON_MERGE",
+ "BSON_NORMALIZE_",
+ *"BSON_SET_BSON",
+ "BSON_UNWIND",
+ "CASE",
+ "CEIL",
+ "CHAR",
+ "CHARACTER_LENGTH",
+ "CHARSET",
+ "COALESCE",
+ "CONCAT",
+ "CONCAT_WS",
+ "CONNECTION_ID",
+ "CONV",
+ "CAST",
+ "CONVERT",
+ "CONVERT_TZ",
+ "COS",
+ "COT",
+ "COUNT",
+ "CRC32",
+ "CURRENT_DATE",
+ "CURDATE",
+ "CURRENT_TIME",
+ "CURTIME",
+ "CURRENT_TIMESTAMP",
+ "DATABASE",
+ "DATE",
+ "DATE_ADD",
+ "DATE_FORMAT",
+ "DATE_SUB",
+ "DATE_TRUNC",
+ "DATEDIFF",
+ "DAY",
+ "DAYNAME",
+ "DAYOFWEEK",
+ "DAYOFYEAR",
+ "DECODE",
+ "DEGREES",
+ "DENSE_RANK",
+ "DOT_PRODUCT",
+ "ELT",
+ "ESTIMATED_QUERY_LEAF_MEMORY",
+ "ESTIMATED_QUERY_RUNTIME",
+ "EUCLIDEAN_DISTANCE",
+ "EXP",
+ "EXTRACT",
+ "FIELD",
+ "FIRST",
+ "FIRST_VALUE",
+ "FLOOR",
+ "FORMAT",
+ "FOUND_ROWS",
+ "FROM_BASE64",
+ "FROM_DAYS",
+ "FROM_UNIXTIME",
+ "GEOGRAPHY_AREA",
+ "GEOGRAPHY_CONTAINS",
+ "GEOGRAPHY_DISTANCE",
+ "GEOGRAPHY_INTERSECTS",
+ "GEOGRAPHY_LATITUDE",
+ "GEOGRAPHY_LENGTH",
+ "GEOGRAPHY_LONGITUDE",
+ "GEOGRAPHY_POINT",
+ "GEOGRAPHY_WITHIN_DISTANCE",
+ "GET_FORMAT",
+ "GREATEST",
+ "GROUP_CONCAT",
+ "HEX",
+ "HIGHLIGHT",
+ "HOUR",
+ "IF",
+ "IN",
+ "INET_ATON",
+ "INET_NTOA",
+ "INET6_ATON",
+ "INET6_NTOA",
+ "INITCAP",
+ "INSTR",
+ "IS_BSON_NULL",
+ "IS_UUID",
+ "ISNULL",
+ "ISNUMERIC",
+ "JSON_AGG",
+ "JSON_ARRAY_CONTAINS_DOUBLE",
+ "JSON_ARRAY_CONTAINS_STRING",
+ "JSON_ARRAY_CONTAINS_JSON",
+ "JSON_ARRAY_PACK",
+ "JSON_ARRAY_PUSH_DOUBLE",
+ "JSON_ARRAY_PUSH_STRING",
+ "JSON_ARRAY_PUSH_JSON",
+ "JSON_ARRAY_UNPACK",
+ "JSON_BUILD_ARRAY",
+ "JSON_BUILD_OBJECT",
+ "JSON_DELETE_KEY",
+ "JSON_EXTRACT_DOUBLE",
+ "JSON_EXTRACT_STRING",
+ "JSON_EXTRACT_JSON",
+ "JSON_EXTRACT_BIGINT",
+ "JSON_GET_TYPE",
+ "JSON_KEYS",
+ "JSON_LENGTH",
+ "JSON_MATCH_ANY",
+ "JSON_MERGE_PATCH",
+ "JSON_PRETTY",
+ "JSON_SET_DOUBLE",
+ "JSON_SET_STRING",
+ "JSON_SET_JSON",
+ "JSON_SPLICE_DOUBLE",
+ "JSON_SPLICE_STRING",
+ "JSON_SPLICE_JSON",
+ "JSON_TO_ARRAY",
+ "LAG",
+ "LAST",
+ "LAST_DAY",
+ "LAST_INSERT_ID",
+ "LAST_VALUE",
+ "LCASE",
+ "LEAD",
+ "LEAST",
+ "LEFT",
+ "LENGTH",
+ "LIKE",
+ "LN",
+ "LOCALTIMESTAMP",
+ "LOCATE",
+ "LOG",
+ "LOG10",
+ "LOG2",
+ "LPAD",
+ "LTRIM",
+ "MATCH",
+ "MAX",
+ "MD5",
+ "MEDIAN",
+ "MICROSECOND",
+ "MIN",
+ "MINUTE",
+ "MOD",
+ "MONTH",
+ "MONTHNAME",
+ "MONTHS_BETWEEN",
+ "NOPARAM",
+ "NOW",
+ "NTH_VALUE",
+ "NTILE",
+ "NULLIF",
+ "NVL",
+ "IFNULL",
+ "PERCENT_RANK",
+ "PERCENTILE_CONT",
+ "MEDIAN",
+ "PERCENTILE_DISC",
+ "PI",
+ "POW",
+ "QUARTER",
+ "QUOTE",
+ "RADIANS",
+ "RAND",
+ "RANK",
+ "REDUCE",
+ "REGEXP_INSTR",
+ "REGEXP_MATCH",
+ "REGEXP_REPLACE",
+ "REGEXP_SUBSTR",
+ "REPLACE",
+ "REVERSE",
+ "RIGHT",
+ "RLIKE",
+ "REGEXP",
+ "ROUND",
+ "ROW_COUNT",
+ "ROW_NUMBER",
+ "RPAD",
+ "RTRIM",
+ "SCALAR_VECTOR_MUL",
+ "SEC_TO_TIME",
+ "SECOND",
+ "SECRET",
+ "SET",
+ "SHA1",
+ "SHA2",
+ "SIGMOID",
+ "SIGN",
+ "SIN",
+ "SLEEP",
+ "SPLIT",
+ "SQRT",
+ "STD",
+ "STDDEV",
+ "STDDEV_POP",
+ "STDDEV_SAMP",
+ "STR_TO_DATE",
+ "strcmp",
+ "STRING_BYTES",
+ "SUBSTRING",
+ "SUBSTRING_INDEX",
+ "SUM",
+ "SYS_GUID",
+ "UUID",
+ "TAN",
+ "TIME",
+ "TIME_BUCKET",
+ "TIME_FORMAT",
+ "TIME_TO_SEC",
+ "TIMEDIFF",
+ "TIMESTAMP",
+ "TIMESTAMPADD",
+ "TIMESTAMPDIFF",
+ "TO_BASE64",
+ "TO_CHAR",
+ "TO_DATE",
+ "TO_DAYS",
+ "TO_JSON",
+ "TO_NUMBER",
+ "TO_SECONDS",
+ "TO_TIMESTAMP",
+ "TRIM",
+ "TRUNC",
+ "TRUNCATE",
+ "UCASE",
+ "UNHEX",
+ "UNIX_TIMESTAMP",
+ "USER",
+ "UTC_DATE",
+ "UTC_TIME",
+ "UTC_TIMESTAMP",
+ "UUID_TO_BIN",
+ "VARIANCE",
+ "VAR_SAMP",
+ "VECTOR_ADD",
+ "VECTOR_ELEMENTS_SUM",
+ "VECTOR_KTH_ELEMENT",
+ "VECTOR_MUL",
+ "VECTOR_NUM_ELEMENTS",
+ "VECTOR_SORT",
+ "VECTOR_SUB",
+ "VECTOR_SUBVECTOR",
+ "VECTOR_SUM",
+ "WEEK",
+ "WEEKDAY",
+ "YEAR",
+ ]
+
+ if (database_name := cls.get_default_schema(database, None)) is not None:
+ df = database.get_df(
+ f"SHOW FUNCTIONS IN `{database_name.replace('`', '``')}`"
+ )
Review Comment:
### Inefficient Function Name Resolution <sub></sub>
<details>
<summary>Tell me more</summary>
###### What is the issue?
The get_function_names method maintains a large hardcoded list of functions and then additionally queries the database to fetch more functions, potentially duplicating function names and performing unnecessary database calls.
###### Why this matters
This approach adds overhead by making a database round-trip for every function name lookup, even though most standard functions are already known. The database query could be expensive when performed frequently, especially in autocomplete scenarios.
###### Suggested change ∙ *Feature Preview*
Cache the database-specific functions on first retrieval and merge with the standard functions. Implement as:
```python
@classmethod
def get_function_names(cls, database: Database) -> list[str]:
if not hasattr(cls, '_cached_db_functions'):
cls._cached_db_functions = {}
db_id = database.id
if db_id not in cls._cached_db_functions:
# Fetch from DB only once and cache
if (database_name := cls.get_default_schema(database, None)) is not None:
try:
df = database.get_df(f"SHOW FUNCTIONS IN `{database_name.replace('`', '``')}`")
function_name_col = f"Functions_in_{database_name}"
if function_name_col in df:
cls._cached_db_functions[db_id] = df[function_name_col].tolist()
else:
cls._cached_db_functions[db_id] = []
except Exception:
cls._cached_db_functions[db_id] = []
logger.error("Error fetching database functions", exc_info=True)
return cls.base_functions + cls._cached_db_functions.get(db_id, [])
```
###### Provide feedback to improve future suggestions
[](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/a1be72a8-0476-40e6-a75b-24098c18dd76/upvote) [](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/a1be72a8-0476-40e6-a75b-24098c18dd76?what_not_true=true) [](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/a1be72a8-0476-40e6-a75b-24098c18dd76?what_out_of_scope=true) [](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/a1be72a8-0476-40e6-a75b-24098c18dd76?what_not_in_standard=true) [](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/a1be72a8-0476-40e6-a75b-24098c18dd76)
</details>
<sub>
💬 Looking for more details? Reply to this comment to chat with Korbit.
</sub>
<!--- korbi internal id:95ebca98-a837-4861-9c4e-df23bb8db69c -->
[](95ebca98-a837-4861-9c4e-df23bb8db69c)
##########
superset/db_engine_specs/singlestore.py:
##########
@@ -0,0 +1,536 @@
+import logging
+import re
+from datetime import datetime
+from typing import Any, Optional
+from urllib import parse
+
+from sqlalchemy import types
+from sqlalchemy.engine import URL
+
+from superset.constants import TimeGrain
+from superset.db_engine_specs.base import BaseEngineSpec, ColumnTypeMapping, LimitMethod
+from superset.models.core import Database
+from superset.models.sql_lab import Query
+from superset.utils.core import GenericDataType
+
+logger = logging.getLogger(__name__)
+
+
+class SingleStoreSpec(BaseEngineSpec):
+ engine_name = "SingleStore"
+
+ engine = "singlestoredb"
+ drivers = {"singlestoredb": "SingleStore Python client"}
+ default_driver = "singlestoredb"
+
+ limit_method = LimitMethod.FORCE_LIMIT
+ allows_joins = True
+ allows_subqueries = True
+ allows_alias_in_select = True
+ allows_alias_in_orderby = True
+ time_groupby_inline = False
+ allows_alias_to_source_column = True
+ allows_hidden_orderby_agg = True
+ allows_hidden_cc_in_orderby = False
+ allows_cte_in_subquery = True
+ allow_limit_clause = True
+ max_column_name_length = 256
+ allows_sql_comments = True
+ allows_escaped_colons = True
+ supports_file_upload = True
+ supports_dynamic_schema = True
+ disable_ssh_tunneling = False
+
+ sqlalchemy_uri_placeholder = (
+ "singlestoredb://{username}:{password}@{host}:{port}/{database}"
+ )
+
+ _time_grain_expressions = {
+ None: "{col}",
+ TimeGrain.SECOND: "DATE_TRUNC('second', {col})",
+ TimeGrain.MINUTE: "DATE_TRUNC('minute', {col})",
+ TimeGrain.HOUR: "DATE_TRUNC('hour', {col})",
+ TimeGrain.DAY: "DATE_TRUNC('day', {col})",
+ TimeGrain.WEEK: "DATE_TRUNC('week', {col})",
+ TimeGrain.MONTH: "DATE_TRUNC('month', {col})",
+ TimeGrain.QUARTER: "DATE_TRUNC('quarter', {col})",
+ TimeGrain.YEAR: "DATE_TRUNC('year', {col})",
+ }
+
+ column_type_mappings: tuple[ColumnTypeMapping, ...] = (
+ (
+ re.compile(r"^tinyint", re.IGNORECASE),
+ types.SmallInteger(),
+ GenericDataType.NUMERIC,
+ ),
+ (
+ re.compile(r"^mediumint", re.IGNORECASE),
+ types.Integer(),
+ GenericDataType.NUMERIC,
+ ),
+ (
+ re.compile(r"^year", re.IGNORECASE),
+ types.Integer(),
+ GenericDataType.NUMERIC,
+ ),
+ (
+ re.compile(r"^bit", re.IGNORECASE),
+ types.LargeBinary(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^(var)?binary", re.IGNORECASE),
+ types.LargeBinary(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^(tiny|medium|long)?blob", re.IGNORECASE),
+ types.LargeBinary(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^json", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^bson", re.IGNORECASE),
+ types.LargeBinary(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^geographypoint", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^geography", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^vector", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^enum", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^set", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ )
+
+ @classmethod
+ def get_function_names(
+ cls,
+ database: Database,
+ ) -> list[str]:
+ """
+ Get a list of function names that are able to be called on the database.
+ Used for SQL Lab autocomplete.
+
+ :param database: The database to get functions for
+ :return: A list of function names usable in the database
+ """
+
+ functions = [
+ "ABS",
+ "ACOS",
+ "ADDTIME",
+ "AES_DECRYPT",
+ "AES_ENCRYPT",
+ "AGGREGATOR_ID",
+ "ANY_VALUE",
+ "APPROX_COUNT_DISTINCT",
+ "APPROX_COUNT_DISTINCT_ACCUMULATE",
+ "APPROX_COUNT_DISTINCT_COMBINE",
+ "APPROX_COUNT_DISTINCT_ESTIMATE",
+ "APPROX_GEOGRAPHY_INTERSECTS",
+ "APPROX_PERCENTILE",
+ "ASCII",
+ "ASIN",
+ "ATAN",
+ "ATAN2",
+ "ATAN",
+ "AVG",
+ "BETWEEN",
+ "NOT",
+ "BIN",
+ "BIN_TO_UUID",
+ "BINARY",
+ "BIT_AND",
+ "BIT_COUNT",
+ "BIT_OR",
+ "BIT_XOR",
+ "BM25",
+ "BSON_ARRAY_CONTAINS_BSON",
+ "BSON_ARRAY_PUSH",
+ "BSON_ARRAY_SLICE",
+ "BSON_BUILD_ARRAY",
+ "BSON_BUILD_OBJECT",
+ "BSON_COMPARE",
+ "BSON_EXTRACT_BIGINT",
+ "BSON_EXTRACT_BOOL",
+ "BSON_EXTRACT_BSON",
+ "BSON_EXTRACT_DATETIME",
+ "BSON_EXTRACT_DOUBLE",
+ "BSON_EXTRACT_STRING",
+ "BSON_GET_TYPE",
+ "BSON_INCLUDE_MASK",
+ "BSON_EXCLUDE_MASK",
+ "BSON_LENGTH",
+ "BSON_MATCH_ANY",
+ "BSON_MATCH_ANY_EXISTS",
+ "BSON_MERGE",
+ "BSON_NORMALIZE_",
+ *"BSON_SET_BSON",
+ "BSON_UNWIND",
+ "CASE",
+ "CEIL",
+ "CHAR",
+ "CHARACTER_LENGTH",
+ "CHARSET",
+ "COALESCE",
+ "CONCAT",
+ "CONCAT_WS",
+ "CONNECTION_ID",
+ "CONV",
+ "CAST",
+ "CONVERT",
+ "CONVERT_TZ",
+ "COS",
+ "COT",
+ "COUNT",
+ "CRC32",
+ "CURRENT_DATE",
+ "CURDATE",
+ "CURRENT_TIME",
+ "CURTIME",
+ "CURRENT_TIMESTAMP",
+ "DATABASE",
+ "DATE",
+ "DATE_ADD",
+ "DATE_FORMAT",
+ "DATE_SUB",
+ "DATE_TRUNC",
+ "DATEDIFF",
+ "DAY",
+ "DAYNAME",
+ "DAYOFWEEK",
+ "DAYOFYEAR",
+ "DECODE",
+ "DEGREES",
+ "DENSE_RANK",
+ "DOT_PRODUCT",
+ "ELT",
+ "ESTIMATED_QUERY_LEAF_MEMORY",
+ "ESTIMATED_QUERY_RUNTIME",
+ "EUCLIDEAN_DISTANCE",
+ "EXP",
+ "EXTRACT",
+ "FIELD",
+ "FIRST",
+ "FIRST_VALUE",
+ "FLOOR",
+ "FORMAT",
+ "FOUND_ROWS",
+ "FROM_BASE64",
+ "FROM_DAYS",
+ "FROM_UNIXTIME",
+ "GEOGRAPHY_AREA",
+ "GEOGRAPHY_CONTAINS",
+ "GEOGRAPHY_DISTANCE",
+ "GEOGRAPHY_INTERSECTS",
+ "GEOGRAPHY_LATITUDE",
+ "GEOGRAPHY_LENGTH",
+ "GEOGRAPHY_LONGITUDE",
+ "GEOGRAPHY_POINT",
+ "GEOGRAPHY_WITHIN_DISTANCE",
+ "GET_FORMAT",
+ "GREATEST",
+ "GROUP_CONCAT",
+ "HEX",
+ "HIGHLIGHT",
+ "HOUR",
+ "IF",
+ "IN",
+ "INET_ATON",
+ "INET_NTOA",
+ "INET6_ATON",
+ "INET6_NTOA",
+ "INITCAP",
+ "INSTR",
+ "IS_BSON_NULL",
+ "IS_UUID",
+ "ISNULL",
+ "ISNUMERIC",
+ "JSON_AGG",
+ "JSON_ARRAY_CONTAINS_DOUBLE",
+ "JSON_ARRAY_CONTAINS_STRING",
+ "JSON_ARRAY_CONTAINS_JSON",
+ "JSON_ARRAY_PACK",
+ "JSON_ARRAY_PUSH_DOUBLE",
+ "JSON_ARRAY_PUSH_STRING",
+ "JSON_ARRAY_PUSH_JSON",
+ "JSON_ARRAY_UNPACK",
+ "JSON_BUILD_ARRAY",
+ "JSON_BUILD_OBJECT",
+ "JSON_DELETE_KEY",
+ "JSON_EXTRACT_DOUBLE",
+ "JSON_EXTRACT_STRING",
+ "JSON_EXTRACT_JSON",
+ "JSON_EXTRACT_BIGINT",
+ "JSON_GET_TYPE",
+ "JSON_KEYS",
+ "JSON_LENGTH",
+ "JSON_MATCH_ANY",
+ "JSON_MERGE_PATCH",
+ "JSON_PRETTY",
+ "JSON_SET_DOUBLE",
+ "JSON_SET_STRING",
+ "JSON_SET_JSON",
+ "JSON_SPLICE_DOUBLE",
+ "JSON_SPLICE_STRING",
+ "JSON_SPLICE_JSON",
+ "JSON_TO_ARRAY",
+ "LAG",
+ "LAST",
+ "LAST_DAY",
+ "LAST_INSERT_ID",
+ "LAST_VALUE",
+ "LCASE",
+ "LEAD",
+ "LEAST",
+ "LEFT",
+ "LENGTH",
+ "LIKE",
+ "LN",
+ "LOCALTIMESTAMP",
+ "LOCATE",
+ "LOG",
+ "LOG10",
+ "LOG2",
+ "LPAD",
+ "LTRIM",
+ "MATCH",
+ "MAX",
+ "MD5",
+ "MEDIAN",
+ "MICROSECOND",
+ "MIN",
+ "MINUTE",
+ "MOD",
+ "MONTH",
+ "MONTHNAME",
+ "MONTHS_BETWEEN",
+ "NOPARAM",
+ "NOW",
+ "NTH_VALUE",
+ "NTILE",
+ "NULLIF",
+ "NVL",
+ "IFNULL",
+ "PERCENT_RANK",
+ "PERCENTILE_CONT",
+ "MEDIAN",
+ "PERCENTILE_DISC",
+ "PI",
+ "POW",
+ "QUARTER",
+ "QUOTE",
+ "RADIANS",
+ "RAND",
+ "RANK",
+ "REDUCE",
+ "REGEXP_INSTR",
+ "REGEXP_MATCH",
+ "REGEXP_REPLACE",
+ "REGEXP_SUBSTR",
+ "REPLACE",
+ "REVERSE",
+ "RIGHT",
+ "RLIKE",
+ "REGEXP",
+ "ROUND",
+ "ROW_COUNT",
+ "ROW_NUMBER",
+ "RPAD",
+ "RTRIM",
+ "SCALAR_VECTOR_MUL",
+ "SEC_TO_TIME",
+ "SECOND",
+ "SECRET",
+ "SET",
+ "SHA1",
+ "SHA2",
+ "SIGMOID",
+ "SIGN",
+ "SIN",
+ "SLEEP",
+ "SPLIT",
+ "SQRT",
+ "STD",
+ "STDDEV",
+ "STDDEV_POP",
+ "STDDEV_SAMP",
+ "STR_TO_DATE",
+ "strcmp",
+ "STRING_BYTES",
+ "SUBSTRING",
+ "SUBSTRING_INDEX",
+ "SUM",
+ "SYS_GUID",
+ "UUID",
+ "TAN",
+ "TIME",
+ "TIME_BUCKET",
+ "TIME_FORMAT",
+ "TIME_TO_SEC",
+ "TIMEDIFF",
+ "TIMESTAMP",
+ "TIMESTAMPADD",
+ "TIMESTAMPDIFF",
+ "TO_BASE64",
+ "TO_CHAR",
+ "TO_DATE",
+ "TO_DAYS",
+ "TO_JSON",
+ "TO_NUMBER",
+ "TO_SECONDS",
+ "TO_TIMESTAMP",
+ "TRIM",
+ "TRUNC",
+ "TRUNCATE",
+ "UCASE",
+ "UNHEX",
+ "UNIX_TIMESTAMP",
+ "USER",
+ "UTC_DATE",
+ "UTC_TIME",
+ "UTC_TIMESTAMP",
+ "UUID_TO_BIN",
+ "VARIANCE",
+ "VAR_SAMP",
+ "VECTOR_ADD",
+ "VECTOR_ELEMENTS_SUM",
+ "VECTOR_KTH_ELEMENT",
+ "VECTOR_MUL",
+ "VECTOR_NUM_ELEMENTS",
+ "VECTOR_SORT",
+ "VECTOR_SUB",
+ "VECTOR_SUBVECTOR",
+ "VECTOR_SUM",
+ "WEEK",
+ "WEEKDAY",
+ "YEAR",
+ ]
+
+ if (database_name := cls.get_default_schema(database, None)) is not None:
+ df = database.get_df(
+ f"SHOW FUNCTIONS IN `{database_name.replace('`', '``')}`"
+ )
+
+ function_name_col = f"Functions_in_{database_name}"
+ if function_name_col in df:
+ functions += df[function_name_col].tolist()
+ else:
+ columns = df.columns.values.tolist()
+ logger.error(
+ "Payload from `SHOW FUNCTIONS` has the incorrect format. "
+ "Expected column `%s`, found: %s.",
+ function_name_col,
+ ", ".join(columns),
+ exc_info=True,
+ )
+
+ return functions
+
+ @classmethod
+ def epoch_to_dttm(cls) -> str:
+ return "from_unixtime({col})"
+
+ @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"CAST('{dttm.date().isoformat()}' AS DATE)"
+ if isinstance(sqla_type, types.TIMESTAMP):
+ return f"""('{dttm.isoformat(sep=" ", timespec="microseconds")}' :> TIMESTAMP(6))""" # noqa: E501
+ if isinstance(sqla_type, types.DateTime):
+ return f"""CAST('{dttm.isoformat(sep=" ", timespec="microseconds")}' AS DATETIME(6))""" # noqa: E501
+ if isinstance(sqla_type, types.Time):
+ return f"""CAST('{dttm.strftime("%H:%M:%S.%f")}' AS TIME(6))"""
+
+ return None
+
+ @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]]:
+ uri, new_connect_args = super().adjust_engine_params(
+ uri,
+ connect_args,
+ catalog,
+ schema,
+ )
+
+ if schema:
+ uri = uri.set(database=parse.quote(schema, safe=""))
+
+ return uri, new_connect_args
+
+ @classmethod
+ def get_schema_from_engine_params(
+ cls,
+ sqlalchemy_uri: URL,
+ connect_args: dict[str, Any],
+ ) -> Optional[str]:
+ """
+ Return the configured schema.
+
+ A MySQL database is a SQLAlchemy schema.
+ """
+ return parse.unquote(sqlalchemy_uri.database)
Review Comment:
### Missing None Check in Schema Extraction <sub></sub>
<details>
<summary>Tell me more</summary>
###### What is the issue?
The get_schema_from_engine_params method doesn't handle the case where sqlalchemy_uri.database is None.
###### Why this matters
This could lead to a TypeError when trying to unquote None, causing unexpected crashes in the application.
###### Suggested change ∙ *Feature Preview*
Add None check before unquoting:
```python
return parse.unquote(sqlalchemy_uri.database) if sqlalchemy_uri.database else None
```
###### Provide feedback to improve future suggestions
[](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/97ed4b6a-8151-4364-8188-afb7363debcb/upvote) [](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/97ed4b6a-8151-4364-8188-afb7363debcb?what_not_true=true) [](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/97ed4b6a-8151-4364-8188-afb7363debcb?what_out_of_scope=true) [](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/97ed4b6a-8151-4364-8188-afb7363debcb?what_not_in_standard=true) [](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/97ed4b6a-8151-4364-8188-afb7363debcb)
</details>
<sub>
💬 Looking for more details? Reply to this comment to chat with Korbit.
</sub>
<!--- korbi internal id:72f11f02-2613-4fc5-81f4-42ba0cd60d22 -->
[](72f11f02-2613-4fc5-81f4-42ba0cd60d22)
##########
superset/db_engine_specs/singlestore.py:
##########
@@ -0,0 +1,536 @@
+import logging
+import re
+from datetime import datetime
+from typing import Any, Optional
+from urllib import parse
+
+from sqlalchemy import types
+from sqlalchemy.engine import URL
+
+from superset.constants import TimeGrain
+from superset.db_engine_specs.base import BaseEngineSpec, ColumnTypeMapping, LimitMethod
+from superset.models.core import Database
+from superset.models.sql_lab import Query
+from superset.utils.core import GenericDataType
+
+logger = logging.getLogger(__name__)
+
+
+class SingleStoreSpec(BaseEngineSpec):
+ engine_name = "SingleStore"
+
+ engine = "singlestoredb"
+ drivers = {"singlestoredb": "SingleStore Python client"}
+ default_driver = "singlestoredb"
+
+ limit_method = LimitMethod.FORCE_LIMIT
+ allows_joins = True
+ allows_subqueries = True
+ allows_alias_in_select = True
+ allows_alias_in_orderby = True
+ time_groupby_inline = False
+ allows_alias_to_source_column = True
+ allows_hidden_orderby_agg = True
+ allows_hidden_cc_in_orderby = False
+ allows_cte_in_subquery = True
+ allow_limit_clause = True
+ max_column_name_length = 256
+ allows_sql_comments = True
+ allows_escaped_colons = True
+ supports_file_upload = True
+ supports_dynamic_schema = True
+ disable_ssh_tunneling = False
+
+ sqlalchemy_uri_placeholder = (
+ "singlestoredb://{username}:{password}@{host}:{port}/{database}"
+ )
+
+ _time_grain_expressions = {
+ None: "{col}",
+ TimeGrain.SECOND: "DATE_TRUNC('second', {col})",
+ TimeGrain.MINUTE: "DATE_TRUNC('minute', {col})",
+ TimeGrain.HOUR: "DATE_TRUNC('hour', {col})",
+ TimeGrain.DAY: "DATE_TRUNC('day', {col})",
+ TimeGrain.WEEK: "DATE_TRUNC('week', {col})",
+ TimeGrain.MONTH: "DATE_TRUNC('month', {col})",
+ TimeGrain.QUARTER: "DATE_TRUNC('quarter', {col})",
+ TimeGrain.YEAR: "DATE_TRUNC('year', {col})",
+ }
+
+ column_type_mappings: tuple[ColumnTypeMapping, ...] = (
+ (
+ re.compile(r"^tinyint", re.IGNORECASE),
+ types.SmallInteger(),
+ GenericDataType.NUMERIC,
+ ),
+ (
+ re.compile(r"^mediumint", re.IGNORECASE),
+ types.Integer(),
+ GenericDataType.NUMERIC,
+ ),
+ (
+ re.compile(r"^year", re.IGNORECASE),
+ types.Integer(),
+ GenericDataType.NUMERIC,
+ ),
+ (
+ re.compile(r"^bit", re.IGNORECASE),
+ types.LargeBinary(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^(var)?binary", re.IGNORECASE),
+ types.LargeBinary(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^(tiny|medium|long)?blob", re.IGNORECASE),
+ types.LargeBinary(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^json", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^bson", re.IGNORECASE),
+ types.LargeBinary(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^geographypoint", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^geography", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^vector", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^enum", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^set", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ )
+
+ @classmethod
+ def get_function_names(
+ cls,
+ database: Database,
+ ) -> list[str]:
+ """
+ Get a list of function names that are able to be called on the database.
+ Used for SQL Lab autocomplete.
+
+ :param database: The database to get functions for
+ :return: A list of function names usable in the database
+ """
+
+ functions = [
+ "ABS",
+ "ACOS",
+ "ADDTIME",
+ "AES_DECRYPT",
+ "AES_ENCRYPT",
+ "AGGREGATOR_ID",
+ "ANY_VALUE",
+ "APPROX_COUNT_DISTINCT",
+ "APPROX_COUNT_DISTINCT_ACCUMULATE",
+ "APPROX_COUNT_DISTINCT_COMBINE",
+ "APPROX_COUNT_DISTINCT_ESTIMATE",
+ "APPROX_GEOGRAPHY_INTERSECTS",
+ "APPROX_PERCENTILE",
+ "ASCII",
+ "ASIN",
+ "ATAN",
+ "ATAN2",
+ "ATAN",
+ "AVG",
+ "BETWEEN",
+ "NOT",
+ "BIN",
+ "BIN_TO_UUID",
+ "BINARY",
+ "BIT_AND",
+ "BIT_COUNT",
+ "BIT_OR",
+ "BIT_XOR",
+ "BM25",
+ "BSON_ARRAY_CONTAINS_BSON",
+ "BSON_ARRAY_PUSH",
+ "BSON_ARRAY_SLICE",
+ "BSON_BUILD_ARRAY",
+ "BSON_BUILD_OBJECT",
+ "BSON_COMPARE",
+ "BSON_EXTRACT_BIGINT",
+ "BSON_EXTRACT_BOOL",
+ "BSON_EXTRACT_BSON",
+ "BSON_EXTRACT_DATETIME",
+ "BSON_EXTRACT_DOUBLE",
+ "BSON_EXTRACT_STRING",
+ "BSON_GET_TYPE",
+ "BSON_INCLUDE_MASK",
+ "BSON_EXCLUDE_MASK",
+ "BSON_LENGTH",
+ "BSON_MATCH_ANY",
+ "BSON_MATCH_ANY_EXISTS",
+ "BSON_MERGE",
+ "BSON_NORMALIZE_",
+ *"BSON_SET_BSON",
+ "BSON_UNWIND",
+ "CASE",
+ "CEIL",
+ "CHAR",
+ "CHARACTER_LENGTH",
+ "CHARSET",
+ "COALESCE",
+ "CONCAT",
+ "CONCAT_WS",
+ "CONNECTION_ID",
+ "CONV",
+ "CAST",
+ "CONVERT",
+ "CONVERT_TZ",
+ "COS",
+ "COT",
+ "COUNT",
+ "CRC32",
+ "CURRENT_DATE",
+ "CURDATE",
+ "CURRENT_TIME",
+ "CURTIME",
+ "CURRENT_TIMESTAMP",
+ "DATABASE",
+ "DATE",
+ "DATE_ADD",
+ "DATE_FORMAT",
+ "DATE_SUB",
+ "DATE_TRUNC",
+ "DATEDIFF",
+ "DAY",
+ "DAYNAME",
+ "DAYOFWEEK",
+ "DAYOFYEAR",
+ "DECODE",
+ "DEGREES",
+ "DENSE_RANK",
+ "DOT_PRODUCT",
+ "ELT",
+ "ESTIMATED_QUERY_LEAF_MEMORY",
+ "ESTIMATED_QUERY_RUNTIME",
+ "EUCLIDEAN_DISTANCE",
+ "EXP",
+ "EXTRACT",
+ "FIELD",
+ "FIRST",
+ "FIRST_VALUE",
+ "FLOOR",
+ "FORMAT",
+ "FOUND_ROWS",
+ "FROM_BASE64",
+ "FROM_DAYS",
+ "FROM_UNIXTIME",
+ "GEOGRAPHY_AREA",
+ "GEOGRAPHY_CONTAINS",
+ "GEOGRAPHY_DISTANCE",
+ "GEOGRAPHY_INTERSECTS",
+ "GEOGRAPHY_LATITUDE",
+ "GEOGRAPHY_LENGTH",
+ "GEOGRAPHY_LONGITUDE",
+ "GEOGRAPHY_POINT",
+ "GEOGRAPHY_WITHIN_DISTANCE",
+ "GET_FORMAT",
+ "GREATEST",
+ "GROUP_CONCAT",
+ "HEX",
+ "HIGHLIGHT",
+ "HOUR",
+ "IF",
+ "IN",
+ "INET_ATON",
+ "INET_NTOA",
+ "INET6_ATON",
+ "INET6_NTOA",
+ "INITCAP",
+ "INSTR",
+ "IS_BSON_NULL",
+ "IS_UUID",
+ "ISNULL",
+ "ISNUMERIC",
+ "JSON_AGG",
+ "JSON_ARRAY_CONTAINS_DOUBLE",
+ "JSON_ARRAY_CONTAINS_STRING",
+ "JSON_ARRAY_CONTAINS_JSON",
+ "JSON_ARRAY_PACK",
+ "JSON_ARRAY_PUSH_DOUBLE",
+ "JSON_ARRAY_PUSH_STRING",
+ "JSON_ARRAY_PUSH_JSON",
+ "JSON_ARRAY_UNPACK",
+ "JSON_BUILD_ARRAY",
+ "JSON_BUILD_OBJECT",
+ "JSON_DELETE_KEY",
+ "JSON_EXTRACT_DOUBLE",
+ "JSON_EXTRACT_STRING",
+ "JSON_EXTRACT_JSON",
+ "JSON_EXTRACT_BIGINT",
+ "JSON_GET_TYPE",
+ "JSON_KEYS",
+ "JSON_LENGTH",
+ "JSON_MATCH_ANY",
+ "JSON_MERGE_PATCH",
+ "JSON_PRETTY",
+ "JSON_SET_DOUBLE",
+ "JSON_SET_STRING",
+ "JSON_SET_JSON",
+ "JSON_SPLICE_DOUBLE",
+ "JSON_SPLICE_STRING",
+ "JSON_SPLICE_JSON",
+ "JSON_TO_ARRAY",
+ "LAG",
+ "LAST",
+ "LAST_DAY",
+ "LAST_INSERT_ID",
+ "LAST_VALUE",
+ "LCASE",
+ "LEAD",
+ "LEAST",
+ "LEFT",
+ "LENGTH",
+ "LIKE",
+ "LN",
+ "LOCALTIMESTAMP",
+ "LOCATE",
+ "LOG",
+ "LOG10",
+ "LOG2",
+ "LPAD",
+ "LTRIM",
+ "MATCH",
+ "MAX",
+ "MD5",
+ "MEDIAN",
+ "MICROSECOND",
+ "MIN",
+ "MINUTE",
+ "MOD",
+ "MONTH",
+ "MONTHNAME",
+ "MONTHS_BETWEEN",
+ "NOPARAM",
+ "NOW",
+ "NTH_VALUE",
+ "NTILE",
+ "NULLIF",
+ "NVL",
+ "IFNULL",
+ "PERCENT_RANK",
+ "PERCENTILE_CONT",
+ "MEDIAN",
+ "PERCENTILE_DISC",
+ "PI",
+ "POW",
+ "QUARTER",
+ "QUOTE",
+ "RADIANS",
+ "RAND",
+ "RANK",
+ "REDUCE",
+ "REGEXP_INSTR",
+ "REGEXP_MATCH",
+ "REGEXP_REPLACE",
+ "REGEXP_SUBSTR",
+ "REPLACE",
+ "REVERSE",
+ "RIGHT",
+ "RLIKE",
+ "REGEXP",
+ "ROUND",
+ "ROW_COUNT",
+ "ROW_NUMBER",
+ "RPAD",
+ "RTRIM",
+ "SCALAR_VECTOR_MUL",
+ "SEC_TO_TIME",
+ "SECOND",
+ "SECRET",
+ "SET",
+ "SHA1",
+ "SHA2",
+ "SIGMOID",
+ "SIGN",
+ "SIN",
+ "SLEEP",
+ "SPLIT",
+ "SQRT",
+ "STD",
+ "STDDEV",
+ "STDDEV_POP",
+ "STDDEV_SAMP",
+ "STR_TO_DATE",
+ "strcmp",
+ "STRING_BYTES",
+ "SUBSTRING",
+ "SUBSTRING_INDEX",
+ "SUM",
+ "SYS_GUID",
+ "UUID",
+ "TAN",
+ "TIME",
+ "TIME_BUCKET",
+ "TIME_FORMAT",
+ "TIME_TO_SEC",
+ "TIMEDIFF",
+ "TIMESTAMP",
+ "TIMESTAMPADD",
+ "TIMESTAMPDIFF",
+ "TO_BASE64",
+ "TO_CHAR",
+ "TO_DATE",
+ "TO_DAYS",
+ "TO_JSON",
+ "TO_NUMBER",
+ "TO_SECONDS",
+ "TO_TIMESTAMP",
+ "TRIM",
+ "TRUNC",
+ "TRUNCATE",
+ "UCASE",
+ "UNHEX",
+ "UNIX_TIMESTAMP",
+ "USER",
+ "UTC_DATE",
+ "UTC_TIME",
+ "UTC_TIMESTAMP",
+ "UUID_TO_BIN",
+ "VARIANCE",
+ "VAR_SAMP",
+ "VECTOR_ADD",
+ "VECTOR_ELEMENTS_SUM",
+ "VECTOR_KTH_ELEMENT",
+ "VECTOR_MUL",
+ "VECTOR_NUM_ELEMENTS",
+ "VECTOR_SORT",
+ "VECTOR_SUB",
+ "VECTOR_SUBVECTOR",
+ "VECTOR_SUM",
+ "WEEK",
+ "WEEKDAY",
+ "YEAR",
+ ]
+
+ if (database_name := cls.get_default_schema(database, None)) is not None:
+ df = database.get_df(
+ f"SHOW FUNCTIONS IN `{database_name.replace('`', '``')}`"
+ )
+
+ function_name_col = f"Functions_in_{database_name}"
+ if function_name_col in df:
+ functions += df[function_name_col].tolist()
+ else:
+ columns = df.columns.values.tolist()
+ logger.error(
+ "Payload from `SHOW FUNCTIONS` has the incorrect format. "
+ "Expected column `%s`, found: %s.",
+ function_name_col,
+ ", ".join(columns),
+ exc_info=True,
+ )
+
+ return functions
+
+ @classmethod
+ def epoch_to_dttm(cls) -> str:
+ return "from_unixtime({col})"
+
+ @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"CAST('{dttm.date().isoformat()}' AS DATE)"
+ if isinstance(sqla_type, types.TIMESTAMP):
+ return f"""('{dttm.isoformat(sep=" ", timespec="microseconds")}' :> TIMESTAMP(6))""" # noqa: E501
+ if isinstance(sqla_type, types.DateTime):
+ return f"""CAST('{dttm.isoformat(sep=" ", timespec="microseconds")}' AS DATETIME(6))""" # noqa: E501
+ if isinstance(sqla_type, types.Time):
+ return f"""CAST('{dttm.strftime("%H:%M:%S.%f")}' AS TIME(6))"""
+
+ return None
+
+ @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]]:
+ uri, new_connect_args = super().adjust_engine_params(
+ uri,
+ connect_args,
+ catalog,
+ schema,
+ )
+
+ if schema:
+ uri = uri.set(database=parse.quote(schema, safe=""))
+
+ return uri, new_connect_args
+
+ @classmethod
+ def get_schema_from_engine_params(
+ cls,
+ sqlalchemy_uri: URL,
+ connect_args: dict[str, Any],
+ ) -> Optional[str]:
+ """
+ Return the configured schema.
+
+ A MySQL database is a SQLAlchemy schema.
+ """
Review Comment:
### Incorrect database reference in docstring <sub></sub>
<details>
<summary>Tell me more</summary>
###### What is the issue?
The docstring contains incorrect/confusing information by referencing MySQL when this is a SingleStore spec.
###### Why this matters
Mentioning MySQL in SingleStore-specific code could mislead developers about the database system they're working with.
###### Suggested change ∙ *Feature Preview*
"""Return the configured schema.
In SingleStore, like MySQL, the database name serves as the schema name in SQLAlchemy.
"""
###### Provide feedback to improve future suggestions
[](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/56710688-505c-498e-96a9-71e0ecc1ee2a/upvote) [](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/56710688-505c-498e-96a9-71e0ecc1ee2a?what_not_true=true) [](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/56710688-505c-498e-96a9-71e0ecc1ee2a?what_out_of_scope=true) [](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/56710688-505c-498e-96a9-71e0ecc1ee2a?what_not_in_standard=true) [](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/56710688-505c-498e-96a9-71e0ecc1ee2a)
</details>
<sub>
💬 Looking for more details? Reply to this comment to chat with Korbit.
</sub>
<!--- korbi internal id:fe3a50cf-a684-41a1-bb3f-4b10a141783b -->
[](fe3a50cf-a684-41a1-bb3f-4b10a141783b)
##########
superset/db_engine_specs/singlestore.py:
##########
@@ -0,0 +1,536 @@
+import logging
+import re
+from datetime import datetime
+from typing import Any, Optional
+from urllib import parse
+
+from sqlalchemy import types
+from sqlalchemy.engine import URL
+
+from superset.constants import TimeGrain
+from superset.db_engine_specs.base import BaseEngineSpec, ColumnTypeMapping, LimitMethod
+from superset.models.core import Database
+from superset.models.sql_lab import Query
+from superset.utils.core import GenericDataType
+
+logger = logging.getLogger(__name__)
+
+
+class SingleStoreSpec(BaseEngineSpec):
+ engine_name = "SingleStore"
+
+ engine = "singlestoredb"
+ drivers = {"singlestoredb": "SingleStore Python client"}
+ default_driver = "singlestoredb"
+
+ limit_method = LimitMethod.FORCE_LIMIT
+ allows_joins = True
+ allows_subqueries = True
+ allows_alias_in_select = True
+ allows_alias_in_orderby = True
+ time_groupby_inline = False
+ allows_alias_to_source_column = True
+ allows_hidden_orderby_agg = True
+ allows_hidden_cc_in_orderby = False
+ allows_cte_in_subquery = True
+ allow_limit_clause = True
+ max_column_name_length = 256
+ allows_sql_comments = True
+ allows_escaped_colons = True
+ supports_file_upload = True
+ supports_dynamic_schema = True
+ disable_ssh_tunneling = False
+
+ sqlalchemy_uri_placeholder = (
+ "singlestoredb://{username}:{password}@{host}:{port}/{database}"
+ )
+
+ _time_grain_expressions = {
+ None: "{col}",
+ TimeGrain.SECOND: "DATE_TRUNC('second', {col})",
+ TimeGrain.MINUTE: "DATE_TRUNC('minute', {col})",
+ TimeGrain.HOUR: "DATE_TRUNC('hour', {col})",
+ TimeGrain.DAY: "DATE_TRUNC('day', {col})",
+ TimeGrain.WEEK: "DATE_TRUNC('week', {col})",
+ TimeGrain.MONTH: "DATE_TRUNC('month', {col})",
+ TimeGrain.QUARTER: "DATE_TRUNC('quarter', {col})",
+ TimeGrain.YEAR: "DATE_TRUNC('year', {col})",
+ }
+
+ column_type_mappings: tuple[ColumnTypeMapping, ...] = (
+ (
+ re.compile(r"^tinyint", re.IGNORECASE),
+ types.SmallInteger(),
+ GenericDataType.NUMERIC,
+ ),
+ (
+ re.compile(r"^mediumint", re.IGNORECASE),
+ types.Integer(),
+ GenericDataType.NUMERIC,
+ ),
+ (
+ re.compile(r"^year", re.IGNORECASE),
+ types.Integer(),
+ GenericDataType.NUMERIC,
+ ),
+ (
+ re.compile(r"^bit", re.IGNORECASE),
+ types.LargeBinary(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^(var)?binary", re.IGNORECASE),
+ types.LargeBinary(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^(tiny|medium|long)?blob", re.IGNORECASE),
+ types.LargeBinary(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^json", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^bson", re.IGNORECASE),
+ types.LargeBinary(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^geographypoint", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^geography", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^vector", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^enum", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^set", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ )
+
+ @classmethod
+ def get_function_names(
+ cls,
+ database: Database,
+ ) -> list[str]:
+ """
+ Get a list of function names that are able to be called on the database.
+ Used for SQL Lab autocomplete.
+
+ :param database: The database to get functions for
+ :return: A list of function names usable in the database
+ """
+
+ functions = [
+ "ABS",
+ "ACOS",
+ "ADDTIME",
+ "AES_DECRYPT",
+ "AES_ENCRYPT",
+ "AGGREGATOR_ID",
+ "ANY_VALUE",
+ "APPROX_COUNT_DISTINCT",
+ "APPROX_COUNT_DISTINCT_ACCUMULATE",
+ "APPROX_COUNT_DISTINCT_COMBINE",
+ "APPROX_COUNT_DISTINCT_ESTIMATE",
+ "APPROX_GEOGRAPHY_INTERSECTS",
+ "APPROX_PERCENTILE",
+ "ASCII",
+ "ASIN",
+ "ATAN",
+ "ATAN2",
+ "ATAN",
+ "AVG",
+ "BETWEEN",
+ "NOT",
+ "BIN",
+ "BIN_TO_UUID",
+ "BINARY",
+ "BIT_AND",
+ "BIT_COUNT",
+ "BIT_OR",
+ "BIT_XOR",
+ "BM25",
+ "BSON_ARRAY_CONTAINS_BSON",
+ "BSON_ARRAY_PUSH",
+ "BSON_ARRAY_SLICE",
+ "BSON_BUILD_ARRAY",
+ "BSON_BUILD_OBJECT",
+ "BSON_COMPARE",
+ "BSON_EXTRACT_BIGINT",
+ "BSON_EXTRACT_BOOL",
+ "BSON_EXTRACT_BSON",
+ "BSON_EXTRACT_DATETIME",
+ "BSON_EXTRACT_DOUBLE",
+ "BSON_EXTRACT_STRING",
+ "BSON_GET_TYPE",
+ "BSON_INCLUDE_MASK",
+ "BSON_EXCLUDE_MASK",
+ "BSON_LENGTH",
+ "BSON_MATCH_ANY",
+ "BSON_MATCH_ANY_EXISTS",
+ "BSON_MERGE",
+ "BSON_NORMALIZE_",
+ *"BSON_SET_BSON",
+ "BSON_UNWIND",
+ "CASE",
+ "CEIL",
+ "CHAR",
+ "CHARACTER_LENGTH",
+ "CHARSET",
+ "COALESCE",
+ "CONCAT",
+ "CONCAT_WS",
+ "CONNECTION_ID",
+ "CONV",
+ "CAST",
+ "CONVERT",
+ "CONVERT_TZ",
+ "COS",
+ "COT",
+ "COUNT",
+ "CRC32",
+ "CURRENT_DATE",
+ "CURDATE",
+ "CURRENT_TIME",
+ "CURTIME",
+ "CURRENT_TIMESTAMP",
+ "DATABASE",
+ "DATE",
+ "DATE_ADD",
+ "DATE_FORMAT",
+ "DATE_SUB",
+ "DATE_TRUNC",
+ "DATEDIFF",
+ "DAY",
+ "DAYNAME",
+ "DAYOFWEEK",
+ "DAYOFYEAR",
+ "DECODE",
+ "DEGREES",
+ "DENSE_RANK",
+ "DOT_PRODUCT",
+ "ELT",
+ "ESTIMATED_QUERY_LEAF_MEMORY",
+ "ESTIMATED_QUERY_RUNTIME",
+ "EUCLIDEAN_DISTANCE",
+ "EXP",
+ "EXTRACT",
+ "FIELD",
+ "FIRST",
+ "FIRST_VALUE",
+ "FLOOR",
+ "FORMAT",
+ "FOUND_ROWS",
+ "FROM_BASE64",
+ "FROM_DAYS",
+ "FROM_UNIXTIME",
+ "GEOGRAPHY_AREA",
+ "GEOGRAPHY_CONTAINS",
+ "GEOGRAPHY_DISTANCE",
+ "GEOGRAPHY_INTERSECTS",
+ "GEOGRAPHY_LATITUDE",
+ "GEOGRAPHY_LENGTH",
+ "GEOGRAPHY_LONGITUDE",
+ "GEOGRAPHY_POINT",
+ "GEOGRAPHY_WITHIN_DISTANCE",
+ "GET_FORMAT",
+ "GREATEST",
+ "GROUP_CONCAT",
+ "HEX",
+ "HIGHLIGHT",
+ "HOUR",
+ "IF",
+ "IN",
+ "INET_ATON",
+ "INET_NTOA",
+ "INET6_ATON",
+ "INET6_NTOA",
+ "INITCAP",
+ "INSTR",
+ "IS_BSON_NULL",
+ "IS_UUID",
+ "ISNULL",
+ "ISNUMERIC",
+ "JSON_AGG",
+ "JSON_ARRAY_CONTAINS_DOUBLE",
+ "JSON_ARRAY_CONTAINS_STRING",
+ "JSON_ARRAY_CONTAINS_JSON",
+ "JSON_ARRAY_PACK",
+ "JSON_ARRAY_PUSH_DOUBLE",
+ "JSON_ARRAY_PUSH_STRING",
+ "JSON_ARRAY_PUSH_JSON",
+ "JSON_ARRAY_UNPACK",
+ "JSON_BUILD_ARRAY",
+ "JSON_BUILD_OBJECT",
+ "JSON_DELETE_KEY",
+ "JSON_EXTRACT_DOUBLE",
+ "JSON_EXTRACT_STRING",
+ "JSON_EXTRACT_JSON",
+ "JSON_EXTRACT_BIGINT",
+ "JSON_GET_TYPE",
+ "JSON_KEYS",
+ "JSON_LENGTH",
+ "JSON_MATCH_ANY",
+ "JSON_MERGE_PATCH",
+ "JSON_PRETTY",
+ "JSON_SET_DOUBLE",
+ "JSON_SET_STRING",
+ "JSON_SET_JSON",
+ "JSON_SPLICE_DOUBLE",
+ "JSON_SPLICE_STRING",
+ "JSON_SPLICE_JSON",
+ "JSON_TO_ARRAY",
+ "LAG",
+ "LAST",
+ "LAST_DAY",
+ "LAST_INSERT_ID",
+ "LAST_VALUE",
+ "LCASE",
+ "LEAD",
+ "LEAST",
+ "LEFT",
+ "LENGTH",
+ "LIKE",
+ "LN",
+ "LOCALTIMESTAMP",
+ "LOCATE",
+ "LOG",
+ "LOG10",
+ "LOG2",
+ "LPAD",
+ "LTRIM",
+ "MATCH",
+ "MAX",
+ "MD5",
+ "MEDIAN",
+ "MICROSECOND",
+ "MIN",
+ "MINUTE",
+ "MOD",
+ "MONTH",
+ "MONTHNAME",
+ "MONTHS_BETWEEN",
+ "NOPARAM",
+ "NOW",
+ "NTH_VALUE",
+ "NTILE",
+ "NULLIF",
+ "NVL",
+ "IFNULL",
+ "PERCENT_RANK",
+ "PERCENTILE_CONT",
+ "MEDIAN",
+ "PERCENTILE_DISC",
+ "PI",
+ "POW",
+ "QUARTER",
+ "QUOTE",
+ "RADIANS",
+ "RAND",
+ "RANK",
+ "REDUCE",
+ "REGEXP_INSTR",
+ "REGEXP_MATCH",
+ "REGEXP_REPLACE",
+ "REGEXP_SUBSTR",
+ "REPLACE",
+ "REVERSE",
+ "RIGHT",
+ "RLIKE",
+ "REGEXP",
+ "ROUND",
+ "ROW_COUNT",
+ "ROW_NUMBER",
+ "RPAD",
+ "RTRIM",
+ "SCALAR_VECTOR_MUL",
+ "SEC_TO_TIME",
+ "SECOND",
+ "SECRET",
+ "SET",
+ "SHA1",
+ "SHA2",
+ "SIGMOID",
+ "SIGN",
+ "SIN",
+ "SLEEP",
+ "SPLIT",
+ "SQRT",
+ "STD",
+ "STDDEV",
+ "STDDEV_POP",
+ "STDDEV_SAMP",
+ "STR_TO_DATE",
+ "strcmp",
+ "STRING_BYTES",
+ "SUBSTRING",
+ "SUBSTRING_INDEX",
+ "SUM",
+ "SYS_GUID",
+ "UUID",
+ "TAN",
+ "TIME",
+ "TIME_BUCKET",
+ "TIME_FORMAT",
+ "TIME_TO_SEC",
+ "TIMEDIFF",
+ "TIMESTAMP",
+ "TIMESTAMPADD",
+ "TIMESTAMPDIFF",
+ "TO_BASE64",
+ "TO_CHAR",
+ "TO_DATE",
+ "TO_DAYS",
+ "TO_JSON",
+ "TO_NUMBER",
+ "TO_SECONDS",
+ "TO_TIMESTAMP",
+ "TRIM",
+ "TRUNC",
+ "TRUNCATE",
+ "UCASE",
+ "UNHEX",
+ "UNIX_TIMESTAMP",
+ "USER",
+ "UTC_DATE",
+ "UTC_TIME",
+ "UTC_TIMESTAMP",
+ "UUID_TO_BIN",
+ "VARIANCE",
+ "VAR_SAMP",
+ "VECTOR_ADD",
+ "VECTOR_ELEMENTS_SUM",
+ "VECTOR_KTH_ELEMENT",
+ "VECTOR_MUL",
+ "VECTOR_NUM_ELEMENTS",
+ "VECTOR_SORT",
+ "VECTOR_SUB",
+ "VECTOR_SUBVECTOR",
+ "VECTOR_SUM",
+ "WEEK",
+ "WEEKDAY",
+ "YEAR",
+ ]
+
+ if (database_name := cls.get_default_schema(database, None)) is not None:
+ df = database.get_df(
+ f"SHOW FUNCTIONS IN `{database_name.replace('`', '``')}`"
+ )
+
+ function_name_col = f"Functions_in_{database_name}"
+ if function_name_col in df:
+ functions += df[function_name_col].tolist()
+ else:
+ columns = df.columns.values.tolist()
+ logger.error(
+ "Payload from `SHOW FUNCTIONS` has the incorrect format. "
+ "Expected column `%s`, found: %s.",
+ function_name_col,
+ ", ".join(columns),
+ exc_info=True,
+ )
Review Comment:
### Unnecessary traceback logging <sub></sub>
<details>
<summary>Tell me more</summary>
###### What is the issue?
The error logging includes exc_info=True but there is no active exception being handled, making the traceback information empty and unhelpful.
###### Why this matters
Including exc_info=True without an active exception context adds unnecessary overhead and provides no additional debugging value.
###### Suggested change ∙ *Feature Preview*
```python
columns = df.columns.values.tolist()
logger.error(
"Payload from `SHOW FUNCTIONS` has the incorrect format. "
"Expected column `%s`, found: %s.",
function_name_col,
", ".join(columns)
)
```
###### Provide feedback to improve future suggestions
[](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/f06cc372-5e4c-44f1-b1c0-9076012e5146/upvote) [](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/f06cc372-5e4c-44f1-b1c0-9076012e5146?what_not_true=true) [](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/f06cc372-5e4c-44f1-b1c0-9076012e5146?what_out_of_scope=true) [](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/f06cc372-5e4c-44f1-b1c0-9076012e5146?what_not_in_standard=true) [](https://app.korbit.ai/feedback/aa91ff46-6083-4491-9416-b83dd1994b51/f06cc372-5e4c-44f1-b1c0-9076012e5146)
</details>
<sub>
💬 Looking for more details? Reply to this comment to chat with Korbit.
</sub>
<!--- korbi internal id:67c0e461-d3a1-4948-8bb2-325b47d65b38 -->
[](67c0e461-d3a1-4948-8bb2-325b47d65b38)
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org
Re: [PR] feat(database): add SingleStore engine specification [superset]
Posted by "AdalbertMemSQL (via GitHub)" <gi...@apache.org>.
AdalbertMemSQL commented on code in PR #32887:
URL: https://github.com/apache/superset/pull/32887#discussion_r2028529293
##########
superset/db_engine_specs/singlestore.py:
##########
@@ -0,0 +1,558 @@
+# 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 datetime import datetime
+from typing import Any, Optional
+from urllib import parse
+
+from flask import current_app
+from sqlalchemy import types
+from sqlalchemy.engine import URL
+
+from superset.constants import TimeGrain
+from superset.db_engine_specs.base import BaseEngineSpec, ColumnTypeMapping, LimitMethod
+from superset.models.core import Database
+from superset.models.sql_lab import Query
+from superset.utils.core import GenericDataType
+
+logger = logging.getLogger(__name__)
+
+
+class SingleStoreSpec(BaseEngineSpec):
+ engine_name = "SingleStore"
+
+ engine = "singlestoredb"
+ drivers = {"singlestoredb": "SingleStore Python client"}
+ default_driver = "singlestoredb"
+
+ limit_method = LimitMethod.FORCE_LIMIT
+ allows_joins = True
+ allows_subqueries = True
+ allows_alias_in_select = True
+ allows_alias_in_orderby = True
+ time_groupby_inline = False
+ allows_alias_to_source_column = True
+ allows_hidden_orderby_agg = True
+ allows_hidden_cc_in_orderby = False
+ allows_cte_in_subquery = True
+ allow_limit_clause = True
+ max_column_name_length = 256
+ allows_sql_comments = True
+ allows_escaped_colons = True
+ supports_file_upload = True
+ supports_dynamic_schema = True
+ disable_ssh_tunneling = False
+
+ sqlalchemy_uri_placeholder = (
+ "singlestoredb://{username}:{password}@{host}:{port}/{database}"
+ )
+
+ _time_grain_expressions = {
+ None: "{col}",
+ TimeGrain.SECOND: "DATE_TRUNC('second', {col})",
+ TimeGrain.MINUTE: "DATE_TRUNC('minute', {col})",
+ TimeGrain.HOUR: "DATE_TRUNC('hour', {col})",
+ TimeGrain.DAY: "DATE_TRUNC('day', {col})",
+ TimeGrain.WEEK: "DATE_TRUNC('week', {col})",
+ TimeGrain.MONTH: "DATE_TRUNC('month', {col})",
+ TimeGrain.QUARTER: "DATE_TRUNC('quarter', {col})",
+ TimeGrain.YEAR: "DATE_TRUNC('year', {col})",
+ }
+
+ column_type_mappings: tuple[ColumnTypeMapping, ...] = (
+ (
+ re.compile(r"^tinyint", re.IGNORECASE),
+ types.SmallInteger(),
+ GenericDataType.NUMERIC,
+ ),
+ (
+ re.compile(r"^mediumint", re.IGNORECASE),
+ types.Integer(),
+ GenericDataType.NUMERIC,
+ ),
+ (
+ re.compile(r"^year", re.IGNORECASE),
+ types.Integer(),
+ GenericDataType.NUMERIC,
+ ),
+ (
+ re.compile(r"^bit", re.IGNORECASE),
+ types.LargeBinary(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^(var)?binary", re.IGNORECASE),
+ types.LargeBinary(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^(tiny|medium|long)?blob", re.IGNORECASE),
+ types.LargeBinary(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^json", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^bson", re.IGNORECASE),
+ types.LargeBinary(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^geographypoint", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^geography", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^vector", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^enum", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ (
+ re.compile(r"^set", re.IGNORECASE),
+ types.String(),
+ GenericDataType.STRING,
+ ),
+ )
+
+ @classmethod
+ def get_function_names(
+ cls,
+ database: Database,
+ ) -> list[str]:
+ """
+ Get a list of function names that are able to be called on the database.
+ Used for SQL Lab autocomplete.
+
+ :param database: The database to get functions for
+ :return: A list of function names usable in the database
+ """
+
+ functions = [
+ "ABS",
+ "ACOS",
+ "ADDTIME",
+ "AES_DECRYPT",
+ "AES_ENCRYPT",
+ "AGGREGATOR_ID",
+ "ANY_VALUE",
+ "APPROX_COUNT_DISTINCT",
+ "APPROX_COUNT_DISTINCT_ACCUMULATE",
+ "APPROX_COUNT_DISTINCT_COMBINE",
+ "APPROX_COUNT_DISTINCT_ESTIMATE",
+ "APPROX_GEOGRAPHY_INTERSECTS",
+ "APPROX_PERCENTILE",
+ "ASCII",
+ "ASIN",
+ "ATAN",
+ "ATAN2",
+ "ATAN",
+ "AVG",
+ "BETWEEN",
+ "NOT",
+ "BIN",
+ "BIN_TO_UUID",
+ "BINARY",
+ "BIT_AND",
+ "BIT_COUNT",
+ "BIT_OR",
+ "BIT_XOR",
+ "BM25",
+ "BSON_ARRAY_CONTAINS_BSON",
+ "BSON_ARRAY_PUSH",
+ "BSON_ARRAY_SLICE",
+ "BSON_BUILD_ARRAY",
+ "BSON_BUILD_OBJECT",
+ "BSON_COMPARE",
+ "BSON_EXTRACT_BIGINT",
+ "BSON_EXTRACT_BOOL",
+ "BSON_EXTRACT_BSON",
+ "BSON_EXTRACT_DATETIME",
+ "BSON_EXTRACT_DOUBLE",
+ "BSON_EXTRACT_STRING",
+ "BSON_GET_TYPE",
+ "BSON_INCLUDE_MASK",
+ "BSON_EXCLUDE_MASK",
+ "BSON_LENGTH",
+ "BSON_MATCH_ANY",
+ "BSON_MATCH_ANY_EXISTS",
+ "BSON_MERGE",
+ "BSON_NORMALIZE",
+ "BSON_NORMALIZE_ASC",
+ "BSON_NORMALIZE_DESC",
+ "BSON_NORMALIZE_DESC",
+ "BSON_SET_BSON",
+ "BSON_UNWIND",
+ "CASE",
+ "CEIL",
+ "CHAR",
+ "CHARACTER_LENGTH",
+ "CHARSET",
+ "COALESCE",
+ "CONCAT",
+ "CONCAT_WS",
+ "CONNECTION_ID",
+ "CONV",
+ "CAST",
+ "CONVERT",
+ "CONVERT_TZ",
+ "COS",
+ "COT",
+ "COUNT",
+ "CRC32",
+ "CURRENT_DATE",
+ "CURDATE",
+ "CURRENT_TIME",
+ "CURTIME",
+ "CURRENT_TIMESTAMP",
+ "DATABASE",
+ "DATE",
+ "DATE_ADD",
+ "DATE_FORMAT",
+ "DATE_SUB",
+ "DATE_TRUNC",
+ "DATEDIFF",
+ "DAY",
+ "DAYNAME",
+ "DAYOFWEEK",
+ "DAYOFYEAR",
+ "DECODE",
+ "DEGREES",
+ "DENSE_RANK",
+ "DOT_PRODUCT",
+ "ELT",
+ "ESTIMATED_QUERY_LEAF_MEMORY",
+ "ESTIMATED_QUERY_RUNTIME",
+ "EUCLIDEAN_DISTANCE",
+ "EXP",
+ "EXTRACT",
+ "FIELD",
+ "FIRST",
+ "FIRST_VALUE",
+ "FLOOR",
+ "FORMAT",
+ "FOUND_ROWS",
+ "FROM_BASE64",
+ "FROM_DAYS",
+ "FROM_UNIXTIME",
+ "GEOGRAPHY_AREA",
+ "GEOGRAPHY_CONTAINS",
+ "GEOGRAPHY_DISTANCE",
+ "GEOGRAPHY_INTERSECTS",
+ "GEOGRAPHY_LATITUDE",
+ "GEOGRAPHY_LENGTH",
+ "GEOGRAPHY_LONGITUDE",
+ "GEOGRAPHY_POINT",
+ "GEOGRAPHY_WITHIN_DISTANCE",
+ "GET_FORMAT",
+ "GREATEST",
+ "GROUP_CONCAT",
+ "HEX",
+ "HIGHLIGHT",
+ "HOUR",
+ "IF",
+ "IN",
+ "INET_ATON",
+ "INET_NTOA",
+ "INET6_ATON",
+ "INET6_NTOA",
+ "INITCAP",
+ "INSTR",
+ "IS_BSON_NULL",
+ "IS_UUID",
+ "ISNULL",
+ "ISNUMERIC",
+ "JSON_AGG",
+ "JSON_ARRAY_CONTAINS_DOUBLE",
+ "JSON_ARRAY_CONTAINS_STRING",
+ "JSON_ARRAY_CONTAINS_JSON",
+ "JSON_ARRAY_PACK",
+ "JSON_ARRAY_PUSH_DOUBLE",
+ "JSON_ARRAY_PUSH_STRING",
+ "JSON_ARRAY_PUSH_JSON",
+ "JSON_ARRAY_UNPACK",
+ "JSON_BUILD_ARRAY",
+ "JSON_BUILD_OBJECT",
+ "JSON_DELETE_KEY",
+ "JSON_EXTRACT_DOUBLE",
+ "JSON_EXTRACT_STRING",
+ "JSON_EXTRACT_JSON",
+ "JSON_EXTRACT_BIGINT",
+ "JSON_GET_TYPE",
+ "JSON_KEYS",
+ "JSON_LENGTH",
+ "JSON_MATCH_ANY",
+ "JSON_MERGE_PATCH",
+ "JSON_PRETTY",
+ "JSON_SET_DOUBLE",
+ "JSON_SET_STRING",
+ "JSON_SET_JSON",
+ "JSON_SPLICE_DOUBLE",
+ "JSON_SPLICE_STRING",
+ "JSON_SPLICE_JSON",
+ "JSON_TO_ARRAY",
+ "LAG",
+ "LAST",
+ "LAST_DAY",
+ "LAST_INSERT_ID",
+ "LAST_VALUE",
+ "LCASE",
+ "LEAD",
+ "LEAST",
+ "LEFT",
+ "LENGTH",
+ "LIKE",
+ "LN",
+ "LOCALTIMESTAMP",
+ "LOCATE",
+ "LOG",
+ "LOG10",
+ "LOG2",
+ "LPAD",
+ "LTRIM",
+ "MATCH",
+ "MAX",
+ "MD5",
+ "MEDIAN",
+ "MICROSECOND",
+ "MIN",
+ "MINUTE",
+ "MOD",
+ "MONTH",
+ "MONTHNAME",
+ "MONTHS_BETWEEN",
+ "NOPARAM",
+ "NOW",
+ "NTH_VALUE",
+ "NTILE",
+ "NULLIF",
+ "NVL",
+ "IFNULL",
+ "PERCENT_RANK",
+ "PERCENTILE_CONT",
+ "MEDIAN",
+ "PERCENTILE_DISC",
+ "PI",
+ "POW",
+ "QUARTER",
+ "QUOTE",
+ "RADIANS",
+ "RAND",
+ "RANK",
+ "REDUCE",
+ "REGEXP_INSTR",
+ "REGEXP_MATCH",
+ "REGEXP_REPLACE",
+ "REGEXP_SUBSTR",
+ "REPLACE",
+ "REVERSE",
+ "RIGHT",
+ "RLIKE",
+ "REGEXP",
+ "ROUND",
+ "ROW_COUNT",
+ "ROW_NUMBER",
+ "RPAD",
+ "RTRIM",
+ "SCALAR_VECTOR_MUL",
+ "SEC_TO_TIME",
+ "SECOND",
+ "SECRET",
+ "SET",
+ "SHA1",
+ "SHA2",
+ "SIGMOID",
+ "SIGN",
+ "SIN",
+ "SLEEP",
+ "SPLIT",
+ "SQRT",
+ "STD",
+ "STDDEV",
+ "STDDEV_POP",
+ "STDDEV_SAMP",
+ "STR_TO_DATE",
+ "strcmp",
+ "STRING_BYTES",
+ "SUBSTRING",
+ "SUBSTRING_INDEX",
+ "SUM",
+ "SYS_GUID",
+ "UUID",
+ "TAN",
+ "TIME",
+ "TIME_BUCKET",
+ "TIME_FORMAT",
+ "TIME_TO_SEC",
+ "TIMEDIFF",
+ "TIMESTAMP",
+ "TIMESTAMPADD",
+ "TIMESTAMPDIFF",
+ "TO_BASE64",
+ "TO_CHAR",
+ "TO_DATE",
+ "TO_DAYS",
+ "TO_JSON",
+ "TO_NUMBER",
+ "TO_SECONDS",
+ "TO_TIMESTAMP",
+ "TRIM",
+ "TRUNC",
+ "TRUNCATE",
+ "UCASE",
+ "UNHEX",
+ "UNIX_TIMESTAMP",
+ "USER",
+ "UTC_DATE",
+ "UTC_TIME",
+ "UTC_TIMESTAMP",
+ "UUID_TO_BIN",
+ "VARIANCE",
+ "VAR_SAMP",
+ "VECTOR_ADD",
+ "VECTOR_ELEMENTS_SUM",
+ "VECTOR_KTH_ELEMENT",
+ "VECTOR_MUL",
+ "VECTOR_NUM_ELEMENTS",
+ "VECTOR_SORT",
+ "VECTOR_SUB",
+ "VECTOR_SUBVECTOR",
+ "VECTOR_SUM",
+ "WEEK",
+ "WEEKDAY",
+ "YEAR",
+ ]
+
+ if (database_name := cls.get_default_schema(database, None)) is not None:
+ df = database.get_df(
+ f"SHOW FUNCTIONS IN `{database_name.replace('`', '``')}`"
+ )
+
+ function_name_col = f"Functions_in_{database_name}"
+ if function_name_col in df:
+ functions += df[function_name_col].tolist()
Review Comment:
Updated this
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org
Re: [PR] feat(database): add SingleStore engine specification [superset]
Posted by "rusackas (via GitHub)" <gi...@apache.org>.
rusackas commented on PR #32887:
URL: https://github.com/apache/superset/pull/32887#issuecomment-2931547601
Aside from restoring the SAP Hana link, things look good to me!
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org
Re: [PR] feat(database): add SingleStore engine specification [superset]
Posted by "AdalbertMemSQL (via GitHub)" <gi...@apache.org>.
AdalbertMemSQL commented on PR #32887:
URL: https://github.com/apache/superset/pull/32887#issuecomment-2800589620
Hi @rusackas ,
I’ve just pushed some updates to the PR. Could you please re-run the tests when you get a chance?
Thanks a lot!
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org
Re: [PR] feat(database): add SingleStore engine specification [superset]
Posted by "codecov[bot] (via GitHub)" <gi...@apache.org>.
codecov[bot] commented on PR #32887:
URL: https://github.com/apache/superset/pull/32887#issuecomment-2758656986
## [Codecov](https://app.codecov.io/gh/apache/superset/pull/32887?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) Report
Attention: Patch coverage is `61.17647%` with `33 lines` in your changes missing coverage. Please review.
> Project coverage is 76.06%. Comparing base [(`76d897e`)](https://app.codecov.io/gh/apache/superset/commit/76d897eaa2f9e137102bc194c2e3109c29d0348f?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) to head [(`b7a35a1`)](https://app.codecov.io/gh/apache/superset/commit/b7a35a17dd0af6238e3e7d5fae4857a341c60c31?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache).
> Report is 1688 commits behind head on master.
| [Files with missing lines](https://app.codecov.io/gh/apache/superset/pull/32887?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | Patch % | Lines |
|---|---|---|
| [superset/db\_engine\_specs/singlestore.py](https://app.codecov.io/gh/apache/superset/pull/32887?src=pr&el=tree&filepath=superset%2Fdb_engine_specs%2Fsinglestore.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#diff-c3VwZXJzZXQvZGJfZW5naW5lX3NwZWNzL3NpbmdsZXN0b3JlLnB5) | 61.17% | [33 Missing :warning: ](https://app.codecov.io/gh/apache/superset/pull/32887?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) |
<details><summary>Additional details and impacted files</summary>
```diff
@@ Coverage Diff @@
## master #32887 +/- ##
===========================================
+ Coverage 60.48% 76.06% +15.57%
===========================================
Files 1931 550 -1381
Lines 76236 39571 -36665
Branches 8568 0 -8568
===========================================
- Hits 46114 30100 -16014
+ Misses 28017 9471 -18546
+ Partials 2105 0 -2105
```
| [Flag](https://app.codecov.io/gh/apache/superset/pull/32887/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | Coverage Δ | |
|---|---|---|
| [hive](https://app.codecov.io/gh/apache/superset/pull/32887/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | `48.42% <61.17%> (-0.73%)` | :arrow_down: |
| [javascript](https://app.codecov.io/gh/apache/superset/pull/32887/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | `?` | |
| [postgres](https://app.codecov.io/gh/apache/superset/pull/32887/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | `75.67% <61.17%> (?)` | |
| [presto](https://app.codecov.io/gh/apache/superset/pull/32887/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | `52.90% <61.17%> (-0.91%)` | :arrow_down: |
| [python](https://app.codecov.io/gh/apache/superset/pull/32887/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | `76.06% <61.17%> (+12.56%)` | :arrow_up: |
| [sqlite](https://app.codecov.io/gh/apache/superset/pull/32887/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | `75.17% <61.17%> (?)` | |
| [unit](https://app.codecov.io/gh/apache/superset/pull/32887/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | `?` | |
Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#carryforward-flags-in-the-pull-request-comment) to find out more.
</details>
[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/apache/superset/pull/32887?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache).
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache).
<details><summary> :rocket: New features to boost your workflow: </summary>
- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org
Re: [PR] feat(database): add SingleStore engine specification [superset]
Posted by "rusackas (via GitHub)" <gi...@apache.org>.
rusackas commented on PR #32887:
URL: https://github.com/apache/superset/pull/32887#issuecomment-2776007172
Running CI. 🤞
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org
Re: [PR] feat(database): add SingleStore engine specification [superset]
Posted by "AdalbertMemSQL (via GitHub)" <gi...@apache.org>.
AdalbertMemSQL commented on PR #32887:
URL: https://github.com/apache/superset/pull/32887#issuecomment-2912191338
@rusackas could you run tests one more time?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org