-
Notifications
You must be signed in to change notification settings - Fork 1
/
query.go
134 lines (122 loc) · 3.61 KB
/
query.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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package structql
import (
"fmt"
"github.com/viant/sqlparser"
"github.com/viant/sqlparser/query"
node "github.com/viant/structql/node"
sparser "github.com/viant/structql/parser"
"github.com/viant/xunsafe"
"reflect"
"strconv"
"strings"
)
// Query represents a selector
type Query struct {
query string
sel *query.Select
source reflect.Type
destSlice *xunsafe.Slice
Limit int
node *Node
mapper *Mapper
walker *Walker
CompType reflect.Type
Binding *node.Binding
}
// Type returns dest slice type
func (s *Query) Type() reflect.Type {
return s.destSlice.Type
}
// StructType returns dest struct type
func (s *Query) StructType() reflect.Type {
return unwrapStruct(s.destSlice.Type)
}
// Select returns selection result
func (s *Query) Select(source interface{}) (interface{}, error) {
destSlicePtrValue := reflect.New(s.destSlice.Type)
sourceLen := s.walker.Count(source)
destSlicePtrValue.Elem().Set(reflect.MakeSlice(s.destSlice.Type, 0, sourceLen))
destSlicePtr := destSlicePtrValue.Interface()
destPtr := xunsafe.AsPointer(destSlicePtr)
appender := s.destSlice.Appender(destPtr)
if err := s.mapper.Map(s.walker, source, appender); err != nil {
return nil, err
}
return destSlicePtr, nil
}
// First returns the first selection result
func (s *Query) First(source interface{}) (interface{}, error) {
destSlicePtrValue := reflect.New(s.destSlice.Type)
sourceLen := s.walker.Count(source)
destSlicePtrValue.Elem().Set(reflect.MakeSlice(s.destSlice.Type, 0, sourceLen))
destSlicePtr := destSlicePtrValue.Interface()
destPtr := xunsafe.AsPointer(destSlicePtr)
appender := s.destSlice.Appender(destPtr)
if err := s.mapper.Map(s.walker, source, appender); err != nil {
return nil, err
}
if s.destSlice.Len(destPtr) == 0 {
return nil, nil
}
return s.destSlice.ValuePointerAt(destPtr, 0), nil
}
func unwrapStruct(p reflect.Type) reflect.Type {
if p == nil {
return nil
}
switch p.Kind() {
case reflect.Struct:
return p
case reflect.Ptr:
return unwrapStruct(p.Elem())
case reflect.Slice:
return unwrapStruct(p.Elem())
}
return nil
}
// NewQuery returns a selector
func NewQuery(query string, source, dest reflect.Type, values ...interface{}) (*Query, error) {
var err error
if unwrapStruct(source) == nil {
return nil, fmt.Errorf("invalid source type: %s", source.String())
}
ret := &Query{query: query, source: source, Binding: &node.Binding{}}
value := &node.Values{Values: values, Bindings: ret.Binding}
if ret.sel, err = sqlparser.ParseQuery(query); err != nil {
return nil, fmt.Errorf("failed to parse %w, %v", err, query)
}
from := strings.Trim(sqlparser.Stringify(ret.sel.From.X), "`")
sel, err := sparser.ParseSelector(from)
if err != nil {
return nil, fmt.Errorf("invalid from: %w, %v", err, from)
}
if ret.node, err = NewNode(source, sel, value); err != nil {
return nil, err
}
src := unwrapStruct(ret.node.LeafType())
if ret.mapper, err = NewMapper(src, unwrapStruct(dest), ret.sel); err != nil {
return nil, err
}
if limit := ret.sel.Limit; limit != nil {
ret.Limit, _ = strconv.Atoi(limit.Value)
}
ret.walker = NewWalker(ret.node)
ret.CompType = ret.mapper.dest
if dest == nil {
dest = reflect.PtrTo(ret.CompType)
}
if dest.Kind() != reflect.Slice {
dest = reflect.SliceOf(dest)
}
ret.destSlice = xunsafe.NewSlice(dest)
if ret.sel.Qualify != nil {
leaf := ret.node.Leaf()
if leaf.expr != nil {
return nil, fmt.Errorf("[] expr and WHERE clause can not be used for the same node")
}
if leaf.expr, leaf.exprSel, err = compileCriteria("t", ret.sel.Qualify, leaf.ownerType, value); err != nil {
return nil, err
}
}
return ret, nil
}