morningman opened a new pull request, #69867:
URL: https://github.com/apache/doris/pull/69867

   ### What problem does this PR solve?
   
   Issue Number: None
   
   Related PR: #68631, #68768
   
   Problem Summary:
   
   **In short.** This ports branch-4.1 #68631 (also on branch-4.2) to master. A 
Flight SQL `CreatePreparedStatement` now returns the analyzed result schema of 
the statement, `GetSchema` is implemented for both statements and prepared 
statements, and a prepared execution is checked against the schema that Prepare 
advertised. Before this PR, Prepare always returned a fixed one-column 
placeholder, and `GetSchema` answered UNIMPLEMENTED. The analysis runs in the 
FE only and does not plan or execute the query.
   
   Original author: @Gabriel39
   
   **Background.** Flight SQL clients learn the shape of a result before they 
fetch it. The Arrow Flight SQL JDBC driver prepares every statement and builds 
`ResultSetMetaData` from the dataset schema that `CreatePreparedStatement` 
returns. ADBC exposes the same schema as `execute_schema`, and `GetSchema` is 
the RPC that asks for it without running anything.
   
   **The problem, and what it cost.** On master:
   
   | | master today | with this PR |
   |---|---|---|
   | `CreatePreparedStatement` dataset schema | always `ResultMeta: Utf8`, 
whatever the statement | the statement's columns, names and Arrow types as BE / 
FE will send them; statements whose schema cannot be told before they run keep 
the placeholder (see below) |
   | `GetSchema` for a statement or a prepared statement | UNIMPLEMENTED | the 
analyzed schema, or UNIMPLEMENTED for a statement that keeps the placeholder |
   | Invalid SQL or a missing privilege | fails only at execution | fails at 
Prepare with INVALID_ARGUMENT |
   | Prepared handle used after the session's catalog / database changed, or 
after the result schema changed (e.g. another session's DDL) | runs the 
unqualified SQL in whatever namespace the session has now, with whatever 
columns come back | refused with NOT_FOUND "prepare again", and the result left 
by that execution is dropped |
   | Prepared handle presented by another session | resolved by the identity 
written in the handle | must start with the caller's own identity (from #68768) 
|
   | TIMEV2 / VARBINARY columns in GetTables and Prepare schemas | `Null`, 
while BE sends `float64` / `binary` | `Float64` / `Binary`, as BE sends them |
   
   A client that trusts the advertised schema saw one string column for every 
query, and could not ask for a schema at all without executing.
   
   **How this PR fixes it.**
   
   - `arrowflight/FlightSqlQuerySchema` analyzes the statement on a clone of 
the session variables and checks privileges, as 4.x does: a USE / SWITCH prefix 
is resolved in a temporary namespace, the analysis `StatementContext` is closed 
on the same thread, and the connector statement scope it may have opened is 
closed explicitly. Each field gets the same `doris_type` markers and native 
Variant layout the execution side uses 
(`FlightSqlSchemaHelper.withDorisTypeMetadata` / `nativeVariantField`), and 
TIMESTAMPTZ carries the canonical session time zone that BE stamps on its 
batches.
   - FE-side commands whose result header does not depend on running them now 
report that header without running: HELP, SHOW SNAPSHOT, SHOW CREATE TABLE (one 
header selector shared with `doRun`, including master's stream, plugin-view and 
system-table cases), SHOW PROC `/` and `/current_queries`, SHOW CONFIG, WARM UP 
SELECT, and EXPLAIN PLAN PROCESS (Rule / Before / After).
   - The prepared query kept in `FlightProtocolAdapter` records the SQL, the 
namespace and the advertised schema. A prepared execution re-analyzes the 
statement and compares the result with the advertised schema before it runs, 
and compares the schema it actually produced before it publishes a DoGet 
ticket. On a mismatch, the handle is removed, the execution is cancelled, and 
the deferred coordinator, the channel result and the endpoints are dropped. A 
USE / SWITCH inside the statement rebinds only that handle.
   - `DorisArrowTypeMapping` maps TIMEV2 to `Float64` and VARBINARY to 
`Binary`, the types BE sends. TIMESTAMPTZ keeps the fixed `"UTC"` in GetTables, 
and AGG_STATE keeps `Null` there.
   - `FeServiceClient` rejects a remote Doris metadata timeout whose 
milliseconds do not fit in an int, instead of letting it wrap around.
   
   Where master differs from branch-4.x:
   
   - branch-4.x answers UNIMPLEMENTED at Prepare for statements whose schema it 
cannot tell before they run: DESC, SHOW DATA / PARTITIONS / QUERY STATS / 
PYTHON PACKAGES, other PROC nodes, COPY INTO, WARM UP CLUSTER, multi-statement 
requests that do not start with USE / SWITCH, `?` placeholders, and result 
types the FE cannot describe. Python ADBC falls back to executing without a 
prepared statement, but the Arrow Flight SQL JDBC driver prepares every 
statement and has no fallback, so on master these statements would fail over 
JDBC. `test_arrow_flight_session_lifecycle` runs DESC and `SET @multi = 2; 
SELECT ...` through JDBC. Here, these statements keep today's placeholder 
schema, and their handles are not checked at execution.
   - The analysis and the checks run under the session's command lock 
(`FlightProtocolAdapter.runCommand` / `callCommand`) instead of `synchronized 
(connectContext)`, and the namespace is saved and restored with 
`getNameSpaceContext()` / `restoreNameSpaceContext()`.
   - EXPLAIN PLAN PROCESS has a result over Flight on master, so it gets a 
static header instead of UNIMPLEMENTED. PLAN REPLAYER and SHOW CREATE PROCEDURE 
do not exist on master and are not carried over.
   - SWITCH resolves the default database through the connector provider 
(`Env.defaultDatabaseOnUse`, now shared with `Env.changeCatalog`) instead of 
`EsExternalCatalog.DEFAULT_DB`.
   - TIMESTAMPTZ fields carry the canonical session time zone that BE stamps, 
where branch-4.x uses the raw session value.
   - Only the two handle checks from #68768 that do not depend on parameter 
binding are taken: the caller-prefix check and rebinding only the handle that 
ran the USE / SWITCH. Parameter binding itself is not part of this PR.
   - `GetTableTypes`, the other half of #68631, is implemented by #67899.
   
   Cost: every Prepare, every prepared execution and every `GetSchema` runs one 
more FE analysis of the statement.
   
   **Results.** On a local cluster built from this branch, every suite under 
`arrow_flight_sql_p0` passes: the new `test_schema_only` and 
`test_get_tables_schema`, plus the seven suites that run through Flight JDBC, 
`test_arrow_flight_session_lifecycle` among them. 
`datatype_p0/uuid/test_uuid_arrow_flight` passes too. `test_schema_only` checks 
the advertised schema of each kind of statement against what its execution 
actually returns, including a `group_concat_state` AGG_STATE, which BE sends as 
Utf8.
   
   ### Release note
   
   Flight SQL `CreatePreparedStatement` and `GetSchema` return the analyzed 
result schema of a statement. Statements whose schema is only known once they 
run keep returning the placeholder schema. A prepared statement is refused with 
NOT_FOUND and must be prepared again when the session's catalog or database, or 
the result schema, changed since it was prepared. Invalid SQL and missing 
privileges are reported at Prepare. Flight SQL now describes TIMEV2 columns as 
Float64 and VARBINARY columns as Binary, matching the data it sends.
   
   ### Check List (For Author)
   
   - Test <!-- At least one of them must be included. -->
       - [x] Regression test
           - New `arrow_flight_sql_p0/test_schema_only`. On a local cluster 
built from this branch, all 21 suites under `arrow_flight_sql_p0` and 
`datatype_p0/uuid/test_uuid_arrow_flight` pass. 
`external_table_p0/iceberg/test_iceberg_arrow_flight_split_source` needs the 
docker environment and was not run locally.
       - [x] Unit Test
           - New `DorisFlightSqlSchemaTest`, `FlightSqlExecutionSchemaTest`, 
`SchemaOnlyCommandMetadataTest` and `FeServiceClientTest`. Updated 
`DorisArrowTypeMappingTest` and `DorisFlightSqlProducerTest`; its allocator 
leak test now uses statements that keep the placeholder. Together with the 
existing tests of the changed classes (`mvn -pl :fe-core -am test`, Checkstyle 
included): 32 classes, 390 tests, all pass.
           - Each of three mutations makes only its own tests fail: skipping 
the comparison after execution, answering UNIMPLEMENTED at Prepare as 
branch-4.x does, and dropping the caller-prefix check on handles.
       - [ ] Manual test (add detailed scripts or steps below)
       - [ ] No need to test or manual test. Explain why:
   - Behavior changed:
       - [ ] No.
       - [x] Yes. Prepare returns the analyzed schema, and GetSchema is 
implemented. Invalid SQL and missing privileges fail at Prepare. A prepared 
handle is refused after a namespace or schema change, or when another session 
presents it. TIMEV2 and VARBINARY are described as Float64 and Binary in 
GetTables and Prepare schemas.
   - Does this need documentation?
       - [x] No.
       - [ ] Yes.
   
   ### Check List (For Reviewer who merge this PR)
   
   - [ ] Confirm the release note
   - [ ] Confirm test cases
   - [ ] Confirm document
   - [ ] Add branch pick label
   


-- 
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]

Reply via email to