Skip to content
This repository was archived by the owner on Dec 24, 2022. It is now read-only.

Commit e5a247a

Browse files
committed
Add proper support for parsing Order By expr into token list
1 parent 2a3ebc7 commit e5a247a

File tree

5 files changed

+139
-17
lines changed

5 files changed

+139
-17
lines changed

src/ServiceStack.OrmLite/Expressions/SqlExpression.cs

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -518,11 +518,9 @@ private SqlExpression<T> OrderByDescendingInternal(Expression keySelector)
518518
sep = string.Empty;
519519
useFieldName = true;
520520
orderByProperties.Clear();
521-
var fields = Visit(keySelector).ToString().Split(',');
522-
foreach (var field in fields)
523-
{
524-
orderByProperties.Add(field.Trim() + " DESC");
525-
}
521+
var orderBySql = Visit(keySelector).ToString();
522+
orderBySql.ParseTokens()
523+
.Each(x => orderByProperties.Add(x + " DESC"));
526524
BuildOrderByClauseInternal();
527525
return this;
528526
}
@@ -549,11 +547,9 @@ private SqlExpression<T> ThenByDescendingInternal(Expression keySelector)
549547
{
550548
sep = string.Empty;
551549
useFieldName = true;
552-
var fields = Visit(keySelector).ToString().Split(',');
553-
foreach (var field in fields)
554-
{
555-
orderByProperties.Add(field.Trim() + " DESC");
556-
}
550+
var orderBySql = Visit(keySelector).ToString();
551+
orderBySql.ParseTokens()
552+
.Each(x => orderByProperties.Add(x + " DESC"));
557553
BuildOrderByClauseInternal();
558554
return this;
559555
}

src/ServiceStack.OrmLite/OrmLiteUtils.cs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,5 +580,84 @@ public static List<string> GetNonDefaultValueInsertFields<T>(T obj)
580580
? null
581581
: insertFields;
582582
}
583+
584+
public static List<string> ParseTokens(this string expr)
585+
{
586+
var to = new List<string>();
587+
588+
if (string.IsNullOrEmpty(expr))
589+
return to;
590+
591+
var inDoubleQuotes = false;
592+
var inSingleQuotes = false;
593+
var inBraces = false;
594+
595+
var pos = 0;
596+
597+
for (var i = 0; i < expr.Length; i++)
598+
{
599+
var c = expr[i];
600+
if (inDoubleQuotes)
601+
{
602+
if (c == '"')
603+
inDoubleQuotes = false;
604+
continue;
605+
}
606+
if (inSingleQuotes)
607+
{
608+
if (c == '\'')
609+
inSingleQuotes = false;
610+
continue;
611+
}
612+
if (c == '"')
613+
{
614+
inDoubleQuotes = true;
615+
continue;
616+
}
617+
if (c == '\'')
618+
{
619+
inSingleQuotes = true;
620+
continue;
621+
}
622+
if (c == '(')
623+
{
624+
inBraces = true;
625+
continue;
626+
}
627+
if (c == ')')
628+
{
629+
inBraces = false;
630+
631+
var endPos = expr.IndexOf(',', i);
632+
if (endPos == -1)
633+
endPos = expr.Length;
634+
635+
to.Add(expr.Substring(pos, endPos - pos).Trim());
636+
637+
pos = endPos;
638+
continue;
639+
}
640+
641+
if (c == ',')
642+
{
643+
if (!inBraces)
644+
{
645+
var arg = expr.Substring(pos, i - pos).Trim();
646+
if (!string.IsNullOrEmpty(arg))
647+
to.Add(arg);
648+
pos = i + 1;
649+
}
650+
}
651+
}
652+
653+
var remaining = expr.Substring(pos, expr.Length - pos);
654+
if (!string.IsNullOrEmpty(remaining))
655+
remaining = remaining.Trim();
656+
657+
if (!string.IsNullOrEmpty(remaining))
658+
to.Add(remaining);
659+
660+
return to;
661+
}
583662
}
584663
}

tests/ServiceStack.OrmLite.Tests/Issues/MultiColumnOrderByDescending.cs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
using System.Collections.Generic;
1+
using System;
2+
using System.Collections.Generic;
23
using NUnit.Framework;
4+
using ServiceStack.Logging;
35
using ServiceStack.OrmLite.Tests.Shared;
6+
using ServiceStack.Text;
47

58
namespace ServiceStack.OrmLite.Tests.Issues
69
{
@@ -83,5 +86,37 @@ public void Does_orderbydescending_multiple_columns_using_orderbydescending()
8386
Assert.That(result[0].Id, Is.EqualTo(2));
8487
}
8588
}
89+
90+
public class Record
91+
{
92+
public int Id { get; set; }
93+
public double Value { get; set; }
94+
public DateTime? Time { get; set; }
95+
}
96+
97+
[Test]
98+
public void Can_OrderBy_DateTime()
99+
{
100+
using (var db = OpenDbConnection())
101+
{
102+
db.DropAndCreateTable<Record>();
103+
104+
db.InsertAll(new[] {
105+
new Record
106+
{
107+
Id = 1,
108+
Value = 50,
109+
Time = DateTime.Now,
110+
},
111+
});
112+
113+
var q = db.From<Record>();
114+
q.OrderByDescending(x => x.Time ?? DateTime.UtcNow);
115+
116+
var results = db.Select(q);
117+
118+
results.PrintDump();
119+
}
120+
}
86121
}
87122
}

tests/ServiceStack.OrmLite.Tests/OrderByTests.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
using System.Linq;
1+
using System;
2+
using System.Linq;
23
using NUnit.Framework;
3-
using ServiceStack.Common.Tests.Models;
4+
using ServiceStack.Logging;
45
using ServiceStack.OrmLite.Tests.Expression;
56
using ServiceStack.Text;
67

tests/ServiceStack.OrmLite.Tests/OrmLiteUtilExtensionsTests.cs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
using System.Collections.Generic;
2-
using System.Linq;
3-
using NUnit.Framework;
4-
using ServiceStack.OrmLite;
1+
using NUnit.Framework;
52

63
namespace ServiceStack.OrmLite.Tests
74
{
@@ -36,5 +33,19 @@ public void CanCreateNullInStatementFromEmptyList()
3633

3734
Assert.AreEqual("IN (NULL)", sql);
3835
}
36+
37+
[Test]
38+
public void Can_parse_field_Tokens()
39+
{
40+
Assert.That("FirstName".ParseTokens(), Is.EquivalentTo(new[] { "FirstName" }));
41+
Assert.That("FirstName, LastName".ParseTokens(), Is.EquivalentTo(new[] { "FirstName", "LastName" }));
42+
Assert.That("\"FirstName\"".ParseTokens(), Is.EquivalentTo(new[] { "\"FirstName\"" }));
43+
Assert.That("\"FirstName\",\"LastName\"".ParseTokens(), Is.EquivalentTo(new[] { "\"FirstName\"", "\"LastName\"" }));
44+
Assert.That("COALESCE(\"Time\", '2015-10-05')".ParseTokens(), Is.EquivalentTo(new[] { "COALESCE(\"Time\", '2015-10-05')" }));
45+
Assert.That("\"FirstName\",COALESCE(\"Time\", '2015-10-05'),\"LastName\"".ParseTokens(), Is.EquivalentTo(
46+
new[] { "\"FirstName\"", "COALESCE(\"Time\", '2015-10-05')", "\"LastName\"" }));
47+
Assert.That(" \"FirstName\" , COALESCE(\"Time\", '2015-10-05') , \"LastName\" ".ParseTokens(), Is.EquivalentTo(
48+
new[] { "\"FirstName\"", "COALESCE(\"Time\", '2015-10-05')", "\"LastName\"" }));
49+
}
3950
}
4051
}

0 commit comments

Comments
 (0)