Allow rdbms model ops to use already parsed filter expressions
This commit is contained in:
@@ -67,7 +67,11 @@ func (def *Datasource) init(ctx context.Context, s iterProvider) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
iter, model, err := s(ctx, def.ModelRef, def.filter)
|
||||
// Do the first init to get the model.
|
||||
// For now, this is a ~free operation, but it should change when we allow things
|
||||
// like nesting reports, etc.
|
||||
// @todo refactor datasource descriptors to avoid this
|
||||
_, model, err := s(ctx, def.ModelRef, def.filter)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -76,7 +80,25 @@ func (def *Datasource) init(ctx context.Context, s iterProvider) (err error) {
|
||||
def.OutAttributes = def.outAttrsFromModel(model)
|
||||
}
|
||||
|
||||
def.auxIter = iter
|
||||
pp := make([]string, 0, len(def.OutAttributes)/2+1)
|
||||
for _, a := range def.OutAttributes {
|
||||
if a.Properties().IsPrimary {
|
||||
pp = append(pp, a.Identifier())
|
||||
}
|
||||
}
|
||||
|
||||
// Assure and attempt to correct the provided sort to conform with the data set and the
|
||||
// paging cursor (if any)
|
||||
def.filter, err = assureSort(def.filter, pp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Get the iterator for actual use
|
||||
def.auxIter, _, err = s(ctx, def.ModelRef, def.filter)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = def.validate()
|
||||
if err != nil {
|
||||
|
||||
@@ -32,6 +32,10 @@ type (
|
||||
|
||||
table drivers.TableCodec
|
||||
}
|
||||
|
||||
parsedFilter interface {
|
||||
ExpressionParsed() *ql.ASTNode
|
||||
}
|
||||
)
|
||||
|
||||
func validate(m *dal.Model) error {
|
||||
@@ -93,37 +97,50 @@ func Model(m *dal.Model, c queryRunner, d drivers.Dialect) *model {
|
||||
// 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 d.model.ResourceType == "corteza::compose:module" {
|
||||
// temporary solution
|
||||
//
|
||||
// before DAL some fields were aliased (recordID => ID)
|
||||
switch sym {
|
||||
case "recordID":
|
||||
sym = "ID"
|
||||
}
|
||||
}
|
||||
|
||||
attr := d.model.Attributes.FindByIdent(sym)
|
||||
if attr == nil {
|
||||
return nil, fmt.Errorf("unknown attribute %q used in query expression", node.Symbol)
|
||||
}
|
||||
|
||||
if !attr.Filterable {
|
||||
return nil, fmt.Errorf("attribute %q can not be used in query expression", attr.Ident)
|
||||
}
|
||||
|
||||
return d.table.AttributeExpression(sym)
|
||||
}),
|
||||
ql.SymHandler(d.qlConverterGenericHandlers()),
|
||||
ql.RefHandler(d.dialect.ExprHandler),
|
||||
)
|
||||
|
||||
return pp.Parse(q)
|
||||
}
|
||||
|
||||
func (d *model) convertQuery(n *ql.ASTNode) (out exp.Expression, err error) {
|
||||
pp := ql.Converter(
|
||||
ql.SymHandler(d.qlConverterGenericHandlers()),
|
||||
ql.RefHandler(d.dialect.ExprHandler),
|
||||
)
|
||||
|
||||
return pp.Convert(n)
|
||||
}
|
||||
|
||||
func (d *model) qlConverterGenericHandlers() func(node *ql.ASTNode) (exp.Expression, error) {
|
||||
return 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 d.model.ResourceType == "corteza::compose:module" {
|
||||
// temporary solution
|
||||
//
|
||||
// before DAL some fields were aliased (recordID => ID)
|
||||
switch sym {
|
||||
case "recordID":
|
||||
sym = "ID"
|
||||
}
|
||||
}
|
||||
|
||||
attr := d.model.Attributes.FindByIdent(sym)
|
||||
if attr == nil {
|
||||
return nil, fmt.Errorf("unknown attribute %q used in query expression", node.Symbol)
|
||||
}
|
||||
|
||||
if !attr.Filterable {
|
||||
return nil, fmt.Errorf("attribute %q can not be used in query expression", attr.Ident)
|
||||
}
|
||||
|
||||
return d.table.AttributeExpression(sym)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *model) Truncate(ctx context.Context) error {
|
||||
sql, args, err := d.truncateSql().ToSQL()
|
||||
if err != nil {
|
||||
@@ -391,6 +408,17 @@ func (d *model) searchSql(f filter.Filter) *goqu.SelectDataset {
|
||||
}
|
||||
}
|
||||
|
||||
if pf, ok := f.(parsedFilter); ok {
|
||||
tmp := pf.ExpressionParsed()
|
||||
if tmp != nil {
|
||||
n, err := d.convertQuery(tmp)
|
||||
if err != nil {
|
||||
return base.SetError(err)
|
||||
}
|
||||
cnd = append(cnd, n)
|
||||
}
|
||||
}
|
||||
|
||||
if q := strings.TrimSpace(f.Expression()); len(q) > 0 {
|
||||
if tmp, err = d.parseQuery(q); err != nil {
|
||||
return base.SetError(err)
|
||||
|
||||
Reference in New Issue
Block a user