-- ======================================================
-- DYNAMIC COLUMN DATA TYPE UPDATE SCRIPT (v3)
-- ======================================================
SET XACT_ABORT ON;
SET NOCOUNT ON;
SET LOCK_TIMEOUT 5000;   -- avoid hanging forever on busy objects


    ------------------------------------------------------------
    -- CONFIGURATION 
    ------------------------------------------------------------
    DECLARE @NewDataType NVARCHAR(100) = 'Varchar(9)'; -- <-- Target datatype
    DECLARE @ColumnPattern NVARCHAR(100) = '%DUN%';      -- <-- Column/Parameter pattern
	DECLARE @DryRun BIT = 0                       --1 is for print, 0 is for execute
    ------------------------------------------------------------


------------------------------------------------------------
-- END STEP 6 FIX
------------------------------------------------------------
-- ===============================

-- STEP 4: ALTER TABLE COLUMNS

-- ===============================
DECLARE @Schema       SYSNAME;
DECLARE @Table        SYSNAME;
DECLARE @Column       SYSNAME;
DECLARE @OldDataType  NVARCHAR(128);
DECLARE @AlterSQL     NVARCHAR(MAX);
DECLARE @IsNullable   BIT;            --  track nullability

DECLARE colCur CURSOR LOCAL FAST_FORWARD FOR
    SELECT DISTINCT
        PARSENAME(ObjectName, 2) AS SchemaName,
        PARSENAME(ObjectName, 1) AS TableName,
        ColumnName,
        ColumnDataType
    FROM DBO.TEMP2
    WHERE ObjectType = 'TABLE';
        PARSENAME(TMP.ObjectName, 2) AS SchemaName,
        PARSENAME(TMP.ObjectName, 1) AS TableName,
        TMP.ColumnName,
        TMP.ColumnDataType,
        c.is_nullable
    FROM DBO.TEMP2 TMP
    INNER JOIN sys.schemas s ON s.name = PARSENAME(TMP.ObjectName, 2)
    INNER JOIN sys.tables t ON t.schema_id = s.schema_id AND t.name = PARSENAME(TMP.ObjectName, 1)
    INNER JOIN sys.columns c ON c.object_id = t.object_id AND c.name = TMP.ColumnName
    WHERE TMP.ObjectType = 'TABLE';

OPEN colCur;
FETCH NEXT FROM colCur INTO @Schema, @Table, @Column, @OldDataType;
FETCH NEXT FROM colCur INTO @Schema, @Table, @Column, @OldDataType, @IsNullable;

WHILE @@FETCH_STATUS = 0
BEGIN
    BEGIN TRY

        -- ====================================================
        -- ? Check if column is currently NOT NULL
        --    so we can preserve it after datatype change
        --    If original was NOT NULL ? keep NOT NULL
        --    If original was NULL    ? keep NULL
        --    Without this PK columns become nullable
        --    and PK recreation fails
        -- ====================================================
        SELECT @IsNullable = c.is_nullable
        FROM sys.columns c
        JOIN sys.tables t
            ON t.object_id = c.object_id
        JOIN sys.schemas s
            ON s.schema_id = t.schema_id
        WHERE s.name   = @Schema
          AND t.name   = @Table
          AND c.name   = @Column;
        -- Nullability is now pre-fetched directly in the cursor declaration.
        -- Preserving NOT NULL so PK columns do not become nullable.

        -- ? Build ALTER COLUMN with correct nullability
        SET @AlterSQL = 'ALTER TABLE ' + QUOTENAME(@Schema) + '.' + QUOTENAME(@Table) +
                        ' ALTER COLUMN ' + QUOTENAME(@Column) + ' ' + @NewDataType +
                        -- ? Preserve NOT NULL if column was NOT NULL
                        CASE WHEN @IsNullable = 0
                             THEN ' NOT NULL'
                             ELSE ' NULL'
                        END;

        IF @DryRun = 1
            INSERT INTO MyprintLog (LogMessage, LogDate)
            VALUES (@AlterSQL, SYSDATETIME());
        ELSE
        BEGIN
            PRINT 'Attempting ALTER TABLE: ' + @AlterSQL;
            EXEC sys.sp_executesql @AlterSQL;
			insert into dbo.result(objecttype, objectname,ColumnName,ColumnDataType)
			VALUES('Table',@Table,@Column,@NewDataType)
			INSERT INTO MyprintLog (LogMessage, LogDate)
            VALUES (@AlterSQL, SYSDATETIME());
        END

    END TRY
    BEGIN CATCH
        DECLARE @CatchMsg5     NVARCHAR(MAX)  = ERROR_MESSAGE();
        DECLARE @CatchXact5    SMALLINT       = XACT_STATE();
        DECLARE @CatchObjName5 NVARCHAR(512)  = @Schema + '.' + @Table;

        PRINT '=== ERROR [ALTER_COLUMN] === MSG: ' + @CatchMsg5;

        IF @CatchXact5 = -1
            ROLLBACK TRANSACTION;

        EXEC dbo.usp_LogError
            @Step           = 'ALTER_COLUMN',
            @ObjectType     = 'TABLE',
            @ObjectName     = @CatchObjName5,
            @ColumnName     = @Column,
            @ColumnDataType = @OldDataType,
            @ErrorMessage   = @CatchMsg5,
            @SqlStatement   = @AlterSQL,
            @XactState      = @CatchXact5;

        --THROW;
    END CATCH

    FETCH NEXT FROM colCur INTO @Schema, @Table, @Column, @OldDataType;
    FETCH NEXT FROM colCur INTO @Schema, @Table, @Column, @OldDataType, @IsNullable;
END

CLOSE     colCur;
DEALLOCATE colCur;

PRINT '--- RECREATING SCHEMA-BOUND VIEWS ---';

-- ===============================

-- Recreating schema bound views

-- ===============================
DECLARE @CreateSQL2 NVARCHAR(MAX);
DECLARE @OBJNAME NVARCHAR(MAX)

DECLARE curCreate1 CURSOR FAST_FORWARD FOR
    SELECT createscript, ViewFullName
    FROM SchemaBoundViews ;   
DECLARE curCreate1 CURSOR LOCAL FAST_FORWARD FOR
    SELECT 
        REPLACE(
            SUBSTRING(
                createscript,
                CHARINDEX('CREATE VIEW', UPPER(createscript)),
                LEN(createscript)
            ), 
            'CREATE VIEW', 
            'CREATE OR ALTER VIEW'
        ) AS createscript, 
        ViewFullName
    FROM SchemaBoundViews;   

OPEN curCreate1;
FETCH NEXT FROM curCreate1 INTO @CreateSQL2,@OBJNAME;
PRINT '--- RECREATING SCHEMA-BOUND VIEWS ---';
WHILE @@FETCH_STATUS = 0
BEGIN
begin try
    PRINT 'Creating view:';
	
		SET @CreateSQL2 = SUBSTRING(
                            @CreateSQL2,
                            CHARINDEX('CREATE VIEW', UPPER(@CreateSQL2)),
                            LEN(@CreateSQL2)
                          );

        -- ? Convert to CREATE OR ALTER so it works whether view exists or not
        SET @CreateSQL2 = REPLACE(@CreateSQL2, 'CREATE VIEW', 'CREATE OR ALTER VIEW');
PRINT '--- RECREATING SCHEMA-BOUND VIEWS ---';

   IF @DryRun = 1
            INSERT INTO MyprintLog (LogMessage, LogDate)
            VALUES (CONCAT('-- DRYRUN ALTERING ', @OBJNAME, '  ',
                           @CreateSQL2), SYSDATETIME());
        ELSE
		PRINT '== [RECREATE_SCHEMABOUNDVIEWS] === MSG: '+@OBJNAME
            EXEC sys.sp_executesql @CreateSQL2;
			INSERT INTO MyprintLog (LogMessage, LogDate)
            VALUES ('== [RECREATE_SCHEMABOUNDVIEWS] === MSG: '+@OBJNAME, SYSDATETIME());
END TRY
BEGIN CATCH
    DECLARE @CatchMsg10  NVARCHAR(MAX) = ERROR_MESSAGE();
    DECLARE @CatchXact10 SMALLINT      = XACT_STATE();

    PRINT '=== ERROR [RECREATE_SCHEMABOUNDVIEWS] === MSG: ' + @CatchMsg10;

    IF @CatchXact10 <> -1
        EXEC dbo.usp_LogError
            @Step           = 'RECREATE_OBJECT',
            @ObjectType     = 'SCHEMABOUNDVIEWS',
            @ObjectName     = @OBJNAME,
            @ColumnName     = NULL,
            @ColumnDataType = NULL,
            @ErrorMessage   = @CatchMsg10,
            @SqlStatement   = @CreateSQL2,
            @XactState      = @CatchXact10;
			--THROW;
END CATCH;

    FETCH NEXT FROM curCreate1 INTO @CreateSQL2,@OBJNAME;
END

CLOSE curCreate1;
DEALLOCATE curCreate1;

