cinchdb 0.1.14__py3-none-any.whl → 0.1.17__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.
- cinchdb/__init__.py +5 -1
- cinchdb/cli/commands/__init__.py +2 -1
- cinchdb/cli/commands/data.py +350 -0
- cinchdb/cli/commands/index.py +2 -2
- cinchdb/cli/commands/tenant.py +47 -0
- cinchdb/cli/main.py +3 -6
- cinchdb/config.py +4 -13
- cinchdb/core/connection.py +14 -18
- cinchdb/core/database.py +224 -75
- cinchdb/core/maintenance_utils.py +43 -0
- cinchdb/core/path_utils.py +20 -22
- cinchdb/core/tenant_activation.py +216 -0
- cinchdb/infrastructure/metadata_connection_pool.py +0 -1
- cinchdb/infrastructure/metadata_db.py +108 -1
- cinchdb/managers/branch.py +1 -1
- cinchdb/managers/change_applier.py +21 -22
- cinchdb/managers/column.py +1 -1
- cinchdb/managers/data.py +190 -14
- cinchdb/managers/index.py +1 -2
- cinchdb/managers/query.py +0 -1
- cinchdb/managers/table.py +31 -6
- cinchdb/managers/tenant.py +90 -150
- cinchdb/managers/view.py +1 -1
- cinchdb/plugins/__init__.py +16 -0
- cinchdb/plugins/base.py +80 -0
- cinchdb/plugins/decorators.py +49 -0
- cinchdb/plugins/manager.py +210 -0
- {cinchdb-0.1.14.dist-info → cinchdb-0.1.17.dist-info}/METADATA +19 -24
- {cinchdb-0.1.14.dist-info → cinchdb-0.1.17.dist-info}/RECORD +32 -28
- cinchdb/core/maintenance.py +0 -73
- cinchdb/security/__init__.py +0 -1
- cinchdb/security/encryption.py +0 -108
- {cinchdb-0.1.14.dist-info → cinchdb-0.1.17.dist-info}/WHEEL +0 -0
- {cinchdb-0.1.14.dist-info → cinchdb-0.1.17.dist-info}/entry_points.txt +0 -0
- {cinchdb-0.1.14.dist-info → cinchdb-0.1.17.dist-info}/licenses/LICENSE +0 -0
cinchdb/plugins/base.py
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
"""
|
2
|
+
Simple base class for CinchDB plugins.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from typing import Any, Dict, Optional
|
6
|
+
|
7
|
+
|
8
|
+
class Plugin:
|
9
|
+
"""Simple base class for CinchDB plugins."""
|
10
|
+
|
11
|
+
# Plugin metadata - override in subclass
|
12
|
+
name: str = "unnamed_plugin"
|
13
|
+
version: str = "1.0.0"
|
14
|
+
description: str = ""
|
15
|
+
|
16
|
+
def extend_database(self, db) -> None:
|
17
|
+
"""Add methods or modify the database instance.
|
18
|
+
|
19
|
+
Override this method to add custom methods to database instances:
|
20
|
+
|
21
|
+
Example:
|
22
|
+
def extend_database(self, db):
|
23
|
+
db.my_custom_method = self.my_custom_method
|
24
|
+
"""
|
25
|
+
pass
|
26
|
+
|
27
|
+
def before_query(self, sql: str, params: Optional[tuple] = None) -> tuple:
|
28
|
+
"""Called before executing any SQL query.
|
29
|
+
|
30
|
+
Args:
|
31
|
+
sql: The SQL statement to be executed
|
32
|
+
params: Parameters for the SQL statement
|
33
|
+
|
34
|
+
Returns:
|
35
|
+
Tuple of (modified_sql, modified_params)
|
36
|
+
"""
|
37
|
+
return sql, params
|
38
|
+
|
39
|
+
def after_query(self, sql: str, params: Optional[tuple], result: Any) -> Any:
|
40
|
+
"""Called after executing any SQL query.
|
41
|
+
|
42
|
+
Args:
|
43
|
+
sql: The SQL statement that was executed
|
44
|
+
params: Parameters that were used
|
45
|
+
result: The query result
|
46
|
+
|
47
|
+
Returns:
|
48
|
+
Modified result (or original result)
|
49
|
+
"""
|
50
|
+
return result
|
51
|
+
|
52
|
+
def on_connect(self, db_path: str, connection) -> None:
|
53
|
+
"""Called when a database connection is established.
|
54
|
+
|
55
|
+
Args:
|
56
|
+
db_path: Path to the database file
|
57
|
+
connection: SQLite connection object
|
58
|
+
"""
|
59
|
+
pass
|
60
|
+
|
61
|
+
def on_disconnect(self, db_path: str) -> None:
|
62
|
+
"""Called when a database connection is closed.
|
63
|
+
|
64
|
+
Args:
|
65
|
+
db_path: Path to the database file
|
66
|
+
"""
|
67
|
+
pass
|
68
|
+
|
69
|
+
def cleanup(self) -> None:
|
70
|
+
"""Called when plugin is being unloaded."""
|
71
|
+
pass
|
72
|
+
|
73
|
+
@property
|
74
|
+
def metadata(self) -> Dict[str, Any]:
|
75
|
+
"""Get plugin metadata."""
|
76
|
+
return {
|
77
|
+
"name": self.name,
|
78
|
+
"version": self.version,
|
79
|
+
"description": self.description,
|
80
|
+
}
|
@@ -0,0 +1,49 @@
|
|
1
|
+
"""
|
2
|
+
Simple decorators for plugin development.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from typing import Callable
|
6
|
+
|
7
|
+
|
8
|
+
def database_method(method_name: str):
|
9
|
+
"""Decorator to mark a method for database extension.
|
10
|
+
|
11
|
+
Usage:
|
12
|
+
class Plugin:
|
13
|
+
@database_method("my_method")
|
14
|
+
def my_custom_method(self, db):
|
15
|
+
return "Hello from plugin!"
|
16
|
+
"""
|
17
|
+
def decorator(func: Callable) -> Callable:
|
18
|
+
func._database_method_name = method_name
|
19
|
+
return func
|
20
|
+
return decorator
|
21
|
+
|
22
|
+
|
23
|
+
def auto_extend(plugin_class):
|
24
|
+
"""Class decorator to automatically extend databases with decorated methods.
|
25
|
+
|
26
|
+
Usage:
|
27
|
+
@auto_extend
|
28
|
+
class Plugin:
|
29
|
+
@database_method("custom_query")
|
30
|
+
def custom_query_method(self, db, query):
|
31
|
+
# Method will be added to db instances as db.custom_query()
|
32
|
+
return "Custom result"
|
33
|
+
"""
|
34
|
+
original_extend = getattr(plugin_class, 'extend_database', None)
|
35
|
+
|
36
|
+
def new_extend_database(self, db):
|
37
|
+
# Call original extend_database if it exists
|
38
|
+
if original_extend:
|
39
|
+
original_extend(self, db)
|
40
|
+
|
41
|
+
# Auto-add decorated methods
|
42
|
+
for attr_name in dir(self):
|
43
|
+
attr = getattr(self, attr_name)
|
44
|
+
if callable(attr) and hasattr(attr, '_database_method_name'):
|
45
|
+
method_name = attr._database_method_name
|
46
|
+
setattr(db, method_name, lambda *args, **kwargs: attr(db, *args, **kwargs))
|
47
|
+
|
48
|
+
plugin_class.extend_database = new_extend_database
|
49
|
+
return plugin_class
|
@@ -0,0 +1,210 @@
|
|
1
|
+
"""
|
2
|
+
Simple plugin manager for CinchDB.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import importlib
|
6
|
+
import importlib.util
|
7
|
+
import logging
|
8
|
+
from pathlib import Path
|
9
|
+
from typing import Dict, List, Optional, Any, Union
|
10
|
+
|
11
|
+
from .base import Plugin
|
12
|
+
|
13
|
+
logger = logging.getLogger(__name__)
|
14
|
+
|
15
|
+
|
16
|
+
class PluginManager:
|
17
|
+
"""Simple plugin manager for CinchDB."""
|
18
|
+
|
19
|
+
def __init__(self):
|
20
|
+
self.plugins: Dict[str, Plugin] = {}
|
21
|
+
self._database_instances: List[Any] = []
|
22
|
+
|
23
|
+
def register_plugin(self, plugin: Union[Plugin, type]) -> None:
|
24
|
+
"""Register a plugin instance or class."""
|
25
|
+
# If it's a class, instantiate it
|
26
|
+
if isinstance(plugin, type):
|
27
|
+
plugin = plugin()
|
28
|
+
|
29
|
+
plugin_name = plugin.name
|
30
|
+
|
31
|
+
if plugin_name in self.plugins:
|
32
|
+
logger.warning(f"Plugin {plugin_name} already registered, replacing")
|
33
|
+
|
34
|
+
self.plugins[plugin_name] = plugin
|
35
|
+
|
36
|
+
# Apply to existing database instances
|
37
|
+
for db_instance in self._database_instances:
|
38
|
+
try:
|
39
|
+
plugin.extend_database(db_instance)
|
40
|
+
except Exception as e:
|
41
|
+
logger.error(f"Failed to extend database with plugin {plugin_name}: {e}")
|
42
|
+
|
43
|
+
logger.info(f"Plugin {plugin_name} registered successfully")
|
44
|
+
|
45
|
+
def unregister_plugin(self, plugin_name: str) -> None:
|
46
|
+
"""Unregister a plugin."""
|
47
|
+
if plugin_name in self.plugins:
|
48
|
+
plugin = self.plugins[plugin_name]
|
49
|
+
try:
|
50
|
+
plugin.cleanup()
|
51
|
+
except Exception as e:
|
52
|
+
logger.error(f"Error cleaning up plugin {plugin_name}: {e}")
|
53
|
+
|
54
|
+
del self.plugins[plugin_name]
|
55
|
+
logger.info(f"Plugin {plugin_name} unregistered")
|
56
|
+
|
57
|
+
def load_plugin_from_module(self, module_name: str) -> None:
|
58
|
+
"""Load a plugin from a module name."""
|
59
|
+
try:
|
60
|
+
module = importlib.import_module(module_name)
|
61
|
+
|
62
|
+
# Look for Plugin class first
|
63
|
+
if hasattr(module, 'Plugin'):
|
64
|
+
plugin_instance = module.Plugin()
|
65
|
+
self.register_plugin(plugin_instance)
|
66
|
+
return
|
67
|
+
|
68
|
+
# Fallback: look for any Plugin subclass
|
69
|
+
for attr_name in dir(module):
|
70
|
+
attr = getattr(module, attr_name)
|
71
|
+
if (isinstance(attr, type) and
|
72
|
+
issubclass(attr, Plugin) and
|
73
|
+
attr != Plugin):
|
74
|
+
|
75
|
+
plugin_instance = attr()
|
76
|
+
self.register_plugin(plugin_instance)
|
77
|
+
return
|
78
|
+
|
79
|
+
logger.warning(f"No Plugin class found in module {module_name}")
|
80
|
+
|
81
|
+
except ImportError as e:
|
82
|
+
logger.error(f"Failed to import plugin module {module_name}: {e}")
|
83
|
+
except Exception as e:
|
84
|
+
logger.error(f"Failed to load plugin from {module_name}: {e}")
|
85
|
+
|
86
|
+
def load_plugin_from_file(self, file_path: Path) -> None:
|
87
|
+
"""Load a plugin from a Python file."""
|
88
|
+
try:
|
89
|
+
spec = importlib.util.spec_from_file_location("plugin_module", file_path)
|
90
|
+
if spec and spec.loader:
|
91
|
+
module = importlib.util.module_from_spec(spec)
|
92
|
+
spec.loader.exec_module(module)
|
93
|
+
|
94
|
+
# Look for Plugin class
|
95
|
+
if hasattr(module, 'Plugin'):
|
96
|
+
plugin_instance = module.Plugin()
|
97
|
+
self.register_plugin(plugin_instance)
|
98
|
+
return
|
99
|
+
|
100
|
+
logger.warning(f"No Plugin class found in file {file_path}")
|
101
|
+
|
102
|
+
except Exception as e:
|
103
|
+
logger.error(f"Failed to load plugin from file {file_path}: {e}")
|
104
|
+
|
105
|
+
def load_plugins_from_directory(self, plugins_dir: Path) -> None:
|
106
|
+
"""Load all plugins from a directory."""
|
107
|
+
if not plugins_dir.exists():
|
108
|
+
logger.info(f"Plugins directory {plugins_dir} does not exist")
|
109
|
+
return
|
110
|
+
|
111
|
+
for plugin_file in plugins_dir.glob("*.py"):
|
112
|
+
if plugin_file.name == "__init__.py":
|
113
|
+
continue
|
114
|
+
self.load_plugin_from_file(plugin_file)
|
115
|
+
|
116
|
+
def discover_plugins(self) -> None:
|
117
|
+
"""Discover plugins using entry points and plugins directory."""
|
118
|
+
# Try entry points for installed plugins
|
119
|
+
try:
|
120
|
+
try:
|
121
|
+
from importlib.metadata import entry_points
|
122
|
+
except ImportError:
|
123
|
+
from importlib_metadata import entry_points
|
124
|
+
|
125
|
+
eps = entry_points()
|
126
|
+
if hasattr(eps, 'select'):
|
127
|
+
plugin_eps = eps.select(group='cinchdb.plugins')
|
128
|
+
else:
|
129
|
+
plugin_eps = eps.get('cinchdb.plugins', [])
|
130
|
+
|
131
|
+
for entry_point in plugin_eps:
|
132
|
+
try:
|
133
|
+
plugin_class = entry_point.load()
|
134
|
+
self.register_plugin(plugin_class)
|
135
|
+
except Exception as e:
|
136
|
+
logger.error(f"Failed to load plugin {entry_point.name}: {e}")
|
137
|
+
except Exception as e:
|
138
|
+
logger.debug(f"Entry points not available: {e}")
|
139
|
+
|
140
|
+
# Also check for local plugins directory
|
141
|
+
plugins_dir = Path("plugins")
|
142
|
+
if plugins_dir.exists():
|
143
|
+
self.load_plugins_from_directory(plugins_dir)
|
144
|
+
|
145
|
+
def register_database(self, db_instance) -> None:
|
146
|
+
"""Register a database instance with all plugins."""
|
147
|
+
self._database_instances.append(db_instance)
|
148
|
+
|
149
|
+
# Apply all plugins to this database instance
|
150
|
+
for plugin in self.plugins.values():
|
151
|
+
try:
|
152
|
+
plugin.extend_database(db_instance)
|
153
|
+
except Exception as e:
|
154
|
+
logger.error(f"Failed to extend database with plugin {plugin.name}: {e}")
|
155
|
+
|
156
|
+
def unregister_database(self, db_instance) -> None:
|
157
|
+
"""Unregister a database instance."""
|
158
|
+
if db_instance in self._database_instances:
|
159
|
+
self._database_instances.remove(db_instance)
|
160
|
+
|
161
|
+
def before_query(self, sql: str, params: Optional[tuple] = None) -> tuple:
|
162
|
+
"""Call before_query on all plugins."""
|
163
|
+
for plugin in self.plugins.values():
|
164
|
+
try:
|
165
|
+
sql, params = plugin.before_query(sql, params)
|
166
|
+
except Exception as e:
|
167
|
+
logger.error(f"Plugin {plugin.name} before_query failed: {e}")
|
168
|
+
return sql, params
|
169
|
+
|
170
|
+
def after_query(self, sql: str, params: Optional[tuple], result: Any) -> Any:
|
171
|
+
"""Call after_query on all plugins."""
|
172
|
+
for plugin in self.plugins.values():
|
173
|
+
try:
|
174
|
+
result = plugin.after_query(sql, params, result)
|
175
|
+
except Exception as e:
|
176
|
+
logger.error(f"Plugin {plugin.name} after_query failed: {e}")
|
177
|
+
return result
|
178
|
+
|
179
|
+
def on_connect(self, db_path: str, connection) -> None:
|
180
|
+
"""Call on_connect on all plugins."""
|
181
|
+
for plugin in self.plugins.values():
|
182
|
+
try:
|
183
|
+
plugin.on_connect(db_path, connection)
|
184
|
+
except Exception as e:
|
185
|
+
logger.error(f"Plugin {plugin.name} on_connect failed: {e}")
|
186
|
+
|
187
|
+
def on_disconnect(self, db_path: str) -> None:
|
188
|
+
"""Call on_disconnect on all plugins."""
|
189
|
+
for plugin in self.plugins.values():
|
190
|
+
try:
|
191
|
+
plugin.on_disconnect(db_path)
|
192
|
+
except Exception as e:
|
193
|
+
logger.error(f"Plugin {plugin.name} on_disconnect failed: {e}")
|
194
|
+
|
195
|
+
def get_plugin(self, name: str) -> Optional[Plugin]:
|
196
|
+
"""Get a plugin by name."""
|
197
|
+
return self.plugins.get(name)
|
198
|
+
|
199
|
+
def list_plugins(self) -> List[Dict[str, Any]]:
|
200
|
+
"""List all registered plugins with their metadata."""
|
201
|
+
return [plugin.metadata for plugin in self.plugins.values()]
|
202
|
+
|
203
|
+
def plugin_exists(self, name: str) -> bool:
|
204
|
+
"""Check if a plugin is registered."""
|
205
|
+
return name in self.plugins
|
206
|
+
|
207
|
+
def cleanup_all(self) -> None:
|
208
|
+
"""Cleanup all plugins."""
|
209
|
+
for plugin_name in list(self.plugins.keys()):
|
210
|
+
self.unregister_plugin(plugin_name)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: cinchdb
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.17
|
4
4
|
Summary: A Git-like SQLite database management system with branching and multi-tenancy
|
5
5
|
Project-URL: Homepage, https://github.com/russellromney/cinchdb
|
6
6
|
Project-URL: Documentation, https://russellromney.github.io/cinchdb
|
@@ -31,6 +31,10 @@ Description-Content-Type: text/markdown
|
|
31
31
|
|
32
32
|
**Git-like SQLite database management with branching and multi-tenancy**
|
33
33
|
|
34
|
+
[](https://badge.fury.io/py/cinchdb)
|
35
|
+
[](https://www.python.org/downloads/)
|
36
|
+
|
37
|
+
|
34
38
|
NOTE: CinchDB is in early alpha. This is project to test out an idea. Do not use this in production.
|
35
39
|
|
36
40
|
CinchDB is for projects that need fast queries, isolated data per-tenant [or even per-user](https://turso.tech/blog/give-each-of-your-users-their-own-sqlite-database-b74445f4), and a branchable database that makes it easy to merge changes between branches.
|
@@ -156,24 +160,16 @@ db.update("posts", post_id, {"content": "Updated content"})
|
|
156
160
|
- **Python SDK**: Core functionality for local development
|
157
161
|
- **CLI**: Full-featured command-line interface
|
158
162
|
|
159
|
-
## Security
|
163
|
+
## Security
|
160
164
|
|
161
|
-
|
165
|
+
CinchDB uses standard SQLite security features:
|
162
166
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
+
- **WAL mode**: Better concurrency and crash recovery
|
168
|
+
- **Foreign key constraints**: Enforced data integrity
|
169
|
+
- **File permissions**: Standard OS-level access control
|
170
|
+
- **Multi-tenant isolation**: Separate database files per tenant
|
167
171
|
|
168
|
-
|
169
|
-
pip install pysqlcipher3
|
170
|
-
```
|
171
|
-
|
172
|
-
- **Tenant databases**: Encrypted with ChaCha20-Poly1305 (~2-5% overhead)
|
173
|
-
- **Metadata**: Unencrypted for operational simplicity
|
174
|
-
- **Integration**: Transparent - no code changes needed
|
175
|
-
|
176
|
-
Works without encryption libraries - gracefully falls back to standard SQLite.
|
172
|
+
For production deployments, consider additional security measures at the infrastructure level.
|
177
173
|
|
178
174
|
## Development
|
179
175
|
|
@@ -186,14 +182,13 @@ make test
|
|
186
182
|
|
187
183
|
## Future
|
188
184
|
|
189
|
-
|
190
|
-
|
191
|
-
-
|
192
|
-
-
|
193
|
-
-
|
194
|
-
-
|
195
|
-
-
|
196
|
-
- leader-follower abilities for edge deployment
|
185
|
+
CinchDB focuses on being a simple, reliable SQLite management layer. Future development will prioritize:
|
186
|
+
|
187
|
+
- Remote API server improvements
|
188
|
+
- Better CLI user experience
|
189
|
+
- Performance optimizations
|
190
|
+
- Additional language SDKs (TypeScript, Go, etc.)
|
191
|
+
- Enhanced codegen features
|
197
192
|
|
198
193
|
|
199
194
|
## License
|
@@ -1,44 +1,46 @@
|
|
1
|
-
cinchdb/__init__.py,sha256=
|
1
|
+
cinchdb/__init__.py,sha256=qnBv5kpL4GJjNbQkp-nBcOgrsskwDr7WTnUPCGwg3zs,603
|
2
2
|
cinchdb/__main__.py,sha256=OpkDqn9zkTZhhYgvv_grswWLAHKbmxs4M-8C6Z5HfWY,85
|
3
|
-
cinchdb/config.py,sha256=
|
3
|
+
cinchdb/config.py,sha256=hYcT91mMcV0NEvJMAgtJkihfaxHJ97qqGOetnlaNZjI,4889
|
4
4
|
cinchdb/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
|
-
cinchdb/cli/main.py,sha256=
|
5
|
+
cinchdb/cli/main.py,sha256=x2bF19e0lY_0V3_tx6CO9M3AaFlII75bAdWaOg0ne_A,4587
|
6
6
|
cinchdb/cli/utils.py,sha256=InAGuo7uENvsfBQu6EKIbbiNXqmvg-BecLmhLvOz5qg,6485
|
7
|
-
cinchdb/cli/commands/__init__.py,sha256=
|
7
|
+
cinchdb/cli/commands/__init__.py,sha256=Wjj_cmfscb_mpC-yFXxWW0-pJoBlI-Vpunb37SARsTs,291
|
8
8
|
cinchdb/cli/commands/branch.py,sha256=Nz8YQYJ7lizSXEAv0usTx85TDOC-N5Ul9KIxN8JQtKc,17973
|
9
9
|
cinchdb/cli/commands/codegen.py,sha256=WsRWmXNTDuaLPyECW5psXM9zOQnKHpUiv8BJnBAjMII,6189
|
10
10
|
cinchdb/cli/commands/column.py,sha256=nUVgjYbxSobJR9CC-qdOvrCl1TgFySr-ctQb0djKZtQ,11676
|
11
|
+
cinchdb/cli/commands/data.py,sha256=tRXVeYCShVp5VKJ--Q_XzTokPE6G0gaHSad48iFHa3E,12662
|
11
12
|
cinchdb/cli/commands/database.py,sha256=A3ew8Z987a25dRxmY5XeoB-Ka6SXLKtWogj3XAKUNZE,7332
|
12
|
-
cinchdb/cli/commands/index.py,sha256=
|
13
|
+
cinchdb/cli/commands/index.py,sha256=qL3-UOk_cBLBNR4qK2uPTyFI7qAMxQSE7YtXFXNeSdg,6094
|
13
14
|
cinchdb/cli/commands/query.py,sha256=XW_YL6M5IYHHHMpVB5p-M01kawFxwDOK5B5hGIy_BA8,5044
|
14
15
|
cinchdb/cli/commands/remote.py,sha256=i07hfiAxgrROB9lVJVaKK_nWxT1SGiSbtFb4jvEwxEo,4445
|
15
16
|
cinchdb/cli/commands/table.py,sha256=K233WOV102-g6QlHhL97FhfiGz67zIctbxZblAjY4Lg,10516
|
16
|
-
cinchdb/cli/commands/tenant.py,sha256=
|
17
|
+
cinchdb/cli/commands/tenant.py,sha256=snpMc9Jk2uGmEJCKP39zD26BZLYBhiRxP2M5oGyz0rQ,8309
|
17
18
|
cinchdb/cli/commands/view.py,sha256=ZmS1IW7idzzHAXmgVyY3C4IQRo7toHb6fHNFY_tQJjI,6385
|
18
19
|
cinchdb/cli/handlers/__init__.py,sha256=f2f-Cc96rSBLbVsiIbf-b4pZCKZoHfmhNEvnZ0OurRs,131
|
19
20
|
cinchdb/cli/handlers/codegen_handler.py,sha256=i5we_AbiUW3zfO6pIKWxvtO8OvOqz3H__4xPmTLEuQM,6524
|
20
21
|
cinchdb/core/__init__.py,sha256=iNlT0iO9cM0HLoYwzBavUBoXRh1Tcnz1l_vfbwVxK_Q,246
|
21
|
-
cinchdb/core/connection.py,sha256=
|
22
|
-
cinchdb/core/database.py,sha256=
|
22
|
+
cinchdb/core/connection.py,sha256=_Yt0ki4IbNQJ6lwvloDZ58CCA7SBJwHaB1SU1Yy-rpI,5186
|
23
|
+
cinchdb/core/database.py,sha256=4jZwJE4UMpPv77wyQbn0FaTB1MMJv0BHl3umLJzO0q4,33680
|
23
24
|
cinchdb/core/initializer.py,sha256=CAzq947plgEF-KXV-PD-ycJ8Zy4zXCQqCrmQ0-pV0i4,14474
|
24
|
-
cinchdb/core/
|
25
|
-
cinchdb/core/path_utils.py,sha256=
|
26
|
-
cinchdb/
|
27
|
-
cinchdb/infrastructure/
|
25
|
+
cinchdb/core/maintenance_utils.py,sha256=FYEZK2FybghuGxo37QeTd1DLsuId0p3rKGIlpPTFJZw,1852
|
26
|
+
cinchdb/core/path_utils.py,sha256=xcclmafzETDtivr-Qoi2gP6xZh5C49EoR6duc6YY4gw,4698
|
27
|
+
cinchdb/core/tenant_activation.py,sha256=56FaxgZJ2IGYcF6arJfr0gc63wBpyQ4E9ssJDghlPmA,7290
|
28
|
+
cinchdb/infrastructure/metadata_connection_pool.py,sha256=PeVsyuZKdI3Y7_InWMOn9HcSNhE0MVcP7I7wAj9esmI,4859
|
29
|
+
cinchdb/infrastructure/metadata_db.py,sha256=gP-UORs2Ec33p9_64L2xPTZ51PQDYrmfHlofJfQUM2M,19933
|
28
30
|
cinchdb/managers/__init__.py,sha256=ic61ZUdsg-muq0ETYO6fuZRQWF4j7l920PthTkt2QrE,808
|
29
|
-
cinchdb/managers/branch.py,sha256=
|
30
|
-
cinchdb/managers/change_applier.py,sha256=
|
31
|
+
cinchdb/managers/branch.py,sha256=bEzl83mN9a9KW8AHC_w50QHtVdVPHUzEY-j9T2EAyR4,9445
|
32
|
+
cinchdb/managers/change_applier.py,sha256=JWfK-B42fCc5ANGvIIogzF5SeSCkSO16y67RCNsPu08,15602
|
31
33
|
cinchdb/managers/change_comparator.py,sha256=08pwybpSt36cFwhZRSIkHynvFMUaLKEVwa8Ajn_R9yQ,6862
|
32
34
|
cinchdb/managers/change_tracker.py,sha256=U93BPnuGv8xSaO5qr_y5Q8ppKrVXygozdp5zUvLUqwg,5054
|
33
35
|
cinchdb/managers/codegen.py,sha256=1CfIwjgHnNDdjrq4SzQ9VE7DFgnWfk7RtpupBFUTqxk,21804
|
34
|
-
cinchdb/managers/column.py,sha256=
|
35
|
-
cinchdb/managers/data.py,sha256=
|
36
|
-
cinchdb/managers/index.py,sha256=
|
36
|
+
cinchdb/managers/column.py,sha256=JyBIu372ExMppvqklhtVAc-eZzikIqedB_8uoJtFu-Y,20755
|
37
|
+
cinchdb/managers/data.py,sha256=zAtVLR4GaiY7ijIbWAXC7JN701KtX2zgcg4ZUlGpyts,23176
|
38
|
+
cinchdb/managers/index.py,sha256=55_fafWd4lSju4Nw32B_1Fi1c2DPHxOgAhdsqcTR404,10343
|
37
39
|
cinchdb/managers/merge_manager.py,sha256=R8S2hLkLJg4hLDpeJTzjVkduZgqPOjXtYgOSJhTXXrE,15690
|
38
|
-
cinchdb/managers/query.py,sha256=
|
39
|
-
cinchdb/managers/table.py,sha256=
|
40
|
-
cinchdb/managers/tenant.py,sha256=
|
41
|
-
cinchdb/managers/view.py,sha256=
|
40
|
+
cinchdb/managers/query.py,sha256=hgNHda20PWMijbVRnjTKFGFBvBthep2fulkJEK1gkqU,8562
|
41
|
+
cinchdb/managers/table.py,sha256=mv5p5WCXy1VXKRyaAZquZofdKR0gEth7It82j6p7j0E,15252
|
42
|
+
cinchdb/managers/tenant.py,sha256=gk3Nn68n4avl2mcMcPB5HuKOsOPsMhnvgxHyK8Uwuik,31634
|
43
|
+
cinchdb/managers/view.py,sha256=uXcRnZxF4TCcycbwH7cTje_QU3NO47fmOzJULQHHeVI,7815
|
42
44
|
cinchdb/models/__init__.py,sha256=cZ-ailJ6qu44Iap5Rq555iB-_w9ufXVDBH3rDH-ojk0,622
|
43
45
|
cinchdb/models/base.py,sha256=7j4rlFTP5K9ZuF8vxwC7lMFEaL7O90NJ47Ig5i7ubcw,1320
|
44
46
|
cinchdb/models/branch.py,sha256=gRgLpRFkMC3fxf9ZigVOkS6wdkBERWqlLk0_gOYjqNk,1180
|
@@ -48,13 +50,15 @@ cinchdb/models/project.py,sha256=6GMXUZUsEIebqQJgRXIthWzpWKuNNmJ3drgI1vFDrMo,644
|
|
48
50
|
cinchdb/models/table.py,sha256=nxf__C_YvDOW-6-vdOQ4xKmwWPZwgEdYw1I4mO_C44o,2955
|
49
51
|
cinchdb/models/tenant.py,sha256=wTvGoO_tQdtueVB0faZFVIhSjh_DNJzywflrUpngWTM,1072
|
50
52
|
cinchdb/models/view.py,sha256=q6j-jYzFJuhRJO87rKt6Uv8hOizHQx8xwoPKoH6XnNY,530
|
51
|
-
cinchdb/
|
52
|
-
cinchdb/
|
53
|
+
cinchdb/plugins/__init__.py,sha256=jZtwwY0t_dhFKz-uR2V3KWBmAGknWnoLkiDWSzULdc8,293
|
54
|
+
cinchdb/plugins/base.py,sha256=Upfgw78eOhTAfQd9FwcgGZLIT0zefPDgD4LlpzXgOBg,2232
|
55
|
+
cinchdb/plugins/decorators.py,sha256=vY9Zh5nhGiSM6kO55srGHSP32gnyMq77UtJSO3lULq4,1550
|
56
|
+
cinchdb/plugins/manager.py,sha256=vetdsiwTkadp3BrhjUIp2vkmCpqfuXqy-3yQBdlxruQ,8143
|
53
57
|
cinchdb/utils/__init__.py,sha256=yQQhEjndDiB2SUJybUmp9dvEOQKiR-GySe-WiCius5E,490
|
54
58
|
cinchdb/utils/name_validator.py,sha256=dyGX5bjlTFRA9EGrWRQKp6kR__HSV04hLV5VueJs4IQ,4027
|
55
59
|
cinchdb/utils/sql_validator.py,sha256=aWOGlPX0gBkuR6R1EBP2stbP4PHZuI6FUBi2Ljx7JUI,5815
|
56
|
-
cinchdb-0.1.
|
57
|
-
cinchdb-0.1.
|
58
|
-
cinchdb-0.1.
|
59
|
-
cinchdb-0.1.
|
60
|
-
cinchdb-0.1.
|
60
|
+
cinchdb-0.1.17.dist-info/METADATA,sha256=cTeJ-n2bL2mqALrAZt9yPES_Xt6uC_suDB8HJmNTFTo,6155
|
61
|
+
cinchdb-0.1.17.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
62
|
+
cinchdb-0.1.17.dist-info/entry_points.txt,sha256=VBOIzvnGbkKudMCCmNORS3885QSyjZUVKJQ-Syqa62w,47
|
63
|
+
cinchdb-0.1.17.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
64
|
+
cinchdb-0.1.17.dist-info/RECORD,,
|
cinchdb/core/maintenance.py
DELETED
@@ -1,73 +0,0 @@
|
|
1
|
-
"""Maintenance mode utilities for CinchDB."""
|
2
|
-
|
3
|
-
from pathlib import Path
|
4
|
-
import json
|
5
|
-
from typing import Dict, Any, Optional
|
6
|
-
|
7
|
-
from cinchdb.core.path_utils import get_branch_path
|
8
|
-
|
9
|
-
|
10
|
-
class MaintenanceError(Exception):
|
11
|
-
"""Exception raised when operation blocked by maintenance mode."""
|
12
|
-
|
13
|
-
pass
|
14
|
-
|
15
|
-
|
16
|
-
def is_branch_in_maintenance(project_root: Path, database: str, branch: str) -> bool:
|
17
|
-
"""Check if a branch is in maintenance mode.
|
18
|
-
|
19
|
-
Args:
|
20
|
-
project_root: Path to project root
|
21
|
-
database: Database name
|
22
|
-
branch: Branch name
|
23
|
-
|
24
|
-
Returns:
|
25
|
-
True if in maintenance mode, False otherwise
|
26
|
-
"""
|
27
|
-
branch_path = get_branch_path(project_root, database, branch)
|
28
|
-
maintenance_file = branch_path / ".maintenance_mode"
|
29
|
-
return maintenance_file.exists()
|
30
|
-
|
31
|
-
|
32
|
-
def get_maintenance_info(
|
33
|
-
project_root: Path, database: str, branch: str
|
34
|
-
) -> Optional[Dict[str, Any]]:
|
35
|
-
"""Get maintenance mode information if active.
|
36
|
-
|
37
|
-
Args:
|
38
|
-
project_root: Path to project root
|
39
|
-
database: Database name
|
40
|
-
branch: Branch name
|
41
|
-
|
42
|
-
Returns:
|
43
|
-
Maintenance info dict or None if not in maintenance
|
44
|
-
"""
|
45
|
-
branch_path = get_branch_path(project_root, database, branch)
|
46
|
-
maintenance_file = branch_path / ".maintenance_mode"
|
47
|
-
|
48
|
-
if maintenance_file.exists():
|
49
|
-
with open(maintenance_file, "r") as f:
|
50
|
-
return json.load(f)
|
51
|
-
|
52
|
-
return None
|
53
|
-
|
54
|
-
|
55
|
-
def check_maintenance_mode(project_root: Path, database: str, branch: str) -> None:
|
56
|
-
"""Check maintenance mode and raise error if active.
|
57
|
-
|
58
|
-
Args:
|
59
|
-
project_root: Path to project root
|
60
|
-
database: Database name
|
61
|
-
branch: Branch name
|
62
|
-
|
63
|
-
Raises:
|
64
|
-
MaintenanceError: If branch is in maintenance mode
|
65
|
-
"""
|
66
|
-
if is_branch_in_maintenance(project_root, database, branch):
|
67
|
-
info = get_maintenance_info(project_root, database, branch)
|
68
|
-
reason = (
|
69
|
-
info.get("reason", "Maintenance in progress")
|
70
|
-
if info
|
71
|
-
else "Maintenance in progress"
|
72
|
-
)
|
73
|
-
raise MaintenanceError(f"Branch '{branch}' is in maintenance mode: {reason}")
|
cinchdb/security/__init__.py
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
"""Security module for CinchDB encryption."""
|