Bugfix - Properly escape '\' in strings ()

This commit is contained in:
Finn Bear 2023-05-31 00:36:29 -07:00 committed by GitHub
parent b2adca1851
commit ac9f77a62a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 44 additions and 14 deletions

View file

@ -1,7 +1,7 @@
use crate::sql::common::{take_digits, take_digits_range, take_u32_len};
use crate::sql::duration::Duration;
use crate::sql::error::IResult;
use crate::sql::escape::escape_str;
use crate::sql::escape::quote_str;
use crate::sql::strand::Strand;
use chrono::{DateTime, FixedOffset, Offset, SecondsFormat, TimeZone, Utc};
use chrono::{NaiveDate, NaiveDateTime, NaiveTime};
@ -89,7 +89,7 @@ impl Datetime {
impl Display for Datetime {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(&escape_str(&self.0.to_rfc3339_opts(SecondsFormat::AutoSi, true)), f)
Display::fmt(&quote_str(&self.0.to_rfc3339_opts(SecondsFormat::AutoSi, true)), f)
}
}

View file

@ -14,13 +14,43 @@ const DOUBLE_ESC: &str = r#"\""#;
const BACKTICK: char = '`';
const BACKTICK_ESC: &str = r#"\`"#;
/// Quotes a string with single or double quotes:
/// - cat -> 'cat'
/// - cat's -> "cat's"
/// - cat's "toy" -> "cat's \"toy\""
///
/// Escapes / as //
#[inline]
pub fn escape_str(s: &str) -> Cow<'_, str> {
if s.contains(SINGLE) {
escape_normal(s, DOUBLE, DOUBLE, DOUBLE_ESC)
} else {
Cow::Owned(format!("{SINGLE}{s}{SINGLE}"))
pub fn quote_str(s: &str) -> String {
// Rough approximation of capacity, which may be exceeded
// if things must be escaped.
let mut ret = String::with_capacity(2 + s.len());
fn escape_into(into: &mut String, s: &str, escape_double: bool) {
// Based on internals of str::replace
let mut last_end = 0;
for (start, part) in s.match_indices(|c| c == '\\' || (c == DOUBLE && escape_double)) {
into.push_str(&s[last_end..start]);
into.push_str(if part == "\\" {
"\\\\"
} else {
DOUBLE_ESC
});
last_end = start + part.len();
}
into.push_str(&s[last_end..s.len()]);
}
let quote = if s.contains(SINGLE) {
DOUBLE
} else {
SINGLE
};
ret.push(quote);
escape_into(&mut ret, s, quote == DOUBLE);
ret.push(quote);
ret
}
#[inline]

View file

@ -10,7 +10,7 @@ use crate::sql::comment::{mightbespace, shouldbespace};
use crate::sql::common::commas;
use crate::sql::duration::{duration, Duration};
use crate::sql::error::IResult;
use crate::sql::escape::escape_str;
use crate::sql::escape::quote_str;
use crate::sql::filter::{filters, Filter};
use crate::sql::fmt::is_pretty;
use crate::sql::fmt::pretty_indent;
@ -452,7 +452,7 @@ impl DefineLoginStatement {
impl Display for DefineLoginStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "DEFINE LOGIN {} ON {} PASSHASH {}", self.name, self.base, escape_str(&self.hash))
write!(f, "DEFINE LOGIN {} ON {} PASSHASH {}", self.name, self.base, quote_str(&self.hash))
}
}
@ -601,7 +601,7 @@ impl Display for DefineTokenStatement {
self.name,
self.base,
self.kind,
escape_str(&self.code)
quote_str(&self.code)
)
}
}

View file

@ -1,6 +1,6 @@
use crate::sql::error::Error::Parser;
use crate::sql::error::IResult;
use crate::sql::escape::escape_str;
use crate::sql::escape::quote_str;
use nom::branch::alt;
use nom::bytes::complete::{escaped_transform, is_not, tag, take, take_while_m_n};
use nom::character::complete::char;
@ -73,7 +73,7 @@ impl Strand {
impl Display for Strand {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(&escape_str(&self.0), f)
Display::fmt(&quote_str(&self.0), f)
}
}

View file

@ -1,6 +1,6 @@
use crate::sql::common::is_hex;
use crate::sql::error::IResult;
use crate::sql::escape::escape_str;
use crate::sql::escape::quote_str;
use crate::sql::strand::Strand;
use nom::branch::alt;
use nom::bytes::complete::take_while_m_n;
@ -99,7 +99,7 @@ impl Uuid {
impl Display for Uuid {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(&escape_str(&self.0.to_string()), f)
Display::fmt(&quote_str(&self.0.to_string()), f)
}
}