aboutsummaryrefslogtreecommitdiffstats
path: root/yql/essentials/tests/s-expressions/suites/EquiJoinMerge
diff options
context:
space:
mode:
authorAlexander Smirnov <alex@ydb.tech>2024-11-20 11:14:58 +0000
committerAlexander Smirnov <alex@ydb.tech>2024-11-20 11:14:58 +0000
commit31773f157bf8164364649b5f470f52dece0a4317 (patch)
tree33d0f7eef45303ab68cf08ab381ce5e5e36c5240 /yql/essentials/tests/s-expressions/suites/EquiJoinMerge
parent2c7938962d8689e175574fc1e817c05049f27905 (diff)
parenteff600952d5dfe17942f38f510a8ac2b203bb3a5 (diff)
downloadydb-31773f157bf8164364649b5f470f52dece0a4317.tar.gz
Merge branch 'rightlib' into mergelibs-241120-1113
Diffstat (limited to 'yql/essentials/tests/s-expressions/suites/EquiJoinMerge')
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Bug2333.cfg3
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Bug2333.yql39
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Cross2_12.yql17
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Exclusion2_12.yql19
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Exclusion2_1o2o.yql19
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Exclusion2key2_12.yql19
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Exclusion2key2_1o2o.yql19
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Full2_12.yql19
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Full2_12u.yql19
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Full2_1o2o.yql19
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Full2_1u2.yql19
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Full2_1u2u.yql19
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Full2key12_12.yql19
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Full2key12_1o2o.yql19
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Inner2_12.yql19
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Inner2_12o.yql19
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Inner2_1o2.yql19
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Inner2_1o2o.yql19
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Inner2key2_12.yql19
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Inner2key2_12o.yql19
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Inner2key2_1o2.yql19
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Inner2key2_1o2o.yql19
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/InnerInner3_123.yql24
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/InnerInner3_1o2o3o.yql24
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/InnerInner3key2_123.yql22
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/InnerInner3key2_1o2o3o.yql22
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/JoinWithNonStrict.cfg3
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/JoinWithNonStrict.yql19
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/JoinWithSortedDesc.cfg3
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/JoinWithSortedDesc.yql39
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Left2_12.yql19
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Left2_1o2o.yql19
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Left2key2_12.yql19
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Left2key2_1o2o.yql19
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/LeftOnly2_12.yql17
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/LeftOnly2_1o2o.yql17
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/LeftOnly2key2_12.yql17
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/LeftOnly2key2_1o2o.yql17
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/LeftSemi2_12.yql17
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/LeftSemi2_1o2o.yql17
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/LeftSemi2key2_12.yql17
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/LeftSemi2key2_1o2o.yql17
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Right2_12.yql19
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Right2_1o2o.yql19
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Right2key2_12.yql19
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Right2key2_1o2o.yql19
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/RightOnly2_12.yql17
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/RightOnly2_1o2o.yql17
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/RightOnly2key2_12.yql17
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/RightOnly2key2_1o2o.yql17
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/RightSemi2_12.yql17
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/RightSemi2_1o2o.yql17
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/RightSemi2key2_12.yql19
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/RightSemi2key2_1o2o.yql19
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/bug2333_l.txt11
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/bug2333_l.txt.attr3
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/bug2333_r.txt5
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/bug2333_r.txt.attr3
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/default.cfg9
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/infer_scheme.txt4
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/infer_scheme.txt.attr3
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input1.txt4
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input1.txt.attr3
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input1opt.txt4
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input1opt.txt.attr3
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input1u.txt3
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input1u.txt.attr3
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input2.txt4
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input2.txt.attr3
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input2opt.txt4
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input2opt.txt.attr3
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input2u.txt6
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input2u.txt.attr3
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input3.txt4
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input3.txt.attr3
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input3opt.txt4
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input3opt.txt.attr3
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/sorted_desc.txt4
-rw-r--r--yql/essentials/tests/s-expressions/suites/EquiJoinMerge/sorted_desc.txt.attr46
79 files changed, 1149 insertions, 0 deletions
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Bug2333.cfg b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Bug2333.cfg
new file mode 100644
index 0000000000..06f0f4538a
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Bug2333.cfg
@@ -0,0 +1,3 @@
+in Input1 bug2333_l.txt
+in Input2 bug2333_r.txt
+res result.txt
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Bug2333.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Bug2333.yql
new file mode 100644
index 0000000000..f6b50489a6
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Bug2333.yql
@@ -0,0 +1,39 @@
+(
+(import aggregate_module '"/lib/yql/aggregate.yql")
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"2"))
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergeunsortedfactor" '"0.8"))
+(let world (block '(
+ (let x (Read! world (DataSource '"yt" '"plato") (MrTableConcat (Key '('table (String '"Input1")))) '('"key") '()))
+ (let world (Left! x))
+ (let table1 (Right! x))
+ (let x (Read! world (DataSource '"yt" '"plato") (MrTableConcat (Key '('table (String '"Input2")))) '('"key") '()))
+ (let world (Left! x))
+ (let table2 (Right! x))
+ (let output (block '(
+ (let select (block '(
+ (let core (EquiJoin '(table1 '"L") '(table2 '"R") '('Left '"L" '"R" '('"L" '"key") '('"R" '"key") '()) '()))
+ (let core (FlatMap core (lambda '(row) (AsList (AsStruct '('"L.key" (Member row '"L.key")) '('"R.key" (Member row '"R.key")) '('"IsJoined" ("!=" ("Not" ("Exists" (SqlColumn row '"key" '"R"))) (Bool '"true"))))))))
+ (let core (Aggregate core '('"IsJoined") '('('Count0 (Apply (bind aggregate_module '"count_traits_factory") (TypeOf core) (lambda '(row) (SqlColumn row '"key" '"L")))))))
+ (let core (FlatMap core (lambda '(row) (block '(
+ (let res (AsStruct '('"IsJoined" (Member row '"IsJoined")) '('"column1" (Member row 'Count0))))
+ (let res (AsList res))
+ (return res)
+ )))))
+ (return core)
+ )))
+ (return select)
+ )))
+ (let world (block '(
+ (let result_sink (DataSink 'result))
+ (let world (Write! world result_sink (Key) output '('('type) '('autoref) '('columns '('"IsJoined" '"column1")))))
+ (return (Commit! world result_sink))
+ )))
+ (return world)
+)))
+(let world (block '(
+ (let plato_sink (DataSink '"yt" '"plato"))
+ (let world (Commit! world plato_sink))
+ (return world)
+)))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Cross2_12.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Cross2_12.yql
new file mode 100644
index 0000000000..9b331b9f82
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Cross2_12.yql
@@ -0,0 +1,17 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('Cross '"a" '"b" '() '() '())
+ '()))
+(let res_sink (DataSink 'result))
+(let world (Write! world res_sink (Key) join '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Exclusion2_12.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Exclusion2_12.yql
new file mode 100644
index 0000000000..3518a4c5d4
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Exclusion2_12.yql
@@ -0,0 +1,19 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('Exclusion '"a" '"b" '('"a" '"key") '('"b" '"key" ) '())
+ '()))
+(let res_sink (DataSink 'result))
+(let sortDir '((Bool 'true) (Bool 'true)))
+(let keySelector (lambda '(row) '((Member row '"a.key") (Member row '"a.subkey"))))
+(let world (Write! world res_sink (Key) (Sort join sortDir keySelector) '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Exclusion2_1o2o.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Exclusion2_1o2o.yql
new file mode 100644
index 0000000000..90b210133a
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Exclusion2_1o2o.yql
@@ -0,0 +1,19 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1opt))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2opt))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('Exclusion '"a" '"b" '('"a" '"key") '('"b" '"key" ) '())
+ '()))
+(let res_sink (DataSink 'result))
+(let sortDir '((Bool 'true) (Bool 'true)))
+(let keySelector (lambda '(row) '((Member row '"a.key") (Member row '"a.subkey"))))
+(let world (Write! world res_sink (Key) (Sort join sortDir keySelector) '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Exclusion2key2_12.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Exclusion2key2_12.yql
new file mode 100644
index 0000000000..65f4e896c0
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Exclusion2key2_12.yql
@@ -0,0 +1,19 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('Exclusion '"a" '"b" '('"a" '"key" '"a" '"subkey") '('"b" '"key" '"b" '"subkey") '())
+ '()))
+(let res_sink (DataSink 'result))
+(let sortDir '((Bool 'true) (Bool 'true)))
+(let keySelector (lambda '(row) '((Member row '"a.key") (Member row '"a.subkey"))))
+(let world (Write! world res_sink (Key) (Sort join sortDir keySelector) '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Exclusion2key2_1o2o.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Exclusion2key2_1o2o.yql
new file mode 100644
index 0000000000..a6f66f231e
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Exclusion2key2_1o2o.yql
@@ -0,0 +1,19 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1opt))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2opt))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('Exclusion '"a" '"b" '('"a" '"key" '"a" '"subkey") '('"b" '"key" '"b" '"subkey") '())
+ '()))
+(let res_sink (DataSink 'result))
+(let sortDir '((Bool 'true) (Bool 'true)))
+(let keySelector (lambda '(row) '((Member row '"a.key") (Member row '"a.subkey"))))
+(let world (Write! world res_sink (Key) (Sort join sortDir keySelector) '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Full2_12.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Full2_12.yql
new file mode 100644
index 0000000000..ebb11c9560
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Full2_12.yql
@@ -0,0 +1,19 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('Full '"a" '"b" '('"a" '"key") '('"b" '"key" ) '())
+ '()))
+(let res_sink (DataSink 'result))
+(let sortDir '((Bool 'true) (Bool 'true)))
+(let keySelector (lambda '(row) '((Member row '"a.key") (Member row '"a.subkey"))))
+(let world (Write! world res_sink (Key) (Sort join sortDir keySelector) '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Full2_12u.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Full2_12u.yql
new file mode 100644
index 0000000000..9047dae5cb
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Full2_12u.yql
@@ -0,0 +1,19 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2u))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('Full '"a" '"b" '('"a" '"key") '('"b" '"key" ) '())
+ '()))
+(let res_sink (DataSink 'result))
+(let sortDir '((Bool 'true) (Bool 'true)))
+(let keySelector (lambda '(row) '((Member row '"a.key") (Member row '"a.subkey"))))
+(let world (Write! world res_sink (Key) (Sort join sortDir keySelector) '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Full2_1o2o.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Full2_1o2o.yql
new file mode 100644
index 0000000000..4f3b39aead
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Full2_1o2o.yql
@@ -0,0 +1,19 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1opt))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2opt))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('Full '"a" '"b" '('"a" '"key") '('"b" '"key" ) '())
+ '()))
+(let res_sink (DataSink 'result))
+(let sortDir '((Bool 'true) (Bool 'true)))
+(let keySelector (lambda '(row) '((Member row '"a.key") (Member row '"a.subkey"))))
+(let world (Write! world res_sink (Key) (Sort join sortDir keySelector) '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Full2_1u2.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Full2_1u2.yql
new file mode 100644
index 0000000000..cce29bdeba
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Full2_1u2.yql
@@ -0,0 +1,19 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1u))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('Full '"a" '"b" '('"a" '"key") '('"b" '"key" ) '())
+ '()))
+(let res_sink (DataSink 'result))
+(let sortDir '((Bool 'true) (Bool 'true)))
+(let keySelector (lambda '(row) '((Member row '"a.key") (Member row '"a.subkey"))))
+(let world (Write! world res_sink (Key) (Sort join sortDir keySelector) '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Full2_1u2u.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Full2_1u2u.yql
new file mode 100644
index 0000000000..b2025d9f13
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Full2_1u2u.yql
@@ -0,0 +1,19 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1u))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2u))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('Full '"a" '"b" '('"a" '"key") '('"b" '"key" ) '())
+ '()))
+(let res_sink (DataSink 'result))
+(let sortDir '((Bool 'true) (Bool 'true)))
+(let keySelector (lambda '(row) '((Member row '"a.key") (Member row '"a.subkey"))))
+(let world (Write! world res_sink (Key) (Sort join sortDir keySelector) '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Full2key12_12.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Full2key12_12.yql
new file mode 100644
index 0000000000..e73d11378c
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Full2key12_12.yql
@@ -0,0 +1,19 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('Full '"a" '"b" '('"a" '"key" '"a" '"subkey") '('"b" '"key" '"b" '"subkey") '())
+ '()))
+(let res_sink (DataSink 'result))
+(let sortDir '((Bool 'true) (Bool 'true)))
+(let keySelector (lambda '(row) '((Member row '"a.key") (Member row '"a.subkey"))))
+(let world (Write! world res_sink (Key) (Sort join sortDir keySelector) '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Full2key12_1o2o.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Full2key12_1o2o.yql
new file mode 100644
index 0000000000..02fe6bd9b7
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Full2key12_1o2o.yql
@@ -0,0 +1,19 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1opt))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2opt))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('Full '"a" '"b" '('"a" '"key" '"a" '"subkey") '('"b" '"key" '"b" '"subkey") '())
+ '()))
+(let res_sink (DataSink 'result))
+(let sortDir '((Bool 'true) (Bool 'true)))
+(let keySelector (lambda '(row) '((Member row '"a.key") (Member row '"a.subkey"))))
+(let world (Write! world res_sink (Key) (Sort join sortDir keySelector) '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Inner2_12.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Inner2_12.yql
new file mode 100644
index 0000000000..6a0eca2a30
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Inner2_12.yql
@@ -0,0 +1,19 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('Inner '"a" '"b" '('"a" '"key") '('"b" '"key" ) '())
+ '()))
+(let res_sink (DataSink 'result))
+(let sortDir '((Bool 'true) (Bool 'true) (Bool 'true)))
+(let keySelector (lambda '(row) '((Member row '"a.key") (Member row '"a.subkey") (Member row '"b.value2"))))
+(let world (Write! world res_sink (Key) (Sort join sortDir keySelector) '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Inner2_12o.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Inner2_12o.yql
new file mode 100644
index 0000000000..4aa5328146
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Inner2_12o.yql
@@ -0,0 +1,19 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2opt))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('Inner '"a" '"b" '('"a" '"key") '('"b" '"key" ) '())
+ '()))
+(let res_sink (DataSink 'result))
+(let sortDir '((Bool 'true) (Bool 'true) (Bool 'true)))
+(let keySelector (lambda '(row) '((Member row '"a.key") (Member row '"a.subkey") (Member row '"b.value2"))))
+(let world (Write! world res_sink (Key) (Sort join sortDir keySelector) '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Inner2_1o2.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Inner2_1o2.yql
new file mode 100644
index 0000000000..e114fdd320
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Inner2_1o2.yql
@@ -0,0 +1,19 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1opt))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('Inner '"a" '"b" '('"a" '"key") '('"b" '"key" ) '())
+ '()))
+(let res_sink (DataSink 'result))
+(let sortDir '((Bool 'true) (Bool 'true) (Bool 'true)))
+(let keySelector (lambda '(row) '((Member row '"a.key") (Member row '"a.subkey") (Member row '"b.value2"))))
+(let world (Write! world res_sink (Key) (Sort join sortDir keySelector) '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Inner2_1o2o.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Inner2_1o2o.yql
new file mode 100644
index 0000000000..dfef6fe6c8
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Inner2_1o2o.yql
@@ -0,0 +1,19 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1opt))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2opt))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('Inner '"a" '"b" '('"a" '"key") '('"b" '"key" ) '())
+ '()))
+(let res_sink (DataSink 'result))
+(let sortDir '((Bool 'true) (Bool 'true) (Bool 'true)))
+(let keySelector (lambda '(row) '((Member row '"a.key") (Member row '"a.subkey") (Member row '"b.value2"))))
+(let world (Write! world res_sink (Key) (Sort join sortDir keySelector) '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Inner2key2_12.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Inner2key2_12.yql
new file mode 100644
index 0000000000..28175bd604
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Inner2key2_12.yql
@@ -0,0 +1,19 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('Inner '"a" '"b" '('"a" '"key" '"a" '"subkey") '('"b" '"key" '"b" '"subkey") '())
+ '()))
+(let res_sink (DataSink 'result))
+(let sortDir '((Bool 'true) (Bool 'true) (Bool 'true)))
+(let keySelector (lambda '(row) '((Member row '"a.key") (Member row '"a.subkey") (Member row '"b.value2"))))
+(let world (Write! world res_sink (Key) (Sort join sortDir keySelector) '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Inner2key2_12o.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Inner2key2_12o.yql
new file mode 100644
index 0000000000..981d1a26d3
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Inner2key2_12o.yql
@@ -0,0 +1,19 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2opt))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('Inner '"a" '"b" '('"a" '"key" '"a" '"subkey") '('"b" '"key" '"b" '"subkey") '())
+ '()))
+(let res_sink (DataSink 'result))
+(let sortDir '((Bool 'true) (Bool 'true) (Bool 'true)))
+(let keySelector (lambda '(row) '((Member row '"a.key") (Member row '"a.subkey") (Member row '"b.value2"))))
+(let world (Write! world res_sink (Key) (Sort join sortDir keySelector) '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Inner2key2_1o2.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Inner2key2_1o2.yql
new file mode 100644
index 0000000000..f217a74ac3
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Inner2key2_1o2.yql
@@ -0,0 +1,19 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1opt))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('Inner '"a" '"b" '('"a" '"key" '"a" '"subkey") '('"b" '"key" '"b" '"subkey") '())
+ '()))
+(let res_sink (DataSink 'result))
+(let sortDir '((Bool 'true) (Bool 'true) (Bool 'true)))
+(let keySelector (lambda '(row) '((Member row '"a.key") (Member row '"a.subkey") (Member row '"b.value2"))))
+(let world (Write! world res_sink (Key) (Sort join sortDir keySelector) '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Inner2key2_1o2o.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Inner2key2_1o2o.yql
new file mode 100644
index 0000000000..c397a2f86c
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Inner2key2_1o2o.yql
@@ -0,0 +1,19 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1opt))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2opt))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('Inner '"a" '"b" '('"a" '"key" '"a" '"subkey") '('"b" '"key" '"b" '"subkey") '())
+ '()))
+(let res_sink (DataSink 'result))
+(let sortDir '((Bool 'true) (Bool 'true) (Bool 'true)))
+(let keySelector (lambda '(row) '((Member row '"a.key") (Member row '"a.subkey") (Member row '"b.value2"))))
+(let world (Write! world res_sink (Key) (Sort join sortDir keySelector) '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/InnerInner3_123.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/InnerInner3_123.yql
new file mode 100644
index 0000000000..5f4c36f6e0
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/InnerInner3_123.yql
@@ -0,0 +1,24 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinallowcolumnrenames" 'true))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input3))) '('key 'subkey 'value3) '()))
+(let world (Left! x))
+(let table3 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b) '(table3 'c)
+ '('Inner
+ '('Inner '"a" '"b" '('"a" '"key") '('"b" '"key" ) '())
+ '"c" '('"a" '"key") '('"c" '"key") '()) '()))
+(let res_sink (DataSink 'result))
+(let sortDir '((Bool 'true) (Bool 'true) (Bool 'true)))
+(let keySelector (lambda '(row) '((Member row '"a.key") (Member row '"a.subkey") (Member row '"b.value2"))))
+(let world (Write! world res_sink (Key) (Sort join sortDir keySelector) '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/InnerInner3_1o2o3o.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/InnerInner3_1o2o3o.yql
new file mode 100644
index 0000000000..959778c020
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/InnerInner3_1o2o3o.yql
@@ -0,0 +1,24 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinallowcolumnrenames" 'true))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1opt))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2opt))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input3opt))) '('key 'subkey 'value3) '()))
+(let world (Left! x))
+(let table3 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b) '(table3 'c)
+ '('Inner
+ '('Inner '"a" '"b" '('"a" '"key") '('"b" '"key" ) '())
+ '"c" '('"a" '"key") '('"c" '"key") '()) '()))
+(let res_sink (DataSink 'result))
+(let sortDir '((Bool 'true) (Bool 'true) (Bool 'true)))
+(let keySelector (lambda '(row) '((Member row '"a.key") (Member row '"a.subkey") (Member row '"b.value2"))))
+(let world (Write! world res_sink (Key) (Sort join sortDir keySelector) '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/InnerInner3key2_123.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/InnerInner3key2_123.yql
new file mode 100644
index 0000000000..af7186040d
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/InnerInner3key2_123.yql
@@ -0,0 +1,22 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinallowcolumnrenames" 'true))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input3))) '('key 'subkey 'value3) '()))
+(let world (Left! x))
+(let table3 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b) '(table3 'c)
+ '('Inner
+ '('Inner '"a" '"b" '('"a" '"key" '"a" '"subkey") '('"b" '"key" '"b" '"subkey") '())
+ '"c" '('"a" '"key" '"a" '"subkey") '('"c" '"key" '"c" '"subkey") '()) '()))
+(let res_sink (DataSink 'result))
+(let world (Write! world res_sink (Key) join '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/InnerInner3key2_1o2o3o.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/InnerInner3key2_1o2o3o.yql
new file mode 100644
index 0000000000..f1e98e1303
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/InnerInner3key2_1o2o3o.yql
@@ -0,0 +1,22 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinallowcolumnrenames" 'true))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1opt))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2opt))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input3opt))) '('key 'subkey 'value3) '()))
+(let world (Left! x))
+(let table3 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b) '(table3 'c)
+ '('Inner
+ '('Inner '"a" '"b" '('"a" '"key" '"a" '"subkey") '('"b" '"key" '"b" '"subkey") '())
+ '"c" '('"a" '"key" '"a" '"subkey") '('"c" '"key" '"c" '"subkey") '()) '()))
+(let res_sink (DataSink 'result))
+(let world (Write! world res_sink (Key) join '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/JoinWithNonStrict.cfg b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/JoinWithNonStrict.cfg
new file mode 100644
index 0000000000..f73a18f37a
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/JoinWithNonStrict.cfg
@@ -0,0 +1,3 @@
+in Input1 input1.txt
+in Input2 infer_scheme.txt
+res result.txt
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/JoinWithNonStrict.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/JoinWithNonStrict.yql
new file mode 100644
index 0000000000..46c35ef2a9
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/JoinWithNonStrict.yql
@@ -0,0 +1,19 @@
+(
+
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergeunsortedfactor" '"3.0"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1))) '('key 'subkey 'value) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2))) '('key) '('('infer_scheme))))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('Inner '"a" '"b" '('"a" '"key") '('"b" '"key" ) '())
+ '()))
+(let res_sink (DataSink 'result))
+(let world (Write! world res_sink (Key) join '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/JoinWithSortedDesc.cfg b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/JoinWithSortedDesc.cfg
new file mode 100644
index 0000000000..68f6b6b97b
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/JoinWithSortedDesc.cfg
@@ -0,0 +1,3 @@
+in Input1 bug2333_l.txt
+in Input2 sorted_desc.txt
+res result.txt
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/JoinWithSortedDesc.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/JoinWithSortedDesc.yql
new file mode 100644
index 0000000000..0cd1abd0e9
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/JoinWithSortedDesc.yql
@@ -0,0 +1,39 @@
+(
+(import aggregate_module '"/lib/yql/aggregate.yql")
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"2"))
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergeunsortedfactor" '"2"))
+(let world (block '(
+ (let x (Read! world (DataSource '"yt" '"plato") (MrTableConcat (Key '('table (String '"Input1")))) '('"key") '()))
+ (let world (Left! x))
+ (let table1 (Right! x))
+ (let x (Read! world (DataSource '"yt" '"plato") (MrTableConcat (Key '('table (String '"Input2")))) '('"key") '()))
+ (let world (Left! x))
+ (let table2 (Right! x))
+ (let output (block '(
+ (let select (block '(
+ (let core (EquiJoin '(table1 '"L") '(table2 '"R") '('Left '"L" '"R" '('"L" '"key") '('"R" '"key") '()) '()))
+ (let core (FlatMap core (lambda '(row) (AsList (AsStruct '('"L.key" (Member row '"L.key")) '('"R.key" (Member row '"R.key")) '('"IsJoined" ("!=" ("Not" ("Exists" (SqlColumn row '"key" '"R"))) (Bool '"true"))))))))
+ (let core (Aggregate core '('"IsJoined") '('('Count0 (Apply (bind aggregate_module '"count_traits_factory") (TypeOf core) (lambda '(row) (SqlColumn row '"key" '"L")))))))
+ (let core (FlatMap core (lambda '(row) (block '(
+ (let res (AsStruct '('"IsJoined" (Member row '"IsJoined")) '('"column1" (Member row 'Count0))))
+ (let res (AsList res))
+ (return res)
+ )))))
+ (return core)
+ )))
+ (return select)
+ )))
+ (let world (block '(
+ (let result_sink (DataSink 'result))
+ (let world (Write! world result_sink (Key) output '('('type) '('autoref) '('columns '('"IsJoined" '"column1")))))
+ (return (Commit! world result_sink))
+ )))
+ (return world)
+)))
+(let world (block '(
+ (let plato_sink (DataSink '"yt" '"plato"))
+ (let world (Commit! world plato_sink))
+ (return world)
+)))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Left2_12.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Left2_12.yql
new file mode 100644
index 0000000000..203d420f05
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Left2_12.yql
@@ -0,0 +1,19 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('Left '"a" '"b" '('"a" '"key") '('"b" '"key" ) '())
+ '()))
+(let res_sink (DataSink 'result))
+(let sortDir '((Bool 'true) (Bool 'true) (Bool 'true)))
+(let keySelector (lambda '(row) '((Member row '"a.key") (Member row '"a.subkey") (Member row '"b.value2"))))
+(let world (Write! world res_sink (Key) (Sort join sortDir keySelector) '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Left2_1o2o.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Left2_1o2o.yql
new file mode 100644
index 0000000000..1cc15375b5
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Left2_1o2o.yql
@@ -0,0 +1,19 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1opt))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2opt))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('Left '"a" '"b" '('"a" '"key") '('"b" '"key" ) '())
+ '()))
+(let res_sink (DataSink 'result))
+(let sortDir '((Bool 'true) (Bool 'true) (Bool 'true)))
+(let keySelector (lambda '(row) '((Member row '"a.key") (Member row '"a.subkey") (Member row '"b.value2"))))
+(let world (Write! world res_sink (Key) (Sort join sortDir keySelector) '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Left2key2_12.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Left2key2_12.yql
new file mode 100644
index 0000000000..f376fd59f5
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Left2key2_12.yql
@@ -0,0 +1,19 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('Left '"a" '"b" '('"a" '"key" '"a" '"subkey") '('"b" '"key" '"b" '"subkey") '())
+ '()))
+(let res_sink (DataSink 'result))
+(let sortDir '((Bool 'true) (Bool 'true)))
+(let keySelector (lambda '(row) '((Member row '"a.key") (Member row '"a.subkey"))))
+(let world (Write! world res_sink (Key) (Sort join sortDir keySelector) '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Left2key2_1o2o.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Left2key2_1o2o.yql
new file mode 100644
index 0000000000..ecbcf52649
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Left2key2_1o2o.yql
@@ -0,0 +1,19 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1opt))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2opt))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('Left '"a" '"b" '('"a" '"key" '"a" '"subkey") '('"b" '"key" '"b" '"subkey") '())
+ '()))
+(let res_sink (DataSink 'result))
+(let sortDir '((Bool 'true) (Bool 'true)))
+(let keySelector (lambda '(row) '((Member row '"a.key") (Member row '"a.subkey"))))
+(let world (Write! world res_sink (Key) (Sort join sortDir keySelector) '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/LeftOnly2_12.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/LeftOnly2_12.yql
new file mode 100644
index 0000000000..7ad7499a36
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/LeftOnly2_12.yql
@@ -0,0 +1,17 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('LeftOnly '"a" '"b" '('"a" '"key") '('"b" '"key" ) '())
+ '()))
+(let res_sink (DataSink 'result))
+(let world (Write! world res_sink (Key) join '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/LeftOnly2_1o2o.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/LeftOnly2_1o2o.yql
new file mode 100644
index 0000000000..eee4c48726
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/LeftOnly2_1o2o.yql
@@ -0,0 +1,17 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1opt))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2opt))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('LeftOnly '"a" '"b" '('"a" '"key") '('"b" '"key" ) '())
+ '()))
+(let res_sink (DataSink 'result))
+(let world (Write! world res_sink (Key) join '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/LeftOnly2key2_12.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/LeftOnly2key2_12.yql
new file mode 100644
index 0000000000..7983fd5c84
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/LeftOnly2key2_12.yql
@@ -0,0 +1,17 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('LeftOnly '"a" '"b" '('"a" '"key" '"a" '"subkey") '('"b" '"key" '"b" '"subkey") '())
+ '()))
+(let res_sink (DataSink 'result))
+(let world (Write! world res_sink (Key) join '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/LeftOnly2key2_1o2o.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/LeftOnly2key2_1o2o.yql
new file mode 100644
index 0000000000..72d92bae07
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/LeftOnly2key2_1o2o.yql
@@ -0,0 +1,17 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1opt))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2opt))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('LeftOnly '"a" '"b" '('"a" '"key" '"a" '"subkey") '('"b" '"key" '"b" '"subkey") '())
+ '()))
+(let res_sink (DataSink 'result))
+(let world (Write! world res_sink (Key) join '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/LeftSemi2_12.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/LeftSemi2_12.yql
new file mode 100644
index 0000000000..b9b78b1e30
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/LeftSemi2_12.yql
@@ -0,0 +1,17 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('LeftSemi '"a" '"b" '('"a" '"key") '('"b" '"key" ) '())
+ '()))
+(let res_sink (DataSink 'result))
+(let world (Write! world res_sink (Key) join '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/LeftSemi2_1o2o.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/LeftSemi2_1o2o.yql
new file mode 100644
index 0000000000..cbfc95831d
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/LeftSemi2_1o2o.yql
@@ -0,0 +1,17 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1opt))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2opt))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('LeftSemi '"a" '"b" '('"a" '"key") '('"b" '"key" ) '())
+ '()))
+(let res_sink (DataSink 'result))
+(let world (Write! world res_sink (Key) join '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/LeftSemi2key2_12.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/LeftSemi2key2_12.yql
new file mode 100644
index 0000000000..fb3e649ac7
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/LeftSemi2key2_12.yql
@@ -0,0 +1,17 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('LeftSemi '"a" '"b" '('"a" '"key" '"a" '"subkey") '('"b" '"key" '"b" '"subkey") '())
+ '()))
+(let res_sink (DataSink 'result))
+(let world (Write! world res_sink (Key) join '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/LeftSemi2key2_1o2o.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/LeftSemi2key2_1o2o.yql
new file mode 100644
index 0000000000..c97954aec4
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/LeftSemi2key2_1o2o.yql
@@ -0,0 +1,17 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1opt))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2opt))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('LeftSemi '"a" '"b" '('"a" '"key" '"a" '"subkey") '('"b" '"key" '"b" '"subkey") '())
+ '()))
+(let res_sink (DataSink 'result))
+(let world (Write! world res_sink (Key) join '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Right2_12.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Right2_12.yql
new file mode 100644
index 0000000000..c0cfeddbac
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Right2_12.yql
@@ -0,0 +1,19 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('Right '"a" '"b" '('"a" '"key") '('"b" '"key" ) '())
+ '()))
+(let res_sink (DataSink 'result))
+(let sortDir '((Bool 'true) (Bool 'true)))
+(let keySelector (lambda '(row) '((Member row '"a.key") (Member row '"a.subkey"))))
+(let world (Write! world res_sink (Key) (Sort join sortDir keySelector) '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Right2_1o2o.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Right2_1o2o.yql
new file mode 100644
index 0000000000..0d9096f170
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Right2_1o2o.yql
@@ -0,0 +1,19 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1opt))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2opt))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('Right '"a" '"b" '('"a" '"key") '('"b" '"key" ) '())
+ '()))
+(let res_sink (DataSink 'result))
+(let sortDir '((Bool 'true) (Bool 'true)))
+(let keySelector (lambda '(row) '((Member row '"a.key") (Member row '"a.subkey"))))
+(let world (Write! world res_sink (Key) (Sort join sortDir keySelector) '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Right2key2_12.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Right2key2_12.yql
new file mode 100644
index 0000000000..d6e27227a0
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Right2key2_12.yql
@@ -0,0 +1,19 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('Right '"a" '"b" '('"a" '"key" '"a" '"subkey") '('"b" '"key" '"b" '"subkey") '())
+ '()))
+(let res_sink (DataSink 'result))
+(let sortDir '((Bool 'true) (Bool 'true)))
+(let keySelector (lambda '(row) '((Member row '"a.key") (Member row '"a.subkey"))))
+(let world (Write! world res_sink (Key) (Sort join sortDir keySelector) '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Right2key2_1o2o.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Right2key2_1o2o.yql
new file mode 100644
index 0000000000..26a9da603e
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/Right2key2_1o2o.yql
@@ -0,0 +1,19 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1opt))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2opt))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('Right '"a" '"b" '('"a" '"key" '"a" '"subkey") '('"b" '"key" '"b" '"subkey") '())
+ '()))
+(let res_sink (DataSink 'result))
+(let sortDir '((Bool 'true) (Bool 'true)))
+(let keySelector (lambda '(row) '((Member row '"a.key") (Member row '"a.subkey"))))
+(let world (Write! world res_sink (Key) (Sort join sortDir keySelector) '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/RightOnly2_12.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/RightOnly2_12.yql
new file mode 100644
index 0000000000..ac557b7c39
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/RightOnly2_12.yql
@@ -0,0 +1,17 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('RightOnly '"a" '"b" '('"a" '"key") '('"b" '"key" ) '())
+ '()))
+(let res_sink (DataSink 'result))
+(let world (Write! world res_sink (Key) join '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/RightOnly2_1o2o.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/RightOnly2_1o2o.yql
new file mode 100644
index 0000000000..818530c644
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/RightOnly2_1o2o.yql
@@ -0,0 +1,17 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1opt))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2opt))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('RightOnly '"a" '"b" '('"a" '"key") '('"b" '"key" ) '())
+ '()))
+(let res_sink (DataSink 'result))
+(let world (Write! world res_sink (Key) join '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/RightOnly2key2_12.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/RightOnly2key2_12.yql
new file mode 100644
index 0000000000..57525fe5cd
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/RightOnly2key2_12.yql
@@ -0,0 +1,17 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('RightOnly '"a" '"b" '('"a" '"key" '"a" '"subkey") '('"b" '"key" '"b" '"subkey") '())
+ '()))
+(let res_sink (DataSink 'result))
+(let world (Write! world res_sink (Key) join '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/RightOnly2key2_1o2o.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/RightOnly2key2_1o2o.yql
new file mode 100644
index 0000000000..d94a379a98
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/RightOnly2key2_1o2o.yql
@@ -0,0 +1,17 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1opt))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2opt))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('RightOnly '"a" '"b" '('"a" '"key" '"a" '"subkey") '('"b" '"key" '"b" '"subkey") '())
+ '()))
+(let res_sink (DataSink 'result))
+(let world (Write! world res_sink (Key) join '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/RightSemi2_12.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/RightSemi2_12.yql
new file mode 100644
index 0000000000..2d97e5de4f
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/RightSemi2_12.yql
@@ -0,0 +1,17 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('RightSemi '"a" '"b" '('"a" '"key") '('"b" '"key" ) '())
+ '()))
+(let res_sink (DataSink 'result))
+(let world (Write! world res_sink (Key) join '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/RightSemi2_1o2o.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/RightSemi2_1o2o.yql
new file mode 100644
index 0000000000..78812b0504
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/RightSemi2_1o2o.yql
@@ -0,0 +1,17 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1opt))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2opt))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('RightSemi '"a" '"b" '('"a" '"key") '('"b" '"key" ) '())
+ '()))
+(let res_sink (DataSink 'result))
+(let world (Write! world res_sink (Key) join '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/RightSemi2key2_12.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/RightSemi2key2_12.yql
new file mode 100644
index 0000000000..614f56af07
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/RightSemi2key2_12.yql
@@ -0,0 +1,19 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('RightSemi '"a" '"b" '('"a" '"key" '"a" '"subkey") '('"b" '"key" '"b" '"subkey") '())
+ '()))
+(let res_sink (DataSink 'result))
+(let sortDir '((Bool 'true) (Bool 'true)))
+(let keySelector (lambda '(row) '((Member row '"b.key") (Member row '"b.subkey"))))
+(let world (Write! world res_sink (Key) (Sort join sortDir keySelector) '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/RightSemi2key2_1o2o.yql b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/RightSemi2key2_1o2o.yql
new file mode 100644
index 0000000000..36754914d3
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/RightSemi2key2_1o2o.yql
@@ -0,0 +1,19 @@
+(
+(let world (Configure! world (DataSource '"yt" '"$all") '"Attr" '"joinmergetableslimit" '"10"))
+(let mr_source (DataSource 'yt 'plato))
+(let x (Read! world mr_source (Key '('table (String 'Input1opt))) '('key 'subkey 'value1) '()))
+(let world (Left! x))
+(let table1 (Right! x))
+(let x (Read! world mr_source (Key '('table (String 'Input2opt))) '('key 'subkey 'value2) '()))
+(let world (Left! x))
+(let table2 (Right! x))
+(let join (EquiJoin '(table1 'a) '(table2 'b)
+ '('RightSemi '"a" '"b" '('"a" '"key" '"a" '"subkey") '('"b" '"key" '"b" '"subkey") '())
+ '()))
+(let res_sink (DataSink 'result))
+(let sortDir '((Bool 'true) (Bool 'true)))
+(let keySelector (lambda '(row) '((Member row '"b.key") (Member row '"b.subkey"))))
+(let world (Write! world res_sink (Key) (Sort join sortDir keySelector) '('('type))))
+(let world (Commit! world res_sink))
+(return world)
+)
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/bug2333_l.txt b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/bug2333_l.txt
new file mode 100644
index 0000000000..629585c06e
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/bug2333_l.txt
@@ -0,0 +1,11 @@
+{"key"="020";"subkey"=1;"value1"="q"};
+{"key"="022";"subkey"=1;"value1"="q"};
+{"key"="030";"subkey"=1;"value1"="q"};
+{"key"="040";"subkey"=1;"value1"="abc"};
+{"key"="050";"subkey"=1;"value1"="abc"};
+{"key"="060";"subkey"=1;"value1"="abc"};
+{"key"="075";"subkey"=1;"value1"="abc"};
+{"key"="075";"subkey"=1;"value1"="qzz"};
+{"key"="100";"subkey"=1;"value1"="ddd"};
+{"key"="500";"subkey"=1;"value1"="ddd"};
+{"key"="800";"subkey"=1;"value1"="ddd"};
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/bug2333_l.txt.attr b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/bug2333_l.txt.attr
new file mode 100644
index 0000000000..636afb35de
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/bug2333_l.txt.attr
@@ -0,0 +1,3 @@
+{
+ "_yql_row_spec"={"SortMembers"=["key"];"SortedBy"=["key"];"SortDirections"=[1];"SortedByTypes"=[["DataType";"String"]];"Type"=["StructType";[["key";["DataType";"String"]];["subkey";["DataType";"Int32"]];["value1";["DataType";"String"]]]]}
+}
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/bug2333_r.txt b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/bug2333_r.txt
new file mode 100644
index 0000000000..a2389716e5
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/bug2333_r.txt
@@ -0,0 +1,5 @@
+{"key"="320";"subkey"=1;"value1"="q"};
+{"key"="065";"subkey"=1;"value1"="abc"};
+{"key"="075";"subkey"=1;"value1"="qzz"};
+{"key"="850";"subkey"=1;"value1"="ddd"};
+{"key"="800";"subkey"=1;"value1"="ddd"};
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/bug2333_r.txt.attr b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/bug2333_r.txt.attr
new file mode 100644
index 0000000000..905390e41e
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/bug2333_r.txt.attr
@@ -0,0 +1,3 @@
+{
+ "_yql_row_spec"={"Type"=["StructType";[["key";["DataType";"String"]];["subkey";["DataType";"Int32"]];["value1";["DataType";"String"]]]]}
+}
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/default.cfg b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/default.cfg
new file mode 100644
index 0000000000..17f55ef14f
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/default.cfg
@@ -0,0 +1,9 @@
+res result.txt
+in Input1 input1.txt
+in Input2 input2.txt
+in Input1u input1u.txt
+in Input2u input2u.txt
+in Input3 input3.txt
+in Input1opt input1opt.txt
+in Input2opt input2opt.txt
+in Input3opt input3opt.txt
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/infer_scheme.txt b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/infer_scheme.txt
new file mode 100644
index 0000000000..79c1bc3e75
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/infer_scheme.txt
@@ -0,0 +1,4 @@
+{"key"="020"};
+{"key"="075";"subkey"="1";"value"="abc"};
+{"key"="150";"subkey"="4";"value"="qzz"};
+{"key"="800";"subkey"="2";"value"="ddd"};
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/infer_scheme.txt.attr b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/infer_scheme.txt.attr
new file mode 100644
index 0000000000..3c0f7e55c1
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/infer_scheme.txt.attr
@@ -0,0 +1,3 @@
+{
+ "infer_schema" = %true
+} \ No newline at end of file
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input1.txt b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input1.txt
new file mode 100644
index 0000000000..ad71c8be16
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input1.txt
@@ -0,0 +1,4 @@
+{"key"="020";"subkey"=1;"value1"="q"};
+{"key"="075";"subkey"=1;"value1"="abc"};
+{"key"="075";"subkey"=1;"value1"="qzz"};
+{"key"="800";"subkey"=1;"value1"="ddd"};
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input1.txt.attr b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input1.txt.attr
new file mode 100644
index 0000000000..22819a0f3b
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input1.txt.attr
@@ -0,0 +1,3 @@
+{
+ "_yql_row_spec"={"SortMembers"=["key";"subkey"];"SortedBy"=["key";"subkey"];"SortDirections"=[1;1];"SortedByTypes"=[["DataType";"String"];["DataType";"Int32"]];"Type"=["StructType";[["key";["DataType";"String"]];["subkey";["DataType";"Int32"]];["value1";["DataType";"String"]]]]}
+}
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input1opt.txt b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input1opt.txt
new file mode 100644
index 0000000000..ad71c8be16
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input1opt.txt
@@ -0,0 +1,4 @@
+{"key"="020";"subkey"=1;"value1"="q"};
+{"key"="075";"subkey"=1;"value1"="abc"};
+{"key"="075";"subkey"=1;"value1"="qzz"};
+{"key"="800";"subkey"=1;"value1"="ddd"};
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input1opt.txt.attr b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input1opt.txt.attr
new file mode 100644
index 0000000000..bd2fbd34c0
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input1opt.txt.attr
@@ -0,0 +1,3 @@
+{
+ "_yql_row_spec"={"SortMembers"=["key";"subkey"];"SortedBy"=["key";"subkey"];"SortDirections"=[1;1];"SortedByTypes"=[["OptionalType";["DataType";"String"]];["OptionalType";["DataType";"Int32"]]];"Type"=["StructType";[["key";["OptionalType";["DataType";"String"]]];["subkey";["OptionalType";["DataType";"Int32"]]];["value1";["OptionalType";["DataType";"String"]]]]]}
+}
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input1u.txt b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input1u.txt
new file mode 100644
index 0000000000..630726fbc7
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input1u.txt
@@ -0,0 +1,3 @@
+{"key"="020";"subkey"=1;"value1"="q"};
+{"key"="075";"subkey"=1;"value1"="abc"};
+{"key"="800";"subkey"=1;"value1"="ddd"};
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input1u.txt.attr b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input1u.txt.attr
new file mode 100644
index 0000000000..d666ee078f
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input1u.txt.attr
@@ -0,0 +1,3 @@
+{
+ "_yql_row_spec"={"UniqueKeys"=%true;"SortMembers"=["key";"subkey"];"SortedBy"=["key";"subkey"];"SortDirections"=[1;1];"SortedByTypes"=[["DataType";"String"];["DataType";"Int32"]];"Type"=["StructType";[["key";["DataType";"String"]];["subkey";["DataType";"Int32"]];["value1";["DataType";"String"]]]]}
+}
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input2.txt b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input2.txt
new file mode 100644
index 0000000000..ea8b550fc1
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input2.txt
@@ -0,0 +1,4 @@
+{"key"="075";"subkey"=1;"value2"=3.};
+{"key"="075";"subkey"=2;"value2"=4.5};
+{"key"="150";"subkey"=1;"value2"=5.5};
+{"key"="800";"subkey"=1;"value2"=1.};
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input2.txt.attr b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input2.txt.attr
new file mode 100644
index 0000000000..2f3f372953
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input2.txt.attr
@@ -0,0 +1,3 @@
+{
+ "_yql_row_spec"={"SortMembers"=["key";"subkey"];"SortedBy"=["key";"subkey"];"SortDirections"=[1;1];"SortedByTypes"=[["DataType";"String"];["DataType";"Int32"]];"Type"=["StructType";[["key";["DataType";"String"]];["subkey";["DataType";"Int32"]];["value2";["DataType";"Double"]]]]}
+}
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input2opt.txt b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input2opt.txt
new file mode 100644
index 0000000000..ea8b550fc1
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input2opt.txt
@@ -0,0 +1,4 @@
+{"key"="075";"subkey"=1;"value2"=3.};
+{"key"="075";"subkey"=2;"value2"=4.5};
+{"key"="150";"subkey"=1;"value2"=5.5};
+{"key"="800";"subkey"=1;"value2"=1.};
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input2opt.txt.attr b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input2opt.txt.attr
new file mode 100644
index 0000000000..4a4ea783a0
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input2opt.txt.attr
@@ -0,0 +1,3 @@
+{
+ "_yql_row_spec"={"SortMembers"=["key";"subkey"];"SortedBy"=["key";"subkey"];"SortDirections"=[1;1];"SortedByTypes"=[["OptionalType";["DataType";"String"]];["OptionalType";["DataType";"Int32"]]];"Type"=["StructType";[["key";["OptionalType";["DataType";"String"]]];["subkey";["OptionalType";["DataType";"Int32"]]];["value2";["OptionalType";["DataType";"Double"]]]]]}
+}
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input2u.txt b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input2u.txt
new file mode 100644
index 0000000000..3e93a3a132
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input2u.txt
@@ -0,0 +1,6 @@
+{"key"="075";"subkey"=1;"value2"=3.};
+{"key"="150";"subkey"=1;"value2"=5.5};
+{"key"="800";"subkey"=1;"value2"=1.};
+{"key"="801";"subkey"=1;"value2"=1.1};
+{"key"="802";"subkey"=1;"value2"=1.2};
+{"key"="803";"subkey"=1;"value2"=1.3};
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input2u.txt.attr b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input2u.txt.attr
new file mode 100644
index 0000000000..8ffdb455dd
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input2u.txt.attr
@@ -0,0 +1,3 @@
+{
+ "_yql_row_spec"={"UniqueKeys"=%true;"SortMembers"=["key";"subkey"];"SortedBy"=["key";"subkey"];"SortDirections"=[1;1];"SortedByTypes"=[["DataType";"String"];["DataType";"Int32"]];"Type"=["StructType";[["key";["DataType";"String"]];["subkey";["DataType";"Int32"]];["value2";["DataType";"Double"]]]]}
+}
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input3.txt b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input3.txt
new file mode 100644
index 0000000000..71e5aa381b
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input3.txt
@@ -0,0 +1,4 @@
+{"key"="021";"subkey"=3;"value3"=-67};
+{"key"="075";"subkey"=3;"value3"=23};
+{"key"="151";"subkey"=3;"value3"=666};
+{"key"="800";"subkey"=1;"value3"=567};
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input3.txt.attr b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input3.txt.attr
new file mode 100644
index 0000000000..531b5fb378
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input3.txt.attr
@@ -0,0 +1,3 @@
+{
+ "_yql_row_spec"={"SortMembers"=["key";"subkey"];"SortedBy"=["key";"subkey"];"SortDirections"=[1;1];"SortedByTypes"=[["DataType";"String"];["DataType";"Int32"]];"Type"=["StructType";[["key";["DataType";"String"]];["subkey";["DataType";"Int32"]];["value3";["DataType";"Int64"]]]]}
+}
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input3opt.txt b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input3opt.txt
new file mode 100644
index 0000000000..71e5aa381b
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input3opt.txt
@@ -0,0 +1,4 @@
+{"key"="021";"subkey"=3;"value3"=-67};
+{"key"="075";"subkey"=3;"value3"=23};
+{"key"="151";"subkey"=3;"value3"=666};
+{"key"="800";"subkey"=1;"value3"=567};
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input3opt.txt.attr b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input3opt.txt.attr
new file mode 100644
index 0000000000..37e5810187
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/input3opt.txt.attr
@@ -0,0 +1,3 @@
+{
+ "_yql_row_spec"={"SortMembers"=["key";"subkey"];"SortedBy"=["key";"subkey"];"SortDirections"=[1;1];"SortedByTypes"=[["OptionalType";["DataType";"String"]];["OptionalType";["DataType";"Int32"]]];"Type"=["StructType";[["key";["OptionalType";["DataType";"String"]]];["subkey";["OptionalType";["DataType";"Int32"]]];["value3";["OptionalType";["DataType";"Int64"]]]]]}
+}
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/sorted_desc.txt b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/sorted_desc.txt
new file mode 100644
index 0000000000..ac8e34386d
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/sorted_desc.txt
@@ -0,0 +1,4 @@
+{"_yql_column_0"="\xE0\xC7\xCF\xCF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\xFC";"key"="800";"subkey"="2";"value"="ddd"};
+{"_yql_column_0"="\xE0\xCE\xCA\xCF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\xFC";"key"="150";"subkey"="4";"value"="qzz"};
+{"_yql_column_0"="\xE0\xCF\xC8\xCA\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\xFC";"key"="075";"subkey"="1";"value"="abc"};
+{"_yql_column_0"="\xE0\xCF\xCD\xCF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\xFC";"key"="020";"subkey"="3";"value"="q"};
diff --git a/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/sorted_desc.txt.attr b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/sorted_desc.txt.attr
new file mode 100644
index 0000000000..903b4c7d93
--- /dev/null
+++ b/yql/essentials/tests/s-expressions/suites/EquiJoinMerge/sorted_desc.txt.attr
@@ -0,0 +1,46 @@
+{
+ "_yql_row_spec" = {
+ "SortMembers" = [
+ "key"
+ ];
+ "SortDirections" = [
+ 0
+ ];
+ "UniqueKeys" = %false;
+ "Type" = [
+ "StructType";
+ [
+ [
+ "key";
+ [
+ "DataType";
+ "String"
+ ]
+ ];
+ [
+ "subkey";
+ [
+ "DataType";
+ "String"
+ ]
+ ];
+ [
+ "value";
+ [
+ "DataType";
+ "String"
+ ]
+ ]
+ ]
+ ];
+ "SortedByTypes" = [
+ [
+ "DataType";
+ "String"
+ ]
+ ];
+ "SortedBy" = [
+ "_yql_column_0"
+ ]
+ }
+} \ No newline at end of file