welp at least it runs
parent
701b619d04
commit
5a343d80f8
236
main.go
236
main.go
|
|
@ -11,13 +11,14 @@ import (
|
|||
"slices"
|
||||
"strings"
|
||||
|
||||
_ "github.com/glebarez/sqlite"
|
||||
//_ "github.com/glebarez/sqlite"
|
||||
"github.com/google/uuid"
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
const (
|
||||
jellyDB = "jellyfin.db"
|
||||
libraryDB = "library.db"
|
||||
jellyDB = "jellyfin.db"
|
||||
libDB = "library.db"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
|
@ -34,7 +35,7 @@ func main() {
|
|||
{
|
||||
if err := cp(path.Join(data, jellyDB), path.Join(workd, jellyDB)); err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
} else if err := cp(path.Join(data, libraryDB), path.Join(workd, libraryDB)); err != nil {
|
||||
} else if err := cp(path.Join(data, libDB), path.Join(workd, libDB)); err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
}
|
||||
|
|
@ -58,7 +59,7 @@ func main() {
|
|||
if n, err := SelectOne[int](jellyfinDB, `SELECT COUNT(*) FROM Users WHERE Username = $1`, toU); err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
} else if n == 1 {
|
||||
} else if err := CloneForColumn(jellyfinDB, `Users`, `Username`, fromU, toU, `Id`, `InternalId`); err != nil {
|
||||
} else if err := CloneForColumn(jellyfinDB, `Users`, `Username`, fromU, toU, `InternalId`); err != nil { // TODO how is InternalId maintained? Chance?
|
||||
log.Fatalf("%v", err)
|
||||
} else if n, err := SelectOne[int](jellyfinDB, `SELECT COUNT(*) FROM Users WHERE Username = $1`, toU); err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
|
|
@ -76,61 +77,73 @@ func main() {
|
|||
}
|
||||
log.Println(toU, toUUID, toID)
|
||||
|
||||
log.Fatalf("not impl get toU, toUUID")
|
||||
|
||||
tables, err := Tables(jellyfinDB)
|
||||
libraryDB, err := sql.Open("sqlite", path.Join(workd, libDB))
|
||||
if err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
defer libraryDB.Close()
|
||||
|
||||
for _, table := range tables {
|
||||
columns, err := Columns(jellyfinDB, table)
|
||||
for _, db := range []*sql.DB{jellyfinDB, libraryDB} {
|
||||
tables, err := Tables(db)
|
||||
if err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
log.Println(table, columns)
|
||||
|
||||
userColumns := slices.DeleteFunc(slices.Clone(columns), func(s string) bool {
|
||||
return !slices.Contains([]string{
|
||||
"user",
|
||||
"userid",
|
||||
}, strings.ToLower(s))
|
||||
})
|
||||
if len(userColumns) == 0 {
|
||||
continue
|
||||
}
|
||||
for _, table := range tables {
|
||||
columns, err := Columns(db, table)
|
||||
if err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
|
||||
for _, column := range userColumns {
|
||||
if n, err := SelectOne[int](jellyfinDB, fmt.Sprintf(`SELECT COUNT(*) FROM %q WHERE %q = $1`, table, column), fromID); err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
} else if n > 0 {
|
||||
if err := CloneForColumn(jellyfinDB, table, column, fromID, toID); err != nil {
|
||||
userColumns := slices.DeleteFunc(slices.Clone(columns), func(s string) bool {
|
||||
return !slices.Contains([]string{
|
||||
"user",
|
||||
"userid",
|
||||
}, strings.ToLower(s))
|
||||
})
|
||||
if len(userColumns) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, column := range userColumns {
|
||||
log.Println(table, column, "...")
|
||||
|
||||
if n, err := SelectOne[int](db, fmt.Sprintf(`SELECT COUNT(*) FROM %q WHERE %q = $1`, table, column), fromID); err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
} else if n, err := SelectOne[int](jellyfinDB, fmt.Sprintf(`SELECT COUNT(*) FROM %q WHERE %q = $1`, table, column), fromUUID); err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
} else if n > 0 {
|
||||
if err := CloneForColumn(jellyfinDB, table, column, fromUUID, toUUID); err != nil {
|
||||
} else if n > 0 {
|
||||
if err := CloneForColumn(db, table, column, fromID, toID); err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
} else if n, err := SelectOne[int](db, fmt.Sprintf(`SELECT COUNT(*) FROM %q WHERE %q = $1`, table, column), fromUUID); err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
} else if n > 0 {
|
||||
if err := CloneForColumn(db, table, column, fromUUID, toUUID); err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.Println(table, userColumns)
|
||||
log.Fatalf("not impl")
|
||||
}
|
||||
}
|
||||
|
||||
func CloneForColumn[T any](db *sql.DB, table, column string, from, to T, omit ...string) error {
|
||||
func CloneForColumn[T any](db *sql.DB, table, column string, from, to T, extraUniques ...string) error {
|
||||
columns, err := Columns(db, table)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO SELECT idx_name FROM PRAGMA_INDEX_LIST('Users') WHERE "unique" to get unique constraints
|
||||
// TODO SELECT col_name FROM PRAGMA_INDEX_INFO(idx_name);
|
||||
// TODO how is .Users.InternalId maintained? by chance?
|
||||
uniqueIntColumns, err := UniqueTypeColumns(db, "INTEGER", table)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
uniqueTextColumns, err := UniqueTypeColumns(db, "TEXT", table)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("unique text columns %+v, unique int columns %+v", uniqueTextColumns, uniqueIntColumns)
|
||||
|
||||
omit := append(append(extraUniques, uniqueIntColumns...), uniqueTextColumns...)
|
||||
notTheseColumns := slices.DeleteFunc(slices.Clone(columns), func(s string) bool { return s == column || slices.Contains(omit, s) })
|
||||
for i := range notTheseColumns {
|
||||
notTheseColumns[i] = fmt.Sprintf("%q", notTheseColumns[i])
|
||||
|
|
@ -140,7 +153,7 @@ func CloneForColumn[T any](db *sql.DB, table, column string, from, to T, omit ..
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
uuidGenColumns := slices.DeleteFunc(notNullTextColumns, func(s string) bool { return !slices.Contains(omit, s) })
|
||||
uuidGenColumns := slices.DeleteFunc(notNullTextColumns, func(s string) bool { return s == column || !slices.Contains(omit, s) })
|
||||
for i := range uuidGenColumns {
|
||||
uuidGenColumns[i] = fmt.Sprintf("%q", uuidGenColumns[i])
|
||||
}
|
||||
|
|
@ -154,34 +167,90 @@ func CloneForColumn[T any](db *sql.DB, table, column string, from, to T, omit ..
|
|||
incrGenColumns[i] = fmt.Sprintf("%q", incrGenColumns[i])
|
||||
}
|
||||
|
||||
q := fmt.Sprintf(
|
||||
`INSERT INTO %q (%q, %s) SELECT $1, %s %s %s FROM %q WHERE %q = $2`,
|
||||
table, column, strings.Join(append(incrGenColumns, append(uuidGenColumns, notTheseColumns...)...), ", "),
|
||||
func() string {
|
||||
incrs := make([]string, len(incrGenColumns))
|
||||
for i := range incrs {
|
||||
incrs[i] = fmt.Sprintf(`%s+100`, incrGenColumns[i])
|
||||
}
|
||||
s := strings.Join(incrs, ", ")
|
||||
if len(incrs) > 0 {
|
||||
s += ", "
|
||||
}
|
||||
return s
|
||||
}(),
|
||||
func() string {
|
||||
uuids := make([]string, len(uuidGenColumns))
|
||||
for i := range uuids {
|
||||
uuids[i] = fmt.Sprintf("'%s'", strings.ToUpper(uuid.New().String()))
|
||||
}
|
||||
s := strings.Join(uuids, ", ")
|
||||
if len(uuids) > 0 {
|
||||
s += ", "
|
||||
}
|
||||
return s
|
||||
}(), strings.Join(notTheseColumns, ", "), table, column,
|
||||
)
|
||||
log.Printf("EXEC | %s (%v, %v)", q, to, from)
|
||||
return Insert(db, q, to, from)
|
||||
return ForEach(db, func(args []any) error {
|
||||
selectMaxes := ""
|
||||
for _, col := range incrGenColumns {
|
||||
selectMaxes += fmt.Sprintf(`(SELECT COALESCE(MAX(%s)+1, 1) FROM %q), `, col, table)
|
||||
}
|
||||
|
||||
values := []any{}
|
||||
for _ = range uuidGenColumns {
|
||||
values = append(values, fmt.Sprintf("'%s'", strings.ToUpper(uuid.New().String())))
|
||||
}
|
||||
for _, arg := range args {
|
||||
values = append(values, *(arg.(*any)))
|
||||
}
|
||||
values = append(values, to)
|
||||
|
||||
q := fmt.Sprintf(
|
||||
`INSERT INTO %q (%s, %q) VALUES (%s %s)`,
|
||||
table, strings.Join(append(incrGenColumns, append(uuidGenColumns, notTheseColumns...)...), ", "), column,
|
||||
selectMaxes, strings.Join(slices.Repeat([]string{"?"}, len(values)), ", "),
|
||||
)
|
||||
|
||||
log.Printf("INSERT | %s (%+v)", q, values)
|
||||
return Exec(db, q, values...)
|
||||
}, fmt.Sprintf(`SELECT %s FROM %q WHERE %q = $1`, strings.Join(notTheseColumns, ", "), table, column), from)
|
||||
/*
|
||||
q := fmt.Sprintf(
|
||||
`INSERT INTO %q (%q, %s) SELECT $1, %s %s %s FROM %q WHERE %q = $2`,
|
||||
table, column, strings.Join(append(incrGenColumns, append(uuidGenColumns, notTheseColumns...)...), ", "),
|
||||
func() string {
|
||||
incrs := make([]string, len(incrGenColumns))
|
||||
for i := range incrs {
|
||||
incrs[i] = fmt.Sprintf(`(SELECT COALESCE(MAX(%s), 1) FROM %q)`, incrGenColumns[i], table)
|
||||
}
|
||||
s := strings.Join(incrs, ", ")
|
||||
if len(incrs) > 0 {
|
||||
s += ", "
|
||||
}
|
||||
return s
|
||||
}(),
|
||||
func() string {
|
||||
uuids := make([]string, len(uuidGenColumns))
|
||||
for i := range uuids {
|
||||
uuids[i] = fmt.Sprintf("'%s'", strings.ToUpper(uuid.New().String()))
|
||||
}
|
||||
s := strings.Join(uuids, ", ")
|
||||
if len(uuids) > 0 {
|
||||
s += ", "
|
||||
}
|
||||
return s
|
||||
}(), strings.Join(notTheseColumns, ", "), table, column,
|
||||
)
|
||||
log.Printf("EXEC | %s (%v, %v)", q, to, from)
|
||||
return Insert(db, q, to, from)
|
||||
*/
|
||||
}
|
||||
|
||||
func ForEach(db *sql.DB, cb func([]any) error, q string, args ...any) error {
|
||||
rows, err := db.Query(q, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
columns, err := rows.Columns()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cols := make([]any, len(columns))
|
||||
|
||||
for rows.Next() {
|
||||
for i := range cols {
|
||||
var a any
|
||||
cols[i] = &a
|
||||
}
|
||||
|
||||
if err := rows.Scan(cols...); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cb(cols); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return rows.Err()
|
||||
}
|
||||
|
||||
func cp(from, to string) error {
|
||||
|
|
@ -217,6 +286,41 @@ func NotNullIntColumns(db *sql.DB, table string) ([]string, error) {
|
|||
return Select[string](db, `SELECT name FROM PRAGMA_TABLE_INFO($1) WHERE "notnull" = 1 AND "type" = 'INTEGER'`, table)
|
||||
}
|
||||
|
||||
func UniqueTypeColumns(db *sql.DB, t, table string) ([]string, error) {
|
||||
pks, err := Select[string](db, `SELECT name FROM PRAGMA_TABLE_INFO($1) WHERE "pk" AND "type" = $2`, table, t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
idxes, err := Select[string](db, `SELECT name AS idx_name FROM PRAGMA_INDEX_LIST($1) WHERE "unique"`, table)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uniqueIdxCols := []string{}
|
||||
for _, idx := range idxes {
|
||||
cols, err := Select[string](db, `SELECT name FROM PRAGMA_INDEX_INFO($1)`, idx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(cols) > 1 {
|
||||
log.Printf("not impl: compound unique indexes like %s.%s's %+v", table, idx, cols)
|
||||
continue
|
||||
}
|
||||
col := cols[0]
|
||||
|
||||
if n, err := SelectOne[int](db, `SELECT COUNT(*) FROM PRAGMA_TABLE_INFO($1) WHERE "name" = $2 AND "type" = $3`, table, col, t); err != nil {
|
||||
return nil, err
|
||||
} else if n > 0 {
|
||||
uniqueIdxCols = append(uniqueIdxCols, col)
|
||||
}
|
||||
}
|
||||
|
||||
cols := append(pks, uniqueIdxCols...)
|
||||
slices.Sort(cols)
|
||||
cols = slices.Compact(cols)
|
||||
return cols, nil
|
||||
}
|
||||
|
||||
func Insert(db *sql.DB, q string, args ...any) error {
|
||||
_, err := db.Exec(q, args...)
|
||||
return err
|
||||
|
|
|
|||
Loading…
Reference in New Issue