# Database Resource Handler

The `DatabaseResourceHandler` manages database resources within the
CleverAgents resource model. It supports four database types (`postgres`,
`mysql`, `sqlite`, `duckdb`) and implements the full `ResourceHandler`
protocol including CRUD operations and checkpoint/rollback methods.

Introduced in v3.7.0 (issues #1241, #836) as part of the ResourceHandler
Protocol Completion epic (#825).

**Module**: `cleveragents.resource.handlers.database`

---

## Supported Resource Types

| Type | Kind | Sandbox Strategy | Connection |
|------|------|-----------------|------------|
| `sqlite` | physical | `transaction_rollback` | File path |
| `postgres` | physical | `transaction_rollback` | Host/port/dbname or connection string |
| `mysql` | physical | `transaction_rollback` | Host/port/dbname or connection string |
| `duckdb` | physical | `transaction_rollback` | File path or in-memory |

All types are user-addable via `agents resource add`.

### CLI Arguments

**SQLite / DuckDB:**

| Argument | Type | Required | Description |
|----------|------|----------|-------------|
| `--path` | string | Yes | Path to the database file |

**PostgreSQL / MySQL:**

| Argument | Type | Required | Description |
|----------|------|----------|-------------|
| `--connection-string` | string | No | Full connection string (overrides individual args) |
| `--host` | string | No | Database server hostname (default: `localhost`) |
| `--port` | integer | No | Database server port |
| `--dbname` | string | No | Database name |
| `--user` | string | No | Database user |
| `--password` | string | No | Database password (masked in logs) |

---

## Content CRUD Methods

All methods raise `ValueError` if the resource has no location.

### `read(*, resource, path="") → Content`

Returns the database schema or connection information.

| Database type | Behaviour |
|---------------|-----------|
| SQLite | Queries `sqlite_master` for tables, views, and indexes; returns DDL as UTF-8 text |
| Remote (postgres, mysql, duckdb) | Returns a connection-info summary with credentials masked |

The `path` argument is ignored for database resources (schema is always read).

### `write(*, resource, path, data) → WriteResult`

Executes SQL statements against the database.

| Database type | Behaviour |
|---------------|-----------|
| SQLite | Executes the SQL in `data` (decoded as UTF-8) using `executescript()` |
| Remote | Returns `WriteResult(success=False, message="not supported")` |

### `delete(*, resource, path="") → DeleteResult`

Drops a table from the database.

| Database type | Behaviour |
|---------------|-----------|
| SQLite | Executes `DROP TABLE IF EXISTS <path>` |
| Remote | Returns `DeleteResult(success=False, message="not supported")` |

If `path` is empty, returns a failure result (no-op rather than dropping all tables).

### `list_children(*, resource) → list[str]`

Lists the tables and views in the database.

| Database type | Behaviour |
|---------------|-----------|
| SQLite | Queries `sqlite_master` for tables and views; returns their names |
| Remote | Returns `[]` |

### `diff(*, resource, other_location) → DiffResult`

Compares the schema of this database against another location by content hash.

Both sides are read via `read()`. If both sides are absent (empty content),
the `EMPTY_CONTENT_HASH` sentinel is used and the result is `no_change=True`.

---

## Checkpoint Methods

Checkpoints allow the plan execution engine to save and restore database
state during the Apply phase.

### `create_checkpoint(*, resource) → CheckpointResult`

Creates a savepoint in the database.

| Database type | Behaviour |
|---------------|-----------|
| SQLite | Opens a connection, executes `SAVEPOINT <id>`, and holds the connection open |
| Remote | Returns a content-hash-based checkpoint (no actual savepoint) |

The returned `CheckpointResult.checkpoint_id` is used to identify the
savepoint for `rollback_to()`.

### `rollback_to(*, resource, checkpoint_id) → RollbackResult`

Rolls back to a previously created savepoint.

| Database type | Behaviour |
|---------------|-----------|
| SQLite | Executes `ROLLBACK TO SAVEPOINT <id>` and closes the held connection |
| Remote | Returns `RollbackResult(success=False, message="not supported")` |

If `checkpoint_id` is not found (e.g. the process restarted), returns a
failure result rather than raising.

---

## Error Handling

All methods handle errors gracefully:

- SQLite errors are caught and returned as failure results with the error
  message in `metadata["error"]`.
- Credentials in connection strings are masked using
  `cleveragents.shared.redaction.mask_database_url()` before appearing in
  logs or error messages.
- No unhandled exceptions propagate from CRUD or checkpoint methods.

---

## CLI Usage

```bash
# Add a SQLite database resource
agents resource add sqlite local/my-db --path /path/to/db.sqlite

# Add a PostgreSQL database resource
agents resource add postgres local/pg-db \
    --host localhost --port 5432 --dbname myapp --user admin

# Add using a full connection string
agents resource add postgres local/pg-db \
    --connection-string "postgresql://admin:secret@localhost:5432/myapp"

# List database resources
agents resource list --type sqlite

# Show database details
agents resource show local/my-db
```

---

## Architecture Notes

`DatabaseResourceHandler` extends `BaseResourceHandler` and uses the
`transaction_rollback` sandbox strategy. SQLite checkpoints hold an open
`sqlite3.Connection` with an active `SAVEPOINT` in the
`_sqlite_checkpoints` dict (keyed by checkpoint ID). The connection is
closed when `rollback_to()` is called or when the handler is garbage
collected.

---

## Related Documentation

- [Resource Handlers](resource_handlers.md) — handler protocol overview
- [Resource Types (Built-in)](resource_types_builtin.md) — all built-in types
- [Checkpointing](checkpointing.md) — checkpoint and rollback system
- [Sandbox](sandbox.md) — sandbox strategies
