#!/usr/bin/env tclsh

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

do_execsql_test select-const-1 {
  SELECT 1
} {1}

do_execsql_test select-const-2 {
  SELECT 2
} {2}

do_execsql_test select-const-3 {
  SELECT 0xDEAF
} {57007}

do_execsql_test select-const-4 {
  SELECT -0xA
} {-10}

do_execsql_test select-true {
  SELECT true
} {1}

do_execsql_test select-false {
  SELECT false
} {0}

do_execsql_test select-text-escape-1 {
  SELECT '''a'
} {'a}

do_execsql_test select-blob-empty {
  SELECT x'';
} {}

do_execsql_test select-blob-ascii {
  SELECT x'6C696D626F';
} {limbo}

do_execsql_test select-blob-emoji {
  SELECT x'F09FA680';
} {🦀}

do_execsql_test select-limit-0 {
  SELECT id FROM users LIMIT 0;
} {}

do_execsql_test select-doubly-qualified {
  SELECT main.users.id FROM users LIMIT 0;
} {}

do_execsql_test_error select-doubly-qualified-wrong-table {
  SELECT main.wrong.id FROM users LIMIT 0;
} {.*}

do_execsql_test_error select-doubly-qualified-wrong-column {
  SELECT main.users.wrong FROM users LIMIT 0;
} {.*}

do_execsql_test select-limit-expression {
  select price from products limit 2 + 1 - 1;
} {79.0
82.0}


# ORDER BY id here because sqlite uses age_idx here and we (yet) don't so force it to evaluate in ID order
do_execsql_test select-limit-true {
  SELECT id FROM users ORDER BY id LIMIT true;
} {1}

do_execsql_test select-limit-false {
  SELECT id FROM users ORDER BY id LIMIT false;
} {}

do_execsql_test realify {
    select price from products limit 1;
} {79.0}

do_execsql_test select-add {
    select u.age + 1 from users u where u.age = 91 limit 1;
} {92}

do_execsql_test select-subtract {
    select u.age - 1 from users u where u.age = 91 limit 1;
} {90}

do_execsql_test case-insensitive-columns {
    select u.aGe + 1 from USERS u where U.AGe = 91 limit 1;
} {92}

do_execsql_test table-star {
    select p.*, p.name from products p limit 1;
} {1|hat|79.0|hat}

do_execsql_test table-star-2 {
    select p.*, u.first_name from users u join products p on u.id = p.id limit 1;
} {1|hat|79.0|Jamie}

do_execsql_test select_with_quoting {
  select `users`.id from [users] where users.[id] = 5;
} {5}

do_execsql_test select_with_quoting_2 {
  select "users".`id` from users where `users`.[id] = 5;
} {5}

do_execsql_test select-rowid {
    select rowid, first_name from users u where rowid = 5;
} {5|Edward}

do_execsql_test select-rowid-2 {
    select u.rowid, first_name from users u where rowid = 5;
} {5|Edward}

do_execsql_test seekrowid {
    select * from users u where u.id = 5;
} {"5|Edward|Miller|christiankramer@example.com|725-281-1033|08522 English Plain|Lake Keith|ID|23283|15"}

do_execsql_test select_parenthesized {
  select (price + 100) from products limit 1;
} {179.0}

do_execsql_test select_case_base_else {
  select case when 0 then 'false' when 1 then 'true' else 'null' end;
} {true}

do_execsql_test select_case_noelse_null {
  select case when 0 then 0 end;
} {}

do_execsql_test select_base_case_else {
  select case 1 when 0 then 'zero' when 1 then 'one' else 'two' end;
} {one}

do_execsql_test select_base_case_null_result {
  select case NULL when 0 then 'first' else 'second' end;
  select case NULL when NULL then 'first' else 'second' end;
  select case 0 when 0 then 'first' else 'second' end;
} {second
second
first}

do_execsql_test select_base_case_noelse_null {
  select case 'null else' when 0 then 0 when 1 then 1 end;
} {}

do_execsql_test select-is-null {
    select null is null, (1 / 0) is null, null is (1 / 0), (1 / 0) is (1 / 0);
    select 4 is null, '4' is null, 0 is null, (1 / 2) is null;
} {1|1|1|1
0|0|0|0}

do_execsql_test select-is-not-null {
    select null is not null, (1 / 0) is not null, null is not (1 / 0), (1 / 0) is not (1 / 0);
    select 4 is not null, '4' is not null, 0 is not null, (1 / 2) is not null;
} {0|0|0|0
1|1|1|1}

do_execsql_test select_bin_shr {
  select 997623670 >> 0, 997623670 >> 1, 997623670 >> 10, 997623670 >> 30;
  select -997623670 >> 0, -997623670 >> 1, -997623670 >> 10, -997623670 >> 30;
  select 997623670 << 0, 997623670 << -1, 997623670 << -10, 997623670 << -30;
  select -997623670 << 0, -997623670 << -1, -997623670 << -10, -997623670 << -30;
} {997623670|498811835|974241|0
-997623670|-498811835|-974242|-1
997623670|498811835|974241|0
-997623670|-498811835|-974242|-1}

do_execsql_test select_bin_shl {
  select 997623670 << 0, 997623670 << 1, 997623670 << 10, 997623670 << 30;
  select -997623670 << 0, -997623670 << 1, -997623670 << 10, -997623670 << 30;
  select 997623670 >> 0, 997623670 >> -1, 997623670 >> -10, 997623670 >> -30;
  select -997623670 >> 0, -997623670 >> -1, -997623670 >> -10, -997623670 >> -30;
} {997623670|1995247340|1021566638080|1071190259091374080
-997623670|-1995247340|-1021566638080|-1071190259091374080
997623670|1995247340|1021566638080|1071190259091374080
-997623670|-1995247340|-1021566638080|-1071190259091374080}

# Test LIKE in SELECT position
do_execsql_test select-like-expression {
    select 'bar' like 'bar%'
} {1}

do_execsql_test select-not-like-expression {
    select 'bar' not like 'bar%'
} {0}

# regression test for float divisor being cast to zero int and panicking
do_execsql_test select-like-expression {
    select 2 % 0.5
} {}

do_execsql_test select_positive_infinite_float {
  SELECT 1.7976931348623157E+308 + 1e308; -- f64::MAX + 1e308
} {Inf}

do_execsql_test select_negative_infinite_float {
  SELECT -1.7976931348623157E+308 - 1e308 -- f64::MIN - 1e308
} {-Inf}

do_execsql_test select_shl_large_negative_float {
    SELECT 1 << -1e19;
    SELECT 1 << -9223372036854775808;  -- i64::MIN
    SELECT 1 << 9223372036854775807;   -- i64::MAX
} {0 0 0}

do_execsql_test select_shl_basic {
    SELECT 1 << 0, 1 << 1, 1 << 2, 1 << 3;
    SELECT 2 << 0, 2 << 1, 2 << 2, 2 << 3;
} {1|2|4|8
2|4|8|16}

do_execsql_test select_shl_negative_numbers {
    SELECT -1 << 0, -1 << 1, -1 << 2, -1 << 3;
    SELECT -2 << 0, -2 << 1, -2 << 2, -2 << 3;
} {-1|-2|-4|-8
-2|-4|-8|-16}
do_execsql_test select_shl_negative_shifts {
    SELECT 8 << -1, 8 << -2, 8 << -3, 8 << -4;
    SELECT -8 << -1, -8 << -2, -8 << -3, -8 << -4;
} {4|2|1|0
-4|-2|-1|-1}

do_execsql_test select_shl_large_shifts {
    SELECT 1 << 62, 1 << 63, 1 << 64;
    SELECT -1 << 62, -1 << 63, -1 << 64;
} {4611686018427387904|-9223372036854775808|0
-4611686018427387904|-9223372036854775808|0}

do_execsql_test select_shl_text_conversion {
    SELECT '1' << '2';
    SELECT '8' << '-2';
    SELECT '-4' << '2';
} {4 2 -16}

do_execsql_test select_shl_chained {
    SELECT (1 << 2) << 3;
    SELECT (2 << 1) << (1 << 1);
} {32 16}

do_execsql_test select_shl_numeric_types {
    SELECT CAST(1 AS INTEGER) << 2;
    SELECT 1.0 << 2;
    SELECT 1.5 << 2;
} {4 4 4}

do_execsql_test select_fuzz_failure_case {
  SELECT (-9 << ((-6) << (9)) >> ((5)) % -10 - + - (-9));
} {-16}

# regression test for https://github.com/tursodatabase/turso/issues/1157
do_execsql_test select-invalid-numeric-text {
  select -'e';
} {0}

do_execsql_test select-invalid-numeric-text {
  select -'E';
} {0}

do_execsql_test_on_specific_db {:memory:} select-union-all-1 {
  CREATE TABLE t1 (x INTEGER);
  CREATE TABLE t2 (x INTEGER);
  CREATE TABLE t3 (x INTEGER);

  INSERT INTO t1 VALUES(1),(2),(3);
  INSERT INTO t2 VALUES(4),(5),(6);
  INSERT INTO t3 VALUES(7),(8),(9);

  SELECT x FROM t1
  UNION ALL
  SELECT x FROM t2
  UNION ALL
  SELECT x FROM t3;
} {1
2
3
4
5
6
7
8
9}

do_execsql_test_on_specific_db {:memory:} select-union-all-with-filters {
  CREATE TABLE t4 (x INTEGER);
  CREATE TABLE t5 (x INTEGER);
  CREATE TABLE t6 (x INTEGER);

  INSERT INTO t4 VALUES(1),(2),(3),(4);
  INSERT INTO t5 VALUES(5),(6),(7),(8);
  INSERT INTO t6 VALUES(9),(10),(11),(12);

  SELECT x FROM t4 WHERE x > 2
  UNION ALL
  SELECT x FROM t5 WHERE x < 7
  UNION ALL
  SELECT x FROM t6 WHERE x = 10;
} {3
4
5
6
10}

do_execsql_test_error select-star-no-from {
  SELECT *;
} {no tables specified}

do_execsql_test_error select-star-and-constant-no-from {
  SELECT *, 1;
} {no tables specified}

do_execsql_test_error select-star-subquery {
  SELECT 1 FROM (SELECT *);
} {no tables specified}

  do_execsql_test_on_specific_db {:memory:} select-union-1 {
  CREATE TABLE t (x TEXT, y TEXT);
  CREATE TABLE u (x TEXT, y TEXT);
  INSERT INTO t VALUES('x','x'),('y','y');
  INSERT INTO u VALUES('x','x'),('y','y');

  select * from t UNION select * from u;
  } {x|x
  y|y}

  do_execsql_test_on_specific_db {:memory:} select-union-all-union {
  CREATE TABLE t (x TEXT, y TEXT);
  CREATE TABLE u (x TEXT, y TEXT);
  CREATE TABLE v (x TEXT, y TEXT);
  INSERT INTO t VALUES('x','x'),('y','y');
  INSERT INTO u VALUES('x','x'),('y','y');
  INSERT INTO v VALUES('x','x'),('y','y');

  select * from t UNION select * from u UNION ALL select * from v;
  } {x|x
  y|y
  x|x
  y|y}

  do_execsql_test_on_specific_db {:memory:} select-union-all-union-2 {
  CREATE TABLE t (x TEXT, y TEXT);
  CREATE TABLE u (x TEXT, y TEXT);
  CREATE TABLE v (x TEXT, y TEXT);
  INSERT INTO t VALUES('x','x'),('y','y');
  INSERT INTO u VALUES('x','x'),('y','y');
  INSERT INTO v VALUES('x','x'),('y','y');

  select * from t UNION ALL select * from u UNION select * from v;
  } {x|x
  y|y}

  do_execsql_test_on_specific_db {:memory:} select-union-3 {
  CREATE TABLE t (x TEXT, y TEXT);
  CREATE TABLE u (x TEXT, y TEXT);
  CREATE TABLE v (x TEXT, y TEXT);
  INSERT INTO t VALUES('x','x'),('y','y');
  INSERT INTO u VALUES('x','x'),('y','y');
  INSERT INTO v VALUES('x','x'),('y','y');

  select * from t UNION select * from u UNION select * from v;
  } {x|x
  y|y}

  do_execsql_test_on_specific_db {:memory:} select-union-4 {
  CREATE TABLE t (x TEXT, y TEXT);
  CREATE TABLE u (x TEXT, y TEXT);
  CREATE TABLE v (x TEXT, y TEXT);
  INSERT INTO t VALUES('x','x'),('y','y');
  INSERT INTO u VALUES('x','x'),('y','y');
  INSERT INTO v VALUES('x','x'),('y','y');

  select * from t UNION select * from u UNION select * from v UNION select * from t;
  } {x|x
  y|y}

  do_execsql_test_on_specific_db {:memory:} select-union-all-union-3 {
  CREATE TABLE t (x TEXT, y TEXT);
  CREATE TABLE u (x TEXT, y TEXT);
  CREATE TABLE v (x TEXT, y TEXT);
  INSERT INTO t VALUES('x','x'),('y','y');
  INSERT INTO u VALUES('x','x'),('y','y');
  INSERT INTO v VALUES('x','x'),('y','y');

  select * from t UNION select * from u UNION select * from v UNION ALL select * from t;
  } {x|x
  y|y
  x|x
  y|y}

  do_execsql_test_on_specific_db {:memory:} select-union-all-with-offset {
  CREATE TABLE t (x TEXT, y TEXT);
  CREATE TABLE u (x TEXT, y TEXT);
  INSERT INTO t VALUES('x','x'),('y','y');
  INSERT INTO u VALUES('x','x'),('y','y'),('z', 'z');

  select * from t UNION ALL select * from u limit 1 offset 1;
  } {y|y}

  do_execsql_test_on_specific_db {:memory:} select-union-with-offset {
  CREATE TABLE t (x TEXT, y TEXT);
  CREATE TABLE u (x TEXT, y TEXT);
  INSERT INTO t VALUES('x','x'),('y','y');
  INSERT INTO u VALUES('x','x'),('y','y'),('z', 'z');

  select * from t UNION select * from u limit 1 offset 1;
  } {y|y}

  do_execsql_test_on_specific_db {:memory:} select-intersect-1 {
    CREATE TABLE t (x TEXT, y TEXT);
    CREATE TABLE u (x TEXT, y TEXT);
    INSERT INTO t VALUES('x','x'),('y','y');
    INSERT INTO u VALUES('x','x'),('z','y');

    select * from t INTERSECT select * from u;
  } {x|x}

  do_execsql_test_on_specific_db {:memory:} select-intersect-2 {
    CREATE TABLE t (x TEXT, y TEXT);
    CREATE TABLE u (x TEXT, y TEXT);
    CREATE TABLE v (x TEXT, y TEXT);
    INSERT INTO t VALUES('x','x'),('y','y');
    INSERT INTO u VALUES('x','x'),('y','y');
    INSERT INTO v VALUES('a','x'),('y','y');

    select * from t INTERSECT select * from u INTERSECT select * from v INTERSECT select * from t;
  } {y|y}

  do_execsql_test_on_specific_db {:memory:} select-intersect-union {
    CREATE TABLE t (x TEXT, y TEXT);
    CREATE TABLE u (x TEXT, y TEXT);
    CREATE TABLE v (x TEXT, y TEXT);
    INSERT INTO t VALUES('x','x'),('y','y');
    INSERT INTO u VALUES('x','x'),('z','y');
    INSERT INTO v VALUES('x','x'),('z','z');

    select * from t INTERSECT select * from u UNION select * from v;
  } {x|x
  z|z}

  do_execsql_test_on_specific_db {:memory:} select-union-intersect {
    CREATE TABLE t (x TEXT, y TEXT);
    CREATE TABLE u (x TEXT, y TEXT);
    CREATE TABLE v (x TEXT, y TEXT);
    INSERT INTO t VALUES('x','x'),('y','y');
    INSERT INTO u VALUES('x','x'),('z','y');
    INSERT INTO v VALUES('x','x'),('z','z');

    select * from t UNION select * from u INTERSECT select * from v;
  } {x|x}

  do_execsql_test_on_specific_db {:memory:} select-union-all-intersect {
    CREATE TABLE t (x TEXT, y TEXT);
    CREATE TABLE u (x TEXT, y TEXT);
    CREATE TABLE v (x TEXT, y TEXT);
    INSERT INTO t VALUES('x','x'),('y','y');
    INSERT INTO u VALUES('x','x'),('z','y');
    INSERT INTO v VALUES('x','x'),('z','z');

    select * from t UNION ALL select * from u INTERSECT select * from v;
  } {x|x}

  do_execsql_test_on_specific_db {:memory:} select-intersect-union-all {
    CREATE TABLE t (x TEXT, y TEXT);
    CREATE TABLE u (x TEXT, y TEXT);
    CREATE TABLE v (x TEXT, y TEXT);
    INSERT INTO t VALUES('x','x'),('y','y');
    INSERT INTO u VALUES('x','x'),('z','y');
    INSERT INTO v VALUES('x','x'),('z','z');

    select * from t INTERSECT select * from u UNION ALL select * from v;
  } {x|x
  x|x
  z|z}

  do_execsql_test_on_specific_db {:memory:} select-intersect-with-limit {
    CREATE TABLE t (x TEXT, y TEXT);
    CREATE TABLE u (x TEXT, y TEXT);
    INSERT INTO t VALUES('x','x'),('y','y'), ('z','z');
    INSERT INTO u VALUES('x','x'),('y','y'), ('z','z');

    select * from t INTERSECT select * from u limit 2;
  } {x|x
  y|y}

  do_execsql_test_on_specific_db {:memory:} select-intersect-with-offset {
    CREATE TABLE t (x TEXT, y TEXT);
    CREATE TABLE u (x TEXT, y TEXT);
    INSERT INTO t VALUES('x','x'),('y','y'), ('z','z');
    INSERT INTO u VALUES('x','x'),('y','y'), ('z','z');

    select * from t INTERSECT select * from u limit 2 offset 1;
  } {y|y
  z|z}

  do_execsql_test_on_specific_db {:memory:} select-intersect-union-with-limit {
    CREATE TABLE t (x TEXT, y TEXT);
    CREATE TABLE u (x TEXT, y TEXT);
    CREATE TABLE v (x TEXT, y TEXT);
    INSERT INTO t VALUES('x','x'),('y','y'), ('z','z');
    INSERT INTO u VALUES('d','d'),('e','e'), ('z','z');
    INSERT INTO v VALUES('a','a'),('b','b');

    select * from t INTERSECT select * from u UNION select * from v limit 3;
  } {a|a
  b|b
  z|z}

  do_execsql_test_on_specific_db {:memory:} select-except-1 {
    CREATE TABLE t (x TEXT, y TEXT);
    CREATE TABLE u (x TEXT, y TEXT);
    INSERT INTO t VALUES('x','x'),('y','y');
    INSERT INTO u VALUES('x','x'),('z','y');

    select * from t EXCEPT select * from u;
  } {y|y}

  do_execsql_test_on_specific_db {:memory:} select-except-2 {
    CREATE TABLE t (x TEXT, y TEXT);
    CREATE TABLE u (x TEXT, y TEXT);
    INSERT INTO t VALUES('x','x'),('y','y');
    INSERT INTO u VALUES('x','x'),('y','y');

    select * from t EXCEPT select * from u;
  } {}

  do_execsql_test_on_specific_db {:memory:} select-except-3 {
    CREATE TABLE t (x TEXT, y TEXT);
    CREATE TABLE u (x TEXT, y TEXT);
    CREATE TABLE v (x TEXT, y TEXT);
    INSERT INTO t VALUES('x','x'),('y','y');
    INSERT INTO u VALUES('x','x'),('a','y');
    INSERT INTO v VALUES('a','x'),('b','y');

    select * from t EXCEPT select * from u EXCEPT select * from v;
  } {y|y}

  do_execsql_test_on_specific_db {:memory:} select-except-limit {
    CREATE TABLE t (x TEXT, y TEXT);
    CREATE TABLE u (x TEXT, y TEXT);
    INSERT INTO t VALUES('a', 'a'),('x','x'),('y','y'),('z','z');
    INSERT INTO u VALUES('x','x'),('z','y');

    select * from t EXCEPT select * from u limit 2;
  } {a|a
  y|y}

  do_execsql_test_on_specific_db {:memory:} select-except-union-all {
    CREATE TABLE t (x TEXT, y TEXT);
    CREATE TABLE u (x TEXT, y TEXT);
    CREATE TABLE v (x TEXT, y TEXT);
    INSERT INTO t VALUES('x','x'),('y','y');
    INSERT INTO u VALUES('x','x'),('z','y');
    INSERT INTO v VALUES('x','x'),('y','y');

    select * from t EXCEPT select * from u UNION ALL select * from v;
  } {y|y
  x|x
  y|y}

  do_execsql_test_on_specific_db {:memory:} select-union-all-except {
    CREATE TABLE t (x TEXT, y TEXT);
    CREATE TABLE u (x TEXT, y TEXT);
    CREATE TABLE v (x TEXT, y TEXT);
    INSERT INTO t VALUES('x','x'),('y','y');
    INSERT INTO u VALUES('x','x'),('z','y');
    INSERT INTO v VALUES('x','x'),('y','y');

    select * from t UNION ALL select * from u EXCEPT select * from v;
  } {z|y}

  do_execsql_test_on_specific_db {:memory:} select-except-union {
    CREATE TABLE t (x TEXT, y TEXT);
    CREATE TABLE u (x TEXT, y TEXT);
    CREATE TABLE v (x TEXT, y TEXT);
    INSERT INTO t VALUES('x','x'),('y','y');
    INSERT INTO u VALUES('x','x'),('z','y');
    INSERT INTO v VALUES('x','x'),('z','z');

    select * from t EXCEPT select * from u UNION select * from v;
  } {x|x
  y|y
  z|z}

  do_execsql_test_on_specific_db {:memory:} select-union-except {
    CREATE TABLE t (x TEXT, y TEXT);
    CREATE TABLE u (x TEXT, y TEXT);
    CREATE TABLE v (x TEXT, y TEXT);
    INSERT INTO t VALUES('x','x'),('y','y');
    INSERT INTO u VALUES('x','x'),('z','y');
    INSERT INTO v VALUES('x','x'),('z','z');

    select * from t UNION select * from u EXCEPT select * from v;
  } {y|y
  z|y}

  do_execsql_test_on_specific_db {:memory:} select-except-intersect {
    CREATE TABLE t (x TEXT, y TEXT);
    CREATE TABLE u (x TEXT, y TEXT);
    CREATE TABLE v (x TEXT, y TEXT);
    INSERT INTO t VALUES('x','x'),('y','y');
    INSERT INTO u VALUES('x','x'),('z','y');
    INSERT INTO v VALUES('y','y'),('z','z');

    select * from t EXCEPT select * from u INTERSECT select * from v;
  } {y|y}

  do_execsql_test_on_specific_db {:memory:} select-intersect-except {
    CREATE TABLE t (x TEXT, y TEXT);
    CREATE TABLE u (x TEXT, y TEXT);
    CREATE TABLE v (x TEXT, y TEXT);
    INSERT INTO t VALUES('x','x'),('y','y');
    INSERT INTO u VALUES('x','x'),('z','y');
    INSERT INTO v VALUES('x','x'),('z','z');

    select * from t INTERSECT select * from u EXCEPT select * from v;
  } {}

  do_execsql_test_on_specific_db {:memory:} select-values-union {
  CREATE TABLE t (x TEXT, y TEXT);
  INSERT INTO t VALUES('x','x'),('y','y');

  values('x', 'x') UNION select * from t;
  } {x|x
  y|y}

  do_execsql_test_on_specific_db {:memory:} select-values-union-2 {
  CREATE TABLE t (x TEXT, y TEXT);
  INSERT INTO t VALUES('x','x'),('y','y');

  values('x', 'x'), ('y', 'y') UNION select * from t;
  } {x|x
  y|y}

  do_execsql_test_on_specific_db {:memory:} select-values-except {
    CREATE TABLE t (x TEXT, y TEXT);
    INSERT INTO t VALUES('x','x'),('y','y');

    select * from t EXCEPT values('x','x'),('z','y');
  } {y|y}

  do_execsql_test_on_specific_db {:memory:} select-values-union-all-limit {
  CREATE TABLE t (x TEXT);
  INSERT INTO t VALUES('x'), ('y'), ('z');

  values('x') UNION ALL select * from t limit 3;
  } {x
  x
  y}

  do_execsql_test_on_specific_db {:memory:} select-values-union-all-limit-1 {
  CREATE TABLE t (x TEXT);
  INSERT INTO t VALUES('x'), ('y'), ('z');

  values('a'), ('b') UNION ALL select * from t limit 3;
  } {a
  b
  x}

  do_execsql_test_on_specific_db {:memory:} select-values-union-all-offset {
  CREATE TABLE t (x TEXT);
  INSERT INTO t VALUES('x'), ('y'), ('z');

  values('a'), ('b') UNION ALL select * from t limit 3 offset 1;
  } {b
  x
  y}

  do_execsql_test_on_specific_db {:memory:} select-values-union-all-offset-1 {
  CREATE TABLE t (x TEXT);
  INSERT INTO t VALUES('i'), ('j'), ('x'), ('y'), ('z');

  values('a') UNION ALL select * from t limit 3 offset 1;
  } {i
  j
  x}

do_execsql_test_on_specific_db {:memory:} select-no-match-in-leaf-page {
    CREATE TABLE t (a INTEGER PRIMARY KEY, b);
    insert into t values (1, randomblob(1024));
    insert into t values (2, randomblob(1024));
    insert into t values (3, randomblob(1024));
    insert into t values (4, randomblob(1024));
    insert into t values (5, randomblob(1024));
    insert into t values (6, randomblob(1024));
    insert into t values (7, randomblob(1024));
    insert into t values (8, randomblob(1024));
    insert into t values (9, randomblob(1024));
    insert into t values (10, randomblob(1024));
    insert into t values (11, randomblob(1024));
    insert into t values (12, randomblob(1024));
    insert into t values (13, randomblob(1024));
    insert into t values (14, randomblob(1024));
    insert into t values (15, randomblob(1024));
    insert into t values (16, randomblob(1024));
    delete from t where a in (3, 6, 9, 12);
    select count(*) from t where a >= 2;
    select count(*) from t where a >= 3;
    select count(*) from t where a >= 4;
    select count(*) from t where a > 1;
    select count(*) from t where a > 2;
    select count(*) from t where a > 3;
    select count(*) from t where a <= 3 ORDER BY a DESC;
    select count(*) from t where a <= 4 ORDER BY a DESC;
    select count(*) from t where a <= 5 ORDER BY a DESC;
    select count(*) from t where a < 2 ORDER BY a DESC;
    select count(*) from t where a < 3 ORDER BY a DESC;
    select count(*) from t where a < 4 ORDER BY a DESC;
} {11
10
10
11
10
10
2
3
4
1
2
2}

do_execsql_test_on_specific_db {:memory:} select-range-search-count-asc-index {
    CREATE TABLE t (a, b);
    CREATE INDEX t_idx ON t(a, b);
    insert into t values (1, 1);
    insert into t values (1, 2);
    insert into t values (1, 3);
    insert into t values (1, 4);
    insert into t values (1, 5);
    insert into t values (1, 6);
    insert into t values (2, 1);
    insert into t values (2, 2);
    insert into t values (2, 3);
    insert into t values (2, 4);
    insert into t values (2, 5);
    insert into t values (2, 6);
    select count(*) from t where a = 1 AND b >= 2 ORDER BY a ASC, b ASC;
    select count(*) from t where a = 1 AND b > 2 ORDER BY a ASC, b ASC;
    select count(*) from t where a = 1 AND b <= 4 ORDER BY a ASC, b ASC;
    select count(*) from t where a = 1 AND b < 4 ORDER BY a ASC, b ASC;
    select count(*) from t where a = 1 AND b >= 2 AND b <= 4 ORDER BY a ASC, b ASC;
    select count(*) from t where a = 1 AND b > 2 AND b <= 4 ORDER BY a ASC, b ASC;
    select count(*) from t where a = 1 AND b >= 2 AND b < 4 ORDER BY a ASC, b ASC;
    select count(*) from t where a = 1 AND b > 2 AND b < 4 ORDER BY a ASC, b ASC;
    
    select count(*) from t where a = 1 AND b >= 2 ORDER BY a DESC, b DESC;
    select count(*) from t where a = 1 AND b > 2 ORDER BY a DESC, b DESC;
    select count(*) from t where a = 1 AND b <= 4 ORDER BY a DESC, b DESC;
    select count(*) from t where a = 1 AND b < 4 ORDER BY a DESC, b DESC;
    select count(*) from t where a = 1 AND b >= 2 AND b <= 4 ORDER BY a DESC, b DESC;
    select count(*) from t where a = 1 AND b > 2 AND b <= 4 ORDER BY a DESC, b DESC;
    select count(*) from t where a = 1 AND b >= 2 AND b < 4 ORDER BY a DESC, b DESC;
    select count(*) from t where a = 1 AND b > 2 AND b < 4 ORDER BY a DESC, b DESC;
} {5
4
4
3
3
2
2
1
5
4
4
3
3
2
2
1}

do_execsql_test_on_specific_db {:memory:} select-range-search-count-desc-index {
    CREATE TABLE t (a, b);
    CREATE INDEX t_idx ON t(a, b DESC);
    insert into t values (1, 1);
    insert into t values (1, 2);
    insert into t values (1, 3);
    insert into t values (1, 4);
    insert into t values (1, 5);
    insert into t values (1, 6);
    insert into t values (2, 1);
    insert into t values (2, 2);
    insert into t values (2, 3);
    insert into t values (2, 4);
    insert into t values (2, 5);
    insert into t values (2, 6);
    select count(*) from t where a = 1 AND b >= 2 ORDER BY a ASC, b DESC;
    select count(*) from t where a = 1 AND b > 2 ORDER BY a ASC, b DESC;
    select count(*) from t where a = 1 AND b <= 4 ORDER BY a ASC, b DESC;
    select count(*) from t where a = 1 AND b < 4 ORDER BY a ASC, b DESC;
    select count(*) from t where a = 1 AND b >= 2 AND b <= 4 ORDER BY a ASC, b DESC;
    select count(*) from t where a = 1 AND b > 2 AND b <= 4 ORDER BY a ASC, b DESC;
    select count(*) from t where a = 1 AND b >= 2 AND b < 4 ORDER BY a ASC, b DESC;
    select count(*) from t where a = 1 AND b > 2 AND b < 4 ORDER BY a ASC, b DESC;
    
    select count(*) from t where a = 1 AND b >= 2 ORDER BY a DESC, b ASC;
    select count(*) from t where a = 1 AND b > 2 ORDER BY a DESC, b ASC;
    select count(*) from t where a = 1 AND b <= 4 ORDER BY a DESC, b ASC;
    select count(*) from t where a = 1 AND b < 4 ORDER BY a DESC, b ASC;
    select count(*) from t where a = 1 AND b >= 2 AND b <= 4 ORDER BY a DESC, b ASC;
    select count(*) from t where a = 1 AND b > 2 AND b <= 4 ORDER BY a DESC, b ASC;
    select count(*) from t where a = 1 AND b >= 2 AND b < 4 ORDER BY a DESC, b ASC;
    select count(*) from t where a = 1 AND b > 2 AND b < 4 ORDER BY a DESC, b ASC;
} {5
4
4
3
3
2
2
1
5
4
4
3
3
2
2
1}

do_execsql_test_on_specific_db {:memory:} select-range-search-scan-asc-index {
    CREATE TABLE t (a, b);
    CREATE INDEX t_idx ON t(a, b);
    insert into t values (1, 1);
    insert into t values (1, 2);
    insert into t values (1, 3);
    insert into t values (1, 4);
    insert into t values (1, 5);
    insert into t values (1, 6);
    insert into t values (2, 1);
    insert into t values (2, 2);
    insert into t values (2, 3);
    insert into t values (2, 4);
    insert into t values (2, 5);
    insert into t values (2, 6);
    select * from t where a = 1 AND b > 1 AND b < 6 ORDER BY a ASC, b ASC;
    select * from t where a = 2 AND b > 1 AND b < 6 ORDER BY a DESC, b DESC;
    select * from t where a = 1 AND b > 1 AND b < 6 ORDER BY a DESC, b ASC;
    select * from t where a = 2 AND b > 1 AND b < 6 ORDER BY a ASC, b DESC;
} {1|2
1|3
1|4
1|5
2|5
2|4
2|3
2|2
1|2
1|3
1|4
1|5
2|5
2|4
2|3
2|2}

do_execsql_test_on_specific_db {:memory:} select-range-search-scan-desc-index {
    CREATE TABLE t (a, b);
    CREATE INDEX t_idx ON t(a, b DESC);
    insert into t values (1, 1);
    insert into t values (1, 2);
    insert into t values (1, 3);
    insert into t values (1, 4);
    insert into t values (1, 5);
    insert into t values (1, 6);
    insert into t values (2, 1);
    insert into t values (2, 2);
    insert into t values (2, 3);
    insert into t values (2, 4);
    insert into t values (2, 5);
    insert into t values (2, 6);
    select * from t where a = 1 AND b > 1 AND b < 6 ORDER BY a ASC, b ASC;
    select * from t where a = 2 AND b > 1 AND b < 6 ORDER BY a DESC, b DESC;
    select * from t where a = 1 AND b > 1 AND b < 6 ORDER BY a DESC, b ASC;
    select * from t where a = 2 AND b > 1 AND b < 6 ORDER BY a ASC, b DESC;
} {1|2
1|3
1|4
1|5
2|5
2|4
2|3
2|2
1|2
1|3
1|4
1|5
2|5
2|4
2|3
2|2}

# Regression tests for double-quoted strings in SELECT statements
do_execsql_test_skip_lines_on_specific_db 1 {:memory:} select-double-quotes-values {
    .dbconfig dqs_dml on
    SELECT * FROM (VALUES ("select", "test"), ("double", "quotes"));
} {select|test
double|quotes}

do_execsql_test_skip_lines_on_specific_db 1 {:memory:} select-double-quotes-no-column {
    .dbconfig dqs_dml on
    SELECT "first"
} {first}

do_execsql_test_skip_lines_on_specific_db 1 {:memory:} select-double-quotes-literal {
    .dbconfig dqs_dml on
    SELECT "literal_string" AS col;
} {literal_string}

do_execsql_test_on_specific_db {:memory:} select-in-simple {
  SELECT 1 IN (1, 2, 3);
  SELECT 4 IN (1, 2, 3);
} {1
0}

do_execsql_test_on_specific_db {:memory:} select-in-with-nulls {
  SELECT 4 IN (1, 4, null);
  SELECT 4 NOT IN (1, 4, null);
} {1
0}

# All should be null
do_execsql_test_on_specific_db {:memory:} select-in-with-nulls-2 {
SELECT 1 IN (2, 3, null);
SELECT 1 NOT IN (2, 3, null);
SELECT null in (null);
} {\n\n}

do_execsql_test_on_specific_db {:memory:} select-in-complex {
  CREATE TABLE test_table (id INTEGER, category TEXT, value INTEGER);
  INSERT INTO test_table VALUES (1, 'A', 10), (2, 'B', 20), (3, 'A', 30), (4, 'C', 40);
  SELECT * FROM test_table WHERE category IN ('A', 'B') AND value IN (10, 30, 40);
} {1|A|10
3|A|30}

foreach {testname limit ans} {
  limit-const-1             1                      {1}
  limit-text-2              '2'                    {1 2}
  limit-bool-true           true                   {1}
  limit-expr-add            1+2+3                  {1 2 3 4 5 6}
  limit-expr-sub            5-2-3                  {}
  limit-expr-paren          (1+1)*2                {1 2 3 4}
  limit-bool-add            true+2                 {1 2 3}
  limit-text-math           '2'*2+1                {1 2 3 4 5}
  limit-bool-false-add      false+4                {1 2 3 4}
  limit-mixed-math          (1+'1')*(1+1)-(5/5)    {1 2 3}
  limit-text-bool           ('false'+2)            {1 2}
  limit-coalesce            COALESCE(NULL,0+1)     {1}
} {
  do_execsql_test limit-complex-exprs-$testname \
    "SELECT id FROM users ORDER BY id LIMIT $limit" $ans
}

do_execsql_test_on_specific_db {:memory:}  limit-expr-can-be-cast-losslessly-1 {
  SELECT 1 LIMIT 1.1 + 2.9;
} {1}

do_execsql_test_on_specific_db {:memory:}  limit-expr-can-be-cast-losslessly-2 {
  CREATE TABLE T(a);
  INSERT INTO T VALUES (1),(1),(1),(1);
  SELECT * FROM T LIMIT 1.6/2 + 3.6/3 + 4*0.25;
} {1
1
1}

# Numeric strings are cast to float. The final evaluation of the expression returns an int losslessly
do_execsql_test_on_specific_db {:memory:}  limit-expr-can-be-cast-losslessly-3 {
  CREATE TABLE T(a);
  INSERT INTO T VALUES (1),(1),(1),(1);
  SELECT * FROM T LIMIT '0.8' + '1.2' + 4*0.25;
} {1
1
1}

# Invalid strings are cast to 0. So expression is valid
do_execsql_test_on_specific_db {:memory:} limit-expr-int-and-string {
  SELECT 1 LIMIT 3/3 + 'test' + 4*'test are best';
} {1}

do_execsql_test_in_memory_error_content  limit-expr-cannot-be-cast-losslessly-1 {
  SELECT 1 LIMIT 1.1;
} {"datatype mismatch"}

do_execsql_test_in_memory_error_content  limit-expr-cannot-be-cast-losslessly-2 {
  SELECT 1 LIMIT 1.1 + 2.2 + 1.9/8;
} {"datatype mismatch"}

# Return error as float in the expression cannot be cast losslessly
do_execsql_test_in_memory_error_content limit-expr-cannot-be-cast-losslessly-3 {
  SELECT 1 LIMIT 1.1 +'a';
} {"datatype mismatch"}

do_execsql_test_in_memory_error_content limit-expr-invalid-data-type-1 {
  SELECT 1 LIMIT 'a';
} {"datatype mismatch"}

do_execsql_test_in_memory_error_content limit-expr-invalid-data-type-2 {
  SELECT 1 LIMIT NULL;
} {"datatype mismatch"}

# The expression below evaluates to NULL as string is cast to 0
do_execsql_test_in_memory_error_content limit-expr-invalid-data-type-3 {
  SELECT 1 LIMIT 1/'iwillbezero ;-; ' ;
} {"datatype mismatch"}

# Expression is evaluated as NULL
do_execsql_test_in_memory_error_content limit-expr-invalid-data-type-4 { 
  SELECT 1 LIMIT 4+NULL;
} {"datatype mismatch"}

do_execsql_test_in_memory_error_content limit-expression-invalid-type {
  SELECT 1 LIMIT '1.xx';
} {"datatype mismatch"}


do_execsql_test_on_specific_db {:memory:} rowid-references {
  CREATE TABLE test_table (id INTEGER);
  INSERT INTO test_table VALUES (5),(5);
  SELECT
    rowid, "rowid", `rowid`, [rowid], _rowid_, "_rowid_", `_rowid_`, [_rowid_], oid, "oid", `oid`, [oid]
  FROM test_table
    WHERE rowid = 2
      AND "rowid" = 2
      AND `rowid` = 2
      AND [rowid] = 2
      AND _rowid_ = 2
      AND "_rowid_" = 2
      AND `_rowid_` = 2
      AND [_rowid_] = 2
      AND oid = 2
      AND "oid" = 2
      AND `oid` = 2
      AND [oid] = 2;
} {2|2|2|2|2|2|2|2|2|2|2|2}

do_execsql_test_on_specific_db {:memory:} null-in-search {
  CREATE TABLE t_x_asc (id INTEGER PRIMARY KEY, x);
  CREATE INDEX t_x_asc_idx ON t_x_asc(x ASC);
  CREATE TABLE t_x_desc (id INTEGER PRIMARY KEY, x);
  CREATE INDEX t_x_desc_idx ON t_x_desc(x DESC);
  INSERT INTO t_x_asc VALUES (1, NULL), (2, 2), (10, 10);
  INSERT INTO t_x_desc VALUES (1, NULL), (2, 2), (10, 10);
  SELECT * FROM t_x_asc WHERE x > 5 ORDER BY x ASC;
  SELECT * FROM t_x_asc WHERE x > 5 ORDER BY x DESC;
  SELECT * FROM t_x_asc WHERE x < 5 ORDER BY x ASC;
  SELECT * FROM t_x_asc WHERE x < 5 ORDER BY x DESC;
  SELECT * FROM t_x_desc WHERE x > 5 ORDER BY x ASC;
  SELECT * FROM t_x_desc WHERE x > 5 ORDER BY x DESC;
  SELECT * FROM t_x_desc WHERE x < 5 ORDER BY x ASC;
  SELECT * FROM t_x_desc WHERE x < 5 ORDER BY x DESC;
} {10|10
10|10
2|2
2|2
10|10
10|10
2|2
2|2}

do_execsql_test_in_memory_any_error limit-column-reference-error {
  CREATE TABLE t(a);
  SELECT * FROM t LIMIT (t.a);
}

do_execsql_test select-binary-collation {
  SELECT 'a' = 'A';
  SELECT 'a' = 'a';
} {0 1}

# https://github.com/tursodatabase/turso/issues/3667 regression test
do_execsql_test_in_memory_error_content rowid-select-from-clause-subquery {
	CREATE TABLE t(a);
	SELECT rowid FROM (SELECT * FROM t);
} {"no such column: rowid"}

do_execsql_test_on_specific_db {:memory:} rowid-select-from-clause-subquery-explicit-works {
	CREATE TABLE t(a);
	INSERT INTO t values ('abc');
	SELECT rowid,a FROM (SELECT rowid,a FROM t);
} {1|abc}

# https://github.com/tursodatabase/turso/issues/3505 regression test
do_execsql_test_in_memory_any_error ambiguous-self-join {
    CREATE TABLE T(a);
	INSERT INTO t VALUES (1), (2), (3); 
	SELECT * fROM t JOIN t;
}

do_execsql_test_on_specific_db {:memory:} unambiguous-self-join {
    CREATE TABLE T(a);
	INSERT INTO t VALUES (1), (2), (3); 
	SELECT * fROM t as ta JOIN t order by ta.a;
} {1|1
1|2
1|3
2|1
2|2
2|3
3|1
3|2
3|3}

# Regression test for https://github.com/tursodatabase/turso/issues/3853
do_execsql_test_on_specific_db {:memory:} order-by-limit-0 {
    CREATE TABLE t(a);
	INSERT INTO t VALUES (1), (2), (3); 
	SELECT * FROM t ORDER BY a LIMIT 0;
} {}

# Regression test for https://github.com/tursodatabase/turso/issues/3853
do_execsql_test_on_specific_db {:memory:} group-by-limit-0 {
    CREATE TABLE t(a);
	INSERT INTO t VALUES (1), (2), (3); 
	SELECT a, COUNT(*) FROM t GROUP BY a LIMIT 0;
} {}

do_execsql_test_in_memory_any_error duplicate-with-cte-name {
  CREATE TABLE t2(x INTEGER);
  WITH t as (SELECT 1), t as (SELECT 2)  SELECT  * FROM t2;
}