Files
upstream-sqlc/internal/engine/dolphin/fingerprint.go
upstream-mirror 0f4df23290
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
snapshot of https://github.com/sqlc-dev/sqlc.git (main, 2026-09-16; blobs > 131072B omitted)
2026-09-16 02:17:34 +05:00

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
}