join fix and runner update to support multiple output files
parent
ab3d7ab971
commit
dd1431760a
|
|
@ -1,3 +1,7 @@
|
|||
# Alteryx example files
|
||||
Alteryx_TestWorkflows/JoinTesting/Output/
|
||||
!Alteryx_TestWorkflows/JoinTesting/Output/**/
|
||||
|
||||
# uv
|
||||
uv.lock
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,226 +1,226 @@
|
|||
Left_Store_ID,Product_ID,Left_Stock_On_Hand
|
||||
15,31,4
|
||||
15,32,16
|
||||
15,33,8
|
||||
15,34,7
|
||||
16,31,14
|
||||
16,32,7
|
||||
16,33,6
|
||||
16,34,2
|
||||
16,35,6
|
||||
17,31,20
|
||||
17,32,15
|
||||
17,33,27
|
||||
17,34,11
|
||||
18,31,4
|
||||
18,32,9
|
||||
18,33,9
|
||||
18,34,8
|
||||
18,35,10
|
||||
19,31,4
|
||||
19,32,5
|
||||
19,33,0
|
||||
19,34,15
|
||||
19,35,14
|
||||
20,31,10
|
||||
20,32,9
|
||||
20,33,28
|
||||
20,34,19
|
||||
21,31,19
|
||||
21,32,3
|
||||
21,33,16
|
||||
21,34,16
|
||||
22,31,34
|
||||
22,32,38
|
||||
22,33,8
|
||||
22,34,6
|
||||
22,35,2
|
||||
23,31,19
|
||||
23,32,11
|
||||
23,33,6
|
||||
23,34,18
|
||||
23,35,4
|
||||
24,31,10
|
||||
24,32,10
|
||||
24,33,4
|
||||
24,34,17
|
||||
24,35,19
|
||||
25,31,0
|
||||
25,32,10
|
||||
25,33,4
|
||||
25,34,23
|
||||
26,31,4
|
||||
26,32,2
|
||||
26,33,2
|
||||
26,34,17
|
||||
26,35,8
|
||||
27,31,13
|
||||
27,32,6
|
||||
27,33,7
|
||||
27,34,9
|
||||
28,31,18
|
||||
28,32,3
|
||||
28,33,9
|
||||
28,34,19
|
||||
29,31,3
|
||||
29,32,7
|
||||
29,33,6
|
||||
29,34,16
|
||||
30,31,20
|
||||
30,32,13
|
||||
30,33,10
|
||||
30,34,18
|
||||
31,31,39
|
||||
31,32,12
|
||||
31,33,20
|
||||
31,34,20
|
||||
32,31,4
|
||||
32,32,8
|
||||
32,33,13
|
||||
32,34,20
|
||||
33,31,7
|
||||
33,32,15
|
||||
33,33,9
|
||||
33,34,14
|
||||
33,35,18
|
||||
34,31,30
|
||||
34,32,19
|
||||
34,33,9
|
||||
34,34,17
|
||||
34,35,20
|
||||
35,31,74
|
||||
35,32,20
|
||||
35,33,14
|
||||
35,34,9
|
||||
36,31,6
|
||||
36,32,7
|
||||
36,33,21
|
||||
36,34,2
|
||||
36,35,12
|
||||
37,31,14
|
||||
37,32,0
|
||||
37,33,10
|
||||
37,34,13
|
||||
37,35,14
|
||||
38,31,17
|
||||
38,32,20
|
||||
38,33,9
|
||||
38,34,18
|
||||
38,35,2
|
||||
39,31,15
|
||||
39,32,5
|
||||
39,33,14
|
||||
39,34,4
|
||||
40,31,5
|
||||
40,32,7
|
||||
40,33,16
|
||||
40,34,5
|
||||
41,31,18
|
||||
41,32,29
|
||||
41,33,13
|
||||
41,34,15
|
||||
41,35,10
|
||||
1,31,7
|
||||
1,32,4
|
||||
1,33,2
|
||||
1,34,0
|
||||
1,35,12
|
||||
2,31,18
|
||||
2,32,10
|
||||
2,33,11
|
||||
2,34,18
|
||||
3,31,29
|
||||
3,32,4
|
||||
3,33,4
|
||||
3,34,7
|
||||
4,31,35
|
||||
4,32,6
|
||||
4,33,2
|
||||
4,34,0
|
||||
4,35,4
|
||||
5,31,31
|
||||
5,32,10
|
||||
5,33,17
|
||||
5,34,10
|
||||
6,31,17
|
||||
6,32,7
|
||||
6,33,7
|
||||
6,34,8
|
||||
6,35,3
|
||||
7,31,15
|
||||
7,32,3
|
||||
7,33,18
|
||||
7,34,2
|
||||
7,35,17
|
||||
8,31,27
|
||||
8,32,7
|
||||
8,33,17
|
||||
8,34,18
|
||||
8,35,8
|
||||
9,31,6
|
||||
9,32,3
|
||||
9,33,9
|
||||
9,34,5
|
||||
9,35,4
|
||||
10,31,7
|
||||
10,32,13
|
||||
10,33,12
|
||||
10,34,16
|
||||
10,35,2
|
||||
11,31,20
|
||||
11,32,4
|
||||
11,33,6
|
||||
11,34,9
|
||||
12,31,13
|
||||
12,32,9
|
||||
12,33,5
|
||||
12,34,9
|
||||
12,35,9
|
||||
13,31,24
|
||||
13,32,7
|
||||
13,33,3
|
||||
13,34,3
|
||||
14,31,5
|
||||
14,32,2
|
||||
14,33,2
|
||||
14,34,8
|
||||
42,31,11
|
||||
42,32,4
|
||||
42,33,18
|
||||
42,34,34
|
||||
42,35,13
|
||||
43,31,18
|
||||
43,32,38
|
||||
43,33,5
|
||||
43,34,7
|
||||
44,31,8
|
||||
44,32,29
|
||||
44,33,0
|
||||
44,34,22
|
||||
45,31,6
|
||||
45,32,6
|
||||
45,33,7
|
||||
45,34,3
|
||||
46,31,13
|
||||
46,32,8
|
||||
46,33,11
|
||||
46,34,24
|
||||
47,31,48
|
||||
47,32,6
|
||||
47,33,13
|
||||
47,34,3
|
||||
48,31,41
|
||||
48,32,7
|
||||
48,33,0
|
||||
48,34,39
|
||||
48,35,3
|
||||
49,31,51
|
||||
49,32,11
|
||||
49,33,15
|
||||
49,34,2
|
||||
49,35,19
|
||||
50,31,18
|
||||
50,32,9
|
||||
50,33,1
|
||||
50,34,17
|
||||
50,35,8
|
||||
Store_ID,Product_ID,Stock_On_Hand
|
||||
15,31,4
|
||||
15,32,16
|
||||
15,33,8
|
||||
15,34,7
|
||||
16,31,14
|
||||
16,32,7
|
||||
16,33,6
|
||||
16,34,2
|
||||
16,35,6
|
||||
17,31,20
|
||||
17,32,15
|
||||
17,33,27
|
||||
17,34,11
|
||||
18,31,4
|
||||
18,32,9
|
||||
18,33,9
|
||||
18,34,8
|
||||
18,35,10
|
||||
19,31,4
|
||||
19,32,5
|
||||
19,33,0
|
||||
19,34,15
|
||||
19,35,14
|
||||
20,31,10
|
||||
20,32,9
|
||||
20,33,28
|
||||
20,34,19
|
||||
21,31,19
|
||||
21,32,3
|
||||
21,33,16
|
||||
21,34,16
|
||||
22,31,34
|
||||
22,32,38
|
||||
22,33,8
|
||||
22,34,6
|
||||
22,35,2
|
||||
23,31,19
|
||||
23,32,11
|
||||
23,33,6
|
||||
23,34,18
|
||||
23,35,4
|
||||
24,31,10
|
||||
24,32,10
|
||||
24,33,4
|
||||
24,34,17
|
||||
24,35,19
|
||||
25,31,0
|
||||
25,32,10
|
||||
25,33,4
|
||||
25,34,23
|
||||
26,31,4
|
||||
26,32,2
|
||||
26,33,2
|
||||
26,34,17
|
||||
26,35,8
|
||||
27,31,13
|
||||
27,32,6
|
||||
27,33,7
|
||||
27,34,9
|
||||
28,31,18
|
||||
28,32,3
|
||||
28,33,9
|
||||
28,34,19
|
||||
42,31,11
|
||||
42,32,4
|
||||
42,33,18
|
||||
42,34,34
|
||||
42,35,13
|
||||
43,31,18
|
||||
43,32,38
|
||||
43,33,5
|
||||
43,34,7
|
||||
44,31,8
|
||||
44,32,29
|
||||
44,33,0
|
||||
44,34,22
|
||||
45,31,6
|
||||
45,32,6
|
||||
45,33,7
|
||||
45,34,3
|
||||
46,31,13
|
||||
46,32,8
|
||||
46,33,11
|
||||
46,34,24
|
||||
47,31,48
|
||||
47,32,6
|
||||
47,33,13
|
||||
47,34,3
|
||||
48,31,41
|
||||
48,32,7
|
||||
48,33,0
|
||||
48,34,39
|
||||
48,35,3
|
||||
49,31,51
|
||||
49,32,11
|
||||
49,33,15
|
||||
49,34,2
|
||||
49,35,19
|
||||
50,31,18
|
||||
50,32,9
|
||||
50,33,1
|
||||
50,34,17
|
||||
50,35,8
|
||||
29,31,3
|
||||
29,32,7
|
||||
29,33,6
|
||||
29,34,16
|
||||
30,31,20
|
||||
30,32,13
|
||||
30,33,10
|
||||
30,34,18
|
||||
31,31,39
|
||||
31,32,12
|
||||
31,33,20
|
||||
31,34,20
|
||||
32,31,4
|
||||
32,32,8
|
||||
32,33,13
|
||||
32,34,20
|
||||
33,31,7
|
||||
33,32,15
|
||||
33,33,9
|
||||
33,34,14
|
||||
33,35,18
|
||||
34,31,30
|
||||
34,32,19
|
||||
34,33,9
|
||||
34,34,17
|
||||
34,35,20
|
||||
35,31,74
|
||||
35,32,20
|
||||
35,33,14
|
||||
35,34,9
|
||||
36,31,6
|
||||
36,32,7
|
||||
36,33,21
|
||||
36,34,2
|
||||
36,35,12
|
||||
37,31,14
|
||||
37,32,0
|
||||
37,33,10
|
||||
37,34,13
|
||||
37,35,14
|
||||
38,31,17
|
||||
38,32,20
|
||||
38,33,9
|
||||
38,34,18
|
||||
38,35,2
|
||||
39,31,15
|
||||
39,32,5
|
||||
39,33,14
|
||||
39,34,4
|
||||
40,31,5
|
||||
40,32,7
|
||||
40,33,16
|
||||
40,34,5
|
||||
41,31,18
|
||||
41,32,29
|
||||
41,33,13
|
||||
41,34,15
|
||||
41,35,10
|
||||
1,31,7
|
||||
1,32,4
|
||||
1,33,2
|
||||
1,34,0
|
||||
1,35,12
|
||||
2,31,18
|
||||
2,32,10
|
||||
2,33,11
|
||||
2,34,18
|
||||
3,31,29
|
||||
3,32,4
|
||||
3,33,4
|
||||
3,34,7
|
||||
4,31,35
|
||||
4,32,6
|
||||
4,33,2
|
||||
4,34,0
|
||||
4,35,4
|
||||
5,31,31
|
||||
5,32,10
|
||||
5,33,17
|
||||
5,34,10
|
||||
6,31,17
|
||||
6,32,7
|
||||
6,33,7
|
||||
6,34,8
|
||||
6,35,3
|
||||
7,31,15
|
||||
7,32,3
|
||||
7,33,18
|
||||
7,34,2
|
||||
7,35,17
|
||||
8,31,27
|
||||
8,32,7
|
||||
8,33,17
|
||||
8,34,18
|
||||
8,35,8
|
||||
9,31,6
|
||||
9,32,3
|
||||
9,33,9
|
||||
9,34,5
|
||||
9,35,4
|
||||
10,31,7
|
||||
10,32,13
|
||||
10,33,12
|
||||
10,34,16
|
||||
10,35,2
|
||||
11,31,20
|
||||
11,32,4
|
||||
11,33,6
|
||||
11,34,9
|
||||
12,31,13
|
||||
12,32,9
|
||||
12,33,5
|
||||
12,34,9
|
||||
12,35,9
|
||||
13,31,24
|
||||
13,32,7
|
||||
13,33,3
|
||||
13,34,3
|
||||
14,31,5
|
||||
14,32,2
|
||||
14,33,2
|
||||
14,34,8
|
||||
|
|
|
|||
|
|
|
@ -1,2 +1,2 @@
|
|||
Product_ID,Right_Product_Name,Right_Product_Category,Right_Product_Cost,Right_Product_Price
|
||||
100,Non-product,NoCat,$1,$1
|
||||
Product_ID,Product_Name,Product_Category,Product_Cost,Product_Price
|
||||
100,Non-product,NoCat,$1,$1
|
||||
|
|
|
|||
|
|
|
@ -1,32 +1,32 @@
|
|||
Product_ID,Product_Name,Product_Category,Product_Cost,Product_Price
|
||||
1,Action Figure,Toys,$9.99,$15.99
|
||||
2,Animal Figures,Toys,$9.99,$12.99
|
||||
3,Barrel O' Slime,Art & Crafts,$1.99,$3.99
|
||||
4,Chutes & Ladders,Games,$9.99,$12.99
|
||||
5,Classic Dominoes,Games,$7.99,$9.99
|
||||
6,Colorbuds,Electronics,$6.99,$14.99
|
||||
7,Dart Gun,Sports & Outdoors,$11.99,$15.99
|
||||
8,Deck Of Cards,Games,$3.99,$6.99
|
||||
9,Dino Egg,Toys,$9.99,$10.99
|
||||
10,Dinosaur Figures,Toys,$10.99,$14.99
|
||||
11,Etch A Sketch,Art & Crafts,$10.99,$20.99
|
||||
12,Foam Disk Launcher,Sports & Outdoors,$8.99,$11.99
|
||||
13,Gamer Headphones,Electronics,$14.99,$20.99
|
||||
14,Glass Marbles,Games,$5.99,$10.99
|
||||
15,Hot Wheels 5-Pack,Toys,$3.99,$5.99
|
||||
16,Jenga,Games,$2.99,$9.99
|
||||
17,Kids Makeup Kit,Art & Crafts,$13.99,$19.99
|
||||
18,Lego Bricks,Toys,$34.99,$39.99
|
||||
19,Magic Sand,Art & Crafts,$13.99,$15.99
|
||||
20,Mini Basketball Hoop,Sports & Outdoors,$8.99,$24.99
|
||||
21,Mini Ping Pong Set,Sports & Outdoors,$6.99,$9.99
|
||||
22,Monopoly,Games,$13.99,$19.99
|
||||
23,Mr. Potatohead,Toys,$4.99,$9.99
|
||||
24,Nerf Gun,Sports & Outdoors,$14.99,$19.99
|
||||
25,PlayDoh Can,Art & Crafts,$1.99,$2.99
|
||||
26,PlayDoh Playset,Art & Crafts,$20.99,$24.99
|
||||
27,PlayDoh Toolkit,Art & Crafts,$3.99,$4.99
|
||||
28,Playfoam,Art & Crafts,$3.99,$10.99
|
||||
29,Plush Pony,Toys,$8.99,$19.99
|
||||
30,Rubik's Cube,Games,$17.99,$19.99
|
||||
100,Non-product,NoCat,$1,$1
|
||||
Product_ID,Product_Name,Product_Category,Product_Cost,Product_Price
|
||||
1,Action Figure,Toys,$9.99,$15.99
|
||||
2,Animal Figures,Toys,$9.99,$12.99
|
||||
3,Barrel O' Slime,Art & Crafts,$1.99,$3.99
|
||||
4,Chutes & Ladders,Games,$9.99,$12.99
|
||||
5,Classic Dominoes,Games,$7.99,$9.99
|
||||
6,Colorbuds,Electronics,$6.99,$14.99
|
||||
7,Dart Gun,Sports & Outdoors,$11.99,$15.99
|
||||
8,Deck Of Cards,Games,$3.99,$6.99
|
||||
9,Dino Egg,Toys,$9.99,$10.99
|
||||
10,Dinosaur Figures,Toys,$10.99,$14.99
|
||||
11,Etch A Sketch,Art & Crafts,$10.99,$20.99
|
||||
12,Foam Disk Launcher,Sports & Outdoors,$8.99,$11.99
|
||||
13,Gamer Headphones,Electronics,$14.99,$20.99
|
||||
14,Glass Marbles,Games,$5.99,$10.99
|
||||
15,Hot Wheels 5-Pack,Toys,$3.99,$5.99
|
||||
16,Jenga,Games,$2.99,$9.99
|
||||
17,Kids Makeup Kit,Art & Crafts,$13.99,$19.99
|
||||
18,Lego Bricks,Toys,$34.99,$39.99
|
||||
19,Magic Sand,Art & Crafts,$13.99,$15.99
|
||||
20,Mini Basketball Hoop,Sports & Outdoors,$8.99,$24.99
|
||||
21,Mini Ping Pong Set,Sports & Outdoors,$6.99,$9.99
|
||||
22,Monopoly,Games,$13.99,$19.99
|
||||
23,Mr. Potatohead,Toys,$4.99,$9.99
|
||||
24,Nerf Gun,Sports & Outdoors,$14.99,$19.99
|
||||
25,PlayDoh Can,Art & Crafts,$1.99,$2.99
|
||||
26,PlayDoh Playset,Art & Crafts,$20.99,$24.99
|
||||
27,PlayDoh Toolkit,Art & Crafts,$3.99,$4.99
|
||||
28,Playfoam,Art & Crafts,$3.99,$10.99
|
||||
29,Plush Pony,Toys,$8.99,$19.99
|
||||
30,Rubik's Cube,Games,$17.99,$19.99
|
||||
100,Non-product,NoCat,$1,$1
|
||||
|
|
|
|||
|
|
|
@ -74,7 +74,10 @@ class OutputDataTool(BaseTool):
|
|||
or "True"
|
||||
)
|
||||
header = header_val.lower() != "false"
|
||||
df.write_csv(str(path), separator=delim, include_header=header)
|
||||
line_end = (opts.findtext("LineEndStyle") or "LF").strip().upper()
|
||||
eol = "\r\n" if line_end == "CRLF" else "\n"
|
||||
df.write_csv(str(path), separator=delim, include_header=header,
|
||||
line_terminator=eol)
|
||||
elif fmt == 25: # Excel
|
||||
df.write_excel(str(path))
|
||||
elif fmt == 2: # Parquet
|
||||
|
|
|
|||
|
|
@ -66,74 +66,80 @@ class JoinTool(BaseTool):
|
|||
"""Apply field selection and renaming from SelectConfiguration."""
|
||||
if df.is_empty() or self.config is None:
|
||||
return df
|
||||
|
||||
|
||||
select_config = self.config.find("SelectConfiguration")
|
||||
if select_config is None:
|
||||
return df
|
||||
|
||||
|
||||
# Find the Configuration for this output connection
|
||||
for cfg in select_config.findall("Configuration"):
|
||||
if cfg.attrib.get("outputConnection") == output_connection:
|
||||
select_fields = cfg.find("SelectFields")
|
||||
if select_fields is None:
|
||||
return df
|
||||
|
||||
# Build column mapping
|
||||
# First, collect explicitly selected fields
|
||||
explicit_selections = [] # list of (src_col, output_name)
|
||||
|
||||
order_changed_el = cfg.find("OrderChanged")
|
||||
order_changed = (
|
||||
order_changed_el is not None
|
||||
and order_changed_el.attrib.get("value", "False") == "True"
|
||||
)
|
||||
|
||||
# Parse field rules
|
||||
rename_map: dict[str, str] = {} # src_col → output_name
|
||||
exclude_set: set[str] = set() # columns explicitly excluded
|
||||
explicit_order: list[str] = [] # for OrderChanged=True
|
||||
has_unknown = False
|
||||
|
||||
unknown_selected = True
|
||||
|
||||
for sf in select_fields.findall("SelectField"):
|
||||
field = sf.attrib.get("field", "")
|
||||
selected = sf.attrib.get("selected", "False") == "True"
|
||||
selected = sf.attrib.get("selected", "True") == "True"
|
||||
rename = sf.attrib.get("rename", "")
|
||||
input_prefix = sf.attrib.get("input", "")
|
||||
|
||||
if not selected:
|
||||
continue
|
||||
|
||||
|
||||
if field == "*Unknown":
|
||||
has_unknown = True
|
||||
unknown_selected = selected
|
||||
continue
|
||||
|
||||
# Resolve column name in the DataFrame
|
||||
if field not in df.columns:
|
||||
continue
|
||||
|
||||
if not selected:
|
||||
exclude_set.add(field)
|
||||
else:
|
||||
# Find the column with prefix
|
||||
src_col = f"{input_prefix}{field}" if input_prefix else field
|
||||
if src_col in df.columns:
|
||||
output_name = rename if rename else field
|
||||
explicit_selections.append((src_col, output_name))
|
||||
elif field in df.columns:
|
||||
output_name = rename if rename else field
|
||||
explicit_selections.append((field, output_name))
|
||||
|
||||
explicit_order.append(field)
|
||||
if rename and rename != field:
|
||||
rename_map[field] = rename
|
||||
|
||||
# Build final column list
|
||||
selected_cols = []
|
||||
rename_map = {}
|
||||
|
||||
# Add explicitly selected columns
|
||||
for src, dst in explicit_selections:
|
||||
selected_cols.append(src)
|
||||
if src != dst:
|
||||
rename_map[src] = dst
|
||||
|
||||
# Handle *Unknown: include all remaining columns, stripping prefixes
|
||||
if has_unknown:
|
||||
explicit_srcs = {src for src, _ in explicit_selections}
|
||||
mentioned = set(explicit_order) | exclude_set
|
||||
|
||||
if order_changed:
|
||||
# Explicit selections first (in specified order), then *Unknown
|
||||
final_cols = list(explicit_order)
|
||||
if has_unknown and unknown_selected:
|
||||
for col in df.columns:
|
||||
if col not in mentioned:
|
||||
final_cols.append(col)
|
||||
else:
|
||||
# Preserve original DataFrame column order
|
||||
final_cols = []
|
||||
for col in df.columns:
|
||||
if col not in explicit_srcs:
|
||||
# Strip Left_/Right_ prefix for output name
|
||||
output_name = col
|
||||
if col.startswith("Left_"):
|
||||
output_name = col[5:]
|
||||
elif col.startswith("Right_"):
|
||||
output_name = col[6:]
|
||||
selected_cols.append(col)
|
||||
if col != output_name:
|
||||
rename_map[col] = output_name
|
||||
|
||||
# Apply selection and renaming
|
||||
if selected_cols:
|
||||
df = df.select(selected_cols)
|
||||
if rename_map:
|
||||
df = df.rename(rename_map)
|
||||
if col in exclude_set:
|
||||
continue
|
||||
if col in mentioned or (has_unknown and unknown_selected):
|
||||
final_cols.append(col)
|
||||
elif not has_unknown and col not in mentioned:
|
||||
# Default: include if not explicitly excluded
|
||||
final_cols.append(col)
|
||||
|
||||
if final_cols:
|
||||
df = df.select(final_cols)
|
||||
if rename_map:
|
||||
df = df.rename(
|
||||
{k: v for k, v in rename_map.items() if k in df.columns}
|
||||
)
|
||||
break
|
||||
return df
|
||||
|
||||
|
|
@ -145,45 +151,40 @@ class JoinTool(BaseTool):
|
|||
) -> tuple[pl.DataFrame, pl.DataFrame, pl.DataFrame]:
|
||||
con = self.ctx.duckdb_con
|
||||
|
||||
# Disambiguate conflicting column names
|
||||
key_l = {k[0] for k in join_keys}
|
||||
key_r = {k[1] for k in join_keys}
|
||||
l_non_key = [c for c in left.columns if c not in key_l]
|
||||
r_non_key = [c for c in right.columns if c not in key_r]
|
||||
# Only right non-key columns that clash with left columns need a prefix
|
||||
conflicts = set(l_non_key) & set(r_non_key)
|
||||
|
||||
# Prefix all non-key columns: Left_ for left, Right_ for right
|
||||
# This matches Alteryx behavior where SelectConfiguration references
|
||||
# fields with these prefixes
|
||||
rename_l = {c: f"Left_{c}" for c in l_non_key}
|
||||
rename_r = {c: f"Right_{c}" for c in r_non_key}
|
||||
# But keep join keys without prefix (they come from left)
|
||||
left_r = left.rename(rename_l) if rename_l else left
|
||||
right_r = right.rename(rename_r) if rename_r else right
|
||||
|
||||
con.register("__join_left__", left_r.to_arrow())
|
||||
con.register("__join_right__", right_r.to_arrow())
|
||||
|
||||
# Map renamed key column names
|
||||
def lk(k: str) -> str:
|
||||
return rename_l.get(k, k)
|
||||
|
||||
def rk(k: str) -> str:
|
||||
return rename_r.get(k, k)
|
||||
# Register the original (un-prefixed) tables
|
||||
con.register("__join_left__", left.to_arrow())
|
||||
con.register("__join_right__", right.to_arrow())
|
||||
|
||||
on_clause = " AND ".join(
|
||||
f'l."{lk(k[0])}" = r."{rk(k[1])}"' for k in join_keys
|
||||
f'l."{k[0]}" = r."{k[1]}"' for k in join_keys
|
||||
)
|
||||
|
||||
# Include right join keys with Right_ prefix for SelectConfiguration
|
||||
r_key_cols_sql = ", ".join(f'r."{rk(k[1])}" AS "Right_{k[1]}"' for k in join_keys)
|
||||
r_cols_sql = ", ".join(f'r."{rk(c)}"' for c in r_non_key)
|
||||
if r_key_cols_sql:
|
||||
r_cols_sql = f"{r_key_cols_sql}, {r_cols_sql}"
|
||||
|
||||
r_key0 = rk(join_keys[0][1])
|
||||
l_key0 = lk(join_keys[0][0])
|
||||
j_sql = f"SELECT l.*, {r_cols_sql} FROM __join_left__ l INNER JOIN __join_right__ r ON {on_clause}"
|
||||
# --- Inner join SELECT ------------------------------------------------
|
||||
# Left columns first (no prefix), then right join keys with Right_
|
||||
# prefix, then right non-key columns (Right_ prefix only on conflicts).
|
||||
l_cols_sql = ", ".join(f'l."{c}"' for c in left.columns)
|
||||
r_key_cols_sql = ", ".join(
|
||||
f'r."{k[1]}" AS "Right_{k[1]}"' for k in join_keys
|
||||
)
|
||||
r_non_key_sql = ", ".join(
|
||||
f'r."{c}" AS "Right_{c}"' if c in conflicts else f'r."{c}"'
|
||||
for c in r_non_key
|
||||
)
|
||||
j_parts = [p for p in (l_cols_sql, r_key_cols_sql, r_non_key_sql) if p]
|
||||
j_select = ", ".join(j_parts)
|
||||
|
||||
l_key0 = join_keys[0][0]
|
||||
r_key0 = join_keys[0][1]
|
||||
|
||||
j_sql = f"SELECT {j_select} FROM __join_left__ l INNER JOIN __join_right__ r ON {on_clause}"
|
||||
# Left/right unmatched keep original column names (no prefixes)
|
||||
l_sql = f'SELECT l.* FROM __join_left__ l LEFT JOIN __join_right__ r ON {on_clause} WHERE r."{r_key0}" IS NULL'
|
||||
r_sql = f'SELECT r.* FROM __join_right__ r LEFT JOIN __join_left__ l ON {on_clause} WHERE l."{l_key0}" IS NULL'
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue