Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/features/connections/connections_panel.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:querya_desktop/core/util/log_ignored.dart';
import 'package:flutter/widgets.dart';
import 'dart:async' show Timer;

Expand Down
3 changes: 2 additions & 1 deletion lib/features/connections/connections_panel_pg_tree.dart
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,8 @@ class _PgSchemaNodeState extends State<_PgSchemaNode> {
List<String> matviews = [];
try {
matviews = await conn.listMaterializedViews(schema: widget.schemaName);
} catch (_) {
} catch (e, st) {
logIgnored('PgTree.materializedViews', e, st);
// pg_matviews / permissions may fail on some servers; keep tree usable.
}
final functions = await conn.listFunctions(schema: widget.schemaName);
Expand Down
4 changes: 3 additions & 1 deletion lib/features/connections/extension_connection_form.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:querya_desktop/core/util/log_ignored.dart';
import 'package:querya_desktop/shared/report_error.dart';
import 'package:querya_desktop/core/database/database_error_mapper.dart';
import 'package:flutter/widgets.dart';
Expand Down Expand Up @@ -327,7 +328,8 @@ Map<String, Object?> _sduiInitialValuesFromConnection(ConnectionRow row) {
values[key] = entry.value;
}
}
} catch (_) {
} catch (e, st) {
logIgnored('ExtensionConnectionForm.driverOptions', e, st);
// Ignore malformed driverOptions; host fields still apply.
}
}
Expand Down
11 changes: 10 additions & 1 deletion lib/features/connections/sqlite_connection_form.dart
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,16 @@ class _SqliteConnectionFormContentState
if (_nameController.text.isEmpty) {
_nameController.text = p.basenameWithoutExtension(path);
}
} catch (_) {}
} catch (e, st) {
if (!mounted) return;
reportError(
context,
e,
stack: st,
where: 'SqliteConnectionForm.pickFile',
action: 'The file could not be chosen',
);
}
}

Future<void> _testConnection() async {
Expand Down
4 changes: 3 additions & 1 deletion lib/features/erd/erd_view.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:querya_desktop/core/util/log_ignored.dart';
import 'dart:async';
import 'dart:math' show max;
import 'dart:typed_data';
Expand Down Expand Up @@ -616,7 +617,8 @@ class _ErdViewState extends material.State<ErdView>
_captureCurrent();
try {
await widget.layoutStore!.write(widget.layoutKey!, _c.toStore());
} catch (_) {
} catch (e, st) {
logIgnored('ErdView.saveLayout', e, st);
// Keeping the layout is a convenience: a failed write is not an error
// the user can act on.
}
Expand Down
5 changes: 4 additions & 1 deletion lib/features/extensions/extension_table_view.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:querya_desktop/core/util/log_ignored.dart';
import 'package:querya_desktop/shared/report_error.dart';
import 'package:querya_desktop/core/database/database_error_mapper.dart';
import 'package:flutter/widgets.dart';
Expand Down Expand Up @@ -125,7 +126,9 @@ class ExtensionTableDataDelegate extends TableDataMutationDelegate {
if (countResult.rows.isNotEmpty && countResult.rows.first.isNotEmpty) {
totalRows = int.tryParse(countResult.rows.first.first);
}
} catch (_) {}
} catch (e, st) {
logIgnored('ExtensionTableView.count fallback', e, st);
}
}
}

Expand Down
14 changes: 13 additions & 1 deletion lib/features/main_screen/main_screen_dialogs.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:querya_desktop/shared/report_error.dart';
import 'package:flutter/widgets.dart';
import 'dart:async';

Expand Down Expand Up @@ -140,7 +141,18 @@ class MainScreenDialogs {
await connectionsPanelKey.currentState?.reloadConnectionsFromDb();
workspace.value = workspace.value.selectConnection(demoConn);
nav.onSqliteOpenSqlWorkspace(demoConn);
} catch (_) {}
} catch (e, st) {
// A silent nothing after the user asked for the demo reads as a dead
// button.
if (!_mounted()) return;
reportError(
_context(),
e,
stack: st,
where: 'MainScreenDialogs.onLaunchDemoPlayground',
action: 'The demo playground could not be opened',
);
}
}

Future<void> handlePendingFileLaunch() async {
Expand Down
5 changes: 4 additions & 1 deletion lib/features/mongodb/mongo_databases_view.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:querya_desktop/core/util/log_ignored.dart';
import 'package:querya_desktop/shared/report_error.dart';
import 'package:querya_desktop/core/database/database_error_mapper.dart';
import 'package:flutter/widgets.dart';
Expand Down Expand Up @@ -116,7 +117,9 @@ class _MongoDatabasesViewState extends State<MongoDatabasesView> {
'admin',
{'listDatabases': 1},
);
} catch (_) {}
} catch (e, st) {
logIgnored('MongoDatabasesView.listDatabases sizes', e, st);
}

final dbList =
dbListResult?['databases'] as List<dynamic>? ?? <dynamic>[];
Expand Down
7 changes: 5 additions & 2 deletions lib/features/mongodb/mongo_documents_view.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:querya_desktop/core/util/log_ignored.dart';
import 'package:querya_desktop/shared/report_error.dart';
import 'package:querya_desktop/core/database/database_error_mapper.dart';
import 'package:flutter/widgets.dart';
Expand Down Expand Up @@ -138,7 +139,8 @@ class _MongoDocumentsViewState extends material.State<MongoDocumentsView> {
_viewMode = MongoDocumentsViewMode.fromString(saved);
});
}
} catch (_) {
} catch (e, st) {
logIgnored('MongoDocumentsView.viewMode', e, st);
// Keep current mode
}
}
Expand Down Expand Up @@ -227,7 +229,8 @@ class _MongoDocumentsViewState extends material.State<MongoDocumentsView> {
);
if (!mounted || seq != _loadSeq) return;
setState(() => _totalCount = count);
} catch (_) {
} catch (e, st) {
logIgnored('MongoDocumentsView.count', e, st);
// Keep the "maybe more" pagination.
}
}
Expand Down
4 changes: 3 additions & 1 deletion lib/features/mongodb/mongo_query_workspace.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:querya_desktop/core/util/log_ignored.dart';
import 'package:flutter/widgets.dart';
import 'dart:async' show unawaited;

Expand Down Expand Up @@ -203,7 +204,8 @@ class _MongoQueryWorkspaceState extends material.State<MongoQueryWorkspace> {
databaseName: widget.database,
sqlText: query,
);
} catch (_) {
} catch (e, st) {
logIgnored('MongoQueryWorkspace.history', e, st);
// Best-effort history logging
}
}
Expand Down
9 changes: 7 additions & 2 deletions lib/features/redis/redis_cli_workspace.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:querya_desktop/core/util/log_ignored.dart';
import 'package:flutter/widgets.dart';
import 'dart:async' show unawaited;
import 'dart:convert';
Expand Down Expand Up @@ -287,7 +288,9 @@ class _RedisCliWorkspaceState extends material.State<RedisCliWorkspace> {
});
}
}
} catch (_) {}
} catch (e, st) {
logIgnored('RedisCliWorkspace.loadHistory', e, st);
}
}

Future<void> _saveHistory() async {
Expand All @@ -299,7 +302,9 @@ class _RedisCliWorkspaceState extends material.State<RedisCliWorkspace> {
_historyStorageKey,
jsonEncode(capped),
);
} catch (_) {}
} catch (e, st) {
logIgnored('RedisCliWorkspace.saveHistory', e, st);
}
}

@override
Expand Down
5 changes: 4 additions & 1 deletion lib/features/redis/redis_key_editor.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:querya_desktop/core/util/log_ignored.dart';
import 'package:querya_desktop/shared/report_error.dart';
import 'package:querya_desktop/core/database/database_error_mapper.dart';
import 'package:flutter/widgets.dart';
Expand Down Expand Up @@ -520,7 +521,9 @@ class _RedisKeyEditorState extends material.State<RedisKeyEditor> {
} catch (e) {
try {
await widget.connection.lrem(_cmdKey, 1, sentinel);
} catch (_) {}
} catch (cleanupError, cleanupStack) {
logIgnored('RedisKeyEditor.cleanup sentinel', cleanupError, cleanupStack);
}
rethrow;
}
await _load();
Expand Down
9 changes: 7 additions & 2 deletions lib/features/settings/preferences_mcp_section.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:querya_desktop/core/util/log_ignored.dart';
import 'package:querya_desktop/shared/report_error.dart';
import 'package:flutter/widgets.dart';
import 'dart:async' show StreamSubscription, unawaited;
Expand Down Expand Up @@ -89,7 +90,9 @@ class PreferencesMcpSectionState extends material.State<PreferencesMcpSection> {
try {
activity = await (widget.loadActivity ??
() => LocalDb.instance.listMcpActivity(limit: 50))();
} catch (_) {}
} catch (e, st) {
logIgnored('PreferencesMcpSection.load activity', e, st);
}
if (!mounted) return;
setState(() {
_enabled = enabled;
Expand All @@ -112,7 +115,9 @@ class PreferencesMcpSectionState extends material.State<PreferencesMcpSection> {
final list = await (widget.loadActivity ??
() => LocalDb.instance.listMcpActivity(limit: 50))();
if (mounted) setState(() => _activity = list);
} catch (_) {}
} catch (e, st) {
logIgnored('PreferencesMcpSection.loadActivity', e, st);
}
}

Future<void> _setEnabled(bool v) async {
Expand Down
5 changes: 4 additions & 1 deletion lib/features/sqlite/sqlite_overview_tab.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:querya_desktop/core/util/log_ignored.dart';
import 'package:flutter/widgets.dart';
import 'dart:io';
import 'dart:math' as math;
Expand Down Expand Up @@ -129,7 +130,9 @@ class _SqliteOverviewTabState extends material.State<SqliteOverviewTab> {
return f.lengthSync();
}
}
} catch (_) {}
} catch (e, st) {
logIgnored('SqliteOverviewTab.fileSize', e, st);
}
return 0;
}

Expand Down
5 changes: 4 additions & 1 deletion lib/features/sqlite/sqlite_sql_workspace.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:querya_desktop/core/util/log_ignored.dart';
import 'dart:async';

import 'package:flutter/material.dart' as material;
Expand Down Expand Up @@ -186,7 +187,9 @@ class SqliteSqlExecutionDelegate extends BaseSqlExecutionDelegate {
[target.tableName],
);
isView = kind.isNotEmpty && kind.first['type'] == 'view';
} catch (_) {}
} catch (e, st) {
logIgnored('SqliteSqlWorkspace.isView probe', e, st);
}

return SqlResultGridSchema.fromLoad(
await loadTableViewSchema(
Expand Down
15 changes: 12 additions & 3 deletions lib/shared/services/data_export_service.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'package:querya_desktop/core/util/querya_log.dart';
import 'package:querya_desktop/core/util/log_ignored.dart';
import 'dart:convert';
import 'dart:io';
import 'dart:isolate';
Expand Down Expand Up @@ -459,16 +461,23 @@ class DataExportService {
await sink.close();
sink = null;
return SaveExportOutcome.written;
} on Object {
} on Object catch (error, stack) {
// The caller gets the outcome; the reason goes to the log.
QueryaLog.instance.error('DataExportService', 'export to a file failed',
error: error, stack: stack);
try {
await sink?.close();
} catch (_) {}
} catch (e, st) {
logIgnored('DataExportService.closeSink', e, st);
}
try {
final partial = File(path);
if (await partial.exists()) {
await partial.delete();
}
} catch (_) {}
} catch (e, st) {
logIgnored('DataExportService.deletePartial', e, st);
}
return SaveExportOutcome.error;
}
}
Expand Down
54 changes: 54 additions & 0 deletions test/guards/no_silent_catch_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import 'dart:io';

import 'package:flutter_test/flutter_test.dart';

/// Error-handling epic (#1764, #1763): a `catch` in the UI layer must do
/// something with the error: `logIgnored`, the reporting helpers, a rethrow or
/// a real fallback. An empty block (or one that holds only a comment) hides a
/// failure from both the user and the log.
///
/// The files below keep a few empty blocks on purpose: they try to parse a
/// value in a second form and fall back, so the failure is the control flow.
/// The number is exact, so removing one of them has to lower it here too.
void main() {
const allowed = {
'lib/features/mongodb/mongo_field_codec.dart': 1,
'lib/features/mongodb/mql_parser.dart': 1,
'lib/features/workspace/data_grid_value_panel.dart': 6,
};

final emptyCatch = RegExp(
r'catch\s*\([^)]*\)\s*\{(?:\s|//[^\n]*|/\*[\s\S]*?\*/)*\}',
);

String norm(File f) => f.path.replaceAll(r'\', '/');

test('lib/features and lib/shared have no empty catch blocks', () {
final counts = <String, int>{};
for (final dir in ['lib/features', 'lib/shared']) {
for (final e in Directory(dir).listSync(recursive: true)) {
if (e is! File || !e.path.endsWith('.dart')) continue;
final n = emptyCatch.allMatches(e.readAsStringSync()).length;
if (n > 0) counts[norm(e)] = n;
}
}

final problems = <String>[];
for (final entry in counts.entries) {
final expected = allowed[entry.key] ?? 0;
if (entry.value > expected) {
problems.add('${entry.key}: ${entry.value} empty catch blocks '
'(allowed $expected): call logIgnored(where, e, st) or report it');
}
}
for (final entry in allowed.entries) {
final actual = counts[entry.key] ?? 0;
if (actual < entry.value) {
problems.add('${entry.key}: ${entry.value} allowed but only $actual '
'left: lower the number in this test');
}
}

expect(problems, isEmpty, reason: problems.join('\n'));
});
}
Loading