google-pso-data-validator 6.3.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.
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/PKG-INFO +1 -1
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/data_validation/__main__.py +37 -31
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/data_validation/cli_tools.py +26 -13
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/data_validation/config_manager.py +8 -1
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/data_validation/consts.py +7 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/data_validation/data_validation.py +21 -7
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/data_validation/exceptions.py +5 -1
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/data_validation/metadata.py +1 -3
- google_pso_data_validator-6.4.0/data_validation/util.py +24 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/google_pso_data_validator.egg-info/PKG-INFO +1 -1
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/google_pso_data_validator.egg-info/SOURCES.txt +1 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/setup.py +1 -1
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_oracle/__init__.py +10 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/LICENSE +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/README.md +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/data_validation/__init__.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/data_validation/app.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/data_validation/client_info.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/data_validation/clients.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/data_validation/combiner.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/data_validation/find_tables.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/data_validation/gcs_helper.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/data_validation/jellyfish_distance.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/data_validation/partition_builder.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/data_validation/query_builder/__init__.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/data_validation/query_builder/partition_row_builder.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/data_validation/query_builder/query_builder.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/data_validation/query_builder/random_row_builder.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/data_validation/result_handlers/__init__.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/data_validation/result_handlers/bigquery.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/data_validation/result_handlers/text.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/data_validation/schema_validation.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/data_validation/secret_manager.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/data_validation/state_manager.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/data_validation/validation_builder.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/google_pso_data_validator.egg-info/dependency_links.txt +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/google_pso_data_validator.egg-info/entry_points.txt +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/google_pso_data_validator.egg-info/requires.txt +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/google_pso_data_validator.egg-info/top_level.txt +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/setup.cfg +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_addon/__init__.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_addon/api.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_addon/operations.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_cloud_spanner/__init__.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_cloud_spanner/api.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_cloud_spanner/client.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_cloud_spanner/compiler.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_cloud_spanner/datatypes.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_cloud_spanner/registry.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_cloud_spanner/tests/__init__.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_cloud_spanner/tests/conftest.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_cloud_spanner/to_pandas.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_db2/__init__.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_db2/api.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_db2/compiler.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_db2/datatypes.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_db2/registry.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_impala/__init__.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_impala/api.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_mssql/__init__.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_mssql/api.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_mssql/datatypes.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_mssql/registry.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_mysql/__init__.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_mysql/base_sql_compiler/__init__.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_mysql/base_sql_compiler/select_builder.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_mysql/compiler.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_oracle/api.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_oracle/compiler.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_oracle/datatypes.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_oracle/registry.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_postgres/__init__.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_postgres/client.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_postgres/datatypes.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_redshift/__init__.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_redshift/api.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_redshift/compiler.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_snowflake/__init__.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_snowflake/api.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_snowflake/datatypes.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_teradata/__init__.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_teradata/api.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_teradata/compiler.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_teradata/datatypes.py +0 -0
- {google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/third_party/ibis/ibis_teradata/registry.py +0 -0
{google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/data_validation/__main__.py
RENAMED
|
@@ -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
|
|
@@ -220,8 +222,8 @@ def _get_calculated_config(args, config_manager: ConfigManager) -> List[dict]:
|
|
|
220
222
|
def _get_comparison_config(args, config_manager: ConfigManager) -> List[dict]:
|
|
221
223
|
col_list = (
|
|
222
224
|
None
|
|
223
|
-
if
|
|
224
|
-
else cli_tools.get_arg_list(
|
|
225
|
+
if args.comparison_fields == "*"
|
|
226
|
+
else cli_tools.get_arg_list(args.comparison_fields)
|
|
225
227
|
)
|
|
226
228
|
comparison_fields = config_manager.build_comp_fields(
|
|
227
229
|
col_list,
|
|
@@ -331,22 +333,25 @@ def build_config_managers_from_args(
|
|
|
331
333
|
args: Namespace, validate_cmd: str = None
|
|
332
334
|
) -> List[ConfigManager]:
|
|
333
335
|
"""Return a list of config managers ready to execute."""
|
|
334
|
-
configs = []
|
|
335
336
|
|
|
336
|
-
|
|
337
|
-
|
|
337
|
+
def _build_configs():
|
|
338
|
+
configs = []
|
|
338
339
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
config_manager = ConfigManager.build_config_manager(**pre_build_configs)
|
|
340
|
+
# Get pre build configs to build ConfigManager objects
|
|
341
|
+
pre_build_configs_list = cli_tools.get_pre_build_configs(args, validate_cmd)
|
|
342
342
|
|
|
343
|
-
#
|
|
344
|
-
|
|
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)
|
|
345
346
|
|
|
346
|
-
|
|
347
|
-
|
|
347
|
+
# Append post build configs to ConfigManager object
|
|
348
|
+
config_manager = build_config_from_args(args, config_manager)
|
|
348
349
|
|
|
349
|
-
|
|
350
|
+
# Append ConfigManager object to configs list
|
|
351
|
+
configs.append(config_manager)
|
|
352
|
+
return configs
|
|
353
|
+
|
|
354
|
+
return util.timed_call("Build config", _build_configs)
|
|
350
355
|
|
|
351
356
|
|
|
352
357
|
def config_runner(args):
|
|
@@ -388,9 +393,26 @@ def config_runner(args):
|
|
|
388
393
|
)
|
|
389
394
|
config_file_names = cli_tools.list_validations(config_dir=args.config_dir)
|
|
390
395
|
config_managers = []
|
|
396
|
+
errors = False
|
|
391
397
|
for file in config_file_names:
|
|
392
398
|
config_managers = build_config_managers_from_yaml(args, file)
|
|
393
|
-
|
|
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
|
+
)
|
|
394
416
|
else:
|
|
395
417
|
if args.kube_completions:
|
|
396
418
|
logging.warning(
|
|
@@ -543,23 +565,7 @@ def run_validations(args, config_managers):
|
|
|
543
565
|
"""
|
|
544
566
|
# TODO(issue/31): Add parallel execution logic
|
|
545
567
|
for config_manager in config_managers:
|
|
546
|
-
|
|
547
|
-
logging.info(
|
|
548
|
-
"Currently running the validation for YAML file: %s",
|
|
549
|
-
config_manager.config[consts.CONFIG_FILE],
|
|
550
|
-
)
|
|
551
|
-
try:
|
|
552
|
-
run_validation(
|
|
553
|
-
config_manager, dry_run=args.dry_run, verbose=args.verbose
|
|
554
|
-
)
|
|
555
|
-
except Exception as e:
|
|
556
|
-
logging.error(
|
|
557
|
-
"Error %s occurred while running config file %s. Skipping it for now.",
|
|
558
|
-
str(e),
|
|
559
|
-
config_manager.config[consts.CONFIG_FILE],
|
|
560
|
-
)
|
|
561
|
-
else:
|
|
562
|
-
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)
|
|
563
569
|
|
|
564
570
|
|
|
565
571
|
def store_yaml_config_file(args, config_managers):
|
{google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/data_validation/cli_tools.py
RENAMED
|
@@ -990,6 +990,12 @@ def _add_common_arguments(
|
|
|
990
990
|
# TODO: update if we start to support other statuses
|
|
991
991
|
help="Comma separated list of statuses to filter the validation results. Supported statuses are (success, fail). If no list is provided, all statuses are returned",
|
|
992
992
|
)
|
|
993
|
+
optional_arguments.add_argument(
|
|
994
|
+
"--run-id",
|
|
995
|
+
"-rid",
|
|
996
|
+
default=None,
|
|
997
|
+
help="Set a string for the run_id, if None is input then a randomly generated UUID will be used, which is the default behaviour",
|
|
998
|
+
)
|
|
993
999
|
|
|
994
1000
|
|
|
995
1001
|
def _check_positive(value: int) -> int:
|
|
@@ -1407,8 +1413,8 @@ def get_pre_build_configs(args: Namespace, validate_cmd: str) -> List[Dict]:
|
|
|
1407
1413
|
result_handler_config = None
|
|
1408
1414
|
|
|
1409
1415
|
# Set filter_config and threshold. Not supported in case of schema validation
|
|
1410
|
-
filter_config = getattr(args,
|
|
1411
|
-
threshold = getattr(args,
|
|
1416
|
+
filter_config = getattr(args, consts.CONFIG_FILTERS, [])
|
|
1417
|
+
threshold = getattr(args, consts.CONFIG_THRESHOLD, 0.0)
|
|
1412
1418
|
|
|
1413
1419
|
# Get labels
|
|
1414
1420
|
if args.labels is None:
|
|
@@ -1425,8 +1431,10 @@ def get_pre_build_configs(args: Namespace, validate_cmd: str) -> List[Dict]:
|
|
|
1425
1431
|
format = args.format if args.format else "table"
|
|
1426
1432
|
|
|
1427
1433
|
# Get random row arguments. Only in row validations these attributes can be present.
|
|
1434
|
+
# Bad coding here, but keeping it so as not to introduce a breaking change. See
|
|
1435
|
+
# consts.py Line 17 for a more detailed explanation.
|
|
1428
1436
|
use_random_rows = getattr(args, "use_random_row", False)
|
|
1429
|
-
random_row_batch_size = getattr(args,
|
|
1437
|
+
random_row_batch_size = getattr(args, consts.CONFIG_RANDOM_ROW_BATCH_SIZE, None)
|
|
1430
1438
|
|
|
1431
1439
|
# Get table list. Not supported in case of custom query validation
|
|
1432
1440
|
is_filesystem = source_client._source_type == "FileSystem"
|
|
@@ -1459,23 +1467,28 @@ def get_pre_build_configs(args: Namespace, validate_cmd: str) -> List[Dict]:
|
|
|
1459
1467
|
for table_obj in tables_list:
|
|
1460
1468
|
pre_build_configs = {
|
|
1461
1469
|
"config_type": config_type,
|
|
1462
|
-
|
|
1463
|
-
|
|
1470
|
+
consts.CONFIG_SOURCE_CONN_NAME: args.source_conn,
|
|
1471
|
+
consts.CONFIG_TARGET_CONN_NAME: args.target_conn,
|
|
1464
1472
|
"table_obj": table_obj,
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1473
|
+
consts.CONFIG_LABELS: labels,
|
|
1474
|
+
consts.CONFIG_THRESHOLD: threshold,
|
|
1475
|
+
consts.CONFIG_FORMAT: format,
|
|
1476
|
+
consts.CONFIG_USE_RANDOM_ROWS: use_random_rows,
|
|
1477
|
+
consts.CONFIG_RANDOM_ROW_BATCH_SIZE: random_row_batch_size,
|
|
1470
1478
|
"source_client": source_client,
|
|
1471
1479
|
"target_client": target_client,
|
|
1472
1480
|
"result_handler_config": result_handler_config,
|
|
1473
1481
|
"filter_config": filter_config,
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1482
|
+
consts.CONFIG_FILTER_STATUS: filter_status,
|
|
1483
|
+
consts.CONFIG_TRIM_STRING_PKS: getattr(
|
|
1484
|
+
args, consts.CONFIG_TRIM_STRING_PKS, False
|
|
1485
|
+
),
|
|
1486
|
+
consts.CONFIG_CASE_INSENSITIVE_MATCH: getattr(
|
|
1487
|
+
args, consts.CONFIG_CASE_INSENSITIVE_MATCH, False
|
|
1488
|
+
),
|
|
1477
1489
|
consts.CONFIG_ROW_CONCAT: getattr(args, consts.CONFIG_ROW_CONCAT, None),
|
|
1478
1490
|
consts.CONFIG_ROW_HASH: getattr(args, consts.CONFIG_ROW_HASH, None),
|
|
1491
|
+
consts.CONFIG_RUN_ID: getattr(args, consts.CONFIG_RUN_ID, None),
|
|
1479
1492
|
"verbose": args.verbose,
|
|
1480
1493
|
}
|
|
1481
1494
|
if (
|
|
@@ -245,6 +245,11 @@ class ConfigManager(object):
|
|
|
245
245
|
"""Return field from Config"""
|
|
246
246
|
return self._config.get(consts.CONFIG_ROW_HASH, [])
|
|
247
247
|
|
|
248
|
+
@property
|
|
249
|
+
def run_id(self):
|
|
250
|
+
"""Return field from Config"""
|
|
251
|
+
return self._config.get(consts.CONFIG_RUN_ID, None)
|
|
252
|
+
|
|
248
253
|
@property
|
|
249
254
|
def filters(self):
|
|
250
255
|
"""Return Filters from Config"""
|
|
@@ -504,6 +509,7 @@ class ConfigManager(object):
|
|
|
504
509
|
case_insensitive_match=None,
|
|
505
510
|
concat=None,
|
|
506
511
|
hash=None,
|
|
512
|
+
run_id=None,
|
|
507
513
|
verbose=False,
|
|
508
514
|
):
|
|
509
515
|
if isinstance(filter_config, dict):
|
|
@@ -536,6 +542,7 @@ class ConfigManager(object):
|
|
|
536
542
|
consts.CONFIG_CASE_INSENSITIVE_MATCH: case_insensitive_match,
|
|
537
543
|
consts.CONFIG_ROW_CONCAT: concat,
|
|
538
544
|
consts.CONFIG_ROW_HASH: hash,
|
|
545
|
+
consts.CONFIG_RUN_ID: run_id,
|
|
539
546
|
}
|
|
540
547
|
|
|
541
548
|
return ConfigManager(
|
|
@@ -1160,7 +1167,7 @@ class ConfigManager(object):
|
|
|
1160
1167
|
col_names.append(col)
|
|
1161
1168
|
return col_names
|
|
1162
1169
|
|
|
1163
|
-
def build_comp_fields(self, col_list: list, exclude_cols: bool = False) ->
|
|
1170
|
+
def build_comp_fields(self, col_list: list, exclude_cols: bool = False) -> dict:
|
|
1164
1171
|
"""This is a utility function processing comp-fields values like we do for hash/concat."""
|
|
1165
1172
|
source_table = self.get_source_ibis_calculated_table()
|
|
1166
1173
|
casefold_source_columns = {_.casefold(): str(_) for _ in source_table.columns}
|
{google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/data_validation/consts.py
RENAMED
|
@@ -14,6 +14,12 @@
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
# Configuration Fields
|
|
17
|
+
# These are all keys into the config dict. Where these values come from the command line, these would
|
|
18
|
+
# match with the command line argument - so --secret-manager-type value would be inserted with the
|
|
19
|
+
# 'secret_manager_type' key.
|
|
20
|
+
# This is true for the most part, except where a typo gets introduced, i.e. --use-random-row became use_random_rows
|
|
21
|
+
# Fixing this would be a breaking change. A future cleanup to prevent this would be to use
|
|
22
|
+
# f'--{config_constant.repl('_','-')}' as the name argument to argparse.
|
|
17
23
|
SOURCE_TYPE = "source_type"
|
|
18
24
|
SECRET_MANAGER_TYPE = "secret_manager_type"
|
|
19
25
|
SECRET_MANAGER_PROJECT_ID = "secret_manager_project_id"
|
|
@@ -49,6 +55,7 @@ CONFIG_TRIM_STRING_PKS = "trim_string_pks"
|
|
|
49
55
|
CONFIG_CASE_INSENSITIVE_MATCH = "case_insensitive_match"
|
|
50
56
|
CONFIG_ROW_CONCAT = "concat"
|
|
51
57
|
CONFIG_ROW_HASH = "hash"
|
|
58
|
+
CONFIG_RUN_ID = "run_id"
|
|
52
59
|
CONFIG_SOURCE_COLUMN = "source_column"
|
|
53
60
|
CONFIG_TARGET_COLUMN = "target_column"
|
|
54
61
|
CONFIG_THRESHOLD = "threshold"
|
|
@@ -16,11 +16,11 @@ import json
|
|
|
16
16
|
import logging
|
|
17
17
|
import warnings
|
|
18
18
|
from concurrent.futures import ThreadPoolExecutor
|
|
19
|
-
|
|
20
19
|
import ibis.backends.pandas
|
|
21
20
|
import pandas
|
|
21
|
+
import uuid
|
|
22
22
|
|
|
23
|
-
from data_validation import combiner, consts, metadata
|
|
23
|
+
from data_validation import combiner, consts, metadata, util
|
|
24
24
|
from data_validation.config_manager import ConfigManager
|
|
25
25
|
from data_validation.query_builder.random_row_builder import RandomRowBuilder
|
|
26
26
|
from data_validation.schema_validation import SchemaValidation
|
|
@@ -63,6 +63,9 @@ class DataValidation(object):
|
|
|
63
63
|
self.run_metadata = metadata.RunMetadata()
|
|
64
64
|
self.run_metadata.labels = self.config_manager.labels
|
|
65
65
|
|
|
66
|
+
# Use a generated uuid for the run_id if None was supplied via config
|
|
67
|
+
self.run_metadata.run_id = self.config_manager.run_id or str(uuid.uuid4())
|
|
68
|
+
|
|
66
69
|
# Initialize Validation Builder if None was supplied
|
|
67
70
|
self.validation_builder = validation_builder or ValidationBuilder(
|
|
68
71
|
self.config_manager
|
|
@@ -88,7 +91,7 @@ class DataValidation(object):
|
|
|
88
91
|
"""Execute Queries and Store Results"""
|
|
89
92
|
# Apply random row filter before validations run
|
|
90
93
|
if self.config_manager.use_random_rows():
|
|
91
|
-
self._add_random_row_filter
|
|
94
|
+
util.timed_call("Random row filter", self._add_random_row_filter)
|
|
92
95
|
|
|
93
96
|
# Run correct execution for the given validation type
|
|
94
97
|
if self.config_manager.validation_type == consts.ROW_VALIDATION:
|
|
@@ -98,7 +101,9 @@ class DataValidation(object):
|
|
|
98
101
|
)
|
|
99
102
|
elif self.config_manager.validation_type == consts.SCHEMA_VALIDATION:
|
|
100
103
|
"""Perform only schema validation"""
|
|
101
|
-
result_df =
|
|
104
|
+
result_df = util.timed_call(
|
|
105
|
+
"Schema validation", self.schema_validator.execute
|
|
106
|
+
)
|
|
102
107
|
else:
|
|
103
108
|
result_df = self._execute_validation(
|
|
104
109
|
self.validation_builder, process_in_memory=True
|
|
@@ -289,6 +294,7 @@ class DataValidation(object):
|
|
|
289
294
|
|
|
290
295
|
def _execute_validation(self, validation_builder, process_in_memory=True):
|
|
291
296
|
"""Execute Against a Supplied Validation Builder"""
|
|
297
|
+
|
|
292
298
|
self.run_metadata.validations = validation_builder.get_metadata()
|
|
293
299
|
|
|
294
300
|
source_query = validation_builder.get_source_query()
|
|
@@ -319,12 +325,18 @@ class DataValidation(object):
|
|
|
319
325
|
# Submit the two query network calls concurrently
|
|
320
326
|
futures.append(
|
|
321
327
|
executor.submit(
|
|
322
|
-
|
|
328
|
+
util.timed_call,
|
|
329
|
+
"Source query",
|
|
330
|
+
self.config_manager.source_client.execute,
|
|
331
|
+
source_query,
|
|
323
332
|
)
|
|
324
333
|
)
|
|
325
334
|
futures.append(
|
|
326
335
|
executor.submit(
|
|
327
|
-
|
|
336
|
+
util.timed_call,
|
|
337
|
+
"Target query",
|
|
338
|
+
self.config_manager.target_client.execute,
|
|
339
|
+
target_query,
|
|
328
340
|
)
|
|
329
341
|
)
|
|
330
342
|
source_df = futures[0].result()
|
|
@@ -335,7 +347,9 @@ class DataValidation(object):
|
|
|
335
347
|
)
|
|
336
348
|
|
|
337
349
|
try:
|
|
338
|
-
result_df =
|
|
350
|
+
result_df = util.timed_call(
|
|
351
|
+
"Generate report",
|
|
352
|
+
combiner.generate_report,
|
|
339
353
|
pandas_client,
|
|
340
354
|
self.run_metadata,
|
|
341
355
|
pandas_client.table(combiner.DEFAULT_SOURCE),
|
{google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/data_validation/metadata.py
RENAMED
|
@@ -14,11 +14,9 @@
|
|
|
14
14
|
|
|
15
15
|
"""Metadata classes with data about the validation run."""
|
|
16
16
|
|
|
17
|
-
|
|
18
17
|
import dataclasses
|
|
19
18
|
import datetime
|
|
20
19
|
import typing
|
|
21
|
-
import uuid
|
|
22
20
|
|
|
23
21
|
from data_validation import consts
|
|
24
22
|
|
|
@@ -64,7 +62,7 @@ class ValidationMetadata(object):
|
|
|
64
62
|
|
|
65
63
|
@dataclasses.dataclass
|
|
66
64
|
class RunMetadata(object):
|
|
67
|
-
run_id: str = dataclasses.field(default_factory=
|
|
65
|
+
run_id: str = dataclasses.field(default_factory=str)
|
|
68
66
|
validations: dict = dataclasses.field(default_factory=dict)
|
|
69
67
|
labels: list = dataclasses.field(default_factory=list)
|
|
70
68
|
start_time: typing.Optional[datetime.datetime] = dataclasses.field(
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Copyright 2024 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
import logging
|
|
16
|
+
import time
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def timed_call(log_txt, fn, *args, **kwargs):
|
|
20
|
+
t0 = time.time()
|
|
21
|
+
result = fn(*args, **kwargs)
|
|
22
|
+
elapsed = time.time() - t0
|
|
23
|
+
logging.debug(f"{log_txt} elapsed: {round(elapsed,2)}s")
|
|
24
|
+
return result
|
|
@@ -21,6 +21,7 @@ data_validation/partition_builder.py
|
|
|
21
21
|
data_validation/schema_validation.py
|
|
22
22
|
data_validation/secret_manager.py
|
|
23
23
|
data_validation/state_manager.py
|
|
24
|
+
data_validation/util.py
|
|
24
25
|
data_validation/validation_builder.py
|
|
25
26
|
data_validation/query_builder/__init__.py
|
|
26
27
|
data_validation/query_builder/partition_row_builder.py
|
|
@@ -108,6 +108,16 @@ class Backend(BaseAlchemyBackend):
|
|
|
108
108
|
sa_url,
|
|
109
109
|
poolclass=sa.pool.StaticPool,
|
|
110
110
|
arraysize=self.arraysize,
|
|
111
|
+
# The hardcoding of 128 below is not great but is the simplest way of dealing with:
|
|
112
|
+
# https://github.com/GoogleCloudPlatform/professional-services-data-validator/issues/1250
|
|
113
|
+
# There is a quirk of SQLAlchemy behaviour that is discussed in comments of issue 1250 that
|
|
114
|
+
# we cannot easily deal with, even if we "fixed" it it could be months or years before we can
|
|
115
|
+
# take advantage of it.
|
|
116
|
+
# We manage the max length of identifiers generated by DVT in clients.get_max_column_length()
|
|
117
|
+
# and in all other cases re-use existing column names (i.e. we know the length is already fine).
|
|
118
|
+
# Therefore the ugly hardcoding of 128 kicks the can down the road and unblocks a customer
|
|
119
|
+
# who is working with Oracle 11g and a max identifier length of 30.
|
|
120
|
+
max_identifier_length=128,
|
|
111
121
|
)
|
|
112
122
|
try:
|
|
113
123
|
# Identify the session in Oracle as DVT, no-op if this fails.
|
|
File without changes
|
|
File without changes
|
{google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/data_validation/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/data_validation/client_info.py
RENAMED
|
File without changes
|
{google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/data_validation/clients.py
RENAMED
|
File without changes
|
{google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/data_validation/combiner.py
RENAMED
|
File without changes
|
{google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/data_validation/find_tables.py
RENAMED
|
File without changes
|
{google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.0}/data_validation/gcs_helper.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
|
{google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.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
|
|
File without changes
|
{google_pso_data_validator-6.3.0 → google_pso_data_validator-6.4.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
|
|
File without changes
|