collate-data-diff 0.11.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. collate_data_diff-0.11.2/LICENSE +18 -0
  2. collate_data_diff-0.11.2/PKG-INFO +77 -0
  3. collate_data_diff-0.11.2/README.md +14 -0
  4. collate_data_diff-0.11.2/data_diff/__init__.py +180 -0
  5. collate_data_diff-0.11.2/data_diff/__main__.py +618 -0
  6. collate_data_diff-0.11.2/data_diff/abcs/__init__.py +0 -0
  7. collate_data_diff-0.11.2/data_diff/abcs/compiler.py +13 -0
  8. collate_data_diff-0.11.2/data_diff/abcs/database_types.py +308 -0
  9. collate_data_diff-0.11.2/data_diff/cloud/__init__.py +2 -0
  10. collate_data_diff-0.11.2/data_diff/cloud/data_source.py +318 -0
  11. collate_data_diff-0.11.2/data_diff/cloud/datafold_api.py +304 -0
  12. collate_data_diff-0.11.2/data_diff/config.py +127 -0
  13. collate_data_diff-0.11.2/data_diff/databases/__init__.py +17 -0
  14. collate_data_diff-0.11.2/data_diff/databases/_connect.py +306 -0
  15. collate_data_diff-0.11.2/data_diff/databases/base.py +1291 -0
  16. collate_data_diff-0.11.2/data_diff/databases/bigquery.py +315 -0
  17. collate_data_diff-0.11.2/data_diff/databases/clickhouse.py +203 -0
  18. collate_data_diff-0.11.2/data_diff/databases/databricks.py +248 -0
  19. collate_data_diff-0.11.2/data_diff/databases/duckdb.py +192 -0
  20. collate_data_diff-0.11.2/data_diff/databases/mssql.py +229 -0
  21. collate_data_diff-0.11.2/data_diff/databases/mysql.py +159 -0
  22. collate_data_diff-0.11.2/data_diff/databases/oracle.py +195 -0
  23. collate_data_diff-0.11.2/data_diff/databases/postgresql.py +258 -0
  24. collate_data_diff-0.11.2/data_diff/databases/presto.py +197 -0
  25. collate_data_diff-0.11.2/data_diff/databases/redshift.py +217 -0
  26. collate_data_diff-0.11.2/data_diff/databases/snowflake.py +207 -0
  27. collate_data_diff-0.11.2/data_diff/databases/trino.py +50 -0
  28. collate_data_diff-0.11.2/data_diff/databases/vertica.py +160 -0
  29. collate_data_diff-0.11.2/data_diff/dbt.py +604 -0
  30. collate_data_diff-0.11.2/data_diff/dbt_config_validators.py +65 -0
  31. collate_data_diff-0.11.2/data_diff/dbt_parser.py +523 -0
  32. collate_data_diff-0.11.2/data_diff/diff_tables.py +416 -0
  33. collate_data_diff-0.11.2/data_diff/errors.py +74 -0
  34. collate_data_diff-0.11.2/data_diff/format.py +359 -0
  35. collate_data_diff-0.11.2/data_diff/hashdiff_tables.py +264 -0
  36. collate_data_diff-0.11.2/data_diff/info_tree.py +62 -0
  37. collate_data_diff-0.11.2/data_diff/joindiff_tables.py +399 -0
  38. collate_data_diff-0.11.2/data_diff/lexicographic_space.py +240 -0
  39. collate_data_diff-0.11.2/data_diff/parse_time.py +74 -0
  40. collate_data_diff-0.11.2/data_diff/py.typed +0 -0
  41. collate_data_diff-0.11.2/data_diff/queries/__init__.py +0 -0
  42. collate_data_diff-0.11.2/data_diff/queries/api.py +200 -0
  43. collate_data_diff-0.11.2/data_diff/queries/ast_classes.py +798 -0
  44. collate_data_diff-0.11.2/data_diff/queries/base.py +24 -0
  45. collate_data_diff-0.11.2/data_diff/queries/extras.py +29 -0
  46. collate_data_diff-0.11.2/data_diff/query_utils.py +56 -0
  47. collate_data_diff-0.11.2/data_diff/schema.py +52 -0
  48. collate_data_diff-0.11.2/data_diff/table_segment.py +286 -0
  49. collate_data_diff-0.11.2/data_diff/thread_utils.py +98 -0
  50. collate_data_diff-0.11.2/data_diff/tracking.py +237 -0
  51. collate_data_diff-0.11.2/data_diff/utils.py +625 -0
  52. collate_data_diff-0.11.2/data_diff/version.py +1 -0
  53. collate_data_diff-0.11.2/pyproject.toml +99 -0
@@ -0,0 +1,18 @@
1
+ Copyright 2022 DataFold Inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7
+ the Software, and to permit persons to whom the Software is furnished to do so,
8
+ subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,77 @@
1
+ Metadata-Version: 2.1
2
+ Name: collate-data-diff
3
+ Version: 0.11.2
4
+ Summary: Command-line tool and Python library to efficiently diff rows across two different databases.
5
+ Home-page: https://github.com/open-metadata/collate-data-diff
6
+ License: MIT
7
+ Author: Collate
8
+ Requires-Python: >=3.8.0,<4.0
9
+ Classifier: Development Status :: 2 - Pre-Alpha
10
+ Classifier: Environment :: Console
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: Information Technology
13
+ Classifier: Intended Audience :: System Administrators
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.8
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Topic :: Database :: Database Engines/Servers
22
+ Classifier: Typing :: Typed
23
+ Provides-Extra: all-dbs
24
+ Provides-Extra: clickhouse
25
+ Provides-Extra: duckdb
26
+ Provides-Extra: mssql
27
+ Provides-Extra: mysql
28
+ Provides-Extra: oracle
29
+ Provides-Extra: postgresql
30
+ Provides-Extra: preql
31
+ Provides-Extra: presto
32
+ Provides-Extra: redshift
33
+ Provides-Extra: snowflake
34
+ Provides-Extra: trino
35
+ Provides-Extra: vertica
36
+ Requires-Dist: attrs (>=23.1.0)
37
+ Requires-Dist: click (>=8.1)
38
+ Requires-Dist: clickhouse-driver ; extra == "clickhouse" or extra == "all-dbs"
39
+ Requires-Dist: cryptography ; extra == "snowflake" or extra == "all-dbs"
40
+ Requires-Dist: dbt-core (>=1.0.0,<2.0.0)
41
+ Requires-Dist: dsnparse (<0.2.0)
42
+ Requires-Dist: duckdb ; extra == "duckdb" or extra == "all-dbs"
43
+ Requires-Dist: keyring
44
+ Requires-Dist: mashumaro[msgpack] (>=2.9,<3.11.0)
45
+ Requires-Dist: mysql-connector-python (>=8.0.29) ; extra == "mysql" or extra == "all-dbs"
46
+ Requires-Dist: oracledb ; extra == "oracle" or extra == "all-dbs"
47
+ Requires-Dist: preql (>=0.2.19) ; extra == "preql" or extra == "all-dbs"
48
+ Requires-Dist: presto-python-client ; extra == "presto" or extra == "all-dbs"
49
+ Requires-Dist: psycopg2 ; extra == "postgresql" or extra == "redshift" or extra == "all-dbs"
50
+ Requires-Dist: pydantic (>=1.10.12)
51
+ Requires-Dist: pyodbc (>=4.0.39) ; extra == "mssql" or extra == "all-dbs"
52
+ Requires-Dist: rich
53
+ Requires-Dist: snowflake-connector-python (>=3.0.2,<4.0.0) ; extra == "snowflake" or extra == "all-dbs"
54
+ Requires-Dist: tabulate (>=0.9.0)
55
+ Requires-Dist: toml (>=0.10.2)
56
+ Requires-Dist: trino (>=0.314.0) ; extra == "trino" or extra == "all-dbs"
57
+ Requires-Dist: typing-extensions (>=4.0.1)
58
+ Requires-Dist: urllib3 (<2)
59
+ Requires-Dist: vertica-python ; extra == "vertica" or extra == "all-dbs"
60
+ Project-URL: Repository, https://github.com/open-metadata/collate-data-diff
61
+ Description-Content-Type: text/markdown
62
+
63
+ Forked from the archived repository [datafold/data-diff](https://github.com/datafold/data-diff)
64
+ ---
65
+
66
+ # data-diff: Compare datasets fast, within or across SQL databases
67
+
68
+ ## Contributors
69
+
70
+ <a href="https://github.com/datafold/data-diff/graphs/contributors">
71
+ <img src="https://contributors-img.web.app/image?repo=datafold/data-diff" />
72
+ </a>
73
+
74
+ ## License
75
+
76
+ This project is licensed under the terms of the [MIT License](https://github.com/datafold/data-diff/blob/master/LICENSE).
77
+
@@ -0,0 +1,14 @@
1
+ Forked from the archived repository [datafold/data-diff](https://github.com/datafold/data-diff)
2
+ ---
3
+
4
+ # data-diff: Compare datasets fast, within or across SQL databases
5
+
6
+ ## Contributors
7
+
8
+ <a href="https://github.com/datafold/data-diff/graphs/contributors">
9
+ <img src="https://contributors-img.web.app/image?repo=datafold/data-diff" />
10
+ </a>
11
+
12
+ ## License
13
+
14
+ This project is licensed under the terms of the [MIT License](https://github.com/datafold/data-diff/blob/master/LICENSE).
@@ -0,0 +1,180 @@
1
+ from typing import Sequence, Tuple, Iterator, Optional, Union
2
+
3
+ from data_diff.abcs.database_types import DbTime, DbPath
4
+ from data_diff.databases import Database
5
+ from data_diff.tracking import disable_tracking
6
+ from data_diff.databases._connect import connect
7
+ from data_diff.diff_tables import Algorithm
8
+ from data_diff.hashdiff_tables import HashDiffer, DEFAULT_BISECTION_THRESHOLD, DEFAULT_BISECTION_FACTOR
9
+ from data_diff.joindiff_tables import JoinDiffer, TABLE_WRITE_LIMIT
10
+ from data_diff.table_segment import TableSegment
11
+ from data_diff.utils import eval_name_template, Vector
12
+
13
+
14
+ def connect_to_table(
15
+ db_info: Union[str, dict],
16
+ table_name: Union[DbPath, str],
17
+ key_columns: str = ("id",),
18
+ thread_count: Optional[int] = 1,
19
+ **kwargs,
20
+ ) -> TableSegment:
21
+ """Connects to the given database, and creates a TableSegment instance
22
+
23
+ Parameters:
24
+ db_info: Either a URI string, or a dict of connection options.
25
+ table_name: Name of the table as a string, or a tuple that signifies the path.
26
+ key_columns: Names of the key columns
27
+ thread_count: Number of threads for this connection (only if using a threadpooled db implementation)
28
+
29
+ See Also:
30
+ :meth:`connect`
31
+ """
32
+ if isinstance(key_columns, str):
33
+ key_columns = (key_columns,)
34
+
35
+ db: Database = connect(db_info, thread_count=thread_count)
36
+
37
+ if isinstance(table_name, str):
38
+ table_name = db.dialect.parse_table_name(table_name)
39
+
40
+ return TableSegment(db, table_name, key_columns, **kwargs)
41
+
42
+
43
+ def diff_tables(
44
+ table1: TableSegment,
45
+ table2: TableSegment,
46
+ *,
47
+ # Name of the key column, which uniquely identifies each row (usually id)
48
+ key_columns: Sequence[str] = None,
49
+ # Name of updated column, which signals that rows changed (usually updated_at or last_update)
50
+ update_column: str = None,
51
+ # Extra columns to compare
52
+ extra_columns: Tuple[str, ...] = None,
53
+ # Start/end key_column values, used to restrict the segment
54
+ min_key: Vector = None,
55
+ max_key: Vector = None,
56
+ # Start/end update_column values, used to restrict the segment
57
+ min_update: DbTime = None,
58
+ max_update: DbTime = None,
59
+ # Enable/disable threaded diffing. Needed to take advantage of database threads.
60
+ threaded: bool = True,
61
+ # Maximum size of each threadpool. None = auto. Only relevant when threaded is True.
62
+ # There may be many pools, so number of actual threads can be a lot higher.
63
+ max_threadpool_size: Optional[int] = 1,
64
+ # Algorithm
65
+ algorithm: Algorithm = Algorithm.AUTO,
66
+ # An additional 'where' expression to restrict the search space.
67
+ where: str = None,
68
+ # Into how many segments to bisect per iteration (hashdiff only)
69
+ bisection_factor: int = DEFAULT_BISECTION_FACTOR,
70
+ # When should we stop bisecting and compare locally (in row count; hashdiff only)
71
+ bisection_threshold: int = DEFAULT_BISECTION_THRESHOLD,
72
+ # Enable/disable validating that the key columns are unique. (joindiff only)
73
+ validate_unique_key: bool = True,
74
+ # Enable/disable sampling of exclusive rows. Creates a temporary table. (joindiff only)
75
+ sample_exclusive_rows: bool = False,
76
+ # Path of new table to write diff results to. Disabled if not provided. (joindiff only)
77
+ materialize_to_table: Union[str, DbPath] = None,
78
+ # Materialize every row, not just those that are different. (joindiff only)
79
+ materialize_all_rows: bool = False,
80
+ # Maximum number of rows to write when materializing, per thread. (joindiff only)
81
+ table_write_limit: int = TABLE_WRITE_LIMIT,
82
+ # Skips diffing any rows with null keys. (joindiff only)
83
+ skip_null_keys: bool = False,
84
+ ) -> Iterator:
85
+ """Finds the diff between table1 and table2.
86
+
87
+ Parameters:
88
+ key_columns (Tuple[str, ...]): Name of the key column, which uniquely identifies each row (usually id)
89
+ update_column (str, optional): Name of updated column, which signals that rows changed.
90
+ Usually updated_at or last_update. Used by `min_update` and `max_update`.
91
+ extra_columns (Tuple[str, ...], optional): Extra columns to compare
92
+ min_key (:data:`Vector`, optional): Lowest key value, used to restrict the segment
93
+ max_key (:data:`Vector`, optional): Highest key value, used to restrict the segment
94
+ min_update (:data:`DbTime`, optional): Lowest update_column value, used to restrict the segment
95
+ max_update (:data:`DbTime`, optional): Highest update_column value, used to restrict the segment
96
+ threaded (bool): Enable/disable threaded diffing. Needed to take advantage of database threads.
97
+ max_threadpool_size (int): Maximum size of each threadpool. ``None`` means auto.
98
+ Only relevant when `threaded` is ``True``.
99
+ There may be many pools, so number of actual threads can be a lot higher.
100
+ where (str, optional): An additional 'where' expression to restrict the search space.
101
+ algorithm (:class:`Algorithm`): Which diffing algorithm to use (`HASHDIFF` or `JOINDIFF`. Default=`AUTO`)
102
+ bisection_factor (int): Into how many segments to bisect per iteration. (Used when algorithm is `HASHDIFF`)
103
+ bisection_threshold (Number): Minimal row count of segment to bisect, otherwise download
104
+ and compare locally. (Used when algorithm is `HASHDIFF`).
105
+ validate_unique_key (bool): Enable/disable validating that the key columns are unique. (used for `JOINDIFF`. default: True)
106
+ Single query, and can't be threaded, so it's very slow on non-cloud dbs.
107
+ Future versions will detect UNIQUE constraints in the schema.
108
+ sample_exclusive_rows (bool): Enable/disable sampling of exclusive rows. Creates a temporary table. (used for `JOINDIFF`. default: False)
109
+ materialize_to_table (Union[str, DbPath], optional): Path of new table to write diff results to. Disabled if not provided. Used for `JOINDIFF`.
110
+ materialize_all_rows (bool): Materialize every row, not just those that are different. (used for `JOINDIFF`. default: False)
111
+ table_write_limit (int): Maximum number of rows to write when materializing, per thread.
112
+ skip_null_keys (bool): Skips diffing any rows with null PKs (displays a warning if any are null) (used for `JOINDIFF`. default: False)
113
+
114
+ Note:
115
+ The following parameters are used to override the corresponding attributes of the given :class:`TableSegment` instances:
116
+ `key_columns`, `update_column`, `extra_columns`, `min_key`, `max_key`, `where`.
117
+ If different values are needed per table, it's possible to omit them here, and instead set
118
+ them directly when creating each :class:`TableSegment`.
119
+
120
+ Example:
121
+ >>> table1 = connect_to_table('postgresql:///', 'Rating', 'id')
122
+ >>> list(diff_tables(table1, table1))
123
+ []
124
+
125
+ See Also:
126
+ :class:`TableSegment`
127
+ :class:`HashDiffer`
128
+ :class:`JoinDiffer`
129
+
130
+ """
131
+ if isinstance(key_columns, str):
132
+ key_columns = (key_columns,)
133
+
134
+ tables = [table1, table2]
135
+ override_attrs = {
136
+ k: v
137
+ for k, v in dict(
138
+ key_columns=key_columns,
139
+ update_column=update_column,
140
+ extra_columns=extra_columns,
141
+ min_key=min_key,
142
+ max_key=max_key,
143
+ min_update=min_update,
144
+ max_update=max_update,
145
+ where=where,
146
+ ).items()
147
+ if v is not None
148
+ }
149
+
150
+ segments = [t.new(**override_attrs) for t in tables] if override_attrs else tables
151
+
152
+ algorithm = Algorithm(algorithm)
153
+ if algorithm == Algorithm.AUTO:
154
+ algorithm = Algorithm.JOINDIFF if table1.database is table2.database else Algorithm.HASHDIFF
155
+
156
+ if algorithm == Algorithm.HASHDIFF:
157
+ differ = HashDiffer(
158
+ bisection_factor=bisection_factor,
159
+ bisection_threshold=bisection_threshold,
160
+ threaded=threaded,
161
+ max_threadpool_size=max_threadpool_size,
162
+ )
163
+ elif algorithm == Algorithm.JOINDIFF:
164
+ if isinstance(materialize_to_table, str):
165
+ table_name = eval_name_template(materialize_to_table)
166
+ materialize_to_table = table1.database.dialect.parse_table_name(table_name)
167
+ differ = JoinDiffer(
168
+ threaded=threaded,
169
+ max_threadpool_size=max_threadpool_size,
170
+ validate_unique_key=validate_unique_key,
171
+ sample_exclusive_rows=sample_exclusive_rows,
172
+ materialize_to_table=materialize_to_table,
173
+ materialize_all_rows=materialize_all_rows,
174
+ table_write_limit=table_write_limit,
175
+ skip_null_keys=skip_null_keys,
176
+ )
177
+ else:
178
+ raise ValueError(f"Unknown algorithm: {algorithm}")
179
+
180
+ return differ.diff_tables(*segments)