[KYUUBI #7458] Add server-side statement interceptor SPI - #7530
[KYUUBI #7458] Add server-side statement interceptor SPI#7530wangzhigang1999 wants to merge 12 commits into
Conversation
|
Design note —
Also manually verified on a Kubernetes deployment: REWRITE/REJECT over Beeline (reject surfaces SQLState |
|
Before taking a detailed look, another common use case, in addition to REWRITE/REJECT, is to tune the config. |
Good point. The current SPI exposes the statement-level conf overlay as read-only, so an interceptor cannot tune it. I am considering letting StatementInterceptResult carry a config-overlay delta. The interceptor chain would accumulate those updates in order, each following interceptor would see the updated overlay, and later values would win. The final overlay would then be passed to the engine operation together with the intercepted statement. I prefer an explicit result delta over making StatementInterceptContext mutable, since config tuning can also be combined with REWRITE and the chain semantics remain clear. Does that match the use case you have in mind? |
f6088e5 to
e463209
Compare
There was a problem hiding this comment.
Pull request overview
Adds a gateway-level, server-side StatementInterceptor SPI that runs on each interactive statement before operation creation/routing, enabling statement inspection/rewrite/reject and per-statement config tuning, while preserving a stable statementId correlated with the resulting operation handle.
Changes:
- Introduces
StatementInterceptor/StatementInterceptContext/StatementInterceptResultSPI inkyuubi-server-plugin, plus server-side execution and lifecycle management (eager init, fail-closed, close on shutdown). - Wires interception into the interactive execution path and pre-allocates
OperationHandleso the interceptor observes the same id returned to clients. - Propagates per-statement config overlays into Spark engine statement execution and plan-only paths; adds unit + end-to-end coverage and user documentation.
Reviewed changes
Copilot reviewed 21 out of 21 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| kyuubi-server/src/test/scala/org/apache/kyuubi/plugin/StatementInterceptionSuite.scala | Unit tests for interceptor chaining semantics, fail-closed behavior, config overlay accumulation, and thread-safety. |
| kyuubi-server/src/test/scala/org/apache/kyuubi/plugin/PluginLoaderSuite.scala | Tests loading/ordering/error handling for statement interceptors via PluginLoader. |
| kyuubi-server/src/test/scala/org/apache/kyuubi/operation/StatementInterceptorSuite.scala | End-to-end JDBC→server→engine tests validating proceed/rewrite/reject/tune plus statementId and impersonation behavior. |
| kyuubi-server/src/main/scala/org/apache/kyuubi/session/KyuubiSessionManager.scala | Eagerly loads/initializes/closes interceptors; adds interceptStatement entry point used by sessions. |
| kyuubi-server/src/main/scala/org/apache/kyuubi/session/KyuubiSessionImpl.scala | Runs interception before parsing/routing; pre-allocates and reuses an OperationHandle for stable statementId. |
| kyuubi-server/src/main/scala/org/apache/kyuubi/plugin/StatementInterception.scala | Implements interceptor initialization lifecycle + per-statement execution chain and fail-closed semantics. |
| kyuubi-server/src/main/scala/org/apache/kyuubi/plugin/PluginLoader.scala | Adds loadStatementInterceptors to instantiate configured interceptor classes. |
| kyuubi-server/src/main/scala/org/apache/kyuubi/operation/KyuubiOperationManager.scala | Adds overloads allowing server-side injection of a pre-allocated OperationHandle. |
| kyuubi-server/src/main/scala/org/apache/kyuubi/operation/KyuubiOperation.scala | Makes operation handle injectable via constructor parameter to preserve statement id during superclass init. |
| kyuubi-server/src/main/scala/org/apache/kyuubi/operation/ExecuteStatement.scala | Accepts injected OperationHandle for statement operations. |
| kyuubi-server/src/main/scala/org/apache/kyuubi/operation/ExecutedCommandExec.scala | Accepts injected OperationHandle for server-side command operations. |
| kyuubi-common/src/main/scala/org/apache/kyuubi/config/KyuubiConf.scala | Adds kyuubi.operation.statement.interceptors server-only immutable config entry. |
| externals/kyuubi-spark-sql-engine/src/main/scala/org/apache/kyuubi/engine/spark/operation/SparkSQLOperationManager.scala | Passes per-statement overlay into Spark operations (execute + plan-only). |
| externals/kyuubi-spark-sql-engine/src/main/scala/org/apache/kyuubi/engine/spark/operation/PlanOnlyStatement.scala | Applies per-statement overlay via an operation-scoped SQLConf. |
| externals/kyuubi-spark-sql-engine/src/main/scala/org/apache/kyuubi/engine/spark/operation/ExecuteStatement.scala | Applies per-statement overlay via an operation-scoped SQLConf for Spark statement execution. |
| extensions/server/kyuubi-server-plugin/src/main/java/org/apache/kyuubi/plugin/StatementInterceptResult.java | Defines immutable interceptor decision object (proceed/rewrite/reject) plus config overlay delta. |
| extensions/server/kyuubi-server-plugin/src/main/java/org/apache/kyuubi/plugin/StatementInterceptor.java | Defines the SPI interface and lifecycle (initialize/beforeExecuteStatement/close). |
| extensions/server/kyuubi-server-plugin/src/main/java/org/apache/kyuubi/plugin/StatementInterceptContext.java | Defines stable gateway context (JDK types only) including statementId. |
| docs/extensions/server/statement_interceptor.rst | Documents SPI contract, config, semantics, and examples/notes. |
| docs/extensions/server/index.rst | Adds statement interceptor docs page to server extensions index. |
| docs/configuration/settings.md | Adds generated settings entry for kyuubi.operation.statement.interceptors. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Add a user-facing English doc for the statement interceptor SPI under docs/extensions/server and register it in the server extensions toctree.
…ptor Pre-allocate the operation handle before interception and reuse it for the operation instead of letting the operation self-generate one, and add StatementInterceptContext#statementId so an interceptor can correlate the intercepted statement with its later operation and result set. The id equals the operation handle the client receives.
…cted statements Expose StatementInterceptContext#realUser, the authenticated user before any impersonation, alongside user() which is documented as the effective (proxy) user the statement runs as, so an interceptor can authorize and audit both the effective and the real identity. Throw the REJECT error with SQLState 42000 (access rule violation) so JDBC and BI clients can tell a policy rejection from a syntax error programmatically rather than by parsing the message text. Claude-Session: https://claude.ai/code/session_01Mco2CEWmiF95hJFVy77usj
…ommon unchanged Inject the pre-allocated operation handle through KyuubiOperation's `override protected val handle` constructor param and revert the AbstractOperation constructor in kyuubi-common to its original single-arg form. The handle stays a constructor paramaccessor so AbstractOperation's statementId reads it during superclass initialization. Other refinements: - Reduce StatementInterceptContextImpl to `override val` constructor params. - Drop the redundant defensive copies of the already read-only confOverlay and server conf maps; normalize null ipAddress/engineType with Option. - Mark statementInterceptors @volatile. - Document that engineType() returns the upper-cased kyuubi.engine.type enum names such as SPARK_SQL, and that batch and Data Agent REST paths are not intercepted.
0730404 to
5c7440f
Compare
Why are the changes needed?
First half of #7458 (the statement hook; the result-set hook remains follow-up work). Kyuubi lacks a gateway-level, per-statement hook before a statement is routed to an engine; the existing Spark authorization path is Spark-specific. This adds a server-side
StatementInterceptorSPI for interactive statements before operation creation and routing. Interceptors can inspect, rewrite, reject, or tune per-statement configuration, providing one stable hook for authorization, risky-SQL interception, rewriting, and execution tuning.kyuubi-server-pluginmodule and exposes JDK types only; session credentials and mutable session configuration are not exposed.kyuubi.operation.statement.interceptorshas server audience and is immutable to session users. Plugins initialize eagerly, run in configured order, and fail closed on exceptions or invalid results.statementId()is allocated before interception and, when execution proceeds, matches the client operation handle for later correlation.The SPI currently covers interactive statements only.
How was this patch tested?
Coverage includes unit tests (
StatementInterceptionSuite,PluginLoaderSuite) and the JDBC-to-server-to-engineStatementInterceptorSuitefor proceed, rewrite, reject,executeOnServer, fail-closed behavior, concurrency, statement correlation, and configuration tuning.After rebasing onto current
apache/master, the refresh was validated with:dev/reformatdev/gen/gen_all_config_docs.sh(AllKyuubiConfiguration: 1/1)build/mvn -Pflink-provided,hive-provided,spark-provided test -pl kyuubi-server -am -Dtest=none -DwildcardSuites=org.apache.kyuubi.plugin.StatementInterceptionSuite(18/18)Was this patch authored or co-authored using generative AI tooling?
Assisted-by: Claude Opus 4.8 and Codex GPT-5