aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorqrort <qrort@yandex-team.com>2023-04-05 16:59:33 +0300
committerqrort <qrort@yandex-team.com>2023-04-05 16:59:33 +0300
commit0cd25c0602c477754d77ad095746c3e141179dde (patch)
tree683c6396c7700b8832b555fb93d57a805f46d243
parent5c2106832696ae404f02462aac3a0a0ac735e728 (diff)
downloadydb-0cd25c0602c477754d77ad095746c3e141179dde.tar.gz
pg regression tests over YDB
-rw-r--r--ydb/tests/functional/postgresql/cases/boolean.out223
-rw-r--r--ydb/tests/functional/postgresql/cases/boolean.sql92
-rw-r--r--ydb/tests/functional/postgresql/cases/char.out11
-rw-r--r--ydb/tests/functional/postgresql/cases/char.sql8
-rw-r--r--ydb/tests/functional/postgresql/cases/char_1.out11
-rw-r--r--ydb/tests/functional/postgresql/cases/char_2.out11
-rw-r--r--ydb/tests/functional/postgresql/cases/comments.out63
-rw-r--r--ydb/tests/functional/postgresql/cases/comments.sql40
-rw-r--r--ydb/tests/functional/postgresql/cases/float4.out65
-rw-r--r--ydb/tests/functional/postgresql/cases/float4.sql21
-rw-r--r--ydb/tests/functional/postgresql/cases/float8.out234
-rw-r--r--ydb/tests/functional/postgresql/cases/float8.sql63
-rw-r--r--ydb/tests/functional/postgresql/cases/horology.out331
-rw-r--r--ydb/tests/functional/postgresql/cases/horology.sql158
-rw-r--r--ydb/tests/functional/postgresql/cases/int2.out65
-rw-r--r--ydb/tests/functional/postgresql/cases/int2.sql31
-rw-r--r--ydb/tests/functional/postgresql/cases/int4.out185
-rw-r--r--ydb/tests/functional/postgresql/cases/int4.sql85
-rw-r--r--ydb/tests/functional/postgresql/cases/int8.out160
-rw-r--r--ydb/tests/functional/postgresql/cases/int8.sql77
-rw-r--r--ydb/tests/functional/postgresql/cases/name.out17
-rw-r--r--ydb/tests/functional/postgresql/cases/name.sql9
-rw-r--r--ydb/tests/functional/postgresql/cases/numeric.out352
-rw-r--r--ydb/tests/functional/postgresql/cases/numeric.sql122
-rw-r--r--ydb/tests/functional/postgresql/cases/strings.out480
-rw-r--r--ydb/tests/functional/postgresql/cases/strings.sql196
-rw-r--r--ydb/tests/functional/postgresql/cases/text.out42
-rw-r--r--ydb/tests/functional/postgresql/cases/text.sql15
-rw-r--r--ydb/tests/functional/postgresql/common/__init__.py83
-rw-r--r--ydb/tests/functional/postgresql/common/differ.py92
-rw-r--r--ydb/tests/functional/postgresql/test_postgres.py105
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)