@@ -85,7 +85,6 @@ uchar ddl_log_file_magic[]=
8585{ (uchar) 254 , (uchar) 254 , (uchar) 11 , (uchar) 2 };
8686
8787/* Action names for ddl_log_action_code */
88-
8988const char *ddl_log_action_name[DDL_LOG_LAST_ACTION]=
9089{
9190 " Unknown" , " partitioning delete" , " partitioning rename" ,
@@ -96,6 +95,136 @@ const char *ddl_log_action_name[DDL_LOG_LAST_ACTION]=
9695 " delete tmp file" , " create trigger" , " alter table" , " store query"
9796};
9897
98+ /* *
99+ Acquire MDL exclusive lock on a single table name.
100+ @param thd MySQL thread handle
101+ @param db_name Database name
102+ @param tbl_name Table name
103+ */
104+ static void ddl_log_acquire_table_mdl (
105+ THD *thd, const char *db_name, const char *tbl_name)
106+ {
107+ if (!db_name || !tbl_name || !*db_name || !*tbl_name)
108+ return ;
109+ MDL_request request;
110+ MDL_REQUEST_INIT (&request, MDL_key::TABLE, db_name, tbl_name,
111+ MDL_EXCLUSIVE, MDL_TRANSACTION);
112+
113+ thd->mdl_context .acquire_lock (&request, 0 );
114+ }
115+
116+ /* *
117+ Conditionally acquire MDL based on database name availability.
118+ If db_name is empty, treat table_name as full path and parse it.
119+ Otherwise, use db_name and table_name directly.
120+ @param thd MySQL thread handle
121+ @param db_name Database name (may be empty)
122+ @param table_name Table name or full path
123+ */
124+ static void ddl_log_acquire_table_mdl (
125+ THD *thd, const LEX_CSTRING &db_name, const LEX_CSTRING &table_name)
126+ {
127+ if (db_name.length == 0 )
128+ {
129+ /* If db is empty, treat table_name as full path and parse it */
130+ const char *full_name= table_name.str ;
131+ if (!full_name || !*full_name)
132+ return ;
133+
134+ /* Skip leading ./ if present */
135+ if (full_name[0 ] == ' .' && full_name[1 ] == ' /' )
136+ full_name+= 2 ;
137+
138+ /* Find database/table separator (either '/' or '.') */
139+ const char *separator= strchr (full_name, ' /' );
140+ if (!separator)
141+ separator= strchr (full_name, ' .' );
142+
143+ if (!separator)
144+ return ;
145+
146+ const char *db_start= full_name;
147+ const char *tbl_start= separator + 1 ;
148+ size_t db_len= separator - db_start;
149+ size_t tbl_len= strlen (tbl_start);
150+
151+ if (db_len == 0 || tbl_len == 0 || db_len > NAME_LEN || tbl_len > NAME_LEN)
152+ return ;
153+
154+ /* Handle partition table names - look for partition markers like #P# */
155+ const char *part_marker= strstr (tbl_start, " #P#" );
156+ if (part_marker)
157+ tbl_len= part_marker - tbl_start;
158+
159+ /* Copy and null-terminate names */
160+ char parsed_db_name[NAME_LEN + 1 ];
161+ char parsed_tbl_name[NAME_LEN + 1 ];
162+ memcpy (parsed_db_name, db_start, db_len);
163+ parsed_db_name[db_len]= ' \0 ' ;
164+ memcpy (parsed_tbl_name, tbl_start, tbl_len);
165+ parsed_tbl_name[tbl_len]= ' \0 ' ;
166+
167+ ddl_log_acquire_table_mdl (thd, parsed_db_name, parsed_tbl_name);
168+ }
169+ else
170+ {
171+ /* Use db and table names directly */
172+ ddl_log_acquire_table_mdl (thd, db_name.str , table_name.str );
173+ }
174+ }
175+
176+ /* *
177+ Acquire MDL exclusive locks for DDL log operations based on action type.
178+ @param thd MySQL thread handle
179+ @param ddl_log_entry DDL log entry containing action type and table names
180+ */
181+ static void ddl_log_acquire_mdl (THD *thd, DDL_LOG_ENTRY *ddl_log_entry)
182+ {
183+ if (memcmp (ddl_log_entry->handler_name .str , " InnoDB" ,
184+ ddl_log_entry->handler_name .length ))
185+ return ;
186+
187+ switch (ddl_log_entry->action_type ) {
188+ case DDL_LOG_DELETE_ACTION:
189+ case DDL_LOG_REPLACE_ACTION:
190+ /* Acquire MDL on table being deleted/replaced */
191+ ddl_log_acquire_table_mdl (thd, ddl_log_entry->db , ddl_log_entry->name );
192+ break ;
193+
194+ case DDL_LOG_RENAME_ACTION:
195+ case DDL_LOG_RENAME_TABLE_ACTION:
196+ /* Acquire MDL on both source and destination tables for rename */
197+ ddl_log_acquire_table_mdl (thd, ddl_log_entry->from_db ,
198+ ddl_log_entry->from_name );
199+ ddl_log_acquire_table_mdl (thd, ddl_log_entry->db , ddl_log_entry->name );
200+ break ;
201+
202+ case DDL_LOG_ALTER_TABLE_ACTION:
203+ /* Acquire MDL on all tables involved in alter table operations */
204+ ddl_log_acquire_table_mdl (thd, ddl_log_entry->db , ddl_log_entry->name );
205+ ddl_log_acquire_table_mdl (thd, ddl_log_entry->from_db ,
206+ ddl_log_entry->from_name );
207+ ddl_log_acquire_table_mdl (thd, ddl_log_entry->db , ddl_log_entry->extra_name );
208+ break ;
209+
210+ case DDL_LOG_EXCHANGE_ACTION:
211+ {
212+ /* Acquire MDL on primary table */
213+ ddl_log_acquire_table_mdl (thd, ddl_log_entry->db , ddl_log_entry->name );
214+
215+ /* Acquire MDL on source table for exchange */
216+ ddl_log_acquire_table_mdl (thd, ddl_log_entry->from_db ,
217+ ddl_log_entry->from_name );
218+
219+ ddl_log_acquire_table_mdl (thd, ddl_log_entry->db , ddl_log_entry->tmp_name );
220+ break ;
221+ }
222+ default :
223+ /* No MDL acquisition needed for other action types */
224+ break ;
225+ }
226+ }
227+
99228/* Number of phases per entry */
100229const uchar ddl_log_entry_phases[DDL_LOG_LAST_ACTION]=
101230{
@@ -1446,6 +1575,10 @@ static int ddl_log_execute_action(THD *thd, MEM_ROOT *mem_root,
14461575 invers_fn_flags|= FN_FROM_IS_TMP;
14471576 }
14481577
1578+ if (!frm_action)
1579+ ddl_log_acquire_mdl (thd, ddl_log_entry);
1580+
1581+ /* Acquire MDL for table operations */
14491582 switch (ddl_log_entry->action_type ) {
14501583 case DDL_LOG_REPLACE_ACTION:
14511584 case DDL_LOG_DELETE_ACTION:
0 commit comments