google-pso-data-validator 7.6.0__tar.gz → 7.6.1__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-7.6.0/google_pso_data_validator.egg-info → google_pso_data_validator-7.6.1}/PKG-INFO +1 -1
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/combiner.py +12 -12
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/config_manager.py +19 -13
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/consts.py +17 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/query_builder/query_builder.py +9 -1
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/validation_builder.py +63 -11
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1/google_pso_data_validator.egg-info}/PKG-INFO +1 -1
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/setup.py +1 -1
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_addon/operations.py +19 -1
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_postgres/client.py +7 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_postgres/registry.py +9 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/LICENSE +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/README.md +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/__init__.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/__main__.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/app.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/cli_tools.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/client_info.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/clients.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/data_validation.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/exceptions.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/find_tables.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/gcs_helper.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/jellyfish_distance.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/metadata.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/partition_builder.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/query_builder/__init__.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/query_builder/partition_row_builder.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/query_builder/random_row_builder.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/raw_query.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/result_handlers/__init__.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/result_handlers/base_backend.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/result_handlers/bigquery.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/result_handlers/factory.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/result_handlers/postgres.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/result_handlers/text.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/schema_validation.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/secret_manager.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/state_manager.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/util.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/google_pso_data_validator.egg-info/SOURCES.txt +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/google_pso_data_validator.egg-info/dependency_links.txt +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/google_pso_data_validator.egg-info/entry_points.txt +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/google_pso_data_validator.egg-info/requires.txt +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/google_pso_data_validator.egg-info/top_level.txt +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/setup.cfg +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_addon/__init__.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_addon/api.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_biquery/__init__.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_biquery/api.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_cloud_spanner/__init__.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_cloud_spanner/api.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_cloud_spanner/client.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_cloud_spanner/compiler.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_cloud_spanner/datatypes.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_cloud_spanner/registry.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_cloud_spanner/tests/__init__.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_cloud_spanner/tests/conftest.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_cloud_spanner/to_pandas.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_db2/__init__.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_db2/api.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_db2/compiler.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_db2/datatypes.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_db2/registry.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_impala/__init__.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_impala/api.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_mssql/__init__.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_mssql/api.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_mssql/datatypes.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_mssql/registry.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_mysql/__init__.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_mysql/base_sql_compiler/__init__.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_mysql/base_sql_compiler/select_builder.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_mysql/compiler.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_oracle/__init__.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_oracle/api.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_oracle/compiler.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_oracle/datatypes.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_oracle/registry.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_postgres/__init__.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_postgres/datatypes.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_redshift/__init__.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_redshift/api.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_redshift/compiler.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_snowflake/__init__.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_snowflake/api.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_snowflake/datatypes.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_teradata/__init__.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_teradata/api.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_teradata/compiler.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_teradata/datatypes.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/third_party/ibis/ibis_teradata/registry.py +0 -0
{google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/combiner.py
RENAMED
|
@@ -406,18 +406,18 @@ def _pivot_result(
|
|
|
406
406
|
consts.AGGREGATION_TYPE
|
|
407
407
|
),
|
|
408
408
|
ibis.literal(validation.get_table_name(result_type)).name(
|
|
409
|
-
|
|
409
|
+
consts.COMBINER_TABLE_NAME
|
|
410
410
|
),
|
|
411
411
|
# Cast to string to ensure types match, even when column
|
|
412
412
|
# name is NULL (such as for count aggregations).
|
|
413
413
|
ibis.literal(validation.get_column_name(result_type))
|
|
414
414
|
.cast("string")
|
|
415
|
-
.name(
|
|
415
|
+
.name(consts.COMBINER_COLUMN_NAME),
|
|
416
416
|
primary_keys,
|
|
417
417
|
ibis.literal(validation.num_random_rows).name(
|
|
418
418
|
consts.NUM_RANDOM_ROWS
|
|
419
419
|
),
|
|
420
|
-
result[field].cast("string").name(
|
|
420
|
+
result[field].cast("string").name(consts.COMBINER_AGG_VALUE),
|
|
421
421
|
)
|
|
422
422
|
+ join_on_fields
|
|
423
423
|
)
|
|
@@ -469,11 +469,11 @@ def _join_pivots(
|
|
|
469
469
|
+ [
|
|
470
470
|
source[consts.VALIDATION_TYPE],
|
|
471
471
|
source[consts.AGGREGATION_TYPE],
|
|
472
|
-
source[consts.
|
|
473
|
-
source[
|
|
472
|
+
source[consts.COMBINER_TABLE_NAME],
|
|
473
|
+
source[consts.COMBINER_COLUMN_NAME],
|
|
474
474
|
source[consts.CONFIG_PRIMARY_KEYS],
|
|
475
475
|
source[consts.NUM_RANDOM_ROWS],
|
|
476
|
-
source[
|
|
476
|
+
source[consts.COMBINER_AGG_VALUE],
|
|
477
477
|
differences[consts.VALIDATION_DIFFERENCE],
|
|
478
478
|
differences[consts.VALIDATION_PCT_DIFFERENCE],
|
|
479
479
|
differences[consts.VALIDATION_PCT_THRESHOLD],
|
|
@@ -488,12 +488,12 @@ def _join_pivots(
|
|
|
488
488
|
source_difference[consts.AGGREGATION_TYPE]
|
|
489
489
|
.fillna(target[consts.AGGREGATION_TYPE])
|
|
490
490
|
.name(consts.AGGREGATION_TYPE),
|
|
491
|
-
source_difference[
|
|
492
|
-
source_difference[
|
|
493
|
-
source_difference[
|
|
494
|
-
target[
|
|
495
|
-
target[
|
|
496
|
-
target[
|
|
491
|
+
source_difference[consts.COMBINER_TABLE_NAME].name(consts.SOURCE_TABLE_NAME),
|
|
492
|
+
source_difference[consts.COMBINER_COLUMN_NAME].name(consts.SOURCE_COLUMN_NAME),
|
|
493
|
+
source_difference[consts.COMBINER_AGG_VALUE].name(consts.SOURCE_AGG_VALUE),
|
|
494
|
+
target[consts.COMBINER_TABLE_NAME].name(consts.TARGET_TABLE_NAME),
|
|
495
|
+
target[consts.COMBINER_COLUMN_NAME].name(consts.TARGET_COLUMN_NAME),
|
|
496
|
+
target[consts.COMBINER_AGG_VALUE].name(consts.TARGET_AGG_VALUE),
|
|
497
497
|
group_by_columns,
|
|
498
498
|
source_difference[consts.CONFIG_PRIMARY_KEYS],
|
|
499
499
|
source_difference[consts.NUM_RANDOM_ROWS],
|
|
@@ -611,7 +611,7 @@ class ConfigManager(object):
|
|
|
611
611
|
self.build_config_calculated_fields(
|
|
612
612
|
[casefold_source_columns[field.casefold()]],
|
|
613
613
|
[casefold_target_columns[field.casefold()]],
|
|
614
|
-
|
|
614
|
+
consts.CALC_FIELD_RSTRIP,
|
|
615
615
|
alias,
|
|
616
616
|
0,
|
|
617
617
|
)
|
|
@@ -812,16 +812,16 @@ class ConfigManager(object):
|
|
|
812
812
|
consts.CONFIG_FIELD_ALIAS
|
|
813
813
|
]
|
|
814
814
|
depth = 1
|
|
815
|
-
calc_func =
|
|
815
|
+
calc_func = consts.CALC_FIELD_LENGTH
|
|
816
816
|
elif column_type in ["string", "!string"]:
|
|
817
|
-
calc_func =
|
|
817
|
+
calc_func = consts.CALC_FIELD_LENGTH
|
|
818
818
|
|
|
819
819
|
elif self._is_uuid(column_type, target_column_type):
|
|
820
820
|
calc_func = consts.CONFIG_CAST
|
|
821
821
|
cast_type = consts.CONFIG_CAST_UUID_STRING
|
|
822
822
|
|
|
823
823
|
elif column_type in ["binary", "!binary"]:
|
|
824
|
-
calc_func =
|
|
824
|
+
calc_func = consts.CALC_FIELD_BYTE_LENGTH
|
|
825
825
|
|
|
826
826
|
elif column_type in ["timestamp", "!timestamp", "date", "!date"]:
|
|
827
827
|
if (
|
|
@@ -841,7 +841,7 @@ class ConfigManager(object):
|
|
|
841
841
|
]
|
|
842
842
|
depth = 1
|
|
843
843
|
|
|
844
|
-
calc_func =
|
|
844
|
+
calc_func = consts.CALC_FIELD_EPOCH_SECONDS
|
|
845
845
|
if agg_type == consts.CONFIG_TYPE_SUM:
|
|
846
846
|
# It is possible to exceed int64 when summing epoch_seconds therefore cast to string.
|
|
847
847
|
# See issue 1391 for details.
|
|
@@ -1138,7 +1138,7 @@ class ConfigManager(object):
|
|
|
1138
1138
|
target_table: "ibis.expr.types.Table",
|
|
1139
1139
|
) -> dict:
|
|
1140
1140
|
"""Mutates col_config to contain any overrides. Also returns col_config for convenience."""
|
|
1141
|
-
if col_config["calc_type"] !=
|
|
1141
|
+
if col_config["calc_type"] != consts.CALC_FIELD_CAST:
|
|
1142
1142
|
return col_config
|
|
1143
1143
|
|
|
1144
1144
|
source_table_schema = {k: v for k, v in source_table.schema().items()}
|
|
@@ -1186,13 +1186,19 @@ class ConfigManager(object):
|
|
|
1186
1186
|
|
|
1187
1187
|
def _get_order_of_operations(self, calc_type: str) -> List[str]:
|
|
1188
1188
|
"""Return order of operations for row validation."""
|
|
1189
|
-
order_of_operations = [
|
|
1189
|
+
order_of_operations = [
|
|
1190
|
+
consts.CALC_FIELD_CAST,
|
|
1191
|
+
consts.CALC_FIELD_IFNULL,
|
|
1192
|
+
consts.CALC_FIELD_RSTRIP,
|
|
1193
|
+
]
|
|
1190
1194
|
if self.case_insensitive_match():
|
|
1191
|
-
order_of_operations.append(
|
|
1192
|
-
if calc_type ==
|
|
1193
|
-
order_of_operations.extend(
|
|
1194
|
-
|
|
1195
|
-
|
|
1195
|
+
order_of_operations.append(consts.CALC_FIELD_UPPER)
|
|
1196
|
+
if calc_type == consts.CALC_FIELD_HASH:
|
|
1197
|
+
order_of_operations.extend(
|
|
1198
|
+
[consts.CALC_FIELD_CONCAT, consts.CALC_FIELD_HASH]
|
|
1199
|
+
)
|
|
1200
|
+
elif calc_type == consts.CALC_FIELD_CONCAT:
|
|
1201
|
+
order_of_operations.append(consts.CALC_FIELD_CONCAT)
|
|
1196
1202
|
|
|
1197
1203
|
return order_of_operations
|
|
1198
1204
|
|
|
@@ -1241,7 +1247,7 @@ class ConfigManager(object):
|
|
|
1241
1247
|
previous_level = [x for x in casefold_source_columns.keys()]
|
|
1242
1248
|
else:
|
|
1243
1249
|
previous_level = [k for k, v in column_aliases.items() if v == i - 1]
|
|
1244
|
-
if calc in [
|
|
1250
|
+
if calc in [consts.CALC_FIELD_CONCAT, consts.CALC_FIELD_HASH]:
|
|
1245
1251
|
col = {}
|
|
1246
1252
|
col["source_reference"] = previous_level
|
|
1247
1253
|
col["target_reference"] = previous_level
|
{google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/consts.py
RENAMED
|
@@ -181,6 +181,11 @@ FAILED_SOURCE_NOT_IN_TARGET = "failed_rows_present_in_source_not_in_target"
|
|
|
181
181
|
FAILED_TARGET_NOT_IN_SOURCE = "failed_rows_present_in_target_not_in_source"
|
|
182
182
|
FAILED_PRESENT_IN_BOTH_TABLES = "failed_rows_present_in_both_source_and_target"
|
|
183
183
|
|
|
184
|
+
# Combiner only constants
|
|
185
|
+
COMBINER_TABLE_NAME = "dvt_table_name"
|
|
186
|
+
COMBINER_COLUMN_NAME = "dvt_column_name"
|
|
187
|
+
COMBINER_AGG_VALUE = "dvt_agg_value"
|
|
188
|
+
|
|
184
189
|
# SQL Template Formatting
|
|
185
190
|
# TODO: should this be managed in query_builder if that is the only place its used?
|
|
186
191
|
COUNT_STAR = "{count_star}"
|
|
@@ -239,3 +244,15 @@ MAX_CONCAT_COLUMNS_DEFAULTS = {
|
|
|
239
244
|
# Minimizing risk of: [Error 3556] Too many columns defined for this table.
|
|
240
245
|
"teradata": 500,
|
|
241
246
|
}
|
|
247
|
+
|
|
248
|
+
# CalculatedField expression constants.
|
|
249
|
+
CALC_FIELD_CAST = "cast"
|
|
250
|
+
CALC_FIELD_CONCAT = "concat"
|
|
251
|
+
CALC_FIELD_BYTE_LENGTH = "byte_length"
|
|
252
|
+
CALC_FIELD_EPOCH_SECONDS = "epoch_seconds"
|
|
253
|
+
CALC_FIELD_HASH = "hash"
|
|
254
|
+
CALC_FIELD_IFNULL = "ifnull"
|
|
255
|
+
CALC_FIELD_LENGTH = "length"
|
|
256
|
+
CALC_FIELD_PADDED_CHAR_LENGTH = "padded_char_length"
|
|
257
|
+
CALC_FIELD_RSTRIP = "rstrip"
|
|
258
|
+
CALC_FIELD_UPPER = "upper"
|
|
@@ -358,6 +358,14 @@ class CalculatedField(object):
|
|
|
358
358
|
fields,
|
|
359
359
|
)
|
|
360
360
|
|
|
361
|
+
@staticmethod
|
|
362
|
+
def padded_char_length(config, fields):
|
|
363
|
+
return CalculatedField(
|
|
364
|
+
ibis.expr.types.StringValue.padded_char_length,
|
|
365
|
+
config,
|
|
366
|
+
fields,
|
|
367
|
+
)
|
|
368
|
+
|
|
361
369
|
@staticmethod
|
|
362
370
|
def byte_length(config, fields):
|
|
363
371
|
return CalculatedField(
|
|
@@ -403,7 +411,7 @@ class CalculatedField(object):
|
|
|
403
411
|
@staticmethod
|
|
404
412
|
def custom(config, fields):
|
|
405
413
|
"""Returns a CalculatedField instance built for any custom ibis expression
|
|
406
|
-
|
|
414
|
+
e.g. 'ibis.expr.api.StringValue.replace'. For a list of supported functions,
|
|
407
415
|
see https://github.com/ibis-project/ibis/blob/1.4.0/ibis/expr/api.py
|
|
408
416
|
Args:
|
|
409
417
|
expr (Str): A custom ibis expression to be used as a calc field
|
|
@@ -11,8 +11,9 @@
|
|
|
11
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
|
-
import logging
|
|
15
14
|
from copy import deepcopy
|
|
15
|
+
import logging
|
|
16
|
+
from typing import TYPE_CHECKING
|
|
16
17
|
|
|
17
18
|
from data_validation import consts, metadata
|
|
18
19
|
from data_validation.clients import get_max_in_list_size
|
|
@@ -25,6 +26,9 @@ from data_validation.query_builder.query_builder import (
|
|
|
25
26
|
QueryBuilder,
|
|
26
27
|
)
|
|
27
28
|
|
|
29
|
+
if TYPE_CHECKING:
|
|
30
|
+
import ibis
|
|
31
|
+
|
|
28
32
|
|
|
29
33
|
def list_to_sublists(id_list: list, max_size: int) -> list:
|
|
30
34
|
"""Return a list of items as a list of lists based on a max list length of max_size."""
|
|
@@ -87,6 +91,23 @@ class ValidationBuilder(object):
|
|
|
87
91
|
else:
|
|
88
92
|
return FilterField.isin(column_name, in_list)
|
|
89
93
|
|
|
94
|
+
def _is_padded_char(
|
|
95
|
+
self,
|
|
96
|
+
client: "ibis.backends.base.BaseBackend",
|
|
97
|
+
raw_column_metadata: dict,
|
|
98
|
+
column_name: str,
|
|
99
|
+
) -> bool:
|
|
100
|
+
# Clients only need to implement _is_char_type_padded method if they support padded strings.
|
|
101
|
+
return (
|
|
102
|
+
(
|
|
103
|
+
client.is_char_type_padded(raw_column_metadata[column_name])
|
|
104
|
+
if raw_column_metadata.get(column_name)
|
|
105
|
+
else False
|
|
106
|
+
)
|
|
107
|
+
if hasattr(client, "is_char_type_padded") and raw_column_metadata
|
|
108
|
+
else False
|
|
109
|
+
)
|
|
110
|
+
|
|
90
111
|
def clone(self):
|
|
91
112
|
cloned_builder = ValidationBuilder(self.config_manager)
|
|
92
113
|
|
|
@@ -347,32 +368,63 @@ class ValidationBuilder(object):
|
|
|
347
368
|
threshold=self.config_manager.threshold,
|
|
348
369
|
)
|
|
349
370
|
|
|
350
|
-
def
|
|
371
|
+
def _get_calc_type(
|
|
372
|
+
self,
|
|
373
|
+
calc_field: dict,
|
|
374
|
+
column_name: str,
|
|
375
|
+
client: "ibis.backends.base.BaseBackend",
|
|
376
|
+
raw_data_types: dict,
|
|
377
|
+
) -> str:
|
|
378
|
+
if calc_field[
|
|
379
|
+
consts.CONFIG_TYPE
|
|
380
|
+
] == consts.CALC_FIELD_LENGTH and self._is_padded_char(
|
|
381
|
+
client,
|
|
382
|
+
raw_data_types,
|
|
383
|
+
column_name,
|
|
384
|
+
):
|
|
385
|
+
calc_type = consts.CALC_FIELD_PADDED_CHAR_LENGTH
|
|
386
|
+
else:
|
|
387
|
+
calc_type = calc_field[consts.CONFIG_TYPE]
|
|
388
|
+
# Check if valid calc field and return correct object.
|
|
389
|
+
if not hasattr(CalculatedField, calc_type):
|
|
390
|
+
raise Exception("Unknown Calculation Type: {}".format(calc_type))
|
|
391
|
+
return calc_type
|
|
392
|
+
|
|
393
|
+
def add_calc(self, calc_field: dict):
|
|
351
394
|
"""Add CalculatedField to Queries
|
|
352
395
|
|
|
353
396
|
Args:
|
|
354
397
|
calc_field (Dict): An object with source, target, and cast info
|
|
355
398
|
"""
|
|
356
|
-
#
|
|
399
|
+
# Prepare source and target payloads
|
|
357
400
|
source_config = deepcopy(calc_field)
|
|
358
401
|
source_fields = calc_field[consts.CONFIG_CALCULATED_SOURCE_COLUMNS]
|
|
359
402
|
target_config = deepcopy(calc_field)
|
|
360
403
|
target_fields = calc_field[consts.CONFIG_CALCULATED_TARGET_COLUMNS]
|
|
361
|
-
#
|
|
404
|
+
# Grab calc field metadata
|
|
362
405
|
alias = calc_field[consts.CONFIG_FIELD_ALIAS]
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
406
|
+
source_calc_type = self._get_calc_type(
|
|
407
|
+
calc_field,
|
|
408
|
+
source_fields[0],
|
|
409
|
+
self.source_client,
|
|
410
|
+
self.config_manager.get_source_raw_data_types(),
|
|
411
|
+
)
|
|
412
|
+
target_calc_type = self._get_calc_type(
|
|
413
|
+
calc_field,
|
|
414
|
+
target_fields[0],
|
|
415
|
+
self.target_client,
|
|
416
|
+
self.config_manager.get_target_raw_data_types(),
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
source_field = getattr(CalculatedField, source_calc_type)(
|
|
368
420
|
config=source_config, fields=source_fields
|
|
369
421
|
)
|
|
370
|
-
target_field = getattr(CalculatedField,
|
|
422
|
+
target_field = getattr(CalculatedField, target_calc_type)(
|
|
371
423
|
config=target_config, fields=target_fields
|
|
372
424
|
)
|
|
373
425
|
self.source_builder.add_calculated_field(source_field)
|
|
374
426
|
self.target_builder.add_calculated_field(target_field)
|
|
375
|
-
#
|
|
427
|
+
# Register calc field under alias
|
|
376
428
|
self.calculated_aliases[alias] = calc_field
|
|
377
429
|
|
|
378
430
|
def get_source_query(self):
|
|
@@ -58,7 +58,7 @@ from ibis.backends.mysql.compiler import MySQLExprTranslator
|
|
|
58
58
|
from ibis.backends.pandas.dispatch import execute_node
|
|
59
59
|
from ibis.backends.pandas.execution.temporal import execute_epoch_seconds
|
|
60
60
|
from ibis.backends.postgres.compiler import PostgreSQLExprTranslator
|
|
61
|
-
from ibis.expr.types import BinaryValue, NumericValue, TemporalValue
|
|
61
|
+
from ibis.expr.types import BinaryValue, NumericValue, StringValue, TemporalValue
|
|
62
62
|
|
|
63
63
|
# Do not remove these lines, they trigger patching of Ibis code.
|
|
64
64
|
import third_party.ibis.ibis_biquery.api # noqa
|
|
@@ -106,6 +106,12 @@ class BinaryLength(ops.Value):
|
|
|
106
106
|
output_shape = rlz.shape_like("arg")
|
|
107
107
|
|
|
108
108
|
|
|
109
|
+
class PaddedCharLength(ops.Value):
|
|
110
|
+
arg = rlz.one_of([rlz.value(dt.String)])
|
|
111
|
+
output_dtype = dt.int32
|
|
112
|
+
output_shape = rlz.shape_like("arg")
|
|
113
|
+
|
|
114
|
+
|
|
109
115
|
class ToChar(ops.Value):
|
|
110
116
|
arg = rlz.one_of(
|
|
111
117
|
[
|
|
@@ -128,6 +134,10 @@ def compile_binary_length(binary_value):
|
|
|
128
134
|
return BinaryLength(binary_value).to_expr()
|
|
129
135
|
|
|
130
136
|
|
|
137
|
+
def compile_padded_char_length(char_value):
|
|
138
|
+
return PaddedCharLength(char_value).to_expr()
|
|
139
|
+
|
|
140
|
+
|
|
131
141
|
def compile_to_char(numeric_value, fmt):
|
|
132
142
|
return ToChar(numeric_value, fmt=fmt).to_expr()
|
|
133
143
|
|
|
@@ -598,6 +608,8 @@ execute_epoch_seconds = execute_epoch_seconds_new
|
|
|
598
608
|
|
|
599
609
|
BinaryValue.byte_length = compile_binary_length
|
|
600
610
|
|
|
611
|
+
StringValue.padded_char_length = compile_padded_char_length
|
|
612
|
+
|
|
601
613
|
NumericValue.to_char = compile_to_char
|
|
602
614
|
TemporalValue.to_char = compile_to_char
|
|
603
615
|
|
|
@@ -614,6 +626,8 @@ AlchemyExprTranslator._registry[RawSQL] = format_raw_sql
|
|
|
614
626
|
AlchemyExprTranslator._registry[ops.HashBytes] = format_hashbytes_alchemy
|
|
615
627
|
ExprTranslator._registry[RawSQL] = format_raw_sql
|
|
616
628
|
ExprTranslator._registry[ops.HashBytes] = format_hashbytes_base
|
|
629
|
+
# Base length of padded string is the same as for a standard string.
|
|
630
|
+
ExprTranslator._registry[PaddedCharLength] = ExprTranslator._registry[ops.StringLength]
|
|
617
631
|
|
|
618
632
|
ImpalaExprTranslator._registry[ops.Cast] = sa_cast_hive
|
|
619
633
|
ImpalaExprTranslator._registry[RawSQL] = format_raw_sql
|
|
@@ -639,6 +653,10 @@ PostgreSQLExprTranslator._registry[BinaryLength] = sa_format_binary_length
|
|
|
639
653
|
PostgreSQLExprTranslator._registry[
|
|
640
654
|
ops.ExtractEpochSeconds
|
|
641
655
|
] = postgres_registry.sa_epoch_seconds
|
|
656
|
+
PostgreSQLExprTranslator._registry[
|
|
657
|
+
PaddedCharLength
|
|
658
|
+
] = postgres_registry.sa_format_postgres_padded_char_length
|
|
659
|
+
|
|
642
660
|
|
|
643
661
|
MsSqlExprTranslator._registry[ops.HashBytes] = mssql_registry.sa_format_hashbytes
|
|
644
662
|
MsSqlExprTranslator._registry[RawSQL] = sa_format_raw_sql
|
|
@@ -207,8 +207,15 @@ def _raw_column_metadata(
|
|
|
207
207
|
)
|
|
208
208
|
|
|
209
209
|
|
|
210
|
+
def _is_char_type_padded(self, char_type: Tuple) -> bool:
|
|
211
|
+
"""Define this method if the backend supports character/string types that are padded and returns
|
|
212
|
+
padded values, which DVT may want to trim"""
|
|
213
|
+
return char_type[0] == "character"
|
|
214
|
+
|
|
215
|
+
|
|
210
216
|
PostgresBackend._metadata = _metadata
|
|
211
217
|
PostgresBackend.list_databases = list_schemas
|
|
212
218
|
PostgresBackend.do_connect = do_connect
|
|
213
219
|
PostgresBackend.list_primary_key_columns = _list_primary_key_columns
|
|
214
220
|
PostgresBackend.raw_column_metadata = _raw_column_metadata
|
|
221
|
+
PostgresBackend.is_char_type_padded = _is_char_type_padded
|
|
@@ -32,3 +32,12 @@ def sa_epoch_seconds(translator, op):
|
|
|
32
32
|
sa.extract("epoch", sa.func.date_trunc(sa.sql.literal_column("'second'"), arg)),
|
|
33
33
|
sa.BIGINT,
|
|
34
34
|
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def sa_format_postgres_padded_char_length(translator, op):
|
|
38
|
+
"""Inject concat of empty string into length(bpchar) to protect trailing spaces.
|
|
39
|
+
|
|
40
|
+
Without this workaround the bpchar value is implicitly cast to varchar and loses trailing spaces.
|
|
41
|
+
"""
|
|
42
|
+
arg = translator.translate(op.arg)
|
|
43
|
+
return sa.func.char_length(sa.func.concat(arg, sa.text("''")))
|
|
File without changes
|
|
File without changes
|
{google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/__init__.py
RENAMED
|
File without changes
|
{google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/__main__.py
RENAMED
|
File without changes
|
|
File without changes
|
{google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/cli_tools.py
RENAMED
|
File without changes
|
{google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/client_info.py
RENAMED
|
File without changes
|
{google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/clients.py
RENAMED
|
File without changes
|
|
File without changes
|
{google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/exceptions.py
RENAMED
|
File without changes
|
{google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/find_tables.py
RENAMED
|
File without changes
|
{google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/gcs_helper.py
RENAMED
|
File without changes
|
|
File without changes
|
{google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/metadata.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/data_validation/raw_query.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-7.6.0 → google_pso_data_validator-7.6.1}/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
|
|
File without changes
|
|
File without changes
|
{google_pso_data_validator-7.6.0 → google_pso_data_validator-7.6.1}/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
|