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.
Files changed (122) hide show
  1. matrixone/__init__.py +155 -0
  2. matrixone/account.py +723 -0
  3. matrixone/async_client.py +3913 -0
  4. matrixone/async_metadata_manager.py +311 -0
  5. matrixone/async_orm.py +123 -0
  6. matrixone/async_vector_index_manager.py +633 -0
  7. matrixone/base_client.py +208 -0
  8. matrixone/client.py +4672 -0
  9. matrixone/config.py +452 -0
  10. matrixone/connection_hooks.py +286 -0
  11. matrixone/exceptions.py +89 -0
  12. matrixone/logger.py +782 -0
  13. matrixone/metadata.py +820 -0
  14. matrixone/moctl.py +219 -0
  15. matrixone/orm.py +2277 -0
  16. matrixone/pitr.py +646 -0
  17. matrixone/pubsub.py +771 -0
  18. matrixone/restore.py +411 -0
  19. matrixone/search_vector_index.py +1176 -0
  20. matrixone/snapshot.py +550 -0
  21. matrixone/sql_builder.py +844 -0
  22. matrixone/sqlalchemy_ext/__init__.py +161 -0
  23. matrixone/sqlalchemy_ext/adapters.py +163 -0
  24. matrixone/sqlalchemy_ext/dialect.py +534 -0
  25. matrixone/sqlalchemy_ext/fulltext_index.py +895 -0
  26. matrixone/sqlalchemy_ext/fulltext_search.py +1686 -0
  27. matrixone/sqlalchemy_ext/hnsw_config.py +194 -0
  28. matrixone/sqlalchemy_ext/ivf_config.py +252 -0
  29. matrixone/sqlalchemy_ext/table_builder.py +351 -0
  30. matrixone/sqlalchemy_ext/vector_index.py +1721 -0
  31. matrixone/sqlalchemy_ext/vector_type.py +948 -0
  32. matrixone/version.py +580 -0
  33. matrixone_python_sdk-0.1.0.dist-info/METADATA +706 -0
  34. matrixone_python_sdk-0.1.0.dist-info/RECORD +122 -0
  35. matrixone_python_sdk-0.1.0.dist-info/WHEEL +5 -0
  36. matrixone_python_sdk-0.1.0.dist-info/entry_points.txt +5 -0
  37. matrixone_python_sdk-0.1.0.dist-info/licenses/LICENSE +200 -0
  38. matrixone_python_sdk-0.1.0.dist-info/top_level.txt +2 -0
  39. tests/__init__.py +19 -0
  40. tests/offline/__init__.py +20 -0
  41. tests/offline/conftest.py +77 -0
  42. tests/offline/test_account.py +703 -0
  43. tests/offline/test_async_client_query_comprehensive.py +1218 -0
  44. tests/offline/test_basic.py +54 -0
  45. tests/offline/test_case_sensitivity.py +227 -0
  46. tests/offline/test_connection_hooks_offline.py +287 -0
  47. tests/offline/test_dialect_schema_handling.py +609 -0
  48. tests/offline/test_explain_methods.py +346 -0
  49. tests/offline/test_filter_logical_in.py +237 -0
  50. tests/offline/test_fulltext_search_comprehensive.py +795 -0
  51. tests/offline/test_ivf_config.py +249 -0
  52. tests/offline/test_join_methods.py +281 -0
  53. tests/offline/test_join_sqlalchemy_compatibility.py +276 -0
  54. tests/offline/test_logical_in_method.py +237 -0
  55. tests/offline/test_matrixone_version_parsing.py +264 -0
  56. tests/offline/test_metadata_offline.py +557 -0
  57. tests/offline/test_moctl.py +300 -0
  58. tests/offline/test_moctl_simple.py +251 -0
  59. tests/offline/test_model_support_offline.py +359 -0
  60. tests/offline/test_model_support_simple.py +225 -0
  61. tests/offline/test_pinecone_filter_offline.py +377 -0
  62. tests/offline/test_pitr.py +585 -0
  63. tests/offline/test_pubsub.py +712 -0
  64. tests/offline/test_query_update.py +283 -0
  65. tests/offline/test_restore.py +445 -0
  66. tests/offline/test_snapshot_comprehensive.py +384 -0
  67. tests/offline/test_sql_escaping_edge_cases.py +551 -0
  68. tests/offline/test_sqlalchemy_integration.py +382 -0
  69. tests/offline/test_sqlalchemy_vector_integration.py +434 -0
  70. tests/offline/test_table_builder.py +198 -0
  71. tests/offline/test_unified_filter.py +398 -0
  72. tests/offline/test_unified_transaction.py +495 -0
  73. tests/offline/test_vector_index.py +238 -0
  74. tests/offline/test_vector_operations.py +688 -0
  75. tests/offline/test_vector_type.py +174 -0
  76. tests/offline/test_version_core.py +328 -0
  77. tests/offline/test_version_management.py +372 -0
  78. tests/offline/test_version_standalone.py +652 -0
  79. tests/online/__init__.py +20 -0
  80. tests/online/conftest.py +216 -0
  81. tests/online/test_account_management.py +194 -0
  82. tests/online/test_advanced_features.py +344 -0
  83. tests/online/test_async_client_interfaces.py +330 -0
  84. tests/online/test_async_client_online.py +285 -0
  85. tests/online/test_async_model_insert_online.py +293 -0
  86. tests/online/test_async_orm_online.py +300 -0
  87. tests/online/test_async_simple_query_online.py +802 -0
  88. tests/online/test_async_transaction_simple_query.py +300 -0
  89. tests/online/test_basic_connection.py +130 -0
  90. tests/online/test_client_online.py +238 -0
  91. tests/online/test_config.py +90 -0
  92. tests/online/test_config_validation.py +123 -0
  93. tests/online/test_connection_hooks_new_online.py +217 -0
  94. tests/online/test_dialect_schema_handling_online.py +331 -0
  95. tests/online/test_filter_logical_in_online.py +374 -0
  96. tests/online/test_fulltext_comprehensive.py +1773 -0
  97. tests/online/test_fulltext_label_online.py +433 -0
  98. tests/online/test_fulltext_search_online.py +842 -0
  99. tests/online/test_ivf_stats_online.py +506 -0
  100. tests/online/test_logger_integration.py +311 -0
  101. tests/online/test_matrixone_query_orm.py +540 -0
  102. tests/online/test_metadata_online.py +579 -0
  103. tests/online/test_model_insert_online.py +255 -0
  104. tests/online/test_mysql_driver_validation.py +213 -0
  105. tests/online/test_orm_advanced_features.py +2022 -0
  106. tests/online/test_orm_cte_integration.py +269 -0
  107. tests/online/test_orm_online.py +270 -0
  108. tests/online/test_pinecone_filter.py +708 -0
  109. tests/online/test_pubsub_operations.py +352 -0
  110. tests/online/test_query_methods.py +225 -0
  111. tests/online/test_query_update_online.py +433 -0
  112. tests/online/test_search_vector_index.py +557 -0
  113. tests/online/test_simple_fulltext_online.py +915 -0
  114. tests/online/test_snapshot_comprehensive.py +998 -0
  115. tests/online/test_sqlalchemy_engine_integration.py +336 -0
  116. tests/online/test_sqlalchemy_integration.py +425 -0
  117. tests/online/test_transaction_contexts.py +1219 -0
  118. tests/online/test_transaction_insert_methods.py +356 -0
  119. tests/online/test_transaction_query_methods.py +288 -0
  120. tests/online/test_unified_filter_online.py +529 -0
  121. tests/online/test_vector_comprehensive.py +706 -0
  122. tests/online/test_version_management.py +291 -0
matrixone/version.py ADDED
@@ -0,0 +1,580 @@
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 Version Management Module
17
+
18
+ Provides version comparison, compatibility checking, and version-aware feature management
19
+ for MatrixOne Python SDK.
20
+
21
+ Features:
22
+ 1. Version parsing and comparison (e.g., 3.0.1, 3.0.2 > 3.0.1)
23
+ 2. Backend version compatibility checking
24
+ 3. Feature availability checking based on version
25
+ 4. Version-aware error messages and suggestions
26
+ 5. Decorators for version checking on methods
27
+ """
28
+
29
+ import functools
30
+ import re
31
+ from dataclasses import dataclass
32
+ from enum import Enum
33
+ from typing import Callable, Dict, Optional, Union
34
+
35
+ from .exceptions import MatrixOneError
36
+
37
+
38
+ class VersionComparison(Enum):
39
+ """Version comparison results"""
40
+
41
+ LESS = -1
42
+ EQUAL = 0
43
+ GREATER = 1
44
+
45
+
46
+ @dataclass
47
+ class VersionInfo:
48
+ """Version information container"""
49
+
50
+ major: int
51
+ minor: int
52
+ patch: int
53
+
54
+ def __str__(self) -> str:
55
+ return f"{self.major}.{self.minor}.{self.patch}"
56
+
57
+ def __repr__(self) -> str:
58
+ return f"VersionInfo({self.major}, {self.minor}, {self.patch})"
59
+
60
+ def __eq__(self, other) -> bool:
61
+ """Check if two versions are equal"""
62
+ if not isinstance(other, VersionInfo):
63
+ return False
64
+ return (self.major, self.minor, self.patch) == (other.major, other.minor, other.patch)
65
+
66
+ def __lt__(self, other) -> bool:
67
+ """Check if this version is less than other version"""
68
+ if not isinstance(other, VersionInfo):
69
+ return NotImplemented
70
+ return (self.major, self.minor, self.patch) < (other.major, other.minor, other.patch)
71
+
72
+ def __le__(self, other) -> bool:
73
+ """Check if this version is less than or equal to other version"""
74
+ if not isinstance(other, VersionInfo):
75
+ return NotImplemented
76
+ return (self.major, self.minor, self.patch) <= (other.major, other.minor, other.patch)
77
+
78
+ def __gt__(self, other) -> bool:
79
+ """Check if this version is greater than other version"""
80
+ if not isinstance(other, VersionInfo):
81
+ return NotImplemented
82
+ return (self.major, self.minor, self.patch) > (other.major, other.minor, other.patch)
83
+
84
+ def __ge__(self, other) -> bool:
85
+ """Check if this version is greater than or equal to other version"""
86
+ if not isinstance(other, VersionInfo):
87
+ return NotImplemented
88
+ return (self.major, self.minor, self.patch) >= (other.major, other.minor, other.patch)
89
+
90
+
91
+ @dataclass
92
+ class FeatureRequirement:
93
+ """Feature version requirement"""
94
+
95
+ feature_name: str
96
+ min_version: Optional[VersionInfo] = None
97
+ max_version: Optional[VersionInfo] = None
98
+ description: Optional[str] = None
99
+ alternative: Optional[str] = None
100
+
101
+
102
+ class VersionManager:
103
+ """
104
+ MatrixOne Version Manager
105
+
106
+ Handles version parsing, comparison, and compatibility checking.
107
+ Supports semantic versioning format: major.minor.patch (e.g., 3.0.1)
108
+ """
109
+
110
+ # Version pattern for parsing
111
+ VERSION_PATTERN = re.compile(r"^(\d+)\.(\d+)\.(\d+)$")
112
+
113
+ def __init__(self):
114
+ self._current_backend_version: Optional[VersionInfo] = None
115
+ self._feature_requirements: Dict[str, FeatureRequirement] = {}
116
+ self._version_hints: Dict[str, str] = {}
117
+
118
+ def parse_version(self, version_string: str) -> VersionInfo:
119
+ """
120
+ Parse version string into VersionInfo object
121
+
122
+ Args:
123
+
124
+ version_string: Version string in format "major.minor.patch" (e.g., "3.0.1")
125
+ Special case: "999.0.0" represents development version (highest priority)
126
+
127
+ Returns:
128
+
129
+ VersionInfo object
130
+
131
+ Raises:
132
+
133
+ ValueError: If version string format is invalid
134
+ """
135
+ if not isinstance(version_string, str):
136
+ raise ValueError(f"Version string must be a string, got {type(version_string)}")
137
+
138
+ match = self.VERSION_PATTERN.match(version_string.strip())
139
+ if not match:
140
+ raise ValueError(f"Invalid version format: '{version_string}'. Expected format: major.minor.patch (e.g., 3.0.1)")
141
+
142
+ major, minor, patch = map(int, match.groups())
143
+
144
+ # Special handling for development version
145
+ if major == 999:
146
+ # This is a development version - it has highest priority
147
+ pass
148
+
149
+ return VersionInfo(major, minor, patch)
150
+
151
+ def compare_versions(self, version1: Union[str, VersionInfo], version2: Union[str, VersionInfo]) -> VersionComparison:
152
+ """
153
+ Compare two versions
154
+
155
+ Args:
156
+
157
+ version1: First version (string or VersionInfo)
158
+ version2: Second version (string or VersionInfo)
159
+
160
+ Returns:
161
+
162
+ VersionComparison result
163
+
164
+ Examples:
165
+
166
+ compare_versions("3.0.2", "3.0.1") -> VersionComparison.GREATER
167
+ compare_versions("2.1.19", "3.0.9") -> VersionComparison.LESS
168
+ compare_versions("3.0.1", "3.0.1") -> VersionComparison.EQUAL
169
+ """
170
+ # Parse versions if they are strings
171
+ if isinstance(version1, str):
172
+ version1 = self.parse_version(version1)
173
+ if isinstance(version2, str):
174
+ version2 = self.parse_version(version2)
175
+
176
+ # Compare major versions
177
+ if version1.major != version2.major:
178
+ return VersionComparison.GREATER if version1.major > version2.major else VersionComparison.LESS
179
+
180
+ # Compare minor versions
181
+ if version1.minor != version2.minor:
182
+ return VersionComparison.GREATER if version1.minor > version2.minor else VersionComparison.LESS
183
+
184
+ # Compare patch versions
185
+ if version1.patch != version2.patch:
186
+ return VersionComparison.GREATER if version1.patch > version2.patch else VersionComparison.LESS
187
+
188
+ return VersionComparison.EQUAL
189
+
190
+ def is_version_compatible(
191
+ self,
192
+ required_version: Union[str, VersionInfo],
193
+ current_version: Optional[Union[str, VersionInfo]] = None,
194
+ operator: str = ">=",
195
+ ) -> bool:
196
+ """
197
+ Check if current version is compatible with required version
198
+
199
+ Args:
200
+
201
+ required_version: Required version
202
+ current_version: Current version (uses backend version if None)
203
+ operator: Comparison operator (">=", ">", "<=", "<", "==", "!=")
204
+
205
+ Returns:
206
+
207
+ True if compatible, False otherwise
208
+ """
209
+ if current_version is None:
210
+ current_version = self._current_backend_version
211
+
212
+ if current_version is None:
213
+ # If no backend version is set, assume compatibility for now
214
+ # In real implementation, you might want to raise an error
215
+ return True
216
+
217
+ # Parse versions if they are strings
218
+ if isinstance(required_version, str):
219
+ required_version = self.parse_version(required_version)
220
+ if isinstance(current_version, str):
221
+ current_version = self.parse_version(current_version)
222
+
223
+ comparison = self.compare_versions(current_version, required_version)
224
+
225
+ if operator == ">=":
226
+ return comparison in [VersionComparison.EQUAL, VersionComparison.GREATER]
227
+ elif operator == ">":
228
+ return comparison == VersionComparison.GREATER
229
+ elif operator == "<=":
230
+ return comparison in [VersionComparison.EQUAL, VersionComparison.LESS]
231
+ elif operator == "<":
232
+ return comparison == VersionComparison.LESS
233
+ elif operator == "==":
234
+ return comparison == VersionComparison.EQUAL
235
+ elif operator == "!=":
236
+ return comparison != VersionComparison.EQUAL
237
+ else:
238
+ raise ValueError(f"Unsupported operator: {operator}")
239
+
240
+ def set_backend_version(self, version: Union[str, VersionInfo]) -> None:
241
+ """
242
+ Set the current backend version
243
+
244
+ Args:
245
+
246
+ version: Backend version string or VersionInfo object
247
+ """
248
+ if isinstance(version, str):
249
+ version = self.parse_version(version)
250
+ self._current_backend_version = version
251
+
252
+ def get_backend_version(self) -> Optional[VersionInfo]:
253
+ """Get current backend version"""
254
+ return self._current_backend_version
255
+
256
+ def is_development_version(self, version: Optional[Union[str, VersionInfo]] = None) -> bool:
257
+ """
258
+ Check if a version is a development version
259
+
260
+ Args:
261
+
262
+ version: Version to check (uses current backend version if None)
263
+
264
+ Returns:
265
+
266
+ True if it's a development version (999.x.x), False otherwise
267
+ """
268
+ if version is None:
269
+ version = self._current_backend_version
270
+
271
+ if version is None:
272
+ return False
273
+
274
+ if isinstance(version, str):
275
+ version = self.parse_version(version)
276
+
277
+ return version.major == 999
278
+
279
+ def _parse_matrixone_version(self, version_string: str) -> Optional[str]:
280
+ """
281
+ Parse MatrixOne version string to extract semantic version
282
+
283
+ Handles formats:
284
+ 1. "8.0.30-MatrixOne-v" -> "999.0.0" (development version, highest)
285
+ 2. "8.0.30-MatrixOne-v3.0.0" -> "3.0.0" (release version)
286
+ 3. "MatrixOne 3.0.1" -> "3.0.1" (fallback format)
287
+
288
+ Args:
289
+
290
+ version_string: Raw version string from MatrixOne
291
+
292
+ Returns:
293
+
294
+ Semantic version string or None if parsing fails
295
+ """
296
+ import re
297
+
298
+ if not version_string:
299
+ return None
300
+
301
+ version_string = version_string.strip()
302
+
303
+ # Pattern 1: Development version "8.0.30-MatrixOne-v" (v后面为空)
304
+ dev_pattern = r"^(\d+\.\d+\.\d+)-MatrixOne-v$"
305
+ dev_match = re.search(dev_pattern, version_string)
306
+ if dev_match:
307
+ # Development version - assign highest version number
308
+ return "999.0.0"
309
+
310
+ # Pattern 2: Release version "8.0.30-MatrixOne-v3.0.0" (v后面有版本号)
311
+ release_pattern = r"^(\d+\.\d+\.\d+)-MatrixOne-v(\d+\.\d+\.\d+)$"
312
+ release_match = re.search(release_pattern, version_string)
313
+ if release_match:
314
+ # Extract the semantic version part
315
+ semantic_version = release_match.group(2)
316
+ return semantic_version
317
+
318
+ # Pattern 3: Fallback format "MatrixOne 3.0.1", "Version 2.5.0", or "3.0.1"
319
+ # Match clean version strings or strings that start with common prefixes
320
+ if (
321
+ version_string.startswith("MatrixOne ")
322
+ or version_string.startswith("Version ")
323
+ or re.match(r"^\d+\.\d+\.\d+$", version_string)
324
+ ):
325
+ fallback_pattern = r"(\d+\.\d+\.\d+)"
326
+ fallback_match = re.search(fallback_pattern, version_string)
327
+ if fallback_match:
328
+ return fallback_match.group(1)
329
+
330
+ # For invalid MatrixOne formats like "8.0.30-MatrixOne" or "8.0.30-MatrixOne-v-"
331
+ # return None instead of falling back to extracting version numbers
332
+ return None
333
+
334
+ def register_feature_requirement(self, feature_requirement: FeatureRequirement) -> None:
335
+ """
336
+ Register a feature requirement
337
+
338
+ Args:
339
+
340
+ feature_requirement: FeatureRequirement object
341
+ """
342
+ self._feature_requirements[feature_requirement.feature_name] = feature_requirement
343
+
344
+ def is_feature_available(self, feature_name: str) -> bool:
345
+ """
346
+ Check if a feature is available in current backend version
347
+
348
+ Args:
349
+
350
+ feature_name: Name of the feature to check
351
+
352
+ Returns:
353
+
354
+ True if feature is available, False otherwise
355
+ """
356
+ if feature_name not in self._feature_requirements:
357
+ # If feature is not registered, assume it's available
358
+ return True
359
+
360
+ requirement = self._feature_requirements[feature_name]
361
+ current_version = self._current_backend_version
362
+
363
+ if current_version is None:
364
+ # If no backend version is set, assume feature is available
365
+ return True
366
+
367
+ # Check minimum version requirement
368
+ if requirement.min_version and not self.is_version_compatible(requirement.min_version, current_version, ">="):
369
+ return False
370
+
371
+ # Check maximum version requirement
372
+ if requirement.max_version and not self.is_version_compatible(requirement.max_version, current_version, "<="):
373
+ return False
374
+
375
+ return True
376
+
377
+ def get_feature_info(self, feature_name: str) -> Optional[FeatureRequirement]:
378
+ """
379
+ Get feature requirement information
380
+
381
+ Args:
382
+
383
+ feature_name: Name of the feature
384
+
385
+ Returns:
386
+
387
+ FeatureRequirement object or None if not found
388
+ """
389
+ return self._feature_requirements.get(feature_name)
390
+
391
+ def get_version_hint(self, feature_name: str, error_context: str = "") -> str:
392
+ """
393
+ Get helpful hint message for version-related errors
394
+
395
+ Args:
396
+
397
+ feature_name: Name of the feature
398
+ error_context: Additional context for the error
399
+
400
+ Returns:
401
+
402
+ Helpful hint message
403
+ """
404
+ if feature_name not in self._feature_requirements:
405
+ return f"Feature '{feature_name}' is not registered for version checking."
406
+
407
+ requirement = self._feature_requirements[feature_name]
408
+ current_version = self._current_backend_version
409
+
410
+ if current_version is None:
411
+ return "Backend version is not set. Please set the backend version using set_backend_version()."
412
+
413
+ hints = []
414
+
415
+ if requirement.min_version and not self.is_version_compatible(requirement.min_version, current_version, ">="):
416
+ hints.append(
417
+ f"Feature '{feature_name}' requires backend version {requirement.min_version} or higher, "
418
+ f"but current version is {current_version}"
419
+ )
420
+
421
+ if requirement.max_version and not self.is_version_compatible(requirement.max_version, current_version, "<="):
422
+ hints.append(
423
+ f"Feature '{feature_name}' is not supported in backend version {requirement.max_version} or higher, "
424
+ f"but current version is {current_version}"
425
+ )
426
+
427
+ if requirement.alternative:
428
+ hints.append(f"Alternative: {requirement.alternative}")
429
+
430
+ if requirement.description:
431
+ hints.append(f"Description: {requirement.description}")
432
+
433
+ if error_context:
434
+ hints.append(f"Context: {error_context}")
435
+
436
+ return "\n".join(hints)
437
+
438
+
439
+ # Global version manager instance
440
+ _version_manager = VersionManager()
441
+
442
+
443
+ def get_version_manager() -> VersionManager:
444
+ """Get the global version manager instance"""
445
+ return _version_manager
446
+
447
+
448
+ def requires_version(
449
+ min_version: Optional[str] = None,
450
+ max_version: Optional[str] = None,
451
+ feature_name: Optional[str] = None,
452
+ description: Optional[str] = None,
453
+ alternative: Optional[str] = None,
454
+ raise_error: bool = True,
455
+ ) -> Callable:
456
+ """
457
+ Decorator for version checking on methods
458
+
459
+ Args:
460
+
461
+ min_version: Minimum required version (e.g., "3.0.1")
462
+ max_version: Maximum supported version (e.g., "3.0.5")
463
+ feature_name: Name of the feature (defaults to function name)
464
+ description: Description of the feature
465
+ alternative: Alternative approach or workaround
466
+ raise_error: Whether to raise error if version check fails
467
+
468
+ Returns:
469
+
470
+ Decorated function
471
+ """
472
+
473
+ def decorator(func: Callable) -> Callable:
474
+ @functools.wraps(func)
475
+ def wrapper(*args, **kwargs):
476
+ # Get feature name
477
+ feature = feature_name or func.__name__
478
+
479
+ # Register feature requirement if not already registered
480
+ if feature not in _version_manager._feature_requirements:
481
+ min_ver = _version_manager.parse_version(min_version) if min_version else None
482
+ max_ver = _version_manager.parse_version(max_version) if max_version else None
483
+
484
+ requirement = FeatureRequirement(
485
+ feature_name=feature,
486
+ min_version=min_ver,
487
+ max_version=max_ver,
488
+ description=description,
489
+ alternative=alternative,
490
+ )
491
+ _version_manager.register_feature_requirement(requirement)
492
+
493
+ # Check if feature is available
494
+ if not _version_manager.is_feature_available(feature):
495
+ if raise_error:
496
+ hint = _version_manager.get_version_hint(feature, f"Method: {func.__name__}")
497
+ raise VersionError(f"Feature '{feature}' is not available in current backend version.\n{hint}")
498
+ else:
499
+ # Log warning and return None or default value
500
+ print(f"Warning: Feature '{feature}' is not available in current backend version")
501
+ return None
502
+
503
+ return func(*args, **kwargs)
504
+
505
+ # Add metadata to the function
506
+ wrapper._version_requirement = {
507
+ "min_version": min_version,
508
+ "max_version": max_version,
509
+ "feature_name": feature_name,
510
+ "description": description,
511
+ "alternative": alternative,
512
+ }
513
+
514
+ return wrapper
515
+
516
+ return decorator
517
+
518
+
519
+ class VersionError(MatrixOneError):
520
+ """Raised when version compatibility check fails"""
521
+
522
+ pass
523
+
524
+
525
+ # Initialize common feature requirements
526
+ def _initialize_default_features():
527
+ """Initialize default feature requirements for common MatrixOne features"""
528
+
529
+ # Snapshot features
530
+ _version_manager.register_feature_requirement(
531
+ FeatureRequirement(
532
+ feature_name="snapshot_cluster_level",
533
+ min_version=_version_manager.parse_version("1.0.0"),
534
+ description="Cluster-level snapshot functionality",
535
+ alternative="Use database-level snapshots instead",
536
+ )
537
+ )
538
+
539
+ _version_manager.register_feature_requirement(
540
+ FeatureRequirement(
541
+ feature_name="snapshot_account_level",
542
+ min_version=_version_manager.parse_version("1.0.0"),
543
+ description="Account-level snapshot functionality",
544
+ alternative="Use database-level snapshots instead",
545
+ )
546
+ )
547
+
548
+ # PITR features
549
+ _version_manager.register_feature_requirement(
550
+ FeatureRequirement(
551
+ feature_name="pitr_point_in_time_recovery",
552
+ min_version=_version_manager.parse_version("1.0.0"),
553
+ description="Point-in-time recovery functionality",
554
+ alternative="Use snapshot restore instead",
555
+ )
556
+ )
557
+
558
+ # Pub/Sub features
559
+ _version_manager.register_feature_requirement(
560
+ FeatureRequirement(
561
+ feature_name="pubsub_publications",
562
+ min_version=_version_manager.parse_version("1.0.0"),
563
+ description="Publication and subscription functionality",
564
+ alternative="Use direct table queries instead",
565
+ )
566
+ )
567
+
568
+ # Account management features
569
+ _version_manager.register_feature_requirement(
570
+ FeatureRequirement(
571
+ feature_name="account_management",
572
+ min_version=_version_manager.parse_version("1.0.0"),
573
+ description="Account and user management functionality",
574
+ alternative="Use SQL DDL statements directly",
575
+ )
576
+ )
577
+
578
+
579
+ # Initialize default features
580
+ _initialize_default_features()