55from databricks .labs .blueprint .parallel import Threads
66from databricks .sdk import WorkspaceClient
77from databricks .sdk .errors import NotFound
8- from databricks .sdk .service .catalog import PermissionsChange , SecurableType , TableType
8+ from databricks .sdk .service .catalog import (
9+ PermissionsChange ,
10+ Privilege ,
11+ SecurableType ,
12+ TableType ,
13+ )
914
1015from databricks .labs .ucx .framework .crawlers import SqlBackend
1116from databricks .labs .ucx .hive_metastore import TablesCrawler
@@ -229,7 +234,8 @@ def move_tables(
229234 self ._move_table , from_catalog , from_schema , table .name , to_catalog , to_schema , del_table
230235 )
231236 )
232- else :
237+ continue
238+ if table .view_definition :
233239 view_tasks .append (
234240 partial (
235241 self ._move_view ,
@@ -242,11 +248,70 @@ def move_tables(
242248 table .view_definition ,
243249 )
244250 )
251+ continue
252+ logger .warning (
253+ f"table { from_table } was not identified as a valid table or view. skipping this table..."
254+ )
245255 Threads .strict ("Creating tables" , table_tasks )
246256 logger .info (f"Moved { len (list (table_tasks ))} tables to the new schema { to_schema } ." )
247257 Threads .strict ("Creating views" , view_tasks )
248258 logger .info (f"Moved { len (list (view_tasks ))} views to the new schema { to_schema } ." )
249259
260+ def alias_tables (
261+ self ,
262+ from_catalog : str ,
263+ from_schema : str ,
264+ from_table : str ,
265+ to_catalog : str ,
266+ to_schema : str ,
267+ ):
268+ try :
269+ self ._ws .schemas .get (f"{ from_catalog } .{ from_schema } " )
270+ except NotFound :
271+ logger .error (f"schema { from_schema } not found in catalog { from_catalog } , enter correct schema details." )
272+ return
273+ try :
274+ self ._ws .schemas .get (f"{ to_catalog } .{ to_schema } " )
275+ except NotFound :
276+ logger .warning (f"schema { to_schema } not found in { to_catalog } , creating..." )
277+ self ._ws .schemas .create (to_schema , to_catalog )
278+
279+ tables = self ._ws .tables .list (from_catalog , from_schema )
280+ alias_tasks = []
281+ filtered_tables = [table for table in tables if from_table in [table .name , "*" ]]
282+ for table in filtered_tables :
283+ try :
284+ self ._ws .tables .get (f"{ to_catalog } .{ to_schema } .{ table .name } " )
285+ logger .warning (
286+ f"table { from_table } already present in { from_catalog } .{ from_schema } . skipping this table..."
287+ )
288+ continue
289+ except NotFound :
290+ if table .table_type and table .table_type in (TableType .EXTERNAL , TableType .MANAGED ):
291+ alias_tasks .append (
292+ partial (self ._alias_table , from_catalog , from_schema , table .name , to_catalog , to_schema )
293+ )
294+ continue
295+ if table .view_definition :
296+ alias_tasks .append (
297+ partial (
298+ self ._move_view ,
299+ from_catalog ,
300+ from_schema ,
301+ table .name ,
302+ to_catalog ,
303+ to_schema ,
304+ False ,
305+ table .view_definition ,
306+ )
307+ )
308+ continue
309+ logger .warning (
310+ f"table { from_table } was not identified as a valid table or view. skipping this table..."
311+ )
312+ Threads .strict ("Creating aliases" , alias_tasks )
313+ logger .info (f"Created { len (list (alias_tasks ))} table and view aliases in the new schema { to_schema } ." )
314+
250315 def _move_table (
251316 self ,
252317 from_catalog : str ,
@@ -273,13 +338,45 @@ def _move_table(
273338 logger .error (f"Failed to move table { from_table_name } : { err !s} " , exc_info = True )
274339 return False
275340
276- def _reapply_grants (self , from_table_name , to_table_name ):
341+ def _alias_table (
342+ self ,
343+ from_catalog : str ,
344+ from_schema : str ,
345+ from_table : str ,
346+ to_catalog : str ,
347+ to_schema : str ,
348+ ) -> bool :
349+ from_table_name = f"{ from_catalog } .{ from_schema } .{ from_table } "
350+ to_table_name = f"{ to_catalog } .{ to_schema } .{ from_table } "
351+ try :
352+ self ._create_alias_view (from_table_name , to_table_name )
353+ self ._reapply_grants (from_table_name , to_table_name , target_view = True )
354+ return True
355+ except NotFound as err :
356+ if "[TABLE_OR_VIEW_NOT_FOUND]" in str (err ) or "[DELTA_TABLE_NOT_FOUND]" in str (err ):
357+ logger .error (f"Could not find table { from_table_name } . Table not found." )
358+ else :
359+ logger .error (f"Failed to alias table { from_table_name } : { err !s} " , exc_info = True )
360+ return False
361+
362+ def _reapply_grants (self , from_table_name , to_table_name , * , target_view : bool = False ):
277363 grants = self ._ws .grants .get (SecurableType .TABLE , from_table_name )
278364 if grants .privilege_assignments is not None :
279365 logger .info (f"Applying grants on table { to_table_name } " )
280- grants_changes = [
281- PermissionsChange (pair .privileges , pair .principal ) for pair in grants .privilege_assignments
282- ]
366+ grants_changes = []
367+ for permission in grants .privilege_assignments :
368+ if not permission .privileges :
369+ continue
370+ if not target_view :
371+ grants_changes .append (PermissionsChange (list (permission .privileges ), permission .principal ))
372+ continue
373+ privileges = set ()
374+ for privilege in permission .privileges :
375+ if privilege != Privilege .MODIFY :
376+ privileges .add (privilege )
377+ if privileges :
378+ grants_changes .append (PermissionsChange (list (privileges ), permission .principal ))
379+
283380 self ._ws .grants .update (SecurableType .TABLE , to_table_name , changes = grants_changes )
284381
285382 def _recreate_table (self , from_table_name , to_table_name ):
@@ -288,6 +385,11 @@ def _recreate_table(self, from_table_name, to_table_name):
288385 logger .info (f"Creating table { to_table_name } " )
289386 self ._backend .execute (create_table_sql )
290387
388+ def _create_alias_view (self , from_table_name , to_table_name ):
389+ create_view_sql = f"CREATE VIEW { to_table_name } AS SELECT * FROM { from_table_name } "
390+ logger .info (f"Creating view { to_table_name } on { from_table_name } " )
391+ self ._backend .execute (create_view_sql )
392+
291393 def _move_view (
292394 self ,
293395 from_catalog : str ,
0 commit comments