DeveelDB  20151217
complete SQL database system, primarly developed for .NET/Mono frameworks
ExpressionStringBuilder.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 using System.Text;
21 
22 using Deveel.Data.Sql.Objects;
23 using Deveel.Data.Types;
24 
25 namespace Deveel.Data.Sql.Expressions {
27  private readonly StringBuilder builder;
28  private bool rootQuery = true;
29 
31  builder = new StringBuilder();
32  }
33 
34  public string ToSqlString(SqlExpression expression) {
35  rootQuery = expression is SqlQueryExpression;
36 
37  Visit(expression);
38  return builder.ToString();
39  }
40 
41  public override SqlExpression VisitAssign(SqlAssignExpression assign) {
42  Visit(assign.ReferenceExpression);
43  builder.Append(" = ");
44  Visit(assign.ValueExpression);
45 
46  return assign;
47  }
48 
49  public override SqlExpression VisitBinary(SqlBinaryExpression binaryEpression) {
50  Visit(binaryEpression.Left);
51 
52  var binaryOpString = GetBinaryOperatorString(binaryEpression.ExpressionType);
53  builder.AppendFormat(" {0} ", binaryOpString);
54 
55  Visit(binaryEpression.Right);
56 
57  return binaryEpression;
58  }
59 
60  private static string GetBinaryOperatorString(SqlExpressionType expressionType) {
61  switch (expressionType) {
62  case SqlExpressionType.Add:
63  return "+";
64  case SqlExpressionType.Subtract:
65  return "-";
66  case SqlExpressionType.Divide:
67  return "/";
68  case SqlExpressionType.Multiply:
69  return "*";
70  case SqlExpressionType.Modulo:
71  return "%";
72  case SqlExpressionType.Equal:
73  return "=";
74  case SqlExpressionType.NotEqual:
75  return "<>";
76  case SqlExpressionType.GreaterThan:
77  return ">";
78  case SqlExpressionType.GreaterOrEqualThan:
79  return ">=";
80  case SqlExpressionType.SmallerThan:
81  return "<";
82  case SqlExpressionType.SmallerOrEqualThan:
83  return "<=";
84  case SqlExpressionType.Is:
85  return "IS";
86  case SqlExpressionType.IsNot:
87  return "IS NOT";
88  case SqlExpressionType.Like:
89  return "LIKE";
90  case SqlExpressionType.NotLike:
91  return "NOT LIKE";
92  case SqlExpressionType.AllEqual:
93  return "= ALL";
94  case SqlExpressionType.AllNotEqual:
95  return "<> ALL";
96  case SqlExpressionType.AllGreaterThan:
97  return "> ALL";
98  case SqlExpressionType.AllGreaterOrEqualThan:
99  return ">= ALL";
100  case SqlExpressionType.AllSmallerThan:
101  return "< ALL";
102  case SqlExpressionType.AllSmallerOrEqualThan:
103  return "<= ALL";
104  case SqlExpressionType.AnyEqual:
105  return "= ANY";
106  case SqlExpressionType.AnyNotEqual:
107  return "<> ANY";
108  case SqlExpressionType.AnyGreaterThan:
109  return "> ANY";
110  case SqlExpressionType.AnyGreaterOrEqualThan:
111  return ">= ANY";
112  case SqlExpressionType.AnySmallerThan:
113  return "< ANY";
114  case SqlExpressionType.AnySmallerOrEqualThan:
115  return "<= ANY";
116  case SqlExpressionType.Or:
117  return "OR";
118  case SqlExpressionType.And:
119  return "AND";
120  case SqlExpressionType.XOr:
121  return "XOR";
122  default:
123  throw new NotSupportedException();
124  }
125  }
126 
127  public override SqlExpression VisitCast(SqlCastExpression castExpression) {
128  builder.Append("CAST ");
129  Visit(castExpression.Value);
130  builder.Append(" AS ");
131  builder.Append(castExpression.SqlType);
132 
133  return base.VisitCast(castExpression);
134  }
135 
137  return base.VisitConditional(conditional);
138  }
139 
141  var value = constant.Value;
142  if (value.Type is QueryType) {
143  // TODO: convert to sql string also a QUERY PLAN
144  builder.Append("({QUERY})");
145  } else if (value.Type is ArrayType) {
146  var array = (SqlArray) value.Value;
147  if (array.IsNull) {
148  builder.Append("NULL");
149  } else {
150  builder.Append("(");
151  var sz = array.Length;
152  for (int i = 0; i < sz; i++) {
153  Visit(array[i]);
154 
155  if (i < sz - 1)
156  builder.Append(", ");
157  }
158  builder.Append(")");
159  }
160  } else if (value.Type is NullType) {
161  builder.Append("NULL");
162  } else if (value.Type.IsPrimitive) {
163  if (value.IsNull) {
164  builder.Append("NULL");
165  } else {
166  builder.Append(value.Value);
167  }
168  }
169 
170  return constant;
171  }
172 
174  builder.Append(expression.FunctioName);
175  builder.Append("(");
176 
177  if (expression.Arguments != null &&
178  expression.Arguments.Length > 0) {
179  var args = expression.Arguments;
180  var argc = args.Length;
181 
182  for (int i = 0; i < argc; i++) {
183  Visit(args[i]);
184 
185  if (i < argc - 1)
186  builder.Append(", ");
187  }
188  }
189 
190  builder.Append(")");
191 
192  return expression;
193  }
194 
195  private void PrintQueryColumns(IEnumerable<SelectColumn> selectColumns) {
196  var columns = selectColumns.ToArray();
197  var sz = columns.Length;
198  for (int i = 0; i < sz; i++) {
199  var column = columns[i];
200 
201  if (column.IsGlob) {
202  if (column.IsAll) {
203  builder.Append("*");
204  } else {
205  builder.AppendFormat("{0}.*", column.TableName);
206  }
207  } else {
208  Visit(column.Expression);
209  }
210 
211  if (!String.IsNullOrEmpty(column.Alias))
212  builder.AppendFormat(" AS {0}", column.Alias);
213 
214  if (i < sz - 1)
215  builder.Append(", ");
216  }
217  }
218 
219  public override SqlExpression VisitQuery(SqlQueryExpression query) {
220  if (!rootQuery)
221  builder.Append("(");
222 
223  builder.Append("SELECT ");
224  if (query.Distinct)
225  builder.Append("DISTINCT ");
226 
227  PrintQueryColumns(query.SelectColumns);
228  builder.Append(" ");
229 
230  PrintFromClause(query.FromClause);
231 
232  if (query.WhereExpression != null) {
233  builder.Append(" WHERE ");
234  Visit(query.WhereExpression);
235  }
236 
237  if (query.GroupBy != null) {
238  builder.Append(" GROUP BY ");
239  VisitExpressionList(query.GroupBy.ToArray());
240 
241  if (query.HavingExpression != null) {
242  builder.Append(" HVAING ");
243  Visit(query.HavingExpression);
244  }
245  }
246 
247  if (query.GroupMax != null) {
248  builder.Append(" GROUP MAX ");
249  builder.Append(query.GroupMax.FullName);
250  }
251 
252  // TODO: COMPOSITE ...
253 
254  if (!rootQuery)
255  builder.Append(")");
256 
257  return query;
258  }
259 
260  private void PrintFromClause(FromClause fromClause) {
261  if (fromClause == null || fromClause.IsEmpty)
262  return;
263 
264  builder.Append("FROM ");
265 
266  var tables = fromClause.AllTables.ToList();
267  for (int i = 0; i < tables.Count; i++) {
268  var source = tables[i];
269 
270  JoinPart joinPart = null;
271 
272  if (i > 0) {
273  joinPart = fromClause.GetJoinPart(i - 1);
274  if (joinPart != null) {
275  if (joinPart.JoinType == JoinType.Inner) {
276  builder.Append(" INNER JOIN ");
277  } else if (joinPart.JoinType == JoinType.Right) {
278  builder.Append(" RIGHT OUTER JOIN ");
279  } else if (joinPart.JoinType == JoinType.Left) {
280  builder.Append(" LEFT OUTER JOIN ");
281  } else if (joinPart.JoinType == JoinType.Full) {
282  builder.Append(" FULL OUTER JOINT ");
283  }
284  }
285  }
286 
287  if (source.IsSubQuery) {
288  builder.Append("(");
289  Visit(source.SubQuery);
290  builder.Append(")");
291  } else {
292  builder.Append(source.Name);
293  }
294 
295  if (!String.IsNullOrEmpty(source.Alias)) {
296  builder.Append(" AS ");
297  builder.Append(source.Alias);
298  }
299 
300  if (i < tables.Count - 1) {
301  if (joinPart == null) {
302  builder.Append(", ");
303  } else {
304  builder.Append(" ON ");
305  Visit(joinPart.OnExpression);
306  }
307  }
308  }
309  }
310 
312  builder.Append(reference.ReferenceName);
313  return reference;
314  }
315 
316  public override SqlExpression VisitTuple(SqlTupleExpression expression) {
317  builder.Append("(");
318 
319  var sz = expression.Expressions.Length;
320  for (int i = 0; i < sz; i++) {
321  Visit(expression.Expressions[i]);
322  if (i < sz - 1)
323  builder.Append(", ");
324  }
325 
326  builder.Append(")");
327  return expression;
328  }
329 
330  public override SqlExpression VisitUnary(SqlUnaryExpression unary) {
331  var unaryOpString = GetUnaryOperatorString(unary.ExpressionType);
332  builder.Append(unaryOpString);
333  builder.Append(" ");
334  Visit(unary.Operand);
335 
336  return unary;
337  }
338 
339  private string GetUnaryOperatorString(SqlExpressionType unaryType) {
340  switch (unaryType) {
341  case SqlExpressionType.UnaryPlus:
342  return "+";
343  case SqlExpressionType.Negate:
344  return "-";
345  case SqlExpressionType.Not:
346  return "NOT";
347  default:
348  throw new NotSupportedException();
349  }
350  }
351 
353  builder.AppendFormat(":{0}", reference.VariableName);
354  return reference;
355  }
356  }
357 }
ObjectName ReferenceName
Gets the name of the object referenced by the expression.
SqlExpression Operand
Gets the operand expression that is computed.
SqlExpression Value
Gets the expression whose evaluated value will be converted.
override SqlExpression VisitConditional(SqlConditionalExpression conditional)
An expression that references an object within a context.
A long string in the system.
Handles expressions computed against an unary operator.
override SqlExpression VisitConstant(SqlConstantExpression constant)
JoinType
Enumerates the kind of group join in a selection query.
Definition: JoinType.cs:23
override SqlExpression VisitUnary(SqlUnaryExpression unary)
SqlType SqlType
Gets the destination type of the conversion
static string GetBinaryOperatorString(SqlExpressionType expressionType)
override SqlExpression VisitQuery(SqlQueryExpression query)
string GetUnaryOperatorString(SqlExpressionType unaryType)
override SqlExpression VisitTuple(SqlTupleExpression expression)
An SqlExpression that will cast a value retrieved by the evaluation of another expression into a give...
JoinPart GetJoinPart(int offset)
Gets the descriptor of the join at the given offset.
Definition: FromClause.cs:174
ISqlObject Value
Gets the underlined value that is handled.
Definition: DataObject.cs:84
override SqlExpression VisitAssign(SqlAssignExpression assign)
SqlExpressionType
All the possible type of SqlExpression supported
DataObject Value
Gets the constant value of the expression.
void PrintQueryColumns(IEnumerable< SelectColumn > selectColumns)
A container for the FROM clause of a select statement.
Definition: FromClause.cs:32
IEnumerable< FromTable > AllTables
Gets an enumeration of all the tables that are the source of the query.
Definition: FromClause.cs:68
override SqlExpression VisitCast(SqlCastExpression castExpression)
string FullName
Gets the full reference name formatted.
Definition: ObjectName.cs:114
override SqlExpression VisitVariableReference(SqlVariableReferenceExpression reference)
override SqlExpression VisitFunctionCall(SqlFunctionCallExpression expression)
Visits the expression that calls the function defined.
An expression that holds a constant value.
override SqlExpression VisitReference(SqlReferenceExpression reference)
A data type that represents the NULL value of a given SQL data type.
Definition: NullType.cs:29
Defines the base class for instances that represent SQL expression tree nodes.
An object that provides methods for accessing a finite collection of SQL expressions.
Definition: SqlArray.cs:28
override SqlExpression VisitBinary(SqlBinaryExpression binaryEpression)