google-pso-data-validator 5.0.0__tar.gz → 5.1.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-5.0.0 → google_pso_data_validator-5.1.0}/PKG-INFO +14 -14
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/README.md +7 -5
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/data_validation/cli_tools.py +25 -4
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/data_validation/clients.py +17 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/data_validation/config_manager.py +7 -3
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/data_validation/consts.py +1 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/data_validation/data_validation.py +3 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/data_validation/gcs_helper.py +4 -4
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/data_validation/partition_builder.py +26 -26
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/data_validation/query_builder/query_builder.py +14 -2
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/data_validation/validation_builder.py +34 -4
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/google_pso_data_validator.egg-info/PKG-INFO +14 -14
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/google_pso_data_validator.egg-info/requires.txt +3 -4
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/setup.py +7 -9
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_impala/api.py +4 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/LICENSE +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/data_validation/__init__.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/data_validation/__main__.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/data_validation/app.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/data_validation/client_info.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/data_validation/combiner.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/data_validation/exceptions.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/data_validation/jellyfish_distance.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/data_validation/metadata.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/data_validation/query_builder/__init__.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/data_validation/query_builder/partition_row_builder.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/data_validation/query_builder/random_row_builder.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/data_validation/result_handlers/__init__.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/data_validation/result_handlers/bigquery.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/data_validation/result_handlers/text.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/data_validation/schema_validation.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/data_validation/secret_manager.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/data_validation/state_manager.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/google_pso_data_validator.egg-info/SOURCES.txt +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/google_pso_data_validator.egg-info/dependency_links.txt +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/google_pso_data_validator.egg-info/entry_points.txt +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/google_pso_data_validator.egg-info/top_level.txt +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/setup.cfg +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_addon/__init__.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_addon/api.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_addon/operations.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_cloud_spanner/__init__.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_cloud_spanner/api.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_cloud_spanner/client.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_cloud_spanner/compiler.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_cloud_spanner/datatypes.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_cloud_spanner/registry.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_cloud_spanner/tests/__init__.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_cloud_spanner/tests/conftest.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_cloud_spanner/to_pandas.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_db2/__init__.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_db2/api.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_db2/compiler.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_db2/datatypes.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_db2/registry.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_impala/__init__.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_mssql/__init__.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_mssql/api.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_mssql/datatypes.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_mysql/__init__.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_mysql/base_sql_compiler/__init__.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_mysql/base_sql_compiler/select_builder.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_mysql/compiler.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_oracle/__init__.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_oracle/api.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_oracle/compiler.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_oracle/datatypes.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_oracle/registry.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_postgres/__init__.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_postgres/client.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_postgres/datatypes.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_redshift/__init__.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_redshift/api.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_redshift/compiler.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_snowflake/__init__.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_snowflake/api.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_snowflake/datatypes.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_teradata/__init__.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_teradata/api.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_teradata/compiler.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_teradata/datatypes.py +0 -0
- {google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_teradata/registry.py +0 -0
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: google-pso-data-validator
|
|
3
|
-
Version: 5.
|
|
3
|
+
Version: 5.1.0
|
|
4
4
|
Summary: A package to enable easy data validation
|
|
5
|
-
|
|
6
|
-
Author:
|
|
7
|
-
|
|
8
|
-
Classifier: Development Status :: 3 - Alpha
|
|
5
|
+
Author: PSO DVT Engineering team
|
|
6
|
+
Author-email: data-validator-eng@google.com
|
|
7
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
8
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
10
9
|
Classifier: Programming Language :: Python
|
|
11
10
|
Classifier: Programming Language :: Python :: 3.8
|
|
@@ -16,7 +15,7 @@ Requires-Python: >=3.8
|
|
|
16
15
|
Description-Content-Type: text/markdown
|
|
17
16
|
License-File: LICENSE
|
|
18
17
|
Requires-Dist: attrs==23.1.0
|
|
19
|
-
Requires-Dist: grpcio==1.53.
|
|
18
|
+
Requires-Dist: grpcio==1.53.2
|
|
20
19
|
Requires-Dist: lazy-object-proxy==1.9.0
|
|
21
20
|
Requires-Dist: marshmallow==3.19.0
|
|
22
21
|
Requires-Dist: fsspec>=2022.8.2
|
|
@@ -24,7 +23,7 @@ Requires-Dist: google-api-python-client==2.91.0
|
|
|
24
23
|
Requires-Dist: ibis-framework==5.1.0
|
|
25
24
|
Requires-Dist: impyla==0.18.0
|
|
26
25
|
Requires-Dist: SQLAlchemy==1.4.49
|
|
27
|
-
Requires-Dist: PyMySQL==1.1.
|
|
26
|
+
Requires-Dist: PyMySQL==1.1.1
|
|
28
27
|
Requires-Dist: psycopg2-binary==2.9.6
|
|
29
28
|
Requires-Dist: PyYAML==6.0
|
|
30
29
|
Requires-Dist: pandas==1.5.3
|
|
@@ -35,12 +34,11 @@ Requires-Dist: google-cloud-bigquery==3.11.3
|
|
|
35
34
|
Requires-Dist: google-cloud-bigquery-storage==2.20.0
|
|
36
35
|
Requires-Dist: google-cloud-spanner==3.36.0
|
|
37
36
|
Requires-Dist: google-cloud-storage==2.10.0
|
|
38
|
-
Requires-Dist: setuptools>=34.0.0
|
|
39
37
|
Requires-Dist: jellyfish==1.0.0
|
|
40
38
|
Requires-Dist: tabulate==0.9.0
|
|
41
39
|
Requires-Dist: Flask==2.3.2
|
|
42
40
|
Requires-Dist: parsy==2.1
|
|
43
|
-
Requires-Dist: google-cloud-secret-manager
|
|
41
|
+
Requires-Dist: google-cloud-secret-manager>=2.16.0
|
|
44
42
|
Provides-Extra: apache-airflow
|
|
45
43
|
Requires-Dist: 1.10.11; extra == "apache-airflow"
|
|
46
44
|
Provides-Extra: pyspark
|
|
@@ -256,6 +254,8 @@ data-validation (--verbose or -v) (--log-level or -ll) validate row
|
|
|
256
254
|
Row batch size used for random row filters (default 10,000).
|
|
257
255
|
[--filter-status or -fs STATUSES_LIST]
|
|
258
256
|
Comma separated list of statuses to filter the validation results. Supported statuses are (success, fail). If no list is provided, all statuses are returned.
|
|
257
|
+
[--trim-string-pks, -tsp]
|
|
258
|
+
Trims string based primary key values, intended for use when one engine uses padded string semantics (e.g. CHAR(n)) and the other does not (e.g. VARCHAR(n)).
|
|
259
259
|
```
|
|
260
260
|
#### Generate Table Partitions for Large Table Row Validations
|
|
261
261
|
|
|
@@ -357,12 +357,12 @@ data-validation (--verbose or -v) (--log-level or -ll) validate custom-query col
|
|
|
357
357
|
Source sql query
|
|
358
358
|
Either --source-query or --source-query-file must be provided
|
|
359
359
|
--source-query-file SOURCE_QUERY_FILE, -sqf SOURCE_QUERY_FILE
|
|
360
|
-
File containing the source sql
|
|
360
|
+
File containing the source sql command. Supports GCS and local paths.
|
|
361
361
|
--target-query TARGET_QUERY, -tq TARGET_QUERY
|
|
362
362
|
Target sql query
|
|
363
363
|
Either --target-query or --target-query-file must be provided
|
|
364
364
|
--target-query-file TARGET_QUERY_FILE, -tqf TARGET_QUERY_FILE
|
|
365
|
-
File containing the target sql
|
|
365
|
+
File containing the target sql command. Supports GCS and local paths.
|
|
366
366
|
[--count COLUMNS] Comma separated list of columns for count or * for all columns
|
|
367
367
|
[--sum COLUMNS] Comma separated list of columns for sum or * for all numeric
|
|
368
368
|
[--min COLUMNS] Comma separated list of columns for min or * for all numeric
|
|
@@ -419,12 +419,12 @@ data-validation (--verbose or -v) (--log-level or -ll) validate custom-query row
|
|
|
419
419
|
Source sql query
|
|
420
420
|
Either --source-query or --source-query-file must be provided
|
|
421
421
|
--source-query-file SOURCE_QUERY_FILE, -sqf SOURCE_QUERY_FILE
|
|
422
|
-
File containing the source sql
|
|
422
|
+
File containing the source sql command. Supports GCS and local paths.
|
|
423
423
|
--target-query TARGET_QUERY, -tq TARGET_QUERY
|
|
424
424
|
Target sql query
|
|
425
425
|
Either --target-query or --target-query-file must be provided
|
|
426
426
|
--target-query-file TARGET_QUERY_FILE, -tqf TARGET_QUERY_FILE
|
|
427
|
-
File containing the target sql
|
|
427
|
+
File containing the target sql command. Supports GCS and local paths.
|
|
428
428
|
--comparison-fields or -comp-fields FIELDS
|
|
429
429
|
Comma separated list of columns to compare. Can either be a physical column or an alias
|
|
430
430
|
See: *Calculated Fields* section for details
|
|
@@ -522,7 +522,7 @@ View the complete YAML file for a Grouped Column validation on the
|
|
|
522
522
|
You can scale DVT for large table validations by running the tool in a distributed manner. To optimize the validation speed for large tables, you can use GKE Jobs ([Google Kubernetes Jobs](https://cloud.google.com/kubernetes-engine/docs/how-to/deploying-workloads-overview#batch_jobs)) or [Cloud Run Jobs](https://cloud.google.com/run/docs/create-jobs). If you are not familiar with Kubernetes or Cloud Run Jobs, see [Scaling DVT with Distributed Jobs](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/internal/distributed_jobs.md) for a detailed overview.
|
|
523
523
|
|
|
524
524
|
|
|
525
|
-
We recommend first generating table partitions with the `generate-table-partitions` command for your large tables. Then, use Cloud Run or GKE to distribute validating each chunk in parallel. See the [Cloud Run Jobs Quickstart sample](https://github.com/GoogleCloudPlatform/professional-services-data-validator/tree/develop/samples/cloud_run_jobs) to get started.
|
|
525
|
+
We recommend first generating table partitions with the `generate-table-partitions` command for your large tables. Then, use Cloud Run or GKE to distribute validating each chunk in parallel. See the [Cloud Run Jobs Quickstart sample](https://github.com/GoogleCloudPlatform/professional-services-data-validator/tree/develop/samples/cloud_run_jobs) to get started.
|
|
526
526
|
|
|
527
527
|
When running DVT in a distributed fashion, both the `--kube-completions` and `--config-dir` flags are required. The `--kube-completions` flag specifies that the validation is being run in indexed completion mode in Kubernetes or as multiple independent tasks in Cloud Run. If the `-kc` option is used and you are not running in indexed mode, you will receive a warning and the container will process all the validations sequentially. If the `-kc` option is used and a config directory is not provided (i.e. a `--config-file` is provided instead), a warning is issued.
|
|
528
528
|
|
|
@@ -208,6 +208,8 @@ data-validation (--verbose or -v) (--log-level or -ll) validate row
|
|
|
208
208
|
Row batch size used for random row filters (default 10,000).
|
|
209
209
|
[--filter-status or -fs STATUSES_LIST]
|
|
210
210
|
Comma separated list of statuses to filter the validation results. Supported statuses are (success, fail). If no list is provided, all statuses are returned.
|
|
211
|
+
[--trim-string-pks, -tsp]
|
|
212
|
+
Trims string based primary key values, intended for use when one engine uses padded string semantics (e.g. CHAR(n)) and the other does not (e.g. VARCHAR(n)).
|
|
211
213
|
```
|
|
212
214
|
#### Generate Table Partitions for Large Table Row Validations
|
|
213
215
|
|
|
@@ -309,12 +311,12 @@ data-validation (--verbose or -v) (--log-level or -ll) validate custom-query col
|
|
|
309
311
|
Source sql query
|
|
310
312
|
Either --source-query or --source-query-file must be provided
|
|
311
313
|
--source-query-file SOURCE_QUERY_FILE, -sqf SOURCE_QUERY_FILE
|
|
312
|
-
File containing the source sql
|
|
314
|
+
File containing the source sql command. Supports GCS and local paths.
|
|
313
315
|
--target-query TARGET_QUERY, -tq TARGET_QUERY
|
|
314
316
|
Target sql query
|
|
315
317
|
Either --target-query or --target-query-file must be provided
|
|
316
318
|
--target-query-file TARGET_QUERY_FILE, -tqf TARGET_QUERY_FILE
|
|
317
|
-
File containing the target sql
|
|
319
|
+
File containing the target sql command. Supports GCS and local paths.
|
|
318
320
|
[--count COLUMNS] Comma separated list of columns for count or * for all columns
|
|
319
321
|
[--sum COLUMNS] Comma separated list of columns for sum or * for all numeric
|
|
320
322
|
[--min COLUMNS] Comma separated list of columns for min or * for all numeric
|
|
@@ -371,12 +373,12 @@ data-validation (--verbose or -v) (--log-level or -ll) validate custom-query row
|
|
|
371
373
|
Source sql query
|
|
372
374
|
Either --source-query or --source-query-file must be provided
|
|
373
375
|
--source-query-file SOURCE_QUERY_FILE, -sqf SOURCE_QUERY_FILE
|
|
374
|
-
File containing the source sql
|
|
376
|
+
File containing the source sql command. Supports GCS and local paths.
|
|
375
377
|
--target-query TARGET_QUERY, -tq TARGET_QUERY
|
|
376
378
|
Target sql query
|
|
377
379
|
Either --target-query or --target-query-file must be provided
|
|
378
380
|
--target-query-file TARGET_QUERY_FILE, -tqf TARGET_QUERY_FILE
|
|
379
|
-
File containing the target sql
|
|
381
|
+
File containing the target sql command. Supports GCS and local paths.
|
|
380
382
|
--comparison-fields or -comp-fields FIELDS
|
|
381
383
|
Comma separated list of columns to compare. Can either be a physical column or an alias
|
|
382
384
|
See: *Calculated Fields* section for details
|
|
@@ -474,7 +476,7 @@ View the complete YAML file for a Grouped Column validation on the
|
|
|
474
476
|
You can scale DVT for large table validations by running the tool in a distributed manner. To optimize the validation speed for large tables, you can use GKE Jobs ([Google Kubernetes Jobs](https://cloud.google.com/kubernetes-engine/docs/how-to/deploying-workloads-overview#batch_jobs)) or [Cloud Run Jobs](https://cloud.google.com/run/docs/create-jobs). If you are not familiar with Kubernetes or Cloud Run Jobs, see [Scaling DVT with Distributed Jobs](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/internal/distributed_jobs.md) for a detailed overview.
|
|
475
477
|
|
|
476
478
|
|
|
477
|
-
We recommend first generating table partitions with the `generate-table-partitions` command for your large tables. Then, use Cloud Run or GKE to distribute validating each chunk in parallel. See the [Cloud Run Jobs Quickstart sample](https://github.com/GoogleCloudPlatform/professional-services-data-validator/tree/develop/samples/cloud_run_jobs) to get started.
|
|
479
|
+
We recommend first generating table partitions with the `generate-table-partitions` command for your large tables. Then, use Cloud Run or GKE to distribute validating each chunk in parallel. See the [Cloud Run Jobs Quickstart sample](https://github.com/GoogleCloudPlatform/professional-services-data-validator/tree/develop/samples/cloud_run_jobs) to get started.
|
|
478
480
|
|
|
479
481
|
When running DVT in a distributed fashion, both the `--kube-completions` and `--config-dir` flags are required. The `--kube-completions` flag specifies that the validation is being run in indexed completion mode in Kubernetes or as multiple independent tasks in Cloud Run. If the `-kc` option is used and you are not running in indexed mode, you will receive a warning and the container will process all the validations sequentially. If the `-kc` option is used and a config directory is not provided (i.e. a `--config-file` is provided instead), a warning is issued.
|
|
480
482
|
|
{google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/data_validation/cli_tools.py
RENAMED
|
@@ -484,6 +484,15 @@ def _configure_row_parser(row_parser):
|
|
|
484
484
|
"-rbs",
|
|
485
485
|
help="Row batch size used for random row filters (default 10,000).",
|
|
486
486
|
)
|
|
487
|
+
optional_arguments.add_argument(
|
|
488
|
+
"--trim-string-pks",
|
|
489
|
+
"-tsp",
|
|
490
|
+
action="store_true",
|
|
491
|
+
help=(
|
|
492
|
+
"Trims string based primary key values, intended for use when one engine uses "
|
|
493
|
+
"padded string semantics (e.g. CHAR(n)) and the other does not (e.g. VARCHAR(n))."
|
|
494
|
+
),
|
|
495
|
+
)
|
|
487
496
|
|
|
488
497
|
# Group required arguments
|
|
489
498
|
required_arguments = row_parser.add_argument_group("required arguments")
|
|
@@ -954,6 +963,18 @@ def _add_common_arguments(optional_arguments, required_arguments):
|
|
|
954
963
|
)
|
|
955
964
|
|
|
956
965
|
|
|
966
|
+
def _check_no_partitions(value: str) -> int:
|
|
967
|
+
"""Check that number of partitions is between [2-10,000]
|
|
968
|
+
Using function to validate rather than choices as error message prints all choices.
|
|
969
|
+
"""
|
|
970
|
+
if value.isdigit() and 2 <= int(value) <= 10000:
|
|
971
|
+
return int(value)
|
|
972
|
+
else:
|
|
973
|
+
raise argparse.ArgumentTypeError(
|
|
974
|
+
f"{value} is not valid for number of partitions, use number in range 2 to 10000"
|
|
975
|
+
)
|
|
976
|
+
|
|
977
|
+
|
|
957
978
|
def _add_common_partition_arguments(optional_arguments, required_arguments):
|
|
958
979
|
"""Add all arguments common to get-partition command"""
|
|
959
980
|
|
|
@@ -978,10 +999,9 @@ def _add_common_partition_arguments(optional_arguments, required_arguments):
|
|
|
978
999
|
"--partition-num",
|
|
979
1000
|
"-pn",
|
|
980
1001
|
required=True,
|
|
981
|
-
help="Number of partitions/config files to generate",
|
|
982
|
-
type=
|
|
983
|
-
|
|
984
|
-
metavar="[1-1000]",
|
|
1002
|
+
help="Number of partitions/config files to generate, a number from 2 to 10,000",
|
|
1003
|
+
type=_check_no_partitions,
|
|
1004
|
+
metavar="[2-10000]",
|
|
985
1005
|
)
|
|
986
1006
|
|
|
987
1007
|
# Optional arguments
|
|
@@ -1387,6 +1407,7 @@ def get_pre_build_configs(args: Namespace, validate_cmd: str) -> List[Dict]:
|
|
|
1387
1407
|
"result_handler_config": result_handler_config,
|
|
1388
1408
|
"filter_config": filter_config,
|
|
1389
1409
|
"filter_status": filter_status,
|
|
1410
|
+
"trim_string_pks": getattr(args, "trim_string_pks", False),
|
|
1390
1411
|
"verbose": args.verbose,
|
|
1391
1412
|
}
|
|
1392
1413
|
pre_build_configs_list.append(pre_build_configs)
|
{google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/data_validation/clients.py
RENAMED
|
@@ -274,6 +274,23 @@ def get_max_column_length(client):
|
|
|
274
274
|
return 128
|
|
275
275
|
|
|
276
276
|
|
|
277
|
+
def get_max_in_list_size(client, in_list_over_expressions=False):
|
|
278
|
+
if client.name == "snowflake":
|
|
279
|
+
if in_list_over_expressions:
|
|
280
|
+
# This is a workaround for Snowflake limitation:
|
|
281
|
+
# SQL compilation error: In-list contains more than 50 non-constant values
|
|
282
|
+
# getattr(..., "cast") expression above is looking for lists where the contents are casts and not simple literals.
|
|
283
|
+
return 50
|
|
284
|
+
else:
|
|
285
|
+
return 16000
|
|
286
|
+
elif is_oracle_client(client):
|
|
287
|
+
# This is a workaround for Oracle limitation:
|
|
288
|
+
# ORA-01795: maximum number of expressions in a list is 1000
|
|
289
|
+
return 1000
|
|
290
|
+
else:
|
|
291
|
+
return None
|
|
292
|
+
|
|
293
|
+
|
|
277
294
|
CLIENT_LOOKUP = {
|
|
278
295
|
"BigQuery": get_bigquery_client,
|
|
279
296
|
"Impala": impala_connect,
|
|
@@ -112,6 +112,10 @@ class ConfigManager(object):
|
|
|
112
112
|
"""Return number of random rows or None."""
|
|
113
113
|
return self.random_row_batch_size() if self.use_random_rows() else None
|
|
114
114
|
|
|
115
|
+
def trim_string_pks(self):
|
|
116
|
+
"""Return if the validation should trim string primary keys."""
|
|
117
|
+
return self._config.get(consts.CONFIG_TRIM_STRING_PKS) or False
|
|
118
|
+
|
|
115
119
|
def process_in_memory(self):
|
|
116
120
|
"""Return whether to process in memory or on a remote platform."""
|
|
117
121
|
return True
|
|
@@ -459,6 +463,7 @@ class ConfigManager(object):
|
|
|
459
463
|
result_handler_config=None,
|
|
460
464
|
filter_config=None,
|
|
461
465
|
filter_status=None,
|
|
466
|
+
trim_string_pks=None,
|
|
462
467
|
verbose=False,
|
|
463
468
|
):
|
|
464
469
|
if isinstance(filter_config, dict):
|
|
@@ -487,6 +492,7 @@ class ConfigManager(object):
|
|
|
487
492
|
consts.CONFIG_USE_RANDOM_ROWS: use_random_rows,
|
|
488
493
|
consts.CONFIG_RANDOM_ROW_BATCH_SIZE: random_row_batch_size,
|
|
489
494
|
consts.CONFIG_FILTER_STATUS: filter_status,
|
|
495
|
+
consts.CONFIG_TRIM_STRING_PKS: trim_string_pks,
|
|
490
496
|
}
|
|
491
497
|
|
|
492
498
|
return ConfigManager(
|
|
@@ -1034,8 +1040,7 @@ class ConfigManager(object):
|
|
|
1034
1040
|
"""Return query from input file"""
|
|
1035
1041
|
query = ""
|
|
1036
1042
|
try:
|
|
1037
|
-
|
|
1038
|
-
query = file.read()
|
|
1043
|
+
query = gcs_helper.read_file(filename, download_as_text=True)
|
|
1039
1044
|
query = query.rstrip(";\n")
|
|
1040
1045
|
except IOError:
|
|
1041
1046
|
logging.error("Cannot read query file: ", filename)
|
|
@@ -1045,7 +1050,6 @@ class ConfigManager(object):
|
|
|
1045
1050
|
"Expected file with sql query, got empty file or file with white spaces. "
|
|
1046
1051
|
f"input file: {filename}"
|
|
1047
1052
|
)
|
|
1048
|
-
file.close()
|
|
1049
1053
|
return query
|
|
1050
1054
|
|
|
1051
1055
|
def get_query_from_inline(self, inline_query):
|
{google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/data_validation/consts.py
RENAMED
|
@@ -45,6 +45,7 @@ CONFIG_CALCULATED_TARGET_COLUMNS = "target_calculated_columns"
|
|
|
45
45
|
CONFIG_USE_RANDOM_ROWS = "use_random_rows"
|
|
46
46
|
CONFIG_RANDOM_ROW_BATCH_SIZE = "random_row_batch_size"
|
|
47
47
|
CONFIG_PRIMARY_KEYS = "primary_keys"
|
|
48
|
+
CONFIG_TRIM_STRING_PKS = "trim_string_pks"
|
|
48
49
|
CONFIG_SOURCE_COLUMN = "source_column"
|
|
49
50
|
CONFIG_TARGET_COLUMN = "target_column"
|
|
50
51
|
CONFIG_THRESHOLD = "threshold"
|
|
@@ -142,6 +142,9 @@ class DataValidation(object):
|
|
|
142
142
|
**{source_pk_column: query[source_pk_column].cast("string")}
|
|
143
143
|
)
|
|
144
144
|
|
|
145
|
+
if self.config_manager.trim_string_pks():
|
|
146
|
+
query = query.mutate(**{source_pk_column: query[source_pk_column].rstrip()})
|
|
147
|
+
|
|
145
148
|
random_rows = self.config_manager.source_client.execute(query)
|
|
146
149
|
if len(random_rows) == 0:
|
|
147
150
|
return
|
{google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/data_validation/gcs_helper.py
RENAMED
|
@@ -50,12 +50,12 @@ def _get_gcs_file_path(gcs_file_path: str) -> str:
|
|
|
50
50
|
return "".join(gcs_file_path[5:].split("/", 1)[1:])
|
|
51
51
|
|
|
52
52
|
|
|
53
|
-
def _read_gcs_file(file_path: str
|
|
53
|
+
def _read_gcs_file(file_path: str, download_as_text: bool = False):
|
|
54
54
|
gcs_bucket = get_gcs_bucket(file_path)
|
|
55
55
|
blob = gcs_bucket.blob(_get_gcs_file_path(file_path))
|
|
56
56
|
if not blob:
|
|
57
57
|
raise ValueError(f"Invalid Cloud Storage Path: {file_path}")
|
|
58
|
-
return blob.download_as_bytes()
|
|
58
|
+
return blob.download_as_text() if download_as_text else blob.download_as_bytes()
|
|
59
59
|
|
|
60
60
|
|
|
61
61
|
def _write_gcs_file(file_path: str, data: str):
|
|
@@ -64,9 +64,9 @@ def _write_gcs_file(file_path: str, data: str):
|
|
|
64
64
|
blob.upload_from_string(data)
|
|
65
65
|
|
|
66
66
|
|
|
67
|
-
def read_file(file_path: str):
|
|
67
|
+
def read_file(file_path: str, download_as_text: bool = False):
|
|
68
68
|
if _is_gcs_path(file_path):
|
|
69
|
-
return _read_gcs_file(file_path)
|
|
69
|
+
return _read_gcs_file(file_path, download_as_text)
|
|
70
70
|
else:
|
|
71
71
|
with open(file_path, "r") as f:
|
|
72
72
|
return f.read()
|
|
@@ -32,7 +32,6 @@ class PartitionBuilder:
|
|
|
32
32
|
self.table_count = len(config_managers)
|
|
33
33
|
self.args = args
|
|
34
34
|
self.config_dir = self._get_arg_config_dir()
|
|
35
|
-
self.primary_keys = self._get_primary_keys()
|
|
36
35
|
|
|
37
36
|
def _get_arg_config_dir(self) -> str:
|
|
38
37
|
"""Return String yaml config folder path."""
|
|
@@ -41,10 +40,6 @@ class PartitionBuilder:
|
|
|
41
40
|
|
|
42
41
|
return self.args.config_dir
|
|
43
42
|
|
|
44
|
-
def _get_primary_keys(self) -> str:
|
|
45
|
-
"""Return the Primary Keys"""
|
|
46
|
-
return cli_tools.get_arg_list(self.args.primary_keys)
|
|
47
|
-
|
|
48
43
|
def _get_yaml_from_config(self, config_manager: ConfigManager) -> Dict:
|
|
49
44
|
"""Return dict objects formatted for yaml validations.
|
|
50
45
|
|
|
@@ -106,8 +101,13 @@ class PartitionBuilder:
|
|
|
106
101
|
for config_manager in self.config_managers: # For each pair of tables
|
|
107
102
|
validation_builder = ValidationBuilder(config_manager)
|
|
108
103
|
|
|
104
|
+
source_pks, target_pks = [], []
|
|
105
|
+
for pk in config_manager.primary_keys:
|
|
106
|
+
source_pks.append(pk["source_column"])
|
|
107
|
+
target_pks.append(pk["target_column"])
|
|
108
|
+
|
|
109
109
|
source_partition_row_builder = PartitionRowBuilder(
|
|
110
|
-
|
|
110
|
+
source_pks,
|
|
111
111
|
config_manager.source_client,
|
|
112
112
|
config_manager.source_schema,
|
|
113
113
|
config_manager.source_table,
|
|
@@ -115,7 +115,7 @@ class PartitionBuilder:
|
|
|
115
115
|
)
|
|
116
116
|
source_table = source_partition_row_builder.query
|
|
117
117
|
target_partition_row_builder = PartitionRowBuilder(
|
|
118
|
-
|
|
118
|
+
target_pks,
|
|
119
119
|
config_manager.target_client,
|
|
120
120
|
config_manager.target_schema,
|
|
121
121
|
config_manager.target_table,
|
|
@@ -150,9 +150,9 @@ class PartitionBuilder:
|
|
|
150
150
|
# First we number each row in the source table. Using row_number instead of ntile since it is
|
|
151
151
|
# available on all platforms (Teradata does not support NTILE). For our purposes, it is likely
|
|
152
152
|
# more efficient
|
|
153
|
-
window1 = ibis.window(order_by=
|
|
153
|
+
window1 = ibis.window(order_by=source_pks)
|
|
154
154
|
row_number = (ibis.row_number().over(window1) + 1).name(consts.DVT_POS_COL)
|
|
155
|
-
dvt_keys =
|
|
155
|
+
dvt_keys = source_pks.copy()
|
|
156
156
|
dvt_keys.append(row_number)
|
|
157
157
|
rownum_table = source_table.select(dvt_keys)
|
|
158
158
|
# Rownum table is just the primary key columns in the source table along with
|
|
@@ -182,7 +182,7 @@ class PartitionBuilder:
|
|
|
182
182
|
)
|
|
183
183
|
> 0
|
|
184
184
|
)
|
|
185
|
-
first_keys_table = rownum_table[cond].order_by(
|
|
185
|
+
first_keys_table = rownum_table[cond].order_by(source_pks)
|
|
186
186
|
|
|
187
187
|
# Up until this point, we have built the table expression, have not executed the query yet.
|
|
188
188
|
# The query is now executed to find the first element of each partition
|
|
@@ -219,13 +219,13 @@ class PartitionBuilder:
|
|
|
219
219
|
|
|
220
220
|
filter_source_clause = less_than_value(
|
|
221
221
|
source_table,
|
|
222
|
-
|
|
223
|
-
first_elements[1, : len(
|
|
222
|
+
source_pks,
|
|
223
|
+
first_elements[1, : len(source_pks)],
|
|
224
224
|
)
|
|
225
225
|
filter_target_clause = less_than_value(
|
|
226
226
|
target_table,
|
|
227
|
-
|
|
228
|
-
first_elements[1, : len(
|
|
227
|
+
target_pks,
|
|
228
|
+
first_elements[1, : len(target_pks)],
|
|
229
229
|
)
|
|
230
230
|
source_where_list.append(
|
|
231
231
|
self._extract_where(
|
|
@@ -243,21 +243,21 @@ class PartitionBuilder:
|
|
|
243
243
|
for i in range(1, first_elements.shape[0] - 1):
|
|
244
244
|
filter_source_clause = geq_value(
|
|
245
245
|
source_table,
|
|
246
|
-
|
|
247
|
-
first_elements[i, : len(
|
|
246
|
+
source_pks,
|
|
247
|
+
first_elements[i, : len(source_pks)],
|
|
248
248
|
) & less_than_value(
|
|
249
249
|
source_table,
|
|
250
|
-
|
|
251
|
-
first_elements[i + 1, : len(
|
|
250
|
+
source_pks,
|
|
251
|
+
first_elements[i + 1, : len(source_pks)],
|
|
252
252
|
)
|
|
253
253
|
filter_target_clause = geq_value(
|
|
254
254
|
target_table,
|
|
255
|
-
|
|
256
|
-
first_elements[i, : len(
|
|
255
|
+
target_pks,
|
|
256
|
+
first_elements[i, : len(target_pks)],
|
|
257
257
|
) & less_than_value(
|
|
258
258
|
target_table,
|
|
259
|
-
|
|
260
|
-
first_elements[i + 1, : len(
|
|
259
|
+
target_pks,
|
|
260
|
+
first_elements[i + 1, : len(target_pks)],
|
|
261
261
|
)
|
|
262
262
|
source_where_list.append(
|
|
263
263
|
self._extract_where(
|
|
@@ -273,13 +273,13 @@ class PartitionBuilder:
|
|
|
273
273
|
)
|
|
274
274
|
filter_source_clause = geq_value(
|
|
275
275
|
source_table,
|
|
276
|
-
|
|
277
|
-
first_elements[len(first_elements) - 1, : len(
|
|
276
|
+
source_pks,
|
|
277
|
+
first_elements[len(first_elements) - 1, : len(source_pks)],
|
|
278
278
|
)
|
|
279
279
|
filter_target_clause = geq_value(
|
|
280
280
|
target_table,
|
|
281
|
-
|
|
282
|
-
first_elements[len(first_elements) - 1, : len(
|
|
281
|
+
target_pks,
|
|
282
|
+
first_elements[len(first_elements) - 1, : len(target_pks)],
|
|
283
283
|
)
|
|
284
284
|
source_where_list.append(
|
|
285
285
|
self._extract_where(
|
|
@@ -179,6 +179,10 @@ class FilterField(object):
|
|
|
179
179
|
"""
|
|
180
180
|
return FilterField(None, left=expr)
|
|
181
181
|
|
|
182
|
+
@staticmethod
|
|
183
|
+
def or_(field_list: list):
|
|
184
|
+
return FilterField(ibis.or_, left=field_list)
|
|
185
|
+
|
|
182
186
|
def compile(self, ibis_table):
|
|
183
187
|
if self.expr is None:
|
|
184
188
|
return operations.compile_raw_sql(ibis_table, self.left)
|
|
@@ -189,11 +193,16 @@ class FilterField(object):
|
|
|
189
193
|
if self.right_field:
|
|
190
194
|
self.right = ibis_table[self.right_field]
|
|
191
195
|
|
|
192
|
-
|
|
196
|
+
if self.expr == ibis.or_:
|
|
197
|
+
return self.expr(*[_.compile(ibis_table) for _ in self.left])
|
|
198
|
+
else:
|
|
199
|
+
return self.expr(self.left, self.right)
|
|
193
200
|
|
|
194
201
|
|
|
195
202
|
class ComparisonField(object):
|
|
196
|
-
def __init__(
|
|
203
|
+
def __init__(
|
|
204
|
+
self, field_name: str, alias: str = None, cast: str = None, trim: bool = None
|
|
205
|
+
):
|
|
197
206
|
"""A representation of a comparison field used to build a query.
|
|
198
207
|
|
|
199
208
|
Args:
|
|
@@ -204,6 +213,7 @@ class ComparisonField(object):
|
|
|
204
213
|
self.field_name = field_name
|
|
205
214
|
self.alias = alias
|
|
206
215
|
self.cast = cast
|
|
216
|
+
self.trim = trim
|
|
207
217
|
|
|
208
218
|
def compile(self, ibis_table):
|
|
209
219
|
# Fields are supplied on compile or on build
|
|
@@ -211,6 +221,8 @@ class ComparisonField(object):
|
|
|
211
221
|
alias = self.alias or self.field_name
|
|
212
222
|
if self.cast:
|
|
213
223
|
comparison_field = comparison_field.force_cast(self.cast)
|
|
224
|
+
elif self.trim and comparison_field.type().is_string():
|
|
225
|
+
comparison_field = comparison_field.rstrip()
|
|
214
226
|
comparison_field = comparison_field.name(alias)
|
|
215
227
|
|
|
216
228
|
return comparison_field
|
|
@@ -15,6 +15,7 @@ import logging
|
|
|
15
15
|
from copy import deepcopy
|
|
16
16
|
|
|
17
17
|
from data_validation import consts, metadata
|
|
18
|
+
from data_validation.clients import get_max_in_list_size
|
|
18
19
|
from data_validation.query_builder.query_builder import (
|
|
19
20
|
AggregateField,
|
|
20
21
|
CalculatedField,
|
|
@@ -25,6 +26,11 @@ from data_validation.query_builder.query_builder import (
|
|
|
25
26
|
)
|
|
26
27
|
|
|
27
28
|
|
|
29
|
+
def list_to_sublists(id_list: list, max_size: int) -> list:
|
|
30
|
+
"""Return a list of items as a list of lists based on a max list length of max_size."""
|
|
31
|
+
return [id_list[_ : _ + max_size] for _ in range(0, len(id_list), max_size)]
|
|
32
|
+
|
|
33
|
+
|
|
28
34
|
class ValidationBuilder(object):
|
|
29
35
|
def __init__(self, config_manager):
|
|
30
36
|
"""Initialize a ValidationBuilder client which supplies the
|
|
@@ -60,6 +66,27 @@ class ValidationBuilder(object):
|
|
|
60
66
|
self.add_primary_keys()
|
|
61
67
|
self.add_query_limit()
|
|
62
68
|
|
|
69
|
+
def _construct_isin_filter(
|
|
70
|
+
self, client, column_name: str, in_list: list
|
|
71
|
+
) -> FilterField:
|
|
72
|
+
"""Return a FilterField object that is either isin(...) or (isin(...) OR isin(...) OR...)."""
|
|
73
|
+
|
|
74
|
+
max_in_list_size = get_max_in_list_size(
|
|
75
|
+
client,
|
|
76
|
+
in_list_over_expressions=bool(
|
|
77
|
+
in_list and getattr(in_list[0], "cast", None)
|
|
78
|
+
),
|
|
79
|
+
)
|
|
80
|
+
if max_in_list_size and len(in_list) > max_in_list_size:
|
|
81
|
+
source_filters = [
|
|
82
|
+
FilterField.isin(column_name, _)
|
|
83
|
+
for _ in list_to_sublists(in_list, max_in_list_size)
|
|
84
|
+
]
|
|
85
|
+
return FilterField.or_(source_filters)
|
|
86
|
+
|
|
87
|
+
else:
|
|
88
|
+
return FilterField.isin(column_name, in_list)
|
|
89
|
+
|
|
63
90
|
def clone(self):
|
|
64
91
|
cloned_builder = ValidationBuilder(self.config_manager)
|
|
65
92
|
|
|
@@ -237,12 +264,13 @@ class ValidationBuilder(object):
|
|
|
237
264
|
# grab calc field metadata
|
|
238
265
|
alias = primary_key.get(consts.CONFIG_FIELD_ALIAS)
|
|
239
266
|
cast = primary_key.get(consts.CONFIG_CAST)
|
|
267
|
+
trim = self.config_manager.trim_string_pks()
|
|
240
268
|
# check if valid calc field and return correct object
|
|
241
269
|
source_field = ComparisonField(
|
|
242
|
-
field_name=source_field_name, alias=alias, cast=cast
|
|
270
|
+
field_name=source_field_name, alias=alias, cast=cast, trim=trim
|
|
243
271
|
)
|
|
244
272
|
target_field = ComparisonField(
|
|
245
|
-
field_name=target_field_name, alias=alias, cast=cast
|
|
273
|
+
field_name=target_field_name, alias=alias, cast=cast, trim=trim
|
|
246
274
|
)
|
|
247
275
|
self.source_builder.add_comparison_field(source_field)
|
|
248
276
|
self.target_builder.add_comparison_field(target_field)
|
|
@@ -271,11 +299,13 @@ class ValidationBuilder(object):
|
|
|
271
299
|
filter_field[consts.CONFIG_FILTER_TARGET_VALUE],
|
|
272
300
|
)
|
|
273
301
|
elif filter_field[consts.CONFIG_TYPE] == consts.FILTER_TYPE_ISIN:
|
|
274
|
-
source_filter =
|
|
302
|
+
source_filter = self._construct_isin_filter(
|
|
303
|
+
self.source_client,
|
|
275
304
|
filter_field[consts.CONFIG_FILTER_SOURCE_COLUMN],
|
|
276
305
|
filter_field[consts.CONFIG_FILTER_SOURCE_VALUE],
|
|
277
306
|
)
|
|
278
|
-
target_filter =
|
|
307
|
+
target_filter = self._construct_isin_filter(
|
|
308
|
+
self.target_client,
|
|
279
309
|
filter_field[consts.CONFIG_FILTER_TARGET_COLUMN],
|
|
280
310
|
filter_field[consts.CONFIG_FILTER_TARGET_VALUE],
|
|
281
311
|
)
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: google-pso-data-validator
|
|
3
|
-
Version: 5.
|
|
3
|
+
Version: 5.1.0
|
|
4
4
|
Summary: A package to enable easy data validation
|
|
5
|
-
|
|
6
|
-
Author:
|
|
7
|
-
|
|
8
|
-
Classifier: Development Status :: 3 - Alpha
|
|
5
|
+
Author: PSO DVT Engineering team
|
|
6
|
+
Author-email: data-validator-eng@google.com
|
|
7
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
8
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
10
9
|
Classifier: Programming Language :: Python
|
|
11
10
|
Classifier: Programming Language :: Python :: 3.8
|
|
@@ -16,7 +15,7 @@ Requires-Python: >=3.8
|
|
|
16
15
|
Description-Content-Type: text/markdown
|
|
17
16
|
License-File: LICENSE
|
|
18
17
|
Requires-Dist: attrs==23.1.0
|
|
19
|
-
Requires-Dist: grpcio==1.53.
|
|
18
|
+
Requires-Dist: grpcio==1.53.2
|
|
20
19
|
Requires-Dist: lazy-object-proxy==1.9.0
|
|
21
20
|
Requires-Dist: marshmallow==3.19.0
|
|
22
21
|
Requires-Dist: fsspec>=2022.8.2
|
|
@@ -24,7 +23,7 @@ Requires-Dist: google-api-python-client==2.91.0
|
|
|
24
23
|
Requires-Dist: ibis-framework==5.1.0
|
|
25
24
|
Requires-Dist: impyla==0.18.0
|
|
26
25
|
Requires-Dist: SQLAlchemy==1.4.49
|
|
27
|
-
Requires-Dist: PyMySQL==1.1.
|
|
26
|
+
Requires-Dist: PyMySQL==1.1.1
|
|
28
27
|
Requires-Dist: psycopg2-binary==2.9.6
|
|
29
28
|
Requires-Dist: PyYAML==6.0
|
|
30
29
|
Requires-Dist: pandas==1.5.3
|
|
@@ -35,12 +34,11 @@ Requires-Dist: google-cloud-bigquery==3.11.3
|
|
|
35
34
|
Requires-Dist: google-cloud-bigquery-storage==2.20.0
|
|
36
35
|
Requires-Dist: google-cloud-spanner==3.36.0
|
|
37
36
|
Requires-Dist: google-cloud-storage==2.10.0
|
|
38
|
-
Requires-Dist: setuptools>=34.0.0
|
|
39
37
|
Requires-Dist: jellyfish==1.0.0
|
|
40
38
|
Requires-Dist: tabulate==0.9.0
|
|
41
39
|
Requires-Dist: Flask==2.3.2
|
|
42
40
|
Requires-Dist: parsy==2.1
|
|
43
|
-
Requires-Dist: google-cloud-secret-manager
|
|
41
|
+
Requires-Dist: google-cloud-secret-manager>=2.16.0
|
|
44
42
|
Provides-Extra: apache-airflow
|
|
45
43
|
Requires-Dist: 1.10.11; extra == "apache-airflow"
|
|
46
44
|
Provides-Extra: pyspark
|
|
@@ -256,6 +254,8 @@ data-validation (--verbose or -v) (--log-level or -ll) validate row
|
|
|
256
254
|
Row batch size used for random row filters (default 10,000).
|
|
257
255
|
[--filter-status or -fs STATUSES_LIST]
|
|
258
256
|
Comma separated list of statuses to filter the validation results. Supported statuses are (success, fail). If no list is provided, all statuses are returned.
|
|
257
|
+
[--trim-string-pks, -tsp]
|
|
258
|
+
Trims string based primary key values, intended for use when one engine uses padded string semantics (e.g. CHAR(n)) and the other does not (e.g. VARCHAR(n)).
|
|
259
259
|
```
|
|
260
260
|
#### Generate Table Partitions for Large Table Row Validations
|
|
261
261
|
|
|
@@ -357,12 +357,12 @@ data-validation (--verbose or -v) (--log-level or -ll) validate custom-query col
|
|
|
357
357
|
Source sql query
|
|
358
358
|
Either --source-query or --source-query-file must be provided
|
|
359
359
|
--source-query-file SOURCE_QUERY_FILE, -sqf SOURCE_QUERY_FILE
|
|
360
|
-
File containing the source sql
|
|
360
|
+
File containing the source sql command. Supports GCS and local paths.
|
|
361
361
|
--target-query TARGET_QUERY, -tq TARGET_QUERY
|
|
362
362
|
Target sql query
|
|
363
363
|
Either --target-query or --target-query-file must be provided
|
|
364
364
|
--target-query-file TARGET_QUERY_FILE, -tqf TARGET_QUERY_FILE
|
|
365
|
-
File containing the target sql
|
|
365
|
+
File containing the target sql command. Supports GCS and local paths.
|
|
366
366
|
[--count COLUMNS] Comma separated list of columns for count or * for all columns
|
|
367
367
|
[--sum COLUMNS] Comma separated list of columns for sum or * for all numeric
|
|
368
368
|
[--min COLUMNS] Comma separated list of columns for min or * for all numeric
|
|
@@ -419,12 +419,12 @@ data-validation (--verbose or -v) (--log-level or -ll) validate custom-query row
|
|
|
419
419
|
Source sql query
|
|
420
420
|
Either --source-query or --source-query-file must be provided
|
|
421
421
|
--source-query-file SOURCE_QUERY_FILE, -sqf SOURCE_QUERY_FILE
|
|
422
|
-
File containing the source sql
|
|
422
|
+
File containing the source sql command. Supports GCS and local paths.
|
|
423
423
|
--target-query TARGET_QUERY, -tq TARGET_QUERY
|
|
424
424
|
Target sql query
|
|
425
425
|
Either --target-query or --target-query-file must be provided
|
|
426
426
|
--target-query-file TARGET_QUERY_FILE, -tqf TARGET_QUERY_FILE
|
|
427
|
-
File containing the target sql
|
|
427
|
+
File containing the target sql command. Supports GCS and local paths.
|
|
428
428
|
--comparison-fields or -comp-fields FIELDS
|
|
429
429
|
Comma separated list of columns to compare. Can either be a physical column or an alias
|
|
430
430
|
See: *Calculated Fields* section for details
|
|
@@ -522,7 +522,7 @@ View the complete YAML file for a Grouped Column validation on the
|
|
|
522
522
|
You can scale DVT for large table validations by running the tool in a distributed manner. To optimize the validation speed for large tables, you can use GKE Jobs ([Google Kubernetes Jobs](https://cloud.google.com/kubernetes-engine/docs/how-to/deploying-workloads-overview#batch_jobs)) or [Cloud Run Jobs](https://cloud.google.com/run/docs/create-jobs). If you are not familiar with Kubernetes or Cloud Run Jobs, see [Scaling DVT with Distributed Jobs](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/internal/distributed_jobs.md) for a detailed overview.
|
|
523
523
|
|
|
524
524
|
|
|
525
|
-
We recommend first generating table partitions with the `generate-table-partitions` command for your large tables. Then, use Cloud Run or GKE to distribute validating each chunk in parallel. See the [Cloud Run Jobs Quickstart sample](https://github.com/GoogleCloudPlatform/professional-services-data-validator/tree/develop/samples/cloud_run_jobs) to get started.
|
|
525
|
+
We recommend first generating table partitions with the `generate-table-partitions` command for your large tables. Then, use Cloud Run or GKE to distribute validating each chunk in parallel. See the [Cloud Run Jobs Quickstart sample](https://github.com/GoogleCloudPlatform/professional-services-data-validator/tree/develop/samples/cloud_run_jobs) to get started.
|
|
526
526
|
|
|
527
527
|
When running DVT in a distributed fashion, both the `--kube-completions` and `--config-dir` flags are required. The `--kube-completions` flag specifies that the validation is being run in indexed completion mode in Kubernetes or as multiple independent tasks in Cloud Run. If the `-kc` option is used and you are not running in indexed mode, you will receive a warning and the container will process all the validations sequentially. If the `-kc` option is used and a config directory is not provided (i.e. a `--config-file` is provided instead), a warning is issued.
|
|
528
528
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
attrs==23.1.0
|
|
2
|
-
grpcio==1.53.
|
|
2
|
+
grpcio==1.53.2
|
|
3
3
|
lazy-object-proxy==1.9.0
|
|
4
4
|
marshmallow==3.19.0
|
|
5
5
|
fsspec>=2022.8.2
|
|
@@ -7,7 +7,7 @@ google-api-python-client==2.91.0
|
|
|
7
7
|
ibis-framework==5.1.0
|
|
8
8
|
impyla==0.18.0
|
|
9
9
|
SQLAlchemy==1.4.49
|
|
10
|
-
PyMySQL==1.1.
|
|
10
|
+
PyMySQL==1.1.1
|
|
11
11
|
psycopg2-binary==2.9.6
|
|
12
12
|
PyYAML==6.0
|
|
13
13
|
pandas==1.5.3
|
|
@@ -18,12 +18,11 @@ google-cloud-bigquery==3.11.3
|
|
|
18
18
|
google-cloud-bigquery-storage==2.20.0
|
|
19
19
|
google-cloud-spanner==3.36.0
|
|
20
20
|
google-cloud-storage==2.10.0
|
|
21
|
-
setuptools>=34.0.0
|
|
22
21
|
jellyfish==1.0.0
|
|
23
22
|
tabulate==0.9.0
|
|
24
23
|
Flask==2.3.2
|
|
25
24
|
parsy==2.1
|
|
26
|
-
google-cloud-secret-manager
|
|
25
|
+
google-cloud-secret-manager>=2.16.0
|
|
27
26
|
|
|
28
27
|
[apache-airflow]
|
|
29
28
|
1.10.11
|
|
@@ -18,8 +18,8 @@ import setuptools
|
|
|
18
18
|
|
|
19
19
|
name = "google-pso-data-validator"
|
|
20
20
|
description = "A package to enable easy data validation"
|
|
21
|
-
version = "5.
|
|
22
|
-
release_status = "Development Status ::
|
|
21
|
+
version = "5.1.0"
|
|
22
|
+
release_status = "Development Status :: 4 - Beta"
|
|
23
23
|
|
|
24
24
|
with open("README.md", "r") as fh:
|
|
25
25
|
long_description = fh.read()
|
|
@@ -27,7 +27,7 @@ with open("README.md", "r") as fh:
|
|
|
27
27
|
dependencies = [
|
|
28
28
|
# Dependency corrections from our requirements
|
|
29
29
|
"attrs==23.1.0",
|
|
30
|
-
"grpcio==1.53.
|
|
30
|
+
"grpcio==1.53.2",
|
|
31
31
|
"lazy-object-proxy==1.9.0",
|
|
32
32
|
"marshmallow==3.19.0",
|
|
33
33
|
# Core dependencies
|
|
@@ -36,7 +36,7 @@ dependencies = [
|
|
|
36
36
|
"ibis-framework==5.1.0",
|
|
37
37
|
"impyla==0.18.0",
|
|
38
38
|
"SQLAlchemy==1.4.49",
|
|
39
|
-
"PyMySQL==1.1.
|
|
39
|
+
"PyMySQL==1.1.1",
|
|
40
40
|
"psycopg2-binary==2.9.6",
|
|
41
41
|
"PyYAML==6.0",
|
|
42
42
|
"pandas==1.5.3",
|
|
@@ -47,12 +47,11 @@ dependencies = [
|
|
|
47
47
|
"google-cloud-bigquery-storage==2.20.0",
|
|
48
48
|
"google-cloud-spanner==3.36.0",
|
|
49
49
|
"google-cloud-storage==2.10.0",
|
|
50
|
-
"setuptools>=34.0.0",
|
|
51
50
|
"jellyfish==1.0.0",
|
|
52
51
|
"tabulate==0.9.0",
|
|
53
52
|
"Flask==2.3.2",
|
|
54
53
|
"parsy==2.1",
|
|
55
|
-
"google-cloud-secret-manager
|
|
54
|
+
"google-cloud-secret-manager>=2.16.0",
|
|
56
55
|
]
|
|
57
56
|
|
|
58
57
|
extras_require = {
|
|
@@ -74,11 +73,10 @@ setuptools.setup(
|
|
|
74
73
|
name=name,
|
|
75
74
|
description=description,
|
|
76
75
|
version=version,
|
|
77
|
-
author="
|
|
78
|
-
author_email="
|
|
76
|
+
author="PSO DVT Engineering team",
|
|
77
|
+
author_email="data-validator-eng@google.com",
|
|
79
78
|
long_description=long_description,
|
|
80
79
|
long_description_content_type="text/markdown",
|
|
81
|
-
url="https://github.com/pypa/sampleproject",
|
|
82
80
|
packages=packages,
|
|
83
81
|
classifiers=[
|
|
84
82
|
release_status,
|
|
@@ -197,6 +197,10 @@ def _get_schema_using_query(self, query):
|
|
|
197
197
|
# Removing LIMIT 0 around query since it returns no results in Hive
|
|
198
198
|
cur = self.raw_sql(f"SELECT * FROM ({query}) t0 LIMIT 1")
|
|
199
199
|
cur.fetchall()
|
|
200
|
+
cur.description = [
|
|
201
|
+
(description[0].replace("t0.", "", 1), *description[1:])
|
|
202
|
+
for description in cur.description
|
|
203
|
+
]
|
|
200
204
|
ibis_fields = self._adapt_types(cur.description)
|
|
201
205
|
cur.release()
|
|
202
206
|
|
|
File without changes
|
{google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/data_validation/__init__.py
RENAMED
|
File without changes
|
{google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/data_validation/__main__.py
RENAMED
|
File without changes
|
|
File without changes
|
{google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/data_validation/client_info.py
RENAMED
|
File without changes
|
{google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/data_validation/combiner.py
RENAMED
|
File without changes
|
{google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/data_validation/exceptions.py
RENAMED
|
File without changes
|
|
File without changes
|
{google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/data_validation/metadata.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/data_validation/state_manager.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{google_pso_data_validator-5.0.0 → google_pso_data_validator-5.1.0}/third_party/ibis/ibis_db2/api.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|