Skip to content

docs: reproducible verification for the OpenADS Plus PRs (#22/#23/#24) — automated-review findings are against stale revisions#30

Open
Admnwk wants to merge 9 commits into
FiveTechSoft:mainfrom
Admnwk:docs/plus-backends-verification
Open

docs: reproducible verification for the OpenADS Plus PRs (#22/#23/#24) — automated-review findings are against stale revisions#30
Admnwk wants to merge 9 commits into
FiveTechSoft:mainfrom
Admnwk:docs/plus-backends-verification

Conversation

@Admnwk

@Admnwk Admnwk commented Jun 21, 2026

Copy link
Copy Markdown
Collaborator

What this is

A short verification report (docs/openads-plus/VERIFICATION.md) for the three OpenADS Plus backend PRs — #22 (PostgreSQL), #23 (MariaDB), #24 (ODBC) — so they can be reviewed against the code and the test suite rather than against the inline automated-review comments.

Why

The inline Gemini Code Assist comments on those PRs were generated against earlier revisions of each branch. Every "high-severity" item it raised — null-deref on pucField, SELECT * fragility, cross-schema mixing, missing locks, unescaped identifiers — is already handled at the current HEAD. Read literally, those comments describe a snapshot that predates the polish commits, not the code you would merge.

That puts a reviewer in an unfair spot ("the bot says there's a bug — do I merge or not?"). This report removes the guesswork.

The proof is reproducible in minutes

The clearest one (no database server needed) is ODBC, #24:

tools\scripts\build_msvc_x64_odbc.bat
build\odbc-msvc\tests\openads_unit_tests.exe      ->  528 / 528  (44,666 assertions)
pwsh tools\scripts\run_odbc_tests.ps1             ->    4 / 4    (throwaway Access .accdb, zero server)
pwsh tools\scripts\run_firebird_odbc_tests.ps1    ->    4 / 4    (real Firebird .fdb)

The report maps each flagged item to the exact guard at HEAD (file + the few lines that handle it), for all three PRs.

Note

Gemini Code Assist (consumer) is being retired — new installs blocked 2026-06-18, reviews ceasing 2026-07-17 — and its comments on these PRs predate the current HEAD. They should not block the merges; the code + suites are the ground truth.

Happy to add any fixture or test you'd like to see covered.

DEVAI and others added 9 commits June 18, 2026 13:37
Wire Mg telemetry + Studio HTTP sessions. See tools/openmonitor/README.md.
…back

Rollback restores record buffers via refresh_record_buffer() without
unnecessary index reposition. AdsConnect60 falls back to the process
default connection when the caller passes a null connection handle.

Co-Authored-By: Grok Code <noreply@x.ai>
CDX multi-tag indexes on one file allocated pages independently per
sub-tree, causing key loss and seek misses on secondary tags. Use a
shared per-file page allocator and fix erase on branch trees after seek.

Transaction commit/rollback flushed all bound tables, including handles
already closed via AdsCloseTable, which could crash on rollback. Purge
table bindings on close and flush only tables touched by the active
transaction.

Add CDX multi-level erase unit test and ABI M5 smoke tests for multi-tag
rollback seek and post-commit index visibility.

Co-Authored-By: Grok Code <noreply@x.ai>
…path canon

- rddads_default_connection: thread_local ADSHANDLE (avoid cross-thread overwrite)

- CDX allocator: canonicalize_path via absolute+lexically_normal for g_cdx_alloc_tail keys

- CDX erase duplicate-key walk guard raised to 1048576

Co-Authored-By: Grok Code <noreply@x.ai>
…llback)

Lands fix/cdx-tx-safety: 321b2d9 (rollback restores via refresh_record_buffer
+ AdsConnect60 default-connection fallback — fixes rddads open-on-one-conn /
begin-tx-on-another mismatch that left single-table rollback unrestored),
abcaadb (multi-tag CDX allocator/branch-erase + safe tx flush), cf424d6
(thread-local default conn + path canon, Gemini review).

Verified: full C++ suite 527 cases / 64700 assertions green (incl. the +127
abi_m5_smoke and +48 cdx_multilevel regression tests these commits add).
Re-ran the adsexhaust rddads stress harness against this engine: 68/13 -> 75/6
deterministic; all 6 residual failures isolated to harness test artifacts
(exact == vs space-padded C field, cross-section state bleed, counter base),
not engine defects.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a unit case covering the multi-table edge of transaction rollback:
two tables appended in the same transaction, rolled back, then the first
table's index is queried after a close/reopen (fresh on-disk read).

This path had no coverage. An earlier engine revision crashed (SIGSEGV)
on exactly this scenario; the current engine keeps the first table's
deleted phantom seekable through its index and stays alive. The case
locks that behavior in so the multi-table rollback path cannot silently
regress to dropping the first table's index entry.

Full unit suite stays green (528 cases).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Test-only branch. Adds a C++ regression guard that PASSES on current main
and SIGSEGVs on the pre-fix engine state (3bf3955). No engine change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…t#22/FiveTechSoft#23/FiveTechSoft#24)

The inline automated-review (Gemini Code Assist) comments on these PRs were
generated against earlier revisions of each branch; every flagged item is
already handled at the current HEAD. This report maps each finding to the
guard that addresses it and gives a one-command way to confirm the suites are
green (ODBC: 528/528 unit + live Access/Firebird e2e, zero server needed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a new Rust-based terminal and web monitor tool, openmonitor, for openads_serverd telemetry, alongside significant backend enhancements to transaction rollback handling, connection resolution, and CDX index page allocation. Feedback on these changes highlights a few critical issues: a bug in rollback_to_savepoint where touched tables are collected after truncating operations, a potential denial-of-service vulnerability in openmonitor due to uncontrolled memory allocation from network-provided frame lengths, blocking connection attempts in TcpStream::connect, and a potential out-of-bounds selection issue with selected_session upon refreshing the HTTP sessions list.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines 337 to +341
tx_.truncate_ops_to(idx);
std::unordered_set<Handle> touched;
for (const auto& op : tx_.ops()) {
touched.insert(static_cast<Handle>(op.table));
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

In rollback_to_savepoint, touched is populated from tx_.ops() after calling tx_.truncate_ops_to(idx). This means touched will only contain tables that have remaining operations before the savepoint, and will completely miss any tables that were modified only after the savepoint (which are the exact tables whose changes were just rolled back and need their record buffers refreshed).\n\nTo fix this, collect the touched table handles from the operations being undone (from idx to the end of ops) before truncating the transaction operations.

    std::unordered_set<Handle> touched;\n    for (std::size_t i = idx; i < ops.size(); ++i) {\n        touched.insert(static_cast<Handle>(ops[i].table));\n    }\n    tx_.truncate_ops_to(idx);

Comment on lines +181 to +183
let len = u32::from_be_bytes([header[0], header[1], header[2], header[3]]) as usize;
let opcode = header[4];
let mut payload = vec![0u8; len];

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

security-high high

The frame length len is read directly from the network as a u32 and then used to allocate a vector of size len (vec![0u8; len]). If a corrupted or malicious packet specifies an extremely large length (e.g., u32::MAX), this will cause an out-of-memory (OOM) panic or exhaust system resources, leading to a Denial of Service (DoS).\n\nEnforce a reasonable maximum limit on the frame size (e.g., 16 MB) before allocating the payload vector.

    let len = u32::from_be_bytes([header[0], header[1], header[2], header[3]]) as usize;\n    if len > 16 * 1024 * 1024 {\n        return Err(anyhow!(\"frame length too large: {len}\"));\n    }\n    let opcode = header[4];\n    let mut payload = vec![0u8; len];

Comment on lines +193 to +194
let addr = format!("{host}:{port}");
let mut stream = TcpStream::connect(&addr).with_context(|| format!("connect {addr}"))?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

TcpStream::connect uses the operating system's default connection timeout, which can be up to 2 minutes. If the target server is offline or dropping packets, this will block the calling thread (including the TUI event loop or the web server thread) for a very long time, leading to a poor user experience.\n\nUse TcpStream::connect_timeout with a reasonable timeout (e.g., 5 seconds) to fail fast if the server is unreachable.

    let addr = format!(\"{host}:{port}\");\n    let addrs = std::net::ToSocketAddrs::to_socket_addrs(&addr)\n        .with_context(|| format!(\"resolve {addr}\"))?;\n    let mut stream = None;\n    for socket_addr in addrs {\n        if let Ok(s) = TcpStream::connect_timeout(&socket_addr, Duration::from_secs(5)) {\n            stream = Some(s);\n            break;\n        }\n    }\n    let mut stream = stream.ok_or_else(|| anyhow!(\"failed to connect to any resolved address for {addr}\"))?;

Comment on lines +79 to +82
fn refresh(&mut self, cfg: &MonitorConfig) {
self.inner.refresh(cfg);
self.last_refresh = Instant::now();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

When the sessions list is refreshed, some sessions might have been closed, which can leave selected_session pointing to an out-of-bounds index. While the current rendering and action code bounds-checks safely, it can result in no session being highlighted or selected.\n\nClamp selected_session to the new sessions list size during refresh to keep the selection valid.

    fn refresh(&mut self, cfg: &MonitorConfig) {\n        self.inner.refresh(cfg);\n        self.last_refresh = Instant::now();\n        if !self.inner.http.sessions.is_empty() {\n            self.selected_session = self.selected_session.min(self.inner.http.sessions.len() - 1);\n        } else {\n            self.selected_session = 0;\n        }\n    }

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.

1 participant