Mercurial > pub > bltoolkit
comparison Source/Data/Sql/SqlProvider/OracleSqlProvider.cs @ 0:f990fcb411a9
Копия текущей версии из github
author | cin |
---|---|
date | Thu, 27 Mar 2014 21:46:09 +0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:f990fcb411a9 |
---|---|
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 } |