Skip to content

Commit 54f35fb

Browse files
committed
Handle new pg_stat_statements column names
Update pg_stat_statements collector to handle the new column names in PostgreSQL 13. Fixes: #502 Signed-off-by: SuperQ <superq@gmail.com>
1 parent 74800f4 commit 54f35fb

File tree

2 files changed

+75
-5
lines changed

2 files changed

+75
-5
lines changed

Diff for: collector/pg_stat_statements.go

+28-2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"context"
1818
"database/sql"
1919

20+
"github.com/blang/semver/v4"
2021
"github.com/go-kit/log"
2122
"github.com/prometheus/client_golang/prometheus"
2223
)
@@ -90,12 +91,37 @@ var (
9091
)
9192
ORDER BY seconds_total DESC
9293
LIMIT 100;`
94+
95+
pgStatStatementsNewQuery = `SELECT
96+
pg_get_userbyid(userid) as user,
97+
pg_database.datname,
98+
pg_stat_statements.queryid,
99+
pg_stat_statements.calls as calls_total,
100+
pg_stat_statements.total_exec_time / 1000.0 as seconds_total,
101+
pg_stat_statements.rows as rows_total,
102+
pg_stat_statements.blk_read_time / 1000.0 as block_read_seconds_total,
103+
pg_stat_statements.blk_write_time / 1000.0 as block_write_seconds_total
104+
FROM pg_stat_statements
105+
JOIN pg_database
106+
ON pg_database.oid = pg_stat_statements.dbid
107+
WHERE
108+
total_time > (
109+
SELECT percentile_cont(0.1)
110+
WITHIN GROUP (ORDER BY total_time)
111+
FROM pg_stat_statements
112+
)
113+
ORDER BY seconds_total DESC
114+
LIMIT 100;`
93115
)
94116

95117
func (PGStatStatementsCollector) Update(ctx context.Context, instance *instance, ch chan<- prometheus.Metric) error {
118+
query := pgStatStatementsQuery
119+
if instance.version.GE(semver.MustParse("13.0.0")) {
120+
query = pgStatStatementsNewQuery
121+
}
122+
96123
db := instance.getDB()
97-
rows, err := db.QueryContext(ctx,
98-
pgStatStatementsQuery)
124+
rows, err := db.QueryContext(ctx, query)
99125

100126
if err != nil {
101127
return err

Diff for: collector/pg_stat_statements_test.go

+47-3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"testing"
1818

1919
"github.com/DATA-DOG/go-sqlmock"
20+
"github.com/blang/semver/v4"
2021
"github.com/prometheus/client_golang/prometheus"
2122
dto "github.com/prometheus/client_model/go"
2223
"github.com/smartystreets/goconvey/convey"
@@ -29,7 +30,7 @@ func TestPGStateStatementsCollector(t *testing.T) {
2930
}
3031
defer db.Close()
3132

32-
inst := &instance{db: db}
33+
inst := &instance{db: db, version: semver.MustParse("12.0.0")}
3334

3435
columns := []string{"user", "datname", "queryid", "calls_total", "seconds_total", "rows_total", "block_read_seconds_total", "block_write_seconds_total"}
3536
rows := sqlmock.NewRows(columns).
@@ -72,12 +73,12 @@ func TestPGStateStatementsCollectorNull(t *testing.T) {
7273
}
7374
defer db.Close()
7475

75-
inst := &instance{db: db}
76+
inst := &instance{db: db, version: semver.MustParse("13.3.7")}
7677

7778
columns := []string{"user", "datname", "queryid", "calls_total", "seconds_total", "rows_total", "block_read_seconds_total", "block_write_seconds_total"}
7879
rows := sqlmock.NewRows(columns).
7980
AddRow(nil, nil, nil, nil, nil, nil, nil, nil)
80-
mock.ExpectQuery(sanitizeQuery(pgStatStatementsQuery)).WillReturnRows(rows)
81+
mock.ExpectQuery(sanitizeQuery(pgStatStatementsNewQuery)).WillReturnRows(rows)
8182

8283
ch := make(chan prometheus.Metric)
8384
go func() {
@@ -107,3 +108,46 @@ func TestPGStateStatementsCollectorNull(t *testing.T) {
107108
t.Errorf("there were unfulfilled exceptions: %s", err)
108109
}
109110
}
111+
112+
func TestPGStateStatementsCollectorNewPG(t *testing.T) {
113+
db, mock, err := sqlmock.New()
114+
if err != nil {
115+
t.Fatalf("Error opening a stub db connection: %s", err)
116+
}
117+
defer db.Close()
118+
119+
inst := &instance{db: db, version: semver.MustParse("13.3.7")}
120+
121+
columns := []string{"user", "datname", "queryid", "calls_total", "seconds_total", "rows_total", "block_read_seconds_total", "block_write_seconds_total"}
122+
rows := sqlmock.NewRows(columns).
123+
AddRow("postgres", "postgres", 1500, 5, 0.4, 100, 0.1, 0.2)
124+
mock.ExpectQuery(sanitizeQuery(pgStatStatementsNewQuery)).WillReturnRows(rows)
125+
126+
ch := make(chan prometheus.Metric)
127+
go func() {
128+
defer close(ch)
129+
c := PGStatStatementsCollector{}
130+
131+
if err := c.Update(context.Background(), inst, ch); err != nil {
132+
t.Errorf("Error calling PGStatStatementsCollector.Update: %s", err)
133+
}
134+
}()
135+
136+
expected := []MetricResult{
137+
{labels: labelMap{"user": "postgres", "datname": "postgres", "queryid": "1500"}, metricType: dto.MetricType_COUNTER, value: 5},
138+
{labels: labelMap{"user": "postgres", "datname": "postgres", "queryid": "1500"}, metricType: dto.MetricType_COUNTER, value: 0.4},
139+
{labels: labelMap{"user": "postgres", "datname": "postgres", "queryid": "1500"}, metricType: dto.MetricType_COUNTER, value: 100},
140+
{labels: labelMap{"user": "postgres", "datname": "postgres", "queryid": "1500"}, metricType: dto.MetricType_COUNTER, value: 0.1},
141+
{labels: labelMap{"user": "postgres", "datname": "postgres", "queryid": "1500"}, metricType: dto.MetricType_COUNTER, value: 0.2},
142+
}
143+
144+
convey.Convey("Metrics comparison", t, func() {
145+
for _, expect := range expected {
146+
m := readMetric(<-ch)
147+
convey.So(expect, convey.ShouldResemble, m)
148+
}
149+
})
150+
if err := mock.ExpectationsWereMet(); err != nil {
151+
t.Errorf("there were unfulfilled exceptions: %s", err)
152+
}
153+
}

0 commit comments

Comments
 (0)