-
Notifications
You must be signed in to change notification settings - Fork 3.9k
feat(engine): basic math expressions #19407
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…nto spiridonov-rate-aggregate
|
Thanks, that makes sense if you're trying to prepare for supporting math over two vectors. I suspect computations over two vectors (in a way that's compatible with LogQL/PromQL) is going to be trickier than it seems. I believe, in relational algebra terms, they're expressed as a combination of an inner join and a projection. So, given a query like A physical plan mapping literally to the algebra could be This needs to explicitly be an inner join, since LogQL's metric queries require the sample to exist on both side of the expression (this matches PromQL's behaviour). ExampleFor example, the two inputs of OuterJoin
and
is joined into
and is projected to
I think it's probably okay if we wanted to have a node which combines the work of projection and inner joins, though I do think it's possible to represent these operations using projections, which we will have a separate node for, and separating them out may be easier to understand in the plans. All that said, I do wonder if math on two vectors is going to require a lot more thought. Would we be able to simplify the logic here if we descoped that from our consideration? |
ashwanthgoli
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm
chaudum
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you also add some test cases to pkg/engine/internal/planner/planner_test.go
| builder = builder.Cast(unwrapIdentifier, unwrapOperation) | ||
| op := convertBinaryArithmeticOp(e.Op) | ||
| if op == types.BinaryOpInvalid { | ||
| return nil, errUnimplemented |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An invalid op type should return an error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have an inconsistent pattern for function like convertBinaryArithmeticOp. Some panic, and some return Ivalid enum value. Here I return Invalid that indicates that this op is not implemented. I don't want to panic here, and I don't want to make the function more complex by returning error just for that.
| // both left and right expressions have obj scans, replace with join | ||
| if leftInput != nil && rightInput != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
leftInput and rightInput are nil only in case they are literals, arent't they?
So checking for !- nil does not mean the node is a scan node? This assumption seems error prone.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
leftInput and rightInput are nil if they are literals, bin ops or unary ops.
| Column: "value_right", | ||
| Type: types.ColumnTypeGenerated, | ||
| } | ||
| join := &Join{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the Join used for? I cannot see a usage in the executor. Should the executor return an error in case sees a join in the plan?
I assume this is the plumbing you mentioned in the PR description
Two inputs will be implemented as inner joins. For now I only added some plumbing to the planner for adding Join node before math expressions with two inputs. Joins themselves are not implemented yet, and the logical planner will throw a NotImplementer error if such a query is passed into it, but the plumbing code is there for better picture of overall design.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this is that plumbing from the description. Join is not implemented in the executor yet. But this code is not actually reachable because the logical planner will throw nonImplemented error when there are math expressions with two inputs.
| return nil, nil, nil, err | ||
| } | ||
|
|
||
| columnRef := newColumnExpr(types.ColumnNameGeneratedValue, types.ColumnTypeGenerated) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could there be a problem with colliding column names if there are nested math expressions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There might be something I am not aware of. But there are couple unit tests that test nested math expressions and they behave as expected.
What this PR does / why we need it:
In order to add support for
rate()aggregation function, which will be implemented ascount_over_time/$interval, I had to add support for math expressions. Math expressions are implemented as Projections, where Expand expression is evaluated from other columns and added to the result. Binary expressions with only single input are supported for now. Things likesum_over_time/count_over_timewill be implemented later. Two inputs will be implemented as inner joins. For now I only added some plumbing to the planner for adding Join node before math expressions with two inputs. Joins themselves are not implemented yet, and the logical planner will throw a NotImplementer error if such a query is passed into it, but the plumbing code is there for better picture of overall design.valuecolumn as the result.Planner.process()to return a singleNodefor simplicity. I do not see a case where it can return several children at once, however I see examples of "complex" children (MakeTableorProjection) where a subtree can be returned instead of a single node.Minor:
Which issue(s) this PR fixes:
Fixes #
Special notes for your reviewer:
Diff for
pkg/engine/internal/planner/logical/planner.gois ugly here, it is better to view the new file as the whole to understand it better. I basically split one large function into 3 pieces without changing much in that logic.Checklist
CONTRIBUTING.mdguide (required)featPRs are unlikely to be accepted unless a case can be made for the feature actually being a bug fix to existing behavior.docs/sources/setup/upgrade/_index.mddeprecated-config.yamlanddeleted-config.yamlfiles respectively in thetools/deprecated-config-checkerdirectory. Example PR