Mercurial > pub > bltoolkit
diff Source/Data/Sql/SqlProvider/OracleSqlProvider.cs @ 0:f990fcb411a9
Копия текущей версии из github
author | cin |
---|---|
date | Thu, 27 Mar 2014 21:46:09 +0400 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/Data/Sql/SqlProvider/OracleSqlProvider.cs Thu Mar 27 21:46:09 2014 +0400 @@ -0,0 +1,378 @@ +using System; +using System.Data; +using System.Text; + +namespace BLToolkit.Data.Sql.SqlProvider +{ + using DataProvider; + using Reflection; + + public class OracleSqlProvider : BasicSqlProvider + { + public override bool IsCountSubQuerySupported { get { return false; } } + public override bool IsIdentityParameterRequired { get { return true; } } + public override int MaxInListValuesCount { get { return 1000; } } + + protected override void BuildSelectClause(StringBuilder sb) + { + if (SqlQuery.From.Tables.Count == 0) + { + AppendIndent(sb).Append("SELECT").AppendLine(); + BuildColumns(sb); + AppendIndent(sb).Append("FROM SYS.DUAL").AppendLine(); + } + else + base.BuildSelectClause(sb); + } + + protected override void BuildGetIdentity(StringBuilder sb) + { + var identityField = SqlQuery.Insert.Into.GetIdentityField(); + + if (identityField == null) + throw new SqlException("Identity field must be defined for '{0}'.", SqlQuery.Insert.Into.Name); + + AppendIndent(sb).AppendLine("RETURNING"); + AppendIndent(sb).Append("\t"); + BuildExpression(sb, identityField, false, true); + sb.AppendLine(" INTO :IDENTITY_PARAMETER"); + } + + public override ISqlExpression GetIdentityExpression(SqlTable table, SqlField identityField, bool forReturning) + { + if (table.SequenceAttributes != null) + { + var attr = GetSequenceNameAttribute(table, false); + + if (attr != null) + return new SqlExpression(attr.SequenceName + ".nextval", Precedence.Primary); + } + + return base.GetIdentityExpression(table, identityField, forReturning); + } + + protected override bool BuildWhere() + { + return base.BuildWhere() || !NeedSkip && NeedTake && SqlQuery.OrderBy.IsEmpty && SqlQuery.Having.IsEmpty; + } + + string _rowNumberAlias; + + protected override ISqlProvider CreateSqlProvider() + { + return new OracleSqlProvider(); + } + + protected override void BuildSql(StringBuilder sb) + { + var buildRowNum = NeedSkip || NeedTake && (!SqlQuery.OrderBy.IsEmpty || !SqlQuery.Having.IsEmpty); + var aliases = null as string[]; + + if (buildRowNum) + { + aliases = GetTempAliases(2, "t"); + + if (_rowNumberAlias == null) + _rowNumberAlias = GetTempAliases(1, "rn")[0]; + + AppendIndent(sb).AppendFormat("SELECT {0}.*", aliases[1]).AppendLine(); + AppendIndent(sb).Append("FROM"). AppendLine(); + AppendIndent(sb).Append("("). AppendLine(); + Indent++; + + AppendIndent(sb).AppendFormat("SELECT {0}.*, ROWNUM as {1}", aliases[0], _rowNumberAlias).AppendLine(); + AppendIndent(sb).Append("FROM"). AppendLine(); + AppendIndent(sb).Append("("). AppendLine(); + Indent++; + } + + base.BuildSql(sb); + + if (buildRowNum) + { + Indent--; + AppendIndent(sb).Append(") ").Append(aliases[0]).AppendLine(); + + if (NeedTake && NeedSkip) + { + AppendIndent(sb).AppendLine("WHERE"); + AppendIndent(sb).Append("\tROWNUM <= "); + BuildExpression(sb, Add<int>(SqlQuery.Select.SkipValue, SqlQuery.Select.TakeValue)); + sb.AppendLine(); + } + + Indent--; + AppendIndent(sb).Append(") ").Append(aliases[1]).AppendLine(); + AppendIndent(sb).Append("WHERE").AppendLine(); + + Indent++; + + if (NeedTake && NeedSkip) + { + AppendIndent(sb).AppendFormat("{0}.{1} > ", aliases[1], _rowNumberAlias); + BuildExpression(sb, SqlQuery.Select.SkipValue); + } + else if (NeedTake) + { + AppendIndent(sb).AppendFormat("{0}.{1} <= ", aliases[1], _rowNumberAlias); + BuildExpression(sb, Precedence.Comparison, SqlQuery.Select.TakeValue); + } + else + { + AppendIndent(sb).AppendFormat("{0}.{1} > ", aliases[1], _rowNumberAlias); + BuildExpression(sb, Precedence.Comparison, SqlQuery.Select.SkipValue); + } + + sb.AppendLine(); + Indent--; + } + } + + protected override void BuildWhereSearchCondition(StringBuilder sb, SqlQuery.SearchCondition condition) + { + if (NeedTake && !NeedSkip && SqlQuery.OrderBy.IsEmpty && SqlQuery.Having.IsEmpty) + { + BuildPredicate( + sb, + Precedence.LogicalConjunction, + new SqlQuery.Predicate.ExprExpr( + new SqlExpression(null, "ROWNUM", Precedence.Primary), + SqlQuery.Predicate.Operator.LessOrEqual, + SqlQuery.Select.TakeValue)); + + if (base.BuildWhere()) + { + sb.Append(" AND "); + BuildSearchCondition(sb, Precedence.LogicalConjunction, condition); + } + } + else + BuildSearchCondition(sb, Precedence.Unknown, condition); + } + + public override ISqlExpression ConvertExpression(ISqlExpression expr) + { + expr = base.ConvertExpression(expr); + + if (expr is SqlBinaryExpression) + { + var be = (SqlBinaryExpression)expr; + + switch (be.Operation) + { + case "%": return new SqlFunction(be.SystemType, "MOD", be.Expr1, be.Expr2); + case "&": return new SqlFunction(be.SystemType, "BITAND", be.Expr1, be.Expr2); + case "|": // (a + b) - BITAND(a, b) + return Sub( + Add(be.Expr1, be.Expr2, be.SystemType), + new SqlFunction(be.SystemType, "BITAND", be.Expr1, be.Expr2), + be.SystemType); + + case "^": // (a + b) - BITAND(a, b) * 2 + return Sub( + Add(be.Expr1, be.Expr2, be.SystemType), + Mul(new SqlFunction(be.SystemType, "BITAND", be.Expr1, be.Expr2), 2), + be.SystemType); + case "+": return be.SystemType == typeof(string)? new SqlBinaryExpression(be.SystemType, be.Expr1, "||", be.Expr2, be.Precedence): expr; + } + } + else if (expr is SqlFunction) + { + var func = (SqlFunction) expr; + + switch (func.Name) + { + case "Coalesce" : return new SqlFunction(func.SystemType, "Nvl", func.Parameters); + case "Convert" : + { + var ftype = TypeHelper.GetUnderlyingType(func.SystemType); + + if (ftype == typeof(bool)) + { + var ex = AlternativeConvertToBoolean(func, 1); + if (ex != null) + return ex; + } + + if (ftype == typeof(DateTime) || ftype == typeof(DateTimeOffset)) + { + if (IsTimeDataType(func.Parameters[0])) + { + if (func.Parameters[1].SystemType == typeof(string)) + return func.Parameters[1]; + + return new SqlFunction(func.SystemType, "To_Char", func.Parameters[1], new SqlValue("HH24:MI:SS")); + } + + if (TypeHelper.GetUnderlyingType(func.Parameters[1].SystemType) == typeof(DateTime) && + IsDateDataType(func.Parameters[0], "Date")) + { + return new SqlFunction(func.SystemType, "Trunc", func.Parameters[1], new SqlValue("DD")); + } + + return new SqlFunction(func.SystemType, "To_Timestamp", func.Parameters[1], new SqlValue("YYYY-MM-DD HH24:MI:SS")); + } + + return new SqlExpression(func.SystemType, "Cast({0} as {1})", Precedence.Primary, FloorBeforeConvert(func), func.Parameters[0]); + } + case "ContainsExactly": + return func.Parameters.Length == 2 ? + new SqlFunction(func.SystemType, "Contains", func.Parameters[1], func.Parameters[0]) : + new SqlFunction(func.SystemType, "Contains", func.Parameters[1], func.Parameters[0], func.Parameters[2]); + case "CharIndex" : + return func.Parameters.Length == 2? + new SqlFunction(func.SystemType, "InStr", func.Parameters[1], func.Parameters[0]): + new SqlFunction(func.SystemType, "InStr", func.Parameters[1], func.Parameters[0], func.Parameters[2]); + case "AddYear" : return new SqlFunction(func.SystemType, "Add_Months", func.Parameters[0], Mul(func.Parameters[1], 12)); + case "AddQuarter" : return new SqlFunction(func.SystemType, "Add_Months", func.Parameters[0], Mul(func.Parameters[1], 3)); + case "AddMonth" : return new SqlFunction(func.SystemType, "Add_Months", func.Parameters[0], func.Parameters[1]); + case "AddDayOfYear" : + case "AddWeekDay" : + case "AddDay" : return Add<DateTime>(func.Parameters[0], func.Parameters[1]); + case "AddWeek" : return Add<DateTime>(func.Parameters[0], Mul(func.Parameters[1], 7)); + case "AddHour" : return Add<DateTime>(func.Parameters[0], Div(func.Parameters[1], 24)); + case "AddMinute" : return Add<DateTime>(func.Parameters[0], Div(func.Parameters[1], 60 * 24)); + case "AddSecond" : return Add<DateTime>(func.Parameters[0], Div(func.Parameters[1], 60 * 60 * 24)); + case "AddMillisecond" : return Add<DateTime>(func.Parameters[0], Div(func.Parameters[1], 1000 * 60 * 60 * 24)); + case "Avg" : + return new SqlFunction( + func.SystemType, + "Round", + new SqlFunction(func.SystemType, "AVG", func.Parameters[0]), + new SqlValue(27)); + } + } + else if (expr is SqlExpression) + { + var e = (SqlExpression)expr; + + if (e.Expr.StartsWith("To_Number(To_Char(") && e.Expr.EndsWith(", 'FF'))")) + return Div(new SqlExpression(e.SystemType, e.Expr.Replace("To_Number(To_Char(", "to_Number(To_Char("), e.Parameters), 1000); + } + + return expr; + } + + protected override void BuildFunction(StringBuilder sb, SqlFunction func) + { + func = ConvertFunctionParameters(func); + base.BuildFunction(sb, func); + } + + protected override void BuildDataType(StringBuilder sb, SqlDataType type) + { + switch (type.SqlDbType) + { + case SqlDbType.BigInt : sb.Append("Number(19)"); break; + case SqlDbType.TinyInt : sb.Append("Number(3)"); break; + case SqlDbType.Money : sb.Append("Number(19,4)"); break; + case SqlDbType.SmallMoney : sb.Append("Number(10,4)"); break; + case SqlDbType.NVarChar : + sb.Append("VarChar2"); + if (type.Length > 0) + sb.Append('(').Append(type.Length).Append(')'); + break; + default : base.BuildDataType(sb, type); break; + } + } + + public override SqlQuery Finalize(SqlQuery sqlQuery) + { + CheckAliases(sqlQuery, 30); + + new QueryVisitor().Visit(sqlQuery.Select, element => + { + if (element.ElementType == QueryElementType.SqlParameter) + ((SqlParameter)element).IsQueryParameter = false; + }); + + sqlQuery = base.Finalize(sqlQuery); + + switch (sqlQuery.QueryType) + { + case QueryType.Delete : return GetAlternativeDelete(sqlQuery); + case QueryType.Update : return GetAlternativeUpdate(sqlQuery); + default : return sqlQuery; + } + } + + protected override void BuildFromClause(StringBuilder sb) + { + if (!SqlQuery.IsUpdate) + base.BuildFromClause(sb); + } + + public override void BuildValue(StringBuilder sb, object value) + { + if (value is Guid) + { + var s = ((Guid)value).ToString("N"); + + sb + .Append("Cast('") + .Append(s.Substring( 6, 2)) + .Append(s.Substring( 4, 2)) + .Append(s.Substring( 2, 2)) + .Append(s.Substring( 0, 2)) + .Append(s.Substring(10, 2)) + .Append(s.Substring( 8, 2)) + .Append(s.Substring(14, 2)) + .Append(s.Substring(12, 2)) + .Append(s.Substring(16, 16)) + .Append("' as raw(16))"); + } + else if (value is DateTime) + { + sb.AppendFormat("TO_TIMESTAMP('{0:yyyy-MM-dd HH:mm:ss.fffffff}', 'YYYY-MM-DD HH24:MI:SS.FF7')", value); + } + else + base.BuildValue(sb, value); + } + + protected override void BuildColumnExpression(StringBuilder sb, ISqlExpression expr, string alias, ref bool addAlias) + { + var wrap = false; + + if (expr.SystemType == typeof(bool)) + { + if (expr is SqlQuery.SearchCondition) + wrap = true; + else + { + var ex = expr as SqlExpression; + wrap = ex != null && ex.Expr == "{0}" && ex.Parameters.Length == 1 && ex.Parameters[0] is SqlQuery.SearchCondition; + } + } + + if (wrap) sb.Append("CASE WHEN "); + base.BuildColumnExpression(sb, expr, alias, ref addAlias); + if (wrap) sb.Append(" THEN 1 ELSE 0 END"); + } + + public override object Convert(object value, ConvertType convertType) + { + switch (convertType) + { + case ConvertType.NameToQueryParameter: + return ":" + value; + } + + return value; + } + + protected override void BuildInsertOrUpdateQuery(StringBuilder sb) + { + BuildInsertOrUpdateQueryAsMerge(sb, "FROM SYS.DUAL"); + } + + protected override void BuildEmptyInsert(StringBuilder sb) + { + sb.Append("VALUES "); + + foreach (var col in SqlQuery.Insert.Into.Fields) + sb.Append("(DEFAULT)"); + + sb.AppendLine(); + } + } +}