cinchdb 0.1.7__py3-none-any.whl → 0.1.9__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/core/database.py CHANGED
@@ -507,8 +507,12 @@ class CinchDB:
507
507
  # Named index
508
508
  db.create_index("products", ["category", "price"], name="idx_category_price")
509
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
+
510
514
  if self.is_local:
511
- return self.indexes.create_index(table, columns, name, unique)
515
+ return self.indexes.create_index(table, index.columns, index.name, index.unique)
512
516
  else:
513
517
  # Remote index creation
514
518
  result = self._make_request(
@@ -516,9 +520,9 @@ class CinchDB:
516
520
  "/indexes",
517
521
  json={
518
522
  "table": table,
519
- "columns": columns,
520
- "name": name,
521
- "unique": unique,
523
+ "columns": index.columns,
524
+ "name": index.name,
525
+ "unique": index.unique,
522
526
  },
523
527
  )
524
528
  return result.get("name")
cinchdb/managers/index.py CHANGED
@@ -52,14 +52,18 @@ class IndexManager:
52
52
  Raises:
53
53
  ValueError: If table doesn't exist or columns are invalid
54
54
  """
55
- if not columns:
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:
56
60
  raise ValueError("At least one column must be specified for the index")
57
61
 
58
62
  # Generate index name if not provided
59
- if not name:
60
- column_str = "_".join(columns)
61
- unique_prefix = "uniq_" if unique else "idx_"
62
- name = f"{unique_prefix}{table}_{column_str}"
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}"
63
67
 
64
68
  # Get connection to main tenant database (indexes are branch-level)
65
69
  db_path = get_tenant_db_path(
@@ -79,18 +83,18 @@ class IndexManager:
79
83
  result = conn.execute(f"PRAGMA table_info({table})")
80
84
  existing_columns = {row[1] for row in result.fetchall()}
81
85
 
82
- invalid_columns = set(columns) - existing_columns
86
+ invalid_columns = set(index.columns) - existing_columns
83
87
  if invalid_columns:
84
88
  raise ValueError(
85
89
  f"Columns {invalid_columns} do not exist in table '{table}'"
86
90
  )
87
91
 
88
92
  # Build and execute CREATE INDEX statement
89
- unique_clause = "UNIQUE " if unique else ""
93
+ unique_clause = "UNIQUE " if index.unique else ""
90
94
  if_not_exists_clause = "IF NOT EXISTS " if if_not_exists else ""
91
- column_list = ", ".join(columns)
95
+ column_list = ", ".join(index.columns)
92
96
 
93
- sql = f"CREATE {unique_clause}INDEX {if_not_exists_clause}{name} ON {table} ({column_list})"
97
+ sql = f"CREATE {unique_clause}INDEX {if_not_exists_clause}{index.name} ON {table} ({column_list})"
94
98
 
95
99
  try:
96
100
  result = conn.execute(sql)
@@ -98,18 +102,18 @@ class IndexManager:
98
102
  except sqlite3.Error as e:
99
103
  if "already exists" in str(e):
100
104
  if not if_not_exists:
101
- raise ValueError(f"Index '{name}' already exists")
105
+ raise ValueError(f"Index '{index.name}' already exists")
102
106
  else:
103
107
  raise
104
108
 
105
109
  # Track the change
106
110
  self._track_change(
107
111
  ChangeType.CREATE_INDEX,
108
- name,
109
- {"table": table, "columns": columns, "unique": unique}
112
+ index.name,
113
+ {"table": table, "columns": index.columns, "unique": index.unique}
110
114
  )
111
115
 
112
- return name
116
+ return index.name
113
117
 
114
118
  def drop_index(self, name: str, if_exists: bool = True) -> None:
115
119
  """Drop an index.
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,14 @@ class TenantManager:
104
104
  conn.execute(f"DELETE FROM {table}")
105
105
 
106
106
  conn.commit()
107
+
108
+ # Vacuum the database to reduce size
109
+ # Must use raw sqlite3 connection with autocommit mode for VACUUM
110
+ import sqlite3
111
+ vacuum_conn = sqlite3.connect(str(new_db_path))
112
+ vacuum_conn.isolation_level = None # Autocommit mode required for VACUUM
113
+ vacuum_conn.execute("VACUUM")
114
+ vacuum_conn.close()
107
115
 
108
116
  return Tenant(
109
117
  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.7
3
+ Version: 0.1.9
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
@@ -19,7 +19,7 @@ cinchdb/cli/handlers/__init__.py,sha256=f2f-Cc96rSBLbVsiIbf-b4pZCKZoHfmhNEvnZ0Ou
19
19
  cinchdb/cli/handlers/codegen_handler.py,sha256=i5we_AbiUW3zfO6pIKWxvtO8OvOqz3H__4xPmTLEuQM,6524
20
20
  cinchdb/core/__init__.py,sha256=iNlT0iO9cM0HLoYwzBavUBoXRh1Tcnz1l_vfbwVxK_Q,246
21
21
  cinchdb/core/connection.py,sha256=SlKyEfIpeaDws8M6SfEbvCEVnt26zBY1RYwHtTXj0kY,5110
22
- cinchdb/core/database.py,sha256=FRbo6YAGd39q-BTf27Jp8WWpD630R1JkpsRSDIha_Mw,21880
22
+ cinchdb/core/database.py,sha256=yeBxzxgKz20Dtoilzj609GHrD3UL2bWG4ArWrRM2VnA,22090
23
23
  cinchdb/core/initializer.py,sha256=CjnJSMuR1NrHobyFfwL44tUeH8VE62q02bijEtVH3p4,6922
24
24
  cinchdb/core/maintenance.py,sha256=PAgrSL7Cj9p3rKHV0h_L7gupN6nLD0-5eQpJZNiqyEs,2097
25
25
  cinchdb/core/path_utils.py,sha256=J2UEu1X_NFOqDamcsrPrC7ZitGTg9Y-HFjmx4sHf5j8,3806
@@ -31,26 +31,26 @@ cinchdb/managers/change_tracker.py,sha256=U93BPnuGv8xSaO5qr_y5Q8ppKrVXygozdp5zUv
31
31
  cinchdb/managers/codegen.py,sha256=1CfIwjgHnNDdjrq4SzQ9VE7DFgnWfk7RtpupBFUTqxk,21804
32
32
  cinchdb/managers/column.py,sha256=YhYq-hnH0o2BqZkyihnsY5KIWEztzs-_iLJNZMdVUkk,20807
33
33
  cinchdb/managers/data.py,sha256=zS1HkMGf436m6f8VdFAqQbQFgo4sL5yKJRcRf4A6lIc,16253
34
- cinchdb/managers/index.py,sha256=ertiAo1clumLq6K_atgR_g8JP4Qv5QX1MMuKjF6ANGc,10113
34
+ cinchdb/managers/index.py,sha256=n9bCXggZP6muJQZXCpTT46JvuvcbbnYgeV3j6iXtTVM,10371
35
35
  cinchdb/managers/merge_manager.py,sha256=R8S2hLkLJg4hLDpeJTzjVkduZgqPOjXtYgOSJhTXXrE,15690
36
36
  cinchdb/managers/query.py,sha256=pBlbqoovnFsZ36pB7nv8NtzcTFwtT26hp8IlwjIx29Q,7301
37
- cinchdb/managers/table.py,sha256=KWSAfZCJafKZPx-dRG7KwQUGVqVQ56ARMVBllb3VBig,13114
38
- cinchdb/managers/tenant.py,sha256=QONC5R8tW5CJoGUxECJmQyiSP12mz3rZBmRqxCsKjmM,8726
37
+ cinchdb/managers/table.py,sha256=GltELZ465M8JYwZB5xoMDOvyhRYm-HflPJsQQTStD2c,13837
38
+ cinchdb/managers/tenant.py,sha256=wBAGox6CYVPKp6y05iBRFUY2xF6QvOGzVa2WMsGPDsE,9081
39
39
  cinchdb/managers/view.py,sha256=v9gYtRufZyxywPKLGvIjvlUXcxYh9CLRArefu9QX6zk,7809
40
- cinchdb/models/__init__.py,sha256=382OuS0BaKPA71GjqNW5lfVhtUYqmcMlLRin7HPi6XI,602
40
+ cinchdb/models/__init__.py,sha256=cZ-ailJ6qu44Iap5Rq555iB-_w9ufXVDBH3rDH-ojk0,622
41
41
  cinchdb/models/base.py,sha256=7j4rlFTP5K9ZuF8vxwC7lMFEaL7O90NJ47Ig5i7ubcw,1320
42
42
  cinchdb/models/branch.py,sha256=gRgLpRFkMC3fxf9ZigVOkS6wdkBERWqlLk0_gOYjqNk,1180
43
43
  cinchdb/models/change.py,sha256=YpBWdI6yMT3uucd8duET9s75xr5JUWJqurkkyTlXPlk,1449
44
44
  cinchdb/models/database.py,sha256=QrWd_SkE1G8TMWflO4sXRUbSdbqcrfGOt2e-PS7OW7A,971
45
45
  cinchdb/models/project.py,sha256=6GMXUZUsEIebqQJgRXIthWzpWKuNNmJ3drgI1vFDrMo,644
46
- cinchdb/models/table.py,sha256=k2_nnyW1E6-UM2Zw49K5njP_fxySW3HVWymcR_a0e_0,2759
46
+ cinchdb/models/table.py,sha256=hamYjYUunNnVBMcE5NNyubS5Y8_wTJ8_aMnA5r8mpkE,3097
47
47
  cinchdb/models/tenant.py,sha256=UKYTKM4mQH3IqEjI_tOU5CszwBWH4cXa3lI0mpMFF_4,967
48
48
  cinchdb/models/view.py,sha256=q6j-jYzFJuhRJO87rKt6Uv8hOizHQx8xwoPKoH6XnNY,530
49
49
  cinchdb/utils/__init__.py,sha256=yQQhEjndDiB2SUJybUmp9dvEOQKiR-GySe-WiCius5E,490
50
50
  cinchdb/utils/name_validator.py,sha256=dyGX5bjlTFRA9EGrWRQKp6kR__HSV04hLV5VueJs4IQ,4027
51
51
  cinchdb/utils/sql_validator.py,sha256=aWOGlPX0gBkuR6R1EBP2stbP4PHZuI6FUBi2Ljx7JUI,5815
52
- cinchdb-0.1.7.dist-info/METADATA,sha256=YGREASqsSGqr36eTZqUalkNYuTeParXqPNQs2a5QLt0,6334
53
- cinchdb-0.1.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
54
- cinchdb-0.1.7.dist-info/entry_points.txt,sha256=VBOIzvnGbkKudMCCmNORS3885QSyjZUVKJQ-Syqa62w,47
55
- cinchdb-0.1.7.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
56
- cinchdb-0.1.7.dist-info/RECORD,,
52
+ cinchdb-0.1.9.dist-info/METADATA,sha256=GAGW_oGksTwm_MMCxupXuVTslERomeeGge0KDzr6AoE,6334
53
+ cinchdb-0.1.9.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
54
+ cinchdb-0.1.9.dist-info/entry_points.txt,sha256=VBOIzvnGbkKudMCCmNORS3885QSyjZUVKJQ-Syqa62w,47
55
+ cinchdb-0.1.9.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
56
+ cinchdb-0.1.9.dist-info/RECORD,,