CombinedSchema

Struct CombinedSchema 

Source
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: usize

Total number of columns across all tables

§hidden_columns: HashSet<usize>

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

Source

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.

Source

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

Source

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

Source

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.

Source

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

Source

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.

Source

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.

Source

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.

Source

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.

Source

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

Source

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 reference
  • column - The column name (used for error message only)
§Returns
  • Ok(()) if the reference is unambiguous
  • Err(ExecutorError::AmbiguousColumnName) if the table appears multiple times
§Example
-- This should fail validation:
SELECT A.f1 FROM test1 A, test2 A;  -- "A" appears twice
Source

pub fn get_table(&self, table_name: &str) -> Option<&(usize, TableSchema)>

Get a table schema by name (case-insensitive lookup)

Source

pub fn contains_table(&self, table_name: &str) -> bool

Check if a table exists (case-insensitive lookup)

Source

pub fn table_names(&self) -> Vec<String>

Get all table names as strings (using display form)

Source

pub fn insert_table( &mut self, name: String, start_index: usize, schema: TableSchema, )

Insert or update a table in the schema

Source

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.

Source

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 references
  • column - 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.

Source

pub fn is_column_hidden(&self, idx: usize) -> bool

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.

Source

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.

Source

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)
Source

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

Source

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)
Source

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) containing all column indices that should be coalesced for this column name.

Source

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.

Source

pub fn get_column_replacement(&self, hidden_idx: usize) -> Option<usize>

Get the replacement column index for a hidden column, if any.

Source

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.

Source

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.

Source

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.

Source

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) if the given index is part of a coalesce chain.

Source

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

Source§

fn clone(&self) -> CombinedSchema

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for CombinedSchema

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<G1, G2> Within<G2> for G1
where G2: Contains<G1>,

Source§

fn is_within(&self, b: &G2) -> bool

Source§

impl<T> Allocation for T
where T: RefUnwindSafe + Send + Sync,