Skip to main content

fraiseql_core/db/
identifier.rs

1//! Database identifier quoting utilities.
2//!
3//! This module provides database-specific identifier quoting functions that handle
4//! schema-qualified identifiers (e.g., `schema.table`, `catalog.schema.table`).
5//!
6//! Each function splits on `.` and quotes each component with the appropriate syntax
7//! for the target database.
8
9/// Quote a PostgreSQL identifier.
10///
11/// PostgreSQL uses double quotes for identifiers. Schema-qualified names
12/// (e.g., `schema.table`) are split and quoted per component.
13///
14/// # Examples
15///
16/// ```ignore
17/// assert_eq!(quote_postgres_identifier("v_user"), "\"v_user\"");
18/// assert_eq!(quote_postgres_identifier("benchmark.v_user"), "\"benchmark\".\"v_user\"");
19/// assert_eq!(
20///     quote_postgres_identifier("catalog.schema.table"),
21///     "\"catalog\".\"schema\".\"table\""
22/// );
23/// ```
24#[inline]
25#[must_use]
26pub fn quote_postgres_identifier(identifier: &str) -> String {
27    identifier
28        .split('.')
29        .map(|part| format!("\"{}\"", part))
30        .collect::<Vec<_>>()
31        .join(".")
32}
33
34/// Quote a MySQL identifier.
35///
36/// MySQL uses backticks for identifiers. Schema-qualified names
37/// (e.g., `database.table`) are split and quoted per component.
38///
39/// # Examples
40///
41/// ```ignore
42/// assert_eq!(quote_mysql_identifier("v_user"), "`v_user`");
43/// assert_eq!(quote_mysql_identifier("mydb.v_user"), "`mydb`.`v_user`");
44/// assert_eq!(
45///     quote_mysql_identifier("catalog.schema.table"),
46///     "`catalog`.`schema`.`table`"
47/// );
48/// ```
49#[inline]
50#[must_use]
51pub fn quote_mysql_identifier(identifier: &str) -> String {
52    identifier
53        .split('.')
54        .map(|part| format!("`{}`", part))
55        .collect::<Vec<_>>()
56        .join(".")
57}
58
59/// Quote a SQLite identifier.
60///
61/// SQLite uses double quotes for identifiers. Schema-qualified names
62/// (e.g., `schema.table`) are split and quoted per component.
63///
64/// # Examples
65///
66/// ```ignore
67/// assert_eq!(quote_sqlite_identifier("v_user"), "\"v_user\"");
68/// assert_eq!(quote_sqlite_identifier("main.v_user"), "\"main\".\"v_user\"");
69/// assert_eq!(
70///     quote_sqlite_identifier("catalog.schema.table"),
71///     "\"catalog\".\"schema\".\"table\""
72/// );
73/// ```
74#[inline]
75#[must_use]
76pub fn quote_sqlite_identifier(identifier: &str) -> String {
77    identifier
78        .split('.')
79        .map(|part| format!("\"{}\"", part))
80        .collect::<Vec<_>>()
81        .join(".")
82}
83
84/// Quote a SQL Server identifier.
85///
86/// SQL Server uses square brackets for identifiers. Schema-qualified names
87/// (e.g., `schema.table`) are split and quoted per component.
88///
89/// # Examples
90///
91/// ```ignore
92/// assert_eq!(quote_sqlserver_identifier("v_user"), "[v_user]");
93/// assert_eq!(quote_sqlserver_identifier("dbo.v_user"), "[dbo].[v_user]");
94/// assert_eq!(
95///     quote_sqlserver_identifier("catalog.schema.table"),
96///     "[catalog].[schema].[table]"
97/// );
98/// ```
99#[inline]
100#[must_use]
101pub fn quote_sqlserver_identifier(identifier: &str) -> String {
102    identifier
103        .split('.')
104        .map(|part| format!("[{}]", part))
105        .collect::<Vec<_>>()
106        .join(".")
107}
108
109#[cfg(test)]
110mod tests {
111    use super::*;
112
113    #[test]
114    fn test_postgres_simple_identifier() {
115        assert_eq!(quote_postgres_identifier("v_user"), "\"v_user\"");
116    }
117
118    #[test]
119    fn test_postgres_schema_qualified() {
120        assert_eq!(quote_postgres_identifier("benchmark.v_user"), "\"benchmark\".\"v_user\"");
121    }
122
123    #[test]
124    fn test_postgres_three_part_name() {
125        assert_eq!(
126            quote_postgres_identifier("catalog.schema.table"),
127            "\"catalog\".\"schema\".\"table\""
128        );
129    }
130
131    #[test]
132    fn test_mysql_simple_identifier() {
133        assert_eq!(quote_mysql_identifier("v_user"), "`v_user`");
134    }
135
136    #[test]
137    fn test_mysql_schema_qualified() {
138        assert_eq!(quote_mysql_identifier("mydb.v_user"), "`mydb`.`v_user`");
139    }
140
141    #[test]
142    fn test_mysql_three_part_name() {
143        assert_eq!(quote_mysql_identifier("catalog.schema.table"), "`catalog`.`schema`.`table`");
144    }
145
146    #[test]
147    fn test_sqlite_simple_identifier() {
148        assert_eq!(quote_sqlite_identifier("v_user"), "\"v_user\"");
149    }
150
151    #[test]
152    fn test_sqlite_schema_qualified() {
153        assert_eq!(quote_sqlite_identifier("main.v_user"), "\"main\".\"v_user\"");
154    }
155
156    #[test]
157    fn test_sqlite_three_part_name() {
158        assert_eq!(
159            quote_sqlite_identifier("catalog.schema.table"),
160            "\"catalog\".\"schema\".\"table\""
161        );
162    }
163
164    #[test]
165    fn test_sqlserver_simple_identifier() {
166        assert_eq!(quote_sqlserver_identifier("v_user"), "[v_user]");
167    }
168
169    #[test]
170    fn test_sqlserver_schema_qualified() {
171        assert_eq!(quote_sqlserver_identifier("dbo.v_user"), "[dbo].[v_user]");
172    }
173
174    #[test]
175    fn test_sqlserver_three_part_name() {
176        assert_eq!(
177            quote_sqlserver_identifier("catalog.schema.table"),
178            "[catalog].[schema].[table]"
179        );
180    }
181}