package sqlite import ( "database/sql" "strings" "time" "gitlab.com/tozd/go/errors" "go.alanpearce.eu/homestead/internal/buffer" "go.alanpearce.eu/homestead/internal/storage" "go.alanpearce.eu/x/log" ) type Reader struct { db *sql.DB log *log.Logger queries struct { getFile *sql.Stmt checkPath *sql.Stmt } } func NewReader(db *sql.DB, log *log.Logger) (r *Reader, err error) { r = &Reader{ log: log, db: db, } r.queries.getFile, err = r.db.Prepare(` SELECT file.content_type, file.last_modified, file.etag, file.style_hash, content.encoding, content.body FROM url INNER JOIN file USING (url_id) INNER JOIN content USING (file_id) WHERE url.path = ? `) if err != nil { return nil, errors.WithMessage(err, "preparing select statement") } r.queries.checkPath, err = r.db.Prepare(` SELECT EXISTS( SELECT 1 FROM url WHERE path = ? ) AS differs `) if err != nil { return nil, errors.WithMessage(err, "preparing check path statement") } return r, nil } func (r *Reader) GetFile(filename string) (*storage.File, error) { file := &storage.File{ Encodings: make(map[string]*buffer.Buffer, 1), } var unixTime int64 var encoding string var content []byte r.log.Debug("querying db for file", "filename", filename) rows, err := r.queries.getFile.Query(filename) if err != nil { return nil, errors.WithMessage(err, "querying database") } defer rows.Close() count := 0 for rows.Next() { count += 1 err = rows.Scan( &file.ContentType, &unixTime, &file.Etag, &file.StyleHash, &encoding, &content, ) if err != nil { return nil, errors.WithMessage(err, "querying database") } file.LastModified = time.Unix(unixTime, 0) file.Encodings[encoding] = buffer.NewBuffer(content) } if count == 0 { return nil, nil } if err := rows.Err(); err != nil { return nil, errors.WithMessage(err, "error iterating rows") } return file, nil } func (r *Reader) CanonicalisePath(path string) (cPath string, differs bool) { cPath = path switch { case path == "/": differs = false case strings.HasSuffix(path, "/index.html"): cPath, differs = strings.CutSuffix(path, "index.html") case !strings.HasSuffix(path, "/"): cPath += "/" err := r.queries.checkPath.QueryRow(cPath).Scan(&differs) if err != nil { r.log.Warn("error canonicalising path", "path", path, "error", err) return } case strings.HasSuffix(path, "/"): cPath = strings.TrimSuffix(path, "/") err := r.queries.checkPath.QueryRow(cPath).Scan(&differs) if err != nil { r.log.Warn("error canonicalising path", "path", path, "error", err) return } } return }