Skip to content

Commit

Permalink
wip: param bind env
Browse files Browse the repository at this point in the history
  • Loading branch information
stergiotis committed Jan 5, 2025
1 parent 9704921 commit af5e7a8
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 18 deletions.
49 changes: 49 additions & 0 deletions public/db/clickhouse/dsl/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,55 @@ import (
"iter"
)

type ParamBindEnv struct {
bind map[string]*chparser.SettingExprList
}

func NewParamBindEnv() *ParamBindEnv {
return &ParamBindEnv{
bind: make(map[string]*chparser.SettingExprList, 32),
}
}
func (inst *ParamBindEnv) Has(name string) (has bool) {
_, has = inst.bind[name]
return
}

var ErrParamAlreadyBound = eh.Errorf("parameter is already bound to a value")

func (inst *ParamBindEnv) IsEmpty() bool {
return len(inst.bind) == 0
}
func (inst *ParamBindEnv) AddDistinct(p *chparser.SettingExprList) (err error) {
if p == nil {
return
}
name := p.Name.Name
if inst.Has(name) {
err = eb.Build().Str("param", name).Errorf("unable to add: %w", ErrParamAlreadyBound)
return
}
inst.bind[name] = p
return
}
func (inst *ParamBindEnv) Set(p *chparser.SettingExprList) {
if p == nil {
return
}
name := p.Name.Name
inst.bind[name] = p
return
}
func (inst *ParamBindEnv) IterSql() iter.Seq2[string, string] {
return func(yield func(string, string) bool) {
for _, p := range inst.bind {
if !yield(p.Name.String(), p.Expr.String()) {
return
}
}
}
}

type ParamSlotSet struct {
typesLu map[string][]*chparser.QueryParam
paramOccurrences int
Expand Down
63 changes: 45 additions & 18 deletions public/db/clickhouse/dsl/parseddqlquery.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ type ParsedDqlQuery struct {
inputSql string
ast *chparser.SelectQuery

paramSet *ParamSlotSet
paramSetErr error
noParams bool
paramBindEnv *ParamBindEnv

paramSlotSet *ParamSlotSet
paramSlotSetErr error
noParams bool
}

func (inst *ParsedDqlQuery) String() string {
Expand All @@ -26,34 +28,44 @@ func (inst *ParsedDqlQuery) String() string {
func (inst *ParsedDqlQuery) GetAst() *chparser.SelectQuery {
return inst.ast
}
func (inst *ParsedDqlQuery) GetParamBindEnv() (paramBindEnv *ParamBindEnv) {
if inst.paramBindEnv.IsEmpty() {
return nil
}
return inst.paramBindEnv
}
func (inst *ParsedDqlQuery) GetParamSlotSet() (paramSet *ParamSlotSet, err error) {
if inst.noParams {
return
}
if inst.paramSet == nil && inst.paramSetErr == nil {
if inst.paramSlotSet == nil && inst.paramSlotSetErr == nil {
ps := NewParamSlotsSet()
d := newParamSlotsDiscoverer()
err = d.discover(inst.ast, ps)
if err != nil {
err = eh.Errorf("error while discovering paramset: %w", err)
inst.paramSetErr = err
inst.paramSlotSetErr = err
return
}
if ps.IsEmpty() {
inst.noParams = true
ps = nil
}
inst.paramSet = ps
inst.paramSlotSet = ps
}
paramSet = inst.paramSet
err = inst.paramSetErr
paramSet = inst.paramSlotSet
err = inst.paramSlotSetErr
return
}

func NewParsedDqlQuery(sql string) (inst *ParsedDqlQuery, err error) {
inst = &ParsedDqlQuery{
inputSql: sql,
ast: nil,
inputSql: sql,
ast: nil,
paramBindEnv: NewParamBindEnv(),
paramSlotSet: nil,
paramSlotSetErr: nil,
noParams: false,
}
err = inst.parse()
if err != nil {
Expand All @@ -62,22 +74,33 @@ func NewParsedDqlQuery(sql string) (inst *ParsedDqlQuery, err error) {
}
return
}
func removeParamSettingsFromExprs(exprs []chparser.Expr) (exprsOut []chparser.Expr) {
func (inst *ParsedDqlQuery) removeParamSettingsFromExprs(exprs []chparser.Expr) (exprsOut []chparser.Expr, err error) {
const paramPrefixName = "param_" // Note: param names are case-sensitive
bindEnv := inst.paramBindEnv
for _, expr := range exprs {
switch exprt := expr.(type) {
case *chparser.SetStmt:
items := slices.DeleteFunc(exprt.Settings.Items, func(list *chparser.SettingExprList) bool {
for _, list := range exprt.Settings.Items {
name := list.Name.Name
if strings.HasPrefix(list.Name.Name, "param_") {
log.Info().Str("name", name).Msg("removing set param value expression")
return true
if strings.HasPrefix(name, paramPrefixName) {
if bindEnv != nil {
err = bindEnv.AddDistinct(list)
if err != nil {
return
}
} else {
log.Info().Str("name", name).Msg("removing set param value expression")
}
}
return false
}
exprt.Settings.Items = slices.DeleteFunc(exprt.Settings.Items, func(list *chparser.SettingExprList) bool {
name := list.Name.Name
return strings.HasPrefix(name, paramPrefixName)
})
exprt.Settings.Items = items
break
}
}

exprsOut = slices.DeleteFunc(exprs, func(expr chparser.Expr) bool {
switch exprt := expr.(type) {
case *chparser.SetStmt:
Expand All @@ -96,7 +119,11 @@ func (inst *ParsedDqlQuery) parse() (err error) {
return
}

exprs = removeParamSettingsFromExprs(exprs)
exprs, err = inst.removeParamSettingsFromExprs(exprs)
if err != nil {
err = eh.Errorf("unable to remove param settings from expressions: %w", err)
return
}
if len(exprs) != 1 {
err = eb.Build().Int("nExprs", len(exprs)).Errorf("sql must contain exactly on expression")
return
Expand Down

0 comments on commit af5e7a8

Please sign in to comment.