mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-21 23:21:59 +00:00
dbms: added support for hex escape sequences in strings [#METR-19072].
This commit is contained in:
parent
c39a17e65a
commit
8c8210062a
@ -1,8 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <DB/IO/ReadHelpers.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
inline std::string escapeForFileName(const std::string & s)
|
||||
{
|
||||
std::string res;
|
||||
@ -30,25 +34,13 @@ inline std::string escapeForFileName(const std::string & s)
|
||||
return res;
|
||||
}
|
||||
|
||||
inline char unhex(char c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '0' ... '9':
|
||||
return c - '0';
|
||||
case 'A' ... 'F':
|
||||
return c - 'A' + 10;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
inline std::string unescapeForFileName(const std::string & s)
|
||||
{
|
||||
std::string res;
|
||||
const char * pos = s.data();
|
||||
const char * end = pos + s.size();
|
||||
|
||||
|
||||
while (pos != end)
|
||||
{
|
||||
if (*pos != '%')
|
||||
@ -57,17 +49,19 @@ inline std::string unescapeForFileName(const std::string & s)
|
||||
{
|
||||
/// пропустим '%'
|
||||
if (++pos == end) break;
|
||||
|
||||
|
||||
char val = unhex(*pos) * 16;
|
||||
|
||||
|
||||
if (++pos == end) break;
|
||||
|
||||
|
||||
val += unhex(*pos);
|
||||
|
||||
|
||||
res += val;
|
||||
}
|
||||
|
||||
|
||||
++pos;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ namespace DB
|
||||
|
||||
/// Функции-помошники для форматированного чтения
|
||||
|
||||
static inline char parseEscapeSequence(char c)
|
||||
inline char parseEscapeSequence(char c)
|
||||
{
|
||||
switch(c)
|
||||
{
|
||||
@ -49,6 +49,21 @@ static inline char parseEscapeSequence(char c)
|
||||
}
|
||||
}
|
||||
|
||||
inline char unhex(char c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '0' ... '9':
|
||||
return c - '0';
|
||||
case 'a' ... 'f':
|
||||
return c - 'a' + 10;
|
||||
case 'A' ... 'F':
|
||||
return c - 'A' + 10;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Эти функции находятся в VarInt.h
|
||||
/// inline void throwReadAfterEOF()
|
||||
|
@ -142,6 +142,34 @@ static inline const char * find_first_tab_lf_or_backslash(const char * begin, co
|
||||
}
|
||||
|
||||
|
||||
/** Распарсить escape-последовательность, которая может быть простой (один символ после бэкслеша) или более сложной (несколько символов).
|
||||
* Предполагается, что курсор расположен на символе \
|
||||
*/
|
||||
static void parseComplexEscapeSequence(String & s, ReadBuffer & buf)
|
||||
{
|
||||
++buf.position();
|
||||
if (buf.eof())
|
||||
throw Exception("Cannot parse escape sequence", ErrorCodes::CANNOT_PARSE_ESCAPE_SEQUENCE);
|
||||
|
||||
if (*buf.position() == 'x')
|
||||
{
|
||||
++buf.position();
|
||||
/// escape-последовательность вида \xAA
|
||||
UInt8 c1;
|
||||
UInt8 c2;
|
||||
readPODBinary(c1, buf);
|
||||
readPODBinary(c2, buf);
|
||||
s += static_cast<char>(unhex(c1) * 16 + unhex(c2));
|
||||
}
|
||||
else
|
||||
{
|
||||
/// Обычная escape-последовательность из одного символа.
|
||||
s += parseEscapeSequence(*buf.position());
|
||||
++buf.position();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void readEscapedString(DB::String & s, DB::ReadBuffer & buf)
|
||||
{
|
||||
s = "";
|
||||
@ -159,13 +187,7 @@ void readEscapedString(DB::String & s, DB::ReadBuffer & buf)
|
||||
return;
|
||||
|
||||
if (*buf.position() == '\\')
|
||||
{
|
||||
++buf.position();
|
||||
if (buf.eof())
|
||||
throw Exception("Cannot parse escape sequence", DB::ErrorCodes::CANNOT_PARSE_ESCAPE_SEQUENCE);
|
||||
s += DB::parseEscapeSequence(*buf.position());
|
||||
++buf.position();
|
||||
}
|
||||
parseComplexEscapeSequence(s, buf);
|
||||
}
|
||||
}
|
||||
|
||||
@ -233,13 +255,7 @@ static void readAnyQuotedString(String & s, ReadBuffer & buf)
|
||||
}
|
||||
|
||||
if (*buf.position() == '\\')
|
||||
{
|
||||
++buf.position();
|
||||
if (buf.eof())
|
||||
throw Exception("Cannot parse escape sequence", ErrorCodes::CANNOT_PARSE_ESCAPE_SEQUENCE);
|
||||
s += parseEscapeSequence(*buf.position());
|
||||
++buf.position();
|
||||
}
|
||||
parseComplexEscapeSequence(s, buf);
|
||||
}
|
||||
|
||||
throw Exception("Cannot parse quoted string: expected closing quote",
|
||||
|
@ -375,40 +375,21 @@ bool ParserStringLiteral::parseImpl(Pos & pos, Pos end, ASTPtr & node, Pos & max
|
||||
return false;
|
||||
}
|
||||
|
||||
++pos;
|
||||
ReadBuffer in(const_cast<char *>(pos), end - pos, 0);
|
||||
|
||||
while (pos != end)
|
||||
try
|
||||
{
|
||||
size_t bytes = 0;
|
||||
for (; pos + bytes != end; ++bytes)
|
||||
if (pos[bytes] == '\\' || pos[bytes] == '\'')
|
||||
break;
|
||||
|
||||
s.append(pos, bytes);
|
||||
pos += bytes;
|
||||
|
||||
if (*pos == '\'')
|
||||
{
|
||||
++pos;
|
||||
node = new ASTLiteral(StringRange(begin, pos), s);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (*pos == '\\')
|
||||
{
|
||||
++pos;
|
||||
if (pos == end)
|
||||
{
|
||||
expected = "escape sequence";
|
||||
return false;
|
||||
}
|
||||
s += parseEscapeSequence(*pos);
|
||||
++pos;
|
||||
}
|
||||
readQuotedString(s, in);
|
||||
}
|
||||
catch (const Exception & e)
|
||||
{
|
||||
expected = "string literal";
|
||||
return false;
|
||||
}
|
||||
|
||||
expected = "closing single quote";
|
||||
return false;
|
||||
pos += in.count();
|
||||
node = new ASTLiteral(StringRange(begin, pos), s);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
@ -0,0 +1 @@
|
||||
0 Р
|
@ -0,0 +1 @@
|
||||
SELECT '\x30 \xD0\xA0';
|
Loading…
Reference in New Issue
Block a user