-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathviewmap.go
118 lines (109 loc) · 2.42 KB
/
viewmap.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
package genddl
import (
"bufio"
"bytes"
"fmt"
"go/ast"
"go/types"
"io"
"log"
"strconv"
"strings"
)
const (
viewSelectStatementFuncName = "_selectStatement"
)
type ViewMap struct {
Name string
Columns []string
SelectStatement string
}
type newViewMapInput struct {
name string
st *ast.StructType
funcs []*ast.FuncDecl
ti *types.Info
}
func NewViewMap(input newViewMapInput) (*ViewMap, error) {
st := input.st
funcs := input.funcs
name := input.name
columns := make([]string, 0, len(st.Fields.List))
for _, field := range st.Fields.List {
t := input.ti.TypeOf(field.Type)
tagText := field.Tag.Value
cs, err := columnsByFields(t, input.ti, tagText, "")
if err != nil {
return nil, fmt.Errorf("failed to get columns by fields: %w", err)
}
for _, c := range cs {
columns = append(columns, c.name)
}
}
selectStatement := retrieveSelectStatementByFuncs(funcs)
if selectStatement == "" {
return nil, nil
}
return &ViewMap{
Name: name,
Columns: columns,
SelectStatement: selectStatement,
}, nil
}
func retrieveSelectStatementByFuncs(funcs []*ast.FuncDecl) string {
for _, funcDecl := range funcs {
if funcDecl.Name.Name != viewSelectStatementFuncName {
continue
}
var rt *ast.ReturnStmt
ast.Inspect(funcDecl.Body, func(n ast.Node) bool {
switch t := n.(type) {
case *ast.ReturnStmt:
rt = t
return false
default:
return true
}
})
if rt == nil {
continue
}
for _, lit := range rt.Results {
bl, ok := lit.(*ast.BasicLit)
if !ok {
continue
}
uq, err := strconv.Unquote(bl.Value)
if err != nil {
log.Printf("error by unquote: %s", err)
return ""
}
uq = strings.Trim(uq, ";\n\t ")
scanner := bufio.NewScanner(strings.NewReader(uq))
b := &bytes.Buffer{}
for scanner.Scan() {
b.WriteString(" ")
b.WriteString(scanner.Text())
b.WriteString("\n")
}
return strings.TrimSuffix(b.String(), "\n")
}
}
return ""
}
func (vm *ViewMap) fields(dialect Dialect) string {
sb := &strings.Builder{}
for i, c := range vm.Columns {
if i != 0 {
sb.WriteString(", ")
}
sb.WriteString(dialect.QuoteField(c))
}
return sb.String()
}
func (vm *ViewMap) WriteDDL(w io.Writer, dialect Dialect) error {
io.WriteString(w, "CREATE VIEW "+dialect.QuoteField(vm.Name)+"\n")
io.WriteString(w, " ("+vm.fields(dialect)+") AS\n")
io.WriteString(w, vm.SelectStatement+";\n\n")
return nil
}