diff --git a/src/EFCore.SqlServer/Storage/Internal/SqlServerStringTypeMapping.cs b/src/EFCore.SqlServer/Storage/Internal/SqlServerStringTypeMapping.cs index 96884903e92..92dc3a42e62 100644 --- a/src/EFCore.SqlServer/Storage/Internal/SqlServerStringTypeMapping.cs +++ b/src/EFCore.SqlServer/Storage/Internal/SqlServerStringTypeMapping.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Generic; using System.Data; using System.Data.Common; using System.Text; @@ -171,6 +172,10 @@ protected override string GenerateNonNullSqlLiteral(object value) int length; var concatenated = false; var openApostrophe = false; + var lastConcatStartPoint = 0; + var concatCount = 1; + var concatStartList = new List(); + var useOldBehavior = AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue23518", out var enabled) && enabled; for (i = 0; i < stringValue.Length; i++) { var lineFeed = stringValue[i] == '\n'; @@ -183,11 +188,7 @@ protected override string GenerateNonNullSqlLiteral(object value) { if (!openApostrophe) { - if (builder.Length != 0) - { - builder.Append(", "); - concatenated = true; - } + AddConcateOperatorIfNeeded(); if (IsUnicode) { @@ -209,11 +210,7 @@ protected override string GenerateNonNullSqlLiteral(object value) openApostrophe = false; } - if (builder.Length != 0) - { - builder.Append(", "); - concatenated = true; - } + AddConcateOperatorIfNeeded(); if (IsUnicode) { @@ -229,11 +226,7 @@ protected override string GenerateNonNullSqlLiteral(object value) { if (!openApostrophe) { - if (builder.Length != 0) - { - builder.Append(", "); - concatenated = true; - } + AddConcateOperatorIfNeeded(); if (IsUnicode) { @@ -253,11 +246,7 @@ protected override string GenerateNonNullSqlLiteral(object value) { if (!openApostrophe) { - if (builder.Length != 0) - { - builder.Append(", "); - concatenated = true; - } + AddConcateOperatorIfNeeded(); if (IsUnicode) { @@ -276,11 +265,21 @@ protected override string GenerateNonNullSqlLiteral(object value) builder.Append('\''); } - if (concatenated) + if (useOldBehavior) + { + if (concatenated) + { + builder.Insert(0, "CONCAT(") + .Append(')'); + } + } + else { - builder - .Insert(0, "CONCAT(") - .Append(')'); + for (var j = concatStartList.Count - 1; j >= 0; j--) + { + builder.Insert(concatStartList[j], "CONCAT(") + .Append(')'); + } } if (builder.Length == 0) @@ -294,6 +293,33 @@ protected override string GenerateNonNullSqlLiteral(object value) } return builder.ToString(); + + void AddConcateOperatorIfNeeded() + { + if (builder.Length != 0) + { + builder.Append(", "); + if (useOldBehavior) + { + concatenated = true; + } + else + { + concatCount++; + + if (concatCount == 2) + { + concatStartList.Add(lastConcatStartPoint); + } + + if (concatCount == 254) + { + lastConcatStartPoint = builder.Length; + concatCount = 1; + } + } + } + } } } } diff --git a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteStringTypeMapping.cs b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteStringTypeMapping.cs index 37dc0fc2560..791e4f6a7f0 100644 --- a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteStringTypeMapping.cs +++ b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteStringTypeMapping.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Generic; using System.Data; using System.Text; using JetBrains.Annotations; @@ -66,6 +67,9 @@ protected override string GenerateNonNullSqlLiteral(object value) int length; var concatenated = false; var openApostrophe = false; + var concatCount = 1; + var nestedBracketCount = 0; + var useOldBehavior = AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue23459", out var enabled) && enabled; for (i = 0; i < stringValue.Length; i++) { var lineFeed = stringValue[i] == '\n'; @@ -78,11 +82,7 @@ protected override string GenerateNonNullSqlLiteral(object value) { if (!openApostrophe) { - if (builder.Length != 0) - { - builder.Append(" || "); - concatenated = true; - } + AddConcateOperatorIfNeeded(); builder.Append('\''); openApostrophe = true; @@ -99,11 +99,7 @@ protected override string GenerateNonNullSqlLiteral(object value) openApostrophe = false; } - if (builder.Length != 0) - { - builder.Append(" || "); - concatenated = true; - } + AddConcateOperatorIfNeeded(); builder .Append("CHAR(") @@ -115,11 +111,7 @@ protected override string GenerateNonNullSqlLiteral(object value) { if (!openApostrophe) { - if (builder.Length != 0) - { - builder.Append(" || "); - concatenated = true; - } + AddConcateOperatorIfNeeded(); builder.Append("'"); openApostrophe = true; @@ -137,11 +129,7 @@ protected override string GenerateNonNullSqlLiteral(object value) { if (!openApostrophe) { - if (builder.Length != 0) - { - builder.Append(" || "); - concatenated = true; - } + AddConcateOperatorIfNeeded(); builder.Append('\''); openApostrophe = true; @@ -155,6 +143,15 @@ protected override string GenerateNonNullSqlLiteral(object value) builder.Append('\''); } + if (!useOldBehavior) + { + while (nestedBracketCount > 0) + { + builder.Append(")"); + nestedBracketCount--; + } + } + if (concatenated) { builder @@ -168,6 +165,26 @@ protected override string GenerateNonNullSqlLiteral(object value) } return builder.ToString(); + + void AddConcateOperatorIfNeeded() + { + if (builder.Length != 0) + { + builder.Append(" || "); + concatenated = true; + + if (!useOldBehavior) + { + concatCount++; + if (concatCount == 998) + { + builder.Append("("); + concatCount = 1; + nestedBracketCount++; + } + } + } + } } } } diff --git a/test/EFCore.Specification.Tests/BuiltInDataTypesTestBase.cs b/test/EFCore.Specification.Tests/BuiltInDataTypesTestBase.cs index 87b6faf777f..0554261cfc8 100644 --- a/test/EFCore.Specification.Tests/BuiltInDataTypesTestBase.cs +++ b/test/EFCore.Specification.Tests/BuiltInDataTypesTestBase.cs @@ -2084,6 +2084,14 @@ public virtual void Optional_datetime_reading_null_from_database() } } + [ConditionalFact] + public virtual void Can_insert_query_multiline_string() + { + using var context = CreateContext(); + + Assert.Equal(Fixture.ReallyLargeString, Assert.Single(context.Set()).Value); + } + public abstract class BuiltInDataTypesFixtureBase : SharedStoreFixtureBase { protected override string StoreName { get; } = "BuiltInDataTypes"; @@ -2091,6 +2099,9 @@ public abstract class BuiltInDataTypesFixtureBase : SharedStoreFixtureBase 9000; + public virtual string ReallyLargeString + => string.Join("", Enumerable.Repeat(Environment.NewLine, 1001)); + protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context) { modelBuilder.Entity(); @@ -2346,6 +2357,14 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con .HasData( new DateTimeEnclosure { Id = 1, DateTimeOffset = new DateTimeOffset(2020, 3, 12, 1, 1, 1, new TimeSpan(3, 0, 0)) }, new DateTimeEnclosure { Id = 2 }); + + modelBuilder.Entity() + .HasData( + new StringEnclosure + { + Id = 1, + Value = ReallyLargeString + }); } protected static void MakeRequired(ModelBuilder modelBuilder) @@ -3152,5 +3171,12 @@ protected class DateTimeEnclosure public int Id { get; set; } public DateTimeOffset? DateTimeOffset { get; set; } } + + protected class StringEnclosure + { + public int Id { get; set; } + + public string Value { get; set; } + } } }