Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions normalizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,11 @@ func (n *Normalizer) shouldStripIdentifierQuotes(token *Token, lastValueToken *L
if isAliasContext(lastValueToken) && !token.isSimpleIdentifier {
return false
}
// Bracket-quoted identifiers whose content contains whitespace cannot be
// safely unquoted: the stripped form would produce multiple SQL tokens.
if strings.ContainsAny(token.Value, " \t\n\r") {
return false
}
return true
}

Expand Down Expand Up @@ -470,6 +475,12 @@ func (n *Normalizer) appendSpace(token *Token, lastValueToken *LastValueToken, n
return
}

// do not add a space between a dot-suffixed identifier and the following identifier,
// because the lexer splits e.g. t.[Col] into "t." and "[Col]" as separate tokens
if lastValueToken != nil && len(lastValueToken.Value) > 1 && lastValueToken.Value[len(lastValueToken.Value)-1] == '.' && (token.Type == IDENT || token.Type == QUOTED_IDENT) {
return
}

// do not add a space after a colon when followed by an identifier or quoted identifier,
// BUT only if the token before the colon was NOT an identifier (to preserve MySQL labels and Snowflake semi-structured access)
// This handles Oracle bind variables like :param or :"param"
Expand Down
79 changes: 79 additions & 0 deletions normalizer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1041,6 +1041,85 @@ func TestNormalizeDeobfuscatedSQL(t *testing.T) {
WithDBMS(DBMSSQLServer),
},
},
{
// simple bracket-quoted column after dot — no space, brackets stripped, metadata on
input: `SELECT t.[SimpleCol] FROM dbo.SomeTable AS t`,
expected: `SELECT t.SimpleCol FROM dbo.SomeTable AS t`,
statementMetadata: StatementMetadata{
Tables: []string{`dbo.SomeTable`},
Comments: []string{},
Commands: []string{"SELECT"},
Procedures: []string{},
Size: 19,
},
normalizationConfig: &normalizerConfig{
CollectComments: true,
CollectCommands: true,
CollectTables: true,
KeepSQLAlias: true,
},
lexerOptions: []lexerOption{
WithDBMS(DBMSSQLServer),
},
},
{
// same as above with all metadata collection disabled — output must be identical
input: `SELECT t.[SimpleCol] FROM dbo.SomeTable AS t`,
expected: `SELECT t.SimpleCol FROM dbo.SomeTable AS t`,
statementMetadata: StatementMetadata{
Tables: []string{},
Comments: []string{},
Commands: []string{},
Procedures: []string{},
Size: 0,
},
normalizationConfig: &normalizerConfig{
KeepSQLAlias: true,
},
lexerOptions: []lexerOption{
WithDBMS(DBMSSQLServer),
},
},
{
input: `SELECT t.[Column With Spaces] FROM dbo.SomeTable AS t`,
expected: `SELECT t.[Column With Spaces] FROM dbo.SomeTable AS t`,
statementMetadata: StatementMetadata{
Tables: []string{`dbo.SomeTable`},
Comments: []string{},
Commands: []string{"SELECT"},
Procedures: []string{},
Size: 19,
},
normalizationConfig: &normalizerConfig{
CollectComments: true,
CollectCommands: true,
CollectTables: true,
KeepSQLAlias: true,
},
lexerOptions: []lexerOption{
WithDBMS(DBMSSQLServer),
},
},
{
input: `SELECT [First Name], [Last Name] FROM [My Table]`,
expected: `SELECT [First Name], [Last Name] FROM [My Table]`,
statementMetadata: StatementMetadata{
Tables: []string{`My Table`},
Comments: []string{},
Commands: []string{"SELECT"},
Procedures: []string{},
Size: 14,
},
normalizationConfig: &normalizerConfig{
CollectComments: true,
CollectCommands: true,
CollectTables: true,
KeepSQLAlias: true,
},
lexerOptions: []lexerOption{
WithDBMS(DBMSSQLServer),
},
},
{
input: `( @p1 bigint ) SELECT * from dbm_user as prepared_user WHERE id = @p1`,
expected: `SELECT * from dbm_user as prepared_user WHERE id = @p1`,
Expand Down
Loading