Files
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

299 lines
7.5 KiB
Go

package googlesql
import (
"log"
"strings"
zjast "github.com/sqlc-dev/zetajones/ast"
"github.com/sqlc-dev/sqlc/internal/debug"
"github.com/sqlc-dev/sqlc/internal/sql/ast"
)
func todo(n zjast.Node) *ast.TODO {
if debug.Active {
log.Printf("googlesql.convert: Unknown node type %T\n", n)
}
return &ast.TODO{}
}
// identifier passes table and column identifiers through unchanged. GoogleSQL
// keywords are case-insensitive, but table names (on BigQuery) are
// case-sensitive, so unlike the MySQL and ClickHouse engines we do not
// lowercase identifiers here. Function and type names, which are
// case-insensitive, are lowercased at their call sites instead.
func identifier(id string) string {
return id
}
func NewIdentifier(t string) *ast.String {
return &ast.String{Str: identifier(t)}
}
// pathParts returns the identifier names of a dotted path expression.
func pathParts(n *zjast.PathExpression) []string {
if n == nil {
return nil
}
parts := make([]string, 0, len(n.Names))
for _, id := range n.Names {
if id == nil {
continue
}
parts = append(parts, id.Name)
}
return parts
}
func parseTableName(n *zjast.PathExpression) *ast.TableName {
name := &ast.TableName{}
parts := pathParts(n)
switch len(parts) {
case 0:
case 1:
name.Name = identifier(parts[0])
case 2:
name.Schema = identifier(parts[0])
name.Name = identifier(parts[1])
default:
name.Catalog = identifier(parts[0])
name.Schema = identifier(parts[len(parts)-2])
name.Name = identifier(parts[len(parts)-1])
}
return name
}
func parseRangeVar(n *zjast.PathExpression) *ast.RangeVar {
tn := parseTableName(n)
rv := &ast.RangeVar{}
if n != nil {
rv.Location = n.Pos()
}
if tn.Catalog != "" {
catalog := tn.Catalog
rv.Catalogname = &catalog
}
if tn.Schema != "" {
schema := tn.Schema
rv.Schemaname = &schema
}
if tn.Name != "" {
relname := tn.Name
rv.Relname = &relname
}
return rv
}
func convertAlias(a *zjast.Alias) *ast.Alias {
if a == nil || a.Identifier == nil {
return nil
}
name := identifier(a.Identifier.Name)
return &ast.Alias{Aliasname: &name}
}
// whereExpr unwraps a WHERE clause node to its expression. The DELETE and
// UPDATE statement nodes hold either a *WhereClause or a bare expression.
func whereExpr(node zjast.Node) zjast.Node {
if wc, ok := node.(*zjast.WhereClause); ok {
return wc.Expr
}
return node
}
// stringLiteralValue concatenates and unquotes the components of a string
// literal.
func stringLiteralValue(n *zjast.StringLiteral) string {
var sb strings.Builder
for _, comp := range n.Components {
if comp == nil {
continue
}
sb.WriteString(unquote(comp.Image))
}
return sb.String()
}
// unquote strips the optional r/b prefix and the surrounding quotes from a
// string literal component and decodes escape sequences for non-raw strings.
// It is best-effort: malformed input is returned unchanged.
func unquote(image string) string {
raw := false
i := 0
prefix:
for i < len(image) {
switch image[i] {
case 'r', 'R':
raw = true
i++
case 'b', 'B':
i++
default:
break prefix
}
}
s := image[i:]
for _, q := range []string{`'''`, `"""`, `'`, `"`} {
if len(s) >= 2*len(q) && strings.HasPrefix(s, q) && strings.HasSuffix(s, q) {
s = s[len(q) : len(s)-len(q)]
if raw || !strings.ContainsRune(s, '\\') {
return s
}
return decodeEscapes(s)
}
}
return image
}
func decodeEscapes(s string) string {
var sb strings.Builder
for i := 0; i < len(s); i++ {
ch := s[i]
if ch != '\\' || i+1 >= len(s) {
sb.WriteByte(ch)
continue
}
i++
switch s[i] {
case 'n':
sb.WriteByte('\n')
case 't':
sb.WriteByte('\t')
case 'r':
sb.WriteByte('\r')
case 'a':
sb.WriteByte('\a')
case 'b':
sb.WriteByte('\b')
case 'f':
sb.WriteByte('\f')
case 'v':
sb.WriteByte('\v')
case '\\', '\'', '"', '?', '`':
sb.WriteByte(s[i])
default:
// Unknown escape (\x, \u, ...): keep it verbatim.
sb.WriteByte('\\')
sb.WriteByte(s[i])
}
}
return sb.String()
}
// typeName renders a zetajones type node (used by CAST) as a lowercased type
// name. Nested types degrade to a readable representation.
// typeName spells a type the way the analysis core reads one: a family
// applied to its arguments, with an array's element, a struct's labelled
// fields, a range's subtype and a map's key and value nested, and a
// parameter list's integers and MAX written as they were.
func typeName(node zjast.Node) string {
switch t := node.(type) {
case *zjast.SimpleType:
return strings.ToLower(strings.Join(pathParts(t.Name), ".")) + typeParameters(t.TypeParameters)
case *zjast.ArrayType:
return "array(" + typeName(t.ElementType) + ")"
case *zjast.StructType:
fields := make([]string, 0, len(t.Fields))
for _, f := range t.Fields {
field := typeName(f.Type)
if f.Name != nil {
field = identifier(f.Name.Name) + ": " + field
}
fields = append(fields, field)
}
return "struct(" + strings.Join(fields, ", ") + ")"
case *zjast.RangeType:
return "range(" + typeName(t.ElementType) + ")"
case *zjast.MapType:
return "map(" + typeName(t.KeyType) + ", " + typeName(t.ValueType) + ")"
default:
return ""
}
}
// typeParameters spells a parameter list, STRING(10) or BYTES(MAX), as the
// arguments of a call.
func typeParameters(params *zjast.TypeParameterList) string {
if params == nil || len(params.Parameters) == 0 {
return ""
}
parts := make([]string, 0, len(params.Parameters))
for _, p := range params.Parameters {
switch v := p.(type) {
case *zjast.IntLiteral:
parts = append(parts, v.Image)
case *zjast.MaxLiteral:
parts = append(parts, "max")
default:
continue
}
}
if len(parts) == 0 {
return ""
}
return "(" + strings.Join(parts, ", ") + ")"
}
// columnSchemaTypeName renders a CREATE TABLE column schema as a lowercased
// type name. Nested types degrade to a readable representation.
// columnSchemaTypeName spells a column's type the way typeName spells a
// type.
func columnSchemaTypeName(node zjast.Node) string {
switch t := node.(type) {
case *zjast.SimpleColumnSchema:
return strings.ToLower(strings.Join(pathParts(t.Type), ".")) + typeParameters(t.TypeParameters)
case *zjast.ArrayColumnSchema:
return "array(" + columnSchemaTypeName(t.ElementSchema) + ")"
case *zjast.StructColumnSchema:
fields := make([]string, 0, len(t.Fields))
for _, f := range t.Fields {
field := columnSchemaTypeName(f.Schema)
if f.Name != nil {
field = identifier(f.Name.Name) + ": " + field
}
fields = append(fields, field)
}
return "struct(" + strings.Join(fields, ", ") + ")"
default:
return ""
}
}
// columnAttributes returns the attribute list (NOT NULL, PRIMARY KEY, ...) of
// a column schema, if any.
func columnAttributes(node zjast.Node) *zjast.ColumnAttributeList {
switch t := node.(type) {
case *zjast.SimpleColumnSchema:
return t.Attributes
case *zjast.ArrayColumnSchema:
return t.Attributes
case *zjast.StructColumnSchema:
return t.Attributes
default:
return nil
}
}
// columnRefFields flattens an identifier, path expression, or dotted
// identifier chain into ColumnRef fields.
func columnRefFields(node zjast.Node) ([]ast.Node, bool) {
switch n := node.(type) {
case *zjast.Identifier:
return []ast.Node{NewIdentifier(n.Name)}, true
case *zjast.PathExpression:
var fields []ast.Node
for _, part := range pathParts(n) {
fields = append(fields, NewIdentifier(part))
}
return fields, len(fields) > 0
case *zjast.DotIdentifier:
base, ok := columnRefFields(n.Expr)
if !ok || n.Name == nil {
return nil, false
}
return append(base, NewIdentifier(n.Name.Name)), true
default:
return nil, false
}
}