Skip to content

Release 1.0.2 - #77

Merged
ZhuchkaTriplesix merged 35 commits into
mainfrom
release/1.0.2
Sep 29, 2026
Merged

ZhuchkaTriplesix merged 35 commits into
mainfrom
release/1.0.2

Conversation

@ZhuchkaTriplesix

Copy link
Copy Markdown
Member

Описание

Релиз 1.0.2: слияние dev в main. Версия поднята до 1.0.2 (Cargo.toml, Cargo.lock, manifest.json, README).

Состав

Связанные Issue

Closes #45, Closes #46, Closes #47, Closes #48, Closes #49, Closes #50, Closes #51, Closes #52, Closes #53, Closes #54, Closes #55, Closes #56, Closes #57, Closes #58, Closes #59, Closes #60

Чеклист

  • cargo fmt / clippy -D warnings
  • cargo test (114 passed)
  • После merge: тег v1.0.2 на main (запускает Release workflow)

ZhuchkaTriplesix and others added 30 commits July 14, 2026 12:04
…tObjectMetadata, getSystemMetrics, killQuery)
fix(driver): fix URL and ssl mapping for connections
…extension

fix(packaging): handle Windows binary extension (.exe) in manifest.json and packaging script
…desktop

feat(manifest): expand engines.querya_desktop compatibility range
fix(security): isolate temporary scratch directory per user and restrict permissions
…ents

nodeId values (e.g. "table.analytics.events") were split naively on '.'
in db.expandTreeNode, sdui.contextActions and db.getObjectMetadata.
ClickHouse allows literal '.' inside backtick-quoted database/table
names and inside partition values, so such an identifier misaligned
every downstream segment once split.

Add src/utils/node_id.rs with encode_id_segment/decode_id_segment,
escaping '.' and '~' within each dynamic segment before it is joined
into a nodeId, and split_node_id to reverse it. Wire it into
src/sdui/tree.rs (node construction), src/rpc/handlers/schema.rs and
src/sdui/actions.rs (node parsing). Closes #56
fix(schema): implement robust delimiter escaping for nodeId path segments
…ats scalar queries

handle_get_server_stats and handle_get_object_metadata requested
SELECT version(), SELECT uptime() and SHOW CREATE TABLE with FORMAT
JSONCompactEachRow (a single data line, no headers), then parsed the
response with parse_compact_output, which unconditionally expects
FORMAT JSONCompactEachRowWithNamesAndTypes (names line + types line +
data lines). Against a real ClickHouse server this always failed to
parse, silently falling back to "ClickHouse"/0/"" defaults.

Switch all three queries to FORMAT JSONCompactEachRowWithNamesAndTypes
so parse_compact_output can read them, and update the version/uptime
network-error fallbacks to the same shape. Closes #57
…alar-queries

fix(schema): fix format mismatch in getObjectMetadata and getServerStats scalar queries
…semicolons in tabular query detection

is_tabular_query in handle_query classified queries by uppercasing the
raw SQL and checking its literal prefix, which broke on:
- a leading `-- comment` or `/* comment */` (misclassified as
  non-tabular, silently returning 0 columns/0 rows)
- CTE `WITH ... SELECT` queries (`WITH` wasn't in the prefix list)
- a trailing `;` (FORMAT was appended after it, producing a ClickHouse
  syntax error, since FORMAT must precede the statement terminator)

Classify on strip_sql_comments_and_trim(trimmed_sql) instead of a raw
uppercase, add WITH to the tabular prefix list, and strip trailing
`;`/whitespace before appending the FORMAT clause. Closes #58
…semicolon

fix(query): support CTE WITH queries, leading comments, and trailing semicolons in tabular query detection
…ng in SQL parser. Closes #59

strip_sql_comments_and_trim and split_sql_statements already handle
backslash-escaped quotes (\') and SQL-standard doubled quotes ('') inside
string literals correctly — this was fixed as a side effect of the Safe
Mode multi-statement bypass fix (#54, commit 751393e), which rewrote the
in_string branch of both functions to consume the escape/doubled-quote
pair instead of treating the first quote as a closing quote.

Add regression tests for both functions plus enforce_safe_mode_precheck
covering the exact failure scenarios from #59: a query with an escaped
quote followed by `-- ` no longer has its trailing FROM clause eaten by
a fake comment, and a doubled-quote literal no longer leaks its content
as bare keywords or desynchronizes string tracking for the rest of the
query.
…handling

test(query): add regression coverage for escaped/doubled quote handling in SQL parser
…p/Tuple columns. Closes #60

column.stats always ran min()/max()/topK() on the raw column and
column.top_10 always ran a plain GROUP BY on it, both of which ClickHouse
rejects for Array/Map/Tuple columns (ILLEGAL_TYPE_OF_ARGUMENT on
min/max/topK, and Map isn't groupable/hashable for GROUP BY).

get_context_actions_for_node now takes the column's ClickHouse type
(looked up from system.columns in handle_context_actions, skipped for
mock/test connections) and, for Array/Map/Tuple columns, builds
column.stats around min(length())/max(length()) instead of min()/max()/
topK(), and omits column.top_10 entirely since exact-value grouping on
a collection column isn't meaningful. Scalar columns keep the original
query unchanged.
…x-types

fix(actions): use type-appropriate Column Profiler query for Array/Map/Tuple columns
…llocations. Closes #47

QueryParams.limit was deserialized but never used, so db.query with a
limit still fully parsed and materialized unbounded result sets in
memory (every cell heap-allocated as a serde_json::Value), risking OOM
under ClickHouse Sandbox's 256 MB ceiling on large tables.

- parse_compact_output now takes an Option<usize> limit and stops
  parsing (and allocating) further data rows once it's reached, instead
  of parsing everything and discarding the excess. handle_query passes
  QueryParams.limit through on both the mock and real ClickHouse paths.
- Replace the Vec<&str> line collection with direct iteration over
  output_text.lines(), removing an intermediate allocation of the whole
  line list before any parsing starts.
- Add query_id directly to QueryResult instead of round-tripping through
  serde_json::to_value(...) once to get a Value, then mutating it via
  as_object_mut() to splice in a queryId key.
…d-reduce-parse-allocations

perf(query): enforce limit parameter and reduce compact row parsing allocations
…anifest contributions. Closes #48

Querya Desktop's Command Palette invokes commands.execute for actions
declared under contributions.commands in manifest.json, but the driver
neither declared any commands nor implemented the method (returning
-32601 Method not found).

- manifest.json now declares four ClickHouse maintenance commands:
  clickhouse.optimizeFinal, clickhouse.deduplicate, clickhouse.serverStats,
  clickhouse.dropPartition.
- New src/rpc/handlers/commands.rs implements handle_execute, dispatching
  on commandId. clickhouse.serverStats delegates straight to
  handle_get_server_stats. The other three build the same SQL as their
  sdui.contextActions equivalents (OPTIMIZE ... FINAL / DEDUPLICATE,
  ALTER ... DROP PARTITION) and run it through handle_query, reusing
  existing Safe Mode and injection-safe quoting/escaping.
- The Command Palette doesn't yet forward workspace selection (nodeId) to
  drivers, so database/table/partition are accepted as optional params;
  a command that needs a target and doesn't get one returns a clear
  -32602 error instead of guessing or operating on the wrong object.
- Registered "commands.execute" in router.rs.
feat(rpc): support Command Palette actions via commands.execute and manifest contributions
…uffering. Closes #49

db.query's tabular path called reqwest::Response::text(), forcing the
entire HTTP response body into one contiguous String before any parsing
started. For a large analytical SELECT (tens of MB of JSON lines), the
raw text and the parsed QueryResult rows coexisted in memory at once,
risking OOM under the 256 MB ClickHouse Sandbox ceiling.

- New ClickHouseClient::post_sql_response returns the raw streaming
  reqwest::Response on success (reusing the existing readonly-retry
  logic) instead of buffering it into a String.
- New src/driver/streaming::stream_compact_output reads that response's
  bytes_stream() through a LinesCodec-based FramedRead and parses rows
  incrementally as they arrive, stopping — without reading the rest of
  the network response — once `limit` rows are parsed or a 200 MB
  safety byte cap is hit even when no limit was given.
- Extracted parse_columns/parse_and_normalize_row out of
  parse_compact_output so both the buffered (schema introspection, mock
  mode, tree building) and streaming (large query results) parsers share
  identical row normalization.
- QueryResult gained isTruncated, set whenever limit or the safety cap
  cut a result short, so callers can tell rows isn't the complete set.
- handle_query's tabular branch now goes through post_sql_response +
  stream_compact_output; the non-tabular (mutation/DDL) branch is
  unchanged, since those responses are always small.
…-responses

perf(driver): stream ClickHouse HTTP responses instead of full-text buffering
ZhuchkaTriplesix and others added 5 commits September 29, 2026 10:09
…son framing. Closes #50

write_ndjson sanitized raw '\n'/'\r' bytes by collecting the entire
payload into a new String via .chars().map(...).collect(), so every
multi-megabyte tabular response containing a stray newline (e.g. in an
error message or embedded query text) got a full extra heap copy just
to swap a handful of bytes for spaces.

'\n' and '\r' are single-byte ASCII code points, so this rewrites the
sanitization as a byte-slice scan: write the clean slice up to each
newline straight to the writer, then a single space byte, and advance
past it — no intermediate String is ever allocated. A payload with no
newlines (the common case) still goes out in one write_all call, same
as before.
…c-framing

perf(transport): eliminate full-string heap reallocation in write_ndjson framing
…efix-scanner

perf(safemode): zero-allocation token scanner for Safe Mode SQL precheck
@ZhuchkaTriplesix
ZhuchkaTriplesix merged commit f6207b2 into main Sep 29, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment