google-pso-data-validator 6.2.0__tar.gz → 6.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.
Files changed (85) hide show
  1. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/PKG-INFO +28 -11
  2. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/README.md +27 -10
  3. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/data_validation/__main__.py +96 -91
  4. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/data_validation/cli_tools.py +128 -60
  5. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/data_validation/clients.py +5 -1
  6. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/data_validation/combiner.py +34 -20
  7. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/data_validation/config_manager.py +108 -42
  8. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/data_validation/consts.py +8 -0
  9. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/data_validation/data_validation.py +21 -7
  10. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/data_validation/exceptions.py +5 -1
  11. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/data_validation/metadata.py +1 -3
  12. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/data_validation/partition_builder.py +2 -0
  13. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/data_validation/query_builder/partition_row_builder.py +8 -2
  14. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/data_validation/query_builder/query_builder.py +5 -0
  15. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/data_validation/secret_manager.py +9 -5
  16. google_pso_data_validator-6.4.0/data_validation/util.py +24 -0
  17. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/data_validation/validation_builder.py +0 -2
  18. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/google_pso_data_validator.egg-info/PKG-INFO +28 -11
  19. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/google_pso_data_validator.egg-info/SOURCES.txt +1 -0
  20. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/setup.py +6 -6
  21. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_addon/api.py +21 -2
  22. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_addon/operations.py +17 -5
  23. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_db2/registry.py +1 -0
  24. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_oracle/__init__.py +10 -0
  25. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_oracle/datatypes.py +2 -2
  26. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_oracle/registry.py +9 -0
  27. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_postgres/client.py +7 -1
  28. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_teradata/registry.py +29 -0
  29. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/LICENSE +0 -0
  30. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/data_validation/__init__.py +0 -0
  31. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/data_validation/app.py +0 -0
  32. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/data_validation/client_info.py +0 -0
  33. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/data_validation/find_tables.py +0 -0
  34. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/data_validation/gcs_helper.py +0 -0
  35. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/data_validation/jellyfish_distance.py +0 -0
  36. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/data_validation/query_builder/__init__.py +0 -0
  37. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/data_validation/query_builder/random_row_builder.py +0 -0
  38. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/data_validation/result_handlers/__init__.py +0 -0
  39. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/data_validation/result_handlers/bigquery.py +0 -0
  40. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/data_validation/result_handlers/text.py +0 -0
  41. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/data_validation/schema_validation.py +0 -0
  42. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/data_validation/state_manager.py +0 -0
  43. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/google_pso_data_validator.egg-info/dependency_links.txt +0 -0
  44. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/google_pso_data_validator.egg-info/entry_points.txt +0 -0
  45. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/google_pso_data_validator.egg-info/requires.txt +0 -0
  46. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/google_pso_data_validator.egg-info/top_level.txt +0 -0
  47. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/setup.cfg +0 -0
  48. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_addon/__init__.py +0 -0
  49. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_cloud_spanner/__init__.py +0 -0
  50. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_cloud_spanner/api.py +0 -0
  51. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_cloud_spanner/client.py +0 -0
  52. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_cloud_spanner/compiler.py +0 -0
  53. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_cloud_spanner/datatypes.py +0 -0
  54. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_cloud_spanner/registry.py +0 -0
  55. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_cloud_spanner/tests/__init__.py +0 -0
  56. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_cloud_spanner/tests/conftest.py +0 -0
  57. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_cloud_spanner/to_pandas.py +0 -0
  58. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_db2/__init__.py +0 -0
  59. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_db2/api.py +0 -0
  60. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_db2/compiler.py +0 -0
  61. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_db2/datatypes.py +0 -0
  62. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_impala/__init__.py +0 -0
  63. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_impala/api.py +0 -0
  64. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_mssql/__init__.py +0 -0
  65. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_mssql/api.py +0 -0
  66. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_mssql/datatypes.py +0 -0
  67. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_mssql/registry.py +0 -0
  68. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_mysql/__init__.py +0 -0
  69. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_mysql/base_sql_compiler/__init__.py +0 -0
  70. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_mysql/base_sql_compiler/select_builder.py +0 -0
  71. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_mysql/compiler.py +0 -0
  72. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_oracle/api.py +0 -0
  73. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_oracle/compiler.py +0 -0
  74. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_postgres/__init__.py +0 -0
  75. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_postgres/datatypes.py +0 -0
  76. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_redshift/__init__.py +0 -0
  77. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_redshift/api.py +0 -0
  78. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_redshift/compiler.py +0 -0
  79. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_snowflake/__init__.py +0 -0
  80. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_snowflake/api.py +0 -0
  81. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_snowflake/datatypes.py +0 -0
  82. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_teradata/__init__.py +0 -0
  83. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_teradata/api.py +0 -0
  84. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_teradata/compiler.py +0 -0
  85. {google_pso_data_validator-6.2.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_teradata/datatypes.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: google-pso-data-validator
3
- Version: 6.2.0
3
+ Version: 6.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
@@ -267,13 +267,13 @@ data-validation
267
267
  [--case-insensitive-match, -cim]
268
268
  Performs a case insensitive match by adding an UPPER() before comparison.
269
269
  ```
270
- #### Generate Table Partitions for Large Table Row Validations
270
+ #### Generate Partitions for Large Row Validations
271
271
 
272
- When performing row validations, Data Validation Tool brings each row into memory and can run into MemoryError. Below is the command syntax for generating table partitions in order to perform row validations on large tables to alleviate MemoryError. Each partition contains a range of primary key(s) and the ranges of keys across partitions are distinct. The partitions have nearly equal number of rows. See *Primary Keys* section
272
+ When performing row validations, Data Validation Tool brings each row into memory and can run into MemoryError. Below is the command syntax for generating partitions in order to perform row validations on large dataset (table or custom-query) to alleviate MemoryError. Each partition contains a range of primary key(s) and the ranges of keys across partitions are distinct. The partitions have nearly equal number of rows. See *Primary Keys* section
273
273
 
274
- The command generates and stores multiple YAML validations each representing a chunk of the large table using filters (`WHERE primary_key(s) >= X AND primary_key(s) < Y`) in one YAML file. The parameter parts-per-file, specifies the number of validations in one YAML file. Each yaml file will have parts-per-file validations in it - except the last one which will contain the remaining partitions (i.e. parts-per-file may not divide partition-num evenly). You can then run the validations in the directory serially (or in parallel in multiple containers, VMs) with the `data-validation configs run --config-dir PATH` command as described [here](https://github.com/GoogleCloudPlatform/professional-services-data-validator#yaml-configuration-files).
274
+ The command generates and stores multiple YAML validations each representing a chunk of the large dataset using filters (`WHERE primary_key(s) >= X AND primary_key(s) < Y`) in YAML files. The parameter parts-per-file, specifies the number of validations in one YAML file. Each yaml file will have parts-per-file validations in it - except the last one which will contain the remaining partitions (i.e. parts-per-file may not divide partition-num evenly). You can then run the validations in the directory serially (or in parallel in multiple containers, VMs) with the `data-validation configs run --config-dir PATH` command as described [here](https://github.com/GoogleCloudPlatform/professional-services-data-validator#yaml-configuration-files).
275
275
 
276
- The command takes the same parameters as required for `Row Validation` *plus* a few parameters to support partitioning. Single and multiple primary keys are supported and keys can be of any indexable type, except for date and timestamp type. A parameter used in earlier versions, ```partition-key``` is no longer supported.
276
+ The command takes the same parameters as required for `Row Validation` *plus* a few parameters to support partitioning. Single and multiple primary keys are supported and keys can be of any indexable type, except for date and timestamp type. You can specify tables that are being validated or the source and target custom query. A parameter used in earlier versions, ```partition-key``` is no longer supported.
277
277
 
278
278
  ```
279
279
  data-validation
@@ -292,6 +292,17 @@ data-validation
292
292
  Comma separated list of tables in the form schema.table=target_schema.target_table
293
293
  Target schema name and table name are optional.
294
294
  i.e 'bigquery-public-data.new_york_citibike.citibike_trips'
295
+ Either --tables-list or --source-query (or file) and --target-query (or file) must be provided
296
+ --source-query SOURCE_QUERY, -sq SOURCE_QUERY
297
+ Source sql query
298
+ Either --tables-list or --source-query (or file) and --target-query (or file) must be provided
299
+ --source-query-file SOURCE_QUERY_FILE, -sqf SOURCE_QUERY_FILE
300
+ File containing the source sql command. Supports GCS and local paths.
301
+ --target-query TARGET_QUERY, -tq TARGET_QUERY
302
+ Target sql query
303
+ Either --tables-list or --source-query (or file) and --target-query (or file) must be provided
304
+ --target-query-file TARGET_QUERY_FILE, -tqf TARGET_QUERY_FILE
305
+ File containing the target sql command. Supports GCS and local paths.
295
306
  --primary-keys PRIMARY_KEYS, -pk PRIMARY_KEYS
296
307
  Comma separated list of primary key columns 'col_a,col_b'. See *Primary Keys* section
297
308
  --comparison-fields or -comp-fields FIELDS
@@ -303,6 +314,7 @@ data-validation
303
314
  Directory Path to store YAML Config Files
304
315
  GCS: Provide a full gs:// path of the target directory. Eg: `gs://<BUCKET>/partitions_dir`
305
316
  Local: Provide a relative path of the target directory. Eg: `partitions_dir`
317
+ If invoked with -tbls parameter, the validations are stored in a directory named <schema>.<table>, otherwise the directory is named `custom.<random_string>`
306
318
  --partition-num INT, -pn INT
307
319
  Number of partitions into which the table should be split, e.g. 1000 or 10000
308
320
  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)
@@ -581,10 +593,10 @@ View the complete YAML file for a Grouped Column validation on the
581
593
 
582
594
  ### Scaling DVT
583
595
 
584
- 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.
596
+ You can scale DVT for large 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.
585
597
 
586
598
 
587
- 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.
599
+ We recommend first generating partitions with the `generate-table-partitions` command for your large datasets (tables or queries). Then, use Cloud Run or GKE to distribute the validation of 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.
588
600
 
589
601
  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.
590
602
 
@@ -719,10 +731,15 @@ resultset that breaks down the count of rows per calendar date.
719
731
  Row level validations can involve either a hash/checksum, concat, or comparison fields.
720
732
  A hash validation (`--hash '*'`) will first sanitize the data with the following
721
733
  operations on all or selected columns: CAST to string, IFNULL replace with a default
722
- replacement string, RSTRIP, and UPPER. Then, it will CONCAT() the results
723
- and run a SHA256() hash and compare the source and target results. Since each row will
724
- be returned in the result set, it is recommended to utilize the `--use-random-row` feature
725
- to validate a subset of the table.
734
+ replacement string and RSTRIP. Then, it will CONCAT() the results
735
+ and run a SHA256() hash and compare the source and target results.
736
+
737
+ When there are data type mismatches for columns, for example dates compared to timestamps and
738
+ booleans compared with numeric columns, you may see other expressions in SQL statements which
739
+ ensure that consistent values are used to build comparison values.
740
+
741
+ Since each row will be returned in the result set if is recommended recommended to validate a
742
+ subset of the table. The `--filters` and `--use-random-row` options can be used for this purpose.
726
743
 
727
744
  Please note that SHA256 is not a supported function on Teradata systems. If you wish to perform
728
745
  this comparison on Teradata you will need to [deploy a UDF to perform the conversion](https://github.com/akuroda/teradata-udf-sha2/blob/master/src/sha256.c).
@@ -226,13 +226,13 @@ data-validation
226
226
  [--case-insensitive-match, -cim]
227
227
  Performs a case insensitive match by adding an UPPER() before comparison.
228
228
  ```
229
- #### Generate Table Partitions for Large Table Row Validations
229
+ #### Generate Partitions for Large Row Validations
230
230
 
231
- When performing row validations, Data Validation Tool brings each row into memory and can run into MemoryError. Below is the command syntax for generating table partitions in order to perform row validations on large tables to alleviate MemoryError. Each partition contains a range of primary key(s) and the ranges of keys across partitions are distinct. The partitions have nearly equal number of rows. See *Primary Keys* section
231
+ When performing row validations, Data Validation Tool brings each row into memory and can run into MemoryError. Below is the command syntax for generating partitions in order to perform row validations on large dataset (table or custom-query) to alleviate MemoryError. Each partition contains a range of primary key(s) and the ranges of keys across partitions are distinct. The partitions have nearly equal number of rows. See *Primary Keys* section
232
232
 
233
- The command generates and stores multiple YAML validations each representing a chunk of the large table using filters (`WHERE primary_key(s) >= X AND primary_key(s) < Y`) in one YAML file. The parameter parts-per-file, specifies the number of validations in one YAML file. Each yaml file will have parts-per-file validations in it - except the last one which will contain the remaining partitions (i.e. parts-per-file may not divide partition-num evenly). You can then run the validations in the directory serially (or in parallel in multiple containers, VMs) with the `data-validation configs run --config-dir PATH` command as described [here](https://github.com/GoogleCloudPlatform/professional-services-data-validator#yaml-configuration-files).
233
+ The command generates and stores multiple YAML validations each representing a chunk of the large dataset using filters (`WHERE primary_key(s) >= X AND primary_key(s) < Y`) in YAML files. The parameter parts-per-file, specifies the number of validations in one YAML file. Each yaml file will have parts-per-file validations in it - except the last one which will contain the remaining partitions (i.e. parts-per-file may not divide partition-num evenly). You can then run the validations in the directory serially (or in parallel in multiple containers, VMs) with the `data-validation configs run --config-dir PATH` command as described [here](https://github.com/GoogleCloudPlatform/professional-services-data-validator#yaml-configuration-files).
234
234
 
235
- The command takes the same parameters as required for `Row Validation` *plus* a few parameters to support partitioning. Single and multiple primary keys are supported and keys can be of any indexable type, except for date and timestamp type. A parameter used in earlier versions, ```partition-key``` is no longer supported.
235
+ The command takes the same parameters as required for `Row Validation` *plus* a few parameters to support partitioning. Single and multiple primary keys are supported and keys can be of any indexable type, except for date and timestamp type. You can specify tables that are being validated or the source and target custom query. A parameter used in earlier versions, ```partition-key``` is no longer supported.
236
236
 
237
237
  ```
238
238
  data-validation
@@ -251,6 +251,17 @@ data-validation
251
251
  Comma separated list of tables in the form schema.table=target_schema.target_table
252
252
  Target schema name and table name are optional.
253
253
  i.e 'bigquery-public-data.new_york_citibike.citibike_trips'
254
+ Either --tables-list or --source-query (or file) and --target-query (or file) must be provided
255
+ --source-query SOURCE_QUERY, -sq SOURCE_QUERY
256
+ Source sql query
257
+ Either --tables-list or --source-query (or file) and --target-query (or file) must be provided
258
+ --source-query-file SOURCE_QUERY_FILE, -sqf SOURCE_QUERY_FILE
259
+ File containing the source sql command. Supports GCS and local paths.
260
+ --target-query TARGET_QUERY, -tq TARGET_QUERY
261
+ Target sql query
262
+ Either --tables-list or --source-query (or file) and --target-query (or file) must be provided
263
+ --target-query-file TARGET_QUERY_FILE, -tqf TARGET_QUERY_FILE
264
+ File containing the target sql command. Supports GCS and local paths.
254
265
  --primary-keys PRIMARY_KEYS, -pk PRIMARY_KEYS
255
266
  Comma separated list of primary key columns 'col_a,col_b'. See *Primary Keys* section
256
267
  --comparison-fields or -comp-fields FIELDS
@@ -262,6 +273,7 @@ data-validation
262
273
  Directory Path to store YAML Config Files
263
274
  GCS: Provide a full gs:// path of the target directory. Eg: `gs://<BUCKET>/partitions_dir`
264
275
  Local: Provide a relative path of the target directory. Eg: `partitions_dir`
276
+ If invoked with -tbls parameter, the validations are stored in a directory named <schema>.<table>, otherwise the directory is named `custom.<random_string>`
265
277
  --partition-num INT, -pn INT
266
278
  Number of partitions into which the table should be split, e.g. 1000 or 10000
267
279
  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)
@@ -540,10 +552,10 @@ View the complete YAML file for a Grouped Column validation on the
540
552
 
541
553
  ### Scaling DVT
542
554
 
543
- 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.
555
+ You can scale DVT for large 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.
544
556
 
545
557
 
546
- 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.
558
+ We recommend first generating partitions with the `generate-table-partitions` command for your large datasets (tables or queries). Then, use Cloud Run or GKE to distribute the validation of 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.
547
559
 
548
560
  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.
549
561
 
@@ -678,10 +690,15 @@ resultset that breaks down the count of rows per calendar date.
678
690
  Row level validations can involve either a hash/checksum, concat, or comparison fields.
679
691
  A hash validation (`--hash '*'`) will first sanitize the data with the following
680
692
  operations on all or selected columns: CAST to string, IFNULL replace with a default
681
- replacement string, RSTRIP, and UPPER. Then, it will CONCAT() the results
682
- and run a SHA256() hash and compare the source and target results. Since each row will
683
- be returned in the result set, it is recommended to utilize the `--use-random-row` feature
684
- to validate a subset of the table.
693
+ replacement string and RSTRIP. Then, it will CONCAT() the results
694
+ and run a SHA256() hash and compare the source and target results.
695
+
696
+ When there are data type mismatches for columns, for example dates compared to timestamps and
697
+ booleans compared with numeric columns, you may see other expressions in SQL statements which
698
+ ensure that consistent values are used to build comparison values.
699
+
700
+ Since each row will be returned in the result set if is recommended recommended to validate a
701
+ subset of the table. The `--filters` and `--use-random-row` options can be used for this purpose.
685
702
 
686
703
  Please note that SHA256 is not a supported function on Teradata systems. If you wish to perform
687
704
  this comparison on Teradata you will need to [deploy a UDF to perform the conversion](https://github.com/akuroda/teradata-udf-sha2/blob/master/src/sha256.c).
@@ -24,7 +24,9 @@ from data_validation import (
24
24
  cli_tools,
25
25
  clients,
26
26
  consts,
27
+ exceptions,
27
28
  state_manager,
29
+ util,
28
30
  )
29
31
  from data_validation.config_manager import ConfigManager
30
32
  from data_validation.data_validation import DataValidation
@@ -160,7 +162,7 @@ def get_aggregate_config(args, config_manager: ConfigManager):
160
162
  return aggregate_configs
161
163
 
162
164
 
163
- def get_calculated_config(args, config_manager: ConfigManager) -> List[dict]:
165
+ def _get_calculated_config(args, config_manager: ConfigManager) -> List[dict]:
164
166
  """Return list of formatted calculated objects.
165
167
 
166
168
  Args:
@@ -217,8 +219,41 @@ def get_calculated_config(args, config_manager: ConfigManager) -> List[dict]:
217
219
  return calculated_configs
218
220
 
219
221
 
222
+ def _get_comparison_config(args, config_manager: ConfigManager) -> List[dict]:
223
+ col_list = (
224
+ None
225
+ if args.comparison_fields == "*"
226
+ else cli_tools.get_arg_list(args.comparison_fields)
227
+ )
228
+ comparison_fields = config_manager.build_comp_fields(
229
+ col_list,
230
+ args.exclude_columns,
231
+ )
232
+ # We can't have the PK columns in the comparison SQL twice therefore filter them out here if included.
233
+ comparison_fields = [
234
+ _
235
+ for _ in comparison_fields
236
+ if _ not in cli_tools.get_arg_list(args.primary_keys.casefold())
237
+ ]
238
+
239
+ # As per #1190, add rstrip for Teradata string comparison fields
240
+ if (
241
+ config_manager.source_client.name == "teradata"
242
+ or config_manager.target_client.name == "teradata"
243
+ ):
244
+ comparison_fields = config_manager.add_rstrip_to_comp_fields(comparison_fields)
245
+
246
+ return config_manager.build_config_comparison_fields(comparison_fields)
247
+
248
+
220
249
  def build_config_from_args(args: Namespace, config_manager: ConfigManager):
221
- """Append build configs to ConfigManager object.
250
+ """This function is used to append build configs to the config manager for all validation commands and generate-table-partitions.
251
+ Instead of having two separate commands, e.g. validate row and validate custom-query row, generate-table-partitions
252
+ uses implicit choice of table or custom-query. A user can specify either tables or source/target query/file,
253
+ but not both. In the case of generate-table-partitions with custom query, the user will not provide
254
+ args.custom_query_type. However, the code will inject args.custom_query_type as 'row' before invoking
255
+ build_config_from_args.
256
+
222
257
 
223
258
  Args:
224
259
  args (Namespace): User specified Arguments
@@ -227,14 +262,14 @@ def build_config_from_args(args: Namespace, config_manager: ConfigManager):
227
262
 
228
263
  # Append SCHEMA_VALIDATION configs
229
264
  if config_manager.validation_type == consts.SCHEMA_VALIDATION:
230
- if args.exclusion_columns is not None:
265
+ if args.exclusion_columns:
231
266
  exclusion_columns = cli_tools.get_arg_list(args.exclusion_columns)
232
267
  config_manager.append_exclusion_columns(
233
268
  [col.casefold() for col in exclusion_columns]
234
269
  )
235
270
  config_manager.append_allow_list(args.allow_list, args.allow_list_file)
236
271
 
237
- # Append CUSTOM_QUERY configs
272
+ # Append configs specific to CUSTOM_QUERY (i.e. query strings or strings from files)
238
273
  if config_manager.validation_type == consts.CUSTOM_QUERY:
239
274
  config_manager.append_custom_query_type(args.custom_query_type)
240
275
 
@@ -252,75 +287,37 @@ def build_config_from_args(args: Namespace, config_manager: ConfigManager):
252
287
  )
253
288
  )
254
289
 
255
- # For custom-query column command
256
- if args.custom_query_type == consts.COLUMN_VALIDATION.lower():
257
- config_manager.append_aggregates(get_aggregate_config(args, config_manager))
258
-
259
- # For custom-query row command
260
- if args.custom_query_type == consts.ROW_VALIDATION.lower():
261
- # Append Comparison fields
262
- if args.comparison_fields is not None:
263
- comparison_fields = cli_tools.get_arg_list(
264
- args.comparison_fields, default_value=[]
265
- )
266
-
267
- # As per #1190, add rstrip for Teradata string comparison fields
268
- if (
269
- config_manager.source_client.name == "teradata"
270
- or config_manager.target_client.name == "teradata"
271
- ):
272
- comparison_fields = config_manager.add_rstrip_to_comp_fields(
273
- comparison_fields
274
- )
275
-
276
- config_manager.append_comparison_fields(
277
- config_manager.build_config_comparison_fields(comparison_fields)
278
- )
279
-
280
- # Append calculated fields: --hash/--concat
281
- config_manager.append_calculated_fields(
282
- get_calculated_config(args, config_manager)
283
- )
284
-
285
- # Append primary_keys
286
- primary_keys = cli_tools.get_arg_list(args.primary_keys)
287
- config_manager.append_primary_keys(
288
- config_manager.build_column_configs(primary_keys)
289
- )
290
-
291
- # Append COLUMN_VALIDATION configs
292
- if config_manager.validation_type == consts.COLUMN_VALIDATION:
290
+ # Append COLUMN_VALIDATION configs, including custom-query column validation
291
+ if (
292
+ config_manager.validation_type == consts.COLUMN_VALIDATION
293
+ or config_manager.validation_type == consts.CUSTOM_QUERY
294
+ and args.custom_query_type == consts.COLUMN_VALIDATION.lower()
295
+ ):
293
296
  config_manager.append_aggregates(get_aggregate_config(args, config_manager))
294
- if args.grouped_columns is not None:
297
+ if (
298
+ config_manager.validation_type == consts.COLUMN_VALIDATION
299
+ and args.grouped_columns # grouped_columns not supported in custom queries - at least now.
300
+ ):
295
301
  grouped_columns = cli_tools.get_arg_list(args.grouped_columns)
296
302
  config_manager.append_query_groups(
297
303
  config_manager.build_column_configs(grouped_columns)
298
304
  )
299
305
 
300
- # Append ROW_VALIDATION configs
301
- if config_manager.validation_type == consts.ROW_VALIDATION:
306
+ # Append ROW_VALIDATION configs, including custom-query row validation
307
+ if (
308
+ config_manager.validation_type == consts.ROW_VALIDATION
309
+ or config_manager.validation_type == consts.CUSTOM_QUERY
310
+ and args.custom_query_type == consts.ROW_VALIDATION.lower()
311
+ ):
302
312
  # Append calculated fields: --hash/--concat
303
313
  config_manager.append_calculated_fields(
304
- get_calculated_config(args, config_manager)
314
+ _get_calculated_config(args, config_manager)
305
315
  )
306
316
 
307
317
  # Append Comparison fields
308
- if args.comparison_fields is not None:
309
- comparison_fields = cli_tools.get_arg_list(
310
- args.comparison_fields, default_value=[]
311
- )
312
-
313
- # As per #1190, add rstrip for Teradata string comparison fields
314
- if (
315
- config_manager.source_client.name == "teradata"
316
- or config_manager.target_client.name == "teradata"
317
- ):
318
- comparison_fields = config_manager.add_rstrip_to_comp_fields(
319
- comparison_fields
320
- )
321
-
318
+ if args.comparison_fields:
322
319
  config_manager.append_comparison_fields(
323
- config_manager.build_config_comparison_fields(comparison_fields)
320
+ _get_comparison_config(args, config_manager)
324
321
  )
325
322
 
326
323
  # Append primary_keys
@@ -336,22 +333,25 @@ def build_config_managers_from_args(
336
333
  args: Namespace, validate_cmd: str = None
337
334
  ) -> List[ConfigManager]:
338
335
  """Return a list of config managers ready to execute."""
339
- configs = []
340
336
 
341
- # Get pre build configs to build ConfigManager objects
342
- pre_build_configs_list = cli_tools.get_pre_build_configs(args, validate_cmd)
337
+ def _build_configs():
338
+ configs = []
339
+
340
+ # Get pre build configs to build ConfigManager objects
341
+ pre_build_configs_list = cli_tools.get_pre_build_configs(args, validate_cmd)
343
342
 
344
- # Build a list of ConfigManager objects
345
- for pre_build_configs in pre_build_configs_list:
346
- config_manager = ConfigManager.build_config_manager(**pre_build_configs)
343
+ # Build a list of ConfigManager objects
344
+ for pre_build_configs in pre_build_configs_list:
345
+ config_manager = ConfigManager.build_config_manager(**pre_build_configs)
347
346
 
348
- # Append post build configs to ConfigManager object
349
- config_manager = build_config_from_args(args, config_manager)
347
+ # Append post build configs to ConfigManager object
348
+ config_manager = build_config_from_args(args, config_manager)
350
349
 
351
- # Append ConfigManager object to configs list
352
- configs.append(config_manager)
350
+ # Append ConfigManager object to configs list
351
+ configs.append(config_manager)
352
+ return configs
353
353
 
354
- return configs
354
+ return util.timed_call("Build config", _build_configs)
355
355
 
356
356
 
357
357
  def config_runner(args):
@@ -393,9 +393,26 @@ def config_runner(args):
393
393
  )
394
394
  config_file_names = cli_tools.list_validations(config_dir=args.config_dir)
395
395
  config_managers = []
396
+ errors = False
396
397
  for file in config_file_names:
397
398
  config_managers = build_config_managers_from_yaml(args, file)
398
- run_validations(args, config_managers)
399
+ try:
400
+ logging.info(
401
+ "Currently running the validation for YAML file: %s",
402
+ file,
403
+ )
404
+ run_validations(args, config_managers)
405
+ except Exception as e:
406
+ errors = True
407
+ logging.error(
408
+ "Error '%s' occurred while running config file %s. Skipping it for now.",
409
+ str(e),
410
+ file,
411
+ )
412
+ if errors:
413
+ raise exceptions.ValidationException(
414
+ "Some of the validations raised an exception"
415
+ )
399
416
  else:
400
417
  if args.kube_completions:
401
418
  logging.warning(
@@ -548,23 +565,7 @@ def run_validations(args, config_managers):
548
565
  """
549
566
  # TODO(issue/31): Add parallel execution logic
550
567
  for config_manager in config_managers:
551
- if config_manager.config and consts.CONFIG_FILE in config_manager.config:
552
- logging.info(
553
- "Currently running the validation for YAML file: %s",
554
- config_manager.config[consts.CONFIG_FILE],
555
- )
556
- try:
557
- run_validation(
558
- config_manager, dry_run=args.dry_run, verbose=args.verbose
559
- )
560
- except Exception as e:
561
- logging.error(
562
- "Error %s occurred while running config file %s. Skipping it for now.",
563
- str(e),
564
- config_manager.config[consts.CONFIG_FILE],
565
- )
566
- else:
567
- run_validation(config_manager, dry_run=args.dry_run, verbose=args.verbose)
568
+ run_validation(config_manager, dry_run=args.dry_run, verbose=args.verbose)
568
569
 
569
570
 
570
571
  def store_yaml_config_file(args, config_managers):
@@ -599,7 +600,11 @@ def partition_and_store_config_files(args: Namespace) -> None:
599
600
  None
600
601
  """
601
602
  # Default Validate Type
602
- config_managers = build_config_managers_from_args(args, consts.ROW_VALIDATION)
603
+ if args.tables_list:
604
+ config_managers = build_config_managers_from_args(args, consts.ROW_VALIDATION)
605
+ else:
606
+ setattr(args, "custom_query_type", "row")
607
+ config_managers = build_config_managers_from_args(args, consts.CUSTOM_QUERY)
603
608
  partition_builder = PartitionBuilder(config_managers, args)
604
609
  partition_builder.partition_configs()
605
610