Skip to content

Commit 407fee3

Browse files
SingThatSongcincuranet
authored andcommitted
MigrationHistory hex literal limit workaround (DNET-769)
1 parent d5398b6 commit 407fee3

File tree

2 files changed

+114
-4
lines changed

2 files changed

+114
-4
lines changed

Provider/src/EntityFramework.Firebird/FbMigrationSqlGenerator.cs

Lines changed: 97 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717

1818
using System;
1919
using System.Collections.Generic;
20+
using System.Collections.ObjectModel;
2021
using System.Data.Common;
2122
using System.Data.Entity;
2223
using System.Data.Entity.Core.Common;
2324
using System.Data.Entity.Core.Common.CommandTrees;
25+
using System.Data.Entity.Core.Common.CommandTrees.ExpressionBuilder;
2426
using System.Data.Entity.Core.Metadata.Edm;
2527
using System.Data.Entity.Infrastructure.DependencyResolution;
2628
using System.Data.Entity.Migrations.Model;
@@ -30,6 +32,7 @@
3032
using System.Text;
3133
using System.Text.RegularExpressions;
3234
using EntityFramework.Firebird.SqlGen;
35+
using FirebirdSql.Data.Common;
3336

3437
namespace EntityFramework.Firebird
3538
{
@@ -446,11 +449,101 @@ protected virtual IEnumerable<MigrationStatement> Generate(HistoryOperation oper
446449
switch (commandTree.CommandTreeKind)
447450
{
448451
case DbCommandTreeKind.Insert:
449-
using (var writer = SqlWriter())
452+
const int migrationIdColumn = 0;
453+
const int contextKeyColumn = 1;
454+
const int modelColumn = 2;
455+
const int versionColumn = 3;
456+
457+
// Trial and error value, not sure if correct or how to get correct one
458+
const int maxChunkLength = 32000;
459+
460+
var dbInsert = (DbInsertCommandTree)commandTree;
461+
var modelData = ((dbInsert.SetClauses[modelColumn] as DbSetClause).Value as DbConstantExpression).Value as byte[];
462+
463+
// If model length is less than max value, stick to original version
464+
if (modelData.Length < maxChunkLength)
450465
{
451-
writer.Write(DmlSqlGenerator.GenerateInsertSql((DbInsertCommandTree)commandTree, out _,
452-
generateParameters: false));
453-
yield return Statement(writer);
466+
using (var writer = SqlWriter())
467+
{
468+
writer.Write(DmlSqlGenerator.GenerateInsertSql(dbInsert, out _, generateParameters: false));
469+
yield return Statement(writer);
470+
}
471+
}
472+
else
473+
{
474+
// If it's bigger - we split it into chunks, as big as possible
475+
var dataChunks = modelData.Split(maxChunkLength);
476+
477+
// We can't change CommandTree, but we can create new one, only difference being data length
478+
using (var writer = SqlWriter())
479+
{
480+
ReadOnlyCollection<DbModificationClause> setClauses = new ReadOnlyCollection<DbModificationClause>(
481+
new List<DbModificationClause>
482+
{
483+
dbInsert.SetClauses[migrationIdColumn],
484+
dbInsert.SetClauses[contextKeyColumn],
485+
DbExpressionBuilder.SetClause(
486+
((DbSetClause)dbInsert.SetClauses[modelColumn]).Property,
487+
dataChunks.ElementAt(0).ToArray()
488+
),
489+
dbInsert.SetClauses[versionColumn],
490+
});
491+
492+
493+
var newCommandTree = new DbInsertCommandTree(
494+
dbInsert.MetadataWorkspace,
495+
commandTree.DataSpace,
496+
dbInsert.Target,
497+
setClauses,
498+
dbInsert.Returning);
499+
500+
writer.Write(DmlSqlGenerator.GenerateInsertSql(newCommandTree, out _, generateParameters: false));
501+
yield return Statement(writer);
502+
}
503+
504+
// Now we have first Insert, let's update it with chunks of remaing data
505+
foreach (var dataChunk in dataChunks.Skip(1))
506+
{
507+
using (var writer = SqlWriter())
508+
{
509+
DbPropertyExpression modelProperty = (dbInsert.SetClauses[modelColumn] as DbSetClause).Property as DbPropertyExpression;
510+
511+
ReadOnlyCollection<DbModificationClause> modificationClauses = new ReadOnlyCollection<DbModificationClause>(
512+
new List<DbModificationClause>
513+
{
514+
// Updating existing chunk of data with subsequent part
515+
DbExpressionBuilder.SetClause(
516+
modelProperty,
517+
// TODO: Better solution required
518+
// Best if we could use DbExpression.Concat, but it returns DbFunctionExpression, which is not supported
519+
// Here we'll get SET Model = 'data', which we can update as text later
520+
dataChunk.ToArray()
521+
)
522+
});
523+
524+
var updateCommandTree = new DbUpdateCommandTree(dbInsert.MetadataWorkspace,
525+
dbInsert.DataSpace,
526+
dbInsert.Target,
527+
// Predicate is MigrationId value
528+
DbExpressionBuilder.Equal(
529+
((DbSetClause)dbInsert.SetClauses[migrationIdColumn]).Property,
530+
((DbSetClause)dbInsert.SetClauses[migrationIdColumn]).Value),
531+
modificationClauses,
532+
dbInsert.Returning);
533+
534+
writer.Write(DmlSqlGenerator.GenerateUpdateSql(updateCommandTree, out _, generateParameters: false));
535+
536+
// Since we couldn't concat before, replacing query as string
537+
// Replacing SET Model = 'data'
538+
// with SET Model = Model || 'data'
539+
// Model being first is important, since these are parts of single value
540+
var statement = writer.ToString();
541+
var newStatement = statement.Replace($"SET \"{modelProperty.Property.Name}\" = ",
542+
$"SET \"{modelProperty.Property.Name}\" = \"{modelProperty.Property.Name}\" || ");
543+
544+
yield return Statement(newStatement);
545+
}
546+
}
454547
}
455548
break;
456549
case DbCommandTreeKind.Delete:

Provider/src/FirebirdSql.Data.FirebirdClient/Common/Extensions.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
//$Authors = Jiri Cincura (jiri@cincura.net)
1717

1818
using System;
19+
using System.Collections.Generic;
1920
using System.IO;
21+
using System.Linq;
2022
using System.Net.Sockets;
2123

2224
namespace FirebirdSql.Data.Common
@@ -104,5 +106,20 @@ public static string ToHexString(this byte[] b)
104106
{
105107
return BitConverter.ToString(b).Replace("-", string.Empty);
106108
}
109+
110+
/// <summary>
111+
/// Splits an array into several smaller arrays.
112+
/// </summary>
113+
/// <typeparam name="T">The type of the array.</typeparam>
114+
/// <param name="array">The array to split.</param>
115+
/// <param name="size">The size of the smaller arrays.</param>
116+
/// <returns>An array containing smaller arrays.</returns>
117+
public static IEnumerable<IEnumerable<T>> Split<T>(this T[] array, int size)
118+
{
119+
for (var i = 0; i < (float)array.Length / size; i++)
120+
{
121+
yield return array.Skip(i * size).Take(size);
122+
}
123+
}
107124
}
108125
}

0 commit comments

Comments
 (0)