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
13 changes: 10 additions & 3 deletions lib/core/database/postgres_connection.dart
Original file line number Diff line number Diff line change
Expand Up @@ -743,16 +743,23 @@ class PostgresConnection with SshTunnelHost {
stats['version'] = ver.first[0] as String;

final settings = await _queued((c) => c.execute(
"SELECT name, setting FROM pg_settings "
"WHERE name IN ('max_connections','shared_buffers','work_mem',"
// lower(name): the setting is called "TimeZone", which a plain list of
// lower-case names never matched (the Timezone row stayed empty).
"SELECT lower(name), setting, unit FROM pg_settings "
"WHERE lower(name) IN ('max_connections','shared_buffers','work_mem',"
"'effective_cache_size','server_version','data_directory',"
"'listen_addresses','port','server_encoding','timezone')",
));
final settingsMap = <String, String>{};
final settingUnits = <String, String>{};
for (final row in settings) {
settingsMap[row[0] as String] = row[1] as String;
final name = row[0] as String;
settingsMap[name] = row[1] as String;
final unit = row[2];
if (unit is String && unit.isNotEmpty) settingUnits[name] = unit;
}
stats['settings'] = settingsMap;
stats['setting_units'] = settingUnits;

final activity = await _queued((c) => c.execute(
"SELECT count(*) AS total, "
Expand Down
29 changes: 29 additions & 0 deletions lib/features/postgresql/pg_setting_format.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/// A `pg_settings` value with its [unit] spelled out (#1822 run-through): the
/// raw `16384` of `shared_buffers` means 16384 blocks of 8 kB, so the card
/// shows `128 MB`. Values with an unknown or no unit come back as they are.
String formatPgSetting(String setting, String? unit) {
final n = int.tryParse(setting);
if (n == null || unit == null || unit.isEmpty) return setting;
final bytes = switch (unit) {
'B' => n,
'kB' => n * 1024,
'8kB' => n * 8 * 1024,
'MB' => n * 1024 * 1024,
'GB' => n * 1024 * 1024 * 1024,
_ => null,
};
if (bytes == null) return '$setting $unit';
const steps = [
(1024 * 1024 * 1024 * 1024, 'TB'),
(1024 * 1024 * 1024, 'GB'),
(1024 * 1024, 'MB'),
(1024, 'kB'),
];
for (final (size, label) in steps) {
if (bytes >= size) {
final v = bytes / size;
return '${v == v.roundToDouble() ? v.round() : v.toStringAsFixed(1)} $label';
}
}
return '$bytes B';
}
12 changes: 8 additions & 4 deletions lib/features/postgresql/postgres_stats_view.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:querya_desktop/features/postgresql/pg_setting_format.dart';
import 'package:flutter/widgets.dart';
import 'dart:math' as math;

Expand Down Expand Up @@ -510,17 +511,20 @@ class _PostgresStatsViewState extends material.State<PostgresStatsView>
material.Widget _serverSettingsCard(
material.BuildContext context, Map<String, dynamic> stats) {
final settings = stats['settings'] as Map<String, String>? ?? {};
final units = stats['setting_units'] as Map<String, String>? ?? {};
String setting(String name) => settings[name] == null
? '—'
: formatPgSetting(settings[name]!, units[name]);
return _card(
context,
'Server Settings',
material.Column(
mainAxisSize: material.MainAxisSize.min,
crossAxisAlignment: material.CrossAxisAlignment.stretch,
children: [
_row(context, 'Shared buffers', settings['shared_buffers'] ?? '—'),
_row(context, 'Work mem', settings['work_mem'] ?? '—'),
_row(context, 'Effective cache',
settings['effective_cache_size'] ?? '—'),
_row(context, 'Shared buffers', setting('shared_buffers')),
_row(context, 'Work mem', setting('work_mem')),
_row(context, 'Effective cache', setting('effective_cache_size')),
_row(context, 'Encoding', settings['server_encoding'] ?? '—'),
_row(context, 'Timezone', settings['timezone'] ?? '—'),
],
Expand Down
50 changes: 23 additions & 27 deletions lib/features/postgresql/postgres_table_toolbar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -89,18 +89,16 @@ class PostgresTableToolbar extends material.StatelessWidget {
),
material.Expanded(
flex: 2,
// Wraps in a narrow pane: a sideways scroll hid Prev / Next /
// Refresh and the mouse wheel does not scroll sideways.
child: material.LayoutBuilder(
builder: (context, constraints) {
return material.SingleChildScrollView(
scrollDirection: material.Axis.horizontal,
child: material.ConstrainedBox(
constraints: material.BoxConstraints(
minWidth: constraints.maxWidth,
),
child: material.Row(
mainAxisAlignment: material.MainAxisAlignment.end,
mainAxisSize: material.MainAxisSize.min,
children: [
builder: (context, constraints) => material.Wrap(
alignment: material.WrapAlignment.end,
runAlignment: material.WrapAlignment.end,
crossAxisAlignment: material.WrapCrossAlignment.center,
spacing: 6,
runSpacing: 6,
children: [
material.Container(
padding: const material.EdgeInsets.symmetric(
horizontal: 8,
Expand All @@ -118,26 +116,31 @@ class PostgresTableToolbar extends material.StatelessWidget {
),
),
),
const Gap(6),
if (editAction != null) ...[
editAction!,
const Gap(4),
],
if (editAction != null)
// Never wider than the pane: a long "N pending
// changes" row shrinks instead of overflowing.
material.ConstrainedBox(
constraints: material.BoxConstraints(
maxWidth: constraints.maxWidth),
child: material.FittedBox(
fit: material.BoxFit.scaleDown,
alignment: material.Alignment.centerRight,
child: editAction,
),
),
QueryaButton.secondary(
label: 'SQL',
icon: material.Icons.code_rounded,
onPressed: onOpenSql,
size: QueryaControlSize.sm,
),
const Gap(4),
QueryaButton.secondary(
label: 'Privileges',
icon: material.Icons.admin_panel_settings_rounded,
onPressed: onOpenPrivileges,
size: QueryaControlSize.sm,
),
if (isMaterializedView && !customSqlActive) ...[
const Gap(4),
QueryaButton.secondary(
label: 'Refresh MV',
icon: material.Icons.sync_rounded,
Expand All @@ -146,40 +149,33 @@ class PostgresTableToolbar extends material.StatelessWidget {
),
],
if (customSqlActive) ...[
const Gap(4),
QueryaButton.secondary(
label: 'Table',
icon: material.Icons.table_chart_rounded,
onPressed: loading ? null : onExitCustomMode,
size: QueryaControlSize.sm,
),
],
const Gap(4),
QueryaButton.secondary(
label: 'Prev',
icon: material.Icons.chevron_left_rounded,
onPressed: canGoPrevious ? onGoPrevious : null,
size: QueryaControlSize.sm,
),
const Gap(4),
QueryaButton.secondary(
label: 'Next',
icon: material.Icons.chevron_right_rounded,
onPressed: canGoNext ? onGoNext : null,
size: QueryaControlSize.sm,
),
const Gap(8),
QueryaButton.secondary(
label: 'Refresh',
icon: material.Icons.refresh_rounded,
onPressed: loading ? null : onRefresh,
size: QueryaControlSize.sm,
),
],
),
),
);
},
],
),
),
),
],
Expand Down
22 changes: 22 additions & 0 deletions test/features/postgresql/pg_setting_format_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:querya_desktop/features/postgresql/pg_setting_format.dart';

void main() {
test('block and kilobyte settings are shown as sizes', () {
expect(formatPgSetting('16384', '8kB'), '128 MB');
expect(formatPgSetting('524288', '8kB'), '4 GB');
expect(formatPgSetting('4096', 'kB'), '4 MB');
expect(formatPgSetting('64', 'kB'), '64 kB');
expect(formatPgSetting('1536', 'kB'), '1.5 MB');
});

test('no unit or a non-number stays as it is', () {
expect(formatPgSetting('100', null), '100');
expect(formatPgSetting('100', ''), '100');
expect(formatPgSetting('Etc/UTC', null), 'Etc/UTC');
});

test('a unit that is not memory is kept next to the value', () {
expect(formatPgSetting('5000', 'ms'), '5000 ms');
});
}
25 changes: 25 additions & 0 deletions test/features/postgresql/postgres_table_toolbar_layout_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -129,5 +129,30 @@ void main() {
);
});
});

testWidgets('every action stays on screen in a narrow pane (#1822 run)',
(tester) async {
await tester.binding.setSurfaceSize(const material.Size(700, 300));
addTearDown(() => tester.binding.setSurfaceSize(null));
await tester.pumpWidget(
MaterialApp(
theme: queryaDarkMaterialTheme,
darkTheme: queryaDarkMaterialTheme,
themeMode: material.ThemeMode.dark,
home: material.Scaffold(
body: material.Align(
alignment: material.Alignment.topLeft,
child: _toolbar(title: 'shop.orders'),
),
),
),
);
await tester.pump();

for (final label in ['SQL', 'Privileges', 'Prev', 'Next', 'Refresh']) {
final rect = tester.getRect(find.text(label));
expect(rect.right, lessThanOrEqualTo(700), reason: label);
}
});
});
}
Loading