-- ============================================= -- 2026-09-30 mfr sync: "String or binary data would be truncated" (msg 8152) -- -- Root cause: mfr's free-text [Note] on ServiceObject and Company has no length limit -- (live mfr data on 2026-09-30: ServiceObject.Note up to 753 chars, Company.Note up to 723), -- but [mfr__serviceobjects].[Note], [mfr__companies].[Note] and their table types -- [mfr__tt__serviceobjects] / [mfr__tt__companies] were NVARCHAR(255). The merge procs -- [mfr__updt__serviceobjects] / [mfr__updt__companies] copy the staging table into the type, -- so every batch containing such a row (the entity's own sync and every Company/Contact/ -- ServiceRequest/ServiceObject sync that $expands it) was rejected as a whole. -- -- This script (idempotent, safe to re-run; one transaction, all-or-nothing): -- 1. patches the [@company] table variable in fds__getRequest_details, fds__prepInvoice -- and fds__prepStorno_recreate (they INSERT ... SELECT cy.* from [mfr__companies]): -- [Note] -> nvarchar(max) (without this, widening the table would move the truncation -- into invoice/request preparation for customers with a long note), and -- [CustomValues] / [MainContactId] -> NULL (both nullable in [mfr__companies]; the -- NOT NULL declaration raised 515 for a company without main contact). Patched IN -- PLACE on the deployed definition (only the @company block changes), so other edits -- to those procs already deployed on this server are kept. Same result as the SSDT -- versions of these procs. -- 2. ALTERs [mfr__serviceobjects].[Note] and [mfr__companies].[Note] to NVARCHAR(MAX). -- 3. Drops/recreates the table types [mfr__tt__serviceobjects] / [mfr__tt__companies] -- (a table type cannot be ALTERed). The procs that DECLARE them, -- [mfr__updt__serviceobjects] / [mfr__updt__companies], are dropped first and recreated -- from the SSDT source; explicit GRANT/DENY on proc and type are captured and re-applied. -- 4. Queues a re-sync (fds__mfr_updaterequests) for the rows that were rejected, because -- the short sync only fetches rows newer than the local watermark and would skip them. -- -- Recommended: stop the Fuchs_DataService worker while the script runs. -- Matches SSDT (Fuchs_Database) as of 2026-09-30. -- ============================================= SET NOCOUNT ON; SET XACT_ABORT ON; SET QUOTED_IDENTIFIER ON; SET ANSI_NULLS ON; DECLARE @type_so nvarchar(max) = N'CREATE TYPE [dbo].[mfr__tt__serviceobjects] AS TABLE ( [setid] VARCHAR (5) NULL, [Id] BIGINT NOT NULL, [Location#ID] BIGINT NULL, [Name] NVARCHAR (255) NULL, [Note] NVARCHAR (MAX) NULL, [CustomValues] NVARCHAR (MAX) NOT NULL, [DateModified] DATETIME NOT NULL, [ExternalId] NVARCHAR (255) NULL, [MappingId] NVARCHAR (255) NULL, [QuickSearch] NVARCHAR (255) NULL, [Version] INT NOT NULL, [CreateGeoLocation] BIT NOT NULL, [IsWarehouse] BIT NOT NULL, [ParentServiceObjectId] BIGINT NOT NULL, [CompanyId] BIGINT NOT NULL, [ProductId] BIGINT NOT NULL);'; DECLARE @proc_so nvarchar(max) = N'-- ============================================= -- Author: -- Create date: -- Description: -- ============================================= CREATE PROCEDURE [dbo].[mfr__updt__serviceobjects] @tblname as nvarchar(50) , @referencetable nvarchar(50) , @tgtid bigint AS BEGIN SET NOCOUNT ON; SET XACT_ABORT ON; BEGIN TRY DECLARE @now datetime = GETUTCDATE(); DECLARE @tmp [dbo].[mfr__tt__serviceobjects]; DECLARE @tmp_cmd nvarchar(1000) = N''SELECT DISTINCT * FROM '' + @tblname INSERT INTO @tmp EXECUTE [sp_executesql] @tmp_cmd; BEGIN TRANSACTION; IF @referencetable = ''mfr__serviceobjects'' BEGIN DELETE p --OUTput deleted.*, @now INTO [dbo].[mfr__d_*PartnerSet] FROM [dbo].[mfr__*PartnerSet] as p WHERE p.Property like ''ServiceObject[:]%'' AND EXISTS (SELECT * FROM @tmp as tmp where tmp.[id] = p.[EntityId]); DELETE cv --OUTput deleted.*, @now INTO [dbo].[mfr__d_#customvalues] FROM [dbo].[mfr__#customvalues] as cv WHERE cv.Property like ''ServiceObject[:]%'' AND EXISTS (SELECT * FROM @tmp as tmp where tmp.[id] = cv.[EntityId]); DELETE lc --OUTPUT deleted.*, @now INTO [dbo].[mfr__d_#locations] FROM [dbo].[mfr__#locations] as lc WHERE lc.Property like ''ServiceObject[:]%'' AND EXISTS (SELECT * FROM @tmp as tmp where tmp.[id] = lc.[EntityId]); DELETE itm --OUTPUT deleted.*, @now INTO [dbo].[mfr__d_items] FROM [dbo].[mfr__items] as itm WHERE EXISTS (SELECT * FROM @tmp as tmp where tmp.[id] = itm.[ServiceObjectId] and ISNULL(itm.[ServiceRequestId],0) = 0); END MERGE [dbo].[mfr__serviceobjects] as TARGET USING @tmp as SOURCE ON TARGET.[Id] = SOURCE.[Id] WHEN MATCHED AND (TARGET.[Version] <= SOURCE.[Version] OR TARGET.[DateModified] <= SOURCE.[DateModified]) THEN UPDATE SET [Id] = SOURCE.[Id] ,[Location#ID] = SOURCE.[Location#ID] ,[Name] = SOURCE.[Name] ,[Note] = SOURCE.[Note] ,[CustomValues] = SOURCE.[CustomValues] ,[DateModified] = SOURCE.[DateModified] ,[ExternalId] = SOURCE.[ExternalId] ,[MappingId] = SOURCE.[MappingId] ,[QuickSearch] = SOURCE.[QuickSearch] ,[Version] = SOURCE.[Version] ,[CreateGeoLocation] = SOURCE.[CreateGeoLocation] ,[IsWarehouse] = SOURCE.[IsWarehouse] ,[ParentServiceObjectId] = SOURCE.[ParentServiceObjectId] ,[CompanyId] = SOURCE.[CompanyId] ,[ProductId] = SOURCE.[ProductId] WHEN NOT MATCHED BY TARGET THEN INSERT ( [Id] ,[Location#ID] ,[Name] ,[Note] ,[CustomValues] ,[DateModified] ,[ExternalId] ,[MappingId] ,[QuickSearch] ,[Version] ,[CreateGeoLocation] ,[IsWarehouse] ,[ParentServiceObjectId] ,[CompanyId] ,[ProductId]) VALUES (SOURCE.[Id] ,SOURCE.[Location#ID] ,SOURCE.[Name] ,SOURCE.[Note] ,SOURCE.[CustomValues] ,SOURCE.[DateModified] ,SOURCE.[ExternalId] ,SOURCE.[MappingId] ,SOURCE.[QuickSearch] ,SOURCE.[Version] ,SOURCE.[CreateGeoLocation] ,SOURCE.[IsWarehouse] ,SOURCE.[ParentServiceObjectId] ,SOURCE.[CompanyId] ,SOURCE.[ProductId]); COMMIT TRANSACTION; END TRY BEGIN CATCH IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION; INSERT INTO dbo.[fds__admin_dberrors] ([UserName], [ErrorNumber], [ErrorState], [ErrorSeverity], [ErrorLine], [ErrorProcedure], [ErrorMessage], [ErrorDateTime]) VALUES (SUSER_SNAME(), ERROR_NUMBER(), ERROR_STATE(), ERROR_SEVERITY(), ERROR_LINE(), ERROR_PROCEDURE(), ERROR_MESSAGE(), GETDATE()); THROW; END CATCH; END'; DECLARE @type_co nvarchar(max) = N'CREATE TYPE [dbo].[mfr__tt__companies] AS TABLE ( [setid] VARCHAR (5) NULL, [Id] BIGINT NOT NULL, [Location#ID] BIGINT NULL, [Version] INT NOT NULL, [IsPhysicalPerson] BIT NOT NULL, [IsOwner] BIT NOT NULL, [IsEmailInvoicingActive] BIT NOT NULL, [IsSupplier] BIT NOT NULL, [MappingId] NVARCHAR (255) NULL, [ExternalId] NVARCHAR (255) NULL, [Name] NVARCHAR (255) NULL, [Note] NVARCHAR (MAX) NULL, [SupportTelephone] NVARCHAR (255) NULL, [SupportFax] NVARCHAR (255) NULL, [SupportMail] NVARCHAR (255) NULL, [QuickSearch] NVARCHAR (255) NULL, [DateModified] DATETIME NOT NULL, [CustomValues] NVARCHAR (MAX) NULL, [MainContactId] BIGINT NULL);'; DECLARE @proc_co nvarchar(max) = N'-- ============================================= -- Description: mfr sync — merges one staged batch of Company entities into [mfr__companies]. -- @tblname is the bulk-copied staging table (same layout as [mfr__tt__companies]); -- [setid] is the batch marker. A full entity reset (@referencetable = ''mfr__companies'') -- first drops the company''s dependent partner/custom-value/location/comment rows so -- they are rebuilt from the same batch. Duplicate Ids within one batch (paged reads -- overlapping a concurrent mfr edit) are collapsed to the highest Version before the -- MERGE, otherwise MERGE raises 8672. -- ============================================= CREATE PROCEDURE [dbo].[mfr__updt__companies] @tblname as nvarchar(50) , @referencetable nvarchar(50) , @tgtid bigint AS BEGIN SET NOCOUNT ON; SET XACT_ABORT ON; BEGIN TRY DECLARE @tmp [dbo].[mfr__tt__companies]; DECLARE @tmp_cmd nvarchar(1000) = N''SELECT DISTINCT * FROM '' + @tblname INSERT INTO @tmp EXECUTE [sp_executesql] @tmp_cmd; -- Wrapping the DELETEs + MERGE below in one transaction gives all-or-nothing atomicity for -- this entity''s sync batch (same pattern in every mfr__updt__* proc). During a large full -- reset this can hold locks on the affected tables for as long as the batch takes to run — -- an accepted, documented risk, not an oversight. See -- Docs/Decisions/0022-mfr-sync-transaction-blocking-accepted-risk.md before changing this. BEGIN TRANSACTION; IF @referencetable = ''mfr__companies'' BEGIN DELETE p --OUTput deleted.*, @now INTO [dbo].[mfr__d_*PartnerSet] FROM [dbo].[mfr__*PartnerSet] as p WHERE p.Property like ''Company[:]%'' AND EXISTS (SELECT * FROM @tmp as tmp where tmp.[id] = p.[EntityId]); DELETE cv --OUTput deleted.*, @now INTO [dbo].[mfr__d_#customvalues] FROM [dbo].[mfr__#customvalues] as cv WHERE cv.Property like ''Company[:]%'' AND EXISTS (SELECT * FROM @tmp as tmp where tmp.[id] = cv.[EntityId]); DELETE lc --OUTPUT deleted.*, @now INTO [dbo].[mfr__d_#locations] FROM [dbo].[mfr__#locations] as lc WHERE lc.Property like ''Company[:]%'' AND EXISTS (SELECT * FROM @tmp as tmp where tmp.[id] = lc.[EntityId]); DELETE cm --OUTPUT deleted.*, @now INTO [dbo].[mfr__d_comments] FROM [dbo].[mfr__comments] as cm WHERE EXISTS (SELECT * FROM @tmp as tmp where tmp.[id] = cm.[CompanyId] AND ISNULL(cm.[ServiceRequestId],0) = 0); END; WITH tmp as ( SELECT *, ROW_NUMBER() OVER (PARTITION BY [Id] ORDER BY [Version] DESC, [DateModified] DESC) as ''#'' FROM @tmp ), SOURCE as ( SELECT * FROM tmp WHERE [#] = 1 ) MERGE [dbo].[mfr__companies] as TARGET USING SOURCE ON TARGET.[Id] = SOURCE.[Id] WHEN MATCHED AND (TARGET.[Version] <= SOURCE.[Version] OR TARGET.[DateModified] <= SOURCE.[DateModified]) THEN UPDATE SET [Id] = SOURCE.[Id] ,[Location#ID] = SOURCE.[Location#ID] ,[Version] = SOURCE.[Version] ,[IsPhysicalPerson] = SOURCE.[IsPhysicalPerson] ,[IsOwner] = SOURCE.[IsOwner] ,[IsEmailInvoicingActive] = SOURCE.[IsEmailInvoicingActive] ,[IsSupplier] = SOURCE.[IsSupplier] ,[MappingId] = SOURCE.[MappingId] ,[ExternalId] = SOURCE.[ExternalId] ,[Name] = SOURCE.[Name] ,[Note] = SOURCE.[Note] ,[SupportTelephone] = SOURCE.[SupportTelephone] ,[SupportFax] = SOURCE.[SupportFax] ,[SupportMail] = SOURCE.[SupportMail] ,[QuickSearch] = SOURCE.[QuickSearch] ,[DateModified] = SOURCE.[DateModified] ,[CustomValues] = SOURCE.[CustomValues] ,[MainContactId] = SOURCE.[MainContactId] WHEN NOT MATCHED BY TARGET THEN INSERT ( [Id] ,[Location#ID] ,[Version] ,[IsPhysicalPerson] ,[IsOwner] ,[IsEmailInvoicingActive] ,[IsSupplier] ,[MappingId] ,[ExternalId] ,[Name] ,[Note] ,[SupportTelephone] ,[SupportFax] ,[SupportMail] ,[QuickSearch] ,[DateModified] ,[CustomValues] ,[MainContactId]) VALUES (SOURCE.[Id] ,SOURCE.[Location#ID] ,SOURCE.[Version] ,SOURCE.[IsPhysicalPerson] ,SOURCE.[IsOwner] ,SOURCE.[IsEmailInvoicingActive] ,SOURCE.[IsSupplier] ,SOURCE.[MappingId] ,SOURCE.[ExternalId] ,SOURCE.[Name] ,SOURCE.[Note] ,SOURCE.[SupportTelephone] ,SOURCE.[SupportFax] ,SOURCE.[SupportMail] ,SOURCE.[QuickSearch] ,SOURCE.[DateModified] ,SOURCE.[CustomValues] ,SOURCE.[MainContactId]); COMMIT TRANSACTION; END TRY BEGIN CATCH IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION; INSERT INTO dbo.[fds__admin_dberrors] ([UserName], [ErrorNumber], [ErrorState], [ErrorSeverity], [ErrorLine], [ErrorProcedure], [ErrorMessage], [ErrorDateTime]) VALUES (SUSER_SNAME(), ERROR_NUMBER(), ERROR_STATE(), ERROR_SEVERITY(), ERROR_LINE(), ERROR_PROCEDURE(), ERROR_MESSAGE(), GETDATE()); THROW; END CATCH; END'; DECLARE @perms TABLE ([stmt] nvarchar(max) NOT NULL); DECLARE @stmt nvarchar(max); BEGIN TRY BEGIN TRANSACTION; -- ---- 1. the [@company] table variable in the invoice/request preparation procs -- Three column changes, matching the SSDT versions of these procs: -- [Note] nvarchar(255) -> nvarchar(max) (this fix) -- [CustomValues] NOT NULL -> NULL (nullable in [mfr__companies]; NOT NULL here -- [MainContactId] NOT NULL -> NULL raised 515 on INSERT ... SELECT cy.* for a -- company without main contact / custom values) -- Only the "DECLARE @company TABLE ( ... );" block is edited: the same column names occur in other -- table variables of these procs (e.g. @SReq.[CustomValues] NOT NULL), which must stay as they are. DECLARE @procname sysname, @def nvarchar(max), @pos int, @i int, @blkStart int, @blkEnd int, @blk nvarchar(max), @blkNew nvarchar(max); DECLARE @blkMarker nvarchar(50) = N'DECLARE @company TABLE'; DECLARE proc_cur CURSOR LOCAL FAST_FORWARD FOR SELECT v.[name] FROM (VALUES (N'fds__getRequest_details'), (N'fds__prepInvoice'), (N'fds__prepStorno_recreate')) AS v([name]); OPEN proc_cur; FETCH NEXT FROM proc_cur INTO @procname; WHILE @@FETCH_STATUS = 0 BEGIN SET @def = OBJECT_DEFINITION(OBJECT_ID(N'[dbo].' + QUOTENAME(@procname))); -- Locate the CREATE keyword of "CREATE PROCEDURE" (deployed headers vary in spacing). SET @pos = CHARINDEX(N'PROCEDURE', ISNULL(@def, N'')); SET @i = @pos - 1; WHILE @i > 0 AND SUBSTRING(@def, @i, 1) IN (N' ', NCHAR(9), NCHAR(10), NCHAR(13)) SET @i -= 1; SET @pos = CASE WHEN @pos > 0 AND @i >= 6 AND SUBSTRING(@def, @i - 5, 6) = N'CREATE' THEN @i - 5 ELSE 0 END; -- Locate the single "DECLARE @company TABLE ( ... );" block (ends at the first ');' after the marker). SET @blkStart = CHARINDEX(@blkMarker, ISNULL(@def, N'')); SET @blkEnd = CASE WHEN @blkStart > 0 THEN CHARINDEX(N');', @def, @blkStart) ELSE 0 END; IF @def IS NULL RAISERROR(N'WARNING: [dbo].[%s] not found - skipped.', 10, 1, @procname) WITH NOWAIT; ELSE IF @pos = 0 OR @blkStart = 0 OR @blkEnd = 0 OR CHARINDEX(@blkMarker, @def, @blkStart + 1) > 0 RAISERROR(N'WARNING: [dbo].[%s] could not be patched automatically (no CREATE PROCEDURE header, or the @company TABLE block is missing/ambiguous) - edit its @company declaration by hand: [Note] nvarchar(max), [CustomValues] NULL, [MainContactId] NULL.', 10, 1, @procname) WITH NOWAIT; ELSE BEGIN SET @blk = SUBSTRING(@def, @blkStart, @blkEnd - @blkStart + 2); SET @blkNew = REPLACE(@blk, N'[Note] [nvarchar](255) NULL', N'[Note] [nvarchar](max) NULL'); SET @blkNew = REPLACE(@blkNew, N'[CustomValues] [nvarchar](max) NOT NULL', N'[CustomValues] [nvarchar](max) NULL'); SET @blkNew = REPLACE(@blkNew, N'[MainContactId] [bigint] NOT NULL', N'[MainContactId] [bigint] NULL'); IF @blkNew = @blk PRINT CONCAT(N'[', @procname, N'] @company block already matches SSDT - skipped.'); ELSE BEGIN -- block first (it lies after the header), then CREATE -> ALTER (changes the length before it) SET @def = STUFF(@def, @blkStart, DATALENGTH(@blk) / 2, @blkNew); SET @def = STUFF(@def, @pos, LEN(N'CREATE'), N'ALTER'); EXEC (@def); PRINT CONCAT(N'[', @procname, N'] @company block patched: [Note] nvarchar(max), [CustomValues] NULL, [MainContactId] NULL.'); END END FETCH NEXT FROM proc_cur INTO @procname; END CLOSE proc_cur; DEALLOCATE proc_cur; -- ---- 2. target tables IF EXISTS (SELECT * FROM sys.columns WHERE [object_id] = OBJECT_ID(N'[dbo].[mfr__serviceobjects]') AND [name] = N'Note' AND [max_length] <> -1) BEGIN ALTER TABLE [dbo].[mfr__serviceobjects] ALTER COLUMN [Note] NVARCHAR (MAX) NULL; PRINT N'[mfr__serviceobjects].[Note] widened to NVARCHAR(MAX).'; END ELSE PRINT N'[mfr__serviceobjects].[Note] already NVARCHAR(MAX) - skipped.'; IF EXISTS (SELECT * FROM sys.columns WHERE [object_id] = OBJECT_ID(N'[dbo].[mfr__companies]') AND [name] = N'Note' AND [max_length] <> -1) BEGIN ALTER TABLE [dbo].[mfr__companies] ALTER COLUMN [Note] NVARCHAR (MAX) NULL; PRINT N'[mfr__companies].[Note] widened to NVARCHAR(MAX).'; END ELSE PRINT N'[mfr__companies].[Note] already NVARCHAR(MAX) - skipped.'; -- ---- 3. table types (+ the merge procs that declare them) -- ---- [mfr__tt__serviceobjects] + [mfr__updt__serviceobjects] IF EXISTS (SELECT * FROM sys.table_types AS tt JOIN sys.columns AS c ON c.[object_id] = tt.[type_table_object_id] WHERE tt.[schema_id] = SCHEMA_ID(N'dbo') AND tt.[name] = N'mfr__tt__serviceobjects' AND c.[name] = N'Note' AND c.[max_length] <> -1) BEGIN -- A table type cannot be ALTERed. [mfr__updt__serviceobjects] DECLAREs it, so the proc is dropped -- first and recreated from the SSDT source afterwards; explicit grants are re-applied. INSERT INTO @perms ([stmt]) SELECT CONCAT(CASE p.[state] WHEN 'D' THEN N'DENY ' ELSE N'GRANT ' END, p.[permission_name], CASE p.[class] WHEN 1 THEN N' ON OBJECT::[dbo].' + QUOTENAME(o.[name]) ELSE N' ON TYPE::[dbo].' + QUOTENAME(t.[name]) END, N' TO ', QUOTENAME(USER_NAME(p.[grantee_principal_id])), CASE p.[state] WHEN 'W' THEN N' WITH GRANT OPTION' ELSE N'' END, N';') FROM sys.database_permissions AS p LEFT JOIN sys.objects AS o ON p.[class] = 1 AND o.[object_id] = p.[major_id] LEFT JOIN sys.types AS t ON p.[class] = 6 AND t.[user_type_id] = p.[major_id] WHERE (p.[class] = 1 AND o.[object_id] = OBJECT_ID(N'[dbo].[mfr__updt__serviceobjects]')) OR (p.[class] = 6 AND t.[name] = N'mfr__tt__serviceobjects' AND t.[schema_id] = SCHEMA_ID(N'dbo')); IF OBJECT_ID(N'[dbo].[mfr__updt__serviceobjects]', N'P') IS NOT NULL DROP PROCEDURE [dbo].[mfr__updt__serviceobjects]; DROP TYPE [dbo].[mfr__tt__serviceobjects]; EXEC (@type_so); EXEC (@proc_so); PRINT N'[mfr__tt__serviceobjects] recreated with [Note] NVARCHAR(MAX); [mfr__updt__serviceobjects] recreated.'; END ELSE PRINT N'[mfr__tt__serviceobjects].[Note] already NVARCHAR(MAX) - skipped.'; -- ---- [mfr__tt__companies] + [mfr__updt__companies] IF EXISTS (SELECT * FROM sys.table_types AS tt JOIN sys.columns AS c ON c.[object_id] = tt.[type_table_object_id] WHERE tt.[schema_id] = SCHEMA_ID(N'dbo') AND tt.[name] = N'mfr__tt__companies' AND c.[name] = N'Note' AND c.[max_length] <> -1) BEGIN -- A table type cannot be ALTERed. [mfr__updt__companies] DECLAREs it, so the proc is dropped -- first and recreated from the SSDT source afterwards; explicit grants are re-applied. INSERT INTO @perms ([stmt]) SELECT CONCAT(CASE p.[state] WHEN 'D' THEN N'DENY ' ELSE N'GRANT ' END, p.[permission_name], CASE p.[class] WHEN 1 THEN N' ON OBJECT::[dbo].' + QUOTENAME(o.[name]) ELSE N' ON TYPE::[dbo].' + QUOTENAME(t.[name]) END, N' TO ', QUOTENAME(USER_NAME(p.[grantee_principal_id])), CASE p.[state] WHEN 'W' THEN N' WITH GRANT OPTION' ELSE N'' END, N';') FROM sys.database_permissions AS p LEFT JOIN sys.objects AS o ON p.[class] = 1 AND o.[object_id] = p.[major_id] LEFT JOIN sys.types AS t ON p.[class] = 6 AND t.[user_type_id] = p.[major_id] WHERE (p.[class] = 1 AND o.[object_id] = OBJECT_ID(N'[dbo].[mfr__updt__companies]')) OR (p.[class] = 6 AND t.[name] = N'mfr__tt__companies' AND t.[schema_id] = SCHEMA_ID(N'dbo')); IF OBJECT_ID(N'[dbo].[mfr__updt__companies]', N'P') IS NOT NULL DROP PROCEDURE [dbo].[mfr__updt__companies]; DROP TYPE [dbo].[mfr__tt__companies]; EXEC (@type_co); EXEC (@proc_co); PRINT N'[mfr__tt__companies] recreated with [Note] NVARCHAR(MAX); [mfr__updt__companies] recreated.'; END ELSE PRINT N'[mfr__tt__companies].[Note] already NVARCHAR(MAX) - skipped.'; DECLARE perm_cur CURSOR LOCAL FAST_FORWARD FOR SELECT [stmt] FROM @perms; OPEN perm_cur; FETCH NEXT FROM perm_cur INTO @stmt; WHILE @@FETCH_STATUS = 0 BEGIN EXEC (@stmt); PRINT N'Re-applied: ' + @stmt; FETCH NEXT FROM perm_cur INTO @stmt; END CLOSE perm_cur; DEALLOCATE perm_cur; -- ---- 4. queue a re-sync of the rejected rows -- Rows with a >255-char Note in mfr on 2026-09-30 (read-only scan of the mfr OData API). DECLARE @resync TABLE ([entity_name] varchar(255) NOT NULL, [Id] bigint NOT NULL); INSERT INTO @resync ([entity_name], [Id]) VALUES ('ServiceObject', 24296783880), ('ServiceObject', 34080915468), ('ServiceObject', 38722404355) ,('ServiceObject', 105289089024), ('ServiceObject', 105292464132), ('ServiceObject', 105292464134) ,('Company', 104430043136), ('Company', 105288990720); -- Plus every row of the last rejected batch kept by the data service (if present). IF OBJECT_ID(N'[dbo].[t_err_mfr__serviceobjects]', N'U') IS NOT NULL INSERT INTO @resync EXEC (N'SELECT DISTINCT ''ServiceObject'', CAST([Id] AS bigint) FROM [dbo].[t_err_mfr__serviceobjects] WHERE [Id] IS NOT NULL;'); IF OBJECT_ID(N'[dbo].[t_err_mfr__companies]', N'U') IS NOT NULL INSERT INTO @resync EXEC (N'SELECT DISTINCT ''Company'', CAST([Id] AS bigint) FROM [dbo].[t_err_mfr__companies] WHERE [Id] IS NOT NULL;'); INSERT INTO [dbo].[fds__mfr_updaterequests] ([entity_name], [Id]) SELECT DISTINCT r.[entity_name], r.[Id] FROM @resync AS r WHERE NOT EXISTS (SELECT * FROM [dbo].[fds__mfr_updaterequests] AS q WHERE q.[entity_name] = r.[entity_name] AND q.[Id] = r.[Id]); PRINT CONCAT(N'Re-sync requests queued: ', @@ROWCOUNT); COMMIT TRANSACTION; PRINT N'mfr truncation fix applied.'; END TRY BEGIN CATCH IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION; THROW; END CATCH; GO -- ---- verification -- Expect max_length = -1 on all four rows. SELECT [object] = o.[name], [column] = c.[name], c.[max_length] FROM sys.columns AS c JOIN sys.objects AS o ON o.[object_id] = c.[object_id] WHERE c.[name] = N'Note' AND o.[name] IN (N'mfr__serviceobjects', N'mfr__companies') UNION ALL SELECT tt.[name], c.[name], c.[max_length] FROM sys.table_types AS tt JOIN sys.columns AS c ON c.[object_id] = tt.[type_table_object_id] WHERE c.[name] = N'Note' AND tt.[name] IN (N'mfr__tt__serviceobjects', N'mfr__tt__companies'); -- Expect no row (every @company block widened and made nullable; other table variables are not inspected). SELECT [still_unpatched] = o.[name], [company_block] = b.[blk] FROM sys.sql_modules AS m JOIN sys.objects AS o ON o.[object_id] = m.[object_id] CROSS APPLY (SELECT [s] = CHARINDEX(N'DECLARE @company TABLE', m.[definition])) AS p CROSS APPLY (SELECT [blk] = CASE WHEN p.[s] > 0 THEN SUBSTRING(m.[definition], p.[s], CHARINDEX(N');', m.[definition], p.[s]) - p.[s] + 2) ELSE N'' END) AS b WHERE o.[name] IN (N'fds__getRequest_details', N'fds__prepInvoice', N'fds__prepStorno_recreate') AND (b.[blk] LIKE N'%[[]Note] [[]nvarchar](255)%' OR b.[blk] LIKE N'%[[]CustomValues] [[]nvarchar](max) NOT NULL%' OR b.[blk] LIKE N'%[[]MainContactId] [[]bigint] NOT NULL%' OR p.[s] = 0); -- Once the worker has run, the queue drains and the counts approach mfr's -- (2026-09-30: 5531 ServiceObjects, 4625 Companies). SELECT [queued] = COUNT(*) FROM [dbo].[fds__mfr_updaterequests] WHERE [entity_name] IN ('ServiceObject', 'Company'); SELECT [serviceobjects] = (SELECT COUNT(*) FROM [dbo].[mfr__serviceobjects]), [companies] = (SELECT COUNT(*) FROM [dbo].[mfr__companies]);