1+ {# SQL Server specific implementation to create a primary key #}
2+ {%- macro sqlserver__create_primary_key(table_relation, column_names, verify_permissions, quote_columns= false) - %}
3+ {%- set constraint_name = (table_relation .identifier ~ " _" ~ column_names|join (' _' ) ~ " _PK" ) | upper - %}
4+
5+ {%- if constraint_name|length > 63 %}
6+ {%- set constraint_name_query %}
7+ select ' PK_' || md5( ' {{ constraint_name }}' )::varchar as " constraint_name"
8+ {%- endset - %}
9+ {%- set results = run_query(constraint_name_query) - %}
10+ {%- set constraint_name = results .columns [0 ].values ()[0 ] - %}
11+ {% endif %}
12+
13+ {%- set columns_csv = dbt_constraints .get_quoted_column_csv (column_names, quote_columns) - %}
14+
15+ {# - Check that the table does not already have this PK/UK -#}
16+ {%- if not dbt_constraints .unique_constraint_exists (table_relation, column_names) - %}
17+
18+ {%- if dbt_constraints .have_ownership_priv (table_relation, verify_permissions) - %}
19+
20+ {%- do log(" Creating primary key: " ~ constraint_name, info= true) - %}
21+ {%- call statement(' add_pk' , fetch_result= False, auto_begin= True) - %}
22+ ALTER TABLE {{table_relation}} ADD CONSTRAINT {{constraint_name}} PRIMARY KEY ( {{columns_csv}} )
23+ {%- endcall - %}
24+ {{ adapter .commit () }}
25+
26+ {%- else - %}
27+ {%- do log(" Skipping " ~ constraint_name ~ " because of insufficient privileges: " ~ table_relation, info= false) - %}
28+ {%- endif - %}
29+
30+ {%- else - %}
31+ {%- do log(" Skipping " ~ constraint_name ~ " because PK/UK already exists: " ~ table_relation ~ " " ~ column_names, info= false) - %}
32+ {%- endif - %}
33+
34+ {%- endmacro - %}
35+
36+
37+
38+ {# SQL Server specific implementation to create a unique key #}
39+ {%- macro sqlserver__create_unique_key(table_relation, column_names, verify_permissions, quote_columns= false) - %}
40+ {%- set constraint_name = (table_relation .identifier ~ " _" ~ column_names|join (' _' ) ~ " _UK" ) | upper - %}
41+
42+ {%- if constraint_name|length > 63 %}
43+ {%- set constraint_name_query %}
44+ select ' UK_' || md5( ' {{ constraint_name }}' )::varchar as " constraint_name"
45+ {%- endset - %}
46+ {%- set results = run_query(constraint_name_query) - %}
47+ {%- set constraint_name = results .columns [0 ].values ()[0 ] - %}
48+ {% endif %}
49+
50+ {%- set columns_csv = dbt_constraints .get_quoted_column_csv (column_names, quote_columns) - %}
51+
52+ {# - Check that the table does not already have this PK/UK -#}
53+ {%- if not dbt_constraints .unique_constraint_exists (table_relation, column_names) - %}
54+
55+ {%- if dbt_constraints .have_ownership_priv (table_relation, verify_permissions) - %}
56+
57+ {%- do log(" Creating unique key: " ~ constraint_name, info= true) - %}
58+ {%- call statement(' add_uk' , fetch_result= False, auto_begin= True) - %}
59+ ALTER TABLE {{table_relation}} ADD CONSTRAINT {{constraint_name}} UNIQUE ( {{columns_csv}} )
60+ {%- endcall - %}
61+ {{ adapter .commit () }}
62+
63+ {%- else - %}
64+ {%- do log(" Skipping " ~ constraint_name ~ " because of insufficient privileges: " ~ table_relation, info= false) - %}
65+ {%- endif - %}
66+
67+ {%- else - %}
68+ {%- do log(" Skipping " ~ constraint_name ~ " because PK/UK already exists: " ~ table_relation ~ " " ~ column_names, info= false) - %}
69+ {%- endif - %}
70+
71+ {%- endmacro - %}
72+
73+ {# SQL Server specific implementation to create a not null constraint #}
74+ {%- macro sqlserver__create_not_null(table_relation, column_names, verify_permissions, quote_columns= false) - %}
75+ {%- set columns_list = dbt_constraints .get_quoted_column_list (column_names, quote_columns) - %}
76+
77+ {%- if dbt_constraints .have_ownership_priv (table_relation, verify_permissions) - %}
78+
79+ {%- set modify_statements= [] - %}
80+ {%- for column in columns_list - %}
81+ {%- set modify_statements = modify_statements .append ( " ALTER COLUMN " ~ column ~ " SET NOT NULL" ) - %}
82+ {%- endfor - %}
83+ {%- set modify_statement_csv = modify_statements | join (" , " ) - %}
84+ {%- do log(" Creating not null constraint for: " ~ columns_list | join (" , " ) ~ " in " ~ table_relation, info= true) - %}
85+ {%- call statement(' add_nn' , fetch_result= False, auto_begin= True) - %}
86+ ALTER TABLE {{table_relation}} {{ modify_statement_csv }};
87+ {%- endcall - %}
88+ {{ adapter .commit () }}
89+
90+ {%- else - %}
91+ {%- do log(" Skipping not null constraint for " ~ columns_list | join (" , " ) ~ " in " ~ table_relation ~ " because of insufficient privileges: " ~ table_relation, info= true) - %}
92+ {%- endif - %}
93+ {%- endmacro - %}
94+
95+ {# SQL Server specific implementation to create a foreign key #}
96+ {%- macro sqlserver__create_foreign_key(pk_table_relation, pk_column_names, fk_table_relation, fk_column_names, verify_permissions, quote_columns= true) - %}
97+ {%- set constraint_name = (fk_table_relation .identifier ~ " _" ~ fk_column_names|join (' _' ) ~ " _FK" ) | upper - %}
98+
99+ {%- if constraint_name|length > 63 %}
100+ {%- set constraint_name_query %}
101+ select ' FK_' || md5( ' {{ constraint_name }}' )::varchar as " constraint_name"
102+ {%- endset - %}
103+ {%- set results = run_query(constraint_name_query) - %}
104+ {%- set constraint_name = results .columns [0 ].values ()[0 ] - %}
105+ {% endif %}
106+
107+ {%- set fk_columns_csv = dbt_constraints .get_quoted_column_csv (fk_column_names, quote_columns) - %}
108+ {%- set pk_columns_csv = dbt_constraints .get_quoted_column_csv (pk_column_names, quote_columns) - %}
109+ {# - Check that the PK table has a PK or UK -#}
110+ {%- if dbt_constraints .unique_constraint_exists (pk_table_relation, pk_column_names) - %}
111+ {# - Check if the table already has this foreign key -#}
112+ {%- if not dbt_constraints .foreign_key_exists (fk_table_relation, fk_column_names) - %}
113+
114+ {%- if dbt_constraints .have_ownership_priv (fk_table_relation, verify_permissions) and dbt_constraints .have_references_priv (pk_table_relation, verify_permissions) - %}
115+
116+ {%- do log(" Creating foreign key: " ~ constraint_name ~ " referencing " ~ pk_table_relation .identifier ~ " " ~ pk_column_names, info= true) - %}
117+ {%- call statement(' add_fk' , fetch_result= False, auto_begin= True) - %}
118+ ALTER TABLE {{fk_table_relation}} ADD CONSTRAINT {{constraint_name}} FOREIGN KEY ( {{fk_columns_csv}} ) REFERENCES {{pk_table_relation}} ( {{pk_columns_csv}} ) ON DELETE NO ACTION DEFERRABLE INITIALLY DEFERRED
119+ {%- endcall - %}
120+ {{ adapter .commit () }}
121+
122+ {%- else - %}
123+ {%- do log(" Skipping " ~ constraint_name ~ " because of insufficient privileges: " ~ fk_table_relation ~ " referencing " ~ pk_table_relation, info= true) - %}
124+ {%- endif - %}
125+
126+ {%- else - %}
127+ {%- do log(" Skipping " ~ constraint_name ~ " because FK already exists: " ~ fk_table_relation ~ " " ~ fk_column_names, info= false) - %}
128+ {%- endif - %}
129+ {%- else - %}
130+ {%- do log(" Skipping " ~ constraint_name ~ " because a PK/UK was not found on the PK table: " ~ pk_table_relation ~ " " ~ pk_column_names, info= true) - %}
131+ {%- endif - %}
132+
133+ {%- endmacro - %}
134+
135+
136+
137+ {# - This macro is used in create macros to avoid duplicate PK/UK constraints
138+ and to skip FK where no PK/ UK constraint exists on the parent table - # }
139+ {%- macro sqlserver__unique_constraint_exists(table_relation, column_names) - %}
140+ {%- set lookup_query - %}
141+ select c .oid as constraint_name
142+ , upper (col .attname ) as column_name
143+ from pg_constraint c
144+ cross join lateral unnest(c .conkey ) as con(conkey)
145+ join pg_class tbl on tbl .oid = c .conrelid
146+ join pg_namespace ns on ns .oid = tbl .relnamespace
147+ join pg_attribute col on (col .attrelid = tbl .oid
148+ and col .attnum = con .conkey )
149+ where c .contype in (' p' , ' u' )
150+ and ns .nspname ilike ' {{table_relation.schema}}'
151+ and tbl .relname ilike ' {{table_relation.identifier}}'
152+ order by constraint_name
153+ {%- endset - %}
154+ {%- do log(" Lookup: " ~ lookup_query, info= false) - %}
155+ {%- set constraint_list = run_query(lookup_query) - %}
156+ {%- if constraint_list .columns [" column_name" ].values () | count > 0 - %}
157+ {%- for constraint in constraint_list .group_by (" constraint_name" ) - %}
158+ {%- if dbt_constraints .column_list_matches (constraint .columns[" column_name" ].values (), column_names ) - %}
159+ {%- do log(" Found PK/UK key: " ~ table_relation ~ " " ~ column_names, info= false) - %}
160+ {{ return(true) }}
161+ {%- endif - %}
162+ {% endfor %}
163+ {%- endif - %}# }
164+
165+ {# - If we get this far then the table does not have either constraint -#}
166+ {%- do log(" No PK/UK key: " ~ table_relation ~ " " ~ column_names, info= false) - %}
167+ {{ return(false) }}
168+ {%- endmacro - %}
169+
170+
171+
172+ {# - This macro is used in create macros to avoid duplicate FK constraints -#}
173+ {%- macro sqlserver__foreign_key_exists(table_relation, column_names) - %}
174+ {%- set lookup_query - %}
175+ select c .oid as fk_name
176+ , upper (col .attname ) as fk_column_name
177+ from pg_constraint c
178+ cross join lateral unnest(c .conkey ) as con(conkey)
179+ join pg_class tbl on tbl .oid = c .conrelid
180+ join pg_namespace ns on ns .oid = tbl .relnamespace
181+ join pg_attribute col on (col .attrelid = tbl .oid
182+ and col .attnum = con .conkey )
183+ where c .contype in (' f' )
184+ and ns .nspname ilike ' {{table_relation.schema}}'
185+ and tbl .relname ilike ' {{table_relation.identifier}}'
186+ order by fk_name
187+ {%- endset - %}
188+ {%- do log(" Lookup: " ~ lookup_query, info= false) - %}
189+ {%- set constraint_list = run_query(lookup_query) - %}
190+ {%- if constraint_list .columns [" fk_column_name" ].values () | count > 0 - %}
191+ {%- for constraint in constraint_list .group_by (" fk_name" ) - %}
192+ {%- if dbt_constraints .column_list_matches (constraint .columns[" fk_column_name" ].values (), column_names ) - %}
193+ {%- do log(" Found FK key: " ~ table_relation ~ " " ~ column_names, info= false) - %}
194+ {{ return(true) }}
195+ {%- endif - %}
196+ {% endfor %}
197+ {%- endif - %}
198+
199+ {# - If we get this far then the table does not have this constraint -#}
200+ {%- do log(" No FK key: " ~ table_relation ~ " " ~ column_names, info= false) - %}
201+ {{ return(false) }}
202+ {%- endmacro - %}
203+
204+
205+ {%- macro sqlserver__have_references_priv(table_relation, verify_permissions) - %}
206+ {%- if verify_permissions is sameas true - %}
207+
208+ {%- set lookup_query - %}
209+ select case when count (* ) > 0 then ' y' else ' n' end as " have_references"
210+ from information_schema .table_privileges t
211+ join information_schema .enabled_roles er on t .grantee = er .role_name
212+ where upper (t .table_schema ) = upper (' {{table_relation.schema}}' )
213+ and upper (t .table_name ) = upper (' {{table_relation.identifier}}' )
214+ {%- endset - %}
215+ {%- do log(" Lookup: " ~ lookup_query, info= false) - %}
216+ {%- set results = run_query(lookup_query) - %}
217+ {%- if " y" in ( results .columns [" have_references" ].values () ) - %}
218+ {{ return(true) }}
219+ {%- endif - %}
220+
221+ {{ return(false) }}
222+ {%- else - %}
223+ {{ return(true) }}
224+ {%- endif - %}
225+ {%- endmacro - %}
226+
227+
228+ {%- macro sqlserver__have_ownership_priv(table_relation, verify_permissions) - %}
229+ {%- if verify_permissions is sameas true - %}
230+
231+ {%- set lookup_query - %}
232+ select case when count (* ) > 0 then ' y' else ' n' end as " have_ownership"
233+ from pg_catalog .pg_tables t
234+ join information_schema .enabled_roles er on t .tableowner = er .role_name
235+ where upper (t .schemaname ) = upper (' {{table_relation.schema}}' )
236+ and upper (t .tablename ) = upper (' {{table_relation.identifier}}' )
237+ {%- endset - %}
238+ {%- do log(" Lookup: " ~ lookup_query, info= false) - %}
239+ {%- set results = run_query(lookup_query) - %}
240+ {%- if " y" in ( results .columns [" have_ownership" ].values () ) - %}
241+ {{ return(true) }}
242+ {%- endif - %}
243+
244+ {{ return(false) }}
245+ {%- else - %}
246+ {{ return(true) }}
247+ {%- endif - %}
248+ {%- endmacro - %}
249+
250+
251+ {% macro sqlserver__drop_referential_constraints(relation) - %}
252+ {%- set lookup_query - %}
253+ select constraint_name
254+ from information_schema .table_constraints
255+ where table_schema = ' {{relation.schema}}'
256+ and table_name= ' {{relation.identifier}}'
257+ and constraint_type in (' FOREIGN KEY' , ' PRIMARY KEY' , ' UNIQUE' )
258+ {%- endset - %}
259+ {%- set constraint_list = run_query(lookup_query) - %}
260+
261+ {%- for constraint_name in constraint_list .columns [" constraint_name" ].values () - %}
262+ {%- do log(" Dropping constraint: " ~ constraint_name ~ " from table " ~ relation, info= false) - %}
263+ {%- call statement(' drop_constraint_cascade' , fetch_result= False, auto_begin= True) - %}
264+ ALTER TABLE {{relation}} DROP CONSTRAINT IF EXISTS " {{constraint_name}}" CASCADE
265+ {%- endcall - %}
266+ {{ adapter .commit () }}
267+ {% endfor %}
268+
269+ {% endmacro %}
270+
271+ {# - SQL Server will error if you try to truncate tables with FK constraints or tables with PK/UK constraints
272+ referenced by FK so we will drop all constraints before truncating tables - # }
273+ {% macro sqlserver__truncate_relation(relation) - %}
274+ {{ sqlserver__drop_referential_constraints(relation) }}
275+ {{ return(adapter .dispatch (' truncate_relation' , ' dbt' )(relation)) }}
276+ {% endmacro %}
277+
278+ {# - SQL Server will get deadlocks if you try to drop tables with FK constraints or tables with PK/UK constraints
279+ referenced by FK so we will drop all constraints before dropping tables - # }
280+ {% macro sqlserver__drop_relation(relation) - %}
281+ {{ sqlserver__drop_referential_constraints(relation) }}
282+ {{ return(adapter .dispatch (' drop_relation' , ' dbt' )(relation)) }}
283+ {% endmacro %}
0 commit comments