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

Commit 4aa61bf

Browse files
committed
Add support for sqlOrderByFields
1 parent bac4b11 commit 4aa61bf

File tree

5 files changed

+91
-59
lines changed

5 files changed

+91
-59
lines changed

src/ServiceStack.OrmLite/DbScripts.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,8 @@ public string sqlSkip(ScriptScopeContext scope, int? offset) =>
209209
dialect(scope, d => padCondition(d.SqlLimit(offset, null)));
210210
public string sqlTake(ScriptScopeContext scope, int? limit) =>
211211
dialect(scope, d => padCondition(d.SqlLimit(null, limit)));
212+
public string sqlOrderByFields(ScriptScopeContext scope, string orderBy) =>
213+
dialect(scope, d => OrmLiteUtils.OrderByFields(d, orderBy));
212214
public string ormliteVar(ScriptScopeContext scope, string name) =>
213215
dialect(scope, d => d.Variables.TryGetValue(name, out var value) ? value : null);
214216

src/ServiceStack.OrmLite/DbScriptsAsync.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,8 @@ public string sqlSkip(ScriptScopeContext scope, int? offset) =>
210210
dialect(scope, d => padCondition(d.SqlLimit(offset, null)));
211211
public string sqlTake(ScriptScopeContext scope, int? limit) =>
212212
dialect(scope, d => padCondition(d.SqlLimit(null, limit)));
213+
public string sqlOrderByFields(ScriptScopeContext scope, string orderBy) =>
214+
dialect(scope, d => OrmLiteUtils.OrderByFields(d, orderBy));
213215
public string ormliteVar(ScriptScopeContext scope, string name) =>
214216
dialect(scope, d => d.Variables.TryGetValue(name, out var value) ? value : null);
215217

src/ServiceStack.OrmLite/OrmLiteUtils.cs

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -737,58 +737,42 @@ private static int TryGuessColumnIndex(string fieldName, Dictionary<string, int>
737737
// e.g. CustomerId (C#) vs customer_id (DB)
738738
var dbFieldNameWithNoUnderscores = dbFieldName.Replace("_", "");
739739
if (string.Compare(fieldName, dbFieldNameWithNoUnderscores, StringComparison.OrdinalIgnoreCase) == 0)
740-
{
741740
return i;
742-
}
743741

744742
// Next guess: Maybe the DB field has special characters?
745743
// e.g. Quantity (C#) vs quantity% (DB)
746744
var dbFieldNameSanitized = AllowedPropertyCharsRegex.Replace(dbFieldName, string.Empty);
747745
if (string.Compare(fieldName, dbFieldNameSanitized, StringComparison.OrdinalIgnoreCase) == 0)
748-
{
749746
return i;
750-
}
751747

752748
// Next guess: Maybe the DB field has special characters *and* has underscores?
753749
// e.g. Quantity (C#) vs quantity_% (DB)
754750
if (string.Compare(fieldName, dbFieldNameSanitized.Replace("_", string.Empty), StringComparison.OrdinalIgnoreCase) == 0)
755-
{
756751
return i;
757-
}
758752

759753
// Next guess: Maybe the DB field has some prefix that we don't have in our C# field?
760754
// e.g. CustomerId (C#) vs t130CustomerId (DB)
761755
if (dbFieldName.EndsWith(fieldName, StringComparison.OrdinalIgnoreCase))
762-
{
763756
return i;
764-
}
765757

766758
// Next guess: Maybe the DB field has some prefix that we don't have in our C# field *and* has underscores?
767759
// e.g. CustomerId (C#) vs t130_CustomerId (DB)
768760
if (dbFieldNameWithNoUnderscores.EndsWith(fieldName, StringComparison.OrdinalIgnoreCase))
769-
{
770761
return i;
771-
}
772762

773763
// Next guess: Maybe the DB field has some prefix that we don't have in our C# field *and* has special characters?
774764
// e.g. CustomerId (C#) vs t130#CustomerId (DB)
775765
if (dbFieldNameSanitized.EndsWith(fieldName, StringComparison.OrdinalIgnoreCase))
776-
{
777766
return i;
778-
}
779767

780768
// Next guess: Maybe the DB field has some prefix that we don't have in our C# field *and* has underscores *and* has special characters?
781769
// e.g. CustomerId (C#) vs t130#Customer_I#d (DB)
782770
if (dbFieldNameSanitized.Replace("_", "").EndsWith(fieldName, StringComparison.OrdinalIgnoreCase))
783-
{
784771
return i;
785-
}
786772

787773
// Cater for Naming Strategies like PostgreSQL that has lower_underscore names
788774
if (dbFieldNameSanitized.Replace("_", "").EndsWith(fieldName.Replace("_", ""), StringComparison.OrdinalIgnoreCase))
789-
{
790775
return i;
791-
}
792776
}
793777

794778
return NotFound;
@@ -1137,5 +1121,34 @@ public static string QuotedLiteral(string text) => text == null || text.IndexOf(
11371121
: "'" + text + "'";
11381122

11391123
public static string UnquotedColumnName(string columnExpr) => columnExpr.LastRightPart('.').StripDbQuotes();
1124+
1125+
public static string OrderByFields(IOrmLiteDialectProvider dialect, string orderBy)
1126+
{
1127+
if (string.IsNullOrEmpty(orderBy))
1128+
return string.Empty;
1129+
1130+
var sb = StringBuilderCache.Allocate();
1131+
1132+
var fields = orderBy.Split(',');
1133+
const string Asc = "";
1134+
const string Desc = " DESC";
1135+
1136+
var orderBySuffix = Asc;
1137+
foreach (var fieldName in fields)
1138+
{
1139+
if (sb.Length > 0)
1140+
sb.Append(", ");
1141+
1142+
var reverse = fieldName.StartsWith("-");
1143+
var useSuffix = reverse
1144+
? orderBySuffix == Asc ? Desc : Asc
1145+
: orderBySuffix;
1146+
var useName = reverse ? fieldName.Substring(1) : fieldName;
1147+
var quotedName = dialect.GetQuotedColumnName(useName);
1148+
1149+
sb.Append(quotedName + useSuffix);
1150+
}
1151+
return StringBuilderCache.ReturnAndFree(sb);
1152+
}
11401153
}
11411154
}

tests/ServiceStack.OrmLite.Tests/OrderByTests.cs

Lines changed: 48 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -15,68 +15,73 @@ public OrderByTests(DialectContext context) : base(context) {}
1515
[Test]
1616
public void Can_order_by_random()
1717
{
18-
using (var db = OpenDbConnection())
19-
{
20-
db.DropAndCreateTable<LetterFrequency>();
18+
using var db = OpenDbConnection();
19+
db.DropAndCreateTable<LetterFrequency>();
2120

22-
10.Times(i => db.Insert(new LetterFrequency { Letter = ('A' + i).ToString() }));
21+
10.Times(i => db.Insert(new LetterFrequency { Letter = ('A' + i).ToString() }));
2322

24-
var rowIds1 = db.Select(db.From<LetterFrequency>().OrderBy(x => x.Id)).Map(x => x.Id);
25-
var rowIds2 = db.Select(db.From<LetterFrequency>().OrderBy(x => x.Id)).Map(x => x.Id);
23+
var rowIds1 = db.Select(db.From<LetterFrequency>().OrderBy(x => x.Id)).Map(x => x.Id);
24+
var rowIds2 = db.Select(db.From<LetterFrequency>().OrderBy(x => x.Id)).Map(x => x.Id);
2625

27-
Assert.That(rowIds1.SequenceEqual(rowIds2));
26+
Assert.That(rowIds1.SequenceEqual(rowIds2));
2827

29-
rowIds1 = db.Select(db.From<LetterFrequency>().OrderByRandom()).Map(x => x.Id);
30-
rowIds2 = db.Select(db.From<LetterFrequency>().OrderByRandom()).Map(x => x.Id);
28+
rowIds1 = db.Select(db.From<LetterFrequency>().OrderByRandom()).Map(x => x.Id);
29+
rowIds2 = db.Select(db.From<LetterFrequency>().OrderByRandom()).Map(x => x.Id);
3130

32-
Assert.That(!rowIds1.SequenceEqual(rowIds2));
33-
}
31+
Assert.That(!rowIds1.SequenceEqual(rowIds2));
3432
}
3533

3634
[Test]
3735
public void Can_OrderBy_and_ThenBy()
3836
{
39-
using (var db = OpenDbConnection())
40-
{
41-
db.DropAndCreateTable<LetterFrequency>();
37+
using var db = OpenDbConnection();
38+
db.DropAndCreateTable<LetterFrequency>();
4239

43-
db.Insert(new LetterFrequency {Letter = "C" });
44-
db.Insert(new LetterFrequency {Letter = "C" });
45-
db.Insert(new LetterFrequency {Letter = "B" });
46-
db.Insert(new LetterFrequency {Letter = "A" });
40+
db.Insert(new LetterFrequency {Letter = "C" });
41+
db.Insert(new LetterFrequency {Letter = "C" });
42+
db.Insert(new LetterFrequency {Letter = "B" });
43+
db.Insert(new LetterFrequency {Letter = "A" });
4744

48-
var q = db.From<LetterFrequency>();
49-
q.OrderBy(nameof(LetterFrequency.Letter))
50-
.ThenBy(nameof(LetterFrequency.Id));
45+
var q = db.From<LetterFrequency>();
46+
q.OrderBy(nameof(LetterFrequency.Letter))
47+
.ThenBy(nameof(LetterFrequency.Id));
5148

52-
var tracks = db.Select(q);
49+
var tracks = db.Select(q);
5350

54-
Assert.That(tracks.First().Letter, Is.EqualTo("A"));
55-
Assert.That(tracks.Last().Letter, Is.EqualTo("C"));
56-
}
51+
Assert.That(tracks.First().Letter, Is.EqualTo("A"));
52+
Assert.That(tracks.Last().Letter, Is.EqualTo("C"));
5753
}
5854

5955
[Test]
6056
public void Can_OrderBy_multi_table_expression()
6157
{
62-
using (var db = OpenDbConnection())
63-
{
64-
db.DropAndCreateTable<LetterFrequency>();
65-
db.DropAndCreateTable<LetterWeighting>();
66-
67-
var letters = "A,B,C,D,E".Split(',');
68-
var i = 0;
69-
letters.Each(letter => {
70-
var id = db.Insert(new LetterFrequency {Letter = letter}, selectIdentity: true);
71-
db.Insert(new LetterWeighting {LetterFrequencyId = id, Weighting = ++i * 10});
72-
});
73-
74-
var q = db.From<LetterFrequency>()
75-
.Join<LetterWeighting>()
76-
.OrderBy<LetterFrequency, LetterWeighting>((f, w) => f.Id > w.Weighting ? f.Id : w.Weighting);
77-
78-
var results = db.Select(q);
79-
}
58+
using var db = OpenDbConnection();
59+
db.DropAndCreateTable<LetterFrequency>();
60+
db.DropAndCreateTable<LetterWeighting>();
61+
62+
var letters = "A,B,C,D,E".Split(',');
63+
var i = 0;
64+
letters.Each(letter => {
65+
var id = db.Insert(new LetterFrequency {Letter = letter}, selectIdentity: true);
66+
db.Insert(new LetterWeighting {LetterFrequencyId = id, Weighting = ++i * 10});
67+
});
68+
69+
var q = db.From<LetterFrequency>()
70+
.Join<LetterWeighting>()
71+
.OrderBy<LetterFrequency, LetterWeighting>((f, w) => f.Id > w.Weighting ? f.Id : w.Weighting);
72+
73+
var results = db.Select(q);
74+
}
75+
76+
[Test]
77+
public void Can_OrderByFields()
78+
{
79+
using var db = OpenDbConnection();
80+
var d = db.GetDialectProvider();
81+
Assert.That(OrmLiteUtils.OrderByFields(d,"a").NormalizeQuotes(), Is.EqualTo("'a'"));
82+
Assert.That(OrmLiteUtils.OrderByFields(d,"a,b").NormalizeQuotes(), Is.EqualTo("'a', 'b'"));
83+
Assert.That(OrmLiteUtils.OrderByFields(d,"-a,b").NormalizeQuotes(), Is.EqualTo("'a' desc, 'b'"));
84+
Assert.That(OrmLiteUtils.OrderByFields(d,"a,-b").NormalizeQuotes(), Is.EqualTo("'a', 'b' desc"));
8085
}
8186
}
8287
}

tests/ServiceStack.OrmLite.Tests/TestHelpers.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,16 @@ public static string NormalizeSql(this string sql)
1717
.TrimEnd();
1818
}
1919

20+
public static string NormalizeQuotes(this string sql)
21+
{
22+
return sql.ToLower()
23+
.Replace("\"", "'")
24+
.Replace("`", "'")
25+
.Replace("[", "'")
26+
.Replace("]", "'")
27+
.TrimEnd();
28+
}
29+
2030
public static string PreNormalizeSql(this string sql, IDbConnection db)
2131
{
2232
var paramString = db.GetDialectProvider().ParamString;

0 commit comments

Comments
 (0)