cinchdb 0.1.15__py3-none-any.whl → 0.1.18__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.
@@ -10,7 +10,8 @@ from cinchdb.models import Change, ChangeType, Tenant
10
10
  from cinchdb.managers.change_tracker import ChangeTracker
11
11
  from cinchdb.managers.tenant import TenantManager
12
12
  from cinchdb.core.connection import DatabaseConnection
13
- from cinchdb.core.path_utils import get_tenant_db_path, get_branch_path
13
+ from cinchdb.core.path_utils import get_tenant_db_path as get_tenant_db_path, get_branch_path
14
+ from cinchdb.infrastructure.metadata_connection_pool import get_metadata_db
14
15
 
15
16
  logger = logging.getLogger(__name__)
16
17
 
@@ -388,21 +389,17 @@ class ChangeApplier:
388
389
 
389
390
  def _enter_maintenance_mode(self) -> None:
390
391
  """Enter maintenance mode to block writes during schema changes."""
391
- # Create a maintenance mode file that all connections can check
392
- branch_path = get_branch_path(self.project_root, self.database, self.branch)
393
- maintenance_file = branch_path / ".maintenance_mode"
394
-
395
- with open(maintenance_file, "w") as f:
396
- import json
397
-
398
- json.dump(
399
- {
400
- "active": True,
401
- "reason": "Schema update in progress",
402
- "started_at": datetime.now().isoformat(),
403
- },
404
- f,
392
+ try:
393
+ metadata_db = get_metadata_db(self.project_root)
394
+ metadata_db.set_branch_maintenance(
395
+ self.database,
396
+ self.branch,
397
+ True,
398
+ "Schema update in progress"
405
399
  )
400
+ except Exception as e:
401
+ logger.error(f"Failed to enter maintenance mode: {e}")
402
+ # Continue anyway - we'll try to proceed with the schema update
406
403
 
407
404
  # Give time for any in-flight writes to complete
408
405
  # Can be disabled for tests via environment variable
@@ -414,11 +411,11 @@ class ChangeApplier:
414
411
 
415
412
  def _exit_maintenance_mode(self) -> None:
416
413
  """Exit maintenance mode to allow writes again."""
417
- branch_path = get_branch_path(self.project_root, self.database, self.branch)
418
- maintenance_file = branch_path / ".maintenance_mode"
419
-
420
- if maintenance_file.exists():
421
- maintenance_file.unlink()
414
+ try:
415
+ metadata_db = get_metadata_db(self.project_root)
416
+ metadata_db.set_branch_maintenance(self.database, self.branch, False)
417
+ except Exception as e:
418
+ logger.error(f"Failed to exit maintenance mode: {e}")
422
419
 
423
420
  def is_in_maintenance_mode(self) -> bool:
424
421
  """Check if branch is in maintenance mode.
@@ -426,6 +423,8 @@ class ChangeApplier:
426
423
  Returns:
427
424
  True if in maintenance mode, False otherwise
428
425
  """
429
- branch_path = get_branch_path(self.project_root, self.database, self.branch)
430
- maintenance_file = branch_path / ".maintenance_mode"
431
- return maintenance_file.exists()
426
+ try:
427
+ metadata_db = get_metadata_db(self.project_root)
428
+ return metadata_db.is_branch_in_maintenance(self.database, self.branch)
429
+ except Exception:
430
+ return False
@@ -379,21 +379,58 @@ class CodegenManager:
379
379
  results: Dict[str, Any],
380
380
  ) -> Dict[str, Any]:
381
381
  """Generate TypeScript interface models."""
382
- # TODO: Implement TypeScript generation
383
- # For now, return placeholder
384
- results["files_generated"].append("typescript_generation_todo.md")
385
-
386
- placeholder_path = output_dir / "typescript_generation_todo.md"
387
- with open(placeholder_path, "w") as f:
388
- content = "# TypeScript Generation\n\nTypeScript model generation will be implemented in a future update.\n"
389
- if include_tables:
390
- content += "\nTables will be generated as TypeScript interfaces.\n"
391
- if include_views:
392
- content += (
393
- "\nViews will be generated as read-only TypeScript interfaces.\n"
394
- )
395
- f.write(content)
396
-
382
+ # Generate interfaces for tables
383
+ if include_tables:
384
+ tables = self.table_manager.list_tables()
385
+ for table in tables:
386
+ interface_content = self._generate_typescript_table_interface(table)
387
+ file_name = f"{self._to_pascal_case(table.name)}.ts"
388
+ file_path = output_dir / file_name
389
+
390
+ with open(file_path, "w") as f:
391
+ f.write(interface_content)
392
+
393
+ results["tables_processed"].append(table.name)
394
+ results["files_generated"].append(file_name)
395
+
396
+ # Generate interfaces for views
397
+ if include_views:
398
+ views = self.view_manager.list_views()
399
+ for view in views:
400
+ interface_content = self._generate_typescript_view_interface(view)
401
+ file_name = f"{self._to_pascal_case(view.name)}View.ts"
402
+ file_path = output_dir / file_name
403
+
404
+ with open(file_path, "w") as f:
405
+ f.write(interface_content)
406
+
407
+ results["views_processed"].append(view.name)
408
+ results["files_generated"].append(file_name)
409
+
410
+ # Generate main index file
411
+ index_content = self._generate_typescript_index(
412
+ results["tables_processed"],
413
+ results["views_processed"]
414
+ )
415
+ index_path = output_dir / "index.ts"
416
+ with open(index_path, "w") as f:
417
+ f.write(index_content)
418
+ results["files_generated"].append("index.ts")
419
+
420
+ # Generate API client
421
+ client_content = self._generate_typescript_client()
422
+ client_path = output_dir / "client.ts"
423
+ with open(client_path, "w") as f:
424
+ f.write(client_content)
425
+ results["files_generated"].append("client.ts")
426
+
427
+ # Generate types file
428
+ types_content = self._generate_typescript_types()
429
+ types_path = output_dir / "types.ts"
430
+ with open(types_path, "w") as f:
431
+ f.write(types_content)
432
+ results["files_generated"].append("types.ts")
433
+
397
434
  return results
398
435
 
399
436
  def _get_view_columns(self, view_name: str) -> Dict[str, str]:
@@ -454,6 +491,326 @@ class CodegenManager:
454
491
  # Split on underscores and capitalize each part
455
492
  parts = name.replace("-", "_").split("_")
456
493
  return "".join(word.capitalize() for word in parts if word)
494
+
495
+ def _sqlite_to_typescript_type(self, sqlite_type: str, column_name: str = "") -> str:
496
+ """Convert SQLite type to TypeScript type string."""
497
+ sqlite_type = sqlite_type.upper()
498
+
499
+ # Special case for timestamp fields
500
+ if column_name in ["created_at", "updated_at"]:
501
+ return "string" # ISO date strings
502
+
503
+ if "INT" in sqlite_type:
504
+ return "number"
505
+ elif sqlite_type in ["REAL", "FLOAT", "DOUBLE", "NUMERIC"]:
506
+ return "number"
507
+ elif sqlite_type == "BLOB":
508
+ return "Uint8Array"
509
+ elif sqlite_type == "BOOLEAN":
510
+ return "boolean"
511
+ else:
512
+ # TEXT, VARCHAR, etc.
513
+ return "string"
514
+
515
+ def _generate_typescript_table_interface(self, table: Table) -> str:
516
+ """Generate TypeScript interface for a table."""
517
+ interface_name = self._to_pascal_case(table.name)
518
+
519
+ content = [
520
+ f"/**",
521
+ f" * Generated interface for {table.name} table",
522
+ f" */",
523
+ f"export interface {interface_name} {{",
524
+ ]
525
+
526
+ # Generate properties for each column
527
+ for column in table.columns:
528
+ ts_type = self._sqlite_to_typescript_type(column.type, column.name)
529
+ optional = "?" if column.nullable and column.name not in ["id", "created_at"] else ""
530
+ content.append(f" {column.name}{optional}: {ts_type};")
531
+
532
+ content.append("}")
533
+ content.append("")
534
+
535
+ # Generate input interface (for create/update operations)
536
+ content.extend([
537
+ f"/**",
538
+ f" * Input interface for creating/updating {table.name} records",
539
+ f" */",
540
+ f"export interface {interface_name}Input {{",
541
+ ])
542
+
543
+ for column in table.columns:
544
+ # Skip auto-generated fields in input
545
+ if column.name in ["id", "created_at", "updated_at"]:
546
+ continue
547
+ ts_type = self._sqlite_to_typescript_type(column.type, column.name)
548
+ optional = "?" if column.nullable else ""
549
+ content.append(f" {column.name}{optional}: {ts_type};")
550
+
551
+ content.append("}")
552
+ content.append("")
553
+
554
+ return "\n".join(content)
555
+
556
+ def _generate_typescript_view_interface(self, view: View) -> str:
557
+ """Generate TypeScript interface for a view."""
558
+ interface_name = f"{self._to_pascal_case(view.name)}View"
559
+
560
+ # Get view columns from PRAGMA
561
+ columns = self._get_view_columns(view.name)
562
+
563
+ content = [
564
+ f"/**",
565
+ f" * Generated interface for {view.name} view (read-only)",
566
+ f" */",
567
+ f"export interface {interface_name} {{",
568
+ ]
569
+
570
+ # Generate properties for each column
571
+ for column_name, column_type in columns.items():
572
+ ts_type = self._sqlite_to_typescript_type(column_type, column_name)
573
+ content.append(f" {column_name}: {ts_type};")
574
+
575
+ content.append("}")
576
+ content.append("")
577
+
578
+ return "\n".join(content)
579
+
580
+ def _generate_typescript_index(self, tables: List[str], views: List[str]) -> str:
581
+ """Generate TypeScript index file exporting all interfaces."""
582
+ content = [
583
+ "/**",
584
+ " * Generated TypeScript models for CinchDB",
585
+ " */",
586
+ "",
587
+ ]
588
+
589
+ # Export tables
590
+ for table_name in tables:
591
+ interface_name = self._to_pascal_case(table_name)
592
+ content.append(f"export {{ {interface_name}, {interface_name}Input }} from './{interface_name}';")
593
+
594
+ # Export views
595
+ for view_name in views:
596
+ interface_name = f"{self._to_pascal_case(view_name)}View"
597
+ content.append(f"export {{ {interface_name} }} from './{interface_name}';")
598
+
599
+ # Export client and types
600
+ content.extend([
601
+ "",
602
+ "export { CinchDBClient } from './client';",
603
+ "export * from './types';",
604
+ "",
605
+ ])
606
+
607
+ return "\n".join(content)
608
+
609
+ def _generate_typescript_client(self) -> str:
610
+ """Generate TypeScript API client class."""
611
+ content = [
612
+ "/**",
613
+ " * CinchDB TypeScript API Client",
614
+ " */",
615
+ "",
616
+ "import { QueryResult, CreateResult, UpdateResult, DeleteResult } from './types';",
617
+ "",
618
+ "export class CinchDBClient {",
619
+ " private baseUrl: string;",
620
+ " private headers: HeadersInit;",
621
+ "",
622
+ " constructor(baseUrl: string, apiKey: string) {",
623
+ " this.baseUrl = baseUrl;",
624
+ " this.headers = {",
625
+ " 'Content-Type': 'application/json',",
626
+ " 'X-API-Key': apiKey,",
627
+ " };",
628
+ " }",
629
+ "",
630
+ " /**",
631
+ " * Execute a query against the database",
632
+ " */",
633
+ " async query<T = any>(sql: string, params?: any[]): Promise<QueryResult<T>> {",
634
+ " const response = await fetch(`${this.baseUrl}/api/v1/query`, {",
635
+ " method: 'POST',",
636
+ " headers: this.headers,",
637
+ " body: JSON.stringify({ sql, params }),",
638
+ " });",
639
+ "",
640
+ " if (!response.ok) {",
641
+ " throw new Error(`Query failed: ${response.statusText}`);",
642
+ " }",
643
+ "",
644
+ " return response.json();",
645
+ " }",
646
+ "",
647
+ " /**",
648
+ " * Select records from a table",
649
+ " */",
650
+ " async select<T = any>(",
651
+ " table: string,",
652
+ " filters?: Record<string, any>,",
653
+ " limit?: number,",
654
+ " offset?: number",
655
+ " ): Promise<T[]> {",
656
+ " const params = new URLSearchParams();",
657
+ " if (filters) {",
658
+ " Object.entries(filters).forEach(([key, value]) => {",
659
+ " params.append(key, String(value));",
660
+ " });",
661
+ " }",
662
+ " if (limit !== undefined) params.append('limit', String(limit));",
663
+ " if (offset !== undefined) params.append('offset', String(offset));",
664
+ "",
665
+ " const response = await fetch(",
666
+ " `${this.baseUrl}/api/v1/tables/${table}/records?${params}`,",
667
+ " {",
668
+ " method: 'GET',",
669
+ " headers: this.headers,",
670
+ " }",
671
+ " );",
672
+ "",
673
+ " if (!response.ok) {",
674
+ " throw new Error(`Select failed: ${response.statusText}`);",
675
+ " }",
676
+ "",
677
+ " const result = await response.json();",
678
+ " return result.records;",
679
+ " }",
680
+ "",
681
+ " /**",
682
+ " * Create a new record",
683
+ " */",
684
+ " async create<T = any>(table: string, data: Partial<T>): Promise<CreateResult<T>> {",
685
+ " const response = await fetch(`${this.baseUrl}/api/v1/tables/${table}/records`, {",
686
+ " method: 'POST',",
687
+ " headers: this.headers,",
688
+ " body: JSON.stringify(data),",
689
+ " });",
690
+ "",
691
+ " if (!response.ok) {",
692
+ " throw new Error(`Create failed: ${response.statusText}`);",
693
+ " }",
694
+ "",
695
+ " return response.json();",
696
+ " }",
697
+ "",
698
+ " /**",
699
+ " * Update a record by ID",
700
+ " */",
701
+ " async update<T = any>(",
702
+ " table: string,",
703
+ " id: string,",
704
+ " data: Partial<T>",
705
+ " ): Promise<UpdateResult<T>> {",
706
+ " const response = await fetch(",
707
+ " `${this.baseUrl}/api/v1/tables/${table}/records/${id}`,",
708
+ " {",
709
+ " method: 'PUT',",
710
+ " headers: this.headers,",
711
+ " body: JSON.stringify(data),",
712
+ " }",
713
+ " );",
714
+ "",
715
+ " if (!response.ok) {",
716
+ " throw new Error(`Update failed: ${response.statusText}`);",
717
+ " }",
718
+ "",
719
+ " return response.json();",
720
+ " }",
721
+ "",
722
+ " /**",
723
+ " * Delete a record by ID",
724
+ " */",
725
+ " async delete(table: string, id: string): Promise<DeleteResult> {",
726
+ " const response = await fetch(",
727
+ " `${this.baseUrl}/api/v1/tables/${table}/records/${id}`,",
728
+ " {",
729
+ " method: 'DELETE',",
730
+ " headers: this.headers,",
731
+ " }",
732
+ " );",
733
+ "",
734
+ " if (!response.ok) {",
735
+ " throw new Error(`Delete failed: ${response.statusText}`);",
736
+ " }",
737
+ "",
738
+ " return response.json();",
739
+ " }",
740
+ "",
741
+ " /**",
742
+ " * Bulk create multiple records",
743
+ " */",
744
+ " async bulkCreate<T = any>(",
745
+ " table: string,",
746
+ " records: Partial<T>[]",
747
+ " ): Promise<CreateResult<T>[]> {",
748
+ " const response = await fetch(",
749
+ " `${this.baseUrl}/api/v1/tables/${table}/records/bulk`,",
750
+ " {",
751
+ " method: 'POST',",
752
+ " headers: this.headers,",
753
+ " body: JSON.stringify({ records }),",
754
+ " }",
755
+ " );",
756
+ "",
757
+ " if (!response.ok) {",
758
+ " throw new Error(`Bulk create failed: ${response.statusText}`);",
759
+ " }",
760
+ "",
761
+ " return response.json();",
762
+ " }",
763
+ "}",
764
+ "",
765
+ ]
766
+
767
+ return "\n".join(content)
768
+
769
+ def _generate_typescript_types(self) -> str:
770
+ """Generate TypeScript type definitions."""
771
+ content = [
772
+ "/**",
773
+ " * Common TypeScript type definitions for CinchDB",
774
+ " */",
775
+ "",
776
+ "export interface QueryResult<T = any> {",
777
+ " success: boolean;",
778
+ " data: T[];",
779
+ " rowCount: number;",
780
+ " error?: string;",
781
+ "}",
782
+ "",
783
+ "export interface CreateResult<T = any> {",
784
+ " success: boolean;",
785
+ " data: T;",
786
+ " error?: string;",
787
+ "}",
788
+ "",
789
+ "export interface UpdateResult<T = any> {",
790
+ " success: boolean;",
791
+ " data: T;",
792
+ " rowsAffected: number;",
793
+ " error?: string;",
794
+ "}",
795
+ "",
796
+ "export interface DeleteResult {",
797
+ " success: boolean;",
798
+ " rowsAffected: number;",
799
+ " error?: string;",
800
+ "}",
801
+ "",
802
+ "export interface PaginationParams {",
803
+ " limit?: number;",
804
+ " offset?: number;",
805
+ "}",
806
+ "",
807
+ "export interface FilterParams {",
808
+ " [key: string]: any;",
809
+ "}",
810
+ "",
811
+ ]
812
+
813
+ return "\n".join(content)
457
814
 
458
815
  def _generate_cinch_models_class(self) -> str:
459
816
  """Generate the CinchModels container class."""
@@ -6,7 +6,7 @@ from typing import List, Any, Optional
6
6
  from cinchdb.models import Column, Change, ChangeType
7
7
  from cinchdb.core.connection import DatabaseConnection
8
8
  from cinchdb.core.path_utils import get_tenant_db_path
9
- from cinchdb.core.maintenance import check_maintenance_mode
9
+ from cinchdb.core.maintenance_utils import check_maintenance_mode
10
10
  from cinchdb.managers.change_tracker import ChangeTracker
11
11
  from cinchdb.managers.table import TableManager
12
12
 
@@ -18,7 +18,8 @@ class ColumnManager:
18
18
  PROTECTED_COLUMNS = {"id", "created_at", "updated_at"}
19
19
 
20
20
  def __init__(
21
- self, project_root: Path, database: str, branch: str, tenant: str = "main"
21
+ self, project_root: Path, database: str, branch: str, tenant: str = "main",
22
+ encryption_manager=None
22
23
  ):
23
24
  """Initialize column manager.
24
25
 
@@ -27,14 +28,16 @@ class ColumnManager:
27
28
  database: Database name
28
29
  branch: Branch name
29
30
  tenant: Tenant name (default: main)
31
+ encryption_manager: EncryptionManager instance for encrypted connections
30
32
  """
31
33
  self.project_root = Path(project_root)
32
34
  self.database = database
33
35
  self.branch = branch
34
36
  self.tenant = tenant
37
+ self.encryption_manager = encryption_manager
35
38
  self.db_path = get_tenant_db_path(project_root, database, branch, tenant)
36
39
  self.change_tracker = ChangeTracker(project_root, database, branch)
37
- self.table_manager = TableManager(project_root, database, branch, tenant)
40
+ self.table_manager = TableManager(project_root, database, branch, tenant, encryption_manager)
38
41
 
39
42
  def list_columns(self, table_name: str) -> List[Column]:
40
43
  """List all columns in a table.
@@ -415,7 +418,7 @@ class ColumnManager:
415
418
 
416
419
  create_sql = f"CREATE TABLE {temp_table} ({', '.join(col_defs)})"
417
420
 
418
- with DatabaseConnection(self.db_path) as conn:
421
+ with DatabaseConnection(self.db_path, tenant_id=self.tenant, encryption_manager=self.encryption_manager) as conn:
419
422
  # Create new table
420
423
  conn.execute(create_sql)
421
424
 
@@ -488,7 +491,7 @@ class ColumnManager:
488
491
 
489
492
  # If making NOT NULL, check for NULL values
490
493
  if not nullable:
491
- with DatabaseConnection(self.db_path) as conn:
494
+ with DatabaseConnection(self.db_path, tenant_id=self.tenant, encryption_manager=self.encryption_manager) as conn:
492
495
  cursor = conn.execute(
493
496
  f"SELECT COUNT(*) FROM {table_name} WHERE {column_name} IS NULL"
494
497
  )
cinchdb/managers/data.py CHANGED
@@ -9,7 +9,7 @@ from pydantic import BaseModel
9
9
 
10
10
  from cinchdb.core.connection import DatabaseConnection
11
11
  from cinchdb.core.path_utils import get_tenant_db_path
12
- from cinchdb.core.maintenance import check_maintenance_mode
12
+ from cinchdb.core.maintenance_utils import check_maintenance_mode
13
13
  from cinchdb.managers.table import TableManager
14
14
  from cinchdb.managers.query import QueryManager
15
15
 
@@ -20,7 +20,8 @@ class DataManager:
20
20
  """Manages data operations within a database tenant."""
21
21
 
22
22
  def __init__(
23
- self, project_root: Path, database: str, branch: str, tenant: str = "main"
23
+ self, project_root: Path, database: str, branch: str, tenant: str = "main",
24
+ encryption_manager=None
24
25
  ):
25
26
  """Initialize data manager.
26
27
 
@@ -29,14 +30,16 @@ class DataManager:
29
30
  database: Database name
30
31
  branch: Branch name
31
32
  tenant: Tenant name (default: main)
33
+ encryption_manager: EncryptionManager instance for encrypted connections
32
34
  """
33
35
  self.project_root = Path(project_root)
34
36
  self.database = database
35
37
  self.branch = branch
36
38
  self.tenant = tenant
39
+ self.encryption_manager = encryption_manager
37
40
  self.db_path = get_tenant_db_path(project_root, database, branch, tenant)
38
- self.table_manager = TableManager(project_root, database, branch, tenant)
39
- self.query_manager = QueryManager(project_root, database, branch, tenant)
41
+ self.table_manager = TableManager(project_root, database, branch, tenant, encryption_manager)
42
+ self.query_manager = QueryManager(project_root, database, branch, tenant, encryption_manager)
40
43
 
41
44
  def select(
42
45
  self,
@@ -73,7 +76,7 @@ class DataManager:
73
76
  if offset:
74
77
  query += f" OFFSET {offset}"
75
78
 
76
- with DatabaseConnection(self.db_path) as conn:
79
+ with DatabaseConnection(self.db_path, tenant_id=self.tenant, encryption_manager=self.encryption_manager) as conn:
77
80
  cursor = conn.execute(query, params)
78
81
  rows = cursor.fetchall()
79
82
 
@@ -130,7 +133,7 @@ class DataManager:
130
133
  VALUES ({", ".join(placeholders)})
131
134
  """
132
135
 
133
- with DatabaseConnection(self.db_path) as conn:
136
+ with DatabaseConnection(self.db_path, tenant_id=self.tenant, encryption_manager=self.encryption_manager) as conn:
134
137
  try:
135
138
  conn.execute(query, record_data)
136
139
  conn.commit()
@@ -233,7 +236,7 @@ class DataManager:
233
236
 
234
237
  params = {**update_data, "id": data["id"]}
235
238
 
236
- with DatabaseConnection(self.db_path) as conn:
239
+ with DatabaseConnection(self.db_path, tenant_id=self.tenant, encryption_manager=self.encryption_manager) as conn:
237
240
  try:
238
241
  conn.execute(query, params)
239
242
  conn.commit()
@@ -273,7 +276,7 @@ class DataManager:
273
276
 
274
277
  query = f"DELETE FROM {table_name} WHERE {where_clause}"
275
278
 
276
- with DatabaseConnection(self.db_path) as conn:
279
+ with DatabaseConnection(self.db_path, tenant_id=self.tenant, encryption_manager=self.encryption_manager) as conn:
277
280
  try:
278
281
  cursor = conn.execute(query, params)
279
282
  deleted_count = cursor.rowcount
@@ -320,7 +323,7 @@ class DataManager:
320
323
  table_name = self._get_table_name(type(instances[0]))
321
324
  created_instances = []
322
325
 
323
- with DatabaseConnection(self.db_path) as conn:
326
+ with DatabaseConnection(self.db_path, tenant_id=self.tenant, encryption_manager=self.encryption_manager) as conn:
324
327
  try:
325
328
  for instance in instances:
326
329
  data = instance.model_dump()
@@ -370,7 +373,7 @@ class DataManager:
370
373
  if where_clause:
371
374
  query += f" WHERE {where_clause}"
372
375
 
373
- with DatabaseConnection(self.db_path) as conn:
376
+ with DatabaseConnection(self.db_path, tenant_id=self.tenant, encryption_manager=self.encryption_manager) as conn:
374
377
  cursor = conn.execute(query, params)
375
378
  result = cursor.fetchone()
376
379
  return result["count"] if result else 0
@@ -521,7 +524,7 @@ class DataManager:
521
524
 
522
525
  sql = f"DELETE FROM {table} WHERE {where_clause}"
523
526
 
524
- with DatabaseConnection(self.db_path) as conn:
527
+ with DatabaseConnection(self.db_path, tenant_id=self.tenant, encryption_manager=self.encryption_manager) as conn:
525
528
  cursor = conn.execute(sql, params)
526
529
  conn.commit()
527
530
  return cursor.rowcount
@@ -585,7 +588,7 @@ class DataManager:
585
588
 
586
589
  sql = f"UPDATE {table} SET {', '.join(set_clauses)} WHERE {where_clause}"
587
590
 
588
- with DatabaseConnection(self.db_path) as conn:
591
+ with DatabaseConnection(self.db_path, tenant_id=self.tenant, encryption_manager=self.encryption_manager) as conn:
589
592
  cursor = conn.execute(sql, all_params)
590
593
  conn.commit()
591
594
  return cursor.rowcount
@@ -616,7 +619,7 @@ class DataManager:
616
619
  sql = f"UPDATE {table} SET {set_clause} WHERE id = ?"
617
620
  params.append(record_id)
618
621
 
619
- with DatabaseConnection(self.db_path) as conn:
622
+ with DatabaseConnection(self.db_path, tenant_id=self.tenant, encryption_manager=self.encryption_manager) as conn:
620
623
  cursor = conn.execute(sql, params)
621
624
  conn.commit()
622
625
 
@@ -645,7 +648,7 @@ class DataManager:
645
648
  """
646
649
  sql = f"DELETE FROM {table} WHERE id = ?"
647
650
 
648
- with DatabaseConnection(self.db_path) as conn:
651
+ with DatabaseConnection(self.db_path, tenant_id=self.tenant, encryption_manager=self.encryption_manager) as conn:
649
652
  cursor = conn.execute(sql, [record_id])
650
653
  conn.commit()
651
654
  return cursor.rowcount > 0