Skip to content
Open
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
2 changes: 2 additions & 0 deletions dialects/tests/expressions_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@ supported_expressions:
- CURRENT_DATE
- CURRENT_TIMESTAMP
- CURRENT_TIMEZONE
- expression: WINDOW_FUNCTION
supports_offset_expr: false
28 changes: 22 additions & 6 deletions proto/substrait/algebra.proto
Original file line number Diff line number Diff line change
Expand Up @@ -1402,17 +1402,33 @@ message Expression {
// Defines that the bound extends this far back from the current record.
message Preceding {
// A strictly positive integer specifying the number of records that
// the window extends back from the current record. Required. Use
// CurrentRow for offset zero and Following for negative offsets.
int64 offset = 1;
// the window extends back from the current record. Required.
// Use CurrentRow for offset zero and Following for negative offsets.
oneof offset_mode {

@yongchul yongchul Jun 17, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@vbarua here is a requested SQL example for the scenario. adapted from SQL server documentation.
https://learn.microsoft.com/en-us/sql/t-sql/functions/lag-transact-sql?view=sql-server-ver17#a-compare-values-between-years

USE AdventureWorks2022;  
GO  
SELECT BusinessEntityID, YEAR(QuotaDate) AS SalesYear, SalesQuota AS CurrentQuota,   
       LAG(SalesQuota, LookBack,0) OVER (ORDER BY YEAR(QuotaDate)) AS PreviousQuota  
FROM Sales.SalesPersonQuotaHistory  
WHERE BusinessEntityID = 275 AND YEAR(QuotaDate) IN ('2005','2006');

The modification I made is LookBack in the LAG(), means you differentiate the offset per business entity id (let's say per rank of business entity id, for the senior rank look into further history). You can change LookBack to arbitrary scalar expression except another analytical function.

LookBack creates a new windowing spec including expression.

int64 offset = 1;

// Expression evaluated into a strictly positive integer specifying
// the offset from the current record. Evaluating to null, zero, or
// a negative integer should result in an error.
// Recommended type for offset is int64.
Expression offset_expr = 2;
}
}

// Defines that the bound extends this far ahead of the current record.
message Following {
// A strictly positive integer specifying the number of records that
// the window extends ahead of the current record. Required. Use
// CurrentRow for offset zero and Preceding for negative offsets.
int64 offset = 1;
// the window extends ahead of the current record. Required.
// Use CurrentRow for offset zero and Preceding for negative offsets.
oneof offset_mode {
int64 offset = 1;

// Expression evaluated into a strictly positive integer specifying
// the offset from the current record. Evaluating to null, zero, or
// a negative integer should result in an error.
// Recommended type for offset is int64.
Expression offset_expr = 2;
}
Comment thread
yongchul marked this conversation as resolved.
Outdated
}

// Defines that the bound extends to or from the current record.
Expand Down
6 changes: 4 additions & 2 deletions site/docs/expressions/window_functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ When binding a window function, the binding must include the following additiona
| Property | Description | Required |
| ----------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
| Partition | A list of partitioning expressions. | False, defaults to a single partition for the entire dataset |
| Lower Bound | Bound Following(int64), Bound Trailing(int64) or CurrentRow. | False, defaults to start of partition |
| Upper Bound | Bound Following(int64), Bound Trailing(int64) or CurrentRow. | False, defaults to end of partition |
| Lower Bound | Preceding, Following, CurrentRow, or Unbounded. | False, defaults to start of partition |
| Upper Bound | Preceding, Following, CurrentRow, or Unbounded. | False, defaults to end of partition |

`Preceding` and `Following` define offsets relative to the current record. Exactly one offset mode must be provided: a literal integer (`offset`) or an expression that evaluates to a strictly positive integer (`offset_expr`). `offset_expr` should use type `int64`; evaluating to null, zero, or a negative integer should result in an error. Use `CurrentRow` for offset zero, and use the opposite bound direction for negative offsets.

## Aggregate Functions as Window Functions

Expand Down
7 changes: 7 additions & 0 deletions text/dialect_schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1100,6 +1100,13 @@ definitions:
metadata:
type: object
description: Arbitrary data created by the dialect author.
supports_offset_expr:
type: boolean
default: false
description: >
Whether this dialect supports expression-based offsets for window
function bounds. If false, only literal offsets are supported for
Preceding and Following bounds. Defaults to false.
required: [expression]
if_then_expression:
type: object
Expand Down
Loading