1package query
2
3import (
4	"encoding/json"
5	"errors"
6	"fmt"
7
8	"github.com/influxdata/influxdb/models"
9	"github.com/influxdata/influxql"
10)
11
12const (
13	// WarningLevel is the message level for a warning.
14	WarningLevel = "warning"
15)
16
17// TagSet is a fundamental concept within the query system. It represents a composite series,
18// composed of multiple individual series that share a set of tag attributes.
19type TagSet struct {
20	Tags       map[string]string
21	Filters    []influxql.Expr
22	SeriesKeys []string
23	Key        []byte
24}
25
26// AddFilter adds a series-level filter to the Tagset.
27func (t *TagSet) AddFilter(key string, filter influxql.Expr) {
28	t.SeriesKeys = append(t.SeriesKeys, key)
29	t.Filters = append(t.Filters, filter)
30}
31
32func (t *TagSet) Len() int           { return len(t.SeriesKeys) }
33func (t *TagSet) Less(i, j int) bool { return t.SeriesKeys[i] < t.SeriesKeys[j] }
34func (t *TagSet) Swap(i, j int) {
35	t.SeriesKeys[i], t.SeriesKeys[j] = t.SeriesKeys[j], t.SeriesKeys[i]
36	t.Filters[i], t.Filters[j] = t.Filters[j], t.Filters[i]
37}
38
39// Reverse reverses the order of series keys and filters in the TagSet.
40func (t *TagSet) Reverse() {
41	for i, j := 0, len(t.Filters)-1; i < j; i, j = i+1, j-1 {
42		t.Filters[i], t.Filters[j] = t.Filters[j], t.Filters[i]
43		t.SeriesKeys[i], t.SeriesKeys[j] = t.SeriesKeys[j], t.SeriesKeys[i]
44	}
45}
46
47// LimitTagSets returns a tag set list with SLIMIT and SOFFSET applied.
48func LimitTagSets(a []*TagSet, slimit, soffset int) []*TagSet {
49	// Ignore if no limit or offset is specified.
50	if slimit == 0 && soffset == 0 {
51		return a
52	}
53
54	// If offset is beyond the number of tag sets then return nil.
55	if soffset > len(a) {
56		return nil
57	}
58
59	// Clamp limit to the max number of tag sets.
60	if soffset+slimit > len(a) {
61		slimit = len(a) - soffset
62	}
63	return a[soffset : soffset+slimit]
64}
65
66// Message represents a user-facing message to be included with the result.
67type Message struct {
68	Level string `json:"level"`
69	Text  string `json:"text"`
70}
71
72// ReadOnlyWarning generates a warning message that tells the user the command
73// they are using is being used for writing in a read only context.
74//
75// This is a temporary method while to be used while transitioning to read only
76// operations for issue #6290.
77func ReadOnlyWarning(stmt string) *Message {
78	return &Message{
79		Level: WarningLevel,
80		Text:  fmt.Sprintf("deprecated use of '%s' in a read only context, please use a POST request instead", stmt),
81	}
82}
83
84// Result represents a resultset returned from a single statement.
85// Rows represents a list of rows that can be sorted consistently by name/tag.
86type Result struct {
87	// StatementID is just the statement's position in the query. It's used
88	// to combine statement results if they're being buffered in memory.
89	StatementID int
90	Series      models.Rows
91	Messages    []*Message
92	Partial     bool
93	Err         error
94}
95
96// MarshalJSON encodes the result into JSON.
97func (r *Result) MarshalJSON() ([]byte, error) {
98	// Define a struct that outputs "error" as a string.
99	var o struct {
100		StatementID int           `json:"statement_id"`
101		Series      []*models.Row `json:"series,omitempty"`
102		Messages    []*Message    `json:"messages,omitempty"`
103		Partial     bool          `json:"partial,omitempty"`
104		Err         string        `json:"error,omitempty"`
105	}
106
107	// Copy fields to output struct.
108	o.StatementID = r.StatementID
109	o.Series = r.Series
110	o.Messages = r.Messages
111	o.Partial = r.Partial
112	if r.Err != nil {
113		o.Err = r.Err.Error()
114	}
115
116	return json.Marshal(&o)
117}
118
119// UnmarshalJSON decodes the data into the Result struct
120func (r *Result) UnmarshalJSON(b []byte) error {
121	var o struct {
122		StatementID int           `json:"statement_id"`
123		Series      []*models.Row `json:"series,omitempty"`
124		Messages    []*Message    `json:"messages,omitempty"`
125		Partial     bool          `json:"partial,omitempty"`
126		Err         string        `json:"error,omitempty"`
127	}
128
129	err := json.Unmarshal(b, &o)
130	if err != nil {
131		return err
132	}
133	r.StatementID = o.StatementID
134	r.Series = o.Series
135	r.Messages = o.Messages
136	r.Partial = o.Partial
137	if o.Err != "" {
138		r.Err = errors.New(o.Err)
139	}
140	return nil
141}
142