cinchdb 0.1.6__py3-none-any.whl → 0.1.8__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.
@@ -1 +1,16 @@
1
1
  """CLI command modules."""
2
+
3
+ from . import database, branch, tenant, table, column, view, query, codegen, remote, index
4
+
5
+ __all__ = [
6
+ "database",
7
+ "branch",
8
+ "tenant",
9
+ "table",
10
+ "column",
11
+ "view",
12
+ "query",
13
+ "codegen",
14
+ "remote",
15
+ "index",
16
+ ]
@@ -0,0 +1,186 @@
1
+ """Index management commands for CinchDB CLI."""
2
+
3
+ import typer
4
+ from typing import List, Optional
5
+ from rich import print
6
+ from rich.table import Table
7
+ from rich.console import Console
8
+
9
+ from cinchdb.config import Config
10
+ from cinchdb.managers.index import IndexManager
11
+ from cinchdb.cli.utils import handle_cli_error
12
+
13
+ app = typer.Typer(help="Manage database indexes")
14
+ console = Console()
15
+
16
+
17
+ @app.command("create")
18
+ @handle_cli_error
19
+ def create_index(
20
+ table: str = typer.Argument(..., help="Table name"),
21
+ columns: List[str] = typer.Argument(..., help="Column names to index"),
22
+ name: Optional[str] = typer.Option(None, "--name", "-n", help="Index name"),
23
+ unique: bool = typer.Option(False, "--unique", "-u", help="Create unique index"),
24
+ database: Optional[str] = typer.Option(None, "--database", "-d", help="Database name"),
25
+ branch: Optional[str] = typer.Option(None, "--branch", "-b", help="Branch name"),
26
+ ):
27
+ """Create an index on a table.
28
+
29
+ Indexes are created at the branch level and apply to all tenants.
30
+
31
+ Examples:
32
+ cinch index create users email
33
+ cinch index create orders user_id created_at --name idx_user_orders
34
+ cinch index create products sku --unique
35
+ """
36
+ config = Config()
37
+ project_config = config.load()
38
+
39
+ # Use provided values or defaults
40
+ database = database or project_config.active_database
41
+ branch = branch or project_config.active_branch
42
+
43
+ manager = IndexManager(config.base_dir, database, branch)
44
+
45
+ try:
46
+ index_name = manager.create_index(table, columns, name, unique)
47
+
48
+ unique_text = "[green]UNIQUE[/green] " if unique else ""
49
+ columns_text = ", ".join(columns)
50
+ print(f"✓ Created {unique_text}index [bold cyan]{index_name}[/bold cyan] on {table}({columns_text})")
51
+
52
+ except ValueError as e:
53
+ print(f"[red]✗ Error:[/red] {e}")
54
+ raise typer.Exit(1)
55
+
56
+
57
+ @app.command("drop")
58
+ @handle_cli_error
59
+ def drop_index(
60
+ name: str = typer.Argument(..., help="Index name"),
61
+ database: Optional[str] = typer.Option(None, "--database", "-d", help="Database name"),
62
+ branch: Optional[str] = typer.Option(None, "--branch", "-b", help="Branch name"),
63
+ ):
64
+ """Drop an index.
65
+
66
+ Indexes are managed at the branch level.
67
+
68
+ Example:
69
+ cinch index drop idx_users_email
70
+ """
71
+ config = Config()
72
+ project_config = config.load()
73
+
74
+ # Use provided values or defaults
75
+ database = database or project_config.active_database
76
+ branch = branch or project_config.active_branch
77
+
78
+ manager = IndexManager(config.base_dir, database, branch)
79
+
80
+ try:
81
+ manager.drop_index(name)
82
+ print(f"✓ Dropped index [bold cyan]{name}[/bold cyan]")
83
+ except ValueError as e:
84
+ print(f"[red]✗ Error:[/red] {e}")
85
+ raise typer.Exit(1)
86
+
87
+
88
+ @app.command("list")
89
+ @handle_cli_error
90
+ def list_indexes(
91
+ table: Optional[str] = typer.Argument(None, help="Table name to filter indexes"),
92
+ database: Optional[str] = typer.Option(None, "--database", "-d", help="Database name"),
93
+ branch: Optional[str] = typer.Option(None, "--branch", "-b", help="Branch name"),
94
+ ):
95
+ """List indexes for a table or all tables.
96
+
97
+ Indexes are managed at the branch level and apply to all tenants.
98
+
99
+ Examples:
100
+ cinch index list
101
+ cinch index list users
102
+ """
103
+ config = Config()
104
+ project_config = config.load()
105
+
106
+ # Use provided values or defaults
107
+ database = database or project_config.active_database
108
+ branch = branch or project_config.active_branch
109
+
110
+ manager = IndexManager(config.base_dir, database, branch)
111
+
112
+ indexes = manager.list_indexes(table)
113
+
114
+ if not indexes:
115
+ if table:
116
+ print(f"No indexes found for table [cyan]{table}[/cyan]")
117
+ else:
118
+ print("No indexes found")
119
+ return
120
+
121
+ # Create table for display
122
+ table_obj = Table(title=f"Indexes{f' for {table}' if table else ''}")
123
+ table_obj.add_column("Name", style="cyan")
124
+ table_obj.add_column("Table", style="yellow")
125
+ table_obj.add_column("Columns", style="green")
126
+ table_obj.add_column("Unique", style="magenta")
127
+
128
+ for idx in indexes:
129
+ columns_str = ", ".join(idx["columns"])
130
+ unique_str = "✓" if idx["unique"] else ""
131
+ table_obj.add_row(
132
+ idx["name"],
133
+ idx["table"],
134
+ columns_str,
135
+ unique_str
136
+ )
137
+
138
+ console.print(table_obj)
139
+
140
+
141
+ @app.command("info")
142
+ @handle_cli_error
143
+ def index_info(
144
+ name: str = typer.Argument(..., help="Index name"),
145
+ database: Optional[str] = typer.Option(None, "--database", "-d", help="Database name"),
146
+ branch: Optional[str] = typer.Option(None, "--branch", "-b", help="Branch name"),
147
+ ):
148
+ """Show detailed information about an index.
149
+
150
+ Example:
151
+ cinch index info idx_users_email
152
+ """
153
+ config = Config()
154
+ project_config = config.load()
155
+
156
+ # Use provided values or defaults
157
+ database = database or project_config.active_database
158
+ branch = branch or project_config.active_branch
159
+
160
+ manager = IndexManager(config.base_dir, database, branch)
161
+
162
+ try:
163
+ info = manager.get_index_info(name)
164
+
165
+ print(f"\nIndex: [bold cyan]{info['name']}[/bold cyan]")
166
+ print(f"Table: [yellow]{info['table']}[/yellow]")
167
+ print(f"Columns: [green]{', '.join(info['columns'])}[/green]")
168
+ print(f"Unique: [magenta]{'Yes' if info['unique'] else 'No'}[/magenta]")
169
+ print(f"Partial: [blue]{'Yes' if info.get('partial') else 'No'}[/blue]")
170
+
171
+ if info.get('sql'):
172
+ print(f"\nSQL Definition:")
173
+ print(f"[dim]{info['sql']}[/dim]")
174
+
175
+ if info.get('columns_info'):
176
+ print(f"\nColumn Details:")
177
+ for col in info['columns_info']:
178
+ print(f" - Position {col['position']}: {col['column_name']}")
179
+
180
+ except ValueError as e:
181
+ print(f"[red]✗ Error:[/red] {e}")
182
+ raise typer.Exit(1)
183
+
184
+
185
+ if __name__ == "__main__":
186
+ app()
cinchdb/cli/main.py CHANGED
@@ -14,6 +14,7 @@ from cinchdb.cli.commands import (
14
14
  view,
15
15
  codegen,
16
16
  remote,
17
+ index,
17
18
  )
18
19
 
19
20
  app = typer.Typer(
@@ -42,6 +43,7 @@ app.add_typer(tenant.app, name="tenant", help="Tenant management commands")
42
43
  app.add_typer(table.app, name="table", help="Table management commands")
43
44
  app.add_typer(column.app, name="column", help="Column management commands")
44
45
  app.add_typer(view.app, name="view", help="View management commands")
46
+ app.add_typer(index.app, name="index", help="Index management commands")
45
47
  app.add_typer(codegen.app, name="codegen", help="Code generation commands")
46
48
  app.add_typer(remote.app, name="remote", help="Remote instance management")
47
49
 
cinchdb/core/database.py CHANGED
@@ -17,6 +17,7 @@ if TYPE_CHECKING:
17
17
  from cinchdb.managers.tenant import TenantManager
18
18
  from cinchdb.managers.codegen import CodegenManager
19
19
  from cinchdb.managers.merge_manager import MergeManager
20
+ from cinchdb.managers.index import IndexManager
20
21
 
21
22
 
22
23
  class CinchDB:
@@ -108,6 +109,7 @@ class CinchDB:
108
109
  self._tenant_manager: Optional["TenantManager"] = None
109
110
  self._codegen_manager: Optional["CodegenManager"] = None
110
111
  self._merge_manager: Optional["MergeManager"] = None
112
+ self._index_manager: Optional["IndexManager"] = None
111
113
 
112
114
  @property
113
115
  def session(self):
@@ -303,6 +305,21 @@ class CinchDB:
303
305
  self._merge_manager = MergeManager(self.project_dir, self.database)
304
306
  return self._merge_manager
305
307
 
308
+ @property
309
+ def indexes(self) -> "IndexManager":
310
+ """Access index operations (local only)."""
311
+ if not self.is_local:
312
+ raise RuntimeError(
313
+ "Direct manager access not available for remote connections"
314
+ )
315
+ if self._index_manager is None:
316
+ from cinchdb.managers.index import IndexManager
317
+
318
+ self._index_manager = IndexManager(
319
+ self.project_dir, self.database, self.branch
320
+ )
321
+ return self._index_manager
322
+
306
323
  # Convenience methods for common operations
307
324
 
308
325
  def query(
@@ -460,6 +477,56 @@ class CinchDB:
460
477
  # Remote delete
461
478
  self._make_request("DELETE", f"/tables/{table}/data/{id}")
462
479
 
480
+ def create_index(
481
+ self,
482
+ table: str,
483
+ columns: List[str],
484
+ name: Optional[str] = None,
485
+ unique: bool = False,
486
+ ) -> str:
487
+ """Create an index on a table at the branch level.
488
+
489
+ Indexes are created for the current branch and apply to all tenants.
490
+
491
+ Args:
492
+ table: Table name
493
+ columns: List of column names to index
494
+ name: Optional index name (auto-generated if not provided)
495
+ unique: Whether to create a unique index
496
+
497
+ Returns:
498
+ str: Name of the created index
499
+
500
+ Examples:
501
+ # Simple index on one column
502
+ db.create_index("users", ["email"])
503
+
504
+ # Unique compound index
505
+ db.create_index("orders", ["user_id", "order_number"], unique=True)
506
+
507
+ # Named index
508
+ db.create_index("products", ["category", "price"], name="idx_category_price")
509
+ """
510
+ # Convert parameters to Index model for validation
511
+ from cinchdb.models import Index
512
+ index = Index(columns=columns, name=name, unique=unique)
513
+
514
+ if self.is_local:
515
+ return self.indexes.create_index(table, index.columns, index.name, index.unique)
516
+ else:
517
+ # Remote index creation
518
+ result = self._make_request(
519
+ "POST",
520
+ "/indexes",
521
+ json={
522
+ "table": table,
523
+ "columns": index.columns,
524
+ "name": index.name,
525
+ "unique": index.unique,
526
+ },
527
+ )
528
+ return result.get("name")
529
+
463
530
  def list_changes(self) -> List["Change"]:
464
531
  """List all changes for the current branch.
465
532
 
@@ -0,0 +1,304 @@
1
+ """Index management for CinchDB."""
2
+
3
+ from pathlib import Path
4
+ from typing import List, Dict, Any, Optional
5
+ import sqlite3
6
+ import json
7
+ from datetime import datetime, timezone
8
+ import uuid
9
+
10
+ from cinchdb.core.connection import DatabaseConnection
11
+ from cinchdb.core.path_utils import get_tenant_db_path
12
+ from cinchdb.models.change import Change, ChangeType
13
+
14
+
15
+ class IndexManager:
16
+ """Manages database indexes for CinchDB tables at the branch level."""
17
+
18
+ def __init__(
19
+ self, project_dir: Path, database: str, branch: str
20
+ ):
21
+ """Initialize IndexManager.
22
+
23
+ Args:
24
+ project_dir: Path to the project directory
25
+ database: Database name
26
+ branch: Branch name
27
+ """
28
+ self.project_dir = Path(project_dir)
29
+ self.database = database
30
+ self.branch = branch
31
+
32
+ def create_index(
33
+ self,
34
+ table: str,
35
+ columns: List[str],
36
+ name: Optional[str] = None,
37
+ unique: bool = False,
38
+ if_not_exists: bool = True,
39
+ ) -> str:
40
+ """Create an index on a table.
41
+
42
+ Args:
43
+ table: Table name
44
+ columns: List of column names to index
45
+ name: Optional index name (auto-generated if not provided)
46
+ unique: Whether to create a unique index
47
+ if_not_exists: Whether to use IF NOT EXISTS clause
48
+
49
+ Returns:
50
+ str: Name of the created index
51
+
52
+ Raises:
53
+ ValueError: If table doesn't exist or columns are invalid
54
+ """
55
+ # Convert parameters to Index model for validation
56
+ from cinchdb.models import Index
57
+ index = Index(columns=columns, name=name, unique=unique)
58
+
59
+ if not index.columns:
60
+ raise ValueError("At least one column must be specified for the index")
61
+
62
+ # Generate index name if not provided
63
+ if not index.name:
64
+ column_str = "_".join(index.columns)
65
+ unique_prefix = "uniq_" if index.unique else "idx_"
66
+ index.name = f"{unique_prefix}{table}_{column_str}"
67
+
68
+ # Get connection to main tenant database (indexes are branch-level)
69
+ db_path = get_tenant_db_path(
70
+ self.project_dir, self.database, self.branch, "main"
71
+ )
72
+
73
+ with DatabaseConnection(db_path) as conn:
74
+ # Verify table exists
75
+ result = conn.execute(
76
+ "SELECT name FROM sqlite_master WHERE type='table' AND name=?",
77
+ [table]
78
+ )
79
+ if not result.fetchone():
80
+ raise ValueError(f"Table '{table}' does not exist")
81
+
82
+ # Verify columns exist
83
+ result = conn.execute(f"PRAGMA table_info({table})")
84
+ existing_columns = {row[1] for row in result.fetchall()}
85
+
86
+ invalid_columns = set(index.columns) - existing_columns
87
+ if invalid_columns:
88
+ raise ValueError(
89
+ f"Columns {invalid_columns} do not exist in table '{table}'"
90
+ )
91
+
92
+ # Build and execute CREATE INDEX statement
93
+ unique_clause = "UNIQUE " if index.unique else ""
94
+ if_not_exists_clause = "IF NOT EXISTS " if if_not_exists else ""
95
+ column_list = ", ".join(index.columns)
96
+
97
+ sql = f"CREATE {unique_clause}INDEX {if_not_exists_clause}{index.name} ON {table} ({column_list})"
98
+
99
+ try:
100
+ result = conn.execute(sql)
101
+ conn.commit()
102
+ except sqlite3.Error as e:
103
+ if "already exists" in str(e):
104
+ if not if_not_exists:
105
+ raise ValueError(f"Index '{index.name}' already exists")
106
+ else:
107
+ raise
108
+
109
+ # Track the change
110
+ self._track_change(
111
+ ChangeType.CREATE_INDEX,
112
+ index.name,
113
+ {"table": table, "columns": index.columns, "unique": index.unique}
114
+ )
115
+
116
+ return index.name
117
+
118
+ def drop_index(self, name: str, if_exists: bool = True) -> None:
119
+ """Drop an index.
120
+
121
+ Args:
122
+ name: Index name
123
+ if_exists: Whether to use IF EXISTS clause
124
+
125
+ Raises:
126
+ ValueError: If index doesn't exist and if_exists is False
127
+ """
128
+ # Get connection to main tenant database (indexes are branch-level)
129
+ db_path = get_tenant_db_path(
130
+ self.project_dir, self.database, self.branch, "main"
131
+ )
132
+
133
+ with DatabaseConnection(db_path) as conn:
134
+
135
+ # Check if index exists
136
+ result = conn.execute(
137
+ "SELECT name FROM sqlite_master WHERE type='index' AND name=?",
138
+ [name]
139
+ )
140
+ exists = result.fetchone() is not None
141
+
142
+ if not exists and not if_exists:
143
+ raise ValueError(f"Index '{name}' does not exist")
144
+
145
+ if exists:
146
+ if_exists_clause = "IF EXISTS " if if_exists else ""
147
+ sql = f"DROP INDEX {if_exists_clause}{name}"
148
+
149
+ result = conn.execute(sql)
150
+ conn.commit()
151
+
152
+ # Track the change
153
+ self._track_change(ChangeType.DROP_INDEX, name, {})
154
+
155
+ def list_indexes(self, table: Optional[str] = None) -> List[Dict[str, Any]]:
156
+ """List indexes for a table or all tables.
157
+
158
+ Args:
159
+ table: Optional table name to filter indexes
160
+
161
+ Returns:
162
+ List of index information dictionaries
163
+ """
164
+ # Get connection to main tenant database (indexes are branch-level)
165
+ db_path = get_tenant_db_path(
166
+ self.project_dir, self.database, self.branch, "main"
167
+ )
168
+
169
+ indexes = []
170
+
171
+ with DatabaseConnection(db_path) as conn:
172
+
173
+ # Get all indexes (excluding SQLite internal indexes)
174
+ if table:
175
+ result = conn.execute(
176
+ """
177
+ SELECT name, tbl_name, sql
178
+ FROM sqlite_master
179
+ WHERE type='index'
180
+ AND tbl_name=?
181
+ AND sql IS NOT NULL
182
+ """,
183
+ [table]
184
+ )
185
+ else:
186
+ result = conn.execute(
187
+ """
188
+ SELECT name, tbl_name, sql
189
+ FROM sqlite_master
190
+ WHERE type='index'
191
+ AND sql IS NOT NULL
192
+ """
193
+ )
194
+
195
+ for row in result.fetchall():
196
+ index_name, table_name, sql = row
197
+
198
+ # Parse unique from SQL
199
+ is_unique = "CREATE UNIQUE INDEX" in sql.upper()
200
+
201
+ # Get indexed columns
202
+ pragma_result = conn.execute(f"PRAGMA index_info({index_name})")
203
+ columns = [info[2] for info in pragma_result.fetchall()]
204
+
205
+ indexes.append({
206
+ "name": index_name,
207
+ "table": table_name,
208
+ "columns": columns,
209
+ "unique": is_unique,
210
+ "sql": sql
211
+ })
212
+
213
+ return indexes
214
+
215
+ def get_index_info(self, name: str) -> Dict[str, Any]:
216
+ """Get detailed information about a specific index.
217
+
218
+ Args:
219
+ name: Index name
220
+
221
+ Returns:
222
+ Dictionary with index information
223
+
224
+ Raises:
225
+ ValueError: If index doesn't exist
226
+ """
227
+ # Get connection to main tenant database (indexes are branch-level)
228
+ db_path = get_tenant_db_path(
229
+ self.project_dir, self.database, self.branch, "main"
230
+ )
231
+
232
+ with DatabaseConnection(db_path) as conn:
233
+
234
+ # Get index info
235
+ result = conn.execute(
236
+ """
237
+ SELECT name, tbl_name, sql
238
+ FROM sqlite_master
239
+ WHERE type='index'
240
+ AND name=?
241
+ """,
242
+ [name]
243
+ )
244
+
245
+ row = result.fetchone()
246
+ if not row:
247
+ raise ValueError(f"Index '{name}' does not exist")
248
+
249
+ index_name, table_name, sql = row
250
+
251
+ # Parse unique from SQL
252
+ is_unique = "CREATE UNIQUE INDEX" in (sql or "").upper()
253
+
254
+ # Get indexed columns with more details
255
+ pragma_result = conn.execute(f"PRAGMA index_info({index_name})")
256
+ columns_info = []
257
+ for info in pragma_result.fetchall():
258
+ columns_info.append({
259
+ "position": info[0],
260
+ "column_id": info[1],
261
+ "column_name": info[2]
262
+ })
263
+
264
+ # Get index statistics
265
+ xinfo_result = conn.execute(f"PRAGMA index_xinfo({index_name})")
266
+ extended_info = xinfo_result.fetchall()
267
+
268
+ return {
269
+ "name": index_name,
270
+ "table": table_name,
271
+ "columns": [col["column_name"] for col in columns_info],
272
+ "columns_info": columns_info,
273
+ "unique": is_unique,
274
+ "sql": sql,
275
+ "partial": sql and "WHERE" in sql.upper() if sql else False
276
+ }
277
+
278
+ def _track_change(
279
+ self, change_type: ChangeType, entity_name: str, metadata: Dict[str, Any]
280
+ ) -> None:
281
+ """Track a change for this branch.
282
+
283
+ Args:
284
+ change_type: Type of change
285
+ entity_name: Name of the entity being changed
286
+ metadata: Additional metadata about the change
287
+ """
288
+ # Import here to avoid circular dependency
289
+ from cinchdb.managers.change_tracker import ChangeTracker
290
+
291
+ tracker = ChangeTracker(self.project_dir, self.database, self.branch)
292
+
293
+ change = Change(
294
+ id=str(uuid.uuid4()),
295
+ type=change_type,
296
+ entity_type="index",
297
+ entity_name=entity_name,
298
+ branch=self.branch,
299
+ metadata=metadata,
300
+ applied=True,
301
+ created_at=datetime.now(timezone.utc),
302
+ )
303
+
304
+ tracker.add_change(change)
cinchdb/managers/table.py CHANGED
@@ -1,9 +1,12 @@
1
1
  """Table management for CinchDB."""
2
2
 
3
3
  from pathlib import Path
4
- from typing import List
4
+ from typing import List, Optional, TYPE_CHECKING
5
5
 
6
6
  from cinchdb.models import Table, Column, Change, ChangeType
7
+
8
+ if TYPE_CHECKING:
9
+ from cinchdb.models import Index
7
10
  from cinchdb.core.connection import DatabaseConnection
8
11
  from cinchdb.core.path_utils import get_tenant_db_path
9
12
  from cinchdb.core.maintenance import check_maintenance_mode
@@ -61,12 +64,13 @@ class TableManager:
61
64
 
62
65
  return tables
63
66
 
64
- def create_table(self, table_name: str, columns: List[Column]) -> Table:
65
- """Create a new table with optional foreign key constraints.
67
+ def create_table(self, table_name: str, columns: List[Column], indexes: Optional[List["Index"]] = None) -> Table:
68
+ """Create a new table with optional foreign key constraints and indexes.
66
69
 
67
70
  Args:
68
71
  table_name: Name of the table
69
72
  columns: List of Column objects defining the schema
73
+ indexes: Optional list of Index objects to create on the table
70
74
 
71
75
  Returns:
72
76
  Created Table object
@@ -168,6 +172,20 @@ class TableManager:
168
172
  applier = ChangeApplier(self.project_root, self.database, self.branch)
169
173
  applier.apply_change(change.id)
170
174
 
175
+ # Create indexes if specified
176
+ if indexes:
177
+ from cinchdb.managers.index import IndexManager
178
+ index_manager = IndexManager(self.project_root, self.database, self.branch)
179
+
180
+ for index in indexes:
181
+ index_manager.create_index(
182
+ table=table_name,
183
+ columns=index.columns,
184
+ name=index.name,
185
+ unique=index.unique,
186
+ if_not_exists=True
187
+ )
188
+
171
189
  # Return the created table
172
190
  return Table(
173
191
  name=table_name,
@@ -104,6 +104,10 @@ class TenantManager:
104
104
  conn.execute(f"DELETE FROM {table}")
105
105
 
106
106
  conn.commit()
107
+
108
+ # Open a new connection to vacuum the database
109
+ with DatabaseConnection(new_db_path) as conn:
110
+ conn.execute("VACUUM")
107
111
 
108
112
  return Tenant(
109
113
  name=tenant_name,
@@ -5,7 +5,7 @@ from .project import Project
5
5
  from .database import Database
6
6
  from .branch import Branch
7
7
  from .tenant import Tenant
8
- from .table import Table, Column, ColumnType, ForeignKeyRef, ForeignKeyAction
8
+ from .table import Table, Column, ColumnType, ForeignKeyRef, ForeignKeyAction, Index
9
9
  from .view import View
10
10
  from .change import Change, ChangeType
11
11
 
@@ -21,6 +21,7 @@ __all__ = [
21
21
  "ColumnType",
22
22
  "ForeignKeyRef",
23
23
  "ForeignKeyAction",
24
+ "Index",
24
25
  "View",
25
26
  "Change",
26
27
  "ChangeType",
cinchdb/models/table.py CHANGED
@@ -27,6 +27,16 @@ class ForeignKeyRef(BaseModel):
27
27
  )
28
28
 
29
29
 
30
+ class Index(BaseModel):
31
+ """Index specification for table columns."""
32
+
33
+ model_config = ConfigDict(extra="forbid")
34
+
35
+ columns: List[str] = Field(description="Column names to index")
36
+ name: Optional[str] = Field(default=None, description="Index name")
37
+ unique: bool = Field(default=False, description="Create unique index")
38
+
39
+
30
40
  class Column(BaseModel):
31
41
  """Represents a column in a table."""
32
42
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cinchdb
3
- Version: 0.1.6
3
+ Version: 0.1.8
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
@@ -2,13 +2,14 @@ cinchdb/__init__.py,sha256=NZdSzfhRguSBTjJ2dcESOQYy53OZEuBndlB7U08GMY0,179
2
2
  cinchdb/__main__.py,sha256=OpkDqn9zkTZhhYgvv_grswWLAHKbmxs4M-8C6Z5HfWY,85
3
3
  cinchdb/config.py,sha256=gocjMnYKLWhgvnteo6zprgwtK6Oevoxq547J_v-C9Ns,5265
4
4
  cinchdb/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- cinchdb/cli/main.py,sha256=Icr_uhe_zXPAuhM9NB7evR5b1ZP7f_N40HQcC1JFhQ0,4706
5
+ cinchdb/cli/main.py,sha256=tGX8Z78Oy4rukXth56Xp_pm52rijIDujyMdhSEhFWPw,4790
6
6
  cinchdb/cli/utils.py,sha256=NREFxN9k53FnPbDoPt4SXmdZzlzw9zUMv5ICQwTT8gk,5679
7
- cinchdb/cli/commands/__init__.py,sha256=gQ6tnU0Rvm0-ESWFUBU-KDl5dpNOpUTG509hXOQQjwY,27
7
+ cinchdb/cli/commands/__init__.py,sha256=IRUIPHgdpF4hBDbDy0SgaWn39o5GNUjH54_C42sjlQg,273
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=ISHRmcoLf1fAbPqC2MaAYH7Fc6xZWtzCMSRh7_9o-lY,11757
11
11
  cinchdb/cli/commands/database.py,sha256=-UCOnn3VatdNog-fX5pguJ2GKdSSXQN99-LSVwkvinY,6857
12
+ cinchdb/cli/commands/index.py,sha256=P0sM9lu1rVliacYl49LPqtxNYdrwzKzpVKW10jC-5i4,6096
12
13
  cinchdb/cli/commands/query.py,sha256=XW_YL6M5IYHHHMpVB5p-M01kawFxwDOK5B5hGIy_BA8,5044
13
14
  cinchdb/cli/commands/remote.py,sha256=i07hfiAxgrROB9lVJVaKK_nWxT1SGiSbtFb4jvEwxEo,4445
14
15
  cinchdb/cli/commands/table.py,sha256=NxfOTCd9beaujffiAPiW0Vko0--HS1JVeCwBMp_khx4,10518
@@ -18,7 +19,7 @@ cinchdb/cli/handlers/__init__.py,sha256=f2f-Cc96rSBLbVsiIbf-b4pZCKZoHfmhNEvnZ0Ou
18
19
  cinchdb/cli/handlers/codegen_handler.py,sha256=i5we_AbiUW3zfO6pIKWxvtO8OvOqz3H__4xPmTLEuQM,6524
19
20
  cinchdb/core/__init__.py,sha256=iNlT0iO9cM0HLoYwzBavUBoXRh1Tcnz1l_vfbwVxK_Q,246
20
21
  cinchdb/core/connection.py,sha256=SlKyEfIpeaDws8M6SfEbvCEVnt26zBY1RYwHtTXj0kY,5110
21
- cinchdb/core/database.py,sha256=IyMo1wfO3mTnnth0m7eP7Dib6_cEC7bTBo1JhOt6Vlo,19792
22
+ cinchdb/core/database.py,sha256=yeBxzxgKz20Dtoilzj609GHrD3UL2bWG4ArWrRM2VnA,22090
22
23
  cinchdb/core/initializer.py,sha256=CjnJSMuR1NrHobyFfwL44tUeH8VE62q02bijEtVH3p4,6922
23
24
  cinchdb/core/maintenance.py,sha256=PAgrSL7Cj9p3rKHV0h_L7gupN6nLD0-5eQpJZNiqyEs,2097
24
25
  cinchdb/core/path_utils.py,sha256=J2UEu1X_NFOqDamcsrPrC7ZitGTg9Y-HFjmx4sHf5j8,3806
@@ -30,25 +31,26 @@ cinchdb/managers/change_tracker.py,sha256=U93BPnuGv8xSaO5qr_y5Q8ppKrVXygozdp5zUv
30
31
  cinchdb/managers/codegen.py,sha256=1CfIwjgHnNDdjrq4SzQ9VE7DFgnWfk7RtpupBFUTqxk,21804
31
32
  cinchdb/managers/column.py,sha256=YhYq-hnH0o2BqZkyihnsY5KIWEztzs-_iLJNZMdVUkk,20807
32
33
  cinchdb/managers/data.py,sha256=zS1HkMGf436m6f8VdFAqQbQFgo4sL5yKJRcRf4A6lIc,16253
34
+ cinchdb/managers/index.py,sha256=n9bCXggZP6muJQZXCpTT46JvuvcbbnYgeV3j6iXtTVM,10371
33
35
  cinchdb/managers/merge_manager.py,sha256=R8S2hLkLJg4hLDpeJTzjVkduZgqPOjXtYgOSJhTXXrE,15690
34
36
  cinchdb/managers/query.py,sha256=pBlbqoovnFsZ36pB7nv8NtzcTFwtT26hp8IlwjIx29Q,7301
35
- cinchdb/managers/table.py,sha256=KWSAfZCJafKZPx-dRG7KwQUGVqVQ56ARMVBllb3VBig,13114
36
- cinchdb/managers/tenant.py,sha256=QONC5R8tW5CJoGUxECJmQyiSP12mz3rZBmRqxCsKjmM,8726
37
+ cinchdb/managers/table.py,sha256=GltELZ465M8JYwZB5xoMDOvyhRYm-HflPJsQQTStD2c,13837
38
+ cinchdb/managers/tenant.py,sha256=Wx32u71ejZEKStaPRTPw_TqCoE8XzAqFfCRNvmBfNlE,8879
37
39
  cinchdb/managers/view.py,sha256=v9gYtRufZyxywPKLGvIjvlUXcxYh9CLRArefu9QX6zk,7809
38
- cinchdb/models/__init__.py,sha256=382OuS0BaKPA71GjqNW5lfVhtUYqmcMlLRin7HPi6XI,602
40
+ cinchdb/models/__init__.py,sha256=cZ-ailJ6qu44Iap5Rq555iB-_w9ufXVDBH3rDH-ojk0,622
39
41
  cinchdb/models/base.py,sha256=7j4rlFTP5K9ZuF8vxwC7lMFEaL7O90NJ47Ig5i7ubcw,1320
40
42
  cinchdb/models/branch.py,sha256=gRgLpRFkMC3fxf9ZigVOkS6wdkBERWqlLk0_gOYjqNk,1180
41
43
  cinchdb/models/change.py,sha256=YpBWdI6yMT3uucd8duET9s75xr5JUWJqurkkyTlXPlk,1449
42
44
  cinchdb/models/database.py,sha256=QrWd_SkE1G8TMWflO4sXRUbSdbqcrfGOt2e-PS7OW7A,971
43
45
  cinchdb/models/project.py,sha256=6GMXUZUsEIebqQJgRXIthWzpWKuNNmJ3drgI1vFDrMo,644
44
- cinchdb/models/table.py,sha256=k2_nnyW1E6-UM2Zw49K5njP_fxySW3HVWymcR_a0e_0,2759
46
+ cinchdb/models/table.py,sha256=hamYjYUunNnVBMcE5NNyubS5Y8_wTJ8_aMnA5r8mpkE,3097
45
47
  cinchdb/models/tenant.py,sha256=UKYTKM4mQH3IqEjI_tOU5CszwBWH4cXa3lI0mpMFF_4,967
46
48
  cinchdb/models/view.py,sha256=q6j-jYzFJuhRJO87rKt6Uv8hOizHQx8xwoPKoH6XnNY,530
47
49
  cinchdb/utils/__init__.py,sha256=yQQhEjndDiB2SUJybUmp9dvEOQKiR-GySe-WiCius5E,490
48
50
  cinchdb/utils/name_validator.py,sha256=dyGX5bjlTFRA9EGrWRQKp6kR__HSV04hLV5VueJs4IQ,4027
49
51
  cinchdb/utils/sql_validator.py,sha256=aWOGlPX0gBkuR6R1EBP2stbP4PHZuI6FUBi2Ljx7JUI,5815
50
- cinchdb-0.1.6.dist-info/METADATA,sha256=1vNN2cSZJi_B_ho9yHtnLRjYt3x537gd905QAOtJyK0,6334
51
- cinchdb-0.1.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
52
- cinchdb-0.1.6.dist-info/entry_points.txt,sha256=VBOIzvnGbkKudMCCmNORS3885QSyjZUVKJQ-Syqa62w,47
53
- cinchdb-0.1.6.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
54
- cinchdb-0.1.6.dist-info/RECORD,,
52
+ cinchdb-0.1.8.dist-info/METADATA,sha256=Lx66m_6wkzVWt_N2rtcO5xVBVL5XHNxlfaOI-5-DN30,6334
53
+ cinchdb-0.1.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
54
+ cinchdb-0.1.8.dist-info/entry_points.txt,sha256=VBOIzvnGbkKudMCCmNORS3885QSyjZUVKJQ-Syqa62w,47
55
+ cinchdb-0.1.8.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
56
+ cinchdb-0.1.8.dist-info/RECORD,,