Some checks failed
kotlin / test (push) Has been skipped
python / test (push) Has been skipped
typescript / test (push) Has been skipped
go / build darwin/amd64 (push) Has been cancelled
go / build linux/amd64 (push) Has been cancelled
go / build windows/amd64 (push) Has been cancelled
go / build darwin/arm64 (push) Has been cancelled
go / build linux/arm64 (push) Has been cancelled
go / build windows/arm64 (push) Has been cancelled
go / test (push) Has been cancelled
go / vuln_check (push) Has been cancelled
56 lines
1.9 KiB
Go
56 lines
1.9 KiB
Go
package dolphin
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
pcast "github.com/sqlc-dev/marino/ast"
|
|
"github.com/sqlc-dev/marino/format"
|
|
"github.com/sqlc-dev/marino/parser"
|
|
)
|
|
|
|
// deparen unwraps parenthesized expressions so a redundant pair of
|
|
// parentheses added or dropped by formatting does not change the
|
|
// fingerprint. Everything else about the tree survives untouched.
|
|
type deparen struct{}
|
|
|
|
func (v deparen) Enter(n pcast.Node) (pcast.Node, bool) { return n, false }
|
|
func (v deparen) Leave(n pcast.Node) (pcast.Node, bool) {
|
|
if p, ok := n.(*pcast.ParenthesesExpr); ok {
|
|
return p.Expr, true
|
|
}
|
|
return n, true
|
|
}
|
|
|
|
// Fingerprint reduces a statement to a canonical form that survives changes
|
|
// in whitespace, keyword case, quoting style and redundant parentheses —
|
|
// and nothing else. Identifier case is preserved: MySQL table names are
|
|
// case-sensitive on most servers, so a formatting pass that changes one is
|
|
// a semantic change and must not fingerprint equal. fmt uses this as its
|
|
// proof that a formatted statement still means what the author wrote; a
|
|
// statement whose fingerprint it cannot match is left exactly as written.
|
|
func (p *Parser) Fingerprint(sql string) (string, error) {
|
|
// A fresh parser: p.pingcap carries per-parse comment state that
|
|
// ParseFile is still using.
|
|
stmts, _, err := parser.New().Parse(sql, "", "")
|
|
if err != nil {
|
|
return "", normalizeErr(err)
|
|
}
|
|
flags := format.RestoreStringSingleQuotes |
|
|
format.RestoreKeyWordUppercase |
|
|
format.RestoreNameBackQuotes
|
|
parts := make([]string, 0, len(stmts))
|
|
for _, s := range stmts {
|
|
n, ok := s.Accept(deparen{})
|
|
if !ok {
|
|
return "", fmt.Errorf("fingerprint: rewrite failed")
|
|
}
|
|
var sb strings.Builder
|
|
if err := n.Restore(format.NewRestoreCtx(flags, &sb)); err != nil {
|
|
return "", fmt.Errorf("fingerprint: %w", err)
|
|
}
|
|
parts = append(parts, sb.String())
|
|
}
|
|
return strings.Join(parts, "; "), nil
|
|
}
|