morningman opened a new pull request, #67966:
URL: https://github.com/apache/doris/pull/67966
### What problem does this PR solve?
Issue Number: #67578
Related: #67577
Related PR: #67900 (the Flight session lock and per-statement hooks this
builds on)
Problem Summary:
First server-side item of #67578 (A1). The Arrow Flight SQL session actions
were left at the Arrow Java defaults: `SetSessionOptions` and
`GetSessionOptions` answered `UNIMPLEMENTED`, and `CloseSession` was
implemented but answered twice when it failed. So a client had no way to set
the session's catalog, database or a session variable other than sending `SET`
/ `USE` / `SWITCH` as SQL statements, and the ADBC connection options
`adbc.connection.catalog` / `adbc.connection.db_schema`
(`conn.adbc_current_catalog` / `conn.adbc_current_db_schema` in Python) failed
on Doris, as did the Flight SQL JDBC driver's `catalog` connection property.
This PR implements the three actions on the Doris session:
```
ADBC driver (Go; Python / C++ / R bindings) Flight SQL JDBC
driver 19
adbc.connection.catalog --> session option "catalog" <-- URL
property ?catalog=... (set at connect)
adbc.connection.db_schema --> session option "schema"
adbc.flight.sql.session.option.<name> / optionbool. / optionerase. -->
session option <name>
Connection.Close() --> CloseSession <--
Connection.close()
|
v
DorisFlightSqlProducer.setSessionOptions / getSessionOptions (one
command of the session, under its lock)
|
v
FlightSessionOptions: "catalog" -> SWITCH `x` "schema" -> USE `x`
<name> -> SET SESSION name = <literal>
run through FlightSqlConnectProcessor.handleQuery:
checked, audited, takes effect
exactly as if the client had sent the statement
get(): "catalog", "schema" + every row of SHOW
VARIABLES, values as strings
```
**Keys.** `catalog` and `schema` are the two names the ADBC Flight SQL
driver puts on the wire for the connection options (its
`driverbase.CurrentNamespacer` translates `adbc.connection.catalog` into
`SetSessionOptions({catalog: x})`; the constants in `adbc.h` are
`adbc.connection.catalog` / `adbc.connection.db_schema`, not the `current_*`
spelling in #67578's A1 text) and the one the JDBC driver sends for its
`catalog` property. Every other name is a session variable, as the drivers pass
`adbc.flight.sql.session.option.<name>` through unchanged. The
`adbc.connection.*` names themselves are not accepted: no client sends them.
**SetSessionOptions.** Each option is set on its own and answered on its
own, as the spec asks; the action fails as a whole only when the session cannot
be reached (or is busy: the same `UNAVAILABLE` as any other command of the
session). Within one request `catalog` is applied first and `schema` second,
since `USE` names a database of the current catalog. The value is rendered as
the literal of its own type - a string quoted for the session's `sql_mode` (so
`NO_BACKSLASH_ESCAPES` round-trips), `true` / `false`, an integer, a double;
the empty value that a driver sends to erase an option becomes `SET name =
DEFAULT`; a string list has no literal. Per-name results:
| result | when |
|---|---|
| `INVALID_NAME` | the name is not an identifier or not a session variable
(`VariableMgr.getVarContext` finds nothing; a removed variable or a
MySQL-compatibility-whitelist name that `SET` silently ignores is no variable
of the session either) |
| `INVALID_VALUE` | a string list, a NaN / infinite double, an empty
`catalog` / `schema`; or the statement refused the value: `SET` rejected it
(type, range, the variable's own checker, `time_zone` validation), the catalog
or database does not exist |
| `ERROR` | the variable is read-only or global-only (`SET GLOBAL`
territory), or `USE` / `SWITCH` failed for another reason, e.g. no privilege |
Why the split is done this way: `StmtExecutor.execute` rewrites the error
code of a failed command to `ERR_UNKNOWN_ERROR` (the two ERR cases in the
Flight golden show it), so a `SET` failure cannot be classified afterwards. The
name and the scope are therefore checked before the statement runs, which
leaves the value as the only thing `SET` can still refuse. `USE` and `SWITCH`
write `ERR_BAD_DB_ERROR` / `ERR_UNKNOWN_CATALOG` onto the session state
themselves without throwing, so those two survive and mean "the value"; a
validation failure (privilege) is rewritten and means `ERROR`. Doris checks the
privilege before the existence of a catalog or database, and the option keeps
that order rather than pre-checking existence. The result carries no message
per name (the protocol has none), so the reason a value was refused is logged
at WARN with the statement.
**GetSessionOptions.** `catalog` = the current catalog, `schema` = the
current database (`""` when none), and every session variable `SHOW VARIABLES`
lists, in its text - one representation, the one `SET` accepts back, whatever
the Java type behind the variable (`sql_mode` is a `long` that is set and shown
as names). This is the mapping DSIP-YYY / #67578 A1 give (`GetSessionOptions` ↔
`SHOW VARIABLES`). The consequence for the ADBC driver: read a variable with
the string getter (`get_option`), not `get_option_int`; the driver reports a
type mismatch otherwise.
**CloseSession** already invalidated the bearer token at once
(`FlightTokenManagerImpl.invalidateToken` unregisters the `ConnectContext`
synchronously, and any later call with the token is `UNAUTHENTICATED` at the
authenticator) - verified rather than changed; the fix here is that a failure
now answers `onError` alone instead of `onError` followed by `CLOSED`. The
stale comment claiming that ADBC never calls `CloseSession` is gone: the Go
driver's `Connection.Close()` always calls it, and so does the JDBC driver's
`Connection.close()`.
Type mapping is untouched, per the deferral note under Stage 2 of #67577:
session options carry no Arrow schema.
### Release note
Arrow Flight SQL: the standard session actions are served.
`SetSessionOptions` sets the current catalog (`catalog`), the current database
(`schema`) and session variables (any other name), each answered per name with
`INVALID_NAME` / `INVALID_VALUE` / `ERROR`; `GetSessionOptions` returns them
with every session variable as `SHOW VARIABLES` shows it. With the ADBC Flight
SQL driver this makes `adbc.connection.catalog` / `adbc.connection.db_schema`
and `adbc.flight.sql.session.option.<variable>` work; with the Flight SQL JDBC
driver the `catalog` connection property is applied at connect time (and a
connection whose catalog cannot be set now fails instead of being silently
accepted). `CloseSession` unchanged: the bearer token is invalid at once.
### Check List (For Author)
- Test
- [x] Regression test
- [x] Unit Test
- [x] Manual test (add detailed scripts or steps below)
**Unit tests.** `FlightSessionOptionsTest` (in-process FE, real `SET` /
`USE` / `SWITCH`): the three results, per key; typed and string values; the
empty value restoring the default; quoting under `NO_BACKSLASH_ESCAPES`;
case-insensitive names; the privilege-before-existence order for a user without
privileges; a whole request; and the read-back equal to `VariableMgr.dump`.
`DorisFlightSqlProducerTest`: the listener plumbing of the three actions, an
action waiting for the session's running command and giving up with
`UNAVAILABLE`, `CloseSession` answering once. Protocol goldens
`MysqlPacketGoldenTest` / `FlightResultGoldenTest` unchanged.
**Regression.** `arrow_flight_sql_p0/test_session_options`: drives the
actions with the `FlightSqlClient` shaded inside the Flight SQL JDBC driver on
the classpath (same auth2 handshake as the drivers), checks that `schema` makes
`SHOW TABLES` work in the session, that variables set as options are what `SHOW
VARIABLES` shows, the per-name results, string round-trip, the token being
refused after `CloseSession`; and, black-box, that the JDBC driver connects
with `?catalog=internal` and refuses `?catalog=no_such_catalog` with `Cannot
set session option for catalog`. Whole `arrow_flight_sql_p0` green locally: 10
suites, 0 failed (the docker suite skipped as always), against an FE and a BE
both built from this branch.
**Manual: official ADBC driver** (`adbc_driver_flightsql` 1.12.0, Python):
```
adbc_driver_flightsql 1.12.0 adbc_driver_manager 1.12.0
adbc_current_catalog = 'internal'
adbc_current_db_schema = ''
after set: adbc_current_db_schema = 'adbc_opts_db'
select database() -> [('adbc_opts_db',)]
after set: adbc_current_catalog = 'internal'
query_timeout (string set) = '77'
query_timeout (int set) = '88'
select @@query_timeout -> [(88,)]
enable_profile = 'true'
query_timeout (erased) = '900'
unknown name: ProgrammingError: INVALID_ARGUMENT: [Flight SQL] Could not
set option(s) 'no_such_variable' (invalid name)
unknown schema: ProgrammingError: INVALID_ARGUMENT: [Flight SQL] Could not
set option(s) 'schema' (invalid value)
unknown catalog: ProgrammingError: INVALID_ARGUMENT: [Flight SQL] Could
not set option(s) 'catalog' (invalid value)
bad value: ProgrammingError: INVALID_ARGUMENT: [Flight SQL] Could not set
option(s) 'query_timeout' (invalid value)
read-only variable: ProgrammingError: INVALID_ARGUMENT: [Flight SQL] Could
not set option(s) 'net_buffer_length' (error setting option)
get_option_int on a variable: ProgrammingError: NOT_FOUND: [Flight SQL]
session option query_timeout="900" is not an integer value
all session options: 19040 bytes of JSON, starts with
{"DML_PLAN_RETRY_TIMES":"3","adaptive_pipeline_task_serial_r
closed
```
The `closed` line is `conn.close()`; fe.log shows the `Invalidate bearer
token` of the `CloseSession` it sent, and the session is gone from `SHOW
PROCESSLIST`. The `get_option_int` line is the documented consequence of
returning `SHOW VARIABLES` text: the driver's typed getter refuses it, the
string getter is the one to use.
- Behavior changed:
- [x] Yes. `SetSessionOptions` / `GetSessionOptions` answer instead of
`UNIMPLEMENTED`; a JDBC connection with a `catalog` property that cannot be set
now fails to connect (the driver treats the server's error as fatal, and before
this PR the server's `UNIMPLEMENTED` was swallowed).
- Does this need documentation?
- [x] Yes. Follow-up on the Arrow Flight SQL page: the session options
and the ADBC / JDBC options that map onto them.
### Check List (For Reviewer who merge this PR)
- [ ] Confirm the release note
- [ ] Confirm test cases
- [ ] Confirm document
- [ ] Add branch pick label <!-- Add branch pick label that this PR should
merge into -->
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01Ja986K4PEm2LD8u44L9jd2
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]