Skip to content

Let a Session pass arguments the engine needs at connect - #93

Merged
wudidapaopao merged 1 commit into
mainfrom
feat/session-connection-args
Sep 9, 2026
Merged

wudidapaopao merged 1 commit into
mainfrom
feat/session-connection-args

Conversation

@ShawnChen-Sirius

@ShawnChen-Sirius ShawnChen-Sirius commented Sep 9, 2026 •

Copy link
Copy Markdown
Contributor

Follow-on to #92. That change gave the addon an optional settings array and
stopped there, because it was about durable and this is not — it is the half a
caller can actually reach.

new Session('./data', {
  connectionArgs: [
    '--async_load_databases=0',              // load databases synchronously
    '--async_load_system_database=0',        // and the system tables too
    '--tables_loader_foreground_pool_size=4',
    '--restore_threads=1',
    '--output_format_json_quote_64bit_integers=1',
    '--config-file=/etc/my-chdb.xml',
  ],
})

Why these cannot be SETs

--config-file is not a setting at all. And the arguments deciding how a data
directory loads — async_load_databases, async_load_system_database,
tables_loader_foreground_pool_size, restore_threads — have already taken
effect by the time a query could run, so there is no later moment at which
setting them would mean anything. A downstream moving off its own FFI bindings
depends on all four, plus output_format_json_quote_64bit_integers, which is
what keeps a value past 2^53 intact through JSON.

Checked against engine 26.7.2-rc.2: all six take effect through connect
arguments, changed = 1 on each, and a config file's values are read.

The tests assert what the engine ended up with

Not the argv that went in. The addon can only prove which strings it passed,
and the claim is about what the engine did with them — so each case reads back
out of system.server_settings or system.settings:

"async_load_databases","0"
"async_load_system_database","0"
"restore_threads","1"
"tables_loader_foreground_pool_size","4"

The config-file case writes a real XML and checks the value came from it. The
64-bit case asserts the round trip, {"big":"9007199254740993"}, since past
2^53 a JSON number would not survive it.

Two forms are refused

Both already by the addon, from #92, so both surface here:

  • --path, in any spelling. The data directory is the connection
    registry's key and comes from the first constructor argument; a setting that
    moved it would leave the registry and the engine describing different places,
    and the registry is what enforces one bound directory per process.
  • anything containing a NUL byte. It truncates the argument at the C
    boundary, and a truncated prefix can be an option that takes the next entry
    as its value — which is how --path gets smuggled past the check above. That
    was measured on Bind the durable ABI in the addon, and ship the default Node engine #92: ['--path\0x', '/other'] bound /other while the
    registry recorded the requested path.

Nothing else is validated. An unknown setting name or a malformed value is the
engine's to reject, not this layer's to guess at.

Tests

Suite Result
test/v3/connection-args.test.ts (new) 8 pass
npm run test:v3 486 pass
npm run test:durable 136 pass
npm run test:durable:node 24 pass

npm run typecheck reports the same 13 pre-existing errors under test/v3/**
as on main. npm test (mocha) cannot run on Node 26 locally — yargs fails to
load — which is unchanged and unrelated.

The option is additive: absent, the addon is called with one argument, exactly
as every existing caller calls it, and a test pins that.

🤖 Generated with Claude Code

Note

Add connectionArgs option to Session for engine startup arguments

  • Adds an optional connectionArgs string array to SessionOptions so engine command-line settings can be supplied at connect time, before the first query.
  • The Session constructor validates that the option is an array of strings (throwing TypeError otherwise) and forwards it to native connection creation as a second argument; when absent, the existing one-argument call is unchanged.
  • The native addon rejects arguments that change the data directory or contain NUL bytes, surfacing errors through the existing connection-error handling.
  • Adds a test module covering supported settings (timezone, query-size, async loading, config files, JSON integer quoting), rejection of path-changing and NUL-containing arguments, and validation of invalid option types.
  • Risk: non-string or non-array values for connectionArgs now throw synchronously during construction in index.js; callers passing malformed values will see a TypeError rather than a silent ignore.
📊 Macroscope summarized 42bc08f. 2 files reviewed, 1 issue evaluated, 1 issue filtered, 0 comments posted

🗂️ Filtered Issues

index.js — 0 comments posted, 1 evaluated, 1 filtered
  • line 654: connectionArgs.some(...) skips holes in a sparse array, so new Session(path, { connectionArgs: ['--flag', , '--other'] }) passes this validation even though the hole is not a string. The native wrapper subsequently reads that index as undefined and throws, but the surrounding try converts its TypeError into ChdbConnectionError; this violates the new API's promised synchronous TypeError validation for non-string entries and gives callers the wrong error category. [ Out of scope (post-validation triage) ]

#92 gave the addon a settings array and stopped there, because that change was
about durable and this one is not. It is the half a caller can reach:

    new Session('./data', {
      connectionArgs: ['--async_load_databases=0', '--restore_threads=1',
                       '--config-file=/etc/my-chdb.xml'],
    })

None of these can be a `SET`. `--config-file` is not a setting at all, and the
arguments deciding how a data directory loads — `async_load_databases`,
`async_load_system_database`, `tables_loader_foreground_pool_size`,
`restore_threads` — have already taken effect by the time a query could run,
so there is no later moment at which setting them would mean anything. A
downstream moving off its own FFI bindings depends on all four, plus
`output_format_json_quote_64bit_integers`, which is what keeps a value past
2^53 intact through JSON.

Each is asserted against what the engine actually ended up with, read back out
of `system.server_settings` and `system.settings`, rather than against the
argv that went in: the addon can only prove which strings it passed, and the
claim is about what the engine did with them. The config-file case writes a
real XML and checks the value came from it.

Two forms are refused, both already by the addon, so both surface here: `--path`,
since the data directory is the connection registry's key and comes from the
first argument, and anything containing a NUL byte, which truncates the
argument at the C boundary and lets the next entry become its value. Nothing
else is validated — an unknown setting name is the engine's to reject, not
this layer's to guess at.

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

Copy link
Copy Markdown
Contributor Author

@wudidapaopao please review this PR

@wudidapaopao wudidapaopao left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

@wudidapaopao
wudidapaopao merged commit 0559be3 into main Sep 9, 2026
23 checks passed
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.

2 participants