Uploaded image for project: 'MariaDB Server'
  1. MariaDB Server
  2. MDEV-41224

Split select_handler selection into pre- and post-optimization phases

    XMLWordPrintable

Details

    • New Feature
    • Status: Open (View Workflow)
    • Major
    • Resolution: Unresolved
    • None
    • None
    • None

    Description

      Description

      MariaDB's select_handler interface allows a storage engine to execute a complete SELECT. Currently, handler discovery and selection happen before JOIN::optimize_inner(), and selecting a handler prevents normal local optimization.

      This makes it impossible to base handler selection on the optimized local plan and leaves no local plan available for fallback if the selected handler rejects the query before producing output.

      The limitation is not specific to InnoDB. A select handler may be used for queries over tables owned by any storage engine, including from inside a transaction, provided that the handler implements the required transaction and visibility semantics.

      Current behavior

      find_single_select_handler() asks the engines owning the query tables for a handler before JOIN::optimize() completes. If a handler is returned, it is attached to SELECT_LEX::pushdown_select, and JOIN::optimize_inner() is skipped.

      This has the following consequences:

      1. The handler cannot inspect the cost or other properties of the optimized local plan or data, e.g. "freshness" of the data in the in the secondary storage that is reachable via SelectHandler attached to a primary storage engine.
      2. If handler initialization fails, there is no optimized local plan available for fallback.
      3. A handler normally cannot process a query unless it owns one of its tables.
      4. select_handler::execute() does not distinguish a failure before client-visible output from a failure after metadata or rows have been sent.

      Proposed solution

      Decouple handler providers from table ownership

      After checking the storage engines that own the query tables, optionally allow other installed engines exposing create_select to inspect the query.

      This makes it possible for a select handler to process a query even when none of its tables are owned by the handler's storage engine. The source tables may belong to InnoDB or to any other storage engine.

      The candidate engine remains responsible for deciding whether it can process the query. Returning NULL means that the engine refuses the query and normal execution continues.

      Introduce opt-in two-phase handler selection

      For engines that opt in through a new handlerton capability flag, split handler selection into two phases:

      • *Phase A — before optimization:* inspect the original query and create a candidate handler. A text-emitting handler may snapshot the query using SELECT_LEX_UNIT::print() before optimizer transformations modify the parse tree. The candidate is not attached to SELECT_LEX::pushdown_select yet.
      • *Phase B — after local optimization:* inspect the completed local plan and either accept or reject handler execution. If accepted, attach the candidate to SELECT_LEX::pushdown_select; otherwise, destroy it and retain the local plan.

      The regular optimizer must run with the original optimizer_switch settings. The implementation must not require copying fragments of JOIN::optimize_inner() or manually restoring optimizer mutations.

      Policy decisions such as cost thresholds, supported syntax, source table eligibility, or data freshness may be implemented by the handler. The split interface itself must not prescribe a particular policy.

      Preserve compatibility with existing handlers

      Changing the optimization order may break tree-walking consumers that expect an unoptimized SELECT_LEX. The two-phase path must therefore be opt-in.

      Existing engines must retain the current call ordering and behavior unless they explicitly enable the new interface.

      Add a safe fallback point

      Add an explicit way to determine whether result-set metadata or rows have already been emitted.

      select_handler::execute() should return a distinct result indicating that execution failed before any client-visible output was sent. On this result, the server can destroy the handler, clear SELECT_LEX::pushdown_select, and execute the already optimized local plan.

      A failure from handler preparation should similarly reject the candidate and continue with local execution.

      After metadata or rows have been sent, fallback is not safe and the error must be returned to the client.

      Transaction semantics

      The server must not impose a blanket restriction on using a split select handler inside an active transaction.

      A select handler may be selected inside a transaction when it can provide the visibility and consistency semantics required by its use case. A handler that cannot provide the required transaction view may reject the query during its capability or policy checks and leave execution to the local plan.

      Transaction eligibility is therefore part of the handler's contract and policy, not a generic restriction of the split select_handler interface.

      Acceptance criteria

      1. Handler selection can be split into a pre-optimization phase and a post-optimization phase.
      2. In the pre-optimization phase, an engine can inspect the original query and create a candidate handler without attaching it to SELECT_LEX::pushdown_select.
      3. Normal local optimization completes before the final decision for engines opting into the split interface.
      4. In the post-optimization phase, the candidate can inspect the optimized local plan and either accept or reject execution.
      5. If the candidate rejects execution, the already optimized local plan is used.
      6. A handler may be considered for queries whose tables are owned by any storage engine; the mechanism is not limited to InnoDB.
      7. A handler may be used inside an active transaction when its capability and policy checks allow it.
      8. The server does not apply a generic prohibition based on THD::in_active_multi_stmt_transaction().
      9. A handler that cannot provide the required transaction visibility may reject the query and leave it to normal local execution.
      10. Failure before result metadata or rows are emitted can fall back to the local plan.
      11. Failure after client-visible output has started is returned to the client without attempting fallback.
      12. Engines that do not opt into split handler selection retain the existing select_handler behavior and optimization order.
      13. Existing tree-walking consumers are unaffected unless they explicitly opt into the new interface.
      14. Tests cover both transactional and non-transactional execution.
      15. Tests cover source tables owned by multiple storage-engine types rather than assuming InnoDB ownership.
      16. Tests cover CTE serialization, prepared-statement re-execution, candidate rejection, and fallback.

      Attachments

        Issue Links

          Activity

            People

              drrtuy Roman
              drrtuy Roman
              Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

                Created:
                Updated:

                Time Tracking

                  Estimated:
                  Original Estimate - 0d
                  0d
                  Remaining:
                  Remaining Estimate - 0d
                  0d
                  Logged:
                  Time Spent - 1.75h
                  1.75h

                  Git Integration

                    Error rendering 'com.xiplink.jira.git.jira_git_plugin:git-issue-webpanel'. Please contact your Jira administrators.