google-pso-data-validator 8.3.0__tar.gz → 8.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. {google_pso_data_validator-8.3.0/google_pso_data_validator.egg-info → google_pso_data_validator-8.4.0}/PKG-INFO +5 -3
  2. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/README.md +2 -1
  3. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/data_validation/__main__.py +1 -0
  4. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/data_validation/cli_tools.py +26 -12
  5. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/data_validation/clients.py +42 -13
  6. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/data_validation/config_manager.py +2 -2
  7. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/data_validation/consts.py +5 -0
  8. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/data_validation/exceptions.py +4 -0
  9. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/data_validation/partition_builder.py +10 -1
  10. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/data_validation/query_builder/random_row_builder.py +2 -1
  11. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0/google_pso_data_validator.egg-info}/PKG-INFO +5 -3
  12. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/google_pso_data_validator.egg-info/SOURCES.txt +5 -0
  13. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/google_pso_data_validator.egg-info/requires.txt +2 -1
  14. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/setup.py +3 -2
  15. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_addon/operations.py +15 -0
  16. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_bigquery/__init__.py +20 -10
  17. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_mssql/registry.py +1 -1
  18. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_oracle/datatypes.py +1 -1
  19. google_pso_data_validator-8.4.0/third_party/ibis/ibis_sybase/__init__.py +186 -0
  20. google_pso_data_validator-8.4.0/third_party/ibis/ibis_sybase/api.py +42 -0
  21. google_pso_data_validator-8.4.0/third_party/ibis/ibis_sybase/compiler.py +101 -0
  22. google_pso_data_validator-8.4.0/third_party/ibis/ibis_sybase/datatypes.py +96 -0
  23. google_pso_data_validator-8.4.0/third_party/ibis/ibis_sybase/registry.py +209 -0
  24. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/LICENSE +0 -0
  25. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/data_validation/__init__.py +0 -0
  26. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/data_validation/app.py +0 -0
  27. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/data_validation/client_info.py +0 -0
  28. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/data_validation/combiner.py +0 -0
  29. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/data_validation/data_validation.py +0 -0
  30. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/data_validation/find_tables.py +0 -0
  31. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/data_validation/gcs_helper.py +0 -0
  32. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/data_validation/jellyfish_distance.py +0 -0
  33. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/data_validation/metadata.py +0 -0
  34. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/data_validation/query_builder/__init__.py +0 -0
  35. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/data_validation/query_builder/partition_row_builder.py +0 -0
  36. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/data_validation/query_builder/query_builder.py +0 -0
  37. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/data_validation/raw_query.py +0 -0
  38. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/data_validation/result_handlers/__init__.py +0 -0
  39. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/data_validation/result_handlers/base_backend.py +0 -0
  40. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/data_validation/result_handlers/bigquery.py +0 -0
  41. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/data_validation/result_handlers/factory.py +0 -0
  42. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/data_validation/result_handlers/postgres.py +0 -0
  43. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/data_validation/result_handlers/text.py +0 -0
  44. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/data_validation/schema_validation.py +0 -0
  45. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/data_validation/secret_manager.py +0 -0
  46. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/data_validation/state_manager.py +0 -0
  47. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/data_validation/util.py +0 -0
  48. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/data_validation/validation_builder.py +0 -0
  49. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/google_pso_data_validator.egg-info/dependency_links.txt +0 -0
  50. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/google_pso_data_validator.egg-info/entry_points.txt +0 -0
  51. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/google_pso_data_validator.egg-info/top_level.txt +0 -0
  52. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/setup.cfg +0 -0
  53. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_addon/__init__.py +0 -0
  54. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_addon/api.py +0 -0
  55. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_bigquery/api.py +0 -0
  56. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_bigquery/registry.py +0 -0
  57. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_cloud_spanner/__init__.py +0 -0
  58. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_cloud_spanner/api.py +0 -0
  59. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_cloud_spanner/client.py +0 -0
  60. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_cloud_spanner/compiler.py +0 -0
  61. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_cloud_spanner/datatypes.py +0 -0
  62. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_cloud_spanner/registry.py +0 -0
  63. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_cloud_spanner/tests/__init__.py +0 -0
  64. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_cloud_spanner/tests/conftest.py +0 -0
  65. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_cloud_spanner/to_pandas.py +0 -0
  66. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_db2/__init__.py +0 -0
  67. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_db2/api.py +0 -0
  68. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_db2/compiler.py +0 -0
  69. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_db2/datatypes.py +0 -0
  70. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_db2/registry.py +0 -0
  71. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_impala/__init__.py +0 -0
  72. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_impala/api.py +0 -0
  73. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_mssql/__init__.py +0 -0
  74. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_mssql/api.py +0 -0
  75. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_mssql/datatypes.py +0 -0
  76. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_mysql/__init__.py +0 -0
  77. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_mysql/base_sql_compiler/__init__.py +0 -0
  78. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_mysql/base_sql_compiler/select_builder.py +0 -0
  79. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_mysql/compiler.py +0 -0
  80. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_oracle/__init__.py +0 -0
  81. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_oracle/api.py +0 -0
  82. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_oracle/compiler.py +0 -0
  83. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_oracle/registry.py +0 -0
  84. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_postgres/__init__.py +0 -0
  85. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_postgres/client.py +0 -0
  86. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_postgres/datatypes.py +0 -0
  87. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_postgres/registry.py +0 -0
  88. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_redshift/__init__.py +0 -0
  89. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_redshift/api.py +0 -0
  90. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_redshift/compiler.py +0 -0
  91. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_snowflake/__init__.py +0 -0
  92. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_snowflake/api.py +0 -0
  93. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_snowflake/datatypes.py +0 -0
  94. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_teradata/__init__.py +0 -0
  95. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_teradata/api.py +0 -0
  96. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_teradata/compiler.py +0 -0
  97. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_teradata/datatypes.py +0 -0
  98. {google_pso_data_validator-8.3.0 → google_pso_data_validator-8.4.0}/third_party/ibis/ibis_teradata/registry.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: google-pso-data-validator
3
- Version: 8.3.0
3
+ Version: 8.4.0
4
4
  Summary: A package to enable easy data validation
5
5
  Author: PSO DVT Engineering team
6
6
  Author-email: data-validator-eng@google.com
@@ -28,7 +28,7 @@ Requires-Dist: jellyfish>=1.1.0
28
28
  Requires-Dist: pandas
29
29
  Requires-Dist: parsy>=2.1
30
30
  Requires-Dist: psycopg2-binary>=2.9.9
31
- Requires-Dist: pyarrow==14.0.1
31
+ Requires-Dist: pyarrow
32
32
  Requires-Dist: pydata-google-auth>=1.8.2
33
33
  Requires-Dist: PyMySQL>=1.1.1
34
34
  Requires-Dist: PyYAML>=6.0.2
@@ -45,6 +45,7 @@ Requires-Dist: freezegun; extra == "develop"
45
45
  Requires-Dist: pyfakefs; extra == "develop"
46
46
  Requires-Dist: pytest; extra == "develop"
47
47
  Requires-Dist: pytest-cov; extra == "develop"
48
+ Requires-Dist: pytest-rerunfailures; extra == "develop"
48
49
  Requires-Dist: pytest-timeout; extra == "develop"
49
50
  Dynamic: author
50
51
  Dynamic: author-email
@@ -92,8 +93,9 @@ DVT supports the following connection types:
92
93
  * [Postgres](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#postgres)
93
94
  * [Redshift](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#redshift)
94
95
  * [Spanner](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#google-spanner)
95
- * [Teradata](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#teradata)
96
96
  * [Snowflake](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#snowflake)
97
+ * [Sybase ASE](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#sybase)
98
+ * [Teradata](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#teradata)
97
99
 
98
100
  The [Connections](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md) page provides details about how to create
99
101
  and list connections for the validation tool.
@@ -33,8 +33,9 @@ DVT supports the following connection types:
33
33
  * [Postgres](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#postgres)
34
34
  * [Redshift](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#redshift)
35
35
  * [Spanner](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#google-spanner)
36
- * [Teradata](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#teradata)
37
36
  * [Snowflake](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#snowflake)
37
+ * [Sybase ASE](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#sybase)
38
+ * [Teradata](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#teradata)
38
39
 
39
40
  The [Connections](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md) page provides details about how to create
40
41
  and list connections for the validation tool.
@@ -596,6 +596,7 @@ def partition_and_store_config_files(args: "Namespace") -> None:
596
596
  setattr(args, "custom_query_type", "row")
597
597
  config_managers = build_config_managers_from_args(args, consts.CUSTOM_QUERY)
598
598
  partition_builder = PartitionBuilder(config_managers, args)
599
+ partition_builder.check_partition_configs()
599
600
  partition_builder.partition_configs()
600
601
 
601
602
 
@@ -73,6 +73,10 @@ if TYPE_CHECKING:
73
73
  CONNECTION_SOURCE_FIELDS = {
74
74
  consts.SOURCE_TYPE_BIGQUERY: [
75
75
  ["project_id", "GCP Project to use for BigQuery"],
76
+ [
77
+ "client_project_id",
78
+ "(Optional) BigQuery job/billing project (can differ from data project)",
79
+ ],
76
80
  ["google_service_account_key_path", "(Optional) GCP SA Key Path"],
77
81
  [
78
82
  "api_endpoint",
@@ -84,7 +88,7 @@ CONNECTION_SOURCE_FIELDS = {
84
88
  ],
85
89
  ],
86
90
  consts.SOURCE_TYPE_TERADATA: [
87
- ["host", "Desired Teradata host"],
91
+ ["host", "Teradata host"],
88
92
  ["port", "Teradata port to connect on"],
89
93
  ["user_name", "User used to connect"],
90
94
  ["password", "Password for supplied user"],
@@ -93,7 +97,7 @@ CONNECTION_SOURCE_FIELDS = {
93
97
  ["json_params", "(Optional) Additional teradatasql JSON string parameters"],
94
98
  ],
95
99
  consts.SOURCE_TYPE_ORACLE: [
96
- ["host", "Desired Oracle host"],
100
+ ["host", "Oracle host"],
97
101
  ["port", "Oracle port to connect on"],
98
102
  ["user", "User used to connect"],
99
103
  ["thick_mode", "Flag indicating thick_mode"],
@@ -103,7 +107,7 @@ CONNECTION_SOURCE_FIELDS = {
103
107
  ["connect_args", "(Optional) Additional connection argument mapping"],
104
108
  ],
105
109
  consts.SOURCE_TYPE_MSSQL: [
106
- ["host", "Desired SQL Server host (default localhost)"],
110
+ ["host", "SQL Server host (default localhost)"],
107
111
  ["port", "SQL Server port to connect on (default 1433)"],
108
112
  ["user", "User used to connect"],
109
113
  ["password", "Password for supplied user"],
@@ -112,7 +116,7 @@ CONNECTION_SOURCE_FIELDS = {
112
116
  ["url", "SQL Server SQLAlchemy connection URL"],
113
117
  ],
114
118
  consts.SOURCE_TYPE_MYSQL: [
115
- ["host", "Desired MySQL host (default localhost)"],
119
+ ["host", "MySQL host (default localhost)"],
116
120
  ["port", "MySQL port to connect on (default 3306)"],
117
121
  ["user", "User used to connect"],
118
122
  ["password", "Password for supplied user"],
@@ -126,14 +130,14 @@ CONNECTION_SOURCE_FIELDS = {
126
130
  ["connect_args", "(Optional) Additional connection argument mapping"],
127
131
  ],
128
132
  consts.SOURCE_TYPE_POSTGRES: [
129
- ["host", "Desired PostgreSQL host."],
133
+ ["host", "PostgreSQL host."],
130
134
  ["port", "PostgreSQL port to connect on (e.g. 5432)"],
131
135
  ["user", "Username to connect to"],
132
136
  ["password", "Password for authentication of user"],
133
137
  ["database", "Database in PostgreSQL to connect to (default postgres)"],
134
138
  ],
135
139
  consts.SOURCE_TYPE_REDSHIFT: [
136
- ["host", "Desired Redshift host."],
140
+ ["host", "Redshift host."],
137
141
  ["port", "Redshift port to connect on (e.g. 5439)"],
138
142
  ["user", "Username to connect to"],
139
143
  ["password", "Password for authentication of user"],
@@ -149,16 +153,26 @@ CONNECTION_SOURCE_FIELDS = {
149
153
  '(Optional) GCP Spanner API endpoint (e.g. "https://spanner-mypsc.p.googleapis.com")',
150
154
  ],
151
155
  ],
156
+ consts.SOURCE_TYPE_SYBASE: [
157
+ ["host", "Sybase host (default localhost)"],
158
+ ["port", "Sybase port to connect on (default 5000)"],
159
+ ["user", "User used to connect"],
160
+ ["password", "Password for supplied user"],
161
+ ["database", "Database to connect to (default master)"],
162
+ ["odbc_driver", "ODBC driver name"],
163
+ ["query", "Connection query parameters"],
164
+ ["url", "Sybase SQLAlchemy connection URL"],
165
+ ],
152
166
  consts.SOURCE_TYPE_FILESYSTEM: [
153
167
  ["table_name", "Table name to use as reference for file data"],
154
168
  ["file_path", "The local, s3, or GCS file path to the data"],
155
169
  ["file_type", "The file type of the file. 'csv', 'orc', 'parquet' or 'json'"],
156
170
  ],
157
171
  consts.SOURCE_TYPE_IMPALA: [
158
- ["host", "Desired Impala host"],
159
- ["port", "Desired Impala port (10000 if not provided)"],
160
- ["database", "Desired Impala database (default if not provided)"],
161
- ["auth_mechanism", "Desired Impala auth mechanism (PLAIN if not provided)"],
172
+ ["host", "Impala host"],
173
+ ["port", "Impala port (10000 if not provided)"],
174
+ ["database", "Impala database (default if not provided)"],
175
+ ["auth_mechanism", "Impala auth mechanism (PLAIN if not provided)"],
162
176
  [
163
177
  "kerberos_service_name",
164
178
  "Desired Kerberos service name ('impala' if not provided)",
@@ -183,8 +197,8 @@ CONNECTION_SOURCE_FIELDS = {
183
197
  ["http_path", "URL path of HTTP proxy"],
184
198
  ],
185
199
  consts.SOURCE_TYPE_DB2: [
186
- ["host", "Desired DB2 host"],
187
- ["port", "Desired DB2 port (50000 if not provided)"],
200
+ ["host", "DB2 host"],
201
+ ["port", "DB2 port (50000 if not provided)"],
188
202
  ["user", "Username to connect to"],
189
203
  ["password", "Password for authentication of user"],
190
204
  ["database", "Database in DB2 to connect to"],
@@ -16,7 +16,7 @@
16
16
  from contextlib import contextmanager
17
17
  import copy
18
18
  import logging
19
- from typing import TYPE_CHECKING
19
+ from typing import TYPE_CHECKING, Optional
20
20
  import warnings
21
21
 
22
22
  import google.oauth2.service_account
@@ -56,6 +56,7 @@ IBIS_ALCHEMY_BACKENDS = [
56
56
  "mssql",
57
57
  "redshift",
58
58
  "snowflake",
59
+ "sybase",
59
60
  ]
60
61
 
61
62
 
@@ -93,19 +94,32 @@ try:
93
94
  except ImportError:
94
95
  db2_connect = _raise_missing_client_error("pip install ibm_db_sa")
95
96
 
97
+ # Sybase requires sqlalchemy_sybase package.
98
+ try:
99
+ from third_party.ibis.ibis_sybase.api import sybase_connect
100
+ except ImportError:
101
+ sybase_connect = _raise_missing_client_error("pip install sqlalchemy_sybase")
102
+
96
103
 
97
104
  def get_google_bigquery_client(
98
- project_id: str, credentials=None, api_endpoint: str = None
105
+ project_id: str,
106
+ credentials=None,
107
+ api_endpoint: Optional[str] = None,
108
+ quota_project_id: Optional[str] = None,
99
109
  ):
100
110
  info = client_info.get_http_client_info()
101
111
  job_config = bigquery.QueryJobConfig(
102
112
  connection_properties=[bigquery.ConnectionProperty("time_zone", "UTC")]
103
113
  )
114
+ effective_project = quota_project_id or project_id
104
115
  options = None
105
- if api_endpoint:
106
- options = client_options.ClientOptions(api_endpoint=api_endpoint)
116
+ if api_endpoint or quota_project_id:
117
+ options = client_options.ClientOptions(
118
+ api_endpoint=api_endpoint,
119
+ quota_project_id=quota_project_id if quota_project_id else None,
120
+ )
107
121
  return bigquery.Client(
108
- project=project_id,
122
+ project=effective_project,
109
123
  client_info=info,
110
124
  credentials=credentials,
111
125
  default_query_job_config=job_config,
@@ -113,10 +127,17 @@ def get_google_bigquery_client(
113
127
  )
114
128
 
115
129
 
116
- def _get_google_bqstorage_client(credentials=None, api_endpoint: str = None):
130
+ def _get_google_bqstorage_client(
131
+ credentials=None,
132
+ api_endpoint: Optional[str] = None,
133
+ quota_project_id: Optional[str] = None,
134
+ ):
117
135
  options = None
118
- if api_endpoint:
119
- options = client_options.ClientOptions(api_endpoint=api_endpoint)
136
+ if api_endpoint or quota_project_id:
137
+ options = client_options.ClientOptions(
138
+ api_endpoint=api_endpoint,
139
+ quota_project_id=quota_project_id if quota_project_id else None,
140
+ )
120
141
  from google.cloud import bigquery_storage_v1 as bigquery_storage
121
142
 
122
143
  return bigquery_storage.BigQueryReadClient(
@@ -129,20 +150,26 @@ def get_bigquery_client(
129
150
  project_id: str,
130
151
  dataset_id: str = "",
131
152
  credentials=None,
132
- api_endpoint: str = None,
133
- storage_api_endpoint: str = None,
153
+ api_endpoint: Optional[str] = None,
154
+ storage_api_endpoint: Optional[str] = None,
155
+ client_project_id: Optional[str] = None,
134
156
  ):
135
157
  google_client = get_google_bigquery_client(
136
- project_id, credentials=credentials, api_endpoint=api_endpoint
158
+ project_id,
159
+ credentials=credentials,
160
+ api_endpoint=api_endpoint,
161
+ quota_project_id=client_project_id,
137
162
  )
138
163
  bqstorage_client = None
139
164
  if storage_api_endpoint:
140
165
  bqstorage_client = _get_google_bqstorage_client(
141
- credentials=credentials, api_endpoint=storage_api_endpoint
166
+ credentials=credentials,
167
+ api_endpoint=storage_api_endpoint,
168
+ quota_project_id=client_project_id,
142
169
  )
143
170
 
144
171
  return bigquery_connect(
145
- project_id=project_id,
172
+ project_id=project_id or client_project_id,
146
173
  dataset_id=dataset_id,
147
174
  credentials=credentials,
148
175
  bigquery_client=google_client,
@@ -202,6 +229,7 @@ def get_ibis_table(client, schema_name, table_name, database_name=None):
202
229
  "db2",
203
230
  "mssql",
204
231
  "redshift",
232
+ "sybase",
205
233
  ]:
206
234
  return client.table(table_name, database=database_name, schema=schema_name)
207
235
  elif client.name == "pandas":
@@ -400,5 +428,6 @@ CLIENT_LOOKUP = {
400
428
  consts.SOURCE_TYPE_MSSQL: mssql_connect,
401
429
  consts.SOURCE_TYPE_SNOWFLAKE: snowflake_connect,
402
430
  consts.SOURCE_TYPE_SPANNER: spanner_connect,
431
+ consts.SOURCE_TYPE_SYBASE: sybase_connect,
403
432
  consts.SOURCE_TYPE_DB2: db2_connect,
404
433
  }
@@ -71,7 +71,7 @@ class ConfigManager(object):
71
71
  """Return config object."""
72
72
  return self._config
73
73
 
74
- def get_source_connection(self):
74
+ def get_source_connection(self) -> dict:
75
75
  """Return source connection object."""
76
76
  if not self._source_conn:
77
77
  if self._config.get(consts.CONFIG_SOURCE_CONN):
@@ -82,7 +82,7 @@ class ConfigManager(object):
82
82
 
83
83
  return self._source_conn
84
84
 
85
- def get_target_connection(self):
85
+ def get_target_connection(self) -> dict:
86
86
  """Return target connection object."""
87
87
  if not self._target_conn:
88
88
  if self._config.get(consts.CONFIG_TARGET_CONN):
@@ -139,6 +139,7 @@ SOURCE_TYPE_POSTGRES = "Postgres"
139
139
  SOURCE_TYPE_REDSHIFT = "Redshift"
140
140
  SOURCE_TYPE_SNOWFLAKE = "Snowflake"
141
141
  SOURCE_TYPE_SPANNER = "Spanner"
142
+ SOURCE_TYPE_SYBASE = "Sybase"
142
143
  SOURCE_TYPE_TERADATA = "Teradata"
143
144
 
144
145
  # BigQuery Result Handler Configs
@@ -149,6 +150,7 @@ TABLE_ID = "table_id"
149
150
  GOOGLE_SERVICE_ACCOUNT_KEY_PATH = "google_service_account_key_path"
150
151
  API_ENDPOINT = "api_endpoint"
151
152
  STORAGE_API_ENDPOINT = "storage_api_endpoint"
153
+ CLIENT_PROJECT_ID = "client_project_id"
152
154
 
153
155
  # Result Handler Output Table Fields
154
156
  VALIDATION_TYPE = "validation_type"
@@ -262,3 +264,6 @@ CALC_FIELD_LENGTH = "length"
262
264
  CALC_FIELD_PADDED_CHAR_LENGTH = "padded_char_length"
263
265
  CALC_FIELD_RSTRIP = "rstrip"
264
266
  CALC_FIELD_UPPER = "upper"
267
+
268
+ # Backends that do not support window functions.
269
+ NO_WINDOW_FUNCTION_SUPPORT = [SOURCE_TYPE_SYBASE]
@@ -27,3 +27,7 @@ class ResultHandlerException(Exception):
27
27
 
28
28
  class SchemaValidationException(ValidationException):
29
29
  pass
30
+
31
+
32
+ class PartitionBuilderException(ValidationException):
33
+ pass
@@ -20,7 +20,7 @@ import re
20
20
  import datetime
21
21
  from typing import List, Dict, TYPE_CHECKING
22
22
 
23
- from data_validation import cli_tools, consts, util
23
+ from data_validation import cli_tools, consts, exceptions, util
24
24
  from data_validation.config_manager import ConfigManager
25
25
  from data_validation.query_builder.partition_row_builder import PartitionRowBuilder
26
26
  from data_validation.validation_builder import ValidationBuilder
@@ -105,6 +105,15 @@ class PartitionBuilder:
105
105
  yaml_configs_list = self._add_partition_filters(partition_filters)
106
106
  self._store_partitions(yaml_configs_list)
107
107
 
108
+ def check_partition_configs(self) -> None:
109
+ for config_manager in self.config_managers:
110
+ source_type = config_manager.get_source_connection()[consts.SOURCE_TYPE]
111
+ if source_type in consts.NO_WINDOW_FUNCTION_SUPPORT:
112
+ raise exceptions.PartitionBuilderException(
113
+ f"Source client {source_type} does not support window functions, "
114
+ "please use this connection as a target connection for generate-table-partitions."
115
+ )
116
+
108
117
  @staticmethod
109
118
  def _extract_where(table_expr: "IbisTable", client: "BaseBackend") -> str:
110
119
  """Given a ibis table expression with a filter (i.e. WHERE) clause, this function extracts the
@@ -30,9 +30,10 @@ RANDOM_SORT_SUPPORTS = [
30
30
  "mssql",
31
31
  "db2",
32
32
  "mysql",
33
- "spanner",
34
33
  "redshift",
35
34
  "snowflake",
35
+ "spanner",
36
+ "sybase",
36
37
  ]
37
38
 
38
39
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: google-pso-data-validator
3
- Version: 8.3.0
3
+ Version: 8.4.0
4
4
  Summary: A package to enable easy data validation
5
5
  Author: PSO DVT Engineering team
6
6
  Author-email: data-validator-eng@google.com
@@ -28,7 +28,7 @@ Requires-Dist: jellyfish>=1.1.0
28
28
  Requires-Dist: pandas
29
29
  Requires-Dist: parsy>=2.1
30
30
  Requires-Dist: psycopg2-binary>=2.9.9
31
- Requires-Dist: pyarrow==14.0.1
31
+ Requires-Dist: pyarrow
32
32
  Requires-Dist: pydata-google-auth>=1.8.2
33
33
  Requires-Dist: PyMySQL>=1.1.1
34
34
  Requires-Dist: PyYAML>=6.0.2
@@ -45,6 +45,7 @@ Requires-Dist: freezegun; extra == "develop"
45
45
  Requires-Dist: pyfakefs; extra == "develop"
46
46
  Requires-Dist: pytest; extra == "develop"
47
47
  Requires-Dist: pytest-cov; extra == "develop"
48
+ Requires-Dist: pytest-rerunfailures; extra == "develop"
48
49
  Requires-Dist: pytest-timeout; extra == "develop"
49
50
  Dynamic: author
50
51
  Dynamic: author-email
@@ -92,8 +93,9 @@ DVT supports the following connection types:
92
93
  * [Postgres](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#postgres)
93
94
  * [Redshift](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#redshift)
94
95
  * [Spanner](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#google-spanner)
95
- * [Teradata](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#teradata)
96
96
  * [Snowflake](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#snowflake)
97
+ * [Sybase ASE](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#sybase)
98
+ * [Teradata](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#teradata)
97
99
 
98
100
  The [Connections](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md) page provides details about how to create
99
101
  and list connections for the validation tool.
@@ -85,6 +85,11 @@ third_party/ibis/ibis_redshift/compiler.py
85
85
  third_party/ibis/ibis_snowflake/__init__.py
86
86
  third_party/ibis/ibis_snowflake/api.py
87
87
  third_party/ibis/ibis_snowflake/datatypes.py
88
+ third_party/ibis/ibis_sybase/__init__.py
89
+ third_party/ibis/ibis_sybase/api.py
90
+ third_party/ibis/ibis_sybase/compiler.py
91
+ third_party/ibis/ibis_sybase/datatypes.py
92
+ third_party/ibis/ibis_sybase/registry.py
88
93
  third_party/ibis/ibis_teradata/__init__.py
89
94
  third_party/ibis/ibis_teradata/api.py
90
95
  third_party/ibis/ibis_teradata/compiler.py
@@ -12,7 +12,7 @@ jellyfish>=1.1.0
12
12
  pandas
13
13
  parsy>=2.1
14
14
  psycopg2-binary>=2.9.9
15
- pyarrow==14.0.1
15
+ pyarrow
16
16
  pydata-google-auth>=1.8.2
17
17
  PyMySQL>=1.1.1
18
18
  PyYAML>=6.0.2
@@ -29,6 +29,7 @@ freezegun
29
29
  pyfakefs
30
30
  pytest
31
31
  pytest-cov
32
+ pytest-rerunfailures
32
33
  pytest-timeout
33
34
 
34
35
  [pyspark]
@@ -18,7 +18,7 @@ import setuptools
18
18
 
19
19
  name = "google-pso-data-validator"
20
20
  description = "A package to enable easy data validation"
21
- version = "8.3.0"
21
+ version = "8.4.0"
22
22
  release_status = "Development Status :: 4 - Beta"
23
23
 
24
24
  with open("README.md", "r") as fh:
@@ -39,7 +39,7 @@ dependencies = [
39
39
  "pandas",
40
40
  "parsy>=2.1",
41
41
  "psycopg2-binary>=2.9.9",
42
- "pyarrow==14.0.1", # ibis-framework 7.1.0 depends on pyarrow<15 and >=2
42
+ "pyarrow",
43
43
  "pydata-google-auth>=1.8.2",
44
44
  "PyMySQL>=1.1.1",
45
45
  "PyYAML>=6.0.2",
@@ -57,6 +57,7 @@ extras_require = {
57
57
  "pyfakefs",
58
58
  "pytest",
59
59
  "pytest-cov",
60
+ "pytest-rerunfailures",
60
61
  "pytest-timeout",
61
62
  ],
62
63
  }
@@ -92,6 +92,12 @@ try:
92
92
  except Exception:
93
93
  SnowflakeExprTranslator = None
94
94
 
95
+ # Sybase requires sqlalchemy_sybase package.
96
+ try:
97
+ from third_party.ibis.ibis_sybase.compiler import SybaseExprTranslator
98
+ except Exception:
99
+ SybaseExprTranslator = None
100
+
95
101
 
96
102
  # Cast of datetime64 NaT to int64 and then in seconds results in the value below.
97
103
  # We need to use this value in the datetime.date simulation of the datetime64 behaviour.
@@ -591,3 +597,12 @@ if SnowflakeExprTranslator:
591
597
  SnowflakeExprTranslator._registry[ops.RandomScalar] = sa_format_random
592
598
  SnowflakeExprTranslator._registry[BinaryLength] = sa_format_binary_length
593
599
  SnowflakeExprTranslator._registry[ops.RStrip] = _sa_whitespace_rstrip
600
+
601
+ if SybaseExprTranslator:
602
+ SybaseExprTranslator._registry[
603
+ BinaryLength
604
+ ] = mssql_registry.sa_format_binary_length
605
+ SybaseExprTranslator._registry[RawSQL] = sa_format_raw_sql
606
+ SybaseExprTranslator._registry[
607
+ PaddedCharLength
608
+ ] = mssql_registry.sa_format_string_length
@@ -22,12 +22,12 @@ from pydata_google_auth import cache
22
22
  from ibis.backends.bigquery import (
23
23
  Backend as BigQueryBackend,
24
24
  _create_client_info,
25
- parse_project_and_dataset,
26
25
  CLIENT_ID,
27
26
  CLIENT_SECRET,
28
27
  EXTERNAL_DATA_SCOPES,
29
28
  SCOPES,
30
29
  )
30
+ from ibis.backends.bigquery.client import parse_project_and_dataset
31
31
 
32
32
  if TYPE_CHECKING:
33
33
  import google.cloud.bigquery_storage_v1
@@ -53,9 +53,12 @@ class Backend(BigQueryBackend):
53
53
  bqstorage_client: "google.cloud.bigquery_storage_v1.BigQueryReadClient" = None,
54
54
  ):
55
55
  """Copy of Ibis v5 BigQuery do_connect() customized for DVT, see original method for docs."""
56
- default_project_id = ""
56
+ client_project_id = (
57
+ bigquery_client.project if bigquery_client is not None else None
58
+ )
59
+ default_project_id = None
57
60
 
58
- if credentials is None:
61
+ if bigquery_client is None and credentials is None:
59
62
  scopes = SCOPES
60
63
  if auth_external_data:
61
64
  scopes = EXTERNAL_DATA_SCOPES
@@ -83,14 +86,11 @@ class Backend(BigQueryBackend):
83
86
  credentials_cache=credentials_cache,
84
87
  use_local_webserver=auth_local_webserver,
85
88
  )
89
+ project_id = project_id or default_project_id
86
90
 
87
- project_id = project_id or default_project_id
88
-
89
- (
90
- self.data_project,
91
- self.billing_project,
92
- self.dataset,
93
- ) = parse_project_and_dataset(project_id, dataset_id)
91
+ self.billing_project = client_project_id or project_id
92
+ self.data_project = project_id or self.billing_project
93
+ self.dataset = None
94
94
 
95
95
  if bigquery_client is None:
96
96
  self.client = bq.Client(
@@ -133,6 +133,16 @@ class Backend(BigQueryBackend):
133
133
  query_result._project = orig_project
134
134
  return arrow_obj
135
135
 
136
+ def _parse_project_and_dataset(self, dataset) -> tuple[str, str]:
137
+ """Copied from Ibis code and modified to use self.data_project."""
138
+ if not dataset and not self.dataset:
139
+ raise ValueError("Unable to determine BigQuery dataset.")
140
+ project, _, dataset = parse_project_and_dataset(
141
+ self.data_project,
142
+ dataset or f"{self.data_project}.{self.dataset}",
143
+ )
144
+ return project, dataset
145
+
136
146
  def list_primary_key_columns(self, database: str, table: str) -> list:
137
147
  """Return a list of primary key column names."""
138
148
  # TODO: Related to issue-1253, it's not clear if this is possible, we should revisit if it becomes a requirement.
@@ -51,7 +51,7 @@ def sa_table_column(t, op):
51
51
 
52
52
 
53
53
  def strftime(translator, op):
54
- """Use MS SQL CONVERT() in place of STRFTIME().
54
+ """Use SQL Server CONVERT() in place of STRFTIME().
55
55
 
56
56
  This is pretty restrictive due to the limited styles offered by SQL Server,
57
57
  we've just covered off the generic formats used when casting date based columns
@@ -91,7 +91,7 @@ _type_mapping = {
91
91
  # _handle_failed_column_type_inference().
92
92
  # This method has a flaw that is doesn't prefix the table name with the schema
93
93
  # which causes a table not found exception.
94
- # I (nj@2024-02-15) felt the simplest change was to add LONG RAW to SQL Alchemy
94
+ # I (nj@2024-02-15) felt the simplest change was to add LONG RAW to SQLAlchemy
95
95
  # as a RAW variant, as below.
96
96
  if "LONG RAW" not in OracleDialect_oracledb.ischema_names:
97
97
  OracleDialect_oracledb.ischema_names["LONG RAW"] = oracle.RAW