Skip to content

Commit af5e7a8

Browse files
committed
wip: param bind env
1 parent 9704921 commit af5e7a8

File tree

2 files changed

+94
-18
lines changed

2 files changed

+94
-18
lines changed

public/db/clickhouse/dsl/params.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,55 @@ import (
99
"iter"
1010
)
1111

12+
type ParamBindEnv struct {
13+
bind map[string]*chparser.SettingExprList
14+
}
15+
16+
func NewParamBindEnv() *ParamBindEnv {
17+
return &ParamBindEnv{
18+
bind: make(map[string]*chparser.SettingExprList, 32),
19+
}
20+
}
21+
func (inst *ParamBindEnv) Has(name string) (has bool) {
22+
_, has = inst.bind[name]
23+
return
24+
}
25+
26+
var ErrParamAlreadyBound = eh.Errorf("parameter is already bound to a value")
27+
28+
func (inst *ParamBindEnv) IsEmpty() bool {
29+
return len(inst.bind) == 0
30+
}
31+
func (inst *ParamBindEnv) AddDistinct(p *chparser.SettingExprList) (err error) {
32+
if p == nil {
33+
return
34+
}
35+
name := p.Name.Name
36+
if inst.Has(name) {
37+
err = eb.Build().Str("param", name).Errorf("unable to add: %w", ErrParamAlreadyBound)
38+
return
39+
}
40+
inst.bind[name] = p
41+
return
42+
}
43+
func (inst *ParamBindEnv) Set(p *chparser.SettingExprList) {
44+
if p == nil {
45+
return
46+
}
47+
name := p.Name.Name
48+
inst.bind[name] = p
49+
return
50+
}
51+
func (inst *ParamBindEnv) IterSql() iter.Seq2[string, string] {
52+
return func(yield func(string, string) bool) {
53+
for _, p := range inst.bind {
54+
if !yield(p.Name.String(), p.Expr.String()) {
55+
return
56+
}
57+
}
58+
}
59+
}
60+
1261
type ParamSlotSet struct {
1362
typesLu map[string][]*chparser.QueryParam
1463
paramOccurrences int

public/db/clickhouse/dsl/parseddqlquery.go

Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@ type ParsedDqlQuery struct {
1515
inputSql string
1616
ast *chparser.SelectQuery
1717

18-
paramSet *ParamSlotSet
19-
paramSetErr error
20-
noParams bool
18+
paramBindEnv *ParamBindEnv
19+
20+
paramSlotSet *ParamSlotSet
21+
paramSlotSetErr error
22+
noParams bool
2123
}
2224

2325
func (inst *ParsedDqlQuery) String() string {
@@ -26,34 +28,44 @@ func (inst *ParsedDqlQuery) String() string {
2628
func (inst *ParsedDqlQuery) GetAst() *chparser.SelectQuery {
2729
return inst.ast
2830
}
31+
func (inst *ParsedDqlQuery) GetParamBindEnv() (paramBindEnv *ParamBindEnv) {
32+
if inst.paramBindEnv.IsEmpty() {
33+
return nil
34+
}
35+
return inst.paramBindEnv
36+
}
2937
func (inst *ParsedDqlQuery) GetParamSlotSet() (paramSet *ParamSlotSet, err error) {
3038
if inst.noParams {
3139
return
3240
}
33-
if inst.paramSet == nil && inst.paramSetErr == nil {
41+
if inst.paramSlotSet == nil && inst.paramSlotSetErr == nil {
3442
ps := NewParamSlotsSet()
3543
d := newParamSlotsDiscoverer()
3644
err = d.discover(inst.ast, ps)
3745
if err != nil {
3846
err = eh.Errorf("error while discovering paramset: %w", err)
39-
inst.paramSetErr = err
47+
inst.paramSlotSetErr = err
4048
return
4149
}
4250
if ps.IsEmpty() {
4351
inst.noParams = true
4452
ps = nil
4553
}
46-
inst.paramSet = ps
54+
inst.paramSlotSet = ps
4755
}
48-
paramSet = inst.paramSet
49-
err = inst.paramSetErr
56+
paramSet = inst.paramSlotSet
57+
err = inst.paramSlotSetErr
5058
return
5159
}
5260

5361
func NewParsedDqlQuery(sql string) (inst *ParsedDqlQuery, err error) {
5462
inst = &ParsedDqlQuery{
55-
inputSql: sql,
56-
ast: nil,
63+
inputSql: sql,
64+
ast: nil,
65+
paramBindEnv: NewParamBindEnv(),
66+
paramSlotSet: nil,
67+
paramSlotSetErr: nil,
68+
noParams: false,
5769
}
5870
err = inst.parse()
5971
if err != nil {
@@ -62,22 +74,33 @@ func NewParsedDqlQuery(sql string) (inst *ParsedDqlQuery, err error) {
6274
}
6375
return
6476
}
65-
func removeParamSettingsFromExprs(exprs []chparser.Expr) (exprsOut []chparser.Expr) {
77+
func (inst *ParsedDqlQuery) removeParamSettingsFromExprs(exprs []chparser.Expr) (exprsOut []chparser.Expr, err error) {
78+
const paramPrefixName = "param_" // Note: param names are case-sensitive
79+
bindEnv := inst.paramBindEnv
6680
for _, expr := range exprs {
6781
switch exprt := expr.(type) {
6882
case *chparser.SetStmt:
69-
items := slices.DeleteFunc(exprt.Settings.Items, func(list *chparser.SettingExprList) bool {
83+
for _, list := range exprt.Settings.Items {
7084
name := list.Name.Name
71-
if strings.HasPrefix(list.Name.Name, "param_") {
72-
log.Info().Str("name", name).Msg("removing set param value expression")
73-
return true
85+
if strings.HasPrefix(name, paramPrefixName) {
86+
if bindEnv != nil {
87+
err = bindEnv.AddDistinct(list)
88+
if err != nil {
89+
return
90+
}
91+
} else {
92+
log.Info().Str("name", name).Msg("removing set param value expression")
93+
}
7494
}
75-
return false
95+
}
96+
exprt.Settings.Items = slices.DeleteFunc(exprt.Settings.Items, func(list *chparser.SettingExprList) bool {
97+
name := list.Name.Name
98+
return strings.HasPrefix(name, paramPrefixName)
7699
})
77-
exprt.Settings.Items = items
78100
break
79101
}
80102
}
103+
81104
exprsOut = slices.DeleteFunc(exprs, func(expr chparser.Expr) bool {
82105
switch exprt := expr.(type) {
83106
case *chparser.SetStmt:
@@ -96,7 +119,11 @@ func (inst *ParsedDqlQuery) parse() (err error) {
96119
return
97120
}
98121

99-
exprs = removeParamSettingsFromExprs(exprs)
122+
exprs, err = inst.removeParamSettingsFromExprs(exprs)
123+
if err != nil {
124+
err = eh.Errorf("unable to remove param settings from expressions: %w", err)
125+
return
126+
}
100127
if len(exprs) != 1 {
101128
err = eb.Build().Int("nExprs", len(exprs)).Errorf("sql must contain exactly on expression")
102129
return

0 commit comments

Comments
 (0)