3
0

Fix concurrecy issues with QL query conversion

Internal QL parser's state prevents this from being reusable.
This commit is contained in:
Tomaž Jerman
2022-09-28 13:08:11 +02:00
parent 92c2a0d12e
commit b4ae293fdb
2 changed files with 29 additions and 19 deletions

View File

@@ -66,7 +66,7 @@ func (c *connection) withModel(m *dal.Model, fn func(m *model) error) error {
)
c.mux.RLock()
defer c.mux.RUnlock()
if cached, ok := c.models[cacheKey(m)]; ok {
if cached, ok := c.models[key]; ok {
return fn(cached)
}

View File

@@ -24,16 +24,11 @@ type (
sqlx.ExecerContext
}
queryParser interface {
Parse(string) (exp.Expression, error)
}
model struct {
model *dal.Model
conn queryRunner
queryParser queryParser
dialect drivers.Dialect
dialect drivers.Dialect
table drivers.TableCodec
}
@@ -74,20 +69,35 @@ func validate(m *dal.Model) error {
func Model(m *dal.Model, c queryRunner, d drivers.Dialect) *model {
var (
ms = &model{
model: m,
conn: c,
dialect: d,
queryParser: ql.Converter(),
table: drivers.NewTableCodec(m, d),
model: m,
conn: c,
dialect: d,
table: drivers.NewTableCodec(m, d),
}
)
ms.queryParser = ql.Converter(
return ms
}
// parseQuery parses the query into the goqu expression
//
// The parse query initializes a fresh converter instance because QL converter
// uses some internal state to keep track of symbols and stuff.
//
// When doing parallel requests over the same model, unexpected... stuff happens.
//
// Alternative solution would introduce a mutes on the internal model but that
// is probably the same or worse as this.
//
// @todo benchmark to see if this re-init is a bad idea; I don't think it should be
// since we're just initializing fairly light structs.
func (d *model) parseQuery(q string) (out exp.Expression, err error) {
pp := ql.Converter(
ql.SymHandler(func(node *ql.ASTNode) (exp.Expression, error) {
// @note normalize system idents on the RDBMS level for filters
// offloaded to the database.
sym := dal.NormalizeAttrNames(node.Symbol)
if ms.model.ResourceType == "corteza::compose:module" {
if d.model.ResourceType == "corteza::compose:module" {
// temporary solution
//
// before DAL some fields were aliased (recordID => ID)
@@ -97,7 +107,7 @@ func Model(m *dal.Model, c queryRunner, d drivers.Dialect) *model {
}
}
attr := ms.model.Attributes.FindByIdent(sym)
attr := d.model.Attributes.FindByIdent(sym)
if attr == nil {
return nil, fmt.Errorf("unknown attribute %q used in query expression", node.Symbol)
}
@@ -106,12 +116,12 @@ func Model(m *dal.Model, c queryRunner, d drivers.Dialect) *model {
return nil, fmt.Errorf("attribute %q can not be used in query expression", attr.Ident)
}
return ms.table.AttributeExpression(sym)
return d.table.AttributeExpression(sym)
}),
ql.RefHandler(d.ExprHandler),
ql.RefHandler(d.dialect.ExprHandler),
)
return ms
return pp.Parse(q)
}
func (d *model) Truncate(ctx context.Context) error {
@@ -382,7 +392,7 @@ func (d *model) searchSql(f filter.Filter) *goqu.SelectDataset {
}
if q := strings.TrimSpace(f.Expression()); len(q) > 0 {
if tmp, err = d.queryParser.Parse(q); err != nil {
if tmp, err = d.parseQuery(q); err != nil {
return base.SetError(err)
}