예제 #1
0
        public static void EnumNodes(Expression root, EnumNodesCallBack callback, EnumNodesCallBack postCallback, params object[] args)
        {
            switch( root.NodeType )
            {
                case NodeType.Literal:
                case NodeType.Parameter:
                case NodeType.Context:
                {
                    if( callback != null )
                    {
                        callback(root, args);
                    }
                    if( postCallback != null )
                    {
                        postCallback(root, args);
                    }
                    return;
                }
                case NodeType.Binary:
                {
                    if( callback == null || callback(root, args) )
                    {
                        Binary node = (Binary)root;
                        Expression.EnumNodes(node.Left, callback, postCallback, args);
                        Expression.EnumNodes(node.Right, callback, postCallback, args);

                        if( postCallback != null )
                        {
                            postCallback(root, args);
                        }
                    }
                    return;
                }
                case NodeType.Unary:
                {
                    if( callback == null || callback(root, args) )
                    {
                        Unary node = (Unary)root;
                        Expression.EnumNodes(node.Operand, callback, postCallback, args);

                        if( postCallback != null )
                        {
                            postCallback(root, args);
                        }
                    }
                    return;
                }
                case NodeType.Axis:
                case NodeType.Filter:
                {
                    if( callback == null || callback(root, args) )
                    {
                        Filter node = (Filter)root;
                        Expression.EnumNodes(node.Source, callback, postCallback, args);
                        Expression.EnumNodes(node.Constraint, callback, postCallback, args);

                        if( postCallback != null )
                        {
                            postCallback(root, args);
                        }
                    }
                    return;
                }
                case NodeType.Property:
                {
                    if( callback == null || callback(root, args) )
                    {
                        Property node = (Property)root;
                        Expression.EnumNodes(node.Source, callback, postCallback, args);

                        if( postCallback != null )
                        {
                            postCallback(root, args);
                        }
                    }
                    return;
                }
                case NodeType.Parent:
                {
                    if( callback == null || callback(root, args) )
                    {
                        Parent node = (Parent)root;
                        Expression.EnumNodes(node.Source, callback, postCallback, args);

                        if( postCallback != null )
                        {
                            postCallback(root, args);
                        }
                    }
                    return;
                }
                case NodeType.Function:
                {
                    if( callback == null || callback(root, args) )
                    {
                        Function node = (Function)root;
                        for( int i = 0; i < node.Params.Length; i++ )
                        {
                            Expression.EnumNodes(node.Params[i], callback, postCallback, args);
                        }

                        if( postCallback != null )
                        {
                            postCallback(root, args);
                        }
                    }
                    return;
                }
                case NodeType.TypeFilter:
                {
                    if( callback == null || callback(root, args) )
                    {
                        TypeFilter node = (TypeFilter)root;
                        Expression.EnumNodes(node.Source, callback, postCallback, args);

                        if( postCallback != null )
                        {
                            postCallback(root, args);
                        }
                    }
                    return;
                }
                case NodeType.Empty:
                {
                    return;
                }
                case NodeType.OrderBy:
                {
                    if( callback == null || callback(root, args) )
                    {
                        OrderBy node = (OrderBy)root;
                        Expression.EnumNodes(node.Source, callback, postCallback, args);

                        if( postCallback != null )
                        {
                            postCallback(root, args);
                        }
                    }
                    return;
                }
                default:
                {
                    throw new NotSupportedException("Expression type '" + root.GetType() + "' is not currently supported.");
                }
            }
        }
예제 #2
0
		private void WriteSqlQuery(TextWriter w, Expression expr)
		{
			switch( expr.NodeType )
			{
				case NodeType.Property:
				{
					Property property = (Property)expr;
					
					EntityMap map = _maps[property.OwnerClass];
					FieldMap field = map.GetFieldMap(property.Name);
					if( field == null )
					{
						throw new Exception("Property '" + property.Name + "' could not be found for entity type '" + property.OwnerClass + "'.");
					}

					string alias;
					if( !field.IsLookup )
					{
						alias = GetAlias(property);
					}
					else // lookup field
					{
						LookupMap lookup = (LookupMap)field;
						alias = (string)_lookupFieldToAliasMap[lookup.FieldAlias];
					}
					if( alias == null )
					{
						throw new Exception("Could not find table alias for property '" + property.Name + "' in entity '" + property.OwnerClass + "'.");
					}

					WriteSqlColumn(w, alias, field.Field);
					return;
				}
				case NodeType.Parameter:
				{
					Query.Parameter node = (Query.Parameter)expr;

					string name;
					if( !_provider.NoNamedParameters )
					{
						name = _provider.ParameterPrefix + "P" + node.Ordinal;
					}
					else
					{
						name = "?";
					}

					_parameterTable.Add( new OPathParameter(name, node.ValueType) );
	
					w.Write(name);
					return;
				}
				case NodeType.Literal:
				{
					Literal node = (Literal)expr;
					WriteLiteral(w, node.Value);
					return;
				}
				case NodeType.Binary:
				{
					Binary node = (Binary)expr;

					bool isFormat;
					string keyword = GetSqlKeyword(node.Operator, out isFormat);

					if( isFormat )
					{
						WriteFormat(w, keyword, node.Left, node.Right);
					}
					else
					{
						w.Write('(');
						WriteSqlQuery(w, node.Left);
						w.Write(' ');
						w.Write(keyword);
						w.Write(' ');
						WriteSqlQuery(w, node.Right);
						w.Write(')');
					}
					return;
				}
				case NodeType.Unary:
				{
					Unary node = (Unary)expr;

					if( node.Operator == UnaryOperator.Exists )
					{
						w.Write(GetSqlKeyword(node.Operator));
						w.Write("\n(\n");
						WriteSqlQuery(w, node.Operand);
						w.Write("\n)");
					}
					else if( node.Operator == UnaryOperator.IsNull )
					{
						w.Write('(');
						WriteSqlQuery(w, node.Operand);
						w.Write(' ');
						w.Write(GetSqlKeyword(node.Operator));
						w.Write(')');
					}
					else
					{
						w.Write(GetSqlKeyword(node.Operator));
						w.Write('(');
						WriteSqlQuery(w, node.Operand);
						w.Write(')');
					}
					return;
				}
				case NodeType.Filter:
				{
					Filter filter = (Filter)expr;

					// the only supported filters are the root and ones directly below an exists node
					if( filter.Owner != null && (filter.Owner.NodeType != NodeType.Unary || (filter.Owner as Unary).Operator != UnaryOperator.Exists) )
					{
						throw new NotSupportedException("Filter with source type '" + filter.Source.NodeType + "' was not expected.");
					}

					EntityMap entity = _maps[filter.ValueType];
					string entityAlias = GetNextAlias();
					filter.Alias = entityAlias;

					bool whereStarted = false;
					if( filter.Source.NodeType == NodeType.TypeFilter ) // root filter/outer select
					{
						Join[] lookupJoins;
						WriteSelectClause(w, entity, entityAlias, out lookupJoins);

						Join[] joins;
						if( _orderByNode != null )
						{
							Join[] orderByJoins = GetJoinsFromOrderBy(entity, entityAlias, _orderByNode);
							joins = new Join[lookupJoins.Length + orderByJoins.Length];
							lookupJoins.CopyTo(joins, 0);
							orderByJoins.CopyTo(joins, lookupJoins.Length);
						}
						else
						{
							joins = lookupJoins;
						}

						WriteFromClause(w, entity.Table, entityAlias, joins);
						if( entity.BaseEntity != null )
						{
							WriteSubEntityConstraint(w, entityAlias, entity.TypeField, entity.TypeValue, ref whereStarted);
						}
						WriteFilterConstraints(w, filter, whereStarted);
						
						// add the default sort order if the entity has one defined but the query is not ordered
						if( _orderByNode == null && entity.SortOrder != null && entity.SortOrder.Length > 0 )
						{
							w.Write("\nORDER BY ");
							w.Write(entity.SortOrder);
						}
					}
					else if( filter.Source.NodeType == NodeType.Property || filter.Source.NodeType == NodeType.Filter ) // nested filter/subquery
					{
						Expression source = filter.Source;
						while( source.NodeType == NodeType.Filter )
						{
							source = (source as Filter).Source;
						}
						if( source.NodeType != NodeType.Property ) throw new Exception("Could not find source property for filter.");

						Property relProperty = (Property)source;
						if( !relProperty.IsRelational ) throw new Exception("Expected source property for Filter node to be relational.");

						RelationMap relation = relProperty.RelationMap;
						EntityMap sourceEntity = _maps[relProperty.OwnerClass];
						string sourceAlias = GetAlias(relProperty);

						string relationConstraint = ReplaceTableNameWithAlias(relation.Filter, sourceEntity.Table, sourceAlias);
						relationConstraint = ReplaceTableNameWithAlias(relationConstraint, entity.Table, entityAlias);
						
						switch( relation.Relationship )
						{
							case Relationship.Parent:
							{
								w.Write("SELECT *");
								WriteFromClause(w, entity.Table, entityAlias, null);
								if( entity.BaseEntity != null )
								{
									WriteSubEntityConstraint(w, entityAlias, entity.TypeField, entity.TypeValue, ref whereStarted);
								}
								WriteJoinCondition(w, sourceAlias, relation.Fields, entityAlias, GetFieldNames(entity.KeyFields), relationConstraint, true, ref whereStarted);
								WriteFilterConstraints(w, filter, whereStarted);
								break;
							}
							case Relationship.Child:
							{
								w.Write("SELECT *");
								WriteFromClause(w, entity.Table, entityAlias, null);
								if( entity.BaseEntity != null )
								{
									WriteSubEntityConstraint(w, entityAlias, entity.TypeField, entity.TypeValue, ref whereStarted);
								}
								WriteJoinCondition(w, sourceAlias, GetFieldNames(sourceEntity.KeyFields), entityAlias, relation.Fields, relationConstraint, true, ref whereStarted);
								WriteFilterConstraints(w, filter, whereStarted);
								break;
							}
							case Relationship.Many:
							{
								ManyMap junctionMap = (ManyMap)relation;
								string junctionAlias = GetNextAlias();

								relationConstraint = ReplaceTableNameWithAlias(relationConstraint, junctionMap.Table, junctionAlias);								

								// write the junction and child table, inner joined together in one select
								w.Write("SELECT *");
								WriteFromClause(w, junctionMap.Table, junctionAlias, null);
								w.Write(", ");
								WriteSqlTable(w, entity.Table, entityAlias);
								if( entity.BaseEntity != null )
								{
									WriteSubEntityConstraint(w, entityAlias, entity.TypeField, entity.TypeValue, ref whereStarted);
								}
								WriteJoinCondition(w, sourceAlias, GetFieldNames(sourceEntity.KeyFields), junctionAlias, junctionMap.Source, null, true, ref whereStarted);
								WriteJoinCondition(w, junctionAlias, junctionMap.Dest, entityAlias, GetFieldNames(entity.KeyFields), relationConstraint, true, ref whereStarted);
								WriteFilterConstraints(w, filter, whereStarted);

								break;
							}
							default:
							{
								throw new NotSupportedException("Relationship type '" + relation.Relationship + "' is not supported.");
							}
						}
					}
					else
					{
						throw new NotImplementedException("Filter with source type '" + filter.Source.NodeType + "' was not expected.");
					}
					return;
				}
				case NodeType.Function:
				{
					Function node = (Function)expr;

					bool isFormat;
					string keyword = GetSqlKeyword(node.Operator, out isFormat);
					if( isFormat )
					{
						WriteFormat(w, keyword, node.Params);
					}
					else
					{
						if( node.Operator != FunctionOperator.In )
						{
							throw new NotSupportedException("Function operator '" + node.Operator + "' was not expected.");
						}
						w.Write('(');
						WriteSqlQuery(w, node.Params[0]);
						w.Write(' ');
						w.Write(keyword);
						w.Write(" (");
						for( int i = 1; i < node.Params.Length; i++ )
						{
							if( i > 1 ) w.Write(", ");
							WriteSqlQuery(w, node.Params[i]);
						}
						w.Write("))");
					}
					return;
				}
				case NodeType.OrderBy:
				{
					OrderBy node = (OrderBy)expr;
					_orderByNode = node;

					Filter filter = (node.Source as Filter);
					if( filter == null )
					{
						throw new Exception("Expected source of OrderBy node to be Filter.");
					}

					WriteSqlQuery(w, filter);

					w.Write("\nORDER BY ");
					for( int i = 0; i < node.OrderByItems.Count; i++ )
					{
						OrderByItem item = node.OrderByItems[i];

						FieldMap field = item.FieldInfo;
						if( field == null )
						{
							throw new Exception("Field info does not exists for OrderByItem '" + item.Item + "'.");
						}

						string alias = (item.Join == null) ? filter.Alias : item.Join.Alias;

						if( i > 0 )	w.Write(", ");
						WriteSqlColumn(w, alias, field.Field);
						w.Write( (item.Ascending) ? " ASC" : " DESC");
					}
					return;
				}
				case NodeType.Empty:
				{
					return;
				}
				default:
				{
					throw new NotSupportedException("Expression type '" + expr.GetType() + "' is not currently supported.");
				}
			}
		}
예제 #3
0
        public static void EnumNodes(Expression root, EnumNodesCallBack callback, EnumNodesCallBack postCallback, params object[] args)
        {
            switch (root.NodeType)
            {
            case NodeType.Literal:
            case NodeType.Parameter:
            case NodeType.Context:
            {
                if (callback != null)
                {
                    callback(root, args);
                }
                if (postCallback != null)
                {
                    postCallback(root, args);
                }
                return;
            }

            case NodeType.Binary:
            {
                if (callback == null || callback(root, args))
                {
                    Binary node = (Binary)root;
                    Expression.EnumNodes(node.Left, callback, postCallback, args);
                    Expression.EnumNodes(node.Right, callback, postCallback, args);

                    if (postCallback != null)
                    {
                        postCallback(root, args);
                    }
                }
                return;
            }

            case NodeType.Unary:
            {
                if (callback == null || callback(root, args))
                {
                    Unary node = (Unary)root;
                    Expression.EnumNodes(node.Operand, callback, postCallback, args);

                    if (postCallback != null)
                    {
                        postCallback(root, args);
                    }
                }
                return;
            }

            case NodeType.Axis:
            case NodeType.Filter:
            {
                if (callback == null || callback(root, args))
                {
                    Filter node = (Filter)root;
                    Expression.EnumNodes(node.Source, callback, postCallback, args);
                    Expression.EnumNodes(node.Constraint, callback, postCallback, args);

                    if (postCallback != null)
                    {
                        postCallback(root, args);
                    }
                }
                return;
            }

            case NodeType.Property:
            {
                if (callback == null || callback(root, args))
                {
                    Property node = (Property)root;
                    Expression.EnumNodes(node.Source, callback, postCallback, args);

                    if (postCallback != null)
                    {
                        postCallback(root, args);
                    }
                }
                return;
            }

            case NodeType.Parent:
            {
                if (callback == null || callback(root, args))
                {
                    Parent node = (Parent)root;
                    Expression.EnumNodes(node.Source, callback, postCallback, args);

                    if (postCallback != null)
                    {
                        postCallback(root, args);
                    }
                }
                return;
            }

            case NodeType.Function:
            {
                if (callback == null || callback(root, args))
                {
                    Function node = (Function)root;
                    for (int i = 0; i < node.Params.Length; i++)
                    {
                        Expression.EnumNodes(node.Params[i], callback, postCallback, args);
                    }

                    if (postCallback != null)
                    {
                        postCallback(root, args);
                    }
                }
                return;
            }

            case NodeType.TypeFilter:
            {
                if (callback == null || callback(root, args))
                {
                    TypeFilter node = (TypeFilter)root;
                    Expression.EnumNodes(node.Source, callback, postCallback, args);

                    if (postCallback != null)
                    {
                        postCallback(root, args);
                    }
                }
                return;
            }

            case NodeType.Empty:
            {
                return;
            }

            case NodeType.OrderBy:
            {
                if (callback == null || callback(root, args))
                {
                    OrderBy node = (OrderBy)root;
                    Expression.EnumNodes(node.Source, callback, postCallback, args);

                    if (postCallback != null)
                    {
                        postCallback(root, args);
                    }
                }
                return;
            }

            default:
            {
                throw new NotSupportedException("Expression type '" + root.GetType() + "' is not currently supported.");
            }
            }
        }
예제 #4
0
        private void WriteSqlQuery(TextWriter w, Expression expr)
        {
            switch (expr.NodeType)
            {
            case NodeType.Property:
            {
                Property property = (Property)expr;

                EntityMap map   = _maps[property.OwnerClass];
                FieldMap  field = map.GetFieldMap(property.Name);
                if (field == null)
                {
                    throw new Exception("Property '" + property.Name + "' could not be found for entity type '" + property.OwnerClass + "'.");
                }

                string alias;
                if (!field.IsLookup)
                {
                    alias = GetAlias(property);
                }
                else                         // lookup field
                {
                    LookupMap lookup = (LookupMap)field;
                    alias = (string)_lookupFieldToAliasMap[lookup.FieldAlias];
                }
                if (alias == null)
                {
                    throw new Exception("Could not find table alias for property '" + property.Name + "' in entity '" + property.OwnerClass + "'.");
                }

                WriteSqlColumn(w, alias, field.Field);
                return;
            }

            case NodeType.Parameter:
            {
                Query.Parameter node = (Query.Parameter)expr;

                string name;
                if (!_provider.NoNamedParameters)
                {
                    name = _provider.ParameterPrefix + "P" + node.Ordinal;
                }
                else
                {
                    name = "?";
                }

                _parameterTable.Add(new OPathParameter(name, node.ValueType));

                w.Write(name);
                return;
            }

            case NodeType.Literal:
            {
                Literal node = (Literal)expr;
                WriteLiteral(w, node.Value);
                return;
            }

            case NodeType.Binary:
            {
                Binary node = (Binary)expr;

                bool   isFormat;
                string keyword = GetSqlKeyword(node.Operator, out isFormat);

                if (isFormat)
                {
                    WriteFormat(w, keyword, node.Left, node.Right);
                }
                else
                {
                    w.Write('(');
                    WriteSqlQuery(w, node.Left);
                    w.Write(' ');
                    w.Write(keyword);
                    w.Write(' ');
                    WriteSqlQuery(w, node.Right);
                    w.Write(')');
                }
                return;
            }

            case NodeType.Unary:
            {
                Unary node = (Unary)expr;

                if (node.Operator == UnaryOperator.Exists)
                {
                    w.Write(GetSqlKeyword(node.Operator));
                    w.Write("\n(\n");
                    WriteSqlQuery(w, node.Operand);
                    w.Write("\n)");
                }
                else if (node.Operator == UnaryOperator.IsNull)
                {
                    w.Write('(');
                    WriteSqlQuery(w, node.Operand);
                    w.Write(' ');
                    w.Write(GetSqlKeyword(node.Operator));
                    w.Write(')');
                }
                else
                {
                    w.Write(GetSqlKeyword(node.Operator));
                    w.Write('(');
                    WriteSqlQuery(w, node.Operand);
                    w.Write(')');
                }
                return;
            }

            case NodeType.Filter:
            {
                Filter filter = (Filter)expr;

                // the only supported filters are the root and ones directly below an exists node
                if (filter.Owner != null && (filter.Owner.NodeType != NodeType.Unary || (filter.Owner as Unary).Operator != UnaryOperator.Exists))
                {
                    throw new NotSupportedException("Filter with source type '" + filter.Source.NodeType + "' was not expected.");
                }

                EntityMap entity      = _maps[filter.ValueType];
                string    entityAlias = GetNextAlias();
                filter.Alias = entityAlias;

                bool whereStarted = false;
                if (filter.Source.NodeType == NodeType.TypeFilter)                          // root filter/outer select
                {
                    Join[] lookupJoins;
                    WriteSelectClause(w, entity, entityAlias, out lookupJoins);

                    Join[] joins;
                    if (_orderByNode != null)
                    {
                        Join[] orderByJoins = GetJoinsFromOrderBy(entity, entityAlias, _orderByNode);
                        joins = new Join[lookupJoins.Length + orderByJoins.Length];
                        lookupJoins.CopyTo(joins, 0);
                        orderByJoins.CopyTo(joins, lookupJoins.Length);
                    }
                    else
                    {
                        joins = lookupJoins;
                    }

                    WriteFromClause(w, entity.Table, entityAlias, joins);
                    if (entity.BaseEntity != null)
                    {
                        WriteSubEntityConstraint(w, entityAlias, entity.TypeField, entity.TypeValue, ref whereStarted);
                    }
                    WriteFilterConstraints(w, filter, whereStarted);

                    // add the default sort order if the entity has one defined but the query is not ordered
                    if (_orderByNode == null && entity.SortOrder != null && entity.SortOrder.Length > 0)
                    {
                        w.Write("\nORDER BY ");
                        w.Write(entity.SortOrder);
                    }
                }
                else if (filter.Source.NodeType == NodeType.Property || filter.Source.NodeType == NodeType.Filter)                          // nested filter/subquery
                {
                    Expression source = filter.Source;
                    while (source.NodeType == NodeType.Filter)
                    {
                        source = (source as Filter).Source;
                    }
                    if (source.NodeType != NodeType.Property)
                    {
                        throw new Exception("Could not find source property for filter.");
                    }

                    Property relProperty = (Property)source;
                    if (!relProperty.IsRelational)
                    {
                        throw new Exception("Expected source property for Filter node to be relational.");
                    }

                    RelationMap relation     = relProperty.RelationMap;
                    EntityMap   sourceEntity = _maps[relProperty.OwnerClass];
                    string      sourceAlias  = GetAlias(relProperty);

                    string relationConstraint = ReplaceTableNameWithAlias(relation.Filter, sourceEntity.Table, sourceAlias);
                    relationConstraint = ReplaceTableNameWithAlias(relationConstraint, entity.Table, entityAlias);

                    switch (relation.Relationship)
                    {
                    case Relationship.Parent:
                    {
                        w.Write("SELECT *");
                        WriteFromClause(w, entity.Table, entityAlias, null);
                        if (entity.BaseEntity != null)
                        {
                            WriteSubEntityConstraint(w, entityAlias, entity.TypeField, entity.TypeValue, ref whereStarted);
                        }
                        WriteJoinCondition(w, sourceAlias, relation.Fields, entityAlias, GetFieldNames(entity.KeyFields), relationConstraint, true, ref whereStarted);
                        WriteFilterConstraints(w, filter, whereStarted);
                        break;
                    }

                    case Relationship.Child:
                    {
                        w.Write("SELECT *");
                        WriteFromClause(w, entity.Table, entityAlias, null);
                        if (entity.BaseEntity != null)
                        {
                            WriteSubEntityConstraint(w, entityAlias, entity.TypeField, entity.TypeValue, ref whereStarted);
                        }
                        WriteJoinCondition(w, sourceAlias, GetFieldNames(sourceEntity.KeyFields), entityAlias, relation.Fields, relationConstraint, true, ref whereStarted);
                        WriteFilterConstraints(w, filter, whereStarted);
                        break;
                    }

                    case Relationship.Many:
                    {
                        ManyMap junctionMap   = (ManyMap)relation;
                        string  junctionAlias = GetNextAlias();

                        relationConstraint = ReplaceTableNameWithAlias(relationConstraint, junctionMap.Table, junctionAlias);

                        // write the junction and child table, inner joined together in one select
                        w.Write("SELECT *");
                        WriteFromClause(w, junctionMap.Table, junctionAlias, null);
                        w.Write(", ");
                        WriteSqlTable(w, entity.Table, entityAlias);
                        if (entity.BaseEntity != null)
                        {
                            WriteSubEntityConstraint(w, entityAlias, entity.TypeField, entity.TypeValue, ref whereStarted);
                        }
                        WriteJoinCondition(w, sourceAlias, GetFieldNames(sourceEntity.KeyFields), junctionAlias, junctionMap.Source, null, true, ref whereStarted);
                        WriteJoinCondition(w, junctionAlias, junctionMap.Dest, entityAlias, GetFieldNames(entity.KeyFields), relationConstraint, true, ref whereStarted);
                        WriteFilterConstraints(w, filter, whereStarted);

                        break;
                    }

                    default:
                    {
                        throw new NotSupportedException("Relationship type '" + relation.Relationship + "' is not supported.");
                    }
                    }
                }
                else
                {
                    throw new NotImplementedException("Filter with source type '" + filter.Source.NodeType + "' was not expected.");
                }
                return;
            }

            case NodeType.Function:
            {
                Function node = (Function)expr;

                bool   isFormat;
                string keyword = GetSqlKeyword(node.Operator, out isFormat);
                if (isFormat)
                {
                    WriteFormat(w, keyword, node.Params);
                }
                else
                {
                    if (node.Operator != FunctionOperator.In)
                    {
                        throw new NotSupportedException("Function operator '" + node.Operator + "' was not expected.");
                    }
                    w.Write('(');
                    WriteSqlQuery(w, node.Params[0]);
                    w.Write(' ');
                    w.Write(keyword);
                    w.Write(" (");
                    for (int i = 1; i < node.Params.Length; i++)
                    {
                        if (i > 1)
                        {
                            w.Write(", ");
                        }
                        WriteSqlQuery(w, node.Params[i]);
                    }
                    w.Write("))");
                }
                return;
            }

            case NodeType.OrderBy:
            {
                OrderBy node = (OrderBy)expr;
                _orderByNode = node;

                Filter filter = (node.Source as Filter);
                if (filter == null)
                {
                    throw new Exception("Expected source of OrderBy node to be Filter.");
                }

                WriteSqlQuery(w, filter);

                w.Write("\nORDER BY ");
                for (int i = 0; i < node.OrderByItems.Count; i++)
                {
                    OrderByItem item = node.OrderByItems[i];

                    FieldMap field = item.FieldInfo;
                    if (field == null)
                    {
                        throw new Exception("Field info does not exists for OrderByItem '" + item.Item + "'.");
                    }

                    string alias = (item.Join == null) ? filter.Alias : item.Join.Alias;

                    if (i > 0)
                    {
                        w.Write(", ");
                    }
                    WriteSqlColumn(w, alias, field.Field);
                    w.Write((item.Ascending) ? " ASC" : " DESC");
                }
                return;
            }

            case NodeType.Empty:
            {
                return;
            }

            default:
            {
                throw new NotSupportedException("Expression type '" + expr.GetType() + "' is not currently supported.");
            }
            }
        }