Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Dec 23, 2025

Thanks for assigning this issue to me. I'm starting to work on it and will keep this PR's description up to date as I form a plan and make progress.

Original prompt

This section details on the original issue you should resolve

<issue_title>Window Offset Functions (LEAD/LAG/FIRST_VALUE/LAST_VALUE) Not Implemented</issue_title>
<issue_description>## Summary
Window offset functions LEAD(), LAG(), FIRST_VALUE(), and LAST_VALUE() are not implemented in AlaSQL. These are essential SQL:2003 standard window functions that allow accessing rows relative to the current row within a partition.

Current Behavior

// Attempting to use LEAD():
alasql('SELECT category, amount, LEAD(amount) OVER (PARTITION BY category ORDER BY amount) AS next_amt FROM data');

// Error: alasql.fn.LEAD is not a function

The same error occurs for LAG(), FIRST_VALUE(), and LAST_VALUE().

Expected Behavior

LEAD() - Access Next Row Value

var data = [
  {category: 'A', amount: 10},
  {category: 'A', amount: 20},
  {category: 'A', amount: 30},
  {category: 'B', amount: 40}
];

alasql('SELECT category, amount, LEAD(amount) OVER (PARTITION BY category ORDER BY amount) AS next_amt FROM ?', [data]);

// Expected output:
[
  {category: 'A', amount: 10, next_amt: 20},   // Next in partition
  {category: 'A', amount: 20, next_amt: 30},   // Next in partition
  {category: 'A', amount: 30, next_amt: null}, // Last in partition
  {category: 'B', amount: 40, next_amt: null}  // Last in partition
]

LAG() - Access Previous Row Value

alasql('SELECT category, amount, LAG(amount) OVER (PARTITION BY category ORDER BY amount) AS prev_amt FROM ?', [data]);

// Expected output:
[
  {category: 'A', amount: 10, prev_amt: null}, // First in partition
  {category: 'A', amount: 20, prev_amt: 10},   // Previous in partition
  {category: 'A', amount: 30, prev_amt: 20},   // Previous in partition
  {category: 'B', amount: 40, prev_amt: null}  // First in partition
]

FIRST_VALUE() - Access First Row in Window

alasql('SELECT category, amount, FIRST_VALUE(amount) OVER (PARTITION BY category ORDER BY amount) AS first_amt FROM ?', [data]);

// Expected output:
[
  {category: 'A', amount: 10, first_amt: 10}, // First in partition
  {category: 'A', amount: 20, first_amt: 10}, // First in partition
  {category: 'A', amount: 30, first_amt: 10}, // First in partition
  {category: 'B', amount: 40, first_amt: 40}  // First in partition
]

LAST_VALUE() - Access Last Row in Window

alasql('SELECT category, amount, LAST_VALUE(amount) OVER (PARTITION BY category ORDER BY amount) AS last_amt FROM ?', [data]);

// Expected output (with proper frame specification):
[
  {category: 'A', amount: 10, last_amt: 30}, // Last in partition
  {category: 'A', amount: 20, last_amt: 30}, // Last in partition
  {category: 'A', amount: 30, last_amt: 30}, // Last in partition
  {category: 'B', amount: 40, last_amt: 40}  // Last in partition
]

Use Cases

1. Calculate Period-over-Period Change

// Calculate month-over-month sales change
alasql(`
  SELECT 
    month, 
    sales,
    sales - LAG(sales) OVER (ORDER BY month) AS mom_change,
    (sales - LAG(sales) OVER (ORDER BY month)) / LAG(sales) OVER (ORDER BY month) * 100 AS mom_pct
  FROM monthly_sales
`);

2. Find Next Event Time

// Find time until next customer visit
alasql(`
  SELECT 
    customer_id,
    visit_date,
    LEAD(visit_date) OVER (PARTITION BY customer_id ORDER BY visit_date) AS next_visit,
    DATEDIFF(day, visit_date, LEAD(visit_date) OVER (PARTITION BY customer_id ORDER BY visit_date)) AS days_until_next
  FROM customer_visits
`);

3. Compare with Baseline

// Compare each employee salary to department min/max
alasql(`
  SELECT 
    dept,
    emp_name,
    salary,
    FIRST_VALUE(salary) OVER (PARTITION BY dept ORDER BY salary) AS dept_min,
    LAST_VALUE(salary) OVER (PARTITION BY dept ORDER BY salary ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS dept_max
  FROM employees
`);

Function Signatures

LEAD(expression [, offset [, default]]) OVER (...)

  • expression: Column or expression to retrieve from the lead row
  • offset: Number of rows forward (default: 1)
  • default: Value to return when lead row doesn't exist (default: NULL)

LAG(expression [, offset [, default]]) OVER (...)

  • expression: Column or expression to retrieve from the lag row
  • offset: Number of rows backward (default: 1)
  • default: Value to return when lag row doesn't exist (default: NULL)

FIRST_VALUE(expression) OVER (...)

  • expression: Column or expression to retrieve from first row in window frame

LAST_VALUE(expression) OVER (...)

  • expression: Column or expression to retrieve from last row in window frame
  • Note: Requires proper frame specification (ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)

Implementation Requirements

1. Parser Updates

Add function tokens to src/alasqlparser.jison:

LEAD | LAG | FIRST_VALUE | LAST_VALUE

2. Function R...


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Window Offset Functions (LEAD/LAG/FIRST_VALUE/LAST_VALUE) Not Implemented

2 participants