matrixone-python-sdk 0.1.0__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.
- matrixone/__init__.py +155 -0
- matrixone/account.py +723 -0
- matrixone/async_client.py +3913 -0
- matrixone/async_metadata_manager.py +311 -0
- matrixone/async_orm.py +123 -0
- matrixone/async_vector_index_manager.py +633 -0
- matrixone/base_client.py +208 -0
- matrixone/client.py +4672 -0
- matrixone/config.py +452 -0
- matrixone/connection_hooks.py +286 -0
- matrixone/exceptions.py +89 -0
- matrixone/logger.py +782 -0
- matrixone/metadata.py +820 -0
- matrixone/moctl.py +219 -0
- matrixone/orm.py +2277 -0
- matrixone/pitr.py +646 -0
- matrixone/pubsub.py +771 -0
- matrixone/restore.py +411 -0
- matrixone/search_vector_index.py +1176 -0
- matrixone/snapshot.py +550 -0
- matrixone/sql_builder.py +844 -0
- matrixone/sqlalchemy_ext/__init__.py +161 -0
- matrixone/sqlalchemy_ext/adapters.py +163 -0
- matrixone/sqlalchemy_ext/dialect.py +534 -0
- matrixone/sqlalchemy_ext/fulltext_index.py +895 -0
- matrixone/sqlalchemy_ext/fulltext_search.py +1686 -0
- matrixone/sqlalchemy_ext/hnsw_config.py +194 -0
- matrixone/sqlalchemy_ext/ivf_config.py +252 -0
- matrixone/sqlalchemy_ext/table_builder.py +351 -0
- matrixone/sqlalchemy_ext/vector_index.py +1721 -0
- matrixone/sqlalchemy_ext/vector_type.py +948 -0
- matrixone/version.py +580 -0
- matrixone_python_sdk-0.1.0.dist-info/METADATA +706 -0
- matrixone_python_sdk-0.1.0.dist-info/RECORD +122 -0
- matrixone_python_sdk-0.1.0.dist-info/WHEEL +5 -0
- matrixone_python_sdk-0.1.0.dist-info/entry_points.txt +5 -0
- matrixone_python_sdk-0.1.0.dist-info/licenses/LICENSE +200 -0
- matrixone_python_sdk-0.1.0.dist-info/top_level.txt +2 -0
- tests/__init__.py +19 -0
- tests/offline/__init__.py +20 -0
- tests/offline/conftest.py +77 -0
- tests/offline/test_account.py +703 -0
- tests/offline/test_async_client_query_comprehensive.py +1218 -0
- tests/offline/test_basic.py +54 -0
- tests/offline/test_case_sensitivity.py +227 -0
- tests/offline/test_connection_hooks_offline.py +287 -0
- tests/offline/test_dialect_schema_handling.py +609 -0
- tests/offline/test_explain_methods.py +346 -0
- tests/offline/test_filter_logical_in.py +237 -0
- tests/offline/test_fulltext_search_comprehensive.py +795 -0
- tests/offline/test_ivf_config.py +249 -0
- tests/offline/test_join_methods.py +281 -0
- tests/offline/test_join_sqlalchemy_compatibility.py +276 -0
- tests/offline/test_logical_in_method.py +237 -0
- tests/offline/test_matrixone_version_parsing.py +264 -0
- tests/offline/test_metadata_offline.py +557 -0
- tests/offline/test_moctl.py +300 -0
- tests/offline/test_moctl_simple.py +251 -0
- tests/offline/test_model_support_offline.py +359 -0
- tests/offline/test_model_support_simple.py +225 -0
- tests/offline/test_pinecone_filter_offline.py +377 -0
- tests/offline/test_pitr.py +585 -0
- tests/offline/test_pubsub.py +712 -0
- tests/offline/test_query_update.py +283 -0
- tests/offline/test_restore.py +445 -0
- tests/offline/test_snapshot_comprehensive.py +384 -0
- tests/offline/test_sql_escaping_edge_cases.py +551 -0
- tests/offline/test_sqlalchemy_integration.py +382 -0
- tests/offline/test_sqlalchemy_vector_integration.py +434 -0
- tests/offline/test_table_builder.py +198 -0
- tests/offline/test_unified_filter.py +398 -0
- tests/offline/test_unified_transaction.py +495 -0
- tests/offline/test_vector_index.py +238 -0
- tests/offline/test_vector_operations.py +688 -0
- tests/offline/test_vector_type.py +174 -0
- tests/offline/test_version_core.py +328 -0
- tests/offline/test_version_management.py +372 -0
- tests/offline/test_version_standalone.py +652 -0
- tests/online/__init__.py +20 -0
- tests/online/conftest.py +216 -0
- tests/online/test_account_management.py +194 -0
- tests/online/test_advanced_features.py +344 -0
- tests/online/test_async_client_interfaces.py +330 -0
- tests/online/test_async_client_online.py +285 -0
- tests/online/test_async_model_insert_online.py +293 -0
- tests/online/test_async_orm_online.py +300 -0
- tests/online/test_async_simple_query_online.py +802 -0
- tests/online/test_async_transaction_simple_query.py +300 -0
- tests/online/test_basic_connection.py +130 -0
- tests/online/test_client_online.py +238 -0
- tests/online/test_config.py +90 -0
- tests/online/test_config_validation.py +123 -0
- tests/online/test_connection_hooks_new_online.py +217 -0
- tests/online/test_dialect_schema_handling_online.py +331 -0
- tests/online/test_filter_logical_in_online.py +374 -0
- tests/online/test_fulltext_comprehensive.py +1773 -0
- tests/online/test_fulltext_label_online.py +433 -0
- tests/online/test_fulltext_search_online.py +842 -0
- tests/online/test_ivf_stats_online.py +506 -0
- tests/online/test_logger_integration.py +311 -0
- tests/online/test_matrixone_query_orm.py +540 -0
- tests/online/test_metadata_online.py +579 -0
- tests/online/test_model_insert_online.py +255 -0
- tests/online/test_mysql_driver_validation.py +213 -0
- tests/online/test_orm_advanced_features.py +2022 -0
- tests/online/test_orm_cte_integration.py +269 -0
- tests/online/test_orm_online.py +270 -0
- tests/online/test_pinecone_filter.py +708 -0
- tests/online/test_pubsub_operations.py +352 -0
- tests/online/test_query_methods.py +225 -0
- tests/online/test_query_update_online.py +433 -0
- tests/online/test_search_vector_index.py +557 -0
- tests/online/test_simple_fulltext_online.py +915 -0
- tests/online/test_snapshot_comprehensive.py +998 -0
- tests/online/test_sqlalchemy_engine_integration.py +336 -0
- tests/online/test_sqlalchemy_integration.py +425 -0
- tests/online/test_transaction_contexts.py +1219 -0
- tests/online/test_transaction_insert_methods.py +356 -0
- tests/online/test_transaction_query_methods.py +288 -0
- tests/online/test_unified_filter_online.py +529 -0
- tests/online/test_vector_comprehensive.py +706 -0
- tests/online/test_version_management.py +291 -0
matrixone/pitr.py
ADDED
@@ -0,0 +1,646 @@
|
|
1
|
+
# Copyright 2021 - 2022 Matrix Origin
|
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
|
+
"""
|
16
|
+
MatrixOne Python SDK - PITR Manager
|
17
|
+
Provides Point-in-Time Recovery functionality for MatrixOne
|
18
|
+
"""
|
19
|
+
|
20
|
+
from datetime import datetime
|
21
|
+
from typing import List, Optional
|
22
|
+
|
23
|
+
from .exceptions import PitrError
|
24
|
+
from .version import requires_version
|
25
|
+
|
26
|
+
|
27
|
+
class Pitr:
|
28
|
+
"""PITR (Point-in-Time Recovery) object"""
|
29
|
+
|
30
|
+
def __init__(
|
31
|
+
self,
|
32
|
+
name: str,
|
33
|
+
created_time: datetime,
|
34
|
+
modified_time: datetime,
|
35
|
+
level: str,
|
36
|
+
account_name: Optional[str] = None,
|
37
|
+
database_name: Optional[str] = None,
|
38
|
+
table_name: Optional[str] = None,
|
39
|
+
range_value: int = 1,
|
40
|
+
range_unit: str = "d",
|
41
|
+
):
|
42
|
+
"""
|
43
|
+
Initialize PITR object
|
44
|
+
|
45
|
+
Args::
|
46
|
+
|
47
|
+
name: PITR name
|
48
|
+
created_time: Creation time
|
49
|
+
modified_time: Last modification time
|
50
|
+
level: PITR level (cluster, account, database, table)
|
51
|
+
account_name: Account name (for account/database/table level)
|
52
|
+
database_name: Database name (for database/table level)
|
53
|
+
table_name: Table name (for table level)
|
54
|
+
range_value: Time range value (1-100)
|
55
|
+
range_unit: Time range unit (h, d, mo, y)
|
56
|
+
"""
|
57
|
+
self.name = name
|
58
|
+
self.created_time = created_time
|
59
|
+
self.modified_time = modified_time
|
60
|
+
self.level = level
|
61
|
+
self.account_name = account_name
|
62
|
+
self.database_name = database_name
|
63
|
+
self.table_name = table_name
|
64
|
+
self.range_value = range_value
|
65
|
+
self.range_unit = range_unit
|
66
|
+
|
67
|
+
def __repr__(self):
|
68
|
+
return f"<Pitr(name='{self.name}', level='{self.level}', " f"range={self.range_value}{self.range_unit})>"
|
69
|
+
|
70
|
+
|
71
|
+
class PitrManager:
|
72
|
+
"""
|
73
|
+
Manager for Point-in-Time Recovery (PITR) operations in MatrixOne.
|
74
|
+
|
75
|
+
This class provides comprehensive PITR functionality for recovering data
|
76
|
+
to specific points in time. PITR allows you to restore databases or
|
77
|
+
tables to their state at any point in time, providing granular recovery
|
78
|
+
capabilities for data protection and disaster recovery.
|
79
|
+
|
80
|
+
Key Features:
|
81
|
+
|
82
|
+
- Point-in-time recovery for databases and tables
|
83
|
+
- Recovery to specific timestamps
|
84
|
+
- Integration with backup and snapshot systems
|
85
|
+
- Transaction-aware recovery operations
|
86
|
+
- Support for both cluster and table-level recovery
|
87
|
+
|
88
|
+
Supported Recovery Levels:
|
89
|
+
- CLUSTER: Full cluster recovery to a specific point in time
|
90
|
+
- DATABASE: Database-level recovery to a specific point in time
|
91
|
+
- TABLE: Table-level recovery to a specific point in time
|
92
|
+
|
93
|
+
Usage Examples::
|
94
|
+
|
95
|
+
# Initialize PITR manager
|
96
|
+
pitr = client.pitr
|
97
|
+
|
98
|
+
# Recover database to specific timestamp
|
99
|
+
pitr.recover_database(
|
100
|
+
database='my_database',
|
101
|
+
timestamp='2024-01-15 10:30:00',
|
102
|
+
target_database='recovered_database'
|
103
|
+
)
|
104
|
+
|
105
|
+
# Recover table to specific timestamp
|
106
|
+
pitr.recover_table(
|
107
|
+
database='my_database',
|
108
|
+
table='users',
|
109
|
+
timestamp='2024-01-15 10:30:00',
|
110
|
+
target_database='recovered_database',
|
111
|
+
target_table='recovered_users'
|
112
|
+
)
|
113
|
+
|
114
|
+
# List available recovery points
|
115
|
+
recovery_points = pitr.list_recovery_points('my_database')
|
116
|
+
|
117
|
+
# Get recovery status
|
118
|
+
status = pitr.get_recovery_status('recovery_job_id')
|
119
|
+
|
120
|
+
Note: PITR functionality requires MatrixOne version 1.0.0 or higher and
|
121
|
+
appropriate backup infrastructure. Recovery operations may take significant
|
122
|
+
time depending on the amount of data and the target timestamp.
|
123
|
+
"""
|
124
|
+
|
125
|
+
def __init__(self, client):
|
126
|
+
"""Initialize PitrManager with client connection"""
|
127
|
+
self._client = client
|
128
|
+
|
129
|
+
@requires_version(
|
130
|
+
min_version="1.0.0",
|
131
|
+
feature_name="pitr_cluster_level",
|
132
|
+
description="Cluster-level Point-in-Time Recovery functionality",
|
133
|
+
alternative="Use snapshot restore instead",
|
134
|
+
)
|
135
|
+
def create_cluster_pitr(self, name: str, range_value: int = 1, range_unit: str = "d") -> Pitr:
|
136
|
+
"""
|
137
|
+
Create cluster-level PITR
|
138
|
+
|
139
|
+
Args::
|
140
|
+
|
141
|
+
name: PITR name
|
142
|
+
range_value: Time range value (1-100)
|
143
|
+
range_unit: Time range unit (h, d, mo, y)
|
144
|
+
|
145
|
+
Returns::
|
146
|
+
|
147
|
+
Pitr: Created PITR object
|
148
|
+
|
149
|
+
Raises::
|
150
|
+
|
151
|
+
PitrError: If PITR creation fails
|
152
|
+
|
153
|
+
Example
|
154
|
+
|
155
|
+
>>> pitr = client.pitr.create_cluster_pitr("cluster_pitr1", 1, "d")
|
156
|
+
"""
|
157
|
+
try:
|
158
|
+
self._validate_range(range_value, range_unit)
|
159
|
+
|
160
|
+
sql = f"CREATE PITR {self._client._escape_identifier(name)} " f"FOR CLUSTER RANGE {range_value} '{range_unit}'"
|
161
|
+
|
162
|
+
result = self._client.execute(sql)
|
163
|
+
if result is None:
|
164
|
+
raise PitrError(f"Failed to create cluster PITR '{name}'") from None
|
165
|
+
|
166
|
+
# Return PITR object (we'll get the actual details via SHOW PITR)
|
167
|
+
return self.get(name)
|
168
|
+
|
169
|
+
except Exception as e:
|
170
|
+
raise PitrError(f"Failed to create cluster PITR '{name}': {e}") from None
|
171
|
+
|
172
|
+
def create_account_pitr(
|
173
|
+
self,
|
174
|
+
name: str,
|
175
|
+
account_name: Optional[str] = None,
|
176
|
+
range_value: int = 1,
|
177
|
+
range_unit: str = "d",
|
178
|
+
) -> Pitr:
|
179
|
+
"""
|
180
|
+
Create account-level PITR
|
181
|
+
|
182
|
+
Args::
|
183
|
+
|
184
|
+
name: PITR name
|
185
|
+
account_name: Account name (None for current account)
|
186
|
+
range_value: Time range value (1-100)
|
187
|
+
range_unit: Time range unit (h, d, mo, y)
|
188
|
+
|
189
|
+
Returns::
|
190
|
+
|
191
|
+
Pitr: Created PITR object
|
192
|
+
|
193
|
+
Raises::
|
194
|
+
|
195
|
+
PitrError: If PITR creation fails
|
196
|
+
|
197
|
+
Example
|
198
|
+
|
199
|
+
>>> # For current account
|
200
|
+
>>> pitr = client.pitr.create_account_pitr("account_pitr1", range_value=2, range_unit="h")
|
201
|
+
>>>
|
202
|
+
>>> # For specific account (cluster admin only)
|
203
|
+
>>> pitr = client.pitr.create_account_pitr("account_pitr1", "acc1", 1, "d")
|
204
|
+
"""
|
205
|
+
try:
|
206
|
+
self._validate_range(range_value, range_unit)
|
207
|
+
|
208
|
+
if account_name:
|
209
|
+
sql = (
|
210
|
+
f"CREATE PITR {self._client._escape_identifier(name)} "
|
211
|
+
f"FOR ACCOUNT {self._client._escape_identifier(account_name)} "
|
212
|
+
f"RANGE {range_value} '{range_unit}'"
|
213
|
+
)
|
214
|
+
else:
|
215
|
+
sql = (
|
216
|
+
f"CREATE PITR {self._client._escape_identifier(name)} " f"FOR ACCOUNT RANGE {range_value} '{range_unit}'"
|
217
|
+
)
|
218
|
+
|
219
|
+
result = self._client.execute(sql)
|
220
|
+
if result is None:
|
221
|
+
raise PitrError(f"Failed to create account PITR '{name}'") from None
|
222
|
+
|
223
|
+
return self.get(name)
|
224
|
+
|
225
|
+
except Exception as e:
|
226
|
+
raise PitrError(f"Failed to create account PITR '{name}': {e}") from None
|
227
|
+
|
228
|
+
def create_database_pitr(self, name: str, database_name: str, range_value: int = 1, range_unit: str = "d") -> Pitr:
|
229
|
+
"""
|
230
|
+
Create database-level PITR
|
231
|
+
|
232
|
+
Args::
|
233
|
+
|
234
|
+
name: PITR name
|
235
|
+
database_name: Database name
|
236
|
+
range_value: Time range value (1-100)
|
237
|
+
range_unit: Time range unit (h, d, mo, y)
|
238
|
+
|
239
|
+
Returns::
|
240
|
+
|
241
|
+
Pitr: Created PITR object
|
242
|
+
|
243
|
+
Raises::
|
244
|
+
|
245
|
+
PitrError: If PITR creation fails
|
246
|
+
|
247
|
+
Example
|
248
|
+
|
249
|
+
>>> pitr = client.pitr.create_database_pitr("db_pitr1", "db1", 1, "y")
|
250
|
+
"""
|
251
|
+
try:
|
252
|
+
self._validate_range(range_value, range_unit)
|
253
|
+
|
254
|
+
sql = (
|
255
|
+
f"CREATE PITR {self._client._escape_identifier(name)} "
|
256
|
+
f"FOR DATABASE {self._client._escape_identifier(database_name)} "
|
257
|
+
f"RANGE {range_value} '{range_unit}'"
|
258
|
+
)
|
259
|
+
|
260
|
+
result = self._client.execute(sql)
|
261
|
+
if result is None:
|
262
|
+
raise PitrError(f"Failed to create database PITR '{name}'") from None
|
263
|
+
|
264
|
+
return self.get(name)
|
265
|
+
|
266
|
+
except Exception as e:
|
267
|
+
raise PitrError(f"Failed to create database PITR '{name}': {e}") from None
|
268
|
+
|
269
|
+
def create_table_pitr(
|
270
|
+
self,
|
271
|
+
name: str,
|
272
|
+
database_name: str,
|
273
|
+
table_name: str,
|
274
|
+
range_value: int = 1,
|
275
|
+
range_unit: str = "d",
|
276
|
+
) -> Pitr:
|
277
|
+
"""
|
278
|
+
Create table-level PITR
|
279
|
+
|
280
|
+
Args::
|
281
|
+
|
282
|
+
name: PITR name
|
283
|
+
database_name: Database name
|
284
|
+
table_name: Table name
|
285
|
+
range_value: Time range value (1-100)
|
286
|
+
range_unit: Time range unit (h, d, mo, y)
|
287
|
+
|
288
|
+
Returns::
|
289
|
+
|
290
|
+
Pitr: Created PITR object
|
291
|
+
|
292
|
+
Raises::
|
293
|
+
|
294
|
+
PitrError: If PITR creation fails
|
295
|
+
|
296
|
+
Example
|
297
|
+
|
298
|
+
>>> pitr = client.pitr.create_table_pitr("tab_pitr1", "db1", "t1", 1, "y")
|
299
|
+
"""
|
300
|
+
try:
|
301
|
+
self._validate_range(range_value, range_unit)
|
302
|
+
|
303
|
+
sql = (
|
304
|
+
f"CREATE PITR {self._client._escape_identifier(name)} "
|
305
|
+
f"FOR TABLE {self._client._escape_identifier(database_name)} "
|
306
|
+
f"{self._client._escape_identifier(table_name)} "
|
307
|
+
f"RANGE {range_value} '{range_unit}'"
|
308
|
+
)
|
309
|
+
|
310
|
+
result = self._client.execute(sql)
|
311
|
+
if result is None:
|
312
|
+
raise PitrError(f"Failed to create table PITR '{name}'") from None
|
313
|
+
|
314
|
+
return self.get(name)
|
315
|
+
|
316
|
+
except Exception as e:
|
317
|
+
raise PitrError(f"Failed to create table PITR '{name}': {e}") from None
|
318
|
+
|
319
|
+
def get(self, name: str) -> Pitr:
|
320
|
+
"""
|
321
|
+
Get PITR by name
|
322
|
+
|
323
|
+
Args::
|
324
|
+
|
325
|
+
name: PITR name
|
326
|
+
|
327
|
+
Returns::
|
328
|
+
|
329
|
+
Pitr: PITR object
|
330
|
+
|
331
|
+
Raises::
|
332
|
+
|
333
|
+
PitrError: If PITR not found
|
334
|
+
"""
|
335
|
+
try:
|
336
|
+
sql = f"SHOW PITR WHERE pitr_name = {self._client._escape_string(name)}"
|
337
|
+
result = self._client.execute(sql)
|
338
|
+
|
339
|
+
if not result or not result.rows:
|
340
|
+
raise PitrError(f"PITR '{name}' not found") from None
|
341
|
+
|
342
|
+
row = result.rows[0]
|
343
|
+
return self._row_to_pitr(row)
|
344
|
+
|
345
|
+
except Exception as e:
|
346
|
+
raise PitrError(f"Failed to get PITR '{name}': {e}") from None
|
347
|
+
|
348
|
+
def list(
|
349
|
+
self,
|
350
|
+
level: Optional[str] = None,
|
351
|
+
account_name: Optional[str] = None,
|
352
|
+
database_name: Optional[str] = None,
|
353
|
+
table_name: Optional[str] = None,
|
354
|
+
) -> List[Pitr]:
|
355
|
+
"""
|
356
|
+
List PITRs with optional filters
|
357
|
+
|
358
|
+
Args::
|
359
|
+
|
360
|
+
level: Filter by PITR level (cluster, account, database, table)
|
361
|
+
account_name: Filter by account name
|
362
|
+
database_name: Filter by database name
|
363
|
+
table_name: Filter by table name
|
364
|
+
|
365
|
+
Returns::
|
366
|
+
|
367
|
+
List[Pitr]: List of PITR objects
|
368
|
+
"""
|
369
|
+
try:
|
370
|
+
conditions = []
|
371
|
+
|
372
|
+
if level:
|
373
|
+
conditions.append(f"pitr_level = {self._client._escape_string(level)}")
|
374
|
+
if account_name:
|
375
|
+
conditions.append(f"account_name = {self._client._escape_string(account_name)}")
|
376
|
+
if database_name:
|
377
|
+
conditions.append(f"database_name = {self._client._escape_string(database_name)}")
|
378
|
+
if table_name:
|
379
|
+
conditions.append(f"table_name = {self._client._escape_string(table_name)}")
|
380
|
+
|
381
|
+
if conditions:
|
382
|
+
where_clause = " WHERE " + " AND ".join(conditions)
|
383
|
+
else:
|
384
|
+
where_clause = ""
|
385
|
+
|
386
|
+
sql = f"SHOW PITR{where_clause}"
|
387
|
+
result = self._client.execute(sql)
|
388
|
+
|
389
|
+
if not result or not result.rows:
|
390
|
+
return []
|
391
|
+
|
392
|
+
return [self._row_to_pitr(row) for row in result.rows]
|
393
|
+
|
394
|
+
except Exception as e:
|
395
|
+
raise PitrError(f"Failed to list PITRs: {e}") from None
|
396
|
+
|
397
|
+
def alter(self, name: str, range_value: int, range_unit: str) -> Pitr:
|
398
|
+
"""
|
399
|
+
Alter PITR range
|
400
|
+
|
401
|
+
Args::
|
402
|
+
|
403
|
+
name: PITR name
|
404
|
+
range_value: New time range value (1-100)
|
405
|
+
range_unit: New time range unit (h, d, mo, y)
|
406
|
+
|
407
|
+
Returns::
|
408
|
+
|
409
|
+
Pitr: Updated PITR object
|
410
|
+
|
411
|
+
Raises::
|
412
|
+
|
413
|
+
PitrError: If PITR alteration fails
|
414
|
+
"""
|
415
|
+
try:
|
416
|
+
self._validate_range(range_value, range_unit)
|
417
|
+
|
418
|
+
sql = f"ALTER PITR {self._client._escape_identifier(name)} " f"RANGE {range_value} '{range_unit}'"
|
419
|
+
|
420
|
+
result = self._client.execute(sql)
|
421
|
+
if result is None:
|
422
|
+
raise PitrError(f"Failed to alter PITR '{name}'") from None
|
423
|
+
|
424
|
+
return self.get(name)
|
425
|
+
|
426
|
+
except Exception as e:
|
427
|
+
raise PitrError(f"Failed to alter PITR '{name}': {e}") from None
|
428
|
+
|
429
|
+
def delete(self, name: str) -> bool:
|
430
|
+
"""
|
431
|
+
Delete PITR
|
432
|
+
|
433
|
+
Args::
|
434
|
+
|
435
|
+
name: PITR name
|
436
|
+
|
437
|
+
Returns::
|
438
|
+
|
439
|
+
bool: True if deletion was successful
|
440
|
+
|
441
|
+
Raises::
|
442
|
+
|
443
|
+
PitrError: If PITR deletion fails
|
444
|
+
"""
|
445
|
+
try:
|
446
|
+
sql = f"DROP PITR {self._client._escape_identifier(name)}"
|
447
|
+
result = self._client.execute(sql)
|
448
|
+
return result is not None
|
449
|
+
|
450
|
+
except Exception as e:
|
451
|
+
raise PitrError(f"Failed to delete PITR '{name}': {e}") from None
|
452
|
+
|
453
|
+
def _validate_range(self, range_value: int, range_unit: str) -> None:
|
454
|
+
"""Validate PITR range parameters"""
|
455
|
+
if not (1 <= range_value <= 100):
|
456
|
+
raise PitrError("Range value must be between 1 and 100") from None
|
457
|
+
|
458
|
+
valid_units = ["h", "d", "mo", "y"]
|
459
|
+
if range_unit not in valid_units:
|
460
|
+
raise PitrError(f"Range unit must be one of: {', '.join(valid_units)}") from None
|
461
|
+
|
462
|
+
def _row_to_pitr(self, row: tuple) -> Pitr:
|
463
|
+
"""Convert database row to Pitr object"""
|
464
|
+
# Expected columns: pitr_name, created_time, modified_time, pitr_level,
|
465
|
+
# account_name, database_name, table_name, pitr_length, pitr_unit
|
466
|
+
return Pitr(
|
467
|
+
name=row[0],
|
468
|
+
created_time=row[1],
|
469
|
+
modified_time=row[2],
|
470
|
+
level=row[3],
|
471
|
+
account_name=row[4] if row[4] != "*" else None,
|
472
|
+
database_name=row[5] if row[5] != "*" else None,
|
473
|
+
table_name=row[6] if row[6] != "*" else None,
|
474
|
+
range_value=row[7],
|
475
|
+
range_unit=row[8],
|
476
|
+
)
|
477
|
+
|
478
|
+
|
479
|
+
class TransactionPitrManager(PitrManager):
|
480
|
+
"""PitrManager for use within transactions"""
|
481
|
+
|
482
|
+
def __init__(self, client, transaction_wrapper):
|
483
|
+
"""Initialize TransactionPitrManager with client and transaction wrapper"""
|
484
|
+
super().__init__(client)
|
485
|
+
self._transaction_wrapper = transaction_wrapper
|
486
|
+
|
487
|
+
def create_cluster_pitr(self, name: str, range_value: int = 1, range_unit: str = "d") -> Pitr:
|
488
|
+
"""Create cluster PITR within transaction"""
|
489
|
+
return self._create_pitr_with_executor("cluster", name, range_value, range_unit)
|
490
|
+
|
491
|
+
def create_account_pitr(
|
492
|
+
self,
|
493
|
+
name: str,
|
494
|
+
account_name: Optional[str] = None,
|
495
|
+
range_value: int = 1,
|
496
|
+
range_unit: str = "d",
|
497
|
+
) -> Pitr:
|
498
|
+
"""Create account PITR within transaction"""
|
499
|
+
return self._create_pitr_with_executor("account", name, range_value, range_unit, account_name)
|
500
|
+
|
501
|
+
def create_database_pitr(self, name: str, database_name: str, range_value: int = 1, range_unit: str = "d") -> Pitr:
|
502
|
+
"""Create database PITR within transaction"""
|
503
|
+
return self._create_pitr_with_executor("database", name, range_value, range_unit, None, database_name)
|
504
|
+
|
505
|
+
def create_table_pitr(
|
506
|
+
self,
|
507
|
+
name: str,
|
508
|
+
database_name: str,
|
509
|
+
table_name: str,
|
510
|
+
range_value: int = 1,
|
511
|
+
range_unit: str = "d",
|
512
|
+
) -> Pitr:
|
513
|
+
"""Create table PITR within transaction"""
|
514
|
+
return self._create_pitr_with_executor("table", name, range_value, range_unit, None, database_name, table_name)
|
515
|
+
|
516
|
+
def _create_pitr_with_executor(
|
517
|
+
self,
|
518
|
+
level: str,
|
519
|
+
name: str,
|
520
|
+
range_value: int,
|
521
|
+
range_unit: str,
|
522
|
+
account_name: Optional[str] = None,
|
523
|
+
database_name: Optional[str] = None,
|
524
|
+
table_name: Optional[str] = None,
|
525
|
+
) -> Pitr:
|
526
|
+
"""Create PITR with custom executor (for transaction support)"""
|
527
|
+
try:
|
528
|
+
self._validate_range(range_value, range_unit)
|
529
|
+
|
530
|
+
if level == "cluster":
|
531
|
+
sql = (
|
532
|
+
f"CREATE PITR {self._client._escape_identifier(name)} " f"FOR CLUSTER RANGE {range_value} '{range_unit}'"
|
533
|
+
)
|
534
|
+
elif level == "account":
|
535
|
+
if account_name:
|
536
|
+
sql = (
|
537
|
+
f"CREATE PITR {self._client._escape_identifier(name)} "
|
538
|
+
f"FOR ACCOUNT {self._client._escape_identifier(account_name)} "
|
539
|
+
f"RANGE {range_value} '{range_unit}'"
|
540
|
+
)
|
541
|
+
else:
|
542
|
+
sql = (
|
543
|
+
f"CREATE PITR {self._client._escape_identifier(name)} "
|
544
|
+
f"FOR ACCOUNT RANGE {range_value} '{range_unit}'"
|
545
|
+
)
|
546
|
+
elif level == "database":
|
547
|
+
sql = (
|
548
|
+
f"CREATE PITR {self._client._escape_identifier(name)} "
|
549
|
+
f"FOR DATABASE {self._client._escape_identifier(database_name)} "
|
550
|
+
f"RANGE {range_value} '{range_unit}'"
|
551
|
+
)
|
552
|
+
elif level == "table":
|
553
|
+
sql = (
|
554
|
+
f"CREATE PITR {self._client._escape_identifier(name)} "
|
555
|
+
f"FOR TABLE {self._client._escape_identifier(database_name)} "
|
556
|
+
f"TABLE {self._client._escape_identifier(table_name)} "
|
557
|
+
f"RANGE {range_value} '{range_unit}'"
|
558
|
+
)
|
559
|
+
else:
|
560
|
+
raise PitrError(f"Invalid PITR level: {level}") from None
|
561
|
+
|
562
|
+
result = self._transaction_wrapper.execute(sql)
|
563
|
+
if result is None:
|
564
|
+
raise PitrError(f"Failed to create {level} PITR '{name}'") from None
|
565
|
+
|
566
|
+
return self.get(name)
|
567
|
+
|
568
|
+
except Exception as e:
|
569
|
+
raise PitrError(f"Failed to create {level} PITR '{name}': {e}") from None
|
570
|
+
|
571
|
+
def get(self, name: str) -> Pitr:
|
572
|
+
"""Get PITR within transaction"""
|
573
|
+
try:
|
574
|
+
sql = f"SHOW PITR WHERE pitr_name = {self._client._escape_string(name)}"
|
575
|
+
result = self._transaction_wrapper.execute(sql)
|
576
|
+
|
577
|
+
if not result or not result.rows:
|
578
|
+
raise PitrError(f"PITR '{name}' not found") from None
|
579
|
+
|
580
|
+
row = result.rows[0]
|
581
|
+
return self._row_to_pitr(row)
|
582
|
+
|
583
|
+
except Exception as e:
|
584
|
+
raise PitrError(f"Failed to get PITR '{name}': {e}") from None
|
585
|
+
|
586
|
+
def list(
|
587
|
+
self,
|
588
|
+
level: Optional[str] = None,
|
589
|
+
account_name: Optional[str] = None,
|
590
|
+
database_name: Optional[str] = None,
|
591
|
+
table_name: Optional[str] = None,
|
592
|
+
) -> List[Pitr]:
|
593
|
+
"""List PITRs within transaction"""
|
594
|
+
try:
|
595
|
+
conditions = []
|
596
|
+
|
597
|
+
if level:
|
598
|
+
conditions.append(f"pitr_level = {self._client._escape_string(level)}")
|
599
|
+
if account_name:
|
600
|
+
conditions.append(f"account_name = {self._client._escape_string(account_name)}")
|
601
|
+
if database_name:
|
602
|
+
conditions.append(f"database_name = {self._client._escape_string(database_name)}")
|
603
|
+
if table_name:
|
604
|
+
conditions.append(f"table_name = {self._client._escape_string(table_name)}")
|
605
|
+
|
606
|
+
if conditions:
|
607
|
+
where_clause = " WHERE " + " AND ".join(conditions)
|
608
|
+
else:
|
609
|
+
where_clause = ""
|
610
|
+
|
611
|
+
sql = f"SHOW PITR{where_clause}"
|
612
|
+
result = self._transaction_wrapper.execute(sql)
|
613
|
+
|
614
|
+
if not result or not result.rows:
|
615
|
+
return []
|
616
|
+
|
617
|
+
return [self._row_to_pitr(row) for row in result.rows]
|
618
|
+
|
619
|
+
except Exception as e:
|
620
|
+
raise PitrError(f"Failed to list PITRs: {e}") from None
|
621
|
+
|
622
|
+
def alter(self, name: str, range_value: int, range_unit: str) -> Pitr:
|
623
|
+
"""Alter PITR within transaction"""
|
624
|
+
try:
|
625
|
+
self._validate_range(range_value, range_unit)
|
626
|
+
|
627
|
+
sql = f"ALTER PITR {self._client._escape_identifier(name)} " f"RANGE {range_value} '{range_unit}'"
|
628
|
+
|
629
|
+
result = self._transaction_wrapper.execute(sql)
|
630
|
+
if result is None:
|
631
|
+
raise PitrError(f"Failed to alter PITR '{name}'") from None
|
632
|
+
|
633
|
+
return self.get(name)
|
634
|
+
|
635
|
+
except Exception as e:
|
636
|
+
raise PitrError(f"Failed to alter PITR '{name}': {e}") from None
|
637
|
+
|
638
|
+
def delete(self, name: str) -> bool:
|
639
|
+
"""Delete PITR within transaction"""
|
640
|
+
try:
|
641
|
+
sql = f"DROP PITR {self._client._escape_identifier(name)}"
|
642
|
+
result = self._transaction_wrapper.execute(sql)
|
643
|
+
return result is not None
|
644
|
+
|
645
|
+
except Exception as e:
|
646
|
+
raise PitrError(f"Failed to delete PITR '{name}': {e}") from None
|