modified: frontend/src/components/SqlEditor.tsx
This commit is contained in:
@@ -96,24 +96,40 @@ function getFkSuggestions(
|
|||||||
tablesInQuery: TableInfo[],
|
tablesInQuery: TableInfo[],
|
||||||
schema: SchemaData,
|
schema: SchemaData,
|
||||||
monaco: Monaco,
|
monaco: Monaco,
|
||||||
range: any
|
range: any,
|
||||||
|
sql: string
|
||||||
): any[] {
|
): any[] {
|
||||||
const suggestions: any[] = [];
|
const suggestions: any[] = [];
|
||||||
|
const aliases = parseTableAliases(sql);
|
||||||
|
|
||||||
|
// Create reverse alias map: tableName -> alias (or tableName if no alias)
|
||||||
|
const tableToAlias = new Map<string, string>();
|
||||||
|
for (const [alias, tableName] of aliases) {
|
||||||
|
// Prefer shorter alias over table name
|
||||||
|
const existing = tableToAlias.get(tableName);
|
||||||
|
if (!existing || alias.length < existing.length) {
|
||||||
|
tableToAlias.set(tableName, alias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (const table of tablesInQuery) {
|
for (const table of tablesInQuery) {
|
||||||
for (const fk of table.foreign_keys) {
|
for (const fk of table.foreign_keys) {
|
||||||
// Find the referenced table
|
// Find the referenced table
|
||||||
const refTable = schema.tables.find(t => t.name === fk.references_table);
|
const refTable = schema.tables.find(t => t.name === fk.references_table);
|
||||||
if (refTable && tablesInQuery.some(t => t.name === refTable.name)) {
|
if (refTable && tablesInQuery.some(t => t.name === refTable.name)) {
|
||||||
const joinCondition = `${table.name}.${fk.column} = ${refTable.name}.${fk.references_column}`;
|
// Use alias if available, otherwise table name
|
||||||
|
const sourceAlias = tableToAlias.get(table.name) || table.name;
|
||||||
|
const targetAlias = tableToAlias.get(refTable.name) || refTable.name;
|
||||||
|
|
||||||
|
const joinCondition = `${sourceAlias}.${fk.column} = ${targetAlias}.${fk.references_column}`;
|
||||||
suggestions.push({
|
suggestions.push({
|
||||||
label: joinCondition,
|
label: joinCondition,
|
||||||
kind: monaco.languages.CompletionItemKind.Snippet,
|
kind: monaco.languages.CompletionItemKind.Snippet,
|
||||||
insertText: joinCondition,
|
insertText: joinCondition,
|
||||||
detail: `FK: ${table.name} → ${refTable.name}`,
|
detail: `FK: ${table.name}.${fk.column} → ${refTable.name}.${fk.references_column}`,
|
||||||
documentation: `Foreign key relationship`,
|
documentation: `Foreign key relationship between ${table.name} and ${refTable.name}`,
|
||||||
range,
|
range,
|
||||||
sortText: '0' + joinCondition, // Sort FK suggestions first
|
sortText: '0' + joinCondition,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -194,12 +210,16 @@ export default function SqlEditor({ value, onChange, databaseId, height }: SqlEd
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if we're after ON keyword (FK suggestions)
|
// Check if we're after ON keyword (FK suggestions)
|
||||||
const onMatch = textBeforeCursor.match(/\bON\s+$/i);
|
// Match: "ON ", "ON ", "on ", after JOIN ... ON
|
||||||
|
const fullText = model.getValue();
|
||||||
|
const onMatch = textBeforeCursor.match(/\bON\s*$/i) || textUntilPosition.match(/\bJOIN\s+\w+\s+\w*\s+ON\s*$/i);
|
||||||
if (onMatch && currentSchema) {
|
if (onMatch && currentSchema) {
|
||||||
const tablesInQuery = findTablesInQuery(textUntilPosition, currentSchema);
|
const tablesInQuery = findTablesInQuery(fullText, currentSchema);
|
||||||
const fkSuggestions = getFkSuggestions(tablesInQuery, currentSchema, monaco, range);
|
const fkSuggestions = getFkSuggestions(tablesInQuery, currentSchema, monaco, range, fullText);
|
||||||
|
// Add FK suggestions to the top, but also include other suggestions
|
||||||
if (fkSuggestions.length > 0) {
|
if (fkSuggestions.length > 0) {
|
||||||
return { suggestions: fkSuggestions };
|
suggestions = [...fkSuggestions, ...suggestions];
|
||||||
|
return { suggestions };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user