Skip to content

Update dependency org.questdb:questdb to v9.4.3 - #319

Open
renovate[bot] wants to merge 1 commit into
mainfrom
renovate/org.questdb-questdb-9.x
Open

renovate[bot] wants to merge 1 commit into
mainfrom
renovate/org.questdb-questdb-9.x

Conversation

@renovate

@renovate renovate Bot commented Oct 3, 2025 •

Copy link
Copy Markdown

Note

This PR body was truncated due to platform limits.

This PR contains the following updates:

Package Change Age Confidence
org.questdb:questdb (source) 9.0.3 → 9.4.3 age confidence

Release Notes

questdb/questdb (org.questdb:questdb)

v9.4.3

Compare Source

QuestDB 9.4.3

QuestDB 9.4.3 brings some key bug-fixes, along with parquet-native tables and new parquet-querying performance enhancements, with order-of-magnitude speedups for ORDER BY ... LIMIT queries.

For any questions or feedback, please join us on Slack or on Discourse.

See also our prettier release notes page.

Breaking changes 💥

  • Two-bound LIMIT null semantics changed, aligning LIMIT lo, hi with LIMIT n:
    • LIMIT null, n now returns the first N rows (previously the full sorted set)
    • LIMIT n, null now returns the empty result (previously all rows).
    • LIMIT null is unchanged, and returns the full result set.
  • EXPLAIN plan output rewording for encodable sorts:
    • There was inconsistency in the rendering for 'encoding sorts'; this plan node will now render consistently as Encode sort light.
    • If you assert any particular query plan shapes in your regression suite, you will need to update the assertions.

Highlights

Table-level Parquet format

Tables can now store partitions in PARQUET format by default, rather than only NATIVE. Previously, you would need to:

  • Run ALTER TABLE ... CONVERT PARTITION ... TO PARQUET queries to synchronously convert partitions in QuestDB OSS.
  • Enable STORAGE POLICY to asynchronously convert partitions in QuestDB Enterprise.
CREATE TABLE trades (ts TIMESTAMP, price DOUBLE, sym SYMBOL)
TIMESTAMP(ts) PARTITION BY DAY FORMAT PARQUET WAL;

ALTER TABLE trades SET FORMAT PARQUET;
ALTER TABLE trades SET FORMAT NATIVE;

Please note that:

  • FORMAT PARQUET can only be set on partitioned write-ahead-log (WAL) tables.
  • When applying FORMAT PARQUET via ALTER TABLE, historical partitions will not be automatically converted.
  • FORMAT NATIVE remains the default format for tables in QuestDB.
  • Out-of-order writes are more expensive when applied to parquet partitions.
Memory-budgeted Parquet decode cache

There is a fine balance of memory budget versus CPU time when scanning and decoding parquet row groups. A fixed number of decode slots was an inflexible way to balance these two concerns.

Therefore, we have introduced a new memory-budget configuration and deprecated the old slot-based config:

  • New: cairo.sql.parquet.cache.memory.size
    • This is a per-cursor byte budget for decoded row groups (default 256 MB).
  • Deprecated: cairo.sql.parquet.frame.cache.capacity
    • This is still accepted, but not used.
Faster ORDER BY ... LIMIT N

The query engine has a number of optimisations based on the native-format data layout. We've revised the long top-K query path for parquet partitions, by removing a sorting tree and pruning projected columns earlier in the process. This is most valuable for queries over wide tables, with a significant sort plus limit workload.

This dramatically reduces unnecessary row-group decoding, and thus improves the scalability of this query shape for larger limits:

latency_questdb
Concurrent ILP/TCP stale-writes fix

ILP/TCP is a mostly-deprecated ingestion path, though maintained for backwards compatibility. As part of this maintenance, we have fixed a bug which could cause stale data to be written to a table, affecting DECIMAL, LONG256 and implicit casts from BINARY to SYMBOL. This only impacted concurrent writes, so a straightforward single worker TCP stream was unaffected.

Thank you to our users who continue to use this feature and give us valuable feedback that helps us to keep it well-maintained.

With the upcoming QuestDB 10.0 release, we recommend planning an upgrade to our new ingestion API, which brings dramatically better performance and usability across the board.

Changelog

Features
Performance
Fixes
  • fix(sql): fix internal error when a dotted name follows an operator by @​bluestreak01 in #​7260
  • fix(core): fix NATIVE_INDEX_READER leak when a posting-index row cursor is closed after its reader by @​bluestreak01 in #​7256
  • fix(sql): fix wrong results in splice, right and full outer joins with a left-table filter by @​puzpuzpuz in #​7225
  • fix(core): fix unexpected logout during HTTP session rotation by @​brunocalza in #​7249
  • fix(sql): fix internal error when an IN subquery contains a join, and handle declared variables in join ON clauses by @​puzpuzpuz in #​7211
  • fix(core): fix snapshot restore accepting truncated parquet partitions when _pm sidecar exists by @​bluestreak01 in #​7243
  • fix(core): fix POSTING-indexed symbol queries returning wrong rows or crashing by @​bluestreak01 in #​7253
  • fix(wal): fix symbol value corruption when skipping replaced transactions across DDL by @​jovfer in #​7248
  • fix(core): fix Parquet crashes on malformed files and data corruption on partial reads by @​glasstiger in #​7223
  • fix(ilp): fix data corruption in concurrent ILP over TCP by @​jerrinot in #​7240
  • fix(sql): fix wrong first/last results in parallel GROUP BY by @​ideoma in #​7224
  • fix(core): fix posting index crash and wrong covered reads after rollback by @​bluestreak01 in #​7229
  • fix(qwp): fix unresponsive network I/O under heavy QWP ingestion by @​jerrinot in #​7231
  • fix(core): fix crash and native memory overrun in covering index out-of-order commits by @​bluestreak01 in #​7227

Full Changelog: 9.4.2...9.4.3

v9.4.2

Compare Source

QuestDB 9.4.2

QuestDB 9.4.2 is a hardening release that builds on 9.4.1, driven by continued fuzz testing and stricter query-result assertions. It includes bugfixes for the new posting index, parquet tables, temporal joins, and a variety of other SQL queries. This release also brings a Web Console upgrade, which brings some fixes and a new utility for sharing queries with your colleagues.

For any questions or feedback, please join us on Slack or on Discourse.

See also our prettier release notes page.

Breaking changes 💥

  • A plain INDEX TYPE POSTING (no INCLUDE) is now non-covering.
    • cairo.posting.index.auto.include.timestamp=true was not intended to make all posting indexes covering by default.
    • This config flag automatically adds the designated timestamp to your INCLUDE list if you do not include it yourself.
    • This ensures that queries which return the designated timestamp will be executed via the covering index, and not accidentally fall back to table scans.
  • SHOW CREATE TABLE now rejects views and materialized views.
    • Previously it rendered a misleading CREATE TABLE statement for any kind of entity. You should use SHOW CREATE MATERIALIZED VIEW and SHOW CREATE VIEW instead for their corresponding entities.

Highlights

Web Console

You can now share a query quick-link with your colleagues, via a new editor drop-down on the green 'run' button. Simply click the copy list entry and paste the resulting URL to a friend or colleague. The URL will automatically open and run the query, making collaboration easier.

image

For long-form collaboration, don't forget that you can share and version control entire editor tabs using Tab Exporting, or share production-ready queries via CREATE VIEW.

We've also improved the visibility of Storage Policy (an Enterprise feature) settings, and reduced the eagerness of query validation, which should smooth out the overall editor experience.

Changelist

Full Changelog: 9.4.1...9.4.2

v9.4.1

Compare Source

QuestDB 9.4.1

QuestDB 9.4.1 is a hardening release that builds on 9.4.0. Introduction of our new SQL fuzz-testing engine and test framework improvements has flushed out more than 60 latent bugs around query correctness and resource leaks. 9.4.1 also brings additional hardening and bugfixes for the newly released posting/covering indexes, performance enhancements for materialised views, upgrades to parquet querying, and new aggregate and window functions.

For any questions or feedback, please join us on Slack or on Discourse.

See also our prettier release notes page.

Breaking changes 💥

  • Parquet metadata files (_pm) will be migrated to a new version at startup.
  • Calculations of the shape INT - FLOAT now returns FLOAT type, not DOUBLE.
  • Nested INT calculations compared to LONG values will now run at 64-bit width.
  • STRING and VARCHAR with <=/>= null comparisons will now correctly return matching null rows.
  • twap() now correctly rejects non-designated timestamps.
  • EXPLAIN plan output has changed for non-keyed group by/sample by, if you assert a particular shape of the query plan, you'll need to update your assertions.

Highlights

New SQL Query fuzzer

QuestDB contains almost 1 million lines of test code, covering unit, integration, fuzz, and performance tests. This release includes a new fuzz testing mechanism specifically to improve the robustness of the SQL engine. This enhances our existing fuzz testing suite that runs 24/7, ensuring rare edge cases or flaky bugs are reproduced and fixed at the earliest opportunity.

Additionally, we've revised our unit testing API to ensure that additional assertions are executed and checked by default, closing some under-asserted tests.

So far, this has caught more than 60 bugs, and we're looking forward to finding and fixing more!

New SQL functions
array_agg()

A new aggregate that collects/rolls-up per-group values into a DOUBLE[]:

SELECT symbol, array_agg(price) FROM trades SAMPLE BY 1m;

See the aggregate functions reference and the array type.

regr_r2()

regr_r2(y, x) is the standard SQL coefficient of determination — how well a linear regression of Y on X fits, on a 0–1 scale. It is handy for separating real trends from noise, e.g. finding sensors whose temperature is genuinely trending up versus drifting randomly.

Window functions for DECIMAL columns

All six DECIMAL sub-types (D8–D256) now work with the window functions first_value, last_value, nth_value, lag, lead, min, max, count, sum, and avg — across the same frame shapes (whole partition, ROWS BETWEEN, RANGE BETWEEN, partitioned and non-partitioned) supported for the primitive numeric types.

See the window functions reference and the DECIMAL type.

Opt-in native memory caps for window functions and ORDER BY

Users can now opt-in to memory caps for SQL queries that rely on window functions and sorts:

cairo.sql.window.cache.max.bytes
cairo.sql.window.rowid.max.bytes
cairo.sql.window.tree.max.bytes
cairo.sql.sort.key.max.bytes
cairo.sql.sort.light.value.max.bytes
cairo.sql.sort.value.max.bytes

This will help avoid any instability caused by edge case queries; for example, when a window function tries to sort a table with billions of rows.

Performance

Materialized Views

Refreshing materialised views during an out-of-order backfill will is much faster and more efficient. A new adaptive algorithm balances the number of rows refreshed versus the number of commits, ensuring that performance is maintained across varied or dynamic workloads. Refreshing a 512 symbol view with a constant -12 hours backfill plus live ingestion dropped from ~160ms to just ~2ms, with consistent performance.

Materialized Views will also now discard no-op transactions much earlier, avoiding wasted work.

SQL
  • 2-3x speedup for twap() and sparkline. This has a small memory overhead for very high cardinality group by queries, approximately ~15%.
  • Non-keyed group by/sample will now always run in parallel.
  • 1.5-2x faster covering index builds over parquet partitions.

Bug fixes

SQL
  • Fixed corr, stddev, var, and covar aggregates returning NULL on sparse-NULL data under parallel group by.
  • Fixed twap() and sparkline() returning wrong results under concurrent query load.
  • Fixed joins on a SYMBOL key combined with a UUID or DECIMAL key failing with an internal error.
  • Fixed a rare JIT-compiler crash (SIGSEGV) during query execution.
Storage, Parquet and WAL
  • Fixed a crash and boot-time crash loop when deduplicating a STRING dedup key whose partition data file exceeds 2 GiB.
  • The WAL sequencer can now rebuild a bricked or version-mismatched _meta from the committed transaction log, surviving table renames.
  • CREATE MATERIALIZED VIEW ... TTL N months is now validated up front and rejected when N months isn't a whole multiple of the derived partition size.
  • Fixed tables with an indexed SYMBOL column getting stuck mid-conversion to Parquet under a storage policy.
Protocols
  • Fixed QWP WebSocket upgrades and reject responses tearing down the connection when they couldn't be sent in a single write.
  • QWP now drains the receive buffer on graceful WebSocket close, so the CLOSE frame and its diagnostic reach the client instead of surfacing as ECONNRESET.
  • QWP now splits large query results across multiple batches instead of failing when they don't fit the HTTP send buffer.
  • The /api/v1/sql/validate endpoint is now side-effect free — it no longer enforces permissions, runs inline statements, or emits logs/metrics, but does resolve the target object so validating against a missing table reports it.

Changelog

Full Changelog: 9.4.0...9.4.1

v9.4.0

Compare Source

QuestDB 9.4.0

QuestDB 9.4.0 introduces a compact, high-performance posting and covering index for SYMBOL columns, a local parquet metadata sidecar that unlocks row-group pruning, parallelised SAMPLE BY FILL with new cross-column FILL(PREV) syntax, three new window functions, and sparkline() / bar() text visualisations. It also delivers meaningful GROUP BY / hash-join speed-ups and fixes a number of correctness issues across the SQL planner, the WAL apply path, and the PGWire protocol.

For any questions or feedback, please join us on Slack or on Discourse.

See also our prettier release notes page.

Highlights

Posting and covering index for SYMBOL columns

A new INDEX TYPE POSTING for SYMBOL columns delivers ~13x smaller index files and 1.3-1.5x faster lookups vs. the BITMAP index, at a ~9% write-amplification cost. An optional INCLUDE (...) list builds a covering sidecar so queries that read only the indexed column plus the included columns skip the column files entirely:

CREATE TABLE trades (
    ts TIMESTAMP,
    sym SYMBOL INDEX TYPE POSTING INCLUDE (price, qty),
    price DOUBLE,
    qty INT
) TIMESTAMP(ts) PARTITION BY DAY;

ALTER TABLE trades
    ALTER COLUMN sym ADD INDEX TYPE POSTING INCLUDE (price, qty);

Supported query shapes that benefit from the covering path include WHERE sym = 'X', WHERE sym IN (...), LATEST ON ts PARTITION BY sym, and SELECT DISTINCT sym. Covering data is ALP-compressed for FLOAT/DOUBLE, FoR bit-packed for integer types, and FSST-compressed for STRING/VARCHAR. Native AVX2 decoding fast paths kick in for common bit-widths.

A few practical notes:

  • ⚠️ Drop the posting index before rolling back to an older QuestDB version. Pre-9.4.0 binaries do not recognise the new index type in column metadata and will refuse to open a table that has a posting index. If you need to downgrade, run ALTER TABLE <t> ALTER COLUMN <sym> DROP INDEX on every posting-indexed column first.
  • High-cardinality SYMBOL columns benefit most — hundreds to thousands of distinct values on wide tables where the win from skipping full column files is largest.
  • The designated timestamp is auto-appended to INCLUDE when you supply an INCLUDE clause, so SHOW CREATE TABLE renders INCLUDE (price, qty) back as INCLUDE (price, qty, ts). Controlled by cairo.posting.index.auto.include.timestamp (default true).
  • Verify the covering path with EXPLAIN — the plan shows CoveringIndex on: sym with: ..., with op: latest for LATEST ON queries and op: distinct for SELECT DISTINCT. SHOW COLUMNS and table_columns() also expose new indexType and indexInclude fields.
  • Async GROUP BY and filter paths through the covering index are currently slower than the regular plan in some workloads. A follow-up release will close this gap. If EXPLAIN shows a query picking the covering path and you see a regression, opt that query out with /*+ no_covering */ (or /*+ no_index */ to disable indexing entirely) until the optimisations land.

See the posting index and covering index documentation for the full feature reference, encoding variants (POSTING DELTA / POSTING EF, both for benchmarking), and storage layout details. by @​bluestreak01 in #​6861

Local parquet metadata sidecar

Each parquet partition now ships with a compact binary _pm sidecar that stores column descriptors, per-row-group byte ranges, encodings and min/max statistics. The query planner reads pruning information from _pm without ever opening data.parquet, which is a prerequisite for efficient cold-storage scans. A migration (Mig940) generates _pm files for all existing parquet partitions on engine upgrade. by @​RaphDal in #​6913

Cross-column FILL(PREV) and parallel SAMPLE BY FILL

Imagine a stream of FX quotes where you want one-minute bars of average bid and ask. On quiet minutes you want both prices to show the last known ask — not the last bid for bid_price and the last ask for ask_price, which is what FILL(PREV, PREV) would give you. Before 9.4.0 that meant dropping down to a CTE with last_value(...) IGNORE NULLS OVER (...) and a coalesce per column. Now it is a one-liner:

SELECT timestamp, symbol,
       avg(bid_price) AS bid_price,
       avg(ask_price) AS ask_price
FROM core_price
WHERE symbol = 'EURUSD' AND timestamp IN '$today'
SAMPLE BY 100T FILL(PREV(ask_price), PREV);

FILL(PREV(col_ref)) carries the previous value of another aggregate column by alias. The reference must match the target column's type, cannot be broadcast across aggregates, and is rejected when either side is a SYMBOL. FILL(NULL), FILL(<constant>), and bare FILL(PREV) can be mixed freely in the same per-aggregate fill list.

At the same time, SAMPLE BY FILL(NULL | <constant> | PREV) moves from the sequential cursor path onto QuestDB's parallel GROUP BY path. Read-side wins compound for keyed queries on wide tables and for queries with FROM/TO boundaries — keyed FROM-TO with constant bounds is also now supported natively, removing the previous cookbook workaround.

Other fixes that ride along:

  • Sub-day SAMPLE BY with TIME ZONE and FROM/TO no longer misaligns the fill grid by the timezone offset.
  • ALIGN TO CALENDAR WITH OFFSET combined with FILL no longer falls into an infinite fill loop.
  • SAMPLE BY FILL now works on tables with pre-1970 timestamps.

FILL(LINEAR) and ALIGN TO FIRST OBSERVATION continue to use the cursor path. See the SAMPLE BY FILL options and the Fill from one column cookbook recipe. by @​jovfer in #​6946

New window functions
  • ntile(n) — distributes rows of an ordered partition into n approximately equal buckets.
  • cume_dist() — cumulative distribution of the current row within its partition.
  • nth_value(expr, n) — the n-th value within the current frame; supports DOUBLE, LONG, and TIMESTAMP arguments. Works with ROWS and RANGE frames.

All three honour PARTITION BY and ORDER BY. by @​jovfer in #​6925 and #​7037

Text visualisations: sparkline() and bar()

Two new functions render numeric data as Unicode glyphs directly in SQL output:

SELECT symbol, sparkline(price) FROM trades SAMPLE BY 1m;
-- ▁▂▄▆█▇▅▃▂▁

SELECT symbol, bar(volume, 0, max(volume) OVER ()) FROM daily_volumes;
-- ▊▋▌▍▎▏

sparkline() is an aggregate that renders a trend line with auto-scaling, clamping and width control. bar() is a scalar that renders a single value as a horizontal bar with eight levels of sub-character precision. Both return VARCHAR and work across all clients. by @​javier in #​6975

Configurable parquet encodings on export

PARQUET_ENCODING(...) now propagates through projected streaming parquet exports — COPY (SELECT ...) TO ... WITH format parquet and /exp?query=... preserve the configured encoding instead of silently reverting to defaults for pass-through columns. by @​RaphDal in #​6949

Performance

  • Parallel keyed GROUP BY: batched aggregate dispatch eliminates per-row virtual calls on the reducer, with up to ~4.6x speed-up on single-column count() over fixed-size keys and 1.4-1.8x on multi-aggregate GROUP BY queries. Also fixes a pre-existing data-correctness bug in count(uuid) for UUIDs whose high half equals Long.MIN_VALUE. by @​puzpuzpuz in #​6959
  • GROUP BY, hash joins and count_distinct: a faster xxh3-derived hash finalizer and denser hash tables shave ~15-25% off many GROUP BY queries and improve worst-case probe behaviour by orders of magnitude on adversarial inputs. ClickBench total time improves by ~1.2% across 43 queries. by @​puzpuzpuz in #​6997
  • Parallel top-K with SELECT projections: ORDER BY ... LIMIT N now uses the parallel top-K path even when a column projection (duplicate columns, computed columns) sits between the limit and the filtered scan. by @​DHRUV6029 in #​6993
  • Timestamp equality filters: comparing a TIMESTAMP column against a string bind variable (or any runtime constant) no longer re-parses the value on every row. EXPLAIN plans may show the equality arguments swapped. by @​jerrinot in #​6986
  • Lateral join decorrelation: shared sub-query computations now execute once instead of being cloned per correlated reference, by introducing a shared-cursor framework that GROUP BY factories also use. by @​kafka1991 in #​6941

Web Console

  • Create materialized view from the table menu: a new context-menu action on tables and existing materialized views generates a starter CREATE MATERIALIZED VIEW statement and drops it into the editor. From a base table the generator infers REFRESH IMMEDIATE, SAMPLE BY, PARTITION BY and TTL per QuestDB's default rules, and picks sum/last aggregates by column-name pattern. From an existing materialized view it produces a downsample one rung up the SAMPLE BY ladder, re-roots WITH BASE at the source view, rewrites aggregates against the layer-1 aliases, and steps TTL up to match. Disabled on non-WAL tables and tables without a designated timestamp. in questdb/ui#557

  • Context-aware SQL autocompletion: function suggestions now filter by grammar context, so the popup stops dumping the full function list at every identifier position. Examples:

    • SELECT * FROM trades WHERE price = c| previously mixed scalars and aggregates (coalesce, count, count_distinct, corr, covar_*, ...) and now suggests scalars only (coalesce, concat, cos, ceil).
    • SELECT * FROM trades ASOF JOIN m| shrinks from 332 mixed entries to the three actual join targets (materialized_views, memory_metrics, table_writer_metrics).
    • INSERT INTO trades VALUES (n| previously offered nothing; now suggests now, now_ns, nullif, nanos, netmask.

    Statement-start suggestions also include implicit-SELECT targets, and the editor no longer hangs on a trailing comma in the SELECT list. in questdb/ui#556

Bug fixes

SQL planner and execution
  • Fixed window functions nested inside GROUP BY expressions producing wrong results — e.g. avg(x) - avg(x) OVER () combined with GROUP BY. Such shapes are now rejected at compile time with a clear error rather than computing silently incorrect output. by @​jovfer in #​6943
  • Fixed nested window inside aggregate with explicit GROUP BY (max(avg(x) OVER ()) patterns). by @​jovfer in #​6955
  • Fixed EMA, VWEMA and KSUM failures when combined with other window functions in the same query. by @​jerrinot in #​7030
  • Fixed WINDOW JOIN crash when the master side projects a symbol column. by @​puzpuzpuz in #​7098
  • Fixed wrong results from the JIT filter when UUID bind variables are involved. by @​nwoolmer in #​7049
  • Fixed BETWEEN evaluation inside sub-expressions (e.g. when it appears as an operand of a larger boolean expression). by @​brunocalza in #​7044
  • Fixed silent drop of outer-join predicates of the form y.a = y.b (both sides on the slave); fixed NOT rewriting in UNION ALL queries that previously only descended into the left side; isolated parser state to allow concurrent SqlParser instances. by @​bluestreak01 in #​7027
  • Fixed empty LIMIT inside a DECLARE sub-query. by @​jerrinot in #​6979
  • Fixed crash on SELECT-CHOOSE with an explicit timestamp clause combined with a column rename. by @​bluestreak01 in #​6987
  • Fixed ASOF/LT joins losing the timestamp shift when the slave subquery already shifts its timestamp. by @​jerrinot in #​6981
  • Fixed arg_max / arg_min returning the wrong precision when the timestamp argument is a TIMESTAMP_NS column. by @​bluestreak01 in #​7078
  • Fixed SAMPLE BY bucket timestamp after a DST-skipped day. by @​bluestreak01 in #​6988
Storage and WAL
Protocols

Changelist

Full Changelog: 9.3.5...9.4.0

v9.3.5

Compare Source

QuestDB 9.3.5

QuestDB 9.3.5 introduces lateral joins, SQL-standard UNNEST, statistical window functions, and corrects SAMPLE BY timezone handling during DST transitions. It also delivers multiple join performance improvements and important Parquet export fixes.

For any questions or feedback, please join us on Slack or on Discourse.

See also our prettier release notes page.

Breaking changes 💥

  • SAMPLE BY timezone bucket alignment: Sub-day SAMPLE BY queries with ALIGN TO CALENDAR TIME ZONE now produce correct bucket alignment during DST transitions. Key changes:
    • Sub-day buckets are uniformly spaced in UTC using the timezone's standard offset. During DST fall-back, repeated local hours produce separate rows instead of merging.
    • FROM/TO boundaries are now interpreted as local time in the specified timezone (previously UTC).
    • Queries without TIME ZONE, with fixed-offset timezones, or with super-day strides (day+) are unaffected.
    • See https://questdb.com/docs/query/sql/sample-by/#time-zone-transitions for details.
    • by @​amunra in #​6858

Highlights

Lateral joins

QuestDB now supports lateral joins, enabling subqueries in the FROM clause to reference columns from preceding tables. The implementation uses a decorrelation optimizer that transforms correlated subqueries into efficient set-based execution.

SELECT o.id, t.total
FROM orders o
JOIN LATERAL (
    SELECT sum(qty) AS total FROM trades WHERE order_id = o.id
) t;

LEFT LATERAL and implicit CROSS LATERAL are also supported:

SELECT o.id, t.qty
FROM orders o, LATERAL (SELECT qty FROM trades WHERE order_id = o.id LIMIT 3) t;
SQL-standard UNNEST

The new UNNEST operator expands array columns and JSON arrays into rows, enabling relational operations on array data:

-- Array expansion with position tracking
SELECT t.ts, u.price, u.pos
FROM trades t, UNNEST(t.prices) WITH ORDINALITY u(price, pos);

-- JSON object array extraction
SELECT u.price, u.name
FROM events e, UNNEST(e.payload COLUMNS(price DOUBLE, name VARCHAR)) u;

Multiple arrays can be unnested simultaneously with NULL padding for mismatched lengths, following PostgreSQL-compatible behavior.

Statistical window functions

New window functions for statistical analysis:

  • stddev_pop(), stddev_samp() / stddev() — standard deviation
  • var_pop(), var_samp() / variance() — variance
  • covar_pop(), covar_samp() — covariance
  • corr() — correlation coefficient

All functions support ROWS, RANGE, and PARTITION BY frame modes with bounded and unbounded frames. Non-removable frames use Welford's online algorithm for numerical stability.

Multi-table HORIZON JOIN

HORIZON JOIN now accepts multiple right-hand-side tables in a single query, aggregating columns from several time-series sources against a common master table:

SELECT avg(b.bid) AS avg_bid, avg(a.ask) AS avg_ask
FROM trades AS t
HORIZON JOIN bids AS b ON (t.sym = b.sym)
HORIZON JOIN asks AS a ON (t.sym = a.sym)
    LIST (-2s, 0, 2s) AS h
GROUP BY h.offset;
Other
  • Per-column bloom filter configuration in CREATE TABLE and ALTER TABLE via the PARQUET() clause (e.g., a VARCHAR PARQUET(BLOOM_FILTER)).
  • ALTER TABLE ADD/DROP COLUMN now works correctly on tables with Parquet-formatted partitions.
  • arg_max(varchar, ...) group-by functions for timestamp, double, long, and int key types.
  • RLE dictionary encoding support for STRING columns in Parquet.
  • Imprecise dates in tick expressions.
  • SHOW TABLES results sorted alphabetically (behind cairo.metadata.cache.snapshot.ordered config).

Performance

  • Hash joins: SYMBOL key columns now compare as integers instead of strings, extending the optimization already used by ASOF/LT joins. by @​kafka1991 in #​6948
  • ASOF, HORIZON and WINDOW JOIN: Faster execution on large partitioned tables via lazy partition opening — queries that target a narrow time range skip I/O for untouched partitions. by @​puzpuzpuz in #​6837
  • ASOF and LT JOIN: ~1.6x faster execution on multi-key symbol columns (8.1s → 5.2s on a 100M/50M row join). by @​puzpuzpuz in #​6915
  • Wide tables: Reduced partition open overhead — mmap calls scale with referenced columns, not total columns. by @​puzpuzpuz in #​6919
  • WAL commit batching: Optimized batching for trailing INSERT transactions. by @​ideoma in #​6918
  • Array access: ~2.4x faster constant-index element access for 1D and 2D arrays (31ms → 13ms on 10M rows). by @​puzpuzpuz in #​6908

Web Console

  • Custom AI provider support: The AI Assistant now supports configuring custom providers like OpenRouter, Ollama, or LiteLLM, with your own API endpoints, keys, and models beyond the built-in Anthropic and OpenAI options. in questdb/ui#541
  • Improved SQL autocompletion: SQL editor autocompletion is now powered by the new SQL parser, delivering more accurate suggestions with refined ergonomics. in questdb/ui#536
  • Table details drawer improvements: The table details drawer can be opened from the right sidebar with a table selector. You can also navigate between tables by searching the table name from the header without leaving the drawer. in [questdb/ui

[!IMPORTANT]
✂ PR body was truncated to here.


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@renovate
renovate Bot force-pushed the renovate/org.questdb-questdb-9.x branch from f04eafe to b78c53f Compare October 10, 2025 21:10
@renovate
renovate Bot force-pushed the renovate/org.questdb-questdb-9.x branch from b78c53f to a243acb Compare October 21, 2025 14:08
@renovate
renovate Bot force-pushed the renovate/org.questdb-questdb-9.x branch from a243acb to 3578c1c Compare November 3, 2025 19:40
@renovate renovate Bot changed the title Update dependency org.questdb:questdb to v9.1.0 Update dependency org.questdb:questdb to v9.1.1 Nov 3, 2025
@renovate
renovate Bot force-pushed the renovate/org.questdb-questdb-9.x branch from 3578c1c to cc81fda Compare November 14, 2025 00:03
@renovate renovate Bot changed the title Update dependency org.questdb:questdb to v9.1.1 Update dependency org.questdb:questdb to v9.2.0 Nov 14, 2025
@renovate
renovate Bot force-pushed the renovate/org.questdb-questdb-9.x branch from cc81fda to 34cc8bd Compare November 25, 2025 17:32
@renovate renovate Bot changed the title Update dependency org.questdb:questdb to v9.2.0 Update dependency org.questdb:questdb to v9.2.1 Nov 25, 2025
@renovate
renovate Bot force-pushed the renovate/org.questdb-questdb-9.x branch from 34cc8bd to f8e98c5 Compare December 1, 2025 16:14
@renovate renovate Bot changed the title Update dependency org.questdb:questdb to v9.2.1 Update dependency org.questdb:questdb to v9.2.2 Dec 1, 2025
@renovate
renovate Bot force-pushed the renovate/org.questdb-questdb-9.x branch from f8e98c5 to 5617ddb Compare December 18, 2025 13:14
@renovate renovate Bot changed the title Update dependency org.questdb:questdb to v9.2.2 Update dependency org.questdb:questdb to v9.2.3 Dec 18, 2025
@renovate
renovate Bot force-pushed the renovate/org.questdb-questdb-9.x branch from 5617ddb to 2d59c6e Compare December 30, 2025 14:04
@renovate
renovate Bot force-pushed the renovate/org.questdb-questdb-9.x branch from 2d59c6e to 3dd7f38 Compare January 9, 2026 13:36
@renovate renovate Bot changed the title Update dependency org.questdb:questdb to v9.2.3 Update dependency org.questdb:questdb to v9.3.0 Jan 9, 2026
@renovate
renovate Bot force-pushed the renovate/org.questdb-questdb-9.x branch from 3dd7f38 to 1d2c13e Compare January 14, 2026 17:09
@renovate renovate Bot changed the title Update dependency org.questdb:questdb to v9.3.0 Update dependency org.questdb:questdb to v9.3.1 Jan 14, 2026
@renovate
renovate Bot force-pushed the renovate/org.questdb-questdb-9.x branch from 1d2c13e to b86c4d4 Compare January 28, 2026 19:07
@renovate renovate Bot changed the title Update dependency org.questdb:questdb to v9.3.1 Update dependency org.questdb:questdb to v9.3.2 Jan 28, 2026
@renovate
renovate Bot force-pushed the renovate/org.questdb-questdb-9.x branch from b86c4d4 to 0e18f8f Compare February 25, 2026 17:02
@renovate renovate Bot changed the title Update dependency org.questdb:questdb to v9.3.2 Update dependency org.questdb:questdb to v9.3.3 Feb 25, 2026
@renovate
renovate Bot force-pushed the renovate/org.questdb-questdb-9.x branch from 0e18f8f to 4d53b3a Compare March 13, 2026 10:58
@renovate
renovate Bot force-pushed the renovate/org.questdb-questdb-9.x branch from 4d53b3a to b72c863 Compare March 26, 2026 17:56
@renovate renovate Bot changed the title Update dependency org.questdb:questdb to v9.3.3 Update dependency org.questdb:questdb to v9.3.4 Mar 26, 2026
@renovate
renovate Bot force-pushed the renovate/org.questdb-questdb-9.x branch from b72c863 to 74b19d7 Compare April 13, 2026 18:13
@renovate renovate Bot changed the title Update dependency org.questdb:questdb to v9.3.4 Update dependency org.questdb:questdb to v9.3.5 Apr 13, 2026
@renovate
renovate Bot force-pushed the renovate/org.questdb-questdb-9.x branch from 74b19d7 to 3044449 Compare May 21, 2026 23:56
@renovate renovate Bot changed the title Update dependency org.questdb:questdb to v9.3.5 Update dependency org.questdb:questdb to v9.4.0 May 21, 2026
@renovate
renovate Bot force-pushed the renovate/org.questdb-questdb-9.x branch from 3044449 to 1bf6e80 Compare June 3, 2026 11:08
@renovate renovate Bot changed the title Update dependency org.questdb:questdb to v9.4.0 Update dependency org.questdb:questdb to v9.4.1 Jun 3, 2026
@renovate
renovate Bot force-pushed the renovate/org.questdb-questdb-9.x branch from 1bf6e80 to c44d3c4 Compare June 9, 2026 16:11
@renovate renovate Bot changed the title Update dependency org.questdb:questdb to v9.4.1 Update dependency org.questdb:questdb to v9.4.2 Jun 9, 2026
@renovate
renovate Bot force-pushed the renovate/org.questdb-questdb-9.x branch from c44d3c4 to f874c2b Compare June 15, 2026 20:54
@renovate renovate Bot changed the title Update dependency org.questdb:questdb to v9.4.2 Update dependency org.questdb:questdb to v9.4.3 Jun 15, 2026
@renovate
renovate Bot force-pushed the renovate/org.questdb-questdb-9.x branch from f874c2b to e60f602 Compare October 13, 2026 12:45

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants