Skip to content

[fix](flight-sql) Return analyzed result schemas from Prepare and GetSchema - #69867

Merged
morningman merged 8 commits into
apache:masterfrom
morningman:b2m-t48-flight-analyzed-schema
Nov 4, 2026
Merged

morningman merged 8 commits into
apache:masterfrom
morningman:b2m-t48-flight-analyzed-schema

Conversation

@morningman

@morningman morningman commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

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, and results with an untyped NULL column, 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 fails only at execution fails at Prepare with INVALID_ARGUMENT
A query whose privilege check fails fails only at execution still fails only at execution: Prepare keeps the placeholder, because only the check on the executed plan decides (see below), and GetSchema answers UNIMPLEMENTED without a schema
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: 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.
  • Privileges are checked on the analyzed plan after the CTEs that execution inlines have been inlined (PullUpCteAnchor, CTEInline, as the rewriter does before its own check). A CTE used once is therefore checked only on the columns the query reads, and a CTE used more than once on every column it selects, which is also how execution checks them. The rewriter normalizes the plan further before it checks privileges. For example, it drops an ORDER BY under a derived table, so the analyzed plan can read a column that the executed plan does not. When the analyzed plan is denied, the statement keeps the placeholder and execution decides. No schema is disclosed in either case.
  • 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, and EXPLAIN PLAN PROCESS (Rule / Before / After).
  • ShowStoragePolicyCommand.getMetaData() returned the four-column USING header for every form, while plain SHOW STORAGE POLICY answers the seven columns of StoragePolicy.STORAGE_META_DATA. It now returns the header of the form that runs. This also corrects the column definitions that MySQL COM_STMT_PREPARE reports for the statement. No other SHOW command declares a header that differs from the one it returns.
  • WARM UP SELECT keeps the placeholder. It answers OK instead of its statistics whenever planning proves its source empty: a predicate that folds to false, no partition left after pruning, or partitions without data.
  • A result with an untyped NULL column keeps the placeholder too, at any depth (SELECT NULL, SELECT id, NULL FROM t, ARRAY(NULL)). DorisArrowTypeMapping gives NULL_TYPE Arrow Null, but BE builds such a column as a UInt8 flagged as a NULL literal and sends it as nullable Bool, so an advertised Null would expire the handle at every execution. A typed NULL (CAST(NULL AS VARCHAR(20))) is described as its type.
  • 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. It is the session's next request whether or not these checks pass: before them, it drops what the previous request left, as a statement's execution does, while Prepare and GetSchema are metadata requests and leave it. On a mismatch, the handle is removed, the execution is cancelled, and the deferred coordinator, the channel result and the endpoints are dropped. A handle whose statement can no longer be described when it is re-analyzed, for example after a column grant was revoked, expires the same way, and the statement prepared again keeps the placeholder. 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.
  • branch-4.x checks privileges on the analyzed plan as it is, so a CTE used once is checked on every column it selects, and any denial fails Prepare. Here, CTEs are inlined first, and a remaining denial keeps the placeholder.
  • branch-4.x advertises the statistics header for WARM UP SELECT and then rejects, as a schema change, an execution whose source turned out empty. Here, WARM UP SELECT keeps the placeholder.
  • branch-4.x advertises Arrow Null for an untyped NULL column, which BE sends as Bool, so every prepared execution of such a query is rejected as a schema change. Here, such a result keeps the placeholder.
  • 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 [fix](flight-sql) Support prepared query parameter binding on branch-4.1 #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 [fix](flight-sql) Return analyzed query schemas and table type metadata #68631, is implemented by [fix](arrow-flight) Support table type metadata and filters #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. That covers queries, the side-effect-free frontend-side commands executed as prepared statements (SHOW STORAGE POLICY among them), a WARM UP SELECT on an empty table (executed in cloud mode, the only mode that warms up internal tables), and 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, and results with an untyped NULL column, keep returning the placeholder schema, and so do queries whose privilege check fails, which still fail when they run. 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 is reported at Prepare. Flight SQL now describes TIMEV2 columns as Float64 and VARBINARY columns as Binary, matching the data it sends. The prepared metadata of SHOW STORAGE POLICY lists the seven columns it returns.

Check List (For Author)

  • Test
    • Regression test
      • New arrow_flight_sql_p0/test_schema_only. On a local cluster built from this branch, all suites under arrow_flight_sql_p0 and datatype_p0/uuid/test_uuid_arrow_flight pass, and query_p0/operator/test_sort_operator passes over Flight SQL (-g arrow_flight_sql), where the previous head fails it with "Prepared statement schema changed". external_table_p0/iceberg/test_iceberg_arrow_flight_split_source needs the docker environment and was not run locally.
    • 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 (run-fe-ut.sh plus fe-core Checkstyle): 45 classes, 555 tests, all pass.
      • Each of eleven mutations makes only its own tests fail: skipping the comparison after execution, answering UNIMPLEMENTED at Prepare as branch-4.x does, dropping the caller-prefix check on handles, checking privileges without inlining CTEs, failing Prepare on a privilege denial, answering the USING header for every SHOW STORAGE POLICY, advertising a header for WARM UP SELECT, advertising Arrow Null for an untyped NULL column, checking a prepared handle before ending the previous request, ending the previous request on GetSchema too, and keeping a checked handle whose statement can no longer be described.
    • Manual test (add detailed scripts or steps below)
    • No need to test or manual test. Explain why:
  • Behavior changed:
    • No.
    • Yes. Prepare returns the analyzed schema, and GetSchema is implemented. Invalid SQL fails at Prepare; a failing privilege check still fails at execution. 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. The prepared metadata of SHOW STORAGE POLICY lists its seven columns.
  • Does this need documentation?
    • No.
    • Yes.

Check List (For Reviewer who merge this PR)

  • Confirm the release note
  • Confirm test cases
  • Confirm document
  • Add branch pick label

morningman and others added 5 commits October 30, 2026 00:15
…command

### What problem does this PR solve?

Issue Number: None

Related PR: apache#68631

Problem Summary:

branch-4.1 apache#68631 (also on branch-4.2) makes Flight SQL Prepare and
GetSchema advertise the real result schema of a statement. For a command
that answers from the frontend, that schema is its result header. Several
commands only pick their header while they run, by what they find, and
master has no way to ask them for it without running them:

- HELP returns a topic, keyword or category header depending on what the
  help catalog matches.
- SHOW SNAPSHOT returns the detail header only when both the SNAPSHOT and
  TIMESTAMP filters are given, after validating them.
- SHOW CREATE TABLE returns a table, view, stream or materialized view
  header depending on the table.
- SHOW PROC builds its header from the node it opens.
- SHOW CONFIG and WARM UP SELECT return rows, but their
  `getResultSetMetaData()` is the empty default of `Command`.

This commit adds, as apache#68631 does, a way to resolve each header without
running the command: `HelpCommand.getMetaData(ConnectContext)` (the help
catalog's matches, without reading topic text),
`ShowSnapshotCommand.getMetaData(ConnectContext)` (validates the filters,
never touches the repository, and is now what `handleShowSnapshot` uses),
`ShowCreateTableCommand.getMetaData(ConnectContext)`,
`ShowProcCommand.getMetaData(ConnectContext)` (the privilege check, then
the static headers of `/` and `/current_queries`, and null for any other
node, since opening one can reach remote nodes), the
`getResultSetMetaData()` overrides of `ShowConfigCommand` and
`WarmupSelectCommand`, and catalog / database getters on `UseCommand`.

Where master differs from branch-4.x:

- master's SHOW CREATE TABLE has more header branches than 4.x (stream
  tables, the sys-table redirect, plugin-driven tables and views). The
  header choice of `doRun` is factored out into one method that the new
  `getMetaData(ConnectContext)` shares, instead of a second copy.
- master has no SHOW CREATE PROCEDURE (PL/SQL was removed in apache#58700), and
  its SHOW PROCEDURE STATUS already reports its header as a ShowCommand.

### Release note

None

### Check List (For Author)

- Test: Unit Test
    - `SchemaOnlyCommandMetadataTest` (from apache#68631): the HELP headers match
      what `doRun` returns for a topic, a keyword with one or several
      topics, one or several categories and no match, without reading the
      topic; SHOW SNAPSHOT resolves its header without the backup handler
      and rejects an invalid filter
    - `ShowCreateTableCommandTest`, `ShowSnapshotCommandTest`,
      `ShowConfigCommandTest` still pass
- Behavior changed: No
- Does this need documentation: No

Co-authored-by: Gabriel <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
### What problem does this PR solve?

Issue Number: None

Related PR: apache#68631, apache#67577

Problem Summary:

`DorisArrowTypeMapping` is the one place FE turns a Doris type into an
Arrow type. It describes TIMEV2 and VARBINARY as Arrow Null, while BE
sends float64 and binary for them (`convert_to_arrow_type` in
`be/src/format/arrow/arrow_row_batch.cpp`). Its class comment and
`DorisArrowTypeMappingTest` record these cells as known to disagree with
BE and kept until the BE Arrow type layer is reworked (apache#67577).

The next commit makes Flight SQL Prepare advertise the analyzed result
schema from this mapping and checks every prepared execution against the
schema BE returns. With the two Null cells, every prepared execution of a
TIME or VARBINARY result would be rejected as a schema change.
branch-4.1 apache#68631 (also on branch-4.2) corrects the same two cells in its
shared type mapping.

TIMEV2 now maps to Arrow Double and VARBINARY to Arrow Binary, as BE sends
them. GetTables reports the same types in its `table_schema` for such
columns. TIMESTAMPTZ (literal "UTC" where BE stamps the session time zone)
and AGG_STATE (Null) stay as they are; Prepare handles those two on its
own path.

### Release note

Arrow Flight SQL GetTables describes TIME and VARBINARY columns as Arrow
Double and Binary, the types the results carry, instead of Null.

### Check List (For Author)

- Test: Unit Test
    - `DorisArrowTypeMappingTest`: the TIMEV2 and VARBINARY rows are
      updated; the other rows and `test_get_tables_schema` (no TIME or
      VARBINARY column) are unchanged
- Behavior changed: Yes. GetTables `table_schema` reports Double for TIME
  columns and Binary for VARBINARY columns
- Does this need documentation: No

Co-authored-by: Gabriel <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
…Schema

### What problem does this PR solve?

Issue Number: None

Related PR: apache#68631, apache#68768

Problem Summary:

On master, Flight SQL `CreatePreparedStatement` registers the SQL and
answers with the same one-column `ResultMeta: Utf8` dataset schema for
every statement, without analyzing it, and `GetSchema` for a statement or
a prepared statement is UNIMPLEMENTED. A client that reads the prepared
schema gets wrong columns: Python ADBC `adbc_execute_schema()` uses it
directly. An invalid statement also prepares successfully and only fails
when it runs. branch-4.1 apache#68631 (also on branch-4.2) fixes this.

This commit ports apache#68631 onto master's Flight session layer:

- `FlightSqlQuerySchema` resolves the result schema with the FE-only
  `PrepareCommandPlanner` and `CheckPrivileges`, without building an
  executor or sending fragments. It works on a clone of the session
  variables, applies the dialect conversion and the original-SQL parse
  fallback as execution does, resolves USE / SWITCH prefixes in a
  namespace it puts back afterwards, and builds the Arrow fields as BE
  does, with the `doris_type` markers execution adds at every depth and
  native Variant fields. A command's schema is its result header, or the
  OK `StatusResult` for the no-row command categories and the concrete
  DML commands.
- Prepare analyzes before it registers a handle, so a statement that
  cannot run leaves none behind. Prepare fails with INVALID_ARGUMENT for
  invalid SQL or a missing privilege. Otherwise the handle keeps the SQL,
  the namespace it was prepared in and the advertised schema.
- A prepared execution analyzes the SQL again and rejects a schema
  different from the advertised one, for example after a `sql_mode`
  change. After execution it compares the schema of the actual result too.
  On a mismatch it drops the handle, cancels the query, drops what the
  request left (deferred coordinator, cached result, endpoints) and fails
  with NOT_FOUND. A handle used in another catalog or database expires;
  a USE or SWITCH in the statement itself rebinds only that handle.
- GetSchema for a statement and for a prepared statement return the
  analyzed schema.

Where master differs from branch-4.x:

- Master serializes the commands of a session with
  `FlightProtocolAdapter.callCommand`. The analysis runs inside it,
  instead of the `synchronized (connectContext)` blocks of 4.x, so
  execution cannot touch the analysis's temporary session state. The
  prepared statements are kept in `FlightProtocolAdapter` as
  `PreparedQuery`, where master keeps them, and `ConnectContext` only
  forwards. The namespace is captured and restored with
  `NameSpaceContext`, which does not contact the catalog.
- A statement whose result schema only running it can tell keeps today's
  `ResultMeta` placeholder on Prepare, and its executions are not
  checked. That covers DESCRIBE, SHOW DATA / PARTITIONS / QUERY STATS /
  PYTHON PACKAGES, PROC nodes other than `/` and `/current_queries`, COPY
  INTO, WARM UP CLUSTER, a request whose statements before the last are
  not USE or SWITCH, `?` placeholders, and a result type FE cannot
  describe as BE sends it. 4.x answers UNIMPLEMENTED there. Python ADBC
  then executes without preparing, but the Arrow Flight SQL JDBC driver
  prepares every statement and does not fall back, so DESC and
  `SET @v = 1; SELECT @v`, which master's
  `test_arrow_flight_session_lifecycle` runs over JDBC, would fail. GetSchema
  for such a statement, or for such a handle, answers UNIMPLEMENTED.
- EXPLAIN PLAN PROCESS has a Flight result on master (Rule / Before /
  After), so it gets that header; 4.x rejects it. PLAN REPLAYER (removed
  by apache#67811) and the Lance index commands are not on master.
- SWITCH resolves the default database of a catalog without a database
  layer through the connector provider (`Env.defaultDatabaseOnUse`, now
  shared with `Env.changeCatalog`), where 4.x named the ES catalog.
- TIMESTAMPTZ results carry the canonical session time zone that FE sends
  BE (`TimeUtils.getCanonicalTimeZone`), as BE stamps it.
- Closing the analysis's `StatementContext` leaves the connector statement
  scope of a Flight statement to the query-finish cleanup, which never runs
  for an analysis, so the analysis closes that scope itself.
- From branch-4.2 apache#68768, which is otherwise left to the next batch (it
  adds parameter binding): a prepared statement handle must carry the
  calling session's peer identity and a non-empty id. Get-flight-info,
  get-schema and close of a prepared statement all check this, and the
  session comes from the caller rather than from the handle.

Each Prepare, and each execution of a prepared statement whose schema was
advertised, runs one more FE analysis of the statement. Physical planning
and execution are unchanged.

### Release note

Arrow Flight SQL Prepare and GetSchema return the real result schema of a
query instead of a placeholder, and a prepared statement whose result
schema changes must be prepared again.

### Check List (For Author)

- Test: Unit Test
    - `DorisFlightSqlSchemaTest` (from apache#68631, on `ConnectContext.forFlight`):
      analyzed schemas, privileges, namespaces, schema drift, command
      headers, the placeholder for statements that cannot be told, and the
      handle checks
    - `FlightSqlExecutionSchemaTest` (from apache#68631): the analyzed schema
      matches the labels and types of a real `handleQuery`
    - `DorisFlightSqlProducerTest#createPreparedStatementDoesNotLeakChannelAllocator`
      now prepares statements that keep the placeholder and still checks
      the channel allocator returns to zero
- Behavior changed: Yes. Prepare fails for invalid SQL or a missing
  privilege instead of the execution; a prepared statement expires when
  its schema changes or it is used in another namespace
- Does this need documentation: No

Co-authored-by: Gabriel <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
### What problem does this PR solve?

Issue Number: None

Related PR: apache#68631

Problem Summary:

branch-4.1 apache#68631 (also on branch-4.2) adds `test_schema_only`, which
drives Prepare, GetSchema and prepared execution through a plain Flight
SQL client against a cluster. This commit brings it to master:

- Unaliased literals, functions and CASTs keep the labels execution
  gives them.
- Analyzed schemas for typed, nested, information_schema and FE-local
  results, and the headers of the commands that resolve them without
  running.
- `sql_mode` drift expires a prepared handle.
- SET and SET_VAR hints are not applied by schema discovery.
- Invalid SQL is rejected with INVALID_ARGUMENT.
- A handle that changes its own namespace stays usable, while a handle
  used in another namespace expires.

Adapted to master: the SHOW CREATE PROCEDURE and PLAN REPLAYER items are
dropped (neither exists on master), EXPLAIN PLAN PROCESS is checked for
its Rule / Before / After header, and statements whose schema only
running them can tell (SHOW PYTHON PACKAGES, SHOW QUERY STATS, DESC,
`SET ...; SELECT ...`) are checked to keep the placeholder on Prepare,
answer UNIMPLEMENTED on GetSchema and still run. The
`group_concat_state` case also executes, so the advertised UTF8 type is
checked against what BE sends.

### Release note

None

### Check List (For Author)

- Test: Regression test
    - `arrow_flight_sql_p0/test_schema_only`
- Behavior changed: No
- Does this need documentation: No

Co-authored-by: Gabriel <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
…verflow

### What problem does this PR solve?

Issue Number: None

Related PR: apache#68631

Problem Summary:

`FeServiceClient`, which reads the metadata of a remote Doris catalog,
takes the catalog's timeout in seconds and gives the pooled Thrift sockets
milliseconds. master already converts the unit (apache#60557), but with
`(int) TimeUnit.SECONDS.toMillis(timeoutS)`, so a timeout over about 24
days wraps around to a negative or small socket timeout. branch-4.1
apache#68631 (also on branch-4.2) converts with an overflow check. This commit
does the same with `Math.toIntExact`, so such a value fails when the
client is built.

### Release note

None

### Check List (For Author)

- Test: Unit Test
    - `FeServiceClientTest` (from apache#68631): `Integer.MAX_VALUE` seconds is
      rejected, and a metadata call answered by a local Thrift server after
      100 ms succeeds under a 10 second timeout
- Behavior changed: No
- Does this need documentation: No

Co-authored-by: Gabriel <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
@hello-stephen

Copy link
Copy Markdown
Contributor

Thank you for your contribution to Apache Doris.
Don't know what should be done next? See How to process your PR.

Please clearly describe your PR:

  1. What problem was fixed (it's best to include specific error reporting information). How it was fixed.
  2. Which behaviors were modified. What was the previous behavior, what is it now, why was it modified, and what possible impacts might there be.
  3. What features were added. Why was this function added?
  4. Which code was refactored and why was this part of the code refactored?
  5. Which functions were optimized and what is the difference before and after the optimization?

@morningman

Copy link
Copy Markdown
Contributor Author

run buildall

@morningman

Copy link
Copy Markdown
Contributor Author

/review

@github-actions github-actions Bot 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.

Reviewed exact head b4f86e886005b8323596ea8b74ef1c0b9f4e9e2c. Three P2 correctness regressions are reported inline: a single-use CTE can fail Prepare under column-level grants; ordinary SHOW STORAGE POLICY advertises the wrong header; and an empty-input WARM UP SELECT returns OK instead of its advertised statistics header. These cases should be fixed and covered by prepared-execution tests.

Critical checkpoint conclusions:

  • Goal: analyzed Prepare/GetSchema and prepared-schema validation are implemented, with the three exceptions above. The unit and Flight regression tests were inspected.
  • Scope and reuse: discovery reuses PrepareCommandPlanner, shared Arrow mapping/metadata helpers, namespace snapshots and the session command lock. The independent timeout conversion rejects int-millisecond overflow.
  • Concurrency: action workers and RPC commands serialize through the session command lock, protecting the prepared map, borrowed context and handle namespace. Existing bounded, ID-ordered table locking is preserved; no new lock-order defect was found.
  • Lifecycle: analysis closes its StatementContexts/connector scopes and restores captured state in finally. Schema-only requests allocate schema objects rather than Arrow result vectors. Mismatch cancellation and result/deferred-resource cleanup precede ticket publication.
  • Configuration: none added; relevant existing settings are re-read for analysis and execution.
  • Compatibility: dynamic/unsupported statements retain placeholder preparation and unchecked execution. Cloud and non-cloud use shared paths. The inline findings identify existing behavior rejected by the new checks.
  • Thrift/protobuf: no gensrc definitions or field IDs changed.
  • Parallel paths: reviewed statement/prepared GetSchema, Prepare, checked/placeholder execution, FE command/BE query results, namespace prefixes and shared MySQL command helpers. TIMEV2/VARBINARY and recursive logical metadata agree with the BE converters.
  • Conditional/error checks: schema mismatches fail before publication and unknown schemas fall back explicitly; two reported command paths incorrectly claim a stable header. The CTE check runs before required-column handling is equivalent to execution.
  • Test coverage: invalid SQL, grants, session restoration, namespace/SQL-mode drift, caller handles, nested types, labels/nullability, cleanup, allocator use and timeout conversion have coverage. The three concrete inline scenarios are missing.
  • Test results: no golden output files changed. Expected schemas were compared with production metadata/conversion code. Tests were inspected, not executed; the author's reported passing runs were not independently reproduced.
  • Observability: analysis errors retain their cause, schema expiry provides prepare-again guidance, and cancellation failures are logged. No separate logging/metrics defect was established.
  • Persistence/transactions: no EditLog, storage-format, visible-version, delete-bitmap or transaction/failover protocol changes.
  • Writes/atomicity: discovery does not execute commands; existing DML transactions remain in the ordinary executor.
  • FE/BE state: no new transmitted variables. TIMESTAMPTZ follows the existing canonical session-zone convention.
  • Performance: repeated analysis intentionally validates drift; no additional concrete allocation or complexity defect was found.
  • Other concerns and user focus: no additional user-provided focus or existing inline findings. All changed files, candidates and risk focuses have dispositions; no concrete static coverage gap remains.

The review converged after two rounds: every assigned reviewer returned NO_NEW_VALUABLE_FINDINGS in round 2. Builds, tests, benchmarks and runtime Arrow/JDBC validation were not run under the static-only review contract.

Review converged after 2 round(s).

CascadesContext cascades = planner.getCascadesContext();
Plan analyzed = cascades.getRewritePlan();
// PrepareCommandPlanner stops before the rewrite phase that normally checks privileges.
new CheckPrivileges().rewriteRoot(analyzed, cascades.getCurrentJobContext());

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.

[P2] Preserve required-column pruning for single-use CTEs

With only SELECT(id) granted on t(id, secret), WITH c AS (SELECT id, secret FROM t) SELECT id FROM c succeeds through ordinary planning: CTEInline removes the single deterministic reference's anchor, then CheckPrivileges prunes the unused secret. At this analyzed-only point the reduced tree is still:

CTEAnchor
  Producer: Project(id, secret, Scan(t))
  Project(id, Consumer(c))

ColumnPruning preserves the producer's outputs here, and CheckPrivileges.visitLogicalCTEConsumer explicitly checks its complete output bitmap, so this call demands SELECT(secret). Prepare/GetSchema now reject an otherwise valid query, and Flight JDBC fails before execution. Apply execution-equivalent CTE inlining/required-column handling before this check.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 7c7393c8c18. Confirmed. Prepare checked privileges on the analyzed plan, where the CTE is still a LogicalCTEAnchor over its producer and ColumnPruning treats every producer output as needed, so a CTE used once was checked on every column it selects. Execution first inlines that CTE (CTEInline, inline_cte_referenced_threshold = 1) and checks only the columns the query reads. The same applies to SELECT * in the CTE and to column-level policies of external catalogs. FlightSqlQuerySchema.checkPrivileges now runs PullUpCteAnchor and CTEInline on the analyzed plan before CheckPrivileges, as the rewriter does before its own check. A CTE used more than once is still checked on its full output, and execution does the same (TestCheckPrivileges pins that). Inlining alone does not make the two checks equivalent, because the rewriter normalizes the plan further before it checks privileges. For instance, it drops an ORDER BY under a derived table, so SELECT id FROM (SELECT id, secret FROM t ORDER BY secret) d runs for a user with only SELECT_PRIV(id), while its analyzed plan still reads secret. A denial on the analyzed plan therefore no longer fails Prepare. The statement keeps the placeholder schema and runs unchecked, so execution decides as it did before this PR, and GetSchema answers UNIMPLEMENTED with the denial and no schema. DorisFlightSqlSchemaTest#columnGrantsAreCheckedAsExecutionChecksThem grants SELECT_PRIV(id) and checks each statement against the rewriter (PlanChecker). Four single-use CTE shapes return [id] from GetSchema and Prepare. SELECT name, the CTE used twice and the derived-table ORDER BY keep the placeholder. The test fails without the inlining, and schemaDiscoveryChecksTablePrivileges fails if a denial fails Prepare again.

}
return stringMetadata("Explain String(Nereids Planner)");
}
ResultSetMetaData metadata = command.getResultSetMetaData();

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.

[P2] Resolve the ordinary SHOW STORAGE POLICY header

For SHOW STORAGE POLICY, ShowStoragePolicyCommand.getMetaData() always returns the four-column USING header, while doRun() delegates to PolicyMgr.showStoragePolicy() and returns the seven-column StoragePolicy.STORAGE_META_DATA, even when there are no policy rows. This generic getter therefore advertises four columns at Prepare, and executePreparedStatement subsequently rejects every execution as a schema change. Flight JDBC prepares every statement, so the command stops working through that driver. Resolve the ordinary versus USING header here, or retain the placeholder until the correct header is available.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 7c7393c8c18. Confirmed, and the wrong header predates this PR. Since #44393, ShowStoragePolicyCommand.getMetaData() has returned the four-column USING header for every form. Plain SHOW STORAGE POLICY returns PolicyMgr.showStoragePolicy(), whose header is the seven-column StoragePolicy.STORAGE_META_DATA whether or not there are rows. Nothing compared the two until now: MySQL COM_STMT_PREPARE has reported the four columns as well, but MySQL clients use the column definitions that come with the execution. getMetaData() now returns the header doRun answers with for each form, so Prepare advertises seven columns for the plain form and four for USING and USING FOR. I also went through every ShowCommand subclass looking for the same mismatch, a getMetaData() that differs from the header built by doRun or by the manager it delegates to, and found no other. The headers that depend on more than the syntax are the ones this PR already resolves (HELP, SHOW SNAPSHOT, SHOW CREATE TABLE, SHOW PROC) or keeps on the placeholder. SchemaOnlyCommandMetadataTest#storagePolicyHeadersMatchExecution compares the header of doRun with getMetaData() for all three forms and fails without the fix. test_schema_only now also executes its side-effect-free frontend-side statements as prepared statements, SHOW STORAGE POLICY among them, and compares the executed schema with the advertised one. Until now it only compared Prepare with GetSchema, and both come from the same analysis.

@Override
public ShowResultSetMetaData getResultSetMetaData() {
// Unlike ordinary INSERT, warmup returns per-backend statistics rather than OK.
return metaData;

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.

[P2] Keep empty warmups consistent with the advertised header

With file caching enabled, WARM UP SELECT id FROM t WHERE FALSE on an ordinary table optimizes to BlackholeSink(EmptyRelation). This sets emptyInsert=true, so requiresTransaction() is false and InsertIntoTableCommand.runInternal() returns before registering the warmup statistics listener. Execution succeeds without a result set, and Flight synthesizes the one-column StatusResult. Advertising these six columns makes the new prepared-execution comparison reject that successful empty warmup with NOT_FOUND/schema changed. Emit a statistics result on the empty success path, or retain placeholder preparation until execution guarantees this header; cover the prepared execution of a false-predicate warmup.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 7c7393c8c18. Confirmed, and the case is wider than WHERE FALSE. Planning replaces the source with an empty relation whenever it proves the source empty: a predicate that folds to false, partition pruning that leaves no partition, or selected partitions without data (PruneEmptyPartition). Warming up a table that has no data yet is enough. The sink is then BlackholeSink(EmptyRelation) and requiresTransaction() is false. InsertIntoTableCommand.runInternal returns before the statistics listener is registered, and Flight answers its synthesized StatusResult. So the result header of WARM UP SELECT depends on what planning finds, which is the kind of statement this PR already keeps on the placeholder. WARM UP SELECT now joins that list in FlightSqlQuerySchema.commandMetadata: Prepare advertises the placeholder, its executions are not compared, and GetSchema answers UNIMPLEMENTED, as on master before this PR. The getResultSetMetaData() override is removed, so MySQL COM_STMT_PREPARE describes the statement as it does on master. Another fix would have been to make the command answer its statistics, with an all-zero TOTAL row, on the empty path as well. That changes what MySQL clients get for an empty warmup, so it is not part of this PR. DorisFlightSqlSchemaTest now lists WARM UP SELECT among the placeholder statements. warmupSelectRunsUncheckedWhateverItsSourceHolds runs one prepared warmup whose execution answers OK and one that answers statistics; both checks fail if WARM UP SELECT gets an advertised header again. test_schema_only prepares a WARM UP SELECT on an empty table and, in cloud mode (the only mode that warms up internal tables), runs it.

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-H: Total hot run time: 28283 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit b4f86e886005b8323596ea8b74ef1c0b9f4e9e2c, data reload: false

------ Round 1 ----------------------------------
============================================
q1	17590	5142	5079	5079
q2	2129	308	245	245
q3	10264	1349	809	809
q4	4683	461	336	336
q5	7415	786	535	535
q6	183	185	147	147
q7	745	807	590	590
q8	9282	1341	1032	1032
q9	5472	4193	4154	4154
q10	6841	1275	1029	1029
q11	409	254	230	230
q12	629	393	285	285
q13	18080	3550	2992	2992
q14	277	273	248	248
q15	q16	715	701	655	655
q17	1179	743	577	577
q18	7016	6157	6103	6103
q19	1100	949	603	603
q20	392	332	232	232
q21	4527	2147	2347	2147
q22	349	292	255	255
Total cold run time: 99277 ms
Total hot run time: 28283 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	5609	5597	5604	5597
q2	665	552	569	552
q3	5231	5434	4858	4858
q4	1997	2125	1545	1545
q5	5257	5125	5367	5125
q6	253	199	154	154
q7	2300	2120	1888	1888
q8	2976	2638	2689	2638
q9	8045	7937	7913	7913
q10	3764	3675	3392	3392
q11	604	435	421	421
q12	694	673	542	542
q13	3633	3852	3286	3286
q14	317	327	282	282
q15	q16	693	699	657	657
q17	8334	7097	6817	6817
q18	12993	12194	12825	12194
q19	902	775	777	775
q20	2161	2133	1919	1919
q21	5588	4533	4651	4533
q22	482	436	425	425
Total cold run time: 72498 ms
Total hot run time: 65513 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 150494 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit b4f86e886005b8323596ea8b74ef1c0b9f4e9e2c, data reload: false

query5	4302	576	438	438
query6	452	213	193	193
query7	4824	446	252	252
query8	348	168	150	150
query9	8763	3187	3179	3179
query10	468	315	250	250
query11	5978	3846	3558	3558
query12	155	90	88	88
query13	1257	424	309	309
query14	6123	3879	3564	3564
query14_1	3402	3403	3389	3389
query15	206	197	177	177
query16	950	425	390	390
query17	829	639	504	504
query18	2399	414	297	297
query19	192	163	136	136
query20	84	80	80	80
query21	198	122	107	107
query22	13013	13021	12835	12835
query23	13299	12645	12351	12351
query23_1	12432	12523	12514	12514
query24	7392	811	458	458
query24_1	491	474	478	474
query25	511	418	366	366
query26	1250	255	144	144
query27	2744	439	271	271
query28	4590	2019	2034	2019
query29	1574	575	424	424
query30	292	217	181	181
query31	999	920	811	811
query32	138	84	89	84
query33	506	290	248	248
query34	881	851	490	490
query35	704	752	630	630
query36	812	809	718	718
query37	120	95	81	81
query38	1738	1732	1649	1649
query39	793	782	740	740
query39_1	738	727	740	727
query40	220	114	94	94
query41	65	64	61	61
query42	83	82	81	81
query43	352	372	308	308
query44	1295	691	691	691
query45	174	173	171	171
query46	830	960	577	577
query47	2986	3011	2642	2642
query48	290	302	215	215
query49	570	398	284	284
query50	698	271	211	211
query51	10417	10524	10352	10352
query52	76	91	71	71
query53	184	205	165	165
query54	237	199	170	170
query55	70	66	62	62
query56	224	207	214	207
query57	1638	1608	1556	1556
query58	284	259	250	250
query59	2162	2208	1975	1975
query60	285	234	220	220
query61	153	165	163	163
query62	389	345	319	319
query63	182	153	155	153
query64	2570	903	753	753
query65	3306	3244	3268	3244
query66	1785	411	294	294
query67	20113	20153	19632	19632
query68	2951	1010	623	623
query69	385	288	253	253
query70	825	764	767	764
query71	288	232	213	213
query72	2705	2589	2476	2476
query73	507	522	296	296
query74	4622	4533	4332	4332
query75	2331	2286	1921	1921
query76	2313	1023	591	591
query77	347	381	279	279
query78	9582	9757	9227	9227
query79	914	851	526	526
query80	480	430	379	379
query81	517	322	274	274
query82	209	134	106	106
query83	197	198	184	184
query84	300	128	108	108
query85	823	474	441	441
query86	257	244	230	230
query87	1977	1946	1790	1790
query88	3693	2778	2812	2778
query89	316	279	260	260
query90	2095	170	165	165
query91	158	154	126	126
query92	93	91	88	88
query93	976	992	583	583
query94	447	307	268	268
query95	562	372	301	301
query96	682	488	240	240
query97	2458	2431	2331	2331
query98	160	155	146	146
query99	744	764	649	649
Total cold run time: 232238 ms
Total hot run time: 150494 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
ClickBench: Total hot run time: 24.27 s
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/clickbench-tools
ClickBench test result on commit b4f86e886005b8323596ea8b74ef1c0b9f4e9e2c, data reload: false

query1	0.01	0.00	0.01
query2	0.09	0.05	0.06
query3	0.27	0.09	0.09
query4	1.61	0.10	0.08
query5	0.23	0.24	0.24
query6	1.17	0.67	0.68
query7	0.04	0.01	0.01
query8	0.05	0.04	0.03
query9	0.40	0.35	0.36
query10	0.58	0.57	0.57
query11	0.20	0.15	0.14
query12	0.17	0.15	0.14
query13	0.51	0.49	0.50
query14	1.03	1.02	1.03
query15	0.62	0.60	0.62
query16	0.30	0.35	0.34
query17	1.16	1.17	1.08
query18	0.21	0.20	0.18
query19	2.06	1.98	1.99
query20	0.02	0.02	0.01
query21	15.48	0.21	0.13
query22	4.78	0.05	0.06
query23	16.26	0.33	0.12
query24	2.91	0.53	0.36
query25	0.12	0.05	0.05
query26	1.26	0.21	0.15
query27	0.05	0.05	0.05
query28	3.39	0.74	0.35
query29	12.49	4.25	3.32
query30	0.28	0.13	0.10
query31	2.77	0.54	0.33
query32	3.21	0.60	0.51
query33	3.14	3.23	3.17
query34	15.37	3.99	3.38
query35	3.32	3.35	3.36
query36	0.54	0.43	0.45
query37	0.09	0.06	0.07
query38	0.05	0.04	0.04
query39	0.04	0.03	0.02
query40	0.16	0.15	0.14
query41	0.09	0.03	0.03
query42	0.04	0.03	0.03
query43	0.04	0.04	0.03
Total cold run time: 96.61 s
Total hot run time: 24.27 s

…es, SHOW STORAGE POLICY and WARM UP SELECT

### What problem does this PR solve?

Issue Number: None

Related PR: apache#68631

Problem Summary: Review of this port found three statements that run over
the Arrow Flight SQL JDBC driver on master but failed once Prepare started
to analyze them. The driver prepares every statement and does not fall
back when Prepare refuses one.

1. A CTE used once, under column-level privileges. Prepare checked
   privileges on the analyzed plan. There the CTE is still a
   `LogicalCTEAnchor` over its producer, and `ColumnPruning` treats every
   producer output as needed. So with only `SELECT_PRIV(id)` on
   `t(id, secret)`, `WITH c AS (SELECT id, secret FROM t) SELECT id FROM c`
   failed Prepare with INVALID_ARGUMENT. Execution runs the query, because
   it inlines the CTE first and then checks only the columns read. Prepare
   now runs `PullUpCteAnchor` and `CTEInline` on the analyzed plan before
   `CheckPrivileges`, as the rewriter does. The rewriter normalizes the
   plan further before its own check; for example, it drops an ORDER BY
   under a derived table. So a denial on the analyzed plan does not show
   that execution is denied. Such a statement now keeps the placeholder
   schema and runs unchecked, so execution decides as it did before this
   PR, and GetSchema answers UNIMPLEMENTED without a schema.
2. SHOW STORAGE POLICY. `ShowStoragePolicyCommand.getMetaData()` returned
   the four-column USING header for every form, but the plain form returns
   the seven columns of `StoragePolicy.STORAGE_META_DATA`. Every prepared
   execution therefore failed with NOT_FOUND "Prepared statement schema
   changed". It now returns the header that `doRun` uses for each form.
   This also corrects the column definitions that MySQL `COM_STMT_PREPARE`
   reports for the statement.
3. WARM UP SELECT. When planning proves the source empty (a predicate that
   folds to false, no partition left after pruning, or partitions without
   data), the statement returns OK instead of its statistics, and its
   prepared execution failed as a schema change. Its header depends on
   what planning finds, so WARM UP SELECT now keeps the placeholder schema
   like the other statements whose result depends on execution. The
   `getResultSetMetaData()` override added for it is removed.

The regression suite `test_schema_only` now also executes its
side-effect-free frontend-side statements as prepared statements, compares
the executed schema with the advertised one, and runs a WARM UP SELECT on
an empty table.

### Release note

Flight SQL Prepare keeps the placeholder schema for WARM UP SELECT and for
queries whose privilege check fails; those queries still fail when they
run. The prepared metadata of SHOW STORAGE POLICY lists the seven columns
it returns.

### Check List (For Author)

- Test: Unit Test, Regression test
    - `DorisFlightSqlSchemaTest`: new
      `columnGrantsAreCheckedAsExecutionChecksThem` and
      `warmupSelectRunsUncheckedWhateverItsSourceHolds`; updated
      `schemaDiscoveryChecksTablePrivileges` and
      `commandsWithUnknownOrDynamicMetadataKeepThePlaceholder`
    - `SchemaOnlyCommandMetadataTest`: new
      `storagePolicyHeadersMatchExecution`
    - `arrow_flight_sql_p0/test_schema_only`: prepared executions of
      frontend-side commands, and WARM UP SELECT on an empty table (its
      execution only in cloud mode, the only mode that warms up internal
      tables)
    - 41 FE test classes (536 tests) pass and fe-core Checkstyle is clean;
      skipping the inlining, failing Prepare on a denial again, returning
      the USING header for every form and validating WARM UP SELECT each
      turn their tests red
    - Local cluster: all 21 suites under `arrow_flight_sql_p0` and
      `test_uuid_arrow_flight` pass; the strengthened `test_schema_only`
      fails on the previous head at the SHOW STORAGE POLICY header
- Behavior changed: Yes. A query whose privilege check on the analyzed
  plan fails now keeps the placeholder at Prepare and fails at execution.
  WARM UP SELECT keeps the placeholder. SHOW STORAGE POLICY reports its
  seven columns.
- Does this need documentation: No

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@morningman

Copy link
Copy Markdown
Contributor Author

Addressed review round 1 in 7c7393c8c18.

  • 4419077176: before checking privileges, Prepare now inlines the CTEs that execution inlines, so a CTE used once is checked only on the columns the query reads. If the analyzed plan is still denied, for example because of an ORDER BY under a derived table on a column without the grant, the statement keeps the placeholder schema and execution decides, as it did before this PR. GetSchema answers UNIMPLEMENTED without a schema.
  • 4419077184: ShowStoragePolicyCommand.getMetaData() returns the header of the form that runs: seven columns for SHOW STORAGE POLICY, four for USING. No other SHOW command has this mismatch.
  • 4419077191: WARM UP SELECT keeps the placeholder schema. It answers OK instead of its statistics whenever planning finds its source empty, and that includes a table without data.

New and changed tests, each red without its fix:

  • DorisFlightSqlSchemaTest adds columnGrantsAreCheckedAsExecutionChecksThem, replaces the WARM UP header test with warmupSelectRunsUncheckedWhateverItsSourceHolds, and updates two tests.
  • SchemaOnlyCommandMetadataTest adds storagePolicyHeadersMatchExecution.
  • test_schema_only now executes its side-effect-free frontend-side statements as prepared statements and compares the executed schema with the advertised one. It also prepares a WARM UP SELECT on an empty table and, in cloud mode (the only mode that warms up internal tables), executes it. Against the previous head the strengthened suite fails at the SHOW STORAGE POLICY header.

41 related FE test classes, 536 tests, pass with Checkstyle.

@morningman

Copy link
Copy Markdown
Contributor Author

run buildall

@morningman

Copy link
Copy Markdown
Contributor Author

/review

@github-actions github-actions Bot 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.

Reviewed PR #69867 at 7c7393c8c18d62fa9afd2b20405ce24c96c1a5fd. One new P2 correctness finding: untyped NULL result fields advertise Arrow Null while BE sends nullable Bool, so the new comparison rejects an unchanged valid prepared query. The inline comment includes the concrete SQL, conversion chain and fix direction.

The review converged after 2 rounds. All three assigned reviewers returned NO_NEW_VALUABLE_FINDINGS in round 2; all 21 changed files were covered, and all candidates and initial risks have verified dispositions. No unresolved review candidate remains.

The existing findings are fixed at this head: 4419077176 (single-use CTE column pruning) now inlines CTEs before privilege checks and keeps the placeholder for remaining analyzed-plan denials; 4419077184 (ordinary SHOW STORAGE POLICY) now selects the same seven-column header as execution; 4419077191 (empty warmups) now retains the placeholder and skips the execution-schema comparison. These were independently revalidated and are not reposted. There is no existing P0/P1 finding to carry forward. No additional user-provided review focus was supplied.

Critical-checkpoint conclusions:

  • Goal and proof: Prepare/GetSchema now derive metadata without dispatching queries, and checked execution compares the advertised and actual schema before publishing tickets. The NULL representation mismatch means this goal is incomplete for that query family. Inspected unit and regression tests exercise the main flows, but miss untyped NULL execution.
  • Scope, clarity and reuse: The implementation reuses PrepareCommandPlanner, command metadata, the shared Arrow mapper and FlightSqlSchemaHelper. The Env helper extracts existing provider-default behavior; the timeout conversion is a small, explicit overflow check. No further abstraction or scope defect was established.
  • Concurrency: RPC/worker entrypoints serialize mutable prepared queries, session state, channel results and endpoints through the existing command lock. Deferred executors use their separate monitor; cleanup runs outside it. Existing sorted table locking and planner cleanup are preserved. No new deadlock or unsynchronized access was established.
  • Lifecycle and initialization: Analysis deep-clones session variables, restores borrowed state and namespace in finally, and explicitly closes statement/connector scopes. ExecutedQuery retains the executor before processor close; mismatch cancellation precedes deferred-result cleanup. The new static placeholder schema has no identified initialization cycle. No additional resource-lifetime issue was found.
  • Configuration: No configuration item is added; existing session settings are used and reanalysis detects schema-changing settings.
  • Compatibility: Dynamic/unknown schemas retain placeholder preparation for Flight JDBC. TIMEV2/VARBINARY match existing BE wire types. No storage-format or persistent-symbol migration is introduced; M1 is a new query compatibility regression.
  • Thrift/protobuf: No definition or field/enum ID changes; release-branch ID comparison is not applicable.
  • Parallel paths: Inspected statement/prepared GetSchema and execution, local command/BE result paths, nested fields, canonical time zones, empty/folded/union/subquery outputs and aggregate-state handling. Flight already disables incompatible MySQL cache, FE SELECT and point-query shortcuts. No additional independent defect was verified.
  • Conditional/error handling: Unsupported shapes deliberately use placeholders at Prepare and UNIMPLEMENTED at GetSchema; invalid SQL, stale handles, invalid filters and timeout overflow have explicit failure paths. The NULL_TYPE exception to the Arrow Null rejection is the inline finding.
  • Test coverage: Inspected positive and negative schema/handle/namespace tests, column grants and CTE shapes, synchronization with latches, allocator checks, mismatch cancellation, command-header-versus-execution checks, and the new Flight regression suite. Bare NULL and nested untyped NULL require real prepared-execution coverage; typed NULL casts do not cover them.
  • Expected results: No golden .out file is added or edited. The new suite checks protocol metadata and state directly. No further incorrect expected value was identified by source inspection.
  • Observability: Client errors identify invalid handles/schema changes and existing producer/cancellation logs expose failures. No additional required metric or logging gap was established for these metadata paths.
  • Transactions and persistence: No EditLog/replay, commit/visibility, failover or delete-bitmap behavior is changed; these checkpoints are not applicable.
  • Data writes and crashes: Discovery does not execute DML/DDL; existing execution remains responsible for transactionality. No new write atomicity or recovery path is introduced, and mismatch cleanup is covered by the lifecycle review.
  • FE/BE variables: No new cross-process variable or sending point is added; the time zone uses the existing canonical query-global value.
  • Performance: Extra FE analysis at Prepare/GetSchema and checked prepared execution is the stated feature cost. No additional demonstrated hot-path performance defect was found; no benchmark was run.
  • Other issues: Rechecked inherited commands, conditional headers, USE/SWITCH remembered/default database behavior and timeout callers. No further independent finding was substantiated.

Validation limits: static source and test inspection only. No builds, tests, benchmarks, runtime queries or external-connector validation were performed; reported author test results were not treated as locally verified results.

Review converged after 2 round(s).

// BE stamps the session time zone, where the shared mapping still says "UTC" (#67577).
arrowType = new ArrowType.Timestamp(((ArrowType.Timestamp) arrowType).getUnit(), timezone);
}
if (arrowType instanceof ArrowType.Null && primitive != PrimitiveType.NULL_TYPE) {

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.

[P2] Match BE's wire type for untyped NULL outputs

SELECT NULL AS n reaches this branch as NULL_TYPE, so Prepare advertises Arrow Null. BE's DataTypeFactory represents TYPE_NULL as nullable DataTypeUInt8 (TYPE_BOOLEAN), and arrow_row_batch.cpp therefore emits Arrow Bool. The new matchesExecutionField comparison rejects that unchanged query with NOT_FOUND / "schema changed" and removes its handle. Flight JDBC prepares every statement, so this breaks an otherwise valid query; nested untyped NULL fields share the mismatch. Resolve NULL_TYPE to the same wire representation recursively, or retain the placeholder until supported, and cover prepared execution of an untyped NULL. The new nullable regression case uses CAST(NULL AS VARCHAR), which avoids this path.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 36772dc. Confirmed: data_type_factory.cpp builds an untyped NULL column as a DataTypeUInt8 flagged as a NULL literal, whose primitive type is BOOLEAN, so convert_to_arrow_type sends nullable Bool and the TYPE_NULL -> arrow::null() branch is never reached for it. The advertised Null therefore failed the comparison after every prepared execution; query_p0/operator/test_sort_operator (select org_id,null from dim_org ...) failed this way over the Arrow Flight SQL JDBC driver in P0, and fails the same way on a local cluster with the previous head.

arrowField already keeps the placeholder for every result type that maps to Arrow Null; NULL_TYPE was the one exception, and now it is not. A result with an untyped NULL column, at any depth (arrowField recurses into ARRAY, MAP and STRUCT), keeps the placeholder schema on Prepare, its executions are not checked, and GetSchema answers UNIMPLEMENTED, which is what master does without this port. A typed NULL such as CAST(NULL AS VARCHAR(20)) is still described as its type. I did not advertise Bool instead: that would make the frontend depend on how BE happens to represent a NULL literal, and an untyped NULL column carries no type a client could use.

Tests: DorisFlightSqlSchemaTest#untypedNullResultsKeepThePlaceholder covers SELECT NULL, SELECT id, NULL AS n FROM ... ORDER BY id, and NULL inside ARRAY, MAP and STRUCT, and fails with the previous condition. test_schema_only adds SELECT NULL and SELECT ARRAY(NULL) to the placeholder statements and runs a prepared SELECT k, NULL AS n ... twice through the backends. On a local cluster, test_sort_operator over Flight SQL fails with the previous head and passes with this one.

@hello-stephen

Copy link
Copy Markdown
Contributor

FE Regression Coverage Report

Increment line coverage 30.37% (133/438) 🎉
Increment coverage report
Complete coverage report

@hello-stephen

Copy link
Copy Markdown
Contributor

FE UT Coverage Report

Increment line coverage 82.87% (295/356) 🎉
Increment coverage report
Complete coverage report

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-H: Total hot run time: 28649 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 7c7393c8c18d62fa9afd2b20405ce24c96c1a5fd, data reload: false

------ Round 1 ----------------------------------
============================================
q1	17616	5226	5065	5065
q2	2150	322	258	258
q3	10260	1400	813	813
q4	4673	462	341	341
q5	7439	795	523	523
q6	178	180	150	150
q7	746	810	597	597
q8	9245	1359	1050	1050
q9	5551	4187	4205	4187
q10	6805	1286	1026	1026
q11	439	250	227	227
q12	642	400	275	275
q13	18107	3617	3006	3006
q14	275	273	246	246
q15	q16	723	717	673	673
q17	1212	752	580	580
q18	7060	6171	6086	6086
q19	1105	967	598	598
q20	384	331	233	233
q21	5427	2427	2428	2427
q22	376	321	288	288
Total cold run time: 100413 ms
Total hot run time: 28649 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	5996	5850	6133	5850
q2	776	600	603	600
q3	5637	5846	5314	5314
q4	2015	2166	1574	1574
q5	5685	5839	5610	5610
q6	245	183	144	144
q7	2242	1999	1828	1828
q8	2945	2662	2666	2662
q9	7972	7610	7466	7466
q10	3618	3575	3207	3207
q11	587	416	400	400
q12	652	690	508	508
q13	3261	3618	3017	3017
q14	281	289	267	267
q15	q16	656	690	620	620
q17	7803	7014	6832	6832
q18	12993	12164	12963	12164
q19	914	789	790	789
q20	2123	2138	1925	1925
q21	5660	4617	4672	4617
q22	466	441	414	414
Total cold run time: 72527 ms
Total hot run time: 65808 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 151146 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 7c7393c8c18d62fa9afd2b20405ce24c96c1a5fd, data reload: false

query5	4311	603	438	438
query6	423	210	193	193
query7	4813	446	224	224
query8	319	179	147	147
query9	8801	3185	3167	3167
query10	479	304	242	242
query11	5942	3830	3534	3534
query12	156	90	86	86
query13	1252	438	323	323
query14	6051	3933	3610	3610
query14_1	3446	3334	3399	3334
query15	211	202	188	188
query16	951	423	408	408
query17	860	676	540	540
query18	2443	429	308	308
query19	189	169	168	168
query20	89	85	84	84
query21	209	128	106	106
query22	13001	13006	12782	12782
query23	13414	12851	12331	12331
query23_1	12580	12552	12536	12536
query24	7358	826	466	466
query24_1	475	478	476	476
query25	539	439	330	330
query26	1245	252	133	133
query27	2778	452	266	266
query28	4620	1996	1991	1991
query29	1538	580	422	422
query30	301	221	187	187
query31	1009	918	820	820
query32	151	91	83	83
query33	518	288	240	240
query34	894	850	495	495
query35	739	761	631	631
query36	822	819	747	747
query37	127	96	90	90
query38	1754	1702	1660	1660
query39	784	796	758	758
query39_1	737	725	750	725
query40	211	113	110	110
query41	70	64	62	62
query42	86	78	79	78
query43	329	361	319	319
query44	1320	690	708	690
query45	168	174	165	165
query46	828	949	568	568
query47	3012	2810	2914	2810
query48	306	346	219	219
query49	578	391	298	298
query50	669	281	208	208
query51	10726	10494	10558	10494
query52	79	80	74	74
query53	185	207	148	148
query54	234	205	181	181
query55	76	68	62	62
query56	248	224	215	215
query57	1649	1595	1575	1575
query58	287	264	255	255
query59	2165	2186	2002	2002
query60	294	242	222	222
query61	144	140	141	140
query62	392	332	292	292
query63	184	154	157	154
query64	2738	949	792	792
query65	3298	3268	3253	3253
query66	1781	413	307	307
query67	20046	20134	19919	19919
query68	2968	993	610	610
query69	402	281	245	245
query70	858	783	805	783
query71	306	232	213	213
query72	2735	2713	2459	2459
query73	539	523	302	302
query74	4654	4492	4355	4355
query75	2352	2282	1922	1922
query76	2321	1015	617	617
query77	346	375	281	281
query78	9829	9721	9225	9225
query79	996	910	547	547
query80	771	411	352	352
query81	557	311	282	282
query82	706	137	104	104
query83	265	193	173	173
query84	314	127	96	96
query85	885	413	391	391
query86	338	235	226	226
query87	2010	1944	1828	1828
query88	3789	2786	2779	2779
query89	333	288	244	244
query90	1775	175	172	172
query91	165	154	129	129
query92	96	90	91	90
query93	1060	1022	580	580
query94	464	307	268	268
query95	582	398	310	310
query96	694	490	239	239
query97	2439	2412	2333	2333
query98	166	151	150	150
query99	767	765	654	654
Total cold run time: 234608 ms
Total hot run time: 151146 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
ClickBench: Total hot run time: 24.23 s
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/clickbench-tools
ClickBench test result on commit 7c7393c8c18d62fa9afd2b20405ce24c96c1a5fd, data reload: false

query1	0.01	0.01	0.01
query2	0.10	0.05	0.05
query3	0.27	0.10	0.08
query4	1.61	0.10	0.08
query5	0.26	0.24	0.25
query6	1.17	0.69	0.68
query7	0.04	0.01	0.01
query8	0.06	0.04	0.04
query9	0.40	0.35	0.35
query10	0.61	0.61	0.60
query11	0.20	0.15	0.14
query12	0.19	0.15	0.15
query13	0.49	0.49	0.49
query14	1.03	1.00	1.02
query15	0.62	0.60	0.62
query16	0.31	0.34	0.33
query17	1.13	1.09	1.11
query18	0.19	0.20	0.20
query19	2.10	2.04	1.98
query20	0.01	0.01	0.02
query21	15.56	0.18	0.13
query22	4.94	0.05	0.06
query23	16.30	0.33	0.13
query24	2.82	0.51	0.37
query25	0.11	0.05	0.06
query26	1.07	0.21	0.16
query27	0.05	0.04	0.05
query28	3.85	0.83	0.35
query29	12.47	4.22	3.21
query30	0.28	0.13	0.11
query31	2.77	0.57	0.32
query32	3.23	0.60	0.49
query33	3.21	3.27	3.22
query34	15.55	4.01	3.43
query35	3.35	3.30	3.33
query36	0.55	0.47	0.40
query37	0.09	0.07	0.06
query38	0.05	0.04	0.04
query39	0.04	0.03	0.03
query40	0.18	0.14	0.14
query41	0.09	0.03	0.02
query42	0.04	0.02	0.02
query43	0.04	0.04	0.03
Total cold run time: 97.44 s
Total hot run time: 24.23 s

… schema

### What problem does this PR solve?

Issue Number: None

Related PR: apache#68631

Problem Summary:

Prepare advertised an untyped NULL result column (NULL_TYPE) as Arrow
`Null`, the type `DorisArrowTypeMapping` gives NULL_TYPE. BE never sends
that: `data_type_factory.cpp` builds the column as a `DataTypeUInt8`
flagged as a NULL literal, whose primitive type is BOOLEAN, so
`convert_to_arrow_type` sends nullable `Bool`. The comparison after a
prepared execution therefore failed, removed the handle and answered
NOT_FOUND "Prepared statement schema changed; prepare again" for every
execution of such a query. Over the Arrow Flight SQL JDBC driver, which
prepares every statement, `select org_id, null from dim_org ...` in
`query_p0/operator/test_sort_operator` failed this way. Before apache#69867 the
query ran, because Prepare always returned the placeholder schema.

`FlightSqlQuerySchema.arrowField` already treats a result type that maps
to Arrow `Null` as one this frontend cannot describe as BE sends it, and
keeps the placeholder for it, except for NULL_TYPE. It now covers
NULL_TYPE too, at any depth since `arrowField` recurses into ARRAY, MAP
and STRUCT children: Prepare returns the placeholder, the executions are
not checked, and GetSchema answers UNIMPLEMENTED, as on master before
apache#69867. A NULL with a type (`CAST(NULL AS VARCHAR(20))`) is still
described as that type. Advertising `Bool` instead would tie the frontend
to how BE happens to store a NULL literal.

### Release note

Flight SQL Prepare keeps the placeholder schema for a result with an untyped
NULL column, so its prepared executions run instead of failing with
"Prepared statement schema changed".

### Check List (For Author)

- Test: Unit Test, Regression test
    - `DorisFlightSqlSchemaTest#untypedNullResultsKeepThePlaceholder`:
      `SELECT NULL`, `SELECT id, NULL ... ORDER BY id`, and NULL inside
      ARRAY, MAP and STRUCT keep the placeholder, their handles carry no
      schema to check, and GetSchema answers UNIMPLEMENTED. It fails with
      the previous condition restored.
    - `arrow_flight_sql_p0/test_schema_only` adds `SELECT NULL` and
      `SELECT ARRAY(NULL)` to the placeholder statements and runs a
      prepared `SELECT k, NULL AS n ...` twice through the backends.
    - 41 FE test classes (537 tests) pass and fe-core Checkstyle is clean.
    - Local cluster: `test_sort_operator` over Flight SQL fails with
      "Prepared statement schema changed" on the previous head and passes
      on this one; all 20 suites under `arrow_flight_sql_p0` and
      `test_uuid_arrow_flight` pass; the strengthened `test_schema_only`
      fails on the previous head at `SELECT NULL AS n`.
- Behavior changed: Yes. A result with an untyped NULL column keeps the
  placeholder schema on Prepare, as master does without this port.
- Does this need documentation: No

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@morningman

Copy link
Copy Markdown
Contributor Author

run buildall

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-H: Total hot run time: 28548 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 36772dcc0271773aa6044ad33cd6a737a8f65f16, data reload: false

------ Round 1 ----------------------------------
============================================
q1	17583	5211	5054	5054
q2	2146	302	288	288
q3	10207	1403	831	831
q4	4682	469	337	337
q5	7445	797	539	539
q6	180	188	153	153
q7	740	821	602	602
q8	9255	1339	1093	1093
q9	5533	4225	4216	4216
q10	6835	1294	1020	1020
q11	425	245	228	228
q12	644	409	279	279
q13	18100	3898	3009	3009
q14	275	271	252	252
q15	q16	728	725	656	656
q17	1223	750	580	580
q18	6941	6194	6131	6131
q19	1116	958	599	599
q20	387	335	235	235
q21	5703	2180	2287	2180
q22	350	305	266	266
Total cold run time: 100498 ms
Total hot run time: 28548 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	5621	5603	5615	5603
q2	704	554	548	548
q3	5200	5396	4897	4897
q4	2004	2135	1561	1561
q5	5248	5172	5793	5172
q6	245	193	151	151
q7	2289	2147	1884	1884
q8	3074	2807	2782	2782
q9	8142	8091	8143	8091
q10	3750	3706	3317	3317
q11	610	458	403	403
q12	669	690	524	524
q13	3661	3806	3225	3225
q14	330	330	286	286
q15	q16	751	726	650	650
q17	8334	7523	6928	6928
q18	12985	12161	12932	12161
q19	929	819	829	819
q20	2174	2137	1918	1918
q21	5745	4684	4785	4684
q22	504	461	425	425
Total cold run time: 72969 ms
Total hot run time: 66029 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 151716 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 36772dcc0271773aa6044ad33cd6a737a8f65f16, data reload: false

query5	4322	592	440	440
query6	428	212	204	204
query7	4808	446	230	230
query8	316	168	154	154
query9	8800	3207	3202	3202
query10	489	312	265	265
query11	5995	3847	3574	3574
query12	151	111	88	88
query13	1243	460	323	323
query14	6153	3921	3668	3668
query14_1	3470	3442	3436	3436
query15	209	202	186	186
query16	964	441	400	400
query17	852	629	513	513
query18	2404	415	310	310
query19	189	155	133	133
query20	82	85	83	83
query21	206	125	104	104
query22	12945	13073	12756	12756
query23	13543	12909	12386	12386
query23_1	12634	12595	12544	12544
query24	7363	860	487	487
query24_1	480	477	470	470
query25	522	394	337	337
query26	1271	253	138	138
query27	2772	438	280	280
query28	4630	2025	1988	1988
query29	1563	594	444	444
query30	303	222	182	182
query31	1017	935	808	808
query32	146	91	99	91
query33	521	293	256	256
query34	886	873	520	520
query35	750	770	635	635
query36	816	807	723	723
query37	131	99	84	84
query38	1787	1760	1673	1673
query39	794	789	742	742
query39_1	733	726	736	726
query40	228	126	100	100
query41	72	68	69	68
query42	89	89	85	85
query43	356	361	318	318
query44	1363	693	723	693
query45	183	181	166	166
query46	848	988	569	569
query47	2882	3020	2814	2814
query48	307	316	232	232
query49	588	397	309	309
query50	713	287	218	218
query51	10739	10579	10717	10579
query52	83	84	72	72
query53	188	217	158	158
query54	254	210	215	210
query55	74	71	68	68
query56	236	244	225	225
query57	1648	1635	1572	1572
query58	300	273	270	270
query59	2105	2145	1953	1953
query60	310	253	235	235
query61	172	171	177	171
query62	392	333	296	296
query63	231	158	160	158
query64	2741	951	756	756
query65	3368	3360	3294	3294
query66	1781	423	300	300
query67	20114	20328	20046	20046
query68	2915	1009	614	614
query69	375	281	246	246
query70	845	814	779	779
query71	305	238	202	202
query72	2787	2578	2335	2335
query73	512	539	296	296
query74	4659	4509	4360	4360
query75	2346	2297	1902	1902
query76	2315	1014	620	620
query77	345	379	265	265
query78	9805	9643	9186	9186
query79	1034	862	530	530
query80	549	432	348	348
query81	521	323	269	269
query82	574	149	108	108
query83	300	205	187	187
query84	315	117	107	107
query85	814	436	383	383
query86	316	242	236	236
query87	2001	1937	1816	1816
query88	3821	2774	2770	2770
query89	329	279	289	279
query90	1885	175	165	165
query91	163	154	126	126
query92	102	95	91	91
query93	1016	1022	577	577
query94	450	319	285	285
query95	603	389	302	302
query96	700	491	240	240
query97	2476	2433	2319	2319
query98	165	152	153	152
query99	763	767	660	660
Total cold run time: 234847 ms
Total hot run time: 151716 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
ClickBench: Total hot run time: 24.13 s
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/clickbench-tools
ClickBench test result on commit 36772dcc0271773aa6044ad33cd6a737a8f65f16, data reload: false

query1	0.00	0.00	0.00
query2	0.08	0.05	0.05
query3	0.26	0.09	0.09
query4	1.61	0.09	0.10
query5	0.24	0.23	0.24
query6	1.17	0.67	0.68
query7	0.04	0.01	0.01
query8	0.05	0.04	0.04
query9	0.39	0.35	0.35
query10	0.57	0.61	0.58
query11	0.19	0.15	0.14
query12	0.19	0.15	0.14
query13	0.48	0.48	0.51
query14	1.03	1.01	1.00
query15	0.64	0.59	0.61
query16	0.31	0.35	0.34
query17	1.14	1.12	1.11
query18	0.21	0.20	0.20
query19	2.06	1.96	2.02
query20	0.02	0.02	0.01
query21	15.50	0.20	0.14
query22	4.79	0.06	0.05
query23	16.26	0.32	0.12
query24	2.99	0.58	0.29
query25	0.14	0.05	0.06
query26	1.05	0.22	0.16
query27	0.06	0.03	0.05
query28	3.85	0.80	0.37
query29	12.50	4.11	3.24
query30	0.27	0.14	0.10
query31	2.77	0.55	0.33
query32	3.23	0.59	0.50
query33	3.15	3.19	3.18
query34	15.41	3.95	3.41
query35	3.33	3.32	3.34
query36	0.56	0.42	0.42
query37	0.09	0.07	0.07
query38	0.04	0.04	0.04
query39	0.04	0.03	0.02
query40	0.17	0.14	0.13
query41	0.09	0.03	0.03
query42	0.04	0.02	0.02
query43	0.04	0.04	0.03
Total cold run time: 97.05 s
Total hot run time: 24.13 s

@morningman

Copy link
Copy Markdown
Contributor Author

Addressed review round 2 in 36772dcc027.

  • 4420363330: an untyped NULL result column now keeps the placeholder schema, at any depth (ARRAY, MAP, STRUCT). BE builds such a column as a UInt8 flagged as a NULL literal and sends it as nullable Bool, so the advertised Null made every prepared execution fail with "Prepared statement schema changed", which is how query_p0/operator/test_sort_operator (select org_id,null from dim_org ...) failed in P0. Its executions are no longer checked, and GetSchema answers UNIMPLEMENTED, as on master today. A typed NULL is still described as its type.

New and changed tests, each red without the fix:

  • DorisFlightSqlSchemaTest adds untypedNullResultsKeepThePlaceholder (top-level and nested NULL; the placeholder assertions of commandsWithUnknownOrDynamicMetadataKeepThePlaceholder move into a shared helper).
  • test_schema_only adds SELECT NULL and SELECT ARRAY(NULL) to the placeholder statements and runs a prepared SELECT k, NULL AS n ... twice through the backends. Against the previous head it fails at SELECT NULL AS n.

41 related FE test classes, 537 tests, pass with Checkstyle. On a local cluster, all arrow_flight_sql_p0 suites and test_uuid_arrow_flight pass, and test_sort_operator over Flight SQL fails on the previous head and passes on this one.

@morningman

Copy link
Copy Markdown
Contributor Author

/review

@github-actions github-actions Bot 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.

Reviewed apache/doris PR 69867 at exact head 36772dc against base 2a4191c. The change adds FE-side Flight SQL schema discovery and prepared-execution schema checks, with BE Arrow type alignment and command metadata coverage. I found one new P2 lifecycle issue described inline: a schema-known prepared GetFlightInfo can fail during schema/namespace revalidation before beginRequest clears the prior Flight request, leaving a deferred coordinator or cached result until the idle cleanup bound. The four existing P2 threads (CTE privilege pruning, SHOW STORAGE POLICY, empty WARM UP SELECT, and untyped NULL) were independently revalidated as fixed at this head and are not reposted. No existing P0/P1 finding still applies.

Critical checkpoints: the analyzer restores session variables, state, executor, namespace, and analysis resources; command locking and deferred-executor ownership are serialized; FE Arrow mapping matches the BE wire types for supported scalar and nested fields and preserves placeholders for unsupported representations; command headers match execution forms; and the Env/timeout refactors have no additional issue. The user focus file has no extra focus. This was a static review only; builds and tests were inspected but not executed. Convergence was reached after 2 bounded rounds.

Review converged after 2 round(s).

return FlightProtocolAdapter.of(connectContext).callCommand(connectContext,
() -> executeQueryStatement(executedPeerIdentity, connectContext,
connectContext.getPreparedQuery(preparedStatementId), descriptor));
() -> executePreparedStatement(context.peerIdentity(), connectContext, preparedStatementId,

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.

[P2] Clear the previous request before a prepared schema precheck can fail

getFlightInfoPreparedStatement runs checkPreparedSchema() before executeQueryStatement() reaches FlightProtocolAdapter.beginRequest(). If the schema is stale (for example after a sql_mode change or DDL), checkPreparedSchema removes the handle and throws NOT_FOUND, so the prior request's deferred coordinator, endpoints, and cached frontend result are never reset. A client that executes a remote query, then makes this failed prepared request, leaves that coordinator pinned until the idle timeout instead of following the normal query cleanup path. Start the request cleanup before this precheck, or explicitly perform the same cleanup on every precheck failure.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in be929ac. Confirmed: executePreparedStatement ran preparedQuery() and checkPreparedSchema() before executeQueryStatement() reached FlightProtocolAdapter.beginRequest(), and getFlightInfoPreparedStatement has no catch of its own. A handle refused there (missing, prepared in another namespace, a changed schema, a failed re-analysis) therefore left the previous request's deferred coordinator, its frontend-side result and its endpoints in place. Without this PR, every prepared GetFlightInfo reached executeQueryStatement(); even a missing handle failed inside its try, and the catch cancelled and finalized the previous request's deferred coordinator.

executePreparedStatement now calls beginRequest() before the checks. A prepared GetFlightInfo is the session's next request whatever the checks find, and the call in executeQueryStatement() then finds nothing left. The call is not in the command lock (runCommand / callCommand) or in preparedQuery(): Prepare, GetSchema and the other metadata requests go through those too, and they must leave the previous request alone (FlightProtocolAdapter's class comment, FlightSessionOptionsTest#testTheOptionsLeaveTheLastRequestAlone).

Two parts of the description go further than what happens:

  • The state is not kept until the idle timeout whatever comes next. The session's next statement, or its next prepared execution that passes the checks, drops it, and so does session teardown. Only when neither arrives does the timeout checker finalize the deferred coordinator, and its bound is arrow_flight_deferred_query_idle_timeout_second (3600 s) or the query's own execution timeout, whichever is longer, counted from that query's start, not wait_timeout.
  • Only a query whose coordinator must outlive GetFlightInfo, a batch-mode external-table scan or a remote Doris scan (coord.mustOutliveDispatch()), leaves a deferred coordinator; every other query closes its coordinator before GetFlightInfo returns. As for the sql_mode example: SET sql_mode = ... is itself a GetFlightInfo, which already dropped the previous query's coordinator and leaves only its own OK result. For a schema change to leave a coordinator behind, it has to come from outside the session's requests, such as SetSessionOptions or another session's DDL or REVOKE.

The same commit expires a checked handle whose statement can no longer be described when it is re-analyzed, for example after a column grant was revoked so that the analyzed plan is denied, or after a DDL that turned a result column into an untyped NULL. Such a handle answered UNIMPLEMENTED and was kept, so every later execution failed the same way. The check now compares the schema Prepare would advertise now, which for such a statement is the placeholder: the handle is removed, the request answers NOT_FOUND "Prepared statement schema changed; prepare again", and the statement prepared again keeps the placeholder and runs unchecked.

Tests: DorisFlightSqlSchemaTest adds one test for each way the checks refuse a handle: a missing handle, a namespace change, a sql_mode drift, a dropped view (INVALID_ARGUMENT, the handle is kept) and a revoked column grant (NOT_FOUND, the handle is removed, and preparing again gives the placeholder). Each sends GetSchema and GetFlightInfo after a previous request left a deferred executor, a frontend-side result and an endpoint, and checks the status, whether the handle is kept, and that GetFlightInfo finalized and dropped all three while GetSchema left them. Without the new beginRequest() call, the five tests fail at GetFlightInfo; with the call in preparedQuery() instead, they fail at GetSchema; with the previous UNIMPLEMENTED for a re-analysis that cannot describe the result, only the revoked-grant test fails. branch-4.1 and branch-4.2 check the handle in the same order.

…red handle

### What problem does this PR solve?

Issue Number: None

Related PR: apache#68631

Problem Summary:

A prepared GetFlightInfo checks its handle before it executes anything:
the handle must exist, the session must still be in the namespace it was
prepared in, and the statement re-analyzed now must have the schema that
Prepare advertised. `executePreparedStatement` ran these checks before
`executeQueryStatement` reached `FlightProtocolAdapter.beginRequest()`,
where a request drops what the previous one left: its deferred
coordinator, the frontend-side result no DoGet took, and its endpoints.
A handle that the checks refused therefore left all three in place. The
previous query's deferred coordinator, which a batch-mode external-table
scan or a remote Doris scan leaves, kept its workload group queue slot
and its query registration until the session's next statement, the
timeout checker or teardown, and the previous frontend-side result could
still be fetched. Before apache#69867, every prepared GetFlightInfo reached
`executeQueryStatement`, and a missing handle failed inside it, where the
catch finalized the previous coordinator.

`executePreparedStatement` now calls `beginRequest()` before the checks:
a prepared execution is the session's next request whatever they find.
Prepare and GetSchema are metadata requests and still leave the previous
request alone, so the call is neither in the session's command lock nor
in `preparedQuery()`, which GetSchema goes through too.

A checked handle whose statement could no longer be described when it was
re-analyzed answered UNIMPLEMENTED and was kept, so every later execution
failed the same way. That happens after a column grant is revoked so that
the analyzed plan is denied, or after a DDL that turns a result column
into an untyped NULL. The check now compares the schema Prepare would
advertise now, the placeholder for such a statement: the handle is
removed and the request answers NOT_FOUND "Prepared statement schema
changed; prepare again", as for any other schema change, and the
statement prepared again keeps the placeholder and runs unchecked.
Prepare and the check share `advertisedSchema` for this.

### Release note

A Flight SQL prepared execution refused before it runs no longer keeps the
previous query's deferred coordinator and result. A prepared statement
whose result schema can no longer be determined before it runs answers
NOT_FOUND and must be prepared again.

### Check List (For Author)

- Test: Unit Test, Regression test
    - `DorisFlightSqlSchemaTest` adds one test for each way the checks
      refuse a handle: a missing handle, a namespace change, a sql_mode
      drift, a dropped view (INVALID_ARGUMENT, the handle is kept) and a
      revoked column grant (NOT_FOUND, the handle is removed, and
      preparing again gives the placeholder). Each sends GetSchema and
      GetFlightInfo after a previous request left a deferred executor, a
      frontend-side result and an endpoint, and checks that only
      GetFlightInfo finished it.
    - Without the new `beginRequest()` call the five tests fail at
      GetFlightInfo; with the call in `preparedQuery()` they fail at
      GetSchema; with the previous UNIMPLEMENTED only the revoked-grant
      test fails.
    - 45 FE test classes (555 tests) pass and fe-core Checkstyle is clean.
    - Local cluster: all 21 suites under `arrow_flight_sql_p0`,
      `test_uuid_arrow_flight` and `test_sort_operator` over Flight SQL
      pass.
- Behavior changed: Yes. A prepared GetFlightInfo that its checks refuse
  finishes the previous request first. A checked handle that can no
  longer be described is removed and answers NOT_FOUND instead of
  UNIMPLEMENTED.
- Does this need documentation: No

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@morningman

Copy link
Copy Markdown
Contributor Author

run buildall

@morningman

Copy link
Copy Markdown
Contributor Author

Addressed review round 3 in be929ac2e82.

  • 4430799614: a prepared GetFlightInfo now drops what the previous request left, its deferred coordinator, the frontend-side result nobody pulled and its endpoints, before it checks the handle. A handle refused as missing, as prepared in another namespace, or because the re-analysis changed or failed no longer leaves them in place. Prepare and GetSchema are metadata requests and still leave them.
  • Found while covering every exit of those checks: a checked handle whose statement can no longer be described when it is re-analyzed, for example after a column grant was revoked so that the analyzed plan is denied, answered UNIMPLEMENTED and was kept, so every later execution failed the same way. It now expires like a handle whose schema changed (NOT_FOUND "Prepared statement schema changed; prepare again"), and the statement prepared again keeps the placeholder schema and runs.

New tests, each red without its fix:

  • DorisFlightSqlSchemaTest adds missingHandleFinishesThePreviousRequestOnExecution, namespaceChangeFinishesThePreviousRequestOnExecution, sqlModeDriftFinishesThePreviousRequestOnExecution, failedReanalysisFinishesThePreviousRequestOnExecution and checkedHandleExpiresOnceOnlyExecutionCanTellItsSchema. Each sends GetSchema and GetFlightInfo after a previous request left a deferred executor, a frontend-side result and an endpoint, and checks the status, whether the handle is kept, and that only GetFlightInfo finished the previous request. Without the new call the five fail at GetFlightInfo, with the call moved into preparedQuery() they fail at GetSchema, and with the previous UNIMPLEMENTED only the revoked-grant test fails.

45 related FE test classes, 555 tests, pass with Checkstyle. On a local cluster, all arrow_flight_sql_p0 suites, test_uuid_arrow_flight and test_sort_operator over Flight SQL pass.

@morningman

Copy link
Copy Markdown
Contributor Author

/review

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-H: Total hot run time: 28429 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit be929ac2e825a818d978b490d057a1777a830d0d, data reload: false

------ Round 1 ----------------------------------
============================================
q1	17615	5145	5282	5145
q2	2139	292	238	238
q3	10282	1356	809	809
q4	4681	471	336	336
q5	7441	788	528	528
q6	182	187	151	151
q7	743	801	603	603
q8	9252	1334	1106	1106
q9	5416	4206	4162	4162
q10	6822	1283	1024	1024
q11	428	248	228	228
q12	635	399	280	280
q13	18106	3622	3049	3049
q14	271	266	246	246
q15	q16	723	708	669	669
q17	1345	757	578	578
q18	6921	6165	6111	6111
q19	1102	953	600	600
q20	390	342	232	232
q21	4777	2083	2232	2083
q22	348	287	251	251
Total cold run time: 99619 ms
Total hot run time: 28429 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	5620	5585	5612	5585
q2	683	555	530	530
q3	5171	5371	4944	4944
q4	1975	2136	1532	1532
q5	5228	5155	5511	5155
q6	261	198	162	162
q7	2420	2052	1872	1872
q8	3061	2812	2668	2668
q9	8122	8020	8010	8010
q10	3695	3704	3268	3268
q11	620	440	409	409
q12	690	694	527	527
q13	3497	4032	3244	3244
q14	307	328	282	282
q15	q16	695	704	658	658
q17	8269	7600	6956	6956
q18	12918	12112	12857	12112
q19	913	803	817	803
q20	2126	2144	1889	1889
q21	5524	4625	4673	4625
q22	484	443	435	435
Total cold run time: 72279 ms
Total hot run time: 65666 ms

@github-actions github-actions Bot 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.

Round 1 complete and converged for PR 69867 at head be929ac. I reviewed all 21 changed files and the required FE/Flight lifecycle, command metadata, Arrow mapping, catalog, timeout, concurrency, cleanup, error handling, and test-coverage checkpoints. The new schema-only analysis restores session/planner state and closes analysis resources; prepared-handle identity, namespace binding, schema drift checks, local/remote result ownership, and deferred-query cleanup are consistent. Command headers were checked against execution headers, and FE mappings were compared with BE Arrow output including nested fields, logical markers, timestamps, VARIANT, TIMEV2, VARBINARY, and AGG_STATE. The Env and FeServiceClient changes were traced through all call sites and boundary values. No verified new finding remains, so there are no inline comments. Existing P2 threads 4419077176, 4419077184, 4419077191, 4420363330, and 4430799614 were revalidated and their fixes are present on this head; none is an applicable P0/P1 blocking finding. User review focus: none provided. This was a static review; builds and tests were not run.

Review converged after 1 round(s).

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 150940 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit be929ac2e825a818d978b490d057a1777a830d0d, data reload: false

query5	4310	587	434	434
query6	414	209	187	187
query7	4804	458	224	224
query8	337	165	152	152
query9	8776	3172	3180	3172
query10	463	323	253	253
query11	5951	3841	3562	3562
query12	146	94	89	89
query13	1253	443	337	337
query14	6154	3890	3630	3630
query14_1	3408	3428	3417	3417
query15	209	196	181	181
query16	942	468	447	447
query17	859	675	529	529
query18	2423	428	315	315
query19	194	159	140	140
query20	85	83	84	83
query21	209	126	106	106
query22	12916	13026	12873	12873
query23	13574	12718	12413	12413
query23_1	12504	12582	12497	12497
query24	7411	837	478	478
query24_1	478	482	493	482
query25	545	422	369	369
query26	1258	262	137	137
query27	2765	446	277	277
query28	4613	2030	2031	2030
query29	1587	599	456	456
query30	299	232	183	183
query31	1016	924	805	805
query32	142	93	95	93
query33	522	303	240	240
query34	894	851	513	513
query35	736	745	634	634
query36	803	846	704	704
query37	128	103	87	87
query38	1765	1763	1647	1647
query39	785	789	745	745
query39_1	740	734	753	734
query40	224	125	99	99
query41	71	69	69	69
query42	87	84	83	83
query43	341	370	314	314
query44	1327	701	702	701
query45	181	219	164	164
query46	822	978	559	559
query47	2913	2949	2772	2772
query48	302	301	210	210
query49	550	397	279	279
query50	679	285	219	219
query51	10663	10672	10353	10353
query52	77	77	74	74
query53	195	212	149	149
query54	236	188	185	185
query55	69	69	59	59
query56	225	219	225	219
query57	1637	1612	1565	1565
query58	277	259	265	259
query59	2256	2263	1967	1967
query60	288	242	241	241
query61	146	141	142	141
query62	378	325	289	289
query63	184	160	160	160
query64	2756	919	772	772
query65	3307	3264	3265	3264
query66	1787	442	304	304
query67	20198	20092	19954	19954
query68	3015	998	620	620
query69	390	288	247	247
query70	837	776	747	747
query71	320	231	211	211
query72	2704	2574	2312	2312
query73	521	527	287	287
query74	4586	4465	4330	4330
query75	2323	2307	1900	1900
query76	2298	1008	575	575
query77	342	362	270	270
query78	9865	9774	9185	9185
query79	1004	840	538	538
query80	1231	420	346	346
query81	596	322	278	278
query82	608	133	103	103
query83	306	205	197	197
query84	312	129	99	99
query85	891	417	360	360
query86	404	237	224	224
query87	2028	1959	1825	1825
query88	3761	2756	2742	2742
query89	331	283	240	240
query90	1919	167	160	160
query91	166	147	124	124
query92	98	90	90	90
query93	1009	996	574	574
query94	639	300	272	272
query95	559	318	289	289
query96	674	497	237	237
query97	2449	2440	2324	2324
query98	159	150	145	145
query99	757	758	650	650
Total cold run time: 235320 ms
Total hot run time: 150940 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
ClickBench: Total hot run time: 24.23 s
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/clickbench-tools
ClickBench test result on commit be929ac2e825a818d978b490d057a1777a830d0d, data reload: false

query1	0.00	0.00	0.00
query2	0.09	0.04	0.05
query3	0.26	0.09	0.09
query4	1.60	0.10	0.10
query5	0.24	0.24	0.24
query6	1.17	0.67	0.67
query7	0.04	0.01	0.00
query8	0.06	0.03	0.03
query9	0.39	0.35	0.36
query10	0.60	0.61	0.58
query11	0.20	0.14	0.14
query12	0.18	0.14	0.15
query13	0.49	0.49	0.48
query14	1.02	1.03	1.01
query15	0.62	0.61	0.60
query16	0.34	0.36	0.35
query17	1.09	1.15	1.15
query18	0.20	0.19	0.20
query19	2.05	2.00	2.01
query20	0.02	0.02	0.01
query21	15.48	0.19	0.13
query22	4.80	0.06	0.05
query23	16.25	0.32	0.13
query24	2.82	0.54	0.40
query25	0.11	0.06	0.04
query26	1.20	0.22	0.15
query27	0.05	0.05	0.04
query28	3.75	0.74	0.36
query29	12.50	4.08	3.20
query30	0.28	0.13	0.10
query31	2.77	0.56	0.33
query32	3.26	0.60	0.50
query33	3.14	3.23	3.20
query34	15.52	3.96	3.38
query35	3.33	3.32	3.36
query36	0.53	0.44	0.40
query37	0.09	0.07	0.06
query38	0.05	0.03	0.03
query39	0.04	0.03	0.02
query40	0.17	0.14	0.15
query41	0.08	0.04	0.02
query42	0.04	0.03	0.03
query43	0.04	0.03	0.03
Total cold run time: 96.96 s
Total hot run time: 24.23 s

@morningman

Copy link
Copy Markdown
Contributor Author

run external

@924060929 924060929 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.

Flight SQL clients need result metadata before execution. This PR replaces placeholder-only preparation with FE analysis, implements GetSchema, and validates prepared results before publishing tickets while preserving placeholder fallback for undiscoverable schemas.

I independently reviewed the specified head against merge-base 2a4191c. No actionable P0/P1/P2 issue remains within the FE scope and necessary BE Arrow boundaries. The review covered analysis-stage ownership, command headers, nested types and nullability, namespace/settings drift, command serialization, and result cleanup. All five previous P2 findings were rechecked against current code and are fixed. Recommendation: APPROVE.

Gabriel39's approval targets this same head. The supplied snapshot reports successful P0/cloud P0, FE UT, compilation and AI review, but failed FE coverage. Required-check rules were not supplied, so this is not a merge-readiness determination. Static review only; no builds or tests were run.

Automated FE-focused static review (gpt-6.1-sol, high). No build or tests were run.

@morningman
morningman merged commit 1adeb3a into apache:master Nov 4, 2026
45 of 46 checks passed
@github-actions

github-actions Bot commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

PR approved by anyone and no changes requested.

@github-actions github-actions Bot added the approved Indicates a PR has been approved by one committer. label Nov 4, 2026
@github-actions

github-actions Bot commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

PR approved by at least one committer and no changes requested.

morrySnow added a commit to morrySnow/incubator-doris that referenced this pull request Nov 5, 2026
…hemas

### Background

ConnectContext coordinates client admission and resource lifetime. Flight prepared statements now validate execution response schemas against the schema advertised during Prepare.

### What problem does this PR solve?

Issue Number: None
Related PR: apache#70064, apache#69867
PR Type: Refactor

Problem Summary:

Integrate upstream/master while retaining deferred session cleanup and self-KILL responses. A self KILL CONNECTION response must carry the advertised StatusResult schema while avoiding a DoGet ticket for its revoked session. Also snapshot the volatile executor when reading its timeout to avoid racing the previous command's clear().

### Details

- **Change map:** Integrate upstream/master at 897bb25; resolve the Flight producer response conflict, add prepared self-KILL tests, adapt the new schema fixture to admission/cleanup, and use one local executor snapshot for timeout calculation. The other upstream changes are retained as-is.
- **Moves vs. logic changes:** Retain current Flight schema handling and the unified reference-based cleanup; represent self-KILL with its matching status schema and no ticket.
- **Behavior change:** Prepared self KILL CONNECTION succeeds without a schema mismatch; self KILL QUERY retains its session and prepared handle.
- **Compatibility:** No wire definitions, stored formats or configuration changes. The response header matches Prepare.
- **Performance impact:** No new shared state, lock or command-path allocation for timeout calculation.
- **Rollback:** Revert the lifecycle PR as a unit; no metadata migration is required.

### Testing

- ./run-fe-ut.sh --run with 17 selected lifecycle, protocol, timeout, KILL and schema test classes: 272 tests passed, zero failures/errors/skips; full script BUILD SUCCESS.
- mvn -f fe/pom.xml checkstyle:check -pl fe-core: passed.
- git diff --cached upstream/master --check: passed.
- Verified report totals and source/compiled-class timestamps. No MySQL golden changes.
- Cluster regression tests were not run.

### Release note

None

### Check List (For Author)

- Behavior changed: Yes (see Details).
- Does this need documentation: Yes; ownership and caller responsibilities are documented in the English class comments.
- Pre-submission status: Partially verified: selected unit tests and style checks passed; cluster regression was not run.

### AI Disclosure

- Tool: OpenAI Codex (GPT-6).
- Generated: Conflict resolution, tests and this description.
- Human review: Pending.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by one committer. backport/4.2-master reviewed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants