Try to plan `col IN (SELECT col2 FROM tbl ...)` as a semi/anti join.
(
outer_expr: &Expr,
subquery: &ast::Query,
negated: bool,
catalog: &dyn SqlCatalog,
functions: &FunctionRegistry,
temporal: crate::TemporalScope,
)
| 152 | |
| 153 | /// Try to plan `col IN (SELECT col2 FROM tbl ...)` as a semi/anti join. |
| 154 | fn try_plan_in_subquery( |
| 155 | outer_expr: &Expr, |
| 156 | subquery: &ast::Query, |
| 157 | negated: bool, |
| 158 | catalog: &dyn SqlCatalog, |
| 159 | functions: &FunctionRegistry, |
| 160 | temporal: crate::TemporalScope, |
| 161 | ) -> Result<Option<SubqueryJoin>> { |
| 162 | // Extract outer column name. |
| 163 | let outer_col = match outer_expr { |
| 164 | Expr::Identifier(ident) => normalize_ident(ident), |
| 165 | Expr::CompoundIdentifier(parts) if parts.len() >= 3 => { |
| 166 | let qualified: String = parts |
| 167 | .iter() |
| 168 | .map(normalize_ident) |
| 169 | .collect::<Vec<_>>() |
| 170 | .join("."); |
| 171 | return Err(SqlError::Unsupported { |
| 172 | detail: format!( |
| 173 | "schema-qualified column reference '{qualified}': {SCHEMA_QUALIFIED_MSG}" |
| 174 | ), |
| 175 | }); |
| 176 | } |
| 177 | Expr::CompoundIdentifier(parts) if parts.len() == 2 => normalize_ident(&parts[1]), |
| 178 | _ => return Ok(None), // Complex expression, can't rewrite. |
| 179 | }; |
| 180 | |
| 181 | // Plan the inner SELECT. |
| 182 | let inner_plan = super::select::plan_query(subquery, catalog, functions, temporal)?; |
| 183 | |
| 184 | // Extract the projected column from the inner plan. |
| 185 | let inner_col = extract_single_projected_column(subquery)?; |
| 186 | |
| 187 | Ok(Some(SubqueryJoin { |
| 188 | outer_column: outer_col, |
| 189 | inner_plan, |
| 190 | inner_column: inner_col, |
| 191 | join_type: if negated { |
| 192 | JoinType::Anti |
| 193 | } else { |
| 194 | JoinType::Semi |
| 195 | }, |
| 196 | })) |
| 197 | } |
| 198 | |
| 199 | /// Extract the single column name from a subquery's SELECT list. |
| 200 | /// |
no test coverage detected