Skip to content

Commit

Permalink
Add functionality to support custom sql query using with select format
Browse files Browse the repository at this point in the history
- Add functionality to support custom sql query using `with select` format
- Add unit test
- Fixes GH-2753
  • Loading branch information
mysticmind authored and jeremydmiller committed Dec 2, 2023
1 parent c31827d commit 9a1a576
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 1 deletion.
19 changes: 19 additions & 0 deletions src/DocumentDbTests/Reading/query_by_sql.cs
Original file line number Diff line number Diff line change
Expand Up @@ -379,4 +379,23 @@ public async Task get_count_asynchronously()
var sum = sumResults.Single();
sum.ShouldBe(4);
}

[Fact]
public async Task can_query_using_with_select()
{
await using var session = theStore.LightweightSession();
var time = DateTimeOffset.UtcNow;
var u = new User { FirstName = "Jeremy", LastName = "Miller", ModifiedAt = time, Age = 28 };
session.Store(u);
await session.SaveChangesAsync();

var users =
await
session.QueryAsync<User>(
"with my_with_query as (select data from mt_doc_user where data ->> 'FirstName' = 'Jeremy') select data from my_with_query");
var user = users.Single();

user.LastName.ShouldBe("Miller");
user.Id.ShouldBe(u.Id);
}
}
38 changes: 37 additions & 1 deletion src/Marten/Linq/QueryHandlers/UserSuppliedQueryHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ public UserSuppliedQueryHandler(IMartenSession session, string sql, object[] par
{
_sql = sql.TrimStart();
_parameters = parameters;
SqlContainsCustomSelect = _sql.StartsWith("select", StringComparison.OrdinalIgnoreCase);
SqlContainsCustomSelect = _sql.StartsWith("select", StringComparison.OrdinalIgnoreCase)
|| IsWithFollowedBySelect(_sql);

_selectClause = GetSelectClause(session);
_selector = (ISelector<T>)_selectClause.BuildSelector(session);
Expand Down Expand Up @@ -136,4 +137,39 @@ private ISelectClause GetSelectClause(IMartenSession session)

return session.StorageFor(typeof(T));
}

private static bool IsWithFollowedBySelect(string sql)
{
var parenthesesLevel = 0;
var isWithBlockDetected = false;

for (var i = 0; i < sql.Length; i++)
{
var c = sql[i];

// Check for parentheses to handle nested structures
if (c == '(')
{
parenthesesLevel++;
}
else if (c == ')')
{
parenthesesLevel--;
}

// Detect the beginning of the WITH block
if (!isWithBlockDetected && i < sql.Length - 4 && sql.Substring(i, 4).Equals("with", StringComparison.OrdinalIgnoreCase))
{
isWithBlockDetected = true;
}

// Detect the beginning of the SELECT block only if WITH block is detected and at top-level
if (isWithBlockDetected && i < sql.Length - 6 && sql.Substring(i, 6).Equals("select", StringComparison.OrdinalIgnoreCase) && parenthesesLevel == 0)
{
return true;
}
}

return false;
}
}

0 comments on commit 9a1a576

Please sign in to comment.