diff --git a/frontend/src/components/datasources/__tests__/utils.test.ts b/frontend/src/components/datasources/__tests__/utils.test.ts index 151be1b1883..cd968049996 100644 --- a/frontend/src/components/datasources/__tests__/utils.test.ts +++ b/frontend/src/components/datasources/__tests__/utils.test.ts @@ -24,154 +24,335 @@ describe("sqlCode", () => { name: "email" as const, } as DataTableColumn; - it("should generate basic SQL without sqlTableContext", () => { - const result = sqlCode({ table: mockTable, columnName: mockColumn.name }); - expect(result).toBe( - "_df = mo.sql(f'SELECT \"email\" FROM users LIMIT 100')", - ); - }); + describe("basic SQL generation", () => { + it("should generate basic SQL without sqlTableContext", () => { + const result = sqlCode({ table: mockTable, columnName: mockColumn.name }); + expect(result).toBe( + "_df = mo.sql(f'SELECT \"email\" FROM users LIMIT 100')", + ); + }); - it("should generate SQL with default schema", () => { - const sqlTableContext: SQLTableContext = { - engine: DUCKDB_ENGINE, - schema: "public", - defaultSchema: "public", - defaultDatabase: "mydb", - database: "mydb", - }; - - const result = sqlCode({ - table: mockTable, - columnName: mockColumn.name, - sqlTableContext, - }); - expect(result).toBe('_df = mo.sql(f"SELECT email FROM users LIMIT 100")'); - }); + it("should generate SQL with default schema", () => { + const sqlTableContext: SQLTableContext = { + engine: DUCKDB_ENGINE, + schema: "public", + defaultSchema: "public", + defaultDatabase: "mydb", + database: "mydb", + dialect: "duckdb", + }; - it("should generate SQL with non-default schema", () => { - const sqlTableContext: SQLTableContext = { - engine: DUCKDB_ENGINE, - schema: "analytics", - defaultSchema: "public", - defaultDatabase: "mydb", - database: "mydb", - }; - - const result = sqlCode({ - table: mockTable, - columnName: mockColumn.name, - sqlTableContext, - }); - expect(result).toBe( - '_df = mo.sql(f"SELECT email FROM analytics.users LIMIT 100")', - ); - }); + const result = sqlCode({ + table: mockTable, + columnName: mockColumn.name, + sqlTableContext, + }); + expect(result).toBe( + '_df = mo.sql(f"""\nSELECT email FROM users LIMIT 100\n""")', + ); + }); + + it("should generate SQL with non-default schema", () => { + const sqlTableContext: SQLTableContext = { + engine: DUCKDB_ENGINE, + schema: "analytics", + defaultSchema: "public", + defaultDatabase: "mydb", + database: "mydb", + dialect: "duckdb", + }; + + const result = sqlCode({ + table: mockTable, + columnName: mockColumn.name, + sqlTableContext, + }); + expect(result).toBe( + '_df = mo.sql(f"""\nSELECT email FROM analytics.users LIMIT 100\n""")', + ); + }); + + it("should generate SQL with non-default engine", () => { + const sqlTableContext: SQLTableContext = { + engine: "snowflake", + schema: "public", + defaultSchema: "public", + defaultDatabase: "mydb", + database: "mydb", + dialect: "snowflake", + }; + + const result = sqlCode({ + table: mockTable, + columnName: mockColumn.name, + sqlTableContext, + }); + expect(result).toBe( + '_df = mo.sql(f"""\nSELECT email FROM users LIMIT 100\n""", engine=snowflake)', + ); + }); - it("should generate SQL with non-default engine", () => { - const sqlTableContext: SQLTableContext = { - engine: "snowflake", - schema: "public", - defaultSchema: "public", - defaultDatabase: "mydb", - database: "mydb", - }; - - const result = sqlCode({ - table: mockTable, - columnName: mockColumn.name, - sqlTableContext, - }); - expect(result).toBe( - '_df = mo.sql(f"SELECT email FROM users LIMIT 100", engine=snowflake)', - ); + it("should generate SQL with non-default database", () => { + const sqlTableContext: SQLTableContext = { + engine: DUCKDB_ENGINE, + schema: "public", + defaultSchema: "public", + defaultDatabase: "memory", + database: "remote", + dialect: "duckdb", + }; + + const result = sqlCode({ + table: mockTable, + columnName: mockColumn.name, + sqlTableContext, + }); + expect(result).toBe( + '_df = mo.sql(f"""\nSELECT email FROM remote.users LIMIT 100\n""")', + ); + }); + + it("should generate SQL for schemaless tables", () => { + const sqlTableContext: SQLTableContext = { + engine: DUCKDB_ENGINE, + schema: "", + defaultDatabase: "mydb", + database: "mydb", + dialect: "duckdb", + }; + + const result = sqlCode({ + table: mockTable, + columnName: mockColumn.name, + sqlTableContext, + }); + expect(result).toBe( + '_df = mo.sql(f"""\nSELECT email FROM users LIMIT 100\n""")', + ); + + const sqlTableContext2: SQLTableContext = { + engine: DUCKDB_ENGINE, + schema: "", + defaultDatabase: "remote", + database: "another_db", + dialect: "duckdb", + }; + + const result2 = sqlCode({ + table: mockTable, + columnName: mockColumn.name, + sqlTableContext: sqlTableContext2, + }); + expect(result2).toBe( + '_df = mo.sql(f"""\nSELECT email FROM another_db.users LIMIT 100\n""")', + ); + }); }); - it("should generate SQL with non-default schema and non-default engine", () => { - const sqlTableContext: SQLTableContext = { - engine: "bigquery", - schema: "sales", - defaultSchema: "public", - defaultDatabase: "mydb", - database: "mydb", - }; - - const result = sqlCode({ - table: mockTable, - columnName: mockColumn.name, - sqlTableContext, - }); - expect(result).toBe( - '_df = mo.sql(f"SELECT email FROM sales.users LIMIT 100", engine=bigquery)', - ); + describe("BigQuery dialect", () => { + it("should use backticks for table names", () => { + const sqlTableContext: SQLTableContext = { + engine: "bigquery", + schema: "public", + defaultSchema: "public", + defaultDatabase: "mydb", + database: "mydb", + dialect: "bigquery", + }; + + const result = sqlCode({ + table: mockTable, + columnName: mockColumn.name, + sqlTableContext, + }); + expect(result).toBe( + '_df = mo.sql(f"""\nSELECT email FROM `users` LIMIT 100\n""", engine=bigquery)', + ); + }); + + it("should use backticks for database.schema.table", () => { + const sqlTableContext: SQLTableContext = { + engine: "bigquery", + schema: "sales", + defaultSchema: "public", + defaultDatabase: "mydb", + database: "remote", + dialect: "bigquery", + }; + + const result = sqlCode({ + table: mockTable, + columnName: mockColumn.name, + sqlTableContext, + }); + expect(result).toBe( + '_df = mo.sql(f"""\nSELECT email FROM `remote.sales.users` LIMIT 100\n""", engine=bigquery)', + ); + }); + + it("should handle case-insensitive dialect name", () => { + const sqlTableContext: SQLTableContext = { + engine: "bigquery", + schema: "public", + defaultSchema: "public", + defaultDatabase: "mydb", + database: "mydb", + dialect: "BigQuery", + }; + + const result = sqlCode({ + table: mockTable, + columnName: mockColumn.name, + sqlTableContext, + }); + expect(result).toBe( + '_df = mo.sql(f"""\nSELECT email FROM `users` LIMIT 100\n""", engine=bigquery)', + ); + }); }); - it("should generate SQL with non-default database", () => { - const sqlTableContext: SQLTableContext = { - engine: DUCKDB_ENGINE, - schema: "public", - defaultSchema: "public", - defaultDatabase: "memory", - database: "remote", - }; - - const result = sqlCode({ - table: mockTable, - columnName: mockColumn.name, - sqlTableContext, - }); - expect(result).toBe( - '_df = mo.sql(f"SELECT email FROM remote.users LIMIT 100")', - ); + describe("MSSQL dialect", () => { + it("should use TOP 100 instead of LIMIT", () => { + const sqlTableContext: SQLTableContext = { + engine: "mssql", + schema: "dbo", + defaultSchema: "dbo", + defaultDatabase: "master", + database: "master", + dialect: "mssql", + }; + + const result = sqlCode({ + table: mockTable, + columnName: mockColumn.name, + sqlTableContext, + }); + expect(result).toBe( + '_df = mo.sql(f"""\nSELECT TOP 100 email FROM users\n""", engine=mssql)', + ); + }); + + it("should use TOP 100 with database and schema prefix", () => { + const sqlTableContext: SQLTableContext = { + engine: "mssql", + schema: "sales", + defaultSchema: "dbo", + defaultDatabase: "master", + database: "analytics", + dialect: "mssql", + }; + + const result = sqlCode({ + table: mockTable, + columnName: mockColumn.name, + sqlTableContext, + }); + expect(result).toBe( + '_df = mo.sql(f"""\nSELECT TOP 100 email FROM analytics.sales.users\n""", engine=mssql)', + ); + }); }); - it("should generate SQL with non-default database and non-default schema", () => { - const sqlTableContext: SQLTableContext = { - engine: "bigquery", - schema: "sales", - defaultSchema: "public", - defaultDatabase: "mydb", - database: "remote", - }; - - const result = sqlCode({ - table: mockTable, - columnName: mockColumn.name, - sqlTableContext, - }); - expect(result).toBe( - '_df = mo.sql(f"SELECT email FROM remote.sales.users LIMIT 100", engine=bigquery)', - ); + describe("TimescaleDB dialect", () => { + it("should wrap table name with double quotes", () => { + const sqlTableContext: SQLTableContext = { + engine: "timescaledb", + schema: "public", + defaultSchema: "public", + defaultDatabase: "mydb", + database: "mydb", + dialect: "timescaledb", + }; + + const result = sqlCode({ + table: mockTable, + columnName: mockColumn.name, + sqlTableContext, + }); + expect(result).toBe( + '_df = mo.sql(f"""\nSELECT email FROM "users" LIMIT 100\n""", engine=timescaledb)', + ); + }); + + it("should wrap database, schema, and table name with double quotes", () => { + const sqlTableContext: SQLTableContext = { + engine: "timescaledb", + schema: "sales", + defaultSchema: "public", + defaultDatabase: "mydb", + database: "remote", + dialect: "timescaledb", + }; + + const result = sqlCode({ + table: mockTable, + columnName: mockColumn.name, + sqlTableContext, + }); + expect(result).toBe( + '_df = mo.sql(f"""\nSELECT email FROM "remote"."sales"."users" LIMIT 100\n""", engine=timescaledb)', + ); + }); + + it("should handle schemaless database with double quotes", () => { + const sqlTableContext: SQLTableContext = { + engine: "timescaledb", + schema: "", + defaultDatabase: "mydb", + database: "remote", + dialect: "timescaledb", + }; + + const result = sqlCode({ + table: mockTable, + columnName: mockColumn.name, + sqlTableContext, + }); + expect(result).toBe( + '_df = mo.sql(f"""\nSELECT email FROM "remote"."users" LIMIT 100\n""", engine=timescaledb)', + ); + }); }); - it("should generate SQL for schemaless tables", () => { - const sqlTableContext: SQLTableContext = { - engine: DUCKDB_ENGINE, - schema: "", - defaultDatabase: "mydb", - database: "mydb", - }; - - const result = sqlCode({ - table: mockTable, - columnName: mockColumn.name, - sqlTableContext, - }); - expect(result).toBe('_df = mo.sql(f"SELECT email FROM users LIMIT 100")'); - - const sqlTableContext2: SQLTableContext = { - engine: DUCKDB_ENGINE, - schema: "", - defaultDatabase: "remote", - database: "another_db", - }; - - const result2 = sqlCode({ - table: mockTable, - columnName: mockColumn.name, - sqlTableContext: sqlTableContext2, - }); - expect(result2).toBe( - '_df = mo.sql(f"SELECT email FROM another_db.users LIMIT 100")', - ); + describe("fallback behavior", () => { + it("should use default formatter for unknown dialect", () => { + const sqlTableContext: SQLTableContext = { + engine: "postgres", + schema: "public", + defaultSchema: "public", + defaultDatabase: "mydb", + database: "mydb", + dialect: "unknown_dialect", + }; + + const result = sqlCode({ + table: mockTable, + columnName: mockColumn.name, + sqlTableContext, + }); + expect(result).toBe( + '_df = mo.sql(f"""\nSELECT email FROM users LIMIT 100\n""", engine=postgres)', + ); + }); + + it("should use default formatter when dialect is not provided", () => { + const sqlTableContext: SQLTableContext = { + engine: "postgres", + schema: "public", + defaultSchema: "public", + defaultDatabase: "mydb", + database: "mydb", + dialect: "", + }; + + const result = sqlCode({ + table: mockTable, + columnName: mockColumn.name, + sqlTableContext, + }); + expect(result).toBe( + '_df = mo.sql(f"""\nSELECT email FROM users LIMIT 100\n""", engine=postgres)', + ); + }); }); }); diff --git a/frontend/src/components/datasources/datasources.tsx b/frontend/src/components/datasources/datasources.tsx index f0a8dd6afca..2e3ea2ac894 100644 --- a/frontend/src/components/datasources/datasources.tsx +++ b/frontend/src/components/datasources/datasources.tsx @@ -214,6 +214,7 @@ export const DataSources: React.FC = () => { databaseName={database.name} hasSearch={hasSearch} searchValue={searchValue} + dialect={connection.dialect} /> ))} @@ -340,6 +341,7 @@ const SchemaList: React.FC<{ schemas: DatabaseSchema[]; defaultSchema?: string | null; defaultDatabase?: string | null; + dialect: string; engineName: string; databaseName: string; hasSearch: boolean; @@ -348,6 +350,7 @@ const SchemaList: React.FC<{ schemas, defaultSchema, defaultDatabase, + dialect, engineName, databaseName, hasSearch, @@ -384,6 +387,7 @@ const SchemaList: React.FC<{ schema: schema.name, defaultSchema: defaultSchema, defaultDatabase: defaultDatabase, + dialect: dialect, }} /> diff --git a/frontend/src/components/datasources/utils.ts b/frontend/src/components/datasources/utils.ts index 0be4cdfd567..86678685944 100644 --- a/frontend/src/components/datasources/utils.ts +++ b/frontend/src/components/datasources/utils.ts @@ -1,7 +1,11 @@ /* Copyright 2024 Marimo. All rights reserved. */ + +import { BigQueryDialect } from "@marimo-team/codemirror-sql/dialects"; +import { isKnownDialect } from "@/core/codemirror/language/languages/sql/utils"; import type { SQLTableContext } from "@/core/datasets/data-source-connections"; import { DUCKDB_ENGINE } from "@/core/datasets/engines"; import type { DataTable, DataType } from "@/core/kernel/messages"; +import { logNever } from "@/utils/assertNever"; import type { ColumnHeaderStatsKey } from "../data-table/types"; // Some databases have no schemas, so we don't show it (eg. Clickhouse) @@ -9,6 +13,78 @@ export function isSchemaless(schemaName: string) { return schemaName === ""; } +interface SqlCodeFormatter { + /** + * Format the table name based on dialect-specific rules + */ + formatTableName: (tableName: string) => string; + /** + * Format the SELECT clause + */ + formatSelectClause: (columnName: string, tableName: string) => string; +} + +const defaultFormatter: SqlCodeFormatter = { + formatTableName: (tableName: string) => tableName, + formatSelectClause: (columnName: string, tableName: string) => + `SELECT ${columnName} FROM ${tableName} LIMIT 100`, +}; + +function getFormatter(dialect: string): SqlCodeFormatter { + dialect = dialect.toLowerCase(); + if (!isKnownDialect(dialect)) { + return defaultFormatter; + } + + switch (dialect) { + case "bigquery": { + const quote = BigQueryDialect.spec.identifierQuotes; + return { + // BigQuery uses backticks for identifiers + formatTableName: (tableName: string) => `${quote}${tableName}${quote}`, + formatSelectClause: defaultFormatter.formatSelectClause, + }; + } + case "mssql": + case "sqlserver": + return { + formatTableName: defaultFormatter.formatTableName, + formatSelectClause: (columnName: string, tableName: string) => + `SELECT TOP 100 ${columnName} FROM ${tableName}`, + }; + case "timescaledb": + return { + // TimescaleDB uses double quotes for identifiers + formatTableName: (tableName: string) => { + const parts = tableName.split("."); + return parts.map((part) => `"${part}"`).join("."); + }, + formatSelectClause: defaultFormatter.formatSelectClause, + }; + case "postgresql": + case "postgres": + case "db2": + case "mysql": + case "sqlite": + case "duckdb": + case "mariadb": + case "cassandra": + case "noql": + case "athena": + case "hive": + case "redshift": + case "snowflake": + case "flink": + case "mongodb": + case "oracle": + case "oracledb": + return defaultFormatter; + default: + logNever(dialect); + return defaultFormatter; + } +} + export function sqlCode({ table, columnName, @@ -19,8 +95,14 @@ export function sqlCode({ sqlTableContext?: SQLTableContext; }) { if (sqlTableContext) { - const { engine, schema, defaultSchema, defaultDatabase, database } = - sqlTableContext; + const { + engine, + schema, + defaultSchema, + defaultDatabase, + database, + dialect, + } = sqlTableContext; let tableName = table.name; // Set the fully qualified table name based on schema and database @@ -39,11 +121,18 @@ export function sqlCode({ } } + const formatter = getFormatter(dialect); + const formattedTableName = formatter.formatTableName(tableName); + const selectClause = formatter.formatSelectClause( + columnName, + formattedTableName, + ); + if (engine === DUCKDB_ENGINE) { - return `_df = mo.sql(f"SELECT ${columnName} FROM ${tableName} LIMIT 100")`; + return `_df = mo.sql(f"""\n${selectClause}\n""")`; } - return `_df = mo.sql(f"SELECT ${columnName} FROM ${tableName} LIMIT 100", engine=${engine})`; + return `_df = mo.sql(f"""\n${selectClause}\n""", engine=${engine})`; } return `_df = mo.sql(f'SELECT "${columnName}" FROM ${table.name} LIMIT 100')`; diff --git a/frontend/src/core/codemirror/language/languages/sql/completion-store.ts b/frontend/src/core/codemirror/language/languages/sql/completion-store.ts index 5aed29b6c4b..b0a81122be9 100644 --- a/frontend/src/core/codemirror/language/languages/sql/completion-store.ts +++ b/frontend/src/core/codemirror/language/languages/sql/completion-store.ts @@ -134,7 +134,7 @@ class SQLCompletionStore { if (!connection) { return ModifiedStandardSQL; } - return guessDialect(connection) ?? ModifiedStandardSQL; + return guessDialect(connection); } getCompletionSource(connectionName: ConnectionName): SQLConfig | null { @@ -152,7 +152,7 @@ class SQLCompletionStore { const schema = this.cache.getOrCreate(connection); return { - dialect: guessDialect(connection) ?? ModifiedStandardSQL, + dialect: guessDialect(connection), schema: schema.shouldAddLocalTables ? { ...schema.schema, ...getTablesMap() } : schema.schema, diff --git a/frontend/src/core/codemirror/language/languages/sql/sql.ts b/frontend/src/core/codemirror/language/languages/sql/sql.ts index 6ce36cdc86a..a9b4e88e0e6 100644 --- a/frontend/src/core/codemirror/language/languages/sql/sql.ts +++ b/frontend/src/core/codemirror/language/languages/sql/sql.ts @@ -38,6 +38,7 @@ import type { HotkeyProvider } from "@/core/hotkeys/hotkeys"; import type { ValidateSQLResult } from "@/core/kernel/messages"; import { store } from "@/core/state/jotai"; import { resolvedThemeAtom } from "@/theme/useTheme"; +import { logNever } from "@/utils/assertNever"; import { Logger } from "@/utils/Logger"; import { variableCompletionSource } from "../../embedded/embedded-python"; import { languageMetadataField } from "../../metadata"; @@ -52,6 +53,7 @@ import { } from "./completion-sources"; import { SCHEMA_CACHE } from "./completion-store"; import { getSQLMode, type SQLMode } from "./sql-mode"; +import { isKnownDialect } from "./utils"; const DEFAULT_DIALECT = DuckDBDialect; const DEFAULT_PARSER_DIALECT = "DuckDB"; @@ -353,6 +355,11 @@ function connectionNameToParserDialect( ): ParserDialects | null { const dialect = SCHEMA_CACHE.getInternalDialect(connectionName)?.toLowerCase(); + + if (!dialect || !isKnownDialect(dialect)) { + return null; + } + switch (dialect) { case "postgresql": case "postgres": @@ -385,8 +392,15 @@ function connectionNameToParserDialect( case "flink": return "FlinkSQL"; case "mongodb": + case "noql": return "Noql"; + case "oracle": + case "oracledb": + case "timescaledb": + Logger.debug("Unsupported dialect", { dialect }); + return null; default: + logNever(dialect); return null; } } diff --git a/frontend/src/core/codemirror/language/languages/sql/utils.ts b/frontend/src/core/codemirror/language/languages/sql/utils.ts index 80143e69f9c..5ebf3ff40e2 100644 --- a/frontend/src/core/codemirror/language/languages/sql/utils.ts +++ b/frontend/src/core/codemirror/language/languages/sql/utils.ts @@ -17,14 +17,52 @@ import { DuckDBDialect, } from "@marimo-team/codemirror-sql/dialects"; import type { DataSourceConnection } from "@/core/kernel/messages"; +import { logNever } from "@/utils/assertNever"; +import { Logger } from "@/utils/Logger"; + +const KNOWN_DIALECTS_ARRAY = [ + "postgresql", + "postgres", + "db2", + "mysql", + "sqlite", + "mssql", + "sqlserver", + "duckdb", + "mariadb", + "cassandra", + "noql", + "athena", + "bigquery", + "hive", + "redshift", + "snowflake", + "flink", + "mongodb", + "oracle", + "oracledb", + "timescaledb", +] as const; +const KNOWN_DIALECTS: ReadonlySet = new Set(KNOWN_DIALECTS_ARRAY); +type KnownDialect = (typeof KNOWN_DIALECTS_ARRAY)[number]; + +export function isKnownDialect(dialect: string): dialect is KnownDialect { + return KNOWN_DIALECTS.has(dialect); +} /** * Guess the CodeMirror SQL dialect from the backend connection dialect. + * If unknown, return the standard SQL dialect. */ export function guessDialect( connection: Pick, -): SQLDialect | undefined { - switch (connection.dialect) { +): SQLDialect { + const dialect = connection.dialect; + if (!isKnownDialect(dialect)) { + return ModifiedStandardSQL; + } + + switch (dialect) { case "postgresql": case "postgres": return PostgreSQL; @@ -46,8 +84,21 @@ export function guessDialect( return PLSQL; case "bigquery": return BigQueryDialect; + case "timescaledb": + return PostgreSQL; // TimescaleDB is a PostgreSQL dialect + case "athena": + case "db2": + case "hive": + case "redshift": + case "snowflake": + case "flink": + case "mongodb": + case "noql": + Logger.debug("Unsupported dialect", { dialect }); + return ModifiedStandardSQL; default: - return undefined; + logNever(dialect); + return ModifiedStandardSQL; } } diff --git a/frontend/src/core/datasets/__tests__/data-source.test.ts b/frontend/src/core/datasets/__tests__/data-source.test.ts index 9c2cfec2ef6..419ce7d2689 100644 --- a/frontend/src/core/datasets/__tests__/data-source.test.ts +++ b/frontend/src/core/datasets/__tests__/data-source.test.ts @@ -270,6 +270,7 @@ describe("add table list", () => { engine: "conn1" as ConnectionName, database: "db1", schema: "public", + dialect: "sqlite", }); const conn1 = newState.connectionsMap.get("conn1" as ConnectionName); @@ -283,6 +284,7 @@ describe("add table list", () => { engine: "conn1" as ConnectionName, database: "db1", schema: "public", + dialect: "sqlite", }; const tableList: DataTable[] = [ @@ -344,6 +346,7 @@ describe("add table list", () => { engine: "conn1" as ConnectionName, database: "db1", schema: "non_existent", + dialect: "sqlite", }); const conn1 = newState.connectionsMap.get("conn1" as ConnectionName); @@ -407,6 +410,7 @@ describe("add table", () => { engine: "conn1" as ConnectionName, database: "db1", schema: "public", + dialect: "sqlite", }); const conn1 = newState.connectionsMap.get("conn1" as ConnectionName); @@ -420,6 +424,7 @@ describe("add table", () => { engine: "conn1" as ConnectionName, database: "db1", schema: "public", + dialect: "sqlite", }; const table: DataTable = { @@ -476,6 +481,7 @@ describe("add table", () => { engine: "conn1" as ConnectionName, database: "db1", schema: "non_existent", + dialect: "sqlite", }); const conn1 = newState.connectionsMap.get("conn1" as ConnectionName); diff --git a/frontend/src/core/datasets/data-source-connections.ts b/frontend/src/core/datasets/data-source-connections.ts index 0c3837b3d93..6116b4639ea 100644 --- a/frontend/src/core/datasets/data-source-connections.ts +++ b/frontend/src/core/datasets/data-source-connections.ts @@ -47,6 +47,7 @@ export interface SQLTableContext { engine: string; database: string; schema: string; + dialect: string; defaultSchema?: string | null; defaultDatabase?: string | null; }