Skip to main content

TableSource

Trait TableSource 

Source
pub trait TableSource:
    DataSource
    + Clone
    + 'static {
    type Column<Type>: ColumnLike<Type> + Clone
       where Type: ColumnType;
    type AnyType: ColumnType;
    type Value: Clone + Send + Sync + 'static;
    type Id: Send + Sync + Clone + Hash + Eq + 'static;
    type Source: TableSourceSpec;
    type Condition: Clone + Send + Sync + 'static;

Show 32 methods // Required methods fn create_column<Type: ColumnType>(&self, name: &str) -> Self::Column<Type>; fn to_any_column<Type: ColumnType>( &self, column: Self::Column<Type>, ) -> Self::Column<Self::AnyType>; fn convert_any_column<Type: ColumnType>( &self, any_column: Self::Column<Self::AnyType>, ) -> Option<Self::Column<Type>>; fn expr( &self, template: impl Into<String>, parameters: Vec<ExpressiveEnum<Self::Value>>, ) -> Expression<Self::Value>; fn search_table_condition<E>( &self, table: &Table<Self, E>, search_value: &str, ) -> Self::Condition where E: Entity<Self::Value>, Self: Sized; fn list_table_values<'life0, 'life1, 'async_trait, E>( &'life0 self, table: &'life1 Table<Self, E>, ) -> Pin<Box<dyn Future<Output = Result<IndexMap<Self::Id, Record<Self::Value>>>> + Send + 'async_trait>> where E: Entity<Self::Value> + 'async_trait, Self: Sized + 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait; fn get_table_value<'life0, 'life1, 'life2, 'async_trait, E>( &'life0 self, table: &'life1 Table<Self, E>, id: &'life2 Self::Id, ) -> Pin<Box<dyn Future<Output = Result<Option<Record<Self::Value>>>> + Send + 'async_trait>> where E: Entity<Self::Value> + 'async_trait, Self: Sized + 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait; fn get_table_some_value<'life0, 'life1, 'async_trait, E>( &'life0 self, table: &'life1 Table<Self, E>, ) -> Pin<Box<dyn Future<Output = Result<Option<(Self::Id, Record<Self::Value>)>>> + Send + 'async_trait>> where E: Entity<Self::Value> + 'async_trait, Self: Sized + 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait; fn get_table_count<'life0, 'life1, 'async_trait, E>( &'life0 self, table: &'life1 Table<Self, E>, ) -> Pin<Box<dyn Future<Output = Result<i64>> + Send + 'async_trait>> where E: Entity<Self::Value> + 'async_trait, Self: Sized + 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait; fn get_table_sum<'life0, 'life1, 'life2, 'async_trait, E>( &'life0 self, table: &'life1 Table<Self, E>, column: &'life2 Self::Column<Self::AnyType>, ) -> Pin<Box<dyn Future<Output = Result<Self::Value>> + Send + 'async_trait>> where E: Entity<Self::Value> + 'async_trait, Self: Sized + 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait; fn get_table_max<'life0, 'life1, 'life2, 'async_trait, E>( &'life0 self, table: &'life1 Table<Self, E>, column: &'life2 Self::Column<Self::AnyType>, ) -> Pin<Box<dyn Future<Output = Result<Self::Value>> + Send + 'async_trait>> where E: Entity<Self::Value> + 'async_trait, Self: Sized + 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait; fn get_table_min<'life0, 'life1, 'life2, 'async_trait, E>( &'life0 self, table: &'life1 Table<Self, E>, column: &'life2 Self::Column<Self::AnyType>, ) -> Pin<Box<dyn Future<Output = Result<Self::Value>> + Send + 'async_trait>> where E: Entity<Self::Value> + 'async_trait, Self: Sized + 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait; fn insert_table_value<'life0, 'life1, 'life2, 'life3, 'async_trait, E>( &'life0 self, table: &'life1 Table<Self, E>, id: &'life2 Self::Id, record: &'life3 Record<Self::Value>, ) -> Pin<Box<dyn Future<Output = Result<Record<Self::Value>>> + Send + 'async_trait>> where E: Entity<Self::Value> + 'async_trait, Self: Sized + 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait; fn replace_table_value<'life0, 'life1, 'life2, 'life3, 'async_trait, E>( &'life0 self, table: &'life1 Table<Self, E>, id: &'life2 Self::Id, record: &'life3 Record<Self::Value>, ) -> Pin<Box<dyn Future<Output = Result<Record<Self::Value>>> + Send + 'async_trait>> where E: Entity<Self::Value> + 'async_trait, Self: Sized + 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait; fn patch_table_value<'life0, 'life1, 'life2, 'life3, 'async_trait, E>( &'life0 self, table: &'life1 Table<Self, E>, id: &'life2 Self::Id, partial: &'life3 Record<Self::Value>, ) -> Pin<Box<dyn Future<Output = Result<Record<Self::Value>>> + Send + 'async_trait>> where E: Entity<Self::Value> + 'async_trait, Self: Sized + 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait; fn delete_table_value<'life0, 'life1, 'life2, 'async_trait, E>( &'life0 self, table: &'life1 Table<Self, E>, id: &'life2 Self::Id, ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>> where E: Entity<Self::Value> + 'async_trait, Self: Sized + 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait; fn delete_table_all_values<'life0, 'life1, 'async_trait, E>( &'life0 self, table: &'life1 Table<Self, E>, ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>> where E: Entity<Self::Value> + 'async_trait, Self: Sized + 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait; fn insert_table_return_id_value<'life0, 'life1, 'life2, 'async_trait, E>( &'life0 self, table: &'life1 Table<Self, E>, record: &'life2 Record<Self::Value>, ) -> Pin<Box<dyn Future<Output = Result<Self::Id>> + Send + 'async_trait>> where E: Entity<Self::Value> + 'async_trait, Self: Sized + 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait; fn related_in_condition<SourceE: Entity<Self::Value> + 'static>( &self, target_field: &str, source_table: &Table<Self, SourceE>, source_column: &str, ) -> Self::Condition where Self: Sized; fn column_table_values_expr<'a, E, Type: ColumnType>( &'a self, table: &Table<Self, E>, column: &Self::Column<Type>, ) -> AssociatedExpression<'a, Self, Self::Value, Vec<Type>> where E: Entity<Self::Value> + 'static, Self: ExprDataSource<Self::Value> + Sized; // Provided methods fn eq_condition(field: &str, value: &str) -> Result<Self::Condition> { ... } fn eq_value_condition( &self, field: &str, value: Self::Value, ) -> Result<Self::Condition> { ... } fn in_values_condition( &self, field: &str, values: Vec<Self::Value>, ) -> Result<Self::Condition> { ... } fn condition_equality( &self, condition: &Self::Condition, ) -> Option<(String, Self::Value)> { ... } fn describe_condition(&self, condition: &Self::Condition) -> String { ... } fn condition_column(&self, condition: &Self::Condition) -> Option<String> { ... } fn can_confine_writes(&self) -> bool { ... } fn coerce_reference_value(&self, value: Self::Value) -> Self::Value { ... } fn stream_table_values<'a, E>( &'a self, table: &Table<Self, E>, ) -> Pin<Box<dyn Stream<Item = Result<(Self::Id, Record<Self::Value>)>> + Send + 'a>> where E: Entity<Self::Value> + 'a, Self: Sized { ... } fn related_correlated_condition( &self, target_table: &str, target_field: &str, source_table: &str, source_column: &str, ) -> Self::Condition { ... } fn supports_traversal(&self) -> bool { ... } fn traversal_path_expr( &self, hops: &[&str], column: &str, ) -> Option<Expression<Self::Value>> { ... }
}
Expand description

A data source that holds tables: it names their column, value, id, source and condition types, and runs Table’s reads and writes.

Required Associated Types§

Source

type Column<Type>: ColumnLike<Type> + Clone where Type: ColumnType

Source

type AnyType: ColumnType

Source

type Value: Clone + Send + Sync + 'static

Source

type Id: Send + Sync + Clone + Hash + Eq + 'static

Source

type Source: TableSourceSpec

How this backend names its source. Most backends use String; SQL/SurrealDB backends use SelectSource<Self::Select> so a table can be sourced from an arbitrary sub-SELECT. See crate::source.

Source

type Condition: Clone + Send + Sync + 'static

The condition type stored by Table. SQL/SurrealDB backends use Expression<Self::Value>; document-oriented backends like MongoDB can use a native filter type (e.g. bson::Document).

Required Methods§

Source

fn create_column<Type: ColumnType>(&self, name: &str) -> Self::Column<Type>

Create a new column with the given name

Source

fn to_any_column<Type: ColumnType>( &self, column: Self::Column<Type>, ) -> Self::Column<Self::AnyType>

Convert a typed column to type-erased column

Source

fn convert_any_column<Type: ColumnType>( &self, any_column: Self::Column<Self::AnyType>, ) -> Option<Self::Column<Type>>

Attempt to convert a type-erased column back to typed column

Source

fn expr( &self, template: impl Into<String>, parameters: Vec<ExpressiveEnum<Self::Value>>, ) -> Expression<Self::Value>

Create an expression from a template and parameters, similar to Expression::new

Source

fn search_table_condition<E>( &self, table: &Table<Self, E>, search_value: &str, ) -> Self::Condition
where E: Entity<Self::Value>, Self: Sized,

Create a search condition matching search_value across the columns of Table::search_columns: those flagged Searchable, or every column when none is. A backend may leave out columns it can’t filter on (dotted paths, imported columns). This is a server-side capability — it pushes the match down to the backend’s query engine.

Different vendors implement search differently:

  • SQL: field LIKE '%value%' (returns Expression)
  • SurrealDB: field CONTAINS 'value' (returns Expression)
  • MongoDB: { field: { $regex: 'value' } } (returns MongoCondition)

Contract for backends that cannot search server-side (e.g. CSV, redb): return a condition that yields an ErrorKind::Unsupported error when it is resolved — never a silent match-all (which leaks every row as if filtered) and never a panic. Searching loaded/in-memory data is the Lens/Diorama layer’s responsibility, not the backend’s.

Source

fn list_table_values<'life0, 'life1, 'async_trait, E>( &'life0 self, table: &'life1 Table<Self, E>, ) -> Pin<Box<dyn Future<Output = Result<IndexMap<Self::Id, Record<Self::Value>>>> + Send + 'async_trait>>
where E: Entity<Self::Value> + 'async_trait, Self: Sized + 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Get all data from a table as Record values with IDs (for ReadableValueSet implementation)

Source

fn get_table_value<'life0, 'life1, 'life2, 'async_trait, E>( &'life0 self, table: &'life1 Table<Self, E>, id: &'life2 Self::Id, ) -> Pin<Box<dyn Future<Output = Result<Option<Record<Self::Value>>>> + Send + 'async_trait>>
where E: Entity<Self::Value> + 'async_trait, Self: Sized + 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Get a single record by ID as Record value (for ReadableValueSet implementation).

Returns Ok(None) when no record exists with the given ID.

Source

fn get_table_some_value<'life0, 'life1, 'async_trait, E>( &'life0 self, table: &'life1 Table<Self, E>, ) -> Pin<Box<dyn Future<Output = Result<Option<(Self::Id, Record<Self::Value>)>>> + Send + 'async_trait>>
where E: Entity<Self::Value> + 'async_trait, Self: Sized + 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Get some data from a table as Record value with ID (for ReadableValueSet implementation)

Source

fn get_table_count<'life0, 'life1, 'async_trait, E>( &'life0 self, table: &'life1 Table<Self, E>, ) -> Pin<Box<dyn Future<Output = Result<i64>> + Send + 'async_trait>>
where E: Entity<Self::Value> + 'async_trait, Self: Sized + 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Get count of records in the table

Source

fn get_table_sum<'life0, 'life1, 'life2, 'async_trait, E>( &'life0 self, table: &'life1 Table<Self, E>, column: &'life2 Self::Column<Self::AnyType>, ) -> Pin<Box<dyn Future<Output = Result<Self::Value>> + Send + 'async_trait>>
where E: Entity<Self::Value> + 'async_trait, Self: Sized + 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Get sum of a column in the table (returns native value type)

Source

fn get_table_max<'life0, 'life1, 'life2, 'async_trait, E>( &'life0 self, table: &'life1 Table<Self, E>, column: &'life2 Self::Column<Self::AnyType>, ) -> Pin<Box<dyn Future<Output = Result<Self::Value>> + Send + 'async_trait>>
where E: Entity<Self::Value> + 'async_trait, Self: Sized + 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Get maximum value of a column in the table (returns native value type)

Source

fn get_table_min<'life0, 'life1, 'life2, 'async_trait, E>( &'life0 self, table: &'life1 Table<Self, E>, column: &'life2 Self::Column<Self::AnyType>, ) -> Pin<Box<dyn Future<Output = Result<Self::Value>> + Send + 'async_trait>>
where E: Entity<Self::Value> + 'async_trait, Self: Sized + 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Get minimum value of a column in the table (returns native value type)

Source

fn insert_table_value<'life0, 'life1, 'life2, 'life3, 'async_trait, E>( &'life0 self, table: &'life1 Table<Self, E>, id: &'life2 Self::Id, record: &'life3 Record<Self::Value>, ) -> Pin<Box<dyn Future<Output = Result<Record<Self::Value>>> + Send + 'async_trait>>
where E: Entity<Self::Value> + 'async_trait, Self: Sized + 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait,

Insert a record by ID. Confined and idempotent: when the id is already stored in table.conditions(), return the stored row unchanged; when it is held by a row outside the set, fail with Conflict; a new row must fit the conditions, else Conflict and nothing is stored. Refuse with Unsupported when a condition can be neither applied nor evaluated. See WritableValueSet for the table.

Source

fn replace_table_value<'life0, 'life1, 'life2, 'life3, 'async_trait, E>( &'life0 self, table: &'life1 Table<Self, E>, id: &'life2 Self::Id, record: &'life3 Record<Self::Value>, ) -> Pin<Box<dyn Future<Output = Result<Record<Self::Value>>> + Send + 'async_trait>>
where E: Entity<Self::Value> + 'async_trait, Self: Sized + 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait,

Replace a record by ID, creating it when missing. Confined and idempotent: an id held by a row outside table.conditions() fails with Conflict; the written row must fit the conditions, else Conflict and nothing changes. Refuse with Unsupported when a condition can be neither applied nor evaluated. See WritableValueSet for the table.

Source

fn patch_table_value<'life0, 'life1, 'life2, 'life3, 'async_trait, E>( &'life0 self, table: &'life1 Table<Self, E>, id: &'life2 Self::Id, partial: &'life3 Record<Self::Value>, ) -> Pin<Box<dyn Future<Output = Result<Record<Self::Value>>> + Send + 'async_trait>>
where E: Entity<Self::Value> + 'async_trait, Self: Sized + 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait,

Merge partial into the row with this ID. Confined: a missing row or one outside table.conditions() is NotFound (marked); the merged row must still fit the conditions, else Conflict and nothing changes. Refuse with Unsupported when a condition can be neither applied nor evaluated. See WritableValueSet for the table.

Source

fn delete_table_value<'life0, 'life1, 'life2, 'async_trait, E>( &'life0 self, table: &'life1 Table<Self, E>, id: &'life2 Self::Id, ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>
where E: Entity<Self::Value> + 'async_trait, Self: Sized + 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Delete a record by ID. Confined and idempotent: delete only when the row is in table.conditions(); a missing row or a row outside the set is Ok(()) (no lookup is needed — SQL: DELETE … WHERE id = ? AND <conditions>). Refuse with Unsupported when a condition can be neither applied nor evaluated. See WritableValueSet for the table.

Source

fn delete_table_all_values<'life0, 'life1, 'async_trait, E>( &'life0 self, table: &'life1 Table<Self, E>, ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>
where E: Entity<Self::Value> + 'async_trait, Self: Sized + 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Delete every record the table represents (for WritableValueSet).

Honour table.conditions(). A conditioned table is a SUBSET, and every read path already treats it as one, so deleting it must delete that subset. An implementation that builds its statement from table_name() alone silently truncates the table instead — the caller narrowed to one row, and lost all of them. An unconditioned table still deletes everything, which is the only case this looks like.

Source

fn insert_table_return_id_value<'life0, 'life1, 'life2, 'async_trait, E>( &'life0 self, table: &'life1 Table<Self, E>, record: &'life2 Record<Self::Value>, ) -> Pin<Box<dyn Future<Output = Result<Self::Id>> + Send + 'async_trait>>
where E: Entity<Self::Value> + 'async_trait, Self: Sized + 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Insert a record under a generated ID and return it. Confined, not retry-safe: the record must fit table.conditions(), else Conflict and nothing is stored; a retry after a lost reply stores a second row. Refuse with Unsupported when a condition can be neither applied nor evaluated.

Source

fn related_in_condition<SourceE: Entity<Self::Value> + 'static>( &self, target_field: &str, source_table: &Table<Self, SourceE>, source_column: &str, ) -> Self::Condition
where Self: Sized,

Build a condition for “target_field IN (values of source_column from source_table)”.

Used by the relationship traversal system (get_ref_as) to filter a target table by foreign key values from a source table.

Each backend implements this natively:

  • SQL backends build a subquery: "id" IN (SELECT "bakery_id" FROM "client" WHERE ...)
  • MongoDB builds a deferred { field: { "$in": [...] } } document
  • CSV fetches values in memory and builds an IN condition
Source

fn column_table_values_expr<'a, E, Type: ColumnType>( &'a self, table: &Table<Self, E>, column: &Self::Column<Type>, ) -> AssociatedExpression<'a, Self, Self::Value, Vec<Type>>
where E: Entity<Self::Value> + 'static, Self: ExprDataSource<Self::Value> + Sized,

Return an associated expression that, when resolved, yields all values of the given typed column from this table (respecting current conditions).

For query-language backends, this can be a subquery expression. For simple backends (CSV), this uses a DeferredFn that loads data and extracts the column values at execution time.

The returned AssociatedExpression can be:

  • Executed directly: .get().await -> Result<Vec<Type>>
  • Composed into expressions: used via Expressive trait in in_() conditions
ⓘ
let fk_col = source.get_column::<String>("bakery_id").unwrap();
let fk_values = source.data_source().column_table_values_expr(&source, &fk_col);
// Execute: let ids = fk_values.get().await?;
// Or compose: target.add_condition(target["id"].in_((fk_values)));

Provided Methods§

Source

fn eq_condition(field: &str, value: &str) -> Result<Self::Condition>

Build a textual field == value condition.

Generic UIs (e.g. CLI argument parsers) only have strings on hand, so they need a way to construct a Self::Condition without knowing the backend’s native expression type. Backends that don’t support raw text filtering can leave the default, which returns an error.

Source

fn eq_value_condition( &self, field: &str, value: Self::Value, ) -> Result<Self::Condition>

Build a field == value condition with a typed Self::Value.

Sibling of TableSource::eq_condition for the case where the caller already has a native-typed value in hand (e.g. Reference::resolve_from_row pulling a join value out of a Record<Self::Value>). Avoids going through string serialization and avoids the Expression → T::Condition coercion that some document backends only support via a panic stub.

Backends that participate in row-based reference traversal override; the default returns an error so existing impls compile.

Source

fn in_values_condition( &self, field: &str, values: Vec<Self::Value>, ) -> Result<Self::Condition>

Build a field IN (values) condition from native-typed values: the list-valued sibling of Self::eq_value_condition. Self::condition_column names field for it. An empty values matches no row. The default returns an error.

Source

fn condition_equality( &self, condition: &Self::Condition, ) -> Option<(String, Self::Value)>

When condition is a literal column = value, return the pair.

Table::add_condition registers it as a set invariant: an insert or replace through the table fills an absent column, a patch may not change it. Expression backends use crate::conditions::literal_equality; document backends read their own filter shape. Default None: the condition still narrows, it just fills nothing.

Source

fn describe_condition(&self, condition: &Self::Condition) -> String

condition as text for people, e.g. client_id = 7: what a log entry records for a bulk delete. Not for parsing; use Self::condition_column / Self::condition_equality in code. A part resolved only at query time renders as a placeholder.

Source

fn condition_column(&self, condition: &Self::Condition) -> Option<String>

The column condition narrows to values, for the shapes this backend can read (col = v, col IN (…), comparisons, LIKE), with a value — not a column or an arbitrary expression — on the right. None when it can’t tell — a raw expression, a combination, a negation (!=, NOT IN, NOT LIKE), col = other_col — so a check that requires a condition on some column fails safe.

Source

fn can_confine_writes(&self) -> bool

Whether every by-id write (insert_table_value, replace_table_value, patch_table_value, delete_table_value, insert_table_return_id_value) honours the write contract over table.conditions(): it only touches rows in the set, and only leaves rows that still belong to it. A source that returns false is refused any by-id write through a conditioned table: Table refuses it with Unsupported before reaching the source. Default false.

Source

fn coerce_reference_value(&self, value: Self::Value) -> Self::Value

Coerce a reference join value into the backend’s native id form.

Reference traversal (resolve_from_row) reads the join value raw out of the parent row and uses it for both the narrowing eq-condition and the target’s insert invariant. On scalar-FK backends any value compares as-is, so the default is identity. Backends with a richer id type override: SurrealDB re-tags a "table:key" string — a record id that round-tripped through JSON or a script — back into a record id, so the condition renders a record literal (and an inserted child stores a link), not a quoted string that matches nothing.

Source

fn stream_table_values<'a, E>( &'a self, table: &Table<Self, E>, ) -> Pin<Box<dyn Stream<Item = Result<(Self::Id, Record<Self::Value>)>> + Send + 'a>>
where E: Entity<Self::Value> + 'a, Self: Sized,

Stream all records from a table as (Id, Record) pairs.

Default implementation wraps list_table_values into a stream. Backends with native streaming (e.g. REST APIs with pagination) can override this to yield records incrementally.

Source

fn related_correlated_condition( &self, target_table: &str, target_field: &str, source_table: &str, source_column: &str, ) -> Self::Condition

Build a correlated condition: target_table.target_field = source_table.source_column.

Used by get_subquery_as / get_subquery_erased for embedding correlated subqueries inside SELECT expressions (e.g. (SELECT COUNT(*) FROM order WHERE order.client_id = client.id)), and by the generic implicit-reference lowering. SQL and SurrealDB backends produce table-qualified equality; backends without correlated subqueries leave the default (which panics — they must also keep supports_traversal false).

Source

fn supports_traversal(&self) -> bool

Whether this backend can lower a dotted active column ("country.name") into its own query — a nested correlated subquery for SQL, a native idiom path for SurrealDB, $lookup stages for MongoDB. Backends that can build none of these (e.g. CSV, REST) leave the default false, and Table::with_active_columns rejects dotted names against them at build time rather than at fetch time.

Source

fn traversal_path_expr( &self, hops: &[&str], column: &str, ) -> Option<Expression<Self::Value>>

Build a backend-native path expression for a dotted active column, tried before the generic correlated-subquery chain.

hops holds each traversed relation’s foreign-key/link field (not its registry name — a relation named owner over a link field client must lower to client.…), and column the final field. The default returns None, so backends fall back to the generic chain built on related_correlated_condition. SurrealDB overrides this to emit an idiom path (batch.golf_course.name, each segment escaped separately); multi-hop comes for free. The returned expression is unaliased — select() aliases it to the dotted name.

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§