# Data Model: Direct Database Access for Scripts **Date**: 2026-01-08 **Branch**: `002-direct-db-access` ## Entity Overview ``` ┌─────────────────┐ │ DatabaseInfo │ └─────────────────┘ │ ▼ ┌─────────────────┐ ┌─────────────────┐ │ SchemaInfo │───────│ TableInfo │ └─────────────────┘ └─────────────────┘ │ ▼ ┌─────────────────┐ │ ColumnInfo │ └─────────────────┘ ``` ## New Entities ### DatabaseInfo Information about the current database connection, returned by load operations and queryable separately. | Field | Type | Required | Description | |-------|------|----------|-------------| | db_path | string | Yes | Absolute path to SQLite database file; `:memory:` for in-memory | | is_persistent | boolean | Yes | True if file-based, false if in-memory | | source_file | string | No | Path to XER file that was loaded | | loaded_at | datetime | Yes | When data was loaded | | schema | SchemaInfo | Yes | Database schema information | **Validation Rules**: - db_path must be an absolute path (or `:memory:`) - loaded_at must be ISO 8601 format ### SchemaInfo Metadata describing the database structure. | Field | Type | Required | Description | |-------|------|----------|-------------| | tables | list[TableInfo] | Yes | All tables in the database | | version | string | Yes | Schema version (matches server version) | ### TableInfo Information about a single database table. | Field | Type | Required | Description | |-------|------|----------|-------------| | name | string | Yes | Table name | | columns | list[ColumnInfo] | Yes | Column definitions | | primary_key | list[string] | Yes | Column(s) forming primary key | | foreign_keys | list[ForeignKeyInfo] | No | Foreign key relationships | | row_count | integer | Yes | Number of rows in table | ### ColumnInfo Information about a single column. | Field | Type | Required | Description | |-------|------|----------|-------------| | name | string | Yes | Column name | | type | string | Yes | SQLite data type (TEXT, INTEGER, REAL, etc.) | | nullable | boolean | Yes | Whether NULL values are allowed | | default | string | No | Default value if any | ### ForeignKeyInfo Foreign key relationship information. | Field | Type | Required | Description | |-------|------|----------|-------------| | column | string | Yes | Column in this table | | references_table | string | Yes | Referenced table | | references_column | string | Yes | Referenced column | ## Extended load_xer Response The existing load_xer tool response is extended with database information: ```json { "success": true, "project": { ... }, "activity_count": 4440, "relationship_count": 8583, "database": { "db_path": "/path/to/schedule.sqlite", "is_persistent": true, "source_file": "/path/to/schedule.xer", "loaded_at": "2026-01-08T14:30:00", "schema": { "version": "0.2.0", "tables": [ { "name": "activities", "columns": [ {"name": "task_id", "type": "TEXT", "nullable": false}, {"name": "task_name", "type": "TEXT", "nullable": false}, ... ], "primary_key": ["task_id"], "foreign_keys": [ {"column": "proj_id", "references_table": "projects", "references_column": "proj_id"} ], "row_count": 4440 }, ... ] } } } ``` ## New Tool Input Schema ### load_xer (extended) New optional parameter: | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | db_path | string | No | Path for persistent database file. If omitted, uses in-memory database. If empty string, auto-generates path from XER filename. | ### get_database_info No input parameters required. Returns current database information. ## SQLite Schema (Unchanged) The underlying database schema remains unchanged from feature 001-schedule-tools: - `projects` - Project metadata - `activities` - Task/activity data - `relationships` - Predecessor/successor relationships - `wbs` - Work breakdown structure - `calendars` - Calendar definitions The difference is storage location (file vs memory), not structure. ## Query Examples for Scripts Once scripts have the database path, they can execute standard SQL: ```python import sqlite3 # Connect using path from load_xer response conn = sqlite3.connect("/path/to/schedule.sqlite") # Query activities cursor = conn.execute(""" SELECT task_code, task_name, target_start_date, target_end_date FROM activities WHERE task_type = 'TT_Mile' ORDER BY target_start_date """) for row in cursor: print(row) ``` ```sql -- Find critical path activities SELECT task_code, task_name, total_float_hr_cnt FROM activities WHERE driving_path_flag = 1 ORDER BY target_start_date; -- Join activities with WBS SELECT a.task_code, a.task_name, w.wbs_name FROM activities a JOIN wbs w ON a.wbs_id = w.wbs_id; ```