Skip to content

Commit

Permalink
consolidate escape logic, match clickhouse-connect
Browse files Browse the repository at this point in the history
  • Loading branch information
serprex committed Oct 2, 2024
1 parent 3f87b34 commit 8356f30
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 32 deletions.
42 changes: 19 additions & 23 deletions src/sql/escape.rs
Original file line number Diff line number Diff line change
@@ -1,35 +1,31 @@
use std::fmt;

// Trust clickhouse-connect https://github.com/ClickHouse/clickhouse-connect/blob/5d85563410f3ec378cb199ec51d75e033211392c/clickhouse_connect/driver/binding.py#L15

// See https://clickhouse.tech/docs/en/sql-reference/syntax/#syntax-string-literal
pub(crate) fn string(src: &str, dst: impl fmt::Write) -> fmt::Result {
escape(src, dst, '\'')
pub(crate) fn string(src: &str, dst: &mut impl fmt::Write) -> fmt::Result {
dst.write_char('\'')?;
escape(src, dst)?;
dst.write_char('\'')
}

// See https://clickhouse.tech/docs/en/sql-reference/syntax/#syntax-identifiers
pub(crate) fn identifier(src: &str, dst: impl fmt::Write) -> fmt::Result {
escape(src, dst, '`')
pub(crate) fn identifier(src: &str, dst: &mut impl fmt::Write) -> fmt::Result {
dst.write_char('\'')?;
escape(src, dst)?;
dst.write_char('\'')
}

fn escape(src: &str, mut dst: impl fmt::Write, ch: char) -> fmt::Result {
dst.write_char(ch)?;

// TODO: escape newlines?
for (idx, part) in src.split(ch).enumerate() {
if idx > 0 {
dst.write_char('\\')?;
dst.write_char(ch)?;
}

for (idx, part) in part.split('\\').enumerate() {
if idx > 0 {
dst.write_str("\\\\")?;
}

dst.write_str(part)?;
}
pub(crate) fn escape(src: &str, dst: &mut impl fmt::Write) -> fmt::Result {
const REPLACE: &[char] = &['\\', '\'', '`', '\t', '\n'];
let mut rest = src;
while let Some(nextidx) = rest.find(REPLACE) {
let (before, after) = rest.split_at(nextidx);
rest = after;
dst.write_str(before)?;
dst.write_char('\\')?;
}

dst.write_char(ch)
dst.write_str(rest)
}

#[test]
Expand Down
10 changes: 1 addition & 9 deletions src/sql/ser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -321,15 +321,7 @@ impl<'a, W: Write> Serializer for ParamSerializer<'a, W> {
fn serialize_str(self, value: &str) -> Result {
// ClickHouse expects strings in params to be unquoted until inside a nested type
// nested types go through serialize_seq which'll quote strings
let mut rest = value;
while let Some(nextidx) = rest.find('\\') {
let (before, after) = rest.split_at(nextidx + 1);
rest = after;
self.writer.write_str(before)?;
self.writer.write_char('\\')?;
}
self.writer.write_str(rest)?;
Ok(())
Ok(escape::escape(value, self.writer)?)
}

#[inline]
Expand Down

0 comments on commit 8356f30

Please sign in to comment.