use super::{BuiltIn, SqlWriter};
use crate::column::Column;
use crate::role::Role;
use crate::spec::TableSpec;
use crate::statement::ClaimShape;
fn read_by_role<'a>(spec: &TableSpec<'a>) -> impl Iterator<Item = (Role, Column<'a>)> {
Role::ALL
.iter()
.filter(|role| {
matches!(
role,
Role::Id | Role::PartitionKey | Role::Attempt | Role::Headers | Role::Payload
)
})
.filter_map(|&role| spec.column(role).map(|column| (role, column)))
}
impl<D> SqlWriter<'_, D>
where
D: BuiltIn + ?Sized,
{
pub(crate) fn columns(&mut self, spec: &TableSpec<'_>) -> &mut Self {
if spec.selects_all() {
return self.push("*");
}
for (index, column) in spec.columns().enumerate() {
if index > 0 {
self.push(", ");
}
self.ident(column.name());
}
self
}
pub(crate) fn moved_columns(&mut self, spec: &TableSpec<'_>) -> &mut Self {
if spec.selects_all() {
return self.push("*");
}
let expiry = spec.column(Role::LockedUntil).map(|column| column.name());
for (index, column) in spec.columns().enumerate() {
if index > 0 {
self.push(", ");
}
if expiry == Some(column.name()) {
self.push("NULL");
} else {
self.ident(column.name());
}
}
self
}
pub(crate) fn role_columns(&mut self, spec: &TableSpec<'_>) -> &mut Self {
self.roles_read(spec, false)
}
fn roles_read(&mut self, spec: &TableSpec<'_>, counted: bool) -> &mut Self {
for (index, (role, column)) in read_by_role(spec).enumerate() {
if index > 0 {
self.push(", ");
}
self.ident(column.name());
if counted && role == Role::Attempt {
self.push(D::UNCOUNT);
}
self.push(" AS ").ident(role.attribute());
}
self
}
fn columns_before_count(&mut self, spec: &TableSpec<'_>) -> &mut Self {
if spec.selects_all() {
return self.push("*");
}
let attempt = spec.column(Role::Attempt).map(|column| column.name());
for (index, column) in spec.columns().enumerate() {
if index > 0 {
self.push(", ");
}
self.ident(column.name());
if attempt == Some(column.name()) {
self.push(D::UNCOUNT).push(" AS ").ident(column.name());
}
}
self
}
#[cfg(feature = "postgres")]
pub(super) fn role_names(&mut self, spec: &TableSpec<'_>) -> &mut Self {
for (index, (role, _)) in read_by_role(spec).enumerate() {
if index > 0 {
self.push(", ");
}
self.ident(role.attribute());
}
self
}
pub(super) fn claimed_columns(&mut self, spec: &TableSpec<'_>, shape: ClaimShape) -> &mut Self {
match shape {
ClaimShape::Rows => self.columns(spec),
ClaimShape::Ids => self.ident(spec.id().name()),
ClaimShape::Roles => self.role_columns(spec),
}
}
pub(super) fn counted_columns(&mut self, spec: &TableSpec<'_>, shape: ClaimShape) -> &mut Self {
match shape {
ClaimShape::Rows => self.columns_before_count(spec),
ClaimShape::Ids => self.ident(spec.id().name()),
ClaimShape::Roles => self.roles_read(spec, true),
}
}
}
#[cfg(all(test, feature = "postgres"))]
mod tests {
use crate::column::Column;
use crate::dialect::Dialect;
use crate::form::Form;
use crate::postgres::Postgres;
use crate::row_lock::RowLock;
use crate::spec::TableSpec;
use crate::statement::{ClaimShape, StatementError};
const ASSEMBLED: TableSpec<'static> =
TableSpec::new("order_jobs", Column::new("job_id"), Form::RowLock)
.group(Column::new("name"))
.data(&[Column::new("tenant")])
.fetching(&[Column::new("note")]);
#[test]
fn a_claim_and_a_fetch_name_the_messages_columns_after_the_headers()
-> Result<(), StatementError> {
let claim = Postgres.lock_claim(&ASSEMBLED, ClaimShape::Rows)?;
assert!(
claim
.sql()
.starts_with(r#"SELECT "job_id", "name", "tenant", "note" FROM "order_jobs""#),
"{}",
claim.sql()
);
let fetch = Postgres.fetch(&ASSEMBLED)?;
assert!(
fetch
.sql()
.starts_with(r#"SELECT "job_id", "name", "tenant", "note" FROM "order_jobs""#),
"{}",
fetch.sql()
);
Ok(())
}
}