@@ -442,7 +442,8 @@ func (i *TursoServerClient) finalizeUpload(uploadID string) error {
442442}
443443
444444type ExportInfo struct {
445- CurrentGeneration int `json:"current_generation"`
445+ CurrentGeneration int `json:"current_generation"`
446+ DbType string `json:"db_type"`
446447}
447448
448449const EncryptionKeyHeader = "x-turso-encryption-key"
@@ -484,11 +485,23 @@ func (i *TursoServerClient) Export(outputFile string, withMetadata bool, remoteE
484485 return fmt .Errorf ("failed to write export to file: %w" , err )
485486 }
486487
487- lastFrameNo , err := i .ExportWAL (outputFile , & info , remoteEncryptionKey )
488- if err != nil {
489- return fmt .Errorf ("failed to export WAL: %w" , err )
488+ lastFrameNo := 0
489+ if info .DbType == "tursodb" {
490+ // tursodb databases persist writes in a logical log rather than a
491+ // SQLite WAL: fetch the canonical log the server assembles from its
492+ // durable log fragments.
493+ if err := i .ExportLog (outputFile , & info , remoteEncryptionKey ); err != nil {
494+ return fmt .Errorf ("failed to export logical log: %w" , err )
495+ }
496+ } else {
497+ lastFrameNo , err = i .ExportWAL (outputFile , & info , remoteEncryptionKey )
498+ if err != nil {
499+ return fmt .Errorf ("failed to export WAL: %w" , err )
500+ }
490501 }
491- if withMetadata {
502+ if withMetadata && info .DbType == "tursodb" {
503+ fmt .Fprintln (os .Stderr , "Warning: --with-metadata is not supported for tursodb databases and will be ignored." )
504+ } else if withMetadata {
492505 if err := i .ExportMetadata (outputFile , & info , lastFrameNo ); err != nil {
493506 return fmt .Errorf ("failed to export metadata: %w" , err )
494507 }
@@ -497,6 +510,39 @@ func (i *TursoServerClient) Export(outputFile string, withMetadata bool, remoteE
497510 return nil
498511}
499512
513+ // ExportLog downloads the canonical tursodb logical log for the generation
514+ // into <outputFile>-log. The server assembles it from its durable log
515+ // fragments with the same last-attempt-wins chain walk its own recovery
516+ // uses, so the file matches the .db-log a server would rebuild for this
517+ // generation.
518+ func (i * TursoServerClient ) ExportLog (outputFile string , info * ExportInfo , remoteEncryptionKey string ) error {
519+ headers := map [string ]string {}
520+ if remoteEncryptionKey != "" {
521+ headers [EncryptionKeyHeader ] = remoteEncryptionKey
522+ }
523+ logRes , err := i .client .GetWithHeaders (fmt .Sprintf ("/export/%d/log" , info .CurrentGeneration ), nil , headers )
524+ if err != nil {
525+ return fmt .Errorf ("failed to fetch logical log: %w" , err )
526+ }
527+ defer logRes .Body .Close ()
528+ if logRes .StatusCode != http .StatusOK {
529+ return parseResponseError (logRes )
530+ }
531+
532+ out , err := os .Create (outputFile + "-log" )
533+ if err != nil {
534+ return fmt .Errorf ("failed to create log file: %w" , err )
535+ }
536+ defer out .Close ()
537+ if _ , err := io .Copy (out , logRes .Body ); err != nil {
538+ return fmt .Errorf ("failed to write logical log to file: %w" , err )
539+ }
540+ if err := out .Sync (); err != nil {
541+ return fmt .Errorf ("failed to sync log file: %w" , err )
542+ }
543+ return nil
544+ }
545+
500546func (i * TursoServerClient ) ExportWAL (outputFile string , info * ExportInfo , remoteEncryptionKey string ) (int , error ) {
501547 walFile := outputFile + "-wal"
502548 walOut , err := os .Create (walFile )
0 commit comments