diff options
author | qrort <qrort@yandex-team.com> | 2023-04-05 16:59:33 +0300 |
---|---|---|
committer | qrort <qrort@yandex-team.com> | 2023-04-05 16:59:33 +0300 |
commit | 0cd25c0602c477754d77ad095746c3e141179dde (patch) | |
tree | 683c6396c7700b8832b555fb93d57a805f46d243 | |
parent | 5c2106832696ae404f02462aac3a0a0ac735e728 (diff) | |
download | ydb-0cd25c0602c477754d77ad095746c3e141179dde.tar.gz |
pg regression tests over YDB
31 files changed, 3447 insertions, 0 deletions
diff --git a/ydb/tests/functional/postgresql/cases/boolean.out b/ydb/tests/functional/postgresql/cases/boolean.out new file mode 100644 index 00000000000..df81fc739dc --- /dev/null +++ b/ydb/tests/functional/postgresql/cases/boolean.out @@ -0,0 +1,223 @@ +-- +-- BOOLEAN +-- +-- +-- sanity check - if this fails go insane! +-- +SELECT 1 AS one; + one +----- + 1 +(1 row) + +-- ******************testing built-in type bool******************** +-- check bool input syntax +SELECT true AS true; + true +------ + t +(1 row) + +SELECT false AS false; + false +------- + f +(1 row) + +SELECT bool 't' AS true; + true +------ + t +(1 row) + +SELECT bool ' f ' AS false; + false +------- + f +(1 row) + +SELECT bool 'true' AS true; + true +------ + t +(1 row) + +SELECT bool 'test' AS error; +ERROR: invalid input syntax for type boolean: "test" +LINE 1: SELECT bool 'test' AS error; + ^ +SELECT bool 'false' AS false; + false +------- + f +(1 row) + +SELECT bool 'foo' AS error; +ERROR: invalid input syntax for type boolean: "foo" +LINE 1: SELECT bool 'foo' AS error; + ^ +SELECT bool 'y' AS true; + true +------ + t +(1 row) + +SELECT bool 'yes' AS true; + true +------ + t +(1 row) + +SELECT bool 'yeah' AS error; +ERROR: invalid input syntax for type boolean: "yeah" +LINE 1: SELECT bool 'yeah' AS error; + ^ +SELECT bool 'n' AS false; + false +------- + f +(1 row) + +SELECT bool 'no' AS false; + false +------- + f +(1 row) + +SELECT bool 'nay' AS error; +ERROR: invalid input syntax for type boolean: "nay" +LINE 1: SELECT bool 'nay' AS error; + ^ +SELECT bool 'on' AS true; + true +------ + t +(1 row) + +SELECT bool 'off' AS false; + false +------- + f +(1 row) + +SELECT bool 'of' AS false; + false +------- + f +(1 row) + +SELECT bool 'o' AS error; +ERROR: invalid input syntax for type boolean: "o" +LINE 1: SELECT bool 'o' AS error; + ^ +SELECT bool 'on_' AS error; +ERROR: invalid input syntax for type boolean: "on_" +LINE 1: SELECT bool 'on_' AS error; + ^ +SELECT bool 'off_' AS error; +ERROR: invalid input syntax for type boolean: "off_" +LINE 1: SELECT bool 'off_' AS error; + ^ +SELECT bool '1' AS true; + true +------ + t +(1 row) + +SELECT bool '11' AS error; +ERROR: invalid input syntax for type boolean: "11" +LINE 1: SELECT bool '11' AS error; + ^ +SELECT bool '0' AS false; + false +------- + f +(1 row) + +SELECT bool '000' AS error; +ERROR: invalid input syntax for type boolean: "000" +LINE 1: SELECT bool '000' AS error; + ^ +SELECT bool '' AS error; +ERROR: invalid input syntax for type boolean: "" +LINE 1: SELECT bool '' AS error; + ^ +-- and, or, not in qualifications +SELECT bool 't' or bool 'f' AS true; + true +------ + t +(1 row) + +SELECT bool 't' and bool 'f' AS false; + false +------- + f +(1 row) + +SELECT not bool 'f' AS true; + true +------ + t +(1 row) + +SELECT bool 't' = bool 'f' AS false; + false +------- + f +(1 row) + +SELECT bool 't' <> bool 'f' AS true; + true +------ + t +(1 row) + +SELECT bool 't' > bool 'f' AS true; + true +------ + t +(1 row) + +SELECT bool 't' >= bool 'f' AS true; + true +------ + t +(1 row) + +SELECT bool 'f' < bool 't' AS true; + true +------ + t +(1 row) + +SELECT bool 'f' <= bool 't' AS true; + true +------ + t +(1 row) + +-- explicit casts to/from text +SELECT 'TrUe'::text::boolean AS true, 'fAlse'::text::boolean AS false; + true | false +------+------- + t | f +(1 row) + +SELECT ' true '::text::boolean AS true, + ' FALSE'::text::boolean AS false; + true | false +------+------- + t | f +(1 row) + +SELECT true::boolean::text AS true, false::boolean::text AS false; + true | false +------+------- + true | false +(1 row) + +SELECT ' tru e '::text::boolean AS invalid; -- error +ERROR: invalid input syntax for type boolean: " tru e " +SELECT ''::text::boolean AS invalid; -- error +ERROR: invalid input syntax for type boolean: "" diff --git a/ydb/tests/functional/postgresql/cases/boolean.sql b/ydb/tests/functional/postgresql/cases/boolean.sql new file mode 100644 index 00000000000..3c6715ceea4 --- /dev/null +++ b/ydb/tests/functional/postgresql/cases/boolean.sql @@ -0,0 +1,92 @@ +-- +-- BOOLEAN +-- + +-- +-- sanity check - if this fails go insane! +-- +SELECT 1 AS one; + + +-- ******************testing built-in type bool******************** + +-- check bool input syntax + +SELECT true AS true; + +SELECT false AS false; + +SELECT bool 't' AS true; + +SELECT bool ' f ' AS false; + +SELECT bool 'true' AS true; + +SELECT bool 'test' AS error; + +SELECT bool 'false' AS false; + +SELECT bool 'foo' AS error; + +SELECT bool 'y' AS true; + +SELECT bool 'yes' AS true; + +SELECT bool 'yeah' AS error; + +SELECT bool 'n' AS false; + +SELECT bool 'no' AS false; + +SELECT bool 'nay' AS error; + +SELECT bool 'on' AS true; + +SELECT bool 'off' AS false; + +SELECT bool 'of' AS false; + +SELECT bool 'o' AS error; + +SELECT bool 'on_' AS error; + +SELECT bool 'off_' AS error; + +SELECT bool '1' AS true; + +SELECT bool '11' AS error; + +SELECT bool '0' AS false; + +SELECT bool '000' AS error; + +SELECT bool '' AS error; + +-- and, or, not in qualifications + +SELECT bool 't' or bool 'f' AS true; + +SELECT bool 't' and bool 'f' AS false; + +SELECT not bool 'f' AS true; + +SELECT bool 't' = bool 'f' AS false; + +SELECT bool 't' <> bool 'f' AS true; + +SELECT bool 't' > bool 'f' AS true; + +SELECT bool 't' >= bool 'f' AS true; + +SELECT bool 'f' < bool 't' AS true; + +SELECT bool 'f' <= bool 't' AS true; + +-- explicit casts to/from text +SELECT 'TrUe'::text::boolean AS true, 'fAlse'::text::boolean AS false; +SELECT ' true '::text::boolean AS true, + ' FALSE'::text::boolean AS false; +SELECT true::boolean::text AS true, false::boolean::text AS false; + +SELECT ' tru e '::text::boolean AS invalid; -- error +SELECT ''::text::boolean AS invalid; -- error diff --git a/ydb/tests/functional/postgresql/cases/char.out b/ydb/tests/functional/postgresql/cases/char.out new file mode 100644 index 00000000000..f41eae1b0d4 --- /dev/null +++ b/ydb/tests/functional/postgresql/cases/char.out @@ -0,0 +1,11 @@ +-- +-- CHAR +-- +-- fixed-length by value +-- internally passed by value if <= 4 bytes in storage +SELECT char 'c' = char 'c' AS true; + true +------ + t +(1 row) + diff --git a/ydb/tests/functional/postgresql/cases/char.sql b/ydb/tests/functional/postgresql/cases/char.sql new file mode 100644 index 00000000000..29cb5fc081c --- /dev/null +++ b/ydb/tests/functional/postgresql/cases/char.sql @@ -0,0 +1,8 @@ +-- +-- CHAR +-- + +-- fixed-length by value +-- internally passed by value if <= 4 bytes in storage + +SELECT char 'c' = char 'c' AS true; diff --git a/ydb/tests/functional/postgresql/cases/char_1.out b/ydb/tests/functional/postgresql/cases/char_1.out new file mode 100644 index 00000000000..f41eae1b0d4 --- /dev/null +++ b/ydb/tests/functional/postgresql/cases/char_1.out @@ -0,0 +1,11 @@ +-- +-- CHAR +-- +-- fixed-length by value +-- internally passed by value if <= 4 bytes in storage +SELECT char 'c' = char 'c' AS true; + true +------ + t +(1 row) + diff --git a/ydb/tests/functional/postgresql/cases/char_2.out b/ydb/tests/functional/postgresql/cases/char_2.out new file mode 100644 index 00000000000..f41eae1b0d4 --- /dev/null +++ b/ydb/tests/functional/postgresql/cases/char_2.out @@ -0,0 +1,11 @@ +-- +-- CHAR +-- +-- fixed-length by value +-- internally passed by value if <= 4 bytes in storage +SELECT char 'c' = char 'c' AS true; + true +------ + t +(1 row) + diff --git a/ydb/tests/functional/postgresql/cases/comments.out b/ydb/tests/functional/postgresql/cases/comments.out new file mode 100644 index 00000000000..aa2f84f2f0d --- /dev/null +++ b/ydb/tests/functional/postgresql/cases/comments.out @@ -0,0 +1,63 @@ +-- +-- COMMENTS +-- +SELECT 'trailing' AS first; -- trailing single line + first +---------- + trailing +(1 row) + +SELECT /* embedded single line */ 'embedded' AS second; + second +---------- + embedded +(1 row) + +SELECT /* both embedded and trailing single line */ 'both' AS third; -- trailing single line + third +------- + both +(1 row) + +SELECT 'before multi-line' AS fourth; + fourth +------------------- + before multi-line +(1 row) + +/* This is an example of SQL which should not execute: + * select 'multi-line'; + */ +SELECT 'after multi-line' AS fifth; + fifth +------------------ + after multi-line +(1 row) + +-- +-- Nested comments +-- +/* +SELECT 'trailing' as x1; -- inside block comment +*/ +/* This block comment surrounds a query which itself has a block comment... +SELECT /* embedded single line */ 'embedded' AS x2; +*/ +SELECT -- continued after the following block comments... +/* Deeply nested comment. + This includes a single apostrophe to make sure we aren't decoding this part as a string. +SELECT 'deep nest' AS n1; +/* Second level of nesting... +SELECT 'deeper nest' as n2; +/* Third level of nesting... +SELECT 'deepest nest' as n3; +*/ +Hoo boy. Still two deep... +*/ +Now just one deep... +*/ +'deeply nested example' AS sixth; + sixth +----------------------- + deeply nested example +(1 row) diff --git a/ydb/tests/functional/postgresql/cases/comments.sql b/ydb/tests/functional/postgresql/cases/comments.sql new file mode 100644 index 00000000000..638533ba88b --- /dev/null +++ b/ydb/tests/functional/postgresql/cases/comments.sql @@ -0,0 +1,40 @@ +-- +-- COMMENTS +-- + +SELECT 'trailing' AS first; -- trailing single line +SELECT /* embedded single line */ 'embedded' AS second; +SELECT /* both embedded and trailing single line */ 'both' AS third; -- trailing single line + +SELECT 'before multi-line' AS fourth; +/* This is an example of SQL which should not execute: + * select 'multi-line'; + */ +SELECT 'after multi-line' AS fifth; + +-- +-- Nested comments +-- + +/* +SELECT 'trailing' as x1; -- inside block comment +*/ + +/* This block comment surrounds a query which itself has a block comment... +SELECT /* embedded single line */ 'embedded' AS x2; +*/ + +SELECT -- continued after the following block comments... +/* Deeply nested comment. + This includes a single apostrophe to make sure we aren't decoding this part as a string. +SELECT 'deep nest' AS n1; +/* Second level of nesting... +SELECT 'deeper nest' as n2; +/* Third level of nesting... +SELECT 'deepest nest' as n3; +*/ +Hoo boy. Still two deep... +*/ +Now just one deep... +*/ +'deeply nested example' AS sixth; diff --git a/ydb/tests/functional/postgresql/cases/float4.out b/ydb/tests/functional/postgresql/cases/float4.out new file mode 100644 index 00000000000..3eb5a17e5d9 --- /dev/null +++ b/ydb/tests/functional/postgresql/cases/float4.out @@ -0,0 +1,65 @@ +-- +-- FLOAT4 +-- +-- bad special inputs +SELECT 'N A N'::float4; +ERROR: invalid input syntax for type real: "N A N" +LINE 1: SELECT 'N A N'::float4; + ^ +SELECT 'NaN x'::float4; +ERROR: invalid input syntax for type real: "NaN x" +LINE 1: SELECT 'NaN x'::float4; + ^ +SELECT ' INFINITY x'::float4; +ERROR: invalid input syntax for type real: " INFINITY x" +LINE 1: SELECT ' INFINITY x'::float4; + ^ +-- test edge-case coercions to integer +SELECT '32767.4'::float4::int2; + int2 +------- + 32767 +(1 row) + +SELECT '32767.6'::float4::int2; +ERROR: smallint out of range +SELECT '-32768.4'::float4::int2; + int2 +-------- + -32768 +(1 row) + +SELECT '-32768.6'::float4::int2; +ERROR: smallint out of range +SELECT '2147483520'::float4::int4; + int4 +------------ + 2147483520 +(1 row) + +SELECT '2147483647'::float4::int4; +ERROR: integer out of range +SELECT '-2147483648.5'::float4::int4; + int4 +------------- + -2147483648 +(1 row) + +SELECT '-2147483900'::float4::int4; +ERROR: integer out of range +SELECT '9223369837831520256'::float4::int8; + int8 +--------------------- + 9223369837831520256 +(1 row) + +SELECT '9223372036854775807'::float4::int8; +ERROR: bigint out of range +SELECT '-9223372036854775808.5'::float4::int8; + int8 +---------------------- + -9223372036854775808 +(1 row) + +SELECT '-9223380000000000000'::float4::int8; +ERROR: bigint out of range diff --git a/ydb/tests/functional/postgresql/cases/float4.sql b/ydb/tests/functional/postgresql/cases/float4.sql new file mode 100644 index 00000000000..917003297d5 --- /dev/null +++ b/ydb/tests/functional/postgresql/cases/float4.sql @@ -0,0 +1,21 @@ +-- +-- FLOAT4 +-- + +-- bad special inputs +SELECT 'N A N'::float4; +SELECT 'NaN x'::float4; +SELECT ' INFINITY x'::float4; +-- test edge-case coercions to integer +SELECT '32767.4'::float4::int2; +SELECT '32767.6'::float4::int2; +SELECT '-32768.4'::float4::int2; +SELECT '-32768.6'::float4::int2; +SELECT '2147483520'::float4::int4; +SELECT '2147483647'::float4::int4; +SELECT '-2147483648.5'::float4::int4; +SELECT '-2147483900'::float4::int4; +SELECT '9223369837831520256'::float4::int8; +SELECT '9223372036854775807'::float4::int8; +SELECT '-9223372036854775808.5'::float4::int8; +SELECT '-9223380000000000000'::float4::int8; diff --git a/ydb/tests/functional/postgresql/cases/float8.out b/ydb/tests/functional/postgresql/cases/float8.out new file mode 100644 index 00000000000..8b4890149f7 --- /dev/null +++ b/ydb/tests/functional/postgresql/cases/float8.out @@ -0,0 +1,234 @@ +-- +-- FLOAT8 +-- +-- test for underflow and overflow handling +SELECT '10e400'::float8; +ERROR: "10e400" is out of range for type double precision +LINE 1: SELECT '10e400'::float8; + ^ +SELECT '-10e400'::float8; +ERROR: "-10e400" is out of range for type double precision +LINE 1: SELECT '-10e400'::float8; + ^ +SELECT '10e-400'::float8; +ERROR: "10e-400" is out of range for type double precision +LINE 1: SELECT '10e-400'::float8; + ^ +SELECT '-10e-400'::float8; +ERROR: "-10e-400" is out of range for type double precision +LINE 1: SELECT '-10e-400'::float8; + ^ +-- bad special inputs +SELECT 'N A N'::float8; +ERROR: invalid input syntax for type double precision: "N A N" +LINE 1: SELECT 'N A N'::float8; + ^ +SELECT 'NaN x'::float8; +ERROR: invalid input syntax for type double precision: "NaN x" +LINE 1: SELECT 'NaN x'::float8; + ^ +SELECT ' INFINITY x'::float8; +ERROR: invalid input syntax for type double precision: " INFINITY x" +LINE 1: SELECT ' INFINITY x'::float8; + ^ +SELECT '42'::float8 / 'Infinity'::float8; + ?column? +---------- + 0 +(1 row) + +-- square root +SELECT sqrt(float8 '64') AS eight; + eight +------- + 8 +(1 row) + +SELECT |/ float8 '64' AS eight; + eight +------- + 8 +(1 row) + +-- power +SELECT power(float8 '144', float8 '0.5'); + power +------- + 12 +(1 row) + +SELECT power(float8 '1', float8 'NaN'); + power +------- + 1 +(1 row) + +SELECT power(float8 'NaN', float8 '0'); + power +------- + 1 +(1 row) + +SELECT power(float8 'inf', float8 '0'); + power +------- + 1 +(1 row) + +SELECT power(float8 '-inf', float8 '0'); + power +------- + 1 +(1 row) + +SELECT power(float8 '0', float8 'inf'); + power +------- + 0 +(1 row) + +SELECT power(float8 '0', float8 '-inf'); +ERROR: zero raised to a negative power is undefined +SELECT power(float8 '1', float8 'inf'); + power +------- + 1 +(1 row) + +SELECT power(float8 '1', float8 '-inf'); + power +------- + 1 +(1 row) + +SELECT power(float8 '-1', float8 'inf'); + power +------- + 1 +(1 row) + +SELECT power(float8 '-1', float8 '-inf'); + power +------- + 1 +(1 row) + +SELECT power(float8 '0.1', float8 'inf'); + power +------- + 0 +(1 row) + +SELECT power(float8 '-0.1', float8 'inf'); + power +------- + 0 +(1 row) + +SELECT power(float8 '1.1', float8 '-inf'); + power +------- + 0 +(1 row) + +SELECT power(float8 '-1.1', float8 '-inf'); + power +------- + 0 +(1 row) + +SELECT power(float8 'inf', float8 '-2'); + power +------- + 0 +(1 row) + +SELECT power(float8 'inf', float8 '-inf'); + power +------- + 0 +(1 row) + +SELECT power(float8 '-inf', float8 '-3'); + power +------- + -0 +(1 row) + +SELECT power(float8 '-inf', float8 '3.5'); +ERROR: a negative number raised to a non-integer power yields a complex result +SELECT power(float8 '-inf', float8 '-inf'); + power +------- + 0 +(1 row) + +SELECT tanh(float8 'infinity'); + tanh +------ + 1 +(1 row) + +SELECT tanh(float8 '-infinity'); + tanh +------ + -1 +(1 row) + +-- acosh(Inf) should be Inf, but some mingw versions produce NaN, so skip test +-- SELECT acosh(float8 'infinity'); +SELECT acosh(float8 '-infinity'); +ERROR: input is out of range +SELECT atanh(float8 'infinity'); +ERROR: input is out of range +SELECT atanh(float8 '-infinity'); +ERROR: input is out of range +-- test edge-case coercions to integer +SELECT '32767.4'::float8::int2; + int2 +------- + 32767 +(1 row) + +SELECT '32767.6'::float8::int2; +ERROR: smallint out of range +SELECT '-32768.4'::float8::int2; + int2 +-------- + -32768 +(1 row) + +SELECT '-32768.6'::float8::int2; +ERROR: smallint out of range +SELECT '2147483647.4'::float8::int4; + int4 +------------ + 2147483647 +(1 row) + +SELECT '2147483647.6'::float8::int4; +ERROR: integer out of range +SELECT '-2147483648.4'::float8::int4; + int4 +------------- + -2147483648 +(1 row) + +SELECT '-2147483648.6'::float8::int4; +ERROR: integer out of range +SELECT '9223372036854773760'::float8::int8; + int8 +--------------------- + 9223372036854773760 +(1 row) + +SELECT '9223372036854775807'::float8::int8; +ERROR: bigint out of range +SELECT '-9223372036854775808.5'::float8::int8; + int8 +---------------------- + -9223372036854775808 +(1 row) + +SELECT '-9223372036854780000'::float8::int8; +ERROR: bigint out of range diff --git a/ydb/tests/functional/postgresql/cases/float8.sql b/ydb/tests/functional/postgresql/cases/float8.sql new file mode 100644 index 00000000000..bc67079b35c --- /dev/null +++ b/ydb/tests/functional/postgresql/cases/float8.sql @@ -0,0 +1,63 @@ +-- +-- FLOAT8 +-- + +-- test for underflow and overflow handling +SELECT '10e400'::float8; +SELECT '-10e400'::float8; +SELECT '10e-400'::float8; +SELECT '-10e-400'::float8; +-- bad special inputs +SELECT 'N A N'::float8; +SELECT 'NaN x'::float8; +SELECT ' INFINITY x'::float8; + +SELECT '42'::float8 / 'Infinity'::float8; + +-- square root +SELECT sqrt(float8 '64') AS eight; + +SELECT |/ float8 '64' AS eight; + +-- power +SELECT power(float8 '144', float8 '0.5'); +SELECT power(float8 '1', float8 'NaN'); +SELECT power(float8 'NaN', float8 '0'); +SELECT power(float8 'inf', float8 '0'); +SELECT power(float8 '-inf', float8 '0'); +SELECT power(float8 '0', float8 'inf'); +SELECT power(float8 '0', float8 '-inf'); +SELECT power(float8 '1', float8 'inf'); +SELECT power(float8 '1', float8 '-inf'); +SELECT power(float8 '-1', float8 'inf'); +SELECT power(float8 '-1', float8 '-inf'); +SELECT power(float8 '0.1', float8 'inf'); +SELECT power(float8 '-0.1', float8 'inf'); +SELECT power(float8 '1.1', float8 '-inf'); +SELECT power(float8 '-1.1', float8 '-inf'); +SELECT power(float8 'inf', float8 '-2'); +SELECT power(float8 'inf', float8 '-inf'); + +SELECT power(float8 '-inf', float8 '-3'); +SELECT power(float8 '-inf', float8 '3.5'); +SELECT power(float8 '-inf', float8 '-inf'); +SELECT tanh(float8 'infinity'); +SELECT tanh(float8 '-infinity'); +-- acosh(Inf) should be Inf, but some mingw versions produce NaN, so skip test +-- SELECT acosh(float8 'infinity'); +SELECT acosh(float8 '-infinity'); +SELECT atanh(float8 'infinity'); +SELECT atanh(float8 '-infinity'); +-- test edge-case coercions to integer +SELECT '32767.4'::float8::int2; +SELECT '32767.6'::float8::int2; +SELECT '-32768.4'::float8::int2; +SELECT '-32768.6'::float8::int2; +SELECT '2147483647.4'::float8::int4; +SELECT '2147483647.6'::float8::int4; +SELECT '-2147483648.4'::float8::int4; +SELECT '-2147483648.6'::float8::int4; +SELECT '9223372036854773760'::float8::int8; +SELECT '9223372036854775807'::float8::int8; +SELECT '-9223372036854775808.5'::float8::int8; +SELECT '-9223372036854780000'::float8::int8; diff --git a/ydb/tests/functional/postgresql/cases/horology.out b/ydb/tests/functional/postgresql/cases/horology.out new file mode 100644 index 00000000000..bdb40e05d81 --- /dev/null +++ b/ydb/tests/functional/postgresql/cases/horology.out @@ -0,0 +1,331 @@ +-- +-- HOROLOGY +-- +-- should fail in mdy mode: +SELECT timestamp with time zone '27/12/2001 04:05:06.789-08'; +ERROR: date/time field value out of range: "27/12/2001 04:05:06.789-08" +LINE 1: SELECT timestamp with time zone '27/12/2001 04:05:06.789-08'... + ^ +HINT: Perhaps you need a different "datestyle" setting. +-- Shorthand values +-- Not directly usable for regression testing since these are not constants. +-- So, just try to test parser and hope for the best - thomas 97/04/26 +SELECT (timestamp without time zone 'today' = (timestamp without time zone 'yesterday' + interval '1 day')) as "True"; + True +------ + t +(1 row) + +SELECT (timestamp without time zone 'today' = (timestamp without time zone 'tomorrow' - interval '1 day')) as "True"; + True +------ + t +(1 row) + +SELECT (timestamp without time zone 'today 10:30' = (timestamp without time zone 'yesterday' + interval '1 day 10 hr 30 min')) as "True"; + True +------ + t +(1 row) + +SELECT (timestamp without time zone '10:30 today' = (timestamp without time zone 'yesterday' + interval '1 day 10 hr 30 min')) as "True"; + True +------ + t +(1 row) + +SELECT (timestamp without time zone 'tomorrow' = (timestamp without time zone 'yesterday' + interval '2 days')) as "True"; + True +------ + t +(1 row) + +SELECT (timestamp without time zone 'tomorrow 16:00:00' = (timestamp without time zone 'today' + interval '1 day 16 hours')) as "True"; + True +------ + t +(1 row) + +SELECT (timestamp without time zone '16:00:00 tomorrow' = (timestamp without time zone 'today' + interval '1 day 16 hours')) as "True"; + True +------ + t +(1 row) + +SELECT (timestamp without time zone 'yesterday 12:34:56' = (timestamp without time zone 'tomorrow' - interval '2 days - 12:34:56')) as "True"; + True +------ + t +(1 row) + +SELECT (timestamp without time zone '12:34:56 yesterday' = (timestamp without time zone 'tomorrow' - interval '2 days - 12:34:56')) as "True"; + True +------ + t +(1 row) + +SELECT (timestamp with time zone 'today' = (timestamp with time zone 'yesterday' + interval '1 day')) as "True"; + True +------ + t +(1 row) + +SELECT (timestamp with time zone 'today' = (timestamp with time zone 'tomorrow' - interval '1 day')) as "True"; + True +------ + t +(1 row) + +SELECT (timestamp with time zone 'tomorrow' = (timestamp with time zone 'yesterday' + interval '2 days')) as "True"; + True +------ + t +(1 row) + +-- +-- time, interval arithmetic +-- +SELECT CAST(interval '02:03' AS time) AS "02:03:00"; + 02:03:00 +---------- + 02:03:00 +(1 row) + +SELECT time '01:30' + interval '02:01' AS "03:31:00"; + 03:31:00 +---------- + 03:31:00 +(1 row) + +SELECT time '01:30' - interval '02:01' AS "23:29:00"; + 23:29:00 +---------- + 23:29:00 +(1 row) + +SELECT time '02:30' + interval '36:01' AS "14:31:00"; + 14:31:00 +---------- + 14:31:00 +(1 row) + +SELECT time '03:30' + interval '1 month 04:01' AS "07:31:00"; + 07:31:00 +---------- + 07:31:00 +(1 row) + +SELECT time with time zone '01:30-08' - interval '02:01' AS "23:29:00-08"; + 23:29:00-08 +------------- + 23:29:00-08 +(1 row) + +SELECT time with time zone '02:30-08' + interval '36:01' AS "14:31:00-08"; + 14:31:00-08 +------------- + 14:31:00-08 +(1 row) + +-- These two tests cannot be used because they default to current timezone, +-- which may be either -08 or -07 depending on the time of year. +-- SELECT time with time zone '01:30' + interval '02:01' AS "03:31:00-08"; +-- SELECT time with time zone '03:30' + interval '1 month 04:01' AS "07:31:00-08"; +-- Try the following two tests instead, as a poor substitute +SELECT CAST(CAST(date 'today' + time with time zone '05:30' + + interval '02:01' AS time with time zone) AS time) AS "07:31:00"; + 07:31:00 +---------- + 07:31:00 +(1 row) + +SELECT CAST(cast(date 'today' + time with time zone '03:30' + + interval '1 month 04:01' as timestamp without time zone) AS time) AS "07:31:00"; + 07:31:00 +---------- + 07:31:00 +(1 row) + +-- SQL9x OVERLAPS operator +-- test with time zone +SELECT (timestamp with time zone '2000-11-27', timestamp with time zone '2000-11-28') + OVERLAPS (timestamp with time zone '2000-11-27 12:00', timestamp with time zone '2000-11-30') AS "True"; + True +------ + t +(1 row) + +SELECT (timestamp with time zone '2000-11-26', timestamp with time zone '2000-11-27') + OVERLAPS (timestamp with time zone '2000-11-27 12:00', timestamp with time zone '2000-11-30') AS "False"; + False +------- + f +(1 row) + +-- test without time zone +SELECT (timestamp without time zone '2000-11-27', timestamp without time zone '2000-11-28') + OVERLAPS (timestamp without time zone '2000-11-27 12:00', timestamp without time zone '2000-11-30') AS "True"; + True +------ + t +(1 row) + +SELECT (timestamp without time zone '2000-11-26', timestamp without time zone '2000-11-27') + OVERLAPS (timestamp without time zone '2000-11-27 12:00', timestamp without time zone '2000-11-30') AS "False"; + False +------- + f +(1 row) + +-- test time and interval +SELECT (time '00:00', time '01:00') + OVERLAPS (time '00:30', time '01:30') AS "True"; + True +------ + t +(1 row) + +-- +-- Comparisons between datetime types, especially overflow cases +--- +SELECT '2202020-10-05'::date::timestamp; -- fail +ERROR: date out of range for timestamp +SELECT '2202020-10-05'::date > '2020-10-05'::timestamp as t; + t +--- + t +(1 row) + +SELECT '2020-10-05'::timestamp > '2202020-10-05'::date as f; + f +--- + f +(1 row) + +SELECT '2202020-10-05'::date::timestamptz; -- fail +ERROR: date out of range for timestamp +SELECT '2202020-10-05'::date > '2020-10-05'::timestamptz as t; + t +--- + t +(1 row) + +SELECT '2020-10-05'::timestamptz > '2202020-10-05'::date as f; + f +--- + f +(1 row) + +SELECT '4714-11-24 BC'::date < '2020-10-05'::timestamptz as t; + t +--- + t +(1 row) + +SELECT '2020-10-05'::timestamptz >= '4714-11-24 BC'::date as t; + t +--- + t +(1 row) + +SELECT '4714-11-24 BC'::timestamp < '2020-10-05'::timestamptz as t; + t +--- + t +(1 row) + +SELECT '2020-10-05'::timestamptz >= '4714-11-24 BC'::timestamp as t; + t +--- + t +(1 row) + +-- +-- Formats +-- +-- +-- to_timestamp() +-- +SELECT to_timestamp('97/Feb/16', 'YYMonDD'); +ERROR: invalid value "/Feb/16" for "Mon" +DETAIL: The given value did not match any of the allowed values for this field. +SELECT to_timestamp('2011-12-18 11:38 PST', 'YYYY-MM-DD HH12:MI TZ'); -- NYI +ERROR: formatting field "TZ" is only supported in to_char +-- +-- Check handling of BC dates +-- +-- +-- Check handling of multiple spaces in format and/or input +-- +SELECT to_date('2011 x12 x18', 'YYYYxMMxDD'); +ERROR: invalid value "x1" for "MM" +DETAIL: Value must be an integer. +-- +-- Check errors for some incorrect usages of to_timestamp() and to_date() +-- +-- Mixture of date conventions (ISO week and Gregorian): +SELECT to_timestamp('2005527', 'YYYYIWID'); +ERROR: invalid combination of date conventions +HINT: Do not mix Gregorian and ISO week date conventions in a formatting template. +-- Insufficient characters in the source string: +SELECT to_timestamp('19971', 'YYYYMMDD'); +ERROR: source string too short for "MM" formatting field +DETAIL: Field requires 2 characters, but only 1 remain. +HINT: If your source string is not fixed-width, try using the "FM" modifier. +-- Insufficient digit characters for a single node: +SELECT to_timestamp('19971)24', 'YYYYMMDD'); +ERROR: invalid value "1)" for "MM" +DETAIL: Field requires 2 characters, but only 1 could be parsed. +HINT: If your source string is not fixed-width, try using the "FM" modifier. +-- We don't accept full-length day or month names if short form is specified: +SELECT to_timestamp('Friday 1-January-1999', 'DY DD MON YYYY'); +ERROR: invalid value "da" for "DD" +DETAIL: Value must be an integer. +SELECT to_timestamp('Fri 1-January-1999', 'DY DD MON YYYY'); +ERROR: invalid value "uary" for "YYYY" +DETAIL: Value must be an integer. +-- Value clobbering: +SELECT to_timestamp('1997-11-Jan-16', 'YYYY-MM-Mon-DD'); +ERROR: conflicting values for "Mon" field in formatting string +DETAIL: This value contradicts a previous setting for the same field type. +-- Non-numeric input: +SELECT to_timestamp('199711xy', 'YYYYMMDD'); +ERROR: invalid value "xy" for "DD" +DETAIL: Value must be an integer. +-- Input that doesn't fit in an int: +SELECT to_timestamp('10000000000', 'FMYYYY'); +ERROR: value for "YYYY" in source string is out of range +DETAIL: Value must be in the range -2147483648 to 2147483647. +-- Out-of-range and not-quite-out-of-range fields: +SELECT to_timestamp('2016-06-13 25:00:00', 'YYYY-MM-DD HH24:MI:SS'); +ERROR: date/time field value out of range: "2016-06-13 25:00:00" +SELECT to_timestamp('2016-06-13 15:60:00', 'YYYY-MM-DD HH24:MI:SS'); +ERROR: date/time field value out of range: "2016-06-13 15:60:00" +SELECT to_timestamp('2016-06-13 15:50:60', 'YYYY-MM-DD HH24:MI:SS'); +ERROR: date/time field value out of range: "2016-06-13 15:50:60" +SELECT to_timestamp('2016-06-13 15:50:55', 'YYYY-MM-DD HH:MI:SS'); +ERROR: hour "15" is invalid for the 12-hour clock +HINT: Use the 24-hour clock, or give an hour between 1 and 12. +SELECT to_timestamp('2016-13-01 15:50:55', 'YYYY-MM-DD HH24:MI:SS'); +ERROR: date/time field value out of range: "2016-13-01 15:50:55" +SELECT to_timestamp('2016-02-30 15:50:55', 'YYYY-MM-DD HH24:MI:SS'); +ERROR: date/time field value out of range: "2016-02-30 15:50:55" +SELECT to_timestamp('2015-02-29 15:50:55', 'YYYY-MM-DD HH24:MI:SS'); +ERROR: date/time field value out of range: "2015-02-29 15:50:55" +SELECT to_timestamp('2015-02-11 86400', 'YYYY-MM-DD SSSS'); +ERROR: date/time field value out of range: "2015-02-11 86400" +SELECT to_timestamp('2015-02-11 86400', 'YYYY-MM-DD SSSSS'); +ERROR: date/time field value out of range: "2015-02-11 86400" +SELECT to_date('2016-13-10', 'YYYY-MM-DD'); +ERROR: date/time field value out of range: "2016-13-10" +SELECT to_date('2016-02-30', 'YYYY-MM-DD'); +ERROR: date/time field value out of range: "2016-02-30" +SELECT to_date('2015-02-29', 'YYYY-MM-DD'); +ERROR: date/time field value out of range: "2015-02-29" +SELECT to_date('2015 366', 'YYYY DDD'); +ERROR: date/time field value out of range: "2015 366" +SELECT to_date('2016 367', 'YYYY DDD'); +ERROR: date/time field value out of range: "2016 367" +-- +-- Check behavior with SQL-style fixed-GMT-offset time zone (cf bug #8572) +-- diff --git a/ydb/tests/functional/postgresql/cases/horology.sql b/ydb/tests/functional/postgresql/cases/horology.sql new file mode 100644 index 00000000000..1fb5fd31fcd --- /dev/null +++ b/ydb/tests/functional/postgresql/cases/horology.sql @@ -0,0 +1,158 @@ +-- +-- HOROLOGY +-- +-- should fail in mdy mode: +SELECT timestamp with time zone '27/12/2001 04:05:06.789-08'; + +-- Shorthand values +-- Not directly usable for regression testing since these are not constants. +-- So, just try to test parser and hope for the best - thomas 97/04/26 +SELECT (timestamp without time zone 'today' = (timestamp without time zone 'yesterday' + interval '1 day')) as "True"; +SELECT (timestamp without time zone 'today' = (timestamp without time zone 'tomorrow' - interval '1 day')) as "True"; +SELECT (timestamp without time zone 'today 10:30' = (timestamp without time zone 'yesterday' + interval '1 day 10 hr 30 min')) as "True"; +SELECT (timestamp without time zone '10:30 today' = (timestamp without time zone 'yesterday' + interval '1 day 10 hr 30 min')) as "True"; +SELECT (timestamp without time zone 'tomorrow' = (timestamp without time zone 'yesterday' + interval '2 days')) as "True"; +SELECT (timestamp without time zone 'tomorrow 16:00:00' = (timestamp without time zone 'today' + interval '1 day 16 hours')) as "True"; +SELECT (timestamp without time zone '16:00:00 tomorrow' = (timestamp without time zone 'today' + interval '1 day 16 hours')) as "True"; +SELECT (timestamp without time zone 'yesterday 12:34:56' = (timestamp without time zone 'tomorrow' - interval '2 days - 12:34:56')) as "True"; +SELECT (timestamp without time zone '12:34:56 yesterday' = (timestamp without time zone 'tomorrow' - interval '2 days - 12:34:56')) as "True"; + +SELECT (timestamp with time zone 'today' = (timestamp with time zone 'yesterday' + interval '1 day')) as "True"; +SELECT (timestamp with time zone 'today' = (timestamp with time zone 'tomorrow' - interval '1 day')) as "True"; +SELECT (timestamp with time zone 'tomorrow' = (timestamp with time zone 'yesterday' + interval '2 days')) as "True"; + + +-- +-- time, interval arithmetic +-- + +SELECT CAST(interval '02:03' AS time) AS "02:03:00"; +SELECT time '01:30' + interval '02:01' AS "03:31:00"; +SELECT time '01:30' - interval '02:01' AS "23:29:00"; +SELECT time '02:30' + interval '36:01' AS "14:31:00"; +SELECT time '03:30' + interval '1 month 04:01' AS "07:31:00"; +SELECT time with time zone '01:30-08' - interval '02:01' AS "23:29:00-08"; +SELECT time with time zone '02:30-08' + interval '36:01' AS "14:31:00-08"; + +-- These two tests cannot be used because they default to current timezone, +-- which may be either -08 or -07 depending on the time of year. +-- SELECT time with time zone '01:30' + interval '02:01' AS "03:31:00-08"; +-- SELECT time with time zone '03:30' + interval '1 month 04:01' AS "07:31:00-08"; +-- Try the following two tests instead, as a poor substitute + +SELECT CAST(CAST(date 'today' + time with time zone '05:30' + + interval '02:01' AS time with time zone) AS time) AS "07:31:00"; + +SELECT CAST(cast(date 'today' + time with time zone '03:30' + + interval '1 month 04:01' as timestamp without time zone) AS time) AS "07:31:00"; + + +-- SQL9x OVERLAPS operator +-- test with time zone +SELECT (timestamp with time zone '2000-11-27', timestamp with time zone '2000-11-28') + OVERLAPS (timestamp with time zone '2000-11-27 12:00', timestamp with time zone '2000-11-30') AS "True"; + +SELECT (timestamp with time zone '2000-11-26', timestamp with time zone '2000-11-27') + OVERLAPS (timestamp with time zone '2000-11-27 12:00', timestamp with time zone '2000-11-30') AS "False"; + +-- test without time zone +SELECT (timestamp without time zone '2000-11-27', timestamp without time zone '2000-11-28') + OVERLAPS (timestamp without time zone '2000-11-27 12:00', timestamp without time zone '2000-11-30') AS "True"; + +SELECT (timestamp without time zone '2000-11-26', timestamp without time zone '2000-11-27') + OVERLAPS (timestamp without time zone '2000-11-27 12:00', timestamp without time zone '2000-11-30') AS "False"; + +-- test time and interval +SELECT (time '00:00', time '01:00') + OVERLAPS (time '00:30', time '01:30') AS "True"; + + +-- +-- Comparisons between datetime types, especially overflow cases +--- + +SELECT '2202020-10-05'::date::timestamp; -- fail +SELECT '2202020-10-05'::date > '2020-10-05'::timestamp as t; +SELECT '2020-10-05'::timestamp > '2202020-10-05'::date as f; + +SELECT '2202020-10-05'::date::timestamptz; -- fail +SELECT '2202020-10-05'::date > '2020-10-05'::timestamptz as t; +SELECT '2020-10-05'::timestamptz > '2202020-10-05'::date as f; + + +SELECT '4714-11-24 BC'::date < '2020-10-05'::timestamptz as t; +SELECT '2020-10-05'::timestamptz >= '4714-11-24 BC'::date as t; + +SELECT '4714-11-24 BC'::timestamp < '2020-10-05'::timestamptz as t; +SELECT '2020-10-05'::timestamptz >= '4714-11-24 BC'::timestamp as t; + +-- +-- Formats +-- + +-- +-- to_timestamp() +-- + + +SELECT to_timestamp('97/Feb/16', 'YYMonDD'); + + +SELECT to_timestamp('2011-12-18 11:38 PST', 'YYYY-MM-DD HH12:MI TZ'); -- NYI + + +-- +-- Check handling of BC dates +-- + +-- +-- Check handling of multiple spaces in format and/or input +-- + +SELECT to_date('2011 x12 x18', 'YYYYxMMxDD'); + +-- +-- Check errors for some incorrect usages of to_timestamp() and to_date() +-- + +-- Mixture of date conventions (ISO week and Gregorian): +SELECT to_timestamp('2005527', 'YYYYIWID'); + +-- Insufficient characters in the source string: +SELECT to_timestamp('19971', 'YYYYMMDD'); + +-- Insufficient digit characters for a single node: +SELECT to_timestamp('19971)24', 'YYYYMMDD'); + +-- We don't accept full-length day or month names if short form is specified: +SELECT to_timestamp('Friday 1-January-1999', 'DY DD MON YYYY'); +SELECT to_timestamp('Fri 1-January-1999', 'DY DD MON YYYY'); + +-- Value clobbering: +SELECT to_timestamp('1997-11-Jan-16', 'YYYY-MM-Mon-DD'); + +-- Non-numeric input: +SELECT to_timestamp('199711xy', 'YYYYMMDD'); + +-- Input that doesn't fit in an int: +SELECT to_timestamp('10000000000', 'FMYYYY'); + +-- Out-of-range and not-quite-out-of-range fields: +SELECT to_timestamp('2016-06-13 25:00:00', 'YYYY-MM-DD HH24:MI:SS'); +SELECT to_timestamp('2016-06-13 15:60:00', 'YYYY-MM-DD HH24:MI:SS'); +SELECT to_timestamp('2016-06-13 15:50:60', 'YYYY-MM-DD HH24:MI:SS'); +SELECT to_timestamp('2016-06-13 15:50:55', 'YYYY-MM-DD HH:MI:SS'); +SELECT to_timestamp('2016-13-01 15:50:55', 'YYYY-MM-DD HH24:MI:SS'); +SELECT to_timestamp('2016-02-30 15:50:55', 'YYYY-MM-DD HH24:MI:SS'); +SELECT to_timestamp('2015-02-29 15:50:55', 'YYYY-MM-DD HH24:MI:SS'); +SELECT to_timestamp('2015-02-11 86400', 'YYYY-MM-DD SSSS'); +SELECT to_timestamp('2015-02-11 86400', 'YYYY-MM-DD SSSSS'); +SELECT to_date('2016-13-10', 'YYYY-MM-DD'); +SELECT to_date('2016-02-30', 'YYYY-MM-DD'); +SELECT to_date('2015-02-29', 'YYYY-MM-DD'); +SELECT to_date('2015 366', 'YYYY DDD'); +SELECT to_date('2016 367', 'YYYY DDD'); + +-- +-- Check behavior with SQL-style fixed-GMT-offset time zone (cf bug #8572) +-- diff --git a/ydb/tests/functional/postgresql/cases/int2.out b/ydb/tests/functional/postgresql/cases/int2.out new file mode 100644 index 00000000000..550c11e08e6 --- /dev/null +++ b/ydb/tests/functional/postgresql/cases/int2.out @@ -0,0 +1,65 @@ +-- +-- INT2 +-- +-- corner cases +SELECT (-1::int2<<15)::text; + text +-------- + -32768 +(1 row) + +SELECT ((-1::int2<<15)+1::int2)::text; + text +-------- + -32767 +(1 row) + +-- check sane handling of INT16_MIN overflow cases +SELECT (-32768)::int2 * (-1)::int2; +ERROR: smallint out of range +SELECT (-32768)::int2 / (-1)::int2; +ERROR: smallint out of range +SELECT (-32768)::int2 % (-1)::int2; + ?column? +---------- + 0 +(1 row) + +-- check rounding when casting from float +SELECT x, x::int2 AS int2_value +FROM (VALUES (-2.5::float8), + (-1.5::float8), + (-0.5::float8), + (0.0::float8), + (0.5::float8), + (1.5::float8), + (2.5::float8)) t(x); + x | int2_value +------+------------ + -2.5 | -2 + -1.5 | -2 + -0.5 | 0 + 0 | 0 + 0.5 | 0 + 1.5 | 2 + 2.5 | 2 +(7 rows) + +-- check rounding when casting from numeric +SELECT x, x::int2 AS int2_value +FROM (VALUES (-2.5::numeric), + (-1.5::numeric), + (-0.5::numeric), + (0.5::numeric), + (1.5::numeric), + (2.5::numeric)) t(x); + x | int2_value +------+------------ + -2.5 | -3 + -1.5 | -2 + -0.5 | -1 + 0.5 | 1 + 1.5 | 2 + 2.5 | 3 +(6 rows) + diff --git a/ydb/tests/functional/postgresql/cases/int2.sql b/ydb/tests/functional/postgresql/cases/int2.sql new file mode 100644 index 00000000000..b56fa584860 --- /dev/null +++ b/ydb/tests/functional/postgresql/cases/int2.sql @@ -0,0 +1,31 @@ +-- +-- INT2 +-- + +-- corner cases +SELECT (-1::int2<<15)::text; +SELECT ((-1::int2<<15)+1::int2)::text; + +-- check sane handling of INT16_MIN overflow cases +SELECT (-32768)::int2 * (-1)::int2; +SELECT (-32768)::int2 / (-1)::int2; +SELECT (-32768)::int2 % (-1)::int2; + +-- check rounding when casting from float +SELECT x, x::int2 AS int2_value +FROM (VALUES (-2.5::float8), + (-1.5::float8), + (-0.5::float8), + (0.0::float8), + (0.5::float8), + (1.5::float8), + (2.5::float8)) t(x); + +-- check rounding when casting from numeric +SELECT x, x::int2 AS int2_value +FROM (VALUES (-2.5::numeric), + (-1.5::numeric), + (-0.5::numeric), + (0.5::numeric), + (1.5::numeric), + (2.5::numeric)) t(x); diff --git a/ydb/tests/functional/postgresql/cases/int4.out b/ydb/tests/functional/postgresql/cases/int4.out new file mode 100644 index 00000000000..6e4bbdf6c04 --- /dev/null +++ b/ydb/tests/functional/postgresql/cases/int4.out @@ -0,0 +1,185 @@ +-- +-- INT4 +-- +-- +-- more complex expressions +-- +-- variations on unary minus parsing +SELECT -2+3 AS one; + one +----- + 1 +(1 row) + +SELECT 4-2 AS two; + two +----- + 2 +(1 row) + +SELECT 2- -1 AS three; + three +------- + 3 +(1 row) + +SELECT 2 - -2 AS four; + four +------ + 4 +(1 row) + +SELECT int2 '2' * int2 '2' = int2 '16' / int2 '4' AS true; + true +------ + t +(1 row) + +SELECT int4 '2' * int2 '2' = int2 '16' / int4 '4' AS true; + true +------ + t +(1 row) + +SELECT int2 '2' * int4 '2' = int4 '16' / int2 '4' AS true; + true +------ + t +(1 row) + +SELECT int4 '1000' < int4 '999' AS false; + false +------- + f +(1 row) + +SELECT 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 AS ten; + ten +----- + 10 +(1 row) + +SELECT 2 + 2 / 2 AS three; + three +------- + 3 +(1 row) + +SELECT (2 + 2) / 2 AS two; + two +----- + 2 +(1 row) + +-- corner case +SELECT (-1::int4<<31)::text; + text +------------- + -2147483648 +(1 row) + +SELECT ((-1::int4<<31)+1)::text; + text +------------- + -2147483647 +(1 row) + +-- check sane handling of INT_MIN overflow cases +SELECT (-2147483648)::int4 * (-1)::int4; +ERROR: integer out of range +SELECT (-2147483648)::int4 / (-1)::int4; +ERROR: integer out of range +SELECT (-2147483648)::int4 % (-1)::int4; + ?column? +---------- + 0 +(1 row) + +SELECT (-2147483648)::int4 * (-1)::int2; +ERROR: integer out of range +SELECT (-2147483648)::int4 / (-1)::int2; +ERROR: integer out of range +-- check rounding when casting from float +SELECT x, x::int4 AS int4_value +FROM (VALUES (-2.5::float8), + (-1.5::float8), + (-0.5::float8), + (0.0::float8), + (0.5::float8), + (1.5::float8), + (2.5::float8)) t(x); + x | int4_value +------+------------ + -2.5 | -2 + -1.5 | -2 + -0.5 | 0 + 0 | 0 + 0.5 | 0 + 1.5 | 2 + 2.5 | 2 +(7 rows) + +-- check rounding when casting from numeric +SELECT x, x::int4 AS int4_value +FROM (VALUES (-2.5::numeric), + (-1.5::numeric), + (-0.5::numeric), + (0.5::numeric), + (1.5::numeric), + (2.5::numeric)) t(x); + x | int4_value +------+------------ + -2.5 | -3 + -1.5 | -2 + -0.5 | -1 + 0.5 | 1 + 1.5 | 2 + 2.5 | 3 +(6 rows) + +-- test gcd() +SELECT a, b, gcd(a, b), gcd(a, -b), gcd(b, a), gcd(-b, a) +FROM (VALUES (0::int4, 0::int4), + (0::int4, 6410818::int4), + (61866666::int4, 6410818::int4), + (-61866666::int4, 6410818::int4), + ((-2147483648)::int4, 1::int4), + ((-2147483648)::int4, 2147483647::int4), + ((-2147483648)::int4, 1073741824::int4)) AS v(a, b); + a | b | gcd | gcd | gcd | gcd +-------------+------------+------------+------------+------------+------------ + 0 | 0 | 0 | 0 | 0 | 0 + 0 | 6410818 | 6410818 | 6410818 | 6410818 | 6410818 + 61866666 | 6410818 | 1466 | 1466 | 1466 | 1466 + -61866666 | 6410818 | 1466 | 1466 | 1466 | 1466 + -2147483648 | 1 | 1 | 1 | 1 | 1 + -2147483648 | 2147483647 | 1 | 1 | 1 | 1 + -2147483648 | 1073741824 | 1073741824 | 1073741824 | 1073741824 | 1073741824 +(7 rows) + +SELECT gcd((-2147483648)::int4, 0::int4); -- overflow +ERROR: integer out of range +SELECT gcd((-2147483648)::int4, (-2147483648)::int4); -- overflow +ERROR: integer out of range +-- test lcm() +SELECT a, b, lcm(a, b), lcm(a, -b), lcm(b, a), lcm(-b, a) +FROM (VALUES (0::int4, 0::int4), + (0::int4, 42::int4), + (42::int4, 42::int4), + (330::int4, 462::int4), + (-330::int4, 462::int4), + ((-2147483648)::int4, 0::int4)) AS v(a, b); + a | b | lcm | lcm | lcm | lcm +-------------+-----+------+------+------+------ + 0 | 0 | 0 | 0 | 0 | 0 + 0 | 42 | 0 | 0 | 0 | 0 + 42 | 42 | 42 | 42 | 42 | 42 + 330 | 462 | 2310 | 2310 | 2310 | 2310 + -330 | 462 | 2310 | 2310 | 2310 | 2310 + -2147483648 | 0 | 0 | 0 | 0 | 0 +(6 rows) + +SELECT lcm((-2147483648)::int4, 1::int4); -- overflow +ERROR: integer out of range +SELECT lcm(2147483647::int4, 2147483646::int4); -- overflow +ERROR: integer out of range diff --git a/ydb/tests/functional/postgresql/cases/int4.sql b/ydb/tests/functional/postgresql/cases/int4.sql new file mode 100644 index 00000000000..1b95ff57e6a --- /dev/null +++ b/ydb/tests/functional/postgresql/cases/int4.sql @@ -0,0 +1,85 @@ +-- +-- INT4 +-- + +-- +-- more complex expressions +-- + +-- variations on unary minus parsing +SELECT -2+3 AS one; + +SELECT 4-2 AS two; + +SELECT 2- -1 AS three; + +SELECT 2 - -2 AS four; + +SELECT int2 '2' * int2 '2' = int2 '16' / int2 '4' AS true; + +SELECT int4 '2' * int2 '2' = int2 '16' / int4 '4' AS true; + +SELECT int2 '2' * int4 '2' = int4 '16' / int2 '4' AS true; + +SELECT int4 '1000' < int4 '999' AS false; + +SELECT 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 AS ten; + +SELECT 2 + 2 / 2 AS three; + +SELECT (2 + 2) / 2 AS two; + +-- corner case +SELECT (-1::int4<<31)::text; +SELECT ((-1::int4<<31)+1)::text; + +-- check sane handling of INT_MIN overflow cases +SELECT (-2147483648)::int4 * (-1)::int4; +SELECT (-2147483648)::int4 / (-1)::int4; +SELECT (-2147483648)::int4 % (-1)::int4; +SELECT (-2147483648)::int4 * (-1)::int2; +SELECT (-2147483648)::int4 / (-1)::int2; + +-- check rounding when casting from float +SELECT x, x::int4 AS int4_value +FROM (VALUES (-2.5::float8), + (-1.5::float8), + (-0.5::float8), + (0.0::float8), + (0.5::float8), + (1.5::float8), + (2.5::float8)) t(x); + +-- check rounding when casting from numeric +SELECT x, x::int4 AS int4_value +FROM (VALUES (-2.5::numeric), + (-1.5::numeric), + (-0.5::numeric), + (0.5::numeric), + (1.5::numeric), + (2.5::numeric)) t(x); + +-- test gcd() +SELECT a, b, gcd(a, b), gcd(a, -b), gcd(b, a), gcd(-b, a) +FROM (VALUES (0::int4, 0::int4), + (0::int4, 6410818::int4), + (61866666::int4, 6410818::int4), + (-61866666::int4, 6410818::int4), + ((-2147483648)::int4, 1::int4), + ((-2147483648)::int4, 2147483647::int4), + ((-2147483648)::int4, 1073741824::int4)) AS v(a, b); + +SELECT gcd((-2147483648)::int4, 0::int4); -- overflow +SELECT gcd((-2147483648)::int4, (-2147483648)::int4); -- overflow + +-- test lcm() +SELECT a, b, lcm(a, b), lcm(a, -b), lcm(b, a), lcm(-b, a) +FROM (VALUES (0::int4, 0::int4), + (0::int4, 42::int4), + (42::int4, 42::int4), + (330::int4, 462::int4), + (-330::int4, 462::int4), + ((-2147483648)::int4, 0::int4)) AS v(a, b); + +SELECT lcm((-2147483648)::int4, 1::int4); -- overflow +SELECT lcm(2147483647::int4, 2147483646::int4); -- overflow diff --git a/ydb/tests/functional/postgresql/cases/int8.out b/ydb/tests/functional/postgresql/cases/int8.out new file mode 100644 index 00000000000..ed54258f9e2 --- /dev/null +++ b/ydb/tests/functional/postgresql/cases/int8.out @@ -0,0 +1,160 @@ +-- +-- INT8 +-- Test int8 64-bit integers. +-- +-- check min/max values and overflow behavior +select '-9223372036854775808'::int8; + int8 +---------------------- + -9223372036854775808 +(1 row) + +select '-9223372036854775809'::int8; +ERROR: value "-9223372036854775809" is out of range for type bigint +LINE 1: select '-9223372036854775809'::int8; + ^ +select '9223372036854775807'::int8; + int8 +--------------------- + 9223372036854775807 +(1 row) + +select '9223372036854775808'::int8; +ERROR: value "9223372036854775808" is out of range for type bigint +LINE 1: select '9223372036854775808'::int8; + ^ +select -('-9223372036854775807'::int8); + ?column? +--------------------- + 9223372036854775807 +(1 row) + +select -('-9223372036854775808'::int8); +ERROR: bigint out of range +select '9223372036854775800'::int8 + '9223372036854775800'::int8; +ERROR: bigint out of range +select '-9223372036854775800'::int8 + '-9223372036854775800'::int8; +ERROR: bigint out of range +select '9223372036854775800'::int8 - '-9223372036854775800'::int8; +ERROR: bigint out of range +select '-9223372036854775800'::int8 - '9223372036854775800'::int8; +ERROR: bigint out of range +select '9223372036854775800'::int8 * '9223372036854775800'::int8; +ERROR: bigint out of range +select '9223372036854775800'::int8 / '0'::int8; +ERROR: division by zero +select '9223372036854775800'::int8 % '0'::int8; +ERROR: division by zero +select abs('-9223372036854775808'::int8); +ERROR: bigint out of range +SELECT CAST('42'::int2 AS int8), CAST('-37'::int2 AS int8); + int8 | int8 +------+------ + 42 | -37 +(1 row) + +SELECT CAST('36854775807.0'::float4 AS int8); + int8 +------------- + 36854775808 +(1 row) + +SELECT CAST('922337203685477580700.0'::float8 AS int8); +ERROR: bigint out of range +-- corner case +SELECT (-1::int8<<63)::text; + text +---------------------- + -9223372036854775808 +(1 row) + +SELECT ((-1::int8<<63)+1)::text; + text +---------------------- + -9223372036854775807 +(1 row) + +-- check sane handling of INT64_MIN overflow cases +SELECT (-9223372036854775808)::int8 * (-1)::int8; +ERROR: bigint out of range +SELECT (-9223372036854775808)::int8 / (-1)::int8; +ERROR: bigint out of range +SELECT (-9223372036854775808)::int8 % (-1)::int8; + ?column? +---------- + 0 +(1 row) + +SELECT (-9223372036854775808)::int8 * (-1)::int4; +ERROR: bigint out of range +SELECT (-9223372036854775808)::int8 / (-1)::int4; +ERROR: bigint out of range +SELECT (-9223372036854775808)::int8 * (-1)::int2; +ERROR: bigint out of range +SELECT (-9223372036854775808)::int8 / (-1)::int2; +ERROR: bigint out of range +-- check rounding when casting from float +SELECT x, x::int8 AS int8_value +FROM (VALUES (-2.5::float8), + (-1.5::float8), + (-0.5::float8), + (0.0::float8), + (0.5::float8), + (1.5::float8), + (2.5::float8)) t(x); + x | int8_value +------+------------ + -2.5 | -2 + -1.5 | -2 + -0.5 | 0 + 0 | 0 + 0.5 | 0 + 1.5 | 2 + 2.5 | 2 +(7 rows) + +-- check rounding when casting from numeric +SELECT x, x::int8 AS int8_value +FROM (VALUES (-2.5::numeric), + (-1.5::numeric), + (-0.5::numeric), + (0.5::numeric), + (1.5::numeric), + (2.5::numeric)) t(x); + x | int8_value +------+------------ + -2.5 | -3 + -1.5 | -2 + -0.5 | -1 + 0.5 | 1 + 1.5 | 2 + 2.5 | 3 +(6 rows) + +-- test gcd() +SELECT gcd((-9223372036854775808)::int8, 0::int8); -- overflow +ERROR: bigint out of range +SELECT gcd((-9223372036854775808)::int8, (-9223372036854775808)::int8); -- overflow +ERROR: bigint out of range +-- test lcm() +SELECT a, b, lcm(a, b), lcm(a, -b), lcm(b, a), lcm(-b, a) +FROM (VALUES (0::int8, 0::int8), + (0::int8, 29893644334::int8), + (29893644334::int8, 29893644334::int8), + (288484263558::int8, 29893644334::int8), + (-288484263558::int8, 29893644334::int8), + ((-9223372036854775808)::int8, 0::int8)) AS v(a, b); + a | b | lcm | lcm | lcm | lcm +----------------------+-------------+------------------+------------------+------------------+------------------ + 0 | 0 | 0 | 0 | 0 | 0 + 0 | 29893644334 | 0 | 0 | 0 | 0 + 29893644334 | 29893644334 | 29893644334 | 29893644334 | 29893644334 | 29893644334 + 288484263558 | 29893644334 | 1261541684539134 | 1261541684539134 | 1261541684539134 | 1261541684539134 + -288484263558 | 29893644334 | 1261541684539134 | 1261541684539134 | 1261541684539134 | 1261541684539134 + -9223372036854775808 | 0 | 0 | 0 | 0 | 0 +(6 rows) + +SELECT lcm((-9223372036854775808)::int8, 1::int8); -- overflow +ERROR: bigint out of range +SELECT lcm(9223372036854775807::int8, 9223372036854775806::int8); -- overflow +ERROR: bigint out of range diff --git a/ydb/tests/functional/postgresql/cases/int8.sql b/ydb/tests/functional/postgresql/cases/int8.sql new file mode 100644 index 00000000000..70ee7c1edf1 --- /dev/null +++ b/ydb/tests/functional/postgresql/cases/int8.sql @@ -0,0 +1,77 @@ +-- +-- INT8 +-- Test int8 64-bit integers. +-- +-- check min/max values and overflow behavior + +select '-9223372036854775808'::int8; +select '-9223372036854775809'::int8; +select '9223372036854775807'::int8; +select '9223372036854775808'::int8; + +select -('-9223372036854775807'::int8); +select -('-9223372036854775808'::int8); + +select '9223372036854775800'::int8 + '9223372036854775800'::int8; +select '-9223372036854775800'::int8 + '-9223372036854775800'::int8; + +select '9223372036854775800'::int8 - '-9223372036854775800'::int8; +select '-9223372036854775800'::int8 - '9223372036854775800'::int8; + +select '9223372036854775800'::int8 * '9223372036854775800'::int8; + +select '9223372036854775800'::int8 / '0'::int8; +select '9223372036854775800'::int8 % '0'::int8; + +select abs('-9223372036854775808'::int8); +SELECT CAST('42'::int2 AS int8), CAST('-37'::int2 AS int8); +SELECT CAST('36854775807.0'::float4 AS int8); +SELECT CAST('922337203685477580700.0'::float8 AS int8); +-- corner case +SELECT (-1::int8<<63)::text; +SELECT ((-1::int8<<63)+1)::text; + +-- check sane handling of INT64_MIN overflow cases +SELECT (-9223372036854775808)::int8 * (-1)::int8; +SELECT (-9223372036854775808)::int8 / (-1)::int8; +SELECT (-9223372036854775808)::int8 % (-1)::int8; +SELECT (-9223372036854775808)::int8 * (-1)::int4; +SELECT (-9223372036854775808)::int8 / (-1)::int4; +SELECT (-9223372036854775808)::int8 * (-1)::int2; +SELECT (-9223372036854775808)::int8 / (-1)::int2; + +-- check rounding when casting from float +SELECT x, x::int8 AS int8_value +FROM (VALUES (-2.5::float8), + (-1.5::float8), + (-0.5::float8), + (0.0::float8), + (0.5::float8), + (1.5::float8), + (2.5::float8)) t(x); + +-- check rounding when casting from numeric +SELECT x, x::int8 AS int8_value +FROM (VALUES (-2.5::numeric), + (-1.5::numeric), + (-0.5::numeric), + (0.5::numeric), + (1.5::numeric), + (2.5::numeric)) t(x); + +-- test gcd() + +SELECT gcd((-9223372036854775808)::int8, 0::int8); -- overflow +SELECT gcd((-9223372036854775808)::int8, (-9223372036854775808)::int8); -- overflow + +-- test lcm() +SELECT a, b, lcm(a, b), lcm(a, -b), lcm(b, a), lcm(-b, a) +FROM (VALUES (0::int8, 0::int8), + (0::int8, 29893644334::int8), + (29893644334::int8, 29893644334::int8), + (288484263558::int8, 29893644334::int8), + (-288484263558::int8, 29893644334::int8), + ((-9223372036854775808)::int8, 0::int8)) AS v(a, b); + +SELECT lcm((-9223372036854775808)::int8, 1::int8); -- overflow +SELECT lcm(9223372036854775807::int8, 9223372036854775806::int8); -- overflow diff --git a/ydb/tests/functional/postgresql/cases/name.out b/ydb/tests/functional/postgresql/cases/name.out new file mode 100644 index 00000000000..2f1afcf796b --- /dev/null +++ b/ydb/tests/functional/postgresql/cases/name.out @@ -0,0 +1,17 @@ +-- +-- NAME +-- all inputs are silently truncated at NAMEDATALEN-1 (63) characters +-- +-- fixed-length by reference +SELECT name 'name string' = name 'name string' AS "True"; + True +------ + t +(1 row) + +SELECT name 'name string' = name 'name string ' AS "False"; + False +------- + f +(1 row) + diff --git a/ydb/tests/functional/postgresql/cases/name.sql b/ydb/tests/functional/postgresql/cases/name.sql new file mode 100644 index 00000000000..ea9fb6d99f7 --- /dev/null +++ b/ydb/tests/functional/postgresql/cases/name.sql @@ -0,0 +1,9 @@ +-- +-- NAME +-- all inputs are silently truncated at NAMEDATALEN-1 (63) characters +-- + +-- fixed-length by reference +SELECT name 'name string' = name 'name string' AS "True"; + +SELECT name 'name string' = name 'name string ' AS "False"; diff --git a/ydb/tests/functional/postgresql/cases/numeric.out b/ydb/tests/functional/postgresql/cases/numeric.out new file mode 100644 index 00000000000..597da1e7418 --- /dev/null +++ b/ydb/tests/functional/postgresql/cases/numeric.out @@ -0,0 +1,352 @@ +-- +-- NUMERIC +-- +SELECT sqrt('-1'::numeric); +ERROR: cannot take square root of a negative number +SELECT sqrt('-inf'::numeric); +ERROR: cannot take square root of a negative number +SELECT ln('0'::numeric); +ERROR: cannot take logarithm of zero +SELECT ln('-1'::numeric); +ERROR: cannot take logarithm of a negative number +SELECT ln('-inf'::numeric); +ERROR: cannot take logarithm of a negative number +-- ****************************** +-- * miscellaneous checks for things that have been broken in the past... +-- ****************************** +-- Check conversion to integers +SELECT (-9223372036854775808.4)::int8; -- ok + int8 +---------------------- + -9223372036854775808 +(1 row) + +SELECT 9223372036854775807.5::int8; -- should fail +ERROR: bigint out of range +SELECT (-2147483648.4)::int4; -- ok + int4 +------------- + -2147483648 +(1 row) + +SELECT 2147483647.4::int4; -- ok + int4 +------------ + 2147483647 +(1 row) + +SELECT 2147483647.5::int4; -- should fail +ERROR: integer out of range +SELECT (-32768.4)::int2; -- ok + int2 +-------- + -32768 +(1 row) + +SELECT 32767.4::int2; -- ok + int2 +------- + 32767 +(1 row) + +SELECT 32767.5::int2; -- should fail +ERROR: smallint out of range +-- Check inf/nan conversion behavior +SELECT '42'::int2::numeric; + numeric +--------- + 42 +(1 row) + +SELECT 'NaN'::numeric::int2; +ERROR: cannot convert NaN to smallint +SELECT 'Infinity'::numeric::int2; +ERROR: cannot convert infinity to smallint +SELECT '-Infinity'::numeric::int2; +ERROR: cannot convert infinity to smallint +SELECT 'NaN'::numeric::int4; +ERROR: cannot convert NaN to integer +SELECT 'Infinity'::numeric::int4; +ERROR: cannot convert infinity to integer +SELECT '-Infinity'::numeric::int4; +ERROR: cannot convert infinity to integer +SELECT 'NaN'::numeric::int8; +ERROR: cannot convert NaN to bigint +SELECT 'Infinity'::numeric::int8; +ERROR: cannot convert infinity to bigint +SELECT '-Infinity'::numeric::int8; +ERROR: cannot convert infinity to bigint +-- Simple check that ceil(), floor(), and round() work correctly +-- Check rounding, it should round ties away from zero. +-- Testing for width_bucket(). For convenience, we test both the +-- numeric and float8 versions of the function in this file. +-- errors +SELECT width_bucket(5.0, 3.0, 4.0, 0); +ERROR: count must be greater than zero +SELECT width_bucket(5.0, 3.0, 4.0, -5); +ERROR: count must be greater than zero +SELECT width_bucket(3.5, 3.0, 3.0, 888); +ERROR: lower bound cannot equal upper bound +SELECT width_bucket(5.0::float8, 3.0::float8, 4.0::float8, 0); +ERROR: count must be greater than zero +SELECT width_bucket(5.0::float8, 3.0::float8, 4.0::float8, -5); +ERROR: count must be greater than zero +SELECT width_bucket(3.5::float8, 3.0::float8, 3.0::float8, 888); +ERROR: lower bound cannot equal upper bound +-- +-- TO_CHAR() +-- +SELECT to_char('100'::numeric, 'FM999.9'); + to_char +--------- + 100. +(1 row) + +SELECT to_char('100'::numeric, 'FM999.'); + to_char +--------- + 100 +(1 row) + +SELECT to_char('100'::numeric, 'FM999'); + to_char +--------- + 100 +(1 row) + +-- Check parsing of literal text in a format string +SELECT to_char('100'::numeric, 'foo999'); + to_char +--------- + foo 100 +(1 row) + +SELECT to_char('100'::numeric, 'f\oo999'); + to_char +---------- + f\oo 100 +(1 row) + +SELECT to_char('100'::numeric, 'f\\oo999'); + to_char +----------- + f\\oo 100 +(1 row) + +SELECT to_char('100'::numeric, 'f\"oo999'); + to_char +---------- + f"oo 100 +(1 row) + +SELECT to_char('100'::numeric, 'f\\"oo999'); + to_char +----------- + f\"oo 100 +(1 row) + +SELECT to_char('100'::numeric, 'f"ool"999'); + to_char +---------- + fool 100 +(1 row) + +SELECT to_char('100'::numeric, 'f"\ool"999'); + to_char +---------- + fool 100 +(1 row) + +SELECT to_char('100'::numeric, 'f"\\ool"999'); + to_char +----------- + f\ool 100 +(1 row) + +SELECT to_char('100'::numeric, 'f"ool\"999'); + to_char +---------- + fool"999 +(1 row) + +SELECT to_char('100'::numeric, 'f"ool\\"999'); + to_char +----------- + fool\ 100 +(1 row) + +-- TO_NUMBER() +-- +SELECT to_number('-34,338,492', '99G999G999'); + to_number +----------- + -34338492 +(1 row) + +SELECT to_number('-34,338,492.654,878', '99G999G999D999G999'); + to_number +------------------ + -34338492.654878 +(1 row) + +SELECT to_number('<564646.654564>', '999999.999999PR'); + to_number +---------------- + -564646.654564 +(1 row) + +SELECT to_number('0.00001-', '9.999999S'); + to_number +----------- + -0.00001 +(1 row) + +SELECT to_number('5.01-', 'FM9.999999S'); + to_number +----------- + -5.01 +(1 row) + +SELECT to_number('5.01-', 'FM9.999999MI'); + to_number +----------- + -5.01 +(1 row) + +SELECT to_number('5 4 4 4 4 8 . 7 8', '9 9 9 9 9 9 . 9 9'); + to_number +----------- + 544448.78 +(1 row) + +SELECT to_number('.01', 'FM9.99'); + to_number +----------- + 0.01 +(1 row) + +SELECT to_number('.0', '99999999.99999999'); + to_number +----------- + 0.0 +(1 row) + +SELECT to_number('.-01', 'S99.99'); + to_number +----------- + -0.01 +(1 row) + +SELECT to_number('.01-', '99.99S'); + to_number +----------- + -0.01 +(1 row) + +SELECT to_number(' . 0 1-', ' 9 9 . 9 9 S'); + to_number +----------- + -0.01 +(1 row) + +SELECT to_number('34,50','999,99'); + to_number +----------- + 3450 +(1 row) + +SELECT to_number('123,000','999G'); + to_number +----------- + 123 +(1 row) + +SELECT to_number('123456','999G999'); + to_number +----------- + 123456 +(1 row) + +SELECT to_number('$1234.56','L9,999.99'); + to_number +----------- + 1234.56 +(1 row) + +SELECT to_number('$1234.56','L99,999.99'); + to_number +----------- + 1234.56 +(1 row) + +SELECT to_number('$1,234.56','L99,999.99'); + to_number +----------- + 1234.56 +(1 row) + +SELECT to_number('1234.56','L99,999.99'); + to_number +----------- + 1234.56 +(1 row) + +SELECT to_number('1,234.56','L99,999.99'); + to_number +----------- + 1234.56 +(1 row) + +SELECT to_number('42nd', '99th'); + to_number +----------- + 42 +(1 row) + +-- +-- Tests for raising to non-integer powers +-- +-- invalid inputs +select 0.0 ^ (-12.34); +ERROR: zero raised to a negative power is undefined +select (-12.34) ^ 1.2; +ERROR: a negative number raised to a non-integer power yields a complex result +-- +-- Tests for LN() +-- +-- Invalid inputs +select ln(-12.34); +ERROR: cannot take logarithm of a negative number +select ln(0.0); +ERROR: cannot take logarithm of zero +-- +-- Tests for LOG() (base 10) +-- +-- invalid inputs +select log(-12.34); +ERROR: cannot take logarithm of a negative number +CONTEXT: SQL function "log" statement 1 +select log(0.0); +ERROR: cannot take logarithm of zero +CONTEXT: SQL function "log" statement 1 +-- +-- Tests for pg_lsn() +-- +SELECT pg_lsn(23783416::numeric); + pg_lsn +----------- + 0/16AE7F8 +(1 row) + +SELECT pg_lsn(0::numeric); + pg_lsn +-------- + 0/0 +(1 row) + +SELECT pg_lsn(-1::numeric); +ERROR: pg_lsn out of range +SELECT pg_lsn(18446744073709551616::numeric); +ERROR: pg_lsn out of range +SELECT pg_lsn('NaN'::numeric); +ERROR: cannot convert NaN to pg_lsn diff --git a/ydb/tests/functional/postgresql/cases/numeric.sql b/ydb/tests/functional/postgresql/cases/numeric.sql new file mode 100644 index 00000000000..88ae3651708 --- /dev/null +++ b/ydb/tests/functional/postgresql/cases/numeric.sql @@ -0,0 +1,122 @@ +-- +-- NUMERIC +-- + +SELECT sqrt('-1'::numeric); +SELECT sqrt('-inf'::numeric); + +SELECT ln('0'::numeric); +SELECT ln('-1'::numeric); +SELECT ln('-inf'::numeric); + +-- ****************************** +-- * miscellaneous checks for things that have been broken in the past... +-- ****************************** +-- Check conversion to integers +SELECT (-9223372036854775808.4)::int8; -- ok +SELECT 9223372036854775807.5::int8; -- should fail +SELECT (-2147483648.4)::int4; -- ok +SELECT 2147483647.4::int4; -- ok +SELECT 2147483647.5::int4; -- should fail +SELECT (-32768.4)::int2; -- ok +SELECT 32767.4::int2; -- ok +SELECT 32767.5::int2; -- should fail + +-- Check inf/nan conversion behavior +SELECT '42'::int2::numeric; +SELECT 'NaN'::numeric::int2; +SELECT 'Infinity'::numeric::int2; +SELECT '-Infinity'::numeric::int2; +SELECT 'NaN'::numeric::int4; +SELECT 'Infinity'::numeric::int4; +SELECT '-Infinity'::numeric::int4; +SELECT 'NaN'::numeric::int8; +SELECT 'Infinity'::numeric::int8; +SELECT '-Infinity'::numeric::int8; + +-- Simple check that ceil(), floor(), and round() work correctly +-- Check rounding, it should round ties away from zero. +-- Testing for width_bucket(). For convenience, we test both the +-- numeric and float8 versions of the function in this file. + +-- errors +SELECT width_bucket(5.0, 3.0, 4.0, 0); +SELECT width_bucket(5.0, 3.0, 4.0, -5); +SELECT width_bucket(3.5, 3.0, 3.0, 888); +SELECT width_bucket(5.0::float8, 3.0::float8, 4.0::float8, 0); +SELECT width_bucket(5.0::float8, 3.0::float8, 4.0::float8, -5); +SELECT width_bucket(3.5::float8, 3.0::float8, 3.0::float8, 888); +-- +-- TO_CHAR() +-- +SELECT to_char('100'::numeric, 'FM999.9'); +SELECT to_char('100'::numeric, 'FM999.'); +SELECT to_char('100'::numeric, 'FM999'); + +-- Check parsing of literal text in a format string +SELECT to_char('100'::numeric, 'foo999'); +SELECT to_char('100'::numeric, 'f\oo999'); +SELECT to_char('100'::numeric, 'f\\oo999'); +SELECT to_char('100'::numeric, 'f\"oo999'); +SELECT to_char('100'::numeric, 'f\\"oo999'); +SELECT to_char('100'::numeric, 'f"ool"999'); +SELECT to_char('100'::numeric, 'f"\ool"999'); +SELECT to_char('100'::numeric, 'f"\\ool"999'); +SELECT to_char('100'::numeric, 'f"ool\"999'); +SELECT to_char('100'::numeric, 'f"ool\\"999'); + +-- TO_NUMBER() +-- +SELECT to_number('-34,338,492', '99G999G999'); +SELECT to_number('-34,338,492.654,878', '99G999G999D999G999'); +SELECT to_number('<564646.654564>', '999999.999999PR'); +SELECT to_number('0.00001-', '9.999999S'); +SELECT to_number('5.01-', 'FM9.999999S'); +SELECT to_number('5.01-', 'FM9.999999MI'); +SELECT to_number('5 4 4 4 4 8 . 7 8', '9 9 9 9 9 9 . 9 9'); +SELECT to_number('.01', 'FM9.99'); +SELECT to_number('.0', '99999999.99999999'); +SELECT to_number('.-01', 'S99.99'); +SELECT to_number('.01-', '99.99S'); +SELECT to_number(' . 0 1-', ' 9 9 . 9 9 S'); +SELECT to_number('34,50','999,99'); +SELECT to_number('123,000','999G'); +SELECT to_number('123456','999G999'); +SELECT to_number('$1234.56','L9,999.99'); +SELECT to_number('$1234.56','L99,999.99'); +SELECT to_number('$1,234.56','L99,999.99'); +SELECT to_number('1234.56','L99,999.99'); +SELECT to_number('1,234.56','L99,999.99'); +SELECT to_number('42nd', '99th'); +-- +-- Tests for raising to non-integer powers +-- + +-- invalid inputs +select 0.0 ^ (-12.34); +select (-12.34) ^ 1.2; + +-- +-- Tests for LN() +-- + +-- Invalid inputs +select ln(-12.34); +select ln(0.0); + +-- +-- Tests for LOG() (base 10) +-- + +-- invalid inputs +select log(-12.34); +select log(0.0); + +-- +-- Tests for pg_lsn() +-- +SELECT pg_lsn(23783416::numeric); +SELECT pg_lsn(0::numeric); +SELECT pg_lsn(-1::numeric); +SELECT pg_lsn(18446744073709551616::numeric); +SELECT pg_lsn('NaN'::numeric); diff --git a/ydb/tests/functional/postgresql/cases/strings.out b/ydb/tests/functional/postgresql/cases/strings.out new file mode 100644 index 00000000000..e99bb1fd7e7 --- /dev/null +++ b/ydb/tests/functional/postgresql/cases/strings.out @@ -0,0 +1,480 @@ +-- +-- STRINGS +-- Test various data entry syntaxes. +-- +-- SQL string continuation syntax +-- E021-03 character string literals +SELECT 'first line' +' - next line' + ' - third line' + AS "Three lines to one"; + Three lines to one +------------------------------------- + first line - next line - third line +(1 row) + +-- illegal string continuation syntax +SELECT 'first line' +' - next line' /* this comment is not allowed here */ +' - third line' + AS "Illegal comment within continuation"; +ERROR: syntax error at or near "' - third line'" +LINE 3: ' - third line' + ^ +SELECT U&'d\0061t\+000061' AS U&"d\0061t\+000061"; + data +------ + data +(1 row) + +SELECT U&'d!0061t\+000061' UESCAPE '!' AS U&"d*0061t\+000061" UESCAPE '*'; + dat\+000061 +------------- + dat\+000061 +(1 row) + +SELECT U&'a\\b' AS "a\b"; + a\b +----- + a\b +(1 row) + +SELECT U&' \' UESCAPE '!' AS "tricky"; + tricky +-------- + \ +(1 row) + +SELECT 'tricky' AS U&"\" UESCAPE '!'; + \ +-------- + tricky +(1 row) + +SELECT U&'wrong: \061'; +ERROR: invalid Unicode escape +LINE 1: SELECT U&'wrong: \061'; + ^ +HINT: Unicode escapes must be \XXXX or \+XXXXXX. +SELECT U&'wrong: \+0061'; +ERROR: invalid Unicode escape +LINE 1: SELECT U&'wrong: \+0061'; + ^ +HINT: Unicode escapes must be \XXXX or \+XXXXXX. +SELECT U&'wrong: +0061' UESCAPE +; +ERROR: UESCAPE must be followed by a simple string literal at or near "+" +LINE 1: SELECT U&'wrong: +0061' UESCAPE +; + ^ +SELECT U&'wrong: +0061' UESCAPE '+'; +ERROR: invalid Unicode escape character at or near "'+'" +LINE 1: SELECT U&'wrong: +0061' UESCAPE '+'; + ^ +SELECT U&'wrong: \db99'; +ERROR: invalid Unicode surrogate pair +LINE 1: SELECT U&'wrong: \db99'; + ^ +SELECT U&'wrong: \db99xy'; +ERROR: invalid Unicode surrogate pair +LINE 1: SELECT U&'wrong: \db99xy'; +-- +-- test conversions between various string types +-- E021-10 implicit casting among the character data types +-- +SELECT CAST(name 'namefield' AS text) AS "text(name)"; + text(name) +------------ + namefield +(1 row) + +SELECT CAST(name 'namefield' AS char(10)) AS "char(name)"; + char(name) +------------ + namefield +(1 row) + +SELECT CAST(name 'namefield' AS varchar) AS "varchar(name)"; + varchar(name) +--------------- + namefield +(1 row) + +-- +-- test SQL string functions +-- E### and T### are feature reference numbers from SQL99 +-- +-- E021-09 trim function +SELECT TRIM(BOTH FROM ' bunch o blanks ') = 'bunch o blanks' AS "bunch o blanks"; + bunch o blanks +---------------- + t +(1 row) + +SELECT TRIM(LEADING FROM ' bunch o blanks ') = 'bunch o blanks ' AS "bunch o blanks "; + bunch o blanks +------------------ + t +(1 row) + +SELECT TRIM(TRAILING FROM ' bunch o blanks ') = ' bunch o blanks' AS " bunch o blanks"; + bunch o blanks +------------------ + t +(1 row) + +SELECT TRIM(BOTH 'x' FROM 'xxxxxsome Xsxxxxx') = 'some Xs' AS "some Xs"; + some Xs +--------- + t +(1 row) + +-- E021-06 substring expression +SELECT SUBSTRING('1234567890' FROM 3) = '34567890' AS "34567890"; + 34567890 +---------- + t +(1 row) + +SELECT SUBSTRING('1234567890' FROM 4 FOR 3) = '456' AS "456"; + 456 +----- + t +(1 row) + +-- test overflow cases +SELECT SUBSTRING('string' FROM 2 FOR 2147483646) AS "tring"; + tring +------- + tring +(1 row) + +SELECT SUBSTRING('string' FROM -10 FOR 2147483646) AS "string"; + string +-------- + string +(1 row) + +SELECT SUBSTRING('string' FROM -10 FOR -2147483646) AS "error"; +ERROR: negative substring length not allowed +-- substring() with just two arguments is not allowed by SQL spec; +-- we accept it, but we interpret the pattern as a POSIX regexp not SQL +SELECT SUBSTRING('abcdefg' FROM 'c.e') AS "cde"; + cde +----- + cde +(1 row) + +-- With a parenthesized subexpression, return only what matches the subexpr +SELECT SUBSTRING('abcdefg' FROM 'b(.*)f') AS "cde"; + cde +----- + cde +(1 row) + +-- T312 character overlay function +SELECT OVERLAY('abcdef' PLACING '45' FROM 4) AS "abc45f"; + abc45f +-------- + abc45f +(1 row) + +SELECT OVERLAY('yabadoo' PLACING 'daba' FROM 5) AS "yabadaba"; + yabadaba +---------- + yabadaba +(1 row) + +SELECT OVERLAY('yabadoo' PLACING 'daba' FROM 5 FOR 0) AS "yabadabadoo"; + yabadabadoo +------------- + yabadabadoo +(1 row) + +SELECT OVERLAY('babosa' PLACING 'ubb' FROM 2 FOR 4) AS "bubba"; + bubba +------- + bubba +(1 row) + +-- +-- test implicit type conversion +-- +-- E021-07 character concatenation +SELECT 'unknown' || ' and unknown' AS "Concat unknown types"; + Concat unknown types +---------------------- + unknown and unknown +(1 row) + +SELECT text 'text' || ' and unknown' AS "Concat text to unknown type"; + Concat text to unknown type +----------------------------- + text and unknown +(1 row) + +-- +-- test length +-- +SELECT length('abcdef') AS "length_6"; + length_6 +---------- + 6 +(1 row) + +-- +-- test strpos +-- +SELECT strpos('abcdef', 'cd') AS "pos_3"; + pos_3 +------- + 3 +(1 row) + +SELECT strpos('abcdef', 'xy') AS "pos_0"; + pos_0 +------- + 0 +(1 row) + +SELECT strpos('abcdef', '') AS "pos_1"; + pos_1 +------- + 1 +(1 row) + +SELECT strpos('', 'xy') AS "pos_0"; + pos_0 +------- + 0 +(1 row) + +SELECT strpos('', '') AS "pos_1"; + pos_1 +------- + 1 +(1 row) + +-- +-- test replace +-- +SELECT replace('abcdef', 'de', '45') AS "abc45f"; + abc45f +-------- + abc45f +(1 row) + +SELECT replace('yabadabadoo', 'ba', '123') AS "ya123da123doo"; + ya123da123doo +--------------- + ya123da123doo +(1 row) + +SELECT replace('yabadoo', 'bad', '') AS "yaoo"; + yaoo +------ + yaoo +(1 row) + +-- +-- test split_part +-- +select split_part('','@',1) AS "empty string"; + empty string +-------------- + +(1 row) + +select split_part('','@',-1) AS "empty string"; + empty string +-------------- + +(1 row) + +select split_part('joeuser@mydatabase','',1) AS "joeuser@mydatabase"; + joeuser@mydatabase +-------------------- + joeuser@mydatabase +(1 row) + +select split_part('joeuser@mydatabase','',2) AS "empty string"; + empty string +-------------- + +(1 row) + +select split_part('joeuser@mydatabase','',-1) AS "joeuser@mydatabase"; + joeuser@mydatabase +-------------------- + joeuser@mydatabase +(1 row) + +select split_part('joeuser@mydatabase','',-2) AS "empty string"; + empty string +-------------- + +(1 row) + +select split_part('joeuser@mydatabase','@',0) AS "an error"; +ERROR: field position must not be zero +select split_part('joeuser@mydatabase','@@',1) AS "joeuser@mydatabase"; + joeuser@mydatabase +-------------------- + joeuser@mydatabase +(1 row) + +select split_part('joeuser@mydatabase','@@',2) AS "empty string"; + empty string +-------------- + +(1 row) + +select split_part('joeuser@mydatabase','@',1) AS "joeuser"; + joeuser +--------- + joeuser +(1 row) + +select split_part('joeuser@mydatabase','@',2) AS "mydatabase"; + mydatabase +------------ + mydatabase +(1 row) + +select split_part('joeuser@mydatabase','@',3) AS "empty string"; + empty string +-------------- + +(1 row) + +select split_part('@joeuser@mydatabase@','@',2) AS "joeuser"; + joeuser +--------- + joeuser +(1 row) + +select split_part('joeuser@mydatabase','@',-1) AS "mydatabase"; + mydatabase +------------ + mydatabase +(1 row) + +select split_part('joeuser@mydatabase','@',-2) AS "joeuser"; + joeuser +--------- + joeuser +(1 row) + +select split_part('joeuser@mydatabase','@',-3) AS "empty string"; + empty string +-------------- + +(1 row) + +select split_part('@joeuser@mydatabase@','@',-2) AS "mydatabase"; + mydatabase +------------ + mydatabase +(1 row) + +-- +-- test to_hex +-- +select to_hex(256*256*256 - 1) AS "ffffff"; + ffffff +-------- + ffffff +(1 row) + +select to_hex(256::bigint*256::bigint*256::bigint*256::bigint - 1) AS "ffffffff"; + ffffffff +---------- + ffffffff +(1 row) + +-- +-- MD5 test suite - from IETF RFC 1321 +-- (see: ftp://ftp.rfc-editor.org/in-notes/rfc1321.txt) +-- +select md5('') = 'd41d8cd98f00b204e9800998ecf8427e' AS "TRUE"; + TRUE +------ + t +(1 row) + +select md5('a') = '0cc175b9c0f1b6a831c399e269772661' AS "TRUE"; + TRUE +------ + t +(1 row) + +select md5('abc') = '900150983cd24fb0d6963f7d28e17f72' AS "TRUE"; + TRUE +------ + t +(1 row) + +select md5('message digest') = 'f96b697d7cb7938d525a2f31aaf161d0' AS "TRUE"; + TRUE +------ + t +(1 row) + +select md5('abcdefghijklmnopqrstuvwxyz') = 'c3fcd3d76192e4007dfb496cca67e13b' AS "TRUE"; + TRUE +------ + t +(1 row) + +select md5('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789') = 'd174ab98d277d9f5a5611c2c9f419d9f' AS "TRUE"; + TRUE +------ + t +(1 row) + +select md5('12345678901234567890123456789012345678901234567890123456789012345678901234567890') = '57edf4a22be3c955ac49da2e2107b67a' AS "TRUE"; + TRUE +------ + t +(1 row) + +select md5(''::bytea) = 'd41d8cd98f00b204e9800998ecf8427e' AS "TRUE"; + TRUE +------ + t +(1 row) + +select md5('a'::bytea) = '0cc175b9c0f1b6a831c399e269772661' AS "TRUE"; + TRUE +------ + t +(1 row) + +select md5('abc'::bytea) = '900150983cd24fb0d6963f7d28e17f72' AS "TRUE"; + TRUE +------ + t +(1 row) + +select md5('message digest'::bytea) = 'f96b697d7cb7938d525a2f31aaf161d0' AS "TRUE"; + TRUE +------ + t +(1 row) + +select md5('abcdefghijklmnopqrstuvwxyz'::bytea) = 'c3fcd3d76192e4007dfb496cca67e13b' AS "TRUE"; + TRUE +------ + t +(1 row) + +select md5('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'::bytea) = 'd174ab98d277d9f5a5611c2c9f419d9f' AS "TRUE"; + TRUE +------ + t +(1 row) + +select md5('12345678901234567890123456789012345678901234567890123456789012345678901234567890'::bytea) = '57edf4a22be3c955ac49da2e2107b67a' AS "TRUE"; + TRUE +------ + t +(1 row) + diff --git a/ydb/tests/functional/postgresql/cases/strings.sql b/ydb/tests/functional/postgresql/cases/strings.sql new file mode 100644 index 00000000000..36422f89e44 --- /dev/null +++ b/ydb/tests/functional/postgresql/cases/strings.sql @@ -0,0 +1,196 @@ +-- +-- STRINGS +-- Test various data entry syntaxes. +-- + +-- SQL string continuation syntax +-- E021-03 character string literals +SELECT 'first line' +' - next line' + ' - third line' + AS "Three lines to one"; + +-- illegal string continuation syntax +SELECT 'first line' +' - next line' /* this comment is not allowed here */ +' - third line' + AS "Illegal comment within continuation"; + +SELECT U&'d\0061t\+000061' AS U&"d\0061t\+000061"; +SELECT U&'d!0061t\+000061' UESCAPE '!' AS U&"d*0061t\+000061" UESCAPE '*'; +SELECT U&'a\\b' AS "a\b"; + +SELECT U&' \' UESCAPE '!' AS "tricky"; +SELECT 'tricky' AS U&"\" UESCAPE '!'; + +SELECT U&'wrong: \061'; +SELECT U&'wrong: \+0061'; +SELECT U&'wrong: +0061' UESCAPE +; +SELECT U&'wrong: +0061' UESCAPE '+'; + +SELECT U&'wrong: \db99'; +SELECT U&'wrong: \db99xy'; +-- +-- test conversions between various string types +-- E021-10 implicit casting among the character data types +-- + +SELECT CAST(name 'namefield' AS text) AS "text(name)"; + +SELECT CAST(name 'namefield' AS char(10)) AS "char(name)"; + +SELECT CAST(name 'namefield' AS varchar) AS "varchar(name)"; + +-- +-- test SQL string functions +-- E### and T### are feature reference numbers from SQL99 +-- + +-- E021-09 trim function +SELECT TRIM(BOTH FROM ' bunch o blanks ') = 'bunch o blanks' AS "bunch o blanks"; + +SELECT TRIM(LEADING FROM ' bunch o blanks ') = 'bunch o blanks ' AS "bunch o blanks "; + +SELECT TRIM(TRAILING FROM ' bunch o blanks ') = ' bunch o blanks' AS " bunch o blanks"; + +SELECT TRIM(BOTH 'x' FROM 'xxxxxsome Xsxxxxx') = 'some Xs' AS "some Xs"; + +-- E021-06 substring expression +SELECT SUBSTRING('1234567890' FROM 3) = '34567890' AS "34567890"; + +SELECT SUBSTRING('1234567890' FROM 4 FOR 3) = '456' AS "456"; + +-- test overflow cases +SELECT SUBSTRING('string' FROM 2 FOR 2147483646) AS "tring"; +SELECT SUBSTRING('string' FROM -10 FOR 2147483646) AS "string"; +SELECT SUBSTRING('string' FROM -10 FOR -2147483646) AS "error"; + +-- substring() with just two arguments is not allowed by SQL spec; +-- we accept it, but we interpret the pattern as a POSIX regexp not SQL +SELECT SUBSTRING('abcdefg' FROM 'c.e') AS "cde"; + +-- With a parenthesized subexpression, return only what matches the subexpr +SELECT SUBSTRING('abcdefg' FROM 'b(.*)f') AS "cde"; + +-- T312 character overlay function +SELECT OVERLAY('abcdef' PLACING '45' FROM 4) AS "abc45f"; + +SELECT OVERLAY('yabadoo' PLACING 'daba' FROM 5) AS "yabadaba"; + +SELECT OVERLAY('yabadoo' PLACING 'daba' FROM 5 FOR 0) AS "yabadabadoo"; + +SELECT OVERLAY('babosa' PLACING 'ubb' FROM 2 FOR 4) AS "bubba"; + +-- +-- test implicit type conversion +-- + +-- E021-07 character concatenation +SELECT 'unknown' || ' and unknown' AS "Concat unknown types"; + +SELECT text 'text' || ' and unknown' AS "Concat text to unknown type"; + +-- +-- test length +-- + +SELECT length('abcdef') AS "length_6"; + +-- +-- test strpos +-- + +SELECT strpos('abcdef', 'cd') AS "pos_3"; + +SELECT strpos('abcdef', 'xy') AS "pos_0"; + +SELECT strpos('abcdef', '') AS "pos_1"; + +SELECT strpos('', 'xy') AS "pos_0"; + +SELECT strpos('', '') AS "pos_1"; + +-- +-- test replace +-- +SELECT replace('abcdef', 'de', '45') AS "abc45f"; + +SELECT replace('yabadabadoo', 'ba', '123') AS "ya123da123doo"; + +SELECT replace('yabadoo', 'bad', '') AS "yaoo"; + +-- +-- test split_part +-- +select split_part('','@',1) AS "empty string"; + +select split_part('','@',-1) AS "empty string"; + +select split_part('joeuser@mydatabase','',1) AS "joeuser@mydatabase"; + +select split_part('joeuser@mydatabase','',2) AS "empty string"; + +select split_part('joeuser@mydatabase','',-1) AS "joeuser@mydatabase"; + +select split_part('joeuser@mydatabase','',-2) AS "empty string"; + +select split_part('joeuser@mydatabase','@',0) AS "an error"; + +select split_part('joeuser@mydatabase','@@',1) AS "joeuser@mydatabase"; + +select split_part('joeuser@mydatabase','@@',2) AS "empty string"; + +select split_part('joeuser@mydatabase','@',1) AS "joeuser"; + +select split_part('joeuser@mydatabase','@',2) AS "mydatabase"; + +select split_part('joeuser@mydatabase','@',3) AS "empty string"; + +select split_part('@joeuser@mydatabase@','@',2) AS "joeuser"; + +select split_part('joeuser@mydatabase','@',-1) AS "mydatabase"; + +select split_part('joeuser@mydatabase','@',-2) AS "joeuser"; + +select split_part('joeuser@mydatabase','@',-3) AS "empty string"; + +select split_part('@joeuser@mydatabase@','@',-2) AS "mydatabase"; + +-- +-- test to_hex +-- +select to_hex(256*256*256 - 1) AS "ffffff"; + +select to_hex(256::bigint*256::bigint*256::bigint*256::bigint - 1) AS "ffffffff"; + +-- +-- MD5 test suite - from IETF RFC 1321 +-- (see: ftp://ftp.rfc-editor.org/in-notes/rfc1321.txt) +-- +select md5('') = 'd41d8cd98f00b204e9800998ecf8427e' AS "TRUE"; + +select md5('a') = '0cc175b9c0f1b6a831c399e269772661' AS "TRUE"; + +select md5('abc') = '900150983cd24fb0d6963f7d28e17f72' AS "TRUE"; + +select md5('message digest') = 'f96b697d7cb7938d525a2f31aaf161d0' AS "TRUE"; + +select md5('abcdefghijklmnopqrstuvwxyz') = 'c3fcd3d76192e4007dfb496cca67e13b' AS "TRUE"; + +select md5('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789') = 'd174ab98d277d9f5a5611c2c9f419d9f' AS "TRUE"; + +select md5('12345678901234567890123456789012345678901234567890123456789012345678901234567890') = '57edf4a22be3c955ac49da2e2107b67a' AS "TRUE"; + +select md5(''::bytea) = 'd41d8cd98f00b204e9800998ecf8427e' AS "TRUE"; + +select md5('a'::bytea) = '0cc175b9c0f1b6a831c399e269772661' AS "TRUE"; + +select md5('abc'::bytea) = '900150983cd24fb0d6963f7d28e17f72' AS "TRUE"; + +select md5('message digest'::bytea) = 'f96b697d7cb7938d525a2f31aaf161d0' AS "TRUE"; + +select md5('abcdefghijklmnopqrstuvwxyz'::bytea) = 'c3fcd3d76192e4007dfb496cca67e13b' AS "TRUE"; + +select md5('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'::bytea) = 'd174ab98d277d9f5a5611c2c9f419d9f' AS "TRUE"; + +select md5('12345678901234567890123456789012345678901234567890123456789012345678901234567890'::bytea) = '57edf4a22be3c955ac49da2e2107b67a' AS "TRUE"; diff --git a/ydb/tests/functional/postgresql/cases/text.out b/ydb/tests/functional/postgresql/cases/text.out new file mode 100644 index 00000000000..c22b52dfd8e --- /dev/null +++ b/ydb/tests/functional/postgresql/cases/text.out @@ -0,0 +1,42 @@ +-- +-- TEXT +-- +SELECT text 'this is a text string' = text 'this is a text string' AS true; + true +------ + t +(1 row) + +SELECT text 'this is a text string' = text 'this is a text strin' AS false; + false +------- + f +(1 row) + +/* + * various string functions + */ +select reverse('abcde'); + reverse +--------- + edcba +(1 row) + +select quote_literal(''); + quote_literal +--------------- + '' +(1 row) + +select quote_literal('abc'''); + quote_literal +--------------- + 'abc''' +(1 row) + +select quote_literal(e'\\'); + quote_literal +--------------- + E'\\' +(1 row) + diff --git a/ydb/tests/functional/postgresql/cases/text.sql b/ydb/tests/functional/postgresql/cases/text.sql new file mode 100644 index 00000000000..959aeed0167 --- /dev/null +++ b/ydb/tests/functional/postgresql/cases/text.sql @@ -0,0 +1,15 @@ +-- +-- TEXT +-- + +SELECT text 'this is a text string' = text 'this is a text string' AS true; + +SELECT text 'this is a text string' = text 'this is a text strin' AS false; + +/* + * various string functions + */ +select reverse('abcde'); +select quote_literal(''); +select quote_literal('abc'''); +select quote_literal(e'\\'); diff --git a/ydb/tests/functional/postgresql/common/__init__.py b/ydb/tests/functional/postgresql/common/__init__.py new file mode 100644 index 00000000000..20190e9ba29 --- /dev/null +++ b/ydb/tests/functional/postgresql/common/__init__.py @@ -0,0 +1,83 @@ +import sys +import logging +from pathlib import Path +from .differ import Differ + + +LOGGER = logging.getLogger(__name__) + + +def setup_logger(): + options = dict( + level=logging.DEBUG, + format='%(levelname)s: %(message)s', + datefmt='%Y-%m-%d %H:%M:%S', + stream=sys.stderr + ) + + logging.basicConfig(**options) + + +setup_logger() + + +def find_sql_tests(path): + tests = [] + + for sql_file in Path(path).glob('*.sql'): + if not sql_file.is_file(): + LOGGER.warning("'%s' is not a file", sql_file.absolute()) + continue + + out_files = list(get_out_files(sql_file)) + if not out_files: + LOGGER.warning("No .out files found for '%s'", sql_file.absolute()) + continue + + tests.append((sql_file.stem, (sql_file, out_files))) + + return tests + + +def diff_sql(run_output, sql, out): + min_diff = sys.maxsize + best_match = out[0] + best_diff = '' + + for out_file in out: + with open(out_file, 'rb') as f: + out_data = f.read() + + last_diff = Differ.diff(out_data, run_output) + diff_len = len(last_diff) + + if diff_len == 0: + return + + if diff_len < min_diff: + min_diff = diff_len + best_match = out_file + best_diff = last_diff + + LOGGER.info("No exact match for '%s'. Best match is '%s'", sql, best_match) + for line in best_diff: + LOGGER.debug(line) + + # We need assert to fail the test properly + assert min_diff == 0 + + +def get_out_files(sql_file): + base_name = sql_file.stem + out_file = sql_file.with_suffix('.out') + + if out_file.is_file(): + yield out_file + + for i in range(1, 10): + nth_out_file = out_file.with_stem('{}_{}'.format(base_name, i)) + + if not nth_out_file.is_file(): + break + + yield nth_out_file diff --git a/ydb/tests/functional/postgresql/common/differ.py b/ydb/tests/functional/postgresql/common/differ.py new file mode 100644 index 00000000000..f690cd94cf4 --- /dev/null +++ b/ydb/tests/functional/postgresql/common/differ.py @@ -0,0 +1,92 @@ +import difflib +import re + + +class Differ: + @classmethod + def diff(cls, left, right): + left = cls.__remove_pg_errors(left).splitlines(keepends=True) + right = cls.__remove_ydb_errors(right).splitlines(keepends=True) + left = list(filter(lambda str: str != b'\n', left)) + right = list(filter(lambda str: str != b'\n', right)) + + cls.__unify_tables(left, right) + return list(difflib.diff_bytes(difflib.unified_diff, left, right, n=0, fromfile=b'eth', tofile=b'out')) + + __reErr = re.compile(b'(^ERROR: [^\n]+)(?:\nLINE \\d+: [^\n]+(?:\n\\s*\\^\\s*)?)?(?:\n(?:HINT|DETAIL|CONTEXT): [^\n]+)*(?:\n|$)', + re.MULTILINE) + + __reYdbErr = re.compile(b'(^psql:[^\n]+\nIssues: \n)(?: *<main>:[^\n]+\n)*( *<main>:(\\d+:\\d+:)? Error: ([^\n]+)\n)\n?(?:\n|$)', re.MULTILINE) + + @classmethod + def __remove_pg_error_msgs(cls, s): + return cls.__reErr.sub(rb"\1", s) + + @classmethod + def __remove_ydb_error_msgs(cls, s): + return cls.__reYdbErr.sub(rb"ERROR: \4", s) + + @classmethod + def __remove_pg_errors(cls, s): + return cls.__reErr.sub(rb"QUERY ERROR\n", s) + + @classmethod + def __remove_ydb_errors(cls, s): + return cls.__reYdbErr.sub(rb"QUERY ERROR\n", s) + + __reUniversalTableMarker = re.compile(rb'^-{3,100}(?:\+-{3,100})*$') + __reTableEndMarker = re.compile(rb'^\(\d+ rows?\)$') + + @classmethod + def __reformat_table_row(cls, row, col_widths): + cells = [c.strip() for c in row[:-1].split(b'|')] + return b'|'.join(c.ljust(w) for (c, w) in zip(cells, col_widths)) + + @classmethod + def __remove_table_headers(cls, lines, header_line_numbers): + for i in reversed(header_line_numbers): + del lines[i] + del lines[i-1] + + @classmethod + def __unify_tables(cls, left, right): + left_headers = [] + right_headers = [] + ucols = [] + + in_table = False + R = enumerate(right) + for (i, l) in enumerate(left): + if in_table: + if cls.__reTableEndMarker.match(l): + in_table = False + continue + + j, r = next(R) + + left[i] = cls.__reformat_table_row(l, ucols) + right[j] = cls.__reformat_table_row(r, ucols) + + continue + + if cls.__reUniversalTableMarker.match(l): + for (j, r) in R: + if cls.__reUniversalTableMarker.match(r): + break + else: + continue + lcols = [len(c) for c in l[:-1].split(b'+')] + rcols = [len(c) for c in r[:-1].split(b'+')] + + if len(lcols) != len(rcols): + continue + + ucols = [max(lw, rw) for lw, rw in zip(lcols, rcols)] + + left_headers.append(i) + right_headers.append(j) + + in_table = True + + cls.__remove_table_headers(left, left_headers) + cls.__remove_table_headers(right, right_headers) diff --git a/ydb/tests/functional/postgresql/test_postgres.py b/ydb/tests/functional/postgresql/test_postgres.py new file mode 100644 index 00000000000..86f1ca5dffe --- /dev/null +++ b/ydb/tests/functional/postgresql/test_postgres.py @@ -0,0 +1,105 @@ +from ydb.tests.library.common import yatest_common +from ydb.tests.library.harness.kikimr_cluster import kikimr_cluster_factory +from ydb.tests.library.harness.kikimr_config import KikimrConfigGenerator +from ydb.tests.library.harness.util import LogLevels + +from common import find_sql_tests, diff_sql + +from yatest.common import execute + +import os +import pytest +import time +import re + + +DATA_PATH = yatest_common.source_path('ydb/tests/functional/postgresql/cases') + + +def get_unique_path_case(sub_folder, file): + test_name = yatest_common.context.test_name or "" + test_name = test_name.replace(':', '_') + lb, rb = re.escape('['), re.escape(']') + test_case = re.search(lb + '(.+?)' + rb, test_name) + assert test_case + dirpath = os.path.join(yatest_common.output_path(), test_case.group(1), sub_folder) + if not os.path.exists(dirpath): + os.makedirs(dirpath, exist_ok=True) + return os.path.join(dirpath, file) + + +def get_tests(): + _, tests = zip(*find_sql_tests(DATA_PATH)) + return tests + + +def get_ids(): + ids, _ = zip(*find_sql_tests(DATA_PATH)) + return ids + + +def psql_binary_path(): + if os.getenv('PSQL_BINARY'): + return yatest_common.binary_path(os.getenv('PSQL_BINARY')) + else: + return yatest_common.work_path('psql/psql') + + +def pgwire_binary_path(): + return yatest_common.binary_path('ydb/apps/pgwire/pgwire') + + +def execute_binary(binary_name, cmd, wait, join_stderr=False): + stdin, stderr, stdout = map( + lambda x: get_unique_path_case(binary_name, x), + ['stdin', 'stderr', 'stdout'] + ) + stdin_file = open(stdin, 'w') + stdout_file = open(stdout, 'w') + stderr_file = stdout_file + if not join_stderr: + stderr_file = open(stderr, 'w') + process = execute( + cmd, + stdin=stdin_file, + stderr=stderr_file, + stdout=stdout_file, + wait=wait + ) + return process, stdin, stderr, stdout + + +class BasePostgresTest(object): + @classmethod + def setup_class(cls): + cls.cluster = kikimr_cluster_factory(KikimrConfigGenerator( + additional_log_configs={'KQP_YQL': LogLevels.DEBUG, 'KQP_COMPILE_ACTOR': LogLevels.DEBUG, 'KQP_COMPILE_REQUEST': LogLevels.DEBUG} + )) + cls.cluster.start() + cls.endpoint = '%s:%s' % (cls.cluster.nodes[1].host, cls.cluster.nodes[1].port) + time.sleep(2) + cls.pgwire, _, _, _ = execute_binary( + 'pgwire', + [pgwire_binary_path(), '--endpoint={}'.format(cls.endpoint), '--stderr'], + wait=False + ) + time.sleep(2) + + @classmethod + def teardown_class(cls): + cls.pgwire.terminate() + cls.cluster.stop() + + +class TestPostgresSuite(BasePostgresTest): + @pytest.mark.parametrize(['sql', 'out'], get_tests(), ids=get_ids()) + def test_postgres_suite(self, sql, out): + _, _, psql_stderr, psql_stdout = execute_binary( + 'psql', + [psql_binary_path(), 'postgresql://root:@localhost:5432/Root', '-a', '-f', sql], + wait=True, + join_stderr=True + ) + + with open(psql_stdout, 'rb') as stdout_file: + diff_sql(stdout_file.read(), sql, out) |