Skip to main content

uqa_sql/expr/scalar_helpers/
quoting.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! SQL quoting scans borrowed input and admits escaped output through the shared string owner.
8
9use uqa_core::{
10    memory::{Produced, ProductionControl, ProductionString},
11    ValueRetentionError,
12};
13
14/// Double-quote unless the identifier is a safe lowercase name that is not a keyword.
15pub fn quote_ident(ident: &str) -> String {
16    quote_ident_with_control(ident, &ProductionControl::uncontrolled())
17        .expect("ordinary identifier quoting")
18        .into_uncontrolled()
19        .expect("ordinary quoted identifier")
20}
21
22pub(in crate::expr) fn quote_ident_with_control(
23    ident: &str,
24    control: &ProductionControl<'_>,
25) -> Result<Produced<String>, ValueRetentionError> {
26    control.check()?;
27    let mut safe = !ident.is_empty();
28    for (index, character) in ident.chars().enumerate() {
29        control.check()?;
30        safe &= character.is_ascii_lowercase()
31            || character == '_'
32            || (index > 0 && (character.is_ascii_digit() || character == '$'));
33    }
34    if safe && !super::is_quoted_keyword(ident) {
35        return control.copy_text(ident);
36    }
37    let mut output = ProductionString::new(*control);
38    output.push('"')?;
39    for character in ident.chars() {
40        if character == '"' {
41            output.push('"')?;
42        }
43        output.push(character)?;
44    }
45    output.push('"')?;
46    output.finish()
47}
48
49/// Single-quote with doubled quotes; backslashes select the escaped-literal form.
50pub(in crate::expr) fn quote_literal_with_control(
51    text: &str,
52    control: &ProductionControl<'_>,
53) -> Result<Produced<String>, ValueRetentionError> {
54    control.check()?;
55    let mut escape = false;
56    for character in text.chars() {
57        control.check()?;
58        escape |= character == '\\';
59    }
60    let mut output = ProductionString::new(*control);
61    if escape {
62        output.push('E')?;
63    }
64    output.push('\'')?;
65    for character in text.chars() {
66        if matches!(character, '\'' | '\\') {
67            output.push(character)?;
68        }
69        output.push(character)?;
70    }
71    output.push('\'')?;
72    output.finish()
73}