func NewQueryPlanNoAggregation(fields []string, where string, cqlQueries []CQLQuery) (*QueryPlanNoAggregation, error) {
	return &QueryPlanNoAggregation{
		fields:     fields,
		cqlQueries: cqlQueries,
		where:      where,
	}, nil
}
NewQueryPlanNoAggregation builds a QueryPlanWithoutServerAggregation.
It is typically called via (*HLQuery).ToQueryPlanWithoutServerAggregation.
package main
import (
	"fmt"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"time"
	"github.com/gocql/gocql"
	"github.com/timescale/tsbs/internal/utils"
)
type QueryPlan interface {
	Execute(*gocql.Session) ([]CQLResult, error)
	DebugQueries(int)
}
type QueryPlanWithServerAggregation struct {
	AggregatorLabel    string
	BucketedCQLQueries map[*utils.TimeInterval][]CQLQuery
}
func NewQueryPlanWithServerAggregation(aggrLabel string, bucketedCQLQueries map[*utils.TimeInterval][]CQLQuery) (*QueryPlanWithServerAggregation, error) {
	qp := &QueryPlanWithServerAggregation{
		AggregatorLabel:    aggrLabel,
		BucketedCQLQueries: bucketedCQLQueries,
	}
	return qp, nil
}
func (qp *QueryPlanWithServerAggregation) Execute(session *gocql.Session) ([]CQLResult, error) {
	sortedKeys := make([]*utils.TimeInterval, 0, len(qp.BucketedCQLQueries))
	for k := range qp.BucketedCQLQueries {
		sortedKeys = append(sortedKeys, k)
	}
	sort.Sort(TimeIntervals(sortedKeys))
	results := make([]CQLResult, 0, len(qp.BucketedCQLQueries))
	for _, k := range sortedKeys {
		agg, err := GetAggregator(qp.AggregatorLabel)
		if err != nil {
			return nil, err
		}
		for _, q := range qp.BucketedCQLQueries[k] {
			iter := session.Query(q.PreparableQueryString, q.Args...).Iter()
			var x float64
			for iter.Scan(&x) {
				agg.Put(x)
			}
			if err := iter.Close(); err != nil {
				return nil, err
			}
		}
		results = append(results, CQLResult{TimeInterval: k, Values: []float64{agg.Get()}})
	}
	return results, nil
}
func (qp *QueryPlanWithServerAggregation) DebugQueries(level int) {
	if level >= 1 {
		n := 0
		for _, qq := range qp.BucketedCQLQueries {
			n += len(qq)
		}
		fmt.Printf("[qpsa] query with server aggregation plan has %d CQLQuery objects\n", n)
	}
	if level >= 2 {
		for k, qq := range qp.BucketedCQLQueries {
			for i, q := range qq {
				fmt.Printf("[qpsa] CQL: %v, %d, %s\n", k, i, q)
			}
		}
	}
}
type QueryPlanWithoutServerAggregation struct {
	Aggregators     map[*utils.TimeInterval]map[string]Aggregator
	GroupByDuration time.Duration
	Fields          []string
	TimeBuckets     []*utils.TimeInterval
	limit           int
	CQLQueries      []CQLQuery
}
func NewQueryPlanWithoutServerAggregation(aggrLabel string, groupByDuration time.Duration, fields []string, timeBuckets []*utils.TimeInterval, limit int, cqlQueries []CQLQuery) (*QueryPlanWithoutServerAggregation, error) {
	aggrs := make(map[*utils.TimeInterval]map[string]Aggregator, len(timeBuckets))
	for _, ti := range timeBuckets {
		if len(aggrs) > 0 && len(aggrs) == limit {
			break
		}
		aggrs[ti] = make(map[string]Aggregator)
		for _, f := range fields {
			aggr, err := GetAggregator(aggrLabel)
			if err != nil {
				return nil, err
			}
			aggrs[ti][f] = aggr
		}
	}
	qp := &QueryPlanWithoutServerAggregation{
		Aggregators:     aggrs,
		GroupByDuration: groupByDuration,
		Fields:          fields,
		TimeBuckets:     timeBuckets,
		limit:           limit,
		CQLQueries:      cqlQueries,
	}
	return qp, nil
}
func csiDebugQueries(cqlQueries []CQLQuery, label string, level int) {
	if level >= 1 {
		fmt.Printf("[%s] query with client aggregation plan has %d CQLQuery objects\n", label, len(cqlQueries))
	}
	if level >= 2 {
		for i, q := range cqlQueries {
			fmt.Printf("[%s] CQL: %d, %s\n", label, i, q)
		}
	}
}
func (qp *QueryPlanWithoutServerAggregation) Execute(session *gocql.Session) ([]CQLResult, error) {
	for _, q := range qp.CQLQueries {
		iter := session.Query(q.PreparableQueryString, q.Args...).Iter()
		var timestampNs int64
		var value float64
		for iter.Scan(&timestampNs, &value) {
			ts := time.Unix(0, timestampNs).UTC()
			tsTruncated := ts.Truncate(qp.GroupByDuration)
			bucketKey, err := utils.NewTimeInterval(tsTruncated, tsTruncated.Add(qp.GroupByDuration))
			if err != nil {
				return nil, err
			}
			if _, ok := qp.Aggregators[bucketKey]; !ok {
				break
			}
			qp.Aggregators[bucketKey][q.Field].Put(value)
		}
		if err := iter.Close(); err != nil {
			return nil, err
		}
	}
	results := make([]CQLResult, 0, len(qp.TimeBuckets))
	for _, ti := range qp.TimeBuckets {
		if _, ok := qp.Aggregators[ti]; !ok {
			continue
		}
		res := CQLResult{TimeInterval: ti, Values: make([]float64, len(qp.Fields))}
		for i, f := range qp.Fields {
			res.Values[i] = qp.Aggregators[ti][f].Get()
		}
		results = append(results, res)
	}
	return results, nil
}
func (qp *QueryPlanWithoutServerAggregation) DebugQueries(level int) {
	csiDebugQueries(qp.CQLQueries, "qpca", level)
}
type QueryPlanNoAggregation struct {
	fields     []string
	where      string
	cqlQueries []CQLQuery
}