1- # ruff: noqa: E501
1+ # ruff: noqa: E501,S608
22# https://github.com/fivetran/fivetran_partner_sdk/blob/main/examples/destination_connector/python/schema_migration_helper.py
33import sys
44
5+ import sqlalchemy as sa
6+
7+ from cratedb_fivetran_destination .model import SqlBag , TypeMap
8+
59sys .path .append ("sdk_pb2" )
610
711from fivetran_sdk import common_pb2 , destination_sdk_pb2
1822class SchemaMigrationHelper :
1923 """Helper class for handling migration operations"""
2024
21- def __init__ (self , table_map ):
25+ def __init__ (self , engine : sa .Engine , table_map ):
26+ self .engine = engine
2227 self .table_map = table_map
2328
2429 def handle_drop (self , drop_op , schema , table ):
2530 """Handles drop operations (drop table, drop column in history mode)."""
2631 entity_case = drop_op .WhichOneof ("entity" )
2732
2833 if entity_case == "drop_table" :
29- # table-map manipulation to simulate drop, replace with actual logic.
30- self .table_map .pop (table , None )
34+ sql = f'DROP TABLE "{ schema } "."{ table } "'
35+ with self .engine .connect () as conn :
36+ conn .execute (sa .text (sql ))
3137
3238 log_message (INFO , f"[Migrate:Drop] Dropping table { schema } .{ table } " )
3339 return destination_sdk_pb2 .MigrateResponse (success = True )
@@ -53,15 +59,18 @@ def handle_drop(self, drop_op, schema, table):
5359 log_message (WARNING , "[Migrate:Drop] No drop entity specified" )
5460 return destination_sdk_pb2 .MigrateResponse (unsupported = True )
5561
56- def handle_copy (self , copy_op , schema , table ):
62+ def handle_copy (self , copy_op , schema , table , table_obj : common_pb2 . Table ):
5763 """Handles copy operations (copy table, copy column, copy table to history mode)."""
5864 entity_case = copy_op .WhichOneof ("entity" )
5965
6066 if entity_case == "copy_table" :
61- # table-map manipulation to simulate copy, replace with actual logic.
6267 copy_table = copy_op .copy_table
63- if copy_table .from_table in self .table_map :
64- self .table_map [copy_table .to_table ] = self .table_map [copy_table .from_table ]
68+ sql = (
69+ f'CREATE TABLE "{ schema } "."{ copy_table .to_table } " '
70+ f'AS SELECT * FROM "{ schema } "."{ copy_table .from_table } "'
71+ )
72+ with self .engine .connect () as conn :
73+ conn .execute (sa .text (sql ))
6574
6675 log_message (
6776 INFO ,
@@ -70,17 +79,25 @@ def handle_copy(self, copy_op, schema, table):
7079 return destination_sdk_pb2 .MigrateResponse (success = True )
7180
7281 if entity_case == "copy_column" :
73- # table-map manipulation to simulate copy column, replace with actual logic.
82+ sql_bag = SqlBag ()
7483 copy_column = copy_op .copy_column
75- table_obj = self .table_map .get (table )
76- if table_obj :
77- for col in table_obj .columns :
78- if col .name == copy_column .from_column :
79- new_col = type (col )()
80- new_col .CopyFrom (col )
81- new_col .name = copy_column .to_column
82- table_obj .columns .add ().CopyFrom (new_col )
83- break
84+ for col in table_obj .columns :
85+ if col .name == copy_column .from_column :
86+ new_col = type (col )()
87+ new_col .CopyFrom (col )
88+ new_col .name = copy_column .to_column
89+ table_obj .columns .add ().CopyFrom (new_col )
90+ type_ = TypeMap .to_cratedb (new_col .type , new_col .params )
91+ sql_bag .add (
92+ f'ALTER TABLE "{ schema } "."{ table } " ADD COLUMN "{ new_col .name } " { type_ } ;'
93+ )
94+ sql_bag .add (f'UPDATE "{ schema } "."{ table } " SET "{ new_col .name } "="{ col .name } ";' )
95+ break
96+
97+ if sql_bag :
98+ with self .engine .connect () as conn :
99+ for command in sql_bag .statements :
100+ conn .execute (sa .text (command ))
84101
85102 log_message (
86103 INFO ,
@@ -116,31 +133,21 @@ def handle_rename(self, rename_op, schema, table):
116133 entity_case = rename_op .WhichOneof ("entity" )
117134
118135 if entity_case == "rename_table" :
119- # table-map manipulation to simulate rename, replace with actual logic.
120136 rt = rename_op .rename_table
121- if rt .from_table in self .table_map :
122- tbl = self .table_map .pop (rt .from_table )
123- # Adjust name inside the Table metadata if needed
124- tbl_copy = tbl .__class__ .FromString (tbl .SerializeToString ())
125- if hasattr (tbl_copy , "name" ):
126- tbl_copy .name = rt .to_table
127- self .table_map [rt .to_table ] = tbl_copy
137+ sql = f'ALTER TABLE "{ schema } "."{ rt .from_table } " RENAME TO "{ rt .to_table } ";'
138+ with self .engine .connect () as conn :
139+ conn .execute (sa .text (sql ))
128140
129141 log_message (
130142 INFO , f"[Migrate:RenameTable] from={ rt .from_table } to={ rt .to_table } schema={ schema } "
131143 )
132144 return destination_sdk_pb2 .MigrateResponse (success = True )
133145
134146 if entity_case == "rename_column" :
135- # table-map manipulation to simulate rename column, replace with actual logic.
136147 rename_column = rename_op .rename_column
137- table_obj = self .table_map .get (table )
138- if table_obj :
139- # Rename the column
140- for col in table_obj .columns :
141- if col .name == rename_column .from_column :
142- col .name = rename_column .to_column
143- break
148+ sql = f'ALTER TABLE "{ schema } "."{ table } " RENAME "{ rename_column .from_column } " TO "{ rename_column .to_column } ";'
149+ with self .engine .connect () as conn :
150+ conn .execute (sa .text (sql ))
144151
145152 log_message (
146153 INFO ,
@@ -151,7 +158,7 @@ def handle_rename(self, rename_op, schema, table):
151158 log_message (WARNING , "[Migrate:Rename] No rename entity specified" )
152159 return destination_sdk_pb2 .MigrateResponse (unsupported = True )
153160
154- def handle_add (self , add_op , schema , table ):
161+ def handle_add (self , add_op , schema , table , table_obj : common_pb2 . Table ):
155162 """Handles add operations (add column in history mode, add column with default value)."""
156163 entity_case = add_op .WhichOneof ("entity" )
157164
@@ -171,13 +178,15 @@ def handle_add(self, add_op, schema, table):
171178 return destination_sdk_pb2 .MigrateResponse (success = True )
172179
173180 if entity_case == "add_column_with_default_value" :
174- # table-map manipulation to simulate add column with default value, replace with actual logic.
175181 add_col_default_with_value = add_op .add_column_with_default_value
176- table_obj = self .table_map .get (table )
177182 if table_obj :
178183 new_col = table_obj .columns .add ()
179184 new_col .name = add_col_default_with_value .column
180185 new_col .type = add_col_default_with_value .column_type
186+ type_ = TypeMap .to_cratedb (new_col .type , new_col .params )
187+ sql = f'ALTER TABLE "{ schema } "."{ table } " ADD COLUMN "{ new_col .name } " { type_ } ;'
188+ with self .engine .connect () as conn :
189+ conn .execute (sa .text (sql ))
181190
182191 log_message (
183192 INFO ,
@@ -190,7 +199,12 @@ def handle_add(self, add_op, schema, table):
190199
191200 def handle_update_column_value (self , upd , schema , table ):
192201 """Handles update column value operation."""
193- # Placeholder: Update all existing rows' column value.
202+ with self .engine .connect () as conn :
203+ conn .execute (
204+ sa .text (f'UPDATE "{ schema } "."{ table } " SET "{ upd .column } "=:value;' ), # noqa: S608
205+ parameters = {"value" : upd .value },
206+ )
207+ conn .execute (sa .text (f'REFRESH TABLE "{ schema } "."{ table } ";' ))
194208
195209 log_message (
196210 INFO ,
0 commit comments