taler-mailbox

Service for asynchronous wallet-to-wallet payment messages
Log | Files | Refs | Submodules | README | LICENSE

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 }