feat: add MotherDuck DB engine spec (#24934)
This commit is contained in:
parent
f6c3f0cbbb
commit
9c54280d85
1
setup.py
1
setup.py
|
|
@ -150,6 +150,7 @@ setup(
|
||||||
"dremio": ["sqlalchemy-dremio>=1.1.5, <1.3"],
|
"dremio": ["sqlalchemy-dremio>=1.1.5, <1.3"],
|
||||||
"drill": ["sqlalchemy-drill==0.1.dev"],
|
"drill": ["sqlalchemy-drill==0.1.dev"],
|
||||||
"druid": ["pydruid>=0.6.5,<0.7"],
|
"druid": ["pydruid>=0.6.5,<0.7"],
|
||||||
|
"duckdb": ["duckdb-engine==0.8.1"],
|
||||||
"dynamodb": ["pydynamodb>=0.4.2"],
|
"dynamodb": ["pydynamodb>=0.4.2"],
|
||||||
"solr": ["sqlalchemy-solr >= 0.2.0"],
|
"solr": ["sqlalchemy-solr >= 0.2.0"],
|
||||||
"elasticsearch": ["elasticsearch-dbapi>=0.2.9, <0.3.0"],
|
"elasticsearch": ["elasticsearch-dbapi>=0.2.9, <0.3.0"],
|
||||||
|
|
|
||||||
|
|
@ -38,17 +38,13 @@ const SqlAlchemyTab = ({
|
||||||
testInProgress?: boolean;
|
testInProgress?: boolean;
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
}) => {
|
}) => {
|
||||||
let fallbackDocsUrl;
|
const fallbackDocsUrl =
|
||||||
let fallbackDisplayText;
|
SupersetText?.DB_MODAL_SQLALCHEMY_FORM?.SQLALCHEMY_DOCS_URL ||
|
||||||
if (SupersetText) {
|
'https://docs.sqlalchemy.org/en/13/core/engines.html';
|
||||||
fallbackDocsUrl =
|
const fallbackDisplayText =
|
||||||
SupersetText.DB_MODAL_SQLALCHEMY_FORM?.SQLALCHEMY_DOCS_URL;
|
SupersetText?.DB_MODAL_SQLALCHEMY_FORM?.SQLALCHEMY_DISPLAY_TEXT ||
|
||||||
fallbackDisplayText =
|
'SQLAlchemy docs';
|
||||||
SupersetText.DB_MODAL_SQLALCHEMY_FORM?.SQLALCHEMY_DISPLAY_TEXT;
|
|
||||||
} else {
|
|
||||||
fallbackDocsUrl = 'https://docs.sqlalchemy.org/en/13/core/engines.html';
|
|
||||||
fallbackDisplayText = 'SQLAlchemy docs';
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<StyledInputContainer>
|
<StyledInputContainer>
|
||||||
|
|
@ -82,9 +78,10 @@ const SqlAlchemyTab = ({
|
||||||
data-test="sqlalchemy-uri-input"
|
data-test="sqlalchemy-uri-input"
|
||||||
value={db?.sqlalchemy_uri || ''}
|
value={db?.sqlalchemy_uri || ''}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
placeholder={t(
|
placeholder={
|
||||||
'dialect+driver://username:password@host:port/database',
|
db?.sqlalchemy_uri_placeholder ||
|
||||||
)}
|
t('dialect+driver://username:password@host:port/database')
|
||||||
|
}
|
||||||
onChange={onInputChange}
|
onChange={onInputChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -202,6 +202,7 @@ export type DBReducerActionType =
|
||||||
configuration_method: CONFIGURATION_METHOD;
|
configuration_method: CONFIGURATION_METHOD;
|
||||||
engine_information?: {};
|
engine_information?: {};
|
||||||
driver?: string;
|
driver?: string;
|
||||||
|
sqlalchemy_uri_placeholder?: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
|
|
@ -946,8 +947,13 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
|
||||||
const selectedDbModel = availableDbs?.databases.filter(
|
const selectedDbModel = availableDbs?.databases.filter(
|
||||||
(db: DatabaseObject) => db.name === database_name,
|
(db: DatabaseObject) => db.name === database_name,
|
||||||
)[0];
|
)[0];
|
||||||
const { engine, parameters, engine_information, default_driver } =
|
const {
|
||||||
selectedDbModel;
|
engine,
|
||||||
|
parameters,
|
||||||
|
engine_information,
|
||||||
|
default_driver,
|
||||||
|
sqlalchemy_uri_placeholder,
|
||||||
|
} = selectedDbModel;
|
||||||
const isDynamic = parameters !== undefined;
|
const isDynamic = parameters !== undefined;
|
||||||
setDB({
|
setDB({
|
||||||
type: ActionType.dbSelected,
|
type: ActionType.dbSelected,
|
||||||
|
|
@ -959,6 +965,7 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
|
||||||
: CONFIGURATION_METHOD.SQLALCHEMY_URI,
|
: CONFIGURATION_METHOD.SQLALCHEMY_URI,
|
||||||
engine_information,
|
engine_information,
|
||||||
driver: default_driver,
|
driver: default_driver,
|
||||||
|
sqlalchemy_uri_placeholder,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ export type DatabaseObject = {
|
||||||
name: string; // synonym to database_name
|
name: string; // synonym to database_name
|
||||||
paramProperties?: Record<string, any>;
|
paramProperties?: Record<string, any>;
|
||||||
sqlalchemy_uri?: string;
|
sqlalchemy_uri?: string;
|
||||||
|
sqlalchemy_uri_placeholder?: string;
|
||||||
parameters?: {
|
parameters?: {
|
||||||
access_token?: string;
|
access_token?: string;
|
||||||
database_name?: string;
|
database_name?: string;
|
||||||
|
|
|
||||||
|
|
@ -1293,6 +1293,9 @@ class DatabaseRestApi(BaseSupersetModelRestApi):
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
type: string
|
type: string
|
||||||
|
sqlalchemy_uri_placeholder:
|
||||||
|
description: Placeholder for the SQLAlchemy URI
|
||||||
|
type: string
|
||||||
default_driver:
|
default_driver:
|
||||||
description: Default driver for the engine
|
description: Default driver for the engine
|
||||||
type: string
|
type: string
|
||||||
|
|
@ -1330,6 +1333,7 @@ class DatabaseRestApi(BaseSupersetModelRestApi):
|
||||||
"name": engine_spec.engine_name,
|
"name": engine_spec.engine_name,
|
||||||
"engine": engine_spec.engine,
|
"engine": engine_spec.engine,
|
||||||
"available_drivers": sorted(drivers),
|
"available_drivers": sorted(drivers),
|
||||||
|
"sqlalchemy_uri_placeholder": engine_spec.sqlalchemy_uri_placeholder,
|
||||||
"preferred": engine_spec.engine_name in preferred_databases,
|
"preferred": engine_spec.engine_name in preferred_databases,
|
||||||
"engine_information": engine_spec.get_public_information(),
|
"engine_information": engine_spec.get_public_information(),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -185,6 +185,12 @@ class BaseEngineSpec: # pylint: disable=too-many-public-methods
|
||||||
engine_aliases: set[str] = set()
|
engine_aliases: set[str] = set()
|
||||||
drivers: dict[str, str] = {}
|
drivers: dict[str, str] = {}
|
||||||
default_driver: str | None = None
|
default_driver: str | None = None
|
||||||
|
|
||||||
|
# placeholder with the SQLAlchemy URI template
|
||||||
|
sqlalchemy_uri_placeholder = (
|
||||||
|
"engine+driver://user:password@host:port/dbname[?key=value&key=value...]"
|
||||||
|
)
|
||||||
|
|
||||||
disable_ssh_tunneling = False
|
disable_ssh_tunneling = False
|
||||||
|
|
||||||
_date_trunc_functions: dict[str, str] = {}
|
_date_trunc_functions: dict[str, str] = {}
|
||||||
|
|
@ -1958,11 +1964,6 @@ class BasicParametersMixin:
|
||||||
# recommended driver name for the DB engine spec
|
# recommended driver name for the DB engine spec
|
||||||
default_driver = ""
|
default_driver = ""
|
||||||
|
|
||||||
# placeholder with the SQLAlchemy URI template
|
|
||||||
sqlalchemy_uri_placeholder = (
|
|
||||||
"engine+driver://user:password@host:port/dbname[?key=value&key=value...]"
|
|
||||||
)
|
|
||||||
|
|
||||||
# query parameter to enable encryption in the database connection
|
# query parameter to enable encryption in the database connection
|
||||||
# for Postgres this would be `{"sslmode": "verify-ca"}`, eg.
|
# for Postgres this would be `{"sslmode": "verify-ca"}`, eg.
|
||||||
encryption_parameters: dict[str, str] = {}
|
encryption_parameters: dict[str, str] = {}
|
||||||
|
|
|
||||||
|
|
@ -80,3 +80,10 @@ class DuckDBEngineSpec(BaseEngineSpec):
|
||||||
cls, database: Database, inspector: Inspector, schema: str | None
|
cls, database: Database, inspector: Inspector, schema: str | None
|
||||||
) -> set[str]:
|
) -> set[str]:
|
||||||
return set(inspector.get_table_names(schema))
|
return set(inspector.get_table_names(schema))
|
||||||
|
|
||||||
|
|
||||||
|
class MotherDuckEngineSpec(DuckDBEngineSpec):
|
||||||
|
engine = "duckdb"
|
||||||
|
engine_name = "MotherDuck"
|
||||||
|
|
||||||
|
sqlalchemy_uri_placeholder = "duckdb:///md:{SERVICE_TOKEN}@{database_name}"
|
||||||
|
|
|
||||||
|
|
@ -3217,6 +3217,7 @@ class TestDatabaseApi(SupersetTestCase):
|
||||||
"engine": "hana",
|
"engine": "hana",
|
||||||
"name": "SAP HANA",
|
"name": "SAP HANA",
|
||||||
"preferred": False,
|
"preferred": False,
|
||||||
|
"sqlalchemy_uri_placeholder": "engine+driver://user:password@host:port/dbname[?key=value&key=value...]",
|
||||||
"engine_information": {
|
"engine_information": {
|
||||||
"supports_file_upload": True,
|
"supports_file_upload": True,
|
||||||
"disable_ssh_tunneling": False,
|
"disable_ssh_tunneling": False,
|
||||||
|
|
@ -3248,6 +3249,7 @@ class TestDatabaseApi(SupersetTestCase):
|
||||||
"engine": "mysql",
|
"engine": "mysql",
|
||||||
"name": "MySQL",
|
"name": "MySQL",
|
||||||
"preferred": True,
|
"preferred": True,
|
||||||
|
"sqlalchemy_uri_placeholder": "mysql://user:password@host:port/dbname[?key=value&key=value...]",
|
||||||
"engine_information": {
|
"engine_information": {
|
||||||
"supports_file_upload": True,
|
"supports_file_upload": True,
|
||||||
"disable_ssh_tunneling": False,
|
"disable_ssh_tunneling": False,
|
||||||
|
|
@ -3258,6 +3260,7 @@ class TestDatabaseApi(SupersetTestCase):
|
||||||
"engine": "hana",
|
"engine": "hana",
|
||||||
"name": "SAP HANA",
|
"name": "SAP HANA",
|
||||||
"preferred": False,
|
"preferred": False,
|
||||||
|
"sqlalchemy_uri_placeholder": "engine+driver://user:password@host:port/dbname[?key=value&key=value...]",
|
||||||
"engine_information": {
|
"engine_information": {
|
||||||
"supports_file_upload": True,
|
"supports_file_upload": True,
|
||||||
"disable_ssh_tunneling": False,
|
"disable_ssh_tunneling": False,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue