google-pso-data-validator 8.2.0__tar.gz → 8.4.0__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.
- {google_pso_data_validator-8.2.0/google_pso_data_validator.egg-info → google_pso_data_validator-8.4.0}/PKG-INFO +12 -11
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/README.md +5 -5
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/data_validation/__main__.py +7 -4
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/data_validation/cli_tools.py +33 -24
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/data_validation/clients.py +42 -13
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/data_validation/config_manager.py +55 -5
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/data_validation/consts.py +5 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/data_validation/data_validation.py +16 -9
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/data_validation/exceptions.py +4 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/data_validation/find_tables.py +18 -8
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/data_validation/partition_builder.py +37 -21
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/data_validation/query_builder/random_row_builder.py +2 -1
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/data_validation/result_handlers/base_backend.py +2 -2
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/data_validation/result_handlers/bigquery.py +49 -4
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/data_validation/result_handlers/factory.py +34 -29
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/data_validation/result_handlers/postgres.py +3 -3
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/data_validation/state_manager.py +1 -4
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/data_validation/util.py +18 -19
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0/google_pso_data_validator.egg-info}/PKG-INFO +12 -11
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/google_pso_data_validator.egg-info/SOURCES.txt +5 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/google_pso_data_validator.egg-info/requires.txt +4 -3
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/setup.py +7 -6
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_addon/operations.py +15 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_bigquery/__init__.py +20 -10
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_mssql/__init__.py +9 -4
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_mssql/registry.py +1 -1
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_oracle/datatypes.py +1 -1
- google_pso_data_validator-8.4.0/third_party/ibis/ibis_sybase/__init__.py +186 -0
- google_pso_data_validator-8.4.0/third_party/ibis/ibis_sybase/api.py +42 -0
- google_pso_data_validator-8.4.0/third_party/ibis/ibis_sybase/compiler.py +101 -0
- google_pso_data_validator-8.4.0/third_party/ibis/ibis_sybase/datatypes.py +96 -0
- google_pso_data_validator-8.4.0/third_party/ibis/ibis_sybase/registry.py +209 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/LICENSE +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/data_validation/__init__.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/data_validation/app.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/data_validation/client_info.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/data_validation/combiner.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/data_validation/gcs_helper.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/data_validation/jellyfish_distance.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/data_validation/metadata.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/data_validation/query_builder/__init__.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/data_validation/query_builder/partition_row_builder.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/data_validation/query_builder/query_builder.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/data_validation/raw_query.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/data_validation/result_handlers/__init__.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/data_validation/result_handlers/text.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/data_validation/schema_validation.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/data_validation/secret_manager.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/data_validation/validation_builder.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/google_pso_data_validator.egg-info/dependency_links.txt +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/google_pso_data_validator.egg-info/entry_points.txt +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/google_pso_data_validator.egg-info/top_level.txt +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/setup.cfg +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_addon/__init__.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_addon/api.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_bigquery/api.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_bigquery/registry.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_cloud_spanner/__init__.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_cloud_spanner/api.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_cloud_spanner/client.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_cloud_spanner/compiler.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_cloud_spanner/datatypes.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_cloud_spanner/registry.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_cloud_spanner/tests/__init__.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_cloud_spanner/tests/conftest.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_cloud_spanner/to_pandas.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_db2/__init__.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_db2/api.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_db2/compiler.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_db2/datatypes.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_db2/registry.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_impala/__init__.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_impala/api.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_mssql/api.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_mssql/datatypes.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_mysql/__init__.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_mysql/base_sql_compiler/__init__.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_mysql/base_sql_compiler/select_builder.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_mysql/compiler.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_oracle/__init__.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_oracle/api.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_oracle/compiler.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_oracle/registry.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_postgres/__init__.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_postgres/client.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_postgres/datatypes.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_postgres/registry.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_redshift/__init__.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_redshift/api.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_redshift/compiler.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_snowflake/__init__.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_snowflake/api.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_snowflake/datatypes.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_teradata/__init__.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_teradata/api.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_teradata/compiler.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_teradata/datatypes.py +0 -0
- {google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_teradata/registry.py +0 -0
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: google-pso-data-validator
|
|
3
|
-
Version: 8.
|
|
3
|
+
Version: 8.4.0
|
|
4
4
|
Summary: A package to enable easy data validation
|
|
5
5
|
Author: PSO DVT Engineering team
|
|
6
6
|
Author-email: data-validator-eng@google.com
|
|
7
7
|
Classifier: Development Status :: 4 - Beta
|
|
8
8
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
9
9
|
Classifier: Programming Language :: Python
|
|
10
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
11
10
|
Classifier: Programming Language :: Python :: 3.9
|
|
12
11
|
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
13
|
Classifier: Operating System :: OS Independent
|
|
14
|
-
Requires-Python: >=3.
|
|
14
|
+
Requires-Python: >=3.9
|
|
15
15
|
Description-Content-Type: text/markdown
|
|
16
16
|
License-File: LICENSE
|
|
17
17
|
Requires-Dist: Flask>=2.2
|
|
18
|
-
Requires-Dist: fsspec
|
|
18
|
+
Requires-Dist: fsspec
|
|
19
19
|
Requires-Dist: google-api-python-client>=2.144.0
|
|
20
20
|
Requires-Dist: google-cloud-bigquery>=3.25.0
|
|
21
21
|
Requires-Dist: google-cloud-bigquery-storage>=2.26.0
|
|
@@ -25,10 +25,10 @@ Requires-Dist: google-cloud-storage>=2.18.2
|
|
|
25
25
|
Requires-Dist: ibis-framework==5.1.0
|
|
26
26
|
Requires-Dist: impyla>=0.19.0
|
|
27
27
|
Requires-Dist: jellyfish>=1.1.0
|
|
28
|
-
Requires-Dist: pandas
|
|
28
|
+
Requires-Dist: pandas
|
|
29
29
|
Requires-Dist: parsy>=2.1
|
|
30
30
|
Requires-Dist: psycopg2-binary>=2.9.9
|
|
31
|
-
Requires-Dist: pyarrow
|
|
31
|
+
Requires-Dist: pyarrow
|
|
32
32
|
Requires-Dist: pydata-google-auth>=1.8.2
|
|
33
33
|
Requires-Dist: PyMySQL>=1.1.1
|
|
34
34
|
Requires-Dist: PyYAML>=6.0.2
|
|
@@ -45,6 +45,7 @@ Requires-Dist: freezegun; extra == "develop"
|
|
|
45
45
|
Requires-Dist: pyfakefs; extra == "develop"
|
|
46
46
|
Requires-Dist: pytest; extra == "develop"
|
|
47
47
|
Requires-Dist: pytest-cov; extra == "develop"
|
|
48
|
+
Requires-Dist: pytest-rerunfailures; extra == "develop"
|
|
48
49
|
Requires-Dist: pytest-timeout; extra == "develop"
|
|
49
50
|
Dynamic: author
|
|
50
51
|
Dynamic: author-email
|
|
@@ -92,8 +93,9 @@ DVT supports the following connection types:
|
|
|
92
93
|
* [Postgres](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#postgres)
|
|
93
94
|
* [Redshift](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#redshift)
|
|
94
95
|
* [Spanner](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#google-spanner)
|
|
95
|
-
* [Teradata](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#teradata)
|
|
96
96
|
* [Snowflake](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#snowflake)
|
|
97
|
+
* [Sybase ASE](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#sybase)
|
|
98
|
+
* [Teradata](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#teradata)
|
|
97
99
|
|
|
98
100
|
The [Connections](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md) page provides details about how to create
|
|
99
101
|
and list connections for the validation tool.
|
|
@@ -340,7 +342,7 @@ data-validation
|
|
|
340
342
|
Directory Path to store YAML Config Files
|
|
341
343
|
GCS: Provide a full gs:// path of the target directory. Eg: `gs://<BUCKET>/partitions_dir`
|
|
342
344
|
Local: Provide a relative path of the target directory. Eg: `partitions_dir`
|
|
343
|
-
|
|
345
|
+
For custom queries, the yaml files are stored in CONFIG_DIR and, for tables the yamls are stored in a directory named <source_schema>.<table> within CONFIG_DIR.
|
|
344
346
|
--partition-num INT, -pn INT
|
|
345
347
|
Number of partitions into which the table should be split, e.g. 1000 or 10000
|
|
346
348
|
In case this value exceeds the row count of the source/target table, it will be decreased to max(source_row_count, target_row_count)
|
|
@@ -653,9 +655,8 @@ The `--config-dir` flag will specify the directory with the YAML files to be exe
|
|
|
653
655
|
|
|
654
656
|
### Validation Reports
|
|
655
657
|
|
|
656
|
-
The result handlers tell DVT where to store the results of each validation. The
|
|
657
|
-
|
|
658
|
-
or print to stdout (default). View the schema of the results table [here](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/terraform/results_schema.json).
|
|
658
|
+
The result handlers tell DVT where to store the results of each validation. The tool can write the results of a validation run to Google BigQuery, PostgreSQL or print to stdout (default). See [result handler setup](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/installation.md#result-handler-setup) for pre-requisites. View the schema of the BigQuery results table [here](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/terraform/results_schema.json).
|
|
659
|
+
|
|
659
660
|
|
|
660
661
|
To output to BigQuery or PostgreSQL, simply include the `-rh` flag during a validation run including
|
|
661
662
|
the schema and table name for the results.
|
|
@@ -33,8 +33,9 @@ DVT supports the following connection types:
|
|
|
33
33
|
* [Postgres](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#postgres)
|
|
34
34
|
* [Redshift](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#redshift)
|
|
35
35
|
* [Spanner](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#google-spanner)
|
|
36
|
-
* [Teradata](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#teradata)
|
|
37
36
|
* [Snowflake](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#snowflake)
|
|
37
|
+
* [Sybase ASE](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#sybase)
|
|
38
|
+
* [Teradata](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#teradata)
|
|
38
39
|
|
|
39
40
|
The [Connections](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md) page provides details about how to create
|
|
40
41
|
and list connections for the validation tool.
|
|
@@ -281,7 +282,7 @@ data-validation
|
|
|
281
282
|
Directory Path to store YAML Config Files
|
|
282
283
|
GCS: Provide a full gs:// path of the target directory. Eg: `gs://<BUCKET>/partitions_dir`
|
|
283
284
|
Local: Provide a relative path of the target directory. Eg: `partitions_dir`
|
|
284
|
-
|
|
285
|
+
For custom queries, the yaml files are stored in CONFIG_DIR and, for tables the yamls are stored in a directory named <source_schema>.<table> within CONFIG_DIR.
|
|
285
286
|
--partition-num INT, -pn INT
|
|
286
287
|
Number of partitions into which the table should be split, e.g. 1000 or 10000
|
|
287
288
|
In case this value exceeds the row count of the source/target table, it will be decreased to max(source_row_count, target_row_count)
|
|
@@ -594,9 +595,8 @@ The `--config-dir` flag will specify the directory with the YAML files to be exe
|
|
|
594
595
|
|
|
595
596
|
### Validation Reports
|
|
596
597
|
|
|
597
|
-
The result handlers tell DVT where to store the results of each validation. The
|
|
598
|
-
|
|
599
|
-
or print to stdout (default). View the schema of the results table [here](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/terraform/results_schema.json).
|
|
598
|
+
The result handlers tell DVT where to store the results of each validation. The tool can write the results of a validation run to Google BigQuery, PostgreSQL or print to stdout (default). See [result handler setup](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/installation.md#result-handler-setup) for pre-requisites. View the schema of the BigQuery results table [here](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/terraform/results_schema.json).
|
|
599
|
+
|
|
600
600
|
|
|
601
601
|
To output to BigQuery or PostgreSQL, simply include the `-rh` flag during a validation run including
|
|
602
602
|
the schema and table name for the results.
|
{google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/data_validation/__main__.py
RENAMED
|
@@ -524,8 +524,8 @@ def run_validation(config_manager: ConfigManager, dry_run=False, verbose=False):
|
|
|
524
524
|
validation_builder=None,
|
|
525
525
|
result_handler=None,
|
|
526
526
|
verbose=verbose,
|
|
527
|
-
|
|
528
|
-
|
|
527
|
+
cached_source_client=source_client,
|
|
528
|
+
cached_target_client=target_client,
|
|
529
529
|
) as validator:
|
|
530
530
|
|
|
531
531
|
if dry_run:
|
|
@@ -533,10 +533,12 @@ def run_validation(config_manager: ConfigManager, dry_run=False, verbose=False):
|
|
|
533
533
|
json.dumps(
|
|
534
534
|
{
|
|
535
535
|
"source_query": util.ibis_table_to_sql(
|
|
536
|
-
validator.validation_builder.get_source_query()
|
|
536
|
+
validator.validation_builder.get_source_query(),
|
|
537
|
+
source_client,
|
|
537
538
|
),
|
|
538
539
|
"target_query": util.ibis_table_to_sql(
|
|
539
|
-
validator.validation_builder.get_target_query()
|
|
540
|
+
validator.validation_builder.get_target_query(),
|
|
541
|
+
target_client,
|
|
540
542
|
),
|
|
541
543
|
},
|
|
542
544
|
indent=4,
|
|
@@ -594,6 +596,7 @@ def partition_and_store_config_files(args: "Namespace") -> None:
|
|
|
594
596
|
setattr(args, "custom_query_type", "row")
|
|
595
597
|
config_managers = build_config_managers_from_args(args, consts.CUSTOM_QUERY)
|
|
596
598
|
partition_builder = PartitionBuilder(config_managers, args)
|
|
599
|
+
partition_builder.check_partition_configs()
|
|
597
600
|
partition_builder.partition_configs()
|
|
598
601
|
|
|
599
602
|
|
{google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/data_validation/cli_tools.py
RENAMED
|
@@ -73,6 +73,10 @@ if TYPE_CHECKING:
|
|
|
73
73
|
CONNECTION_SOURCE_FIELDS = {
|
|
74
74
|
consts.SOURCE_TYPE_BIGQUERY: [
|
|
75
75
|
["project_id", "GCP Project to use for BigQuery"],
|
|
76
|
+
[
|
|
77
|
+
"client_project_id",
|
|
78
|
+
"(Optional) BigQuery job/billing project (can differ from data project)",
|
|
79
|
+
],
|
|
76
80
|
["google_service_account_key_path", "(Optional) GCP SA Key Path"],
|
|
77
81
|
[
|
|
78
82
|
"api_endpoint",
|
|
@@ -84,7 +88,7 @@ CONNECTION_SOURCE_FIELDS = {
|
|
|
84
88
|
],
|
|
85
89
|
],
|
|
86
90
|
consts.SOURCE_TYPE_TERADATA: [
|
|
87
|
-
["host", "
|
|
91
|
+
["host", "Teradata host"],
|
|
88
92
|
["port", "Teradata port to connect on"],
|
|
89
93
|
["user_name", "User used to connect"],
|
|
90
94
|
["password", "Password for supplied user"],
|
|
@@ -93,7 +97,7 @@ CONNECTION_SOURCE_FIELDS = {
|
|
|
93
97
|
["json_params", "(Optional) Additional teradatasql JSON string parameters"],
|
|
94
98
|
],
|
|
95
99
|
consts.SOURCE_TYPE_ORACLE: [
|
|
96
|
-
["host", "
|
|
100
|
+
["host", "Oracle host"],
|
|
97
101
|
["port", "Oracle port to connect on"],
|
|
98
102
|
["user", "User used to connect"],
|
|
99
103
|
["thick_mode", "Flag indicating thick_mode"],
|
|
@@ -103,7 +107,7 @@ CONNECTION_SOURCE_FIELDS = {
|
|
|
103
107
|
["connect_args", "(Optional) Additional connection argument mapping"],
|
|
104
108
|
],
|
|
105
109
|
consts.SOURCE_TYPE_MSSQL: [
|
|
106
|
-
["host", "
|
|
110
|
+
["host", "SQL Server host (default localhost)"],
|
|
107
111
|
["port", "SQL Server port to connect on (default 1433)"],
|
|
108
112
|
["user", "User used to connect"],
|
|
109
113
|
["password", "Password for supplied user"],
|
|
@@ -112,7 +116,7 @@ CONNECTION_SOURCE_FIELDS = {
|
|
|
112
116
|
["url", "SQL Server SQLAlchemy connection URL"],
|
|
113
117
|
],
|
|
114
118
|
consts.SOURCE_TYPE_MYSQL: [
|
|
115
|
-
["host", "
|
|
119
|
+
["host", "MySQL host (default localhost)"],
|
|
116
120
|
["port", "MySQL port to connect on (default 3306)"],
|
|
117
121
|
["user", "User used to connect"],
|
|
118
122
|
["password", "Password for supplied user"],
|
|
@@ -126,14 +130,14 @@ CONNECTION_SOURCE_FIELDS = {
|
|
|
126
130
|
["connect_args", "(Optional) Additional connection argument mapping"],
|
|
127
131
|
],
|
|
128
132
|
consts.SOURCE_TYPE_POSTGRES: [
|
|
129
|
-
["host", "
|
|
133
|
+
["host", "PostgreSQL host."],
|
|
130
134
|
["port", "PostgreSQL port to connect on (e.g. 5432)"],
|
|
131
135
|
["user", "Username to connect to"],
|
|
132
136
|
["password", "Password for authentication of user"],
|
|
133
137
|
["database", "Database in PostgreSQL to connect to (default postgres)"],
|
|
134
138
|
],
|
|
135
139
|
consts.SOURCE_TYPE_REDSHIFT: [
|
|
136
|
-
["host", "
|
|
140
|
+
["host", "Redshift host."],
|
|
137
141
|
["port", "Redshift port to connect on (e.g. 5439)"],
|
|
138
142
|
["user", "Username to connect to"],
|
|
139
143
|
["password", "Password for authentication of user"],
|
|
@@ -149,16 +153,26 @@ CONNECTION_SOURCE_FIELDS = {
|
|
|
149
153
|
'(Optional) GCP Spanner API endpoint (e.g. "https://spanner-mypsc.p.googleapis.com")',
|
|
150
154
|
],
|
|
151
155
|
],
|
|
156
|
+
consts.SOURCE_TYPE_SYBASE: [
|
|
157
|
+
["host", "Sybase host (default localhost)"],
|
|
158
|
+
["port", "Sybase port to connect on (default 5000)"],
|
|
159
|
+
["user", "User used to connect"],
|
|
160
|
+
["password", "Password for supplied user"],
|
|
161
|
+
["database", "Database to connect to (default master)"],
|
|
162
|
+
["odbc_driver", "ODBC driver name"],
|
|
163
|
+
["query", "Connection query parameters"],
|
|
164
|
+
["url", "Sybase SQLAlchemy connection URL"],
|
|
165
|
+
],
|
|
152
166
|
consts.SOURCE_TYPE_FILESYSTEM: [
|
|
153
167
|
["table_name", "Table name to use as reference for file data"],
|
|
154
168
|
["file_path", "The local, s3, or GCS file path to the data"],
|
|
155
169
|
["file_type", "The file type of the file. 'csv', 'orc', 'parquet' or 'json'"],
|
|
156
170
|
],
|
|
157
171
|
consts.SOURCE_TYPE_IMPALA: [
|
|
158
|
-
["host", "
|
|
159
|
-
["port", "
|
|
160
|
-
["database", "
|
|
161
|
-
["auth_mechanism", "
|
|
172
|
+
["host", "Impala host"],
|
|
173
|
+
["port", "Impala port (10000 if not provided)"],
|
|
174
|
+
["database", "Impala database (default if not provided)"],
|
|
175
|
+
["auth_mechanism", "Impala auth mechanism (PLAIN if not provided)"],
|
|
162
176
|
[
|
|
163
177
|
"kerberos_service_name",
|
|
164
178
|
"Desired Kerberos service name ('impala' if not provided)",
|
|
@@ -183,8 +197,8 @@ CONNECTION_SOURCE_FIELDS = {
|
|
|
183
197
|
["http_path", "URL path of HTTP proxy"],
|
|
184
198
|
],
|
|
185
199
|
consts.SOURCE_TYPE_DB2: [
|
|
186
|
-
["host", "
|
|
187
|
-
["port", "
|
|
200
|
+
["host", "DB2 host"],
|
|
201
|
+
["port", "DB2 port (50000 if not provided)"],
|
|
188
202
|
["user", "Username to connect to"],
|
|
189
203
|
["password", "Password for authentication of user"],
|
|
190
204
|
["database", "Database in DB2 to connect to"],
|
|
@@ -1308,21 +1322,16 @@ def _get_result_handler(rc_value: str, sa_file=None) -> dict:
|
|
|
1308
1322
|
if config[0] in connections:
|
|
1309
1323
|
# We received connection_name.results_table.
|
|
1310
1324
|
conn_from_file = get_connection(config[0])
|
|
1311
|
-
if conn_from_file[consts.SOURCE_TYPE]
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
consts.TABLE_ID: config[1],
|
|
1316
|
-
consts.API_ENDPOINT: conn_from_file.get("api_endpoint", None),
|
|
1317
|
-
consts.STORAGE_API_ENDPOINT: conn_from_file.get(
|
|
1318
|
-
"storage_api_endpoint", None
|
|
1319
|
-
),
|
|
1320
|
-
}
|
|
1321
|
-
elif conn_from_file[consts.SOURCE_TYPE] == consts.SOURCE_TYPE_POSTGRES:
|
|
1325
|
+
if conn_from_file[consts.SOURCE_TYPE] in [
|
|
1326
|
+
consts.SOURCE_TYPE_BIGQUERY,
|
|
1327
|
+
consts.SOURCE_TYPE_POSTGRES,
|
|
1328
|
+
]:
|
|
1322
1329
|
result_handler = {
|
|
1323
1330
|
consts.RH_TYPE: conn_from_file[consts.SOURCE_TYPE],
|
|
1324
1331
|
consts.TABLE_ID: config[1],
|
|
1325
|
-
|
|
1332
|
+
# Only store the connection name in the result handler to avoid accidentally
|
|
1333
|
+
# storing credentials in config files.
|
|
1334
|
+
consts.RH_CONN: config[0],
|
|
1326
1335
|
}
|
|
1327
1336
|
# TODO Add filesytem handler too.
|
|
1328
1337
|
else:
|
{google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/data_validation/clients.py
RENAMED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
from contextlib import contextmanager
|
|
17
17
|
import copy
|
|
18
18
|
import logging
|
|
19
|
-
from typing import TYPE_CHECKING
|
|
19
|
+
from typing import TYPE_CHECKING, Optional
|
|
20
20
|
import warnings
|
|
21
21
|
|
|
22
22
|
import google.oauth2.service_account
|
|
@@ -56,6 +56,7 @@ IBIS_ALCHEMY_BACKENDS = [
|
|
|
56
56
|
"mssql",
|
|
57
57
|
"redshift",
|
|
58
58
|
"snowflake",
|
|
59
|
+
"sybase",
|
|
59
60
|
]
|
|
60
61
|
|
|
61
62
|
|
|
@@ -93,19 +94,32 @@ try:
|
|
|
93
94
|
except ImportError:
|
|
94
95
|
db2_connect = _raise_missing_client_error("pip install ibm_db_sa")
|
|
95
96
|
|
|
97
|
+
# Sybase requires sqlalchemy_sybase package.
|
|
98
|
+
try:
|
|
99
|
+
from third_party.ibis.ibis_sybase.api import sybase_connect
|
|
100
|
+
except ImportError:
|
|
101
|
+
sybase_connect = _raise_missing_client_error("pip install sqlalchemy_sybase")
|
|
102
|
+
|
|
96
103
|
|
|
97
104
|
def get_google_bigquery_client(
|
|
98
|
-
project_id: str,
|
|
105
|
+
project_id: str,
|
|
106
|
+
credentials=None,
|
|
107
|
+
api_endpoint: Optional[str] = None,
|
|
108
|
+
quota_project_id: Optional[str] = None,
|
|
99
109
|
):
|
|
100
110
|
info = client_info.get_http_client_info()
|
|
101
111
|
job_config = bigquery.QueryJobConfig(
|
|
102
112
|
connection_properties=[bigquery.ConnectionProperty("time_zone", "UTC")]
|
|
103
113
|
)
|
|
114
|
+
effective_project = quota_project_id or project_id
|
|
104
115
|
options = None
|
|
105
|
-
if api_endpoint:
|
|
106
|
-
options = client_options.ClientOptions(
|
|
116
|
+
if api_endpoint or quota_project_id:
|
|
117
|
+
options = client_options.ClientOptions(
|
|
118
|
+
api_endpoint=api_endpoint,
|
|
119
|
+
quota_project_id=quota_project_id if quota_project_id else None,
|
|
120
|
+
)
|
|
107
121
|
return bigquery.Client(
|
|
108
|
-
project=
|
|
122
|
+
project=effective_project,
|
|
109
123
|
client_info=info,
|
|
110
124
|
credentials=credentials,
|
|
111
125
|
default_query_job_config=job_config,
|
|
@@ -113,10 +127,17 @@ def get_google_bigquery_client(
|
|
|
113
127
|
)
|
|
114
128
|
|
|
115
129
|
|
|
116
|
-
def _get_google_bqstorage_client(
|
|
130
|
+
def _get_google_bqstorage_client(
|
|
131
|
+
credentials=None,
|
|
132
|
+
api_endpoint: Optional[str] = None,
|
|
133
|
+
quota_project_id: Optional[str] = None,
|
|
134
|
+
):
|
|
117
135
|
options = None
|
|
118
|
-
if api_endpoint:
|
|
119
|
-
options = client_options.ClientOptions(
|
|
136
|
+
if api_endpoint or quota_project_id:
|
|
137
|
+
options = client_options.ClientOptions(
|
|
138
|
+
api_endpoint=api_endpoint,
|
|
139
|
+
quota_project_id=quota_project_id if quota_project_id else None,
|
|
140
|
+
)
|
|
120
141
|
from google.cloud import bigquery_storage_v1 as bigquery_storage
|
|
121
142
|
|
|
122
143
|
return bigquery_storage.BigQueryReadClient(
|
|
@@ -129,20 +150,26 @@ def get_bigquery_client(
|
|
|
129
150
|
project_id: str,
|
|
130
151
|
dataset_id: str = "",
|
|
131
152
|
credentials=None,
|
|
132
|
-
api_endpoint: str = None,
|
|
133
|
-
storage_api_endpoint: str = None,
|
|
153
|
+
api_endpoint: Optional[str] = None,
|
|
154
|
+
storage_api_endpoint: Optional[str] = None,
|
|
155
|
+
client_project_id: Optional[str] = None,
|
|
134
156
|
):
|
|
135
157
|
google_client = get_google_bigquery_client(
|
|
136
|
-
project_id,
|
|
158
|
+
project_id,
|
|
159
|
+
credentials=credentials,
|
|
160
|
+
api_endpoint=api_endpoint,
|
|
161
|
+
quota_project_id=client_project_id,
|
|
137
162
|
)
|
|
138
163
|
bqstorage_client = None
|
|
139
164
|
if storage_api_endpoint:
|
|
140
165
|
bqstorage_client = _get_google_bqstorage_client(
|
|
141
|
-
credentials=credentials,
|
|
166
|
+
credentials=credentials,
|
|
167
|
+
api_endpoint=storage_api_endpoint,
|
|
168
|
+
quota_project_id=client_project_id,
|
|
142
169
|
)
|
|
143
170
|
|
|
144
171
|
return bigquery_connect(
|
|
145
|
-
project_id=project_id,
|
|
172
|
+
project_id=project_id or client_project_id,
|
|
146
173
|
dataset_id=dataset_id,
|
|
147
174
|
credentials=credentials,
|
|
148
175
|
bigquery_client=google_client,
|
|
@@ -202,6 +229,7 @@ def get_ibis_table(client, schema_name, table_name, database_name=None):
|
|
|
202
229
|
"db2",
|
|
203
230
|
"mssql",
|
|
204
231
|
"redshift",
|
|
232
|
+
"sybase",
|
|
205
233
|
]:
|
|
206
234
|
return client.table(table_name, database=database_name, schema=schema_name)
|
|
207
235
|
elif client.name == "pandas":
|
|
@@ -400,5 +428,6 @@ CLIENT_LOOKUP = {
|
|
|
400
428
|
consts.SOURCE_TYPE_MSSQL: mssql_connect,
|
|
401
429
|
consts.SOURCE_TYPE_SNOWFLAKE: snowflake_connect,
|
|
402
430
|
consts.SOURCE_TYPE_SPANNER: spanner_connect,
|
|
431
|
+
consts.SOURCE_TYPE_SYBASE: sybase_connect,
|
|
403
432
|
consts.SOURCE_TYPE_DB2: db2_connect,
|
|
404
433
|
}
|
|
@@ -71,7 +71,7 @@ class ConfigManager(object):
|
|
|
71
71
|
"""Return config object."""
|
|
72
72
|
return self._config
|
|
73
73
|
|
|
74
|
-
def get_source_connection(self):
|
|
74
|
+
def get_source_connection(self) -> dict:
|
|
75
75
|
"""Return source connection object."""
|
|
76
76
|
if not self._source_conn:
|
|
77
77
|
if self._config.get(consts.CONFIG_SOURCE_CONN):
|
|
@@ -82,7 +82,7 @@ class ConfigManager(object):
|
|
|
82
82
|
|
|
83
83
|
return self._source_conn
|
|
84
84
|
|
|
85
|
-
def get_target_connection(self):
|
|
85
|
+
def get_target_connection(self) -> dict:
|
|
86
86
|
"""Return target connection object."""
|
|
87
87
|
if not self._target_conn:
|
|
88
88
|
if self._config.get(consts.CONFIG_TARGET_CONN):
|
|
@@ -671,20 +671,38 @@ class ConfigManager(object):
|
|
|
671
671
|
|
|
672
672
|
This is because SQL Server text does not support the len() function to get the length in characters.
|
|
673
673
|
Instead we must fall back to using ByteLength."""
|
|
674
|
+
return self._is_sql_server_type(
|
|
675
|
+
source_column_name, target_column_name, ["text", "ntext"]
|
|
676
|
+
)
|
|
677
|
+
|
|
678
|
+
def _is_sql_server_image(
|
|
679
|
+
self, source_column_name: str, target_column_name: str
|
|
680
|
+
) -> bool:
|
|
681
|
+
"""Returns True when either source or target column is of SQL Server image data type.
|
|
682
|
+
|
|
683
|
+
This is because SQL Server image is deprecated and not supported by a number of SQL functions.
|
|
684
|
+
"""
|
|
685
|
+
return self._is_sql_server_type(
|
|
686
|
+
source_column_name, target_column_name, ["image"]
|
|
687
|
+
)
|
|
688
|
+
|
|
689
|
+
def _is_sql_server_type(
|
|
690
|
+
self, source_column_name: str, target_column_name: str, type_list: List[str]
|
|
691
|
+
) -> bool:
|
|
692
|
+
"""Returns True when either source or target column is of a SQL Server type listed in type_list."""
|
|
674
693
|
|
|
675
694
|
raw_source_types = self.get_source_raw_data_types()
|
|
676
695
|
raw_target_types = self.get_target_raw_data_types()
|
|
677
|
-
text_types = ("text", "ntext")
|
|
678
696
|
return bool(
|
|
679
697
|
(
|
|
680
698
|
self.source_client.name == "mssql"
|
|
681
699
|
and raw_source_types
|
|
682
|
-
and raw_source_types.get(source_column_name, [None])[0] in
|
|
700
|
+
and raw_source_types.get(source_column_name, [None])[0] in type_list
|
|
683
701
|
)
|
|
684
702
|
or (
|
|
685
703
|
self.target_client.name == "mssql"
|
|
686
704
|
and raw_target_types
|
|
687
|
-
and raw_target_types.get(target_column_name, [None])[0] in
|
|
705
|
+
and raw_target_types.get(target_column_name, [None])[0] in type_list
|
|
688
706
|
)
|
|
689
707
|
)
|
|
690
708
|
|
|
@@ -1075,6 +1093,11 @@ class ConfigManager(object):
|
|
|
1075
1093
|
f"Skipping {agg_type} on {column} due to data type: {column_type}"
|
|
1076
1094
|
)
|
|
1077
1095
|
continue
|
|
1096
|
+
elif self._is_sql_server_image(column, column):
|
|
1097
|
+
logging.info(
|
|
1098
|
+
f"Skipping {agg_type} on {column} due to SQL Server image data type"
|
|
1099
|
+
)
|
|
1100
|
+
continue
|
|
1078
1101
|
|
|
1079
1102
|
if require_pre_agg_calc_field(
|
|
1080
1103
|
column_type, target_column_type, agg_type, cast_to_bigint
|
|
@@ -1252,8 +1275,25 @@ class ConfigManager(object):
|
|
|
1252
1275
|
raise ValueError(
|
|
1253
1276
|
"Exclude columns flag cannot be present with column list '*'"
|
|
1254
1277
|
)
|
|
1278
|
+
|
|
1255
1279
|
return casefold_columns
|
|
1256
1280
|
|
|
1281
|
+
def _filter_columns_by_data_type(
|
|
1282
|
+
self, source_columns: dict, target_columns: dict
|
|
1283
|
+
) -> tuple:
|
|
1284
|
+
"""Filter out columns with a data type that is incompatible with DVT."""
|
|
1285
|
+
result_source_columns = source_columns.copy()
|
|
1286
|
+
result_target_columns = target_columns.copy()
|
|
1287
|
+
for source_column, target_column in zip(source_columns, target_columns):
|
|
1288
|
+
if self._is_sql_server_image(source_column, target_column):
|
|
1289
|
+
logging.info(
|
|
1290
|
+
f"Skipping column {source_column} due to SQL Server image data type"
|
|
1291
|
+
)
|
|
1292
|
+
result_source_columns.pop(source_column)
|
|
1293
|
+
result_target_columns.pop(target_column)
|
|
1294
|
+
|
|
1295
|
+
return result_source_columns, result_target_columns
|
|
1296
|
+
|
|
1257
1297
|
def build_dependent_aliases(self, calc_type: str, col_list=None) -> List[Dict]:
|
|
1258
1298
|
"""This is a utility function for determining the required depth of all fields"""
|
|
1259
1299
|
source_table = self.get_source_ibis_calculated_table()
|
|
@@ -1269,6 +1309,13 @@ class ConfigManager(object):
|
|
|
1269
1309
|
casefold_target_columns, col_list
|
|
1270
1310
|
)
|
|
1271
1311
|
|
|
1312
|
+
(
|
|
1313
|
+
casefold_source_columns,
|
|
1314
|
+
casefold_target_columns,
|
|
1315
|
+
) = self._filter_columns_by_data_type(
|
|
1316
|
+
casefold_source_columns, casefold_target_columns
|
|
1317
|
+
)
|
|
1318
|
+
|
|
1272
1319
|
column_aliases = {}
|
|
1273
1320
|
col_names = []
|
|
1274
1321
|
for i, calc in enumerate(self._get_order_of_operations(calc_type)):
|
|
@@ -1325,6 +1372,9 @@ class ConfigManager(object):
|
|
|
1325
1372
|
casefold_source_columns = self._filter_columns_by_column_list(
|
|
1326
1373
|
casefold_source_columns, col_list, exclude_cols=exclude_cols
|
|
1327
1374
|
)
|
|
1375
|
+
casefold_source_columns, _ = self._filter_columns_by_data_type(
|
|
1376
|
+
casefold_source_columns, casefold_source_columns
|
|
1377
|
+
)
|
|
1328
1378
|
return casefold_source_columns
|
|
1329
1379
|
|
|
1330
1380
|
def auto_list_primary_keys(self) -> list:
|
{google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/data_validation/consts.py
RENAMED
|
@@ -139,6 +139,7 @@ SOURCE_TYPE_POSTGRES = "Postgres"
|
|
|
139
139
|
SOURCE_TYPE_REDSHIFT = "Redshift"
|
|
140
140
|
SOURCE_TYPE_SNOWFLAKE = "Snowflake"
|
|
141
141
|
SOURCE_TYPE_SPANNER = "Spanner"
|
|
142
|
+
SOURCE_TYPE_SYBASE = "Sybase"
|
|
142
143
|
SOURCE_TYPE_TERADATA = "Teradata"
|
|
143
144
|
|
|
144
145
|
# BigQuery Result Handler Configs
|
|
@@ -149,6 +150,7 @@ TABLE_ID = "table_id"
|
|
|
149
150
|
GOOGLE_SERVICE_ACCOUNT_KEY_PATH = "google_service_account_key_path"
|
|
150
151
|
API_ENDPOINT = "api_endpoint"
|
|
151
152
|
STORAGE_API_ENDPOINT = "storage_api_endpoint"
|
|
153
|
+
CLIENT_PROJECT_ID = "client_project_id"
|
|
152
154
|
|
|
153
155
|
# Result Handler Output Table Fields
|
|
154
156
|
VALIDATION_TYPE = "validation_type"
|
|
@@ -262,3 +264,6 @@ CALC_FIELD_LENGTH = "length"
|
|
|
262
264
|
CALC_FIELD_PADDED_CHAR_LENGTH = "padded_char_length"
|
|
263
265
|
CALC_FIELD_RSTRIP = "rstrip"
|
|
264
266
|
CALC_FIELD_UPPER = "upper"
|
|
267
|
+
|
|
268
|
+
# Backends that do not support window functions.
|
|
269
|
+
NO_WINDOW_FUNCTION_SUPPORT = [SOURCE_TYPE_SYBASE]
|
|
@@ -16,9 +16,11 @@ import json
|
|
|
16
16
|
import logging
|
|
17
17
|
import warnings
|
|
18
18
|
from concurrent.futures import ThreadPoolExecutor
|
|
19
|
-
import ibis.backends.pandas
|
|
20
|
-
import pandas
|
|
21
19
|
import uuid
|
|
20
|
+
from typing import TYPE_CHECKING
|
|
21
|
+
|
|
22
|
+
import ibis
|
|
23
|
+
import pandas
|
|
22
24
|
|
|
23
25
|
from data_validation import combiner, consts, metadata, util
|
|
24
26
|
from data_validation.config_manager import ConfigManager
|
|
@@ -26,6 +28,9 @@ from data_validation.query_builder.random_row_builder import RandomRowBuilder
|
|
|
26
28
|
from data_validation.schema_validation import SchemaValidation
|
|
27
29
|
from data_validation.validation_builder import ValidationBuilder
|
|
28
30
|
|
|
31
|
+
if TYPE_CHECKING:
|
|
32
|
+
from ibis.backends.base import BaseBackend
|
|
33
|
+
|
|
29
34
|
""" The DataValidation class is where the code becomes source/target aware
|
|
30
35
|
|
|
31
36
|
The class builds specific source and target clients and is likely where someone would go to
|
|
@@ -43,8 +48,8 @@ class DataValidation(object):
|
|
|
43
48
|
schema_validator=None,
|
|
44
49
|
result_handler=None,
|
|
45
50
|
verbose=False,
|
|
46
|
-
|
|
47
|
-
|
|
51
|
+
cached_source_client: "BaseBackend" = None,
|
|
52
|
+
cached_target_client: "BaseBackend" = None,
|
|
48
53
|
):
|
|
49
54
|
"""Initialize a DataValidation client
|
|
50
55
|
|
|
@@ -54,19 +59,21 @@ class DataValidation(object):
|
|
|
54
59
|
schema_validator (SchemaValidation): Optional instance of a SchemaValidation.
|
|
55
60
|
result_handler (ResultHandler): Optional instance of as ResultHandler client.
|
|
56
61
|
verbose (bool): If verbose, the Data Validation client will print the queries run.
|
|
57
|
-
|
|
58
|
-
|
|
62
|
+
cached_source_client: Optional client to avoid unnecessary connections,
|
|
63
|
+
cached_target_client: Optional client to avoid unnecessary connections,
|
|
59
64
|
"""
|
|
60
65
|
self.verbose = verbose
|
|
61
|
-
self._fresh_connections = not bool(
|
|
66
|
+
self._fresh_connections = not bool(
|
|
67
|
+
cached_source_client and cached_target_client
|
|
68
|
+
)
|
|
62
69
|
|
|
63
70
|
# Data Client Management
|
|
64
71
|
self.config = config
|
|
65
72
|
|
|
66
73
|
self.config_manager = ConfigManager(
|
|
67
74
|
config,
|
|
68
|
-
source_client=
|
|
69
|
-
target_client=
|
|
75
|
+
source_client=cached_source_client,
|
|
76
|
+
target_client=cached_target_client,
|
|
70
77
|
verbose=self.verbose,
|
|
71
78
|
)
|
|
72
79
|
|
{google_pso_data_validator-8.2.0 → google_pso_data_validator-8.4.0}/data_validation/find_tables.py
RENAMED
|
@@ -60,15 +60,9 @@ def _compare_match_tables(source_table_map, target_table_map, score_cutoff=0.8)
|
|
|
60
60
|
return table_configs
|
|
61
61
|
|
|
62
62
|
|
|
63
|
-
def
|
|
64
|
-
|
|
65
|
-
):
|
|
66
|
-
"""Return dict with searchable keys for table matching."""
|
|
63
|
+
def _get_table_map_from_obj_list(table_objs: list) -> dict:
|
|
64
|
+
"""Convert list of schema, table tuples into dict with searchable keys for table matching."""
|
|
67
65
|
table_map = {}
|
|
68
|
-
table_objs = clients.get_all_tables(
|
|
69
|
-
client, allowed_schemas=allowed_schemas, tables_only=(not include_views)
|
|
70
|
-
)
|
|
71
|
-
|
|
72
66
|
for table_obj in table_objs:
|
|
73
67
|
table_key = ".".join([t for t in table_obj if t])
|
|
74
68
|
table_map[table_key] = {
|
|
@@ -76,9 +70,25 @@ def _get_table_map(
|
|
|
76
70
|
consts.CONFIG_TABLE_NAME: table_obj[1],
|
|
77
71
|
}
|
|
78
72
|
|
|
73
|
+
# Post process table_map and lower case table_keys, if possible.
|
|
74
|
+
for table_key in [_ for _ in table_map if _ != _.casefold()]:
|
|
75
|
+
if table_key.casefold() not in table_map:
|
|
76
|
+
# Only lower case the key if there isn't one already.
|
|
77
|
+
table_map[table_key.casefold()] = table_map.pop(table_key)
|
|
78
|
+
|
|
79
79
|
return table_map
|
|
80
80
|
|
|
81
81
|
|
|
82
|
+
def _get_table_map(
|
|
83
|
+
client: "ibis.backends.base.BaseBackend", allowed_schemas=None, include_views=False
|
|
84
|
+
) -> dict:
|
|
85
|
+
"""Return dict with searchable keys for table matching."""
|
|
86
|
+
table_objs = clients.get_all_tables(
|
|
87
|
+
client, allowed_schemas=allowed_schemas, tables_only=(not include_views)
|
|
88
|
+
)
|
|
89
|
+
return _get_table_map_from_obj_list(table_objs)
|
|
90
|
+
|
|
91
|
+
|
|
82
92
|
def get_mapped_table_configs(
|
|
83
93
|
source_client: "ibis.backends.base.BaseBackend",
|
|
84
94
|
target_client: "ibis.backends.base.BaseBackend",
|