pub struct Postgres;Expand description
Postgres: double-quoted names, $1 placeholders, rows claimed with FOR UPDATE SKIP LOCKED.
It builds the statements of the row lock form (RowLock), of the lease form (Lease), of
the advisory lock form (Advisory), and the insert. A lease claim is one statement: it locks
the claimable rows, writes their lease, and returns them as they were. Every name is quoted, so
a name keeps its case and may hold any character; a name over 63 bytes, which Postgres would
cut short without a word, is refused. A table on the database’s clock reads
statement_timestamp().
In the advisory lock form a row’s key is the text concat renders from the key’s parts, a
column without a value read as empty text. A session lock on the key’s 64-bit hash
(hashtextextended, Postgres 11 or later) holds the row, so two keys with one hash wait for
each other: a delay, never a double delivery. The claim leaves out the keys another session
holds: it probes each candidate’s key with a shared lock that ends with the claim’s own
transaction, in claim order and only until it has its rows. The take counts the attempt and
returns the row in one statement.
In a table with FIFO groups a claim’s transaction takes its group
(fifo_guard) with a lock the transaction holds on the 64-bit hash of
the table’s name and the group. Two groups with one hash wait for each other: a delay, never
two rows of one group in work.
A table’s transactions open with BEGIN, or at the isolation level it names
(begin): READ COMMITTED, REPEATABLE READ or SERIALIZABLE. Postgres runs
READ UNCOMMITTED as READ COMMITTED, so a table that names it is refused: a level the database
does not keep is a level it lacks.
§Examples
A subscription asks Postgres for the statements of its table, as ruststream-sqlx does when the
subscription starts:
use ruststream_sqlx_dialect::{
ClaimShape, Column, Dialect, Form, Postgres, RowLock, Statement, StatementError, TableSpec,
};
// The ledger as `#[derive(Inbox)]` describes it: one entry of an account in work at a time.
const LEDGER: TableSpec<'static> =
TableSpec::new("ledger", Column::new("entry_id"), Form::RowLock)
.fifo_group(Column::new("account"));
// What a claim's transaction runs on a row lock table: the guard of its group, where the
// table keeps groups in order, then the claim.
fn claim_statements(
dialect: &impl RowLock,
spec: &TableSpec<'_>,
) -> Result<Vec<Statement>, StatementError> {
let mut statements: Vec<Statement> = dialect.fifo_guard(spec)?.into_iter().collect();
statements.push(dialect.lock_claim(spec, ClaimShape::Rows)?);
Ok(statements)
}
fn main() -> Result<(), StatementError> {
// Postgres takes the account's group with a lock the transaction holds, then claims the
// group's head.
let statements = claim_statements(&Postgres, &LEDGER)?;
assert_eq!(statements.len(), 2);
Ok(())
}Trait Implementations§
Source§impl Advisory for Postgres
impl Advisory for Postgres
Source§fn advisory_claim(
&self,
spec: &TableSpec<'_>,
) -> Result<Statement, StatementError>
fn advisory_claim( &self, spec: &TableSpec<'_>, ) -> Result<Statement, StatementError>
Param::Limit claimable rows of the
subscription’s group in claim order, two columns each, the id and the lock key as text
(named __lock). It takes no lock and writes nothing; where the database can tell, it
leaves out the rows whose key another session holds. Read moreSource§fn lock(&self) -> Option<Statement>
fn lock(&self) -> Option<Statement>
Param::Key for the session, without waiting: one
row whose first column is a 64-bit integer, nonzero when the lock was taken. None when the
process keeps the locks. Read moreSource§fn unlock(&self) -> Option<Statement>
fn unlock(&self) -> Option<Statement>
Param::Key: one row whose first column is a
64-bit integer, nonzero when the session held it. None when the process keeps the locks. Read moreSource§fn take(
&self,
spec: &TableSpec<'_>,
shape: ClaimShape,
) -> Result<Vec<Statement>, StatementError>
fn take( &self, spec: &TableSpec<'_>, shape: ClaimShape, ) -> Result<Vec<Statement>, StatementError>
shape, as it was before the count, only
while it is still claimable. The last returns the row, or none when the row is gone or no
longer claimable; a first statement, where there are two, counts the attempt and changes no row
in that case. Read moreimpl Copy for Postgres
Source§impl Dialect for Postgres
impl Dialect for Postgres
Source§fn quote_into(&self, ident: &str, out: &mut String)
fn quote_into(&self, ident: &str, out: &mut String)
Source§fn placeholder_into(&self, index: NonZeroUsize, out: &mut String)
fn placeholder_into(&self, index: NonZeroUsize, out: &mut String)
Source§fn fetch(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError>
fn fetch(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError>
Source§fn ack(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError>
fn ack(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError>
processed_at when the
table has that column. Read moreSource§fn retry(
&self,
spec: &TableSpec<'_>,
) -> Result<Option<Statement>, StatementError>
fn retry( &self, spec: &TableSpec<'_>, ) -> Result<Option<Statement>, StatementError>
None when releasing
the row needs no statement. Read moreSource§fn retry_after(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError>
fn retry_after(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError>
Param::RetryAfter. Read moreSource§fn discard(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError>
fn discard(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError>
processed_at when the table
has that column. Read moreSource§fn dead_letter_group(
&self,
spec: &TableSpec<'_>,
) -> Result<Statement, StatementError>
fn dead_letter_group( &self, spec: &TableSpec<'_>, ) -> Result<Statement, StatementError>
Param::Destination. Read moreSource§fn dead_letter_table(
&self,
spec: &TableSpec<'_>,
target: TableName<'_>,
) -> Result<Vec<Statement>, StatementError>
fn dead_letter_table( &self, spec: &TableSpec<'_>, target: TableName<'_>, ) -> Result<Vec<Statement>, StatementError>
target, a table with the same
columns; they run in one transaction. Read moreSource§fn insert(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError>
fn insert(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError>
TableSpec::columns, each bound as Param::Column. Read moreSource§fn begin(
&self,
opening: Opening,
) -> Result<Option<&'static str>, StatementError>
fn begin( &self, opening: Opening, ) -> Result<Option<&'static str>, StatementError>
opening in place of BEGIN, or None when
BEGIN opens it. Read moreSource§fn fifo_guard(
&self,
spec: &TableSpec<'_>,
) -> Result<Option<Statement>, StatementError>
fn fifo_guard( &self, spec: &TableSpec<'_>, ) -> Result<Option<Statement>, StatementError>
None for a table without FIFO groups, or a dialect that
takes no group. Read moreSource§fn server_version(&self) -> Option<&'static str>
fn server_version(&self) -> Option<&'static str>
None when the
dialect’s statements run on every version of its server. Read moreSource§fn check_server(
&self,
spec: &TableSpec<'_>,
version: &str,
) -> Result<(), StatementError>
fn check_server( &self, spec: &TableSpec<'_>, version: &str, ) -> Result<(), StatementError>
spec need; version is what the
server_version query returned. Read moreimpl Eq for Postgres
Source§impl Lease for Postgres
impl Lease for Postgres
Source§fn lease_claim(
&self,
spec: &TableSpec<'_>,
shape: ClaimShape,
) -> Result<Statement, StatementError>
fn lease_claim( &self, spec: &TableSpec<'_>, shape: ClaimShape, ) -> Result<Statement, StatementError>
Param::Limit rows of the
subscription’s group, in claim order, among the rows whose lease ended by
Param::LeaseNow. Read moreSource§fn extend(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError>
fn extend(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError>
Param::Lease) while the row still holds the delivery’s token
(Param::Held), so a handler that runs longer than one lease keeps
its row. Read moreSource§fn stamp(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError>
fn stamp(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError>
Param::Lease) and counts the attempt, while no lease holds the
row at Param::LeaseNow. Read moreSource§fn claim_writes_lease(&self) -> bool
fn claim_writes_lease(&self) -> bool
Source§fn claim_counts_attempt(&self, spec: &TableSpec<'_>) -> bool
fn claim_counts_attempt(&self, spec: &TableSpec<'_>) -> bool
spec returns carry the attempt the claim counted. A
dialect whose claim returns the rows as they were before answers false; where it answers
true, a delivery reports one less than its row carries. Read moreimpl Opens<ReadCommitted> for Postgres
impl Opens<RepeatableRead> for Postgres
impl Opens<Serializable> for Postgres
Source§impl OutboxDialect for Postgres
impl OutboxDialect for Postgres
Source§fn outbox_fetch(
&self,
spec: &TableSpec<'_>,
) -> Result<Statement, StatementError>
fn outbox_fetch( &self, spec: &TableSpec<'_>, ) -> Result<Statement, StatementError>
Source§fn outbox_mark(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError>
fn outbox_mark(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError>
Source§fn outbox_recover(
&self,
spec: &TableSpec<'_>,
) -> Result<Statement, StatementError>
fn outbox_recover( &self, spec: &TableSpec<'_>, ) -> Result<Statement, StatementError>
Param::Group binds, with every column: what the republish at
startup sends again. Read moreSource§impl RowLock for Postgres
impl RowLock for Postgres
Source§fn lock_claim(
&self,
spec: &TableSpec<'_>,
shape: ClaimShape,
) -> Result<Statement, StatementError>
fn lock_claim( &self, spec: &TableSpec<'_>, shape: ClaimShape, ) -> Result<Statement, StatementError>
Param::Limit rows of the
subscription’s group, in claim order, and locks them until the claim’s transaction ends. Read more