Fix PQL distinct in dax (#2360)

* Fix PQL distinct in dax

When issuing a PQL Distinct() call (or any other call with a "index=" arg),
this commit will attempt to convert the value in the index arg with a
TableKeyer.

* Apply change to call.Children as well

* Add some PQL Distinct (join) test coverage
This commit is contained in:
Travis Turner 2022-12-15 07:59:20 -06:00 committed by GitHub
parent 15d2ee8b07
commit 4e8fe488de
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 67 additions and 4 deletions

View file

@ -2528,7 +2528,21 @@ func (o *orchestrator) translateCall(ctx context.Context, c *pql.Call, tableKeye
// This also applies to all child calls.
if callIndex := c.CallIndex(); callIndex != "" {
index = callIndex
tableKeyer = dax.StringTableKeyer(index)
// TODO(tlt): checking for prefix like this is bad form. Ideally, the
// argument stored in the Call.Args map would be of type TableKey
// (currently they are restricted to type: string). In that case we
// could just pass it through without doing this conversion. (This would
// require changing the logic in Queryer.convertIndex() to set "index"
// to a TableKeyer).
if strings.HasPrefix(index, dax.PrefixTable+dax.TableKeyDelimiter) {
qtid, err := dax.QualifiedTableIDFromKey(index)
if err != nil {
return nil, errors.Wrapf(err, "getting qtid from key: %s", index)
}
tableKeyer = qtid
} else {
tableKeyer = dax.StringTableKeyer(index)
}
}
idx, err := o.schemaIndexInfo(ctx, tableKeyer)
@ -3508,6 +3522,11 @@ func (o *orchestrator) schemaFieldInfo(ctx context.Context, tableKeyer dax.Table
tbl = &v.Table
case *dax.Table:
tbl = v
case dax.QualifiedTableID:
tbl, err = o.schema.TableByID(ctx, v.ID)
if err != nil {
return nil, errors.Wrapf(err, "getting table by id: %s", v.ID)
}
case dax.StringTableKeyer:
tbl, err = o.schema.TableByName(ctx, dax.TableName(v))
if err != nil {
@ -3535,6 +3554,11 @@ func (o *orchestrator) schemaIndexInfo(ctx context.Context, tableKeyer dax.Table
tbl = &v.Table
case *dax.Table:
tbl = v
case dax.QualifiedTableID:
tbl, err = o.schema.TableByID(ctx, v.ID)
if err != nil {
return nil, errors.Wrapf(err, "getting table by id: %s", v.ID)
}
case dax.StringTableKeyer:
tbl, err = o.schema.TableByName(ctx, dax.TableName(v))
if err != nil {

View file

@ -244,6 +244,28 @@ func (q *Queryer) parseAndQueryPQL(ctx context.Context, qual dax.TableQualifier,
return q.QueryPQL(ctx, qual, dax.TableName(table), query)
}
// convertIndex tries to covert any "index" specified in the call.Args map to a
// TableKeyer. Note, since the Call.CallIndex() method currently only looks for
// strings, we can't just set the value to a TableKeyer; we have to set it to
// the equivalent string and then parse it back out later. A TODO would be to
// modify Call.CallIndex() to be TableKeyer aware. I didn't do that along with
// these changes because I'm not sure if we want to introduce dax types into the
// pql package.
func (q *Queryer) convertIndex(ctx context.Context, qual dax.TableQualifier, call *featurebase_pql.Call) {
if index := call.CallIndex(); index != "" {
qtbl, err := q.schemar.TableByName(ctx, qual, dax.TableName(index))
if err != nil {
return
}
call.Args["index"] = string(qtbl.Key())
}
// Apply to children.
for _, child := range call.Children {
q.convertIndex(ctx, qual, child)
}
}
func (q *Queryer) QueryPQL(ctx context.Context, qual dax.TableQualifier, table dax.TableName, pql string) (*featurebase.WireQueryResponse, error) {
// Parse the pql into a pql.Query containing []pql.Call.
qry, err := featurebase_pql.NewParser(strings.NewReader(pql)).Parse()
@ -254,6 +276,9 @@ func (q *Queryer) QueryPQL(ctx context.Context, qual dax.TableQualifier, table d
return nil, errors.Errorf("must have exactly 1 query, but got: %+v", qry.Calls)
}
// Replace any "index" arguments within the PQL with a TableKey.
q.convertIndex(ctx, qual, qry.Calls[0])
qtbl, err := q.schemar.TableByName(ctx, qual, dax.TableName(table))
if err != nil {
return nil, errors.Wrap(err, "converting index to qualified table")
@ -267,10 +292,10 @@ func (q *Queryer) QueryPQL(ctx context.Context, qual dax.TableQualifier, table d
return nil, errors.Errorf("expected single result but got %+v", results.Results)
}
return PQLResultToQueryResult(results.Results[0])
return pqlResultToQueryResult(results.Results[0])
}
func PQLResultToQueryResult(pqlResult interface{}) (*featurebase.WireQueryResponse, error) {
func pqlResultToQueryResult(pqlResult interface{}) (*featurebase.WireQueryResponse, error) {
toTabler, err := server.ToTablerWrapper(pqlResult)
if err != nil {
return nil, errors.Wrap(err, "wrapping as type ToTabler")

View file

@ -28,7 +28,7 @@ var joinTestsOrders = TableTest{
"orders",
srcHdrs(
srcHdr("_id", fldTypeID),
srcHdr("userid", fldTypeID),
srcHdr("userid", fldTypeInt),
srcHdr("price", fldTypeDecimal2),
),
srcRows(
@ -103,4 +103,18 @@ var joinTests = TableTest{
Compare: CompareExactOrdered,
},
},
PQLTests: []PQLTest{
{
name: "distinctjoin",
Table: "users",
PQLs: []string{"Intersect(Distinct(Row(price > 10), index=orders, field=userid))"},
ExpHdrs: hdrs(
hdr("_id", fldTypeID),
),
ExpRows: rows(
row(int64(1)),
row(int64(2)),
),
},
},
}