Compare commits

...

1 Commits

Author SHA1 Message Date
Diego Pucci
3a21559acd fix: Sql Lab incorrect state request 2025-09-27 14:06:17 +03:00
4 changed files with 105 additions and 15 deletions

View File

@@ -518,7 +518,7 @@ export function syncQueryEditor(queryEditor) {
...localStorageQueries.map(query =>
migrateQuery(query.id, newQueryEditor.tabViewId, dispatch),
),
]);
]).then(() => newQueryEditor); // Return the newQueryEditor for chaining
})
.catch(() =>
dispatch(
@@ -649,7 +649,7 @@ export function loadQueryEditor(queryEditor) {
return { type: LOAD_QUERY_EDITOR, queryEditor };
}
export function setTables(tableSchemas) {
export function setTables(tableSchemas, queryEditorId = null) {
const tables = tableSchemas
.filter(tableSchema => tableSchema.description !== null)
.map(tableSchema => {
@@ -661,9 +661,12 @@ export function setTables(tableSchemas) {
indexes,
dataPreviewQueryId,
} = tableSchema.description;
// Use the provided queryEditorId if available, otherwise use tab_state_id
const tableQueryEditorId = queryEditorId || tableSchema.tab_state_id.toString();
return {
dbId: tableSchema.database_id,
queryEditorId: tableSchema.tab_state_id.toString(),
queryEditorId: tableQueryEditorId,
catalog: tableSchema.catalog,
schema: tableSchema.schema,
name: tableSchema.table,
@@ -706,7 +709,8 @@ export function fetchQueryEditor(queryEditor, displayLimit) {
hideLeftBar: json.hide_left_bar,
};
dispatch(loadQueryEditor(loadedQueryEditor));
dispatch(setTables(json.table_schemas || []));
// Pass the queryEditor.id to setTables to ensure correct queryEditorId
dispatch(setTables(json.table_schemas || [], loadedQueryEditor.id));
if (json.latest_query && json.latest_query.resultsKey) {
dispatch(fetchQueryResults(json.latest_query, displayLimit));
}
@@ -953,13 +957,17 @@ export function mergeTable(table, query, prepend) {
export function addTable(queryEditor, tableName, catalogName, schemaName) {
return function (dispatch, getState) {
const { dbId } = getUpToDateQuery(getState(), queryEditor, queryEditor.id);
// Always use the actual queryEditor.id for consistency
// The syncTable function will handle converting to tabViewId when syncing to backend
const table = {
dbId,
queryEditorId: queryEditor.tabViewId ?? queryEditor.id,
queryEditorId: queryEditor.id,
catalog: catalogName,
schema: schemaName,
name: tableName,
};
dispatch(
mergeTable({
...table,
@@ -1016,13 +1024,54 @@ export function runTablePreviewQuery(newTable, runPreviewOnly) {
return Promise.resolve();
};
}
export function syncTable(table, tableMetadata) {
return function (dispatch) {
return function (dispatch, getState) {
// Get the current queryEditor to use its tabViewId if available
const { queryEditors } = getState().sqlLab;
const queryEditor = queryEditors.find(qe => qe.id === table.queryEditorId);
// If queryEditor is still in localStorage (not synced), wait for sync first
if (queryEditor?.inLocalStorage) {
return dispatch(syncQueryEditor(queryEditor)).then((syncedEditor) => {
// After syncing, update the table with the new queryEditorId
const updatedTable = {
...table,
queryEditorId: syncedEditor?.tabViewId || queryEditor.tabViewId || table.queryEditorId
};
return dispatch(syncTable(updatedTable, tableMetadata));
});
}
// Use tabViewId if available, otherwise use the current queryEditorId
const correctQueryEditorId = queryEditor?.tabViewId || table.queryEditorId;
// Only sync to backend if we have a valid integer tabViewId
const canSyncToBackend = Boolean(queryEditor?.tabViewId && Number.isInteger(queryEditor.tabViewId));
if (!canSyncToBackend) {
// If we can't sync to backend, store in localStorage as fallback
dispatch(
mergeTable({
...table,
...tableMetadata,
expanded: true,
initialized: true,
inLocalStorage: true, // Mark as stored in localStorage
}),
);
return Promise.resolve();
}
const sync = isFeatureEnabled(FeatureFlag.SqllabBackendPersistence)
? SupersetClient.post({
endpoint: encodeURI('/tableschemaview/'),
postPayload: { table: { ...tableMetadata, ...table } },
postPayload: {
table: {
...tableMetadata,
...table,
queryEditorId: correctQueryEditorId
}
},
})
: Promise.resolve({ json: { id: table.id } });

View File

@@ -74,11 +74,6 @@ const SqlEditorLeftBar = ({
queryEditorId,
height = 500,
}: SqlEditorLeftBarProps) => {
const allSelectedTables = useSelector<SqlLabRootState, Table[]>(
({ sqlLab }) =>
sqlLab.tables.filter(table => table.queryEditorId === queryEditorId),
shallowEqual,
);
const dispatch = useDispatch();
const queryEditor = useQueryEditor(queryEditorId, [
'dbId',
@@ -87,6 +82,32 @@ const SqlEditorLeftBar = ({
'tabViewId',
]);
const allSelectedTables = useSelector<SqlLabRootState, Table[]>(
({ sqlLab }) => {
// Find the current queryEditor to get its tabViewId
const currentQueryEditor = sqlLab.queryEditors.find(
qe => qe.id === queryEditorId
);
const tabViewId = currentQueryEditor?.tabViewId;
const filtered = sqlLab.tables.filter(table => {
// Match tables that belong to this queryEditor
// Handle both cases: when queryEditor has a tabViewId (synced) or not (unsaved)
const matchesCurrentId = table.queryEditorId === queryEditorId;
const matchesTabViewId = tabViewId && (
table.queryEditorId === tabViewId ||
table.queryEditorId === String(tabViewId)
);
// Also check if the table's queryEditorId matches the tabViewId as a string
const matchesTabViewIdString = tabViewId && table.queryEditorId === String(tabViewId);
return matchesCurrentId || matchesTabViewId || matchesTabViewIdString;
});
return filtered;
},
shallowEqual,
);
const [_emptyResultsWithSearch, setEmptyResultsWithSearch] = useState(false);
const [userSelectedDb, setUserSelected] = useState<DatabaseObject | null>(
null,
@@ -151,6 +172,7 @@ const SqlEditorLeftBar = ({
});
tablesToAdd.forEach(tableName => {
// Use the actual queryEditor object with its current id
dispatch(addTable(queryEditor, tableName, catalogName, schemaName));
});

View File

@@ -125,9 +125,11 @@ export default function getInitialState({
.filter(tableSchema => tableSchema.description !== null)
.forEach(tableSchema => {
const { dataPreviewQueryId, ...persistData } = tableSchema.description;
// Use the active tab ID (which corresponds to the queryEditor.id) for consistency
const queryEditorId = activeTab.id.toString();
const table = {
dbId: tableSchema.database_id ?? 0,
queryEditorId: tableSchema.tab_state_id.toString(),
queryEditorId, // Use the activeTab.id which corresponds to queryEditor.id
catalog: tableSchema.catalog,
schema: tableSchema.schema,
name: tableSchema.table,

View File

@@ -480,12 +480,29 @@ export default function sqlLabReducer(state = {}, action) {
// continue regardless of error
}
// replace localStorage query editor with the server backed one
return alterInArr(
let newState = alterInArr(
state,
'queryEditors',
action.oldQueryEditor,
action.newQueryEditor,
);
// Update queryEditorId for all tables that reference the old queryEditor
const updatedTables = newState.tables.map(table => {
if (table.queryEditorId === action.oldQueryEditor.id) {
const newQueryEditorId = action.newQueryEditor.tabViewId || action.newQueryEditor.id;
return {
...table,
queryEditorId: newQueryEditorId,
};
}
return table;
});
return {
...newState,
tables: updatedTables,
};
},
[actions.MIGRATE_TABLE]() {
try {