use super::claim::CLAIMABLE;
use super::{BuiltIn, SqlWriter};
use crate::form::KeyPart;
use crate::spec::TableSpec;
use crate::statement::Param;
const LOCK_KEY: &str = "__lock";
#[derive(Debug, Clone, Copy)]
pub(crate) enum Probe {
#[cfg(feature = "sqlite")]
Blind,
#[cfg(feature = "mysql")]
Check(&'static str, &'static str),
#[cfg(feature = "postgres")]
Lock(&'static str, &'static str),
}
impl<D> SqlWriter<'_, D>
where
D: BuiltIn + ?Sized,
{
pub(crate) fn key_parts(
&mut self,
key: &[KeyPart<'_>],
separator: &str,
column: impl Fn(&mut Self, &str),
) -> &mut Self {
if key.is_empty() {
return self.literal("");
}
for (index, part) in key.iter().enumerate() {
if index > 0 {
self.push(separator);
}
match part {
KeyPart::Literal(text) => {
self.literal(text);
}
KeyPart::Column(name) => column(self, name),
}
}
self
}
fn lock_key(&mut self, spec: &TableSpec<'_>, key: &[KeyPart<'_>]) -> &mut Self {
let dialect = self.dialect;
dialect.render_lock_key(self, spec.schema(), key);
self
}
pub(super) fn taken_row(&mut self, spec: &TableSpec<'_>) -> &mut Self {
self.push(" WHERE ")
.ident(spec.id().name())
.push(" = ")
.param(Param::Id)
.conditions(spec, &CLAIMABLE, " AND ")
}
pub(super) fn candidates(&mut self, spec: &TableSpec<'_>, key: &[KeyPart<'_>]) -> &mut Self {
match D::PROBE {
#[cfg(feature = "sqlite")]
Probe::Blind => self.candidate_rows(spec, key, None),
#[cfg(feature = "mysql")]
Probe::Check(before, after) => self.candidate_rows(spec, key, Some((before, after))),
#[cfg(feature = "postgres")]
Probe::Lock(before, after) => {
let id = spec.id().name();
self.push("SELECT ")
.ident(id)
.push(", ")
.ident(LOCK_KEY)
.push(" FROM (")
.candidate_rows(spec, key, None)
.push(" OFFSET 0) AS __candidates WHERE ")
.push(before)
.ident(LOCK_KEY)
.push(after)
}
}
.push(" LIMIT ")
.param(Param::Limit)
}
fn candidate_rows(
&mut self,
spec: &TableSpec<'_>,
key: &[KeyPart<'_>],
check: Option<(&str, &str)>,
) -> &mut Self {
let id = spec.id().name();
self.push("SELECT ")
.ident(id)
.push(", ")
.lock_key(spec, key)
.push(" AS ")
.ident(LOCK_KEY)
.push(" FROM ")
.table(spec);
let keyword = self.conditions_then(spec, &CLAIMABLE, " WHERE ");
if let Some((before, after)) = check {
self.push(keyword)
.push(before)
.lock_key(spec, key)
.push(after);
}
self.claim_order(spec, id)
}
}