0
|
1 using System;
|
|
2 using System.Data;
|
|
3 using System.Text;
|
|
4
|
|
5 namespace BLToolkit.Data.Sql.SqlProvider
|
|
6 {
|
|
7 using DataProvider;
|
|
8 using Reflection;
|
|
9
|
|
10 public class OracleSqlProvider : BasicSqlProvider
|
|
11 {
|
|
12 public override bool IsCountSubQuerySupported { get { return false; } }
|
|
13 public override bool IsIdentityParameterRequired { get { return true; } }
|
|
14 public override int MaxInListValuesCount { get { return 1000; } }
|
|
15
|
|
16 protected override void BuildSelectClause(StringBuilder sb)
|
|
17 {
|
|
18 if (SqlQuery.From.Tables.Count == 0)
|
|
19 {
|
|
20 AppendIndent(sb).Append("SELECT").AppendLine();
|
|
21 BuildColumns(sb);
|
|
22 AppendIndent(sb).Append("FROM SYS.DUAL").AppendLine();
|
|
23 }
|
|
24 else
|
|
25 base.BuildSelectClause(sb);
|
|
26 }
|
|
27
|
|
28 protected override void BuildGetIdentity(StringBuilder sb)
|
|
29 {
|
|
30 var identityField = SqlQuery.Insert.Into.GetIdentityField();
|
|
31
|
|
32 if (identityField == null)
|
|
33 throw new SqlException("Identity field must be defined for '{0}'.", SqlQuery.Insert.Into.Name);
|
|
34
|
|
35 AppendIndent(sb).AppendLine("RETURNING");
|
|
36 AppendIndent(sb).Append("\t");
|
|
37 BuildExpression(sb, identityField, false, true);
|
|
38 sb.AppendLine(" INTO :IDENTITY_PARAMETER");
|
|
39 }
|
|
40
|
|
41 public override ISqlExpression GetIdentityExpression(SqlTable table, SqlField identityField, bool forReturning)
|
|
42 {
|
|
43 if (table.SequenceAttributes != null)
|
|
44 {
|
|
45 var attr = GetSequenceNameAttribute(table, false);
|
|
46
|
|
47 if (attr != null)
|
|
48 return new SqlExpression(attr.SequenceName + ".nextval", Precedence.Primary);
|
|
49 }
|
|
50
|
|
51 return base.GetIdentityExpression(table, identityField, forReturning);
|
|
52 }
|
|
53
|
|
54 protected override bool BuildWhere()
|
|
55 {
|
|
56 return base.BuildWhere() || !NeedSkip && NeedTake && SqlQuery.OrderBy.IsEmpty && SqlQuery.Having.IsEmpty;
|
|
57 }
|
|
58
|
|
59 string _rowNumberAlias;
|
|
60
|
|
61 protected override ISqlProvider CreateSqlProvider()
|
|
62 {
|
|
63 return new OracleSqlProvider();
|
|
64 }
|
|
65
|
|
66 protected override void BuildSql(StringBuilder sb)
|
|
67 {
|
|
68 var buildRowNum = NeedSkip || NeedTake && (!SqlQuery.OrderBy.IsEmpty || !SqlQuery.Having.IsEmpty);
|
|
69 var aliases = null as string[];
|
|
70
|
|
71 if (buildRowNum)
|
|
72 {
|
|
73 aliases = GetTempAliases(2, "t");
|
|
74
|
|
75 if (_rowNumberAlias == null)
|
|
76 _rowNumberAlias = GetTempAliases(1, "rn")[0];
|
|
77
|
|
78 AppendIndent(sb).AppendFormat("SELECT {0}.*", aliases[1]).AppendLine();
|
|
79 AppendIndent(sb).Append("FROM"). AppendLine();
|
|
80 AppendIndent(sb).Append("("). AppendLine();
|
|
81 Indent++;
|
|
82
|
|
83 AppendIndent(sb).AppendFormat("SELECT {0}.*, ROWNUM as {1}", aliases[0], _rowNumberAlias).AppendLine();
|
|
84 AppendIndent(sb).Append("FROM"). AppendLine();
|
|
85 AppendIndent(sb).Append("("). AppendLine();
|
|
86 Indent++;
|
|
87 }
|
|
88
|
|
89 base.BuildSql(sb);
|
|
90
|
|
91 if (buildRowNum)
|
|
92 {
|
|
93 Indent--;
|
|
94 AppendIndent(sb).Append(") ").Append(aliases[0]).AppendLine();
|
|
95
|
|
96 if (NeedTake && NeedSkip)
|
|
97 {
|
|
98 AppendIndent(sb).AppendLine("WHERE");
|
|
99 AppendIndent(sb).Append("\tROWNUM <= ");
|
|
100 BuildExpression(sb, Add<int>(SqlQuery.Select.SkipValue, SqlQuery.Select.TakeValue));
|
|
101 sb.AppendLine();
|
|
102 }
|
|
103
|
|
104 Indent--;
|
|
105 AppendIndent(sb).Append(") ").Append(aliases[1]).AppendLine();
|
|
106 AppendIndent(sb).Append("WHERE").AppendLine();
|
|
107
|
|
108 Indent++;
|
|
109
|
|
110 if (NeedTake && NeedSkip)
|
|
111 {
|
|
112 AppendIndent(sb).AppendFormat("{0}.{1} > ", aliases[1], _rowNumberAlias);
|
|
113 BuildExpression(sb, SqlQuery.Select.SkipValue);
|
|
114 }
|
|
115 else if (NeedTake)
|
|
116 {
|
|
117 AppendIndent(sb).AppendFormat("{0}.{1} <= ", aliases[1], _rowNumberAlias);
|
|
118 BuildExpression(sb, Precedence.Comparison, SqlQuery.Select.TakeValue);
|
|
119 }
|
|
120 else
|
|
121 {
|
|
122 AppendIndent(sb).AppendFormat("{0}.{1} > ", aliases[1], _rowNumberAlias);
|
|
123 BuildExpression(sb, Precedence.Comparison, SqlQuery.Select.SkipValue);
|
|
124 }
|
|
125
|
|
126 sb.AppendLine();
|
|
127 Indent--;
|
|
128 }
|
|
129 }
|
|
130
|
|
131 protected override void BuildWhereSearchCondition(StringBuilder sb, SqlQuery.SearchCondition condition)
|
|
132 {
|
|
133 if (NeedTake && !NeedSkip && SqlQuery.OrderBy.IsEmpty && SqlQuery.Having.IsEmpty)
|
|
134 {
|
|
135 BuildPredicate(
|
|
136 sb,
|
|
137 Precedence.LogicalConjunction,
|
|
138 new SqlQuery.Predicate.ExprExpr(
|
|
139 new SqlExpression(null, "ROWNUM", Precedence.Primary),
|
|
140 SqlQuery.Predicate.Operator.LessOrEqual,
|
|
141 SqlQuery.Select.TakeValue));
|
|
142
|
|
143 if (base.BuildWhere())
|
|
144 {
|
|
145 sb.Append(" AND ");
|
|
146 BuildSearchCondition(sb, Precedence.LogicalConjunction, condition);
|
|
147 }
|
|
148 }
|
|
149 else
|
|
150 BuildSearchCondition(sb, Precedence.Unknown, condition);
|
|
151 }
|
|
152
|
|
153 public override ISqlExpression ConvertExpression(ISqlExpression expr)
|
|
154 {
|
|
155 expr = base.ConvertExpression(expr);
|
|
156
|
|
157 if (expr is SqlBinaryExpression)
|
|
158 {
|
|
159 var be = (SqlBinaryExpression)expr;
|
|
160
|
|
161 switch (be.Operation)
|
|
162 {
|
|
163 case "%": return new SqlFunction(be.SystemType, "MOD", be.Expr1, be.Expr2);
|
|
164 case "&": return new SqlFunction(be.SystemType, "BITAND", be.Expr1, be.Expr2);
|
|
165 case "|": // (a + b) - BITAND(a, b)
|
|
166 return Sub(
|
|
167 Add(be.Expr1, be.Expr2, be.SystemType),
|
|
168 new SqlFunction(be.SystemType, "BITAND", be.Expr1, be.Expr2),
|
|
169 be.SystemType);
|
|
170
|
|
171 case "^": // (a + b) - BITAND(a, b) * 2
|
|
172 return Sub(
|
|
173 Add(be.Expr1, be.Expr2, be.SystemType),
|
|
174 Mul(new SqlFunction(be.SystemType, "BITAND", be.Expr1, be.Expr2), 2),
|
|
175 be.SystemType);
|
|
176 case "+": return be.SystemType == typeof(string)? new SqlBinaryExpression(be.SystemType, be.Expr1, "||", be.Expr2, be.Precedence): expr;
|
|
177 }
|
|
178 }
|
|
179 else if (expr is SqlFunction)
|
|
180 {
|
|
181 var func = (SqlFunction) expr;
|
|
182
|
|
183 switch (func.Name)
|
|
184 {
|
|
185 case "Coalesce" : return new SqlFunction(func.SystemType, "Nvl", func.Parameters);
|
|
186 case "Convert" :
|
|
187 {
|
|
188 var ftype = TypeHelper.GetUnderlyingType(func.SystemType);
|
|
189
|
|
190 if (ftype == typeof(bool))
|
|
191 {
|
|
192 var ex = AlternativeConvertToBoolean(func, 1);
|
|
193 if (ex != null)
|
|
194 return ex;
|
|
195 }
|
|
196
|
|
197 if (ftype == typeof(DateTime) || ftype == typeof(DateTimeOffset))
|
|
198 {
|
|
199 if (IsTimeDataType(func.Parameters[0]))
|
|
200 {
|
|
201 if (func.Parameters[1].SystemType == typeof(string))
|
|
202 return func.Parameters[1];
|
|
203
|
|
204 return new SqlFunction(func.SystemType, "To_Char", func.Parameters[1], new SqlValue("HH24:MI:SS"));
|
|
205 }
|
|
206
|
|
207 if (TypeHelper.GetUnderlyingType(func.Parameters[1].SystemType) == typeof(DateTime) &&
|
|
208 IsDateDataType(func.Parameters[0], "Date"))
|
|
209 {
|
|
210 return new SqlFunction(func.SystemType, "Trunc", func.Parameters[1], new SqlValue("DD"));
|
|
211 }
|
|
212
|
|
213 return new SqlFunction(func.SystemType, "To_Timestamp", func.Parameters[1], new SqlValue("YYYY-MM-DD HH24:MI:SS"));
|
|
214 }
|
|
215
|
|
216 return new SqlExpression(func.SystemType, "Cast({0} as {1})", Precedence.Primary, FloorBeforeConvert(func), func.Parameters[0]);
|
|
217 }
|
|
218 case "ContainsExactly":
|
|
219 return func.Parameters.Length == 2 ?
|
|
220 new SqlFunction(func.SystemType, "Contains", func.Parameters[1], func.Parameters[0]) :
|
|
221 new SqlFunction(func.SystemType, "Contains", func.Parameters[1], func.Parameters[0], func.Parameters[2]);
|
|
222 case "CharIndex" :
|
|
223 return func.Parameters.Length == 2?
|
|
224 new SqlFunction(func.SystemType, "InStr", func.Parameters[1], func.Parameters[0]):
|
|
225 new SqlFunction(func.SystemType, "InStr", func.Parameters[1], func.Parameters[0], func.Parameters[2]);
|
|
226 case "AddYear" : return new SqlFunction(func.SystemType, "Add_Months", func.Parameters[0], Mul(func.Parameters[1], 12));
|
|
227 case "AddQuarter" : return new SqlFunction(func.SystemType, "Add_Months", func.Parameters[0], Mul(func.Parameters[1], 3));
|
|
228 case "AddMonth" : return new SqlFunction(func.SystemType, "Add_Months", func.Parameters[0], func.Parameters[1]);
|
|
229 case "AddDayOfYear" :
|
|
230 case "AddWeekDay" :
|
|
231 case "AddDay" : return Add<DateTime>(func.Parameters[0], func.Parameters[1]);
|
|
232 case "AddWeek" : return Add<DateTime>(func.Parameters[0], Mul(func.Parameters[1], 7));
|
|
233 case "AddHour" : return Add<DateTime>(func.Parameters[0], Div(func.Parameters[1], 24));
|
|
234 case "AddMinute" : return Add<DateTime>(func.Parameters[0], Div(func.Parameters[1], 60 * 24));
|
|
235 case "AddSecond" : return Add<DateTime>(func.Parameters[0], Div(func.Parameters[1], 60 * 60 * 24));
|
|
236 case "AddMillisecond" : return Add<DateTime>(func.Parameters[0], Div(func.Parameters[1], 1000 * 60 * 60 * 24));
|
|
237 case "Avg" :
|
|
238 return new SqlFunction(
|
|
239 func.SystemType,
|
|
240 "Round",
|
|
241 new SqlFunction(func.SystemType, "AVG", func.Parameters[0]),
|
|
242 new SqlValue(27));
|
|
243 }
|
|
244 }
|
|
245 else if (expr is SqlExpression)
|
|
246 {
|
|
247 var e = (SqlExpression)expr;
|
|
248
|
|
249 if (e.Expr.StartsWith("To_Number(To_Char(") && e.Expr.EndsWith(", 'FF'))"))
|
|
250 return Div(new SqlExpression(e.SystemType, e.Expr.Replace("To_Number(To_Char(", "to_Number(To_Char("), e.Parameters), 1000);
|
|
251 }
|
|
252
|
|
253 return expr;
|
|
254 }
|
|
255
|
|
256 protected override void BuildFunction(StringBuilder sb, SqlFunction func)
|
|
257 {
|
|
258 func = ConvertFunctionParameters(func);
|
|
259 base.BuildFunction(sb, func);
|
|
260 }
|
|
261
|
|
262 protected override void BuildDataType(StringBuilder sb, SqlDataType type)
|
|
263 {
|
|
264 switch (type.SqlDbType)
|
|
265 {
|
|
266 case SqlDbType.BigInt : sb.Append("Number(19)"); break;
|
|
267 case SqlDbType.TinyInt : sb.Append("Number(3)"); break;
|
|
268 case SqlDbType.Money : sb.Append("Number(19,4)"); break;
|
|
269 case SqlDbType.SmallMoney : sb.Append("Number(10,4)"); break;
|
|
270 case SqlDbType.NVarChar :
|
|
271 sb.Append("VarChar2");
|
|
272 if (type.Length > 0)
|
|
273 sb.Append('(').Append(type.Length).Append(')');
|
|
274 break;
|
|
275 default : base.BuildDataType(sb, type); break;
|
|
276 }
|
|
277 }
|
|
278
|
|
279 public override SqlQuery Finalize(SqlQuery sqlQuery)
|
|
280 {
|
|
281 CheckAliases(sqlQuery, 30);
|
|
282
|
|
283 new QueryVisitor().Visit(sqlQuery.Select, element =>
|
|
284 {
|
|
285 if (element.ElementType == QueryElementType.SqlParameter)
|
|
286 ((SqlParameter)element).IsQueryParameter = false;
|
|
287 });
|
|
288
|
|
289 sqlQuery = base.Finalize(sqlQuery);
|
|
290
|
|
291 switch (sqlQuery.QueryType)
|
|
292 {
|
|
293 case QueryType.Delete : return GetAlternativeDelete(sqlQuery);
|
|
294 case QueryType.Update : return GetAlternativeUpdate(sqlQuery);
|
|
295 default : return sqlQuery;
|
|
296 }
|
|
297 }
|
|
298
|
|
299 protected override void BuildFromClause(StringBuilder sb)
|
|
300 {
|
|
301 if (!SqlQuery.IsUpdate)
|
|
302 base.BuildFromClause(sb);
|
|
303 }
|
|
304
|
|
305 public override void BuildValue(StringBuilder sb, object value)
|
|
306 {
|
|
307 if (value is Guid)
|
|
308 {
|
|
309 var s = ((Guid)value).ToString("N");
|
|
310
|
|
311 sb
|
|
312 .Append("Cast('")
|
|
313 .Append(s.Substring( 6, 2))
|
|
314 .Append(s.Substring( 4, 2))
|
|
315 .Append(s.Substring( 2, 2))
|
|
316 .Append(s.Substring( 0, 2))
|
|
317 .Append(s.Substring(10, 2))
|
|
318 .Append(s.Substring( 8, 2))
|
|
319 .Append(s.Substring(14, 2))
|
|
320 .Append(s.Substring(12, 2))
|
|
321 .Append(s.Substring(16, 16))
|
|
322 .Append("' as raw(16))");
|
|
323 }
|
|
324 else if (value is DateTime)
|
|
325 {
|
|
326 sb.AppendFormat("TO_TIMESTAMP('{0:yyyy-MM-dd HH:mm:ss.fffffff}', 'YYYY-MM-DD HH24:MI:SS.FF7')", value);
|
|
327 }
|
|
328 else
|
|
329 base.BuildValue(sb, value);
|
|
330 }
|
|
331
|
|
332 protected override void BuildColumnExpression(StringBuilder sb, ISqlExpression expr, string alias, ref bool addAlias)
|
|
333 {
|
|
334 var wrap = false;
|
|
335
|
|
336 if (expr.SystemType == typeof(bool))
|
|
337 {
|
|
338 if (expr is SqlQuery.SearchCondition)
|
|
339 wrap = true;
|
|
340 else
|
|
341 {
|
|
342 var ex = expr as SqlExpression;
|
|
343 wrap = ex != null && ex.Expr == "{0}" && ex.Parameters.Length == 1 && ex.Parameters[0] is SqlQuery.SearchCondition;
|
|
344 }
|
|
345 }
|
|
346
|
|
347 if (wrap) sb.Append("CASE WHEN ");
|
|
348 base.BuildColumnExpression(sb, expr, alias, ref addAlias);
|
|
349 if (wrap) sb.Append(" THEN 1 ELSE 0 END");
|
|
350 }
|
|
351
|
|
352 public override object Convert(object value, ConvertType convertType)
|
|
353 {
|
|
354 switch (convertType)
|
|
355 {
|
|
356 case ConvertType.NameToQueryParameter:
|
|
357 return ":" + value;
|
|
358 }
|
|
359
|
|
360 return value;
|
|
361 }
|
|
362
|
|
363 protected override void BuildInsertOrUpdateQuery(StringBuilder sb)
|
|
364 {
|
|
365 BuildInsertOrUpdateQueryAsMerge(sb, "FROM SYS.DUAL");
|
|
366 }
|
|
367
|
|
368 protected override void BuildEmptyInsert(StringBuilder sb)
|
|
369 {
|
|
370 sb.Append("VALUES ");
|
|
371
|
|
372 foreach (var col in SqlQuery.Insert.Into.Fields)
|
|
373 sb.Append("(DEFAULT)");
|
|
374
|
|
375 sb.AppendLine();
|
|
376 }
|
|
377 }
|
|
378 }
|