DeveelDB  20151217
complete SQL database system, primarly developed for .NET/Mono frameworks
ExpressionEvaluatorVisitor.cs
Go to the documentation of this file.
1 //
2 // Copyright 2010-2015 Deveel
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 
17 using System;
18 using System.Collections.Generic;
19 using System.Linq;
20 
21 using Deveel.Data;
22 using Deveel.Data.Routines;
23 using Deveel.Data.Sql.Objects;
24 using Deveel.Data.Sql.Query;
25 using Deveel.Data.Sql.Variables;
26 using Deveel.Data.Types;
27 
28 namespace Deveel.Data.Sql.Expressions {
30  private readonly EvaluateContext context;
31 
33  this.context = context;
34  }
35 
36  public override SqlExpression Visit(SqlExpression expression) {
37  if (expression is QueryReferenceExpression)
38  return VisitQueryReference((QueryReferenceExpression) expression);
39 
40  return base.Visit(expression);
41  }
42 
44  var reference = expression.QueryReference;
45  var value = reference.Evaluate(context.VariableResolver);
46  return SqlExpression.Constant(value);
47  }
48 
50  var info = new List<BinaryEvaluateInfo> {
51  new BinaryEvaluateInfo {Expression = binary.Left, Offset = 0},
52  new BinaryEvaluateInfo {Expression = binary.Right, Offset = 1}
53  }.OrderByDescending(x => x.Precedence);
54 
55  foreach (var evaluateInfo in info) {
56  evaluateInfo.Expression = Visit(evaluateInfo.Expression);
57  }
58 
59  return info.OrderBy(x => x.Offset)
60  .Select(x => x.Expression)
61  .ToArray();
62  }
63 
65  if (left.ExpressionType != SqlExpressionType.Constant)
66  throw new ExpressionEvaluateException("The evaluated left side of a binary expression is not constant");
67  if (right.ExpressionType != SqlExpressionType.Constant)
68  throw new ExpressionEvaluateException("The evaluated right side of a binary expression is not constant.");
69 
70  var value1 = ((SqlConstantExpression) left).Value;
71  var value2 = ((SqlConstantExpression) right).Value;
72 
73  var result = EvaluateBinary(value1, binaryType, value2);
74 
75  return SqlExpression.Constant(result);
76  }
77 
79  if (binaryType.IsAll())
80  return left.Any(binaryType.SubQueryPlainType(), right, context);
81  if (binaryType.IsAny())
82  return left.All(binaryType.SubQueryPlainType(), right, context);
83 
84  switch (binaryType) {
86  return left.Add(right);
88  return left.Subtract(right);
90  return left.Multiply(right);
92  return left.Divide(right);
93  case SqlExpressionType.Modulo:
94  return left.Modulus(right);
95  case SqlExpressionType.GreaterThan:
96  return left.IsGreaterThan(right);
97  case SqlExpressionType.GreaterOrEqualThan:
98  return left.IsGreterOrEqualThan(right);
99  case SqlExpressionType.SmallerThan:
100  return left.IsSmallerThan(right);
101  case SqlExpressionType.SmallerOrEqualThan:
102  return left.IsSmallerOrEqualThan(right);
103  case SqlExpressionType.Equal:
104  return left.IsEqualTo(right);
105  case SqlExpressionType.NotEqual:
106  return left.IsNotEqualTo(right);
107  case SqlExpressionType.Is:
108  return left.Is(right);
110  return left.IsNot(right);
111  case SqlExpressionType.Like:
112  return left.IsLike(right);
113  case SqlExpressionType.NotLike:
114  return left.IsNotLike(right);
115  case SqlExpressionType.And:
116  return left.And(right);
117  case SqlExpressionType.Or:
118  return left.Or(right);
119  case SqlExpressionType.XOr:
120  return left.XOr(right);
121  // TODO: ANY and ALL
122  default:
123  throw new ExpressionEvaluateException(String.Format("The type {0} is not a binary expression or is not supported.", binaryType));
124  }
125  }
126 
127  public override SqlExpression VisitBinary(SqlBinaryExpression binaryEpression) {
128  var sides = EvaluateSides(binaryEpression);
129 
130  var newLeft = sides[0];
131  var newRight = sides[1];
132 
133  return EvaluateBinary(newLeft, binaryEpression.ExpressionType, newRight);
134  }
135 
136  public override SqlExpression VisitCast(SqlCastExpression castExpression) {
137  var valueExp = Visit(castExpression.Value);
138  if (valueExp.ExpressionType != SqlExpressionType.Constant)
139  throw new ExpressionEvaluateException(String.Format("Cannot CAST an expression of type {0}.", valueExp.ExpressionType));
140 
141  var value = ((SqlConstantExpression) valueExp).Value;
142  var casted = value.CastTo(castExpression.SqlType);
143  return SqlExpression.Constant(casted);
144  }
145 
146  //public override SqlExpression VisitConstant(SqlConstantExpression constant) {
147  // var value = constant.Value;
148  // if (value.IsNull)
149  // return constant;
150 
151  // var obj = value.Value;
152  // if (obj is SqlQueryObject) {
153  // return EvaluateQueryPlan((SqlQueryObject) obj);
154  // }
155 
156  // return base.VisitConstant(constant);
157  //}
158 
159  //private SqlConstantExpression EvaluateQueryPlan(SqlQueryObject obj) {
160  // if (context.QueryContext == null)
161  // throw new ExpressionEvaluateException("A query context is required to evaluate a query.");
162 
163  // try {
164  // var plan = obj.QueryPlan;
165  // var result = plan.Evaluate(context.QueryContext);
166 
167  // return SqlExpression.Constant(new DataObject(new TabularType(), SqlTabular.From(result)));
168  // } catch (ExpressionEvaluateException) {
169  // throw;
170  // } catch (Exception ex) {
171  // throw new ExpressionEvaluateException("Could not evaluate a query.", ex);
172  // }
173  //}
174 
176  try {
177  var invoke = new Invoke(expression.FunctioName, expression.Arguments);
178  IRequest request = null;
179  IVariableResolver variableResolver = null;
180  IGroupResolver groupResolver = null;
181  if (context != null) {
182  request = context.Request;
183  variableResolver = context.VariableResolver;
184  groupResolver = context.GroupResolver;
185  }
186 
187  // TODO: if we don't have a return value (PROCEDURES) what should w return?
188  var result = invoke.Execute(request, variableResolver, groupResolver);
189  if (!result.HasReturnValue)
191 
192  return SqlExpression.Constant(result.ReturnValue);
193  } catch (ExpressionEvaluateException) {
194  throw;
195  } catch (Exception ex) {
196  throw new ExpressionEvaluateException(String.Format("Could not evaluate function expression '{0}' because of an error.", expression), ex);
197  }
198  }
199 
200  public override SqlExpression VisitQuery(SqlQueryExpression query) {
201  if (context.Request == null)
202  throw new ExpressionEvaluateException("A query expression is required to evaluate a query.");
203 
204  try {
205  var planner = context.Request.Context.QueryPlanner();
206  var plan = planner.PlanQuery(new QueryInfo(context.Request, query));
207  return SqlExpression.Constant(new DataObject(new QueryType(), new SqlQueryObject(plan)));
208  } catch (ExpressionEvaluateException) {
209  throw;
210  } catch (Exception ex) {
211  throw new ExpressionEvaluateException("Evaluation of a QUERY expression could not generate a plan.", ex);
212  }
213  }
214 
216  var refName = reference.ReferenceName;
217 
218  if (context.VariableResolver == null)
219  throw new ExpressionEvaluateException(String.Format("A resolver is required to dereference variable '{0}'.", refName));
220 
221  try {
222  var value = context.VariableResolver.Resolve(refName);
223  return SqlExpression.Constant(value);
224  } catch (ExpressionEvaluateException) {
225  throw;
226  } catch (Exception ex) {
227  throw new ExpressionEvaluateException(String.Format("An error occurred while trying to dereference '{0}' to a constant value", refName), ex);
228  }
229  }
230 
231  public override SqlExpression VisitUnary(SqlUnaryExpression unary) {
232  var operand = Visit(unary.Operand);
233  if (operand.ExpressionType != SqlExpressionType.Constant)
234  throw new ExpressionEvaluateException("Operand of a unary operator could not be evaluated to a constant.");
235 
236  var result = EvaluateUnary(((SqlConstantExpression)operand).Value, unary.ExpressionType);
237  return SqlExpression.Constant(result);
238  }
239 
240  private DataObject EvaluateUnary(DataObject operand, SqlExpressionType unaryType) {
241  switch (unaryType) {
242  case SqlExpressionType.UnaryPlus:
243  return operand.Plus();
245  case SqlExpressionType.Not:
246  return operand.Negate();
247  default:
248  throw new ExpressionEvaluateException(String.Format("Expression of type '{0}' is not unary.", unaryType));
249  }
250  }
251 
253  if (context.Request == null)
254  throw new ExpressionEvaluateException("Cannot assign a variable outside a query context.");
255 
256  var valueExpression = Visit(assign.ValueExpression);
257 
258  if (valueExpression.ExpressionType != SqlExpressionType.Constant)
259  throw new ExpressionEvaluateException("Cannot assign a variable from a non constant value.");
260 
261  var reference = assign.ReferenceExpression;
262  var value = ((SqlConstantExpression)valueExpression).Value;
263 
264  string variableName;
265 
266  if (reference is SqlVariableReferenceExpression) {
267  variableName = ((SqlVariableReferenceExpression) reference).VariableName;
268  } else if (reference is SqlReferenceExpression) {
269  var refName = ((SqlReferenceExpression) reference).ReferenceName;
270  if (refName.Parent != null) // This might be the assignment to a complex type attribute
271  throw new NotSupportedException(string.Format("Reference to '{0}' is not supported.", refName));
272 
273  variableName = refName.Name;
274  } else {
275  throw new NotSupportedException();
276  }
277 
278  try {
279  context.Request.SetVariable(variableName, valueExpression);
280  } catch (Exception ex) {
281  throw new ExpressionEvaluateException(String.Format("Could not assign value to variable '{0}'", reference), ex);
282  }
283 
284 
285  return SqlExpression.Constant(value);
286  }
287 
288  public override SqlExpression VisitTuple(SqlTupleExpression expression) {
289  var list = expression.Expressions;
290  if (list != null) {
291  list = VisitExpressionList(list);
292  }
293 
294  if (list == null)
295  return SqlExpression.Constant(new DataObject(new ArrayType(-1), SqlArray.Null));
296 
297  // This is not an array, but a subquery
298  if (list.Length == 1 &&
299  list[0].ExpressionType == SqlExpressionType.Constant &&
300  ((SqlConstantExpression)list[0]).Value.Type is QueryType)
301  return list[0];
302 
303  return SqlExpression.Constant(new DataObject(new ArrayType(list.Length), new SqlArray(list)));
304  }
305 
307  var refName = reference.VariableName;
308 
309  if (context.Request == null)
310  throw new ExpressionEvaluateException(String.Format("Cannot dereference variable {0} outside a query context", refName));
311  if (context.VariableResolver == null)
312  throw new ExpressionEvaluateException("The query context does not handle variables.");
313 
314 
315  var variable = context.Request.FindVariable(refName);
316  if (variable == null)
318 
319  return SqlExpression.Constant(variable.Value);
320  }
321 
323  var evalTest = Visit(conditional.TestExpression);
324  if (evalTest.ExpressionType != SqlExpressionType.Constant)
325  throw new ExpressionEvaluateException("The test expression of a conditional must evaluate to a constant value.");
326 
327  var evalTestValue = ((SqlConstantExpression) evalTest).Value;
328  if (!(evalTestValue.Type is BooleanType))
329  throw new ExpressionEvaluateException("The test expression of a conditional must be a boolean value.");
330 
331  if (evalTestValue)
332  return Visit(conditional.TrueExpression);
333 
334  if (conditional.FalseExpression != null)
335  return Visit(conditional.FalseExpression);
336 
338  }
339 
340  #region BinaryEvaluateInfo
341 
343  public SqlExpression Expression { get; set; }
344  public int Offset { get; set; }
345 
346  public int Precedence {
347  get { return Expression.EvaluatePrecedence; }
348  }
349  }
350 
351  #endregion
352  }
353 }
Provides some helper functions for resolving and creating SqlType instances that are primitive to the...
override SqlExpression Visit(SqlExpression expression)
Visits a given SQL expression.
ObjectName ReferenceName
Gets the name of the object referenced by the expression.
DataObject Multiply(DataObject other)
Definition: DataObject.cs:401
SqlExpression Operand
Gets the operand expression that is computed.
override SqlExpression VisitBinary(SqlBinaryExpression binaryEpression)
SqlExpression Value
Gets the expression whose evaluated value will be converted.
An error occurring while evaluating an SqlExpression.
static readonly SqlArray Null
A SQL array that is equivalent to null.
Definition: SqlArray.cs:34
An expression that references an object within a context.
A long string in the system.
Handles expressions computed against an unary operator.
SqlExpression EvaluateBinary(SqlExpression left, SqlExpressionType binaryType, SqlExpression right)
SqlType SqlType
Gets the destination type of the conversion
override SqlExpression VisitTuple(SqlTupleExpression expression)
DataObject Divide(DataObject other)
Definition: DataObject.cs:410
static DataObject Null(SqlType type)
Definition: DataObject.cs:630
An SqlExpression that will cast a value retrieved by the evaluation of another expression into a give...
DataObject IsLike(DataObject pattern)
When the type of this object is a string, this method verifies if the input pattern is compatible (
Definition: DataObject.cs:324
DataObject Modulus(DataObject other)
Definition: DataObject.cs:419
override SqlExpression VisitVariableReference(SqlVariableReferenceExpression reference)
override SqlExpression VisitFunctionCall(SqlFunctionCallExpression expression)
Visits the expression that calls the function defined.
DataObject IsSmallerThan(DataObject other)
Definition: DataObject.cs:287
SqlExpressionType
All the possible type of SqlExpression supported
override SqlExpression VisitAssign(SqlAssignExpression assign)
DataObject IsEqualTo(DataObject other)
Compares to the given object to verify if is it equal to the current.
Definition: DataObject.cs:251
DataObject EvaluateUnary(DataObject operand, SqlExpressionType unaryType)
DataObject IsNotLike(DataObject pattern)
Definition: DataObject.cs:337
DataObject IsSmallerOrEqualThan(DataObject other)
Definition: DataObject.cs:301
Defines a contract used by grouping functions to find information about the current group being evalu...
override SqlExpression VisitReference(SqlReferenceExpression reference)
Represents a dynamic object that encapsulates a defined SqlType and a compatible constant ISqlObject ...
Definition: DataObject.cs:35
DataObject IsGreaterThan(DataObject other)
Definition: DataObject.cs:280
override SqlExpression VisitConditional(SqlConditionalExpression conditional)
The information about the invocation of a routine, including the full name and arguments (as SqlExpre...
Definition: Invoke.cs:30
DataObject Is(DataObject other)
Compares to the given object to verify if is it compatible.
Definition: DataObject.cs:208
DataObject XOr(DataObject other)
Definition: DataObject.cs:446
DataObject IsGreterOrEqualThan(DataObject other)
Definition: DataObject.cs:294
DataObject EvaluateBinary(DataObject left, SqlExpressionType binaryType, DataObject right)
An interface to resolve a variable name to a constant object.
override SqlExpression VisitUnary(SqlUnaryExpression unary)
An expression that holds a constant value.
DataObject IsNotEqualTo(DataObject other)
Compares to the given object to verify if is it not equal to the current.
Definition: DataObject.cs:273
override SqlExpression VisitQuery(SqlQueryExpression query)
DataObject Negate()
Negates the current underlying value of the object.
Definition: DataObject.cs:359
override SqlExpression VisitCast(SqlCastExpression castExpression)
SqlExpression[] EvaluateSides(SqlBinaryExpression binary)
DataObject And(DataObject other)
Definition: DataObject.cs:437
Defines the base class for instances that represent SQL expression tree nodes.
DataObject Any(SqlExpressionType type, DataObject other, EvaluateContext context)
Definition: DataObject.cs:455
static SqlConstantExpression Constant(object value)
An object that provides methods for accessing a finite collection of SQL expressions.
Definition: SqlArray.cs:28
abstract SqlExpressionType ExpressionType
Gets the type code of this SQL expression.
Encapsulates the elements needed to evaluate an SqlExpression
DataObject IsNot(DataObject other)
Compares the given object to verify if it is not compatible with this one.
Definition: DataObject.cs:232
DataObject All(SqlExpressionType type, DataObject other, EvaluateContext context)
Definition: DataObject.cs:462
DataObject Subtract(DataObject other)
Definition: DataObject.cs:392
DataObject Add(DataObject other)
Adds the given value to this object value.
Definition: DataObject.cs:383
DataObject Evaluate(IVariableResolver resolver)
DataObject Or(DataObject other)
Definition: DataObject.cs:428
SqlExpression VisitQueryReference(QueryReferenceExpression expression)