Skip to content

Commit 0233d02

Browse files
committed
fix(sql): scope autocomplete table names to the selected catalog
1 parent bab3445 commit 0233d02

1 file changed

Lines changed: 22 additions & 8 deletions

File tree

frontend/src/components/pages/sql/sql-editor.tsx

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
type CompletionResult,
1616
startCompletion,
1717
} from '@codemirror/autocomplete';
18-
import { PostgreSQL, type SQLNamespace, sql as sqlLanguage } from '@codemirror/lang-sql';
18+
import { PostgreSQL, type SQLNamespace, schemaCompletionSource, sql as sqlLanguage } from '@codemirror/lang-sql';
1919
import { HighlightStyle, indentUnit, syntaxHighlighting, syntaxTree } from '@codemirror/language';
2020
import { EditorState, type Extension, Prec } from '@codemirror/state';
2121
import { EditorView, keymap } from '@codemirror/view';
@@ -192,12 +192,9 @@ function tableNamespace(table: TableRef): SQLNamespace {
192192
};
193193
}
194194

195-
// Builds the lang-sql completion schema from the loaded catalog tree: bare
196-
// table names → columns. Tables are deliberately NOT nested under their
197-
// catalog — Redpanda SQL (Oxla) addresses catalog tables with arrow notation
198-
// (`catalog=>table`), which catalogArrowSource below handles; dot-style
199-
// nesting would advertise syntax the server rejects. Bare entries still give
200-
// alias/column resolution (`FROM default_redpanda_catalog=>cars c` → `c.`).
195+
// Bare table name → columns. Powers alias/column resolution only
196+
// (schemaColumnSource); tables aren't nested under catalogs since Oxla uses
197+
// `catalog=>table` arrow notation, handled by catalogArrowSource.
201198
function buildSchema(catalogs: Catalog[]): SQLNamespace {
202199
const root: Record<string, SQLNamespace> = {};
203200
for (const catalog of catalogs) {
@@ -212,6 +209,20 @@ function buildSchema(catalogs: Catalog[]): SQLNamespace {
212209
return root;
213210
}
214211

212+
// Schema completion limited to dotted members (`c.` → columns); bare table
213+
// names are suppressed at the top level so catalogArrowSource owns them.
214+
function schemaColumnSource(catalogs: Catalog[]): (context: CompletionContext) => CompletionResult | null {
215+
const source = schemaCompletionSource({ dialect: PostgreSQL, schema: buildSchema(catalogs) });
216+
return (context) => {
217+
const result = source(context);
218+
if (!result || result instanceof Promise) {
219+
return null;
220+
}
221+
const dotted = context.state.sliceDoc(result.from - 1, result.from) === '.';
222+
return dotted ? result : null;
223+
};
224+
}
225+
215226
// Matches an identifier followed by `=>` or `.` and a partial table name,
216227
// anchored at the cursor: [, name, gap1, separator, gap2, quote, partial].
217228
const CATALOG_REF_RE = /([A-Za-z_][\w$]*)(\s*)(=>|\.)(\s*)("?)([\w$]*)$/;
@@ -415,7 +426,9 @@ export const SqlEditor = forwardRef<SqlEditorHandle, SqlEditorProps>(
415426
};
416427

417428
const extensions = useMemo(() => {
418-
const sqlSupport = sqlLanguage({ dialect: PostgreSQL, schema: buildSchema(catalogs), upperCaseKeywords: true });
429+
// No `schema` here — schemaColumnSource adds it back for dotted
430+
// completions only, avoiding bare table names at the top level.
431+
const sqlSupport = sqlLanguage({ dialect: PostgreSQL, upperCaseKeywords: true });
419432
return [
420433
// Prec.highest so Mod-Enter beats the default keymap's insertBlankLine.
421434
Prec.highest(
@@ -441,6 +454,7 @@ export const SqlEditor = forwardRef<SqlEditorHandle, SqlEditorProps>(
441454
),
442455
sqlSupport,
443456
sqlSupport.language.data.of({ autocomplete: catalogArrowSource(catalogs) }),
457+
sqlSupport.language.data.of({ autocomplete: schemaColumnSource(catalogs) }),
444458
isDark ? DARK_THEME : LIGHT_THEME,
445459
EditorView.updateListener.of((update) => {
446460
if (update.selectionSet) {

0 commit comments

Comments
 (0)