|
87 | 87 | #} |
88 | 88 |
|
89 | 89 | {% if incremental_substrategy == 'delete+insert' %} |
90 | | - -- 1. run insert_overwrite with delete+insert transaction strategy optimisation |
| 90 | + -- 1. run insert_overwrite with delete+insert (without a transaction) strategy optimisation |
91 | 91 | {{ bq_get_insert_overwrite_with_delete_and_insert_sql(target_relation, source_sql, dest_columns, [predicate], include_sql_header = not tmp_relation_exists) }}; |
| 92 | + {% elif incremental_substrategy == 'commit+delete+insert' %} |
| 93 | + -- 1. run insert_overwrite with delete+insert (with a transaction) strategy optimisation |
| 94 | + {{ bq_get_insert_overwrite_with_delete_and_insert_sql(target_relation, source_sql, dest_columns, [predicate], include_sql_header = not tmp_relation_exists, transactional=True) }}; |
92 | 95 | {% else %} |
93 | 96 | -- 1. run insert_overwrite with merge strategy optimisation |
94 | 97 | {{ get_insert_overwrite_merge_sql(target_relation, source_sql, dest_columns, [predicate], include_sql_header = not tmp_relation_exists) }}; |
|
162 | 165 | ); |
163 | 166 |
|
164 | 167 | {% if incremental_substrategy == 'delete+insert' %} |
165 | | - -- 3. run insert_overwrite with the delete+insert transaction strategy optimisation |
| 168 | + -- 3. run insert_overwrite with the delete+insert (without a transaction) strategy optimisation |
166 | 169 | {{ bq_get_insert_overwrite_with_delete_and_insert_sql(target_relation, source_sql, dest_columns, [predicate]) }}; |
| 170 | + {% elif incremental_substrategy == 'commit+delete+insert' %} |
| 171 | + -- 3. run insert_overwrite with the delete+insert (with a transaction) strategy optimisation |
| 172 | + {{ bq_get_insert_overwrite_with_delete_and_insert_sql(target_relation, source_sql, dest_columns, [predicate], transactional=True) }}; |
167 | 173 | {% else %} |
168 | 174 | -- 3. run insert_overwrite with the merge strategy optimisation |
169 | 175 | {{ get_insert_overwrite_merge_sql(target_relation, source_sql, dest_columns, [predicate]) }}; |
|
177 | 183 |
|
178 | 184 |
|
179 | 185 |
|
180 | | -{% macro bq_get_insert_overwrite_with_delete_and_insert_sql(target, source, dest_columns, predicates, include_sql_header) -%} |
| 186 | +{% macro bq_get_insert_overwrite_with_delete_and_insert_sql(target, source, dest_columns, predicates, include_sql_header, transactional=False) -%} |
181 | 187 | {%- set predicates = [] if predicates is none else [] + predicates -%} |
182 | 188 | {%- set dest_cols_csv = get_quoted_csv(dest_columns | map(attribute="name")) -%} |
183 | 189 | {%- set sql_header = config.get('sql_header', none) -%} |
184 | 190 |
|
185 | 191 | {{ sql_header if sql_header is not none and include_sql_header }} |
186 | 192 |
|
| 193 | + {% if transactional %} |
| 194 | + -- We can rely on a multi-statement transaction to enable atomicity |
| 195 | + -- If something goes south, nothing is committed |
| 196 | + -- DELETE + INSERT allow for isolation lock |
187 | 197 | begin |
188 | | - begin transaction; |
189 | | - |
190 | | - -- (as of Nov 2024) |
191 | | - -- DELETE operations are free if the partition is a DATE |
192 | | - -- * Not free if the partitions are granular (hourly, monthly) |
193 | | - -- or some other conditions like subqueries and so on. |
194 | | - delete from {{ target }} as DBT_INTERNAL_DEST |
195 | | - where true |
196 | | - {%- if predicates %} |
197 | | - {% for predicate in predicates %} |
198 | | - and {{ predicate }} |
199 | | - {% endfor %} |
200 | | - {%- endif -%}; |
201 | | - |
202 | | - |
203 | | - insert into {{ target }} ({{ dest_cols_csv }}) |
204 | | - ( |
205 | | - select {{ dest_cols_csv }} |
206 | | - from {{ source }} |
207 | | - ); |
| 198 | + begin transaction ; |
| 199 | + {% endif %} |
| 200 | + -- DELETE operations are free https://cloud.google.com/bigquery/docs/using-dml-with-partitioned-tables#using_dml_delete_to_delete_partitions |
| 201 | + delete from {{ target }} as DBT_INTERNAL_DEST |
| 202 | + where true |
| 203 | + {% if predicates %} and {{ predicates | join(' and ') }} {% endif %}; |
208 | 204 |
|
209 | | - commit transaction; |
| 205 | + -- INSERT the data |
| 206 | + insert into {{ target }} ({{ dest_cols_csv }}) |
| 207 | + ( |
| 208 | + select {{ dest_cols_csv }} |
| 209 | + from {{ source }} |
| 210 | + ) |
| 211 | + {% if transactional %} |
| 212 | + -- leaving the trailing ; out of the if as the calling macro already adds a leading ; to this output |
| 213 | + ; commit transaction; |
210 | 214 |
|
211 | 215 | exception when error then |
212 | | - raise using message = FORMAT("Error: %s", @@error.message); |
| 216 | + -- If things go south, abort and rollback |
| 217 | + raise using message = FORMAT("dbt error while commit+delete+instert: %s", @@error.message); |
213 | 218 | rollback transaction; |
214 | 219 | end |
| 220 | + {% endif %} |
215 | 221 | {% endmacro %} |
0 commit comments