e6data-python-connector 2.3.12rc31__tar.gz → 2.3.12rc32__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 (87) hide show
  1. {e6data_python_connector-2.3.12rc31/e6data_python_connector.egg-info → e6data_python_connector-2.3.12rc32}/PKG-INFO +69 -2
  2. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/README.md +68 -1
  3. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/cluster_manager.py +44 -2
  4. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/e6data_grpc.py +47 -2
  5. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32/e6data_python_connector.egg-info}/PKG-INFO +69 -2
  6. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/setup.py +1 -1
  7. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/LICENSE +0 -0
  8. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/MANIFEST.in +0 -0
  9. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/__init__.py +0 -0
  10. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/cluster_server/__init__.py +0 -0
  11. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/cluster_server/cluster_pb2.py +0 -0
  12. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/cluster_server/cluster_pb2_grpc.py +0 -0
  13. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/common.py +0 -0
  14. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/connection_pool.py +0 -0
  15. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/constants.py +0 -0
  16. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/datainputstream.py +0 -0
  17. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/date_time_utils.py +0 -0
  18. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/dialect.py +0 -0
  19. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/e6x_vector/__init__.py +0 -0
  20. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/e6x_vector/constants.py +0 -0
  21. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/e6x_vector/ttypes.py +0 -0
  22. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/exceptions.py +0 -0
  23. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/server/QueryEngineService.py +0 -0
  24. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/server/__init__.py +0 -0
  25. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/server/constants.py +0 -0
  26. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/server/e6x_engine_pb2.py +0 -0
  27. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/server/e6x_engine_pb2.pyi +0 -0
  28. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/server/e6x_engine_pb2_grpc.py +0 -0
  29. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/server/ttypes.py +0 -0
  30. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/strategy.py +0 -0
  31. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector/typeId.py +0 -0
  32. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector.egg-info/SOURCES.txt +0 -0
  33. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector.egg-info/dependency_links.txt +0 -0
  34. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector.egg-info/entry_points.txt +0 -0
  35. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector.egg-info/requires.txt +0 -0
  36. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/e6data_python_connector.egg-info/top_level.txt +0 -0
  37. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/gen-py/__init__.py +0 -0
  38. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/gen-py/e6x_vector/__init__.py +0 -0
  39. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/gen-py/e6x_vector/constants.py +0 -0
  40. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/gen-py/e6x_vector/ttypes.py +0 -0
  41. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/pyproject.toml +0 -0
  42. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/setup.cfg +0 -0
  43. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/__init__.py +0 -0
  44. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/analyze_38_nines.py +0 -0
  45. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/analyze_all_cases.py +0 -0
  46. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/analyze_binary.py +0 -0
  47. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/analyze_correct_value.py +0 -0
  48. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/analyze_fields.py +0 -0
  49. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/check_decimal_errors.py +0 -0
  50. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/cleanup_test_files.py +0 -0
  51. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/debug_38_nines.py +0 -0
  52. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/debug_binary.py +0 -0
  53. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/final_test.py +0 -0
  54. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/move_tests.py +0 -0
  55. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/quick_test.py +0 -0
  56. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_38_nines.py +0 -0
  57. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_all_decimal128_cases.py +0 -0
  58. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_cluster_manager_efficiency.py +0 -0
  59. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_cluster_manager_none_strategy.py +0 -0
  60. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_cluster_manager_strategy.py +0 -0
  61. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_comprehensive.py +0 -0
  62. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_connection_pool.py +0 -0
  63. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_connection_pool_e2e.py +0 -0
  64. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_current_implementation.py +0 -0
  65. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_decimal128_binary_parsing.py +0 -0
  66. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_decimal128_parsing.py +0 -0
  67. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_fix.py +0 -0
  68. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_improved_parsing.py +0 -0
  69. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_known_case.py +0 -0
  70. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_manual_analysis.py +0 -0
  71. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_mock_server.py +0 -0
  72. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_multiprocessing_fix.py +0 -0
  73. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_new_implementation.py +0 -0
  74. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_pool_concurrency_simple.py +0 -0
  75. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_pool_threading_multiprocessing.py +0 -0
  76. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_simple_connection_pool.py +0 -0
  77. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_specific_binary.py +0 -0
  78. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_strategy.py +0 -0
  79. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_strategy_logic.py +0 -0
  80. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_strategy_persistence_fix.py +0 -0
  81. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_strategy_sharing_fix.py +0 -0
  82. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/test_user_binary_value.py +0 -0
  83. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/tests.py +0 -0
  84. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/tests_grpc.py +0 -0
  85. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/validate_decimal128.py +0 -0
  86. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/validate_implementation.py +0 -0
  87. {e6data_python_connector-2.3.12rc31 → e6data_python_connector-2.3.12rc32}/test/verify_decimal_fix.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: e6data-python-connector
3
- Version: 2.3.12rc31
3
+ Version: 2.3.12rc32
4
4
  Summary: Client for the e6data distributed SQL Engine.
5
5
  Home-page: https://github.com/e6x-labs/e6data-python-connector
6
6
  Author: e6data, Inc.
@@ -39,7 +39,7 @@ Dynamic: summary
39
39
 
40
40
  # e6data Python Connector
41
41
 
42
- ![version](https://img.shields.io/badge/version-2.3.12rc31-blue.svg)
42
+ ![version](https://img.shields.io/badge/version-2.3.12rc32-blue.svg)
43
43
 
44
44
  ## Introduction
45
45
 
@@ -122,6 +122,7 @@ The `Connection` class supports the following parameters:
122
122
  | `catalog` | str | No | None | Catalog name |
123
123
  | `cluster_name` | str | No | None | Name of the cluster for cluster-specific operations |
124
124
  | `secure` | bool | No | False | Enable SSL/TLS for secure connections |
125
+ | `ssl_cert` | str/bytes | No | None | Path to CA certificate (PEM) or certificate bytes for HTTPS connections |
125
126
  | `auto_resume` | bool | No | True | Automatically resume cluster if suspended |
126
127
  | `grpc_options` | dict | No | None | Additional gRPC configuration options |
127
128
  | `debug` | bool | No | False | Enable debug logging for troubleshooting |
@@ -159,6 +160,72 @@ conn = Connection(
159
160
  )
160
161
  ```
161
162
 
163
+ #### HTTPS Connection with HAProxy
164
+
165
+ When connecting through HAProxy with HTTPS, you can provide a custom CA certificate for secure connections. The `ssl_cert` parameter accepts either a file path to a PEM certificate or the certificate content as bytes.
166
+
167
+ **Using a CA certificate file path:**
168
+
169
+ ```python
170
+ conn = Connection(
171
+ host=host,
172
+ port=443,
173
+ username=username,
174
+ password=password,
175
+ database=database,
176
+ secure=True,
177
+ ssl_cert='/path/to/ca-cert.pem' # Path to your CA certificate
178
+ )
179
+ ```
180
+
181
+ **Reading certificate content as bytes:**
182
+
183
+ ```python
184
+ # Read certificate file and pass as bytes
185
+ with open('/path/to/ca-cert.pem', 'rb') as cert_file:
186
+ cert_data = cert_file.read()
187
+
188
+ conn = Connection(
189
+ host=host,
190
+ port=443,
191
+ username=username,
192
+ password=password,
193
+ database=database,
194
+ secure=True,
195
+ ssl_cert=cert_data # Certificate content as bytes
196
+ )
197
+ ```
198
+
199
+ **Using system CA bundle for publicly signed certificates:**
200
+
201
+ ```python
202
+ # When ssl_cert is None, system default CA bundle is used
203
+ conn = Connection(
204
+ host=host,
205
+ port=443,
206
+ username=username,
207
+ password=password,
208
+ database=database,
209
+ secure=True # Uses system CA bundle by default
210
+ )
211
+ ```
212
+
213
+ **Connection pooling with custom CA certificate:**
214
+
215
+ ```python
216
+ pool = ConnectionPool(
217
+ min_size=2,
218
+ max_size=10,
219
+ host=host,
220
+ port=443,
221
+ username=username,
222
+ password=password,
223
+ database=database,
224
+ secure=True,
225
+ ssl_cert='/path/to/ca-cert.pem' # Custom CA certificate for pool connections
226
+ )
227
+ ```
228
+
162
229
  #### Handling Missing System Dependencies
163
230
 
164
231
  The e6data connector uses the `fastbinary` module (from Apache Thrift) for optimal performance when deserializing data. This module requires system-level dependencies (`python3-devel` and `gcc-c++`) to be installed.
@@ -1,6 +1,6 @@
1
1
  # e6data Python Connector
2
2
 
3
- ![version](https://img.shields.io/badge/version-2.3.12rc31-blue.svg)
3
+ ![version](https://img.shields.io/badge/version-2.3.12rc32-blue.svg)
4
4
 
5
5
  ## Introduction
6
6
 
@@ -83,6 +83,7 @@ The `Connection` class supports the following parameters:
83
83
  | `catalog` | str | No | None | Catalog name |
84
84
  | `cluster_name` | str | No | None | Name of the cluster for cluster-specific operations |
85
85
  | `secure` | bool | No | False | Enable SSL/TLS for secure connections |
86
+ | `ssl_cert` | str/bytes | No | None | Path to CA certificate (PEM) or certificate bytes for HTTPS connections |
86
87
  | `auto_resume` | bool | No | True | Automatically resume cluster if suspended |
87
88
  | `grpc_options` | dict | No | None | Additional gRPC configuration options |
88
89
  | `debug` | bool | No | False | Enable debug logging for troubleshooting |
@@ -120,6 +121,72 @@ conn = Connection(
120
121
  )
121
122
  ```
122
123
 
124
+ #### HTTPS Connection with HAProxy
125
+
126
+ When connecting through HAProxy with HTTPS, you can provide a custom CA certificate for secure connections. The `ssl_cert` parameter accepts either a file path to a PEM certificate or the certificate content as bytes.
127
+
128
+ **Using a CA certificate file path:**
129
+
130
+ ```python
131
+ conn = Connection(
132
+ host=host,
133
+ port=443,
134
+ username=username,
135
+ password=password,
136
+ database=database,
137
+ secure=True,
138
+ ssl_cert='/path/to/ca-cert.pem' # Path to your CA certificate
139
+ )
140
+ ```
141
+
142
+ **Reading certificate content as bytes:**
143
+
144
+ ```python
145
+ # Read certificate file and pass as bytes
146
+ with open('/path/to/ca-cert.pem', 'rb') as cert_file:
147
+ cert_data = cert_file.read()
148
+
149
+ conn = Connection(
150
+ host=host,
151
+ port=443,
152
+ username=username,
153
+ password=password,
154
+ database=database,
155
+ secure=True,
156
+ ssl_cert=cert_data # Certificate content as bytes
157
+ )
158
+ ```
159
+
160
+ **Using system CA bundle for publicly signed certificates:**
161
+
162
+ ```python
163
+ # When ssl_cert is None, system default CA bundle is used
164
+ conn = Connection(
165
+ host=host,
166
+ port=443,
167
+ username=username,
168
+ password=password,
169
+ database=database,
170
+ secure=True # Uses system CA bundle by default
171
+ )
172
+ ```
173
+
174
+ **Connection pooling with custom CA certificate:**
175
+
176
+ ```python
177
+ pool = ConnectionPool(
178
+ min_size=2,
179
+ max_size=10,
180
+ host=host,
181
+ port=443,
182
+ username=username,
183
+ password=password,
184
+ database=database,
185
+ secure=True,
186
+ ssl_cert='/path/to/ca-cert.pem' # Custom CA certificate for pool connections
187
+ )
188
+ ```
189
+
123
190
  #### Handling Missing System Dependencies
124
191
 
125
192
  The e6data connector uses the `fastbinary` module (from Apache Thrift) for optimal performance when deserializing data. This module requires system-level dependencies (`python3-devel` and `gcc-c++`) to be installed.
@@ -140,7 +140,7 @@ class ClusterManager:
140
140
  """
141
141
 
142
142
  def __init__(self, host: str, port: int, user: str, password: str, secure_channel: bool = False, timeout=60 * 5,
143
- cluster_uuid=None, grpc_options=None, debug=False):
143
+ cluster_uuid=None, grpc_options=None, debug=False, ssl_cert=None):
144
144
  """
145
145
  Initializes a new instance of the ClusterManager class.
146
146
 
@@ -156,6 +156,8 @@ class ClusterManager:
156
156
  cluster_uuid (str, optional): The unique identifier for the target cluster;
157
157
  defaults to None.
158
158
  debug (bool, optional): Enable debug logging; defaults to False.
159
+ ssl_cert (str or bytes, optional): Path to CA certificate file (PEM format) or
160
+ certificate content as bytes for secure connections; defaults to None.
159
161
  """
160
162
 
161
163
  self._host = host
@@ -169,6 +171,46 @@ class ClusterManager:
169
171
  if grpc_options is None:
170
172
  self._grpc_options = dict()
171
173
  self._debug = debug
174
+ self._ssl_cert = ssl_cert
175
+
176
+ def _get_ssl_credentials(self):
177
+ """
178
+ Get SSL credentials for secure gRPC channel.
179
+
180
+ Handles three scenarios:
181
+ 1. ssl_cert is a string (file path): Read the PEM certificate from the file
182
+ 2. ssl_cert is bytes: Use the certificate content directly
183
+ 3. ssl_cert is None: Use system default CA bundle
184
+
185
+ Returns:
186
+ grpc.ChannelCredentials: SSL credentials for secure channel
187
+
188
+ Raises:
189
+ FileNotFoundError: If ssl_cert is a file path but the file doesn't exist
190
+ IOError: If ssl_cert file cannot be read
191
+ """
192
+ if self._ssl_cert is None:
193
+ # Use system default CA bundle
194
+ return grpc.ssl_channel_credentials()
195
+ elif isinstance(self._ssl_cert, str):
196
+ # ssl_cert is a file path - read the certificate from file
197
+ try:
198
+ with open(self._ssl_cert, 'rb') as cert_file:
199
+ root_ca_cert = cert_file.read()
200
+ return grpc.ssl_channel_credentials(root_certificates=root_ca_cert)
201
+ except FileNotFoundError:
202
+ logger.error(f"SSL certificate file not found: {self._ssl_cert}")
203
+ raise
204
+ except IOError as e:
205
+ logger.error(f"Failed to read SSL certificate file {self._ssl_cert}: {e}")
206
+ raise
207
+ elif isinstance(self._ssl_cert, bytes):
208
+ # ssl_cert is certificate content as bytes
209
+ return grpc.ssl_channel_credentials(root_certificates=self._ssl_cert)
210
+ else:
211
+ # Invalid type - log warning and use system default
212
+ logger.warning(f"Invalid ssl_cert type: {type(self._ssl_cert)}. Using system default CA bundle.")
213
+ return grpc.ssl_channel_credentials()
172
214
 
173
215
  @property
174
216
  def _get_connection(self):
@@ -184,7 +226,7 @@ class ClusterManager:
184
226
  self._channel = grpc.secure_channel(
185
227
  target='{}:{}'.format(self._host, self._port),
186
228
  options=self._grpc_options,
187
- credentials=grpc.ssl_channel_credentials()
229
+ credentials=self._get_ssl_credentials()
188
230
  )
189
231
  else:
190
232
  self._channel = grpc.insecure_channel(
@@ -331,6 +331,7 @@ class Connection(object):
331
331
  database: str = None,
332
332
  cluster_name: str = None,
333
333
  secure: bool = False,
334
+ ssl_cert = None,
334
335
  auto_resume: bool = True,
335
336
  scheme: str = 'e6data',
336
337
  grpc_options: dict = None,
@@ -356,6 +357,9 @@ class Connection(object):
356
357
  Cluster's name
357
358
  secure: bool, Optional
358
359
  Flag to use a secure channel for data transfer
360
+ ssl_cert: str or bytes, Optional
361
+ Path to CA certificate file (PEM format) or certificate content as bytes for
362
+ secure connections. If None, system default CA bundle is used.
359
363
  auto_resume: bool, Optional
360
364
  Flag to enable auto resume of the cluster before the query execution
361
365
  scheme: string, Optional
@@ -387,6 +391,7 @@ class Connection(object):
387
391
  self._port = port
388
392
 
389
393
  self._secure_channel = secure
394
+ self._ssl_cert = ssl_cert
390
395
 
391
396
  self.catalog_name = catalog
392
397
 
@@ -511,6 +516,45 @@ class Connection(object):
511
516
 
512
517
  return self._cached_grpc_options
513
518
 
519
+ def _get_ssl_credentials(self):
520
+ """
521
+ Get SSL credentials for secure gRPC channel.
522
+
523
+ Handles three scenarios:
524
+ 1. ssl_cert is a string (file path): Read the PEM certificate from the file
525
+ 2. ssl_cert is bytes: Use the certificate content directly
526
+ 3. ssl_cert is None: Use system default CA bundle
527
+
528
+ Returns:
529
+ grpc.ChannelCredentials: SSL credentials for secure channel
530
+
531
+ Raises:
532
+ FileNotFoundError: If ssl_cert is a file path but the file doesn't exist
533
+ IOError: If ssl_cert file cannot be read
534
+ """
535
+ if self._ssl_cert is None:
536
+ # Use system default CA bundle
537
+ return grpc.ssl_channel_credentials()
538
+ elif isinstance(self._ssl_cert, str):
539
+ # ssl_cert is a file path - read the certificate from file
540
+ try:
541
+ with open(self._ssl_cert, 'rb') as cert_file:
542
+ root_ca_cert = cert_file.read()
543
+ return grpc.ssl_channel_credentials(root_certificates=root_ca_cert)
544
+ except FileNotFoundError:
545
+ logger.error(f"SSL certificate file not found: {self._ssl_cert}")
546
+ raise
547
+ except IOError as e:
548
+ logger.error(f"Failed to read SSL certificate file {self._ssl_cert}: {e}")
549
+ raise
550
+ elif isinstance(self._ssl_cert, bytes):
551
+ # ssl_cert is certificate content as bytes
552
+ return grpc.ssl_channel_credentials(root_certificates=self._ssl_cert)
553
+ else:
554
+ # Invalid type - log warning and use system default
555
+ logger.warning(f"Invalid ssl_cert type: {type(self._ssl_cert)}. Using system default CA bundle.")
556
+ return grpc.ssl_channel_credentials()
557
+
514
558
  def _create_client(self):
515
559
  """
516
560
  Creates a gRPC client for the connection.
@@ -531,7 +575,7 @@ class Connection(object):
531
575
  self._channel = grpc.secure_channel(
532
576
  target='{}:{}'.format(self._host, self._port),
533
577
  options=self._get_grpc_options,
534
- credentials=grpc.ssl_channel_credentials()
578
+ credentials=self._get_ssl_credentials()
535
579
  )
536
580
  else:
537
581
  self._channel = grpc.insecure_channel(
@@ -713,7 +757,8 @@ class Connection(object):
713
757
  secure_channel=self._secure_channel,
714
758
  cluster_uuid=self.cluster_name,
715
759
  timeout=self.grpc_auto_resume_timeout_seconds,
716
- debug=self._debug
760
+ debug=self._debug,
761
+ ssl_cert=self._ssl_cert
717
762
  ).resume()
718
763
  return status # Return boolean status directly
719
764
  else:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: e6data-python-connector
3
- Version: 2.3.12rc31
3
+ Version: 2.3.12rc32
4
4
  Summary: Client for the e6data distributed SQL Engine.
5
5
  Home-page: https://github.com/e6x-labs/e6data-python-connector
6
6
  Author: e6data, Inc.
@@ -39,7 +39,7 @@ Dynamic: summary
39
39
 
40
40
  # e6data Python Connector
41
41
 
42
- ![version](https://img.shields.io/badge/version-2.3.12rc31-blue.svg)
42
+ ![version](https://img.shields.io/badge/version-2.3.12rc32-blue.svg)
43
43
 
44
44
  ## Introduction
45
45
 
@@ -122,6 +122,7 @@ The `Connection` class supports the following parameters:
122
122
  | `catalog` | str | No | None | Catalog name |
123
123
  | `cluster_name` | str | No | None | Name of the cluster for cluster-specific operations |
124
124
  | `secure` | bool | No | False | Enable SSL/TLS for secure connections |
125
+ | `ssl_cert` | str/bytes | No | None | Path to CA certificate (PEM) or certificate bytes for HTTPS connections |
125
126
  | `auto_resume` | bool | No | True | Automatically resume cluster if suspended |
126
127
  | `grpc_options` | dict | No | None | Additional gRPC configuration options |
127
128
  | `debug` | bool | No | False | Enable debug logging for troubleshooting |
@@ -159,6 +160,72 @@ conn = Connection(
159
160
  )
160
161
  ```
161
162
 
163
+ #### HTTPS Connection with HAProxy
164
+
165
+ When connecting through HAProxy with HTTPS, you can provide a custom CA certificate for secure connections. The `ssl_cert` parameter accepts either a file path to a PEM certificate or the certificate content as bytes.
166
+
167
+ **Using a CA certificate file path:**
168
+
169
+ ```python
170
+ conn = Connection(
171
+ host=host,
172
+ port=443,
173
+ username=username,
174
+ password=password,
175
+ database=database,
176
+ secure=True,
177
+ ssl_cert='/path/to/ca-cert.pem' # Path to your CA certificate
178
+ )
179
+ ```
180
+
181
+ **Reading certificate content as bytes:**
182
+
183
+ ```python
184
+ # Read certificate file and pass as bytes
185
+ with open('/path/to/ca-cert.pem', 'rb') as cert_file:
186
+ cert_data = cert_file.read()
187
+
188
+ conn = Connection(
189
+ host=host,
190
+ port=443,
191
+ username=username,
192
+ password=password,
193
+ database=database,
194
+ secure=True,
195
+ ssl_cert=cert_data # Certificate content as bytes
196
+ )
197
+ ```
198
+
199
+ **Using system CA bundle for publicly signed certificates:**
200
+
201
+ ```python
202
+ # When ssl_cert is None, system default CA bundle is used
203
+ conn = Connection(
204
+ host=host,
205
+ port=443,
206
+ username=username,
207
+ password=password,
208
+ database=database,
209
+ secure=True # Uses system CA bundle by default
210
+ )
211
+ ```
212
+
213
+ **Connection pooling with custom CA certificate:**
214
+
215
+ ```python
216
+ pool = ConnectionPool(
217
+ min_size=2,
218
+ max_size=10,
219
+ host=host,
220
+ port=443,
221
+ username=username,
222
+ password=password,
223
+ database=database,
224
+ secure=True,
225
+ ssl_cert='/path/to/ca-cert.pem' # Custom CA certificate for pool connections
226
+ )
227
+ ```
228
+
162
229
  #### Handling Missing System Dependencies
163
230
 
164
231
  The e6data connector uses the `fastbinary` module (from Apache Thrift) for optimal performance when deserializing data. This module requires system-level dependencies (`python3-devel` and `gcc-c++`) to be installed.
@@ -12,7 +12,7 @@
12
12
 
13
13
  import setuptools
14
14
 
15
- VERSION = (2, 3, 12, 'rc31',)
15
+ VERSION = (2, 3, 12, 'rc32',)
16
16
 
17
17
 
18
18
  def get_long_desc():