Skip to content

client: add BeginInsert(const Query&) overload#518

Open
iliaal wants to merge 3 commits into
ClickHouse:masterfrom
iliaal:begininsert-query-overload
Open

client: add BeginInsert(const Query&) overload#518
iliaal wants to merge 3 commits into
ClickHouse:masterfrom
iliaal:begininsert-query-overload

Conversation

@iliaal

@iliaal iliaal commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Add Block BeginInsert(const Query& query).

The Impl already supported a fully-configured Query (settings, params, query_id, OnData, etc.). The public surface only exposed the string forms.

This lets callers do proper streaming inserts with the full Query feature set (symmetric to the Query forms already available for Select/Execute).

@iliaal iliaal requested review from mzitnik and slabko as code owners June 10, 2026 20:31
@iliaal iliaal force-pushed the begininsert-query-overload branch 4 times, most recently from 0f33a4c to 2b434f0 Compare June 10, 2026 20:43
Add public overload to support fully configured Query (with settings, params, query_id, callbacks) for streaming inserts.

Removed the legacy string overloads for BeginInsert (they were thin wrappers); string literals now resolve via the Query overload (implicit ctor, which is kept non-explicit for convenience).

Includes the internal fix to pass the full Query to SendQuery (instead of GetText()) so that query_id etc. are preserved when using the rich form.

This was the original local patch; exercised by bindings using settings on insert/writeStart.
@iliaal iliaal force-pushed the begininsert-query-overload branch from 2b434f0 to d3984cf Compare June 10, 2026 20:47
Making the Query ctors explicit broke every call site that passes
string literals to Execute(), Select(), BeginInsert(), etc.

The BeginInsert(const Query&) overload still accepts string literals
via the implicit conversion, matching the PR intent.
Comment thread clickhouse/client.h Outdated
Comment on lines +305 to +306
/// Use BeginInsert(const Query&) for settings, query_id, callbacks etc.
Block BeginInsert(const Query& query);

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.

Why should we take away a part of existing API on which people probably relay? Can BeginInsert(const Query& query) be an addition to the exiting functions?

@slabko

slabko commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

The main reason BeginInsert does not take a Query as a parameter is that Query instances are much more than just a query string and a query ID. They also contain a number of event handlers. These event handlers add significant complexity to the API, and it is not entirely clear how they should be handled in this context.

To avoid this issue, the decision was made not to expose the Query object in such cases. I still stand by that decision because exposing it would create an unforeseen set of use cases that people would come to rely on, making them difficult to support in the future. I am not comfortable extending the API surface area that far just yet.

If you can explain why you need it, we might try to find another solution to it.

@slabko slabko closed this Jun 12, 2026
Restore BeginInsert(const std::string&, const std::string&) alongside the
new BeginInsert(const Query&). Single-string callers already convert to
Query implicitly, but the two-arg (query, query_id) form has no implicit
conversion, so dropping it was a source break. It differs in arity from
the Query overload, so there is no ambiguity.
@iliaal

iliaal commented Jun 12, 2026

Copy link
Copy Markdown
Contributor Author

The driver is per-insert settings, not the handlers.

In my binding every streaming insert attaches Query::SetSetting(...) (async_insert, format settings, plus per-call overrides) and then calls BeginInsert. With only the string forms there's no way to scope settings to a single insert. I'd have to mutate the connection's global settings, which then leak into the next query on the same handle. The Query overload is the only way to pass settings (and bound parameters) without that shared state.

On the handlers: BeginInsert already neutralizes them. Impl::BeginInsert overwrites the caller's OnData with its own block-capture lambda, and the EnsureNull guard resets events_ to null the moment BeginInsert returns. So no handler on the passed Query survives into the SendInsertBlock/EndInsert phase. The data path runs without callbacks regardless of what the Query carries. The only window anything fires is the initial handshake inside BeginInsert, and even there OnData is replaced.

If exposing the full Query still feels too broad, an overload taking just Settings (and query_id) covers my case without touching the callback surface. Happy to go either way.

@slabko slabko reopened this Jun 12, 2026
@slabko

slabko commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

The data path runs without callbacks regardless of what the Query carries. The only window anything fires is the initial handshake inside BeginInsert, and even there OnData is replaced.

I'm not sure that's entirely true. The first thing that comes to mind is a server exception.

If we go down this route, could we throw an exception stating that Query callbacks are not supported for BeginInsert whenever any Query callback is set?

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