matrixone-python-sdk 0.1.11__tar.gz → 0.1.13__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.
- {matrixone_python_sdk-0.1.11/matrixone_python_sdk.egg-info → matrixone_python_sdk-0.1.13}/PKG-INFO +11 -1
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/README_USER.md +10 -0
- matrixone_python_sdk-0.1.13/examples/example_32_branch_operations.py +475 -0
- matrixone_python_sdk-0.1.13/examples/example_33_ivf_rank_search.py +403 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/__init__.py +9 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/async_client.py +41 -13
- matrixone_python_sdk-0.1.13/matrixone/branch.py +872 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/cli_tools.py +2 -2
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/client.py +58 -17
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/clone.py +4 -4
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/exceptions.py +4 -0
- matrixone_python_sdk-0.1.13/matrixone/ivf_rank.py +127 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/pitr.py +16 -16
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/restore.py +8 -8
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/session.py +4 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/snapshot.py +6 -6
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/sqlalchemy_ext/dialect.py +23 -20
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/sqlalchemy_ext/fulltext_index.py +36 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/vector_manager.py +256 -2
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/version.py +12 -2
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13/matrixone_python_sdk.egg-info}/PKG-INFO +11 -1
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone_python_sdk.egg-info/SOURCES.txt +4 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/pyproject.toml +1 -1
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/LICENSE +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/MANIFEST.in +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/README.md +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/examples/example_01_basic_connection.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/examples/example_02_account_management.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/examples/example_03_async_operations.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/examples/example_04_transaction_management.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/examples/example_05_snapshot_restore.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/examples/example_06_sqlalchemy_integration.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/examples/example_07_advanced_features.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/examples/example_08_pubsub_operations.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/examples/example_09_logger_integration.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/examples/example_10_version_management.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/examples/example_11_matrixone_version_demo.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/examples/example_12_vector_basics.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/examples/example_13_vector_indexes.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/examples/example_14_vector_search.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/examples/example_15_vector_advanced.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/examples/example_18_snapshot_orm.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/examples/example_19_sqlalchemy_style_orm.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/examples/example_20_sqlalchemy_engine_integration.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/examples/example_21_advanced_orm_features.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/examples/example_22_unified_sql_builder.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/examples/example_23_load_data_operations.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/examples/example_24_query_update.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/examples/example_25_metadata_operations.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/examples/example_26_stage_operations.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/examples/example_27_export_operations.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/examples/example_28_sqlalchemy_select.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/examples/example_29_complex_queries.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/examples/example_30_with_snapshot_method.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/examples/example_31_cdc_operations.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/examples/example_connection_hooks.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/examples/example_dynamic_logging.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/examples/example_ivf_stats_complete.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/account.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/async_orm.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/base_client.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/cdc.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/config.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/connection_hooks.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/export.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/fulltext_manager.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/index_utils.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/load_data.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/logger.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/metadata.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/moctl.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/orm.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/pubsub.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/search_vector_index.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/sql_builder.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/sqlalchemy_ext/__init__.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/sqlalchemy_ext/fulltext_search.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/sqlalchemy_ext/hnsw_config.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/sqlalchemy_ext/ivf_config.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/sqlalchemy_ext/json_functions.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/sqlalchemy_ext/json_type.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/sqlalchemy_ext/snapshot.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/sqlalchemy_ext/table_builder.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/sqlalchemy_ext/vector_index.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/sqlalchemy_ext/vector_type.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/sqlalchemy_select.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone/stage.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone_python_sdk.egg-info/dependency_links.txt +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone_python_sdk.egg-info/entry_points.txt +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone_python_sdk.egg-info/not-zip-safe +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone_python_sdk.egg-info/requires.txt +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/matrixone_python_sdk.egg-info/top_level.txt +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/mo_diag.py +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/requirements.txt +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/setup.cfg +0 -0
- {matrixone_python_sdk-0.1.11 → matrixone_python_sdk-0.1.13}/setup.py +0 -0
{matrixone_python_sdk-0.1.11/matrixone_python_sdk.egg-info → matrixone_python_sdk-0.1.13}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: matrixone-python-sdk
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.13
|
|
4
4
|
Summary: A comprehensive Python SDK for MatrixOne database operations with vector search, fulltext search, and advanced features
|
|
5
5
|
Home-page: https://github.com/matrixorigin/matrixone
|
|
6
6
|
Author: MatrixOne Team
|
|
@@ -87,6 +87,9 @@ A comprehensive, high-level Python SDK for MatrixOne that provides SQLAlchemy-li
|
|
|
87
87
|
- 📋 [Best Practices](https://matrixone.readthedocs.io/en/latest/best_practices.html)
|
|
88
88
|
- 📖 [API Reference](https://matrixone.readthedocs.io/en/latest/api/index.html)
|
|
89
89
|
|
|
90
|
+
**For Developers:**
|
|
91
|
+
- 🛠️ [MatrixOne Development Guide](../../etc/DEV_README.md) - Complete guide for setting up MatrixOne locally, multi-CN clusters, monitoring, and all `make dev-*` commands
|
|
92
|
+
|
|
90
93
|
---
|
|
91
94
|
|
|
92
95
|
## ✨ Features
|
|
@@ -205,6 +208,13 @@ class User(Base):
|
|
|
205
208
|
|
|
206
209
|
## Quick Start
|
|
207
210
|
|
|
211
|
+
> **💡 Need to set up MatrixOne server?**
|
|
212
|
+
> If you don't have MatrixOne running yet, check out the [MatrixOne Development Guide](../../etc/DEV_README.md) for instructions on:
|
|
213
|
+
> - Starting a standalone MatrixOne instance
|
|
214
|
+
> - Setting up a multi-CN cluster with Docker Compose
|
|
215
|
+
> - Enabling monitoring and metrics
|
|
216
|
+
> - All `make dev-*` commands
|
|
217
|
+
|
|
208
218
|
### Basic Usage
|
|
209
219
|
|
|
210
220
|
```python
|
|
@@ -19,6 +19,9 @@ A comprehensive, high-level Python SDK for MatrixOne that provides SQLAlchemy-li
|
|
|
19
19
|
- 📋 [Best Practices](https://matrixone.readthedocs.io/en/latest/best_practices.html)
|
|
20
20
|
- 📖 [API Reference](https://matrixone.readthedocs.io/en/latest/api/index.html)
|
|
21
21
|
|
|
22
|
+
**For Developers:**
|
|
23
|
+
- 🛠️ [MatrixOne Development Guide](../../etc/DEV_README.md) - Complete guide for setting up MatrixOne locally, multi-CN clusters, monitoring, and all `make dev-*` commands
|
|
24
|
+
|
|
22
25
|
---
|
|
23
26
|
|
|
24
27
|
## ✨ Features
|
|
@@ -137,6 +140,13 @@ class User(Base):
|
|
|
137
140
|
|
|
138
141
|
## Quick Start
|
|
139
142
|
|
|
143
|
+
> **💡 Need to set up MatrixOne server?**
|
|
144
|
+
> If you don't have MatrixOne running yet, check out the [MatrixOne Development Guide](../../etc/DEV_README.md) for instructions on:
|
|
145
|
+
> - Starting a standalone MatrixOne instance
|
|
146
|
+
> - Setting up a multi-CN cluster with Docker Compose
|
|
147
|
+
> - Enabling monitoring and metrics
|
|
148
|
+
> - All `make dev-*` commands
|
|
149
|
+
|
|
140
150
|
### Basic Usage
|
|
141
151
|
|
|
142
152
|
```python
|
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
# Copyright 2021 - 2022 Matrix Origin
|
|
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
|
+
"""
|
|
18
|
+
Example 32: Branch Operations - Git-Style Version Control for Data
|
|
19
|
+
|
|
20
|
+
This example demonstrates Git-style branch operations with real-world scenarios:
|
|
21
|
+
1. Basic API - Simple string-based operations
|
|
22
|
+
2. ORM Model Support - SQLAlchemy model integration
|
|
23
|
+
3. Development Workflow - Create dev branch, test, merge
|
|
24
|
+
4. Safe Data Migration - Snapshot-based testing before applying
|
|
25
|
+
5. A/B Testing - Compare different data transformations
|
|
26
|
+
6. Multi-Team Development - Independent branches with conflict resolution
|
|
27
|
+
7. Database-Level Branching - Schema changes across entire databases
|
|
28
|
+
8. Async Operations - Non-blocking branch operations
|
|
29
|
+
|
|
30
|
+
This example demonstrates the complete branch functionality of MatrixOne.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
import asyncio
|
|
34
|
+
import time
|
|
35
|
+
from matrixone import Client, AsyncClient
|
|
36
|
+
from matrixone.orm import declarative_base
|
|
37
|
+
from sqlalchemy import Column, Integer, String, Numeric
|
|
38
|
+
from matrixone.logger import create_default_logger
|
|
39
|
+
from matrixone.config import get_connection_params, print_config
|
|
40
|
+
|
|
41
|
+
Base = declarative_base()
|
|
42
|
+
|
|
43
|
+
class Product(Base):
|
|
44
|
+
__tablename__ = 'products'
|
|
45
|
+
id = Column(Integer, primary_key=True)
|
|
46
|
+
name = Column(String(100))
|
|
47
|
+
price = Column(Numeric(10, 2))
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class BranchOperationsDemo:
|
|
51
|
+
"""Demonstrates branch operations with comprehensive testing."""
|
|
52
|
+
|
|
53
|
+
def __init__(self):
|
|
54
|
+
self.logger = create_default_logger(sql_log_mode="auto")
|
|
55
|
+
self.results = {
|
|
56
|
+
'tests_run': 0,
|
|
57
|
+
'tests_passed': 0,
|
|
58
|
+
'tests_failed': 0,
|
|
59
|
+
'unexpected_results': [],
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
def test_basic_api(self):
|
|
63
|
+
"""Test 1: Basic API - Simple string-based operations"""
|
|
64
|
+
print("\n=== Test 1: Basic API ===")
|
|
65
|
+
self.results['tests_run'] += 1
|
|
66
|
+
|
|
67
|
+
try:
|
|
68
|
+
host, port, user, password, database = get_connection_params()
|
|
69
|
+
client = Client(logger=self.logger, sql_log_mode="full")
|
|
70
|
+
client.connect(host=host, port=port, user=user, password=password, database=database)
|
|
71
|
+
|
|
72
|
+
# Create and delete
|
|
73
|
+
client.execute("DROP TABLE IF EXISTS users")
|
|
74
|
+
client.execute("CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(50))")
|
|
75
|
+
client.execute("INSERT INTO users VALUES (1, 'Alice')")
|
|
76
|
+
|
|
77
|
+
client.branch.create('users_branch', 'users')
|
|
78
|
+
client.branch.delete('users_branch')
|
|
79
|
+
client.execute("DROP TABLE users")
|
|
80
|
+
|
|
81
|
+
self.logger.info("✅ Basic API test passed")
|
|
82
|
+
self.results['tests_passed'] += 1
|
|
83
|
+
client.disconnect()
|
|
84
|
+
|
|
85
|
+
except Exception as e:
|
|
86
|
+
self.logger.error(f"❌ Basic API test failed: {e}")
|
|
87
|
+
self.results['tests_failed'] += 1
|
|
88
|
+
self.results['unexpected_results'].append({'test': 'basic_api', 'error': str(e)})
|
|
89
|
+
|
|
90
|
+
def test_orm_model_support(self):
|
|
91
|
+
"""Test 2: ORM Model Support - SQLAlchemy model integration"""
|
|
92
|
+
print("\n=== Test 2: ORM Model Support ===")
|
|
93
|
+
self.results['tests_run'] += 1
|
|
94
|
+
|
|
95
|
+
try:
|
|
96
|
+
host, port, user, password, database = get_connection_params()
|
|
97
|
+
client = Client(logger=self.logger, sql_log_mode="full")
|
|
98
|
+
client.connect(host=host, port=port, user=user, password=password, database=database)
|
|
99
|
+
|
|
100
|
+
client.execute("DROP TABLE IF EXISTS products")
|
|
101
|
+
client.execute("DROP TABLE IF EXISTS products_branch")
|
|
102
|
+
|
|
103
|
+
client.create_table(Product)
|
|
104
|
+
client.insert(Product, {'id': 1, 'name': 'Laptop', 'price': 999.99})
|
|
105
|
+
|
|
106
|
+
client.branch.create('products_branch', Product)
|
|
107
|
+
client.branch.delete('products_branch')
|
|
108
|
+
client.drop_table(Product)
|
|
109
|
+
|
|
110
|
+
self.logger.info("✅ ORM model support test passed")
|
|
111
|
+
self.results['tests_passed'] += 1
|
|
112
|
+
client.disconnect()
|
|
113
|
+
|
|
114
|
+
except Exception as e:
|
|
115
|
+
self.logger.error(f"❌ ORM model support test failed: {e}")
|
|
116
|
+
self.results['tests_failed'] += 1
|
|
117
|
+
self.results['unexpected_results'].append({'test': 'orm_model_support', 'error': str(e)})
|
|
118
|
+
|
|
119
|
+
def test_dev_workflow(self):
|
|
120
|
+
"""Test 3: Development Workflow - Create dev branch, test, merge"""
|
|
121
|
+
print("\n=== Test 3: Development Workflow ===")
|
|
122
|
+
self.results['tests_run'] += 1
|
|
123
|
+
|
|
124
|
+
try:
|
|
125
|
+
host, port, user, password, database = get_connection_params()
|
|
126
|
+
client = Client(logger=self.logger, sql_log_mode="full")
|
|
127
|
+
client.connect(host=host, port=port, user=user, password=password, database=database)
|
|
128
|
+
|
|
129
|
+
# Cleanup first
|
|
130
|
+
client.execute("DROP TABLE IF EXISTS users_dev")
|
|
131
|
+
client.execute("DROP TABLE IF EXISTS users_prod")
|
|
132
|
+
|
|
133
|
+
# Setup production
|
|
134
|
+
client.execute("""
|
|
135
|
+
CREATE TABLE users_prod (
|
|
136
|
+
id INT PRIMARY KEY,
|
|
137
|
+
name VARCHAR(100),
|
|
138
|
+
email VARCHAR(100),
|
|
139
|
+
status VARCHAR(20)
|
|
140
|
+
)
|
|
141
|
+
""")
|
|
142
|
+
client.execute("INSERT INTO users_prod VALUES (1, 'Alice', 'alice@example.com', 'active')")
|
|
143
|
+
client.execute("INSERT INTO users_prod VALUES (2, 'Bob', 'bob@example.com', 'active')")
|
|
144
|
+
|
|
145
|
+
# Create dev branch
|
|
146
|
+
client.branch.create('users_dev', 'users_prod')
|
|
147
|
+
|
|
148
|
+
# Make changes in dev (data only, not schema)
|
|
149
|
+
client.execute("INSERT INTO users_dev VALUES (3, 'Charlie', 'charlie@example.com', 'active')")
|
|
150
|
+
|
|
151
|
+
# Compare
|
|
152
|
+
diffs = client.branch.diff('users_dev', 'users_prod')
|
|
153
|
+
|
|
154
|
+
# Merge
|
|
155
|
+
client.branch.merge('users_dev', 'users_prod', on_conflict='accept')
|
|
156
|
+
result = client.execute("SELECT COUNT(*) FROM users_prod")
|
|
157
|
+
count = result.rows[0][0]
|
|
158
|
+
|
|
159
|
+
client.execute("DROP TABLE users_dev")
|
|
160
|
+
client.execute("DROP TABLE users_prod")
|
|
161
|
+
|
|
162
|
+
self.logger.info(f"✅ Development workflow test passed (merged {count} users)")
|
|
163
|
+
self.results['tests_passed'] += 1
|
|
164
|
+
client.disconnect()
|
|
165
|
+
|
|
166
|
+
except Exception as e:
|
|
167
|
+
self.logger.error(f"❌ Development workflow test failed: {e}")
|
|
168
|
+
self.results['tests_failed'] += 1
|
|
169
|
+
self.results['unexpected_results'].append({'test': 'dev_workflow', 'error': str(e)})
|
|
170
|
+
|
|
171
|
+
def test_safe_migration(self):
|
|
172
|
+
"""Test 4: Safe Data Migration - Snapshot-based testing before applying"""
|
|
173
|
+
print("\n=== Test 4: Safe Data Migration ===")
|
|
174
|
+
self.results['tests_run'] += 1
|
|
175
|
+
|
|
176
|
+
try:
|
|
177
|
+
host, port, user, password, database = get_connection_params()
|
|
178
|
+
client = Client(logger=self.logger, sql_log_mode="full")
|
|
179
|
+
client.connect(host=host, port=port, user=user, password=password, database=database)
|
|
180
|
+
|
|
181
|
+
# Setup
|
|
182
|
+
client.execute("DROP TABLE IF EXISTS orders")
|
|
183
|
+
client.execute("""
|
|
184
|
+
CREATE TABLE orders (
|
|
185
|
+
id INT PRIMARY KEY,
|
|
186
|
+
customer_id INT,
|
|
187
|
+
amount DECIMAL(10,2),
|
|
188
|
+
status VARCHAR(20)
|
|
189
|
+
)
|
|
190
|
+
""")
|
|
191
|
+
client.execute("INSERT INTO orders VALUES (1, 101, 100.00, 'pending')")
|
|
192
|
+
client.execute("INSERT INTO orders VALUES (2, 102, 200.00, 'completed')")
|
|
193
|
+
|
|
194
|
+
# Create snapshot
|
|
195
|
+
snap_name = f"orders_backup_{int(time.time())}"
|
|
196
|
+
client.execute(f"CREATE SNAPSHOT {snap_name} FOR TABLE test orders")
|
|
197
|
+
|
|
198
|
+
# Create branch from snapshot
|
|
199
|
+
client.branch.create('orders_test', 'orders', snapshot=snap_name)
|
|
200
|
+
|
|
201
|
+
# Test migration
|
|
202
|
+
client.execute("UPDATE orders_test SET status = 'processing' WHERE status = 'pending'")
|
|
203
|
+
result = client.execute("SELECT COUNT(*) FROM orders_test WHERE status = 'processing'")
|
|
204
|
+
migrated = result.rows[0][0]
|
|
205
|
+
|
|
206
|
+
# Apply to production
|
|
207
|
+
client.execute("UPDATE orders SET status = 'processing' WHERE status = 'pending'")
|
|
208
|
+
|
|
209
|
+
client.execute("DROP TABLE orders_test")
|
|
210
|
+
client.execute("DROP TABLE orders")
|
|
211
|
+
client.execute(f"DROP SNAPSHOT {snap_name}")
|
|
212
|
+
|
|
213
|
+
self.logger.info(f"✅ Safe migration test passed ({migrated} orders updated)")
|
|
214
|
+
self.results['tests_passed'] += 1
|
|
215
|
+
client.disconnect()
|
|
216
|
+
|
|
217
|
+
except Exception as e:
|
|
218
|
+
self.logger.error(f"❌ Safe migration test failed: {e}")
|
|
219
|
+
self.results['tests_failed'] += 1
|
|
220
|
+
self.results['unexpected_results'].append({'test': 'safe_migration', 'error': str(e)})
|
|
221
|
+
|
|
222
|
+
def test_ab_testing(self):
|
|
223
|
+
"""Test 5: A/B Testing - Compare different data transformations"""
|
|
224
|
+
print("\n=== Test 5: A/B Testing ===")
|
|
225
|
+
self.results['tests_run'] += 1
|
|
226
|
+
|
|
227
|
+
try:
|
|
228
|
+
host, port, user, password, database = get_connection_params()
|
|
229
|
+
client = Client(logger=self.logger, sql_log_mode="full")
|
|
230
|
+
client.connect(host=host, port=port, user=user, password=password, database=database)
|
|
231
|
+
|
|
232
|
+
# Setup
|
|
233
|
+
client.execute("DROP TABLE IF EXISTS products")
|
|
234
|
+
client.execute("""
|
|
235
|
+
CREATE TABLE products (
|
|
236
|
+
id INT PRIMARY KEY,
|
|
237
|
+
name VARCHAR(100),
|
|
238
|
+
price DECIMAL(10,2)
|
|
239
|
+
)
|
|
240
|
+
""")
|
|
241
|
+
client.execute("INSERT INTO products VALUES (1, 'Laptop', 1000.00)")
|
|
242
|
+
client.execute("INSERT INTO products VALUES (2, 'Mouse', 50.00)")
|
|
243
|
+
client.execute("INSERT INTO products VALUES (3, 'Desk', 300.00)")
|
|
244
|
+
|
|
245
|
+
# Branch A: 10% discount
|
|
246
|
+
client.branch.create('products_a', 'products')
|
|
247
|
+
client.execute("UPDATE products_a SET price = price * 0.9")
|
|
248
|
+
result = client.execute("SELECT AVG(price) FROM products_a")
|
|
249
|
+
avg_a = float(result.rows[0][0])
|
|
250
|
+
|
|
251
|
+
# Branch B: 15% discount
|
|
252
|
+
client.branch.create('products_b', 'products')
|
|
253
|
+
client.execute("UPDATE products_b SET price = price * 0.85")
|
|
254
|
+
result = client.execute("SELECT AVG(price) FROM products_b")
|
|
255
|
+
avg_b = float(result.rows[0][0])
|
|
256
|
+
|
|
257
|
+
# Merge winner
|
|
258
|
+
client.branch.merge('products_b', 'products', on_conflict='accept')
|
|
259
|
+
|
|
260
|
+
client.execute("DROP TABLE products_a")
|
|
261
|
+
client.execute("DROP TABLE products_b")
|
|
262
|
+
client.execute("DROP TABLE products")
|
|
263
|
+
|
|
264
|
+
self.logger.info(f"✅ A/B testing test passed (A: ${avg_a:.2f}, B: ${avg_b:.2f})")
|
|
265
|
+
self.results['tests_passed'] += 1
|
|
266
|
+
client.disconnect()
|
|
267
|
+
|
|
268
|
+
except Exception as e:
|
|
269
|
+
self.logger.error(f"❌ A/B testing test failed: {e}")
|
|
270
|
+
self.results['tests_failed'] += 1
|
|
271
|
+
self.results['unexpected_results'].append({'test': 'ab_testing', 'error': str(e)})
|
|
272
|
+
|
|
273
|
+
def test_multi_team(self):
|
|
274
|
+
"""Test 6: Multi-Team Development - Independent branches with conflict resolution"""
|
|
275
|
+
print("\n=== Test 6: Multi-Team Development ===")
|
|
276
|
+
self.results['tests_run'] += 1
|
|
277
|
+
|
|
278
|
+
try:
|
|
279
|
+
host, port, user, password, database = get_connection_params()
|
|
280
|
+
client = Client(logger=self.logger, sql_log_mode="full")
|
|
281
|
+
client.connect(host=host, port=port, user=user, password=password, database=database)
|
|
282
|
+
|
|
283
|
+
# Setup
|
|
284
|
+
client.execute("DROP TABLE IF EXISTS inventory")
|
|
285
|
+
client.execute("""
|
|
286
|
+
CREATE TABLE inventory (
|
|
287
|
+
id INT PRIMARY KEY,
|
|
288
|
+
item VARCHAR(100),
|
|
289
|
+
quantity INT,
|
|
290
|
+
warehouse VARCHAR(50)
|
|
291
|
+
)
|
|
292
|
+
""")
|
|
293
|
+
client.execute("INSERT INTO inventory VALUES (1, 'Widget A', 100, 'Warehouse 1')")
|
|
294
|
+
client.execute("INSERT INTO inventory VALUES (2, 'Widget B', 50, 'Warehouse 2')")
|
|
295
|
+
|
|
296
|
+
# Team A: Update warehouse
|
|
297
|
+
client.branch.create('inventory_team_a', 'inventory')
|
|
298
|
+
client.execute("UPDATE inventory_team_a SET warehouse = 'Warehouse 1' WHERE id = 1")
|
|
299
|
+
|
|
300
|
+
# Team B: Update quantities
|
|
301
|
+
client.branch.create('inventory_team_b', 'inventory')
|
|
302
|
+
client.execute("UPDATE inventory_team_b SET quantity = 150 WHERE id = 1")
|
|
303
|
+
|
|
304
|
+
# Merge both
|
|
305
|
+
client.branch.merge('inventory_team_a', 'inventory', on_conflict='accept')
|
|
306
|
+
client.branch.merge('inventory_team_b', 'inventory', on_conflict='accept')
|
|
307
|
+
|
|
308
|
+
client.execute("DROP TABLE inventory_team_a")
|
|
309
|
+
client.execute("DROP TABLE inventory_team_b")
|
|
310
|
+
client.execute("DROP TABLE inventory")
|
|
311
|
+
|
|
312
|
+
self.logger.info("✅ Multi-team development test passed")
|
|
313
|
+
self.results['tests_passed'] += 1
|
|
314
|
+
client.disconnect()
|
|
315
|
+
|
|
316
|
+
except Exception as e:
|
|
317
|
+
self.logger.error(f"❌ Multi-team development test failed: {e}")
|
|
318
|
+
self.results['tests_failed'] += 1
|
|
319
|
+
self.results['unexpected_results'].append({'test': 'multi_team', 'error': str(e)})
|
|
320
|
+
|
|
321
|
+
def test_database_branching(self):
|
|
322
|
+
"""Test 7: Database-Level Branching - Schema changes across entire databases"""
|
|
323
|
+
print("\n=== Test 7: Database-Level Branching ===")
|
|
324
|
+
self.results['tests_run'] += 1
|
|
325
|
+
|
|
326
|
+
try:
|
|
327
|
+
host, port, user, password, database = get_connection_params()
|
|
328
|
+
client = Client(logger=self.logger, sql_log_mode="full")
|
|
329
|
+
client.connect(host=host, port=port, user=user, password=password, database=database)
|
|
330
|
+
|
|
331
|
+
prod_db = f"shop_prod_{int(time.time())}"
|
|
332
|
+
dev_db = f"shop_dev_{int(time.time())}"
|
|
333
|
+
|
|
334
|
+
# Setup production database
|
|
335
|
+
client.execute(f"DROP DATABASE IF EXISTS {prod_db}")
|
|
336
|
+
client.execute(f"CREATE DATABASE {prod_db}")
|
|
337
|
+
client.execute(f"USE {prod_db}")
|
|
338
|
+
client.execute("""
|
|
339
|
+
CREATE TABLE customers (
|
|
340
|
+
id INT PRIMARY KEY,
|
|
341
|
+
name VARCHAR(100)
|
|
342
|
+
)
|
|
343
|
+
""")
|
|
344
|
+
client.execute("""
|
|
345
|
+
CREATE TABLE orders (
|
|
346
|
+
id INT PRIMARY KEY,
|
|
347
|
+
customer_id INT,
|
|
348
|
+
amount DECIMAL(10,2)
|
|
349
|
+
)
|
|
350
|
+
""")
|
|
351
|
+
client.execute("INSERT INTO customers VALUES (1, 'Alice')")
|
|
352
|
+
client.execute("INSERT INTO orders VALUES (1, 1, 100.00)")
|
|
353
|
+
|
|
354
|
+
# Create dev database branch
|
|
355
|
+
client.branch.create(dev_db, prod_db, database=True)
|
|
356
|
+
|
|
357
|
+
# Test schema changes
|
|
358
|
+
client.execute(f"USE {dev_db}")
|
|
359
|
+
client.execute("ALTER TABLE customers ADD COLUMN email VARCHAR(100)")
|
|
360
|
+
client.execute("ALTER TABLE orders ADD COLUMN status VARCHAR(20)")
|
|
361
|
+
result = client.execute("DESCRIBE customers")
|
|
362
|
+
columns = len(result.rows)
|
|
363
|
+
|
|
364
|
+
# Cleanup
|
|
365
|
+
client.execute(f"USE test")
|
|
366
|
+
client.execute(f"DROP DATABASE {prod_db}")
|
|
367
|
+
client.execute(f"DROP DATABASE {dev_db}")
|
|
368
|
+
|
|
369
|
+
self.logger.info(f"✅ Database-level branching test passed ({columns} columns)")
|
|
370
|
+
self.results['tests_passed'] += 1
|
|
371
|
+
client.disconnect()
|
|
372
|
+
|
|
373
|
+
except Exception as e:
|
|
374
|
+
self.logger.error(f"❌ Database-level branching test failed: {e}")
|
|
375
|
+
self.results['tests_failed'] += 1
|
|
376
|
+
self.results['unexpected_results'].append({'test': 'database_branching', 'error': str(e)})
|
|
377
|
+
|
|
378
|
+
async def test_async_operations(self):
|
|
379
|
+
"""Test 8: Async Operations - Non-blocking branch operations"""
|
|
380
|
+
print("\n=== Test 8: Async Operations ===")
|
|
381
|
+
self.results['tests_run'] += 1
|
|
382
|
+
|
|
383
|
+
try:
|
|
384
|
+
host, port, user, password, database = get_connection_params()
|
|
385
|
+
async_client = AsyncClient(logger=self.logger, sql_log_mode="full")
|
|
386
|
+
await async_client.connect(host=host, port=port, user=user, password=password, database=database)
|
|
387
|
+
|
|
388
|
+
# Create table
|
|
389
|
+
await async_client.execute("DROP TABLE IF EXISTS async_test")
|
|
390
|
+
await async_client.execute("CREATE TABLE async_test (id INT PRIMARY KEY)")
|
|
391
|
+
await async_client.execute("INSERT INTO async_test VALUES (1)")
|
|
392
|
+
|
|
393
|
+
# Async branch operations
|
|
394
|
+
await async_client.branch.create('async_test_branch', 'async_test')
|
|
395
|
+
diffs = await async_client.branch.diff('async_test_branch', 'async_test')
|
|
396
|
+
await async_client.branch.delete('async_test_branch')
|
|
397
|
+
|
|
398
|
+
await async_client.execute("DROP TABLE async_test")
|
|
399
|
+
await async_client.disconnect()
|
|
400
|
+
|
|
401
|
+
self.logger.info("✅ Async operations test passed")
|
|
402
|
+
self.results['tests_passed'] += 1
|
|
403
|
+
|
|
404
|
+
except Exception as e:
|
|
405
|
+
self.logger.error(f"❌ Async operations test failed: {e}")
|
|
406
|
+
self.results['tests_failed'] += 1
|
|
407
|
+
self.results['unexpected_results'].append({'test': 'async_operations', 'error': str(e)})
|
|
408
|
+
|
|
409
|
+
def generate_summary_report(self):
|
|
410
|
+
"""Generate comprehensive summary report."""
|
|
411
|
+
print("\n" + "="*80)
|
|
412
|
+
print("Branch Operations Demo - Summary Report")
|
|
413
|
+
print("="*80)
|
|
414
|
+
|
|
415
|
+
total_tests = self.results['tests_run']
|
|
416
|
+
passed_tests = self.results['tests_passed']
|
|
417
|
+
failed_tests = self.results['tests_failed']
|
|
418
|
+
unexpected_results = self.results['unexpected_results']
|
|
419
|
+
|
|
420
|
+
print(f"Total Tests Run: {total_tests}")
|
|
421
|
+
print(f"Tests Passed: {passed_tests}")
|
|
422
|
+
print(f"Tests Failed: {failed_tests}")
|
|
423
|
+
print(f"Success Rate: {(passed_tests/total_tests*100):.1f}%" if total_tests > 0 else "N/A")
|
|
424
|
+
|
|
425
|
+
if unexpected_results:
|
|
426
|
+
print(f"\nUnexpected Results ({len(unexpected_results)}):")
|
|
427
|
+
for i, result in enumerate(unexpected_results, 1):
|
|
428
|
+
print(f" {i}. Test: {result['test']}")
|
|
429
|
+
print(f" Error: {result['error']}")
|
|
430
|
+
else:
|
|
431
|
+
print("\n✓ No unexpected results - all tests behaved as expected")
|
|
432
|
+
|
|
433
|
+
return self.results
|
|
434
|
+
|
|
435
|
+
def run_all_tests(self):
|
|
436
|
+
"""Run all tests"""
|
|
437
|
+
print("\n" + "="*80)
|
|
438
|
+
print("MatrixOne Branch Operations - Complete Examples")
|
|
439
|
+
print("="*80)
|
|
440
|
+
print_config()
|
|
441
|
+
|
|
442
|
+
self.test_basic_api()
|
|
443
|
+
self.test_orm_model_support()
|
|
444
|
+
self.test_dev_workflow()
|
|
445
|
+
self.test_safe_migration()
|
|
446
|
+
self.test_ab_testing()
|
|
447
|
+
self.test_multi_team()
|
|
448
|
+
self.test_database_branching()
|
|
449
|
+
asyncio.run(self.test_async_operations())
|
|
450
|
+
|
|
451
|
+
results = self.generate_summary_report()
|
|
452
|
+
|
|
453
|
+
print("\n" + "="*80)
|
|
454
|
+
print("Key Features Demonstrated:")
|
|
455
|
+
print("="*80)
|
|
456
|
+
print("1. ✓ Simple API: create(), delete(), diff(), merge()")
|
|
457
|
+
print("2. ✓ ORM Model Support: Works with SQLAlchemy models")
|
|
458
|
+
print("3. ✓ Development Workflow: Isolate, test, merge changes")
|
|
459
|
+
print("4. ✓ Safe Migration: Snapshot-based testing")
|
|
460
|
+
print("5. ✓ A/B Testing: Compare different data transformations")
|
|
461
|
+
print("6. ✓ Multi-Team: Independent branches with conflict resolution")
|
|
462
|
+
print("7. ✓ Database Branching: Schema changes across entire databases")
|
|
463
|
+
print("8. ✓ Async Support: Non-blocking operations")
|
|
464
|
+
|
|
465
|
+
return results
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
def main():
|
|
469
|
+
"""Main entry point"""
|
|
470
|
+
demo = BranchOperationsDemo()
|
|
471
|
+
demo.run_all_tests()
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
if __name__ == '__main__':
|
|
475
|
+
main()
|