#!/usr/bin/env tclsh

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

do_execsql_test concat-chars {
  select concat('l', 'i');
} {li}

do_execsql_test concat-char-and-number {
  select concat('l', 1);
} {l1}

do_execsql_test concat-char-and-decimal {
  select concat('l', 1.5);
} {l1.5}

do_execsql_test concat-char-null-char {
  select concat('l', null, 'i');
} {li}

do_execsql_test concat_ws-numbers {
  select concat_ws(',', 1, 2);
} {1,2}

do_execsql_test concat_ws-single-number {
  select concat_ws(',', 1);
} {1}

do_execsql_test concat_ws-null {
  select concat_ws(null, 1, 2);
} {}

do_execsql_test concat_ws-multiple {
  select concat_ws(',', 1, 2), concat_ws(',', 3, 4)
} {1,2|3,4}

do_execsql_test char {
  select char(108, 105)
} {li}

do_execsql_test char-nested {
  select char(106 + 2, 105)
} {li}

do_execsql_test char-empty {
  select char()
} {}

do_execsql_test char-null {
  select char(null)
} {}

do_execsql_test char-non-integer {
  select char('a')
} {}

do_execsql_test abs {
    select abs(1);
} {1}

do_execsql_test abs-negative {
    select abs(-1);
} {1}

do_execsql_test abs-char {
    select abs('a');
} {0.0}

do_execsql_test abs-null {
    select abs(null);
} {}

do_execsql_test ifnull-1 {
  select ifnull(1, 2);
} {1}

do_execsql_test ifnull-2 {
  select ifnull(null, 2);
} {2}

do_execsql_test iif-true {
  select iif(1, 'pass', 'fail');
} {pass}

do_execsql_test iif-false {
  select iif(0, 'fail', 'pass');
} {pass}

do_execsql_test instr-str {
  select instr('limbo', 'im');
} {2}

do_execsql_test instr-str-not-found {
  select instr('limbo', 'xyz');
} {0}

do_execsql_test instr-blob {
  select instr(x'000102', x'01');
} {2}

do_execsql_test instr-blob-not-found {
  select instr(x'000102', x'10');
} {0}

do_execsql_test instr-null {
  select instr(null, 'limbo');
} {}

do_execsql_test instr-integer {
  select instr(123, 2);
} {2}

do_execsql_test instr-integer-not-found {
  select instr(123, 5);
} {0}

do_execsql_test instr-integer-leading-zeros {
  select instr(0001, 0);
} {0}

do_execsql_test instr-real {
  select instr(12.34, 2.3);
} {2}

do_execsql_test instr-real-not-found {
  select instr(12.34, 5);
} {0}

do_execsql_test instr-real-trailing-zeros {
  select instr(1.10000, 0);
} {0}

do_execsql_test instr-blob {
  select instr(x'01020304', x'02');
} {2}

do_execsql_test instr-blob-not-found {
  select instr(x'01020304', x'05');
} {0}

do_execsql_test upper {
  select upper('Limbo')
} {LIMBO}

do_execsql_test upper-number {
  select upper(1)
} {1}

do_execsql_test upper-char {
  select upper('a')
} {A}

do_execsql_test upper-null {
  select upper(null)
} {}

do_execsql_test lower {
  select lower('Limbo')
} {limbo}

do_execsql_test lower-number {
  select lower(1)
} {1}

do_execsql_test lower-char {
  select lower('A')
} {a}

do_execsql_test lower-null {
  select lower(null)
} {}

do_execsql_test replace {
  select replace('test', 'test', 'example')
} {example}

do_execsql_test replace-number {
  select replace('tes3', 3, 0.3)
} {tes0.3}

do_execsql_test replace-null {
  select replace('test', null, 'example')
} {}

do_execsql_test hex {
  select hex('limbo')
} {6C696D626F}

do_execsql_test hex-number {
  select hex(100)
} {313030}

do_execsql_test hex-null {
  select hex(null)
} {}

do_execsql_test unhex-str-ab {
  SELECT unhex('6162');
} {ab}

do_execsql_test unhex-int-ab {
  SELECT unhex(6162);
} {ab}

do_execsql_test unhex-dot-uppercase {
  SELECT unhex('2E');
} {.}

do_execsql_test unhex-dot-lowercase {
  SELECT unhex('2e');
} {.}

do_execsql_test unhex-no-hex {
  SELECT unhex('x');
} {}

do_execsql_test unhex-null {
  SELECT unhex(NULL);
} {}

do_execsql_test unhex-x-y-prefix {
  SELECT unhex('x2E', 'x');
} {.}

do_execsql_test unhex-x-y-suffix {
  SELECT unhex('2Ex', 'x');
} {.}

do_execsql_test unhex-x-y-prefix-suffix {
  SELECT unhex('x2Ex', 'x');
} {.}

do_execsql_test unhex-x-y-incorrect-suffix {
  SELECT unhex('x2Ey', 'x');
} {}

do_execsql_test unhex-x-y-long-prefix {
  SELECT unhex('xyz2E', 'xyz');
} {.}

do_execsql_test unhex-x-y-shorter-suffix {
  SELECT unhex('xyz2Exy', 'xyz');
} {.}

do_execsql_test unhex-x-y-shorter-prefix {
  SELECT unhex('xy2Exyz', 'xyz');
} {.};

do_execsql_test unhex-x-y-random-order {
  SELECT unhex('yx2Ezyx', 'xyz');
} {.};

do_execsql_test unhex-x-y-char-in-the-middle {
  SELECT unhex('yx2xEzyx', 'xyz');
} {};

do_execsql_test unhex-x-y-character-outside-set {
  SELECT unhex('yxn2Ezyx', 'xyz');
} {};

do_execsql_test trim {
  SELECT trim('   Limbo    ');
} {Limbo}

do_execsql_test trim-number {
  SELECT trim(1);
} {1}

do_execsql_test trim-null {
  SELECT trim(null);
} {}

do_execsql_test trim-leading-whitespace {
  SELECT trim('   Leading');
} {Leading}

do_execsql_test trim-trailing-whitespace {
  SELECT trim('Trailing   ');
} {Trailing}

do_execsql_test trim-pattern {
  SELECT trim('Limbo', 'Limbo');
} {}

do_execsql_test trim-pattern-number {
  SELECT trim(1, '1');
} {}

do_execsql_test trim-pattern-null {
  SELECT trim(null, 'null');
} {}

do_execsql_test trim-no-match-pattern {
  SELECT trim('Limbo', 'xyz');
} {Limbo}

do_execsql_test ltrim {
  SELECT ltrim('   Limbo    ');
} {"Limbo    "}

do_execsql_test ltrim-number {
  SELECT ltrim(1);
} {1}

do_execsql_test ltrim-null {
  SELECT ltrim(null);
} {}

do_execsql_test ltrim-leading-whitespace {
  SELECT ltrim('   Leading');
} {Leading}

do_execsql_test ltrim-no-leading-whitespace {
  SELECT ltrim('Limbo');
} {Limbo}

do_execsql_test ltrim-pattern {
  SELECT ltrim('Limbo', 'Limbo');
} {}

do_execsql_test ltrim-pattern-number {
  SELECT ltrim(1, '1');
} {}

do_execsql_test ltrim-pattern-null {
  SELECT ltrim(null, 'null');
} {}

do_execsql_test ltrim-no-match-pattern {
  SELECT ltrim('Limbo', 'xyz');
} {Limbo}

do_execsql_test rtrim {
  SELECT rtrim('   Limbo    ');
} {"   Limbo"}

do_execsql_test rtrim-number {
  SELECT rtrim(1);
} {1}

do_execsql_test rtrim-null {
  SELECT rtrim(null);
} {}

do_execsql_test rtrim-trailing-whitespace {
  SELECT rtrim('Trailing   ');
} {Trailing}

do_execsql_test rtrim-no-trailing-whitespace {
  SELECT rtrim('Limbo');
} {Limbo}

do_execsql_test rtrim-pattern {
  SELECT rtrim('Limbo', 'Limbo');
} {}

do_execsql_test rtrim-pattern-number {
  SELECT rtrim(1, '1');
} {}

do_execsql_test rtrim-pattern-null {
  SELECT rtrim(null, 'null');
} {}

do_execsql_test rtrim-no-match-pattern {
  SELECT rtrim('Limbo', 'xyz');
} {Limbo}

do_execsql_test round-float-no-precision {
  SELECT round(123.456);
} {123.0}

do_execsql_test round-float-with-precision {
  SELECT round(123.456, 2);
} {123.46}

do_execsql_test round-float-with-text-precision {
  SELECT round(123.5, '1');
} {123.5}

do_execsql_test round-text-parsable {
  SELECT round('123.456', 2);
} {123.46}

do_execsql_test round-text-non-parsable {
  SELECT round('abc', 1);
} {0.0}

do_execsql_test round-integer-with-precision {
  SELECT round(123, 1);
} {123.0}

do_execsql_test round-float-negative-precision {
  SELECT round(123.456, -1);
} {123.0}

do_execsql_test round-float-zero-precision {
  SELECT round(123.456, 0);
} {123.0}

do_execsql_test round-null-precision {
  SELECT round(123.456, null);
} {}

do_execsql_test length-text {
  SELECT length('limbo');
} {5}

do_execsql_test lenght-text-utf8-chars {
  SELECT length('ąłóżźć');
} {6}

do_execsql_test length-integer {
  SELECT length(12345);
} {5}

do_execsql_test length-float {
  SELECT length(123.456);
} {7}

do_execsql_test length-null {
  SELECT length(NULL);
} {}

do_execsql_test length-empty-text {
  SELECT length('');
} {0}

do_execsql_test octet-length-text {
  SELECT length('limbo');
} {5}

do_execsql_test octet-lenght-text-utf8-chars {
  SELECT octet_length('ąłóżźć');
} {12}

do_execsql_test octet-length-integer {
  SELECT octet_length(12345);
} {5}

do_execsql_test octet-length-float {
  SELECT octet_length(123.456);
} {7}

do_execsql_test octet-length-null {
  SELECT octet_length(NULL);
} {}

do_execsql_test octet-length-empty-text {
  SELECT octet_length('');
} {0}

do_execsql_test octet-length-date-binary-expr {
  select octet_length(date('now')) = 10;
} {1}

do_execsql_test min-number {
  select min(-10,2,3,+4)
} {-10}

do_execsql_test min-str {
  select min('b','a','z')
} {a}

do_execsql_test min-str-number {
  select min('42',100)
} {100}

do_execsql_test min-blob-number {
  select min(3.14,x'616263')
} {3.14}

do_execsql_test max-str-number {
  select max('42',100)
} {42}

do_execsql_test max-blob-number {
  select max(3.14,x'616263')
} {abc}

do_execsql_test min-null {
  select min(null,null)
} {}

do_execsql_test max-number {
  select max(-10,2,3)
} {3}

do_execsql_test max-str {
  select max('b','a','z')
} {z}

do_execsql_test min-int-float {
  select min(1,5.0)
} {1}

do_execsql_test max-int-float {
  select max(1,5.0)
} {5.0}

do_execsql_test min-float-int {
  select min(5.0,1)
} {1}

do_execsql_test max-null {
  select max(null,null)
} {}

do_execsql_test nullif {
  select nullif(1, 2)
} {1}

do_execsql_test nullif {
  select nullif(1, 1)
} {}

do_execsql_test nullif {
  select nullif('limbo', 'limbo')
} {}

do_execsql_test substr-3-args {
  SELECT substr('limbo', 1, 3);
} {lim}

do_execsql_test substr-3-args-exceed-length {
  SELECT substr('limbo', 1, 10);
} {limbo}

do_execsql_test substr-3-args-start-exceed-length {
  SELECT substr('limbo', 10, 3);
} {}

do_execsql_test substr-2-args {
  SELECT substr('limbo', 3);
} {mbo}

do_execsql_test substring-3-args {
  SELECT substring('limbo', 1, 3);
} {lim}

do_execsql_test substring-3-args-exceed-length {
  SELECT substring('limbo', 1, 10);
} {limbo}

do_execsql_test substring-3-args-start-exceed-length {
  SELECT substring('limbo', 10, 3);
} {}

do_execsql_test substring-2-args {
  SELECT substring('limbo', 3);
} {mbo}

do_execsql_test substring-2-args-exceed-length {
  SELECT substring('limbo', 10);
} {}

do_execsql_test typeof-null {
  SELECT typeof(null);
} {null}

do_execsql_test typeof-null-case {
  SELECT typeof(nuLL);
} {null}

do_execsql_test typeof-text {
  SELECT typeof('hello');
} {text}

do_execsql_test typeof-text-empty {
  SELECT typeof('');
} {text}

do_execsql_test typeof-integer {
  SELECT typeof(123);
} {integer}

do_execsql_test typeof-real {
  SELECT typeof(1.0);
} {real}

do_execsql_test typeof-blob {
  SELECT typeof(x'61');
} {blob}

do_execsql_test typeof-blob-empty {
  SELECT typeof(x'');
} {blob}

do_execsql_test typeof-sum-integer {
  SELECT typeof(sum(age)) from users;
} {integer}

do_execsql_test typeof-sum-real {
  SELECT typeof(sum(price)) from products;
} {real}

do_execsql_test typeof-group_concat {
  SELECT typeof(group_concat(name)) from products;
} {text}

do_execsql_test unicode-a {
  SELECT unicode('a');
} {97}

do_execsql_test unicode-emoji {
  SELECT unicode('😊');
} {128522}

do_execsql_test unicode-empty {
  SELECT unicode('');
} {}

do_execsql_test unicode-number {
  SELECT unicode(23);
} {50}

do_execsql_test unicode-float {
  SELECT unicode(23.45);
} {50}

do_execsql_test unicode-null {
  SELECT unicode(NULL);
} {}

do_execsql_test quote-string-embedded-nul {
  SELECT quote(concat('abc', char(0), 'def'))
} {'abc'}

do_execsql_test quote-string {
  SELECT quote('limbo')
} {'limbo'}

do_execsql_test quote-null {
  SELECT quote(null)
} {NULL}

do_execsql_test quote-integer {
  SELECT quote(123)
} {123}

do_execsql_test sign-positive-integer {
  SELECT sign(42);
} {1}

do_execsql_test sign-negative-integer {
  SELECT sign(-42);
} {-1}

do_execsql_test sign-zero {
  SELECT sign(0);
} {0}

do_execsql_test sign-positive-float {
  SELECT sign(42.0);
} {1}

do_execsql_test sign-negative-float {
  SELECT sign(-42.0);
} {-1}

do_execsql_test sign-zero-float {
  SELECT sign(0.0);
} {0}

do_execsql_test sign-text-positive-integer {
  SELECT sign('42');
} {1}

do_execsql_test sign-text-negative-integer {
  SELECT sign('-42');
} {-1}

do_execsql_test sign-text-zero {
  SELECT sign('0');
} {0}

do_execsql_test sign-text-non-numeric {
  SELECT sign('abc');
} {}

do_execsql_test sign-null {
  SELECT sign(NULL);
} {}

do_execsql_test randomblob-int-2 {
  SELECT length(randomblob(2));
} {2}

do_execsql_test randomblob-int-0 {
  SELECT length(randomblob(0));
} {1}

do_execsql_test randomblob-int-negative {
  SELECT length(randomblob(-2));
} {1}

do_execsql_test randomblob-str-2 {
  SELECT length(randomblob('2'));
} {2}

do_execsql_test zeroblob-int-0 {
  SELECT zeroblob(0) = x'';
} {1}

do_execsql_test zeroblob-int-1 {
  SELECT zeroblob(1) = x'00';
} {1}

do_execsql_test zeroblob-str-3 {
  SELECT zeroblob('3') = x'000000';
} {1}

do_execsql_test zeroblob-str-a {
  SELECT zeroblob('a') = x'';
} {1}

do_execsql_test zeroblob-blob {
  SELECT zeroblob(x'01') = x'';
} {1}

# CAST tests - INTEGER affinity
do_execsql_test cast-text-to-integer {
  SELECT CAST('123' AS INTEGER);
} {123}

do_execsql_test cast-text-with-spaces-to-integer {
  SELECT CAST('  123  ' AS INTEGER);
} {123}

do_execsql_test cast-invalid-text-to-integer {
  SELECT CAST('abc' AS INTEGER);
} {0}

do_execsql_test cast-text-prefix-to-integer {
  SELECT CAST('123abc' AS INTEGER);
} {123}

do_execsql_test cast-float-to-integer {
  SELECT CAST(123.45 AS INTEGER);
} {123}

do_execsql_test cast-large-float-to-integer {
  SELECT CAST(9223372036854775808.0 AS INTEGER);
} {9223372036854775807}

do_execsql_test cast-small-float-to-integer {
  SELECT CAST(-9223372036854775809.0 AS INTEGER);
} {-9223372036854775808}

do_execsql_test cast-text-exp-to-integer {
  SELECT CAST('123e+5' AS INTEGER);
} {123}

# CAST tests - REAL affinity
do_execsql_test cast-text-to-real {
  SELECT CAST('123.45' AS REAL);
} {123.45}

do_execsql_test cast-text-with-spaces-to-real {
  SELECT CAST('  123.45  ' AS REAL);
} {123.45}

do_execsql_test cast-invalid-text-to-real {
  SELECT CAST('abc' AS REAL);
} {0.0}

do_execsql_test cast-text-prefix-to-real {
  SELECT CAST('123.45abc' AS REAL);
} {123.45}

do_execsql_test cast-integer-to-real {
  SELECT CAST(123 AS REAL);
} {123.0}

# CAST tests - TEXT affinity
do_execsql_test cast-integer-to-text {
  SELECT CAST(123 AS TEXT);
} {123}

do_execsql_test cast-real-to-text {
  SELECT CAST(123.45 AS TEXT);
} {123.45}

do_execsql_test cast-blob-to-text {
  SELECT CAST(x'68656C6C6F' AS TEXT);
} {hello}

# CAST tests - BLOB affinity
# not really a great test since it gets converted back to string for the output anyway...
do_execsql_test cast-text-to-blob {
  SELECT hex(CAST('hello' AS BLOB));
} {68656C6C6F}

do_execsql_test cast-integer-to-blob {
  SELECT hex(CAST(123 AS BLOB));
} {313233}

# CAST tests - NUMERIC affinity
do_execsql_test cast-integer-text-to-numeric {
  SELECT typeof(CAST('123' AS NUMERIC)), CAST('123' AS NUMERIC);
} {integer|123}

do_execsql_test cast-float-text-to-numeric {
  SELECT typeof(CAST('123.45' AS NUMERIC)), CAST('123.45' AS NUMERIC);
} {real|123.45}

do_execsql_test cast-small-float-to-numeric {
  SELECT typeof(CAST('1.23' AS NUMERIC)), CAST('1.23' AS NUMERIC);
} {real|1.23}

do_execsql_test_regex sqlite-version-should-return-valid-output {
  SELECT sqlite_version();
} {\d+\.\d+\.\d+}

# TODO COMPAT: sqlite returns 9.22337203685478e+18, do we care...?
# do_execsql_test cast-large-text-to-numeric {
#   SELECT typeof(CAST('9223372036854775808' AS NUMERIC)), CAST('9223372036854775808' AS NUMERIC);
# } {real|9.223372036854776e18}

do_execsql_test cast-null-to-any {
  SELECT CAST(NULL AS INTEGER), CAST(NULL AS TEXT), CAST(NULL AS BLOB), CAST(NULL AS REAL), CAST(NULL AS NUMERIC);
} {||||}

# CAST smoke test in where clause
do_execsql_test cast-in-where {
  select age from users where age = cast('45' as integer) limit 1;
} {45}

# TODO: sqlite seems not enable soundex() by default unless build it with SQLITE_SOUNDEX enabled.
# do_execsql_test soundex-text {
#  select soundex('Pfister'), soundex('husobee'), soundex('Tymczak'), soundex('Ashcraft'), soundex('Robert'), soundex('Rupert'), soundex('Rubin'), soundex('Kant'), soundex('Knuth'), soundex('x'), soundex('');
# } {P236|H210|T522|A261|R163|R163|R150|K530|K530|X000|0000}