Files
pastebin/cmd/migrate/main.go
T

175 lines
4.4 KiB
Go

// migrate - Script para migrar dados do BoltDB para SQLite3
package main
import (
"crypto/sha256"
"encoding/hex"
"flag"
"fmt"
"log"
"math/rand"
"os"
"time"
"database/sql"
"github.com/boltdb/bolt"
_ "modernc.org/sqlite"
)
const shortIDLength = 7
// generateShortID gera um ID único de 7 caracteres
func generateShortID() string {
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
b := make([]byte, shortIDLength)
for i := range b {
b[i] = charset[rand.Intn(len(charset))]
}
return string(b)
}
// generateUniqueShortID gera um ID único que não existe no banco
func generateUniqueShortID(db *sql.DB) (string, error) {
maxAttempts := 100
for i := 0; i < maxAttempts; i++ {
id := generateShortID()
// Verificar se o ID já existe
var exists int
err := db.QueryRow("SELECT 1 FROM pastes WHERE short_id = ? LIMIT 1", id).Scan(&exists)
if err == sql.ErrNoRows {
return id, nil
}
if err != nil {
return "", err
}
}
return "", fmt.Errorf("não foi possível gerar um ID único após %d tentativas", maxAttempts)
}
func main() {
// Initialize random seed
rand.Seed(time.Now().UnixNano())
var boltPath, sqlitePath string
flag.StringVar(&boltPath, "bolt", "", "caminho para o arquivo BoltDB")
flag.StringVar(&sqlitePath, "sqlite", "", "caminho para o arquivo SQLite3 de destino")
flag.Parse()
if boltPath == "" || sqlitePath == "" {
fmt.Fprintf(os.Stderr, "Uso: %s -bolt <arquivo_bolt.db> -sqlite <arquivo_sqlite.db>\n", os.Args[0])
os.Exit(1)
}
// Abrir banco BoltDB
boltDB, err := bolt.Open(boltPath, 0600, &bolt.Options{Timeout: 1 * time.Second})
if err != nil {
log.Fatal("Erro ao abrir BoltDB:", err)
}
defer boltDB.Close()
// Abrir banco SQLite3
sqliteDB, err := sql.Open("sqlite", sqlitePath)
if err != nil {
log.Fatal("Erro ao abrir SQLite3:", err)
}
defer sqliteDB.Close()
// Criar tabela no SQLite3
_, err = sqliteDB.Exec(`
CREATE TABLE IF NOT EXISTS pastes (
short_id TEXT PRIMARY KEY,
content TEXT NOT NULL,
content_hash TEXT NOT NULL,
created_at INTEGER NOT NULL
)
`)
if err != nil {
log.Fatal("Erro ao criar tabela:", err)
}
// Criar índices
_, err = sqliteDB.Exec(`CREATE INDEX IF NOT EXISTS idx_pastes_content_hash ON pastes(content_hash)`)
if err != nil {
log.Fatal("Erro ao criar índice content_hash:", err)
}
_, err = sqliteDB.Exec(`CREATE INDEX IF NOT EXISTS idx_pastes_created_at ON pastes(created_at)`)
if err != nil {
log.Fatal("Erro ao criar índice created_at:", err)
}
// Iniciar transação no SQLite3
tx, err := sqliteDB.Begin()
if err != nil {
log.Fatal("Erro ao iniciar transação:", err)
}
// Preparar statement de inserção
stmt, err := tx.Prepare("INSERT OR IGNORE INTO pastes (short_id, content, content_hash, created_at) VALUES (?, ?, ?, ?)")
if err != nil {
log.Fatal("Erro ao preparar statement:", err)
}
defer stmt.Close()
// Migrar dados do BoltDB para SQLite3
count := 0
duplicates := 0
err = boltDB.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("pastes"))
if b == nil {
return fmt.Errorf("bucket 'pastes' não encontrado")
}
return b.ForEach(func(k, v []byte) error {
// Calcular hash SHA-256 do conteúdo
hash := sha256.Sum256(v)
hashStr := hex.EncodeToString(hash[:])
// Verificar se já existe um paste com o mesmo conteúdo
var existingID string
err := sqliteDB.QueryRow("SELECT short_id FROM pastes WHERE content_hash = ? LIMIT 1", hashStr).Scan(&existingID)
if err == nil {
// Paste já existe, pular
duplicates++
return nil
} else if err != sql.ErrNoRows {
return fmt.Errorf("erro ao verificar duplicata: %v", err)
}
// Gerar ID curto único
shortID, err := generateUniqueShortID(sqliteDB)
if err != nil {
return fmt.Errorf("erro ao gerar ID único: %v", err)
}
// Inserir no SQLite3
_, err = stmt.Exec(shortID, string(v), hashStr, time.Now().Unix())
if err != nil {
return fmt.Errorf("erro ao inserir paste %s: %v", shortID, err)
}
count++
if count%100 == 0 {
fmt.Printf("Migrados %d pastes...\n", count)
}
return nil
})
})
if err != nil {
tx.Rollback()
log.Fatal("Erro durante migração:", err)
}
// Commit da transação
err = tx.Commit()
if err != nil {
log.Fatal("Erro ao fazer commit:", err)
}
fmt.Printf("Migração concluída! %d pastes migrados com sucesso, %d duplicatas ignoradas.\n", count, duplicates)
}