summaryrefslogtreecommitdiffhomepage
path: root/internal/db
diff options
context:
space:
mode:
authorKujtim Hoxha <[email protected]>2025-04-21 19:59:35 +0200
committerGitHub <[email protected]>2025-04-21 19:59:35 +0200
commitf33dff87725764af0b675b5e5b2e011b21c14c90 (patch)
tree4fe2c022305f13775f2cab3cdd80cd808259765b /internal/db
parent6b1c64bcc75b89c530294b6a2d4404682b435d56 (diff)
parent3a6a26981a8074b6ab0eaadb520db986e04799ff (diff)
downloadopencode-f33dff87725764af0b675b5e5b2e011b21c14c90.tar.gz
opencode-f33dff87725764af0b675b5e5b2e011b21c14c90.zip
Merge pull request #27 from kujtimiihoxha/opencode
OpenCode - Initial Implementation
Diffstat (limited to 'internal/db')
-rw-r--r--internal/db/connect.go10
-rw-r--r--internal/db/db.go152
-rw-r--r--internal/db/files.sql.go311
-rw-r--r--internal/db/messages.sql.go34
-rw-r--r--internal/db/migrations/000001_initial.down.sql2
-rw-r--r--internal/db/migrations/000001_initial.up.sql25
-rw-r--r--internal/db/models.go18
-rw-r--r--internal/db/querier.go10
-rw-r--r--internal/db/sql/files.sql71
-rw-r--r--internal/db/sql/messages.sql5
10 files changed, 592 insertions, 46 deletions
diff --git a/internal/db/connect.go b/internal/db/connect.go
index 8bba9cad8..9335bfc26 100644
--- a/internal/db/connect.go
+++ b/internal/db/connect.go
@@ -12,8 +12,8 @@ import (
"github.com/golang-migrate/migrate/v4/database/sqlite3"
_ "github.com/mattn/go-sqlite3"
- "github.com/kujtimiihoxha/termai/internal/config"
- "github.com/kujtimiihoxha/termai/internal/logging"
+ "github.com/kujtimiihoxha/opencode/internal/config"
+ "github.com/kujtimiihoxha/opencode/internal/logging"
)
func Connect() (*sql.DB, error) {
@@ -24,7 +24,7 @@ func Connect() (*sql.DB, error) {
if err := os.MkdirAll(dataDir, 0o700); err != nil {
return nil, fmt.Errorf("failed to create data directory: %w", err)
}
- dbPath := filepath.Join(dataDir, "termai.db")
+ dbPath := filepath.Join(dataDir, "opencode.db")
// Open the SQLite database
db, err := sql.Open("sqlite3", dbPath)
if err != nil {
@@ -48,9 +48,9 @@ func Connect() (*sql.DB, error) {
for _, pragma := range pragmas {
if _, err = db.Exec(pragma); err != nil {
- logging.Warn("Failed to set pragma", pragma, err)
+ logging.Error("Failed to set pragma", pragma, err)
} else {
- logging.Warn("Set pragma", "pragma", pragma)
+ logging.Debug("Set pragma", "pragma", pragma)
}
}
diff --git a/internal/db/db.go b/internal/db/db.go
index 75f626013..16e663804 100644
--- a/internal/db/db.go
+++ b/internal/db/db.go
@@ -24,33 +24,63 @@ func New(db DBTX) *Queries {
func Prepare(ctx context.Context, db DBTX) (*Queries, error) {
q := Queries{db: db}
var err error
+ if q.createFileStmt, err = db.PrepareContext(ctx, createFile); err != nil {
+ return nil, fmt.Errorf("error preparing query CreateFile: %w", err)
+ }
if q.createMessageStmt, err = db.PrepareContext(ctx, createMessage); err != nil {
return nil, fmt.Errorf("error preparing query CreateMessage: %w", err)
}
if q.createSessionStmt, err = db.PrepareContext(ctx, createSession); err != nil {
return nil, fmt.Errorf("error preparing query CreateSession: %w", err)
}
+ if q.deleteFileStmt, err = db.PrepareContext(ctx, deleteFile); err != nil {
+ return nil, fmt.Errorf("error preparing query DeleteFile: %w", err)
+ }
if q.deleteMessageStmt, err = db.PrepareContext(ctx, deleteMessage); err != nil {
return nil, fmt.Errorf("error preparing query DeleteMessage: %w", err)
}
if q.deleteSessionStmt, err = db.PrepareContext(ctx, deleteSession); err != nil {
return nil, fmt.Errorf("error preparing query DeleteSession: %w", err)
}
+ if q.deleteSessionFilesStmt, err = db.PrepareContext(ctx, deleteSessionFiles); err != nil {
+ return nil, fmt.Errorf("error preparing query DeleteSessionFiles: %w", err)
+ }
if q.deleteSessionMessagesStmt, err = db.PrepareContext(ctx, deleteSessionMessages); err != nil {
return nil, fmt.Errorf("error preparing query DeleteSessionMessages: %w", err)
}
+ if q.getFileStmt, err = db.PrepareContext(ctx, getFile); err != nil {
+ return nil, fmt.Errorf("error preparing query GetFile: %w", err)
+ }
+ if q.getFileByPathAndSessionStmt, err = db.PrepareContext(ctx, getFileByPathAndSession); err != nil {
+ return nil, fmt.Errorf("error preparing query GetFileByPathAndSession: %w", err)
+ }
if q.getMessageStmt, err = db.PrepareContext(ctx, getMessage); err != nil {
return nil, fmt.Errorf("error preparing query GetMessage: %w", err)
}
if q.getSessionByIDStmt, err = db.PrepareContext(ctx, getSessionByID); err != nil {
return nil, fmt.Errorf("error preparing query GetSessionByID: %w", err)
}
+ if q.listFilesByPathStmt, err = db.PrepareContext(ctx, listFilesByPath); err != nil {
+ return nil, fmt.Errorf("error preparing query ListFilesByPath: %w", err)
+ }
+ if q.listFilesBySessionStmt, err = db.PrepareContext(ctx, listFilesBySession); err != nil {
+ return nil, fmt.Errorf("error preparing query ListFilesBySession: %w", err)
+ }
+ if q.listLatestSessionFilesStmt, err = db.PrepareContext(ctx, listLatestSessionFiles); err != nil {
+ return nil, fmt.Errorf("error preparing query ListLatestSessionFiles: %w", err)
+ }
if q.listMessagesBySessionStmt, err = db.PrepareContext(ctx, listMessagesBySession); err != nil {
return nil, fmt.Errorf("error preparing query ListMessagesBySession: %w", err)
}
+ if q.listNewFilesStmt, err = db.PrepareContext(ctx, listNewFiles); err != nil {
+ return nil, fmt.Errorf("error preparing query ListNewFiles: %w", err)
+ }
if q.listSessionsStmt, err = db.PrepareContext(ctx, listSessions); err != nil {
return nil, fmt.Errorf("error preparing query ListSessions: %w", err)
}
+ if q.updateFileStmt, err = db.PrepareContext(ctx, updateFile); err != nil {
+ return nil, fmt.Errorf("error preparing query UpdateFile: %w", err)
+ }
if q.updateMessageStmt, err = db.PrepareContext(ctx, updateMessage); err != nil {
return nil, fmt.Errorf("error preparing query UpdateMessage: %w", err)
}
@@ -62,6 +92,11 @@ func Prepare(ctx context.Context, db DBTX) (*Queries, error) {
func (q *Queries) Close() error {
var err error
+ if q.createFileStmt != nil {
+ if cerr := q.createFileStmt.Close(); cerr != nil {
+ err = fmt.Errorf("error closing createFileStmt: %w", cerr)
+ }
+ }
if q.createMessageStmt != nil {
if cerr := q.createMessageStmt.Close(); cerr != nil {
err = fmt.Errorf("error closing createMessageStmt: %w", cerr)
@@ -72,6 +107,11 @@ func (q *Queries) Close() error {
err = fmt.Errorf("error closing createSessionStmt: %w", cerr)
}
}
+ if q.deleteFileStmt != nil {
+ if cerr := q.deleteFileStmt.Close(); cerr != nil {
+ err = fmt.Errorf("error closing deleteFileStmt: %w", cerr)
+ }
+ }
if q.deleteMessageStmt != nil {
if cerr := q.deleteMessageStmt.Close(); cerr != nil {
err = fmt.Errorf("error closing deleteMessageStmt: %w", cerr)
@@ -82,11 +122,26 @@ func (q *Queries) Close() error {
err = fmt.Errorf("error closing deleteSessionStmt: %w", cerr)
}
}
+ if q.deleteSessionFilesStmt != nil {
+ if cerr := q.deleteSessionFilesStmt.Close(); cerr != nil {
+ err = fmt.Errorf("error closing deleteSessionFilesStmt: %w", cerr)
+ }
+ }
if q.deleteSessionMessagesStmt != nil {
if cerr := q.deleteSessionMessagesStmt.Close(); cerr != nil {
err = fmt.Errorf("error closing deleteSessionMessagesStmt: %w", cerr)
}
}
+ if q.getFileStmt != nil {
+ if cerr := q.getFileStmt.Close(); cerr != nil {
+ err = fmt.Errorf("error closing getFileStmt: %w", cerr)
+ }
+ }
+ if q.getFileByPathAndSessionStmt != nil {
+ if cerr := q.getFileByPathAndSessionStmt.Close(); cerr != nil {
+ err = fmt.Errorf("error closing getFileByPathAndSessionStmt: %w", cerr)
+ }
+ }
if q.getMessageStmt != nil {
if cerr := q.getMessageStmt.Close(); cerr != nil {
err = fmt.Errorf("error closing getMessageStmt: %w", cerr)
@@ -97,16 +152,41 @@ func (q *Queries) Close() error {
err = fmt.Errorf("error closing getSessionByIDStmt: %w", cerr)
}
}
+ if q.listFilesByPathStmt != nil {
+ if cerr := q.listFilesByPathStmt.Close(); cerr != nil {
+ err = fmt.Errorf("error closing listFilesByPathStmt: %w", cerr)
+ }
+ }
+ if q.listFilesBySessionStmt != nil {
+ if cerr := q.listFilesBySessionStmt.Close(); cerr != nil {
+ err = fmt.Errorf("error closing listFilesBySessionStmt: %w", cerr)
+ }
+ }
+ if q.listLatestSessionFilesStmt != nil {
+ if cerr := q.listLatestSessionFilesStmt.Close(); cerr != nil {
+ err = fmt.Errorf("error closing listLatestSessionFilesStmt: %w", cerr)
+ }
+ }
if q.listMessagesBySessionStmt != nil {
if cerr := q.listMessagesBySessionStmt.Close(); cerr != nil {
err = fmt.Errorf("error closing listMessagesBySessionStmt: %w", cerr)
}
}
+ if q.listNewFilesStmt != nil {
+ if cerr := q.listNewFilesStmt.Close(); cerr != nil {
+ err = fmt.Errorf("error closing listNewFilesStmt: %w", cerr)
+ }
+ }
if q.listSessionsStmt != nil {
if cerr := q.listSessionsStmt.Close(); cerr != nil {
err = fmt.Errorf("error closing listSessionsStmt: %w", cerr)
}
}
+ if q.updateFileStmt != nil {
+ if cerr := q.updateFileStmt.Close(); cerr != nil {
+ err = fmt.Errorf("error closing updateFileStmt: %w", cerr)
+ }
+ }
if q.updateMessageStmt != nil {
if cerr := q.updateMessageStmt.Close(); cerr != nil {
err = fmt.Errorf("error closing updateMessageStmt: %w", cerr)
@@ -154,35 +234,55 @@ func (q *Queries) queryRow(ctx context.Context, stmt *sql.Stmt, query string, ar
}
type Queries struct {
- db DBTX
- tx *sql.Tx
- createMessageStmt *sql.Stmt
- createSessionStmt *sql.Stmt
- deleteMessageStmt *sql.Stmt
- deleteSessionStmt *sql.Stmt
- deleteSessionMessagesStmt *sql.Stmt
- getMessageStmt *sql.Stmt
- getSessionByIDStmt *sql.Stmt
- listMessagesBySessionStmt *sql.Stmt
- listSessionsStmt *sql.Stmt
- updateMessageStmt *sql.Stmt
- updateSessionStmt *sql.Stmt
+ db DBTX
+ tx *sql.Tx
+ createFileStmt *sql.Stmt
+ createMessageStmt *sql.Stmt
+ createSessionStmt *sql.Stmt
+ deleteFileStmt *sql.Stmt
+ deleteMessageStmt *sql.Stmt
+ deleteSessionStmt *sql.Stmt
+ deleteSessionFilesStmt *sql.Stmt
+ deleteSessionMessagesStmt *sql.Stmt
+ getFileStmt *sql.Stmt
+ getFileByPathAndSessionStmt *sql.Stmt
+ getMessageStmt *sql.Stmt
+ getSessionByIDStmt *sql.Stmt
+ listFilesByPathStmt *sql.Stmt
+ listFilesBySessionStmt *sql.Stmt
+ listLatestSessionFilesStmt *sql.Stmt
+ listMessagesBySessionStmt *sql.Stmt
+ listNewFilesStmt *sql.Stmt
+ listSessionsStmt *sql.Stmt
+ updateFileStmt *sql.Stmt
+ updateMessageStmt *sql.Stmt
+ updateSessionStmt *sql.Stmt
}
func (q *Queries) WithTx(tx *sql.Tx) *Queries {
return &Queries{
- db: tx,
- tx: tx,
- createMessageStmt: q.createMessageStmt,
- createSessionStmt: q.createSessionStmt,
- deleteMessageStmt: q.deleteMessageStmt,
- deleteSessionStmt: q.deleteSessionStmt,
- deleteSessionMessagesStmt: q.deleteSessionMessagesStmt,
- getMessageStmt: q.getMessageStmt,
- getSessionByIDStmt: q.getSessionByIDStmt,
- listMessagesBySessionStmt: q.listMessagesBySessionStmt,
- listSessionsStmt: q.listSessionsStmt,
- updateMessageStmt: q.updateMessageStmt,
- updateSessionStmt: q.updateSessionStmt,
+ db: tx,
+ tx: tx,
+ createFileStmt: q.createFileStmt,
+ createMessageStmt: q.createMessageStmt,
+ createSessionStmt: q.createSessionStmt,
+ deleteFileStmt: q.deleteFileStmt,
+ deleteMessageStmt: q.deleteMessageStmt,
+ deleteSessionStmt: q.deleteSessionStmt,
+ deleteSessionFilesStmt: q.deleteSessionFilesStmt,
+ deleteSessionMessagesStmt: q.deleteSessionMessagesStmt,
+ getFileStmt: q.getFileStmt,
+ getFileByPathAndSessionStmt: q.getFileByPathAndSessionStmt,
+ getMessageStmt: q.getMessageStmt,
+ getSessionByIDStmt: q.getSessionByIDStmt,
+ listFilesByPathStmt: q.listFilesByPathStmt,
+ listFilesBySessionStmt: q.listFilesBySessionStmt,
+ listLatestSessionFilesStmt: q.listLatestSessionFilesStmt,
+ listMessagesBySessionStmt: q.listMessagesBySessionStmt,
+ listNewFilesStmt: q.listNewFilesStmt,
+ listSessionsStmt: q.listSessionsStmt,
+ updateFileStmt: q.updateFileStmt,
+ updateMessageStmt: q.updateMessageStmt,
+ updateSessionStmt: q.updateSessionStmt,
}
}
diff --git a/internal/db/files.sql.go b/internal/db/files.sql.go
new file mode 100644
index 000000000..39def271f
--- /dev/null
+++ b/internal/db/files.sql.go
@@ -0,0 +1,311 @@
+// Code generated by sqlc. DO NOT EDIT.
+// versions:
+// sqlc v1.27.0
+// source: files.sql
+
+package db
+
+import (
+ "context"
+)
+
+const createFile = `-- name: CreateFile :one
+INSERT INTO files (
+ id,
+ session_id,
+ path,
+ content,
+ version,
+ created_at,
+ updated_at
+) VALUES (
+ ?, ?, ?, ?, ?, strftime('%s', 'now'), strftime('%s', 'now')
+)
+RETURNING id, session_id, path, content, version, created_at, updated_at
+`
+
+type CreateFileParams struct {
+ ID string `json:"id"`
+ SessionID string `json:"session_id"`
+ Path string `json:"path"`
+ Content string `json:"content"`
+ Version string `json:"version"`
+}
+
+func (q *Queries) CreateFile(ctx context.Context, arg CreateFileParams) (File, error) {
+ row := q.queryRow(ctx, q.createFileStmt, createFile,
+ arg.ID,
+ arg.SessionID,
+ arg.Path,
+ arg.Content,
+ arg.Version,
+ )
+ var i File
+ err := row.Scan(
+ &i.ID,
+ &i.SessionID,
+ &i.Path,
+ &i.Content,
+ &i.Version,
+ &i.CreatedAt,
+ &i.UpdatedAt,
+ )
+ return i, err
+}
+
+const deleteFile = `-- name: DeleteFile :exec
+DELETE FROM files
+WHERE id = ?
+`
+
+func (q *Queries) DeleteFile(ctx context.Context, id string) error {
+ _, err := q.exec(ctx, q.deleteFileStmt, deleteFile, id)
+ return err
+}
+
+const deleteSessionFiles = `-- name: DeleteSessionFiles :exec
+DELETE FROM files
+WHERE session_id = ?
+`
+
+func (q *Queries) DeleteSessionFiles(ctx context.Context, sessionID string) error {
+ _, err := q.exec(ctx, q.deleteSessionFilesStmt, deleteSessionFiles, sessionID)
+ return err
+}
+
+const getFile = `-- name: GetFile :one
+SELECT id, session_id, path, content, version, created_at, updated_at
+FROM files
+WHERE id = ? LIMIT 1
+`
+
+func (q *Queries) GetFile(ctx context.Context, id string) (File, error) {
+ row := q.queryRow(ctx, q.getFileStmt, getFile, id)
+ var i File
+ err := row.Scan(
+ &i.ID,
+ &i.SessionID,
+ &i.Path,
+ &i.Content,
+ &i.Version,
+ &i.CreatedAt,
+ &i.UpdatedAt,
+ )
+ return i, err
+}
+
+const getFileByPathAndSession = `-- name: GetFileByPathAndSession :one
+SELECT id, session_id, path, content, version, created_at, updated_at
+FROM files
+WHERE path = ? AND session_id = ?
+ORDER BY created_at DESC
+LIMIT 1
+`
+
+type GetFileByPathAndSessionParams struct {
+ Path string `json:"path"`
+ SessionID string `json:"session_id"`
+}
+
+func (q *Queries) GetFileByPathAndSession(ctx context.Context, arg GetFileByPathAndSessionParams) (File, error) {
+ row := q.queryRow(ctx, q.getFileByPathAndSessionStmt, getFileByPathAndSession, arg.Path, arg.SessionID)
+ var i File
+ err := row.Scan(
+ &i.ID,
+ &i.SessionID,
+ &i.Path,
+ &i.Content,
+ &i.Version,
+ &i.CreatedAt,
+ &i.UpdatedAt,
+ )
+ return i, err
+}
+
+const listFilesByPath = `-- name: ListFilesByPath :many
+SELECT id, session_id, path, content, version, created_at, updated_at
+FROM files
+WHERE path = ?
+ORDER BY created_at DESC
+`
+
+func (q *Queries) ListFilesByPath(ctx context.Context, path string) ([]File, error) {
+ rows, err := q.query(ctx, q.listFilesByPathStmt, listFilesByPath, path)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ items := []File{}
+ for rows.Next() {
+ var i File
+ if err := rows.Scan(
+ &i.ID,
+ &i.SessionID,
+ &i.Path,
+ &i.Content,
+ &i.Version,
+ &i.CreatedAt,
+ &i.UpdatedAt,
+ ); err != nil {
+ return nil, err
+ }
+ items = append(items, i)
+ }
+ if err := rows.Close(); err != nil {
+ return nil, err
+ }
+ if err := rows.Err(); err != nil {
+ return nil, err
+ }
+ return items, nil
+}
+
+const listFilesBySession = `-- name: ListFilesBySession :many
+SELECT id, session_id, path, content, version, created_at, updated_at
+FROM files
+WHERE session_id = ?
+ORDER BY created_at ASC
+`
+
+func (q *Queries) ListFilesBySession(ctx context.Context, sessionID string) ([]File, error) {
+ rows, err := q.query(ctx, q.listFilesBySessionStmt, listFilesBySession, sessionID)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ items := []File{}
+ for rows.Next() {
+ var i File
+ if err := rows.Scan(
+ &i.ID,
+ &i.SessionID,
+ &i.Path,
+ &i.Content,
+ &i.Version,
+ &i.CreatedAt,
+ &i.UpdatedAt,
+ ); err != nil {
+ return nil, err
+ }
+ items = append(items, i)
+ }
+ if err := rows.Close(); err != nil {
+ return nil, err
+ }
+ if err := rows.Err(); err != nil {
+ return nil, err
+ }
+ return items, nil
+}
+
+const listLatestSessionFiles = `-- name: ListLatestSessionFiles :many
+SELECT f.id, f.session_id, f.path, f.content, f.version, f.created_at, f.updated_at
+FROM files f
+INNER JOIN (
+ SELECT path, MAX(created_at) as max_created_at
+ FROM files
+ GROUP BY path
+) latest ON f.path = latest.path AND f.created_at = latest.max_created_at
+WHERE f.session_id = ?
+ORDER BY f.path
+`
+
+func (q *Queries) ListLatestSessionFiles(ctx context.Context, sessionID string) ([]File, error) {
+ rows, err := q.query(ctx, q.listLatestSessionFilesStmt, listLatestSessionFiles, sessionID)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ items := []File{}
+ for rows.Next() {
+ var i File
+ if err := rows.Scan(
+ &i.ID,
+ &i.SessionID,
+ &i.Path,
+ &i.Content,
+ &i.Version,
+ &i.CreatedAt,
+ &i.UpdatedAt,
+ ); err != nil {
+ return nil, err
+ }
+ items = append(items, i)
+ }
+ if err := rows.Close(); err != nil {
+ return nil, err
+ }
+ if err := rows.Err(); err != nil {
+ return nil, err
+ }
+ return items, nil
+}
+
+const listNewFiles = `-- name: ListNewFiles :many
+SELECT id, session_id, path, content, version, created_at, updated_at
+FROM files
+WHERE is_new = 1
+ORDER BY created_at DESC
+`
+
+func (q *Queries) ListNewFiles(ctx context.Context) ([]File, error) {
+ rows, err := q.query(ctx, q.listNewFilesStmt, listNewFiles)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ items := []File{}
+ for rows.Next() {
+ var i File
+ if err := rows.Scan(
+ &i.ID,
+ &i.SessionID,
+ &i.Path,
+ &i.Content,
+ &i.Version,
+ &i.CreatedAt,
+ &i.UpdatedAt,
+ ); err != nil {
+ return nil, err
+ }
+ items = append(items, i)
+ }
+ if err := rows.Close(); err != nil {
+ return nil, err
+ }
+ if err := rows.Err(); err != nil {
+ return nil, err
+ }
+ return items, nil
+}
+
+const updateFile = `-- name: UpdateFile :one
+UPDATE files
+SET
+ content = ?,
+ version = ?,
+ updated_at = strftime('%s', 'now')
+WHERE id = ?
+RETURNING id, session_id, path, content, version, created_at, updated_at
+`
+
+type UpdateFileParams struct {
+ Content string `json:"content"`
+ Version string `json:"version"`
+ ID string `json:"id"`
+}
+
+func (q *Queries) UpdateFile(ctx context.Context, arg UpdateFileParams) (File, error) {
+ row := q.queryRow(ctx, q.updateFileStmt, updateFile, arg.Content, arg.Version, arg.ID)
+ var i File
+ err := row.Scan(
+ &i.ID,
+ &i.SessionID,
+ &i.Path,
+ &i.Content,
+ &i.Version,
+ &i.CreatedAt,
+ &i.UpdatedAt,
+ )
+ return i, err
+}
diff --git a/internal/db/messages.sql.go b/internal/db/messages.sql.go
index 4309db181..0555b4330 100644
--- a/internal/db/messages.sql.go
+++ b/internal/db/messages.sql.go
@@ -7,6 +7,7 @@ package db
import (
"context"
+ "database/sql"
)
const createMessage = `-- name: CreateMessage :one
@@ -15,19 +16,21 @@ INSERT INTO messages (
session_id,
role,
parts,
+ model,
created_at,
updated_at
) VALUES (
- ?, ?, ?, ?, strftime('%s', 'now'), strftime('%s', 'now')
+ ?, ?, ?, ?, ?, strftime('%s', 'now'), strftime('%s', 'now')
)
-RETURNING id, session_id, role, parts, created_at, updated_at
+RETURNING id, session_id, role, parts, model, created_at, updated_at, finished_at
`
type CreateMessageParams struct {
- ID string `json:"id"`
- SessionID string `json:"session_id"`
- Role string `json:"role"`
- Parts string `json:"parts"`
+ ID string `json:"id"`
+ SessionID string `json:"session_id"`
+ Role string `json:"role"`
+ Parts string `json:"parts"`
+ Model sql.NullString `json:"model"`
}
func (q *Queries) CreateMessage(ctx context.Context, arg CreateMessageParams) (Message, error) {
@@ -36,6 +39,7 @@ func (q *Queries) CreateMessage(ctx context.Context, arg CreateMessageParams) (M
arg.SessionID,
arg.Role,
arg.Parts,
+ arg.Model,
)
var i Message
err := row.Scan(
@@ -43,8 +47,10 @@ func (q *Queries) CreateMessage(ctx context.Context, arg CreateMessageParams) (M
&i.SessionID,
&i.Role,
&i.Parts,
+ &i.Model,
&i.CreatedAt,
&i.UpdatedAt,
+ &i.FinishedAt,
)
return i, err
}
@@ -70,7 +76,7 @@ func (q *Queries) DeleteSessionMessages(ctx context.Context, sessionID string) e
}
const getMessage = `-- name: GetMessage :one
-SELECT id, session_id, role, parts, created_at, updated_at
+SELECT id, session_id, role, parts, model, created_at, updated_at, finished_at
FROM messages
WHERE id = ? LIMIT 1
`
@@ -83,14 +89,16 @@ func (q *Queries) GetMessage(ctx context.Context, id string) (Message, error) {
&i.SessionID,
&i.Role,
&i.Parts,
+ &i.Model,
&i.CreatedAt,
&i.UpdatedAt,
+ &i.FinishedAt,
)
return i, err
}
const listMessagesBySession = `-- name: ListMessagesBySession :many
-SELECT id, session_id, role, parts, created_at, updated_at
+SELECT id, session_id, role, parts, model, created_at, updated_at, finished_at
FROM messages
WHERE session_id = ?
ORDER BY created_at ASC
@@ -110,8 +118,10 @@ func (q *Queries) ListMessagesBySession(ctx context.Context, sessionID string) (
&i.SessionID,
&i.Role,
&i.Parts,
+ &i.Model,
&i.CreatedAt,
&i.UpdatedAt,
+ &i.FinishedAt,
); err != nil {
return nil, err
}
@@ -130,16 +140,18 @@ const updateMessage = `-- name: UpdateMessage :exec
UPDATE messages
SET
parts = ?,
+ finished_at = ?,
updated_at = strftime('%s', 'now')
WHERE id = ?
`
type UpdateMessageParams struct {
- Parts string `json:"parts"`
- ID string `json:"id"`
+ Parts string `json:"parts"`
+ FinishedAt sql.NullInt64 `json:"finished_at"`
+ ID string `json:"id"`
}
func (q *Queries) UpdateMessage(ctx context.Context, arg UpdateMessageParams) error {
- _, err := q.exec(ctx, q.updateMessageStmt, updateMessage, arg.Parts, arg.ID)
+ _, err := q.exec(ctx, q.updateMessageStmt, updateMessage, arg.Parts, arg.FinishedAt, arg.ID)
return err
}
diff --git a/internal/db/migrations/000001_initial.down.sql b/internal/db/migrations/000001_initial.down.sql
index 4f2712d86..a2b0d1321 100644
--- a/internal/db/migrations/000001_initial.down.sql
+++ b/internal/db/migrations/000001_initial.down.sql
@@ -1,8 +1,10 @@
DROP TRIGGER IF EXISTS update_sessions_updated_at;
DROP TRIGGER IF EXISTS update_messages_updated_at;
+DROP TRIGGER IF EXISTS update_files_updated_at;
DROP TRIGGER IF EXISTS update_session_message_count_on_delete;
DROP TRIGGER IF EXISTS update_session_message_count_on_insert;
DROP TABLE IF EXISTS sessions;
DROP TABLE IF EXISTS messages;
+DROP TABLE IF EXISTS files;
diff --git a/internal/db/migrations/000001_initial.up.sql b/internal/db/migrations/000001_initial.up.sql
index 2fbe5547e..b846ec600 100644
--- a/internal/db/migrations/000001_initial.up.sql
+++ b/internal/db/migrations/000001_initial.up.sql
@@ -18,14 +18,39 @@ UPDATE sessions SET updated_at = strftime('%s', 'now')
WHERE id = new.id;
END;
+-- Files
+CREATE TABLE IF NOT EXISTS files (
+ id TEXT PRIMARY KEY,
+ session_id TEXT NOT NULL,
+ path TEXT NOT NULL,
+ content TEXT NOT NULL,
+ version TEXT NOT NULL,
+ created_at INTEGER NOT NULL, -- Unix timestamp in milliseconds
+ updated_at INTEGER NOT NULL, -- Unix timestamp in milliseconds
+ FOREIGN KEY (session_id) REFERENCES sessions (id) ON DELETE CASCADE,
+ UNIQUE(path, session_id, version)
+);
+
+CREATE INDEX IF NOT EXISTS idx_files_session_id ON files (session_id);
+CREATE INDEX IF NOT EXISTS idx_files_path ON files (path);
+
+CREATE TRIGGER IF NOT EXISTS update_files_updated_at
+AFTER UPDATE ON files
+BEGIN
+UPDATE files SET updated_at = strftime('%s', 'now')
+WHERE id = new.id;
+END;
+
-- Messages
CREATE TABLE IF NOT EXISTS messages (
id TEXT PRIMARY KEY,
session_id TEXT NOT NULL,
role TEXT NOT NULL,
parts TEXT NOT NULL default '[]',
+ model TEXT,
created_at INTEGER NOT NULL, -- Unix timestamp in milliseconds
updated_at INTEGER NOT NULL, -- Unix timestamp in milliseconds
+ finished_at INTEGER, -- Unix timestamp in milliseconds
FOREIGN KEY (session_id) REFERENCES sessions (id) ON DELETE CASCADE
);
diff --git a/internal/db/models.go b/internal/db/models.go
index 1ad8607a9..f00cb6ad1 100644
--- a/internal/db/models.go
+++ b/internal/db/models.go
@@ -8,15 +8,27 @@ import (
"database/sql"
)
-type Message struct {
+type File struct {
ID string `json:"id"`
SessionID string `json:"session_id"`
- Role string `json:"role"`
- Parts string `json:"parts"`
+ Path string `json:"path"`
+ Content string `json:"content"`
+ Version string `json:"version"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
}
+type Message struct {
+ ID string `json:"id"`
+ SessionID string `json:"session_id"`
+ Role string `json:"role"`
+ Parts string `json:"parts"`
+ Model sql.NullString `json:"model"`
+ CreatedAt int64 `json:"created_at"`
+ UpdatedAt int64 `json:"updated_at"`
+ FinishedAt sql.NullInt64 `json:"finished_at"`
+}
+
type Session struct {
ID string `json:"id"`
ParentSessionID sql.NullString `json:"parent_session_id"`
diff --git a/internal/db/querier.go b/internal/db/querier.go
index c9d73ec39..704a97da2 100644
--- a/internal/db/querier.go
+++ b/internal/db/querier.go
@@ -9,15 +9,25 @@ import (
)
type Querier interface {
+ CreateFile(ctx context.Context, arg CreateFileParams) (File, error)
CreateMessage(ctx context.Context, arg CreateMessageParams) (Message, error)
CreateSession(ctx context.Context, arg CreateSessionParams) (Session, error)
+ DeleteFile(ctx context.Context, id string) error
DeleteMessage(ctx context.Context, id string) error
DeleteSession(ctx context.Context, id string) error
+ DeleteSessionFiles(ctx context.Context, sessionID string) error
DeleteSessionMessages(ctx context.Context, sessionID string) error
+ GetFile(ctx context.Context, id string) (File, error)
+ GetFileByPathAndSession(ctx context.Context, arg GetFileByPathAndSessionParams) (File, error)
GetMessage(ctx context.Context, id string) (Message, error)
GetSessionByID(ctx context.Context, id string) (Session, error)
+ ListFilesByPath(ctx context.Context, path string) ([]File, error)
+ ListFilesBySession(ctx context.Context, sessionID string) ([]File, error)
+ ListLatestSessionFiles(ctx context.Context, sessionID string) ([]File, error)
ListMessagesBySession(ctx context.Context, sessionID string) ([]Message, error)
+ ListNewFiles(ctx context.Context) ([]File, error)
ListSessions(ctx context.Context) ([]Session, error)
+ UpdateFile(ctx context.Context, arg UpdateFileParams) (File, error)
UpdateMessage(ctx context.Context, arg UpdateMessageParams) error
UpdateSession(ctx context.Context, arg UpdateSessionParams) (Session, error)
}
diff --git a/internal/db/sql/files.sql b/internal/db/sql/files.sql
new file mode 100644
index 000000000..aba2a6111
--- /dev/null
+++ b/internal/db/sql/files.sql
@@ -0,0 +1,71 @@
+-- name: GetFile :one
+SELECT *
+FROM files
+WHERE id = ? LIMIT 1;
+
+-- name: GetFileByPathAndSession :one
+SELECT *
+FROM files
+WHERE path = ? AND session_id = ?
+ORDER BY created_at DESC
+LIMIT 1;
+
+-- name: ListFilesBySession :many
+SELECT *
+FROM files
+WHERE session_id = ?
+ORDER BY created_at ASC;
+
+-- name: ListFilesByPath :many
+SELECT *
+FROM files
+WHERE path = ?
+ORDER BY created_at DESC;
+
+-- name: CreateFile :one
+INSERT INTO files (
+ id,
+ session_id,
+ path,
+ content,
+ version,
+ created_at,
+ updated_at
+) VALUES (
+ ?, ?, ?, ?, ?, strftime('%s', 'now'), strftime('%s', 'now')
+)
+RETURNING *;
+
+-- name: UpdateFile :one
+UPDATE files
+SET
+ content = ?,
+ version = ?,
+ updated_at = strftime('%s', 'now')
+WHERE id = ?
+RETURNING *;
+
+-- name: DeleteFile :exec
+DELETE FROM files
+WHERE id = ?;
+
+-- name: DeleteSessionFiles :exec
+DELETE FROM files
+WHERE session_id = ?;
+
+-- name: ListLatestSessionFiles :many
+SELECT f.*
+FROM files f
+INNER JOIN (
+ SELECT path, MAX(created_at) as max_created_at
+ FROM files
+ GROUP BY path
+) latest ON f.path = latest.path AND f.created_at = latest.max_created_at
+WHERE f.session_id = ?
+ORDER BY f.path;
+
+-- name: ListNewFiles :many
+SELECT *
+FROM files
+WHERE is_new = 1
+ORDER BY created_at DESC;
diff --git a/internal/db/sql/messages.sql b/internal/db/sql/messages.sql
index 64571158f..a59cebe7d 100644
--- a/internal/db/sql/messages.sql
+++ b/internal/db/sql/messages.sql
@@ -15,10 +15,11 @@ INSERT INTO messages (
session_id,
role,
parts,
+ model,
created_at,
updated_at
) VALUES (
- ?, ?, ?, ?, strftime('%s', 'now'), strftime('%s', 'now')
+ ?, ?, ?, ?, ?, strftime('%s', 'now'), strftime('%s', 'now')
)
RETURNING *;
@@ -26,9 +27,11 @@ RETURNING *;
UPDATE messages
SET
parts = ?,
+ finished_at = ?,
updated_at = strftime('%s', 'now')
WHERE id = ?;
+
-- name: DeleteMessage :exec
DELETE FROM messages
WHERE id = ?;