Skip to content

Commit 83e8d9e

Browse files
authored
Merge pull request #56 from microsoft/v1.5.0rc2
V1.5.0rc2
2 parents 0862e66 + 47c1d72 commit 83e8d9e

File tree

21 files changed

+2315
-283
lines changed

21 files changed

+2315
-283
lines changed

.github/workflows/integration-tests-azure.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212
strategy:
1313
fail-fast: false
1414
matrix:
15-
python_version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
15+
python_version: ["3.8", "3.9", "3.10", "3.11"]
1616
profile: ["ci_azure_auto"]
1717
msodbc_version: ["17", "18"]
1818
max-parallel: 1

.github/workflows/publish-docker.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
publish-docker-client:
1414
strategy:
1515
matrix:
16-
python_version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
16+
python_version: ["3.8", "3.9", "3.10", "3.11"]
1717
docker_target: ["msodbc17", "msodbc18"]
1818
runs-on: ubuntu-latest
1919
permissions:
@@ -24,7 +24,7 @@ jobs:
2424
uses: actions/checkout@v3
2525

2626
- name: Log in to the Container registry
27-
uses: docker/login-action@v2.1.0
27+
uses: docker/login-action@v2.2.0
2828
with:
2929
registry: ghcr.io
3030
username: ${{ github.actor }}

.github/workflows/unit-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
name: Unit tests
1717
strategy:
1818
matrix:
19-
python_version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
19+
python_version: ["3.8", "3.9", "3.10", "3.11"]
2020
runs-on: ubuntu-latest
2121
permissions:
2222
contents: read

.pre-commit-config.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ repos:
1717
- id: check-builtin-literals
1818
- id: check-merge-conflict
1919
- id: no-commit-to-branch
20-
- id: fix-byte-order-marker
20+
# - id: fix-byte-order-marker
2121
- id: mixed-line-ending
2222
- id: check-docstring-first
2323
- repo: 'https://github.com/adrienverge/yamllint'
@@ -66,14 +66,14 @@ repos:
6666
- '--check'
6767
- '--diff'
6868
- repo: 'https://github.com/pycqa/flake8'
69-
rev: 6.0.0
69+
rev: 6.1.0
7070
hooks:
7171
- id: flake8
7272
args:
73-
- '--max-line-length=99'
73+
- '--max-line-length=1000'
7474
- id: flake8
7575
args:
76-
- '--max-line-length=99'
76+
- '--max-line-length=1000'
7777
alias: flake8-check
7878
stages:
7979
- manual

CHANGELOG.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,19 @@
11
# Changelog
22

3+
### v1.5.0-rc1
4+
5+
* Upgraded dbt-fabric adapter to match dbt-core & dbt-tests-adapter version 1.5.2.
6+
* Added constraint support to dbt-fabric adapter.
7+
* Check constraints are not supported.
8+
* Column & model constraints are not supported in CREATE TABLE command by Microsoft Fabric Data Warehouse. Column and model constraints are implemented by ALTER TABLE ADD Constraints command.
9+
* user-defined names for constraints are not currently supported. naming is handled by the adapter, until `SP_RENAME` is supported in Fabric
10+
* Added tests related to constraints.
11+
* Bumped wheel, precommit, docker package versions.
12+
13+
314
### v1.4.0-rc3
415

5-
Updated connection property to track dbt telemetry
16+
Updated connection property to track dbt telemetry by Microsoft.
617

718
### v1.4.0-rc2
819

dbt/adapters/fabric/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
version = "1.4.0-rc3"
1+
version = "1.5.0-rc1"

dbt/adapters/fabric/fabric_adapter.py

Lines changed: 89 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
11
from typing import List, Optional
22

33
import agate
4+
from dbt.adapters.base import Column as BaseColumn
5+
6+
# from dbt.events.functions import fire_event
7+
# from dbt.events.types import SchemaCreation
8+
from dbt.adapters.base.impl import ConstraintSupport
9+
from dbt.adapters.base.meta import available
410
from dbt.adapters.base.relation import BaseRelation
5-
from dbt.adapters.cache import _make_ref_key_msg
11+
from dbt.adapters.cache import _make_ref_key_dict
12+
13+
# from dbt.adapters.cache import _make_ref_key_msg
614
from dbt.adapters.sql import SQLAdapter
715
from dbt.adapters.sql.impl import CREATE_SCHEMA_MACRO_NAME
16+
from dbt.contracts.graph.nodes import ColumnLevelConstraint, ConstraintType, ModelLevelConstraint
817
from dbt.events.functions import fire_event
918
from dbt.events.types import SchemaCreation
1019

@@ -18,9 +27,44 @@ class FabricAdapter(SQLAdapter):
1827
Column = FabricColumn
1928
AdapterSpecificConfigs = FabricConfigs
2029

30+
CONSTRAINT_SUPPORT = {
31+
ConstraintType.check: ConstraintSupport.NOT_SUPPORTED,
32+
ConstraintType.not_null: ConstraintSupport.ENFORCED,
33+
ConstraintType.unique: ConstraintSupport.ENFORCED,
34+
ConstraintType.primary_key: ConstraintSupport.ENFORCED,
35+
ConstraintType.foreign_key: ConstraintSupport.ENFORCED,
36+
}
37+
38+
@available.parse(lambda *a, **k: [])
39+
def get_column_schema_from_query(self, sql: str) -> List[BaseColumn]:
40+
"""Get a list of the Columns with names and data types from the given sql."""
41+
_, cursor = self.connections.add_select_query(sql)
42+
43+
columns = [
44+
self.Column.create(
45+
column_name, self.connections.data_type_code_to_name(column_type_code)
46+
)
47+
# https://peps.python.org/pep-0249/#description
48+
for column_name, column_type_code, *_ in cursor.description
49+
]
50+
return columns
51+
52+
@classmethod
53+
def convert_boolean_type(cls, agate_table, col_idx):
54+
return "bit"
55+
56+
@classmethod
57+
def convert_datetime_type(cls, agate_table, col_idx):
58+
return "datetime2(6)"
59+
60+
@classmethod
61+
def convert_number_type(cls, agate_table, col_idx):
62+
decimals = agate_table.aggregate(agate.MaxPrecision(col_idx))
63+
return "float" if decimals else "int"
64+
2165
def create_schema(self, relation: BaseRelation) -> None:
2266
relation = relation.without_identifier()
23-
fire_event(SchemaCreation(relation=_make_ref_key_msg(relation)))
67+
fire_event(SchemaCreation(relation=_make_ref_key_dict(relation)))
2468
macro_name = CREATE_SCHEMA_MACRO_NAME
2569
kwargs = {
2670
"relation": relation,
@@ -33,10 +77,6 @@ def create_schema(self, relation: BaseRelation) -> None:
3377
self.execute_macro(macro_name, kwargs=kwargs)
3478
self.commit_if_has_connection()
3579

36-
@classmethod
37-
def date_function(cls):
38-
return "getdate()"
39-
4080
@classmethod
4181
def convert_text_type(cls, agate_table, col_idx):
4282
column = agate_table.columns[col_idx]
@@ -46,23 +86,14 @@ def convert_text_type(cls, agate_table, col_idx):
4686
length = max_len if max_len > 16 else 16
4787
return "varchar({})".format(length)
4888

49-
@classmethod
50-
def convert_datetime_type(cls, agate_table, col_idx):
51-
return "datetime2(6)"
52-
53-
@classmethod
54-
def convert_boolean_type(cls, agate_table, col_idx):
55-
return "bit"
56-
57-
@classmethod
58-
def convert_number_type(cls, agate_table, col_idx):
59-
decimals = agate_table.aggregate(agate.MaxPrecision(col_idx))
60-
return "float" if decimals else "int"
61-
6289
@classmethod
6390
def convert_time_type(cls, agate_table, col_idx):
6491
return "time(6)"
6592

93+
@classmethod
94+
def date_function(cls):
95+
return "getdate()"
96+
6697
# Methods used in adapter tests
6798
def timestamp_add_sql(self, add_to: str, number: int = 1, interval: str = "hour") -> str:
6899
# note: 'interval' is not supported for T-SQL
@@ -145,6 +176,45 @@ def run_sql_for_tests(self, sql, fetch, conn):
145176
finally:
146177
conn.transaction_open = False
147178

179+
@available
180+
@classmethod
181+
def render_column_constraint(cls, constraint: ColumnLevelConstraint) -> Optional[str]:
182+
rendered_column_constraint = None
183+
if constraint.type == ConstraintType.not_null:
184+
rendered_column_constraint = "not null "
185+
else:
186+
rendered_column_constraint = ""
187+
188+
if rendered_column_constraint:
189+
rendered_column_constraint = rendered_column_constraint.strip()
190+
191+
return rendered_column_constraint
192+
193+
@classmethod
194+
def render_model_constraint(cls, constraint: ModelLevelConstraint) -> Optional[str]:
195+
constraint_prefix = "add constraint "
196+
column_list = ", ".join(constraint.columns)
197+
198+
if constraint.type == ConstraintType.unique:
199+
return (
200+
constraint_prefix
201+
+ f"{constraint.name} unique nonclustered({column_list}) not enforced"
202+
)
203+
elif constraint.type == ConstraintType.primary_key:
204+
return (
205+
constraint_prefix
206+
+ f"{constraint.name} primary key nonclustered({column_list}) not enforced"
207+
)
208+
elif constraint.type == ConstraintType.foreign_key and constraint.expression:
209+
return (
210+
constraint_prefix
211+
+ f"{constraint.name} foreign key({column_list}) references {constraint.expression} not enforced"
212+
)
213+
elif constraint.type == ConstraintType.custom and constraint.expression:
214+
return f"{constraint_prefix}{constraint.expression}"
215+
else:
216+
return None
217+
148218

149219
COLUMNS_EQUAL_SQL = """
150220
with diff_count as (

dbt/adapters/fabric/fabric_connection_manager.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import time
44
from contextlib import contextmanager
55
from itertools import chain, repeat
6-
from typing import Any, Callable, Dict, Mapping, Optional, Tuple
6+
from typing import Any, Callable, Dict, Mapping, Optional, Tuple, Union
77

88
import agate
99
import dbt.exceptions
@@ -30,6 +30,27 @@
3030

3131
logger = AdapterLogger("fabric")
3232

33+
# https://github.com/mkleehammer/pyodbc/wiki/Data-Types
34+
datatypes = {
35+
# "str": "char",
36+
"str": "varchar",
37+
"uuid.UUID": "uniqueidentifier",
38+
"uuid": "uniqueidentifier",
39+
# "float": "real",
40+
# "float": "float",
41+
"float": "bigint",
42+
# "int": "smallint",
43+
# "int": "tinyint",
44+
"int": "int",
45+
"bytes": "varbinary",
46+
"bool": "bit",
47+
"datetime.date": "date",
48+
"datetime.datetime": "datetime2(6)",
49+
"datetime.time": "time",
50+
"decimal.Decimal": "decimal",
51+
# "decimal.Decimal": "numeric",
52+
}
53+
3354

3455
def convert_bytes_to_mswindows_byte_string(value: bytes) -> bytes:
3556
"""
@@ -469,6 +490,11 @@ def get_response(cls, cursor: Any) -> AdapterResponse:
469490
rows_affected=rows,
470491
)
471492

493+
@classmethod
494+
def data_type_code_to_name(cls, type_code: Union[str, str]) -> str:
495+
data_type = str(type_code)[str(type_code).index("'") + 1 : str(type_code).rindex("'")]
496+
return datatypes[data_type]
497+
472498
def execute(
473499
self, sql: str, auto_begin: bool = True, fetch: bool = False
474500
) -> Tuple[AdapterResponse, agate.Table]:

dbt/include/fabric/macros/adapters/columns.sql

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
{% macro fabric__get_empty_subquery_sql(select_sql, select_sql_header=none) %}
2+
{% if sql.strip().lower().startswith('with') %}
3+
{{ select_sql }}
4+
{% else -%}
5+
select * from (
6+
{{ select_sql }}
7+
) dbt_sbq_tmp
8+
where 1 = 0
9+
{%- endif -%}
10+
11+
{% endmacro %}
12+
113
{% macro fabric__get_columns_in_relation(relation) -%}
214
{% call statement('get_columns_in_relation', fetch_result=True) %}
315

@@ -29,7 +41,6 @@
2941
{{ return(sql_convert_columns_in_relation(table)) }}
3042
{% endmacro %}
3143

32-
3344
{% macro fabric__get_columns_in_query(select_sql) %}
3445
{% call statement('get_columns_in_query', fetch_result=True, auto_begin=False) -%}
3546
select TOP 0 * from (

0 commit comments

Comments
 (0)