protected virtual Expression VisitSelect(SelectExpression select)
		{
			var project = (LambdaExpression)this.Visit(select.Projection);
			var from = (FromExpression)this.Visit(select.From);
			var where = (WhereExpression)this.Visit(select.Where);
			var take = this.Visit(select.Take);
			var orderBy = this.VisitOrderBy(select.OrderBy);

			if (project == select.Projection &&
				from == select.From &&
				where == select.Where &&
				take == select.Take &&
				orderBy == select.OrderBy)
				return select;
			return CompleteExpression.Select(select.Alias, project, from, orderBy, where, select.IsProjection);
		}
		protected override Expression VisitSelect(SelectExpression select)
		{
			return select;
		}
		protected virtual Expression VisitSelect(SelectExpression select)
		{
			this.Write(".Select(");
			this.WriteLine(Indentation.Inner);

			this.Write(".Project(");
			this.WriteLine(Indentation.Inner);
			this.Visit(select.Projection);
			this.WriteLine(Indentation.Outer);
			this.Write("),");
			this.WriteLine(Indentation.Same);

			this.Visit(select.From);

			if (select.Where != null)
			{
				this.Write(",");
				this.WriteLine(Indentation.Same);
				this.Visit(select.Where);
				this.WriteLine(Indentation.Outer);
			}
			this.Write(") as ");
			this.Write(select.Alias);
			return select;
		}
		protected override Expression VisitSelect(SelectExpression select)
		{
			var topLevel = isTopLevel;
			isTopLevel = false;

			sb.Append("select ");

			if (select.Take != null)
			{
				sb.Append("top (");
				this.Visit(select.Take);
				sb.Append(") ");
			}

			var add_comma = false;
			foreach (var c in select.Columns)
			{
				if (add_comma)
					sb.Append(", ");
				else
					add_comma = true;

				this.Visit(c.Declaration);
				sb.AppendFormat(" as [{0}]", c.Name);
			}

			this.AppendNewLine(Indentation.Same);
			sb.Append("from ");
			this.Visit(select.From);
			this.Visit(select.Where);

			// can only write order by if top level or if take != null
			if (topLevel || select.Take != null)
				// can only write order by if order by is not null
				if (select.OrderBy != null)
				{
					if (select.OrderBy.SortColumns.Count == 0 && select.OrderBy.SecondarySortingClause == null)
						throw new InvalidOperationException("How did we end up here?");

					sb.Append("order by ");
					this.VisitOrderBy(select.OrderBy);
				}

			return select;
		}
		private IEnumerable<Expression> GetDistinctColumns(SelectExpression select, Expression expression)
		{
			fromExpressions.Push(select.From);
			var columns = GetDistinctColumns(expression);
			fromExpressions.Pop();
			return columns;
		}
		private OrderByClause BuildNestedOrderBy(SelectExpression select, OrderByClause orderBy)
		{
			if (orderBy == null)
				return null;

			var newOrderBy = new OrderByClause(BuildNestedOrderBy(select, orderBy.SecondarySortingClause));
			var columns = orderBy.SortColumns;
			var unmatchedColumns = new List<Expression>();
			foreach (var col in columns)
			{
				Expression expr = select.TryFindColumn(col.Item1);
				if (expr == null)
					expr = select.AddUnmappedColumn(col.Item1);
				newOrderBy.AppendColumn(Tuple.Create(expr, col.Item2));
			}
			return newOrderBy;
		}
		private OrderByClause BuildNestedOrderBy(SelectExpression select)
		{
			return BuildNestedOrderBy(select, select.OrderBy);
		}
		private SelectExpression BuildNestedSelect(SelectExpression select)
		{
			var orderBy = BuildNestedOrderBy(select);

			var from = CompleteExpression.From(select);
			var projection = ColumnReplacer.ReplaceColumns(select.Projection) as LambdaExpression;
			select = CompleteExpression.Select(
				GetNextAlias(),
				projection,
				from,
				orderBy,
				isProjection: false);
			return select;
		}