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 }