|
1 | 1 | import decimal |
2 | 2 | from abc import ABC, abstractmethod |
3 | | -from typing import Sequence, Optional, Tuple, Union, Dict, List |
| 3 | +from typing import Tuple, Union |
4 | 4 | from datetime import datetime |
5 | 5 |
|
6 | 6 | from runtype import dataclass |
7 | | -from typing_extensions import Self |
8 | 7 |
|
9 | | -from data_diff.abcs.compiler import AbstractCompiler |
10 | 8 | from data_diff.utils import ArithAlphanumeric, ArithUUID, Unknown |
11 | 9 |
|
12 | 10 |
|
@@ -172,255 +170,3 @@ class UnknownColType(ColType): |
172 | 170 | text: str |
173 | 171 |
|
174 | 172 | supported = False |
175 | | - |
176 | | - |
177 | | -class AbstractDialect(ABC): |
178 | | - """Dialect-dependent query expressions""" |
179 | | - |
180 | | - @abstractmethod |
181 | | - def compile(self, compiler: AbstractCompiler, elem, params=None) -> str: |
182 | | - raise NotImplementedError |
183 | | - |
184 | | - @abstractmethod |
185 | | - def parse_table_name(self, name: str) -> DbPath: |
186 | | - "Parse the given table name into a DbPath" |
187 | | - |
188 | | - @property |
189 | | - @abstractmethod |
190 | | - def name(self) -> str: |
191 | | - "Name of the dialect" |
192 | | - |
193 | | - @classmethod |
194 | | - @abstractmethod |
195 | | - def load_mixins(cls, *abstract_mixins) -> Self: |
196 | | - "Load a list of mixins that implement the given abstract mixins" |
197 | | - |
198 | | - @property |
199 | | - @abstractmethod |
200 | | - def ROUNDS_ON_PREC_LOSS(self) -> bool: |
201 | | - "True if db rounds real values when losing precision, False if it truncates." |
202 | | - |
203 | | - @abstractmethod |
204 | | - def quote(self, s: str): |
205 | | - "Quote SQL name" |
206 | | - |
207 | | - @abstractmethod |
208 | | - def concat(self, items: List[str]) -> str: |
209 | | - "Provide SQL for concatenating a bunch of columns into a string" |
210 | | - |
211 | | - @abstractmethod |
212 | | - def is_distinct_from(self, a: str, b: str) -> str: |
213 | | - "Provide SQL for a comparison where NULL = NULL is true" |
214 | | - |
215 | | - @abstractmethod |
216 | | - def to_string(self, s: str) -> str: |
217 | | - # TODO rewrite using cast_to(x, str) |
218 | | - "Provide SQL for casting a column to string" |
219 | | - |
220 | | - @abstractmethod |
221 | | - def random(self) -> str: |
222 | | - "Provide SQL for generating a random number betweein 0..1" |
223 | | - |
224 | | - @abstractmethod |
225 | | - def current_timestamp(self) -> str: |
226 | | - "Provide SQL for returning the current timestamp, aka now" |
227 | | - |
228 | | - @abstractmethod |
229 | | - def current_database(self) -> str: |
230 | | - "Provide SQL for returning the current default database." |
231 | | - |
232 | | - @abstractmethod |
233 | | - def current_schema(self) -> str: |
234 | | - "Provide SQL for returning the current default schema." |
235 | | - |
236 | | - @abstractmethod |
237 | | - def offset_limit( |
238 | | - self, offset: Optional[int] = None, limit: Optional[int] = None, has_order_by: Optional[bool] = None |
239 | | - ) -> str: |
240 | | - "Provide SQL fragment for limit and offset inside a select" |
241 | | - |
242 | | - @abstractmethod |
243 | | - def explain_as_text(self, query: str) -> str: |
244 | | - "Provide SQL for explaining a query, returned as table(varchar)" |
245 | | - |
246 | | - @abstractmethod |
247 | | - def timestamp_value(self, t: datetime) -> str: |
248 | | - "Provide SQL for the given timestamp value" |
249 | | - |
250 | | - @abstractmethod |
251 | | - def set_timezone_to_utc(self) -> str: |
252 | | - "Provide SQL for setting the session timezone to UTC" |
253 | | - |
254 | | - @abstractmethod |
255 | | - def parse_type( |
256 | | - self, |
257 | | - table_path: DbPath, |
258 | | - col_name: str, |
259 | | - type_repr: str, |
260 | | - datetime_precision: int = None, |
261 | | - numeric_precision: int = None, |
262 | | - numeric_scale: int = None, |
263 | | - ) -> ColType: |
264 | | - "Parse type info as returned by the database" |
265 | | - |
266 | | - @abstractmethod |
267 | | - def to_comparable(self, value: str, coltype: ColType) -> str: |
268 | | - """Ensure that the expression is comparable in ``IS DISTINCT FROM``.""" |
269 | | - |
270 | | - |
271 | | -from typing import TypeVar, Generic |
272 | | - |
273 | | -T_Dialect = TypeVar("T_Dialect", bound=AbstractDialect) |
274 | | - |
275 | | - |
276 | | -class AbstractDatabase(Generic[T_Dialect]): |
277 | | - @property |
278 | | - @abstractmethod |
279 | | - def dialect(self) -> T_Dialect: |
280 | | - "The dialect of the database. Used internally by Database, and also available publicly." |
281 | | - |
282 | | - @classmethod |
283 | | - @abstractmethod |
284 | | - def load_mixins(cls, *abstract_mixins) -> type: |
285 | | - "Extend the dialect with a list of mixins that implement the given abstract mixins." |
286 | | - |
287 | | - @property |
288 | | - @abstractmethod |
289 | | - def CONNECT_URI_HELP(self) -> str: |
290 | | - "Example URI to show the user in help and error messages" |
291 | | - |
292 | | - @property |
293 | | - @abstractmethod |
294 | | - def CONNECT_URI_PARAMS(self) -> List[str]: |
295 | | - "List of parameters given in the path of the URI" |
296 | | - |
297 | | - @abstractmethod |
298 | | - def _query(self, sql_code: str) -> list: |
299 | | - "Send query to database and return result" |
300 | | - |
301 | | - @abstractmethod |
302 | | - def query_table_schema(self, path: DbPath) -> Dict[str, tuple]: |
303 | | - """Query the table for its schema for table in 'path', and return {column: tuple} |
304 | | - where the tuple is (table_name, col_name, type_repr, datetime_precision?, numeric_precision?, numeric_scale?) |
305 | | -
|
306 | | - Note: This method exists instead of select_table_schema(), just because not all databases support |
307 | | - accessing the schema using a SQL query. |
308 | | - """ |
309 | | - |
310 | | - @abstractmethod |
311 | | - def select_table_unique_columns(self, path: DbPath) -> str: |
312 | | - "Provide SQL for selecting the names of unique columns in the table" |
313 | | - |
314 | | - @abstractmethod |
315 | | - def query_table_unique_columns(self, path: DbPath) -> List[str]: |
316 | | - """Query the table for its unique columns for table in 'path', and return {column}""" |
317 | | - |
318 | | - @abstractmethod |
319 | | - def _process_table_schema( |
320 | | - self, path: DbPath, raw_schema: Dict[str, tuple], filter_columns: Sequence[str], where: str = None |
321 | | - ): |
322 | | - """Process the result of query_table_schema(). |
323 | | -
|
324 | | - Done in a separate step, to minimize the amount of processed columns. |
325 | | - Needed because processing each column may: |
326 | | - * throw errors and warnings |
327 | | - * query the database to sample values |
328 | | -
|
329 | | - """ |
330 | | - |
331 | | - @abstractmethod |
332 | | - def close(self): |
333 | | - "Close connection(s) to the database instance. Querying will stop functioning." |
334 | | - |
335 | | - @property |
336 | | - @abstractmethod |
337 | | - def is_autocommit(self) -> bool: |
338 | | - "Return whether the database autocommits changes. When false, COMMIT statements are skipped." |
339 | | - |
340 | | - |
341 | | -class AbstractTable(ABC): |
342 | | - @abstractmethod |
343 | | - def select(self, *exprs, distinct=False, **named_exprs) -> "AbstractTable": |
344 | | - """Choose new columns, based on the old ones. (aka Projection) |
345 | | -
|
346 | | - Parameters: |
347 | | - exprs: List of expressions to constitute the columns of the new table. |
348 | | - If not provided, returns all columns in source table (i.e. ``select *``) |
349 | | - distinct: 'select' or 'select distinct' |
350 | | - named_exprs: More expressions to constitute the columns of the new table, aliased to keyword name. |
351 | | -
|
352 | | - """ |
353 | | - # XXX distinct=SKIP |
354 | | - |
355 | | - @abstractmethod |
356 | | - def where(self, *exprs) -> "AbstractTable": |
357 | | - """Filter the rows, based on the given predicates. (aka Selection)""" |
358 | | - |
359 | | - @abstractmethod |
360 | | - def order_by(self, *exprs) -> "AbstractTable": |
361 | | - """Order the rows lexicographically, according to the given expressions.""" |
362 | | - |
363 | | - @abstractmethod |
364 | | - def limit(self, limit: int) -> "AbstractTable": |
365 | | - """Stop yielding rows after the given limit. i.e. take the first 'n=limit' rows""" |
366 | | - |
367 | | - @abstractmethod |
368 | | - def join(self, target) -> "AbstractTable": |
369 | | - """Join the current table with the target table, returning a new table containing both side-by-side. |
370 | | -
|
371 | | - When joining, it's recommended to use explicit tables names, instead of `this`, in order to avoid potential name collisions. |
372 | | -
|
373 | | - Example: |
374 | | - :: |
375 | | -
|
376 | | - person = table('person') |
377 | | - city = table('city') |
378 | | -
|
379 | | - name_and_city = ( |
380 | | - person |
381 | | - .join(city) |
382 | | - .on(person['city_id'] == city['id']) |
383 | | - .select(person['id'], city['name']) |
384 | | - ) |
385 | | - """ |
386 | | - |
387 | | - @abstractmethod |
388 | | - def group_by(self, *keys): |
389 | | - """Behaves like in SQL, except for a small change in syntax: |
390 | | -
|
391 | | - A call to `.agg()` must follow every call to `.group_by()`. |
392 | | -
|
393 | | - Example: |
394 | | - :: |
395 | | -
|
396 | | - # SELECT a, sum(b) FROM tmp GROUP BY 1 |
397 | | - table('tmp').group_by(this.a).agg(this.b.sum()) |
398 | | -
|
399 | | - # SELECT a, sum(b) FROM a GROUP BY 1 HAVING (b > 10) |
400 | | - (table('tmp') |
401 | | - .group_by(this.a) |
402 | | - .agg(this.b.sum()) |
403 | | - .having(this.b > 10) |
404 | | - ) |
405 | | -
|
406 | | - """ |
407 | | - |
408 | | - @abstractmethod |
409 | | - def count(self) -> int: |
410 | | - """SELECT count() FROM self""" |
411 | | - |
412 | | - @abstractmethod |
413 | | - def union(self, other: "ITable"): |
414 | | - """SELECT * FROM self UNION other""" |
415 | | - |
416 | | - @abstractmethod |
417 | | - def union_all(self, other: "ITable"): |
418 | | - """SELECT * FROM self UNION ALL other""" |
419 | | - |
420 | | - @abstractmethod |
421 | | - def minus(self, other: "ITable"): |
422 | | - """SELECT * FROM self EXCEPT other""" |
423 | | - |
424 | | - @abstractmethod |
425 | | - def intersect(self, other: "ITable"): |
426 | | - """SELECT * FROM self INTERSECT other""" |
0 commit comments