Skip to content

Commit f868574

Browse files
committed
tree: change hint injection validation to take FmtFlags arg
Instead of passing around settings everywhere, let the outer code look up the value of `sql.stats.statement_fingerprint.format_mask` and instead pass FmtFlags into the hint injection code. This will simplify the next PR. Informs: #153633 Release note: None
1 parent bfc91ee commit f868574

File tree

2 files changed

+61
-28
lines changed

2 files changed

+61
-28
lines changed

pkg/sql/sem/tree/inject_hints.go

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ package tree
88
import (
99
"reflect"
1010

11-
"github.com/cockroachdb/cockroach/pkg/settings"
1211
"github.com/cockroachdb/errors"
1312
)
1413

@@ -36,22 +35,13 @@ type HintInjectionDonor struct {
3635
collapsed []bool
3736
}
3837

39-
// validationFmtFlags returns the FmtFlags used to check that the donor
40-
// statement and the target statement match. These should be the same FmtFlags
41-
// used for statement fingerprints (including
42-
// sql.stats.statement_fingerprint.format_mask) with FmtHideHints added.
43-
func validationFmtFlags(sv *settings.Values) FmtFlags {
44-
stmtFingerprintFmtMask := FmtHideConstants | FmtFlags(QueryFormattingForFingerprintsMask.Get(sv))
45-
return stmtFingerprintFmtMask | FmtHideHints
46-
}
47-
4838
// NewHintInjectionDonor creates a HintInjectionDonor from a parsed AST. The
4939
// parsed donor statement could be a regular SQL statement or a statement
5040
// fingerprint.
51-
func NewHintInjectionDonor(ast Statement, sv *settings.Values) (*HintInjectionDonor, error) {
41+
func NewHintInjectionDonor(ast Statement, fingerprintFlags FmtFlags) (*HintInjectionDonor, error) {
5242
hd := &HintInjectionDonor{
5343
ast: ast,
54-
validationSQL: AsStringWithFlags(ast, validationFmtFlags(sv)),
44+
validationSQL: FormatStatementHideConstants(ast, fingerprintFlags, FmtHideHints),
5545
collapsed: []bool{false},
5646
}
5747
WalkStmt(hd, ast)
@@ -318,8 +308,8 @@ func (v *hintInjectionVisitor) VisitStatementPost(expr Statement) Statement {
318308

319309
// Validate checks that the target statement exactly matches the donor (except
320310
// for hints).
321-
func (hd *HintInjectionDonor) Validate(stmt Statement, sv *settings.Values) error {
322-
sql := AsStringWithFlags(stmt, validationFmtFlags(sv))
311+
func (hd *HintInjectionDonor) Validate(stmt Statement, fingerprintFlags FmtFlags) error {
312+
sql := FormatStatementHideConstants(stmt, fingerprintFlags, FmtHideHints)
323313
if sql != hd.validationSQL {
324314
return errors.Newf(
325315
"statement does not match hint donor statement: %v vs %v", sql, hd.validationSQL,

pkg/sql/sem/tree/inject_hints_test.go

Lines changed: 57 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,13 @@ func TestInjectHints(t *testing.T) {
2727
defer log.Scope(t).Close(t)
2828

2929
testCases := []struct {
30-
name string
31-
originalSQL string
32-
donorSQL string
33-
expectedSQL string
34-
expectChanged bool
35-
expectError bool
30+
name string
31+
originalSQL string
32+
donorSQL string
33+
expectedSQL string
34+
expectChanged bool
35+
expectError bool
36+
legacyFingerprint bool
3637
}{
3738
{
3839
name: "inject index hint on simple table",
@@ -248,19 +249,55 @@ func TestInjectHints(t *testing.T) {
248249
expectedSQL: "SELECT * FROM (VALUES (5, 6), (7, 8)) AS v (c, d), a@{NO_FULL_SCAN} WHERE b = c",
249250
expectChanged: true,
250251
},
252+
{
253+
name: "tuple with legacy collapsed list",
254+
originalSQL: "SELECT (4, 5, 6) FROM a JOIN b ON c = d",
255+
donorSQL: "SELECT (_, _, __more1_10__) FROM a INNER HASH JOIN b ON c = d",
256+
expectedSQL: "SELECT (4, 5, 6) FROM a INNER HASH JOIN b ON c = d",
257+
expectChanged: true,
258+
legacyFingerprint: true,
259+
},
260+
{
261+
name: "array with legacy collapsed list",
262+
originalSQL: "SELECT ARRAY[4, 5, 6] FROM a JOIN b ON c = d",
263+
donorSQL: "SELECT ARRAY[_, _, __more1_10__] FROM a@{NO_FULL_SCAN} INNER HASH JOIN b@b_d_idx ON c = d",
264+
expectedSQL: "SELECT ARRAY[4, 5, 6] FROM a@{NO_FULL_SCAN} INNER HASH JOIN b@b_d_idx ON c = d",
265+
expectChanged: true,
266+
legacyFingerprint: true,
267+
},
268+
{
269+
name: "values with legacy collapsed lists",
270+
originalSQL: "SELECT * FROM (VALUES (5, 6), (7, 8)) AS v (c, d), a WHERE b = c",
271+
donorSQL: "SELECT * FROM (VALUES (_, _), (__more1_10__)) AS v (c, d), a@{NO_FULL_SCAN} WHERE b = c",
272+
expectedSQL: "SELECT * FROM (VALUES (5, 6), (7, 8)) AS v (c, d), a@{NO_FULL_SCAN} WHERE b = c",
273+
expectChanged: true,
274+
legacyFingerprint: true,
275+
},
251276
{
252277
name: "inner join",
253278
originalSQL: "SELECT * FROM a INNER JOIN b ON true",
254279
donorSQL: "SELECT * FROM a INNER JOIN b ON true",
255280
expectedSQL: "SELECT * FROM a INNER JOIN b ON true",
256281
expectChanged: false,
257282
},
283+
{
284+
name: "inner join in donor",
285+
originalSQL: "SELECT * FROM a JOIN b ON true",
286+
donorSQL: "SELECT * FROM a INNER JOIN b ON true",
287+
expectedSQL: "SELECT * FROM a INNER JOIN b ON true",
288+
expectChanged: true,
289+
},
258290
}
259291

260292
st := cluster.MakeTestingClusterSettings()
261293

262294
for _, tc := range testCases {
263295
t.Run(tc.name, func(t *testing.T) {
296+
var fingerprintFlags tree.FmtFlags
297+
if !tc.legacyFingerprint {
298+
fingerprintFlags = tree.FmtFlags(tree.QueryFormattingForFingerprintsMask.Get(&st.SV))
299+
}
300+
264301
// Parse original statement.
265302
originalStmt, err := parser.ParseOne(tc.originalSQL)
266303
require.NoError(t, err)
@@ -270,15 +307,15 @@ func TestInjectHints(t *testing.T) {
270307
require.NoError(t, err)
271308

272309
// Create hint injection donor.
273-
donor, err := tree.NewHintInjectionDonor(donorStmt.AST, &st.SV)
310+
donor, err := tree.NewHintInjectionDonor(donorStmt.AST, fingerprintFlags)
274311
require.NoError(t, err)
275312

276313
// Validate.
277314
if tc.expectError {
278-
require.Error(t, donor.Validate(originalStmt.AST, &st.SV))
315+
require.Error(t, donor.Validate(originalStmt.AST, fingerprintFlags))
279316
return
280317
} else {
281-
require.NoError(t, donor.Validate(originalStmt.AST, &st.SV))
318+
require.NoError(t, donor.Validate(originalStmt.AST, fingerprintFlags))
282319
}
283320

284321
// Inject hints.
@@ -347,15 +384,21 @@ func TestRandomInjectHints(t *testing.T) {
347384
t.Log(randSQL + ";")
348385

349386
// Generate the statement fingerprint for the random statement.
387+
var fingerprintFlags tree.FmtFlags
388+
if rng.Intn(10) < 9 {
389+
// 90% chance of using the current default statement fingerprint
390+
// formatting, 10% chance of using the legacy statement fingerprint
391+
// formatting.
392+
fingerprintFlags = tree.FmtFlags(tree.QueryFormattingForFingerprintsMask.Get(&st.SV))
393+
}
350394
randStmt, err := parser.ParseOne(randSQL)
351395
require.NoError(t, err)
352-
fingerprintFlags := tree.FmtHideConstants | tree.FmtCollapseLists | tree.FmtConstantsAsUnderscores
353-
randFingerprint := tree.AsStringWithFlags(randStmt.AST, fingerprintFlags)
396+
randFingerprint := tree.FormatStatementHideConstants(randStmt.AST, fingerprintFlags)
354397

355398
// Parse random fingerprint to make the donor AST.
356399
donorStmt, err := parser.ParseOne(randFingerprint)
357400
require.NoError(t, err)
358-
donor, err := tree.NewHintInjectionDonor(donorStmt.AST, &st.SV)
401+
donor, err := tree.NewHintInjectionDonor(donorStmt.AST, fingerprintFlags)
359402
require.NoError(t, err)
360403
donorSQL := tree.AsString(donorStmt.AST)
361404

@@ -366,15 +409,15 @@ func TestRandomInjectHints(t *testing.T) {
366409
require.NoError(t, err)
367410

368411
// Validate.
369-
require.NoError(t, donor.Validate(targetStmt.AST, &st.SV))
412+
require.NoError(t, donor.Validate(targetStmt.AST, fingerprintFlags))
370413

371414
// Inject hints.
372415
result, changed, err := donor.InjectHints(targetStmt.AST)
373416
require.NoError(t, err)
374417

375418
// Check that the rewritten statement matches the donor statement when
376419
// formatted as a statement fingerprint.
377-
resultFingerprint := tree.AsStringWithFlags(result, fingerprintFlags)
420+
resultFingerprint := tree.FormatStatementHideConstants(result, fingerprintFlags)
378421
require.Equal(t, donorSQL, resultFingerprint, "resulting SQL mismatch")
379422
if !changed {
380423
require.Same(t, targetStmt.AST, result, "resulting statement was changed")

0 commit comments

Comments
 (0)