datafusion_common/
join_type.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18//! Defines the [`JoinType`], [`JoinConstraint`] and [`JoinSide`] types.
19
20use std::{
21    fmt::{self, Display, Formatter},
22    str::FromStr,
23};
24
25use crate::error::_not_impl_err;
26use crate::{DataFusionError, Result};
27
28/// Join type
29#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Hash)]
30pub enum JoinType {
31    /// Inner Join - Returns only rows where there is a matching value in both tables based on the join condition.
32    /// For example, if joining table A and B on A.id = B.id, only rows where A.id equals B.id will be included.
33    /// All columns from both tables are returned for the matching rows. Non-matching rows are excluded entirely.
34    Inner,
35    /// Left Join - Returns all rows from the left table and matching rows from the right table.
36    /// If no match, NULL values are returned for columns from the right table.
37    Left,
38    /// Right Join - Returns all rows from the right table and matching rows from the left table.
39    /// If no match, NULL values are returned for columns from the left table.
40    Right,
41    /// Full Join (also called Full Outer Join) - Returns all rows from both tables, matching rows where possible.
42    /// When a row from either table has no match in the other table, the missing columns are filled with NULL values.
43    /// For example, if table A has row X with no match in table B, the result will contain row X with NULL values for all of table B's columns.
44    /// This join type preserves all records from both tables, making it useful when you need to see all data regardless of matches.
45    Full,
46    /// Left Semi Join - Returns rows from the left table that have matching rows in the right table.
47    /// Only columns from the left table are returned.
48    LeftSemi,
49    /// Right Semi Join - Returns rows from the right table that have matching rows in the left table.
50    /// Only columns from the right table are returned.
51    RightSemi,
52    /// Left Anti Join - Returns rows from the left table that do not have a matching row in the right table.
53    LeftAnti,
54    /// Right Anti Join - Returns rows from the right table that do not have a matching row in the left table.
55    RightAnti,
56    /// Left Mark join
57    ///
58    /// Returns one record for each record from the left input. The output contains an additional
59    /// column "mark" which is true if there is at least one match in the right input where the
60    /// join condition evaluates to true. Otherwise, the mark column is false. For more details see
61    /// [1]. This join type is used to decorrelate EXISTS subqueries used inside disjunctive
62    /// predicates.
63    ///
64    /// Note: This we currently do not implement the full null semantics for the mark join described
65    /// in [1] which will be needed if we and ANY subqueries. In our version the mark column will
66    /// only be true for had a match and false when no match was found, never null.
67    ///
68    /// [1]: http://btw2017.informatik.uni-stuttgart.de/slidesandpapers/F1-10-37/paper_web.pdf
69    LeftMark,
70    /// Right Mark Join
71    ///
72    /// Same logic as the LeftMark Join above, however it returns a record for each record from the
73    /// right input.
74    RightMark,
75}
76
77impl JoinType {
78    pub fn is_outer(self) -> bool {
79        self == JoinType::Left || self == JoinType::Right || self == JoinType::Full
80    }
81
82    /// Returns the `JoinType` if the (2) inputs were swapped
83    ///
84    /// Panics if [`Self::supports_swap`] returns false
85    pub fn swap(&self) -> JoinType {
86        match self {
87            JoinType::Inner => JoinType::Inner,
88            JoinType::Full => JoinType::Full,
89            JoinType::Left => JoinType::Right,
90            JoinType::Right => JoinType::Left,
91            JoinType::LeftSemi => JoinType::RightSemi,
92            JoinType::RightSemi => JoinType::LeftSemi,
93            JoinType::LeftAnti => JoinType::RightAnti,
94            JoinType::RightAnti => JoinType::LeftAnti,
95            JoinType::LeftMark => JoinType::RightMark,
96            JoinType::RightMark => JoinType::LeftMark,
97        }
98    }
99
100    /// Does the join type support swapping inputs?
101    pub fn supports_swap(&self) -> bool {
102        matches!(
103            self,
104            JoinType::Inner
105                | JoinType::Left
106                | JoinType::Right
107                | JoinType::Full
108                | JoinType::LeftSemi
109                | JoinType::RightSemi
110                | JoinType::LeftAnti
111                | JoinType::RightAnti
112        )
113    }
114}
115
116impl Display for JoinType {
117    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
118        let join_type = match self {
119            JoinType::Inner => "Inner",
120            JoinType::Left => "Left",
121            JoinType::Right => "Right",
122            JoinType::Full => "Full",
123            JoinType::LeftSemi => "LeftSemi",
124            JoinType::RightSemi => "RightSemi",
125            JoinType::LeftAnti => "LeftAnti",
126            JoinType::RightAnti => "RightAnti",
127            JoinType::LeftMark => "LeftMark",
128            JoinType::RightMark => "RightMark",
129        };
130        write!(f, "{join_type}")
131    }
132}
133
134impl FromStr for JoinType {
135    type Err = DataFusionError;
136
137    fn from_str(s: &str) -> Result<Self> {
138        let s = s.to_uppercase();
139        match s.as_str() {
140            "INNER" => Ok(JoinType::Inner),
141            "LEFT" => Ok(JoinType::Left),
142            "RIGHT" => Ok(JoinType::Right),
143            "FULL" => Ok(JoinType::Full),
144            "LEFTSEMI" => Ok(JoinType::LeftSemi),
145            "RIGHTSEMI" => Ok(JoinType::RightSemi),
146            "LEFTANTI" => Ok(JoinType::LeftAnti),
147            "RIGHTANTI" => Ok(JoinType::RightAnti),
148            "LEFTMARK" => Ok(JoinType::LeftMark),
149            "RIGHTMARK" => Ok(JoinType::RightMark),
150            _ => _not_impl_err!("The join type {s} does not exist or is not implemented"),
151        }
152    }
153}
154
155/// Join constraint
156#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Hash)]
157pub enum JoinConstraint {
158    /// Join ON
159    On,
160    /// Join USING
161    Using,
162}
163
164impl Display for JoinSide {
165    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
166        match self {
167            JoinSide::Left => write!(f, "left"),
168            JoinSide::Right => write!(f, "right"),
169            JoinSide::None => write!(f, "none"),
170        }
171    }
172}
173
174/// Join side.
175/// Stores the referred table side during calculations
176#[derive(Debug, Clone, Copy, PartialEq)]
177pub enum JoinSide {
178    /// Left side of the join
179    Left,
180    /// Right side of the join
181    Right,
182    /// Neither side of the join, used for Mark joins where the mark column does not belong to
183    /// either side of the join
184    None,
185}
186
187impl JoinSide {
188    /// Inverse the join side
189    pub fn negate(&self) -> Self {
190        match self {
191            JoinSide::Left => JoinSide::Right,
192            JoinSide::Right => JoinSide::Left,
193            JoinSide::None => JoinSide::None,
194        }
195    }
196}