github.com/google/trillian-examples@v0.0.0-20240520080811-0d40d35cef0e/experimental/batchmap/sumdb/mapdb/tiledb.go (about) 1 // Copyright 2020 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package mapdb 16 17 import ( 18 "database/sql" 19 "encoding/json" 20 "errors" 21 "fmt" 22 "time" 23 24 "github.com/google/trillian/experimental/batchmap" 25 ) 26 27 // NoRevisionsFound is returned when the DB appears valid but has no revisions in it. 28 type NoRevisionsFound = error 29 30 // TileDB provides read/write access to the generated Map tiles. 31 type TileDB struct { 32 db *sql.DB 33 } 34 35 // NewTileDB creates a TileDB using a file at the given location. 36 // If the file doesn't exist it will be created. 37 func NewTileDB(location string) (*TileDB, error) { 38 db, err := sql.Open("sqlite3", location) 39 if err != nil { 40 return nil, err 41 } 42 return &TileDB{ 43 db: db, 44 }, nil 45 } 46 47 // Init creates the database tables if needed. 48 func (d *TileDB) Init() error { 49 // TODO(mhutchinson): Consider storing the entries too: 50 // CREATE TABLE IF NOT EXISTS entries (revision INTEGER, keyhash BLOB, key STRING, value STRING, PRIMARY KEY (revision, keyhash)) 51 52 if _, err := d.db.Exec("CREATE TABLE IF NOT EXISTS revisions (revision INTEGER PRIMARY KEY, datetime TIMESTAMP, logroot BLOB, count INTEGER)"); err != nil { 53 return err 54 } 55 if _, err := d.db.Exec("CREATE TABLE IF NOT EXISTS tiles (revision INTEGER, path BLOB, tile BLOB, PRIMARY KEY (revision, path))"); err != nil { 56 return err 57 } 58 if _, err := d.db.Exec("CREATE TABLE IF NOT EXISTS logs (module TEXT, revision INTEGER, leaves BLOB, PRIMARY KEY (module, revision))"); err != nil { 59 return err 60 } 61 return nil 62 } 63 64 // NextWriteRevision gets the revision that the next generation of the map should be written at. 65 func (d *TileDB) NextWriteRevision() (int, error) { 66 var rev sql.NullInt32 67 // TODO(mhutchinson): This should be updated to include the max of "tiles" or "logs". 68 if err := d.db.QueryRow("SELECT MAX(revision) FROM tiles").Scan(&rev); err != nil { 69 return 0, fmt.Errorf("failed to get max revision: %v", err) 70 } 71 if rev.Valid { 72 return int(rev.Int32) + 1, nil 73 } 74 return 0, nil 75 } 76 77 // LatestRevision gets the metadata for the last completed write. 78 func (d *TileDB) LatestRevision() (rev int, logroot []byte, count int64, err error) { 79 var sqlRev sql.NullInt32 80 if err := d.db.QueryRow("SELECT revision, logroot, count FROM revisions ORDER BY revision DESC LIMIT 1").Scan(&sqlRev, &logroot, &count); err != nil { 81 return 0, nil, 0, fmt.Errorf("failed to get latest revision: %v", err) 82 } 83 if sqlRev.Valid { 84 return int(sqlRev.Int32), logroot, count, nil 85 } 86 return 0, nil, 0, NoRevisionsFound(errors.New("no revisions found")) 87 } 88 89 // Tile gets the tile at the given path in the given revision of the map. 90 func (d *TileDB) Tile(revision int, path []byte) (*batchmap.Tile, error) { 91 var bs []byte 92 if err := d.db.QueryRow("SELECT tile FROM tiles WHERE revision=? AND path=?", revision, path).Scan(&bs); err != nil { 93 return nil, err 94 } 95 tile := &batchmap.Tile{} 96 if err := json.Unmarshal(bs, tile); err != nil { 97 return nil, fmt.Errorf("failed to parse tile at revision=%d, path=%x: %v", revision, path, err) 98 } 99 return tile, nil 100 } 101 102 // WriteRevision writes the metadata for a completed run into the database. 103 // If this method isn't called then the tiles may be written but this revision will be 104 // skipped by sensible readers because the provenance information isn't available. 105 func (d *TileDB) WriteRevision(rev int, logCheckpoint []byte, count int64) error { 106 now := time.Now() 107 _, err := d.db.Exec("INSERT INTO revisions (revision, datetime, logroot, count) VALUES (?, ?, ?, ?)", rev, now, logCheckpoint, count) 108 if err != nil { 109 return fmt.Errorf("failed to write revision: %w", err) 110 } 111 return nil 112 } 113 114 // Versions gets the log of versions for the given module in the given map revision. 115 func (d *TileDB) Versions(revision int, module string) ([]string, error) { 116 var bs []byte 117 if err := d.db.QueryRow("SELECT leaves FROM logs WHERE revision=? AND module=?", revision, module).Scan(&bs); err != nil { 118 return nil, err 119 } 120 var versions []string 121 if err := json.Unmarshal(bs, &versions); err != nil { 122 return nil, fmt.Errorf("failed to parse tile at revision=%d, path=%x: %v", revision, module, err) 123 } 124 return versions, nil 125 }