Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
30af284
perf(sqlite): inject LIMIT before materializing SQL results
ZhuchkaTriplesix Jul 27, 2026
5a4ffe0
Merge pull request #429 from QueryaHub/issue/415-sqlite-inject-limit
ZhuchkaTriplesix Jul 27, 2026
ca3b549
perf(export): stream DataExportService file saves to IOSink
ZhuchkaTriplesix Jul 27, 2026
847ce6c
Merge pull request #431 from QueryaHub/issue/417-export-stream
ZhuchkaTriplesix Jul 27, 2026
d27176a
perf(sql): clamp oversized LIMIT / FETCH for result caps
ZhuchkaTriplesix Jul 27, 2026
ef24a6d
perf(marketplace): stream SHA256 and file-decode zip archives
ZhuchkaTriplesix Jul 27, 2026
594ecaa
Merge pull request #432 from QueryaHub/issue/416-clamp-onto-dev
ZhuchkaTriplesix Jul 27, 2026
ce4af04
Merge pull request #433 from QueryaHub/issue/418-stream-zip-hash
ZhuchkaTriplesix Jul 27, 2026
bbc2777
perf(extensions): bound JSON-RPC NDJSON line size and pass query limit
ZhuchkaTriplesix Jul 27, 2026
7a1b719
perf(sdui): virtualize SduiTreeBuilder with flattened ListView
ZhuchkaTriplesix Jul 27, 2026
79e055c
perf(editor): honor kSyntaxHighlightIsolateThreshold
ZhuchkaTriplesix Jul 27, 2026
d9b9207
Merge pull request #434 from QueryaHub/issue/419-rpc-payload-bounds
ZhuchkaTriplesix Jul 27, 2026
357b508
Merge pull request #435 from QueryaHub/issue/420-sdui-virtualize
ZhuchkaTriplesix Jul 27, 2026
13417c8
Merge pull request #436 from QueryaHub/issue/424-syntax-highlight-thr…
ZhuchkaTriplesix Jul 27, 2026
8cd3786
perf(results): convert row cells with yielding instead of compute
ZhuchkaTriplesix Jul 27, 2026
e2e1aac
Merge pull request #437 from QueryaHub/issue/421-result-string-yield
ZhuchkaTriplesix Jul 27, 2026
46c5a52
perf(mysql): yield during table browse row→string conversion
ZhuchkaTriplesix Jul 27, 2026
6c510bf
perf(redis): pipeline TYPE/TTL for SCAN batches
ZhuchkaTriplesix Jul 27, 2026
cd110d1
perf(sandbox): bound stderr carry and scan newlines incrementally
ZhuchkaTriplesix Jul 27, 2026
d43dcf2
perf(storage): composite SQL history index and PK prune
ZhuchkaTriplesix Jul 27, 2026
cf13479
Merge pull request #439 from QueryaHub/issue/422-mysql-table-browse-y…
ZhuchkaTriplesix Jul 27, 2026
ea48ce0
perf(theme): incremental definition scan by mtime
ZhuchkaTriplesix Jul 27, 2026
51558dd
Merge pull request #440 from QueryaHub/issue/426-redis-pipeline-type-ttl
ZhuchkaTriplesix Jul 27, 2026
83436e0
Merge pull request #442 from QueryaHub/issue/428-sql-history-index-prune
ZhuchkaTriplesix Jul 27, 2026
a45e76b
Merge pull request #441 from QueryaHub/issue/427-sandbox-stderr-bound
ZhuchkaTriplesix Jul 27, 2026
16d9298
Merge pull request #443 from QueryaHub/issue/425-incremental-theme-scan
ZhuchkaTriplesix Jul 27, 2026
7867fea
perf(ui): 2D-virtualize VirtualResultGrid columns
ZhuchkaTriplesix Jul 27, 2026
4d77ecf
fix(ui): drop redundant foundation import in result grid
ZhuchkaTriplesix Jul 27, 2026
e46d527
Merge pull request #444 from QueryaHub/issue/423-result-grid-2d-virtu…
ZhuchkaTriplesix Jul 27, 2026
07ac20e
fix(ui): honor barrierDismissible Escape on showAppDialog
ZhuchkaTriplesix Jul 27, 2026
2cd9b6c
fix(ui): always pop DDL loading overlay
ZhuchkaTriplesix Jul 27, 2026
b5076a6
fix(ui): clear loading on stats/table early exits
ZhuchkaTriplesix Jul 27, 2026
bfa426a
fix(ui): tree errors, empty table state, scrollable toolbars
ZhuchkaTriplesix Jul 27, 2026
8166790
Merge pull request #458 from QueryaHub/issue/446-app-dialog-escape
ZhuchkaTriplesix Jul 27, 2026
67a6c36
Merge pull request #459 from QueryaHub/issue/447-ddl-overlay-pop
ZhuchkaTriplesix Jul 27, 2026
5073493
Merge pull request #460 from QueryaHub/issue/448-stats-table-loading-…
ZhuchkaTriplesix Jul 27, 2026
e6ad70e
fix(ui): SQL file toasts, chrome scale, Redis/Mongo empty states
ZhuchkaTriplesix Jul 27, 2026
1a8dbd1
Merge pull request #461 from QueryaHub/issue/449-450-451-ui-medium-batch
ZhuchkaTriplesix Jul 27, 2026
d1119eb
fix(ui): prefer const Text in Redis/Mongo empty states
ZhuchkaTriplesix Jul 27, 2026
99d162f
Merge pull request #462 from QueryaHub/issue/452-457-ui-remaining
ZhuchkaTriplesix Jul 27, 2026
44f6d3d
fix: address code-review follow-ups from epic #463
ZhuchkaTriplesix Jul 27, 2026
a3c3967
Merge pull request #469 from QueryaHub/issue/464-468-code-review-fixes
ZhuchkaTriplesix Jul 27, 2026
f5af240
chore(release): prepare pre-release 0.4.11-b
ZhuchkaTriplesix Jul 27, 2026
d44d753
Merge pull request #470 from QueryaHub/chore/release-0.4.11-b
ZhuchkaTriplesix Jul 27, 2026
ed31321
merge(main): resolve pubspec for 0.4.11-b release
ZhuchkaTriplesix Jul 27, 2026
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
24 changes: 22 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.4.11-a] - 2026-07-27
## [0.4.11-b] - 2026-07-27

Post-0.4.11 patch that **ships** security + Linux distro packaging (intended for 0.4.11-a), plus performance bounds (#414), UI reliability (#445), and code-review correctness (#463).

Post-0.4.11 patch: security review hardening (#395–#402) and remaining Linux distro packages (#386).
> Note: GitHub tag `0.4.11-a` was mistakenly placed on the same commit as `0.4.11`, so those binaries did not include the 0.4.11-a changelog. Treat **0.4.11-b** as the first patch after **0.4.11**.

### Added

Expand All @@ -26,6 +28,24 @@ Post-0.4.11 patch: security review hardening (#395–#402) and remaining Linux d
- **Sandbox OS consent (#395)** — fail-closed unsandboxed driver launch without OS wrapper (bubblewrap / consent dialog).
- **Sideload integrity UX (#402)** — local `.zip`/`.qext` install dialog with security notice and optional SHA256.

### Performance

- **SQL result caps (#415 / #416)** — SQLite injects `LIMIT` before materializing rows; Postgres clamps oversized `LIMIT` / `FETCH`.
- **Streaming I/O (#417 / #418)** — export writes stream to disk; marketplace SHA256 + zip extract avoid dual full-buffer copies.
- **RPC / SDUI bounds (#419 / #420)** — NDJSON line size caps; virtualized `SduiTreeBuilder`.
- **Hot-path yielding (#421 / #422)** — result cell string conversion and MySQL table browse yield to the UI isolate.
- **Editor / Redis / sandbox / storage (#424–#428)** — syntax-highlight threshold, Redis TYPE/TTL pipeline, bounded stderr, SQL history index/prune, incremental theme mtime scan.
- **VirtualResultGrid (#423)** — 2D column virtualization for wide result sets.

### Fixed

- **UI reliability (#445 / #446–#457)** — Escape on `showAppDialog`; DDL overlay always pops; stats/table loading clears on early exit; tree error + Retry; empty table state; scrollable toolbars; SQL open/save toasts; title-bar scale; Redis/Mongo empty banners; mounted guards; ResultsTab invariants.
- **Correctness follow-ups (#463 / #464–#468)** — `injectSqlLimit` skips string/dollar quotes; delete partial export files on failure; PG tree clears stale children on error; theme watcher queues in-flight refresh; Redis keys error banner clears on success.

## [0.4.11-a] - 2026-07-27

Prepared changelog for security (#395–#402) + Linux rpm/Flatpak/AUR (#386). **Tag/binaries were mistargeted** (same commit as `0.4.11`); content ships in **0.4.11-b**.

## [0.4.11] - 2026-07-27

Universal UI standard for drivers/extensions, shell UX hardening, Fluid QueryaMotion morphing, virtual grid/pool reliability, performance follow-ups, and dual-channel packaging (portable + installable).
Expand Down
5 changes: 3 additions & 2 deletions docs/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Living document for planned work. Not a commitment order; adjust as priorities change.

**GitHub Latest Release:** [0.4.11](https://github.com/QueryaHub/Querya-Desktop/releases/tag/0.4.11) (2026-07-27).
**Next patch:** **0.4.11-a** — security review (#395–#402) + Linux rpm/Flatpak/AUR (#386); tag pending.
**Next patch:** **0.4.11-b** — security + packaging (from mistagged 0.4.11-a) + perf (#414) + UI (#445) + code-review (#463); tag after merge to `main`.
**Next product release:** **0.5.0** — live Marketplace download and install — see below.

## Theme system
Expand All @@ -26,7 +26,8 @@ Living document for planned work. Not a commitment order; adjust as priorities c
- **Shipped in 0.4.9:** PostgreSQL SSL & connection reliability — see [CHANGELOG.md](../CHANGELOG.md).
- **Shipped in 0.4.10:** Sandboxed extension runtime (Block E), Plugin RPC bridge (Block C), SDUI form/tree builders, local `.zip`/`.qext` install, Registration/Activation for external database drivers (e.g. ClickHouse), in-app updater — see [CHANGELOG.md](../CHANGELOG.md).
- **Shipped in 0.4.11:** Universal UI / SDUI RPC expand, ExtensionTableView, universal export, MySQL/SQLite parity, shell UX (#339), Fluid QueryaMotion (#342) + perf follow-ups (#356), grid/pool/timeout fixes, dual-channel packaging (portable zip + AppImage / `.deb` / Windows setup) — [CHANGELOG.md](../CHANGELOG.md) `[0.4.11]`, [packaging.md](packaging.md), epic [#379](https://github.com/QueryaHub/Querya-Desktop/issues/379).
- **Pending 0.4.11-a:** security hardening (#395–#402), Linux `.rpm` / Flatpak / AUR (#386) — [CHANGELOG.md](../CHANGELOG.md) `[0.4.11-a]`.
- **0.4.11-a:** changelog prepared; GitHub tag mistargeted onto `0.4.11` — do not treat as a shipped patch.
- **Pending 0.4.11-b:** ships security (#395–#402), Linux `.rpm` / Flatpak / AUR (#386), perf (#414), UI reliability (#445), code-review fixes (#463) — [CHANGELOG.md](../CHANGELOG.md) `[0.4.11-b]`.
- **Planned 0.5.0:** Marketplace Launch — live download, `sha256` validation, install themes (and later DB drivers) from the network.

## Query history and favorites
Expand Down
2 changes: 2 additions & 0 deletions docs/security.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ Marketplace downloads, local extension sideload (`.zip` / `.qext`), and in-app u

Archives exceeding these bounds fail closed before files are written to disk. Path traversal checks remain in `archive_path_guard.dart`.

SHA-256 verification for marketplace/sideload streams the file (`sha256.bind(file.openRead())`, same helper as the updater) instead of hashing a full in-memory copy. Zip decode uses a file stream (`InputFileStream`) so the compressed payload is not held as a separate `List<int>` alongside the decoded archive; entry contents are cleared after each write.

## Extension driver OS sandbox

Process-sandbox database drivers launch inside OS-level isolation when available:
Expand Down
16 changes: 12 additions & 4 deletions docs/tz-block-c-rpc-bridge.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,15 @@ final result = await rpcClient.sendRequest('db.connect', credentialsMap);

---

## 4. Контракт Ошибок (Error Mapping)
Плагин должен возвращать ошибки согласно спецификации JSON-RPC. RPC Bridge должен уметь парсить эти ошибки и превращать их в понятные Dart-exceptions:
- Ошибка подключения (Timeout, Wrong Password) -> Показывается в UI в красном Snackbar.
- Синтаксическая ошибка SQL -> Выделяется красным в SQL редакторе.
## 5. Лимиты полезной нагрузки (NDJSON)

Каждый ответ — **одна JSON-строка** на `stdout` (newline-delimited). Хост (`JsonRpcStdioClient`) применяет:

| Лимит | Значение по умолчанию | Поведение |
|-------|----------------------|-----------|
| Макс. длина одной строки ответа | **32 MiB** UTF-8 | Fail closed: `JsonRpcPayloadTooLargeException`, все pending RPC завершаются ошибкой |
| Декод больших строк | **> 64 KiB** | `jsonDecode` уходит в isolate |

Для `db.query` хост всегда передаёт `params.limit` (Preferences → Max rows in results), чтобы драйвер обрезал результат **до** сериализации. Драйверы обязаны уважать `limit`.

Чанкованный / бинарный framing для очень больших выборок — follow-up; до него bound + `limit` обязательны.
9 changes: 9 additions & 0 deletions docs/user-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,13 @@ Preferences (except secrets) live in the same local SQLite file as connection me

High-level feature depth varies by database type. PostgreSQL, MySQL, and SQLite include rich object trees and SQL workspaces (with SQLite utilizing local `.db` files); Redis and MongoDB focus on data exploration and commands suitable for day-to-day development.

### Result row caps (SQL workspaces)

Preferences → **Max rows in results** caps how many rows the grid loads. For **PostgreSQL** and **SQLite**, ad-hoc `SELECT` / `WITH` / `VALUES` are bounded **before** execution:

- No author `LIMIT` → a `LIMIT` equal to the preference is injected.
- Author `LIMIT` / `LIMIT ALL` / `FETCH FIRST n ROWS ONLY` larger than the preference → clamped down to the preference (OFFSET kept when present).

Queries that already use a smaller `LIMIT`, and non-SELECT statements (`INSERT`, `PRAGMA`, …), are left unchanged. The UI may still truncate the displayed grid as a fallback. **MySQL** SQL workspace streams rows and stops at the cap client-side.

For troubleshooting build/run issues, see the main [README.md](../README.md).
55 changes: 4 additions & 51 deletions lib/core/database/postgres_sql.dart
Original file line number Diff line number Diff line change
@@ -1,19 +1,9 @@
// Helpers for ad-hoc SQL workspace (transactions, stripping comments).

/// Removes leading whitespace and `--` line comments (not `/* */`).
String stripLeadingWhitespaceAndLineComments(String sql) {
var s = sql.trimLeft();
while (true) {
if (s.isEmpty) return s;
if (s.startsWith('--')) {
final nl = s.indexOf('\n');
if (nl == -1) return '';
s = s.substring(nl + 1).trimLeft();
continue;
}
return s;
}
}
import 'sql_limit.dart';

export 'sql_limit.dart'
show injectSqlLimit, stripLeadingWhitespaceAndLineComments;

/// True if the first statement looks like explicit transaction control, so we
/// should not prepend `BEGIN` when autocommit is off.
Expand All @@ -36,40 +26,3 @@ bool shouldSkipImplicitBegin(String sql) {

return false;
}

/// Injects a `LIMIT` clause to a read-only query (SELECT, WITH, VALUES)
/// if it does not already contain a `LIMIT` clause.
String injectSqlLimit(String sql, int limit) {
final cleanSql = stripLeadingWhitespaceAndLineComments(sql);
final upper = cleanSql.toUpperCase();

final isSelect = upper.startsWith('SELECT') ||
upper.startsWith('WITH') ||
upper.startsWith('VALUES');

if (!isSelect) {
return sql;
}

// Check if it already has a LIMIT clause
final hasLimit = RegExp(r'\bLIMIT\b', caseSensitive: false).hasMatch(sql);
if (hasLimit) {
return sql;
}

// Strip trailing whitespace and semicolons to build the body
var body = sql.trimRight();
var suffix = '';

while (true) {
if (body.isEmpty) break;
if (body.endsWith(';')) {
body = body.substring(0, body.length - 1).trimRight();
suffix = ';$suffix';
continue;
}
break;
}

return '$body\nLIMIT $limit$suffix';
}
39 changes: 39 additions & 0 deletions lib/core/database/redis_connection.dart
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,45 @@ class RedisConnection {
return result is int ? result : int.tryParse(result.toString()) ?? -1;
}

/// Pipelined TYPE + TTL for a SCAN batch.
///
/// Writes all commands before awaiting replies (redis-dart FIFO parse
/// queue + optional Nagle via [Command.pipe_start]), so a batch of N keys
/// costs ~1 RTT instead of ~2N sequential round-trips.
Future<List<({String type, int ttl})>> typesAndTtls(List<String> keys) async {
if (keys.isEmpty) return const [];
if (!isConnected) {
throw StateError('Not connected to Redis');
}

final cmd = _command;
cmd?.pipe_start();
try {
final typeFutures = <Future<String>>[
for (final key in keys)
sendCommand(['TYPE', key]).then(
(v) => v?.toString() ?? 'none',
onError: (_) => 'unknown',
),
];
final ttlFutures = <Future<int>>[
for (final key in keys)
sendCommand(['TTL', key]).then(
(v) => v is int ? v : int.tryParse(v.toString()) ?? -1,
onError: (_) => -1,
),
];
final types = await Future.wait(typeFutures);
final ttls = await Future.wait(ttlFutures);
return [
for (var i = 0; i < keys.length; i++)
(type: types[i], ttl: ttls[i]),
];
} finally {
cmd?.pipe_end();
}
}

/// GET (string).
Future<String?> get(String key) async {
final result = await sendCommand(['GET', key]);
Expand Down
33 changes: 33 additions & 0 deletions lib/core/database/result_row_string_convert.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/// Converts SQL result cells to display strings without a second isolate copy.
///
/// Prefer this over [compute] for large matrices: shipping `List<List<Object?>>`
/// across isolates often costs more than `toString()` itself and roughly
/// doubles peak memory. Yielding every [yieldEvery] rows keeps the UI isolate
/// responsive for 10k+ row caps.
library;

const int kResultStringConvertYieldEvery = 250;

/// Maps null cells to `'NULL'` and others via [Object.toString].
String resultCellToDisplayString(Object? value) =>
value == null ? 'NULL' : value.toString();

/// Converts [rowValues] to string rows, yielding periodically.
Future<List<List<String>>> convertResultRowsToStringsYielding(
List<List<Object?>> rowValues, {
int yieldEvery = kResultStringConvertYieldEvery,
}) async {
if (rowValues.isEmpty) return const [];

final out = <List<String>>[];
for (var i = 0; i < rowValues.length; i++) {
final row = rowValues[i];
out.add([
for (final value in row) resultCellToDisplayString(value),
]);
if (yieldEvery > 0 && (i + 1) % yieldEvery == 0) {
await Future<void>.delayed(Duration.zero);
}
}
return out;
}
Loading
Loading