Skip to content

Escape backslashes in RedisGraphUtilities.PrepareQuery #155

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/NRedisStack/Graph/GraphCommandBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using NRedisStack.Graph;
using NRedisStack.Graph.Literals;
using NRedisStack.RedisStackCommands;

Expand All @@ -7,6 +8,13 @@ public static class GraphCommandBuilder
{
internal static readonly object CompactQueryFlag = "--COMPACT";

/// <inheritdoc/>
public static SerializedCommand Query(string graphName, string query, Dictionary<string, object> parameters, long? timeout = null)
{
var preparedQuery = RedisGraphUtilities.PrepareQuery(query, parameters);
return Query(graphName, preparedQuery, timeout);
}

/// <inheritdoc/>
public static SerializedCommand Query(string graphName, string query, long? timeout = null)
{
Expand Down
7 changes: 6 additions & 1 deletion src/NRedisStack/Graph/RedisGraphUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,13 @@ internal static string QuoteString(string unquotedString)
{
var quotedString = new StringBuilder(unquotedString.Length + 12);

// Replace order is important, otherwise too many backslashes will be added.
var sanitizedUnquotedString = unquotedString
.Replace("\\", "\\\\")
.Replace("\"", "\\\"");

quotedString.Append('"');
quotedString.Append(unquotedString.Replace("\"", "\\\""));
quotedString.Append(sanitizedUnquotedString);
quotedString.Append('"');

return quotedString.ToString();
Expand Down
80 changes: 75 additions & 5 deletions tests/NRedisStack.Tests/Graph/GraphTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2003,12 +2003,82 @@ public void TestEqualsAndToString()
[Fact]
public void TestPrepareQuery()
{
const string return1Query = "RETURN 1";
const string return1QueryRecordString = "Record{values=1}";

var graph = redisFixture.Redis.GetDatabase().GRAPH();
var res1 = graph.Query("graph", "RETURN 1", new Dictionary<string, object> { { "a", (char)'c' } });
var res2 = graph.Query("graph", "RETURN 1", new Dictionary<string, object> { { "a", null } });
var res3 = graph.Query("graph", "RETURN 1", new Dictionary<string, object> { { "a", new string[]{"foo", "bar"} } });
var res4 = graph.Query("graph", "RETURN 1", new Dictionary<string, object> { { "a", new List<string>{"foo2", "bar2"} } });
// TODO: complete this test

// handle chars
var buildCommand = GraphCommandBuilder.Query("graph", return1Query, new Dictionary<string, object> { { "a", (char)'c' }} );
var expectedPreparedQuery1 = $"CYPHER a=\"c\" {return1Query}";
Assert.Equal(expectedPreparedQuery1, buildCommand.Args[1].ToString()!);
var res1 = graph.Query("graph", buildCommand.Args[1].ToString()!);
Assert.Single(res1);
Assert.Equal(return1QueryRecordString, res1.Single().ToString());

// handle null
var buildCommand2 = GraphCommandBuilder.Query("graph", return1Query, new Dictionary<string, object> { { "a", null } });
var expectedPreparedQuery2 = $"CYPHER a=null {return1Query}";
Assert.Equal(expectedPreparedQuery2,buildCommand2.Args[1].ToString()!);
var res2 = graph.Query("graph",buildCommand2.Args[1].ToString()!);
Assert.Single(res2);
Assert.Equal(return1QueryRecordString, res2.Single().ToString());

// handle arrays
var buildCommand3 = GraphCommandBuilder.Query("graph", return1Query, new Dictionary<string, object> { { "a", new string[] { "foo", "bar" } } });
var expectedPreparedQuery3 = $"CYPHER a=[\"foo\", \"bar\"] {return1Query}";
Assert.Equal(expectedPreparedQuery3,buildCommand3.Args[1].ToString()!);
var res3 = graph.Query("graph",buildCommand3.Args[1].ToString()!);
Assert.Single(res3);
Assert.Equal(return1QueryRecordString, res3.Single().ToString());

// handle lists
var buildCommand4 = GraphCommandBuilder.Query("graph", return1Query, new Dictionary<string, object> { { "a", new List<string> { "foo2", "bar2" } } });
var expectedPreparedQuery4 = $"CYPHER a=[\"foo2\", \"bar2\"] {return1Query}";
Assert.Equal(expectedPreparedQuery4,buildCommand4.Args[1].ToString()!);
var res4 = graph.Query("graph",buildCommand4.Args[1].ToString()!);
Assert.Single(res4);
Assert.Equal(return1QueryRecordString, res4.Single().ToString());

// handle bools
var buildCommand5 = GraphCommandBuilder.Query("graph", return1Query, new Dictionary<string, object> { { "a", true }, { "b", false } });
var expectedPreparedQuery5 = $"CYPHER a=true b=false {return1Query}";
Assert.Equal(expectedPreparedQuery5,buildCommand5.Args[1].ToString()!);
var res5 = graph.Query("graph",buildCommand5.Args[1].ToString()!);
Assert.Single(res5);
Assert.Equal(return1QueryRecordString, res4.Single().ToString());

// handle floats
var buildCommand6 = GraphCommandBuilder.Query("graph", return1Query, new Dictionary<string, object> { { "a", 1.4d } });
var expectedPreparedQuery6 = $"CYPHER a=1.4 {return1Query}";
Assert.Equal(expectedPreparedQuery6,buildCommand6.Args[1].ToString()!);
var res6 = graph.Query("graph",buildCommand6.Args[1].ToString()!);
Assert.Single(res6);
Assert.Equal(return1QueryRecordString, res4.Single().ToString());

// handle ints
var buildCommand7 = GraphCommandBuilder.Query("graph", return1Query, new Dictionary<string, object> { { "a", 5 } });
var expectedPreparedQuery7 = $"CYPHER a=5 {return1Query}";
Assert.Equal(expectedPreparedQuery7,buildCommand7.Args[1].ToString()!);
var res7 = graph.Query("graph",buildCommand7.Args[1].ToString()!);
Assert.Single(res7);
Assert.Equal(return1QueryRecordString, res4.Single().ToString());

// handle quotes
var buildCommand8 = GraphCommandBuilder.Query("graph", return1Query, new Dictionary<string, object> { { "a", "\"abc\"" } });
var expectedPreparedQuery8 = $"CYPHER a=\"\\\"abc\\\"\" {return1Query}";
Assert.Equal(expectedPreparedQuery8,buildCommand8.Args[1].ToString()!);
var res8 = graph.Query("graph",buildCommand8.Args[1].ToString()!);
Assert.Single(res8);
Assert.Equal(return1QueryRecordString, res5.Single().ToString());

// handle backslashes
var buildCommand9 = GraphCommandBuilder.Query("graph", return1Query, new Dictionary<string, object> { { "a", "abc\\" } });
var expectedPreparedQuery9 = $"CYPHER a=\"abc\\\\\" {return1Query}";
Assert.Equal(expectedPreparedQuery9,buildCommand9.Args[1].ToString()!);
var res9 = graph.Query("graph",buildCommand9.Args[1].ToString()!);
Assert.Single(res9);
Assert.Equal(return1QueryRecordString, res6.Single().ToString());
}
#endregion

Expand Down