@@ -72,6 +72,8 @@ func (dec *Decoder) Close() error {
7272 return fmt .Errorf ("unmarshal trailer: %w" , err )
7373 }
7474
75+ // TODO: Ensure last read page is equal to the commit for snapshot LTX files
76+
7577 dec .writeToHash (b [:TrailerChecksumOffset ])
7678
7779 // Compare checksum with checksum in trailer.
@@ -203,24 +205,44 @@ func (dec *Decoder) Verify() error {
203205func (dec * Decoder ) DecodeDatabaseTo (w io.Writer ) error {
204206 if err := dec .DecodeHeader (); err != nil {
205207 return fmt .Errorf ("decode header: %w" , err )
206- } else if ! dec .header .IsSnapshot () {
208+ }
209+
210+ hdr := dec .Header ()
211+ lockPgno := hdr .LockPgno ()
212+ if ! dec .header .IsSnapshot () {
207213 return fmt .Errorf ("cannot decode non-snapshot LTX file to SQLite database" )
208214 }
209215
210216 var pageHeader PageHeader
211217 data := make ([]byte , dec .header .PageSize )
212- for i := 0 ; ; i ++ {
213- if err := dec .DecodePage (& pageHeader , data ); err == io .EOF {
214- break
215- } else if err != nil {
216- return fmt .Errorf ("decode page %d: %w" , i , err )
218+ for pgno := uint32 (1 ); pgno <= hdr .Commit ; pgno ++ {
219+ if pgno == lockPgno {
220+ // Write empty page for lock page.
221+ for i := range data {
222+ data [i ] = 0
223+ }
224+ } else {
225+ // Otherwise read the page from the LTX decoder.
226+ if err := dec .DecodePage (& pageHeader , data ); err != nil {
227+ return fmt .Errorf ("decode page %d: %w" , pgno , err )
228+ } else if pageHeader .Pgno != pgno {
229+ return fmt .Errorf ("unexpected pgno while decoding page: read %d, expected %d" , pageHeader .Pgno , pgno )
230+ }
217231 }
218232
219233 if _ , err := w .Write (data ); err != nil {
220- return fmt .Errorf ("write page %d: %w" , i , err )
234+ return fmt .Errorf ("write page %d: %w" , pgno , err )
221235 }
222236 }
223237
238+ // Issue one more final read and expect to see an EOF. This is required so
239+ // that the decoder can successfully close and validate.
240+ if err := dec .DecodePage (& pageHeader , data ); err == nil {
241+ return fmt .Errorf ("unexpected page %d after commit %d" , pageHeader .Pgno , hdr .Commit )
242+ } else if err != io .EOF {
243+ return fmt .Errorf ("unexpected error decoding after end of database: %w" , err )
244+ }
245+
224246 if err := dec .Close (); err != nil {
225247 return fmt .Errorf ("close decoder: %w" , err )
226248 }
0 commit comments