[Internal] Query: Fixes escaped string parsing in SqlParser (#4054)
* Initial commit * Addressed comments.
This commit is contained in:
Родитель
83509cc173
Коммит
258d960ae3
|
@ -9,6 +9,7 @@ namespace Microsoft.Azure.Cosmos.Query.Core.Parser
|
|||
using System.Collections.Immutable;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using Antlr4.Runtime.Misc;
|
||||
using Antlr4.Runtime.Tree;
|
||||
using Microsoft.Azure.Cosmos.SqlObjects;
|
||||
|
@ -950,8 +951,45 @@ namespace Microsoft.Azure.Cosmos.Query.Core.Parser
|
|||
private static string GetStringValueFromNode(IParseTree parseTree)
|
||||
{
|
||||
string text = parseTree.GetText();
|
||||
string textWithoutQuotes = text.Substring(1, text.Length - 2).Replace("\\\"", "\"");
|
||||
return textWithoutQuotes;
|
||||
|
||||
// 1. Remove leading and trailing (single or double) quotes.
|
||||
// 2. Unescape following characters:
|
||||
// \" => "
|
||||
// \\ => \
|
||||
// \/ => /
|
||||
// Based on the documentation, we should also escape single quote \'.
|
||||
// https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/query/constants#remarks
|
||||
// However that's failing in the parser (before reaching this point) and will be fixed separately (after checking server's behavior).
|
||||
StringBuilder stringBuilder = new StringBuilder(text.Length);
|
||||
for (int index = 1; index < text.Length - 1; index++)
|
||||
{
|
||||
switch (text[index])
|
||||
{
|
||||
case '\\':
|
||||
if ((index + 1) < (text.Length - 1))
|
||||
{
|
||||
switch (text[index + 1])
|
||||
{
|
||||
case '"':
|
||||
case '\\':
|
||||
case '/':
|
||||
stringBuilder.Append(text[index + 1]);
|
||||
index++;
|
||||
break;
|
||||
default:
|
||||
stringBuilder.Append(text[index]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
stringBuilder.Append(text[index]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
private static Number64 GetNumber64ValueFromNode(IParseTree parseTree)
|
||||
|
|
|
@ -135,8 +135,8 @@ namespace Microsoft.Azure.Cosmos.Test.BaselineTest
|
|||
|
||||
Assert.IsTrue(
|
||||
matched,
|
||||
$@" Baseline File {baselinePath},
|
||||
Output File {outputPath},
|
||||
$@" Baseline File {Path.GetFullPath(baselinePath)},
|
||||
Output File {Path.GetFullPath(outputPath)},
|
||||
Expected: {baselineTextSuffix},
|
||||
Actual: {outputTextSuffix}");
|
||||
}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
<Results>
|
||||
<Result>
|
||||
<Input>
|
||||
<Description><![CDATA[Single quoted string literals with escape seqence]]></Description>
|
||||
<Query><![CDATA[SELECT VALUE ['\"DoubleQuote', '\\ReverseSolidus', '\/solidus', '\bBackspace', '\fSeparatorFeed', '\nLineFeed', '\rCarriageReturn', '\tTab', '\u1234']]]></Query>
|
||||
</Input>
|
||||
<Output>
|
||||
<ParsedQuery><![CDATA[SELECT VALUE ["\"DoubleQuote", "\\ReverseSolidus", "/solidus", "\\bBackspace", "\\fSeparatorFeed", "\\nLineFeed", "\\rCarriageReturn", "\\tTab", "\\u1234"]]]></ParsedQuery>
|
||||
</Output>
|
||||
</Result>
|
||||
<Result>
|
||||
<Input>
|
||||
<Description><![CDATA[Double quoted string literals with escape seqence]]></Description>
|
||||
<Query><![CDATA[SELECT VALUE ["'SingleQuote", "\"DoubleQuote", "\\ReverseSolidus", "\/solidus", "\bBackspace", "\fSeparatorFeed", "\nLineFeed", "\rCarriageReturn", "\tTab", "\u1234"]]]></Query>
|
||||
</Input>
|
||||
<Output>
|
||||
<ParsedQuery><![CDATA[SELECT VALUE ["'SingleQuote", "\"DoubleQuote", "\\ReverseSolidus", "/solidus", "\\bBackspace", "\\fSeparatorFeed", "\\nLineFeed", "\\rCarriageReturn", "\\tTab", "\\u1234"]]]></ParsedQuery>
|
||||
</Output>
|
||||
</Result>
|
||||
<Result>
|
||||
<Input>
|
||||
<Description><![CDATA[Single quoted string literals special cases]]></Description>
|
||||
<Query><![CDATA[SELECT VALUE ['\"', '\"\"', '\\', '\\\\', '\/', '\/\/', '\b', '\b\b', '\f', '\f\f', '\n', '\n\n', '\r', '\r\r', '\t', '\t\t', '\u1234', '\u1234\u1234']]]></Query>
|
||||
</Input>
|
||||
<Output>
|
||||
<ParsedQuery><![CDATA[SELECT VALUE ["\"", "\"\"", "\\", "\\\\", "/", "//", "\\b", "\\b\\b", "\\f", "\\f\\f", "\\n", "\\n\\n", "\\r", "\\r\\r", "\\t", "\\t\\t", "\\u1234", "\\u1234\\u1234"]]]></ParsedQuery>
|
||||
</Output>
|
||||
</Result>
|
||||
<Result>
|
||||
<Input>
|
||||
<Description><![CDATA[Double quoted string literals special cases]]></Description>
|
||||
<Query><![CDATA[SELECT VALUE ["\"", "\"\"", "\\", "\\\\", "\/", "\/\/", "\b", "\b\b", "\f", "\f\f", "\n", "\n\n", "\r", "\r\r", "\t", "\t\t", "\u1234", "\u1234\u1234"]]]></Query>
|
||||
</Input>
|
||||
<Output>
|
||||
<ParsedQuery><![CDATA[SELECT VALUE ["\"", "\"\"", "\\", "\\\\", "/", "//", "\\b", "\\b\\b", "\\f", "\\f\\f", "\\n", "\\n\\n", "\\r", "\\r\\r", "\\t", "\\t\\t", "\\u1234", "\\u1234\\u1234"]]]></ParsedQuery>
|
||||
</Output>
|
||||
</Result>
|
||||
</Results>
|
|
@ -82,6 +82,9 @@
|
|||
<None Update="BaselineTest\TestBaseline\GroupByClauseSqlParserBaselineTests.Tests.xml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="BaselineTest\TestBaseline\ScalarExpressionSqlParserBaselineTests.StringLiteral.xml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="BaselineTest\TestBaseline\SelectClauseSqlParserBaselineTests.Tests.xml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
|
|
|
@ -520,6 +520,30 @@
|
|||
this.ExecuteTestSuite(inputs);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void StringLiteral()
|
||||
{
|
||||
List<SqlParserBaselineTestInput> inputs = new List<SqlParserBaselineTestInput>()
|
||||
{
|
||||
// Single quoted string literals do not allow ' even when it's escaped.
|
||||
// Parser currently fails with Antlr4.Runtime.NoViableAltException
|
||||
CreateInput(
|
||||
description: @"Single quoted string literals with escape seqence",
|
||||
scalarExpression: @"['\""DoubleQuote', '\\ReverseSolidus', '\/solidus', '\bBackspace', '\fSeparatorFeed', '\nLineFeed', '\rCarriageReturn', '\tTab', '\u1234']"),
|
||||
CreateInput(
|
||||
description: @"Double quoted string literals with escape seqence",
|
||||
scalarExpression: @"[""'SingleQuote"", ""\""DoubleQuote"", ""\\ReverseSolidus"", ""\/solidus"", ""\bBackspace"", ""\fSeparatorFeed"", ""\nLineFeed"", ""\rCarriageReturn"", ""\tTab"", ""\u1234""]"),
|
||||
CreateInput(
|
||||
description: @"Single quoted string literals special cases",
|
||||
scalarExpression: @"['\""', '\""\""', '\\', '\\\\', '\/', '\/\/', '\b', '\b\b', '\f', '\f\f', '\n', '\n\n', '\r', '\r\r', '\t', '\t\t', '\u1234', '\u1234\u1234']"),
|
||||
CreateInput(
|
||||
description: @"Double quoted string literals special cases",
|
||||
scalarExpression: @"[""\"""", ""\""\"""", ""\\"", ""\\\\"", ""\/"", ""\/\/"", ""\b"", ""\b\b"", ""\f"", ""\f\f"", ""\n"", ""\n\n"", ""\r"", ""\r\r"", ""\t"", ""\t\t"", ""\u1234"", ""\u1234\u1234""]"),
|
||||
};
|
||||
|
||||
this.ExecuteTestSuite(inputs);
|
||||
}
|
||||
|
||||
public static SqlParserBaselineTestInput CreateInput(string description, string scalarExpression)
|
||||
{
|
||||
return new SqlParserBaselineTestInput(description, $"SELECT VALUE {scalarExpression}");
|
||||
|
|
Загрузка…
Ссылка в новой задаче