private void FixTree(Expression expr) { //Debug.WriteLine("\n\nBefore FixTree:\n" + expr.ToXmlString()); //V2: This logic does not handle axis nodes in functions correctly. //V2: Need to crate a RecomposeFunction to move axis out from inside a function (or just don't let this happen - make them use []'s instead) //V2: Be sure to check that all properties in a function are of the same type. // Mixing relations in a function would be bad (e.g., LEN(BillAddress.Zip + ShipAddress.Zip) //V2: Expression.EnumNodes(expr, new Expression.EnumNodesCallBack(this.RecomposeFunction), null, (Function)null); //V2: Debug.WriteLine("\n\nAfter RecomposeFunction:\n" + expr.ToXmlString()); // note: using a post callback to handle binary node chains correctly (found this out the hard way) Expression.EnumNodes(expr, null, new Expression.EnumNodesCallBack(this.RecomposeBinary), null); //Debug.WriteLine("\n\nAfter RecomposeBinary:\n" + expr.ToXmlString()); Expression.EnumNodes(expr, new Expression.EnumNodesCallBack(this.ReplaceAxisCallback)); //Debug.WriteLine("\n\nAfter ReplaceAxisCallback:\n" + expr.ToXmlString()); Expression.EnumNodes(expr, new Expression.EnumNodesCallBack(this.LinkPropertiesToMap), null, new object[0]); //Debug.WriteLine("\n\nAfter LinkPropertiesToMap:\n" + expr.ToXmlString()); Expression.EnumNodes(expr, new Expression.EnumNodesCallBack(this.ReplaceAddWithConcatenation), null, new object[0]); //Debug.WriteLine("\n\nAfter ReplaceAddWithConcatenation:\n" + expr.ToXmlString()); //Debug.WriteLine("\n\nAfter FixTree:\n" + expr.ToXmlString()); }
private bool OleDbSetParameterOrder(Expression node, object[] args) { if (node.NodeType == NodeType.Filter) { // the parameters in this filter are the next in sequence Filter filter = (Filter)node; Expression.EnumNodesCallBack callback = new Expression.EnumNodesCallBack(this.OleDbSetParameterOrderInFilter); Expression.EnumNodes(filter.Source, callback, null, args); Expression.EnumNodes(filter.Constraint, callback, null, args); } return(true); }
private bool LinkPropertiesToMap(Expression node, object[] args) { if (node.NodeType == NodeType.Filter) { Filter filter = (Filter)node; Expression.EnumNodesCallBack callback = new Expression.EnumNodesCallBack(this.LinkPropertiesToMap); if (args.Length > 0) { Expression.EnumNodes(filter.Source, callback, null, args); } // add the new filter to the font of the list object[] newArgs = new object[args.Length + 1]; Array.Copy(args, 0, newArgs, 1, args.Length); newArgs[0] = filter; Expression.EnumNodes(filter.Constraint, callback, null, newArgs); return(false); } else if (node.NodeType == NodeType.Property) { Property property = (Property)node; // determine the level of the filter to link int level = 0; if (property.Source.NodeType == NodeType.Parent) { level = (property.Source as Parent).Level; // replace the parent source with a new context for linking property.Source = new Context(); } if (level >= args.Length) { throw new OPathException("Property '" + property.Name + "' could not be associated to an entity."); } // set the context link to the source of the filter Context context = (Context)property.Source; context.Link = (args[level] as Filter).Source; // set the property info SetPropertyInfo(property); } return(true); }
private void SetParameterOrder(ObjectExpression oe) { if (oe.ParameterCount == 0) { return; } if (_provider.Provider == Provider.Access || _provider.Provider == Provider.OleDb) { // the OleDb driver expects the order of parameters to be specified in "subquery major" order (which I consider a *major* design flaw). // in other words, the order is driven by a post traversal of the select statements (filters), with standard left-to-right order within a given select. Expression.EnumNodesCallBack postCallback = new Expression.EnumNodesCallBack(this.OleDbSetParameterOrder); Expression.EnumNodes(oe.Expression, null, postCallback, 0); } else // not an OleDb provider { // use standard right-to-left order (which the parameters are already in) for (int i = _parameterTable.Count - 1; i >= 0; i--) { _parameterTable[i].Ordinal = i; } } }
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."); } } }
public override void Validate() { // there is currently no way to support traversals within a function Expression.EnumNodes(this, new Expression.EnumNodesCallBack(this.ValidateNoFilters)); // verify argument types based on function switch (_op) { case FunctionOperator.Trim: case FunctionOperator.Len: case FunctionOperator.UpperCase: case FunctionOperator.LowerCase: { if (_args.Length != 1) { throw new OPathException("Invalid number of parameters passed to " + GetDisplayName(_op) + " function."); } if (_args[0].NodeType == NodeType.Parameter) { (_args[0] as Parameter).inferredType = typeof(string); } if (_args[0].ValueType != typeof(string)) { throw new OPathException("Parameter of " + GetDisplayName(_op) + " function must be of type string."); } break; } case FunctionOperator.Left: case FunctionOperator.Right: { if (_args.Length != 2) { throw new OPathException("Invalid number of parameters passed to " + GetDisplayName(_op) + " function."); } if (_args[0].NodeType == NodeType.Parameter) { (_args[0] as Parameter).inferredType = typeof(string); } if (_args[1].NodeType == NodeType.Parameter) { (_args[1] as Parameter).inferredType = typeof(long); } if (_args[0].ValueType != typeof(string)) { throw new OPathException("First parameter of " + GetDisplayName(_op) + " function must be of type string."); } if (!Binary.IsInteger(_args[1].ValueType)) { throw new OPathException("Second parameter of " + GetDisplayName(_op) + " function must be of integer type."); } break; } case FunctionOperator.Substring: { if (_args.Length != 3) { throw new OPathException("Invalid number of parameters passed to " + GetDisplayName(_op) + " function."); } if (_args[0].NodeType == NodeType.Parameter) { (_args[0] as Parameter).inferredType = typeof(string); } if (_args[1].NodeType == NodeType.Parameter) { (_args[1] as Parameter).inferredType = typeof(long); } if (_args[2].NodeType == NodeType.Parameter) { (_args[2] as Parameter).inferredType = typeof(long); } if (_args[0].ValueType != typeof(string)) { throw new OPathException("First parameter of " + GetDisplayName(_op) + " function must be of type string."); } if (!Binary.IsInteger(_args[1].ValueType)) { throw new OPathException("First parameter of " + GetDisplayName(_op) + " function must be of integer type."); } if (!Binary.IsInteger(_args[2].ValueType)) { throw new OPathException("Second parameter of " + GetDisplayName(_op) + " function must be of integer type."); } break; } case FunctionOperator.Like: { if (_args.Length != 2) { throw new OPathException("Invalid number of parameters passed to " + GetDisplayName(_op) + " function."); } if (_args[0].NodeType == NodeType.Parameter) { (_args[0] as Parameter).inferredType = typeof(string); } if (_args[1].NodeType == NodeType.Parameter) { (_args[1] as Parameter).inferredType = typeof(string); } //NOTE: Until type casting is supported, let's just send the left operand to the database and let it try casting //if( _args[0].ValueType != typeof(string) ) //{ // throw new OPathException("Left operand of " + GetDisplayName(_op) + " comparison must be of type string."); //} if (_args[1].ValueType != typeof(string)) { throw new OPathException("Right operand of " + GetDisplayName(_op) + " comparison must be of type string."); } break; } case FunctionOperator.In: { if (_args.Length < 2) { throw new OPathException("Invalid number of parameters passed to " + GetDisplayName(_op) + " function."); } break; } default: { throw new NotSupportedException("Binary operator '" + _op + "' was not expected."); } } }
private void Validate(Expression expr) { // validate every node in the tree Expression.EnumNodes(expr, new Expression.EnumNodesCallBack(this.ValidateNode)); }
private bool RecomposeBinary(Expression node, object[] noargs) // note: internal longhorn name found on www.winfx247.com { if (node.NodeType == NodeType.Binary) { Binary binary = (Binary)node; bool leftIsAxis = (binary.Left.NodeType == NodeType.Axis); bool rightIsAxis = (binary.Right.NodeType == NodeType.Axis); // nothing to do if neither branch contains an axis if (!leftIsAxis && !rightIsAxis) { return(true); } // don't consider moving the binary node if neither branch is constant (and joined by a logical operator) // the binary node should stay above an axis in this case (example: (AAA.BBB = X) && (CCC = Y)) if (!binary.Left.IsConst && !binary.Right.IsConst && Binary.IsOperatorLogical(binary.Operator)) { return(true); } // see if we have an axis on both sides if (leftIsAxis && rightIsAxis) { throw new NotSupportedException("Relationship traversals on both sides of a binary operator is not currently supported."); // recompose the left and right to put both under one axis //V2: Recompose2Axis(binary); //return true; } else // axis on one side only { // get our root axis and find the last (leaf) axis in the chain // the leaf axis will hold the parent binary when moved // (this is to handle multiple dot expressions like: AAA.BBB.CCC = @Value) Axis axis = (leftIsAxis) ? (Axis)binary.Left : (Axis)binary.Right; Axis leafAxis = axis; int parentCount = 1; while (leafAxis.Constraint.NodeType == NodeType.Axis) { leafAxis = (Axis)leafAxis.Constraint; parentCount += 1; } // now make the binary node the constraint of the the leaf axis // and move the existing constraint under the other branch of the binary if (leftIsAxis) { Expression newRight = (Expression)binary.Right.Clone(); Expression.EnumNodes(newRight, new Expression.EnumNodesCallBack(this.IncreaseParentDepth), null, parentCount); leafAxis.Constraint = new Binary(binary.Operator, leafAxis.Constraint, newRight); } else // right is axis { Expression newLeft = (Expression)binary.Left.Clone(); Expression.EnumNodes(newLeft, new Expression.EnumNodesCallBack(this.IncreaseParentDepth), null, parentCount); leafAxis.Constraint = new Binary(binary.Operator, newLeft, leafAxis.Constraint); } Expression.Replace(binary, axis); } } return(true); }