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 129 130 131 132 133 134 135 136 | 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, "/"): err := r.queries.checkPath.QueryRow(cPath + "/").Scan(&differs) if err != nil { r.log.Warn("error canonicalising path", "path", path, "error", err) return } if differs { cPath += "/" } case strings.HasSuffix(path, "/"): tmp := strings.TrimSuffix(path, "/") err := r.queries.checkPath.QueryRow(tmp).Scan(&differs) if err != nil { r.log.Warn("error canonicalising path", "path", path, "error", err) return } if differs { cPath = tmp } } return } |