175 lines
4.4 KiB
Go
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)
|
|
}
|