-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathjoin.go
More file actions
140 lines (122 loc) Β· 3.61 KB
/
Copy pathjoin.go
File metadata and controls
140 lines (122 loc) Β· 3.61 KB
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
135
136
137
138
139
140
package oro
// Source is a structured table, subquery, or raw SQL source.
type Source interface {
sourceAST() SourceAST
}
// TableSource adapts a table name to Source.
type TableSource string
func (source TableSource) sourceAST() SourceAST {
return SourceAST{Table: string(source)}
}
// QuerySource adapts a query builder to a subquery source.
type QuerySource struct {
query any
alias string
}
// Query wraps a query builder as a subquery source.
func Query(query any) QuerySource {
return QuerySource{query: query}
}
// As aliases a query source.
func (source QuerySource) As(alias string) QuerySource {
source.alias = alias
return source
}
func (source QuerySource) sourceAST() SourceAST {
return queryastPendingSource(source.alias, source.query)
}
// Join builds JOIN aliases and ON conditions.
type Join struct {
ast JoinAST
}
// As aliases the joined source.
func (join *Join) As(alias string) *Join {
join.ast.Alias = alias
return join
}
// OnColumn adds an AND column comparison to the join.
func (join *Join) OnColumn(left string, args ...string) *Join {
join.ast.Conditions = append(join.ast.Conditions, buildJoinColumnCondition("and", left, args...))
return join
}
// OrOnColumn adds an OR column comparison to the join.
func (join *Join) OrOnColumn(left string, args ...string) *Join {
join.ast.Conditions = append(join.ast.Conditions, buildJoinColumnCondition("or", left, args...))
return join
}
// Where adds an AND value comparison to the join.
func (join *Join) Where(field string, args ...any) *Join {
join.ast.Conditions = append(join.ast.Conditions, buildJoinValueCondition("and", field, args...))
return join
}
// OrWhere adds an OR value comparison to the join.
func (join *Join) OrWhere(field string, args ...any) *Join {
join.ast.Conditions = append(join.ast.Conditions, buildJoinValueCondition("or", field, args...))
return join
}
// WhereGroup adds a grouped AND condition to the join.
func (join *Join) WhereGroup(fn func(q *Join)) *Join {
if fn == nil {
return join
}
group := &Join{}
fn(group)
if len(group.ast.Conditions) == 0 {
return join
}
join.ast.Conditions = append(join.ast.Conditions, JoinCondition{
Bool: "and",
Group: group.ast.Conditions,
})
return join
}
func buildJoinColumnCondition(boolOp string, left string, args ...string) JoinCondition {
condition := JoinCondition{Bool: boolOp, Left: left, Op: "=", Column: true}
if len(args) == 1 {
condition.Right = args[0]
}
if len(args) >= 2 {
if !IsSafeColumnOperator(args[0]) {
condition.Err = &Error{Op: "join", Kind: ErrInvalidArgument, Field: left}
return condition
}
condition.Op = args[0]
condition.Right = args[1]
}
return condition
}
func buildJoinValueCondition(boolOp string, field string, args ...any) JoinCondition {
condition := JoinCondition{Bool: boolOp, Left: field, Op: "="}
if len(args) == 1 {
condition.Value = args[0]
}
if len(args) >= 2 {
op, _ := args[0].(string)
if !IsSafeConditionOperator(op) {
condition.Err = &Error{Op: "join", Kind: ErrInvalidArgument, Field: field}
return condition
}
condition.Op = op
condition.Value = args[1]
}
return condition
}
func buildJoin(joinType JoinType, source any, fn func(j *Join)) JoinAST {
join := &Join{ast: JoinAST{Type: joinType}}
switch typedSource := source.(type) {
case string:
join.ast.Table = typedSource
case Source:
join.ast.Source = typedSource.sourceAST()
default:
join.ast.Err = &Error{Op: "join", Kind: ErrInvalidArgument}
}
if fn != nil {
fn(join)
}
return join.ast
}
// As aliases a field or expression in SELECT lists.
func As(field string, alias string) FieldExpr {
return FieldExpr{Name: field, Alias: alias}
}