This is an automated email from the ASF dual-hosted git repository.
yiguolei pushed a commit to branch branch-4.1
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-4.1 by this push:
new ec3d4f81596 branch-4.1: [fix](audit) record per-query SET_VAR hint
session variables in audit log (#64569) (#64617) (#64599)
ec3d4f81596 is described below
commit ec3d4f81596774290ca2809e36b5434a20b69dd7
Author: 924060929 <[email protected]>
AuthorDate: Thu Jun 18 15:41:43 2026 +0800
branch-4.1: [fix](audit) record per-query SET_VAR hint session variables in
audit log (#64569) (#64617) (#64599)
## Proposed changes
Cherry-pick from #64569 and #64617:
- #64569: record per-query SET_VAR hint session variables in audit log
- #64617: deflake the regression test added by #64569
---
.../java/org/apache/doris/qe/AuditLogHelper.java | 8 ++-
.../java/org/apache/doris/qe/StmtExecutor.java | 19 ++++++
.../test_audit_log_hint_session_context.groovy | 79 ++++++++++++++++++++++
3 files changed, 105 insertions(+), 1 deletion(-)
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/AuditLogHelper.java
b/fe/fe-core/src/main/java/org/apache/doris/qe/AuditLogHelper.java
index bf66f80a75e..e07012fd75e 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/AuditLogHelper.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/AuditLogHelper.java
@@ -263,7 +263,13 @@ public class AuditLogHelper {
auditEventBuilder.setScheduleTimeMs(summaryProfile.getScheduleTime());
// changed variables
if (ctx.sessionVariable != null) {
- List<List<String>> changedVars =
VariableMgr.dumpChangedVars(ctx.sessionVariable);
+ // Prefer the pre-revert snapshot captured in StmtExecutor so
that per-query
+ // SET_VAR hint values are visible; fall back to the live
session variables when
+ // no snapshot was taken (i.e. the statement used no SET_VAR
hint).
+ List<List<String>> changedVars = (ctx.getExecutor() != null
+ && ctx.getExecutor().getChangedSessionVarsForAudit()
!= null)
+ ? ctx.getExecutor().getChangedSessionVarsForAudit()
+ : VariableMgr.dumpChangedVars(ctx.sessionVariable);
StringBuilder changedVarsStr = new StringBuilder();
changedVarsStr.append("{");
for (int i = 0; i < changedVars.size(); i++) {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java
b/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java
index 6de5d241c60..6d98e97af42 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java
@@ -182,6 +182,10 @@ public class StmtExecutor {
private MysqlSerializer serializer;
private OriginStatement originStmt;
private StatementBase parsedStmt;
+ // Snapshot of changed session variables taken BEFORE per-query SET_VAR
hint values are
+ // reverted, so the audit log (built after execute() returns) can reflect
the values that
+ // were actually in effect for this statement. Null when no SET_VAR hint
was used.
+ private List<List<String>> changedSessionVarsForAudit;
private ProfileType profileType = ProfileType.QUERY;
@Setter
@@ -480,6 +484,10 @@ public class StmtExecutor {
return parsedStmt;
}
+ public List<List<String>> getChangedSessionVarsForAudit() {
+ return changedSessionVarsForAudit;
+ }
+
public boolean isHandleQueryInFe() {
return isHandleQueryInFe;
}
@@ -572,6 +580,17 @@ public class StmtExecutor {
throw e;
}
} finally {
+ // Snapshot changed session variables (including SET_VAR hint
values) BEFORE revert,
+ // so the audit log (logged after execute() returns, i.e. after
the revert below) can
+ // reflect what was actually in effect for this statement instead
of the reverted values.
+ if (sessionVariable.getIsSingleSetVar()) {
+ try {
+ changedSessionVarsForAudit =
VariableMgr.dumpChangedVars(sessionVariable);
+ } catch (Throwable t) {
+ LOG.warn("failed to snapshot changed session variables for
audit. {}",
+ context.getQueryIdentifier(), t);
+ }
+ }
// revert Session Value
try {
VariableMgr.revertSessionValue(sessionVariable);
diff --git
a/regression-test/suites/audit/test_audit_log_hint_session_context.groovy
b/regression-test/suites/audit/test_audit_log_hint_session_context.groovy
new file mode 100644
index 00000000000..0ed4ed7ca83
--- /dev/null
+++ b/regression-test/suites/audit/test_audit_log_hint_session_context.groovy
@@ -0,0 +1,79 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// A session variable changed by a per-query SET_VAR hint is temporary: it is
reverted in
+// StmtExecutor.execute()'s finally block. The audit log is built afterwards
(in auditAfterExec),
+// so without a pre-revert snapshot the hint value never reaches
changed_variables. This case
+// verifies that a per-query SET_VAR(session_context=...) is recorded in the
audit log's
+// changed_variables. The value is quoted so the hint parser keeps the ':' in
the value.
+suite("test_audit_log_hint_session_context", "nonConcurrent") {
+ try {
+ sql "set global enable_audit_plugin = true"
+ } catch (Exception e) {
+ log.warn("skip this case, because " + e.getMessage())
+ assertTrue(e.getMessage().toUpperCase().contains("ADMIN"))
+ return
+ }
+
+ def tbl = "audit_hint_session_context"
+ sql "drop table if exists ${tbl}"
+ sql """
+ CREATE TABLE `${tbl}` (`id` bigint) ENGINE=OLAP
+ DUPLICATE KEY(`id`)
+ DISTRIBUTED BY HASH(`id`) BUCKETS 1
+ PROPERTIES ("replication_allocation" = "tag.location.default: 1")
+ """
+ sql "insert into ${tbl} values (1)"
+
+ // unique markers so the exact statement can be located in the audit log
+ def hintTrace = "trace_id:hint_sc_7F3A2B"
+ def stmtMarker = "audit_hint_sc_marker_7F3A2B"
+
+ sql "truncate table __internal_schema.audit_log"
+
+ // per-query SET_VAR hint sets session_context for this statement only
+ sql """select /*+ SET_VAR(session_context="${hintTrace}") */ id,
'${stmtMarker}' as marker from ${tbl}"""
+
+ Thread.sleep(6000)
+ sql """call flush_audit_log()"""
+
+ // The hint query's audit row must carry session_context in
changed_variables. Match by the
+ // marker AND the exact session_context value: the polling query below is
itself audited and
+ // its statement text also contains the marker, but it sets no
session_context. Picking a
+ // single row via "order by time desc limit 1" could therefore land on
such a self-row (whose
+ // session_context is null) depending on audit-flush ordering, which is
flaky. Counting rows
+ // whose session_context equals the expected value excludes those
self-rows entirely.
+ def retry = 60
+ def query = """select count(*) from __internal_schema.audit_log
+ where stmt like '%${stmtMarker}%'
+ and ELEMENT_AT(changed_variables, 'session_context') =
'${hintTrace}'"""
+ def found = (sql "${query}")[0][0] as long
+ while (found == 0) {
+ if (retry-- < 0) {
+ throw new RuntimeException("audit_log row for the hint query with
the expected "
+ + "session_context was not found")
+ }
+ sleep(3000)
+ sql """call flush_audit_log()"""
+ found = (sql "${query}")[0][0] as long
+ }
+
+ // The per-query SET_VAR hint session_context must be visible in
changed_variables.
+ assertTrue(found >= 1)
+
+ sql "set global enable_audit_plugin = false"
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]