goe-framework 1.0.6__py3-none-any.whl

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 (239) hide show
  1. goe/__init__.py +17 -0
  2. goe/conductor/__init__.py +0 -0
  3. goe/conductor/hybrid_view_service.py +334 -0
  4. goe/config/__init__.py +0 -0
  5. goe/config/config_file.py +53 -0
  6. goe/config/config_validation_functions.py +663 -0
  7. goe/config/option_descriptions.py +38 -0
  8. goe/config/orchestration_config.py +996 -0
  9. goe/config/orchestration_defaults.py +999 -0
  10. goe/connect/__init__.py +0 -0
  11. goe/connect/connect.py +555 -0
  12. goe/connect/connect_backend.py +481 -0
  13. goe/connect/connect_constants.py +23 -0
  14. goe/connect/connect_frontend.py +265 -0
  15. goe/connect/connect_functions.py +112 -0
  16. goe/connect/connect_transport.py +336 -0
  17. goe/data_governance/__init__.py +0 -0
  18. goe/data_governance/cloudera_navigator.py +845 -0
  19. goe/data_governance/hadoop_data_governance.py +616 -0
  20. goe/data_governance/hadoop_data_governance_constants.py +90 -0
  21. goe/data_governance/hadoop_data_governance_interface.py +209 -0
  22. goe/exceptions.py +25 -0
  23. goe/filesystem/__init__.py +0 -0
  24. goe/filesystem/cli_hdfs.py +539 -0
  25. goe/filesystem/goe_azure.py +483 -0
  26. goe/filesystem/goe_dfs.py +424 -0
  27. goe/filesystem/goe_dfs_factory.py +110 -0
  28. goe/filesystem/goe_gcs.py +352 -0
  29. goe/filesystem/goe_s3.py +363 -0
  30. goe/filesystem/web_hdfs.py +507 -0
  31. goe/goe.py +3526 -0
  32. goe/listener/__init__.py +0 -0
  33. goe/listener/__main__.py +36 -0
  34. goe/listener/api/__init__.py +0 -0
  35. goe/listener/api/routes/__init__.py +0 -0
  36. goe/listener/api/routes/docs.py +68 -0
  37. goe/listener/api/routes/orchestration.py +185 -0
  38. goe/listener/api/routes/system.py +186 -0
  39. goe/listener/asgi.py +120 -0
  40. goe/listener/config/__init__.py +10 -0
  41. goe/listener/config/application.py +113 -0
  42. goe/listener/config/gunicorn.conf.py +284 -0
  43. goe/listener/config/logging.py +81 -0
  44. goe/listener/config/router.py +44 -0
  45. goe/listener/core/__init__.py +0 -0
  46. goe/listener/core/enum.py +24 -0
  47. goe/listener/core/events.py +44 -0
  48. goe/listener/core/middleware/__init__.py +6 -0
  49. goe/listener/core/middleware/compression.py +297 -0
  50. goe/listener/core/middleware/cors.py +38 -0
  51. goe/listener/core/middleware/secure_headers.py +37 -0
  52. goe/listener/core/security.py +46 -0
  53. goe/listener/core/worker.py +149 -0
  54. goe/listener/exceptions/__init__.py +38 -0
  55. goe/listener/exceptions/base.py +279 -0
  56. goe/listener/exceptions/errors.py +99 -0
  57. goe/listener/heartbeat.py +47 -0
  58. goe/listener/prestart.py +85 -0
  59. goe/listener/schemas/__init__.py +58 -0
  60. goe/listener/schemas/base.py +102 -0
  61. goe/listener/schemas/error.py +133 -0
  62. goe/listener/schemas/generics.py +40 -0
  63. goe/listener/schemas/orchestration.py +896 -0
  64. goe/listener/schemas/system.py +191 -0
  65. goe/listener/services/__init__.py +8 -0
  66. goe/listener/services/heartbeat.py +115 -0
  67. goe/listener/services/hybrid_view.py +356 -0
  68. goe/listener/services/orchestrate.py +24 -0
  69. goe/listener/services/periodic_tasks.py +196 -0
  70. goe/listener/services/system.py +126 -0
  71. goe/listener/utils/__init__.py +10 -0
  72. goe/listener/utils/cache.py +444 -0
  73. goe/listener/utils/group_by.py +26 -0
  74. goe/listener/utils/orchestrate.py +52 -0
  75. goe/listener/utils/ping.py +47 -0
  76. goe/listener/utils/system.py +83 -0
  77. goe/listener/worker.py +67 -0
  78. goe/listener/wsgi.py +130 -0
  79. goe/offload/__init__.py +0 -0
  80. goe/offload/backend_api.py +2081 -0
  81. goe/offload/backend_table.py +2645 -0
  82. goe/offload/bigquery/__init__.py +0 -0
  83. goe/offload/bigquery/bigquery_backend_api.py +2579 -0
  84. goe/offload/bigquery/bigquery_backend_table.py +664 -0
  85. goe/offload/bigquery/bigquery_column.py +167 -0
  86. goe/offload/bigquery/bigquery_literal.py +137 -0
  87. goe/offload/bigquery/bigquery_predicate.py +68 -0
  88. goe/offload/column_metadata.py +672 -0
  89. goe/offload/factory/__init__.py +0 -0
  90. goe/offload/factory/backend_api_factory.py +130 -0
  91. goe/offload/factory/backend_table_factory.py +162 -0
  92. goe/offload/factory/frontend_api_factory.py +96 -0
  93. goe/offload/factory/offload_source_table_factory.py +92 -0
  94. goe/offload/factory/offload_transport_factory.py +237 -0
  95. goe/offload/factory/offload_transport_rdbms_api_factory.py +64 -0
  96. goe/offload/factory/query_import_factory.py +45 -0
  97. goe/offload/factory/staging_file_factory.py +92 -0
  98. goe/offload/format_literal.py +70 -0
  99. goe/offload/frontend_api.py +882 -0
  100. goe/offload/hadoop/__init__.py +0 -0
  101. goe/offload/hadoop/hadoop_backend_api.py +1961 -0
  102. goe/offload/hadoop/hadoop_backend_table.py +898 -0
  103. goe/offload/hadoop/hadoop_column.py +151 -0
  104. goe/offload/hadoop/hadoop_predicate.py +82 -0
  105. goe/offload/hadoop/hive_backend_api.py +755 -0
  106. goe/offload/hadoop/hive_backend_table.py +257 -0
  107. goe/offload/hadoop/hive_literal.py +91 -0
  108. goe/offload/hadoop/impala_backend_api.py +1215 -0
  109. goe/offload/hadoop/impala_backend_table.py +273 -0
  110. goe/offload/hadoop/impala_literal.py +68 -0
  111. goe/offload/hadoop/sqoop_offload_transport.py +612 -0
  112. goe/offload/microsoft/__init__.py +0 -0
  113. goe/offload/microsoft/mssql_column.py +228 -0
  114. goe/offload/microsoft/mssql_frontend_api.py +650 -0
  115. goe/offload/microsoft/mssql_offload_source_table.py +735 -0
  116. goe/offload/microsoft/mssql_offload_transport_rdbms_api.py +204 -0
  117. goe/offload/microsoft/synapse_backend_api.py +2890 -0
  118. goe/offload/microsoft/synapse_backend_table.py +663 -0
  119. goe/offload/microsoft/synapse_column.py +220 -0
  120. goe/offload/microsoft/synapse_constants.py +29 -0
  121. goe/offload/microsoft/synapse_literal.py +156 -0
  122. goe/offload/microsoft/synapse_predicate.py +71 -0
  123. goe/offload/offload.py +706 -0
  124. goe/offload/offload_constants.py +605 -0
  125. goe/offload/offload_functions.py +588 -0
  126. goe/offload/offload_messages.py +708 -0
  127. goe/offload/offload_metadata_functions.py +462 -0
  128. goe/offload/offload_source_data.py +3257 -0
  129. goe/offload/offload_source_table.py +1295 -0
  130. goe/offload/offload_status_report.py +4038 -0
  131. goe/offload/offload_transport.py +2873 -0
  132. goe/offload/offload_transport_functions.py +466 -0
  133. goe/offload/offload_transport_rdbms_api.py +289 -0
  134. goe/offload/offload_validation.py +1052 -0
  135. goe/offload/offload_xform_functions.py +100 -0
  136. goe/offload/operation/__init__.py +0 -0
  137. goe/offload/operation/data_type_controls.py +365 -0
  138. goe/offload/operation/ddl_file.py +125 -0
  139. goe/offload/operation/not_null_columns.py +107 -0
  140. goe/offload/operation/partition_controls.py +521 -0
  141. goe/offload/operation/sort_columns.py +152 -0
  142. goe/offload/operation/stats_controls.py +425 -0
  143. goe/offload/operation/table_structure_checks.py +181 -0
  144. goe/offload/operation/transport.py +167 -0
  145. goe/offload/option_validation.py +217 -0
  146. goe/offload/oracle/__init__.py +0 -0
  147. goe/offload/oracle/oracle_column.py +326 -0
  148. goe/offload/oracle/oracle_frontend_api.py +1200 -0
  149. goe/offload/oracle/oracle_literal.py +129 -0
  150. goe/offload/oracle/oracle_offload_source_table.py +1765 -0
  151. goe/offload/oracle/oracle_offload_transport_rdbms_api.py +1106 -0
  152. goe/offload/oracle/oracle_predicate.py +129 -0
  153. goe/offload/predicate_offload.py +742 -0
  154. goe/offload/query_import_interface.py +92 -0
  155. goe/offload/snowflake/__init__.py +0 -0
  156. goe/offload/snowflake/snowflake_backend_api.py +2326 -0
  157. goe/offload/snowflake/snowflake_backend_table.py +747 -0
  158. goe/offload/snowflake/snowflake_column.py +161 -0
  159. goe/offload/snowflake/snowflake_literal.py +124 -0
  160. goe/offload/snowflake/snowflake_predicate.py +67 -0
  161. goe/offload/spark/__init__.py +0 -0
  162. goe/offload/spark/dataproc_offload_transport.py +615 -0
  163. goe/offload/spark/livy_offload_transport.py +551 -0
  164. goe/offload/spark/offload_transport_livy_requests.py +113 -0
  165. goe/offload/spark/pyspark_literal.py +69 -0
  166. goe/offload/spark/spark_thrift_backend_api.py +257 -0
  167. goe/offload/staging/__init__.py +0 -0
  168. goe/offload/staging/avro/__init__.py +0 -0
  169. goe/offload/staging/avro/avro_column.py +108 -0
  170. goe/offload/staging/avro/avro_staging_file.py +416 -0
  171. goe/offload/staging/parquet/__init__.py +0 -0
  172. goe/offload/staging/parquet/parquet_column.py +113 -0
  173. goe/offload/staging/parquet/parquet_staging_file.py +311 -0
  174. goe/offload/staging/staging_file.py +155 -0
  175. goe/offload/synthetic_partition_literal.py +249 -0
  176. goe/offload/teradata/__init__.py +0 -0
  177. goe/offload/teradata/teradata_column.py +309 -0
  178. goe/offload/teradata/teradata_frontend_api.py +908 -0
  179. goe/offload/teradata/teradata_literal.py +162 -0
  180. goe/offload/teradata/teradata_offload_source_table.py +1192 -0
  181. goe/offload/teradata/teradata_offload_transport_rdbms_api.py +620 -0
  182. goe/offload/teradata/teradata_partition_expression.py +205 -0
  183. goe/offload/teradata/teradata_predicate.py +86 -0
  184. goe/orchestration/__init__.py +0 -0
  185. goe/orchestration/cli_entry_points.py +79 -0
  186. goe/orchestration/command_steps.py +126 -0
  187. goe/orchestration/execution_id.py +101 -0
  188. goe/orchestration/orchestration_constants.py +56 -0
  189. goe/orchestration/orchestration_lock.py +164 -0
  190. goe/orchestration/orchestration_runner.py +539 -0
  191. goe/persistence/__init__.py +0 -0
  192. goe/persistence/factory/__init__.py +0 -0
  193. goe/persistence/factory/orchestration_repo_client_factory.py +48 -0
  194. goe/persistence/oracle/__init__.py +0 -0
  195. goe/persistence/oracle/oracle_orchestration_repo_client.py +1093 -0
  196. goe/persistence/orchestration_metadata.py +449 -0
  197. goe/persistence/orchestration_repo_client.py +436 -0
  198. goe/persistence/teradata/__init__.py +0 -0
  199. goe/persistence/teradata/teradata_orchestration_repo_client.py +358 -0
  200. goe/schema_sync/__init__.py +0 -0
  201. goe/schema_sync/schema_sync.py +170 -0
  202. goe/schema_sync/schema_sync_analyzer.py +663 -0
  203. goe/schema_sync/schema_sync_command_file.py +94 -0
  204. goe/schema_sync/schema_sync_constants.py +38 -0
  205. goe/schema_sync/schema_sync_processor.py +109 -0
  206. goe/schema_sync/schema_sync_step.py +91 -0
  207. goe/schema_sync/steps/__init__.py +0 -0
  208. goe/schema_sync/steps/add_backend_column.py +107 -0
  209. goe/schema_sync/steps/add_oracle_column.py +101 -0
  210. goe/schema_sync/steps/step_builder.py +30 -0
  211. goe/scripts/__init__.py +0 -0
  212. goe/scripts/agg_validate.py +288 -0
  213. goe/util/__init__.py +0 -0
  214. goe/util/avro_encoder.py +258 -0
  215. goe/util/better_impyla.py +1748 -0
  216. goe/util/exception_trigger.py +52 -0
  217. goe/util/goe_log.py +152 -0
  218. goe/util/goe_log_fh.py +67 -0
  219. goe/util/goe_version.py +18 -0
  220. goe/util/hive_ddl_transform.py +396 -0
  221. goe/util/hive_table_stats.py +1590 -0
  222. goe/util/hs2_connection.py +310 -0
  223. goe/util/json_tools.py +60 -0
  224. goe/util/local_cache.py +312 -0
  225. goe/util/misc_functions.py +775 -0
  226. goe/util/ora_query.py +417 -0
  227. goe/util/parallel_exec.py +222 -0
  228. goe/util/parquet_encoder.py +206 -0
  229. goe/util/password_tools.py +261 -0
  230. goe/util/polling_thread.py +97 -0
  231. goe/util/redis_tools.py +443 -0
  232. goe/util/retry_logic.py +165 -0
  233. goe/util/simple_timer.py +49 -0
  234. goe_framework-1.0.6.dist-info/AUTHORS +10 -0
  235. goe_framework-1.0.6.dist-info/LICENSE +201 -0
  236. goe_framework-1.0.6.dist-info/METADATA +210 -0
  237. goe_framework-1.0.6.dist-info/RECORD +239 -0
  238. goe_framework-1.0.6.dist-info/WHEEL +5 -0
  239. goe_framework-1.0.6.dist-info/top_level.txt +1 -0
goe/__init__.py ADDED
@@ -0,0 +1,17 @@
1
+ # Copyright 2024 The GOE Authors. All rights reserved.
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 importlib.metadata
16
+
17
+ __version__ = importlib.metadata.version("goe-framework")
File without changes
@@ -0,0 +1,334 @@
1
+ #! /usr/bin/env python3
2
+
3
+ # Copyright 2016 The GOE Authors. All rights reserved.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ """ HybridViewService: API over orchestration code that fetches information based on a hybrid view
18
+ """
19
+
20
+ import json
21
+ import logging
22
+
23
+ from goe.config.orchestration_config import OrchestrationConfig
24
+ from goe.offload.factory.backend_api_factory import backend_api_factory
25
+ from goe.offload.factory.backend_table_factory import backend_table_factory
26
+ from goe.offload.factory.offload_source_table_factory import OffloadSourceTable
27
+ from goe.offload.offload_constants import LOG_LEVEL_DEBUG
28
+ from goe.offload.offload_messages import OffloadMessages, VVERBOSE
29
+ from goe.offload.offload_validation import (
30
+ CrossDbValidator,
31
+ build_verification_clauses,
32
+ )
33
+ from goe.persistence.orchestration_metadata import (
34
+ OrchestrationMetadata,
35
+ INCREMENTAL_PREDICATE_TYPE_LIST,
36
+ INCREMENTAL_PREDICATE_TYPE_LIST_AS_RANGE,
37
+ INCREMENTAL_PREDICATE_TYPE_RANGE,
38
+ )
39
+ from goe.util.simple_timer import SimpleTimer
40
+
41
+
42
+ ###############################################################################
43
+ # EXCEPTIONS
44
+ ###############################################################################
45
+
46
+
47
+ class HybridViewServiceException(Exception):
48
+ pass
49
+
50
+
51
+ ###############################################################################
52
+ # CONSTANTS
53
+ ###############################################################################
54
+
55
+ JSON_KEY_BACKEND_NUM_ROWS = "num_rows"
56
+ JSON_KEY_BACKEND_SIZE = "size_in_bytes"
57
+ JSON_KEY_BACKEND_PARTITIONS = "partitions"
58
+ JSON_KEY_BACKEND_PARTITIONING = "partitioning_supported"
59
+ JSON_ALL_BACKEND_KEYS = [
60
+ JSON_KEY_BACKEND_NUM_ROWS,
61
+ JSON_KEY_BACKEND_SIZE,
62
+ JSON_KEY_BACKEND_PARTITIONS,
63
+ JSON_KEY_BACKEND_PARTITIONING,
64
+ ]
65
+
66
+ JSON_KEY_VALIDATE_STATUS = "success"
67
+ JSON_KEY_VALIDATE_MESSAGE = "message"
68
+
69
+
70
+ ###############################################################################
71
+ # LOGGING
72
+ ###############################################################################
73
+
74
+ logger = logging.getLogger(__name__)
75
+ # Disabling logging by default
76
+ logger.addHandler(logging.NullHandler())
77
+
78
+
79
+ ###############################################################################
80
+ # HybridViewService
81
+ ###############################################################################
82
+
83
+
84
+ class HybridViewService(object):
85
+ """API over orchestration code that fetches information based on a hybrid view"""
86
+
87
+ def __init__(self, hybrid_schema, hybrid_view, dry_run=False, existing_log_fh=None):
88
+ raise NotImplementedError(
89
+ "Metadata is no longer keyed on a hybrid view, the viability of Conductor itself needs to be reviewed."
90
+ )
91
+ assert hybrid_schema and hybrid_view
92
+ self._dry_run = dry_run
93
+ self._connection_options = self._build_connection_options()
94
+ self._messages = OffloadMessages(log_fh=existing_log_fh)
95
+ if (
96
+ self._connection_options.log_level == LOG_LEVEL_DEBUG
97
+ and not existing_log_fh
98
+ ):
99
+ self._messages.init_log(
100
+ self._connection_options.log_path, "hybrid_view_service"
101
+ )
102
+ self._debug(
103
+ "Initializing HybridViewService(%s.%s)" % (hybrid_schema, hybrid_view)
104
+ )
105
+ self._offload_metadata = OrchestrationMetadata.from_name(
106
+ hybrid_schema,
107
+ hybrid_view,
108
+ connection_options=self._connection_options,
109
+ messages=self._messages,
110
+ )
111
+ if not self._offload_metadata:
112
+ self._debug("Hybrid view has no metadata")
113
+ raise HybridViewServiceException(
114
+ "Hybrid view has no metadata: %s.%s" % (hybrid_schema, hybrid_view)
115
+ )
116
+ self._debug("Hybrid View metadata:\n%r" % self._offload_metadata)
117
+ self.hybrid_schema = self._offload_metadata.hybrid_owner
118
+ self.hybrid_view = self._offload_metadata.hybrid_view
119
+ self._frontend_table_owner = self._offload_metadata.offloaded_owner
120
+ self._frontend_table_name = self._offload_metadata.offloaded_table
121
+ self._backend_table_owner = self._offload_metadata.backend_owner
122
+ self._backend_table_name = self._offload_metadata.backend_table
123
+ self._ipa_predicate_type = self._offload_metadata.incremental_predicate_type
124
+ self._incremental_keys = self._offload_metadata.incremental_key
125
+ # Initially store in state as None to allow lazy collection via _get_backend_detail()
126
+ self._backend_info = None
127
+ # Initially store in state as None to allow lazy collection via _get_frontend_detail()
128
+ self._frontend_info = None
129
+
130
+ ###########################################################################
131
+ # PRIVATE METHODS
132
+ ###########################################################################
133
+
134
+ def _backend_table_does_not_exist_exception(self):
135
+ raise HybridViewServiceException(
136
+ "Backend table does not exist: %s.%s"
137
+ % (self._backend_table_owner, self._backend_table_name)
138
+ )
139
+
140
+ def _bind_in_predicates(self):
141
+ return bool(
142
+ self._ipa_predicate_type
143
+ in [
144
+ INCREMENTAL_PREDICATE_TYPE_RANGE,
145
+ INCREMENTAL_PREDICATE_TYPE_LIST_AS_RANGE,
146
+ ]
147
+ )
148
+
149
+ def _build_connection_options(self):
150
+ config = OrchestrationConfig.as_defaults({})
151
+ return config
152
+
153
+ def _debug(self, msg):
154
+ self._messages.log(msg, detail=VVERBOSE)
155
+ logger.debug(msg)
156
+
157
+ def _decode_high_values(self, frontend_table, lower_hv_str=None, upper_hv_str=None):
158
+ logger.debug("_decode_high_values(%r, %r)" % (upper_hv_str, lower_hv_str))
159
+ upper_hvs = None
160
+ if upper_hv_str:
161
+ self._debug("Decoding HV string: %r" % upper_hv_str)
162
+ upper_hvs = frontend_table.decode_partition_high_values(upper_hv_str)
163
+ self._debug("Decoded to: %s" % repr(upper_hvs))
164
+ prior_hvs = None
165
+ if lower_hv_str:
166
+ self._debug("Decoding HV string: %r" % lower_hv_str)
167
+ prior_hvs = frontend_table.decode_partition_high_values(lower_hv_str)
168
+ self._debug("Decoded to: %s" % repr(prior_hvs))
169
+ return upper_hvs, prior_hvs
170
+
171
+ def _format_payload(self, payload, as_json):
172
+ if as_json:
173
+ json_payload = json.dumps(payload)
174
+ return json_payload
175
+ else:
176
+ return payload
177
+
178
+ def _get_backend_api(self):
179
+ logger.debug("_get_backend_api(%s)" % self._connection_options.target)
180
+ return backend_api_factory(
181
+ self._connection_options.target,
182
+ self._connection_options,
183
+ self._messages,
184
+ dry_run=self._dry_run,
185
+ )
186
+
187
+ def _get_backend_table(self):
188
+ logger.debug("_get_backend_table(%s)" % self._connection_options.target)
189
+ return backend_table_factory(
190
+ self._backend_table_owner,
191
+ self._backend_table_name,
192
+ self._connection_options.target,
193
+ self._connection_options,
194
+ self._messages,
195
+ hybrid_metadata=self._offload_metadata,
196
+ dry_run=self._dry_run,
197
+ )
198
+
199
+ def _get_backend_detail(self, attribute_name=None):
200
+ logger.debug("_get_backend_detail(%s)" % attribute_name)
201
+ if not self._backend_info:
202
+ self._debug("Initializing backend attributes")
203
+ backend_table = self._get_backend_table()
204
+ if not backend_table.exists():
205
+ self._backend_table_does_not_exist_exception()
206
+ size_in_bytes, num_rows = backend_table.get_table_size_and_row_count()
207
+ backend_partitions = backend_table.get_table_partitions()
208
+ self._backend_info = {
209
+ "columns": backend_table.get_columns(),
210
+ JSON_KEY_BACKEND_SIZE: size_in_bytes,
211
+ JSON_KEY_BACKEND_NUM_ROWS: num_rows,
212
+ JSON_KEY_BACKEND_PARTITIONS: backend_partitions,
213
+ JSON_KEY_BACKEND_PARTITIONING: backend_table.partition_by_column_supported(),
214
+ }
215
+ if attribute_name:
216
+ return self._backend_info[attribute_name]
217
+ else:
218
+ return self._backend_info
219
+
220
+ def _get_frontend_table(self):
221
+ logger.debug(
222
+ "_get_frontend_table(%s.%s)"
223
+ % (self._frontend_table_owner, self._frontend_table_name)
224
+ )
225
+ return OffloadSourceTable.create(
226
+ self._frontend_table_owner,
227
+ self._frontend_table_name,
228
+ self._connection_options,
229
+ self._messages,
230
+ )
231
+
232
+ def _get_frontend_detail(self, attribute_name):
233
+ logger.debug("_get_frontend_detail(%s)" % attribute_name)
234
+ assert attribute_name
235
+ if not self._frontend_info:
236
+ self._debug("Initializing frontend attributes")
237
+ frontend_table = OffloadSourceTable.create(
238
+ self._frontend_table_owner,
239
+ self._frontend_table_name,
240
+ self._connection_options,
241
+ self._messages,
242
+ offload_by_subpartition=self._offload_metadata.is_subpartition_offload(),
243
+ )
244
+ columns = frontend_table.columns
245
+ self._frontend_info = {"columns": columns}
246
+ return self._frontend_info[attribute_name]
247
+
248
+ ###########################################################################
249
+ # PUBLIC METHODS
250
+ ###########################################################################
251
+
252
+ @property
253
+ def backend_partition_by_column_supported(self):
254
+ """Does the backend support partitioning, strictly speaking we don't need this info except for testing"""
255
+ return self._get_backend_detail(JSON_KEY_BACKEND_PARTITIONING)
256
+
257
+ def backend_attributes(self, as_json=True):
258
+ """Return a single dict or JSON document containing information from backend system.
259
+ partition_key_values results in a backend query which can take some time so is excluded by default.
260
+ """
261
+ payload = self._get_backend_detail()
262
+ filtered_payload = {
263
+ k: v for k, v in payload.items() if k in JSON_ALL_BACKEND_KEYS
264
+ }
265
+ return self._format_payload(filtered_payload, as_json)
266
+
267
+ def validate_by_aggregation(self, lower_hv=None, upper_hv=None, as_json=True):
268
+ """Return response from CrossDbValidator as JSON: {'success': True/False, 'message': "blah"}
269
+ lower_hv, upper_hv: DBA_TAB_PARTITIONS.HIGH_VALUE based. Can have both empty, upper_hv only or both.
270
+ """
271
+ if lower_hv:
272
+ assert isinstance(lower_hv, str)
273
+ if upper_hv:
274
+ assert isinstance(upper_hv, str)
275
+ logger.debug("validate_by_aggregation(%r, %r)" % (upper_hv, lower_hv))
276
+ if not self._frontend_table_owner or not self._frontend_table_name:
277
+ raise HybridViewServiceException(
278
+ "Hybrid view is not for an offloaded table: %s.%s"
279
+ % (self.hybrid_schema, self.hybrid_view)
280
+ )
281
+ self._debug("Running Hybrid View validation by aggregation")
282
+ t = SimpleTimer("validate_by_aggregation")
283
+ backend_table = self._get_backend_table()
284
+ if not backend_table.exists():
285
+ self._backend_table_does_not_exist_exception()
286
+ frontend_table = self._get_frontend_table()
287
+ upper_hvs, prior_hvs = self._decode_high_values(
288
+ frontend_table, lower_hv_str=lower_hv, upper_hv_str=upper_hv
289
+ )
290
+ if self._ipa_predicate_type == INCREMENTAL_PREDICATE_TYPE_LIST and upper_hvs:
291
+ # HVs for LIST are lists of tuples, not just a tuple as per RANGE
292
+ upper_hvs = [upper_hvs]
293
+ frontend_filters, query_binds = build_verification_clauses(
294
+ frontend_table,
295
+ self._ipa_predicate_type,
296
+ upper_hvs,
297
+ prior_hvs,
298
+ with_binds=self._bind_in_predicates(),
299
+ )
300
+ self._debug("Generated frontend filters:\n%r" % frontend_filters)
301
+ backend_filters, _ = build_verification_clauses(
302
+ frontend_table,
303
+ self._ipa_predicate_type,
304
+ upper_hvs,
305
+ prior_hvs,
306
+ with_binds=False,
307
+ backend_table=backend_table,
308
+ )
309
+ self._debug("Generated backend filters:\n%r" % backend_filters)
310
+
311
+ validator = CrossDbValidator(
312
+ self._frontend_table_owner,
313
+ self._frontend_table_name,
314
+ self._connection_options,
315
+ backend_table.get_backend_api(),
316
+ messages=self._messages,
317
+ backend_db=self._backend_table_owner,
318
+ backend_table=self._backend_table_name,
319
+ execute=(not self._dry_run),
320
+ )
321
+ status, agg_msg = validator.validate(
322
+ safe=False,
323
+ filters=backend_filters,
324
+ execute=bool(not self._dry_run),
325
+ frontend_filters=frontend_filters,
326
+ frontend_query_params=query_binds,
327
+ )
328
+ self._debug("validate_by_aggregation returning: %s" % status)
329
+ self._debug(t.show())
330
+ payload = {JSON_KEY_VALIDATE_STATUS: status, JSON_KEY_VALIDATE_MESSAGE: agg_msg}
331
+ return self._format_payload(payload, as_json)
332
+
333
+ def validate_by_count(self, lower_hv=None, upper_hv=None, as_json=True):
334
+ raise NotImplementedError("Validate by hybrid view is no longer imeplemented")
goe/config/__init__.py ADDED
File without changes
@@ -0,0 +1,53 @@
1
+ # Copyright 2024 The GOE Authors. All rights reserved.
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 os
16
+ import re
17
+ import sys
18
+ from typing import Optional
19
+
20
+ from dotenv import load_dotenv
21
+
22
+
23
+ CONFIG_FILE_NAME = "offload.env"
24
+ KEY_VALUE_PATTERN = re.compile(r"#?[ ]*([A-Z_0-9]+)=(.*)")
25
+
26
+
27
+ def check_config_path():
28
+ """Check OFFLOAD_HOME in top level command wrappers"""
29
+ if not os.environ.get("OFFLOAD_HOME"):
30
+ print("OFFLOAD_HOME environment variable missing")
31
+ sys.exit(1)
32
+
33
+
34
+ def get_environment_file_path():
35
+ return os.path.join(os.environ.get("OFFLOAD_HOME"), "conf", CONFIG_FILE_NAME)
36
+
37
+
38
+ def load_env(path: str = None):
39
+ """Load GOE environment from a configuration file.
40
+
41
+ By default this is a fixed location: $OFFLOAD_HOME/conf/offload.env.
42
+ In time this will become a parameter and support cloud storage locations.
43
+ """
44
+ if not path:
45
+ path = get_environment_file_path()
46
+
47
+ load_dotenv(path)
48
+
49
+
50
+ def env_key_value_pair(line_from_file: str) -> Optional[tuple]:
51
+ """Used by connect to get the key names from a configuration file"""
52
+ m = KEY_VALUE_PATTERN.match(line_from_file)
53
+ return m.groups() if m else None