Skip to main content

cratestack_sql/
dialect.rs

1use std::fmt::Write;
2
3/// Backend SQL dialect.
4///
5/// The cratestack renderers produce dialect-agnostic SQL for everything
6/// except parameter placeholder syntax: Postgres uses `$1, $2, ...` while
7/// SQLite uses `?1, ?2, ...` (also valid: `?` for positional). Implementors
8/// own that one decision and the backend crates plug in their own impl.
9///
10/// Kept deliberately narrow — adding methods here forces every backend to
11/// implement them, which is the wrong default. New dialect-specific quirks
12/// should live in the backend's own renderer until at least two backends
13/// agree on the shape.
14pub trait Dialect {
15    /// Write a numbered placeholder for bind index `index` (1-based) into
16    /// `sql`. Postgres writes `$N`; SQLite writes `?N`.
17    fn write_placeholder(&self, sql: &mut String, index: usize);
18}
19
20/// Postgres dialect — `$N` placeholders.
21#[derive(Debug, Clone, Copy, Default)]
22pub struct PostgresDialect;
23
24impl Dialect for PostgresDialect {
25    fn write_placeholder(&self, sql: &mut String, index: usize) {
26        let _ = write!(sql, "${index}");
27    }
28}
29
30/// SQLite dialect — `?N` placeholders.
31#[derive(Debug, Clone, Copy, Default)]
32pub struct SqliteDialect;
33
34impl Dialect for SqliteDialect {
35    fn write_placeholder(&self, sql: &mut String, index: usize) {
36        let _ = write!(sql, "?{index}");
37    }
38}
39
40#[cfg(test)]
41mod tests {
42    use super::*;
43
44    #[test]
45    fn postgres_dialect_writes_dollar_placeholders() {
46        let dialect = PostgresDialect;
47        let mut sql = String::new();
48        dialect.write_placeholder(&mut sql, 1);
49        sql.push_str(", ");
50        dialect.write_placeholder(&mut sql, 2);
51        assert_eq!(sql, "$1, $2");
52    }
53
54    #[test]
55    fn sqlite_dialect_writes_question_placeholders() {
56        let dialect = SqliteDialect;
57        let mut sql = String::new();
58        dialect.write_placeholder(&mut sql, 1);
59        sql.push_str(", ");
60        dialect.write_placeholder(&mut sql, 2);
61        assert_eq!(sql, "?1, ?2");
62    }
63}