@@ -2,6 +2,7 @@ package cmd
22
33import (
44 "bufio"
5+ "database/sql"
56 "errors"
67 "fmt"
78 "log"
@@ -14,6 +15,7 @@ import (
1415 "github.com/tursodatabase/turso-cli/internal/flags"
1516 "github.com/tursodatabase/turso-cli/internal/prompt"
1617 "github.com/tursodatabase/turso-cli/internal/turso"
18+ _ "turso.tech/database/tursogo"
1719)
1820
1921const MaxAWSDBSizeBytes = 1024 * 1024 * 1024 * 20 // 20 GB
@@ -88,7 +90,6 @@ func sqliteFileIntegrityChecks(file string, cipher string) error {
8890func tursoDBFileIntegrityChecks (file string ) error {
8991 return databaseFileIntegrityChecks (file , "" , databaseFileChecker {
9092 name : "TursoDB" ,
91- binary : "tursodb" ,
9293 journalMode : "mvcc" ,
9394 settings : tursoDBDatabaseSettings ,
9495 quickCheck : runTursoDBQuickCheck ,
@@ -135,15 +136,27 @@ func databaseFileIntegrityChecks(file, cipher string, checker databaseFileChecke
135136
136137func validateDatabaseSettings (file string , settings databaseSettings , checker databaseFileChecker ) error {
137138 if ! strings .EqualFold (settings .journalMode , checker .journalMode ) {
139+ if checker .binary == "" {
140+ return fmt .Errorf ("database is not in %s mode" , strings .ToUpper (checker .journalMode ))
141+ }
138142 return fmt .Errorf ("database is not in %s mode. Set it with '%s %s \" PRAGMA journal_mode = %s;\" '" , strings .ToUpper (checker .journalMode ), checker .binary , file , strings .ToUpper (checker .journalMode ))
139143 }
140144 if settings .pageSize != "4096" {
145+ if checker .binary == "" {
146+ return errors .New ("database must use 4KB page size" )
147+ }
141148 return fmt .Errorf ("database must use 4KB page size. You can set it with '%s %s \" PRAGMA page_size = 4096; VACUUM;\" '" , checker .binary , file )
142149 }
143150 if settings .autoVacuum != "0" {
151+ if checker .binary == "" {
152+ return errors .New ("database must have autovacuum disabled" )
153+ }
144154 return fmt .Errorf ("database must have autovacuum disabled. You can set it with '%s %s \" PRAGMA auto_vacuum = 0;\" '" , checker .binary , file )
145155 }
146156 if ! strings .EqualFold (settings .encoding , "UTF-8" ) {
157+ if checker .binary == "" {
158+ return errors .New ("database must use UTF-8 encoding" )
159+ }
147160 return fmt .Errorf ("database must use UTF-8 encoding. You can set it with '%s %s \" PRAGMA encoding = 'UTF-8';\" '" , checker .binary , file )
148161 }
149162 return nil
@@ -175,17 +188,38 @@ func sqliteDatabaseSettings(file string) (databaseSettings, error) {
175188}
176189
177190func tursoDBDatabaseSettings (file string ) (databaseSettings , error ) {
178- output , err := exec . Command ( "tursodb" , "-q" , "-m" , "list" , file , databaseSettingsQuery ). CombinedOutput ( )
191+ db , err := openTursoDB ( file )
179192 if err != nil {
180- return databaseSettings {}, fmt .Errorf ("failed to check database settings with TursoDB: %w: %s " , err , strings . TrimSpace ( string ( output )) )
193+ return databaseSettings {}, fmt .Errorf ("failed to check database settings with TursoDB: %w" , err )
181194 }
182- settings , err := parseDatabaseSettings (string (output ))
195+ defer db .Close ()
196+
197+ var settings databaseSettings
198+ err = db .QueryRow (databaseSettingsQuery ).Scan (
199+ & settings .journalMode ,
200+ & settings .pageSize ,
201+ & settings .autoVacuum ,
202+ & settings .encoding ,
203+ )
183204 if err != nil {
184- return databaseSettings {}, fmt .Errorf ("failed to parse database settings from TursoDB: %w" , err )
205+ return databaseSettings {}, fmt .Errorf ("failed to query database settings with TursoDB: %w" , err )
185206 }
186207 return settings , nil
187208}
188209
210+ func openTursoDB (file string ) (* sql.DB , error ) {
211+ db , err := sql .Open ("turso" , file )
212+ if err != nil {
213+ return nil , err
214+ }
215+ db .SetMaxOpenConns (1 )
216+ if err := db .Ping (); err != nil {
217+ db .Close ()
218+ return nil , err
219+ }
220+ return db , nil
221+ }
222+
189223func runQuickCheck (file string ) error {
190224 cmd := exec .Command ("sqlite3" , "-list" , file , "pragma quick_check;" )
191225 if err := cmd .Run (); err != nil {
@@ -195,13 +229,34 @@ func runQuickCheck(file string) error {
195229}
196230
197231func runTursoDBQuickCheck (file string ) error {
198- output , err := exec .Command ("tursodb" , "-q" , "-m" , "list" , file , "PRAGMA quick_check;" ).CombinedOutput ()
232+ db , err := openTursoDB (file )
233+ if err != nil {
234+ return fmt .Errorf ("TursoDB integrity check failed for %s: %w" , file , err )
235+ }
236+ defer db .Close ()
237+
238+ rows , err := db .Query ("PRAGMA quick_check;" )
199239 if err != nil {
200- return fmt .Errorf ("TursoDB integrity check failed for %s: %w: %s" , file , err , strings .TrimSpace (string (output )))
240+ return fmt .Errorf ("TursoDB integrity check failed for %s: %w" , file , err )
241+ }
242+ defer rows .Close ()
243+
244+ checked := false
245+ for rows .Next () {
246+ var result string
247+ if err := rows .Scan (& result ); err != nil {
248+ return fmt .Errorf ("TursoDB integrity check failed for %s: %w" , file , err )
249+ }
250+ checked = true
251+ if result != "ok" {
252+ return fmt .Errorf ("TursoDB integrity check failed for %s: %s" , file , result )
253+ }
254+ }
255+ if err := rows .Err (); err != nil {
256+ return fmt .Errorf ("TursoDB integrity check failed for %s: %w" , file , err )
201257 }
202- fields := strings .Fields (string (output ))
203- if len (fields ) == 0 || fields [len (fields )- 1 ] != "ok" {
204- return fmt .Errorf ("TursoDB integrity check failed for %s: %s" , file , strings .TrimSpace (string (output )))
258+ if ! checked {
259+ return fmt .Errorf ("TursoDB integrity check failed for %s: no result" , file )
205260 }
206261 return nil
207262}
@@ -222,10 +277,17 @@ func checkpointWALBeforeUpload(file string) error {
222277}
223278
224279func prepareTursoDBFile (file string ) error {
225- output , err := exec .Command ("tursodb" , "-q" , "-m" , "list" , file ,
226- "PRAGMA journal_mode = mvcc; PRAGMA wal_checkpoint(TRUNCATE);" ).CombinedOutput ()
280+ db , err := openTursoDB (file )
227281 if err != nil {
228- return fmt .Errorf ("could not prepare %s for TursoDB import: %w: %s" , file , err , strings .TrimSpace (string (output )))
282+ return fmt .Errorf ("could not prepare %s for TursoDB import: %w" , file , err )
283+ }
284+ _ , execErr := db .Exec ("PRAGMA journal_mode = mvcc; PRAGMA wal_checkpoint(TRUNCATE);" )
285+ closeErr := db .Close ()
286+ if execErr != nil {
287+ return fmt .Errorf ("could not prepare %s for TursoDB import: %w" , file , execErr )
288+ }
289+ if closeErr != nil {
290+ return fmt .Errorf ("could not close %s after preparing it for TursoDB import: %w" , file , closeErr )
229291 }
230292
231293 format , err := sniffSQLiteFileFormat (file )
@@ -322,9 +384,6 @@ func handleDBFileAWS(file string, cipher string) (*turso.DBSeed, error) {
322384 }
323385
324386 if tursoDBFlag {
325- if err := checkTursoDBAvailable (); err != nil {
326- return nil , err
327- }
328387 if format != fileFormatMVCC {
329388 fmt .Printf ("Converting %s to TursoDB (MVCC) format for import.\n " , file )
330389 }
@@ -385,11 +444,3 @@ func validateReservedBytes(dbPath string, cipher string) error {
385444 }
386445 return nil
387446}
388-
389- func checkTursoDBAvailable () error {
390- _ , err := exec .LookPath ("tursodb" )
391- if errors .Is (err , exec .ErrNotFound ) {
392- return errors .New ("could not find tursodb on your system. Please install it to import into a TursoDB database" )
393- }
394- return err
395- }
0 commit comments