@@ -4,10 +4,34 @@ import (
44 "fmt"
55 "os"
66 "path"
7+ "regexp"
8+ "strconv"
9+ "strings"
710
811 "gopkg.in/yaml.v2"
912)
1013
14+ // Column usage types
15+ const (
16+ Discard ColumnUsage = iota // Ignore this column
17+ Label // Use this column as a label
18+ Counter // Use this column as a counter
19+ Gauge // Use this column as a gauge
20+
21+ NoVersion PgVersion = - 1
22+ )
23+
24+ var (
25+ pgVerRegex = regexp .MustCompile (`^(\d+)(?:\.(\d+))?(?:\.(\d+))?$` )
26+
27+ columnUsageMapping = map [string ]ColumnUsage {
28+ "DISCARD" : Discard ,
29+ "LABEL" : Label ,
30+ "COUNTER" : Counter ,
31+ "GAUGE" : Gauge ,
32+ }
33+ )
34+
1135// ConfigInterface describes Config methods
1236type ConfigInterface interface {
1337 Load () error
@@ -21,6 +45,189 @@ type Config struct {
2145 dbs map [string ]DbConfig
2246}
2347
48+ // ColumnUsage describes column usage
49+ type ColumnUsage int
50+
51+ // PgVersion describes version in server_version_num format
52+ type PgVersion int
53+
54+ // Metrics describe metrics
55+ type Metrics map [string ]Metric
56+
57+ // VerSQLs contain version specific SQLs
58+ type VerSQLs []VerSQL
59+
60+ // Metric describes metric
61+ type Metric struct {
62+ Usage ColumnUsage `yaml:"usage"`
63+ Description string `yaml:"description"`
64+ }
65+
66+ // VerSQL describes PostgreSQL version specific SQL
67+ type VerSQL struct {
68+ SQL string
69+ MinVer PgVersion
70+ MaxVer PgVersion
71+ }
72+
73+ // Query describes query
74+ type Query struct {
75+ Name string
76+ Metrics Metrics `yaml:"metrics"`
77+ VerSQL VerSQLs `yaml:"query"`
78+ NameColumn string `yaml:"nameColumn"`
79+ ValueColumn string `yaml:"valueColumn"`
80+ }
81+
82+ // UnmarshalYAML unmarshals the yaml
83+ func (v * VerSQLs ) UnmarshalYAML (unmarshal func (interface {}) error ) error {
84+ res := make (VerSQLs , 0 )
85+ var val interface {}
86+
87+ err := unmarshal (& val )
88+ if err != nil {
89+ return fmt .Errorf ("could not unmarshal: %v" , err )
90+ }
91+
92+ switch val := val .(type ) {
93+ case map [interface {}]interface {}:
94+ for k , v := range val {
95+ minPg , maxPg := parseVersionRange (fmt .Sprintf ("%v" , k ))
96+ res = append (res , VerSQL {
97+ MinVer : minPg ,
98+ MaxVer : maxPg ,
99+ SQL : v .(string ),
100+ })
101+ }
102+ case interface {}:
103+ res = append (res , VerSQL {
104+ SQL : val .(string ),
105+ })
106+ }
107+
108+ * v = res
109+
110+ return nil
111+ }
112+
113+ // UnmarshalYAML unmarshals the yaml
114+ func (c * ColumnUsage ) UnmarshalYAML (unmarshal func (interface {}) error ) error {
115+ var value string
116+ unmarshal (& value )
117+ cu , ok := columnUsageMapping [value ]
118+ if ! ok {
119+ return fmt .Errorf ("unknown usage: %v" , value )
120+ }
121+
122+ * c = cu
123+
124+ return nil
125+ }
126+
127+ // UnmarshalYAML unmarshals the yaml
128+ func (m * Metrics ) UnmarshalYAML (unmarshal func (interface {}) error ) error {
129+ value := make (map [string ]Metric , 0 )
130+ queryMetrics := make ([]map [string ]Metric , 0 )
131+
132+ if err := unmarshal (& queryMetrics ); err != nil {
133+ return err
134+ }
135+
136+ for _ , metrics := range queryMetrics {
137+ for name , descr := range metrics {
138+ value [name ] = descr
139+ }
140+ }
141+
142+ * m = value
143+
144+ return nil
145+ }
146+
147+ // UnmarshalYAML unmarshals the yaml
148+ func (v * PgVersion ) UnmarshalYAML (unmarshal func (interface {}) error ) error {
149+ var str string
150+ if err := unmarshal (& str ); err != nil {
151+ return err
152+ }
153+
154+ val := strings .Replace (str , "." , "" , - 1 )
155+ intVal , err := strconv .Atoi (val )
156+ if err != nil {
157+ return fmt .Errorf ("could not convert string: %v" , err )
158+ }
159+ * v = PgVersion (intVal )
160+ return nil
161+ }
162+
163+ // PgVersion returns string representation of the version
164+ func (v PgVersion ) String () string {
165+ return fmt .Sprintf ("%d.%d.%d" , v / 10000 , (v / 100 )% 100 , v % 100 )
166+ }
167+
168+ // Query returns query for the requested postgresql version
169+ func (v VerSQLs ) Query (version PgVersion ) string {
170+ if version == NoVersion ||
171+ (len (v ) == 1 && v [0 ].MaxVer == PgVersion (0 ) && v [0 ].MinVer == PgVersion (0 )) {
172+ return v [0 ].SQL
173+ }
174+
175+ for _ , query := range v {
176+ if (version >= query .MinVer || query .MinVer == 0 ) && (version < query .MaxVer || query .MaxVer == 0 ) {
177+ return query .SQL
178+ }
179+ }
180+
181+ return ""
182+ }
183+
184+ func parseVersion (str string ) PgVersion {
185+ var res int
186+ matches := pgVerRegex .FindStringSubmatch (str )
187+ if matches == nil {
188+ return PgVersion (res )
189+ }
190+ if matches [1 ] != "" {
191+ val , _ := strconv .Atoi (matches [1 ])
192+ res = val * 10000
193+ if val > 9 && matches [2 ] != "" {
194+ val , _ := strconv .Atoi (matches [2 ])
195+ res += val
196+ } else if matches [2 ] != "" {
197+ val , _ := strconv .Atoi (matches [2 ])
198+ res += val * 100
199+ if matches [3 ] != "" {
200+ val , _ := strconv .Atoi (matches [3 ])
201+ res += val
202+ }
203+ }
204+ }
205+
206+ return PgVersion (res )
207+ }
208+
209+ func parseVersionRange (str string ) (PgVersion , PgVersion ) {
210+ var min , max PgVersion
211+ if str == "" {
212+ return min , max
213+ }
214+
215+ parts := strings .Split (str , "-" )
216+ if len (parts ) == 1 {
217+ min = parseVersion (parts [0 ])
218+ max = min
219+ } else {
220+ if parts [0 ] != "" {
221+ min = parseVersion (parts [0 ])
222+ }
223+ if parts [1 ] != "" {
224+ max = parseVersion (parts [1 ])
225+ }
226+ }
227+
228+ return min , max
229+ }
230+
24231// New creates new config
25232func New (filename string ) * Config {
26233 cfg := Config {
0 commit comments