Ejemplo n.º 1
0
            Expression BuildExpression(int fieldIndex, ISqlExpression?sqlExpression)
            {
                Expression expr;

                if (SequenceHelper.UnwrapSubqueryContext(Sequence) is DefaultIfEmptyBuilder.DefaultIfEmptyContext defaultIfEmpty)
                {
                    expr = Builder.BuildSql(_returnType, fieldIndex, sqlExpression);
                    if (defaultIfEmpty.DefaultValue != null && expr is ConvertFromDataReaderExpression convert)
                    {
                        var generator = new ExpressionGenerator();
                        expr = convert.MakeNullable();
                        if (expr.Type.IsNullable())
                        {
                            var exprVar      = generator.AssignToVariable(expr, "nullable");
                            var defaultValue = defaultIfEmpty.DefaultValue;
                            if (defaultValue.Type != expr.Type)
                            {
                                var convertLambda = Builder.MappingSchema.GenerateSafeConvert(defaultValue.Type, expr.Type);
                                defaultValue = InternalExtensions.ApplyLambdaToExpression(convertLambda, defaultValue);
                            }

                            var resultVar = generator.AssignToVariable(defaultValue, "result");

                            generator.AddExpression(Expression.IfThen(
                                                        Expression.NotEqual(exprVar, ExpressionInstances.UntypedNull),
                                                        Expression.Assign(resultVar, Expression.Convert(exprVar, resultVar.Type))));

                            generator.AddExpression(resultVar);

                            expr = generator.Build();
                        }
                    }
                }
                else
                if (_methodName == "Sum" || _returnType.IsNullableType())
                {
                    expr = Builder.BuildSql(_returnType, fieldIndex, sqlExpression);
                }
                else
                {
                    expr = Expression.Block(
                        Expression.Call(null, MemberHelper.MethodOf(() => CheckNullValue(false, null !)), Expression.Call(ExpressionBuilder.DataReaderParam, Methods.ADONet.IsDBNull, ExpressionInstances.Constant0), Expression.Constant(_methodName)),
                        Builder.BuildSql(_returnType, fieldIndex, sqlExpression));
                }

                return(expr);
            }
Ejemplo n.º 2
0
            Expression BuildExpression(int fieldIndex, ISqlExpression?sqlExpression)
            {
                Expression expr;

                if (Sequence is DefaultIfEmptyBuilder.DefaultIfEmptyContext defaultIfEmpty)
                {
                    expr = Builder.BuildSql(_returnType, fieldIndex, sqlExpression);
                    if (defaultIfEmpty.DefaultValue != null && expr is ConvertFromDataReaderExpression convert)
                    {
                        var generator = new ExpressionGenerator();
                        expr = convert.MakeNullable();
                        if (expr.Type.IsNullable())
                        {
                            var exprVar   = generator.AssignToVariable(expr, "nullable");
                            var resultVar = generator.AssignToVariable(defaultIfEmpty.DefaultValue, "result");

                            generator.AddExpression(Expression.IfThen(
                                                        Expression.NotEqual(exprVar, Expression.Constant(null)),
                                                        Expression.Assign(resultVar, Expression.Convert(exprVar, resultVar.Type))));

                            generator.AddExpression(resultVar);

                            expr = generator.Build();
                        }
                    }
                }
                else
                if (_returnType.IsClass || _methodName == "Sum" || _returnType.IsNullable())
                {
                    expr = Builder.BuildSql(_returnType, fieldIndex, sqlExpression);
                }
                else
                {
                    expr = Expression.Block(
                        Expression.Call(null, MemberHelper.MethodOf(() => CheckNullValue(false, null !)), Expression.Call(ExpressionBuilder.DataReaderParam, Methods.ADONet.IsDBNull, Expression.Constant(0)), Expression.Constant(_methodName)),
                        Builder.BuildSql(_returnType, fieldIndex, sqlExpression));
                }

                return(expr);
            }
Ejemplo n.º 3
0
        private static OracleProviderAdapter CreateAdapter(string assemblyName, string clientNamespace, string typesNamespace, string?factoryName)
        {
            var isNative = false;

#if NETFRAMEWORK
            isNative = assemblyName == NativeAssemblyName;
#endif

            var assembly = Common.Tools.TryLoadAssembly(assemblyName, factoryName);
            if (assembly == null)
            {
                throw new InvalidOperationException($"Cannot load assembly {assemblyName}");
            }

            var connectionType  = assembly.GetType($"{clientNamespace}.OracleConnection", true) !;
            var parameterType   = assembly.GetType($"{clientNamespace}.OracleParameter", true) !;
            var dataReaderType  = assembly.GetType($"{clientNamespace}.OracleDataReader", true) !;
            var transactionType = assembly.GetType($"{clientNamespace}.OracleTransaction", true) !;
            var dbType          = assembly.GetType($"{clientNamespace}.OracleDbType", true) !;
            var commandType     = assembly.GetType($"{clientNamespace}.OracleCommand", true) !;

            var mappingSchema = new MappingSchema();

            // do not set default conversion for BFile as it could be converted to file name, byte[], Stream and we don't know what user needs
            var oracleBFileType        = loadType("OracleBFile", DataType.BFile, skipConvertExpression: true) !;
            var oracleBinaryType       = loadType("OracleBinary", DataType.VarBinary) !;
            var oracleBlobType         = loadType("OracleBlob", DataType.Blob) !;
            var oracleClobType         = loadType("OracleClob", DataType.NText) !;
            var oracleDateType         = loadType("OracleDate", DataType.DateTime) !;
            var oracleDecimalType      = loadType("OracleDecimal", DataType.Decimal) !;
            var oracleIntervalDSType   = loadType("OracleIntervalDS", DataType.Time) !;
            var oracleIntervalYMType   = loadType("OracleIntervalYM", DataType.Date) !;
            var oracleStringType       = loadType("OracleString", DataType.NVarChar) !;
            var oracleTimeStampType    = loadType("OracleTimeStamp", DataType.DateTime2) !;
            var oracleTimeStampLTZType = loadType("OracleTimeStampLTZ", DataType.DateTimeOffset) !;
            var oracleTimeStampTZType  = loadType("OracleTimeStampTZ", DataType.DateTimeOffset) !;
            var oracleXmlTypeType      = loadType("OracleXmlType", DataType.Xml) !;
            var oracleXmlStreamType    = loadType("OracleXmlStream", DataType.Xml, true, false) !;
            var oracleRefCursorType    = loadType("OracleRefCursor", DataType.Binary, hasValue: false) !;
            var oracleRefType          = loadType("OracleRef", DataType.Binary, true);

            BulkCopyAdapter?bulkCopy   = null;
            var             typeMapper = new TypeMapper();

            typeMapper.RegisterTypeWrapper <OracleConnection>(connectionType);
            typeMapper.RegisterTypeWrapper <OracleParameter>(parameterType);
            typeMapper.RegisterTypeWrapper <OracleDbType>(dbType);
            typeMapper.RegisterTypeWrapper <OracleCommand>(commandType);
            typeMapper.RegisterTypeWrapper <OracleDataReader>(dataReaderType);
            typeMapper.RegisterTypeWrapper <OracleTimeStampTZ>(oracleTimeStampTZType);
            typeMapper.RegisterTypeWrapper <OracleTimeStampLTZ>(oracleTimeStampLTZType);
            typeMapper.RegisterTypeWrapper <OracleDecimal>(oracleDecimalType);

            if (isNative)
            {
                var bulkCopyType                        = assembly.GetType($"{clientNamespace}.OracleBulkCopy", true) !;
                var bulkCopyOptionsType                 = assembly.GetType($"{clientNamespace}.OracleBulkCopyOptions", true) !;
                var bulkRowsCopiedEventHandlerType      = assembly.GetType($"{clientNamespace}.OracleRowsCopiedEventHandler", true) !;
                var bulkCopyColumnMappingType           = assembly.GetType($"{clientNamespace}.OracleBulkCopyColumnMapping", true) !;
                var bulkCopyColumnMappingCollectionType = assembly.GetType($"{clientNamespace}.OracleBulkCopyColumnMappingCollection", true) !;
                var rowsCopiedEventArgsType             = assembly.GetType($"{clientNamespace}.OracleRowsCopiedEventArgs", true) !;

                // bulk copy types
                typeMapper.RegisterTypeWrapper <OracleBulkCopy>(bulkCopyType);
                typeMapper.RegisterTypeWrapper <OracleBulkCopyOptions>(bulkCopyOptionsType);
                typeMapper.RegisterTypeWrapper <OracleRowsCopiedEventHandler>(bulkRowsCopiedEventHandlerType);
                typeMapper.RegisterTypeWrapper <OracleBulkCopyColumnMapping>(bulkCopyColumnMappingType);
                typeMapper.RegisterTypeWrapper <OracleBulkCopyColumnMappingCollection>(bulkCopyColumnMappingCollectionType);
                typeMapper.RegisterTypeWrapper <OracleRowsCopiedEventArgs>(rowsCopiedEventArgsType);
                typeMapper.FinalizeMappings();

                bulkCopy = new BulkCopyAdapter(
                    typeMapper.BuildWrappedFactory((IDbConnection connection, OracleBulkCopyOptions options) => new OracleBulkCopy((OracleConnection)connection, options)),
                    typeMapper.BuildWrappedFactory((int source, string destination) => new OracleBulkCopyColumnMapping(source, destination)));
            }
            else
            {
                typeMapper.FinalizeMappings();
            }

            var paramMapper      = typeMapper.Type <OracleParameter>();
            var dbTypeBuilder    = paramMapper.Member(p => p.OracleDbType);
            var connectionMapper = typeMapper.Type <OracleConnection>();
            var commandMapper    = typeMapper.Type <OracleCommand>();

            // data reader expressions
            // rd.GetOracleTimeStampTZ(i) => DateTimeOffset
            var generator    = new ExpressionGenerator(typeMapper);
            var rdParam      = Expression.Parameter(typeof(IDataReader), "rd");
            var indexParam   = Expression.Parameter(typeof(int), "i");
            var tstzExpr     = generator.MapExpression((IDataReader rd, int i) => ((OracleDataReader)rd).GetOracleTimeStampTZ(i), rdParam, indexParam);
            var tstzVariable = generator.AssignToVariable(tstzExpr, "tstz");
            var expr         = generator.MapExpression((OracleTimeStampTZ tstz) => new DateTimeOffset(
                                                           tstz.Year, tstz.Month, tstz.Day,
                                                           tstz.Hour, tstz.Minute, tstz.Second,
                                                           tstz.GetTimeZoneOffset()).AddTicks(tstz.Nanosecond / NanosecondsPerTick), tstzVariable);
            generator.AddExpression(expr);
            var body = generator.Build();
            var readDateTimeOffsetFromOracleTimeStampTZ = (Expression <Func <IDataReader, int, DateTimeOffset> >)Expression.Lambda(body, rdParam, indexParam);

            // rd.GetOracleTimeStampLTZ(i) => DateTimeOffset
            generator    = new ExpressionGenerator(typeMapper);
            tstzExpr     = generator.MapExpression((IDataReader rd, int i) => ((OracleDataReader)rd).GetOracleTimeStampLTZ(i).ToOracleTimeStampTZ(), rdParam, indexParam);
            tstzVariable = generator.AssignToVariable(tstzExpr, "tstz");
            expr         = generator.MapExpression((OracleTimeStampTZ tstz) => new DateTimeOffset(
                                                       tstz.Year, tstz.Month, tstz.Day,
                                                       tstz.Hour, tstz.Minute, tstz.Second,
                                                       tstz.GetTimeZoneOffset()).AddTicks(tstz.Nanosecond / NanosecondsPerTick), tstzVariable);
            generator.AddExpression(expr);
            body = generator.Build();
            var readDateTimeOffsetFromOracleTimeStampLTZ = (Expression <Func <IDataReader, int, DateTimeOffset> >)Expression.Lambda(body, rdParam, indexParam);

            // rd.GetOracleDecimal(i) => decimal
            generator = new ExpressionGenerator(typeMapper);
            var decExpr          = generator.MapExpression((IDataReader rd, int i) => ((OracleDataReader)rd).GetOracleDecimal(i), rdParam, indexParam);
            var oracleDecimalVar = generator.AssignToVariable(decExpr, "dec");
            var precision        = generator.AssignToVariable(Expression.Constant(29), "precision");
            var decimalVar       = generator.AddVariable(Expression.Parameter(typeof(decimal), "dec"));
            var label            = Expression.Label(typeof(decimal));

            generator.AddExpression(
                Expression.Loop(
                    Expression.TryCatch(
                        Expression.Block(
                            Expression.Assign(oracleDecimalVar, generator.MapExpression((OracleDecimal d, int p) => OracleDecimal.SetPrecision(d, p), oracleDecimalVar, precision)),
                            Expression.Assign(decimalVar, Expression.Convert(oracleDecimalVar, typeof(decimal))),
                            Expression.Break(label, decimalVar)),
                        Expression.Catch(
                            typeof(OverflowException),
                            Expression.Block(
                                Expression.IfThen(
                                    Expression.LessThanOrEqual(Expression.SubtractAssign(precision, Expression.Constant(1)), Expression.Constant(26)),
                                    Expression.Rethrow())))),
                    label));

            body = generator.Build();

            var readOracleDecimalToDecimalAdv = (Expression <Func <IDataReader, int, decimal> >)Expression.Lambda(body, rdParam, indexParam);
            // workaround for mapper issue with complex reader expressions handling
            // https://github.com/linq2db/linq2db/issues/2032
            var compiledReader = readOracleDecimalToDecimalAdv.Compile();
            readOracleDecimalToDecimalAdv = (Expression <Func <IDataReader, int, decimal> >)Expression.Lambda(
                Expression.Invoke(Expression.Constant(compiledReader), rdParam, indexParam),
                rdParam,
                indexParam);

            var readOracleDecimalToInt     = (Expression <Func <IDataReader, int, int> >)typeMapper.MapLambda <IDataReader, int, int>((rd, i) => (int)(decimal)OracleDecimal.SetPrecision(((OracleDataReader)rd).GetOracleDecimal(i), 27));
            var readOracleDecimalToLong    = (Expression <Func <IDataReader, int, long> >)typeMapper.MapLambda <IDataReader, int, long>((rd, i) => (long)(decimal)OracleDecimal.SetPrecision(((OracleDataReader)rd).GetOracleDecimal(i), 27));
            var readOracleDecimalToDecimal = (Expression <Func <IDataReader, int, decimal> >)typeMapper.MapLambda <IDataReader, int, decimal>((rd, i) => (decimal)OracleDecimal.SetPrecision(((OracleDataReader)rd).GetOracleDecimal(i), 27));

            return(new OracleProviderAdapter(
                       connectionType,
                       dataReaderType,
                       parameterType,
                       commandType,
                       transactionType,
                       mappingSchema,

                       oracleBFileType,
                       oracleBinaryType,
                       oracleBlobType,
                       oracleClobType,
                       oracleDateType,
                       oracleDecimalType,
                       oracleIntervalDSType,
                       oracleIntervalYMType,
                       oracleStringType,
                       oracleTimeStampType,
                       oracleTimeStampLTZType,
                       oracleTimeStampTZType,
                       oracleXmlTypeType,
                       oracleXmlStreamType,
                       oracleRefCursorType,
                       oracleRefType,

                       typeMapper.BuildWrappedFactory((string connectionString) => new OracleConnection(connectionString)),

                       typesNamespace,

                       dbTypeBuilder.BuildSetter <IDbDataParameter>(),
                       dbTypeBuilder.BuildGetter <IDbDataParameter>(),

                       connectionMapper.Member(c => c.HostName).BuildGetter <IDbConnection>(),
                       connectionMapper.Member(c => c.DatabaseName).BuildGetter <IDbConnection>(),


                       commandMapper.Member(p => p.BindByName).BuildSetter <IDbCommand>(),
                       commandMapper.Member(p => p.ArrayBindCount).BuildSetter <IDbCommand>(),
                       commandMapper.Member(p => p.InitialLONGFetchSize).BuildSetter <IDbCommand>(),

                       typeMapper.BuildFactory((DateTimeOffset dto, string offset) => new OracleTimeStampTZ(dto.Year, dto.Month, dto.Day, dto.Hour, dto.Minute, dto.Second, GetDateTimeOffsetNanoseconds(dto), offset)),

                       readDateTimeOffsetFromOracleTimeStampTZ,
                       readDateTimeOffsetFromOracleTimeStampLTZ,
                       readOracleDecimalToDecimalAdv,
                       readOracleDecimalToInt,
                       readOracleDecimalToLong,
                       readOracleDecimalToDecimal,
                       bulkCopy));

            Type?loadType(string typeName, DataType dataType, bool optional = false, bool hasNull = true, bool hasValue = true, bool skipConvertExpression = false)
            {
                var type = assembly !.GetType($"{typesNamespace}.{typeName}", !optional);

                if (type == null)
                {
                    return(null);
                }

                if (hasNull)
                {
                    // if native provider fails here, check that you have ODAC installed properly
                    var getNullValue = Expression.Lambda <Func <object> >(Expression.Convert(ExpressionHelper.Field(type, "Null"), typeof(object))).Compile();
                    mappingSchema.AddScalarType(type, getNullValue(), true, dataType);
                }
                else
                {
                    mappingSchema.AddScalarType(type, null, true, dataType);
                }

                if (skipConvertExpression)
                {
                    return(type);
                }

                // conversion from provider-specific type
                var valueParam = Expression.Parameter(type);

                Expression memberExpression;

                if (!hasValue)
                {
                    memberExpression = valueParam;
                }
                else
                {
                    memberExpression = ExpressionHelper.Property(valueParam, "Value");
                }

                var condition = Expression.Condition(
                    Expression.Equal(valueParam, ExpressionHelper.Field(type, "Null")),
                    Expression.Constant(null, typeof(object)),
                    Expression.Convert(memberExpression, typeof(object)));

                var convertExpression = Expression.Lambda(condition, valueParam);

                mappingSchema.SetConvertExpression(type, typeof(object), convertExpression);

                return(type);
            }
        }
Ejemplo n.º 4
0
        public void Setup()
        {
            var typeMapper = new TypeMapper();

            typeMapper.RegisterTypeWrapper <Wrapped.OracleDataReader>(typeof(Original.OracleDataReader));
            typeMapper.RegisterTypeWrapper <Wrapped.OracleTimeStampTZ>(typeof(Original.OracleTimeStampTZ));
            typeMapper.RegisterTypeWrapper <Wrapped.OracleTimeStampLTZ>(typeof(Original.OracleTimeStampLTZ));
            typeMapper.RegisterTypeWrapper <Wrapped.OracleDecimal>(typeof(Original.OracleDecimal));

            typeMapper.FinalizeMappings();

            // _readDateTimeOffsetFromOracleTimeStampTZ
            var generator    = new ExpressionGenerator(typeMapper);
            var rdParam      = Expression.Parameter(typeof(ITestDataReader), "rd");
            var indexParam   = Expression.Parameter(typeof(int), "i");
            var tstzExpr     = generator.MapExpression((ITestDataReader rd, int i) => ((Wrapped.OracleDataReader)rd).GetOracleTimeStampTZ(i), rdParam, indexParam);
            var tstzVariable = generator.AssignToVariable(tstzExpr, "tstz");
            var expr         = generator.MapExpression((Wrapped.OracleTimeStampTZ tstz) => new DateTimeOffset(
                                                           tstz.Year, tstz.Month, tstz.Day,
                                                           tstz.Hour, tstz.Minute, tstz.Second,
                                                           tstz.GetTimeZoneOffset()).AddTicks(tstz.Nanosecond / NanosecondsPerTick), tstzVariable);

            generator.AddExpression(expr);
            var body = generator.Build();

            _readDateTimeOffsetFromOracleTimeStampTZ = ((Expression <Func <ITestDataReader, int, DateTimeOffset> >)Expression.Lambda(body, rdParam, indexParam)).Compile();

            // _readDateTimeOffsetFromOracleTimeStampLTZ
            generator    = new ExpressionGenerator(typeMapper);
            tstzExpr     = generator.MapExpression((ITestDataReader rd, int i) => ((Wrapped.OracleDataReader)rd).GetOracleTimeStampLTZ(i).ToOracleTimeStampTZ(), rdParam, indexParam);
            tstzVariable = generator.AssignToVariable(tstzExpr, "tstz");
            expr         = generator.MapExpression((Wrapped.OracleTimeStampTZ tstz) => new DateTimeOffset(
                                                       tstz.Year, tstz.Month, tstz.Day,
                                                       tstz.Hour, tstz.Minute, tstz.Second,
                                                       tstz.GetTimeZoneOffset()).AddTicks(tstz.Nanosecond / NanosecondsPerTick), tstzVariable);
            generator.AddExpression(expr);
            body = generator.Build();
            _readDateTimeOffsetFromOracleTimeStampLTZ = ((Expression <Func <ITestDataReader, int, DateTimeOffset> >)Expression.Lambda(body, rdParam, indexParam)).Compile();

            // rd.GetOracleDecimal(i) => decimal
            generator = new ExpressionGenerator(typeMapper);
            var decExpr          = generator.MapExpression((ITestDataReader rd, int i) => ((Wrapped.OracleDataReader)rd).GetOracleDecimal(i), rdParam, indexParam);
            var oracleDecimalVar = generator.AssignToVariable(decExpr, "dec");
            var precision        = generator.AssignToVariable(Expression.Constant(29), "precision");
            var decimalVar       = generator.AddVariable(Expression.Parameter(typeof(decimal), "dec"));
            var label            = Expression.Label(typeof(decimal));

            generator.AddExpression(
                Expression.Loop(
                    Expression.TryCatch(
                        Expression.Block(
                            Expression.Assign(oracleDecimalVar, generator.MapExpression((Wrapped.OracleDecimal d, int p) => Wrapped.OracleDecimal.SetPrecision(d, p), oracleDecimalVar, precision)),
                            Expression.Assign(decimalVar, Expression.Convert(oracleDecimalVar, typeof(decimal))),
                            Expression.Break(label, decimalVar)),
                        Expression.Catch(
                            typeof(OverflowException),
                            Expression.Block(
                                Expression.IfThen(
                                    Expression.LessThanOrEqual(Expression.SubtractAssign(precision, Expression.Constant(1)), Expression.Constant(26)),
                                    Expression.Rethrow())))),
                    label));

            body = generator.Build();

            var readOracleDecimalToDecimalAdv = (Expression <Func <ITestDataReader, int, decimal> >)Expression.Lambda(body, rdParam, indexParam);
            // workaround for mapper issue with complex reader expressions handling
            // https://github.com/linq2db/linq2db/issues/2032
            var compiledReader = readOracleDecimalToDecimalAdv.Compile();

            _readOracleDecimalToDecimalAdv = ((Expression <Func <ITestDataReader, int, decimal> >)Expression.Lambda(
                                                  Expression.Invoke(Expression.Constant(compiledReader), rdParam, indexParam),
                                                  rdParam,
                                                  indexParam)).Compile();

            _readOracleDecimalToInt     = ((Expression <Func <ITestDataReader, int, int> >)typeMapper.MapLambda <ITestDataReader, int, int>((rd, i) => (int)(decimal)Wrapped.OracleDecimal.SetPrecision(((Wrapped.OracleDataReader)rd).GetOracleDecimal(i), 27))).Compile();
            _readOracleDecimalToLong    = ((Expression <Func <ITestDataReader, int, long> >)typeMapper.MapLambda <ITestDataReader, int, long>((rd, i) => (long)(decimal)Wrapped.OracleDecimal.SetPrecision(((Wrapped.OracleDataReader)rd).GetOracleDecimal(i), 27))).Compile();
            _readOracleDecimalToDecimal = ((Expression <Func <ITestDataReader, int, decimal> >)typeMapper.MapLambda <ITestDataReader, int, decimal>((rd, i) => (decimal)Wrapped.OracleDecimal.SetPrecision(((Wrapped.OracleDataReader)rd).GetOracleDecimal(i), 27))).Compile();
        }