pub struct CombinedSchema {
pub table_schemas: HashMap<TableIdentifier, (usize, TableSchema)>,
pub total_columns: usize,
pub hidden_columns: HashSet<usize>,
pub outer_schema: Option<Box<CombinedSchema>>,
pub duplicate_aliases: HashSet<TableIdentifier>,
pub joined_columns: HashSet<String>,
pub using_coalesce_indices: HashMap<String, Vec<usize>>,
pub column_replacement_map: HashMap<usize, usize>,
pub alias_tables: HashSet<TableIdentifier>,
pub shadowed_tables: HashMap<TableIdentifier, HashSet<TableIdentifier>>,
}Expand description
Represents the combined schema from multiple tables (for JOINs)
Fields§
§table_schemas: HashMap<TableIdentifier, (usize, TableSchema)>Map from table identifier to (start_index, TableSchema) start_index is where this table’s columns begin in the combined row TableIdentifier provides both canonical form for lookups and display form for output
total_columns: usizeTotal number of columns across all tables
Columns that are hidden from SELECT * expansion due to NATURAL JOIN deduplication.
These columns are still accessible via qualified references like table.*.
This allows SELECT t1.* to return all columns from t1, while SELECT *
correctly deduplicates columns for NATURAL JOIN.
outer_schema: Option<Box<CombinedSchema>>Reference to outer scope schema for nested subquery column resolution (issue #4493) Forms a linked-list chain similar to SQLite’s NameContext.pNext Enables resolution of columns from multiple nesting levels
duplicate_aliases: HashSet<TableIdentifier>Table aliases/names that appear more than once in the FROM clause (issue #4507) Used to detect ambiguous qualified column references like “A.f1” when table “A” appears twice Stores normalized (lowercase) table identifiers for case-insensitive matching
joined_columns: HashSet<String>Column names that have been joined via NATURAL JOIN or USING clause (issue #4517) These columns exist in multiple tables but should NOT be considered ambiguous because they are logically the same column after the join. Stored as lowercase for case-insensitive matching.
using_coalesce_indices: HashMap<String, Vec<usize>>For USING columns in RIGHT/FULL OUTER JOINs, maps the unqualified column name
(lowercase) to a list of column indices for N-way COALESCE resolution.
When an unqualified reference is made to a USING column, we apply COALESCE
semantics: return the first non-NULL value from the list of columns.
This supports chained joins like t1 NATURAL FULL JOIN t2 NATURAL FULL JOIN t3
where we need COALESCE(t1.id, t2.id, t3.id).
Issue #4783, #4903: USING column semantics differ from SQLite in OUTER JOINs
column_replacement_map: HashMap<usize, usize>Column replacement map for RIGHT/FULL OUTER NATURAL JOINs in SELECT * expansion.
Maps hidden_column_index -> replacement_column_index.
When expanding SELECT *, if a column is hidden but has a replacement, output
the replacement column’s value instead of skipping. This maintains the column
ordering from the left table while using values from the right table.
Example: In t5 NATURAL RIGHT JOIN t4, t5.id is hidden but should be replaced
by t4.id to maintain the output order (id, y, x) instead of (y, id, x).
alias_tables: HashSet<TableIdentifier>Alias tables that are added for parenthesized join expressions (issue #4905).
These are virtual tables that point to the same columns as existing tables.
They exist for column resolution (e.g., j1.id in FROM t1 JOIN (...) AS j1 ON j1.id = t1.id).
Stores the table identifiers of alias tables.
shadowed_tables: HashMap<TableIdentifier, HashSet<TableIdentifier>>Tables that are shadowed by an alias table (issue #4786). When a parenthesized join has an alias, the underlying tables are shadowed and should be skipped in SELECT * expansion. Instead, the alias table’s columns should be used. Maps: aliased table name -> tables shadowed by that alias
Implementations§
Source§impl CombinedSchema
impl CombinedSchema
Sourcepub fn empty() -> Self
pub fn empty() -> Self
Create an empty combined schema with no tables
Used for SELECT statements without a FROM clause when the expression evaluation needs outer context for column resolution.
Sourcepub fn from_table(table_name: String, schema: TableSchema) -> Self
pub fn from_table(table_name: String, schema: TableSchema) -> Self
Create a new combined schema from a single table
Note: Table name is automatically normalized via TableIdentifier for case-insensitive lookups
Sourcepub fn from_derived_table(
alias: String,
column_names: Vec<String>,
column_types: Vec<DataType>,
) -> Self
pub fn from_derived_table( alias: String, column_names: Vec<String>, column_types: Vec<DataType>, ) -> Self
Create a new combined schema from a derived table (subquery result)
Note: Alias is automatically normalized via TableIdentifier for case-insensitive lookups
Sourcepub fn add_join_alias(self, alias: &str) -> Self
pub fn add_join_alias(self, alias: &str) -> Self
Add an alias for a parenthesized join expression.
This is used for expressions like (t1 JOIN t2) AS j1 where j1 becomes an
alias for the combined result. The alias is added as a virtual table containing
all visible columns, allowing references like j1.column to work.
The alias table is marked as “alias-only” and will not be expanded in SELECT *.
Important: The alias table uses start_idx=0 and stores the original column
indices in a mapping, so that j1.column resolves to the correct index in the
actual row data.
Sourcepub fn combine(
left: CombinedSchema,
right_table_name: String,
right_schema: TableSchema,
) -> Self
pub fn combine( left: CombinedSchema, right_table_name: String, right_schema: TableSchema, ) -> Self
Combine two schemas (for JOIN operations)
Note: Right table name is automatically normalized via TableIdentifier for case-insensitive lookups
Sourcepub fn merge(left: CombinedSchema, right: CombinedSchema) -> Self
pub fn merge(left: CombinedSchema, right: CombinedSchema) -> Self
Merge two CombinedSchemas (for JOIN operations with nested joins)
Unlike combine which adds a single table, this method merges ALL tables
from the right schema into the left schema. This is essential for nested
joins like t1 JOIN (t2 JOIN t3 USING(a)) USING(a) where the right side
contains multiple tables that must all remain visible.
The right schema’s tables have their start indices adjusted to account for the left schema’s total column count.
Sourcepub fn get_column_index(
&self,
table: Option<&str>,
column: &str,
) -> Option<usize>
pub fn get_column_index( &self, table: Option<&str>, column: &str, ) -> Option<usize>
Look up a column by name (optionally qualified with table name) Uses case-insensitive matching for table/alias and column names
Searches the current schema level first, then follows the outer_schema chain to search enclosing scopes (similar to SQLite’s NameContext.pNext). This enables correlated subqueries to reference columns from outer queries.
Sourcepub fn get_column_affinity(
&self,
table: Option<&str>,
column: &str,
) -> Option<TypeAffinity>
pub fn get_column_affinity( &self, table: Option<&str>, column: &str, ) -> Option<TypeAffinity>
Get the type affinity for a column by name
Returns the SQLite type affinity for the column, which determines how type coercion is performed in comparisons.
Sourcepub fn is_column_ambiguous(&self, column: &str) -> bool
pub fn is_column_ambiguous(&self, column: &str) -> bool
Check if an unqualified column reference is ambiguous (i.e., exists in multiple tables in the schema)
Returns true if the column exists in more than one table, UNLESS the column is a “joined column” from NATURAL JOIN or USING clause. Joined columns are deduplicated and should be accessible without qualification.
Only relevant for unqualified column references - qualified references (with table prefix) are never ambiguous.
Sourcepub fn has_column(&self, column: &str) -> bool
pub fn has_column(&self, column: &str) -> bool
Check if any table in this schema has a column with the given name.
This is a faster alternative to building a HashSet of all column names for cases where we just need to check if a column exists. Used for WHERE clause alias resolution (SQLite compatibility).
§Arguments
column- The column name (case-insensitive)
§Returns
true if any table in the schema has a column matching this name
Sourcepub fn validate_qualified_reference(
&self,
table: &str,
column: &str,
) -> Result<(), ExecutorError>
pub fn validate_qualified_reference( &self, table: &str, column: &str, ) -> Result<(), ExecutorError>
Validate that a qualified column reference is not ambiguous.
This checks if the table identifier appears more than once in the FROM clause, which would make qualified references like “A.f1” ambiguous (issue #4507).
§Arguments
table- The table name/alias from the qualified referencecolumn- The column name (used for error message only)
§Returns
Ok(())if the reference is unambiguousErr(ExecutorError::AmbiguousColumnName)if the table appears multiple times
§Example
-- This should fail validation:
SELECT A.f1 FROM test1 A, test2 A; -- "A" appears twiceSourcepub fn get_table(&self, table_name: &str) -> Option<&(usize, TableSchema)>
pub fn get_table(&self, table_name: &str) -> Option<&(usize, TableSchema)>
Get a table schema by name (case-insensitive lookup)
Sourcepub fn contains_table(&self, table_name: &str) -> bool
pub fn contains_table(&self, table_name: &str) -> bool
Check if a table exists (case-insensitive lookup)
Sourcepub fn table_names(&self) -> Vec<String>
pub fn table_names(&self) -> Vec<String>
Get all table names as strings (using display form)
Sourcepub fn insert_table(
&mut self,
name: String,
start_index: usize,
schema: TableSchema,
)
pub fn insert_table( &mut self, name: String, start_index: usize, schema: TableSchema, )
Insert or update a table in the schema
Sourcepub fn get_original_column_name(
&self,
table: Option<&str>,
column: &str,
) -> String
pub fn get_original_column_name( &self, table: Option<&str>, column: &str, ) -> String
Get the original column name from the schema for a column reference.
SQLite preserves the schema column name (not the query identifier case) when returning column names in results. This method looks up the column in the schema and returns the original name.
§Arguments
table- Optional table name for qualified references (e.g., “t1” in “t1.col”)column- Column name to look up (case-insensitive)
§Returns
The original column name from the schema, or the input column name if not found.
Sourcepub fn get_full_column_name(&self, table: Option<&str>, column: &str) -> String
pub fn get_full_column_name(&self, table: Option<&str>, column: &str) -> String
Get the fully qualified column name with original table name prefix.
This follows SQLite’s full_column_names=ON behavior where column names
in results are prefixed with the original table name from the schema.
For example, if a table was created as CREATE TABLE test1(f1 int) and
queried with SELECT a.f1 FROM test1 a, this returns test1.f1 (using
the original table name “test1”, not the alias “a”).
§Arguments
table- Optional table alias/name for qualified referencescolumn- Column name to look up (case-insensitive)
§Returns
The fully qualified column name in table.column format, or just the
column name if the table is not found.
Check if a column index is hidden from SELECT * expansion.
Columns are hidden when they are duplicates in a NATURAL JOIN.
For example, in SELECT * FROM t1 NATURAL JOIN t2 where both tables
have column a, the t2.a column is hidden so SELECT * only shows
one copy of a (from t1).
However, SELECT t2.* should still include t2.a because qualified
wildcards expand all columns from that specific table.
Sourcepub fn hide_column(&mut self, idx: usize)
pub fn hide_column(&mut self, idx: usize)
Mark a column as hidden from SELECT * expansion.
This is used by NATURAL JOIN to hide duplicate columns from the right side.
Sourcepub fn add_joined_column(&mut self, column: &str)
pub fn add_joined_column(&mut self, column: &str)
Mark a column name as a “joined column” from NATURAL JOIN or USING clause.
Joined columns exist in multiple tables but should NOT be considered ambiguous because they are logically the same column after the join. This allows unqualified references to these columns without triggering an ambiguity error.
§Arguments
column- The column name (will be normalized to lowercase)
Sourcepub fn add_using_coalesce_pair(
&mut self,
column: &str,
left_idx: usize,
right_idx: usize,
)
pub fn add_using_coalesce_pair( &mut self, column: &str, left_idx: usize, right_idx: usize, )
Add a USING column coalesce pair for RIGHT/FULL OUTER JOINs.
For USING columns in OUTER JOINs, unqualified references should use COALESCE semantics. This method records a column index for later coalesce evaluation. For chained joins, each call extends the existing Vec with new indices.
§Arguments
column- The column name (will be normalized to lowercase)left_idx- Index of the left-side column (first in the chain)right_idx- Index of the right-side column (added to the chain)
Issue #4783, #4903: USING column semantics in OUTER JOINs with N-way coalescing
Sourcepub fn get_using_coalesce_pair(&self, column: &str) -> Option<(usize, usize)>
pub fn get_using_coalesce_pair(&self, column: &str) -> Option<(usize, usize)>
Get the coalesce pair for a USING column, if any. For backwards compatibility, returns the first two indices as a pair.
Returns Some((left_idx, right_idx)) if this column needs COALESCE semantics for OUTER JOIN USING, None otherwise.
§Arguments
column- The column name (will be normalized to lowercase)
Sourcepub fn get_using_coalesce_indices(&self, column: &str) -> Option<&Vec<usize>>
pub fn get_using_coalesce_indices(&self, column: &str) -> Option<&Vec<usize>>
Get all coalesce indices for a USING column (for N-way COALESCE).
Returns Some(&Vec
Sourcepub fn add_column_replacement(
&mut self,
hidden_idx: usize,
replacement_idx: usize,
)
pub fn add_column_replacement( &mut self, hidden_idx: usize, replacement_idx: usize, )
Add a column replacement for SELECT * expansion (for RIGHT/FULL OUTER JOINs).
When a hidden column has a replacement, SELECT * will output the replacement column’s value at the hidden column’s position, maintaining correct column ordering.
Sourcepub fn get_column_replacement(&self, hidden_idx: usize) -> Option<usize>
pub fn get_column_replacement(&self, hidden_idx: usize) -> Option<usize>
Get the replacement column index for a hidden column, if any.
Sourcepub fn get_using_coalesce_rest_for_left(
&self,
left_idx: usize,
) -> Option<&[usize]>
pub fn get_using_coalesce_rest_for_left( &self, left_idx: usize, ) -> Option<&[usize]>
Get all indices for N-way COALESCE for a left-side USING column (for SELECT *).
In FULL OUTER JOIN with USING clause, when expanding SELECT *, we need to apply N-way COALESCE for USING columns. This method returns all indices except the first (which is the “left” index) for coalescing.
Returns Some(&[indices]) if the given index is a left-side USING column, None otherwise.
Sourcepub fn get_using_coalesce_right_for_left(
&self,
left_idx: usize,
) -> Option<usize>
pub fn get_using_coalesce_right_for_left( &self, left_idx: usize, ) -> Option<usize>
Get the right-side column index for a left-side USING column (for COALESCE in SELECT *). For backwards compatibility - returns only the second index (first “right” index).
Returns Some(right_idx) if the given index is a left-side USING column, None otherwise.
Sourcepub fn is_using_coalesce_right_side(&self, idx: usize) -> bool
pub fn is_using_coalesce_right_side(&self, idx: usize) -> bool
Check if the given column index is a right-side of a USING coalesce chain.
These columns should be skipped in SELECT * output because they’re represented by the first column with COALESCE applied.
Sourcepub fn get_all_coalesce_indices_for_column(
&self,
idx: usize,
) -> Option<&Vec<usize>>
pub fn get_all_coalesce_indices_for_column( &self, idx: usize, ) -> Option<&Vec<usize>>
Get all coalesce indices for a column that’s anywhere in the chain.
Unlike get_using_coalesce_rest_for_left which only works if the given index
is the FIRST in the chain, this method returns all indices in the chain if
the given index is found ANYWHERE in the chain. This is needed for N-way
coalescing where the visible column might be in the middle of the chain.
Returns Some(&Vec
Sourcepub fn build_column_name_map(&self) -> HashMap<String, usize>
pub fn build_column_name_map(&self) -> HashMap<String, usize>
Build a map from column names to their indices.
This is used by window function frame calculations to resolve named column references in ORDER BY expressions. The map contains both the original case and lowercase versions of column names for case-insensitive matching.
For columns that appear in multiple tables, the mapping prefers the leftmost table (lowest start_index) to match the behavior of unqualified column lookups.
Trait Implementations§
Source§impl Clone for CombinedSchema
impl Clone for CombinedSchema
Source§fn clone(&self) -> CombinedSchema
fn clone(&self) -> CombinedSchema
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreAuto Trait Implementations§
impl Freeze for CombinedSchema
impl RefUnwindSafe for CombinedSchema
impl Send for CombinedSchema
impl Sync for CombinedSchema
impl Unpin for CombinedSchema
impl UnwindSafe for CombinedSchema
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more