@@ -81,11 +81,17 @@ func DigestNormalized(normalized string) (digest *Digest) {
81
81
// Normalize generates the normalized statements.
82
82
// it will get normalized form of statement text
83
83
// which removes general property of a statement but keeps specific property.
84
+ // possible values for 'redact' is "OFF", "ON" or "MARKER". Passing "" is seen as "OFF".
84
85
//
85
- // for example: Normalize('select 1 from b where a = 1') => 'select ? from b where a = ?'
86
- func Normalize (sql string ) (result string ) {
86
+ // when "OFF", it is returned as is
87
+ // for example, when "ON": Normalize('select 1 from b where a = 1') => 'select ? from b where a = ?'
88
+ // for example, when "MARKER": Normalize('select 1 from b where a = 1') => 'select ‹1› from b where a = ‹1›'
89
+ func Normalize (sql string , redact string ) (result string ) {
90
+ if redact == "" || redact == "OFF" {
91
+ return sql
92
+ }
87
93
d := digesterPool .Get ().(* sqlDigester )
88
- result = d .doNormalize (sql , false )
94
+ result = d .doNormalize (sql , redact , false )
89
95
digesterPool .Put (d )
90
96
return
91
97
}
@@ -109,7 +115,7 @@ func NormalizeForBinding(sql string, forPlanReplayerReload bool) (result string)
109
115
// for example: Normalize('select /*+ use_index(t, primary) */ 1 from b where a = 1') => 'select /*+ use_index(t, primary) */ ? from b where a = ?'
110
116
func NormalizeKeepHint (sql string ) (result string ) {
111
117
d := digesterPool .Get ().(* sqlDigester )
112
- result = d .doNormalize (sql , true )
118
+ result = d .doNormalize (sql , "ON" , true )
113
119
digesterPool .Put (d )
114
120
return
115
121
}
@@ -161,30 +167,30 @@ func (d *sqlDigester) doDigestNormalized(normalized string) (digest *Digest) {
161
167
}
162
168
163
169
func (d * sqlDigester ) doDigest (sql string ) (digest * Digest ) {
164
- d .normalize (sql , false , false , false )
170
+ d .normalize (sql , "ON" , false , false , false )
165
171
d .hasher .Write (d .buffer .Bytes ())
166
172
d .buffer .Reset ()
167
173
digest = NewDigest (d .hasher .Sum (nil ))
168
174
d .hasher .Reset ()
169
175
return
170
176
}
171
177
172
- func (d * sqlDigester ) doNormalize (sql string , keepHint bool ) (result string ) {
173
- d .normalize (sql , keepHint , false , false )
178
+ func (d * sqlDigester ) doNormalize (sql string , redact string , keepHint bool ) (result string ) {
179
+ d .normalize (sql , redact , keepHint , false , false )
174
180
result = d .buffer .String ()
175
181
d .buffer .Reset ()
176
182
return
177
183
}
178
184
179
185
func (d * sqlDigester ) doNormalizeForBinding (sql string , keepHint bool , forPlanReplayerReload bool ) (result string ) {
180
- d .normalize (sql , keepHint , true , forPlanReplayerReload )
186
+ d .normalize (sql , "ON" , keepHint , true , forPlanReplayerReload )
181
187
result = d .buffer .String ()
182
188
d .buffer .Reset ()
183
189
return
184
190
}
185
191
186
192
func (d * sqlDigester ) doNormalizeDigest (sql string ) (normalized string , digest * Digest ) {
187
- d .normalize (sql , false , false , false )
193
+ d .normalize (sql , "ON" , false , false , false )
188
194
normalized = d .buffer .String ()
189
195
d .hasher .Write (d .buffer .Bytes ())
190
196
d .buffer .Reset ()
@@ -194,7 +200,7 @@ func (d *sqlDigester) doNormalizeDigest(sql string) (normalized string, digest *
194
200
}
195
201
196
202
func (d * sqlDigester ) doNormalizeDigestForBinding (sql string ) (normalized string , digest * Digest ) {
197
- d .normalize (sql , false , true , false )
203
+ d .normalize (sql , "ON" , false , true , false )
198
204
normalized = d .buffer .String ()
199
205
d .hasher .Write (d .buffer .Bytes ())
200
206
d .buffer .Reset ()
@@ -212,7 +218,7 @@ const (
212
218
genericSymbolList = - 2
213
219
)
214
220
215
- func (d * sqlDigester ) normalize (sql string , keepHint bool , forBinding bool , forPlanReplayerReload bool ) {
221
+ func (d * sqlDigester ) normalize (sql string , redact string , keepHint bool , forBinding bool , forPlanReplayerReload bool ) {
216
222
d .lexer .reset (sql )
217
223
d .lexer .setKeepHint (keepHint )
218
224
for {
@@ -229,7 +235,7 @@ func (d *sqlDigester) normalize(sql string, keepHint bool, forBinding bool, forP
229
235
continue
230
236
}
231
237
232
- d .reduceLit (& currTok , forBinding )
238
+ d .reduceLit (& currTok , redact , forBinding , forPlanReplayerReload )
233
239
if forPlanReplayerReload {
234
240
// Apply for plan replayer to match specific rules, changing IN (...) to IN (?). This can avoid plan replayer load failures caused by parse errors.
235
241
d .replaceSingleLiteralWithInList (& currTok )
@@ -313,10 +319,33 @@ func (d *sqlDigester) reduceOptimizerHint(tok *token) (reduced bool) {
313
319
return
314
320
}
315
321
316
- func (d * sqlDigester ) reduceLit (currTok * token , forBinding bool ) {
322
+ func (d * sqlDigester ) reduceLit (currTok * token , redact string , forBinding bool , forPlanReplayer bool ) {
317
323
if ! d .isLit (* currTok ) {
318
324
return
319
325
}
326
+
327
+ if redact == "MARKER" && ! forBinding && ! forPlanReplayer {
328
+ switch currTok .lit {
329
+ case "?" , "*" :
330
+ return
331
+ }
332
+ input := currTok .lit
333
+ b := & strings.Builder {}
334
+ b .Grow (len (input ))
335
+ _ , _ = b .WriteRune ('‹' )
336
+ for _ , c := range input {
337
+ if c == '‹' || c == '›' {
338
+ _ , _ = b .WriteRune (c )
339
+ _ , _ = b .WriteRune (c )
340
+ } else {
341
+ _ , _ = b .WriteRune (c )
342
+ }
343
+ }
344
+ _ , _ = b .WriteRune ('›' )
345
+ currTok .lit = b .String ()
346
+ return
347
+ }
348
+
320
349
// count(*) => count(?)
321
350
if currTok .lit == "*" {
322
351
if d .isStarParam () {
0 commit comments