Skip to main content

sqlrite/sql/
dialect.rs

1//! SQLRite SQL dialect.
2//!
3//! Wraps sqlparser's `SQLiteDialect` so we get every SQLite-specific
4//! tokenizer/parser quirk (delimited identifiers, NOTNULL operator,
5//! `LIMIT a, b`, `MATCH`/`REGEXP` infix, …) and overrides only what we
6//! need for SQLRite's vector extensions:
7//!
8//! - `supports_create_index_with_clause = true` — lets the parser
9//!   accept `CREATE INDEX … USING hnsw (col) WITH (metric = 'cosine')`.
10//!   sqlparser's `SQLiteDialect` returns `false` from this method, so
11//!   the WITH clause would otherwise be parked in `index_options` (or
12//!   error). The PostgreSQL dialect already turns it on; we copy that
13//!   behaviour here without taking the rest of the pgsql parser
14//!   divergences.
15//!
16//! Add new dialect overrides here as the surface grows; everything not
17//! explicitly listed defers to the base SQLite dialect.
18use sqlparser::ast::{Expr, Statement};
19use sqlparser::dialect::{Dialect, SQLiteDialect};
20use sqlparser::parser::{Parser, ParserError};
21
22#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
23pub struct SqlriteDialect {
24    inner: SQLiteDialect,
25}
26
27impl SqlriteDialect {
28    pub const fn new() -> Self {
29        Self {
30            inner: SQLiteDialect {},
31        }
32    }
33}
34
35impl Dialect for SqlriteDialect {
36    fn is_delimited_identifier_start(&self, ch: char) -> bool {
37        self.inner.is_delimited_identifier_start(ch)
38    }
39
40    fn identifier_quote_style(&self, identifier: &str) -> Option<char> {
41        self.inner.identifier_quote_style(identifier)
42    }
43
44    fn is_identifier_start(&self, ch: char) -> bool {
45        self.inner.is_identifier_start(ch)
46    }
47
48    fn is_identifier_part(&self, ch: char) -> bool {
49        self.inner.is_identifier_part(ch)
50    }
51
52    fn supports_filter_during_aggregation(&self) -> bool {
53        self.inner.supports_filter_during_aggregation()
54    }
55
56    fn supports_start_transaction_modifier(&self) -> bool {
57        self.inner.supports_start_transaction_modifier()
58    }
59
60    fn supports_in_empty_list(&self) -> bool {
61        self.inner.supports_in_empty_list()
62    }
63
64    fn supports_limit_comma(&self) -> bool {
65        self.inner.supports_limit_comma()
66    }
67
68    fn supports_asc_desc_in_column_definition(&self) -> bool {
69        self.inner.supports_asc_desc_in_column_definition()
70    }
71
72    fn supports_dollar_placeholder(&self) -> bool {
73        self.inner.supports_dollar_placeholder()
74    }
75
76    fn supports_notnull_operator(&self) -> bool {
77        self.inner.supports_notnull_operator()
78    }
79
80    fn parse_statement(&self, parser: &mut Parser) -> Option<Result<Statement, ParserError>> {
81        self.inner.parse_statement(parser)
82    }
83
84    fn parse_infix(
85        &self,
86        parser: &mut Parser,
87        expr: &Expr,
88        precedence: u8,
89    ) -> Option<Result<Expr, ParserError>> {
90        self.inner.parse_infix(parser, expr, precedence)
91    }
92
93    /// SQLRite-specific extension: `CREATE INDEX … USING hnsw (col)
94    /// WITH (metric = 'cosine')` is the canonical way to pick a
95    /// non-L2 distance metric for an HNSW index. See
96    /// `docs/supported-sql.md` and `try_hnsw_probe`.
97    fn supports_create_index_with_clause(&self) -> bool {
98        true
99    }
100}