Skip to content

Commit 5b208c5

Browse files
add table and field info for tables in the sql query to user context (#290)
1 parent 7a8acc0 commit 5b208c5

File tree

3 files changed

+30
-12
lines changed

3 files changed

+30
-12
lines changed

apps/src/metabase/helpers/DOMToState.ts

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { getParsedIframeInfo, RPCs } from 'web'
2-
import { getTablesWithFields } from './getDatabaseSchema';
3-
import { getAllRelevantModelsForSelectedDb, getDatabaseInfo, getDatabases } from './metabaseAPIHelpers';
2+
import { getAllRelevantTablesForSelectedDb, getRelevantTablesForSelectedDb, getTablesWithFields, validateTablesInDB } from './getDatabaseSchema';
3+
import { getAllRelevantModelsForSelectedDb, getDatabaseInfo, getDatabases, getTableData } from './metabaseAPIHelpers';
44
import { getAndFormatOutputTable, getSqlErrorMessage } from './operations';
55
import { isDashboardPageUrl } from './dashboard/util';
66
import { DashboardInfo } from './dashboard/types';
@@ -106,7 +106,8 @@ export { type MetabasePageType } from '../defaultState'
106106

107107
export type MetabaseAppState = MetabaseAppStateSQLEditor | MetabaseAppStateDashboard | MetabaseSemanticQueryAppState | MetabaseAppStateMBQLEditor;
108108

109-
async function getRelevantEntitiesWithFields(sqlQuery: string): Promise<MetabaseTableOrModel[]> {
109+
// no need to fetch fields since we don't want that in limited entities
110+
async function getLimitedEntities(sqlQuery: string): Promise<MetabaseTableOrModel[]> {
110111
const appSettings = RPCs.getAppSettings();
111112

112113
// Early return if conditions not met
@@ -143,7 +144,6 @@ async function getRelevantEntitiesWithFields(sqlQuery: string): Promise<Metabase
143144

144145
const allModels = dbId ? await getAllRelevantModelsForSelectedDb(dbId) : [];
145146
const relevantModels = await getSelectedAndRelevantModels(sqlQuery || "", appSettings.selectedModels, allModels);
146-
const relevantModelsWithFields = await getModelsWithFields(relevantModels);
147147

148148
// Transform and combine tables and models with type annotations
149149
const relevantTablesWithFieldsAndType: MetabaseTableOrModel[] = relevantTablesWithFields.map(table => ({
@@ -154,7 +154,7 @@ async function getRelevantEntitiesWithFields(sqlQuery: string): Promise<Metabase
154154
description: table.description,
155155
}));
156156

157-
const relevantModelsWithFieldsAndType: MetabaseTableOrModel[] = relevantModelsWithFields.map(model => ({
157+
const relevantModelsWithFieldsAndType: MetabaseTableOrModel[] = relevantModels.map(model => ({
158158
type: 'model',
159159
id: model.modelId || 0,
160160
name: model.name,
@@ -174,13 +174,29 @@ export async function convertDOMtoStateSQLQueryV2() : Promise<MetabaseAppStateSQ
174174
const currentCard = processCard(currentCardRaw);
175175
const metabaseOrigin = new URL(metabaseUrl).origin;
176176
const isEmbedded = getParsedIframeInfo().isEmbedded
177-
const limitedEntities = await getRelevantEntitiesWithFields(
178-
get(currentCard, 'dataset_query.native.query', '') || ''
179-
);
177+
const sqlQuery = get(currentCard, 'dataset_query.native.query', '') || ''
178+
const limitedEntities = await getLimitedEntities(sqlQuery);
180179
const dbId = await getSelectedDbId();
181180
const selectedDatabaseInfo = dbId ? await getDatabaseInfo(dbId) : undefined;
182181
const sqlErrorMessage = await getSqlErrorMessage();
183-
182+
// add tables in the sql as relevant tables, after fetching their fields
183+
let relevantTablesWithFields: FormattedTable[] = []
184+
{
185+
const dbTables = await getAllRelevantTablesForSelectedDb(dbId || 0)
186+
const sqlTables = await getTablesFromSqlRegex(sqlQuery)
187+
const defaultSchema = selectedDatabaseInfo?.default_schema
188+
// Apply default schema to tables if needed
189+
if (defaultSchema) {
190+
sqlTables.forEach((table) => {
191+
if (table.schema === undefined || table.schema === '') {
192+
table.schema = defaultSchema;
193+
}
194+
});
195+
}
196+
const validSqlTables = validateTablesInDB(sqlTables, dbTables, defaultSchema)
197+
const sqlTablesWithFields = (await Promise.all(validSqlTables.map(table => getTableData(table.id)))).filter(table => table !== "missing")
198+
relevantTablesWithFields = sqlTablesWithFields
199+
}
184200
return {
185201
type: MetabaseAppStateType.SQLEditor,
186202
version: '2',
@@ -192,7 +208,8 @@ export async function convertDOMtoStateSQLQueryV2() : Promise<MetabaseAppStateSQ
192208
outputMarkdown,
193209
parameterValues,
194210
selectedDatabaseInfo,
195-
sqlErrorMessage
211+
sqlErrorMessage,
212+
relevantTablesWithFields
196213
}
197214
}
198215

apps/src/metabase/helpers/analystModeTypes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export interface MetabaseAppStateSQLEditorV2 extends MetabaseAppStateBase {
2828
outputMarkdown: string
2929
parameterValues: ParameterValues
3030
sqlErrorMessage?: string
31+
relevantTablesWithFields?: FormattedTable[]
3132
}
3233

3334
export interface MetabaseAppStateDashboardV2 extends MetabaseAppStateBase {

apps/src/metabase/helpers/getDatabaseSchema.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ function lowerAndDefaultSchemaAndDedupe(tables: TableAndSchema[]): TableAndSchem
3737
return dedupeAndCountTables(lowered);
3838
}
3939

40-
const validateTablesInDB = (tables: TableAndSchema[], allDBTables: FormattedTable[], default_schema?: string) => {
40+
export const validateTablesInDB = (tables: TableAndSchema[], allDBTables: FormattedTable[], default_schema?: string) => {
4141
const allTablesAsMap = _.fromPairs(allDBTables.map(tableInfo => [getTableKey(tableInfo), tableInfo]));
4242
if (default_schema) {
4343
tables = tables.map(tableInfo => {
@@ -66,7 +66,7 @@ const addTableJoins = (tables: FormattedTable[], tableMap: Record<number | strin
6666
})
6767
}
6868

69-
const getAllRelevantTablesForSelectedDb = async (dbId: number): Promise<FormattedTable[]> => {
69+
export const getAllRelevantTablesForSelectedDb = async (dbId: number): Promise<FormattedTable[]> => {
7070
const [userTables, {tables: allDBTables, default_schema}] = await Promise.all([
7171
getUserTables(dbId),
7272
handlePromise(getDatabaseTablesAndModelsWithoutFields(dbId), "Failed to get database tables", {

0 commit comments

Comments
 (0)