Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorporate 1.7.2 changes #461

Merged
merged 27 commits into from
Jan 25, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
7be1b00
update versions to 1.7
cody-scott Dec 17, 2023
f3ded8b
migrated work for fabric connection
cody-scott Dec 18, 2023
bb8009e
added devcontainer and updated development files
cody-scott Dec 19, 2023
fb41851
update connection managers
cody-scott Dec 19, 2023
19480fb
remove macros applied in fabric
cody-scott Dec 19, 2023
e719d71
update sql server specific macros
cody-scott Dec 19, 2023
e4a7869
updated tests
cody-scott Dec 19, 2023
5394dc7
updated changes and contrib
cody-scott Dec 19, 2023
7c2c1cd
added clone sql
cody-scott Dec 19, 2023
9e84072
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 19, 2023
b36e242
all tests passing locally
Jan 25, 2024
6af1ef5
fix: relax python version for pre commit
Jan 25, 2024
b69565b
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 25, 2024
7483ff1
drop python 3.7 support
Jan 25, 2024
d0a5623
Merge branch 'dev' of https://github.com/dbt-msft/dbt-sqlserver into dev
Jan 25, 2024
09a83a5
drop azure CI action
Jan 25, 2024
bd50fb7
drop support for python 3.7 in integration tests
Jan 25, 2024
fb32960
dont cascade drop downstream views
Jan 25, 2024
3d3050e
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 25, 2024
7dfd63b
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 25, 2024
7e900be
overwrite for cascade exception
Jan 25, 2024
9818d69
Merge branch 'dev' of https://github.com/dbt-msft/dbt-sqlserver into dev
Jan 25, 2024
dae9e93
Merge branch 'dev' of https://github.com/cody-scott/dbt-sqlserver int…
Jan 25, 2024
dc22f68
try 3.7 unit tests again
Jan 25, 2024
2af0ce2
downgrade pre-commit
Jan 25, 2024
28e0cfb
revert 3.7 check
Jan 25, 2024
66fedda
restore pre-commit version
Jan 25, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "Python 3",
"image": "mcr.microsoft.com/devcontainers/python:1-3.10-bookworm",
"features": {
"ghcr.io/devcontainers/features/docker-in-docker:2": {}
},
"postStartCommand": "bash .devcontainer/setup_odbc.sh && bash .devcontainer/install_pyenv.sh && bash .devcontainer/setup_env.sh",
"runArgs": [
"--env-file", "${localWorkspaceFolder}/.devcontainer/test.env"
schlich marked this conversation as resolved.
Show resolved Hide resolved
]
}
6 changes: 6 additions & 0 deletions .devcontainer/install_pyenv.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#/bin/bash
curl https://pyenv.run | bash

echo 'export PYENV_ROOT="$HOME/.pyenv"
[[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"' >> ~/.bashrc
6 changes: 6 additions & 0 deletions .devcontainer/setup_env.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pyenv install 3.10.7
pyenv virtualenv 3.10.7 dbt-sqlserver
pyenv activate dbt-sqlserver

make dev
make server
18 changes: 18 additions & 0 deletions .devcontainer/setup_odbc.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
curl https://packages.microsoft.com/keys/microsoft.asc | sudo tee /etc/apt/trusted.gpg.d/microsoft.asc

#Download appropriate package for the OS version
#Choose only ONE of the following, corresponding to your OS version

#Debian 12
curl https://packages.microsoft.com/config/debian/12/prod.list | sudo tee /etc/apt/sources.list.d/mssql-release.list

sudo apt-get update
sudo ACCEPT_EULA=Y apt-get install -y msodbcsql18
# optional: for bcp and sqlcmd
sudo ACCEPT_EULA=Y apt-get install -y mssql-tools18
echo 'export PATH="$PATH:/opt/mssql-tools18/bin"' >> ~/.bashrc
source ~/.bashrc
# optional: for unixODBC development headers
sudo apt-get install -y unixodbc-dev
# optional: kerberos library for debian-slim distributions
sudo apt-get install -y libgssapi-krb5-2
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
default_language_version:
python: python3.9
python: python3.10
repos:
- repo: 'https://github.com/pre-commit/pre-commit-hooks'
rev: v4.4.0
Expand Down
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
# Changelog

### v1.7.2

Updated to use dbt-fabric as the upstream adapter (https://github.com/dbt-msft/dbt-sqlserver/issues/441#issuecomment-1815837171)[https://github.com/dbt-msft/dbt-sqlserver/issues/441#issuecomment-1815837171] and (https://github.com/microsoft/dbt-fabric/issues/105)[https://github.com/microsoft/dbt-fabric/issues/105]

As the fabric adapter implements the majority of auth and required t-sql, this adapter delegates primarily to SQL auth and SQL Server specific
adaptations (using `SELECT INTO` vs `CREATE TABLE AS`).

Additional major changes pulled from fabric adapter:

* `TIMESTAMP` changing from `DATETIMEOFFSET` to `DATETIME2(6)`
* `STRING` changing from `VARCHAR(MAX)` to `VARCHAR(8000)`


#### Future work to be validated

* Fabric specific items that need further over-rides (clone for example needed overriding)
* Azure Auth elements to be deferred to Fabric, but should be validated
* T-SQL Package to be updated and validated with these changes.

### v1.4.3

Another minor release to follow up on the 1.4 releases.
Expand Down
4 changes: 4 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ make help
[Pre-commit](https://pre-commit.com/) helps us to maintain a consistent style and code quality across the entire project.
After running `make dev`, pre-commit will automatically validate your commits and fix any formatting issues whenever possible.

## Devcontainer

A devcontainer file has been added since 1.7.2 to simpify creating the development environment.

## Testing

The functional tests require a running SQL Server instance. You can easily spin up a local instance with the following command:
Expand Down
1 change: 1 addition & 0 deletions dbt/adapters/sqlserver/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
adapter=SQLServerAdapter,
credentials=SQLServerCredentials,
include_path=sqlserver.PACKAGE_PATH,
dependencies=["fabric"],
)

__all__ = [
Expand Down
2 changes: 1 addition & 1 deletion dbt/adapters/sqlserver/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
version = "1.4.3"
version = "1.7.2"
202 changes: 41 additions & 161 deletions dbt/adapters/sqlserver/sql_server_adapter.py
Original file line number Diff line number Diff line change
@@ -1,175 +1,55 @@
from typing import List, Optional

import agate
from dbt.adapters.base.relation import BaseRelation
from dbt.adapters.cache import _make_ref_key_msg
from dbt.adapters.sql import SQLAdapter
from dbt.adapters.sql.impl import CREATE_SCHEMA_MACRO_NAME
from dbt.events.functions import fire_event
from dbt.events.types import SchemaCreation
# https://github.com/microsoft/dbt-fabric/blob/main/dbt/adapters/fabric/fabric_adapter.py
from dbt.adapters.fabric import FabricAdapter

from dbt.adapters.sqlserver.sql_server_column import SQLServerColumn
from dbt.adapters.sqlserver.sql_server_configs import SQLServerConfigs
from dbt.adapters.sqlserver.sql_server_connection_manager import SQLServerConnectionManager

# from dbt.adapters.capability import Capability, CapabilityDict, CapabilitySupport, Support


class SQLServerAdapter(SQLAdapter):
class SQLServerAdapter(FabricAdapter):
ConnectionManager = SQLServerConnectionManager
Column = SQLServerColumn
AdapterSpecificConfigs = SQLServerConfigs

def create_schema(self, relation: BaseRelation) -> None:
relation = relation.without_identifier()
fire_event(SchemaCreation(relation=_make_ref_key_msg(relation)))
macro_name = CREATE_SCHEMA_MACRO_NAME
kwargs = {
"relation": relation,
}

if self.config.credentials.schema_authorization:
kwargs["schema_authorization"] = self.config.credentials.schema_authorization
macro_name = "sqlserver__create_schema_with_authorization"

self.execute_macro(macro_name, kwargs=kwargs)
self.commit_if_has_connection()
# _capabilities: CapabilityDict = CapabilityDict(
# {
# Capability.SchemaMetadataByRelations: CapabilitySupport(support=Support.Full),
# Capability.TableLastModifiedMetadata: CapabilitySupport(support=Support.Full),
# }
# )

# region - these are implement in fabric but not in sqlserver
# _capabilities: CapabilityDict = CapabilityDict(
# {
# Capability.SchemaMetadataByRelations: CapabilitySupport(support=Support.Full),
# Capability.TableLastModifiedMetadata: CapabilitySupport(support=Support.Full),
# }
# )
# CONSTRAINT_SUPPORT = {
# ConstraintType.check: ConstraintSupport.NOT_SUPPORTED,
# ConstraintType.not_null: ConstraintSupport.ENFORCED,
# ConstraintType.unique: ConstraintSupport.ENFORCED,
# ConstraintType.primary_key: ConstraintSupport.ENFORCED,
# ConstraintType.foreign_key: ConstraintSupport.ENFORCED,
# }

# @available.parse(lambda *a, **k: [])
# def get_column_schema_from_query(self, sql: str) -> List[BaseColumn]:
# """Get a list of the Columns with names and data types from the given sql."""
# _, cursor = self.connections.add_select_query(sql)

# columns = [
# self.Column.create(
# column_name, self.connections.data_type_code_to_name(column_type_code)
# )
# # https://peps.python.org/pep-0249/#description
# for column_name, column_type_code, *_ in cursor.description
# ]
# return columns
# endregion

@classmethod
def date_function(cls):
return "getdate()"

@classmethod
def convert_text_type(cls, agate_table, col_idx):
column = agate_table.columns[col_idx]
# see https://github.com/fishtown-analytics/dbt/pull/2255
lens = [len(d.encode("utf-8")) for d in column.values_without_nulls()]
max_len = max(lens) if lens else 64
length = max_len if max_len > 16 else 16
return "varchar({})".format(length)

@classmethod
def convert_datetime_type(cls, agate_table, col_idx):
return "datetime"

@classmethod
def convert_boolean_type(cls, agate_table, col_idx):
return "bit"

@classmethod
def convert_number_type(cls, agate_table, col_idx):
decimals = agate_table.aggregate(agate.MaxPrecision(col_idx))
return "float" if decimals else "int"

@classmethod
def convert_time_type(cls, agate_table, col_idx):
return "datetime"

# Methods used in adapter tests
def timestamp_add_sql(self, add_to: str, number: int = 1, interval: str = "hour") -> str:
# note: 'interval' is not supported for T-SQL
# for backwards compatibility, we're compelled to set some sort of
# default. A lot of searching has lead me to believe that the
# '+ interval' syntax used in postgres/redshift is relatively common
# and might even be the SQL standard's intention.
return f"DATEADD({interval},{number},{add_to})"

def string_add_sql(
self,
add_to: str,
value: str,
location="append",
) -> str:
"""
`+` is T-SQL's string concatenation operator
"""
if location == "append":
return f"{add_to} + '{value}'"
elif location == "prepend":
return f"'{value}' + {add_to}"
else:
raise ValueError(f'Got an unexpected location value of "{location}"')

def get_rows_different_sql(
self,
relation_a: BaseRelation,
relation_b: BaseRelation,
column_names: Optional[List[str]] = None,
except_operator: str = "EXCEPT",
) -> str:
"""
note: using is not supported on Synapse so COLUMNS_EQUAL_SQL is adjsuted
Generate SQL for a query that returns a single row with a two
columns: the number of rows that are different between the two
relations and the number of mismatched rows.
"""
# This method only really exists for test reasons.
names: List[str]
if column_names is None:
columns = self.get_columns_in_relation(relation_a)
names = sorted((self.quote(c.name) for c in columns))
else:
names = sorted((self.quote(n) for n in column_names))
columns_csv = ", ".join(names)

sql = COLUMNS_EQUAL_SQL.format(
columns=columns_csv,
relation_a=str(relation_a),
relation_b=str(relation_b),
except_op=except_operator,
)

return sql

def valid_incremental_strategies(self):
"""The set of standard builtin strategies which this adapter supports out-of-the-box.
Not used to validate custom strategies defined by end users.
"""
return ["append", "delete+insert", "merge", "insert_overwrite"]

# This is for use in the test suite
def run_sql_for_tests(self, sql, fetch, conn):
cursor = conn.handle.cursor()
try:
cursor.execute(sql)
if not fetch:
conn.handle.commit()
if fetch == "one":
return cursor.fetchone()
elif fetch == "all":
return cursor.fetchall()
else:
return
except BaseException:
if conn.handle and not getattr(conn.handle, "closed", True):
conn.handle.rollback()
raise
finally:
conn.transaction_open = False


COLUMNS_EQUAL_SQL = """
with diff_count as (
SELECT
1 as id,
COUNT(*) as num_missing FROM (
(SELECT {columns} FROM {relation_a} {except_op}
SELECT {columns} FROM {relation_b})
UNION ALL
(SELECT {columns} FROM {relation_b} {except_op}
SELECT {columns} FROM {relation_a})
) as a
), table_a as (
SELECT COUNT(*) as num_rows FROM {relation_a}
), table_b as (
SELECT COUNT(*) as num_rows FROM {relation_b}
), row_count_diff as (
select
1 as id,
table_a.num_rows - table_b.num_rows as difference
from table_a, table_b
)
select
row_count_diff.difference as row_count_difference,
diff_count.num_missing as num_mismatched
from row_count_diff
join diff_count on row_count_diff.id = diff_count.id
""".strip()
21 changes: 3 additions & 18 deletions dbt/adapters/sqlserver/sql_server_column.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,5 @@
from typing import Any, ClassVar, Dict
from dbt.adapters.fabric import FabricColumn

from dbt.adapters.base import Column


class SQLServerColumn(Column):
TYPE_LABELS: ClassVar[Dict[str, str]] = {
"STRING": "VARCHAR(MAX)",
"TIMESTAMP": "DATETIMEOFFSET",
"FLOAT": "FLOAT",
"INTEGER": "INT",
"BOOLEAN": "BIT",
}

@classmethod
def string_type(cls, size: int) -> str:
return f"varchar({size if size > 0 else 'MAX'})"

def literal(self, value: Any) -> str:
return "cast('{}' as {})".format(value, self.data_type)
class SQLServerColumn(FabricColumn):
...
7 changes: 3 additions & 4 deletions dbt/adapters/sqlserver/sql_server_configs.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
from dataclasses import dataclass
from typing import Optional

from dbt.adapters.protocol import AdapterConfig
from dbt.adapters.fabric import FabricConfigs


@dataclass
class SQLServerConfigs(AdapterConfig):
auto_provision_aad_principals: Optional[bool] = False
class SQLServerConfigs(FabricConfigs):
...
Loading
Loading