cinchdb 0.1.13__py3-none-any.whl → 0.1.15__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
@@ -464,38 +464,189 @@ class CinchDB:
464
464
  )
465
465
  return result
466
466
 
467
- def update(self, table: str, id: str, data: Dict[str, Any]) -> Dict[str, Any]:
468
- """Update a record in a table.
467
+ def update(self, table: str, *updates: Dict[str, Any]) -> Dict[str, Any] | List[Dict[str, Any]]:
468
+ """Update one or more records in a table.
469
469
 
470
470
  Args:
471
471
  table: Table name
472
- id: Record ID
473
- data: Updated data as dictionary
472
+ *updates: One or more update dictionaries, each must contain 'id' field
474
473
 
475
474
  Returns:
476
- Updated record
475
+ Single record dict if one record updated, list of dicts if multiple
476
+
477
+ Examples:
478
+ # Single update
479
+ db.update("users", {"id": "123", "name": "John Updated", "status": "active"})
480
+
481
+ # Multiple updates using star expansion
482
+ db.update("users",
483
+ {"id": "123", "name": "John Updated", "status": "active"},
484
+ {"id": "456", "name": "Jane Updated", "email": "jane.new@example.com"},
485
+ {"id": "789", "status": "inactive"}
486
+ )
487
+
488
+ # Or with a list using star expansion
489
+ user_updates = [
490
+ {"id": "abc", "name": "Alice Updated"},
491
+ {"id": "def", "status": "premium"}
492
+ ]
493
+ db.update("users", *user_updates)
477
494
  """
495
+ if not updates:
496
+ raise ValueError("At least one update record must be provided")
497
+
498
+ # Validate that all updates have an 'id' field
499
+ for i, update_data in enumerate(updates):
500
+ if 'id' not in update_data:
501
+ raise ValueError(f"Update record {i} missing required 'id' field")
502
+
478
503
  if self.is_local:
479
- return self.data.update(table, id, data)
504
+ # Single record
505
+ if len(updates) == 1:
506
+ update_data = updates[0].copy()
507
+ record_id = update_data.pop('id')
508
+ return self.data.update_by_id(table, record_id, update_data)
509
+
510
+ # Multiple records - batch update
511
+ results = []
512
+ for update_data in updates:
513
+ update_copy = update_data.copy()
514
+ record_id = update_copy.pop('id')
515
+ result = self.data.update_by_id(table, record_id, update_copy)
516
+ results.append(result)
517
+ return results
480
518
  else:
481
- # Remote update - use new data CRUD endpoint
482
- result = self._make_request(
483
- "PUT", f"/tables/{table}/data/{id}", json={"data": data}
484
- )
485
- return result
519
+ # Remote update
520
+ if len(updates) == 1:
521
+ # Single record - use existing endpoint
522
+ update_data = updates[0].copy()
523
+ record_id = update_data.pop('id')
524
+ result = self._make_request(
525
+ "PUT", f"/tables/{table}/data/{record_id}", json={"data": update_data}
526
+ )
527
+ return result
528
+ else:
529
+ # Multiple records - use bulk endpoint
530
+ result = self._make_request(
531
+ "PUT", f"/tables/{table}/data/bulk", json={"updates": list(updates)}
532
+ )
533
+ return result
486
534
 
487
- def delete(self, table: str, id: str) -> None:
488
- """Delete a record from a table.
535
+ def delete(self, table: str, *ids: str) -> int:
536
+ """Delete one or more records from a table.
489
537
 
490
538
  Args:
491
539
  table: Table name
492
- id: Record ID
540
+ *ids: One or more record IDs
541
+
542
+ Returns:
543
+ Number of records deleted
544
+
545
+ Examples:
546
+ # Single delete
547
+ db.delete("users", "123")
548
+
549
+ # Multiple deletes
550
+ db.delete("users", "123", "456", "789")
551
+
552
+ # Or with a list using star expansion
553
+ user_ids = ["abc", "def", "ghi"]
554
+ db.delete("users", *user_ids)
493
555
  """
556
+ if not ids:
557
+ raise ValueError("At least one ID must be provided")
558
+
494
559
  if self.is_local:
495
- self.data.delete(table, id)
560
+ # Single record
561
+ if len(ids) == 1:
562
+ success = self.data.delete_by_id(table, ids[0])
563
+ return 1 if success else 0
564
+
565
+ # Multiple records - batch delete
566
+ deleted_count = 0
567
+ for record_id in ids:
568
+ success = self.data.delete_by_id(table, record_id)
569
+ if success:
570
+ deleted_count += 1
571
+ return deleted_count
496
572
  else:
497
573
  # Remote delete
498
- self._make_request("DELETE", f"/tables/{table}/data/{id}")
574
+ if len(ids) == 1:
575
+ # Single record - use existing endpoint
576
+ self._make_request("DELETE", f"/tables/{table}/data/{ids[0]}")
577
+ return 1
578
+ else:
579
+ # Multiple records - use bulk endpoint
580
+ result = self._make_request(
581
+ "DELETE", f"/tables/{table}/data/bulk", json={"ids": list(ids)}
582
+ )
583
+ return result.get("deleted_count", len(ids))
584
+
585
+ def delete_where(self, table: str, operator: str = "AND", **filters) -> int:
586
+ """Delete records from a table based on filter criteria.
587
+
588
+ Args:
589
+ table: Table name
590
+ operator: Logical operator to combine conditions - "AND" (default) or "OR"
591
+ **filters: Filter criteria (supports operators like __gt, __lt, __in, __like, __not)
592
+ Multiple conditions are combined with the specified operator
593
+
594
+ Returns:
595
+ Number of records deleted
596
+
597
+ Examples:
598
+ # Delete records where status = 'inactive' (single condition)
599
+ count = db.delete_where('users', status='inactive')
600
+
601
+ # Delete records where status = 'inactive' AND age > 65 (default AND)
602
+ count = db.delete_where('users', status='inactive', age__gt=65)
603
+
604
+ # Delete records where status = 'inactive' OR age > 65
605
+ count = db.delete_where('users', operator='OR', status='inactive', age__gt=65)
606
+
607
+ # Delete records where item_id in [1, 2, 3]
608
+ count = db.delete_where('items', item_id__in=[1, 2, 3])
609
+ """
610
+ if self.is_local:
611
+ return self.data.delete_where(table, operator=operator, **filters)
612
+ else:
613
+ raise NotImplementedError("Remote bulk delete not implemented")
614
+
615
+ def update_where(self, table: str, data: Dict[str, Any], operator: str = "AND", **filters) -> int:
616
+ """Update records in a table based on filter criteria.
617
+
618
+ Args:
619
+ table: Table name
620
+ data: Dictionary of column-value pairs to update
621
+ operator: Logical operator to combine conditions - "AND" (default) or "OR"
622
+ **filters: Filter criteria (supports operators like __gt, __lt, __in, __like, __not)
623
+ Multiple conditions are combined with the specified operator
624
+
625
+ Returns:
626
+ Number of records updated
627
+
628
+ Examples:
629
+ # Update status for all users with age > 65 (single condition)
630
+ count = db.update_where('users', {'status': 'senior'}, age__gt=65)
631
+
632
+ # Update status where age > 65 AND status = 'active' (default AND)
633
+ count = db.update_where('users', {'status': 'senior'}, age__gt=65, status='active')
634
+
635
+ # Update status where age > 65 OR status = 'pending'
636
+ count = db.update_where('users', {'status': 'senior'}, operator='OR', age__gt=65, status='pending')
637
+
638
+ # Update multiple fields where item_id in specific list
639
+ count = db.update_where(
640
+ 'items',
641
+ {'status': 'inactive', 'updated_at': datetime.now()},
642
+ item_id__in=[1, 2, 3]
643
+ )
644
+ """
645
+ if self.is_local:
646
+ return self.data.update_where(table, data, operator=operator, **filters)
647
+ else:
648
+ raise NotImplementedError("Remote bulk update not implemented")
649
+
499
650
 
500
651
  def create_index(
501
652
  self,
@@ -581,31 +732,6 @@ class CinchDB:
581
732
  changes.append(Change(**data))
582
733
  return changes
583
734
 
584
- def optimize_tenant(self, tenant_name: str = None, force: bool = False) -> bool:
585
- """Optimize a tenant's storage with VACUUM and page size adjustment.
586
-
587
- Args:
588
- tenant_name: Name of the tenant to optimize (default: current tenant)
589
- force: If True, always perform optimization
590
-
591
- Returns:
592
- True if optimization was performed, False otherwise
593
-
594
- Examples:
595
- # Optimize current tenant
596
- db.optimize_tenant()
597
-
598
- # Optimize specific tenant
599
- db.optimize_tenant("store_west")
600
-
601
- # Force optimization even if not needed
602
- db.optimize_tenant(force=True)
603
- """
604
- if self.is_local:
605
- tenant_to_optimize = tenant_name or self.tenant
606
- return self.tenants.optimize_tenant_storage(tenant_to_optimize, force=force)
607
- else:
608
- raise NotImplementedError("Remote tenant optimization not implemented")
609
735
 
610
736
  def get_tenant_size(self, tenant_name: str = None) -> dict:
611
737
  """Get storage size information for a tenant.
@@ -639,6 +765,46 @@ class CinchDB:
639
765
  else:
640
766
  raise NotImplementedError("Remote tenant size query not implemented")
641
767
 
768
+ def vacuum_tenant(self, tenant_name: str = None) -> dict:
769
+ """Run VACUUM operation on a specific tenant to optimize storage and performance.
770
+
771
+ VACUUM reclaims space from deleted records, defragments the database file,
772
+ and can improve query performance by rebuilding internal structures.
773
+
774
+ Args:
775
+ tenant_name: Name of tenant to vacuum (default: current tenant)
776
+
777
+ Returns:
778
+ Dictionary with vacuum results:
779
+ - success: Whether vacuum completed successfully
780
+ - tenant: Name of the tenant
781
+ - size_before: Size in bytes before vacuum
782
+ - size_after: Size in bytes after vacuum
783
+ - space_reclaimed: Bytes reclaimed by vacuum
784
+ - space_reclaimed_mb: MB reclaimed (rounded to 2 decimals)
785
+ - duration_seconds: Time taken for vacuum operation
786
+ - error: Error message if vacuum failed
787
+
788
+ Raises:
789
+ ValueError: If tenant doesn't exist or is not materialized
790
+ NotImplementedError: If called on remote database
791
+
792
+ Examples:
793
+ # Vacuum current tenant
794
+ result = db.vacuum_tenant()
795
+ if result['success']:
796
+ print(f"Reclaimed {result['space_reclaimed_mb']:.2f} MB")
797
+
798
+ # Vacuum specific tenant
799
+ result = db.vacuum_tenant("store_east")
800
+ print(f"Vacuum took {result['duration_seconds']} seconds")
801
+ """
802
+ if self.is_local:
803
+ tenant_to_vacuum = tenant_name or self.tenant
804
+ return self.tenants.vacuum_tenant(tenant_to_vacuum)
805
+ else:
806
+ raise NotImplementedError("Remote tenant vacuum not implemented")
807
+
642
808
  def get_storage_info(self) -> dict:
643
809
  """Get storage size information for all tenants in current branch.
644
810
 
@@ -667,35 +833,6 @@ class CinchDB:
667
833
  else:
668
834
  raise NotImplementedError("Remote storage info not implemented")
669
835
 
670
- def optimize_all_tenants(self, force: bool = False) -> dict:
671
- """Optimize storage for all tenants in current branch.
672
-
673
- This is designed to be called periodically to:
674
- - Reclaim unused space with VACUUM
675
- - Adjust page sizes as databases grow
676
- - Keep small databases compact
677
-
678
- Args:
679
- force: If True, optimize all tenants regardless of size
680
-
681
- Returns:
682
- Dictionary with optimization results:
683
- - optimized: List of tenant names that were optimized
684
- - skipped: List of tenant names that were skipped
685
- - errors: List of tuples (tenant_name, error_message)
686
-
687
- Examples:
688
- # Run periodic optimization
689
- results = db.optimize_all_tenants()
690
- print(f"Optimized {len(results['optimized'])} tenants")
691
-
692
- # Force optimization of all tenants
693
- results = db.optimize_all_tenants(force=True)
694
- """
695
- if self.is_local:
696
- return self.tenants.optimize_all_tenants(force=force)
697
- else:
698
- raise NotImplementedError("Remote tenant optimization not implemented")
699
836
 
700
837
  def close(self):
701
838
  """Close any open connections."""
@@ -1,7 +1,7 @@
1
1
  """Path utilities for CinchDB."""
2
2
 
3
3
  from pathlib import Path
4
- from typing import List, Optional
4
+ from typing import List
5
5
 
6
6
 
7
7
  def get_project_root(start_path: Path) -> Path:
@@ -3,7 +3,6 @@
3
3
  import threading
4
4
  from pathlib import Path
5
5
  from typing import Optional, Dict
6
- from weakref import WeakValueDictionary
7
6
 
8
7
  from cinchdb.infrastructure.metadata_db import MetadataDB
9
8
 
@@ -4,7 +4,6 @@ import sqlite3
4
4
  import uuid
5
5
  from pathlib import Path
6
6
  from typing import Optional, List, Dict, Any
7
- from datetime import datetime
8
7
  import json
9
8
 
10
9
 
@@ -117,6 +116,21 @@ class MetadataDB:
117
116
  CREATE INDEX IF NOT EXISTS idx_tenants_shard
118
117
  ON tenants(shard)
119
118
  """)
119
+
120
+ # Add cdc_enabled field to branches table for plugin use
121
+ # Note: This field is managed by plugins (like bdhcnic), not core CinchDB
122
+ try:
123
+ self.conn.execute("""
124
+ ALTER TABLE branches ADD COLUMN cdc_enabled BOOLEAN DEFAULT FALSE
125
+ """)
126
+ except sqlite3.OperationalError:
127
+ # Column already exists
128
+ pass
129
+
130
+ self.conn.execute("""
131
+ CREATE INDEX IF NOT EXISTS idx_branches_cdc_enabled
132
+ ON branches(cdc_enabled)
133
+ """)
120
134
 
121
135
  # Database operations
122
136
  def create_database(self, database_id: str, name: str,
@@ -4,7 +4,7 @@ import json
4
4
  import shutil
5
5
  import uuid
6
6
  from pathlib import Path
7
- from typing import List, Dict, Any, Optional
7
+ from typing import List, Dict, Any
8
8
  from datetime import datetime, timezone
9
9
 
10
10
  from cinchdb.models import Branch
cinchdb/managers/data.py CHANGED
@@ -283,8 +283,8 @@ class DataManager:
283
283
  conn.rollback()
284
284
  raise
285
285
 
286
- def delete_by_id(self, model_class: Type[T], record_id: str) -> bool:
287
- """Delete a single record by ID.
286
+ def delete_model_by_id(self, model_class: Type[T], record_id: str) -> bool:
287
+ """Delete a single record by ID using model class.
288
288
 
289
289
  Args:
290
290
  model_class: Pydantic model class representing the table
@@ -420,12 +420,13 @@ class DataManager:
420
420
  return snake_case
421
421
 
422
422
  def _build_where_clause(
423
- self, filters: Dict[str, Any]
423
+ self, filters: Dict[str, Any], operator: str = "AND"
424
424
  ) -> tuple[str, Dict[str, Any]]:
425
425
  """Build WHERE clause and parameters from filters.
426
426
 
427
427
  Args:
428
428
  filters: Dictionary of column filters
429
+ operator: Logical operator to combine conditions - "AND" (default) or "OR"
429
430
 
430
431
  Returns:
431
432
  Tuple of (where_clause, parameters)
@@ -433,31 +434,34 @@ class DataManager:
433
434
  if not filters:
434
435
  return "", {}
435
436
 
437
+ if operator not in ("AND", "OR"):
438
+ raise ValueError(f"Operator must be 'AND' or 'OR', got: {operator}")
439
+
436
440
  conditions = []
437
441
  params = {}
438
442
 
439
443
  for key, value in filters.items():
440
444
  # Handle special operators (column__operator format)
441
445
  if "__" in key:
442
- column, operator = key.split("__", 1)
443
- param_key = f"{column}_{operator}"
446
+ column, op = key.split("__", 1)
447
+ param_key = f"{column}_{op}"
444
448
 
445
- if operator == "gte":
449
+ if op == "gte":
446
450
  conditions.append(f"{column} >= :{param_key}")
447
451
  params[param_key] = value
448
- elif operator == "lte":
452
+ elif op == "lte":
449
453
  conditions.append(f"{column} <= :{param_key}")
450
454
  params[param_key] = value
451
- elif operator == "gt":
455
+ elif op == "gt":
452
456
  conditions.append(f"{column} > :{param_key}")
453
457
  params[param_key] = value
454
- elif operator == "lt":
458
+ elif op == "lt":
455
459
  conditions.append(f"{column} < :{param_key}")
456
460
  params[param_key] = value
457
- elif operator == "like":
461
+ elif op == "like":
458
462
  conditions.append(f"{column} LIKE :{param_key}")
459
463
  params[param_key] = value
460
- elif operator == "in":
464
+ elif op == "in":
461
465
  if not isinstance(value, (list, tuple)):
462
466
  raise ValueError(
463
467
  f"'in' operator requires list or tuple, got {type(value)}"
@@ -467,10 +471,182 @@ class DataManager:
467
471
  for i, v in enumerate(value):
468
472
  params[f"{param_key}_{i}"] = v
469
473
  else:
470
- raise ValueError(f"Unsupported operator: {operator}")
474
+ raise ValueError(f"Unsupported operator: {op}")
471
475
  else:
472
476
  # Exact match
473
477
  conditions.append(f"{key} = :{key}")
474
478
  params[key] = value
475
479
 
476
- return " AND ".join(conditions), params
480
+ return f" {operator} ".join(conditions), params
481
+
482
+ def delete_where(self, table: str, operator: str = "AND", **filters) -> int:
483
+ """Delete records from a table based on filter criteria.
484
+
485
+ Args:
486
+ table: Table name
487
+ operator: Logical operator to combine conditions - "AND" (default) or "OR"
488
+ **filters: Filter criteria (supports operators like __gt, __lt, __in, __like, __not)
489
+ Multiple conditions are combined with the specified operator
490
+
491
+ Returns:
492
+ Number of records deleted
493
+
494
+ Examples:
495
+ # Delete records where status = 'inactive'
496
+ count = dm.delete_where('users', status='inactive')
497
+
498
+ # Delete records where status = 'inactive' AND age > 65 (default AND)
499
+ count = dm.delete_where('users', status='inactive', age__gt=65)
500
+
501
+ # Delete records where status = 'inactive' OR age > 65
502
+ count = dm.delete_where('users', operator='OR', status='inactive', age__gt=65)
503
+
504
+ # Delete records where age > 65
505
+ count = dm.delete_where('users', age__gt=65)
506
+
507
+ # Delete records where id in [1, 2, 3]
508
+ count = dm.delete_where('users', id__in=[1, 2, 3])
509
+
510
+ # Delete records where name like 'test%'
511
+ count = dm.delete_where('users', name__like='test%')
512
+ """
513
+ # Check maintenance mode
514
+ check_maintenance_mode(self.project_root, self.database, self.branch)
515
+
516
+ if not filters:
517
+ raise ValueError("delete_where requires at least one filter condition")
518
+
519
+ # Build WHERE clause
520
+ where_clause, params = self._build_where_clause(filters, operator)
521
+
522
+ sql = f"DELETE FROM {table} WHERE {where_clause}"
523
+
524
+ with DatabaseConnection(self.db_path) as conn:
525
+ cursor = conn.execute(sql, params)
526
+ conn.commit()
527
+ return cursor.rowcount
528
+
529
+ def update_where(self, table: str, data: Dict[str, Any], operator: str = "AND", **filters) -> int:
530
+ """Update records in a table based on filter criteria.
531
+
532
+ Args:
533
+ table: Table name
534
+ data: Dictionary of column-value pairs to update
535
+ operator: Logical operator to combine conditions - "AND" (default) or "OR"
536
+ **filters: Filter criteria (supports operators like __gt, __lt, __in, __like, __not)
537
+ Multiple conditions are combined with the specified operator
538
+
539
+ Returns:
540
+ Number of records updated
541
+
542
+ Examples:
543
+ # Update status for all users with age > 65
544
+ count = dm.update_where('users', {'status': 'senior'}, age__gt=65)
545
+
546
+ # Update status where age > 65 AND status = 'active' (default AND)
547
+ count = dm.update_where('users', {'status': 'senior'}, age__gt=65, status='active')
548
+
549
+ # Update status where age > 65 OR status = 'pending'
550
+ count = dm.update_where('users', {'status': 'senior'}, operator='OR', age__gt=65, status='pending')
551
+
552
+ # Update multiple fields where id in specific list
553
+ count = dm.update_where(
554
+ 'users',
555
+ {'status': 'inactive', 'updated_at': datetime.now()},
556
+ id__in=[1, 2, 3]
557
+ )
558
+
559
+ # Update where name like pattern
560
+ count = dm.update_where('users', {'category': 'test'}, name__like='test%')
561
+ """
562
+ # Check maintenance mode
563
+ check_maintenance_mode(self.project_root, self.database, self.branch)
564
+
565
+ if not filters:
566
+ raise ValueError("update_where requires at least one filter condition")
567
+
568
+ if not data:
569
+ raise ValueError("update_where requires data to update")
570
+
571
+ # Build WHERE clause
572
+ where_clause, where_params = self._build_where_clause(filters, operator)
573
+
574
+ # Build SET clause
575
+ set_clauses = []
576
+ update_params = {}
577
+
578
+ for key, value in data.items():
579
+ param_key = f"update_{key}"
580
+ set_clauses.append(f"{key} = :{param_key}")
581
+ update_params[param_key] = value
582
+
583
+ # Combine parameters (update params first to avoid conflicts)
584
+ all_params = {**update_params, **where_params}
585
+
586
+ sql = f"UPDATE {table} SET {', '.join(set_clauses)} WHERE {where_clause}"
587
+
588
+ with DatabaseConnection(self.db_path) as conn:
589
+ cursor = conn.execute(sql, all_params)
590
+ conn.commit()
591
+ return cursor.rowcount
592
+
593
+ def update_by_id(self, table: str, record_id: str, data: Dict[str, Any]) -> Dict[str, Any]:
594
+ """Update a single record by ID.
595
+
596
+ Args:
597
+ table: Table name
598
+ record_id: Record ID to update
599
+ data: Dictionary of column-value pairs to update
600
+
601
+ Returns:
602
+ Updated record
603
+ """
604
+ # Build SET clause
605
+ set_parts = []
606
+ params = []
607
+
608
+ for key, value in data.items():
609
+ set_parts.append(f"{key} = ?")
610
+ params.append(value)
611
+
612
+ if not set_parts:
613
+ raise ValueError("No data provided for update")
614
+
615
+ set_clause = ", ".join(set_parts)
616
+ sql = f"UPDATE {table} SET {set_clause} WHERE id = ?"
617
+ params.append(record_id)
618
+
619
+ with DatabaseConnection(self.db_path) as conn:
620
+ cursor = conn.execute(sql, params)
621
+ conn.commit()
622
+
623
+ if cursor.rowcount == 0:
624
+ raise ValueError(f"No record found with id: {record_id}")
625
+
626
+ # Return the updated record
627
+ result = conn.execute(f"SELECT * FROM {table} WHERE id = ?", [record_id])
628
+ row = result.fetchone()
629
+ if row:
630
+ return dict(row)
631
+ else:
632
+ raise ValueError(f"Record not found after update: {record_id}")
633
+
634
+ def delete_by_id(self, table: str, record_id: str) -> bool:
635
+ """Delete a single record by ID using table name.
636
+
637
+ This method is used by the high-level database API.
638
+
639
+ Args:
640
+ table: Table name
641
+ record_id: Record ID to delete
642
+
643
+ Returns:
644
+ True if record was deleted, False if not found
645
+ """
646
+ sql = f"DELETE FROM {table} WHERE id = ?"
647
+
648
+ with DatabaseConnection(self.db_path) as conn:
649
+ cursor = conn.execute(sql, [record_id])
650
+ conn.commit()
651
+ return cursor.rowcount > 0
652
+
cinchdb/managers/index.py CHANGED
@@ -3,7 +3,6 @@
3
3
  from pathlib import Path
4
4
  from typing import List, Dict, Any, Optional
5
5
  import sqlite3
6
- import json
7
6
  from datetime import datetime, timezone
8
7
  import uuid
9
8
 
@@ -263,7 +262,7 @@ class IndexManager:
263
262
 
264
263
  # Get index statistics
265
264
  xinfo_result = conn.execute(f"PRAGMA index_xinfo({index_name})")
266
- extended_info = xinfo_result.fetchall()
265
+ xinfo_result.fetchall()
267
266
 
268
267
  return {
269
268
  "name": index_name,
cinchdb/managers/query.py CHANGED
@@ -6,7 +6,6 @@ from typing import List, Dict, Any, Optional, Type, TypeVar, Union
6
6
  from pydantic import BaseModel, ValidationError
7
7
 
8
8
  from cinchdb.core.connection import DatabaseConnection
9
- from cinchdb.core.path_utils import get_tenant_db_path
10
9
  from cinchdb.utils import validate_query_safe
11
10
  from cinchdb.managers.tenant import TenantManager
12
11