github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/doltcore/doltdocs/docs_table.go (about)

     1  // Copyright 2020 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 doltdocs
    16  
    17  import (
    18  	"context"
    19  	"errors"
    20  	"fmt"
    21  	"strconv"
    22  
    23  	"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
    24  	"github.com/dolthub/dolt/go/libraries/doltcore/row"
    25  	"github.com/dolthub/dolt/go/libraries/doltcore/schema"
    26  	"github.com/dolthub/dolt/go/libraries/doltcore/schema/encoding"
    27  	"github.com/dolthub/dolt/go/libraries/doltcore/table"
    28  	"github.com/dolthub/dolt/go/libraries/doltcore/table/typed/noms"
    29  	"github.com/dolthub/dolt/go/store/types"
    30  )
    31  
    32  // updateDocsTable takes in docTbl param and updates it with the value in docs. It returns the updated table.
    33  func updateDocsTable(ctx context.Context, docTbl *doltdb.Table, docs Docs) (*doltdb.Table, error) {
    34  	m, err := docTbl.GetRowData(ctx)
    35  	if err != nil {
    36  		return nil, err
    37  	}
    38  
    39  	sch, err := docTbl.GetSchema(context.Background())
    40  	if err != nil {
    41  		return nil, err
    42  	}
    43  
    44  	me := m.Edit()
    45  	for _, doc := range docs {
    46  		key, err := docTblKeyFromName(docTbl.Format(), doc.DocPk)
    47  		if err != nil {
    48  			return nil, err
    49  		}
    50  
    51  		docRow, exists, err := table.GetRow(ctx, docTbl, sch, key)
    52  		if err != nil {
    53  			return nil, err
    54  		}
    55  
    56  		if exists && doc.Text == nil {
    57  			me = me.Remove(docRow.NomsMapKey(sch))
    58  		} else if doc.Text != nil {
    59  			docTaggedVals := row.TaggedValues{
    60  				schema.DocNameTag: types.String(doc.DocPk),
    61  				schema.DocTextTag: types.String(doc.Text),
    62  			}
    63  			docRow, err = row.New(types.Format_Default, sch, docTaggedVals)
    64  			if err != nil {
    65  				return nil, err
    66  			}
    67  			me = me.Set(docRow.NomsMapKey(sch), docRow.NomsMapValue(sch))
    68  		}
    69  	}
    70  	updatedMap, err := me.Map(ctx)
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  	if updatedMap.Len() == 0 {
    75  		return nil, ErrEmptyDocsTable
    76  	}
    77  
    78  	docTbl, err = docTbl.UpdateRows(ctx, updatedMap)
    79  
    80  	return docTbl, err
    81  }
    82  
    83  // createDocsTable creates a new in memory table that stores the given doc details.
    84  func createDocsTable(ctx context.Context, vrw types.ValueReadWriter, docs Docs) (*doltdb.Table, error) {
    85  	imt := table.NewInMemTable(Schema)
    86  
    87  	// Determines if the table needs to be created at all and initializes a schema if it does.
    88  	createTable := false
    89  	for _, doc := range docs {
    90  		if doc.Text != nil {
    91  			createTable = true
    92  			docTaggedVals := row.TaggedValues{
    93  				schema.DocNameTag: types.String(doc.DocPk),
    94  				schema.DocTextTag: types.String(doc.Text),
    95  			}
    96  
    97  			docRow, err := row.New(types.Format_Default, Schema, docTaggedVals)
    98  			if err != nil {
    99  				return nil, err
   100  			}
   101  			err = imt.AppendRow(docRow)
   102  			if err != nil {
   103  				return nil, err
   104  			}
   105  		}
   106  	}
   107  
   108  	if createTable {
   109  		rd := table.NewInMemTableReader(imt)
   110  		wr := noms.NewNomsMapCreator(context.Background(), vrw, Schema)
   111  
   112  		_, _, err := table.PipeRows(context.Background(), rd, wr, false)
   113  		if err != nil {
   114  			return nil, err
   115  		}
   116  		rd.Close(context.Background())
   117  		wr.Close(context.Background())
   118  
   119  		schVal, err := encoding.MarshalSchemaAsNomsValue(ctx, vrw, wr.GetSchema())
   120  
   121  		if err != nil {
   122  			return nil, ErrMarshallingSchema
   123  		}
   124  
   125  		empty, err := types.NewMap(ctx, vrw)
   126  		if err != nil {
   127  			return nil, err
   128  		}
   129  
   130  		newDocsTbl, err := doltdb.NewTable(ctx, vrw, schVal, wr.GetMap(), empty, nil)
   131  		if err != nil {
   132  			return nil, err
   133  		}
   134  
   135  		return newDocsTbl, nil
   136  	}
   137  
   138  	return nil, nil
   139  }
   140  
   141  // CreateOrUpdateDocsTable takes a root value and a set of docs and either creates the docs table or updates it with docs.
   142  func CreateOrUpdateDocsTable(ctx context.Context, root *doltdb.RootValue, docs Docs) (*doltdb.Table, error) {
   143  	docsTbl, found, err := root.GetTable(ctx, doltdb.DocTableName)
   144  	if err != nil {
   145  		return nil, err
   146  	}
   147  
   148  	if found {
   149  		return updateDocsTable(ctx, docsTbl, docs)
   150  	} else {
   151  		return createDocsTable(ctx, root.VRW(), docs)
   152  	}
   153  }
   154  
   155  func docTblKeyFromName(fmt *types.NomsBinFormat, name string) (types.Tuple, error) {
   156  	return types.NewTuple(fmt, types.Uint(schema.DocNameTag), types.String(name))
   157  }
   158  
   159  // getDocTextFromTbl returns the Text field of a doc using the provided table and schema and primary key.
   160  func getDocTextFromTbl(ctx context.Context, tbl *doltdb.Table, sch *schema.Schema, docPk string) ([]byte, error) {
   161  	if tbl != nil && sch != nil {
   162  		key, err := docTblKeyFromName(tbl.Format(), docPk)
   163  		if err != nil {
   164  			return nil, err
   165  		}
   166  
   167  		docRow, ok, err := getDocRow(ctx, tbl, *sch, key)
   168  		if err != nil {
   169  			return nil, err
   170  		}
   171  		if ok {
   172  			docValue, _ := docRow.GetColVal(schema.DocTextTag)
   173  			return []byte(docValue.(types.String)), nil
   174  		} else {
   175  			return nil, nil
   176  		}
   177  	} else {
   178  		return nil, nil
   179  	}
   180  }
   181  
   182  // getDocRow returns the associated row of a particular doc from the docTbl given.
   183  func getDocRow(ctx context.Context, docTbl *doltdb.Table, sch schema.Schema, key types.Tuple) (r row.Row, ok bool, err error) {
   184  	rowMap, err := docTbl.GetRowData(ctx)
   185  	if err != nil {
   186  		return nil, false, err
   187  	}
   188  
   189  	var fields types.Value
   190  	fields, ok, err = rowMap.MaybeGet(ctx, key)
   191  	if err != nil || !ok {
   192  		return nil, ok, err
   193  	}
   194  
   195  	r, err = row.FromNoms(sch, key, fields.(types.Tuple))
   196  	return r, ok, err
   197  }
   198  
   199  // getDocTextFromRow updates return the text field of a provided row.
   200  func getDocTextFromRow(r row.Row) ([]byte, error) {
   201  	docValue, ok := r.GetColVal(schema.DocTextTag)
   202  	if !ok {
   203  		return nil, nil
   204  	} else {
   205  		docValStr, err := strconv.Unquote(docValue.HumanReadableString())
   206  		if err != nil {
   207  			return nil, err
   208  		}
   209  		return []byte(docValStr), nil
   210  	}
   211  }
   212  
   213  // getDocPKFromRow updates returns the docPk field of a given row.
   214  func getDocPKFromRow(r row.Row) (string, error) {
   215  	colVal, _ := r.GetColVal(schema.DocNameTag)
   216  	if colVal == nil {
   217  		return "", nil
   218  	} else {
   219  		docName, err := strconv.Unquote(colVal.HumanReadableString())
   220  		if err != nil {
   221  			return "", err
   222  		}
   223  
   224  		return docName, nil
   225  	}
   226  }
   227  
   228  // getFileFromDoc returns the file obj associated with the doc
   229  func getFileFromDoc(docName string) (string, error) {
   230  	if doc, ok := IsSupportedDoc(docName); ok {
   231  		return doc.File, nil
   232  	}
   233  
   234  	return "", fmt.Errorf("Doc name not provided %s", docName)
   235  }
   236  
   237  // GetAllDocs takes a root value and returns all the docs available in the root.
   238  func GetAllDocs(ctx context.Context, root *doltdb.RootValue) (Docs, bool, error) {
   239  	if root == nil {
   240  		return nil, false, nil
   241  	}
   242  
   243  	docsTbl, found, err := root.GetTable(ctx, doltdb.DocTableName)
   244  	if err != nil {
   245  		return nil, false, err
   246  	}
   247  
   248  	if !found {
   249  		return nil, false, err
   250  	}
   251  
   252  	docs, err := getDocsFromTable(ctx, docsTbl)
   253  	return docs, true, err
   254  }
   255  
   256  // getDocsFromTable takes the doltdocs table and a schema and return all docs in the dolt_docs table.
   257  func getDocsFromTable(ctx context.Context, table *doltdb.Table) (Docs, error) {
   258  	ret := make(Docs, 0)
   259  
   260  	sch, err := table.GetSchema(ctx)
   261  	if err != nil {
   262  		return nil, err
   263  	}
   264  
   265  	rows, err := table.GetRowData(ctx)
   266  	if err != nil {
   267  		return nil, err
   268  	}
   269  
   270  	err = rows.IterAll(ctx, func(key, val types.Value) error {
   271  		newRow, err := row.FromNoms(sch, key.(types.Tuple), val.(types.Tuple))
   272  		if err != nil {
   273  			return err
   274  		}
   275  
   276  		cols := sch.GetAllCols().GetColumns()
   277  		colVals := make([]types.Value, len(cols))
   278  		for i, col := range cols {
   279  			colval, ok := newRow.GetColVal(col.Tag)
   280  			if !ok {
   281  				return errors.New("error: could not get doc column value")
   282  			}
   283  			colVals[i] = colval
   284  		}
   285  
   286  		if len(colVals) < 2 {
   287  			return errors.New("error: not enough values read from the table")
   288  		}
   289  
   290  		doc := Doc{}
   291  		doc.DocPk = string(colVals[0].(types.String))
   292  		doc.Text = []byte(colVals[1].(types.String))
   293  		ret = append(ret, doc)
   294  
   295  		return nil
   296  	})
   297  
   298  	return ret, err
   299  }