Skip to content
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

[Window] Support LOGICAL_OR and LOGICAL_AND #42

Merged
merged 3 commits into from
Apr 7, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions internal/function_bind.go
Original file line number Diff line number Diff line change
Expand Up @@ -3707,6 +3707,18 @@ func bindWindowCountIf() func() *WindowAggregator {
}
}

func bindWindowLogicalAnd() func() *WindowAggregator {
return func() *WindowAggregator {
return newSingleItemWindowAggregator(&WINDOW_LOGICAL_AND{})
}
}

func bindWindowLogicalOr() func() *WindowAggregator {
return func() *WindowAggregator {
return newSingleItemWindowAggregator(&WINDOW_LOGICAL_OR{})
}
}

func bindWindowMax() func() *WindowAggregator {
return func() *WindowAggregator {
return newSingleItemWindowAggregator(&WINDOW_MAX{})
Expand Down
2 changes: 2 additions & 0 deletions internal/function_register.go
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,8 @@ var windowFuncs = []*WindowFuncInfo{
{Name: "count", BindFunc: bindWindowCount},
{Name: "count_star", BindFunc: bindWindowCountStar},
{Name: "countif", BindFunc: bindWindowCountIf},
{Name: "logical_and", BindFunc: bindWindowLogicalAnd},
{Name: "logical_or", BindFunc: bindWindowLogicalOr},
{Name: "max", BindFunc: bindWindowMax},
{Name: "min", BindFunc: bindWindowMin},
{Name: "string_agg", BindFunc: bindWindowStringAgg},
Expand Down
46 changes: 46 additions & 0 deletions internal/function_window.go
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,52 @@ func (f *WINDOW_LAG) Done(agg *WindowFuncAggregatedStatus) (Value, error) {
return agg.Values[len(agg.Values)-f.offset-1], nil
}

type WINDOW_LOGICAL_AND struct {
}

func (f *WINDOW_LOGICAL_AND) Done(agg *WindowFuncAggregatedStatus) (Value, error) {
values, err := agg.RelevantValues()
if err != nil {
return nil, err
}

for _, cond := range values {
b, err := cond.ToBool()
if err != nil {
return nil, err
}

if !b {
return BoolValue(false), nil
}
}

return BoolValue(true), nil
}

type WINDOW_LOGICAL_OR struct {
}

func (f *WINDOW_LOGICAL_OR) Done(agg *WindowFuncAggregatedStatus) (Value, error) {
values, err := agg.RelevantValues()
if err != nil {
return nil, err
}

for _, cond := range values {
b, err := cond.ToBool()
if err != nil {
return nil, err
}

if b {
return BoolValue(true), nil
}
}

return BoolValue(true), nil

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this not be BoolValue(false)? Like if there are no trues we return False?

Might make sense to include a test case like this:

SELECT LOGICAL_OR(a) OVER (ORDER BY b), a, b
FROM (
  SELECT CAST(a AS BOOL) AS a, b FROM UNNEST([
    STRUCT(False AS a, 1 AS b), 
    STRUCT(False AS a, 2 AS b), 
    STRUCT(True AS a, 3 AS b), 
    STRUCT(False AS a, 4 AS b)
  ])
);

and also one that tests what happens when there are no rows:

SELECT LOGICAL_OR(a) OVER (ORDER BY a)
FROM (
  SELECT CAST(a AS BOOL) AS a FROM UNNEST([]) a
);

}

type WINDOW_PERCENTILE_CONT struct {
percentile Value
}
Expand Down
12 changes: 12 additions & 0 deletions query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -832,6 +832,18 @@ SELECT LOGICAL_AND(x) AS logical_or FROM toks`,
SELECT LOGICAL_OR(x) AS logical_or FROM toks`,
expectedRows: [][]interface{}{{false}},
},
{
name: "logical_and with window",
query: `WITH toks AS ( SELECT true AS x UNION ALL SELECT false UNION ALL SELECT true)
SELECT LOGICAL_AND(x) OVER (ORDER BY x) FROM toks`,
expectedRows: [][]interface{}{{false}, {false}, {false}},
},
{
name: "logical_or with window",
query: `WITH toks AS ( SELECT true AS x UNION ALL SELECT false UNION ALL SELECT true)
SELECT LOGICAL_OR(x) OVER (ORDER BY x) FROM toks`,
expectedRows: [][]interface{}{{true}, {true}, {true}},
},
{
name: "max from int group",
query: `SELECT MAX(x) AS max FROM UNNEST([8, 37, 4, 55]) AS x`,
Expand Down
Loading