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

Commit dc5ccda

Browse files
committed
Added support for CROSS JOIN in SqlExpression
1 parent 1830151 commit dc5ccda

File tree

2 files changed

+134
-40
lines changed

2 files changed

+134
-40
lines changed

src/ServiceStack.OrmLite/Expressions/SqlExpression.Join.cs

Lines changed: 47 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,17 @@ public SqlExpression<T> FullJoin<Source, Target>(Expression<Func<Source, Target,
6565
return InternalJoin("FULL JOIN", joinExpr);
6666
}
6767

68-
private SqlExpression<T> InternalJoin<Source, Target>(string joinType,
68+
public SqlExpression<T> CrossJoin<Target>(Expression<Func<T, Target, bool>> joinExpr = null)
69+
{
70+
return InternalJoin("CROSS JOIN", joinExpr);
71+
}
72+
73+
public SqlExpression<T> CrossJoin<Source, Target>(Expression<Func<Source, Target, bool>> joinExpr = null)
74+
{
75+
return InternalJoin("CROSS JOIN", joinExpr);
76+
}
77+
78+
private SqlExpression<T> InternalJoin<Source, Target>(string joinType,
6979
Expression<Func<Source, Target, bool>> joinExpr)
7080
{
7181
var sourceDef = typeof(Source).GetModelDefinition();
@@ -74,59 +84,56 @@ private SqlExpression<T> InternalJoin<Source, Target>(string joinType,
7484
return InternalJoin(joinType, joinExpr, sourceDef, targetDef);
7585
}
7686

77-
private SqlExpression<T> InternalJoin(string joinType,
78-
Expression joinExpr, ModelDefinition sourceDef, ModelDefinition targetDef)
87+
private string InternalCreateSqlFromExpression(Expression joinExpr)
7988
{
80-
PrefixFieldWithTableName = true;
81-
82-
var fromExpr = FromExpression;
83-
var sbJoin = new StringBuilder();
84-
85-
string sqlExpr;
89+
return "ON {0}".Fmt(Visit(joinExpr).ToString());
90+
}
8691

87-
//Changes how Sql Expressions are generated.
88-
useFieldName = true;
89-
sep = " ";
92+
private string InternalCreateSqlFromDefinitions(ModelDefinition sourceDef, ModelDefinition targetDef, bool allowMissingOnClause)
93+
{
94+
var parentDef = sourceDef;
95+
var childDef = targetDef;
9096

91-
if (joinExpr != null)
97+
var refField = OrmLiteReadCommandExtensions.GetRefFieldDefIfExists(parentDef, childDef);
98+
if (refField == null)
9299
{
93-
sqlExpr = Visit(joinExpr).ToString();
100+
parentDef = targetDef;
101+
childDef = sourceDef;
102+
refField = OrmLiteReadCommandExtensions.GetRefFieldDefIfExists(parentDef, childDef);
94103
}
95-
else
104+
105+
if (refField == null)
96106
{
97-
var parentDef = sourceDef;
98-
var childDef = targetDef;
107+
if(!allowMissingOnClause)
108+
throw new ArgumentException("Could not infer relationship between {0} and {1}".Fmt(sourceDef.ModelName, targetDef.ModelName));
99109

100-
var refField = OrmLiteReadCommandExtensions.GetRefFieldDefIfExists(parentDef, childDef);
101-
if (refField == null)
102-
{
103-
parentDef = targetDef;
104-
childDef = sourceDef;
105-
refField = OrmLiteReadCommandExtensions.GetRefFieldDefIfExists(parentDef, childDef);
106-
}
110+
return string.Empty;
111+
}
107112

108-
if (refField == null)
109-
{
110-
throw new ArgumentException("Could not infer relationship between {0} and {1}"
111-
.Fmt(sourceDef.ModelName, targetDef.ModelName));
112-
}
113+
return "ON\n({0}.{1} = {2}.{3})".Fmt(
114+
DialectProvider.GetQuotedTableName(parentDef),
115+
SqlColumn(parentDef.PrimaryKey.FieldName),
116+
DialectProvider.GetQuotedTableName(childDef),
117+
SqlColumn(refField.FieldName));
118+
}
113119

114-
sqlExpr = "\n({0}.{1} = {2}.{3})".Fmt(
115-
DialectProvider.GetQuotedTableName(parentDef),
116-
SqlColumn(parentDef.PrimaryKey.FieldName),
117-
DialectProvider.GetQuotedTableName(childDef),
118-
SqlColumn(refField.FieldName));
119-
}
120+
private SqlExpression<T> InternalJoin(string joinType,
121+
Expression joinExpr, ModelDefinition sourceDef, ModelDefinition targetDef)
122+
{
123+
PrefixFieldWithTableName = true;
124+
125+
//Changes how Sql Expressions are generated.
126+
useFieldName = true;
127+
sep = " ";
128+
129+
string sqlExpr = joinExpr != null ? InternalCreateSqlFromExpression(joinExpr)
130+
: InternalCreateSqlFromDefinitions(sourceDef, targetDef, "CROSS JOIN".Equals(joinType));
120131

121132
var joinDef = tableDefs.Contains(targetDef) && !tableDefs.Contains(sourceDef)
122133
? sourceDef
123134
: targetDef;
124135

125-
sbJoin.Append(" {0} {1} ".Fmt(joinType, SqlTable(joinDef)));
126-
sbJoin.Append(" ON ");
127-
sbJoin.Append(sqlExpr);
128-
129-
FromExpression = fromExpr + sbJoin;
136+
FromExpression += " {0} {1} {2}".Fmt(joinType, SqlTable(joinDef), sqlExpr);
130137

131138
if (!tableDefs.Contains(sourceDef))
132139
tableDefs.Add(sourceDef);

tests/ServiceStack.OrmLite.Tests/Expression/SqlExpressionTests.cs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,5 +428,92 @@ public void Can_order_by_Joined_table()
428428
Assert.That(rows.Map(x => x.Id), Is.EqualTo(new[] { 2, 1 }));
429429
}
430430
}
431+
432+
public class CrossJoinTableA
433+
{
434+
public int Id { get; set; }
435+
public string Name { get; set; }
436+
}
437+
438+
public class CrossJoinTableB
439+
{
440+
public int Id { get; set; }
441+
public int Value { get; set; }
442+
}
443+
444+
public class CrossJoinResult
445+
{
446+
public int CrossJoinTableAId { get; set; }
447+
public string Name { get; set; }
448+
public int CrossJoinTableBId { get; set; }
449+
public int Value { get; set; }
450+
451+
public override bool Equals(object obj)
452+
{
453+
var other = obj as CrossJoinResult;
454+
if(other == null)
455+
return false;
456+
457+
return CrossJoinTableAId == other.CrossJoinTableAId && string.Equals(Name, other.Name) && CrossJoinTableBId == other.CrossJoinTableBId && Value == other.Value;
458+
}
459+
}
460+
461+
[Test]
462+
public void Can_perform_a_crossjoin_without_a_join_expression()
463+
{
464+
using(var db = OpenDbConnection())
465+
{
466+
db.DropAndCreateTable<CrossJoinTableA>();
467+
db.DropAndCreateTable<CrossJoinTableB>();
468+
469+
db.Insert(new CrossJoinTableA {Id = 1, Name = "Foo"});
470+
db.Insert(new CrossJoinTableA {Id = 2, Name = "Bar"});
471+
db.Insert(new CrossJoinTableB {Id = 5, Value = 3});
472+
db.Insert(new CrossJoinTableB {Id = 6, Value = 42});
473+
474+
var q = db.From<CrossJoinTableA>().CrossJoin<CrossJoinTableB>().OrderBy<CrossJoinTableA>(x => x.Id).ThenBy<CrossJoinTableB>(x => x.Id);
475+
var result = db.Select<CrossJoinResult>(q);
476+
477+
db.GetLastSql().Print();
478+
479+
Assert.That(result.Count, Is.EqualTo(4));
480+
var expected = new List<CrossJoinResult>
481+
{
482+
new CrossJoinResult { CrossJoinTableAId = 1, Name = "Foo", CrossJoinTableBId = 5, Value = 3 },
483+
new CrossJoinResult { CrossJoinTableAId = 1, Name = "Foo", CrossJoinTableBId = 6, Value = 42 },
484+
new CrossJoinResult { CrossJoinTableAId = 2, Name = "Bar", CrossJoinTableBId = 5, Value = 3},
485+
new CrossJoinResult { CrossJoinTableAId = 2, Name = "Bar", CrossJoinTableBId = 6, Value = 42},
486+
};
487+
Assert.That(result, Is.EquivalentTo(expected));
488+
}
489+
}
490+
491+
[Test]
492+
public void Can_perform_a_crossjoin_with_a_join_expression()
493+
{
494+
using (var db = OpenDbConnection()) {
495+
db.DropAndCreateTable<CrossJoinTableA>();
496+
db.DropAndCreateTable<CrossJoinTableB>();
497+
498+
db.Insert(new CrossJoinTableA { Id = 1, Name = "Foo" });
499+
db.Insert(new CrossJoinTableA { Id = 2, Name = "Bar" });
500+
db.Insert(new CrossJoinTableB { Id = 5, Value = 3 });
501+
db.Insert(new CrossJoinTableB { Id = 6, Value = 42 });
502+
db.Insert(new CrossJoinTableB { Id = 7, Value = 56 });
503+
504+
var q = db.From<CrossJoinTableA>().CrossJoin<CrossJoinTableB>((a, b) => b.Id > 5 && a.Id < 2).OrderBy<CrossJoinTableA>(x => x.Id).ThenBy<CrossJoinTableB>(x => x.Id);
505+
var result = db.Select<CrossJoinResult>(q);
506+
507+
db.GetLastSql().Print();
508+
509+
Assert.That(result.Count, Is.EqualTo(2));
510+
var expected = new List<CrossJoinResult>
511+
{
512+
new CrossJoinResult { CrossJoinTableAId = 1, Name = "Foo", CrossJoinTableBId = 6, Value = 42 },
513+
new CrossJoinResult { CrossJoinTableAId = 1, Name = "Foo", CrossJoinTableBId = 7, Value = 56 },
514+
};
515+
Assert.That(result, Is.EquivalentTo(expected));
516+
}
517+
}
431518
}
432519
}

0 commit comments

Comments
 (0)