github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/sqle/statsnoms/iter.go (about)

     1  // Copyright 2024 Dolthub, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package statsnoms
    16  
    17  import (
    18  	"fmt"
    19  	"strings"
    20  	"time"
    21  
    22  	"github.com/dolthub/go-mysql-server/sql"
    23  	"github.com/dolthub/go-mysql-server/sql/planbuilder"
    24  	"github.com/dolthub/go-mysql-server/sql/stats"
    25  	"gopkg.in/errgo.v2/errors"
    26  
    27  	"github.com/dolthub/dolt/go/libraries/doltcore/schema"
    28  	"github.com/dolthub/dolt/go/store/hash"
    29  	"github.com/dolthub/dolt/go/store/prolly"
    30  	"github.com/dolthub/dolt/go/store/prolly/tree"
    31  	"github.com/dolthub/dolt/go/store/val"
    32  )
    33  
    34  var ErrIncompatibleVersion = errors.New("client stats version mismatch")
    35  
    36  func NewStatsIter(ctx *sql.Context, m prolly.Map) (*statsIter, error) {
    37  	iter, err := m.IterAll(ctx)
    38  	if err != nil {
    39  		return nil, err
    40  	}
    41  	kd, vd := m.Descriptors()
    42  	keyBuilder := val.NewTupleBuilder(kd)
    43  	valueBuilder := val.NewTupleBuilder(vd)
    44  	ns := m.NodeStore()
    45  
    46  	return &statsIter{
    47  		iter:  iter,
    48  		kb:    keyBuilder,
    49  		vb:    valueBuilder,
    50  		ns:    ns,
    51  		planb: planbuilder.New(ctx, nil, sql.NewMysqlParser()),
    52  	}, nil
    53  }
    54  
    55  // statsIter reads histogram buckets into string-compatible types.
    56  // Values that are SQL rows should be converted with statsIter.ParseRow.
    57  // todo: make a JSON compatible container for sql.Row w/ types so that we
    58  // can eagerly convert to sql.Row without sacrificing string printing.
    59  type statsIter struct {
    60  	iter         prolly.MapIter
    61  	kb, vb       *val.TupleBuilder
    62  	ns           tree.NodeStore
    63  	planb        *planbuilder.Builder
    64  	currentQual  string
    65  	currentTypes []sql.Type
    66  }
    67  
    68  var _ sql.RowIter = (*statsIter)(nil)
    69  
    70  func (s *statsIter) Next(ctx *sql.Context) (sql.Row, error) {
    71  	k, v, err := s.iter.Next(ctx)
    72  	if err != nil {
    73  		return nil, err
    74  	}
    75  
    76  	// deserialize K, V
    77  	version, err := tree.GetField(ctx, s.vb.Desc, 0, v, s.ns)
    78  	if err != nil {
    79  		return nil, err
    80  	}
    81  	if version != schema.StatsVersion {
    82  		return nil, fmt.Errorf("%w: write version %d does not match read version %d", ErrIncompatibleVersion, version, schema.StatsVersion)
    83  	}
    84  
    85  	var row sql.Row
    86  	for i := 0; i < s.kb.Desc.Count(); i++ {
    87  		f, err := tree.GetField(ctx, s.kb.Desc, i, k, s.ns)
    88  		if err != nil {
    89  			return nil, err
    90  		}
    91  		row = append(row, f)
    92  	}
    93  
    94  	for i := 0; i < s.vb.Desc.Count(); i++ {
    95  		f, err := tree.GetField(ctx, s.vb.Desc, i, v, s.ns)
    96  		if err != nil {
    97  			return nil, err
    98  		}
    99  		row = append(row, f)
   100  	}
   101  
   102  	dbName := row[schema.StatsDbTag].(string)
   103  	tableName := row[schema.StatsTableTag].(string)
   104  	indexName := row[schema.StatsIndexTag].(string)
   105  	position := row[schema.StatsPositionTag].(int64)
   106  	_ = row[schema.StatsVersionTag]
   107  	commit := hash.Parse(row[schema.StatsCommitHashTag].(string))
   108  	rowCount := row[schema.StatsRowCountTag].(int64)
   109  	distinctCount := row[schema.StatsDistinctCountTag].(int64)
   110  	nullCount := row[schema.StatsNullCountTag].(int64)
   111  	columnsStr := row[schema.StatsColumnsTag].(string)
   112  	typesStr := row[schema.StatsTypesTag].(string)
   113  	upperBoundStr := row[schema.StatsUpperBoundTag].(string)
   114  	upperBoundCnt := row[schema.StatsUpperBoundCntTag].(int64)
   115  	createdAt := row[schema.StatsCreatedAtTag].(time.Time)
   116  
   117  	typs := strings.Split(typesStr, ",")
   118  	for i, t := range typs {
   119  		typs[i] = strings.TrimSpace(t)
   120  	}
   121  
   122  	qual := sql.NewStatQualifier(dbName, tableName, indexName)
   123  	if curQual := qual.String(); !strings.EqualFold(curQual, s.currentQual) {
   124  		s.currentQual = curQual
   125  		s.currentTypes, err = stats.ParseTypeStrings(typs)
   126  		if err != nil {
   127  			return nil, err
   128  		}
   129  	}
   130  
   131  	mcvCountsStr := row[schema.StatsMcvCountsTag].(string)
   132  
   133  	numMcvs := schema.StatsMcvCountsTag - schema.StatsMcv1Tag
   134  	mcvs := make([]string, numMcvs)
   135  	for i, v := range row[schema.StatsMcv1Tag:schema.StatsMcvCountsTag] {
   136  		if v != nil {
   137  			mcvs[i] = v.(string)
   138  		}
   139  	}
   140  
   141  	return sql.Row{
   142  		dbName,
   143  		tableName,
   144  		indexName,
   145  		int(position),
   146  		version,
   147  		commit.String(),
   148  		uint64(rowCount),
   149  		uint64(distinctCount),
   150  		uint64(nullCount),
   151  		columnsStr,
   152  		typesStr,
   153  		upperBoundStr,
   154  		uint64(upperBoundCnt),
   155  		createdAt,
   156  		mcvs[0], mcvs[1], mcvs[2], mcvs[3],
   157  		mcvCountsStr,
   158  	}, nil
   159  }
   160  
   161  func (s *statsIter) ParseRow(rowStr string) (sql.Row, error) {
   162  	var row sql.Row
   163  	for i, v := range strings.Split(rowStr, ",") {
   164  		val, _, err := s.currentTypes[i].Convert(v)
   165  		if err != nil {
   166  			return nil, err
   167  		}
   168  		row = append(row, val)
   169  	}
   170  	return row, nil
   171  }
   172  
   173  func (s *statsIter) Close(context *sql.Context) error {
   174  	return nil
   175  }