Repository navigation
add materialized subqueries #7640
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -74,6 +74,27 @@ Then, for each input value `3` and `4`, the result is emitted, e.g., | |
| {that:4,count:3} | ||
| ``` | ||
|
|
||
| A subquery may also be made independent explicitly by placing the keyword | ||
| `materialized` before its parentheses, as in | ||
| ``` | ||
| materialized ( <query> ) | ||
| ``` | ||
| A materialized subquery is evaluated just once and receives a single `null` | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "is evaluated just once and returns the initial result on each subsequent invocation" ... "a materialized subquery has no access to the expression context in which it is evaluated... the body of the subquery has no parent operator and like all such queries, its input is a single null value" ^^^ I'm not proposing this exact wording... just this direction |
||
| value as its input, so it need not begin with a source. For example, | ||
| the subquery `materialized (values now())` is evaluated once even though | ||
| it does not begin with a `from` operator. | ||
|
|
||
| ```mdtest-spq | ||
| # spq | ||
| values 1,2,3 | ||
| | values materialized (values now()) | ||
| | count(distinct this) | ||
| # input | ||
| # expected output | ||
| 1 | ||
| ``` | ||
|
|
||
| ## Correlated Subqueries | ||
|
|
||
| When a subquery appears within a [SQL operator](../sql/intro.md), | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,9 +18,10 @@ import ( | |
| ) | ||
|
|
||
| type Main struct { | ||
| Types []byte `json:"types"` | ||
| Funcs []*FuncDef `json:"funcs"` | ||
| Body Seq `json:"body"` | ||
| Types []byte `json:"types"` | ||
| Funcs []*FuncDef `json:"funcs"` | ||
| Materialized []*MaterializedDef `json:"materialized"` | ||
| Body Seq `json:"body"` | ||
| } | ||
|
|
||
| type FuncDef struct { | ||
|
|
@@ -31,6 +32,12 @@ type FuncDef struct { | |
| Expr Expr `json:"expr"` | ||
| } | ||
|
|
||
| type MaterializedDef struct { | ||
| Kind string `json:"kind" unpack:""` | ||
| Name string `json:"name"` | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why names? They don't appear in the user facing model. Like functions, it seems like they should be a slice that is ref'd by slot in the slice.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm maybe the names relate to the querydecl name? They can't live in a flat table because they are lexically scoped. |
||
| Body Seq `json:"body"` | ||
| } | ||
|
|
||
| type Op interface { | ||
| opNode() | ||
| } | ||
|
|
@@ -124,6 +131,10 @@ type ( | |
| Message string `json:"message"` | ||
| Meta string `json:"meta"` | ||
| } | ||
| MaterializedRefOp struct { | ||
| Kind string `json:"kind" unpack:""` | ||
| Name string `json:"name"` | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Name -> Slot ? |
||
| } | ||
| MergeOp struct { | ||
| Kind string `json:"kind" unpack:""` | ||
| Exprs []SortExpr `json:"exprs"` | ||
|
|
@@ -206,35 +217,36 @@ type ( | |
| } | ||
| ) | ||
|
|
||
| func (*AggregateOp) opNode() {} | ||
| func (*CombineOp) opNode() {} | ||
| func (*CountOp) opNode() {} | ||
| func (*DebugOp) opNode() {} | ||
| func (*DistinctOp) opNode() {} | ||
| func (*DropOp) opNode() {} | ||
| func (*FilterOp) opNode() {} | ||
| func (*ForkOp) opNode() {} | ||
| func (*FuseOp) opNode() {} | ||
| func (*HashJoinOp) opNode() {} | ||
| func (*HeadOp) opNode() {} | ||
| func (*InferOp) opNode() {} | ||
| func (*JoinOp) opNode() {} | ||
| func (*LoadOp) opNode() {} | ||
| func (*MergeOp) opNode() {} | ||
| func (*OutputOp) opNode() {} | ||
| func (*PassOp) opNode() {} | ||
| func (*PutOp) opNode() {} | ||
| func (*RenameOp) opNode() {} | ||
| func (*ScatterOp) opNode() {} | ||
| func (*SkipOp) opNode() {} | ||
| func (*SlicerOp) opNode() {} | ||
| func (*SortOp) opNode() {} | ||
| func (*SwitchOp) opNode() {} | ||
| func (*TailOp) opNode() {} | ||
| func (*TopOp) opNode() {} | ||
| func (*UniqOp) opNode() {} | ||
| func (*UnnestOp) opNode() {} | ||
| func (*ValuesOp) opNode() {} | ||
| func (*AggregateOp) opNode() {} | ||
| func (*CombineOp) opNode() {} | ||
| func (*CountOp) opNode() {} | ||
| func (*DebugOp) opNode() {} | ||
| func (*DistinctOp) opNode() {} | ||
| func (*DropOp) opNode() {} | ||
| func (*FilterOp) opNode() {} | ||
| func (*ForkOp) opNode() {} | ||
| func (*FuseOp) opNode() {} | ||
| func (*HashJoinOp) opNode() {} | ||
| func (*HeadOp) opNode() {} | ||
| func (*InferOp) opNode() {} | ||
| func (*JoinOp) opNode() {} | ||
| func (*LoadOp) opNode() {} | ||
| func (*MaterializedRefOp) opNode() {} | ||
| func (*MergeOp) opNode() {} | ||
| func (*OutputOp) opNode() {} | ||
| func (*PassOp) opNode() {} | ||
| func (*PutOp) opNode() {} | ||
| func (*RenameOp) opNode() {} | ||
| func (*ScatterOp) opNode() {} | ||
| func (*SkipOp) opNode() {} | ||
| func (*SlicerOp) opNode() {} | ||
| func (*SortOp) opNode() {} | ||
| func (*SwitchOp) opNode() {} | ||
| func (*TailOp) opNode() {} | ||
| func (*TopOp) opNode() {} | ||
| func (*UniqOp) opNode() {} | ||
| func (*UnnestOp) opNode() {} | ||
| func (*ValuesOp) opNode() {} | ||
|
|
||
| // Scanner sources also implement Op and all have suffix "Scan". | ||
| type ( | ||
|
|
||
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.
I would leave out the "cache" concept as that is mechanism leaking into the semantic definition here
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.
Good point. And I just noticed that the next paragraph already includes an explanation that avoids mechanism in the context of automatic materialization. I'm going to integrate
materializebetter with that since the current addition feels disjointed.