Tests passed with MySQL 5.7

This commit is contained in:
Carlos Salguero
2019-09-03 15:26:26 -03:00
parent 203f258031
commit 9190b5e6ac
40 changed files with 2049 additions and 117 deletions

67
Gopkg.lock generated
View File

@@ -128,12 +128,24 @@
version = "v0.1.0"
[[projects]]
digest = "1:0093a7c66d5b9e0cdaf4be5c20e0a9b889d1d839148eeed1d587e99b4cfd90ff"
digest = "1:f4216047c24ab66fb757045febd7dac4edc6f4ad9f6c0063d0755d654d04f25e"
name = "github.com/lib/pq"
packages = [
".",
"oid",
"scram",
]
pruneopts = ""
revision = "3427c32cb71afc948325f299f040e53c1dd78979"
version = "v1.2.0"
[[projects]]
digest = "1:d5d962b7a8d95b9e6226f6b831a7e50237acf1730dcace6e8cb87c6dd628ef54"
name = "github.com/mattn/go-shellwords"
packages = ["."]
pruneopts = ""
revision = "a72fbe27a1b0ed0df2f02754945044ce1456608b"
version = "v1.0.5"
revision = "36a9b3c57cb5caa559ff63fb7e9b585f1c00df75"
version = "v1.0.6"
[[projects]]
digest = "1:a067513044dc491395a58f56f39cedddb5ad35789b832b570c283a64d712f81b"
@@ -159,14 +171,6 @@
pruneopts = ""
revision = "c5d0b4a3add9c9bf5bb26b2ab823289e395a3f98"
[[projects]]
digest = "1:16b4510ba61ab0bb7a4e694ea6396a7b2879f5fabb21e93066e182691f790173"
name = "github.com/percona/pmgo"
packages = ["."]
pruneopts = ""
revision = "497d06e28f910fbe26d5d60f59d36284a6901c6f"
version = "0.5.2"
[[projects]]
digest = "1:1d7e1867c49a6dd9856598ef7c3123604ea3daabf5b83f303ff457bcbc410b1d"
name = "github.com/pkg/errors"
@@ -270,22 +274,14 @@
[[projects]]
branch = "master"
digest = "1:086760278d762dbb0e9a26e09b57f04c89178c86467d8d94fae47d64c222f328"
digest = "1:a530f8e0c0ee8a3b440f9f0b0e9f4e5d5e47cfe3a581086ce32cd8ba114ddf4f"
name = "golang.org/x/crypto"
packages = [
"pbkdf2",
"ssh/terminal",
]
pruneopts = ""
revision = "4def268fd1a49955bfb3dda92fe3db4f924f2285"
[[projects]]
branch = "master"
digest = "1:955694a7c42527d7fb188505a22f10b3e158c6c2cf31fe64b1e62c9ab7b18401"
name = "golang.org/x/net"
packages = ["context"]
pruneopts = ""
revision = "ca1201d0de80cfde86cb01aea620983605dfe99b"
revision = "9756ffdc24725223350eb3266ffb92590d28f278"
[[projects]]
branch = "master"
@@ -297,14 +293,14 @@
[[projects]]
branch = "master"
digest = "1:0b5c2207c72f2d13995040f176feb6e3f453d6b01af2b9d57df76b05ded2e926"
digest = "1:d9222a165eab05f8b8f085c68fdee5ecc670f4f834e3ecbac6069dd3b768a6b3"
name = "golang.org/x/sys"
packages = [
"unix",
"windows",
]
pruneopts = ""
revision = "51ab0e2deafac1f46c46ad59cf0921be2f180c3d"
revision = "c7b8b68b14567162c6602a7c5659ee0f26417c18"
[[projects]]
digest = "1:740b51a55815493a8d0f2b1e0d0ae48fe48953bf7eaf3fcc4198823bf67768c0"
@@ -321,29 +317,6 @@
revision = "342b2e1fbaa52c93f31447ad2c6abc048c63e475"
version = "v0.3.2"
[[projects]]
branch = "v2"
digest = "1:f54ba71a035aac92ced3e902d2bff3734a15d1891daff73ec0f90ef236750139"
name = "gopkg.in/mgo.v2"
packages = [
".",
"bson",
"dbtest",
"internal/json",
"internal/sasl",
"internal/scram",
]
pruneopts = ""
revision = "9856a29383ce1c59f308dd1cf0363a79b5bef6b5"
[[projects]]
branch = "v2"
digest = "1:61a650a53e5e865a91ae9581f02990a4b6e3afcb8d280f19b1e67a3c284944e6"
name = "gopkg.in/tomb.v2"
packages = ["."]
pruneopts = ""
revision = "d5d1b5820637886def9eef33e03a27a9f166942c"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
@@ -355,11 +328,11 @@
"github.com/hashicorp/go-version",
"github.com/howeyc/gopass",
"github.com/kr/pretty",
"github.com/lib/pq",
"github.com/mattn/go-shellwords",
"github.com/montanaflynn/stats",
"github.com/pborman/getopt",
"github.com/percona/go-mysql/query",
"github.com/percona/pmgo",
"github.com/pkg/errors",
"github.com/satori/go.uuid",
"github.com/shirou/gopsutil/process",

View File

@@ -29,6 +29,7 @@ use English qw(-no_match_vars);
use constant PTDEBUG => $ENV{PTDEBUG} || 0;
use Data::Dumper;
use Carp;
$Data::Dumper::Indent = 1;
$Data::Dumper::Sortkeys = 1;
$Data::Dumper::Quotekeys = 0;
@@ -41,7 +42,7 @@ sub new {
}
my $self = {
Quoter => $args{Quoter},
Quoter => $args{Quoter},
};
return bless $self, $class;
@@ -98,13 +99,21 @@ sub _get_first_values {
}
my ($cxn, $tbl, $index, $n_index_cols) = @args{@required_args};
my $q = $self->{Quoter};
my $q = $self->{quoter};
# Select just the index columns.
my $index_struct = $tbl->{tbl_struct}->{keys}->{$index};
my $index_cols = $index_struct->{cols};
my $index_columns = join (', ',
my $index_columns;
eval {
$index_columns = join (', ',
map { $q->quote($_) } @{$index_cols}[0..($n_index_cols - 1)]);
};
if ($EVAL_ERROR) {
confess "$EVAL_ERROR";
}
# Where no index column is null, because we can't > NULL.
my @where;

View File

@@ -1,3 +1,6 @@
AWS_ACCESS_KEY_ID=AKIAJQ2GZPAJ3JZS52HQ
AWS_SECRET_ACCESS_KEY=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
GOCACHE=
GOLANG_DOCKERHUB_TAG=1.10-stretch
TEST_MONGODB_ADMIN_USERNAME=admin
TEST_MONGODB_ADMIN_PASSWORD=admin123456

View File

@@ -11,6 +11,7 @@ import (
"time"
"github.com/Masterminds/semver"
"github.com/kr/pretty"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
@@ -157,6 +158,8 @@ func TestExplain(t *testing.T) {
if err != nil {
t.Fatalf("cannot load sample %s: %s", dir+file.Name(), err)
}
pretty.Println(eq)
query, err := bson.MarshalExtJSON(eq, true, true)
if err != nil {
t.Fatalf("cannot marshal json %s: %s", dir+file.Name(), err)

1
src/go/pt-pg-summary/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
dist

View File

@@ -0,0 +1,37 @@
# This is an example goreleaser.yaml file with some sane defaults.
# Make sure to check the documentation at http://goreleaser.com
before:
hooks:
# you may remove this if you don't use vgo
# - go mod download
# you may remove this if you don't need go generate
- go generate ./...
builds:
-
binary: pt-pg-summary
env:
- CGO_ENABLED=0
goos:
- linux
goarch:
- amd64
ignore:
- goos: darwin
- goarch: 386
archive:
replacements:
darwin: Darwin
linux: Linux
windows: Windows
386: i386
amd64: x86_64
checksum:
name_template: 'checksums.txt'
snapshot:
name_template: "{{ .Tag }}"
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'

View File

@@ -0,0 +1,26 @@
version: '2.2'
services:
postgres9:
image: ${MYSQL_IMAGE:-postgres:9.6}
ports:
- ${POSTGRE_HOST:-127.0.0.1}:${POSTGRE_96_PORT:-6432}:5432
environment:
- POSTGRES_PASSWORD=root
postgres10:
image: ${POSTGRE_IMAGE:-postgres:10.7}
ports:
- ${POSTGRE_HOST:-127.0.0.1}:${POSTGRE_10_PORT:-6433}:5432
environment:
- POSTGRES_PASSWORD=root
postgres11:
image: ${POSTGRE_IMAGE:-postgres:11}
ports:
- ${POSTGRE_HOST:-127.0.0.1}:${POSTGRE_11_PORT:-6434}:5432
environment:
- POSTGRES_PASSWORD=root
postgres12:
image: ${POSTGRE_IMAGE:-postgres:12}
ports:
- ${POSTGRE_HOST:-127.0.0.1}:${POSTGRE_12_PORT:-6435}:5432
environment:
- POSTGRES_PASSWORD=root

View File

@@ -0,0 +1,79 @@
package tu // test utils
import (
"log"
"os"
"os/exec"
"strings"
)
const (
ipv4Host = "127.0.0.1"
ipv6Host = "::1"
username = "postgres"
password = "root"
ipv4PG9Port = "6432"
ipv4PG10Port = "6433"
ipv4PG11Port = "6434"
ipv4PG12Port = "6435"
ipv6PG9Port = "6432"
ipv6PG10Port = "6432"
ipv6PG11Port = "6432"
ipv6PG12Port = "6432"
pg9Container = "pt-pg-summary_postgres9_1"
pg10Container = "pt-pg-summary_postgres10_1"
pg11Container = "pt-pg-summary_postgres11_1"
pg12Container = "pt-pg-summary_postgres12_1"
)
var (
// IPv4Host env(PG_IPV4_HOST) or 127.0.0.1
IPv4Host = getVar("PG_IPV4_HOST", ipv4Host)
// IPv6Host env(PG_IPV6_HOST) or ::1
IPv6Host = getVar("PG_IPV6_HOST", ipv6Host)
// Password env(PG_PASSWORD) or root
Password = getVar("PG_PASSWORD", password)
// Username env(PG_USERNAME) or PG
Username = getVar("PG_USERNAME", username)
IPv4PG9Port = getVar("PG_IPV4_9_PORT", ipv4PG9Port)
IPv4PG10Port = getVar("PG_IPV4_10_PORT", ipv4PG10Port)
IPv4PG11Port = getVar("PG_IPV4_11_PORT", ipv4PG11Port)
IPv4PG12Port = getVar("PG_IPV4_12_PORT", ipv4PG12Port)
IPv6PG9Port = getVar("PG_IPV6_9_PORT", ipv6PG9Port)
IPv6PG10Port = getVar("PG_IPV6_10_PORT", ipv6PG10Port)
IPv6PG11Port = getVar("PG_IPV6_11_PORT", ipv6PG11Port)
IPv6PG12Port = getVar("PG_IPV6_12_PORT", ipv6PG12Port)
PG9DockerIP = getContainerIP(pg9Container)
PG10DockerIP = getContainerIP(pg9Container)
PG11DockerIP = getContainerIP(pg9Container)
PG12DockerIP = getContainerIP(pg9Container)
DefaultPGPort = "5432"
)
func getVar(varname, defaultValue string) string {
if v := os.Getenv(varname); v != "" {
return v
}
return defaultValue
}
func getContainerIP(container string) string {
cmd := []string{"docker", "inspect", "-f", "'{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'", container}
out, err := exec.Command(cmd[0], cmd[1:]...).Output()
if err != nil {
log.Fatalf("error getting IP address of %q container: %s", container, err)
}
ip := strings.TrimSpace(string(out))
if ip == "" {
log.Fatalf("error getting IP address of %q container (empty)", container)
}
return ip
}

View File

@@ -0,0 +1,239 @@
package main
import (
"database/sql"
"fmt"
"os"
"strings"
"text/template"
"github.com/alecthomas/kingpin"
"github.com/percona/percona-toolkit/src/go/lib/pginfo"
"github.com/percona/percona-toolkit/src/go/pt-pg-summary/templates"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
log "github.com/sirupsen/logrus"
_ "github.com/lib/pq"
)
var (
version = "dev"
commit = "none"
date = "unknown"
)
type connOpts struct {
Host string
Port int
User string
Password string
DisableSSL bool
}
type cliOptions struct {
app *kingpin.Application
connOpts connOpts
Config string
DefaultsFile string
ReadSamples string
SaveSamples string
Databases []string
Seconds int
AllDatabases bool
AskPass bool
ListEncryptedTables bool
Verbose bool
Debug bool
}
func main() {
opts, err := parseCommandLineOpts(os.Args[1:])
if err != nil {
fmt.Printf("Cannot parse command line arguments: %s", err)
os.Exit(1)
}
logger := logrus.New()
if opts.Verbose {
logger.SetLevel(logrus.InfoLevel)
}
if opts.Debug {
logger.SetLevel(logrus.DebugLevel)
}
dsn := buildConnString(opts.connOpts, "postgres")
logger.Infof("Connecting to the database server using: %s", safeConnString(opts.connOpts, "postgres"))
db, err := connect(dsn)
if err != nil {
logger.Errorf("Cannot connect to the database: %s\n", err)
opts.app.Usage(os.Args[1:])
os.Exit(1)
}
logger.Infof("Connection OK")
info, err := pginfo.NewWithLogger(db, opts.Databases, opts.Seconds, logger)
if err != nil {
log.Fatalf("Cannot create a data collector instance: %s", err)
}
logger.Info("Getting global information")
errs := info.CollectGlobalInfo(db)
if len(errs) > 0 {
logger.Errorf("Cannot collect info")
for _, err := range errs {
logger.Error(err)
}
}
logger.Info("Collecting per database information")
logger.Debugf("Will collect information for these databases: (%T), %v", info.DatabaseNames(), info.DatabaseNames())
for _, dbName := range info.DatabaseNames() {
dsn := buildConnString(opts.connOpts, dbName)
logger.Infof("Connecting to the %q database", dbName)
conn, err := connect(dsn)
if err != nil {
logger.Errorf("Cannot connect to the %s database: %s", dbName, err)
continue
}
if err := info.CollectPerDatabaseInfo(conn, dbName); err != nil {
logger.Errorf("Cannot collect information for the %s database: %s", dbName, err)
}
conn.Close()
}
masterTmpl, err := template.New("master").Funcs(funcsMap()).Parse(templates.TPL)
if err != nil {
log.Fatal(err)
}
if err := masterTmpl.ExecuteTemplate(os.Stdout, "report", info); err != nil {
log.Fatal(err)
}
}
func connect(dsn string) (*sql.DB, error) {
db, err := sql.Open("postgres", dsn)
if err != nil {
return nil, errors.Wrap(err, "cannot connect to the database")
}
if err := db.Ping(); err != nil {
return nil, errors.Wrap(err, "cannot connect to the database")
}
return db, nil
}
func funcsMap() template.FuncMap {
return template.FuncMap{
"trim": func(s string, size int) string {
if len(s) < size {
return s
}
return s[:size]
},
}
}
func buildConnString(opts connOpts, dbName string) string {
parts := []string{}
if opts.Host != "" {
parts = append(parts, fmt.Sprintf("host=%s", opts.Host))
}
if opts.Port != 0 {
parts = append(parts, fmt.Sprintf("port=%d", opts.Port))
}
if opts.User != "" {
parts = append(parts, fmt.Sprintf("user=%s", opts.User))
}
if opts.Password != "" {
parts = append(parts, fmt.Sprintf("password=%s", opts.Password))
}
if opts.DisableSSL {
parts = append(parts, "sslmode=disable")
}
if dbName == "" {
dbName = "postgres"
}
parts = append(parts, fmt.Sprintf("dbname=%s", dbName))
return strings.Join(parts, " ")
}
// build the same connection string as buildConnString but the password is hidden so
// we can display this in the logs
func safeConnString(opts connOpts, dbName string) string {
parts := []string{}
if opts.Host != "" {
parts = append(parts, fmt.Sprintf("host=%s", opts.Host))
}
if opts.Port != 0 {
parts = append(parts, fmt.Sprintf("port=%d", opts.Port))
}
if opts.User != "" {
parts = append(parts, fmt.Sprintf("user=%s", opts.User))
}
if opts.Password != "" {
parts = append(parts, "password=******")
}
if opts.DisableSSL {
parts = append(parts, "sslmode=disable")
}
if dbName == "" {
dbName = "postgres"
}
parts = append(parts, fmt.Sprintf("dbname=%s", dbName))
return strings.Join(parts, " ")
}
func parseCommandLineOpts(args []string) (cliOptions, error) {
app := kingpin.New("pt-pg-summary", "Percona Toolkit - PostgreSQL Summary")
// version, commit and date will be set at build time by the compiler -ldflags param
app.Version(fmt.Sprintf("%s version %s, git commit %s, date: %s", app.Name, version, commit, date))
opts := cliOptions{app: app}
app.Flag("ask-pass", "Prompt for a password when connecting to PostgreSQL").
Hidden().BoolVar(&opts.AskPass) // hidden because it is not implemented yet
app.Flag("config", "Config file").
Hidden().StringVar(&opts.Config) // hidden because it is not implemented yet
app.Flag("databases", "Summarize this comma-separated list of databases. All if not specified").
StringsVar(&opts.Databases)
app.Flag("defaults-file", "Only read PostgreSQL options from the given file").
Hidden().StringVar(&opts.DefaultsFile) // hidden because it is not implemented yet
app.Flag("host", "Host to connect to").
Short('h').
StringVar(&opts.connOpts.Host)
app.Flag("list-encrypted-tables", "Include a list of the encrypted tables in all databases").
Hidden().BoolVar(&opts.ListEncryptedTables)
app.Flag("password", "Password to use when connecting").
Short('W').
StringVar(&opts.connOpts.Password)
app.Flag("port", "Port number to use for connection").
Short('p').
IntVar(&opts.connOpts.Port)
app.Flag("read-samples", "Create a report from the files found in this directory").
Hidden().StringVar(&opts.ReadSamples) // hidden because it is not implemented yet
app.Flag("save-samples", "Save the data files used to generate the summary in this directory").
Hidden().StringVar(&opts.SaveSamples) // hidden because it is not implemented yet
app.Flag("sleep", "Seconds to sleep when gathering status counters").
Default("10").IntVar(&opts.Seconds)
app.Flag("username", "User for login if not current user").
Short('U').
StringVar(&opts.connOpts.User)
app.Flag("disable-ssl", "Diable SSL for the connection").
Default("true").BoolVar(&opts.connOpts.DisableSSL)
app.Flag("verbose", "Show verbose log").
Default("false").BoolVar(&opts.Verbose)
app.Flag("debug", "Show debug information in the logs").
Default("false").BoolVar(&opts.Debug)
_, err := app.Parse(args)
dbs := []string{}
for _, databases := range opts.Databases {
ds := strings.Split(databases, ",")
dbs = append(dbs, ds...)
}
opts.Databases = dbs
return opts, err
}

View File

@@ -0,0 +1,44 @@
package main
import (
"fmt"
"os"
"testing"
"github.com/percona/percona-toolkit/src/go/pt-pg-summary/internal/tu"
)
func TestMain(m *testing.M) {
os.Exit(m.Run())
}
func TestConnection(t *testing.T) {
tests := []struct {
name string
host string
port string
username string
password string
}{
{"IPv4PG9", tu.IPv4Host, tu.IPv4PG9Port, tu.Username, tu.Password},
{"IPv4PG10", tu.IPv4Host, tu.IPv4PG10Port, tu.Username, tu.Password},
{"IPv4PG11", tu.IPv4Host, tu.IPv4PG11Port, tu.Username, tu.Password},
{"IPv4PG12", tu.IPv4Host, tu.IPv4PG12Port, tu.Username, tu.Password},
// use IPV6 for PostgreSQL 9
//{"IPV6", tu.IPv6Host, tu.IPv6PG9Port, tu.Username, tu.Password},
// use an "external" IP to simulate a remote host
{"remote_host", tu.PG9DockerIP, tu.DefaultPGPort, tu.Username, tu.Password},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
dsn := fmt.Sprintf("host=%s port=%s user=%s password=%s sslmode=disable dbname=%s",
test.host, test.port, test.username, test.password, "postgres")
if _, err := connect(dsn); err != nil {
t.Errorf("Cannot connect to the db using %q: %s", dsn, err)
}
})
}
}

View File

@@ -0,0 +1,43 @@
// Package models contains the types for schema 'public'.
package models
// Code generated by xo. DO NOT EDIT.
// AllDatabases represents a row from '[custom all_databases]'.
type AllDatabases struct {
Datname Name // datname
}
// GetAllDatabases runs a custom query, returning results as AllDatabases.
func GetAllDatabases(db XODB) ([]*AllDatabases, error) {
var err error
// sql query
var sqlstr = `SELECT datname ` +
`FROM pg_database ` +
`WHERE datistemplate = false`
// run query
XOLog(sqlstr)
q, err := db.Query(sqlstr)
if err != nil {
return nil, err
}
defer q.Close()
// load results
res := []*AllDatabases{}
for q.Next() {
ad := AllDatabases{}
// scan
err = q.Scan(&ad.Datname)
if err != nil {
return nil, err
}
res = append(res, &ad)
}
return res, nil
}

View File

@@ -0,0 +1,59 @@
// Package models contains the types for schema 'public'.
package models
// Code generated by xo. DO NOT EDIT.
import (
"database/sql"
"time"
)
// Cluster info
type ClusterInfo struct {
Usename string // usename
Time time.Time // time
ClientAddr string // client_addr
ClientHostname sql.NullString // client_hostname
Version string // version
Started time.Time // started
IsSlave bool // is_slave
}
// GetClusterInfos runs a custom query, returning results as ClusterInfo.
func GetClusterInfos(db XODB) ([]*ClusterInfo, error) {
var err error
// sql query
var sqlstr = `SELECT usename, now() AS "Time", ` +
`client_addr, ` +
`client_hostname, ` +
`version() AS version, ` +
`pg_postmaster_start_time() AS Started, ` +
`pg_is_in_recovery() AS "Is_Slave" ` +
`FROM pg_stat_activity ` +
`WHERE pid = pg_backend_pid()`
// run query
XOLog(sqlstr)
q, err := db.Query(sqlstr)
if err != nil {
return nil, err
}
defer q.Close()
// load results
res := []*ClusterInfo{}
for q.Next() {
ci := ClusterInfo{}
// scan
err = q.Scan(&ci.Usename, &ci.Time, &ci.ClientAddr, &ci.ClientHostname, &ci.Version, &ci.Started, &ci.IsSlave)
if err != nil {
return nil, err
}
res = append(res, &ci)
}
return res, nil
}

View File

@@ -0,0 +1,54 @@
// Package models contains the types for schema 'public'.
package models
// Code generated by xo. DO NOT EDIT.
import (
"database/sql"
)
// Connected clients list
type ConnectedClients struct {
Usename Name // usename
Client sql.NullString // client
State sql.NullString // state
Count sql.NullInt64 // count
}
// GetConnectedClients runs a custom query, returning results as ConnectedClients.
func GetConnectedClients(db XODB) ([]*ConnectedClients, error) {
var err error
// sql query
var sqlstr = `SELECT usename, ` +
`CASE WHEN client_hostname IS NULL THEN client_addr::text ELSE client_hostname END AS client, ` +
`state, count(*) ` +
`FROM pg_stat_activity ` +
`WHERE state IS NOT NULL ` +
`GROUP BY 1,2,3 ` +
`ORDER BY 4 desc,3`
// run query
XOLog(sqlstr)
q, err := db.Query(sqlstr)
if err != nil {
return nil, err
}
defer q.Close()
// load results
res := []*ConnectedClients{}
for q.Next() {
cc := ConnectedClients{}
// scan
err = q.Scan(&cc.Usename, &cc.Client, &cc.State, &cc.Count)
if err != nil {
return nil, err
}
res = append(res, &cc)
}
return res, nil
}

View File

@@ -0,0 +1,44 @@
// Package models contains the types for schema 'public'.
package models
// Code generated by xo. DO NOT EDIT.
// Connections represents a row from '[custom connections]'.
type Connections struct {
State string // state
Count int64 // count
}
// GetConnections runs a custom query, returning results as Connections.
func GetConnections(db XODB) ([]*Connections, error) {
var err error
// sql query
var sqlstr = `SELECT state, count(*) ` +
`FROM pg_stat_activity ` +
`GROUP BY 1`
// run query
XOLog(sqlstr)
q, err := db.Query(sqlstr)
if err != nil {
return nil, err
}
defer q.Close()
// load results
res := []*Connections{}
for q.Next() {
c := Connections{}
// scan
err = q.Scan(&c.State, &c.Count)
if err != nil {
return nil, err
}
res = append(res, &c)
}
return res, nil
}

View File

@@ -0,0 +1,60 @@
// Package models contains the types for schema 'public'.
package models
// Code generated by xo. DO NOT EDIT.
// Counters represents a row from '[custom counters]'.
type Counters struct {
Datname Name // datname
Numbackends int // numbackends
XactCommit int64 // xact_commit
XactRollback int64 // xact_rollback
BlksRead int64 // blks_read
BlksHit int64 // blks_hit
TupReturned int64 // tup_returned
TupFetched int64 // tup_fetched
TupInserted int64 // tup_inserted
TupUpdated int64 // tup_updated
TupDeleted int64 // tup_deleted
Conflicts int64 // conflicts
TempFiles int64 // temp_files
TempBytes int64 // temp_bytes
Deadlocks int64 // deadlocks
}
// GetCounters runs a custom query, returning results as Counters.
func GetCounters(db XODB) ([]*Counters, error) {
var err error
// sql query
var sqlstr = `SELECT datname, numbackends, xact_commit, xact_rollback, ` +
`blks_read, blks_hit, tup_returned, tup_fetched, tup_inserted, ` +
`tup_updated, tup_deleted, conflicts, temp_files, ` +
`temp_bytes, deadlocks ` +
`FROM pg_stat_database ` +
`ORDER BY datname`
// run query
XOLog(sqlstr)
q, err := db.Query(sqlstr)
if err != nil {
return nil, err
}
defer q.Close()
// load results
res := []*Counters{}
for q.Next() {
c := Counters{}
// scan
err = q.Scan(&c.Datname, &c.Numbackends, &c.XactCommit, &c.XactRollback, &c.BlksRead, &c.BlksHit, &c.TupReturned, &c.TupFetched, &c.TupInserted, &c.TupUpdated, &c.TupDeleted, &c.Conflicts, &c.TempFiles, &c.TempBytes, &c.Deadlocks)
if err != nil {
return nil, err
}
res = append(res, &c)
}
return res, nil
}

View File

@@ -0,0 +1,43 @@
// Package models contains the types for schema 'public'.
package models
// Code generated by xo. DO NOT EDIT.
// Databases
type Databases struct {
Datname Name // datname
PgSizePretty string // pg_size_pretty
}
// GetDatabases runs a custom query, returning results as Databases.
func GetDatabases(db XODB) ([]*Databases, error) {
var err error
// sql query
var sqlstr = `SELECT datname, pg_size_pretty(pg_database_size(datname)) ` +
`FROM pg_stat_database`
// run query
XOLog(sqlstr)
q, err := db.Query(sqlstr)
if err != nil {
return nil, err
}
defer q.Close()
// load results
res := []*Databases{}
for q.Next() {
d := Databases{}
// scan
err = q.Scan(&d.Datname, &d.PgSizePretty)
if err != nil {
return nil, err
}
res = append(res, &d)
}
return res, nil
}

View File

@@ -0,0 +1,58 @@
// Package models contains the types for schema 'public'.
package models
// Code generated by xo. DO NOT EDIT.
import (
"database/sql"
)
// DatabaseWaitEvents represents a row from '[custom database_wait_events]'.
type DatabaseWaitEvents struct {
Relname Name // relname
Relkind uint8 // relkind
WaitEventType sql.NullString // wait_event_type
WaitEvent sql.NullString // wait_event
Datname Name // datname
Count sql.NullInt64 // count
}
// GetDatabaseWaitEvents runs a custom query, returning results as DatabaseWaitEvents.
func GetDatabaseWaitEvents(db XODB) ([]*DatabaseWaitEvents, error) {
var err error
// sql query
var sqlstr = `SELECT c.relname, c.relkind, d.wait_event_type, d.wait_event, b.datname, count(*) ` +
`FROM pg_locks a ` +
`JOIN pg_stat_database b ON a.database=b.datid ` +
`JOIN pg_class c ON a.relation=c.oid ` +
`JOIN pg_stat_activity d ON a.pid = d.pid ` +
`WHERE a.relation IS NOT NULL ` +
`AND a.database IS NOT NULL ` +
`AND (d.wait_event_type IS NOT NULL OR d.wait_event IS NOT NULL) ` +
`GROUP BY 1,2,3,4,5`
// run query
XOLog(sqlstr)
q, err := db.Query(sqlstr)
if err != nil {
return nil, err
}
defer q.Close()
// load results
res := []*DatabaseWaitEvents{}
for q.Next() {
dwe := DatabaseWaitEvents{}
// scan
err = q.Scan(&dwe.Relname, &dwe.Relkind, &dwe.WaitEventType, &dwe.WaitEvent, &dwe.Datname, &dwe.Count)
if err != nil {
return nil, err
}
res = append(res, &dwe)
}
return res, nil
}

View File

@@ -0,0 +1,38 @@
version: '2.2'
services:
postgres9:
image: ${MYSQL_IMAGE:-postgres:9.6}
ports:
- ${POSTGRE_HOST:-127.0.0.1}:${POSTGRE_96_PORT:-6432}:5432
environment:
- POSTGRES_PASSWORD=root
networks:
app_net:
ipv6_address: 2001:3200:3200::20
postgres10:
image: ${POSTGRE_IMAGE:-postgres:10.7}
ports:
- ${POSTGRE_HOST:-127.0.0.1}:${POSTGRE_10_PORT:-6433}:5432
environment:
- POSTGRES_PASSWORD=root
postgres11:
image: ${POSTGRE_IMAGE:-postgres:11}
ports:
- ${POSTGRE_HOST:-127.0.0.1}:${POSTGRE_11_PORT:-6434}:5432
environment:
- POSTGRES_PASSWORD=root
postgres12:
image: ${POSTGRE_IMAGE:-postgres:12}
ports:
- ${POSTGRE_HOST:-127.0.0.1}:${POSTGRE_12_PORT:-6435}:5432
environment:
- POSTGRES_PASSWORD=root
networks:
app_net:
enable_ipv6: true
driver: bridge
ipam:
config:
- subnet: "2001:3200:3200::/64"
# gateway: 2001:3200:3200::1

View File

@@ -0,0 +1,296 @@
#!/bin/bash
USERNAME=postgres
PASSWORD=root
PORT9=6432
PORT10=6433
DO_CLEANUP=0
if [ ! "$(docker ps -q -f name=pt-pg-summary_postgres9_1)" ]; then
DO_CLEANUP=1
docker-compose up -d --force-recreate
sleep 20
fi
xo pgsql://${USERNAME}:${PASSWORD}@127.0.0.1:${PORT9}/?sslmode=disable \
--query-mode \
--query-trim \
--query-interpolate \
--query-type AllDatabases \
--package models \
--out ./ << ENDSQL
SELECT datname
FROM pg_database
WHERE datistemplate = false
ENDSQL
xo pgsql://${USERNAME}:${PASSWORD}@127.0.0.1:${PORT9}/?sslmode=disable \
--query-mode \
--query-trim \
--query-interpolate \
--query-only-one \
--query-type PortAndDatadir \
--package models \
--out ./ << ENDSQL
SELECT name,
setting
FROM pg_settings
WHERE name IN ('port','data_directory')
ENDSQL
COMMENT="Tablespaces"
xo pgsql://${USERNAME}:${PASSWORD}@127.0.0.1:${PORT9}/?sslmode=disable \
--query-mode \
--query-trim \
--query-interpolate \
--query-type Tablespaces \
--query-type-comment "$COMMENT" \
--package models \
--out ./ << ENDSQL
SELECT spcname AS Name,
pg_catalog.pg_get_userbyid(spcowner) AS Owner,
pg_catalog.pg_tablespace_location(oid) AS Location
FROM pg_catalog.pg_tablespace
ORDER BY 1
ENDSQL
FIELDS='Usename string,Time time.Time,ClientAddr string,ClientHostname sql.NullString,Version string,Started time.Time,IsSlave bool'
COMMENT='Cluster info'
xo pgsql://${USERNAME}:${PASSWORD}@127.0.0.1:${PORT9}/?sslmode=disable \
--query-mode \
--query-trim \
-k smart \
--query-type ClusterInfo \
--query-fields "$FIELDS" \
--query-interpolate \
--query-type-comment "$COMMENT" \
--query-allow-nulls \
--package models \
--out ./ << ENDSQL
SELECT usename, now() AS "Time",
client_addr,
client_hostname,
version() AS version,
pg_postmaster_start_time() AS Started,
pg_is_in_recovery() AS "Is_Slave"
FROM pg_stat_activity
WHERE pid = pg_backend_pid()
ENDSQL
COMMENT="Databases"
xo pgsql://${USERNAME}:${PASSWORD}@127.0.0.1:${PORT9}/?sslmode=disable \
--query-mode \
--query-trim \
--query-interpolate \
--query-type-comment "$COMMENT" \
--query-type Databases \
--package models \
--out ./ << ENDSQL
SELECT datname, pg_size_pretty(pg_database_size(datname))
FROM pg_stat_database
ENDSQL
xo pgsql://${USERNAME}:${PASSWORD}@127.0.0.1:${PORT9}/?sslmode=disable \
--query-mode \
--query-trim \
--query-interpolate \
--query-type Connections \
--package models \
--out ./ << ENDSQL
SELECT state, count(*)
FROM pg_stat_activity
GROUP BY 1
ENDSQL
xo pgsql://${USERNAME}:${PASSWORD}@127.0.0.1:${PORT9}/?sslmode=disable \
--query-mode \
--query-interpolate \
--query-trim \
--query-type Counters \
--package models \
--out ./ << ENDSQL
SELECT datname, numbackends, xact_commit, xact_rollback,
blks_read, blks_hit, tup_returned, tup_fetched, tup_inserted,
tup_updated, tup_deleted, conflicts, temp_files,
temp_bytes, deadlocks
FROM pg_stat_database
ORDER BY datname
ENDSQL
FIELDS='Relname string, Relkind string,Datname string,Count sql.NullInt64'
COMMENT='Table Access'
xo pgsql://${USERNAME}:${PASSWORD}@127.0.0.1:${PORT9}/?sslmode=disable \
--query-mode \
--query-trim \
--query-type TableAccess \
--query-fields "$FIELDS" \
--query-type-comment "$COMMENT" \
--query-interpolate \
--query-allow-nulls \
--package models \
--out ./ << ENDSQL
SELECT c.relname, c.relkind, b.datname, count(*) FROM pg_locks a
JOIN pg_stat_database b
ON a.database=b.datid
JOIN pg_class c
ON a.relation=c.oid
WHERE a.relation IS NOT NULL
AND a.database IS NOT NULL
GROUP BY 1,2,3
ENDSQL
FIELDS='Name string,Ratio sql.NullFloat64'
COMMENT='Table cache hit ratio'
xo pgsql://${USERNAME}:${PASSWORD}@127.0.0.1:${PORT9}/?sslmode=disable --query-mode --query-trim \
--query-type TableCacheHitRatio \
--query-fields "$FIELDS" \
--query-interpolate \
--query-only-one \
--query-type-comment "$COMMENT" \
--package models \
--out ./ << ENDSQL
SELECT 'cache hit rate' AS name,
CASE WHEN (sum(heap_blks_read) + sum(idx_blks_hit)) > 0
THEN
sum(heap_blks_hit) / (sum(heap_blks_hit) + sum(heap_blks_read))
ELSE 0
END AS ratio
FROM pg_statio_user_tables
ENDSQL
FIELDS='Name string,Ratio sql.NullFloat64'
COMMENT='Table cache hit ratio'
xo pgsql://${USERNAME}:${PASSWORD}@127.0.0.1:${PORT9}/?sslmode=disable \
--query-mode \
--query-fields "$FIELDS" \
--query-trim \
--query-allow-nulls \
--query-only-one \
--query-type IndexCacheHitRatio \
--query-type-comment "$COMMENT" \
--package models \
--out ./ << ENDSQL
SELECT 'index hit rate' AS name,
CASE WHEN sum(idx_blks_hit) IS NULL
THEN 0
ELSE (sum(idx_blks_hit)) / sum(idx_blks_hit + idx_blks_read)
END AS ratio
FROM pg_statio_user_indexes
WHERE (idx_blks_hit + idx_blks_read) > 0
ENDSQL
xo pgsql://${USERNAME}:${PASSWORD}@127.0.0.1:${PORT9}/?sslmode=disable \
--query-mode \
--query-trim \
--query-type GlobalWaitEvents \
--package models \
--out ./ << ENDSQL
SELECT wait_event_type, wait_event, count(*)
FROM pg_stat_activity
WHERE wait_event_type IS NOT NULL
OR wait_event IS NOT NULL
GROUP BY 1,2
ENDSQL
xo pgsql://${USERNAME}:${PASSWORD}@127.0.0.1:${PORT9}/?sslmode=disable \
--query-mode \
--query-trim \
--query-interpolate \
--query-allow-nulls \
--query-type DatabaseWaitEvents \
--package models \
--out ./ << ENDSQL
SELECT c.relname, c.relkind, d.wait_event_type, d.wait_event, b.datname, count(*)
FROM pg_locks a
JOIN pg_stat_database b ON a.database=b.datid
JOIN pg_class c ON a.relation=c.oid
JOIN pg_stat_activity d ON a.pid = d.pid
WHERE a.relation IS NOT NULL
AND a.database IS NOT NULL
AND (d.wait_event_type IS NOT NULL OR d.wait_event IS NOT NULL)
GROUP BY 1,2,3,4,5
ENDSQL
COMMENT="Connected clients list"
xo pgsql://${USERNAME}:${PASSWORD}@127.0.0.1:${PORT9}/?sslmode=disable \
--query-mode \
--query-trim \
--query-type ConnectedClients \
--query-type-comment "$COMMENT" \
--query-allow-nulls \
--query-interpolate \
--package models \
--out ./ << ENDSQL
SELECT usename,
CASE WHEN client_hostname IS NULL THEN client_addr::text ELSE client_hostname END AS client,
state, count(*)
FROM pg_stat_activity
WHERE state IS NOT NULL
GROUP BY 1,2,3
ORDER BY 4 desc,3
ENDSQL
# Postgre 9
xo pgsql://${USERNAME}:${PASSWORD}@127.0.0.1:${PORT9}/?sslmode=disable \
--query-mode \
--query-trim \
--query-type SlaveHosts96 \
--query-interpolate \
--query-allow-nulls \
--package models \
--out ./ << ENDSQL
SELECT application_name, client_addr, state, sent_offset - (replay_offset - (sent_xlog - replay_xlog) * 255 * 16 ^ 6 ) AS byte_lag
FROM ( SELECT application_name, client_addr, client_hostname, state,
('x' || lpad(split_part(sent_location::TEXT, '/', 1), 8, '0'))::bit(32)::bigint AS sent_xlog,
('x' || lpad(split_part(replay_location::TEXT, '/', 1), 8, '0'))::bit(32)::bigint AS replay_xlog,
('x' || lpad(split_part(sent_location::TEXT, '/', 2), 8, '0'))::bit(32)::bigint AS sent_offset,
('x' || lpad(split_part(replay_location::TEXT, '/', 2), 8, '0'))::bit(32)::bigint AS replay_offset
FROM pg_stat_replication ) AS s
ENDSQL
# Postgre 10
xo pgsql://${USERNAME}:${PASSWORD}@127.0.0.1:${PORT10}/?sslmode=disable \
--query-mode \
--query-trim \
--query-interpolate \
--query-allow-nulls \
--query-type SlaveHosts10 \
--package models \
--out ./ << ENDSQL
SELECT application_name, client_addr, state, sent_offset - (replay_offset - (sent_lsn - replay_lsn) * 255 * 16 ^ 6 ) AS byte_lag
FROM ( SELECT application_name, client_addr, client_hostname, state,
('x' || lpad(split_part(sent_lsn::TEXT, '/', 1), 8, '0'))::bit(32)::bigint AS sent_lsn,
('x' || lpad(split_part(replay_lsn::TEXT, '/', 1), 8, '0'))::bit(32)::bigint AS replay_lsn,
('x' || lpad(split_part(sent_lsn::TEXT, '/', 2), 8, '0'))::bit(32)::bigint AS sent_offset,
('x' || lpad(split_part(replay_lsn::TEXT, '/', 2), 8, '0'))::bit(32)::bigint AS replay_offset
FROM pg_stat_replication ) AS s
ENDSQL
xo pgsql://${USERNAME}:${PASSWORD}@127.0.0.1:${PORT10}/?sslmode=disable \
--query-mode \
--query-trim \
--query-only-one \
--query-type ServerVersion \
--package models \
--out ./ << ENDSQL
SELECT current_setting('server_version_num') AS version
ENDSQL
FIELDS='Name string,Setting string'
COMMENT='Settings'
xo pgsql://${USERNAME}:${PASSWORD}@127.0.0.1:${PORT9}/?sslmode=disable \
--query-mode \
--query-fields "$FIELDS" \
--query-trim \
--query-allow-nulls \
--query-type Setting \
--query-type-comment "$COMMENT" \
--package models \
--out ./ << ENDSQL
SELECT name, setting
FROM pg_settings
ENDSQL
if [ $DO_CLEANUP == 1 ]; then
docker-compose down --volumes
fi

View File

@@ -0,0 +1,47 @@
// Package models contains the types for schema 'public'.
package models
// Code generated by xo. DO NOT EDIT.
// GlobalWaitEvents represents a row from '[custom global_wait_events]'.
type GlobalWaitEvents struct {
WaitEventType string // wait_event_type
WaitEvent string // wait_event
Count int64 // count
}
// GetGlobalWaitEvents runs a custom query, returning results as GlobalWaitEvents.
func GetGlobalWaitEvents(db XODB) ([]*GlobalWaitEvents, error) {
var err error
// sql query
const sqlstr = `SELECT wait_event_type, wait_event, count(*) ` +
`FROM pg_stat_activity ` +
`WHERE wait_event_type IS NOT NULL ` +
`OR wait_event IS NOT NULL ` +
`GROUP BY 1,2`
// run query
XOLog(sqlstr)
q, err := db.Query(sqlstr)
if err != nil {
return nil, err
}
defer q.Close()
// load results
res := []*GlobalWaitEvents{}
for q.Next() {
gwe := GlobalWaitEvents{}
// scan
err = q.Scan(&gwe.WaitEventType, &gwe.WaitEvent, &gwe.Count)
if err != nil {
return nil, err
}
res = append(res, &gwe)
}
return res, nil
}

View File

@@ -0,0 +1,38 @@
// Package models contains the types for schema 'public'.
package models
// Code generated by xo. DO NOT EDIT.
import (
"database/sql"
)
// Table cache hit ratio
type IndexCacheHitRatio struct {
Name string // name
Ratio sql.NullFloat64 // ratio
}
// GetIndexCacheHitRatio runs a custom query, returning results as IndexCacheHitRatio.
func GetIndexCacheHitRatio(db XODB) (*IndexCacheHitRatio, error) {
var err error
// sql query
const sqlstr = `SELECT 'index hit rate' AS name, ` +
`CASE WHEN sum(idx_blks_hit) IS NULL ` +
`THEN 0 ` +
`ELSE (sum(idx_blks_hit)) / sum(idx_blks_hit + idx_blks_read) ` +
`END AS ratio ` +
`FROM pg_statio_user_indexes ` +
`WHERE (idx_blks_hit + idx_blks_read) > 0`
// run query
XOLog(sqlstr)
var ichr IndexCacheHitRatio
err = db.QueryRow(sqlstr).Scan(&ichr.Name, &ichr.Ratio)
if err != nil {
return nil, err
}
return &ichr, nil
}

View File

@@ -0,0 +1,31 @@
// Package models contains the types for schema 'public'.
package models
// Code generated by xo. DO NOT EDIT.
// PortAndDatadir represents a row from '[custom port_and_datadir]'.
type PortAndDatadir struct {
Name string // name
Setting string // setting
}
// GetPortAndDatadir runs a custom query, returning results as PortAndDatadir.
func GetPortAndDatadir(db XODB) (*PortAndDatadir, error) {
var err error
// sql query
var sqlstr = `SELECT name, ` +
`setting ` +
`FROM pg_settings ` +
`WHERE name IN ('port','data_directory')`
// run query
XOLog(sqlstr)
var pad PortAndDatadir
err = db.QueryRow(sqlstr).Scan(&pad.Name, &pad.Setting)
if err != nil {
return nil, err
}
return &pad, nil
}

View File

@@ -0,0 +1,27 @@
// Package models contains the types for schema 'public'.
package models
// Code generated by xo. DO NOT EDIT.
// ServerVersion represents a row from '[custom server_version]'.
type ServerVersion struct {
Version string // version
}
// GetServerVersion runs a custom query, returning results as ServerVersion.
func GetServerVersion(db XODB) (*ServerVersion, error) {
var err error
// sql query
const sqlstr = `SELECT current_setting('server_version_num') AS version`
// run query
XOLog(sqlstr)
var sv ServerVersion
err = db.QueryRow(sqlstr).Scan(&sv.Version)
if err != nil {
return nil, err
}
return &sv, nil
}

View File

@@ -0,0 +1,43 @@
// Package models contains the types for schema 'public'.
package models
// Code generated by xo. DO NOT EDIT.
// Settings
type Setting struct {
Name string // name
Setting string // setting
}
// GetSettings runs a custom query, returning results as Setting.
func GetSettings(db XODB) ([]*Setting, error) {
var err error
// sql query
const sqlstr = `SELECT name, setting ` +
`FROM pg_settings`
// run query
XOLog(sqlstr)
q, err := db.Query(sqlstr)
if err != nil {
return nil, err
}
defer q.Close()
// load results
res := []*Setting{}
for q.Next() {
s := Setting{}
// scan
err = q.Scan(&s.Name, &s.Setting)
if err != nil {
return nil, err
}
res = append(res, &s)
}
return res, nil
}

View File

@@ -0,0 +1,54 @@
// Package models contains the types for schema 'public'.
package models
// Code generated by xo. DO NOT EDIT.
import (
"database/sql"
)
// SlaveHosts10 represents a row from '[custom slave_hosts10]'.
type SlaveHosts10 struct {
ApplicationName sql.NullString // application_name
ClientAddr sql.NullString // client_addr
State sql.NullString // state
ByteLag sql.NullFloat64 // byte_lag
}
// GetSlaveHosts10s runs a custom query, returning results as SlaveHosts10.
func GetSlaveHosts10s(db XODB) ([]*SlaveHosts10, error) {
var err error
// sql query
var sqlstr = `SELECT application_name, client_addr, state, sent_offset - (replay_offset - (sent_lsn - replay_lsn) * 255 * 16 ^ 6 ) AS byte_lag ` +
`FROM ( SELECT application_name, client_addr, client_hostname, state, ` +
`('x' || lpad(split_part(sent_lsn::TEXT, '/', 1), 8, '0'))::bit(32)::bigint AS sent_lsn, ` +
`('x' || lpad(split_part(replay_lsn::TEXT, '/', 1), 8, '0'))::bit(32)::bigint AS replay_lsn, ` +
`('x' || lpad(split_part(sent_lsn::TEXT, '/', 2), 8, '0'))::bit(32)::bigint AS sent_offset, ` +
`('x' || lpad(split_part(replay_lsn::TEXT, '/', 2), 8, '0'))::bit(32)::bigint AS replay_offset ` +
`FROM pg_stat_replication ) AS s`
// run query
XOLog(sqlstr)
q, err := db.Query(sqlstr)
if err != nil {
return nil, err
}
defer q.Close()
// load results
res := []*SlaveHosts10{}
for q.Next() {
sh := SlaveHosts10{}
// scan
err = q.Scan(&sh.ApplicationName, &sh.ClientAddr, &sh.State, &sh.ByteLag)
if err != nil {
return nil, err
}
res = append(res, &sh)
}
return res, nil
}

View File

@@ -0,0 +1,54 @@
// Package models contains the types for schema 'public'.
package models
// Code generated by xo. DO NOT EDIT.
import (
"database/sql"
)
// SlaveHosts96 represents a row from '[custom slave_hosts96]'.
type SlaveHosts96 struct {
ApplicationName sql.NullString // application_name
ClientAddr sql.NullString // client_addr
State sql.NullString // state
ByteLag sql.NullFloat64 // byte_lag
}
// GetSlaveHosts96s runs a custom query, returning results as SlaveHosts96.
func GetSlaveHosts96s(db XODB) ([]*SlaveHosts96, error) {
var err error
// sql query
var sqlstr = `SELECT application_name, client_addr, state, sent_offset - (replay_offset - (sent_xlog - replay_xlog) * 255 * 16 ^ 6 ) AS byte_lag ` +
`FROM ( SELECT application_name, client_addr, client_hostname, state, ` +
`('x' || lpad(split_part(sent_location::TEXT, '/', 1), 8, '0'))::bit(32)::bigint AS sent_xlog, ` +
`('x' || lpad(split_part(replay_location::TEXT, '/', 1), 8, '0'))::bit(32)::bigint AS replay_xlog, ` +
`('x' || lpad(split_part(sent_location::TEXT, '/', 2), 8, '0'))::bit(32)::bigint AS sent_offset, ` +
`('x' || lpad(split_part(replay_location::TEXT, '/', 2), 8, '0'))::bit(32)::bigint AS replay_offset ` +
`FROM pg_stat_replication ) AS s`
// run query
XOLog(sqlstr)
q, err := db.Query(sqlstr)
if err != nil {
return nil, err
}
defer q.Close()
// load results
res := []*SlaveHosts96{}
for q.Next() {
sh := SlaveHosts96{}
// scan
err = q.Scan(&sh.ApplicationName, &sh.ClientAddr, &sh.State, &sh.ByteLag)
if err != nil {
return nil, err
}
res = append(res, &sh)
}
return res, nil
}

View File

@@ -0,0 +1,55 @@
// Package models contains the types for schema 'public'.
package models
// Code generated by xo. DO NOT EDIT.
import (
"database/sql"
)
// Table Access
type TableAccess struct {
Relname string // relname
Relkind string // relkind
Datname string // datname
Count sql.NullInt64 // count
}
// GetTableAccesses runs a custom query, returning results as TableAccess.
func GetTableAccesses(db XODB) ([]*TableAccess, error) {
var err error
// sql query
var sqlstr = `SELECT c.relname, c.relkind, b.datname, count(*) FROM pg_locks a ` +
`JOIN pg_stat_database b ` +
`ON a.database=b.datid ` +
`JOIN pg_class c ` +
`ON a.relation=c.oid ` +
`WHERE a.relation IS NOT NULL ` +
`AND a.database IS NOT NULL ` +
`GROUP BY 1,2,3`
// run query
XOLog(sqlstr)
q, err := db.Query(sqlstr)
if err != nil {
return nil, err
}
defer q.Close()
// load results
res := []*TableAccess{}
for q.Next() {
ta := TableAccess{}
// scan
err = q.Scan(&ta.Relname, &ta.Relkind, &ta.Datname, &ta.Count)
if err != nil {
return nil, err
}
res = append(res, &ta)
}
return res, nil
}

View File

@@ -0,0 +1,38 @@
// Package models contains the types for schema 'public'.
package models
// Code generated by xo. DO NOT EDIT.
import (
"database/sql"
)
// Table cache hit ratio
type TableCacheHitRatio struct {
Name string // name
Ratio sql.NullFloat64 // ratio
}
// GetTableCacheHitRatio runs a custom query, returning results as TableCacheHitRatio.
func GetTableCacheHitRatio(db XODB) (*TableCacheHitRatio, error) {
var err error
// sql query
var sqlstr = `SELECT 'cache hit rate' AS name, ` +
`CASE WHEN (sum(heap_blks_read) + sum(idx_blks_hit)) > 0 ` +
`THEN ` +
`sum(heap_blks_hit) / (sum(heap_blks_hit) + sum(heap_blks_read)) ` +
`ELSE 0 ` +
`END AS ratio ` +
`FROM pg_statio_user_tables`
// run query
XOLog(sqlstr)
var tchr TableCacheHitRatio
err = db.QueryRow(sqlstr).Scan(&tchr.Name, &tchr.Ratio)
if err != nil {
return nil, err
}
return &tchr, nil
}

View File

@@ -0,0 +1,47 @@
// Package models contains the types for schema 'public'.
package models
// Code generated by xo. DO NOT EDIT.
// Tablespaces
type Tablespaces struct {
Name Name // name
Owner Name // owner
Location string // location
}
// GetTablespaces runs a custom query, returning results as Tablespaces.
func GetTablespaces(db XODB) ([]*Tablespaces, error) {
var err error
// sql query
var sqlstr = `SELECT spcname AS Name, ` +
`pg_catalog.pg_get_userbyid(spcowner) AS Owner, ` +
`pg_catalog.pg_tablespace_location(oid) AS Location ` +
`FROM pg_catalog.pg_tablespace ` +
`ORDER BY 1`
// run query
XOLog(sqlstr)
q, err := db.Query(sqlstr)
if err != nil {
return nil, err
}
defer q.Close()
// load results
res := []*Tablespaces{}
for q.Next() {
t := Tablespaces{}
// scan
err = q.Scan(&t.Name, &t.Owner, &t.Location)
if err != nil {
return nil, err
}
res = append(res, &t)
}
return res, nil
}

View File

@@ -0,0 +1,11 @@
package models
//go:generate ./gen.sh
type Name string
type Unknown []uint8
func (n Unknown) String() string {
return string(n)
}

View File

@@ -0,0 +1,85 @@
// Package models contains the types for schema 'public'.
package models
// Code generated by xo. DO NOT EDIT.
import (
"database/sql"
"database/sql/driver"
"encoding/csv"
"errors"
"fmt"
"regexp"
"strings"
)
// XODB is the common interface for database operations that can be used with
// types from schema 'public'.
//
// This should work with database/sql.DB and database/sql.Tx.
type XODB interface {
Exec(string, ...interface{}) (sql.Result, error)
Query(string, ...interface{}) (*sql.Rows, error)
QueryRow(string, ...interface{}) *sql.Row
}
// XOLog provides the log func used by generated queries.
var XOLog = func(string, ...interface{}) {}
// ScannerValuer is the common interface for types that implement both the
// database/sql.Scanner and sql/driver.Valuer interfaces.
type ScannerValuer interface {
sql.Scanner
driver.Valuer
}
// StringSlice is a slice of strings.
type StringSlice []string
// quoteEscapeRegex is the regex to match escaped characters in a string.
var quoteEscapeRegex = regexp.MustCompile(`([^\\]([\\]{2})*)\\"`)
// Scan satisfies the sql.Scanner interface for StringSlice.
func (ss *StringSlice) Scan(src interface{}) error {
buf, ok := src.([]byte)
if !ok {
return errors.New("invalid StringSlice")
}
// change quote escapes for csv parser
str := quoteEscapeRegex.ReplaceAllString(string(buf), `$1""`)
str = strings.Replace(str, `\\`, `\`, -1)
// remove braces
str = str[1 : len(str)-1]
// bail if only one
if len(str) == 0 {
*ss = StringSlice([]string{})
return nil
}
// parse with csv reader
cr := csv.NewReader(strings.NewReader(str))
slice, err := cr.Read()
if err != nil {
fmt.Printf("exiting!: %v\n", err)
return err
}
*ss = StringSlice(slice)
return nil
}
// Value satisfies the driver.Valuer interface for StringSlice.
func (ss StringSlice) Value() (driver.Value, error) {
v := make([]string, len(ss))
for i, s := range ss {
v[i] = `"` + strings.Replace(strings.Replace(s, `\`, `\\\`, -1), `"`, `\"`, -1) + `"`
}
return "{" + strings.Join(v, ",") + "}", nil
}
// Slice is a slice of ScannerValuers.
type Slice []ScannerValuer

View File

@@ -0,0 +1,309 @@
package templates
var TPL = `{{define "report"}}
{{ template "port_and_datadir" .PortAndDatadir }}
{{ template "tablespaces" .Tablespaces }}
{{ if .SlaveHosts96 -}}
{{ template "slaves_and_lag" .SlaveHosts96 }}
{{ else if .SlaveHosts10 -}}
{{ template "slaves_and_lag" .SlaveHosts10 }}
{{- end }}
{{ template "cluster" .ClusterInfo }}
{{ template "databases" .AllDatabases }}
{{ template "index_cache_ratios" .IndexCacheHitRatio }}
{{ template "table_cache_ratios" .TableCacheHitRatio }}
{{ template "global_wait_events" .GlobalWaitEvents }}
{{ template "connected_clients" .ConnectedClients }}
{{ template "counters_header" .Sleep }}
{{ template "counters" .Counters }}
{{ template "table_access" .TableAccess }}
{{ template "settings" .Settings }}
{{ template "processes" .Processes }}
{{ end }} {{/* end "report" */}}` +
`
{{ define "port_and_datadir" -}}
##### --- Database Port and Data_Directory --- ####
+----------------------+----------------------------------------------------+
| Name | Setting |
+----------------------+----------------------------------------------------+
| {{ printf "%-20s" .Name }} | {{ printf "%-50s" .Setting }} |
+----------------------+----------------------------------------------------+
{{ end -}}
` +
`{{ define "tablespaces" -}}
##### --- List of Tablespaces ---- ######
+----------------------+----------------------+----------------------------------------------------+
| Name | Owner | Location |
+----------------------+----------------------+----------------------------------------------------+
{{ range . -}}
| {{ printf "%-20s" .Name }} | {{ printf "%-20s" .Owner }} | {{ printf "%-50s" .Location }} |
{{ end -}}
+----------------------+----------------------+----------------------------------------------------+
{{ end -}} {{/* end define */}}
` +
`{{ define "slaves_and_lag" -}}
##### --- Slave and the lag with Master --- ####
{{ if . -}}
+----------------------+----------------------+----------------------------------------------------+
| Application Name | Client Address | State | Lag |
+----------------------+----------------------+----------------------------------------------------+
{{ range . -}}` +
`| {{ printf "%-20s" .ApplicationName }} ` +
`| {{ printf "%-20s" .ClientAddr }} ` +
`| {{ printf "%-50s" .State }} ` +
`| {{ printf "% 4.2f" .ByteLag }}` +
`{{ end -}} {{/* end define */}}
+----------------------+----------------------+----------------------------------------------------+
{{- else -}}
There are no slave hosts
{{ end -}}
{{ end -}}
` +
`{{ define "cluster" -}}
##### --- Cluster Information --- ####
{{ if . -}}
+------------------------------------------------------------------------------------------------------+
{{- range . }}
Usename : {{ printf "%-20s" .Usename }}
Time : {{ printf "%v" .Time }}
Client Address : {{ printf "%-20s" .ClientAddr }}
Client Hostname: {{ trim .ClientHostname.String 80 }}
Version : {{ trim .Version 80 }}
Started : {{ printf "%v" .Started }}
Is Slave : {{ .IsSlave }}
+------------------------------------------------------------------------------------------------------+
{{ end -}}
{{ else -}}
There is no Cluster info
{{ end -}}
{{- end -}} {{/* end define */}}
` +
`{{ define "databases" -}}
##### --- Databases --- ####
+----------------------+------------+
| Dat Name | Size |
+----------------------+------------+
{{ range . -}}
| {{ printf "%-20s" .Datname }} | {{ printf "%10s" .PgSizePretty }} |
{{ end -}}
+----------------------+------------+
{{ end }} {{/* end define */}}
` +
`{{ define "index_cache_ratios" -}}
##### --- Index Cache Hit Ratios --- ####
{{ if . -}}
{{ range $dbname, $value := . }}
Database: {{ $dbname }}
+----------------------+------------+
| Index Name | Ratio |
+----------------------+------------+
| {{ printf "%-20s" .Name }} | {{ printf "% 5.2f" .Ratio.Float64 }} |
+----------------------+------------+
{{ else -}}
No stats available
{{ end -}}
{{ end -}}
{{ end -}} {{/* end define */}}
` +
`{{ define "table_cache_ratios" -}}
##### --- Table Cache Hit Ratios --- ####
{{ if . -}}
{{ range $dbname, $value := . -}}
Database: {{ $dbname }}
+----------------------+------------+
| Index Name | Ratio |
+----------------------+------------+
| {{ printf "%-20s" .Name }} | {{ printf "%5.2f" .Ratio.Float64 }} |
+----------------------+------------+
{{ else -}}
No stats available
{{ end -}}
{{ end }}
{{- end -}} {{/* end define */}}
` +
`{{ define "global_wait_events" -}}
##### --- List of Wait_events for the entire Cluster - all-databases --- ####
{{ if . -}}
+----------------------+----------------------+---------+
| Wait Event Type | Event | Count |
+----------------------+----------------------+---------+
{{ range . -}}
| {{ printf "%-20s" .WaitEventType }} | {{ printf "%-20s" .WaitEvent }} | {{ printf "% 5d" .Count }} |
{{ end -}}
+----------------------+----------------------+---------+
{{ else -}}
No stats available
{{ end -}}
{{- end -}} {{/* end define */}}
` +
`{{ define "connected_clients" -}}
##### --- List of users and client_addr or client_hostname connected to --all-databases --- ####
{{ if . -}}
+----------------------+------------+---------+----------------------+---------+
| Wait Event Type | Client | State | Count |
+----------------------+------------+---------+----------------------+---------+
{{ range . -}}` +
`| {{ printf "%-20s" .Usename }} | ` +
`{{ printf "%-20s" .Client.String }} | ` +
`{{ printf "%-20s" .State.String }} | ` +
`{{ printf "% 7d" .Count.Int64 }} |` + "\n" +
`{{ end -}}
+----------------------+------------+---------+----------------------+---------+
{{ else -}}
No stats available
{{ end -}}
{{- end -}} {{/* end define */}}
` +
/*
Counters header
*/
`{{ define "counters_header" -}}` +
"##### --- Counters diff after {{ . }} seconds --- ####\n" +
`{{end}}` +
/*
Counters
*/
`{{ define "counters" -}}` +
"+----------------------" +
"+-------------" +
"+------------" +
"+--------------" +
"+-------------" +
"+------------" +
"+-------------" +
"+------------" +
"+-------------" +
"+------------" +
"+------------" +
"+-----------" +
"+-----------" +
"+-----------" +
"+------------+" + "\n" +
"| Database " +
"| Numbackends " +
"| XactCommit " +
"| XactRollback " +
"| BlksRead " +
"| BlksHit " +
"| TupReturned " +
"| TupFetched " +
"| TupInserted " +
"| TupUpdated " +
"| TupDeleted " +
"| Conflicts " +
"| TempFiles " +
"| TempBytes " +
"| Deadlocks |" + "\n" +
"+----------------------" +
"+-------------" +
"+------------" +
"+--------------" +
"+-------------" +
"+------------" +
"+-------------" +
"+------------" +
"+-------------" +
"+------------" +
"+------------" +
"+-----------" +
"+-----------" +
"+-----------" +
"+------------+" + "\n" +
`{{ range $key, $value := . -}} ` +
`| {{ printf "%-20s" (index $value 2).Datname }} ` +
`| {{ printf "% 7d" (index $value 2).Numbackends }} ` +
`| {{ printf "% 7d" (index $value 2).XactCommit }} ` +
`| {{ printf "% 7d" (index $value 2).XactRollback }} ` +
`| {{ printf "% 7d" (index $value 2).BlksRead }} ` +
`| {{ printf "% 7d" (index $value 2).BlksHit }} ` +
`| {{ printf "% 7d" (index $value 2).TupReturned }} ` +
`| {{ printf "% 7d" (index $value 2).TupFetched }} ` +
`| {{ printf "% 7d" (index $value 2).TupInserted }} ` +
`| {{ printf "% 7d" (index $value 2).TupUpdated }} ` +
`| {{ printf "% 7d" (index $value 2).TupDeleted }} ` +
`| {{ printf "% 7d" (index $value 2).Conflicts }} ` +
`| {{ printf "% 7d" (index $value 2).TempFiles }} ` +
`| {{ printf "% 7d" (index $value 2).TempBytes }} ` +
`| {{ printf "% 7d" (index $value 2).Deadlocks }} ` +
"|\n" +
`{{ end }}` +
"+----------------------" +
"+-------------" +
"+------------" +
"+--------------" +
"+-------------" +
"+------------" +
"+-------------" +
"+------------" +
"+-------------" +
"+------------" +
"+------------" +
"+-----------" +
"+-----------" +
"+-----------" +
"+------------+" + "\n" +
`{{ end }}` +
`{{ define "table_access" -}}` +
"##### --- Table access per database --- ####\n" +
`{{ range $dbname, $values := . -}}` +
"Database: {{ $dbname }}\n" +
"+----------------------------------------------------" +
"+------" +
"+--------------------------------" +
"+---------+\n" +
"| Relname " +
"| Kind " +
"| Datname " +
"| Count |\n" +
"+----------------------------------------------------" +
"+------" +
"+--------------------------------" +
"+---------+\n" +
`{{ range . -}}
| {{ printf "%-50s" .Relname }} ` +
`| {{ printf "%1s" .Relkind }} ` +
`| {{ printf "%-30s" .Datname }} ` +
`| {{ printf "% 7d" .Count.Int64 }} ` +
"|\n" +
"{{ end }}" +
"+----------------------------------------------------" +
"+------" +
"+--------------------------------" +
"+---------+\n" +
"{{ end -}}" +
"{{ end }}" +
`{{ define "settings" -}}` +
/*
Settings
*/
"##### --- Instance settings --- ####\n" +
" Setting " +
" Value \n" +
`{{ range $name, $values := . -}}` +
` {{ printf "%-45s" .Name }} ` +
`: {{ printf "%-60s" .Setting }} ` +
"\n" +
"{{ end }}" +
"{{ end }}" +
/*
Processes
*/
`{{ define "processes" -}}` +
"##### --- Processes start up command --- ####\n" +
"{{ if . -}}" +
" PID " +
": Command line\n" +
`{{ range $name, $values := . }}` +
` {{ printf "% 5d" .PID }} ` +
`: {{ printf "%-s" .CmdLine }} ` +
"\n" +
"{{ end }}" +
"{{ else }}" +
"No postgres process found\n" +
"{{ end }}" +
"{{ end }}"

View File

@@ -36,6 +36,7 @@ my $dp = new DSNParser(opts=>$dsn_opts);
my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp);
my $dbh = $sb->get_dbh_for('master');
plan skip_all => 'Cannot connect to sandbox master';
if ( !$dbh ) {
plan skip_all => 'Cannot connect to sandbox master';
} else {

View File

@@ -37,6 +37,7 @@ my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp);
my $dbh = $sb->get_dbh_for('master');
my $output;
plan skip_all => 'Cannot connect to sandbox master';
if ( !$dbh ) {
plan skip_all => 'Cannot connect to sandbox master';
}

View File

@@ -1267,14 +1267,6 @@ is_deeply(
'Column having the word "generated" as part of the comment is OK',
) or diag Data::Dumper::Dumper($tbl);
$tbl = $tp->parse( load_file('t/lib/samples/generated_cols_comments.sql') );
warn Data::Dumper::Dumper($tbl);
is_deeply(
$tbl,
{},
'pt-1728',
);
# #############################################################################
# Done.
# #############################################################################

View File

@@ -1,6 +1,6 @@
mysql.columns_priv
CREATE TABLE `columns_priv` (
`Host` char(60) COLLATE utf8_bin NOT NULL DEFAULT '',
`Host` char(255) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL DEFAULT '',
`Db` char(64) COLLATE utf8_bin NOT NULL DEFAULT '',
`User` char(32) COLLATE utf8_bin NOT NULL DEFAULT '',
`Table_name` char(64) COLLATE utf8_bin NOT NULL DEFAULT '',
@@ -8,7 +8,7 @@ CREATE TABLE `columns_priv` (
`Timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`Column_priv` set('Select','Insert','Update','References') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
PRIMARY KEY (`Host`,`Db`,`User`,`Table_name`,`Column_name`)
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin STATS_PERSISTENT=0 COMMENT='Column privileges'
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin STATS_PERSISTENT=0 ROW_FORMAT=DYNAMIC COMMENT='Column privileges'
mysql.component
CREATE TABLE `component` (
@@ -16,11 +16,11 @@ CREATE TABLE `component` (
`component_group_id` int(10) unsigned NOT NULL,
`component_urn` text NOT NULL,
PRIMARY KEY (`component_id`)
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Components'
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='Components'
mysql.db
CREATE TABLE `db` (
`Host` char(60) COLLATE utf8_bin NOT NULL DEFAULT '',
`Host` char(255) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL DEFAULT '',
`Db` char(64) COLLATE utf8_bin NOT NULL DEFAULT '',
`User` char(32) COLLATE utf8_bin NOT NULL DEFAULT '',
`Select_priv` enum('N','Y') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'N',
@@ -44,16 +44,16 @@ CREATE TABLE `db` (
`Trigger_priv` enum('N','Y') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'N',
PRIMARY KEY (`Host`,`Db`,`User`),
KEY `User` (`User`)
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin STATS_PERSISTENT=0 COMMENT='Database privileges'
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin STATS_PERSISTENT=0 ROW_FORMAT=DYNAMIC COMMENT='Database privileges'
mysql.default_roles
CREATE TABLE `default_roles` (
`HOST` char(60) COLLATE utf8_bin NOT NULL DEFAULT '',
`HOST` char(255) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL DEFAULT '',
`USER` char(32) COLLATE utf8_bin NOT NULL DEFAULT '',
`DEFAULT_ROLE_HOST` char(60) COLLATE utf8_bin NOT NULL DEFAULT '%',
`DEFAULT_ROLE_HOST` char(255) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL DEFAULT '%',
`DEFAULT_ROLE_USER` char(32) COLLATE utf8_bin NOT NULL DEFAULT '',
PRIMARY KEY (`HOST`,`USER`,`DEFAULT_ROLE_HOST`,`DEFAULT_ROLE_USER`)
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin STATS_PERSISTENT=0 COMMENT='Default roles'
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin STATS_PERSISTENT=0 ROW_FORMAT=DYNAMIC COMMENT='Default roles'
mysql.engine_cost
CREATE TABLE `engine_cost` (
@@ -65,7 +65,7 @@ CREATE TABLE `engine_cost` (
`comment` varchar(1024) DEFAULT NULL,
`default_value` float GENERATED ALWAYS AS ((case `cost_name` when _utf8mb3'io_block_read_cost' then 1.0 when _utf8mb3'memory_block_read_cost' then 0.25 else NULL end)) VIRTUAL,
PRIMARY KEY (`cost_name`,`engine_name`,`device_type`)
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 STATS_PERSISTENT=0
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 STATS_PERSISTENT=0 ROW_FORMAT=DYNAMIC
mysql.func
CREATE TABLE `func` (
@@ -74,16 +74,16 @@ CREATE TABLE `func` (
`dl` char(128) COLLATE utf8_bin NOT NULL DEFAULT '',
`type` enum('function','aggregate') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
PRIMARY KEY (`name`)
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin STATS_PERSISTENT=0 COMMENT='User defined functions'
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin STATS_PERSISTENT=0 ROW_FORMAT=DYNAMIC COMMENT='User defined functions'
mysql.global_grants
CREATE TABLE `global_grants` (
`USER` char(32) COLLATE utf8_bin NOT NULL DEFAULT '',
`HOST` char(60) COLLATE utf8_bin NOT NULL DEFAULT '',
`HOST` char(255) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL DEFAULT '',
`PRIV` char(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`WITH_GRANT_OPTION` enum('N','Y') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'N',
PRIMARY KEY (`USER`,`HOST`,`PRIV`)
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin STATS_PERSISTENT=0 COMMENT='Extended global grants'
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin STATS_PERSISTENT=0 ROW_FORMAT=DYNAMIC COMMENT='Extended global grants'
mysql.help_category
CREATE TABLE `help_category` (
@@ -93,7 +93,7 @@ CREATE TABLE `help_category` (
`url` text NOT NULL,
PRIMARY KEY (`help_category_id`),
UNIQUE KEY `name` (`name`)
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 STATS_PERSISTENT=0 COMMENT='help categories'
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 STATS_PERSISTENT=0 ROW_FORMAT=DYNAMIC COMMENT='help categories'
mysql.help_keyword
CREATE TABLE `help_keyword` (
@@ -101,14 +101,14 @@ CREATE TABLE `help_keyword` (
`name` char(64) NOT NULL,
PRIMARY KEY (`help_keyword_id`),
UNIQUE KEY `name` (`name`)
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 STATS_PERSISTENT=0 COMMENT='help keywords'
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 STATS_PERSISTENT=0 ROW_FORMAT=DYNAMIC COMMENT='help keywords'
mysql.help_relation
CREATE TABLE `help_relation` (
`help_topic_id` int(10) unsigned NOT NULL,
`help_keyword_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`help_keyword_id`,`help_topic_id`)
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 STATS_PERSISTENT=0 COMMENT='keyword-topic relation'
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 STATS_PERSISTENT=0 ROW_FORMAT=DYNAMIC COMMENT='keyword-topic relation'
mysql.help_topic
CREATE TABLE `help_topic` (
@@ -120,60 +120,60 @@ CREATE TABLE `help_topic` (
`url` text NOT NULL,
PRIMARY KEY (`help_topic_id`),
UNIQUE KEY `name` (`name`)
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 STATS_PERSISTENT=0 COMMENT='help topics'
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 STATS_PERSISTENT=0 ROW_FORMAT=DYNAMIC COMMENT='help topics'
mysql.password_history
CREATE TABLE `password_history` (
`Host` char(60) COLLATE utf8_bin NOT NULL DEFAULT '',
`Host` char(255) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL DEFAULT '',
`User` char(32) COLLATE utf8_bin NOT NULL DEFAULT '',
`Password_timestamp` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
`Password` text COLLATE utf8_bin,
PRIMARY KEY (`Host`,`User`,`Password_timestamp` DESC)
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin STATS_PERSISTENT=0 COMMENT='Password history for user accounts'
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin STATS_PERSISTENT=0 ROW_FORMAT=DYNAMIC COMMENT='Password history for user accounts'
mysql.plugin
CREATE TABLE `plugin` (
`name` varchar(64) NOT NULL DEFAULT '',
`dl` varchar(128) NOT NULL DEFAULT '',
PRIMARY KEY (`name`)
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 STATS_PERSISTENT=0 COMMENT='MySQL plugins'
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 STATS_PERSISTENT=0 ROW_FORMAT=DYNAMIC COMMENT='MySQL plugins'
mysql.procs_priv
CREATE TABLE `procs_priv` (
`Host` char(60) COLLATE utf8_bin NOT NULL DEFAULT '',
`Host` char(255) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL DEFAULT '',
`Db` char(64) COLLATE utf8_bin NOT NULL DEFAULT '',
`User` char(32) COLLATE utf8_bin NOT NULL DEFAULT '',
`Routine_name` char(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`Routine_type` enum('FUNCTION','PROCEDURE') COLLATE utf8_bin NOT NULL,
`Grantor` char(93) COLLATE utf8_bin NOT NULL DEFAULT '',
`Grantor` varchar(288) COLLATE utf8_bin NOT NULL DEFAULT '',
`Proc_priv` set('Execute','Alter Routine','Grant') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`Timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`Host`,`Db`,`User`,`Routine_name`,`Routine_type`),
KEY `Grantor` (`Grantor`)
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin STATS_PERSISTENT=0 COMMENT='Procedure privileges'
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin STATS_PERSISTENT=0 ROW_FORMAT=DYNAMIC COMMENT='Procedure privileges'
mysql.proxies_priv
CREATE TABLE `proxies_priv` (
`Host` char(60) COLLATE utf8_bin NOT NULL DEFAULT '',
`Host` char(255) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL DEFAULT '',
`User` char(32) COLLATE utf8_bin NOT NULL DEFAULT '',
`Proxied_host` char(60) COLLATE utf8_bin NOT NULL DEFAULT '',
`Proxied_host` char(255) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL DEFAULT '',
`Proxied_user` char(32) COLLATE utf8_bin NOT NULL DEFAULT '',
`With_grant` tinyint(1) NOT NULL DEFAULT '0',
`Grantor` char(93) COLLATE utf8_bin NOT NULL DEFAULT '',
`Grantor` varchar(288) COLLATE utf8_bin NOT NULL DEFAULT '',
`Timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`Host`,`User`,`Proxied_host`,`Proxied_user`),
KEY `Grantor` (`Grantor`)
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin STATS_PERSISTENT=0 COMMENT='User proxy privileges'
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin STATS_PERSISTENT=0 ROW_FORMAT=DYNAMIC COMMENT='User proxy privileges'
mysql.role_edges
CREATE TABLE `role_edges` (
`FROM_HOST` char(60) COLLATE utf8_bin NOT NULL DEFAULT '',
`FROM_HOST` char(255) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL DEFAULT '',
`FROM_USER` char(32) COLLATE utf8_bin NOT NULL DEFAULT '',
`TO_HOST` char(60) COLLATE utf8_bin NOT NULL DEFAULT '',
`TO_HOST` char(255) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL DEFAULT '',
`TO_USER` char(32) COLLATE utf8_bin NOT NULL DEFAULT '',
`WITH_ADMIN_OPTION` enum('N','Y') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'N',
PRIMARY KEY (`FROM_HOST`,`FROM_USER`,`TO_HOST`,`TO_USER`)
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin STATS_PERSISTENT=0 COMMENT='Role hierarchy and role grants'
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin STATS_PERSISTENT=0 ROW_FORMAT=DYNAMIC COMMENT='Role hierarchy and role grants'
mysql.server_cost
CREATE TABLE `server_cost` (
@@ -183,12 +183,12 @@ CREATE TABLE `server_cost` (
`comment` varchar(1024) DEFAULT NULL,
`default_value` float GENERATED ALWAYS AS ((case `cost_name` when _utf8mb3'disk_temptable_create_cost' then 20.0 when _utf8mb3'disk_temptable_row_cost' then 0.5 when _utf8mb3'key_compare_cost' then 0.05 when _utf8mb3'memory_temptable_create_cost' then 1.0 when _utf8mb3'memory_temptable_row_cost' then 0.1 when _utf8mb3'row_evaluate_cost' then 0.1 else NULL end)) VIRTUAL,
PRIMARY KEY (`cost_name`)
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 STATS_PERSISTENT=0
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 STATS_PERSISTENT=0 ROW_FORMAT=DYNAMIC
mysql.servers
CREATE TABLE `servers` (
`Server_name` char(64) NOT NULL DEFAULT '',
`Host` char(64) NOT NULL DEFAULT '',
`Host` char(255) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL DEFAULT '',
`Db` char(64) NOT NULL DEFAULT '',
`Username` char(64) NOT NULL DEFAULT '',
`Password` char(64) NOT NULL DEFAULT '',
@@ -197,42 +197,42 @@ CREATE TABLE `servers` (
`Wrapper` char(64) NOT NULL DEFAULT '',
`Owner` char(64) NOT NULL DEFAULT '',
PRIMARY KEY (`Server_name`)
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 STATS_PERSISTENT=0 COMMENT='MySQL Foreign Servers table'
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 STATS_PERSISTENT=0 ROW_FORMAT=DYNAMIC COMMENT='MySQL Foreign Servers table'
mysql.tables_priv
CREATE TABLE `tables_priv` (
`Host` char(60) COLLATE utf8_bin NOT NULL DEFAULT '',
`Host` char(255) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL DEFAULT '',
`Db` char(64) COLLATE utf8_bin NOT NULL DEFAULT '',
`User` char(32) COLLATE utf8_bin NOT NULL DEFAULT '',
`Table_name` char(64) COLLATE utf8_bin NOT NULL DEFAULT '',
`Grantor` char(93) COLLATE utf8_bin NOT NULL DEFAULT '',
`Grantor` varchar(288) COLLATE utf8_bin NOT NULL DEFAULT '',
`Timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`Table_priv` set('Select','Insert','Update','Delete','Create','Drop','Grant','References','Index','Alter','Create View','Show view','Trigger') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`Column_priv` set('Select','Insert','Update','References') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
PRIMARY KEY (`Host`,`Db`,`User`,`Table_name`),
KEY `Grantor` (`Grantor`)
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin STATS_PERSISTENT=0 COMMENT='Table privileges'
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin STATS_PERSISTENT=0 ROW_FORMAT=DYNAMIC COMMENT='Table privileges'
mysql.time_zone
CREATE TABLE `time_zone` (
`Time_zone_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`Use_leap_seconds` enum('Y','N') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'N',
PRIMARY KEY (`Time_zone_id`)
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 STATS_PERSISTENT=0 COMMENT='Time zones'
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 STATS_PERSISTENT=0 ROW_FORMAT=DYNAMIC COMMENT='Time zones'
mysql.time_zone_leap_second
CREATE TABLE `time_zone_leap_second` (
`Transition_time` bigint(20) NOT NULL,
`Correction` int(11) NOT NULL,
PRIMARY KEY (`Transition_time`)
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 STATS_PERSISTENT=0 COMMENT='Leap seconds information for time zones'
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 STATS_PERSISTENT=0 ROW_FORMAT=DYNAMIC COMMENT='Leap seconds information for time zones'
mysql.time_zone_name
CREATE TABLE `time_zone_name` (
`Name` char(64) NOT NULL,
`Time_zone_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`Name`)
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 STATS_PERSISTENT=0 COMMENT='Time zone names'
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 STATS_PERSISTENT=0 ROW_FORMAT=DYNAMIC COMMENT='Time zone names'
mysql.time_zone_transition
CREATE TABLE `time_zone_transition` (
@@ -240,7 +240,7 @@ CREATE TABLE `time_zone_transition` (
`Transition_time` bigint(20) NOT NULL,
`Transition_type_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`Time_zone_id`,`Transition_time`)
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 STATS_PERSISTENT=0 COMMENT='Time zone transitions'
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 STATS_PERSISTENT=0 ROW_FORMAT=DYNAMIC COMMENT='Time zone transitions'
mysql.time_zone_transition_type
CREATE TABLE `time_zone_transition_type` (
@@ -250,11 +250,11 @@ CREATE TABLE `time_zone_transition_type` (
`Is_DST` tinyint(3) unsigned NOT NULL DEFAULT '0',
`Abbreviation` char(8) NOT NULL DEFAULT '',
PRIMARY KEY (`Time_zone_id`,`Transition_type_id`)
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 STATS_PERSISTENT=0 COMMENT='Time zone transition types'
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 STATS_PERSISTENT=0 ROW_FORMAT=DYNAMIC COMMENT='Time zone transition types'
mysql.user
CREATE TABLE `user` (
`Host` char(60) COLLATE utf8_bin NOT NULL DEFAULT '',
`Host` char(255) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL DEFAULT '',
`User` char(32) COLLATE utf8_bin NOT NULL DEFAULT '',
`Select_priv` enum('N','Y') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'N',
`Insert_priv` enum('N','Y') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'N',
@@ -303,8 +303,10 @@ CREATE TABLE `user` (
`Drop_role_priv` enum('N','Y') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'N',
`Password_reuse_history` smallint(5) unsigned DEFAULT NULL,
`Password_reuse_time` smallint(5) unsigned DEFAULT NULL,
`Password_require_current` enum('N','Y') CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`User_attributes` json DEFAULT NULL,
PRIMARY KEY (`Host`,`User`)
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin STATS_PERSISTENT=0 COMMENT='Users and global privileges'
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin STATS_PERSISTENT=0 ROW_FORMAT=DYNAMIC COMMENT='Users and global privileges'
percona_test.checksums
CREATE TABLE `checksums` (

View File

@@ -1,6 +1,6 @@
mysql.user
CREATE TABLE `user` (
`Host` char(60) COLLATE utf8_bin NOT NULL DEFAULT '',
`Host` char(255) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL DEFAULT '',
`User` char(32) COLLATE utf8_bin NOT NULL DEFAULT '',
`Select_priv` enum('N','Y') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'N',
`Insert_priv` enum('N','Y') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'N',
@@ -49,6 +49,8 @@ CREATE TABLE `user` (
`Drop_role_priv` enum('N','Y') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'N',
`Password_reuse_history` smallint(5) unsigned DEFAULT NULL,
`Password_reuse_time` smallint(5) unsigned DEFAULT NULL,
`Password_require_current` enum('N','Y') CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`User_attributes` json DEFAULT NULL,
PRIMARY KEY (`Host`,`User`)
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin STATS_PERSISTENT=0 COMMENT='Users and global privileges'
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin STATS_PERSISTENT=0 ROW_FORMAT=DYNAMIC COMMENT='Users and global privileges'

View File

@@ -201,19 +201,6 @@ wsrep_cluster_size 100
HandlerSocket NoSQL | Not Supported
Fast Hash UDFs | Unknown
# Percona XtraDB Cluster #####################################
Cluster Name | pt_sandbox_cluster
Cluster Address | gcomm://
Cluster Size | 3
Cluster Nodes | 192.168.0.100,192.168.0.100,192.168.0.100
Node Name | 12345
Node Status | Primary
SST Method | rsync
Slave Threads | 2
Ignore Split Brain | false
Ignore Quorum | false
gcache Size | 128M
gcache Directory | /tmp/12345/data/
gcache Name | /tmp/12345/data//galera.cache
# Plugins ####################################################
InnoDB compression | ACTIVE
# Query cache ################################################

View File

@@ -167,7 +167,6 @@ wsrep_local_index 4000000000000 45000000
HandlerSocket NoSQL | Not Supported
Fast Hash UDFs | Unknown
# Percona XtraDB Cluster #####################################
wsrep_on | OFF
# Plugins ####################################################
InnoDB compression | ACTIVE
# Query cache ################################################

View File

@@ -1,2 +1,2 @@
{"classes":[{"attribute":"fingerprint","checksum":"C29D79D8CB57E235AA8E9FA785927259","distillate":"SELECT d.t","example":{"Query_time":"0.000286","as_select":"SELECT i FROM d.t WHERE i=?","query":"PREPARE SELECT i FROM d.t WHERE i=?","ts":"2009-12-08 09:23:49.637394"},"fingerprint":"prepare select i from d.t where i=?","histograms":{"Query_time":[0,0,1,0,0,0,0,0]},"metrics":{"No_good_index_used":{"yes":"0"},"No_index_used":{"yes":"0"},"Query_length":{"avg":"35","max":"35","median":"35","min":"35","pct":"0","pct_95":"35","stddev":"0","sum":"35"},"Query_time":{"avg":"0.000286","max":"0.000286","median":"0.000286","min":"0.000286","pct":"0.333333","pct_95":"0.000286","stddev":"0.000000","sum":"0.000286"},"Statement_id":{"value":2},"Warning_count":{"avg":"0","max":"0","median":"0","min":"0","pct":"0","pct_95":"0","stddev":"0","sum":"0"},"host":{"value":"127.0.0.1"}},"query_count":1,"tables":[{"create":"SHOW CREATE TABLE `d`.`t`\\G","status":"SHOW TABLE STATUS FROM `d` LIKE 't'\\G"}],"ts_max":"2009-12-08 09:23:49.637394","ts_min":"2009-12-08 09:23:49.637394"},{"attribute":"fingerprint","checksum":"53704700F9CECAAF3F79759E7FA2F117","distillate":"SELECT d.t","example":{"Query_time":"0.000281","as_select":"SELECT i FROM d.t WHERE i=\"3\"","query":"EXECUTE SELECT i FROM d.t WHERE i=\"3\"","ts":"2009-12-08 09:23:49.637892"},"fingerprint":"execute select i from d.t where i=?","histograms":{"Query_time":[0,0,1,0,0,0,0,0]},"metrics":{"No_good_index_used":{"yes":"0"},"No_index_used":{"yes":"1"},"Query_length":{"avg":"37","max":"37","median":"37","min":"37","pct":"0","pct_95":"37","stddev":"0","sum":"37"},"Query_time":{"avg":"0.000281","max":"0.000281","median":"0.000281","min":"0.000281","pct":"0.333333","pct_95":"0.000281","stddev":"0.000000","sum":"0.000281"},"Statement_id":{"value":2},"Warning_count":{"avg":"0","max":"0","median":"0","min":"0","pct":"0","pct_95":"0","stddev":"0","sum":"0"},"host":{"value":"127.0.0.1"}},"query_count":1,"tables":[{"create":"SHOW CREATE TABLE `d`.`t`\\G","status":"SHOW TABLE STATUS FROM `d` LIKE 't'\\G"}],"ts_max":"2009-12-08 09:23:49.637892","ts_min":"2009-12-08 09:23:49.637892"},{"attribute":"fingerprint","checksum":"EDBC971AEC392917AA353644DE4C4CB4","distillate":"ADMIN QUIT","example":{"Query_time":"0.000000","query":"administrator command: Quit","ts":"2009-12-08 09:23:49.638381"},"fingerprint":"administrator command: Quit","histograms":{"Query_time":[0,0,0,0,0,0,0,0]},"metrics":{"No_good_index_used":{"yes":"0"},"No_index_used":{"yes":"0"},"Query_length":{"avg":"27","max":"27","median":"27","min":"27","pct":"0","pct_95":"27","stddev":"0","sum":"27"},"Query_time":{"avg":"0.000000","max":"0.000000","median":"0.000000","min":"0.000000","pct":"0.333333","pct_95":"0.000000","stddev":"0.000000","sum":"0.000000"},"Warning_count":{"avg":"0","max":"0","median":"0","min":"0","pct":"0","pct_95":"0","stddev":"0","sum":"0"},"host":{"value":"127.0.0.1"}},"query_count":1,"ts_max":"2009-12-08 09:23:49.638381","ts_min":"2009-12-08 09:23:49.638381"}],"global":{"files":[{"name":"tcpdump021.txt","size":2827}],"metrics":{"No_good_index_used":{"cnt":"0"},"No_index_used":{"cnt":"1"},"Query_length":{"avg":"33","max":"37","median":"34","min":"27","pct_95":"36","stddev":"4","sum":"99"},"Query_time":{"avg":"0.000189","max":"0.000286","median":"0.000273","min":"0.000000","pct_95":"0.000273","stddev":"0.000129","sum":"0.000567"},"Rows_affected":{"avg":"0","max":"0","median":"0","min":"0","pct_95":"0","stddev":"0","sum":"0"},"Warning_count":{"avg":"0","max":"0","median":"0","min":"0","pct_95":"0","stddev":"0","sum":"0"}},"query_count":3,"unique_query_count":3}}
{"classes":[{"attribute":"fingerprint","checksum":"C29D79D8CB57E235AA8E9FA785927259","distillate":"SELECT d.t","example":{"Query_time":"0.000286","as_select":"SELECT i FROM d.t WHERE i=?","query":"PREPARE SELECT i FROM d.t WHERE i=?","ts":"2009-12-08 09:23:49.637394"},"fingerprint":"prepare select i from d.t where i=?","histograms":{"Query_time":[0,0,1,0,0,0,0,0]},"metrics":{"No_good_index_used":{"yes":"0"},"No_index_used":{"yes":"0"},"Query_length":{"avg":"35","max":"35","median":"35","min":"35","pct":"0","pct_95":"35","stddev":"0","sum":"35"},"Query_time":{"avg":"0.000286","max":"0.000286","median":"0.000286","min":"0.000286","pct":"0.333333","pct_95":"0.000286","stddev":"0.000000","sum":"0.000286"},"Statement_id":{"value":2},"Warning_count":{"avg":"0","max":"0","median":"0","min":"0","pct":"0","pct_95":"0","stddev":"0","sum":"0"},"host":{"value":"127.0.0.1"}},"query_count":1,"tables":[{"create":"SHOW CREATE TABLE `d`.`t`\\G","status":"SHOW TABLE STATUS FROM `d` LIKE 't'\\G"}],"ts_max":"2009-12-08 09:23:49.637394","ts_min":"2009-12-08 09:23:49.637394"},{"attribute":"fingerprint","checksum":"53704700F9CECAAF3F79759E7FA2F117","distillate":"SELECT d.t","example":{"Query_time":"0.000281","as_select":"SELECT i FROM d.t WHERE i=\"3\"","query":"EXECUTE SELECT i FROM d.t WHERE i=\"3\"","ts":"2009-12-08 09:23:49.637892"},"fingerprint":"execute select i from d.t where i=?","histograms":{"Query_time":[0,0,1,0,0,0,0,0]},"metrics":{"No_good_index_used":{"yes":"0"},"No_index_used":{"yes":"1"},"Query_length":{"avg":"37","max":"37","median":"37","min":"37","pct":"0","pct_95":"37","stddev":"0","sum":"37"},"Query_time":{"avg":"0.000281","max":"0.000281","median":"0.000281","min":"0.000281","pct":"0.333333","pct_95":"0.000281","stddev":"0.000000","sum":"0.000281"},"Statement_id":{"value":"2"},"Warning_count":{"avg":"0","max":"0","median":"0","min":"0","pct":"0","pct_95":"0","stddev":"0","sum":"0"},"host":{"value":"127.0.0.1"}},"query_count":1,"tables":[{"create":"SHOW CREATE TABLE `d`.`t`\\G","status":"SHOW TABLE STATUS FROM `d` LIKE 't'\\G"}],"ts_max":"2009-12-08 09:23:49.637892","ts_min":"2009-12-08 09:23:49.637892"},{"attribute":"fingerprint","checksum":"EDBC971AEC392917AA353644DE4C4CB4","distillate":"ADMIN QUIT","example":{"Query_time":"0.000000","query":"administrator command: Quit","ts":"2009-12-08 09:23:49.638381"},"fingerprint":"administrator command: Quit","histograms":{"Query_time":[0,0,0,0,0,0,0,0]},"metrics":{"No_good_index_used":{"yes":"0"},"No_index_used":{"yes":"0"},"Query_length":{"avg":"27","max":"27","median":"27","min":"27","pct":"0","pct_95":"27","stddev":"0","sum":"27"},"Query_time":{"avg":"0.000000","max":"0.000000","median":"0.000000","min":"0.000000","pct":"0.333333","pct_95":"0.000000","stddev":"0.000000","sum":"0.000000"},"Warning_count":{"avg":"0","max":"0","median":"0","min":"0","pct":"0","pct_95":"0","stddev":"0","sum":"0"},"host":{"value":"127.0.0.1"}},"query_count":1,"ts_max":"2009-12-08 09:23:49.638381","ts_min":"2009-12-08 09:23:49.638381"}],"global":{"files":[{"name":"tcpdump021.txt","size":2827}],"metrics":{"No_good_index_used":{"cnt":"0"},"No_index_used":{"cnt":"1"},"Query_length":{"avg":"33","max":"37","median":"34","min":"27","pct_95":"36","stddev":"4","sum":"99"},"Query_time":{"avg":"0.000189","max":"0.000286","median":"0.000273","min":"0.000000","pct_95":"0.000273","stddev":"0.000129","sum":"0.000567"},"Rows_affected":{"avg":"0","max":"0","median":"0","min":"0","pct_95":"0","stddev":"0","sum":"0"},"Warning_count":{"avg":"0","max":"0","median":"0","min":"0","pct_95":"0","stddev":"0","sum":"0"}},"query_count":3,"unique_query_count":3}}