Connect passthrough - #25990
Connect passthrough#25990carlopi wants to merge 7 commits into
Conversation
| //! The compiled grammar active for the connection | ||
| shared_ptr<CompiledGrammar> cached_grammar; | ||
| //! The grammar of the database this client is CONNECT-ed to; unset when not connected | ||
| shared_ptr<CompiledGrammar> connected_grammar; |
There was a problem hiding this comment.
Can't we just reuse cached_grammar for this? We'll just have to save the original current_dialect setting on CONNECT and reinstate the cached_grammar on DISCONNECT
There was a problem hiding this comment.
Ah actually, no that's for grammar extensions.
DialectExtension (current_dialect) goes through client_config.current_dialect and does a look-up of the dialect-extension to return its grammar
There was a problem hiding this comment.
Can we use the existing current_dialect setting for this instead? You can create a passthrough dialect, reserve that name by not allowing it through regular SET current_dialect methods and then everything else should just work.
That also eliminates the set/get connected-grammar callbacks, those can just override the "passthrough" name with another dialect extension they register.
You'll only need to preserve and re-instate the existing current_dialect setting on CONNECT and DISCONNECT respectively
The v2 C API surfaces StatementType under the same numeric values, but core had no count sentinel, so a member appended in core could only be caught by two runtime tests probing the values past the last known one. STATEMENT_TYPE_COUNT makes that a static_assert: appending a member shifts the count and fails to compile until the new type has an id in the v2 spec. The two probe tests are no longer needed. Originally first commit at duckdb#25990, but as pointed out better reviewed independently. Newer version of duckdb#25972
A PassthroughStatement carries the source text of a statement that was never handed to the parser, so it can be forwarded verbatim to another engine. The text lives in the base SQLStatement::query field. The C API mirrors StatementType numerically, so the new member is pinned in the v2 spec as well. Nothing produces a PassthroughStatement yet.
StatementTokenMatcher consumes any single token that is not a statement
boundary. Repeating it covers a statement whose syntax this parser does
not know - SQL meant for another engine - while the tokenizer keeps
deciding where the statement ends, so a ';' inside a string, a quoted
identifier or a dollar-quoted body does not split it.
The grammar cannot express this: '!' is silently dropped by the PEG
parser, '.' is not a rule operator, and the operator matcher does not
cover '(' or ','.
No grammar rule uses it yet.
The passthrough grammar replaces the Statement rule with Statement <- DisconnectStatement / PassthroughStatement PassthroughStatement <- StatementToken+ so a client that is CONNECT-ed interprets only DISCONNECT and hands every other statement to the remote verbatim. It keeps the base tokenizer, so statement boundaries stay DuckDB's, and the statement text is filled in from the parse result's extent like any other statement. The grammar is compiled once per database, next to the base grammar. Nothing selects it yet.
An AttachedDatabase carries the grammar used for statements issued while a client is CONNECT-ed to it. It defaults to the passthrough grammar, so a remote gets statements verbatim and decides itself what is valid; a backend that speaks DuckDB SQL can override it and have those statements parsed locally as before. CONNECT installs that grammar, DISCONNECT clears it, and the resolver keeps reading only ClientConfig. Two consequences, both intended. CREATE GRANT against a quack server now fails on the server rather than in the local parser. CONNECT is itself forwarded, so connections chain over several hops - a client that cannot even load the postgres extension can reach Postgres through a hop that can. DISCONNECT stays local, so the client can always get back.
The client attaches to hop A and connects; the CONNECT that follows is forwarded, so hop A is what interprets it and connects onwards to hop B. A query then travels the whole chain, and DISCONNECT brings the client straight back to local. This is what forwarding CONNECT is for: a client that cannot speak - or even load - the backend it ends up talking to can still reach it. The test needs real hops in separate processes, so it lives with the shell tests rather than in sqllogictest.
f3062d1 to
af0d4a9
Compare
Three changes:
StatementToken)AttachedDatabasehas aconnected_grammar, that when set is used whenCONNECT-ed to that specific databaseThese pieces allow, once connected, to pass down any grammar for that to be evaluated on the remote/server side.
Idea is that you can do:
with
ARBITRARY STRINGjust be passed down.See in particular
test/sql/connect/connect_passthrough_quack.testthat demo (via quack) that everything is passed down to the server.This is particularly relevant for
postgreswhere syntax might / will differ, and might not be recognized by DuckDB parser.