pub struct MySql;Expand description
MySQL and MariaDB: backtick-quoted names, ? 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, for MySQL 8.0.1 and MariaDB 10.6 or
later, the first versions that skip locked rows; check_server
refuses an older server for a table in any form. A lease claim selects
the claimable rows, then stamps each one with its lease (stamp) before its
transaction commits. A dead letter into a table copies the row, then deletes it, in one
transaction. Every name is quoted, so a name keeps its case and may hold any character; a name
over 64 characters, which MySQL refuses, is refused before it reaches the server. A table on
the database’s clock reads UTC_TIMESTAMP(6). fetch is refused, so a
claim of the service’s own brings a fetch of its own.
A claim’s transaction opens at READ COMMITTED in both forms (begin for a
table that names no level, begin_lease_claim): under the
server’s default REPEATABLE READ a locking read also locks the gaps between the rows it scans,
so a claim held for a handler would block every insert into the table until it ends. A server
whose binary log records statements (binlog_format = STATEMENT) refuses writes in such a
transaction; the row and mixed formats accept them. A row lock table that names a level opens
its claims at it, any of the four, READ UNCOMMITTED included; REPEATABLE READ and SERIALIZABLE
bring the gap locks back.
In the advisory lock form a row’s key is the text CONCAT_WS renders from the key’s parts, a
column without a value skipped. A session lock (GET_LOCK) holds the row, named by the table’s
database, a dot and the key (app.jobs-7): the server names its locks server-wide, and the
database keeps the locks of two databases on one server apart. The server takes a lock name of
1 to 64 characters, so a longer name is locked by its SHA-256, 64 characters of hex: the claim
selects the name it locks. The claim leaves out the names IS_USED_LOCK reports in use. An
update returns no rows here, so the take counts the attempt, then reads the row.
In a table with FIFO groups a claim’s transaction takes its group
(fifo_guard) with a locking read of the group’s unfinished rows that
skips the rows other transactions hold: the group is the claim’s when the read took all of
them. The rows stay locked until the transaction ends, in the row lock form until the delivery
settles, and a write to one of them from elsewhere waits that long. Each claim reads every
unfinished row of its group, and the whole table where the group’s column has no index. A row
lock table with FIFO groups claims below SERIALIZABLE: every read of a SERIALIZABLE
transaction locks the rows it reads, so the guard would wait for the group’s row in work, and
it refuses such a table with StatementError::FifoAtSerializable.
§Examples
A subscription asks MySQL for the statements of its table, as ruststream-sqlx does when the
subscription starts:
use ruststream_sqlx_dialect::{
ClaimShape, Column, Form, Lease, MySql, Statement, StatementError, TableSpec,
};
// The jobs table as `#[derive(Inbox)]` describes it, with a `#[field(locked_until)]` field.
const JOBS: TableSpec<'static> = TableSpec::new(
"jobs",
Column::new("job_id"),
Form::Lease(Column::new("locked_until")),
);
// What a subscription to a lease table prepares: the claim, and the stamp where the claim
// only selects its rows.
fn lease_statements(
dialect: &impl Lease,
spec: &TableSpec<'_>,
) -> Result<Vec<Statement>, StatementError> {
let mut statements = vec![dialect.lease_claim(spec, ClaimShape::Rows)?];
if !dialect.claim_writes_lease() {
statements.push(dialect.stamp(spec)?);
}
Ok(statements)
}
fn main() -> Result<(), StatementError> {
// MySQL's claim selects the rows, and each row it took is stamped with its lease before
// the claim's transaction commits.
let statements = lease_statements(&MySql, &JOBS)?;
assert_eq!(statements.len(), 2);
Ok(())
}Trait Implementations§
Source§impl Advisory for MySql
impl Advisory for MySql
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 MySql
Source§impl Dialect for MySql
impl Dialect for MySql
Source§fn quote_into(&self, ident: &str, out: &mut String)
fn quote_into(&self, ident: &str, out: &mut String)
Source§fn placeholder_into(&self, _: NonZeroUsize, out: &mut String)
fn placeholder_into(&self, _: NonZeroUsize, out: &mut String)
Source§fn fetch(&self, _: &TableSpec<'_>) -> Result<Statement, StatementError>
fn fetch(&self, _: &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 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,
_: &TableSpec<'_>,
version: &str,
) -> Result<(), StatementError>
fn check_server( &self, _: &TableSpec<'_>, version: &str, ) -> Result<(), StatementError>
spec need; version is what the
server_version query returned. 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 moreimpl Eq for MySql
Source§impl Lease for MySql
impl Lease for MySql
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 begin_lease_claim(&self) -> Option<&'static str>
fn begin_lease_claim(&self) -> Option<&'static str>
BEGIN, or None when BEGIN opens it. Read moreSource§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 MySql
impl Opens<ReadUncommitted> for MySql
impl Opens<RepeatableRead> for MySql
impl Opens<Serializable> for MySql
Source§impl OutboxDialect for MySql
impl OutboxDialect for MySql
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 MySql
impl RowLock for MySql
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