pub struct EmbedPlan {
pub local_column: String,
pub foreign_column: String,
pub foreign_column_type: String,
pub foreign_schema: String,
pub foreign_table: String,
pub is_list: bool,
pub columns: Vec<(String, String)>,
pub junction: Option<EmbedJunction>,
pub function: Option<QualifiedIdentifier>,
pub row_argument: Option<String>,
}Expand description
Everything needed to fetch one relationship’s rows for a set of parents.
Fields§
§local_column: StringColumn on the parent row whose value identifies the parent.
foreign_column: StringColumn on the related table that points back at the parent.
foreign_column_type: StringPostgreSQL type of the foreign column, used to cast the bound array.
foreign_schema: StringSchema of the related table.
foreign_table: StringName of the related table.
is_list: boolWhether the relationship yields many rows per parent.
columns: Vec<(String, String)>Every column pair the join is on, parent side first.
Usually one. A foreign key over several columns joins on all of them, and the pairs are ordered so each parent column sits with the child column it actually references.
junction: Option<EmbedJunction>The junction of a many-to-many relationship, if that is what this is.
The parent and the child share no key; each points at a table that exists to join them, so reaching the child means going through it.
function: Option<QualifiedIdentifier>The function behind a computed relationship, if that is what this is.
A computed relationship is a function taking the parent row and returning rows of the related table. There is no key to join on – the parent row is the argument – so the columns above are empty and the relationship can only be expressed as a call correlated to the parent.
row_argument: Option<String>The name of the parameter that takes the parent’s row, where the function has more than one and the call has to be written by name.
Implementations§
Source§impl EmbedPlan
impl EmbedPlan
Sourcepub fn resolve(
relationship: &Relationship,
schema_cache: &SchemaCache,
) -> Result<Self>
pub fn resolve( relationship: &Relationship, schema_cache: &SchemaCache, ) -> Result<Self>
Resolve a relationship into an embed plan.
Returns an error for relationships this cannot express yet, rather than silently omitting the embedded data.
Sourcepub fn children_sql(
&self,
limit: Option<i64>,
columns: &[String],
) -> Result<String>
pub fn children_sql( &self, limit: Option<i64>, columns: &[String], ) -> Result<String>
SQL that fetches every related row for the given parent key values.
The keys are bound as a single text array and cast to the foreign
column’s type, so the column itself is never wrapped in a cast and an
index on it remains usable. limit bounds the rows per query, not per
parent.
columns is the set of columns the client asked for. An empty set means
every column. Projecting here rather than discarding columns after the
fact matters: an unprojected column is read from the heap, serialised to
JSON by PostgreSQL, sent over the socket and parsed, before being thrown
away. The join column is always included even when it was not requested,
because grouping needs it; the caller strips it afterwards.
Sourcepub fn children_grouped_sql(
&self,
limit: Option<i64>,
columns: &[String],
) -> Result<String>
pub fn children_grouped_sql( &self, limit: Option<i64>, columns: &[String], ) -> Result<String>
SQL that fetches related rows already grouped by their join key.
One row comes back per distinct key: the key itself and a JSON array of that key’s children. Grouping in PostgreSQL rather than in this process removes a per-child-row JSON parse, the per-row hash insert, and the clone of each group onto its parent.
The key is returned as JSON rather than cast to text, so it is rendered by the same code that renders the parents’ keys. Casting to text in SQL would agree for integers and uuids and disagree for a NUMERIC join column, where PostgreSQL and serde_json format differently.
limit still bounds the rows scanned, not the rows per parent, so it is
applied to the inner select exactly as the ungrouped form does.
The child rows belonging to one parent row, as a SELECT correlated against the parent’s alias.
This is the single-query form of embedding: instead of fetching parents, collecting their keys and issuing a second query, the relationship is attached to the parent query as an expression, so PostgreSQL builds the array while it already has the parent row.
inner_select is the child’s SELECT list, which the caller assembles –
its columns, plus any deeper relationship expressions built by calling
this again. Only the caller knows the shape of its own selection tree, so
the recursion lives there and the SQL assembly lives here.
What is done with those rows is the caller’s: [embed_expression]
renders them as JSON, [aggregate_expression] summarises them.
Sourcepub fn embed_expression(
&self,
parent_alias: &str,
parent_row: &str,
child_alias: &str,
inner_select: &str,
limit: Option<i64>,
offset: i64,
child_where: Option<&str>,
order_by: Option<&str>,
function_arguments: Option<&str>,
) -> Result<String>
pub fn embed_expression( &self, parent_alias: &str, parent_row: &str, child_alias: &str, inner_select: &str, limit: Option<i64>, offset: i64, child_where: Option<&str>, order_by: Option<&str>, function_arguments: Option<&str>, ) -> Result<String>
A correlated subselect that yields this relationship as one JSON column.
The rows come from [correlated_rows]; this decides how they are
rendered – an array for a to-many relationship, one object or null for
a to-one.
Sourcepub fn aggregate_expression(
&self,
parent_alias: &str,
parent_row: &str,
child_alias: &str,
aggregate_select: &str,
limit: Option<i64>,
offset: i64,
child_where: Option<&str>,
order_by: Option<&str>,
function_arguments: Option<&str>,
distinct_on: &str,
) -> Result<String>
pub fn aggregate_expression( &self, parent_alias: &str, parent_row: &str, child_alias: &str, aggregate_select: &str, limit: Option<i64>, offset: i64, child_where: Option<&str>, order_by: Option<&str>, function_arguments: Option<&str>, distinct_on: &str, ) -> Result<String>
A correlated subselect that summarises this relationship as one JSON column.
author { articles_aggregate { aggregate { count } } }. The rows are
the same correlated set an embed would render, but a limit here bounds
what is counted rather than what is returned – so the aggregate reads
from the rows as a subquery instead of selecting over them directly.
The subquery carries the child’s own alias, so a predicate or an
ordering written against it goes on reading the same name.
Parent columns are deliberately left alone: they stay ordinary typed columns and are converted to JSON by the same code as an unembedded request, so embedding does not change how a NUMERIC or a timestamp is rendered.
Sourcepub fn order_expression(
&self,
parent_alias: &str,
parent_row: &str,
child_alias: &str,
column_sql: &str,
) -> String
pub fn order_expression( &self, parent_alias: &str, parent_row: &str, child_alias: &str, column_sql: &str, ) -> String
A scalar subselect yielding one column of the related row.
This is what order=clients(name) orders by: the parent has no such
column, so the value is fetched per parent row exactly as the embed
itself is. A to-many relationship has no single value to order by, so
the first row wins – which is what LIMIT 1 says and what PostgREST
does.
Sourcepub fn inner_join_predicate(
&self,
parent_alias: &str,
parent_row: &str,
child_alias: &str,
child_where: Option<&str>,
) -> String
pub fn inner_join_predicate( &self, parent_alias: &str, parent_row: &str, child_alias: &str, child_where: Option<&str>, ) -> String
An EXISTS predicate restricting parents to those with a matching child.
This is what !inner means. The embed expression on its own only
decides what the relationship column contains; without this the parent
row survives even when the relationship matched nothing, which is a
left join. child_where should be the same predicate given to
Self::embed_expression – placeholders may be referenced from both
places, so no parameter needs binding twice.
Trait Implementations§
Auto Trait Implementations§
impl Freeze for EmbedPlan
impl RefUnwindSafe for EmbedPlan
impl Send for EmbedPlan
impl Sync for EmbedPlan
impl Unpin for EmbedPlan
impl UnsafeUnpin for EmbedPlan
impl UnwindSafe for EmbedPlan
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> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
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