DeveelDB  20151217
complete SQL database system, primarly developed for .NET/Mono frameworks
ExpressionBuilder.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 
22 using Deveel.Data.Sql.Objects;
23 using Deveel.Data.Sql.Tables;
24 
25 namespace Deveel.Data.Sql.Parser {
27  public static SqlExpression Build(IExpressionNode node) {
28  if (node is SqlVariableRefExpressionNode)
29  return VisitVariableRefExpression((SqlVariableRefExpressionNode) node);
30  if (node is SqlExpressionTupleNode)
31  return VisitTupleExpression((SqlExpressionTupleNode) node);
32  if (node is SqlQueryExpressionNode)
33  return VisitQueryExpression((SqlQueryExpressionNode) node);
34  if (node is SqlCaseExpressionNode)
35  return VisitCaseExpression((SqlCaseExpressionNode) node);
36  if (node is SqlConstantExpressionNode)
37  return VisitConstantExpression((SqlConstantExpressionNode) node);
39  return VisitFunctionCall((SqlFunctionCallExpressionNode) node);
40  if (node is SqlReferenceExpressionNode)
41  return VisitReferenceExpression((SqlReferenceExpressionNode) node);
42  if (node is SqlBinaryExpressionNode)
43  return VisitBinaryExpression((SqlBinaryExpressionNode) node);
44  if (node is SqlUnaryExpressionNode)
45  return VisitUnaryExpression((SqlUnaryExpressionNode) node);
46  if (node is SqlBetweenExpressionNode)
47  return VisitBetweenExpression((SqlBetweenExpressionNode) node);
48 
49  throw new NotSupportedException();
50  }
51 
54  }
55 
57  return SqlExpression.Tuple(node.Expressions.Select(Build).ToArray());
58  }
59 
61  var selectColumns = GetSelectColumns(node);
62  var exp = new SqlQueryExpression(selectColumns);
63 
64  if (node.FromClause != null) {
65  SetFromClause(exp.FromClause, node.FromClause);
66  }
67 
68  if (node.WhereExpression != null) {
69  exp.WhereExpression = Build(node.WhereExpression);
70  }
71 
72  if (node.GroupBy != null) {
73  var groupBy = new List<SqlExpression>();
74  if (node.GroupBy.GroupExpressions != null)
75  groupBy.AddRange(node.GroupBy.GroupExpressions.Select(Build));
76 
77  exp.GroupBy = groupBy.ToList();
78 
79  var having = node.GroupBy.HavingExpression;
80  if (having != null)
81  exp.HavingExpression = Build(having);
82 
83  }
84 
85  if (node.Composite != null) {
86  var compositeExp = Build(node.Composite.QueryExpression);
87  exp.NextComposite = compositeExp as SqlQueryExpression;
88  exp.IsCompositeAll = node.Composite.IsAll;
89  exp.CompositeFunction = GetCompositeFunction(node.Composite.CompositeFunction);
90  }
91 
92  return exp;
93  }
94 
95  private static CompositeFunction GetCompositeFunction(string s) {
96  if (String.Equals(s, "UNION", StringComparison.OrdinalIgnoreCase))
97  return CompositeFunction.Union;
98  if (String.Equals(s, "EXCEPT", StringComparison.OrdinalIgnoreCase))
99  return CompositeFunction.Except;
100  if (String.Equals(s, "INTERSECT", StringComparison.OrdinalIgnoreCase))
101  return CompositeFunction.Intersect;
102 
103  throw new InvalidOperationException(String.Format("Composite function {0} is invalid.", s));
104  }
105 
106  private static void SetFromTableInClause(FromClause clause, IFromSourceNode source, JoinNode join) {
107  AddSourceToClause(clause, source);
108 
109  if (join != null) {
110  var joinType = JoinType.Inner;
111  if (!String.IsNullOrEmpty(join.JoinType))
112  joinType = GetJoinType(join.JoinType);
113 
114  SqlExpression onExpression = null;
115  if (join.OnExpression != null)
116  onExpression = Build(join.OnExpression);
117 
118  clause.Join(joinType, onExpression);
119 
120  SetFromTableInClause(clause, join.Source, join.NextJoin);
121  }
122  }
123 
124  private static void AddSourceToClause(FromClause clause, IFromSourceNode source) {
125  string alias = null;
126  if (source.Alias != null)
127  alias = source.Alias.Text;
128 
129  if (source is FromTableSourceNode) {
130  var tableSource = (FromTableSourceNode)source;
131  clause.AddTable(alias, tableSource.TableName.Name);
132  } else if (source is FromQuerySourceNode) {
133  var querySource = (FromQuerySourceNode)source;
134  var queryExpression = (SqlQueryExpression) Build(querySource.Query);
135  clause.AddSubQuery(alias, queryExpression);
136  }
137  }
138 
139  private static JoinType GetJoinType(string typeName) {
140  if (String.Equals(typeName, "INNER", StringComparison.OrdinalIgnoreCase) ||
141  String.Equals(typeName, "INNER JOIN", StringComparison.OrdinalIgnoreCase) ||
142  String.Equals(typeName, ",", StringComparison.OrdinalIgnoreCase))
143  return JoinType.Inner;
144  if (String.Equals(typeName, "LEFT OUTER", StringComparison.OrdinalIgnoreCase) ||
145  String.Equals(typeName, "LFT OUTER JOIN", StringComparison.OrdinalIgnoreCase) ||
146  String.Equals(typeName, "LEFT", StringComparison.OrdinalIgnoreCase) ||
147  String.Equals(typeName, "LFT JOIN", StringComparison.OrdinalIgnoreCase))
148  return JoinType.Left;
149  if (String.Equals(typeName, "RIGHT OUTER", StringComparison.OrdinalIgnoreCase) ||
150  String.Equals(typeName, "RIGHT OUTER JOIN", StringComparison.OrdinalIgnoreCase) ||
151  String.Equals(typeName, "RIGHT", StringComparison.OrdinalIgnoreCase) ||
152  String.Equals(typeName, "RIGHT JOIN", StringComparison.OrdinalIgnoreCase))
153  return JoinType.Right;
154 
155  return JoinType.None;
156  }
157 
158  private static void SetFromClause(FromClause clause, FromClauseNode node) {
159  SetFromTableInClause(clause, node.Source, node.Join);
160  }
161 
162  private static IEnumerable<SelectColumn> GetSelectColumns(SqlQueryExpressionNode node) {
163  if (node.IsAll) {
164  return new[] {new SelectColumn(SqlExpression.Reference(new ObjectName("*")))};
165  }
166 
167  var items = new List<SelectColumn>();
168  foreach (var item in node.SelectItems) {
169  SqlExpression exp;
170  if (item.Name != null) {
171  exp = SqlExpression.Reference(ObjectName.Parse(item.Name.Name));
172  } else if (item.Expression != null) {
173  exp = Build(item.Expression);
174  } else {
175  throw new InvalidOperationException();
176  }
177 
178  string alias = null;
179  if (item.Alias != null)
180  alias = item.Alias.Text;
181 
182  items.Add(new SelectColumn(exp, alias));
183  }
184 
185  return items.ToArray();
186  }
187 
188  private static SqlExpression VisitCaseExpression(SqlCaseExpressionNode expressionNode) {
189  throw new NotImplementedException();
190  }
191 
193  var sqlValue = expressionNode.Value;
194  DataObject obj;
195  if (sqlValue is SqlString) {
196  obj = DataObject.VarChar((SqlString) sqlValue);
197  } else if (sqlValue is SqlBoolean) {
198  obj = DataObject.Boolean((SqlBoolean) sqlValue);
199  } else if (sqlValue is SqlNumber) {
200  obj = DataObject.Number((SqlNumber) sqlValue);
201  } else if (sqlValue is SqlNull) {
202  obj = DataObject.Null();
203  } else {
204  throw new NotSupportedException("Constant value not supported.");
205  }
206 
207  return SqlExpression.Constant(obj);
208  }
209 
212  }
213 
215  var args = new List<SqlExpression>();
216  if (node.Arguments != null)
217  args.AddRange(node.Arguments.Select(Build));
218 
219  return SqlExpression.FunctionCall(node.FunctionName, args.ToArray());
220  }
221 
223  var testExp = Build(expressionNode.Expression);
224  var minValue = Build(expressionNode.MinValue);
225  var maxValue = Build(expressionNode.MaxValue);
226 
227  var smallerExp = SqlExpression.SmallerOrEqualThan(testExp, maxValue);
228  var greaterExp = SqlExpression.GreaterOrEqualThan(testExp, minValue);
229 
230  SqlExpression exp = SqlExpression.And(smallerExp, greaterExp);
231 
232  if (expressionNode.Not)
233  exp = SqlExpression.Not(exp);
234 
235  return exp;
236  }
237 
239  var left = Build(expressionNode.Left);
240  var right = Build(expressionNode.Right);
241  var op = expressionNode.Operator;
242 
243  var expType = GetBinaryExpressionType(op);
244 
245  return SqlExpression.Binary(left, expType, right);
246  }
247 
248  private static SqlExpressionType GetBinaryExpressionType(string op) {
249  if (op == "+" ||
250  op == "||")
251  return SqlExpressionType.Add;
252  if (op == "-")
253  return SqlExpressionType.Subtract;
254  if (op == "*")
255  return SqlExpressionType.Multiply;
256  if (op == "/")
257  return SqlExpressionType.Divide;
258  if (op == "%" ||
259  String.Equals(op, "MOD", StringComparison.OrdinalIgnoreCase))
260  return SqlExpressionType.Modulo;
261  if (op == "=")
262  return SqlExpressionType.Equal;
263  if (op == "<>")
264  return SqlExpressionType.NotEqual;
265  if (op == ">")
266  return SqlExpressionType.GreaterThan;
267  if (op == ">=")
268  return SqlExpressionType.GreaterOrEqualThan;
269  if (op == "<")
270  return SqlExpressionType.SmallerThan;
271  if (op == "<=")
272  return SqlExpressionType.SmallerOrEqualThan;
273  if (String.Equals(op, "LIKE", StringComparison.OrdinalIgnoreCase))
274  return SqlExpressionType.Like;
275  if (String.Equals(op, "NOT LIKE", StringComparison.OrdinalIgnoreCase))
276  return SqlExpressionType.NotLike;
277 
278  if (String.Equals(op, "IS", StringComparison.OrdinalIgnoreCase))
279  return SqlExpressionType.Is;
280  if (String.Equals(op, "IS NOT", StringComparison.OrdinalIgnoreCase))
281  return SqlExpressionType.IsNot;
282 
283  if (String.Equals(op, "AND", StringComparison.OrdinalIgnoreCase))
284  return SqlExpressionType.And;
285  if (String.Equals(op, "OR", StringComparison.OrdinalIgnoreCase))
286  return SqlExpressionType.Or;
287 
288  if (String.Equals(op, "IN", StringComparison.OrdinalIgnoreCase))
289  return SqlExpressionType.AnyEqual;
290  if (String.Equals(op, "NOT IN", StringComparison.OrdinalIgnoreCase))
291  return SqlExpressionType.AllNotEqual;
292 
293  throw new ArgumentException(String.Format("The operator {0} is not a binary one.", op));
294  }
295 
297  var expressionType = GetUnaryExpressionType(expressionNode.Operator);
298  var operand = Build(expressionNode.Operand);
299 
300  return SqlExpression.Unary(expressionType, operand);
301  }
302 
303  private static SqlExpressionType GetUnaryExpressionType(string op) {
304  if (op == "+")
305  return SqlExpressionType.UnaryPlus;
306  if (op == "-")
307  return SqlExpressionType.Negate;
308  if (String.Equals(op, "NOT", StringComparison.OrdinalIgnoreCase))
309  return SqlExpressionType.Not;
310 
311  throw new ArgumentException(String.Format("The operator {0} is not a unary one.", op));
312  }
313  }
314 }
static SqlExpressionType GetUnaryExpressionType(string op)
string CompositeFunction
Gets the function used to compose the two queries.
static JoinType GetJoinType(string typeName)
static ObjectName Parse(string s)
Parses the given string into a ObjectName object.
Definition: ObjectName.cs:139
bool IsAll
Gets a boolean value indicating if the selection will return all columns from the sources specified i...
IExpressionNode Operand
Gets the argument of the operator.
static SqlBinaryExpression And(SqlExpression left, SqlExpression right)
static SqlBinaryExpression SmallerOrEqualThan(SqlExpression left, SqlExpression right)
IExpressionNode Right
Gets the right side argument of the expression.
static SqlExpression VisitConstantExpression(SqlConstantExpressionNode expressionNode)
JoinType
Enumerates the kind of group join in a selection query.
Definition: JoinType.cs:23
static SqlExpression VisitBetweenExpression(SqlBetweenExpressionNode expressionNode)
static IEnumerable< SelectColumn > GetSelectColumns(SqlQueryExpressionNode node)
void AddTable(string alias, FromTable table)
Adds a table as source to the query with a given alias.
Definition: FromClause.cs:93
static CompositeFunction GetCompositeFunction(string s)
string Operator
Gets the string representation of the operator.
static DataObject Number(SqlNumber value)
Definition: DataObject.cs:552
static SqlExpression Build(IExpressionNode node)
void Join(JoinType joinType, SqlExpression onExpression)
Sets a join between the last added table and the one that preceeds it.
Definition: FromClause.cs:154
static DataObject Null(SqlType type)
Definition: DataObject.cs:630
IExpressionNode MinValue
Gets the minimum value (inclusive) that Expression must be to evaluate to true.
static SqlTupleExpression Tuple(SqlExpression[] expressions)
An expression that encapsulates a unary operator for a given operand.
An SQL BETWEEN expression node that evaluates to true if the Expression given is between MinValue (in...
static SqlVariableReferenceExpression VariableReference(string varName)
Describes the name of an object within a database.
Definition: ObjectName.cs:44
The node in an SQL query that defines the sources from which to retrieve the data queried...
ObjectNameNode Reference
Gets the full name of the object references.
static DataObject Boolean(SqlBoolean value)
Definition: DataObject.cs:544
A node in the grammar tree that defines a sub-query in a FROM clause.
static SqlExpression VisitFunctionCall(SqlFunctionCallExpressionNode node)
static SqlUnaryExpression Not(SqlExpression operand)
static SqlExpression VisitUnaryExpression(SqlUnaryExpressionNode expressionNode)
ISqlObject Value
Gets an immutable instance of ISqlObject that represents the constant value.
SqlExpressionType
All the possible type of SqlExpression supported
An expression containing a set of other expressions.
string FunctionName
Gets the name of the function to be invoked.
static SqlExpression VisitReferenceExpression(SqlReferenceExpressionNode node)
Represents the node that is a database table as source of a query.
void AddSubQuery(SqlQueryExpression subQuery)
Adds a sub-query expression as source of the query.
Definition: FromClause.cs:134
static SqlExpression VisitTupleExpression(SqlExpressionTupleNode node)
The root node of an expression used to select a set of items from a set of sources defined...
An expression node that references an object within the database context (such as a table...
IEnumerable< IExpressionNode > Arguments
Gets an optional read-only list of arguments to be passed to the function invoked.
static SqlExpression VisitCaseExpression(SqlCaseExpressionNode expressionNode)
A container for the FROM clause of a select statement.
Definition: FromClause.cs:32
static void AddSourceToClause(FromClause clause, IFromSourceNode source)
static SqlExpression VisitVariableRefExpression(SqlVariableRefExpressionNode node)
SqlQueryExpressionNode QueryExpression
Gets the other query to compose the results.
IExpressionNode HavingExpression
Gets the HAVING expression used to filter the grouped results.
Definition: GroupByNode.cs:37
Represents a dynamic object that encapsulates a defined SqlType and a compatible constant ISqlObject ...
Definition: DataObject.cs:35
IEnumerable< SelectItemNode > SelectItems
Gets a read-only list of items that will be returned by the query.
Represents a column selected to be in the output of a select statement.
Definition: SelectColumn.cs:31
References a variable within a SQL execution context.
Defines the base contract of the source of a query.
string Name
The full object name as composed from the input SQL string analyzed.
IEnumerable< IExpressionNode > GroupExpressions
Gets the expression node to group the results.
Definition: GroupByNode.cs:32
string Text
Gets the textual content of the identifier.
string Variable
Gets the name of the variable to reference.
static SqlExpression VisitBinaryExpression(SqlBinaryExpressionNode expressionNode)
static DataObject VarChar(string s)
Definition: DataObject.cs:622
A node describing the JOIN between two sources within a query.
Definition: JoinNode.cs:29
static void SetFromClause(FromClause clause, FromClauseNode node)
IFromSourceNode Source
Gets a read-only list of sources for the query.
bool IsAll
Gets a boolean value indicating whether the composition will be done on all records.
static SqlReferenceExpression Reference(ObjectName objectName)
GroupByNode GroupBy
Gets an optional clause used to group and filter the results of a query.
IdentifierNode Alias
Gets an alias that uniquely identifies the source within a query context.
IExpressionNode WhereExpression
Gets an optional clause that is used to filter the queried objects.
IExpressionNode Left
Gets the left side argument of the expression.
string JoinType
Gets the type of join, as a string that will be operated between the two sources
Definition: JoinNode.cs:57
IExpressionNode MaxValue
Gets the maximum value (inclusive) that Expression must be to evaluate to true.
IExpressionNode OnExpression
Gets the expression used as condition for creating the joined group in a query.
Definition: JoinNode.cs:43
CompositeFunction
The kind of composite function in a CompositeTable.
static SqlBinaryExpression Binary(SqlExpression left, SqlExpressionType expressionType, SqlExpression right)
static SqlExpressionType GetBinaryExpressionType(string op)
Defines the base class for instances that represent SQL expression tree nodes.
An node that represents a constant value set within a context of an SQL command.
static SqlConstantExpression Constant(object value)
static void SetFromTableInClause(FromClause clause, IFromSourceNode source, JoinNode join)
string Operator
Gets the binary operator that will be used to evaluate the final result.
IExpressionNode Expression
Gets the expression to be tested against MinValue and MaxValue.
static SqlFunctionCallExpression FunctionCall(ObjectName functionName)
static SqlExpression VisitQueryExpression(SqlQueryExpressionNode node)
This interface acts like a marker that indicates if a ISqlNode represents a SQL expression.
Represents an expression that evaluates between two other expressions.
IEnumerable< IExpressionNode > Expressions
Gets a read-only list of expression that are contained within this tuple.
A node in a SQL command tree that is used to request a function.
static SqlUnaryExpression Unary(SqlExpressionType expressionType, SqlExpression operand)
static SqlBinaryExpression GreaterOrEqualThan(SqlExpression left, SqlExpression right)
FromClauseNode FromClause
Gets the clause defining the sources from where to query.
QueryCompositeNode Composite
Gets an optional definition for a composition between this query and another.
An SQL node describing an in-line CASE conditional expression.