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 128 | 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 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, &file.StyleHash, &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 } |