@marimo-team/frontend 0.23.16-dev52 → 0.23.16-dev53
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{CellStatus-BT88_2jw.js → CellStatus-BfPw99P2.js} +1 -1
- package/dist/assets/{JsonOutput-0XYUxjxO.js → JsonOutput-E3o1Uk0e.js} +1 -1
- package/dist/assets/{MarimoErrorOutput-DuwnzR5D.js → MarimoErrorOutput-D9Dypu7Q.js} +1 -1
- package/dist/assets/{RenderHTML-BPdXbewN.js → RenderHTML-DRJxD5FQ.js} +1 -1
- package/dist/assets/{RunButton-DJyvIhMO.js → RunButton-y7Y9rjmF.js} +1 -1
- package/dist/assets/{add-cell-with-ai-Dspw1-mO.js → add-cell-with-ai-ByfOWv0Z.js} +1 -1
- package/dist/assets/{add-connection-dialog-B1fFBVqJ.js → add-connection-dialog-DVSFAYXK.js} +1 -1
- package/dist/assets/{agent-panel-iPdDqSEd.js → agent-panel-B2OKzocQ.js} +1 -1
- package/dist/assets/{ai-model-dropdown-BC1SNLKb.js → ai-model-dropdown-Dr2QbDlZ.js} +1 -1
- package/dist/assets/{app-config-button-CuE-bLFn.js → app-config-button-DScmpqcV.js} +1 -1
- package/dist/assets/{blob-C23KHI9W.js → blob-BXcsY70H.js} +1 -1
- package/dist/assets/{cell-editor-D8UAfiUa.js → cell-editor-BuGvBHQI.js} +1 -1
- package/dist/assets/{cell-link-Bu5k33_H.js → cell-link-DGv7Q4Mc.js} +1 -1
- package/dist/assets/{cells-C3V6s-g5.js → cells-CMc2NsXm.js} +58 -58
- package/dist/assets/{chat-display-DSMFTDiK.js → chat-display-CeQftKrl.js} +1 -1
- package/dist/assets/{chat-panel-DuGaXa17.js → chat-panel-BP5hfvS7.js} +1 -1
- package/dist/assets/{chat-ui-TD66NC03.js → chat-ui-DoA0lexh.js} +1 -1
- package/dist/assets/{column-preview-Dqt5ygdi.js → column-preview-BeR1SI6Z.js} +1 -1
- package/dist/assets/{command-palette-DgyUILpb.js → command-palette-DZSYeVCO.js} +1 -1
- package/dist/assets/{common-CIJsW5yR.js → common-NWgkp5L1.js} +1 -1
- package/dist/assets/{components-CniQJAsf.js → components-Mbf0MEIC.js} +1 -1
- package/dist/assets/{datasource-YLl5kKpx.js → datasource-Wt8yUynT.js} +1 -1
- package/dist/assets/{dependency-graph-panel-MPHqARlX.js → dependency-graph-panel-B7TXWKYs.js} +1 -1
- package/dist/assets/{documentation-panel-DZnNktBv.js → documentation-panel-D9MZUKaD.js} +1 -1
- package/dist/assets/{download-CSWknseP.js → download-U_wgQ8mC.js} +1 -1
- package/dist/assets/{edit-page-DVXg8Ibp.js → edit-page-BBc9BGYT.js} +4 -4
- package/dist/assets/{error-entries-DIjVQBFa.js → error-entries-Bs30Z6uG.js} +1 -1
- package/dist/assets/{error-panel-CJSBDHOq.js → error-panel-KY679tDx.js} +1 -1
- package/dist/assets/{file-explorer-panel-sHVZyshk.js → file-explorer-panel-9y0hzNAa.js} +1 -1
- package/dist/assets/{file-icons-DDUQU--N.js → file-icons-D5l3kSYq.js} +1 -1
- package/dist/assets/{file-name-input-CQjQP_zG.js → file-name-input-D_xhjKFp.js} +1 -1
- package/dist/assets/{floating-outline-D1iQUiR_.js → floating-outline-BdgJT2B9.js} +1 -1
- package/dist/assets/{focus-D_UTw9HF.js → focus-D64hL1tZ.js} +1 -1
- package/dist/assets/{form-Bp7lk3pm.js → form-CoqwGbkO.js} +1 -1
- package/dist/assets/{form-B6jUSJBp.js → form-b4jQBV3T.js} +1 -1
- package/dist/assets/{home-page-CX6IV-hM.js → home-page-tVWY9YdF.js} +1 -1
- package/dist/assets/{html-to-image-kpOREGyr.js → html-to-image-Dm7huiQj.js} +1 -1
- package/dist/assets/{index-CrJhpWGp.js → index-Dz8dLbnI.js} +3 -3
- package/dist/assets/{layout-DdPE4xZU.js → layout-DKUs0ozP.js} +3 -3
- package/dist/assets/{logs-panel-DnNiJzUQ.js → logs-panel-BzS7VSh0.js} +1 -1
- package/dist/assets/{markdown-renderer-CqljiwGm.js → markdown-renderer-uWTrQId1.js} +1 -1
- package/dist/assets/{name-cell-input-BQzQI3Ja.js → name-cell-input-BgxcWTfV.js} +1 -1
- package/dist/assets/{outline-panel-BIgVnAC7.js → outline-panel-BT3zzwfa.js} +1 -1
- package/dist/assets/{packages-panel-_HiSQVV7.js → packages-panel-BKfeGuve.js} +1 -1
- package/dist/assets/{panels-b-OL36p2.js → panels-4jSJxEYp.js} +1 -1
- package/dist/assets/{process-output-CJkR5qgD.js → process-output-DE8TTXPC.js} +1 -1
- package/dist/assets/{radio-group-Dm-ZV-tP.js → radio-group-Da80KtoT.js} +1 -1
- package/dist/assets/{readonly-code-display-BiNt9i32.js → readonly-code-display-B8zhuSov.js} +1 -1
- package/dist/assets/{readonly-python-code-DMKb3G0K.js → readonly-python-code-Bmo6cMSq.js} +1 -1
- package/dist/assets/{reveal-component-DN6-mIsb.js → reveal-component-Cv7WZ4r6.js} +1 -1
- package/dist/assets/{run-page-BKCqJ0uJ.js → run-page-CA21zsME.js} +1 -1
- package/dist/assets/{scratchpad-panel-DwZEAfW1.js → scratchpad-panel-Br7RFJkK.js} +1 -1
- package/dist/assets/{session-panel-B-9BnZxS.js → session-panel-BiUht4zM.js} +1 -1
- package/dist/assets/{skeleton-B72xXZAF.js → skeleton-CY-B1asm.js} +1 -1
- package/dist/assets/{snippets-panel-Brc_pmTk.js → snippets-panel-CjiTlCpD.js} +1 -1
- package/dist/assets/{state-BTAocii6.js → state-BJ7IdtXW.js} +1 -1
- package/dist/assets/{state-D9oG57zx.js → state-TgAqts79.js} +1 -1
- package/dist/assets/{tracing-Cn9cTT65.js → tracing-DmQ7KQ3A.js} +1 -1
- package/dist/assets/{tracing-panel-FWoe3_eY.js → tracing-panel-C1TOV9kj.js} +2 -2
- package/dist/assets/{tree-actions-rPl1v1gM.js → tree-actions-enwITSy7.js} +1 -1
- package/dist/assets/{useCellActionButton-Du3JXpNf.js → useCellActionButton-CXvU9GmI.js} +1 -1
- package/dist/assets/{useDeleteCell-DHOwR-CG.js → useDeleteCell-Bp3S6pL8.js} +1 -1
- package/dist/assets/{useDependencyPanelTab-B2PCtSi6.js → useDependencyPanelTab-CewYv8cP.js} +1 -1
- package/dist/assets/{useNotebookActions-DfQAtofT.js → useNotebookActions-l9NAqPKm.js} +1 -1
- package/dist/assets/{useRunCells-Dg11M4pT.js → useRunCells-B-KIwrVZ.js} +1 -1
- package/dist/assets/{useSplitCell-BwWVHida.js → useSplitCell-B0jvSFsl.js} +1 -1
- package/dist/index.html +23 -23
- package/package.json +1 -1
- package/src/core/codemirror/language/__tests__/sql.test.ts +191 -7
- package/src/core/codemirror/language/languages/sql/sql.ts +41 -13
|
@@ -27,6 +27,7 @@ import type { HotkeyProvider } from "@/core/hotkeys/hotkeys";
|
|
|
27
27
|
import { store } from "@/core/state/jotai";
|
|
28
28
|
import type { PlaceholderType } from "../../config/types";
|
|
29
29
|
import { TestSQLCompletionStore } from "../languages/sql/completion-store";
|
|
30
|
+
import * as sqlMode from "../languages/sql/sql-mode";
|
|
30
31
|
import {
|
|
31
32
|
exportedForTesting,
|
|
32
33
|
SQLLanguageAdapter,
|
|
@@ -971,13 +972,63 @@ describe("SQL analysis features", () => {
|
|
|
971
972
|
});
|
|
972
973
|
|
|
973
974
|
describe("CustomSqlParser", () => {
|
|
975
|
+
beforeEach(() => {
|
|
976
|
+
store.set(dataSourceConnectionsAtom, {
|
|
977
|
+
connectionsMap: new Map(),
|
|
978
|
+
latestEngineSelected: DUCKDB_ENGINE,
|
|
979
|
+
});
|
|
980
|
+
});
|
|
981
|
+
|
|
974
982
|
afterEach(() => {
|
|
975
983
|
vi.useRealTimers();
|
|
976
984
|
vi.restoreAllMocks();
|
|
985
|
+
store.set(dataSourceConnectionsAtom, {
|
|
986
|
+
connectionsMap: new Map(),
|
|
987
|
+
latestEngineSelected: DUCKDB_ENGINE,
|
|
988
|
+
});
|
|
977
989
|
});
|
|
978
990
|
|
|
991
|
+
const createNamedConnectionState = ({
|
|
992
|
+
doc,
|
|
993
|
+
engine,
|
|
994
|
+
dialect,
|
|
995
|
+
}: {
|
|
996
|
+
doc: string;
|
|
997
|
+
engine: ConnectionName;
|
|
998
|
+
dialect: string;
|
|
999
|
+
}) => {
|
|
1000
|
+
store.set(dataSourceConnectionsAtom, {
|
|
1001
|
+
connectionsMap: new Map([
|
|
1002
|
+
[
|
|
1003
|
+
engine,
|
|
1004
|
+
{
|
|
1005
|
+
name: engine,
|
|
1006
|
+
dialect,
|
|
1007
|
+
display_name: engine,
|
|
1008
|
+
source: dialect,
|
|
1009
|
+
databases: [],
|
|
1010
|
+
},
|
|
1011
|
+
],
|
|
1012
|
+
]),
|
|
1013
|
+
latestEngineSelected: engine,
|
|
1014
|
+
});
|
|
1015
|
+
return EditorState.create({
|
|
1016
|
+
doc,
|
|
1017
|
+
extensions: [
|
|
1018
|
+
languageMetadataField.init(() => ({
|
|
1019
|
+
dataframeName: "_df",
|
|
1020
|
+
quotePrefix: "f",
|
|
1021
|
+
commentLines: [],
|
|
1022
|
+
showOutput: true,
|
|
1023
|
+
engine,
|
|
1024
|
+
})),
|
|
1025
|
+
],
|
|
1026
|
+
});
|
|
1027
|
+
};
|
|
1028
|
+
|
|
979
1029
|
it("uses backend DuckDB validation", async () => {
|
|
980
1030
|
vi.useFakeTimers();
|
|
1031
|
+
vi.spyOn(sqlMode, "getSQLMode").mockReturnValue("validate");
|
|
981
1032
|
const error = {
|
|
982
1033
|
message: "Backend syntax error",
|
|
983
1034
|
line: 1,
|
|
@@ -1009,13 +1060,146 @@ describe("CustomSqlParser", () => {
|
|
|
1009
1060
|
await vi.runAllTimersAsync();
|
|
1010
1061
|
|
|
1011
1062
|
await expect(result).resolves.toEqual([error]);
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1063
|
+
// No connection is registered for the internal engine, so the dialect
|
|
1064
|
+
// falls back to DEFAULT_PARSER_DIALECT ("DuckDB").
|
|
1065
|
+
expect(request).toHaveBeenCalledWith({
|
|
1066
|
+
dialect: "DuckDB",
|
|
1067
|
+
engine: DUCKDB_ENGINE,
|
|
1068
|
+
onlyParse: false,
|
|
1069
|
+
query: "SELECT",
|
|
1070
|
+
});
|
|
1071
|
+
});
|
|
1072
|
+
|
|
1073
|
+
it("resolves superseded validation requests instead of leaving them pending", async () => {
|
|
1074
|
+
vi.useFakeTimers();
|
|
1075
|
+
vi.spyOn(sqlMode, "getSQLMode").mockReturnValue("validate");
|
|
1076
|
+
const error = {
|
|
1077
|
+
message: "Backend syntax error",
|
|
1078
|
+
line: 1,
|
|
1079
|
+
column: 1,
|
|
1080
|
+
severity: "error" as const,
|
|
1081
|
+
};
|
|
1082
|
+
vi.spyOn(ValidateSQL, "request").mockResolvedValue({
|
|
1083
|
+
error: null,
|
|
1084
|
+
parse_result: { success: false, errors: [error] },
|
|
1085
|
+
request_id: "request-id",
|
|
1086
|
+
validate_result: null,
|
|
1087
|
+
});
|
|
1088
|
+
const state = EditorState.create({
|
|
1089
|
+
doc: "SELECT",
|
|
1090
|
+
extensions: [
|
|
1091
|
+
languageMetadataField.init(() => ({
|
|
1092
|
+
dataframeName: "_df",
|
|
1093
|
+
quotePrefix: "f",
|
|
1094
|
+
commentLines: [],
|
|
1095
|
+
showOutput: true,
|
|
1096
|
+
engine: DUCKDB_ENGINE,
|
|
1097
|
+
})),
|
|
1098
|
+
],
|
|
1099
|
+
});
|
|
1100
|
+
const parser = new exportedForTesting.CustomSqlParser();
|
|
1101
|
+
parser.setFocusState(true);
|
|
1102
|
+
|
|
1103
|
+
// Simulate a rapid edit: a second validation call arrives before the
|
|
1104
|
+
// first call's internal debounce timer has fired. The first call should
|
|
1105
|
+
// resolve (with no errors) rather than hang forever.
|
|
1106
|
+
const first = parser.validateSql("SELECT 1", { state });
|
|
1107
|
+
await vi.advanceTimersByTimeAsync(100);
|
|
1108
|
+
const second = parser.validateSql("SELECT 2", { state });
|
|
1109
|
+
await vi.runAllTimersAsync();
|
|
1110
|
+
|
|
1111
|
+
await expect(first).resolves.toEqual([]);
|
|
1112
|
+
await expect(second).resolves.toEqual([error]);
|
|
1113
|
+
});
|
|
1114
|
+
|
|
1115
|
+
it("uses backend validation for a named DuckDB connection", async () => {
|
|
1116
|
+
vi.useFakeTimers();
|
|
1117
|
+
vi.spyOn(sqlMode, "getSQLMode").mockReturnValue("validate");
|
|
1118
|
+
const query =
|
|
1119
|
+
'INSERT OR IGNORE INTO "rebalance-dates" (Timestamp) VALUES (CURRENT_TIMESTAMP);';
|
|
1120
|
+
const error = {
|
|
1121
|
+
message: "Named DuckDB backend result",
|
|
1122
|
+
line: 1,
|
|
1123
|
+
column: 1,
|
|
1124
|
+
severity: "error" as const,
|
|
1125
|
+
};
|
|
1126
|
+
const request = vi.spyOn(ValidateSQL, "request").mockResolvedValue({
|
|
1127
|
+
error: null,
|
|
1128
|
+
parse_result: { success: false, errors: [error] },
|
|
1129
|
+
request_id: "request-id",
|
|
1130
|
+
validate_result: null,
|
|
1131
|
+
});
|
|
1132
|
+
const state = createNamedConnectionState({
|
|
1133
|
+
doc: query,
|
|
1134
|
+
engine: "con" as ConnectionName,
|
|
1135
|
+
dialect: "duckdb",
|
|
1136
|
+
});
|
|
1137
|
+
const parser = new exportedForTesting.CustomSqlParser();
|
|
1138
|
+
parser.setFocusState(true);
|
|
1139
|
+
|
|
1140
|
+
const result = parser.validateSql(query, { state });
|
|
1141
|
+
await vi.runAllTimersAsync();
|
|
1142
|
+
|
|
1143
|
+
await expect(result).resolves.toEqual([error]);
|
|
1144
|
+
expect(request).toHaveBeenCalledWith({
|
|
1145
|
+
dialect: "DuckDB",
|
|
1146
|
+
engine: "con",
|
|
1147
|
+
onlyParse: true,
|
|
1148
|
+
query,
|
|
1149
|
+
});
|
|
1150
|
+
});
|
|
1151
|
+
|
|
1152
|
+
it("skips client-side parsing for named DuckDB SQL", async () => {
|
|
1153
|
+
const query = "CHECKPOINT;";
|
|
1154
|
+
const state = createNamedConnectionState({
|
|
1155
|
+
doc: query,
|
|
1156
|
+
engine: "con" as ConnectionName,
|
|
1157
|
+
dialect: "duckdb",
|
|
1158
|
+
});
|
|
1159
|
+
const parser = new exportedForTesting.CustomSqlParser();
|
|
1160
|
+
|
|
1161
|
+
await expect(parser.parse(query, { state })).resolves.toEqual({
|
|
1162
|
+
success: true,
|
|
1163
|
+
errors: [],
|
|
1164
|
+
});
|
|
1165
|
+
});
|
|
1166
|
+
|
|
1167
|
+
it("preserves client-side parsing for non-DuckDB connections", async () => {
|
|
1168
|
+
const query = "SELECT TOP 1 * FROM users";
|
|
1169
|
+
const state = createNamedConnectionState({
|
|
1170
|
+
doc: query,
|
|
1171
|
+
engine: "postgres_con" as ConnectionName,
|
|
1172
|
+
dialect: "postgres",
|
|
1173
|
+
});
|
|
1174
|
+
const parser = new exportedForTesting.CustomSqlParser();
|
|
1175
|
+
|
|
1176
|
+
await expect(parser.parse(query, { state })).resolves.toMatchObject({
|
|
1177
|
+
success: false,
|
|
1178
|
+
});
|
|
1179
|
+
});
|
|
1180
|
+
|
|
1181
|
+
it("preserves client-side parsing for an unregistered connection", async () => {
|
|
1182
|
+
// No entry is registered in the data source connections store for this
|
|
1183
|
+
// engine, so its dialect cannot be resolved (unlike a known non-DuckDB
|
|
1184
|
+
// dialect, which resolves to a non-null, non-DuckDB value).
|
|
1185
|
+
const query = "SELECT TOP 1 * FROM users";
|
|
1186
|
+
const state = EditorState.create({
|
|
1187
|
+
doc: query,
|
|
1188
|
+
extensions: [
|
|
1189
|
+
languageMetadataField.init(() => ({
|
|
1190
|
+
dataframeName: "_df",
|
|
1191
|
+
quotePrefix: "f",
|
|
1192
|
+
commentLines: [],
|
|
1193
|
+
showOutput: true,
|
|
1194
|
+
engine: "unregistered_con" as ConnectionName,
|
|
1195
|
+
})),
|
|
1196
|
+
],
|
|
1197
|
+
});
|
|
1198
|
+
const parser = new exportedForTesting.CustomSqlParser();
|
|
1199
|
+
|
|
1200
|
+
await expect(parser.parse(query, { state })).resolves.toMatchObject({
|
|
1201
|
+
success: false,
|
|
1202
|
+
});
|
|
1019
1203
|
});
|
|
1020
1204
|
});
|
|
1021
1205
|
|
|
@@ -66,7 +66,7 @@ import { getSQLMode, type SQLMode } from "./sql-mode";
|
|
|
66
66
|
import { isKnownDialect } from "./utils";
|
|
67
67
|
|
|
68
68
|
const DEFAULT_DIALECT = DuckDBDialect;
|
|
69
|
-
const DEFAULT_PARSER_DIALECT = "DuckDB";
|
|
69
|
+
const DEFAULT_PARSER_DIALECT: ParserDialects = "DuckDB";
|
|
70
70
|
|
|
71
71
|
// A compartment for the SQL config, so we can update the config of codemirror
|
|
72
72
|
const sqlConfigCompartment = new Compartment();
|
|
@@ -372,6 +372,8 @@ class DialectAwareSqlStructureAnalyzer extends SqlStructureAnalyzer {
|
|
|
372
372
|
|
|
373
373
|
class CustomSqlParser extends NodeSqlParser {
|
|
374
374
|
private validationTimeout: number | null = null;
|
|
375
|
+
private pendingValidationResolve: ((errors: SqlParseError[]) => void) | null =
|
|
376
|
+
null;
|
|
375
377
|
private readonly VALIDATION_DELAY_MS = 300; // Wait 300ms after user stops typing
|
|
376
378
|
private isFocused = false; // Only validate if the editor is focused
|
|
377
379
|
|
|
@@ -379,37 +381,51 @@ class CustomSqlParser extends NodeSqlParser {
|
|
|
379
381
|
this.isFocused = focused;
|
|
380
382
|
}
|
|
381
383
|
|
|
384
|
+
private resolvePendingValidation(errors: SqlParseError[]): void {
|
|
385
|
+
this.pendingValidationResolve?.(errors);
|
|
386
|
+
this.pendingValidationResolve = null;
|
|
387
|
+
}
|
|
388
|
+
|
|
382
389
|
private async validateWithDelay(
|
|
383
390
|
sql: string,
|
|
384
|
-
engine:
|
|
391
|
+
engine: ConnectionName,
|
|
385
392
|
dialect: ParserDialects | null,
|
|
386
393
|
): Promise<SqlParseError[]> {
|
|
387
|
-
// Clear any existing delay call
|
|
394
|
+
// Clear any existing delay call, resolving its promise so a superseded
|
|
395
|
+
// request doesn't hang forever.
|
|
388
396
|
if (this.validationTimeout) {
|
|
389
397
|
window.clearTimeout(this.validationTimeout);
|
|
398
|
+
this.resolvePendingValidation([]);
|
|
390
399
|
}
|
|
391
400
|
|
|
392
401
|
// Set up a new request to be called after the delay
|
|
393
402
|
return new Promise((resolve) => {
|
|
403
|
+
this.pendingValidationResolve = resolve;
|
|
394
404
|
this.validationTimeout = window.setTimeout(async () => {
|
|
405
|
+
this.validationTimeout = null;
|
|
406
|
+
|
|
395
407
|
// Only validate if the editor is still focused
|
|
396
408
|
if (!this.isFocused) {
|
|
397
|
-
|
|
409
|
+
this.resolvePendingValidation([]);
|
|
398
410
|
return;
|
|
399
411
|
}
|
|
400
412
|
|
|
401
413
|
try {
|
|
402
|
-
|
|
414
|
+
// For validate mode, we run EXPLAIN queries on the engine, which can be
|
|
415
|
+
// expensive for remote databases. So, we only run for internal engines.
|
|
416
|
+
const sqlMode = INTERNAL_SQL_ENGINES.has(engine)
|
|
417
|
+
? getSQLMode()
|
|
418
|
+
: "default";
|
|
403
419
|
const result = await validateSQL(sql, engine, dialect, sqlMode);
|
|
404
420
|
if (result.error) {
|
|
405
421
|
Logger.error("Failed to validate SQL", { error: result.error });
|
|
406
|
-
|
|
422
|
+
this.resolvePendingValidation([]);
|
|
407
423
|
return;
|
|
408
424
|
}
|
|
409
|
-
|
|
425
|
+
this.resolvePendingValidation(result.parse_result?.errors ?? []);
|
|
410
426
|
} catch (error) {
|
|
411
427
|
Logger.error("Failed to validate SQL", { error });
|
|
412
|
-
|
|
428
|
+
this.resolvePendingValidation([]);
|
|
413
429
|
}
|
|
414
430
|
}, this.VALIDATION_DELAY_MS);
|
|
415
431
|
});
|
|
@@ -420,19 +436,23 @@ class CustomSqlParser extends NodeSqlParser {
|
|
|
420
436
|
opts: { state: EditorState },
|
|
421
437
|
): Promise<SqlParseError[]> {
|
|
422
438
|
const metadata = getSQLMetadata(opts.state);
|
|
439
|
+
const dialect = connectionNameToParserDialect(metadata.engine);
|
|
423
440
|
|
|
424
441
|
// Only validate if the editor is focused
|
|
425
442
|
if (!this.isFocused) {
|
|
426
443
|
return [];
|
|
427
444
|
}
|
|
428
445
|
|
|
429
|
-
// Only perform custom validation for DuckDB
|
|
430
|
-
if (!
|
|
446
|
+
// Only perform custom validation for DuckDB as we have a custom validation endpoint for it.
|
|
447
|
+
if (!isDuckDBConnection(metadata.engine, dialect)) {
|
|
431
448
|
return super.validateSql(sql, opts);
|
|
432
449
|
}
|
|
433
450
|
|
|
434
|
-
|
|
435
|
-
|
|
451
|
+
return this.validateWithDelay(
|
|
452
|
+
sql,
|
|
453
|
+
metadata.engine,
|
|
454
|
+
dialect ?? DEFAULT_PARSER_DIALECT,
|
|
455
|
+
);
|
|
436
456
|
}
|
|
437
457
|
|
|
438
458
|
override async parse(
|
|
@@ -441,9 +461,10 @@ class CustomSqlParser extends NodeSqlParser {
|
|
|
441
461
|
): Promise<NodeSqlParserResult> {
|
|
442
462
|
const metadata = getSQLMetadata(opts.state);
|
|
443
463
|
const engine = metadata.engine;
|
|
464
|
+
const dialect = connectionNameToParserDialect(engine);
|
|
444
465
|
|
|
445
466
|
// For now, always return success for DuckDB
|
|
446
|
-
if (engine
|
|
467
|
+
if (isDuckDBConnection(engine, dialect)) {
|
|
447
468
|
return { success: true, errors: [] };
|
|
448
469
|
}
|
|
449
470
|
|
|
@@ -451,6 +472,13 @@ class CustomSqlParser extends NodeSqlParser {
|
|
|
451
472
|
}
|
|
452
473
|
}
|
|
453
474
|
|
|
475
|
+
function isDuckDBConnection(
|
|
476
|
+
engine: ConnectionName,
|
|
477
|
+
dialect: ParserDialects | null,
|
|
478
|
+
): boolean {
|
|
479
|
+
return engine === DUCKDB_ENGINE || dialect === "DuckDB";
|
|
480
|
+
}
|
|
481
|
+
|
|
454
482
|
/**
|
|
455
483
|
* Update the SQL dialect in the editor view.
|
|
456
484
|
*/
|