Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VARBINARY does NULL is not mapped correctly by default #232

Merged
merged 1 commit into from
Aug 9, 2024

Conversation

ValtsS
Copy link
Contributor

@ValtsS ValtsS commented Aug 6, 2024

VARBINARY seems to be a special case which does not work correctly:

2024-08-05 19:06:04.7033|INFO|Getting replication information for replication set Test
2024-08-05 19:06:07.3604|INFO|Replicating table [dbo].[test2]
2024-08-05 19:06:07.3604|INFO|Starting replication for replication set Test
2024-08-05 19:06:07.3604|INFO|Database Secondary 1 is at version 24994
2024-08-05 19:06:07.3771|INFO|Snapshot isolation is enabled in database Primary
2024-08-05 19:06:07.3771|INFO|Current version of database Primary is 24997
2024-08-05 19:06:07.4134|INFO|Minimum version of table [dbo].[test2] in database Primary is 24984
2024-08-05 19:06:07.4134|DEBUG|Retrieving changes for table [dbo].[test2]: select c.SYS_CHANGE_OPERATION, c.SYS_CHANGE_VERSION, c.SYS_CHANGE_CREATION_VERSION,
                        c.[id], t.[dat]
                        from CHANGETABLE (CHANGES [dbo].[test2], @0) c
                        left outer join [dbo].[test2] t on c.[id] = t.[id] order by coalesce(c.SYS_CHANGE_CREATION_VERSION, c.SYS_CHANGE_VERSION), c.SYS_CHANGE_OPERATION
2024-08-05 19:06:07.4314|INFO|Table [dbo].[test2] has 1 change
2024-08-05 19:06:07.4368|INFO|Replicating 1 change to destination Secondary 1
2024-08-05 19:06:07.4368|DEBUG|Replicating change #1 of 1 (Version 24997, CreationVersion 24997)
2024-08-05 19:06:07.4368|DEBUG|Executing insert: set IDENTITY_INSERT [dbo].[test2] ON; insert into [dbo].[test2] ([id], [dat]) values (@0, @1); set IDENTITY_INSERT [dbo].[test2] OFF (@0 = 4, @1 = )
2024-08-05 19:06:07.4540|ERROR|Error replicating changes to destination Secondary 1 System.Data.SqlClient.SqlException (0x80131904): Implicit conversion from data type nvarchar to varbinary(max) is not allowed. Use the CONVERT function to run this query.
   at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
   at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
   at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
   at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, SqlDataReader ds)
   at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource`1 completion, Boolean sendToPipe, Int32 timeout, Boolean asyncWrite, String methodName)
   at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
   at NPoco.Database.ExecuteNonQueryHelper(DbCommand cmd)
   at NPoco.Database.Execute(String sql, CommandType commandType, Object[] args)
   at SyncChanges.Synchronizer.PerformChange(Database db, Change change) in R:\Projects\IF\Sync2\SyncChanges\SyncChanges\Synchronizer.cs:line 563
   at SyncChanges.Synchronizer.Replicate(DatabaseInfo source, IGrouping`2 destinations, IList`1 tables) in R:\Projects\IF\Sync2\SyncChanges\SyncChanges\Synchronizer.cs:line 349
ClientConnectionId:1e0c653d-c85e-4ced-88b6-eeaa8197277b
Error Number:257,State:3,Class:16 HelpLink.ProdName: Microsoft SQL Server;HelpLink.ProdVer: 14.00.3411;HelpLink.EvtSrc: MSSQLServer;HelpLink.EvtID: 257;HelpLink.BaseHelpUrl: https://go.microsoft.com/fwlink;HelpLink.LinkId: 20476
2024-08-05 19:06:07.4818|INFO|Finished replication with errors

To work around this - check which columns can be nullable and are varbinary (maybe there are some more types?) - then insert SqlBinary.Null

Copy link

codecov bot commented Aug 6, 2024

Codecov Report

Attention: Patch coverage is 83.33333% with 4 lines in your changes missing coverage. Please review.

Project coverage is 87.71%. Comparing base (c7a7b7c) to head (f9ba38c).

Files Patch % Lines
SyncChanges/DataReaderExtensions.cs 76.92% 2 Missing and 1 partial ⚠️
SyncChanges/Synchronizer.cs 90.90% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #232      +/-   ##
==========================================
- Coverage   87.98%   87.71%   -0.27%     
==========================================
  Files           8        9       +1     
  Lines         491      513      +22     
  Branches       68       74       +6     
==========================================
+ Hits          432      450      +18     
- Misses         48       50       +2     
- Partials       11       13       +2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@mganss
Copy link
Owner

mganss commented Aug 9, 2024

Thanks! Can you adjust the varbinary test here?

[Test]
public void VarBinaryTest()
{
try
{
static void CreateUsersTable(string dbName)
{
using var db = GetDatabase(dbName);
db.Execute(@"if not exists (select * from sys.tables where name = 'Users') create table Users (
NUM_TRF int primary key not null,
NAME varchar(255),
DESCRIPTION varchar(1000),
REPORT varbinary(max)
)");
db.Execute(@"alter table Users
enable CHANGE_TRACKING
with (TRACK_COLUMNS_UPDATED = OFF)");
}
DropTable("Users");
CreateUsersTable(SourceDatabaseName);
CreateUsersTable(DestinationDatabaseName);
var sourceUser = new VarBinaryUser { Num_Trf = 1, Name = "Test", Description = "Test Description", Report = new byte[] { 1, 2, 3, 4 } };
using (var db = GetDatabase(SourceDatabaseName))
{
db.Insert(sourceUser);
sourceUser.Name = "Michael Jeffrey Jordan";
db.Update(sourceUser);
}
var synchronizer = new Synchronizer(TestConfig);
var success = synchronizer.Sync();
Assert.That(success, Is.True);
using (var db = GetDatabase(DestinationDatabaseName))
{
var user = db.Single<VarBinaryUser>("select * from Users");
}
}
finally
{
DropTable("Users");
}
}

@ValtsS
Copy link
Contributor Author

ValtsS commented Aug 9, 2024

Later. I don't have any localdb sql server to test with. Could also add tests with transactions to see how they fail :-)

@mganss
Copy link
Owner

mganss commented Aug 9, 2024

Do you know why the current test succeeds? What would need to be modified for it to fail?

@ValtsS
Copy link
Contributor Author

ValtsS commented Aug 9, 2024

It succeeds because varbinary(max) is not null. If you have a NULL value for it - it will fail.

@mganss
Copy link
Owner

mganss commented Aug 9, 2024

Thanks. I'll fix the varbinary test. Would be great if you could add transaction tests later.

@mganss mganss merged commit e84d907 into mganss:master Aug 9, 2024
1 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants