github.com/influxdata/influxdb/v2@v2.7.6/influxql/query/response_writer.go (about)

     1  package query
     2  
     3  //lint:file-ignore SA1019 Ignore for now
     4  
     5  import (
     6  	"context"
     7  	"encoding/csv"
     8  	"encoding/json"
     9  	"io"
    10  	"strconv"
    11  	"time"
    12  
    13  	"github.com/influxdata/influxdb/v2/influxql"
    14  	"github.com/influxdata/influxdb/v2/kit/tracing"
    15  	"github.com/influxdata/influxdb/v2/models"
    16  	"github.com/tinylib/msgp/msgp"
    17  )
    18  
    19  // ResponseWriter is an interface for writing a response.
    20  type ResponseWriter interface {
    21  	// WriteResponse writes a response.
    22  	WriteResponse(ctx context.Context, w io.Writer, resp Response) error
    23  }
    24  
    25  // NewResponseWriter creates a new ResponseWriter based on the Accept header
    26  // in the request that wraps the ResponseWriter.
    27  func NewResponseWriter(encoding influxql.EncodingFormat) ResponseWriter {
    28  	switch encoding {
    29  	case influxql.EncodingFormatAppCSV, influxql.EncodingFormatTextCSV:
    30  		return &csvFormatter{statementID: -1}
    31  	case influxql.EncodingFormatMessagePack:
    32  		return &msgpFormatter{}
    33  	case influxql.EncodingFormatJSON:
    34  		fallthrough
    35  	default:
    36  		// TODO(sgc): Add EncodingFormatJSONPretty
    37  		return &jsonFormatter{Pretty: false}
    38  	}
    39  }
    40  
    41  type jsonFormatter struct {
    42  	Pretty bool
    43  }
    44  
    45  func (f *jsonFormatter) WriteResponse(ctx context.Context, w io.Writer, resp Response) (err error) {
    46  	span, _ := tracing.StartSpanFromContext(ctx)
    47  	defer span.Finish()
    48  
    49  	var b []byte
    50  	if f.Pretty {
    51  		b, err = json.MarshalIndent(resp, "", "    ")
    52  	} else {
    53  		b, err = json.Marshal(resp)
    54  	}
    55  
    56  	if err != nil {
    57  		_, err = io.WriteString(w, err.Error())
    58  	} else {
    59  		_, err = w.Write(b)
    60  	}
    61  
    62  	w.Write([]byte("\n"))
    63  	return err
    64  }
    65  
    66  type csvFormatter struct {
    67  	statementID int
    68  	columns     []string
    69  }
    70  
    71  func (f *csvFormatter) WriteResponse(ctx context.Context, w io.Writer, resp Response) (err error) {
    72  	span, _ := tracing.StartSpanFromContext(ctx)
    73  	defer span.Finish()
    74  
    75  	wr := csv.NewWriter(w)
    76  	if resp.Err != nil {
    77  		wr.Write([]string{"error"})
    78  		wr.Write([]string{resp.Err.Error()})
    79  		wr.Flush()
    80  		return wr.Error()
    81  	}
    82  
    83  	for _, result := range resp.Results {
    84  		if result.StatementID != f.statementID {
    85  			// If there are no series in the result, skip past this result.
    86  			if len(result.Series) == 0 {
    87  				continue
    88  			}
    89  
    90  			// Set the statement id and print out a newline if this is not the first statement.
    91  			if f.statementID >= 0 {
    92  				// Flush the csv writer and write a newline.
    93  				wr.Flush()
    94  				if err := wr.Error(); err != nil {
    95  					return err
    96  				}
    97  
    98  				if _, err := io.WriteString(w, "\n"); err != nil {
    99  					return err
   100  				}
   101  			}
   102  			f.statementID = result.StatementID
   103  
   104  			// Print out the column headers from the first series.
   105  			f.columns = make([]string, 2+len(result.Series[0].Columns))
   106  			f.columns[0] = "name"
   107  			f.columns[1] = "tags"
   108  			copy(f.columns[2:], result.Series[0].Columns)
   109  			if err := wr.Write(f.columns); err != nil {
   110  				return err
   111  			}
   112  		}
   113  
   114  		for i, row := range result.Series {
   115  			if i > 0 && !stringsEqual(result.Series[i-1].Columns, row.Columns) {
   116  				// The columns have changed. Print a newline and reprint the header.
   117  				wr.Flush()
   118  				if err := wr.Error(); err != nil {
   119  					return err
   120  				}
   121  
   122  				if _, err := io.WriteString(w, "\n"); err != nil {
   123  					return err
   124  				}
   125  
   126  				f.columns = make([]string, 2+len(row.Columns))
   127  				f.columns[0] = "name"
   128  				f.columns[1] = "tags"
   129  				copy(f.columns[2:], row.Columns)
   130  				if err := wr.Write(f.columns); err != nil {
   131  					return err
   132  				}
   133  			}
   134  
   135  			f.columns[0] = row.Name
   136  			f.columns[1] = ""
   137  			if len(row.Tags) > 0 {
   138  				hashKey := models.NewTags(row.Tags).HashKey()
   139  				if len(hashKey) > 0 {
   140  					f.columns[1] = string(hashKey[1:])
   141  				}
   142  			}
   143  			for _, values := range row.Values {
   144  				for i, value := range values {
   145  					if value == nil {
   146  						f.columns[i+2] = ""
   147  						continue
   148  					}
   149  
   150  					switch v := value.(type) {
   151  					case float64:
   152  						f.columns[i+2] = strconv.FormatFloat(v, 'f', -1, 64)
   153  					case int64:
   154  						f.columns[i+2] = strconv.FormatInt(v, 10)
   155  					case uint64:
   156  						f.columns[i+2] = strconv.FormatUint(v, 10)
   157  					case string:
   158  						f.columns[i+2] = v
   159  					case bool:
   160  						if v {
   161  							f.columns[i+2] = "true"
   162  						} else {
   163  							f.columns[i+2] = "false"
   164  						}
   165  					case time.Time:
   166  						f.columns[i+2] = strconv.FormatInt(v.UnixNano(), 10)
   167  					case *float64, *int64, *string, *bool:
   168  						f.columns[i+2] = ""
   169  					}
   170  				}
   171  				wr.Write(f.columns)
   172  			}
   173  		}
   174  	}
   175  	wr.Flush()
   176  	return wr.Error()
   177  }
   178  
   179  type msgpFormatter struct{}
   180  
   181  func (f *msgpFormatter) ContentType() string {
   182  	return "application/x-msgpack"
   183  }
   184  
   185  func (f *msgpFormatter) WriteResponse(ctx context.Context, w io.Writer, resp Response) (err error) {
   186  	span, _ := tracing.StartSpanFromContext(ctx)
   187  	defer span.Finish()
   188  
   189  	enc := msgp.NewWriter(w)
   190  	defer enc.Flush()
   191  
   192  	enc.WriteMapHeader(1)
   193  	if resp.Err != nil {
   194  		enc.WriteString("error")
   195  		enc.WriteString(resp.Err.Error())
   196  		return nil
   197  	} else {
   198  		enc.WriteString("results")
   199  		enc.WriteArrayHeader(uint32(len(resp.Results)))
   200  		for _, result := range resp.Results {
   201  			if result.Err != nil {
   202  				enc.WriteMapHeader(1)
   203  				enc.WriteString("error")
   204  				enc.WriteString(result.Err.Error())
   205  				continue
   206  			}
   207  
   208  			sz := 2
   209  			if len(result.Messages) > 0 {
   210  				sz++
   211  			}
   212  			if result.Partial {
   213  				sz++
   214  			}
   215  			enc.WriteMapHeader(uint32(sz))
   216  			enc.WriteString("statement_id")
   217  			enc.WriteInt(result.StatementID)
   218  			if len(result.Messages) > 0 {
   219  				enc.WriteString("messages")
   220  				enc.WriteArrayHeader(uint32(len(result.Messages)))
   221  				for _, msg := range result.Messages {
   222  					enc.WriteMapHeader(2)
   223  					enc.WriteString("level")
   224  					enc.WriteString(msg.Level)
   225  					enc.WriteString("text")
   226  					enc.WriteString(msg.Text)
   227  				}
   228  			}
   229  			enc.WriteString("series")
   230  			enc.WriteArrayHeader(uint32(len(result.Series)))
   231  			for _, series := range result.Series {
   232  				sz := 2
   233  				if series.Name != "" {
   234  					sz++
   235  				}
   236  				if len(series.Tags) > 0 {
   237  					sz++
   238  				}
   239  				if series.Partial {
   240  					sz++
   241  				}
   242  				enc.WriteMapHeader(uint32(sz))
   243  				if series.Name != "" {
   244  					enc.WriteString("name")
   245  					enc.WriteString(series.Name)
   246  				}
   247  				if len(series.Tags) > 0 {
   248  					enc.WriteString("tags")
   249  					enc.WriteMapHeader(uint32(len(series.Tags)))
   250  					for k, v := range series.Tags {
   251  						enc.WriteString(k)
   252  						enc.WriteString(v)
   253  					}
   254  				}
   255  				enc.WriteString("columns")
   256  				enc.WriteArrayHeader(uint32(len(series.Columns)))
   257  				for _, col := range series.Columns {
   258  					enc.WriteString(col)
   259  				}
   260  				enc.WriteString("values")
   261  				enc.WriteArrayHeader(uint32(len(series.Values)))
   262  				for _, values := range series.Values {
   263  					enc.WriteArrayHeader(uint32(len(values)))
   264  					for _, v := range values {
   265  						enc.WriteIntf(v)
   266  					}
   267  				}
   268  				if series.Partial {
   269  					enc.WriteString("partial")
   270  					enc.WriteBool(series.Partial)
   271  				}
   272  			}
   273  			if result.Partial {
   274  				enc.WriteString("partial")
   275  				enc.WriteBool(true)
   276  			}
   277  		}
   278  	}
   279  	return nil
   280  }
   281  
   282  func stringsEqual(a, b []string) bool {
   283  	if len(a) != len(b) {
   284  		return false
   285  	}
   286  	for i := range a {
   287  		if a[i] != b[i] {
   288  			return false
   289  		}
   290  	}
   291  	return true
   292  }