diff --git a/lib/features/workspace/generic_sql_workspace.dart b/lib/features/workspace/generic_sql_workspace.dart index ac85d73a..06da12fe 100644 --- a/lib/features/workspace/generic_sql_workspace.dart +++ b/lib/features/workspace/generic_sql_workspace.dart @@ -506,8 +506,15 @@ class GenericSqlWorkspaceState extends material.State { final Map _gridSchemaCache = {}; - static String _schemaCacheKey(String sql, List cols) => - '${cols.join('\u0001')}\u0000$sql'; + /// Table schemas are cached per (database, schema, table), so any query on a + /// table reuses the lookup, not only the same text. A query with no single + /// target table is not cached. + String? _schemaCacheKey(String sql) { + final target = SqlTableTargetExtractor.extract(sql); + if (target == null) return null; + return '$effectiveDatabase\u0001${target.schema ?? ''}\u0001' + '${target.tableName}'; + } static final _readQueryRegex = RegExp( r'^\s*(select|with|values|show|explain|pragma|describe)\b', @@ -725,8 +732,8 @@ class GenericSqlWorkspaceState extends material.State { // Rows show now. The table schema (keys, types, edit hint) follows: from // the cache, or from the delegate in the background. - final cacheKey = _schemaCacheKey(shownSql, cols); - final cached = _gridSchemaCache[cacheKey]; + final cacheKey = _schemaCacheKey(shownSql); + final cached = cacheKey == null ? null : _gridSchemaCache[cacheKey]; invalidatePane(session); setState(() { session.columns = cols; @@ -753,7 +760,11 @@ class GenericSqlWorkspaceState extends material.State { widget.delegate.resolveTableSchema(shownSql, cols).then( (gridSchema) { if (!mounted) return; - _gridSchemaCache[cacheKey] = gridSchema; + // A result with no columns gets no schema from the delegate, so + // it is not worth keeping for the table. + if (cacheKey != null && cols.isNotEmpty) { + _gridSchemaCache[cacheKey] = gridSchema; + } _applyGridSchema(session, shownSql, cols, outRows, gridSchema, result, scriptStatus); }, diff --git a/test/features/workspace/generic_sql_workspace_schema_test.dart b/test/features/workspace/generic_sql_workspace_schema_test.dart index 4d6f4174..b64e84ab 100644 --- a/test/features/workspace/generic_sql_workspace_schema_test.dart +++ b/test/features/workspace/generic_sql_workspace_schema_test.dart @@ -167,4 +167,26 @@ void main() { await runAndWait(tester, state); expect(delegate.probes, 1); }); + + testWidgets('another query on the same table reuses the lookup (#1171)', + (tester) async { + final delegate = _SchemaDelegate(); + final state = await pumpWorkspace(tester, delegate, 'select * from users'); + await runAndWait(tester, state); + state.activeSession.controller.text = 'select name from users'; + await runAndWait(tester, state); + expect(delegate.lookups.length, 1); + }); + + testWidgets('a DDL run makes the next query on the table look it up again', + (tester) async { + final delegate = _SchemaDelegate(); + final state = await pumpWorkspace(tester, delegate, 'select * from users'); + await runAndWait(tester, state); + state.activeSession.controller.text = 'alter table users add column age int'; + await runAndWait(tester, state); + state.activeSession.controller.text = 'select * from users'; + await runAndWait(tester, state); + expect(delegate.lookups.length, 3); + }); }