mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-10-19 14:07:36 +02:00
165 lines
4.4 KiB
Go
165 lines
4.4 KiB
Go
package database
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// This is a historical foreign-key semantic migration, not a column repair
|
|
// list. Rebuild from the latest definition while retaining extra columns,
|
|
// indexes, triggers and dependent rows in old installations.
|
|
func (db *DB) migrateVulnerabilitiesConversationFK() error {
|
|
ok, err := vulnerabilitiesConversationFKOnDeleteSetNull(db.DB)
|
|
if err != nil || ok {
|
|
return err
|
|
}
|
|
reference, err := openSchemaReference(applicationSchema)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer reference.Close()
|
|
var latest string
|
|
if err := reference.QueryRow(`SELECT sql FROM sqlite_master WHERE type='table' AND name='vulnerabilities'`).Scan(&latest); err != nil {
|
|
return err
|
|
}
|
|
createNew := "CREATE TABLE vulnerabilities_new " + latest[strings.Index(latest, "("):]
|
|
|
|
// Changing foreign_keys inside a transaction is ineffective. Pin one
|
|
// connection and disable it before the transaction to avoid cascading drops.
|
|
ctx := context.Background()
|
|
conn, err := db.Conn(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer conn.Close()
|
|
if _, err := conn.ExecContext(ctx, "PRAGMA foreign_keys=OFF"); err != nil {
|
|
return err
|
|
}
|
|
defer conn.ExecContext(ctx, "PRAGMA foreign_keys=ON")
|
|
tx, err := conn.BeginTx(ctx, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer tx.Rollback()
|
|
if _, err := tx.Exec(createNew); err != nil {
|
|
return err
|
|
}
|
|
var original string
|
|
if err := tx.QueryRow(`SELECT sql FROM sqlite_master WHERE type='table' AND name='vulnerabilities'`).Scan(&original); err != nil {
|
|
return err
|
|
}
|
|
definitions, err := columnDefinitions(original)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
oldColumns, err := schemaColumns(tx, "vulnerabilities")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
newColumns, err := schemaColumns(tx, "vulnerabilities_new")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
present := map[string]bool{}
|
|
for _, c := range newColumns {
|
|
present[strings.ToLower(c.name)] = true
|
|
}
|
|
var names, values []string
|
|
for _, c := range oldColumns {
|
|
key := strings.ToLower(c.name)
|
|
if !present[key] {
|
|
if _, err := tx.Exec("ALTER TABLE vulnerabilities_new ADD COLUMN " + definitions[key]); err != nil {
|
|
return fmt.Errorf("保留漏洞额外字段 %s 失败: %w", c.name, err)
|
|
}
|
|
}
|
|
if c.hidden != 0 {
|
|
continue
|
|
}
|
|
quoted := quoteSchemaName(c.name)
|
|
names = append(names, quoted)
|
|
if key == "retest_log" {
|
|
values = append(values, "COALESCE("+quoted+", '')")
|
|
} else {
|
|
values = append(values, quoted)
|
|
}
|
|
}
|
|
rows, err := tx.Query(`SELECT sql FROM sqlite_master WHERE tbl_name='vulnerabilities' AND type IN ('index','trigger') AND sql IS NOT NULL ORDER BY type, name`)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var objects []string
|
|
for rows.Next() {
|
|
var ddl string
|
|
if err := rows.Scan(&ddl); err != nil {
|
|
rows.Close()
|
|
return err
|
|
}
|
|
objects = append(objects, ddl)
|
|
}
|
|
err = rows.Err()
|
|
rows.Close()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
copyRows := "INSERT INTO vulnerabilities_new (" + strings.Join(names, ",") + ") SELECT " + strings.Join(values, ",") + " FROM vulnerabilities"
|
|
if _, err := tx.Exec(copyRows); err != nil {
|
|
return fmt.Errorf("保留漏洞数据失败: %w", err)
|
|
}
|
|
if _, err := tx.Exec("DROP TABLE vulnerabilities"); err != nil {
|
|
return err
|
|
}
|
|
if _, err := tx.Exec("ALTER TABLE vulnerabilities_new RENAME TO vulnerabilities"); err != nil {
|
|
return err
|
|
}
|
|
for _, ddl := range objects {
|
|
if _, err := tx.Exec(ddl); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
rows, err = tx.Query("PRAGMA foreign_key_check")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
violation := rows.Next()
|
|
err = rows.Err()
|
|
rows.Close()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if violation {
|
|
return fmt.Errorf("漏洞外键迁移校验失败,已保留原数据")
|
|
}
|
|
if err := tx.Commit(); err != nil {
|
|
return err
|
|
}
|
|
db.logger.Info("vulnerabilities 表已迁移:删除对话时保留漏洞记录", zap.Int("preserved_columns", len(oldColumns)))
|
|
return nil
|
|
}
|
|
|
|
func vulnerabilitiesConversationFKOnDeleteSetNull(db *sql.DB) (bool, error) {
|
|
rows, err := db.Query(`PRAGMA foreign_key_list(vulnerabilities)`)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
defer rows.Close()
|
|
found := false
|
|
for rows.Next() {
|
|
var id, seq int
|
|
var table, from, to, onUpdate, onDelete, match string
|
|
if err := rows.Scan(&id, &seq, &table, &from, &to, &onUpdate, &onDelete, &match); err != nil {
|
|
return false, err
|
|
}
|
|
if from == "conversation_id" {
|
|
found = true
|
|
if !strings.EqualFold(onDelete, "SET NULL") {
|
|
return false, nil
|
|
}
|
|
}
|
|
}
|
|
return found, rows.Err()
|
|
}
|