diff UnitTests/Linq/ParserTest.cs @ 0:f990fcb411a9

Копия текущей версии из github
author cin
date Thu, 27 Mar 2014 21:46:09 +0400
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/UnitTests/Linq/ParserTest.cs	Thu Mar 27 21:46:09 2014 +0400
@@ -0,0 +1,966 @@
+using System;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+
+using BLToolkit.Data.Linq;
+using BLToolkit.Data.Linq.Builder;
+using BLToolkit.Data.Sql;
+
+using Data.Linq.Model;
+
+using NUnit.Framework;
+
+namespace Data.Linq
+{
+	[TestFixture]
+	public class ParserTest : TestBase
+	{
+		static ParserTest()
+		{
+			ExpressionBuilder.AddBuilder(new ContextParser());
+		}
+
+		#region IsExpressionTable
+
+		[Test]
+		public void IsExpressionTable1()
+		{
+			using (var db = new TestDbManager())
+			{
+				var ctx = db.Parent
+					.Select    (p1 => p1.ParentID)
+					.GetContext();
+
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Association).Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Object).     Result);
+				Assert.IsTrue (ctx.IsExpression(null, 0, RequestFor.Field).      Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Expression). Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.SubQuery).   Result);
+			}
+		}
+
+		[Test]
+		public void IsExpressionTable2()
+		{
+			using (var db = new TestDbManager())
+			{
+				var ctx = db.Parent
+					.Select    (p1 => p1.ParentID + 1)
+					.GetContext();
+
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Association).Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Object).     Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Field).      Result);
+				Assert.IsTrue (ctx.IsExpression(null, 0, RequestFor.Expression). Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.SubQuery).   Result);
+			}
+		}
+
+		[Test]
+		public void IsExpressionTable3()
+		{
+			using (var db = new TestDbManager())
+			{
+				var ctx = db.Parent
+					.Select    (p1 => p1)
+					.GetContext();
+
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Association).Result);
+				Assert.IsTrue (ctx.IsExpression(null, 0, RequestFor.Object).     Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Field).      Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Expression). Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.SubQuery).   Result);
+			}
+		}
+
+		#endregion
+
+		#region IsExpressionScalar
+
+		[Test]
+		public void IsExpressionScalar1()
+		{
+			using (var db = new TestDbManager())
+			{
+				var ctx = db.Parent
+					.Select    (p1 => p1.ParentID)
+					.Select    (p2 => p2)
+					.GetContext();
+
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Association).Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Object).     Result);
+				Assert.IsTrue (ctx.IsExpression(null, 0, RequestFor.Field).      Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Expression). Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.SubQuery).   Result);
+			}
+		}
+
+		[Test]
+		public void IsExpressionScalar2()
+		{
+			using (var db = new TestDbManager())
+			{
+				var ctx = db.Parent
+					.Select    (p1 => p1.ParentID + 1)
+					.Select    (p2 => p2)
+					.GetContext();
+
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Association).Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Object).     Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Field).      Result);
+				Assert.IsTrue (ctx.IsExpression(null, 0, RequestFor.Expression). Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.SubQuery).   Result);
+			}
+		}
+
+		[Test]
+		public void IsExpressionScalar3()
+		{
+			using (var db = new TestDbManager())
+			{
+				var ctx = db.Parent
+					.Select    (p1 => p1)
+					.Select    (p2 => p2)
+					.GetContext();
+
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Association).Result);
+				Assert.IsTrue (ctx.IsExpression(null, 0, RequestFor.Object).     Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Field).      Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Expression). Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.SubQuery).   Result);
+			}
+		}
+
+		[Test]
+		public void IsExpressionScalar4()
+		{
+			using (var db = new TestDbManager())
+			{
+				var ctx = db.Parent
+					.Select    (p1 => p1.ParentID + 1)
+					.Where     (p3 => p3 == 1)
+					.Select    (p2 => p2)
+					.GetContext();
+
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Association).Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Object).     Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Field).      Result);
+				Assert.IsTrue (ctx.IsExpression(null, 0, RequestFor.Expression). Result);
+				//Assert.IsTrue (ctx.IsExpression(null, 0, RequestFor.SubQuery));
+			}
+		}
+
+		[Test]
+		public void IsExpressionScalar5()
+		{
+			using (var db = new TestDbManager())
+			{
+				var ctx = db.Parent
+					.Select    (p1 => p1)
+					.Select    (p2 => p2.ParentID)
+					.GetContext();
+
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Association).Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Object).     Result);
+				Assert.IsTrue (ctx.IsExpression(null, 0, RequestFor.Field).      Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Expression). Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.SubQuery).   Result);
+			}
+		}
+
+		[Test]
+		public void IsExpressionScalar6()
+		{
+			using (var db = new TestDbManager())
+			{
+				var ctx = db.Child
+					.Select    (p => p.Parent)
+					.Select    (p => p)
+					.GetContext();
+
+				Assert.IsTrue (ctx.IsExpression(null, 0, RequestFor.Association).Result);
+				Assert.IsTrue (ctx.IsExpression(null, 0, RequestFor.Object).     Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Field).      Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Expression). Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.SubQuery).   Result);
+			}
+		}
+
+		[Test]
+		public void IsExpressionScalar7()
+		{
+			using (var db = new TestDbManager())
+			{
+				var ctx = db.Child
+					.Select    (p => p)
+					.Select    (p => p)
+					.Select    (p => p.Parent)
+					.GetContext();
+
+				Assert.IsTrue (ctx.IsExpression(null, 0, RequestFor.Association).Result);
+				Assert.IsTrue (ctx.IsExpression(null, 0, RequestFor.Object).     Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Field).      Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Expression). Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.SubQuery).   Result);
+			}
+		}
+
+		[Test]
+		public void IsExpressionScalar8()
+		{
+			using (var db = new TestDbManager())
+			{
+				var ctx = db.Child
+					.Select    (p  => p)
+					.Select    (p3 => new { p1 = new { p2 = new { p = p3 } } })
+					.Select    (p  => p.p1.p2.p.Parent)
+					.GetContext();
+
+				Assert.IsTrue (ctx.IsExpression(null, 0, RequestFor.Association).Result);
+				Assert.IsTrue (ctx.IsExpression(null, 0, RequestFor.Object).     Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Field).      Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Expression). Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.SubQuery).   Result);
+			}
+		}
+
+		[Test]
+		public void IsExpressionScalar9()
+		{
+			using (var db = new TestDbManager())
+			{
+				var ctx = db.Child
+					.Select    (p  => p)
+					.Select    (p3 => new { p1 = new { p2 = new { p = p3.Parent } } })
+					.Select    (p  => p.p1.p2.p)
+					.GetContext();
+
+				Assert.IsTrue (ctx.IsExpression(null, 0, RequestFor.Association).Result);
+				Assert.IsTrue (ctx.IsExpression(null, 0, RequestFor.Object).     Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Field).      Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Expression). Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.SubQuery).   Result);
+			}
+		}
+
+
+		[Test]
+		public void IsExpressionScalar10()
+		{
+			using (var db = new TestDbManager())
+			{
+				var ctx = db.Child
+					.Select    (p => p)
+					.Select    (p => new { p = new { p } })
+					.Select    (p => p.p)
+					.GetContext();
+
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Association).Result);
+				Assert.IsTrue (ctx.IsExpression(null, 0, RequestFor.Object).     Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Field).      Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Expression). Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.SubQuery).   Result);
+			}
+		}
+
+		[Test]
+		public void IsExpressionScalar11()
+		{
+			using (var db = new TestDbManager())
+			{
+				var ctx = db.Child
+					.Select    (p => p)
+					.Select    (p => new { p = new Child { ChildID = p.ChildID } })
+					.Select    (p => p.p)
+					.GetContext();
+
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Association).Result);
+				Assert.IsTrue (ctx.IsExpression(null, 0, RequestFor.Object).     Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Field).      Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Expression). Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.SubQuery).   Result);
+			}
+		}
+
+		#endregion
+
+		#region IsExpressionSelect
+
+		[Test]
+		public void IsExpressionSelect1()
+		{
+			using (var db = new TestDbManager())
+			{
+				var ctx = db.Parent
+					.Select    (p1 => new { p1.ParentID })
+					.Select    (p2 => p2.ParentID)
+					.GetContext();
+
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Association).Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Object).     Result);
+				Assert.IsTrue (ctx.IsExpression(null, 0, RequestFor.Field).      Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Expression). Result);
+			}
+		}
+
+		[Test]
+		public void IsExpressionSelect2()
+		{
+			using (var db = new TestDbManager())
+			{
+				var ctx = db.Parent
+					.Select    (p1 => new { p = p1.ParentID + 1 })
+					.Select    (p2 => p2.p)
+					.GetContext();
+
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Association).Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Object).     Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Field).      Result);
+				Assert.IsTrue (ctx.IsExpression(null, 0, RequestFor.Expression). Result);
+			}
+		}
+
+		[Test]
+		public void IsExpressionSelect3()
+		{
+			using (var db = new TestDbManager())
+			{
+				var ctx = db.Parent
+					.Select    (p1 => new { p1 })
+					.Select    (p2 => p2.p1)
+					.GetContext();
+
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Association).Result);
+				Assert.IsTrue (ctx.IsExpression(null, 0, RequestFor.Object).     Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Field).      Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Expression). Result);
+			}
+		}
+
+		[Test]
+		public void IsExpressionSelect4()
+		{
+			using (var db = new TestDbManager())
+			{
+				var ctx = db.Parent
+					.Select    (p1 => new { p = p1.ParentID + 1 })
+					.Where     (p3 => p3.p == 1)
+					.Select    (p2 => p2.p)
+					.GetContext();
+
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Association).Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Object).     Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Field).      Result);
+				Assert.IsTrue (ctx.IsExpression(null, 0, RequestFor.Expression). Result);
+			}
+		}
+
+		[Test]
+		public void IsExpressionSelect42()
+		{
+			using (var db = new TestDbManager())
+			{
+				var ctx = db.Parent
+					.Select    (p1 => new { p = p1.ParentID + 1 })
+					.Where     (p3 => p3.p == 1)
+					.Select    (p2 => p2)
+					.GetContext();
+
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Association).Result);
+				Assert.IsTrue (ctx.IsExpression(null, 0, RequestFor.Object).     Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Field).      Result);
+				Assert.IsTrue (ctx.IsExpression(null, 0, RequestFor.Expression). Result);
+			}
+		}
+
+		[Test]
+		public void IsExpressionSelect5()
+		{
+			using (var db = new TestDbManager())
+			{
+				var ctx = db.Parent
+					.Select    (p1 => new { p1 })
+					.Select    (p2 => p2.p1.ParentID)
+					.GetContext();
+
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Association).Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Object).     Result);
+				Assert.IsTrue (ctx.IsExpression(null, 0, RequestFor.Field).      Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Expression). Result);
+			}
+		}
+
+		[Test]
+		public void IsExpressionSelect6()
+		{
+			using (var db = new TestDbManager())
+			{
+				var ctx = db.Parent
+					.Select    (p => new { p })
+					.Select    (p => p)
+					.GetContext();
+
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Association).Result);
+				Assert.IsTrue (ctx.IsExpression(null, 0, RequestFor.Object).     Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Field).      Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Expression). Result);
+			}
+		}
+
+		[Test]
+		public void IsExpressionSelect7()
+		{
+			using (var db = new TestDbManager())
+			{
+				var ctx = db.Child
+					.Select    (p => new { p, p.Parent })
+					.Select    (p => new { p.Parent, p.p.ChildID })
+					.Select    (p => p.Parent)
+					.GetContext();
+
+				Assert.IsTrue (ctx.IsExpression(null, 0, RequestFor.Association).Result);
+				Assert.IsTrue (ctx.IsExpression(null, 0, RequestFor.Object).     Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Field).      Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Expression). Result);
+			}
+		}
+
+		[Test]
+		public void IsExpressionSelect8()
+		{
+			using (var db = new TestDbManager())
+			{
+				var ctx = db.Child
+					.Select    (p => new { p, p.Parent })
+					.Select    (p => new { p.Parent.ParentID, p.p.ChildID })
+					.Select    (p => p.ParentID)
+					.GetContext();
+
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Association).Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Object).     Result);
+				Assert.IsTrue (ctx.IsExpression(null, 0, RequestFor.Field).      Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Expression). Result);
+			}
+		}
+
+		[Test]
+		public void IsExpressionSelect9()
+		{
+			using (var db = new TestDbManager())
+			{
+				var ctx = db.GrandChild
+					.Select    (p => new { p, p.Child })
+					.Select    (p => new { p.Child.Parent.ParentID, p.p.ChildID })
+					.Select    (p => p.ParentID)
+					.GetContext();
+
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Association).Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Object).     Result);
+				Assert.IsTrue (ctx.IsExpression(null, 0, RequestFor.Field).      Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Expression). Result);
+			}
+		}
+
+		[Test]
+		public void IsExpressionSelect10()
+		{
+			using (var db = new TestDbManager())
+			{
+				var ctx = db.Parent
+					.Select    (p => p.Children.Max(c => (int?)c.ChildID) ?? p.Value1)
+					.Select    (p => p)
+					.GetContext();
+
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Association).Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Object).     Result);
+				Assert.IsFalse(ctx.IsExpression(null, 0, RequestFor.Field).      Result);
+				Assert.IsTrue (ctx.IsExpression(null, 0, RequestFor.Expression). Result);
+			}
+		}
+
+		#endregion
+
+		#region ConvertToIndexTable
+
+		[Test]
+		public void ConvertToIndexTable1()
+		{
+			using (var db = new TestDbManager())
+			{
+				var ctx = db.Parent1
+					.Select    (t => t)
+					.GetContext();
+
+				Assert.AreEqual(new[] { 0, 1 }, ctx.ConvertToIndex(null, 0, ConvertFlags.All).Select(_ => _.Index).ToArray());
+				Assert.AreEqual(new[] { 0    }, ctx.ConvertToIndex(null, 0, ConvertFlags.Key).Select(_ => _.Index).ToArray());
+			}
+		}
+
+		[Test]
+		public void ConvertToIndexTable2()
+		{
+			using (var db = new TestDbManager())
+			{
+				var ctx = db.Parent
+					.Select    (t => t)
+					.GetContext();
+
+				Assert.AreEqual(new[] { 0, 1 }, ctx.ConvertToIndex(null, 0, ConvertFlags.All).Select(_ => _.Index).ToArray());
+				Assert.AreEqual(new[] { 0, 1 }, ctx.ConvertToIndex(null, 0, ConvertFlags.Key).Select(_ => _.Index).ToArray());
+			}
+		}
+
+		[Test]
+		public void ConvertToIndexTable3()
+		{
+			using (var db = new TestDbManager())
+			{
+				var ctx = db.Parent
+					.Select    (t => t.ParentID)
+					.GetContext();
+
+				Assert.AreEqual(new[] { 0 }, ctx.ConvertToIndex(null, 0, ConvertFlags.Field).Select(_ => _.Index).ToArray());
+			}
+		}
+
+		[Test]
+		public void ConvertToIndexTable4()
+		{
+			using (var db = new TestDbManager())
+			{
+				var ctx = db.Parent
+					.Select    (t => t.Value1)
+					.GetContext();
+
+				Assert.AreEqual(new[] { 0 }, ctx.ConvertToIndex(null, 0, ConvertFlags.Field).Select(_ => _.Index).ToArray());
+			}
+		}
+
+		[Test]
+		public void ConvertToIndexTable5()
+		{
+			using (var db = new TestDbManager())
+			{
+				var ctx = db.Parent
+					.Select    (t => new { t = new { t } })
+					.Select    (t => t.t.t.ParentID)
+					.Select    (t => t)
+					.GetContext();
+
+				Assert.AreEqual(new[] { 0 }, ctx.ConvertToIndex(null, 0, ConvertFlags.Field).Select(_ => _.Index).ToArray());
+			}
+		}
+
+		#endregion
+
+		#region ConvertToIndex
+
+		[Test]
+		public void ConvertToIndexScalar1()
+		{
+			using (var db = new TestDbManager())
+			{
+				var ctx = db.Parent
+					.Select    (p1 => p1.ParentID)
+					.Select    (p2 => p2)
+					.GetContext();
+
+				Assert.AreEqual(new[] { 0 }, ctx.ConvertToIndex(null, 0, ConvertFlags.Field).Select(_ => _.Index).ToArray());
+			}
+		}
+
+		[Test]
+		public void ConvertToIndexScalar2()
+		{
+			using (var db = new TestDbManager())
+			{
+				var ctx = db.Parent
+					.Select    (p1 => p1.ParentID + 1)
+					.Select    (p2 => p2)
+					.GetContext();
+
+				Assert.AreEqual(new[] { 0 }, ctx.ConvertToIndex(null, 0, ConvertFlags.Field).Select(_ => _.Index).ToArray());
+			}
+		}
+
+		[Test]
+		public void ConvertToIndexScalar3()
+		{
+			using (var db = new TestDbManager())
+			{
+				var ctx = db.Parent
+					.Select    (p1 => p1.ParentID + 1)
+					.Where     (p3 => p3 == 1)
+					.Select    (p2 => p2)
+					.GetContext();
+
+				Assert.AreEqual(new[] { 0 }, ctx.ConvertToIndex(null, 0, ConvertFlags.Field).Select(_ => _.Index).ToArray());
+			}
+		}
+
+		[Test]
+		public void ConvertToIndexScalar4()
+		{
+			using (var db = new TestDbManager())
+			{
+				var ctx = db.Parent
+					.Select    (p1 => new { p = new { p = p1.ParentID } })
+					.Select    (p2 => p2.p.p)
+					.GetContext();
+
+				Assert.AreEqual(new[] { 0 }, ctx.ConvertToIndex(null, 0, ConvertFlags.Field).Select(_ => _.Index).ToArray());
+			}
+		}
+
+		[Test]
+		public void ConvertToIndexJoin1()
+		{
+			using (var db = new TestDbManager())
+			{
+				var q2 =
+					from gc1 in db.GrandChild
+						join max in
+							from gch in db.GrandChild
+							group gch by gch.ChildID into g
+							select g.Max(c => c.GrandChildID)
+						on gc1.GrandChildID equals max
+					select gc1;
+
+				var result =
+					from ch in db.Child
+						join p   in db.Parent on ch.ParentID equals p.ParentID
+						join gc2 in q2        on p.ParentID  equals gc2.ParentID into g
+						from gc3 in g.DefaultIfEmpty()
+				select gc3;
+
+				var ctx = result.GetContext();
+				var idx = ctx.ConvertToIndex(null, 0, ConvertFlags.Key);
+
+				Assert.AreEqual(new[] { 0, 1, 2 }, idx.Select(_ => _.Index).ToArray());
+			}
+		}
+
+		[Test]
+		public void ConvertToIndexJoin2()
+		{
+			using (var db = new TestDbManager())
+			{
+				var result =
+					from ch in db.Child
+						join gc2 in db.GrandChild on ch.ParentID  equals gc2.ParentID into g
+						from gc3 in g.DefaultIfEmpty()
+					select gc3;
+
+				var ctx = result.GetContext();
+				var idx = ctx.ConvertToIndex(null, 0, ConvertFlags.Key);
+
+				Assert.AreEqual(new[] { 0, 1, 2 }, idx.Select(_ => _.Index).ToArray());
+
+				idx = ctx.ConvertToIndex(null, 0, ConvertFlags.All);
+
+				Assert.AreEqual(new[] { 0, 1, 2 }, idx.Select(_ => _.Index).ToArray());
+			}
+		}
+
+		#endregion
+
+		#region ConvertToSql
+
+		[Test]
+		public void ConvertToSql1()
+		{
+			using (var db = new TestDbManager())
+			{
+				var ctx = db.Parent
+					.Select    (p1 => new { p1.ParentID })
+					.Select    (p2 => p2.ParentID)
+					.GetContext();
+
+				var sql = ctx.ConvertToSql(null, 0, ConvertFlags.Field);
+
+				Assert.AreEqual        (1, sql.Length);
+				Assert.IsAssignableFrom(typeof(SqlField), sql[0].Sql);
+				Assert.AreEqual        ("ParentID", ((SqlField)sql[0].Sql).Name);
+			}
+		}
+
+		[Test]
+		public void ConvertToSql2()
+		{
+			using (var db = new TestDbManager())
+			{
+				var ctx = db.Parent
+					.Select    (p1 => new { p = p1.ParentID + 1 })
+					.Select    (p2 => p2.p)
+					.GetContext();
+
+				var sql = ctx.ConvertToSql(null, 0, ConvertFlags.Field);
+
+				Assert.AreEqual        (1, sql.Length);
+				Assert.IsAssignableFrom(typeof(SqlBinaryExpression), sql[0].Sql);
+			}
+		}
+
+		[Test]
+		public void ConvertToSql3()
+		{
+			using (var db = new TestDbManager())
+			{
+				var ctx = db.Parent
+					.Select    (p1 => new { p = p1.ParentID + 1 })
+					.Where     (p3 => p3.p == 1)
+					.Select    (p2 => p2.p)
+					.GetContext();
+
+				var sql = ctx.ConvertToSql(null, 0, ConvertFlags.Field);
+
+				Assert.AreEqual        (1, sql.Length);
+				Assert.IsAssignableFrom(typeof(SqlQuery.Column), sql[0].Sql);
+			}
+		}
+
+		[Test]
+		public void ConvertToSql4()
+		{
+			using (var db = new TestDbManager())
+			{
+				var ctx = db.Parent
+					.Select    (p1 => new { p1 })
+					.Select    (p2 => p2.p1.ParentID)
+					.GetContext();
+
+				var sql = ctx.ConvertToSql(null, 0, ConvertFlags.Field);
+
+				Assert.AreEqual        (1, sql.Length);
+				Assert.IsAssignableFrom(typeof(SqlField), sql[0].Sql);
+				Assert.AreEqual        ("ParentID", ((SqlField)sql[0].Sql).Name);
+			}
+		}
+
+		[Test]
+		public void ConvertToSql5()
+		{
+			using (var db = new TestDbManager())
+			{
+				var ctx = db.Child
+					.Select    (p => new { p, p.Parent })
+					.Select    (p => new { p.Parent.ParentID, p.p.ChildID })
+					.Select    (p => p.ParentID)
+					.GetContext();
+
+				var sql = ctx.ConvertToSql(null, 0, ConvertFlags.Field);
+
+				Assert.AreEqual        (1, sql.Length);
+				Assert.IsAssignableFrom(typeof(SqlField), sql[0].Sql);
+				Assert.AreEqual        ("ParentID", ((SqlField)sql[0].Sql).Name);
+			}
+		}
+
+		#endregion
+
+		#region SqlTest
+
+		[Test]
+		public void Join1()
+		{
+			using (var db = new TestDbManager())
+			{
+				var q =
+					from t in
+						from ch in db.Child
+							join p in db.Parent on ch.ParentID equals p.ParentID
+						select ch.ParentID + p.ParentID
+					where t > 2
+					select t;
+
+				var ctx = q.GetContext();
+				ctx.BuildExpression(null, 0);
+
+				Assert.AreEqual(1, ctx.SqlQuery.Select.Columns.Count);
+			}
+		}
+
+		[Test]
+		public void Join2()
+		{
+			using (var db = new TestDbManager())
+			{
+				var q =
+					from t in
+						from ch in db.Child
+							join p in db.Parent on ch.ParentID equals p.ParentID
+						select new { ID = ch.ParentID + p.ParentID }
+					where t.ID > 2
+					select t;
+
+				var ctx = q.GetContext();
+				ctx.BuildExpression(null, 0);
+
+				Assert.AreEqual(2, ctx.SqlQuery.Select.Columns.Count);
+			}
+		}
+
+		public class MyClass
+		{
+			public int ID;
+		}
+
+		[Test]
+		public void Join3()
+		{
+			using (var db = new TestDbManager())
+			{
+				var q =
+					from p in db.Parent
+					join j in db.Child on p.ParentID equals j.ParentID
+					select p;
+
+				var ctx = q.GetContext();
+				ctx.BuildExpression(null, 0);
+
+				Assert.AreEqual(2, ctx.SqlQuery.Select.Columns.Count);
+			}
+		}
+
+		[Test]
+		public void Join4()
+		{
+			using (var db = new TestDbManager())
+			{
+				var q =
+					from p in db.Parent
+					select new { ID = new MyClass { ID = p.ParentID } }
+					into p
+					join j in
+						from c in db.Child
+						select new { ID = new MyClass { ID = c.ParentID } }
+						on p.ID.ID equals j.ID.ID
+					where p.ID.ID == 1
+					select p;
+
+				var ctx = q.GetContext();
+				ctx.BuildExpression(null, 0);
+
+				Assert.AreEqual(1, ctx.SqlQuery.Select.Columns.Count);
+			}
+		}
+
+		[Test]
+		public void Join5()
+		{
+			using (var db = new TestDbManager())
+			{
+				var q =
+					from p in db.Parent
+						join c in db.Child      on p.ParentID equals c.ParentID
+						join g in db.GrandChild on p.ParentID equals g.ParentID
+					select new { p, c, g } into x
+					select x.c.ParentID;
+
+				var ctx = q.GetContext();
+				var sql = ctx.ConvertToSql(null, 0, ConvertFlags.All);
+
+				Assert.AreEqual(1, sql.Length);
+			}
+		}
+
+		[Test]
+		public void Join6([IncludeDataContexts("Sql2008", "Sql2012")] string context)
+		{
+			using (var db = new TestDbManager(context))
+			{
+				var q =
+					from g in db.GrandChild
+					join p in db.Parent4 on g.Child.ParentID equals p.ParentID
+					select g;
+
+				var ctx = q.GetContext();
+
+				ctx.BuildExpression(null, 0);
+
+				var sql = db.GetSqlText(ctx.SqlQuery);
+
+				CompareSql(sql, @"
+					SELECT
+						[g].[ParentID],
+						[g].[ChildID],
+						[g].[GrandChildID]
+					FROM
+						[GrandChild] [g]
+							LEFT JOIN [Child] [t1] ON [g].[ParentID] = [t1].[ParentID] AND [g].[ChildID] = [t1].[ChildID]
+							INNER JOIN [Parent] [p] ON [t1].[ParentID] = [p].[ParentID]");
+			}
+		}
+
+		#endregion
+	}
+
+	class ContextParser : ISequenceBuilder
+	{
+		public int BuildCounter { get; set; }
+
+		public bool CanBuild(ExpressionBuilder builder, BuildInfo buildInfo)
+		{
+			var call = buildInfo.Expression as MethodCallExpression;
+			return call != null && call.Method.Name == "GetContext";
+		}
+
+		public IBuildContext BuildSequence(ExpressionBuilder builder, BuildInfo buildInfo)
+		{
+			var call = (MethodCallExpression)buildInfo.Expression;
+			return new Context(builder.BuildSequence(new BuildInfo(buildInfo, call.Arguments[0])));
+		}
+
+		public SequenceConvertInfo Convert(ExpressionBuilder builder, BuildInfo buildInfo, ParameterExpression param)
+		{
+			return null;
+		}
+
+		public bool IsSequence(ExpressionBuilder builder, BuildInfo buildInfo)
+		{
+			return builder.IsSequence(new BuildInfo(buildInfo, ((MethodCallExpression)buildInfo.Expression).Arguments[0]));
+		}
+
+		public class Context : PassThroughContext
+		{
+			public Context(IBuildContext context) : base(context)
+			{
+			}
+
+			public override void BuildQuery<T>(Query<T> query, ParameterExpression queryParameter)
+			{
+				query.GetElement = (ctx,db,expr,ps) => this;
+			}
+		}
+	}
+
+	static class Extensions
+	{
+		public static ContextParser.Context GetContext<T>(this IQueryable<T> source)
+		{
+			if (source == null) throw new ArgumentNullException("source");
+
+			return source.Provider.Execute<ContextParser.Context>(
+				Expression.Call(
+					null,
+					((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod(new[] { typeof(T) }),
+					new[] { source.Expression }));
+		}
+
+		static public Expression Unwrap(this Expression ex)
+		{
+			if (ex == null)
+				return null;
+
+			switch (ex.NodeType)
+			{
+				case ExpressionType.Quote          :
+				case ExpressionType.Convert        :
+				case ExpressionType.ConvertChecked : return ((UnaryExpression)ex).Operand.Unwrap();
+			}
+
+			return ex;
+		}
+	}
+}