示例#1
0
        /// <summary>
        ///     Creates a new <see cref="TypeMaterializationInfo" /> instance.
        /// </summary>
        /// <param name="modelClrType"> The type that is needed in the model after conversion. </param>
        /// <param name="property"> The property associated with the type, or <c>null</c> if none. </param>
        /// <param name="typeMappingSource"> The type mapping source to use to find a mapping if the property does not have one already bound. </param>
        /// <param name="fromLeftOuterJoin"> Whether or not the value is coming from a LEFT OUTER JOIN operation. </param>
        /// <param name="index">
        ///     The index of the underlying result set that should be used for this type,
        ///     or -1 if no index mapping is needed.
        /// </param>
        /// <param name="mapping"> The type mapping to use or <c>null</c> to infer one. </param>
        public TypeMaterializationInfo(
            [NotNull] Type modelClrType,
            [CanBeNull] IProperty property,
            [CanBeNull] IRelationalTypeMappingSource typeMappingSource,
            bool?fromLeftOuterJoin,
            int index = -1,
            [CanBeNull] RelationalTypeMapping mapping = null)
        {
            Check.NotNull(modelClrType, nameof(modelClrType));

            if (mapping == null)
            {
                mapping = property?.FindRelationalMapping()
                          ?? typeMappingSource?.GetMapping(modelClrType);
            }

            ProviderClrType = mapping?.Converter?.ProviderClrType
                              ?? modelClrType;

            ModelClrType        = modelClrType;
            Mapping             = mapping;
            Property            = property;
            Index               = index;
            IsFromLeftOuterJoin = fromLeftOuterJoin;
        }
 /// <summary>
 ///     Gets the relational database type for a given object, throwing if no mapping is found.
 /// </summary>
 /// <param name="typeMappingSource">The type mapping source.</param>
 /// <param name="value">The object to get the mapping for.</param>
 /// <returns>The type mapping to be used.</returns>
 public static RelationalTypeMapping GetMappingForValue(
     this IRelationalTypeMappingSource typeMappingSource,
     object?value)
 => value == null ||
 value == DBNull.Value
             ? RelationalTypeMapping.NullMapping
             : typeMappingSource.GetMapping(value.GetType());
示例#3
0
        /// <summary>
        ///     This API supports the Entity Framework Core infrastructure and is not intended to be used
        ///     directly from your code. This API may change or be removed in future releases.
        /// </summary>
        public override void AddDbParameter(DbCommand command, object value)
        {
            Check.NotNull(command, nameof(command));

            if (value == null)
            {
                command.Parameters
                .Add(
                    _typeMappingSource.GetMappingForValue(null)
                    .CreateParameter(command, Name, null));

                return;
            }

            if (value is DbParameter dbParameter)
            {
                command.Parameters.Add(dbParameter);

                return;
            }

            var type = value.GetType();

            command.Parameters.Add(
                _typeMappingSource.GetMapping(type)
                .CreateParameter(command, Name, value, type.IsNullableType()));
        }
示例#4
0
 /// <summary>
 ///     Gets the relational database type for a given object, throwing if no mapping is found.
 /// </summary>
 /// <param name="typeMappingSource"> The type mapping source. </param>
 /// <param name="value"> The object to get the mapping for. </param>
 /// <returns> The type mapping to be used. </returns>
 public static RelationalTypeMapping GetMappingForValue(
     [CanBeNull] this IRelationalTypeMappingSource typeMappingSource,
     [CanBeNull] object value)
 => value == null ||
 value == DBNull.Value ||
 typeMappingSource == null
             ? RelationalTypeMapping.NullMapping
             : typeMappingSource.GetMapping(value.GetType());
示例#5
0
        protected override Expression VisitParameter(ParameterExpression parameterExpression)
        {
            if (!_predicateGenerating || parameterExpression?.Type != typeof(string))
            {
                return(base.VisitParameter(parameterExpression));
            }

            var parameterName = SqlGenerator.GenerateParameterName(parameterExpression.Name);

            if (Sql.ParameterBuilder.Parameters
                .All(p => p.InvariantName != parameterExpression.Name))
            {
                var parameterType = parameterExpression.Type.UnwrapNullableType();
                var typeMapping   = TypeMappingSource.GetMapping(parameterType);

                if (ParameterValues.ContainsKey(parameterExpression.Name))
                {
                    var value = ParameterValues[parameterExpression.Name];

                    typeMapping = TypeMappingSource.GetMappingForValue(value);

                    if (typeMapping == null ||
                        (!typeMapping.ClrType.UnwrapNullableType().IsAssignableFrom(parameterType) &&
                         (parameterType.IsEnum ||
                          !typeof(IConvertible).IsAssignableFrom(parameterType))))
                    {
                        typeMapping = TypeMappingSource.GetMapping(parameterType);
                    }
                }

                Sql.AddParameter(
                    parameterExpression.Name,
                    parameterName,
                    typeMapping,
                    parameterExpression.Type.IsNullableType());
            }

            var parameterNamePlaceholder = SqlGenerator.GenerateParameterNamePlaceholder(parameterExpression.Name);

            AddLowerFunctionToSqlQuery(parameterNamePlaceholder);

            return(parameterExpression);
        }
    /// <summary>
    ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to
    ///     the same compatibility standards as public APIs. It may be changed or removed without notice in
    ///     any release. You should only use it directly in your code with extreme caution and knowing that
    ///     doing so can result in application failures when updating to a new Entity Framework Core release.
    /// </summary>
    protected override Expression VisitExtension(Expression extensionExpression)
    {
        if (extensionExpression is TemporalTableExpression temporalTableExpression)
        {
            Sql.Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(temporalTableExpression.Name, temporalTableExpression.Schema))
            .Append(" FOR SYSTEM_TIME ");

            switch (temporalTableExpression)
            {
            case TemporalAsOfTableExpression asOf:
                Sql.Append("AS OF ")
                .Append(_typeMappingSource.GetMapping(typeof(DateTime)).GenerateSqlLiteral(asOf.PointInTime));
                break;

            case TemporalFromToTableExpression fromTo:
                Sql.Append("FROM ")
                .Append(_typeMappingSource.GetMapping(typeof(DateTime)).GenerateSqlLiteral(fromTo.From))
                .Append(" TO ")
                .Append(_typeMappingSource.GetMapping(typeof(DateTime)).GenerateSqlLiteral(fromTo.To));
                break;

            case TemporalBetweenTableExpression between:
                Sql.Append("BETWEEN ")
                .Append(_typeMappingSource.GetMapping(typeof(DateTime)).GenerateSqlLiteral(between.From))
                .Append(" AND ")
                .Append(_typeMappingSource.GetMapping(typeof(DateTime)).GenerateSqlLiteral(between.To));
                break;

            case TemporalContainedInTableExpression containedIn:
                Sql.Append("CONTAINED IN (")
                .Append(_typeMappingSource.GetMapping(typeof(DateTime)).GenerateSqlLiteral(containedIn.From))
                .Append(", ")
                .Append(_typeMappingSource.GetMapping(typeof(DateTime)).GenerateSqlLiteral(containedIn.To))
                .Append(")");
                break;

            case TemporalAllTableExpression:
                Sql.Append("ALL");
                break;

            default:
                throw new InvalidOperationException(temporalTableExpression.Print());
            }

            if (temporalTableExpression.Alias != null)
            {
                Sql.Append(AliasSeparator)
                .Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(temporalTableExpression.Alias));
            }

            return(temporalTableExpression);
        }

        return(base.VisitExtension(extensionExpression));
    }
    /// <summary>
    ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to
    ///     the same compatibility standards as public APIs. It may be changed or removed without notice in
    ///     any release. You should only use it directly in your code with extreme caution and knowing that
    ///     doing so can result in application failures when updating to a new Entity Framework Core release.
    /// </summary>
    public virtual RawSqlCommand Build(string sql, IEnumerable <object> parameters)
    {
        var relationalCommandBuilder = _relationalCommandBuilderFactory.Create();

        var substitutions = new List <string>();

        var parameterNameGenerator = _parameterNameGeneratorFactory.Create();

        var parameterValues = new Dictionary <string, object?>();

        foreach (var parameter in parameters)
        {
            if (parameter is DbParameter dbParameter)
            {
                if (string.IsNullOrEmpty(dbParameter.ParameterName))
                {
                    dbParameter.ParameterName = _sqlGenerationHelper.GenerateParameterName(parameterNameGenerator.GenerateNext());
                }

                substitutions.Add(_sqlGenerationHelper.GenerateParameterName(dbParameter.ParameterName));
                relationalCommandBuilder.AddRawParameter(dbParameter.ParameterName, dbParameter);
            }
            else
            {
                var parameterName   = parameterNameGenerator.GenerateNext();
                var substitutedName = _sqlGenerationHelper.GenerateParameterName(parameterName);

                substitutions.Add(substitutedName);
                var typeMapping = parameter == null
                        ? _typeMappingSource.GetMappingForValue(null)
                        : _typeMappingSource.GetMapping(parameter.GetType());

                var nullable = parameter == null || parameter.GetType().IsNullableType();

                relationalCommandBuilder.AddParameter(parameterName, substitutedName, typeMapping, nullable);
                parameterValues.Add(parameterName, parameter);
            }
        }

        // ReSharper disable once CoVariantArrayConversion
        sql = string.Format(sql, substitutions.ToArray());

        return(new RawSqlCommand(
                   relationalCommandBuilder.Append(sql).Build(),
                   parameterValues));
    }
示例#8
0
        /// <summary>
        ///     Creates a new <see cref="TypeMaterializationInfo" /> instance.
        /// </summary>
        /// <param name="modelClrType"> The type that is needed in the model after conversion. </param>
        /// <param name="property"> The property associated with the type, or <c>null</c> if none. </param>
        /// <param name="typeMappingSource"> The type mapper to use to find a mapping if the property does not have one already bound. </param>
        /// <param name="index">
        ///     The index of the underlying result set that should be used for this type,
        ///     or -1 if no index mapping is needed.
        /// </param>
        public TypeMaterializationInfo(
            [NotNull] Type modelClrType,
            [CanBeNull] IProperty property,
            [CanBeNull] IRelationalTypeMappingSource typeMappingSource,
            int index = -1)
        {
            Check.NotNull(modelClrType, nameof(modelClrType));

            var mapping = property?.FindRelationalMapping()
                          ?? typeMappingSource?.GetMapping(modelClrType);

            ProviderClrType = mapping?.Converter?.ProviderClrType
                              ?? modelClrType;

            ModelClrType = modelClrType;
            Mapping      = mapping;
            Property     = property;
            Index        = index;
        }
示例#9
0
        /// <summary>
        ///     This API supports the Entity Framework Core infrastructure and is not intended to be used
        ///     directly from your code. This API may change or be removed in future releases.
        /// </summary>
        public virtual TypeScaffoldingInfo FindMapping(
            string storeType,
            bool keyOrIndex,
            bool rowVersion)
        {
            // This is because certain providers can have no type specified as a default type e.g. SQLite
            Check.NotNull(storeType, nameof(storeType));

            var mapping = _typeMappingSource.FindMapping(storeType);

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

            var  canInfer            = false;
            bool?scaffoldUnicode     = null;
            bool?scaffoldFixedLength = null;
            int? scaffoldMaxLength   = null;

            if (mapping.ClrType == typeof(byte[]))
            {
                // Check for inference
                var byteArrayMapping = _typeMappingSource.FindMapping(
                    typeof(byte[]),
                    null,
                    keyOrIndex,
                    rowVersion: rowVersion,
                    size: mapping.Size,
                    fixedLength: mapping.IsFixedLength);

                if (byteArrayMapping.StoreType.Equals(storeType, StringComparison.OrdinalIgnoreCase))
                {
                    canInfer = true;

                    // Check for fixed-length
                    var fixedLengthMapping = _typeMappingSource.FindMapping(
                        typeof(byte[]),
                        null,
                        keyOrIndex,
                        rowVersion: rowVersion,
                        size: mapping.Size,
                        fixedLength: true);

                    scaffoldFixedLength = fixedLengthMapping.IsFixedLength != byteArrayMapping.IsFixedLength ? (bool?)byteArrayMapping.IsFixedLength : null;

                    // Check for size
                    var sizedMapping = _typeMappingSource.FindMapping(
                        typeof(byte[]),
                        null,
                        keyOrIndex,
                        rowVersion: rowVersion,
                        fixedLength: mapping.IsFixedLength);

                    scaffoldMaxLength = sizedMapping.Size != byteArrayMapping.Size ? byteArrayMapping.Size : null;
                }
            }
            else if (mapping.ClrType == typeof(string))
            {
                // Check for inference
                var stringMapping = _typeMappingSource.FindMapping(
                    typeof(string),
                    null,
                    keyOrIndex,
                    unicode: mapping.IsUnicode,
                    size: mapping.Size,
                    fixedLength: mapping.IsFixedLength);

                if (stringMapping.StoreType.Equals(storeType, StringComparison.OrdinalIgnoreCase))
                {
                    canInfer = true;

                    // Check for Unicode
                    var unicodeMapping = _typeMappingSource.FindMapping(
                        typeof(string),
                        null,
                        keyOrIndex,
                        unicode: true,
                        size: mapping.Size,
                        fixedLength: mapping.IsFixedLength);

                    scaffoldUnicode = unicodeMapping.IsUnicode != stringMapping.IsUnicode ? (bool?)stringMapping.IsUnicode : null;

                    // Check for fixed-length
                    var fixedLengthMapping = _typeMappingSource.FindMapping(
                        typeof(string),
                        null,
                        keyOrIndex,
                        unicode: mapping.IsUnicode,
                        size: mapping.Size,
                        fixedLength: true);

                    scaffoldFixedLength = fixedLengthMapping.IsFixedLength != stringMapping.IsFixedLength ? (bool?)stringMapping.IsFixedLength : null;

                    // Check for size
                    var sizedMapping = _typeMappingSource.FindMapping(
                        typeof(string),
                        null,
                        keyOrIndex,
                        unicode: mapping.IsUnicode,
                        fixedLength: mapping.IsFixedLength);

                    scaffoldMaxLength = sizedMapping.Size != stringMapping.Size ? stringMapping.Size : null;
                }
            }
            else
            {
                var defaultMapping = _typeMappingSource.GetMapping(mapping.ClrType);

                if (defaultMapping.StoreType.Equals(storeType, StringComparison.OrdinalIgnoreCase))
                {
                    canInfer = true;
                }
            }

            return(new TypeScaffoldingInfo(
                       mapping.ClrType,
                       canInfer,
                       scaffoldUnicode,
                       scaffoldMaxLength,
                       scaffoldFixedLength));
        }
        public virtual SqlExpression Translate(
            SqlExpression instance,
            MethodInfo method,
            IReadOnlyList <SqlExpression> arguments,
            IDiagnosticsLogger <DbLoggerCategory.Query> logger)
        {
            if (_indexOfMethodInfo.Equals(method))
            {
                return(new MySqlStringComparisonMethodTranslator(_sqlExpressionFactory, _options)
                       .MakeIndexOfExpression(instance, arguments[0]));
            }

            if (_replaceMethodInfo.Equals(method))
            {
                var stringTypeMapping   = ExpressionExtensions.InferTypeMapping(instance, arguments[0], arguments[1]);
                var replacementArgument = _sqlExpressionFactory.ApplyTypeMapping(arguments[1], stringTypeMapping);
                var replaceCall         = _sqlExpressionFactory.NullableFunction(
                    "REPLACE",
                    new[]
                {
                    _sqlExpressionFactory.ApplyTypeMapping(instance, stringTypeMapping),
                    _sqlExpressionFactory.ApplyTypeMapping(arguments[0], stringTypeMapping),
                    _sqlExpressionFactory.ApplyTypeMapping(arguments[1], stringTypeMapping)
                },
                    method.ReturnType,
                    stringTypeMapping);

                // Due to a bug in all versions of MariaDB and all MySQL versions below 8.0.x (exact version that fixed the issue is
                // currently unclear), using `null` as the replacement argument in a REPLACE() call leads to unexpected results, in which
                // the call returns the original string, instead of `null`.
                // See https://jira.mariadb.org/browse/MDEV-24263
                return(_sqlExpressionFactory.Case(
                           new[]
                {
                    new CaseWhenClause(
                        _sqlExpressionFactory.IsNotNull(replacementArgument),
                        replaceCall)
                },
                           _sqlExpressionFactory.Constant(null, RelationalTypeMapping.NullMapping)));
            }

            if (_toLowerMethodInfo.Equals(method) ||
                _toUpperMethodInfo.Equals(method))
            {
                return(_sqlExpressionFactory.NullableFunction(
                           _toLowerMethodInfo.Equals(method) ? "LOWER" : "UPPER",
                           new[] { instance },
                           method.ReturnType,
                           instance.TypeMapping));
            }

            if (_substringMethodInfoWithOneArg.Equals(method))
            {
                return(_sqlExpressionFactory.Function(
                           "SUBSTRING",
                           new[]
                {
                    instance,
                    _sqlExpressionFactory.Add(
                        arguments[0],
                        _sqlExpressionFactory.Constant(1)),
                    _sqlExpressionFactory.NullableFunction(
                        "CHAR_LENGTH",
                        new[] { instance },
                        typeof(int))
                },
                           nullable: true,
                           argumentsPropagateNullability: new[] { true, true, true },
                           method.ReturnType,
                           instance.TypeMapping));
            }

            if (_substringMethodInfoWithTwoArgs.Equals(method))
            {
                return(_sqlExpressionFactory.NullableFunction(
                           "SUBSTRING",
                           new[]
                {
                    instance,
                    _sqlExpressionFactory.Add(
                        arguments[0],
                        _sqlExpressionFactory.Constant(1)),
                    arguments[1]
                },
                           method.ReturnType,
                           instance.TypeMapping));
            }

            if (_isNullOrWhiteSpaceMethodInfo.Equals(method))
            {
                return(_sqlExpressionFactory.OrElse(
                           _sqlExpressionFactory.IsNull(arguments[0]),
                           _sqlExpressionFactory.Equal(
                               ProcessTrimMethod(arguments[0], null, null),
                               _sqlExpressionFactory.Constant(string.Empty))));
            }

            if (_trimStartMethodInfoWithoutArgs?.Equals(method) == true ||
                _trimStartMethodInfoWithCharArg?.Equals(method) == true ||
                _trimStartMethodInfoWithCharArrayArg.Equals(method))
            {
                return(ProcessTrimMethod(instance, arguments.Count > 0 ? arguments[0] : null, "LEADING"));
            }

            if (_trimEndMethodInfoWithoutArgs?.Equals(method) == true ||
                _trimEndMethodInfoWithCharArg?.Equals(method) == true ||
                _trimEndMethodInfoWithCharArrayArg.Equals(method))
            {
                return(ProcessTrimMethod(instance, arguments.Count > 0 ? arguments[0] : null, "TRAILING"));
            }

            if (_trimMethodInfoWithoutArgs?.Equals(method) == true ||
                _trimMethodInfoWithCharArg?.Equals(method) == true ||
                _trimMethodInfoWithCharArrayArg.Equals(method))
            {
                return(ProcessTrimMethod(instance, arguments.Count > 0 ? arguments[0] : null, null));
            }

            if (_containsMethodInfo.Equals(method))
            {
                return(new MySqlStringComparisonMethodTranslator(_sqlExpressionFactory, _options)
                       .MakeContainsExpression(instance, arguments[0]));
            }

            if (_startsWithMethodInfo.Equals(method))
            {
                return(new MySqlStringComparisonMethodTranslator(_sqlExpressionFactory, _options)
                       .MakeStartsWithExpression(instance, arguments[0]));
            }

            if (_endsWithMethodInfo.Equals(method))
            {
                return(new MySqlStringComparisonMethodTranslator(_sqlExpressionFactory, _options)
                       .MakeEndsWithExpression(instance, arguments[0]));
            }

            if (_padLeftWithOneArg.Equals(method))
            {
                return(TranslatePadLeftRight(
                           true,
                           instance,
                           arguments[0],
                           _sqlExpressionFactory.Constant(" "),
                           method.ReturnType));
            }

            if (_padRightWithOneArg.Equals(method))
            {
                return(TranslatePadLeftRight(
                           false,
                           instance,
                           arguments[0],
                           _sqlExpressionFactory.Constant(" "),
                           method.ReturnType));
            }

            if (_padLeftWithTwoArgs.Equals(method))
            {
                return(TranslatePadLeftRight(
                           true,
                           instance,
                           arguments[0],
                           arguments[1],
                           method.ReturnType));
            }

            if (_padRightWithTwoArgs.Equals(method))
            {
                return(TranslatePadLeftRight(
                           false,
                           instance,
                           arguments[0],
                           arguments[1],
                           method.ReturnType));
            }

            if (_firstOrDefaultMethodInfoWithoutArgs.Equals(method))
            {
                return(_sqlExpressionFactory.NullableFunction(
                           "SUBSTRING",
                           new[] { arguments[0], _sqlExpressionFactory.Constant(1), _sqlExpressionFactory.Constant(1) },
                           method.ReturnType));
            }

            if (_lastOrDefaultMethodInfoWithoutArgs.Equals(method))
            {
                return(_sqlExpressionFactory.NullableFunction(
                           "SUBSTRING",
                           new[]
                {
                    arguments[0],
                    _sqlExpressionFactory.NullableFunction(
                        "CHAR_LENGTH",
                        new[] { arguments[0] },
                        typeof(int)),
                    _sqlExpressionFactory.Constant(1)
                },
                           method.ReturnType));
            }

            if (_removeMethodInfoWithOneArg.Equals(method))
            {
                return(_sqlExpressionFactory.NullableFunction(
                           "SUBSTRING",
                           new[]
                {
                    instance,
                    _sqlExpressionFactory.Constant(1),
                    arguments[0],
                },
                           method.ReturnType,
                           instance.TypeMapping));
            }

            if (_removeMethodInfoWithTwoArgs.Equals(method))
            {
                var firstSubString = _sqlExpressionFactory.NullableFunction(
                    "SUBSTRING",
                    new[]
                {
                    instance,
                    _sqlExpressionFactory.Constant(1),
                    arguments[0]
                },
                    method.ReturnType,
                    instance.TypeMapping);

                var secondSubString = _sqlExpressionFactory.NullableFunction(
                    "SUBSTRING",
                    new[]
                {
                    instance,
                    _sqlExpressionFactory.Add(
                        _sqlExpressionFactory.Add(
                            arguments[0],
                            arguments[1]),
                        _sqlExpressionFactory.Constant(1)),
                    _sqlExpressionFactory.Subtract(
                        _sqlExpressionFactory.NullableFunction(
                            "CHAR_LENGTH",
                            new[] { instance },
                            typeof(int)),
                        _sqlExpressionFactory.Add(
                            arguments[0],
                            arguments[1])),
                },
                    method.ReturnType,
                    instance.TypeMapping);

                var concat = _sqlExpressionFactory.NullableFunction(
                    "CONCAT",
                    new[]
                {
                    firstSubString,
                    secondSubString
                },
                    method.ReturnType,
                    instance.TypeMapping);

                return(concat);
            }

            if (_concatMethodInfos.Contains(
                    (method.IsGenericMethod
                    ? method.GetGenericMethodDefinition()
                    : null) ?? method))
            {
                // Handle
                //     string[]
                //     IEnumerable<string>
                //     object[]
                //     IEnumerable<T>
                // and
                //     string, ...
                //     object, ...
                //
                // Some call signature variants can never reach this code, because they will be directly called and thus only their result
                // is translated.
                var concatArguments = arguments[0] is MySqlComplexFunctionArgumentExpression mySqlComplexFunctionArgumentExpression
                    ? new SqlExpression[] { mySqlComplexFunctionArgumentExpression }
                    : arguments.Select(
                    e => e switch
                {
                    SqlConstantExpression c => _sqlExpressionFactory.Constant(c.Value.ToString()),
                    SqlParameterExpression p => p.ApplyTypeMapping(
                        ((MySqlStringTypeMapping)_typeMappingSource.GetMapping(typeof(string))).Clone(forceToString: true)),
                    _ => e,
                })
    /// <summary>
    ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to
    ///     the same compatibility standards as public APIs. It may be changed or removed without notice in
    ///     any release. You should only use it directly in your code with extreme caution and knowing that
    ///     doing so can result in application failures when updating to a new Entity Framework Core release.
    /// </summary>
    protected override Expression VisitExtension(Expression extensionExpression)
    {
        if (extensionExpression is TableExpression tableExpression &&
            tableExpression.FindAnnotation(SqlServerAnnotationNames.TemporalOperationType) != null)
        {
            Sql.Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(tableExpression.Name, tableExpression.Schema))
            .Append(" FOR SYSTEM_TIME ");

            var temporalOperationType = (TemporalOperationType)tableExpression[SqlServerAnnotationNames.TemporalOperationType] !;

            switch (temporalOperationType)
            {
            case TemporalOperationType.All:
                Sql.Append("ALL");
                break;

            case TemporalOperationType.AsOf:
                var pointInTime = (DateTime)tableExpression[SqlServerAnnotationNames.TemporalAsOfPointInTime] !;

                Sql.Append("AS OF ")
                .Append(_typeMappingSource.GetMapping(typeof(DateTime)).GenerateSqlLiteral(pointInTime));
                break;

            case TemporalOperationType.Between:
            case TemporalOperationType.ContainedIn:
            case TemporalOperationType.FromTo:
                var from = _typeMappingSource.GetMapping(typeof(DateTime)).GenerateSqlLiteral(
                    (DateTime)tableExpression[SqlServerAnnotationNames.TemporalRangeOperationFrom] !);

                var to = _typeMappingSource.GetMapping(typeof(DateTime)).GenerateSqlLiteral(
                    (DateTime)tableExpression[SqlServerAnnotationNames.TemporalRangeOperationTo] !);

                switch (temporalOperationType)
                {
                case TemporalOperationType.FromTo:
                    Sql.Append($"FROM {from} TO {to}");
                    break;

                case TemporalOperationType.Between:
                    Sql.Append($"BETWEEN {from} AND {to}");
                    break;

                case TemporalOperationType.ContainedIn:
                    Sql.Append($"CONTAINED IN ({from}, {to})");
                    break;

                default:
                    throw new InvalidOperationException(tableExpression.Print());
                }

                break;

            default:
                throw new InvalidOperationException(tableExpression.Print());
            }

            if (tableExpression.Alias != null)
            {
                Sql.Append(AliasSeparator)
                .Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(tableExpression.Alias));
            }

            return(tableExpression);
        }

        return(base.VisitExtension(extensionExpression));
    }