Skip to content

Commit 4a3e787

Browse files
committed
sql: fix exec+audit logs for BEGIN, COMMIT, SET stmts
Release note (bug fix): Fixed a bug where BEGIN, COMMIT, SET, ROLLBACK, and SAVEPOINT statements would not be written to the execution or audit logs.
1 parent cdf6d15 commit 4a3e787

File tree

3 files changed

+155
-32
lines changed

3 files changed

+155
-32
lines changed

pkg/ccl/auditloggingccl/audit_logging_test.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,22 @@ func TestSingleRoleAuditLogging(t *testing.T) {
156156
`GRANT SELECT ON TABLE u TO root`,
157157
// DML statement
158158
`SELECT * FROM u`,
159+
// The following statements are all executed specially by the conn_executor.
160+
`SET application_name = 'test'`,
161+
`SET CLUSTER SETTING sql.defaults.vectorize = 'on'`,
162+
`BEGIN`,
163+
`SHOW application_name`,
164+
`SAVEPOINT s`,
165+
`RELEASE SAVEPOINT s`,
166+
`SAVEPOINT t`,
167+
`ROLLBACK TO SAVEPOINT t`,
168+
`COMMIT`,
169+
`SHOW COMMIT TIMESTAMP`,
170+
`BEGIN TRANSACTION PRIORITY LOW`,
171+
`ROLLBACK`,
172+
`PREPARE q AS SELECT 1`,
173+
`EXECUTE q`,
174+
`DEALLOCATE q`,
159175
}
160176
testData := []struct {
161177
name string
@@ -167,7 +183,7 @@ func TestSingleRoleAuditLogging(t *testing.T) {
167183
name: "test-all-stmt-types",
168184
role: allStmtTypesRole,
169185
queries: testQueries,
170-
expectedNumLogs: 3,
186+
expectedNumLogs: len(testQueries),
171187
},
172188
{
173189
name: "test-no-stmt-types",
@@ -181,7 +197,7 @@ func TestSingleRoleAuditLogging(t *testing.T) {
181197
role: "testuser",
182198
queries: testQueries,
183199
// One for each test query.
184-
expectedNumLogs: 3,
200+
expectedNumLogs: len(testQueries),
185201
},
186202
}
187203

pkg/sql/conn_executor_exec.go

Lines changed: 131 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ func (ex *connExecutor) execStmt(
133133
// Note: when not using explicit transactions, we go through this transition
134134
// for every statement. It is important to minimize the amount of work and
135135
// allocations performed up to this point.
136-
ev, payload = ex.execStmtInNoTxnState(ctx, ast, res)
136+
ev, payload = ex.execStmtInNoTxnState(ctx, parserStmt, res)
137137

138138
case stateOpen:
139139
var preparedStmt *PreparedStatement
@@ -753,6 +753,72 @@ func (ex *connExecutor) execStmtInOpenState(
753753
}
754754
}(ctx)
755755

756+
// If adminAuditLogging is enabled, we want to check for HasAdminRole
757+
// before maybeLogStatement.
758+
// We must check prior to execution in the case the txn is aborted due to
759+
// an error. HasAdminRole can only be checked in a valid txn.
760+
if adminAuditLog := adminAuditLogEnabled.Get(
761+
&ex.planner.execCfg.Settings.SV,
762+
); adminAuditLog {
763+
if !ex.extraTxnState.hasAdminRoleCache.IsSet {
764+
hasAdminRole, err := ex.planner.HasAdminRole(ctx)
765+
if err != nil {
766+
return makeErrEvent(err)
767+
}
768+
ex.extraTxnState.hasAdminRoleCache.HasAdminRole = hasAdminRole
769+
ex.extraTxnState.hasAdminRoleCache.IsSet = true
770+
}
771+
}
772+
773+
p.stmt = stmt
774+
p.semaCtx.Annotations = tree.MakeAnnotations(stmt.NumAnnotations)
775+
p.extendedEvalCtx.Annotations = &p.semaCtx.Annotations
776+
if err := p.semaCtx.Placeholders.Assign(pinfo, stmt.NumPlaceholders); err != nil {
777+
return makeErrEvent(err)
778+
}
779+
p.extendedEvalCtx.Placeholders = &p.semaCtx.Placeholders
780+
781+
shouldLogToExecAndAudit := true
782+
defer func() {
783+
if !shouldLogToExecAndAudit {
784+
// We don't want to log this statement, since another layer of the
785+
// conn_executor will handle the logging for this statement.
786+
return
787+
}
788+
789+
p.curPlan.init(&p.stmt, &p.instrumentation)
790+
var execErr error
791+
if p, ok := retPayload.(payloadWithError); ok {
792+
execErr = p.errorCause()
793+
}
794+
f := tree.NewFmtCtx(tree.FmtHideConstants)
795+
f.FormatNode(ast)
796+
stmtFingerprintID := appstatspb.ConstructStatementFingerprintID(
797+
f.CloseAndGetString(),
798+
execErr != nil,
799+
ex.implicitTxn(),
800+
p.CurrentDatabase(),
801+
)
802+
803+
p.maybeLogStatement(
804+
ctx,
805+
ex.executorType,
806+
false, /* isCopy */
807+
int(ex.state.mu.autoRetryCounter),
808+
ex.extraTxnState.txnCounter,
809+
0, /* rowsAffected */
810+
ex.state.mu.stmtCount,
811+
0, /* bulkJobId */
812+
execErr,
813+
ex.statsCollector.PhaseTimes().GetSessionPhaseTime(sessionphase.SessionQueryReceived),
814+
&ex.extraTxnState.hasAdminRoleCache,
815+
ex.server.TelemetryLoggingMetrics,
816+
stmtFingerprintID,
817+
&topLevelQueryStats{},
818+
ex.statsCollector,
819+
)
820+
}()
821+
756822
switch s := ast.(type) {
757823
case *tree.BeginTransaction:
758824
// BEGIN is only allowed if we are in an implicit txn.
@@ -833,15 +899,27 @@ func (ex *connExecutor) execStmtInOpenState(
833899
ex.server.cfg.GenerateID(),
834900
)
835901
var rawTypeHints []oid.Oid
902+
903+
// Placeholders should be part of the statement being prepared, not the
904+
// PREPARE statement itself.
905+
oldPlaceholders := p.extendedEvalCtx.Placeholders
906+
p.extendedEvalCtx.Placeholders = nil
836907
if _, err := ex.addPreparedStmt(
837908
ctx, name, prepStmt, typeHints, rawTypeHints, PreparedStatementOriginSQL,
838909
); err != nil {
839910
return makeErrEvent(err)
840911
}
912+
// The call to addPreparedStmt changed the planner stmt to the statement
913+
// being prepared. Set it back to the PREPARE statement, so that it's
914+
// logged correctly.
915+
p.stmt = stmt
916+
p.extendedEvalCtx.Placeholders = oldPlaceholders
841917
return nil, nil, nil
842918
}
843919

844-
p.semaCtx.Annotations = tree.MakeAnnotations(stmt.NumAnnotations)
920+
// Don't write to the exec/audit logs here; it will be handled in
921+
// dispatchToExecutionEngine.
922+
shouldLogToExecAndAudit = false
845923

846924
// For regular statements (the ones that get to this point), we
847925
// don't return any event unless an error happens.
@@ -894,12 +972,6 @@ func (ex *connExecutor) execStmtInOpenState(
894972
return makeErrEvent(err)
895973
}
896974

897-
if err := p.semaCtx.Placeholders.Assign(pinfo, stmt.NumPlaceholders); err != nil {
898-
return makeErrEvent(err)
899-
}
900-
p.extendedEvalCtx.Placeholders = &p.semaCtx.Placeholders
901-
p.extendedEvalCtx.Annotations = &p.semaCtx.Annotations
902-
p.stmt = stmt
903975
if isPausablePortal() {
904976
p.pausablePortal = portal
905977
}
@@ -1453,23 +1525,6 @@ func (ex *connExecutor) dispatchToExecutionEngine(
14531525
}
14541526
}
14551527

1456-
// If adminAuditLogging is enabled, we want to check for HasAdminRole
1457-
// before the deferred maybeLogStatement.
1458-
// We must check prior to execution in the case the txn is aborted due to
1459-
// an error. HasAdminRole can only be checked in a valid txn.
1460-
if adminAuditLog := adminAuditLogEnabled.Get(
1461-
&ex.planner.execCfg.Settings.SV,
1462-
); adminAuditLog {
1463-
if !ex.extraTxnState.hasAdminRoleCache.IsSet {
1464-
hasAdminRole, err := ex.planner.HasAdminRole(ctx)
1465-
if err != nil {
1466-
return err
1467-
}
1468-
ex.extraTxnState.hasAdminRoleCache.HasAdminRole = hasAdminRole
1469-
ex.extraTxnState.hasAdminRoleCache.IsSet = true
1470-
}
1471-
}
1472-
14731528
var err error
14741529
if ppInfo := getPausablePortalInfo(); ppInfo != nil {
14751530
if !ppInfo.dispatchToExecutionEngine.cleanup.isComplete {
@@ -2190,8 +2245,57 @@ var eventStartExplicitTxn fsm.Event = eventTxnStart{ImplicitTxn: fsm.False}
21902245
// the cursor is not advanced. This means that the statement will run again in
21912246
// stateOpen, at each point its results will also be flushed.
21922247
func (ex *connExecutor) execStmtInNoTxnState(
2193-
ctx context.Context, ast tree.Statement, res RestrictedCommandResult,
2248+
ctx context.Context, parserStmt statements.Statement[tree.Statement], res RestrictedCommandResult,
21942249
) (_ fsm.Event, payload fsm.EventPayload) {
2250+
shouldLogToExecAndAudit := true
2251+
defer func() {
2252+
if !shouldLogToExecAndAudit {
2253+
// We don't want to log this statement, since another layer of the
2254+
// conn_executor will handle the logging for this statement.
2255+
return
2256+
}
2257+
2258+
p := &ex.planner
2259+
stmt := makeStatement(parserStmt, ex.server.cfg.GenerateID())
2260+
p.stmt = stmt
2261+
p.semaCtx.Annotations = tree.MakeAnnotations(stmt.NumAnnotations)
2262+
p.extendedEvalCtx.Annotations = &p.semaCtx.Annotations
2263+
p.extendedEvalCtx.Placeholders = &tree.PlaceholderInfo{}
2264+
p.curPlan.init(&p.stmt, &p.instrumentation)
2265+
var execErr error
2266+
if p, ok := payload.(payloadWithError); ok {
2267+
execErr = p.errorCause()
2268+
}
2269+
2270+
f := tree.NewFmtCtx(tree.FmtHideConstants)
2271+
f.FormatNode(stmt.AST)
2272+
stmtFingerprintID := appstatspb.ConstructStatementFingerprintID(
2273+
f.CloseAndGetString(),
2274+
execErr != nil,
2275+
ex.implicitTxn(),
2276+
p.CurrentDatabase(),
2277+
)
2278+
2279+
p.maybeLogStatement(
2280+
ctx,
2281+
ex.executorType,
2282+
false, /* isCopy */
2283+
int(ex.state.mu.autoRetryCounter),
2284+
ex.extraTxnState.txnCounter,
2285+
0, /* rowsAffected */
2286+
0, /* stmtCount */
2287+
0, /* bulkJobId */
2288+
execErr,
2289+
ex.statsCollector.PhaseTimes().GetSessionPhaseTime(sessionphase.SessionQueryReceived),
2290+
&ex.extraTxnState.hasAdminRoleCache,
2291+
ex.server.TelemetryLoggingMetrics,
2292+
stmtFingerprintID,
2293+
&topLevelQueryStats{},
2294+
ex.statsCollector,
2295+
)
2296+
}()
2297+
2298+
ast := parserStmt.AST
21952299
switch s := ast.(type) {
21962300
case *tree.BeginTransaction:
21972301
ex.incrementStartedStmtCounter(ast)
@@ -2225,6 +2329,7 @@ func (ex *connExecutor) execStmtInNoTxnState(
22252329
// historical timestamp even though the statement itself might contain
22262330
// an AOST clause. In these cases the clause is evaluated and applied
22272331
// execStmtInOpenState.
2332+
shouldLogToExecAndAudit = false
22282333
noBeginStmt := (*tree.BeginTransaction)(nil)
22292334
mode, sqlTs, historicalTs, err := ex.beginTransactionTimestampsAndReadMode(ctx, noBeginStmt)
22302335
if err != nil {

pkg/sql/telemetry_logging_test.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1505,15 +1505,17 @@ func TestTelemetryLoggingStmtPosInTxn(t *testing.T) {
15051505

15061506
st.SetTime(timeutil.FromUnixMicros(int64(1e6)))
15071507
db.Exec(t, `BEGIN;`)
1508-
db.Exec(t, `SELECT 1`)
15091508
st.SetTime(timeutil.FromUnixMicros(int64(2 * 1e6)))
1510-
db.Exec(t, `SELECT 2`)
1509+
db.Exec(t, `SELECT 1`)
15111510
st.SetTime(timeutil.FromUnixMicros(int64(3 * 1e6)))
1511+
db.Exec(t, `SELECT 2`)
1512+
st.SetTime(timeutil.FromUnixMicros(int64(4 * 1e6)))
15121513
db.Exec(t, `SELECT 3`)
1514+
st.SetTime(timeutil.FromUnixMicros(int64(5 * 1e6)))
15131515
db.Exec(t, `COMMIT;`)
15141516

15151517
expectedQueries := []string{
1516-
`SELECT ‹1›`, `SELECT ‹2›`, `SELECT ‹3›`,
1518+
`BEGIN`, `SELECT ‹1›`, `SELECT ‹2›`, `SELECT ‹3›`, `COMMIT`,
15171519
}
15181520

15191521
log.Flush()
@@ -1539,7 +1541,7 @@ func TestTelemetryLoggingStmtPosInTxn(t *testing.T) {
15391541
if strings.Contains(e.Message, expected) {
15401542
var sq eventpb.SampledQuery
15411543
require.NoError(t, json.Unmarshal([]byte(e.Message), &sq))
1542-
require.Equal(t, uint32(i+1), sq.StmtPosInTxn, "%s", entries)
1544+
require.Equalf(t, uint32(i), sq.StmtPosInTxn, "stmt=%s entries: %s", expected, entries)
15431545
found = true
15441546
break
15451547
}

0 commit comments

Comments
 (0)