Skip to content

chore(core): SymbolMapWriter nullFlag not maintained when column is not included in INSERT#6645

Merged
bluestreak01 merged 2 commits intomasterfrom
symbol_null_flag_fix
Jan 15, 2026
Merged

chore(core): SymbolMapWriter nullFlag not maintained when column is not included in INSERT#6645
bluestreak01 merged 2 commits intomasterfrom
symbol_null_flag_fix

Conversation

@kafka1991
Copy link
Copy Markdown
Collaborator

@kafka1991 kafka1991 commented Jan 14, 2026

Fixed an issue where SymbolMapWriter.nullFlag was not set to true when Symbol column values were implicitly set to null (via row.append() without explicit value).

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jan 14, 2026

Walkthrough

Adds symbol null flag change tracking across the WAL stack (TableWriter, WalWriter, WalEventWriter, ViewWalWriter). Introduces new dedup removal counters and a dedicated WAL commit pathway. Extends configureNullSetters signature to include columnIndex and symbol writer references. Adds test cases for latest-by queries with null symbols.

Changes

Cohort / File(s) Summary
TableWriter API and Dedup Tracking
core/src/main/java/io/questdb/cairo/TableWriter.java
Added dedupRowsRemovedSinceLastCommit field with public getter/setter methods. Introduced commitWalInsertTransactions(...) method for WAL-based commits. Extended configureNullSetters(...) signature to include columnIndex and symbolMapWriters; all call sites updated to pass these parameters. Relocated getMinTimestamp() declaration within public API layout.
WAL Event Writer Enhancement
core/src/main/java/io/questdb/cairo/wal/WalEventWriter.java
Added symbolMapNullFlagsChanged BoolList field to track symbol map null flag changes. Extended of(...) method signature to accept symbolMapNullFlagsChanged parameter. Updated writeSymbolMapDiffs to emit diffs based on flagChanged entries.
WAL Writer Symbol Null Tracking
core/src/main/java/io/questdb/cairo/wal/WalWriter.java
Introduced symbolMapNullFlagsChanged BoolList field. Extended configureNullSetters(...) signature to include columnIndex, symbolMapNullFlagsChanged, and symbolMapNullFlags. Updated SYMBOL type null-setting logic to mark symbolMapNullFlagsChanged when setting null values. Propagated new parameters across all call sites and initialization paths (openNewSegment, configureEmptySymbol, configureSymbolMapWriter, removeSymbolMapReader, resetSymbolMaps).
WAL View Writer Update
core/src/main/java/io/questdb/cairo/wal/ViewWalWriter.java
Updated events.of(...) call to supply fourth argument (null) for new symbolMapNullFlagsChanged parameter.
Latest-By Null Symbol Tests
core/src/test/java/io/questdb/test/griffin/LatestByTest.java
Added testLatestByInsertNullSymbols() and testLatestByInsertNullSymbolsOnWal() test methods to validate latest-row semantics with null symbol columns, including WAL drain verification.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Suggested labels

Bug, Core

Suggested reviewers

  • RaphDal
  • bluestreak01
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.44% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description check ✅ Passed The description is related to the changeset and explains the issue being fixed regarding Symbol column null flag maintenance during row.append() operations.
Title check ✅ Passed The title describes the fix for SymbolMapWriter nullFlag not being maintained when a symbol column is omitted from INSERT, which directly aligns with the PR's main objective and changes throughout the codebase.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@kafka1991
Copy link
Copy Markdown
Collaborator Author

kafka1991 commented Jan 14, 2026

@CodeRabbit review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jan 14, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@questdb questdb deleted a comment from coderabbitai bot Jan 14, 2026
@kafka1991
Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jan 14, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@core/src/main/java/io/questdb/cairo/TableWriter.java`:
- Around line 783-786: The method addPhysicallyWrittenRows currently accepts any
long and can record negative values; change it to guard against negatives by
returning early (or ignoring) when rows <= 0 before updating
physicallyWrittenRowsSinceLastCommit and calling
metrics.tableWriterMetrics().addPhysicallyWrittenRows(rows); ensure you
reference the existing method addPhysicallyWrittenRows and the fields
physicallyWrittenRowsSinceLastCommit and
metrics.tableWriterMetrics().addPhysicallyWrittenRows in the change so only
positive row counts are recorded.
- Around line 728-730: The addDedupRowsRemoved method currently accepts negative
counts which can corrupt telemetry; update addDedupRowsRemoved(long count) to
validate the input and reject negatives: if count < 0, throw an
IllegalArgumentException (with a clear message including the invalid count)
instead of calling dedupRowsRemovedSinceLastCommit.add(count); otherwise proceed
to add the count. Ensure the exception message references addDedupRowsRemoved
and the provided count so callers can diagnose the issue.
🧹 Nitpick comments (1)
core/src/main/java/io/questdb/cairo/TableWriter.java (1)

3159-3241: Fix is on the right path: SYMBOL null setter now updates SymbolMapWriter.nullFlag.
This should correctly cover implicit nulls from row.append() when the caller didn’t touch the SYMBOL column. One small nit: calling updateNullFlag(true) on every null row might be redundant; if updateNullFlag isn’t already idempotent/cheap, consider a cheap internal guard (only if you have a fast “already true” check available).

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 017350d and c1ec745.

📒 Files selected for processing (5)
  • core/src/main/java/io/questdb/cairo/TableWriter.java
  • core/src/main/java/io/questdb/cairo/wal/ViewWalWriter.java
  • core/src/main/java/io/questdb/cairo/wal/WalEventWriter.java
  • core/src/main/java/io/questdb/cairo/wal/WalWriter.java
  • core/src/test/java/io/questdb/test/griffin/LatestByTest.java
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-07T00:59:31.522Z
Learnt from: bluestreak01
Repo: questdb/questdb PR: 0
File: :0-0
Timestamp: 2025-11-07T00:59:31.522Z
Learning: In QuestDB's Cairo engine, transaction (_txn) files have a strong invariant: they are never truncated below TX_BASE_HEADER_SIZE. Once created, they are either fully formed (size >= header size) or completely removed along with the entire table directory when the table is dropped.

Applied to files:

  • core/src/main/java/io/questdb/cairo/TableWriter.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (35)
  • GitHub Check: New pull request (SelfHosted Running tests with cover on linux-other)
  • GitHub Check: New pull request (SelfHosted Running tests with cover on linux-pgwire)
  • GitHub Check: New pull request (SelfHosted Running tests with cover on linux-cairo-sub)
  • GitHub Check: New pull request (SelfHosted Running tests with cover on linux-cairo-root)
  • GitHub Check: New pull request (SelfHosted Running tests with cover on linux-fuzz2)
  • GitHub Check: New pull request (SelfHosted Running tests with cover on linux-fuzz1)
  • GitHub Check: New pull request (SelfHosted Running tests with cover on linux-griffin-sub)
  • GitHub Check: New pull request (SelfHosted Running tests with cover on linux-griffin-root)
  • GitHub Check: New pull request (Rust Test and Lint on linux-jdk17)
  • GitHub Check: New pull request (SelfHosted Other tests on linux-x86-graal)
  • GitHub Check: New pull request (SelfHosted Other tests on linux-arm64)
  • GitHub Check: New pull request (Trigger Enterprise CI Trigger Enterprise Pipeline)
  • GitHub Check: New pull request (SelfHosted Other tests on linux-x64-zfs)
  • GitHub Check: New pull request (Hosted Running tests on windows-other-2)
  • GitHub Check: New pull request (Hosted Running tests on windows-other-1)
  • GitHub Check: New pull request (Hosted Running tests on windows-pgwire)
  • GitHub Check: New pull request (Hosted Running tests on windows-cairo-2)
  • GitHub Check: New pull request (Hosted Running tests on windows-cairo-1)
  • GitHub Check: New pull request (Hosted Running tests on windows-fuzz2)
  • GitHub Check: New pull request (Hosted Running tests on windows-fuzz1)
  • GitHub Check: New pull request (Hosted Running tests on windows-griffin-sub)
  • GitHub Check: New pull request (Hosted Running tests on windows-griffin-base)
  • GitHub Check: New pull request (Hosted Running tests on mac-other)
  • GitHub Check: New pull request (Hosted Running tests on mac-pgwire)
  • GitHub Check: New pull request (Hosted Running tests on mac-cairo-fuzz)
  • GitHub Check: New pull request (Hosted Running tests on mac-cairo)
  • GitHub Check: New pull request (SelfHosted Griffin tests on linux-arm64)
  • GitHub Check: New pull request (Hosted Running tests on mac-griffin)
  • GitHub Check: New pull request (SelfHosted Griffin tests on linux-x86-graal)
  • GitHub Check: New pull request (SelfHosted Griffin tests on linux-x64-zfs)
  • GitHub Check: New pull request (SelfHosted Cairo tests on linux-x64-zfs)
  • GitHub Check: New pull request (SelfHosted Cairo tests on linux-x86-graal)
  • GitHub Check: New pull request (SelfHosted Cairo tests on linux-arm64)
  • GitHub Check: New pull request (Check Changes Check changes)
  • GitHub Check: build
🔇 Additional comments (16)
core/src/main/java/io/questdb/cairo/TableWriter.java (4)

205-205: Good: LongAdder for dedup removal counter (thread-safe for async tasks).
This matches how physicallyWrittenRowsSinceLastCommit is tracked and should tolerate concurrent increments from O3/WAL worker tasks.


2265-2267: Nice: exported getter for dedup removals since last commit.
This will be useful for WAL apply observability/metrics.


2301-2304: No concerns.


4171-4173: Good: null-setter configuration now passes columnIndex + symbolMapWriters.
This wires the SYMBOL null-flag behavior consistently across base + both O3 setter lists.

core/src/test/java/io/questdb/test/griffin/LatestByTest.java (2)

276-296: Good test coverage for the null symbol flag fix.

This test correctly validates the fix by inserting rows with only the timestamp column populated (implicitly nullifying the symbol columns), then verifying that LATEST ON queries properly handle these null symbols. The expected output correctly shows two rows: one for the null symbol combination and one for the non-null symbols.


298-319: Good WAL-specific test coverage.

This test mirrors the non-WAL test but explicitly exercises the WAL code path where the symbolMapNullFlagsChanged tracking is critical. The drainWalQueue() call ensures the WAL events are processed before verification.

core/src/main/java/io/questdb/cairo/wal/ViewWalWriter.java (1)

71-71: Correct signature update for view writer.

The additional null argument aligns with the extended WalEventWriter.of() signature. Since views don't contain symbol columns, passing null for all symbol-related parameters is appropriate.

core/src/main/java/io/questdb/cairo/wal/WalEventWriter.java (2)

444-449: Clean API extension.

The of() method properly accepts and stores the new symbolMapNullFlagsChanged parameter, maintaining consistency with how other symbol-related state lists are handled.


238-264: Core fix logic is correct.

The updated condition at line 244 now properly triggers symbol map diff writing when symbolMapNullFlagsChanged.get(columnIndex) is true. This ensures the null flag state is persisted to WAL even when no new symbol values are added—which is the essence of this bug fix.

The field symbolMapNullFlagsChanged is guaranteed to be non-null when writeSymbolMapDiffs() executes. The method is only called from appendData(), which is only invoked by WalWriter (not ViewWalWriter). ViewWalWriter passes null values to the of() method but never calls appendData(), so no null safety issue exists.

core/src/main/java/io/questdb/cairo/wal/WalWriter.java (7)

128-128: New tracking field for symbol null flag changes.

The symbolMapNullFlagsChanged list correctly tracks which symbol columns have had their null flag transition from false to true during the current transaction.


592-600: Correct null setter implementation for symbols.

The lambda properly:

  1. Writes VALUE_IS_NULL to the data column
  2. Only marks symbolMapNullFlagsChanged as true when transitioning from a non-null state

The conditional check !symbolMapNullFlags.get(columnIndex) prevents redundant flag updates when multiple null values are inserted for the same column within a transaction.


931-931: Call site updated correctly.

The configureNullSetters invocation now passes the required columnIndex, symbolMapNullFlagsChanged, and symbolMapNullFlags parameters.


1333-1333: Segment rollover correctly resets the change tracking flag.

Resetting symbolMapNullFlagsChanged to false when opening a new segment ensures that each segment's WAL events only capture the null flag changes that occurred within that segment.


2630-2633: Explicit null symbol handling correctly tracks flag changes.

This handles the case where putSym(columnIndex, null) is called explicitly. The logic correctly:

  1. Checks if the null flag isn't already set
  2. Only then marks both the current state and the change flag

Minor note: This code path uses set() while the null setter lambda (line 596-597) uses setQuick(). Both work correctly since the lists are pre-sized, but setQuick() would be marginally more consistent.


190-190: Event writer correctly initialized with null flag tracking.

The WalEventWriter is properly wired with all symbol-related lists, including the new symbolMapNullFlagsChanged tracking list.


1451-1451: Transaction boundary correctly resets change tracking.

After a commit, resetSymbolMaps properly resets symbolMapNullFlagsChanged to false while preserving the actual symbolMapNullFlags state from the reader. This ensures subsequent transactions only track new changes.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@glasstiger
Copy link
Copy Markdown
Contributor

[PR Coverage check]

😍 pass : 81 / 85 (95.29%)

file detail

path covered line new line coverage
🔵 io/questdb/cairo/TableWriter.java 61 65 93.85%
🔵 io/questdb/cairo/wal/WalEventWriter.java 2 2 100.00%
🔵 io/questdb/cairo/wal/WalWriter.java 17 17 100.00%
🔵 io/questdb/cairo/wal/ViewWalWriter.java 1 1 100.00%

@bluestreak01 bluestreak01 changed the title fix(core): SymbolMapWriter nullFlag not maintained when column is not included in INSERT chore(core): SymbolMapWriter nullFlag not maintained when column is not included in INSERT Jan 15, 2026
@bluestreak01 bluestreak01 merged commit 1c120cf into master Jan 15, 2026
44 checks passed
@bluestreak01 bluestreak01 deleted the symbol_null_flag_fix branch January 15, 2026 11:14
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.

3 participants