Source code for operators.lake_pit_table

from config import Config

from airflow.models import BaseOperator
from airflow.utils.decorators import apply_defaults


[docs] class LakePitTableOperator(BaseOperator): """ Create/insert data into a point in time table in the data lake. :param pit_table_name: the name of the point in table that will be created/inserted into :type table_name: str :param source_table_name: the name of the base table that will be read from :type table_name: str :param version: the version of the data, gets inserted into the version column in the pit table :type version: str :param database: the database the table is in, defaults to Config.DATA_LAKE_DB :type database: str :param version_column_name: the name of the version column in the pit table, defaults to "version_date" :type version_column_name: str :raises AirflowException: _description_ """ ui_color = "#9842f5" template_fields = ("pit_table_name", "source_table_name", "version") @apply_defaults def __init__( self: object, pit_table_name: str, source_table_name: str, version: str, database: str = Config.DATA_LAKE_DB, version_column_name: str = "version_date", *args, **kwargs, ): super().__init__(*args, **kwargs) self.pit_table_name = pit_table_name self.source_table_name = source_table_name self.version = version self.database = database self.version_column_name = version_column_name
[docs] def execute( self: object, context: dict, ) -> None: import awswrangler as wr from util.backup_table import backup_table if wr.catalog.does_table_exist( database=Config.DATA_LAKE_DB, table=self.pit_table_name ): # Get the column names of the tables df_og_col_names = wr.athena.read_sql_query( sql=f"SELECT * FROM {self.pit_table_name} LIMIT 1", database=Config.DATA_LAKE_DB, ) og_col_names = [ col for col in df_og_col_names.columns.tolist() if col != self.version_column_name ] df_new_col_names = wr.athena.read_sql_query( sql=f"SELECT * FROM {self.source_table_name} LIMIT 1", database=Config.DATA_LAKE_DB ) new_col_names = df_new_col_names.columns.tolist() if og_col_names == new_col_names: # If the columns are the same, just insert the data insert_query = f"INSERT INTO {self.pit_table_name} SELECT *, '{self.version}' AS {self.version_column_name} FROM {self.source_table_name}" print(insert_query) wr.athena.start_query_execution(insert_query, Config.DATA_LAKE_DB, wait=True) else: # If the columns are different, create a new table containing all columns from both tables, and mark the missing columns as NULL selection_query = "SELECT " for col in new_col_names: if col in og_col_names: selection_query += f"{col}, " else: selection_query += f"NULL AS {col}, " for col in og_col_names: if col not in new_col_names: selection_query += f"{col}, " selection_query = ( selection_query[:-2] + f" FROM {self.pit_table_name} UNION ALL SELECT " ) for col in new_col_names: selection_query += f"{col}, " for col in og_col_names: if col not in new_col_names: selection_query += f"NULL AS {col}, " selection_query = selection_query[:-2] + f" FROM {self.source_table_name}" print( f"Detected column mismatch between {self.source_table_name} and {self.pit_table_name}. Creating new backup table with correct columns\n\n{selection_query}\n" ) backup_table( source_table_name=self.source_table_name, backup_table_name=self.pit_table_name, compare_checksum=False, verbose=True, new_partitions=[self.version_column_name], custom_selection=selection_query, overwrite=True ) else: print(f"Creating new table {self.pit_table_name}") backup_table( source_table_name=self.source_table_name, backup_table_name=self.pit_table_name, compare_checksum=False, verbose=True, new_partitions=[self.version_column_name], custom_selection=f"SELECT *, '{self.version}' AS {self.version_column_name} FROM {self.source_table_name}", )