db.go (2810B)
1 // This file is part of taler-go, the Taler Go implementation. 2 // Copyright (C) 2026 Martin Schanzenbach 3 // 4 // Taler Go is free software: you can redistribute it and/or modify it 5 // under the terms of the GNU Affero General Public License as published 6 // by the Free Software Foundation, either version 3 of the License, 7 // or (at your option) any later version. 8 // 9 // Taler Go is distributed in the hope that it will be useful, but 10 // WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 // Affero General Public License for more details. 13 // 14 // You should have received a copy of the GNU Affero General Public License 15 // along with this program. If not, see <http://www.gnu.org/licenses/>. 16 // 17 // SPDX-License-Identifier: AGPL3.0-or-later 18 19 package util 20 21 import ( 22 "database/sql" 23 "fmt" 24 "os" 25 "os/exec" 26 "path/filepath" 27 ) 28 29 func CheckVersioning(db *sql.DB) (bool, error) { 30 rows, err := db.Query(`SELECT schema_name FROM information_schema.schemata WHERE schema_name='_v';`) 31 if err != nil { 32 return false, err 33 } 34 defer rows.Close() 35 if rows.Next() { 36 fmt.Println("Versioning applied") 37 return true, nil 38 } 39 return false, nil 40 } 41 42 func CheckPatch(db *sql.DB, patchName string) (bool, error) { 43 rows, err := db.Query(`SELECT applied_by FROM _v.patches WHERE patch_name=$1 LIMIT 1;`, patchName) 44 if err != nil { 45 return false, err 46 } 47 defer rows.Close() 48 if rows.Next() { 49 return true, nil 50 } 51 return false, nil 52 } 53 54 func RunSQL(db *sql.DB, patchName string, dbName string) error { 55 path, err := exec.LookPath("psql") 56 if err != nil { 57 return err 58 } 59 _, err = exec.Command(path, dbName, "-f", patchName, "-q", "--set", "ON_ERROR_STOP=1").Output() 60 fmt.Printf("Running: %s %s %s %s %s %s %s\n", path, dbName, "-f", patchName, "-q", "--set", "ON_ERROR_STOP=1") 61 if err != nil { 62 return err 63 } 64 return nil 65 66 } 67 68 func DBInit(db *sql.DB, datahome string, dbName string, patchesPrefix string) error { 69 applied, err := CheckVersioning(db) 70 loadSuffix := filepath.Join(datahome, "sql") 71 if err != nil { 72 fmt.Printf("%v\n", err) 73 } 74 if !applied { 75 err := RunSQL(db, filepath.Join(loadSuffix, "versioning.sql"), dbName) 76 if err != nil { 77 return err 78 } 79 } 80 for i := range 10000 { 81 patchName := fmt.Sprintf("%s-%04d", patchesPrefix, i+1) 82 applied, err := CheckPatch(db, patchName) 83 if err != nil { 84 return err 85 } 86 if applied { 87 fmt.Printf("Patch %s already applied\n", patchName) 88 continue 89 } 90 patchFile := fmt.Sprintf("%s.sql", filepath.Join(loadSuffix, patchName)) 91 if _, err := os.Stat(patchFile); err != nil { 92 fmt.Printf("Patch %s not found, up-to-date.\n", patchFile) 93 break 94 } 95 fmt.Printf("Applying patch %s\n", patchName) 96 err = RunSQL(db, patchFile, dbName) 97 if err != nil { 98 return err 99 } 100 } 101 return nil 102 }