CDC Extended DDL Handling in SQL Server 2022 and 2025
I pay attention to settings that appear in a procedure signature before they are ready to use. A metadata-based script can include them accidentally, even when the documentation says they are unsupported.

This post is part of the Undocumented Changes in SQL Server 2022 and 2025 series. The contents page lists every part in order.
The CDC finding came from three earlier posts: the SQL Server 2022 changed-procedures post, the SQL Server 2022 undocumented DMVs post, and the SQL Server 2025 changed-procedures post. Change data capture (CDC) records data manipulation language (DML) changes from the transaction log. Data definition language (DDL) changes are schema changes such as ALTER TABLE ADD COLUMN.
SQL Server 2022 added an @enable_extended_ddl_handling parameter to sys.sp_cdc_enable_table. It also added sys.fn_cdc_is_ddl_handling_enabled, an undocumented scalar function that appears to read the CDC metadata flag. SQL Server 2025 kept those pieces and added tracing around the internal worker call.
I wanted to know whether the option did anything visible on my SQL Server 2025 test instance, or whether it was only a disabled hook.
The definition trail
In my SQL Server 2019 definition file, sys.sp_cdc_enable_table ended at @allow_partition_switch:
|
1 2 3 4 |
-- SQL Server 2019 @filegroup_name sysname = null, @allow_partition_switch bit = 1 ) |
In the SQL Server 2022 definition file, the procedure had one more parameter and passed it to the internal worker:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
-- SQL Server 2022 @filegroup_name sysname = null, @allow_partition_switch bit = 1, @enable_extended_ddl_handling bit = 0 ) ... exec @retcode = sys.sp_cdc_enable_table_internal @source_schema, @source_name, @capture_instance, @supports_net_changes, @role_name, @index_name, @captured_column_list, @filegroup_name, @allow_partition_switch, @enable_extended_ddl_handling |
The same 2022 body rejects the option unless an internal feature switch allows it:
|
1 2 3 4 5 |
if (@enable_extended_ddl_handling = 1 and ([sys].[fn_cdc_handle_ddl_featureswitch_is_enabled]() <> 1)) begin raiserror(22874, 16, -1) return 1 end |
I did not find sys.fn_cdc_handle_ddl_featureswitch_is_enabled in the definition inventory, and a direct call by that name on the test instance failed with Cannot find either column "sys" or the user-defined function or aggregate "sys.fn_cdc_handle_ddl_featureswitch_is_enabled", or the name is ambiguous. The procedure can still reference it internally, so I treated it as an internal hook rather than a user-callable function.
SQL Server 2022 also added this scalar function:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
create function sys.fn_cdc_is_ddl_handling_enabled ( @object_id int ) returns bit as begin if sys.fn_cdc_is_table_enabled(@object_id) = 1 and exists ( select 1 from [cdc].[change_tables] where source_object_id = @object_id and is_enable_extended_ddl_handling = 1 and is_active = 1 ) begin return 1 end return 0 end |
The CDC function reads cdc.change_tables.is_enable_extended_ddl_handling. In my test database, that column was not present in cdc.change_tables, which mattered later.
sys.sp_MScdc_logddl changed in the same 2019 to 2022 window. The 2022 and 2025 definitions add two parameters and pass them to sys.sp_cdc_logddl_internal:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
@fis_alter_column bit = 0, @fis_drop_table bit = 0, @fis_add_drop_column bit = 0, @new_change_table_prefix sysname = null ... exec @retcode = sys.sp_cdc_logddl_internal @source_object_id, @ddl_command, @ddl_lsn, @ddl_time, @commit_lsn, @source_column_id, @fis_alter_column, @fis_drop_table, @fis_add_drop_column, @new_change_table_prefix |
SQL Server 2025 did not change those two parameters again in my files. The 2025 change in this area is around sys.sp_cdc_enable_table: it adds an EngineEdition 12 error branch and trace-event calls around the internal worker.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
exec sys.sp_cdc_fire_trace_event 0, -1, N'sp_cdc_enable_table', N'entering' exec @retcode = sys.sp_cdc_enable_table_internal @source_schema, @source_name, @capture_instance, @supports_net_changes, @role_name, @index_name, @captured_column_list, @filegroup_name, @allow_partition_switch, @enable_extended_ddl_handling declare @status int = @@error if @status = 0 set @status = @retcode exec sys.sp_cdc_fire_trace_event 0, @status, N'sp_cdc_enable_table', N'complete' |
What Learn says
The live Learn page for sys.sp_cdc_enable_table includes the parameter in the syntax block. Its full argument description is short: “Identified for informational purposes only. Not supported. Future compatibility is not guaranteed.”[1]
The warning matters. The parameter is in a documented procedure signature, but Microsoft does not support using it.
I also searched the SQL Server, Azure and Fabric documentation repositories for the CDC names in this post. I found the sp_cdc_enable_table page and error-message entries for 22874 and 22875. I did not find documentation for sys.fn_cdc_is_ddl_handling_enabled, sys.fn_cdc_handle_ddl_featureswitch_is_enabled, sys.sp_MScdc_logddl, @fis_add_drop_column, or @new_change_table_prefix by whole-word fixed-string search, then by case-insensitive follow-up when the first search found nothing.[2]
The test script
This script is the version I used after one first attempt proved that cdc.change_tables.is_enable_extended_ddl_handling was not a selectable column in the test database. It creates and keeps the scratch database undoc_spinoff_cdc. It creates objects and enables CDC, so it is not read-only. Run it only on a test instance, once, because the table names are fixed. The script does not change server settings, and it leaves the scratch database in place.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
SET NOCOUNT ON; SELECT [ProductVersion] = CONVERT(nvarchar(128), SERVERPROPERTY(N'ProductVersion')) , [Edition] = CONVERT(nvarchar(128), SERVERPROPERTY(N'Edition')) , [EngineEdition] = CONVERT(int, SERVERPROPERTY(N'EngineEdition')); SELECT [servicename] , [status_desc] , [startup_type_desc] FROM sys.dm_server_services WHERE [servicename] LIKE N'SQL Server Agent%'; IF DB_ID(N'undoc_spinoff_cdc') IS NULL BEGIN EXEC (N'CREATE DATABASE [undoc_spinoff_cdc];'); END; GO USE [undoc_spinoff_cdc]; IF (SELECT [is_cdc_enabled] FROM sys.databases WHERE [name] = DB_NAME()) = 0 BEGIN EXEC sys.sp_cdc_enable_db; END; SELECT [parameter_id] , [name] , [system_type_name] = TYPE_NAME([system_type_id]) , [max_length] , [is_output] FROM sys.all_parameters WHERE [object_id] = OBJECT_ID(N'sys.fn_cdc_is_ddl_handling_enabled') ORDER BY [parameter_id]; CREATE TABLE dbo.CdcDefault_20260926 ( CdcDefaultID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_CdcDefault_20260926 PRIMARY KEY, Payload nvarchar(50) NOT NULL ); CREATE TABLE dbo.CdcExtended_20260926 ( CdcExtendedID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_CdcExtended_20260926 PRIMARY KEY, Payload nvarchar(50) NOT NULL ); BEGIN TRY EXEC sys.sp_cdc_enable_table @source_schema = N'dbo' , @source_name = N'CdcDefault_20260926' , @role_name = NULL , @capture_instance = N'dbo_CdcDefault_20260926'; SELECT [step] = N'default enable', [result] = N'succeeded'; END TRY BEGIN CATCH SELECT [step] = N'default enable', [error_number] = ERROR_NUMBER(), [error_message] = ERROR_MESSAGE(); END CATCH; BEGIN TRY EXEC sys.sp_cdc_enable_table @source_schema = N'dbo' , @source_name = N'CdcExtended_20260926' , @role_name = NULL , @capture_instance = N'dbo_CdcExtended_20260926' , @enable_extended_ddl_handling = 1; SELECT [step] = N'extended enable', [result] = N'succeeded'; END TRY BEGIN CATCH SELECT [step] = N'extended enable', [error_number] = ERROR_NUMBER(), [error_message] = ERROR_MESSAGE(); END CATCH; SELECT [column_id], [name], [type_name] = TYPE_NAME([system_type_id]) FROM sys.all_columns WHERE [object_id] = OBJECT_ID(N'cdc.change_tables') ORDER BY [column_id]; BEGIN TRY SELECT [capture_instance] , [source_table] = OBJECT_SCHEMA_NAME([source_object_id]) + N'.' + OBJECT_NAME([source_object_id]) , [fn_value] = CONVERT(int, sys.fn_cdc_is_ddl_handling_enabled([source_object_id])) FROM cdc.change_tables WHERE [source_object_id] IN ( OBJECT_ID(N'dbo.CdcDefault_20260926'), OBJECT_ID(N'dbo.CdcExtended_20260926') ) ORDER BY [capture_instance]; END TRY BEGIN CATCH SELECT [step] = N'fn_cdc_is_ddl_handling_enabled', [error_number] = ERROR_NUMBER(), [error_message] = ERROR_MESSAGE(); END CATCH; SELECT [table_name] = SCHEMA_NAME(t.[schema_id]) + N'.' + t.[name] , c.[column_id] , [column_name] = c.[name] , [type_name] = TYPE_NAME(c.[system_type_id]) FROM sys.tables AS t JOIN sys.columns AS c ON c.[object_id] = t.[object_id] WHERE SCHEMA_NAME(t.[schema_id]) = N'cdc' AND t.[name] IN (N'dbo_CdcDefault_20260926_CT', N'dbo_CdcExtended_20260926_CT') ORDER BY t.[name], c.[column_id]; ALTER TABLE dbo.CdcDefault_20260926 ADD AddedAfterCdc int NULL; ALTER TABLE dbo.CdcExtended_20260926 ADD AddedAfterCdc int NULL; SELECT [table_name] = SCHEMA_NAME(t.[schema_id]) + N'.' + t.[name] , c.[column_id] , [column_name] = c.[name] , [type_name] = TYPE_NAME(c.[system_type_id]) FROM sys.tables AS t JOIN sys.columns AS c ON c.[object_id] = t.[object_id] WHERE (SCHEMA_NAME(t.[schema_id]) = N'dbo' AND t.[name] IN (N'CdcDefault_20260926', N'CdcExtended_20260926')) OR (SCHEMA_NAME(t.[schema_id]) = N'cdc' AND t.[name] IN (N'dbo_CdcDefault_20260926_CT', N'dbo_CdcExtended_20260926_CT')) ORDER BY SCHEMA_NAME(t.[schema_id]), t.[name], c.[column_id]; SELECT [source_schema] = OBJECT_SCHEMA_NAME([source_object_id]) , [source_table] = OBJECT_NAME([source_object_id]) , [ddl_time] , [ddl_command] FROM cdc.ddl_history WHERE [source_object_id] IN ( OBJECT_ID(N'dbo.CdcDefault_20260926'), OBJECT_ID(N'dbo.CdcExtended_20260926') ) ORDER BY [ddl_time], [source_table]; |
What happened on the 2025 instance
I tested this on one SQL Server 2025 CU9 instance, build 17.0.5005.3, Enterprise Developer Edition. SQL Server Agent was stopped, with startup type Manual.
The result matched the Learn page. Microsoft says, “SQL Server Agent doesn’t have to be running when CDC is enabled for a table. However, the capture process doesn’t process the transaction log and write entries to the change table unless SQL Server Agent is running.”[3] Enabling CDC on the database worked. Enabling CDC on the default table also worked, although SQL Server printed four messages saying SQL Server Agent was not running and could not be notified.
Enabling the second table with @enable_extended_ddl_handling = 1 did not work:
|
1 2 3 |
step|error_number|error_message ----|------------|------------- extended enable|22874|Cannot set @enable_extended_ddl_handling parameter to 1 as this feature is not enabled. |
sys.all_parameters showed one input parameter for sys.fn_cdc_is_ddl_handling_enabled, plus the return value:
|
1 2 3 4 |
parameter_id|name|system_type_name|max_length|is_output ------------|----|----------------|----------|--------- 0||bit|1|1 1|@object_id|int|4|0 |
The test database’s cdc.change_tables object had 13 columns. It did not have is_enable_extended_ddl_handling:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
column_id|name|type_name ---------|----|--------- 1|object_id|int 2|version|int 3|source_object_id|int 4|capture_instance|nvarchar 5|start_lsn|binary 6|end_lsn|binary 7|supports_net_changes|bit 8|has_drop_pending|bit 9|role_name|nvarchar 10|index_name|nvarchar 11|filegroup_name|nvarchar 12|create_date|datetime 13|partition_switch|bit |
Calling sys.fn_cdc_is_ddl_handling_enabled for the one capture instance failed because the metadata column was not there:
|
1 2 3 |
step|error_number|error_message ----|------------|------------- fn_cdc_is_ddl_handling_enabled|207|Invalid column name 'is_enable_extended_ddl_handling'. |
After ALTER TABLE ADD AddedAfterCdc int NULL, the source table had the new column. The CDC change table did not. The extended table also had the new source column, but it had no capture instance because the extended enable call failed.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
table_name|column_id|column_name|type_name ----------|---------|-----------|--------- cdc.dbo_CdcDefault_20260926_CT|1|__$start_lsn|binary cdc.dbo_CdcDefault_20260926_CT|2|__$end_lsn|binary cdc.dbo_CdcDefault_20260926_CT|3|__$seqval|binary cdc.dbo_CdcDefault_20260926_CT|4|__$operation|int cdc.dbo_CdcDefault_20260926_CT|5|__$update_mask|varbinary cdc.dbo_CdcDefault_20260926_CT|6|CdcDefaultID|int cdc.dbo_CdcDefault_20260926_CT|7|Payload|nvarchar cdc.dbo_CdcDefault_20260926_CT|8|__$command_id|int dbo.CdcDefault_20260926|1|CdcDefaultID|int dbo.CdcDefault_20260926|2|Payload|nvarchar dbo.CdcDefault_20260926|3|AddedAfterCdc|int dbo.CdcExtended_20260926|1|CdcExtendedID|int dbo.CdcExtended_20260926|2|Payload|nvarchar dbo.CdcExtended_20260926|3|AddedAfterCdc|int |
cdc.ddl_history returned no rows for either source object in this test. With SQL Server Agent stopped, I did not get capture-job processing, and I did not change any server setting to make it run.
What I am taking from it
On this SQL Server 2025 CU9 instance, the feature switch rejected the option when I tried to enable it. The public procedure accepted the parameter name, but it rejected 1. The undocumented function existed and exposed one @object_id int parameter, but it failed in the enabled CDC database because the metadata column it references was absent.
I’m leaving @enable_extended_ddl_handling out of generated setup scripts unless Microsoft changes the support statement and the runtime path. If a script generator includes every visible parameter from sys.sp_cdc_enable_table, it can generate a call that fails with error 22874. I plan to leave @enable_extended_ddl_handling out of generated setup scripts unless Microsoft changes the support statement and the runtime path.
If you have tested CDC extended DDL handling on SQL Server 2022 or 2025, or found a public support statement, tell me on Bluesky or LinkedIn, and I’ll update the notes.
References
- sys.sp_cdc_enable_table (Transact-SQL) - Microsoft Learn. Documents the CDC enable procedure, the unsupported
@enable_extended_ddl_handlingwording, and the SQL Server Agent note. ↩ - MicrosoftDocs/sql-docs, MicrosoftDocs/azure-docs and MicrosoftDocs/fabric-docs - GitHub. Markdown source repositories searched with whole-word fixed-string
git grepand case-insensitive follow-up on 2026-09-26. ↩ - sys.sp_cdc_enable_table (Transact-SQL) - Microsoft Learn. Source of the SQL Server Agent statement quoted in the runtime test section. ↩