// Select the raw data from the database into file_summary_by_instance_rows // - filter out empty values // - merge rows with the same name into a single row // - change FILE_NAME into a more descriptive value. func selectRows(dbh *sql.DB) Rows { var t Rows sql := ` SELECT OBJECT_SCHEMA, OBJECT_NAME, SUM_TIMER_WAIT, SUM_TIMER_READ, SUM_TIMER_WRITE, SUM_TIMER_READ_WITH_SHARED_LOCKS, SUM_TIMER_READ_HIGH_PRIORITY, SUM_TIMER_READ_NO_INSERT, SUM_TIMER_READ_NORMAL, SUM_TIMER_READ_EXTERNAL, SUM_TIMER_WRITE_ALLOW_WRITE, SUM_TIMER_WRITE_CONCURRENT_INSERT, SUM_TIMER_WRITE_LOW_PRIORITY, SUM_TIMER_WRITE_NORMAL, SUM_TIMER_WRITE_EXTERNAL FROM table_lock_waits_summary_by_table WHERE COUNT_STAR > 0` rows, err := dbh.Query(sql) if err != nil { log.Fatal(err) } defer rows.Close() for rows.Next() { var r Row var schema, table string if err := rows.Scan( &schema, &table, &r.sumTimerWait, &r.sumTimerRead, &r.sumTimerWrite, &r.sumTimerReadWithSharedLocks, &r.sumTimerReadHighPriority, &r.sumTimerReadNoInsert, &r.sumTimerReadNormal, &r.sumTimerReadExternal, &r.sumTimerWriteAllowWrite, &r.sumTimerWriteConcurrentInsert, &r.sumTimerWriteLowPriority, &r.sumTimerWriteNormal, &r.sumTimerWriteExternal); err != nil { log.Fatal(err) } r.name = lib.TableName(schema, table) // we collect all data as we may need it later t = append(t, r) } if err := rows.Err(); err != nil { log.Fatal(err) } return t }
func selectRows(dbh *sql.DB) Rows { var t Rows // we collect all information even if it's mainly empty as we may reference it later sql := "SELECT OBJECT_SCHEMA, OBJECT_NAME, COUNT_STAR, SUM_TIMER_WAIT, COUNT_READ, SUM_TIMER_READ, COUNT_WRITE, SUM_TIMER_WRITE, COUNT_FETCH, SUM_TIMER_FETCH, COUNT_INSERT, SUM_TIMER_INSERT, COUNT_UPDATE, SUM_TIMER_UPDATE, COUNT_DELETE, SUM_TIMER_DELETE FROM table_io_waits_summary_by_table WHERE SUM_TIMER_WAIT > 0" rows, err := dbh.Query(sql) if err != nil { log.Fatal(err) } defer rows.Close() for rows.Next() { var schema, table string var r Row if err := rows.Scan( &schema, &table, &r.countStar, &r.sumTimerWait, &r.countRead, &r.sumTimerRead, &r.countWrite, &r.sumTimerWrite, &r.countFetch, &r.sumTimerFetch, &r.countInsert, &r.sumTimerInsert, &r.countUpdate, &r.sumTimerUpdate, &r.countDelete, &r.sumTimerDelete); err != nil { log.Fatal(err) } r.name = lib.TableName(schema, table) // we collect all information even if it's mainly empty as we may reference it later t = append(t, r) } if err := rows.Err(); err != nil { log.Fatal(err) } return t }
// From the original name we want to generate a simpler name to use. // This simpler name may also merge several different filenames into one. func (row Row) simplifyName(globalVariables *global.Variables) string { path := row.name if cachedResult, err := cache.Get(path); err == nil { return cachedResult } // @0024 --> $ (should do this more generically) path = reDollar.ReplaceAllLiteralString(path, "$") // this should probably be ordered from most expected regexp to least if m1 := reTableFile.FindStringSubmatch(path); m1 != nil { // we may match temporary tables so check for them if m2 := reTempTable.FindStringSubmatch(m1[2]); m2 != nil { return cache.Put(path, "<temp_table>") } // we may match partitioned tables so check for them if m3 := rePartTable.FindStringSubmatch(m1[2]); m3 != nil { return cache.Put(path, lib.TableName(m1[1], m3[1])) // <schema>.<table> (less partition info) } return cache.Put(path, rc.Munge(lib.TableName(m1[1], m1[2]))) // <schema>.<table> } if reIbtmp.MatchString(path) { return cache.Put(path, "<ibtmp>") } if reIbdata.MatchString(path) { return cache.Put(path, "<ibdata>") } if reRedoLog.MatchString(path) { return cache.Put(path, "<redo_log>") } if reBinlog.MatchString(path) { return cache.Put(path, "<binlog>") } if reDbOpt.MatchString(path) { return cache.Put(path, "<db_opt>") } if reSlowlog.MatchString(path) { return cache.Put(path, "<slow_log>") } if reAutoCnf.MatchString(path) { return cache.Put(path, "<auto_cnf>") } // relay logs are a bit complicated. If a full path then easy to // identify, but if a relative path we may need to add $datadir, // but also if as I do we have a ../blah/somewhere/path then we // need to make it match too. if len(globalVariables.Get("relay_log")) > 0 { relayLog := globalVariables.Get("relay_log") if relayLog[0] != '/' { // relative path relayLog = cleanupPath(globalVariables.Get("datadir") + relayLog) // datadir always ends in / } reRelayLog := relayLog + `\.(\d{6}|index)$` if regexp.MustCompile(reRelayLog).MatchString(path) { return cache.Put(path, "<relay_log>") } } if rePidFile.MatchString(path) { return cache.Put(path, "<pid_file>") } if reErrorMsg.MatchString(path) { return cache.Put(path, "<errmsg>") } if reCharset.MatchString(path) { return cache.Put(path, "<charset>") } // clean up datadir to <datadir> if len(globalVariables.Get("datadir")) > 0 { reDatadir := regexp.MustCompile("^" + globalVariables.Get("datadir")) path = reDatadir.ReplaceAllLiteralString(path, "<datadir>/") } return cache.Put(path, path) }