#!/usr/bin/env tclsh

set testdir [file dirname $argv0]
source $testdir/tester.tcl

do_execsql_test window-partition-by {
    SELECT
        first_name,
        sum(age) OVER (PARTITION BY zipcode)
    FROM users u
    LIMIT 10;
} {Misty|96
Jessica|56
Christopher|62
Steven|33
Greg|44
Thomas|43
Jeremy|90
Kevin|22
Shannon|60
Steven|7}

do_execsql_test window-partition-by-duplicate-column {
    SELECT
        first_name,
        sum(age) OVER (PARTITION BY zipcode, zipcode)
    FROM users u
    LIMIT 10;
} {Misty|96
Jessica|56
Christopher|62
Steven|33
Greg|44
Thomas|43
Jeremy|90
Kevin|22
Shannon|60
Steven|7}

do_execsql_test window-partition-by-multiple-columns {
    SELECT
        first_name,
        max(age) OVER (PARTITION BY first_name, state),
        min(age) OVER (PARTITION BY first_name, state),
        last_name
    FROM users u
    LIMIT 10;
} {Aaron|43|43|Woods
Aaron|58|58|Baker
Aaron|69|69|Alvarado
Aaron|28|28|Larson
Aaron|75|69|Harris
Aaron|75|69|Peterson
Aaron|56|56|Sims
Aaron|55|55|Fry
Aaron|83|22|Walker
Aaron|83|22|George}

do_execsql_test window-order-by {
    SELECT
        name,
        max(price) OVER (ORDER BY id) AS rolling_max
    FROM products
    ORDER BY rolling_max, name;
} {hat|79.0
accessories|82.0
boots|82.0
cap|82.0
coat|82.0
jeans|82.0
shirt|82.0
shorts|82.0
sneakers|82.0
sweater|82.0
sweatshirt|82.0}

do_execsql_test window-order-by-duplicate-column {
    SELECT
        name,
        max(price) OVER (ORDER BY id, id) AS rolling_max
    FROM products
    ORDER BY rolling_max, name;
} {hat|79.0
accessories|82.0
boots|82.0
cap|82.0
coat|82.0
jeans|82.0
shirt|82.0
shorts|82.0
sneakers|82.0
sweater|82.0
sweatshirt|82.0}

do_execsql_test window-order-by-multiple-columns {
    SELECT
        name,
        max(price) OVER (ORDER BY name, id) AS rolling_max
    FROM products
    ORDER BY rolling_max, name;
} {accessories|81.0
boots|81.0
cap|82.0
coat|82.0
hat|82.0
jeans|82.0
shirt|82.0
shorts|82.0
sneakers|82.0
sweater|82.0
sweatshirt|82.0}

do_execsql_test window-partition-by-and-order-by {
    SELECT
        u.first_name,
        count(*) OVER (PARTITION BY u.city ORDER BY u.first_name)
    FROM users u
    LIMIT 10;
} {Elizabeth|1
Matthew|1
Charles|1
Heidi|1
Sarah|1
Tammy|2
Angelica|1
Rebecca|2
Linda|1
John|1}

do_execsql_test window-without-partition-by-or-order-by {
  SELECT count() OVER () FROM products;
} {11
11
11
11
11
11
11
11
11
11
11}

do_execsql_test window-in-subquery {
    SELECT
        first_name,
        zmax
    FROM (
        SELECT
            u.first_name,
            max(age) OVER (PARTITION BY zipcode) AS zmax
        FROM users u
    )
    WHERE zmax > 20
    LIMIT 5;
} {Misty|96
Jessica|56
Christopher|62
Steven|33
Greg|44}

do_execsql_test window-nested-in-expression {
    SELECT
        first_name,
        (age + max(age) OVER (PARTITION BY zipcode))
    FROM users
    ORDER BY zipcode, first_name
    LIMIT 5;
} {Misty|192
Jessica|112
Christopher|124
Steven|66
Greg|88}

do_execsql_test window-multiple-functions {
    SELECT
        first_name,
        last_name,
        max(age) OVER (PARTITION BY zipcode),
        max(age) OVER (PARTITION BY city),
        min(age) OVER (PARTITION BY state, city ORDER BY last_name),
        count(*) OVER (PARTITION BY state, city ORDER BY last_name),
        sum(age) OVER (ORDER BY city),
        min(age) OVER (ORDER BY city),
        age
    FROM users
    ORDER BY first_name
    LIMIT 10;
} {Aaron|Walter|22|22|22|1|246652|1|22
Aaron|Owens|47|47|47|1|414251|1|47
Aaron|Stephens|34|97|34|1|256145|1|34
Aaron|Powers|71|71|71|1|286366|1|71
Aaron|Kirby|33|33|33|1|257216|1|33
Aaron|Larson|28|88|28|1|417846|1|28
Aaron|Fry|55|55|55|1|227116|1|55
Aaron|Martinez|67|67|67|1|26572|1|67
Aaron|Perez|81|81|81|1|18041|1|81
Aaron|Ray|32|96|32|1|388962|1|32}

do_execsql_test window-with-aggregate {
    SELECT
        max(age),
        count(*) OVER ()
    FROM users;
} {100|1}

do_execsql_test window-with-group-by {
    SELECT
        first_name,
        max(age) OVER (PARTITION BY last_name)
    FROM users
    GROUP BY first_name
    ORDER BY zipcode
    LIMIT 10;
} {Misty|96
Carrie|37
Nicholas|89
Brittany|22
Claire|89
Trevor|25
Diamond|18
Alvin|46
Vanessa|57
Gilbert|50}

do_execsql_test window-group-by-with-aggregate {
    SELECT
        first_name,
        count(*),
        max(age) OVER (PARTITION BY first_name)
    FROM users
    GROUP BY first_name, age
    ORDER BY first_name
    LIMIT 10;
} {Aaron|1|98
Aaron|1|98
Aaron|2|98
Aaron|2|98
Aaron|1|98
Aaron|1|98
Aaron|1|98
Aaron|1|98
Aaron|1|98
Aaron|1|98}

do_execsql_test window-group-by-having {
    SELECT
        first_name,
        count(*),
        max(age) OVER (PARTITION BY first_name)
    FROM users
    GROUP BY first_name, age
    HAVING count(*) > 1
    ORDER BY first_name
    LIMIT 10;
} {Aaron|2|98
Aaron|2|98
Aaron|2|98
Aaron|2|98
Aaron|2|98
Aaron|3|98
Aaron|2|98
Abigail|2|57
Adam|3|57
Adam|2|57}

do_execsql_test window-duplicate-name {
    SELECT
        name,
        sum(price) OVER win1,
        max(price) OVER win1
    FROM products
    WINDOW win1 AS (PARTITION BY id),
           win1 AS (PARTITION BY price)
    ORDER BY name;
} {accessories|81.0|81.0
boots|1.0|1.0
cap|164.0|82.0
coat|33.0|33.0
hat|79.0|79.0
jeans|78.0|78.0
shirt|18.0|18.0
shorts|70.0|70.0
sneakers|164.0|82.0
sweater|25.0|25.0
sweatshirt|74.0|74.0}

do_execsql_test window-name-with-space {
    SELECT
        name,
        sum(price) OVER "foo bar"
    FROM products
    WINDOW "foo bar" AS (PARTITION BY id)
    ORDER BY name;
} {accessories|81.0
boots|1.0
cap|82.0
coat|33.0
hat|79.0
jeans|78.0
shirt|18.0
shorts|70.0
sneakers|82.0
sweater|25.0
sweatshirt|74.0}

do_execsql_test_error_content window-nonexistent-name {
    SELECT
        sum(price) OVER nonexistent
    FROM products;
} {no such window: nonexistent}

do_execsql_test_error_content window-function-in-having {
    SELECT
        name
    FROM products
    GROUP BY name
    HAVING sum(price) OVER (PARTITION BY price) > 40;
} {misuse of window function}

do_execsql_test_error_content window-function-in-group-by {
    SELECT
        name
    FROM products
    GROUP BY sum(price) OVER (PARTITION BY price);
} {misuse of window function}

do_execsql_test_error_content window-nested-function {
    SELECT
        sum(sum(price) OVER (PARTITION BY name)) OVER (PARTITION BY price)
    FROM products;
} {misuse of window function}

do_execsql_test_error_content window-scalar-function {
    SELECT
        lower(name) OVER (PARTITION BY price)
    FROM products;
} {may not be used as a window function}

do_execsql_test_error_content window-nonexistent-function {
    SELECT
        non_existent_func(name) OVER (PARTITION BY price)
    FROM products;
} {no such function}

do_execsql_test_error_content window-scalar-function-star {
    SELECT
        lower(*) OVER (PARTITION BY price)
    FROM products;
} {may not be used as a window function}

do_execsql_test_error_content window-nonexistent-function-star {
    SELECT
        non_existent_func(*) OVER (PARTITION BY price)
    FROM products;
} {no such function}

do_execsql_test window-aggregate-in-partition-by {
    SELECT
        max(price) OVER (PARTITION BY count(*))
    FROM products;
} {79.0}

do_execsql_test window-aggregate-in-order-by {
    SELECT
        max(price) OVER (ORDER BY count(*))
    FROM products;
} {79.0}

do_execsql_test window-aggregate-as-argument {
    SELECT
        max(sum(price)) OVER (ORDER BY name)
    FROM products;
} {623.0}

do_execsql_test window-aggregate-with-group-by-as-argument {
    SELECT
        max(sum(price)) OVER (ORDER BY name)
    FROM products
    GROUP BY price;
} {81.0
81.0
164.0
164.0
164.0
164.0
164.0
164.0
164.0
164.0}

do_execsql_test_error_content window-function-as-aggregate-argument {
    SELECT
        sum(max(price) OVER (ORDER BY name))
    FROM products
    GROUP BY price;
} {misuse of window function}

do_execsql_test_error_content window-function-nested-in-partition-by {
    SELECT
        max(price) OVER (PARTITION BY count(*) OVER())
    FROM products;
} {misuse of window function}

do_execsql_test_error_content window-function-nested-in-order-by {
    SELECT
        max(price) OVER (ORDER BY count(*) OVER())
    FROM products;
} {misuse of window function}

do_execsql_test window-rowid-in-result {
    SELECT
        rowid,
        max(price) OVER (PARTITION BY price)
    FROM products
    ORDER BY name;
} {11|81.0
9|1.0
2|82.0
10|33.0
1|79.0
7|78.0
3|18.0
6|70.0
8|82.0
4|25.0
5|74.0}

do_execsql_test window-rowid-in-order-by {
    SELECT
        name,
        max(price) OVER (PARTITION BY price)
    FROM products
    ORDER BY rowid;
} {hat|79.0
cap|82.0
shirt|18.0
sweater|25.0
sweatshirt|74.0
shorts|70.0
jeans|78.0
sneakers|82.0
boots|1.0
coat|33.0
accessories|81.0}

do_execsql_test window-rowid-as-argument {
    SELECT
        name,
        max(rowid) OVER (PARTITION BY price)
    FROM products
    ORDER BY name;
} {accessories|11
boots|9
cap|8
coat|10
hat|1
jeans|7
shirt|3
shorts|6
sneakers|8
sweater|4
sweatshirt|5}

do_execsql_test window-distinct {
    SELECT
        distinct max(price) OVER (PARTITION BY price)
    FROM products;
} {1.0
18.0
25.0
33.0
70.0
74.0
78.0
79.0
81.0
82.0}

do_execsql_test_error_content window-distinct-as-argument {
    SELECT
        first_name,
        sum(distinct age) OVER (PARTITION BY first_name)
    FROM users;
} {DISTINCT is not supported for window functions}

do_execsql_test_error_content window-distinct-as-argument-2 {
    SELECT
        first_name,
        count(distinct) OVER (PARTITION BY first_name)
    FROM users;
} {DISTINCT is not supported for window functions}

do_execsql_test window-limit-offset {
    SELECT
        first_name,
        age,
        max(age) OVER (PARTITION BY zipcode ORDER BY id)
    FROM users
    LIMIT 3
    OFFSET 2;
} {Christopher|62|62
Steven|33|33
Greg|44|44}

do_execsql_test window-order-by-limit-offset {
    SELECT
        first_name,
        age,
        max(age) OVER (PARTITION BY zipcode ORDER BY id)
    FROM users
    ORDER BY zipcode, id
    LIMIT 3
    OFFSET 2;
} {Christopher|62|62
Steven|33|33
Greg|44|44}

do_execsql_test_on_specific_db {:memory:} window-collate-partition-by {
    CREATE TABLE fruits(name collate nocase);
    INSERT INTO fruits(name) VALUES ('Apple'), ('banana'), ('apple');

    SELECT
        name,
        count(*) OVER (PARTITION BY name)
    FROM fruits;
} {Apple|2
apple|2
banana|1}

do_execsql_test_on_specific_db {:memory:} window-collate-order-by {
    CREATE TABLE fruits(name collate nocase);
    INSERT INTO fruits(name) VALUES ('Apple'),('banana'), ('apple');

    SELECT
        name,
        count(*) OVER (ORDER BY name)
    FROM fruits;
} {Apple|2
apple|2
banana|3}

do_execsql_test window-in-cte {
    WITH w AS (
      SELECT
          u.*,
          max(age) OVER (PARTITION BY zipcode) AS zmax
      FROM users u
    )
    SELECT
        first_name,
        last_name,
        zmax
    FROM w
    ORDER BY first_name, last_name
    LIMIT 5;
} {Aaron|Alvarado|69
Aaron|Baker|91
Aaron|Brown|98
Aaron|Bush|42
Aaron|Clark|26}

do_execsql_test window-named-in-cte {
    WITH w AS (
      SELECT
          u.*,
          max(age) OVER win1 AS zmax
      FROM users u
      WINDOW win1 AS (PARTITION BY zipcode)
    )
    SELECT
        first_name,
        last_name,
        zmax
    FROM w
    ORDER BY first_name, last_name
    LIMIT 5;
} {Aaron|Alvarado|69
Aaron|Baker|91
Aaron|Brown|98
Aaron|Bush|42
Aaron|Clark|26}

do_execsql_test window-empty-partition {
    SELECT
        sum(age) OVER (PARTITION by zipcode)
    FROM users
    WHERE 0;
} {}

do_execsql_test window-single-row-partition {
    SELECT
      first_name,
      sum(age) OVER (PARTITION BY zipcode)
    FROM users
    WHERE zipcode = '00523';
} {Misty|96}

do_execsql_test window-column-in-order-by {
    SELECT
        first_name,
        age,
        sum(age) OVER (PARTITION BY zipcode) AS total_age
    FROM users
    ORDER BY total_age DESC, first_name
    LIMIT 10;
} {Bradley|94|261
Chelsey|77|261
Sara|90|261
Bruce|85|241
Daniel|69|241
Jesse|75|241
Sean|12|241
Benjamin|36|218
John|97|218
Kelly|85|218}

do_execsql_test window-function-in-order-by {
    SELECT
        first_name,
        age
    FROM users
    ORDER BY sum(age) OVER (PARTITION BY zipcode) DESC, first_name
    LIMIT 10;
} {Bradley|94
Chelsey|77
Sara|90
Bruce|85
Daniel|69
Jesse|75
Sean|12
Benjamin|36
John|97
Kelly|85}

do_execsql_test window-complex-argument {
    SELECT
        sum(price between 1 and 50) OVER ()
    FROM products;
} {4
4
4
4
4
4
4
4
4
4
4}
