use std::num::NonZeroUsize;
use crate::advisory::Advisory;
use crate::dialect::Dialect;
use crate::form::KeyPart;
use crate::insert::{Placeholder, Spelling, quote_into};
use crate::lease::Lease;
use crate::opening::{Mode, Opening, Opens, level};
use crate::outbox::OutboxDialect;
use crate::spec::TableSpec;
use crate::statement::{ClaimShape, Param, Statement, StatementError};
use crate::table_name::TableName;
use crate::writer::{BuiltIn, OutboxWriter, Probe, SqlWriter};
const DATABASE_NOW: &str = "strftime('%Y-%m-%dT%H:%M:%f+00:00', 'now')";
const BEGIN_CLAIM: &str = "BEGIN IMMEDIATE";
pub(crate) const SPELLING: Spelling = Spelling {
dialect: "sqlite",
quote: b'`',
placeholder: Placeholder::Question,
default_row: " DEFAULT VALUES",
name_limit: None,
};
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
pub struct Sqlite;
impl BuiltIn for Sqlite {
const SPELLING: Spelling = SPELLING;
const ROW_LOCKS: bool = false;
const BACKSLASH_ESCAPES: bool = false;
const UPDATE_RETURNS: bool = true;
const PROBE: Probe = Probe::Blind;
fn database_now(&self) -> &'static str {
DATABASE_NOW
}
fn database_later(&self, sql: &mut SqlWriter<'_, Self>) {
sql.push("strftime('%Y-%m-%dT%H:%M:%f+00:00', 'now', (")
.param(Param::Delay)
.push(" / 1000000.0) || ' seconds')");
}
fn render_lock_key(&self, sql: &mut SqlWriter<'_, Self>, _: Option<&str>, key: &[KeyPart<'_>]) {
sql.push("CAST(")
.key_parts(key, " || ", |sql, column| {
sql.push("ifnull(").ident(column).push(", '')");
})
.push(" AS TEXT)");
}
}
impl Dialect for Sqlite {
fn name(&self) -> &'static str {
"sqlite"
}
fn quote_into(&self, ident: &str, out: &mut String) {
quote_into(SPELLING.quote, ident, out);
}
fn placeholder_into(&self, _: NonZeroUsize, out: &mut String) {
out.push('?');
}
fn fetch(&self, _: &TableSpec<'_>) -> Result<Statement, StatementError> {
Err(StatementError::UnsupportedFetch {
dialect: self.name(),
})
}
fn ack(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError> {
self.finish_statement(spec)
}
fn retry(&self, spec: &TableSpec<'_>) -> Result<Option<Statement>, StatementError> {
self.retry_statement(spec)
}
fn retry_after(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError> {
self.retry_after_statement(spec)
}
fn discard(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError> {
self.finish_statement(spec)
}
fn dead_letter_group(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError> {
self.dead_letter_group_statement(spec)
}
fn dead_letter_table(
&self,
spec: &TableSpec<'_>,
target: TableName<'_>,
) -> Result<Vec<Statement>, StatementError> {
self.copy_then_delete(spec, target)
}
fn insert(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError> {
self.insert_statement(spec)
}
fn begin(&self, opening: Opening) -> Result<Option<&'static str>, StatementError> {
match opening {
Opening::Default => Ok(None),
Opening::Mode(Mode::Deferred) => Ok(Some("BEGIN DEFERRED")),
Opening::Mode(Mode::Immediate) => Ok(Some("BEGIN IMMEDIATE")),
Opening::Mode(Mode::Exclusive) => Ok(Some("BEGIN EXCLUSIVE")),
Opening::Isolation(_) => Err(opening.refused(self.name())),
}
}
}
impl OutboxDialect for Sqlite {
fn outbox_fetch(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError> {
self.outbox_fetch_statement(spec)
}
fn outbox_mark(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError> {
self.outbox_mark_statement(spec)
}
fn outbox_recover(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError> {
self.outbox_recover_statement(spec)
}
}
impl Opens<level::Deferred> for Sqlite {}
impl Opens<level::Immediate> for Sqlite {}
impl Opens<level::Exclusive> for Sqlite {}
impl Advisory for Sqlite {
fn advisory_claim(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError> {
self.advisory_claim_statement(spec)
}
fn lock(&self) -> Option<Statement> {
None
}
fn unlock(&self) -> Option<Statement> {
None
}
fn take(
&self,
spec: &TableSpec<'_>,
shape: ClaimShape,
) -> Result<Vec<Statement>, StatementError> {
self.take_statements(spec, shape)
}
}
impl Lease for Sqlite {
fn lease_claim(
&self,
spec: &TableSpec<'_>,
shape: ClaimShape,
) -> Result<Statement, StatementError> {
let expiry = self.leased(spec, "lease_claim")?;
let mut sql = SqlWriter::new(self);
sql.returning_claim(spec, shape, expiry.name());
Ok(sql.finish())
}
fn extend(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError> {
self.extend_statement(spec)
}
fn stamp(&self, spec: &TableSpec<'_>) -> Result<Statement, StatementError> {
self.stamp_statement(spec)
}
fn claim_counts_attempt(&self, spec: &TableSpec<'_>) -> bool {
spec.selects_all()
}
fn begin_lease_claim(&self) -> Option<&'static str> {
Some(BEGIN_CLAIM)
}
}