package gui // //import ( // "database/sql" // "fmt" // "github.com/sirupsen/logrus" // "reflect" // "shiny-pancake/internal/database" // "shiny-pancake/logger" // "strconv" // "sync" // "time" // // "github.com/gdamore/tcell/v2" // _ "github.com/lib/pq" // "github.com/rivo/tview" //) // //const ( // batchSize = 80 // The number of rows loaded per batch. // finderPage = "*finder*" // The name of the Finder page. //) // //var ( // app *tview.Application // The tview application. // panels *tview.Pages // The application panels. // finderFocus tview.Primitive // The primitive in the Finder that last had focus. //) // //// Main entry point. //func data(host database.HostDetails) { // var h string // var u string // var pw string // var dbn string // var psqlInfo string // h = host.Hostname // u = host.Username // pw = host.Secret // dbn = host.DatabaseName // // Start the application. // psqlInfo = fmt.Sprintf("host=%s port=%d user=%s "+ // "password=%s dbname=%s sslmode=disable", // h, 5432, u, pw, dbn) // app = tview.NewApplication() // finder(psqlInfo) // if err := app.Run(); err != nil { // fmt.Printf("Error running application: %s\n", err) // } //} // //// Sets up a "Finder" used to navigate the databases, tables, and columns. //func finder(connString string) { // // Create the basic objects. // databases := tview.NewList().ShowSecondaryText(false) // databases.SetBorder(true).SetTitle("Databases") // columns := tview.NewTable().SetBorders(true) // columns.SetBorder(true).SetTitle("Columns") // tables := tview.NewList() // tables.ShowSecondaryText(false). // SetDoneFunc(func() { // tables.Clear() // columns.Clear() // app.SetFocus(databases) // }) // tables.SetBorder(true).SetTitle("Tables") // // // Create the layout. // flex := tview.NewFlex(). // AddItem(databases, 0, 1, true). // AddItem(tables, 0, 1, false). // AddItem(columns, 0, 3, false) // // // We keep one connection pool per database. // dbMutex := sync.Mutex{} // dbs := make(map[string]*sql.DB) // getDB := func(database string) *sql.DB { // // Connect to a new database. // dbMutex.Lock() // defer dbMutex.Unlock() // if db, ok := dbs[database]; ok { // return db // } // db, err := sql.Open("postgres", connString) // if err != nil { // panic(err) // } // dbs[database] = db // return db // } // // // Get a list of all databases. // generalDB, err := sql.Open("postgres", connString) // if err != nil { // panic(err) // } // defer generalDB.Close() // We really close the DB because we only use it for this one query. // rows, err := generalDB.Query("select datname from pg_database where datistemplate = false") // if err != nil { // panic(err) // } // defer rows.Close() // for rows.Next() { // var dbName string // if err := rows.Scan(&dbName); err != nil { // panic(err) // } // databases.AddItem(dbName, "", 0, func() { // // A database was selected. Show all of its tables. // columns.Clear() // tables.Clear() // db := getDB(dbName) // t, err := db.Query("select table_name from information_schema.tables where table_schema not in ('information_schema','pg_catalog')") // if err != nil { // panic(err) // } // defer t.Close() // for t.Next() { // var tableName string // if err := t.Scan(&tableName); err != nil { // panic(err) // } // tables.AddItem(tableName, "", 0, nil) // } // if err := t.Err(); err != nil { // panic(err) // } // app.SetFocus(tables) // // // When the user navigates to a table, show its columns. // tables.SetChangedFunc(func(i int, tableName string, t string, s rune) { // // A table was selected. Show its columns. // columns.Clear() // c, err := db.Query(` // select // c.column_name, // c.is_nullable, // c.data_type, // c.character_maximum_length, // c.numeric_precision, // c.numeric_scale, // c.ordinal_position, // tc.constraint_type pkey // from // information_schema.columns c // left join // information_schema.constraint_column_usage as ccu // on // c.table_schema = ccu.table_schema // and c.table_name = ccu.table_name // and c.column_name = ccu.column_name // left join // information_schema.table_constraints as tc // on // ccu.constraint_schema = tc.constraint_schema // and ccu.constraint_name = tc.constraint_name // where // c.table_schema not in ('information_schema','pg_catalog') // and c.table_name = $1 // `, tableName) // if err != nil { // var l logger.Log // l = logger.Log{"error", logrus.Fields{"database": err.Error()}, "an error occured in view."} // logger.Lgr(&l) // } // defer c.Close() // columns.SetCell(0, 0, &tview.TableCell{Text: "Name", Align: tview.AlignCenter, Color: tcell.ColorYellow}). // SetCell(0, 1, &tview.TableCell{Text: "Type", Align: tview.AlignCenter, Color: tcell.ColorYellow}). // SetCell(0, 2, &tview.TableCell{Text: "Size", Align: tview.AlignCenter, Color: tcell.ColorYellow}). // SetCell(0, 3, &tview.TableCell{Text: "Null", Align: tview.AlignCenter, Color: tcell.ColorYellow}). // SetCell(0, 4, &tview.TableCell{Text: "Constraint", Align: tview.AlignCenter, Color: tcell.ColorYellow}) // for c.Next() { // var ( // columnName, isNullable, dataType string // constraintType sql.NullString // size, numericPrecision, numericScale sql.NullInt64 // ordinalPosition int // ) // if err := c.Scan(&columnName, // &isNullable, // &dataType, // &size, // &numericPrecision, // &numericScale, // &ordinalPosition, // &constraintType, // ); err != nil { // panic(err) // } // sizeText := "" // if size.Valid { // sizeText = strconv.Itoa(int(size.Int64)) // } else if numericPrecision.Valid { // sizeText = strconv.Itoa(int(numericPrecision.Int64)) // if numericScale.Valid { // sizeText += "," + strconv.Itoa(int(numericScale.Int64)) // } // } // color := tcell.ColorWhite // if constraintType.Valid { // color = map[string]tcell.Color{ // "CHECK": tcell.ColorGreen, // "FOREIGN KEY": tcell.ColorDarkMagenta, // "PRIMARY KEY": tcell.ColorRed, // "UNIQUE": tcell.ColorDarkCyan, // }[constraintType.String] // } // columns.SetCell(ordinalPosition, 0, &tview.TableCell{Text: columnName, Color: color}). // SetCell(ordinalPosition, 1, &tview.TableCell{Text: dataType, Color: color}). // SetCell(ordinalPosition, 2, &tview.TableCell{Text: sizeText, Align: tview.AlignRight, Color: color}). // SetCell(ordinalPosition, 3, &tview.TableCell{Text: isNullable, Align: tview.AlignRight, Color: color}). // SetCell(ordinalPosition, 4, &tview.TableCell{Text: constraintType.String, Align: tview.AlignLeft, Color: color}) // } // if err := c.Err(); err != nil { // panic(err) // } // }) // tables.SetCurrentItem(0) // Trigger the initial selection. // // // When the user selects a table, show its content. // tables.SetSelectedFunc(func(i int, tableName string, t string, s rune) { // content(db, dbName, tableName) // }) // }) // } // if err := rows.Err(); err != nil { // panic(err) // } // // // Set up the panels and show the Finder. // panels = tview.NewPages(). // AddPage(finderPage, flex, true, true) // app.SetRoot(panels, true) //} // //// Shows the contents of the given table. //func content(db *sql.DB, dbName, tableName string) { // finderFocus = app.GetFocus() // // // If this page already exists, just show it. // if panels.HasPage(dbName + "." + tableName) { // panels.SwitchToPage(dbName + "." + tableName) // return // } // var l logger.Log // l = logger.Log{"info", logrus.Fields{"database": tableName}, "selected table to view"} // logger.Lgr(&l) // var schemaName string // err := db.QueryRow(fmt.Sprintf("select table_schema from information_schema.tables where table_schema not in ('information_schema','pg_catalog') and table_name = '%s'", tableName)).Scan(&schemaName) // if err != nil { // panic(err) // } // // // We display the data in a table embedded in a frame. // table := tview.NewTable(). // SetFixed(1, 0). // SetBordersColor(tcell.ColorYellow) // frame := tview.NewFrame(table). // SetBorders(0, 0, 0, 0, 0, 0) // frame.SetBorder(true). // SetTitle(fmt.Sprintf(`Contents of table "%s.%s"`, schemaName, tableName)) // // // How many rows does this table have? // var rowCount int // err = db.QueryRow(fmt.Sprintf("select count(*) from %s.%s", schemaName, tableName)).Scan(&rowCount) // if err != nil { // panic(err) // } // // // Load a batch of rows. // loadRows := func(offset int) { // rows, err := db.Query(fmt.Sprintf("select * from %s.%s limit $1 offset $2", schemaName, tableName), batchSize, offset) // if err != nil { // panic(err) // } // defer rows.Close() // // // The first row in the table is the list of column names. // columnNames, err := rows.Columns() // if err != nil { // panic(err) // } // for index, name := range columnNames { // table.SetCell(0, index, &tview.TableCell{Text: name, Align: tview.AlignCenter, Color: tcell.ColorYellow}) // } // // // Read the rows. // columns := make([]interface{}, len(columnNames)) // columnPointers := make([]interface{}, len(columns)) // for index := range columnPointers { // columnPointers[index] = &columns[index] // } // for rows.Next() { // // Read the columns. // err := rows.Scan(columnPointers...) // if err != nil { // panic(err) // } // // // Transfer them to the table. // row := table.GetRowCount() // for index, column := range columns { // switch value := column.(type) { // case int64: // table.SetCell(row, index, &tview.TableCell{Text: strconv.Itoa(int(value)), Align: tview.AlignRight, Color: tcell.ColorDarkCyan}) // case float64: // table.SetCell(row, index, &tview.TableCell{Text: strconv.FormatFloat(value, 'f', 2, 64), Align: tview.AlignRight, Color: tcell.ColorDarkCyan}) // case string: // table.SetCellSimple(row, index, value) // case time.Time: // t := value.Format("2006-01-02") // table.SetCell(row, index, &tview.TableCell{Text: t, Align: tview.AlignRight, Color: tcell.ColorDarkMagenta}) // case []uint8: // str := make([]byte, len(value)) // for index, num := range value { // str[index] = byte(num) // } // table.SetCell(row, index, &tview.TableCell{Text: string(str), Align: tview.AlignRight, Color: tcell.ColorGreen}) // case nil: // table.SetCell(row, index, &tview.TableCell{Text: "NULL", Align: tview.AlignCenter, Color: tcell.ColorRed}) // default: // // We've encountered a type that we don't know yet. // t := reflect.TypeOf(value) // str := "?nil?" // if t != nil { // str = "?" + t.String() + "?" // } // table.SetCellSimple(row, index, str) // } // } // } // if err := rows.Err(); err != nil { // panic(err) // } // // // Show how much we've loaded. // frame.Clear() // loadMore := "" // if table.GetRowCount()-1 < rowCount { // loadMore = " - press Enter to load more" // } // loadMore = fmt.Sprintf("Loaded %d of %d rows%s. esc to return to previous menu", table.GetRowCount()-1, rowCount, loadMore) // frame.AddText(loadMore, false, tview.AlignCenter, tcell.ColorYellow) // } // // // Load the first batch of rows. // loadRows(0) // // // Handle key presses. // table.SetDoneFunc(func(key tcell.Key) { // switch key { // case tcell.KeyEscape: // // Go back to Finder. // panels.SwitchToPage(finderPage) // if finderFocus != nil { // app.SetFocus(finderFocus) // } // case tcell.KeyEnter: // // Load the next batch of rows. // loadRows(table.GetRowCount() - 1) // table.ScrollToEnd() // } // }) // // // Add a new page and show it. // panels.AddPage(dbName+"."+tableName, frame, true, true) //}