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]

Reply via email to