|
1 | 1 | import abc |
2 | 2 | import base64 |
3 | 3 | import csv |
4 | | -import datetime |
5 | 4 | import json |
6 | 5 | import os |
7 | 6 | import shutil |
8 | | -import struct |
9 | 7 | import tempfile |
10 | 8 | from urllib.parse import parse_qs, quote, urlparse |
11 | 9 |
|
12 | 10 | import dlt |
13 | 11 | import dlt.destinations.impl.filesystem.filesystem |
14 | 12 | from dlt.common.configuration.specs import AwsCredentials |
15 | | -from dlt.common.destination.capabilities import DestinationCapabilitiesContext |
16 | | -from dlt.common.schema import Schema |
17 | 13 | from dlt.common.storages.configuration import FileSystemCredentials |
18 | 14 | from dlt.destinations.impl.clickhouse.configuration import ( |
19 | 15 | ClickHouseCredentials, |
20 | 16 | ) |
21 | | -from dlt.destinations.impl.mssql.configuration import MsSqlClientConfiguration |
22 | | -from dlt.destinations.impl.mssql.mssql import ( |
23 | | - HINT_TO_MSSQL_ATTR, |
24 | | - MsSqlJobClient, |
25 | | -) |
26 | | -from dlt.destinations.impl.mssql.sql_client import ( |
27 | | - PyOdbcMsSqlClient, |
28 | | -) |
29 | 17 |
|
30 | 18 | from ingestr.src.errors import MissingValueError |
31 | 19 | from ingestr.src.loader import load_dlt_file |
@@ -155,88 +143,12 @@ def dlt_dest(self, uri: str, **kwargs): |
155 | 143 | return dlt.destinations.duckdb(uri, **kwargs) |
156 | 144 |
|
157 | 145 |
|
158 | | -def handle_datetimeoffset(dto_value: bytes) -> datetime.datetime: |
159 | | - # ref: https://github.com/mkleehammer/pyodbc/issues/134#issuecomment-281739794 |
160 | | - tup = struct.unpack( |
161 | | - "<6hI2h", dto_value |
162 | | - ) # e.g., (2017, 3, 16, 10, 35, 18, 500000000, -6, 0) |
163 | | - return datetime.datetime( |
164 | | - tup[0], |
165 | | - tup[1], |
166 | | - tup[2], |
167 | | - tup[3], |
168 | | - tup[4], |
169 | | - tup[5], |
170 | | - tup[6] // 1000, |
171 | | - datetime.timezone(datetime.timedelta(hours=tup[7], minutes=tup[8])), |
172 | | - ) |
173 | | - |
174 | | - |
175 | | -class OdbcMsSqlClient(PyOdbcMsSqlClient): |
176 | | - SQL_COPT_SS_ACCESS_TOKEN = 1256 |
177 | | - SKIP_CREDENTIALS = {"PWD", "AUTHENTICATION", "UID"} |
178 | | - |
179 | | - def open_connection(self): |
180 | | - cfg = self.credentials._get_odbc_dsn_dict() |
181 | | - if ( |
182 | | - cfg.get("AUTHENTICATION", "").strip().lower() |
183 | | - != "activedirectoryaccesstoken" |
184 | | - ): |
185 | | - return super().open_connection() |
186 | | - |
187 | | - import pyodbc # type: ignore |
188 | | - |
189 | | - dsn = ";".join( |
190 | | - [f"{k}={v}" for k, v in cfg.items() if k not in self.SKIP_CREDENTIALS] |
191 | | - ) |
192 | | - |
193 | | - self._conn = pyodbc.connect( |
194 | | - dsn, |
195 | | - timeout=self.credentials.connect_timeout, |
196 | | - attrs_before={ |
197 | | - self.SQL_COPT_SS_ACCESS_TOKEN: self.serialize_token(cfg["PWD"]), |
198 | | - }, |
199 | | - ) |
200 | | - |
201 | | - # https://github.com/mkleehammer/pyodbc/wiki/Using-an-Output-Converter-function |
202 | | - self._conn.add_output_converter(-155, handle_datetimeoffset) |
203 | | - self._conn.autocommit = True |
204 | | - return self._conn |
205 | | - |
206 | | - def serialize_token(self, token): |
207 | | - # https://github.com/mkleehammer/pyodbc/issues/228#issuecomment-494773723 |
208 | | - encoded = token.encode("utf_16_le") |
209 | | - return struct.pack("<i", len(encoded)) + encoded |
210 | | - |
211 | | - |
212 | | -class MsSqlClient(MsSqlJobClient): |
213 | | - def __init__( |
214 | | - self, |
215 | | - schema: Schema, |
216 | | - config: MsSqlClientConfiguration, |
217 | | - capabilities: DestinationCapabilitiesContext, |
218 | | - ) -> None: |
219 | | - sql_client = OdbcMsSqlClient( |
220 | | - config.normalize_dataset_name(schema), |
221 | | - config.normalize_staging_dataset_name(schema), |
222 | | - config.credentials, |
223 | | - capabilities, |
224 | | - ) |
225 | | - super(MsSqlJobClient, self).__init__(schema, config, sql_client) |
226 | | - self.config: MsSqlClientConfiguration = config |
227 | | - self.sql_client = sql_client |
228 | | - self.active_hints = HINT_TO_MSSQL_ATTR if self.config.create_indexes else {} |
229 | | - self.type_mapper = capabilities.get_type_mapper() |
230 | | - |
231 | | - |
232 | | -class MsSqlDestImpl(dlt.destinations.mssql): |
233 | | - @property |
234 | | - def client_class(self): |
235 | | - return MsSqlClient |
236 | | - |
237 | | - |
238 | 146 | class MsSQLDestination(GenericSqlDestination): |
239 | 147 | def dlt_dest(self, uri: str, **kwargs): |
| 148 | + from ingestr.src.destinations_mssql import ( # type: ignore[import-untyped] |
| 149 | + MsSqlDestImpl, |
| 150 | + ) |
| 151 | + |
240 | 152 | return MsSqlDestImpl(credentials=uri, **kwargs) |
241 | 153 |
|
242 | 154 |
|
|
0 commit comments