internal/storage/sqlite/reader.go (view raw)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | package sqlite
import (
"database/sql"
"io"
"strings"
"time"
"gitlab.com/tozd/go/errors"
"go.alanpearce.eu/website/internal/buffer"
"go.alanpearce.eu/website/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,
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]io.ReadSeeker, 1),
}
var unixTime int64
var encoding string
var content []byte
rows, err := r.queries.getFile.Query(filename)
if err != nil {
if err == sql.ErrNoRows {
return nil, nil
}
return nil, errors.WithMessage(err, "querying database")
}
for rows.Next() {
err = rows.Scan(
&file.ContentType,
&unixTime,
&file.Etag,
&encoding,
&content,
)
if err != nil {
if err == sql.ErrNoRows {
return nil, nil
}
return nil, errors.WithMessage(err, "querying database")
}
file.LastModified = time.Unix(unixTime, 0)
file.Encodings[encoding] = buffer.NewBuffer(content)
}
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
}
|