Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions internal/query/wal.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@ const (
"FROM pg_stat_wal"
)

// SelectStatWALQuery returns the proper query and column count for pg_stat_wal based on PG version.
func SelectStatWALQuery(version int) (string, int) {
// SelectStatWALQuery returns the proper query, column count and diff interval for pg_stat_wal based on PG version.
func SelectStatWALQuery(version int) (string, int, [2]int) {
if version >= 180000 {
return PgStatWALPG18, 7
// PG 18 removed wal_write/wal_sync columns; stats_age is col 6 and must not be diffed.
return PgStatWALPG18, 7, [2]int{2, 5}
}
return PgStatWALDefault, 11
// cols 2-9: wal,KiB..buffers_full; col 10 (stats_age) excluded.
return PgStatWALDefault, 11, [2]int{2, 9}
}
26 changes: 24 additions & 2 deletions internal/query/wal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,35 @@ import (
"testing"
)

// Test_StatWALQueries tests query, executing it against all supported Postgres versions.
func Test_SelectStatWALQuery(t *testing.T) {
testcases := []struct {
version int
wantNcols int
wantDiffIntvl [2]int
}{
{version: 140000, wantNcols: 11, wantDiffIntvl: [2]int{2, 9}},
{version: 150000, wantNcols: 11, wantDiffIntvl: [2]int{2, 9}},
{version: 170000, wantNcols: 11, wantDiffIntvl: [2]int{2, 9}},
// PG 18: removed wal_write/wal_sync; stats_age must be outside the diff interval.
{version: 180000, wantNcols: 7, wantDiffIntvl: [2]int{2, 5}},
}

for _, tc := range testcases {
t.Run(fmt.Sprintf("version/%d", tc.version), func(t *testing.T) {
_, gotNcols, gotDiffIntvl := SelectStatWALQuery(tc.version)
assert.Equal(t, tc.wantNcols, gotNcols)
assert.Equal(t, tc.wantDiffIntvl, gotDiffIntvl)
})
}
}

// Test_StatWALQueries tests query execution against all supported Postgres versions.
func Test_StatWALQueries(t *testing.T) {
versions := []int{140000, 150000, 160000, 170000, 180000}

for _, version := range versions {
t.Run(fmt.Sprintf("pg_stat_wal/%d", version), func(t *testing.T) {
tmpl, _ := SelectStatWALQuery(version)
tmpl, _, _ := SelectStatWALQuery(version)

opts := NewOptions(version, "f", "off", 256, "public")
q, err := Format(tmpl, opts)
Expand Down
47 changes: 47 additions & 0 deletions internal/stat/postgres_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,53 @@ func Test_diff(t *testing.T) {
assert.Error(t, err)
}

// Test_diff_pg18_wal_stats_age reproduces issue #132: when showing WAL statistics on PG 18,
// the stats_age column ('19 days 02:52:00') was incorrectly included in the diff interval
// and caused a parse error. The correct DiffIntvl for PG 18 WAL view is [2, 5].
func Test_diff_pg18_wal_stats_age(t *testing.T) {
// PG 18 pg_stat_wal columns: source, waldir_size, wal_KiB, records, fpi, buffers_full, stats_age
prev := PGresult{
Valid: true, Ncols: 7, Nrows: 1,
Cols: []string{"source", "waldir_size", "wal,KiB", "records", "fpi", "buffers_full", "stats_age"},
Values: [][]sql.NullString{
{
{String: "WAL", Valid: true},
{String: "64 MB", Valid: true},
{String: "12000.00", Valid: true},
{String: "1000", Valid: true},
{String: "50", Valid: true},
{String: "10", Valid: true},
{String: "19 days 02:52:00", Valid: true},
},
},
}
curr := PGresult{
Valid: true, Ncols: 7, Nrows: 1,
Cols: []string{"source", "waldir_size", "wal,KiB", "records", "fpi", "buffers_full", "stats_age"},
Values: [][]sql.NullString{
{
{String: "WAL", Valid: true},
{String: "64 MB", Valid: true},
{String: "12100.00", Valid: true},
{String: "1010", Valid: true},
{String: "55", Valid: true},
{String: "11", Valid: true},
{String: "19 days 03:01:38", Valid: true},
},
},
}

// Old (buggy) interval [2, 9]: stats_age at col 6 is inside the diff range and causes parse error.
_, err := diff(curr, prev, 1, [2]int{2, 9}, 0)
assert.Error(t, err, "stats_age should not be diffable with interval [2,9] on PG18 WAL data")

// Correct interval [2, 5]: stats_age at col 6 is outside the diff range.
got, err := diff(curr, prev, 1, [2]int{2, 5}, 0)
assert.NoError(t, err)
assert.Equal(t, "19 days 03:01:38", got.Values[0][6].String, "stats_age should be copied as-is")
assert.Equal(t, "100.00", got.Values[0][2].String, "wal,KiB delta should be computed")
}

func Test_sort(t *testing.T) {
res := newTestPGresult()
testcases := []struct {
Expand Down
2 changes: 1 addition & 1 deletion internal/view/view.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ func (v Views) Configure(opts query.Options) error {
view.QueryTmpl = query.SelectStatStatementsTimingQuery(opts.Version)
v[k] = view
case "wal":
view.QueryTmpl, view.Ncols = query.SelectStatWALQuery(opts.Version)
view.QueryTmpl, view.Ncols, view.DiffIntvl = query.SelectStatWALQuery(opts.Version)
v[k] = view
}
}
Expand Down
Loading