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

     1  // Copyright 2019 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 schema
    16  
    17  import (
    18  	"strconv"
    19  	"strings"
    20  )
    21  
    22  var FeatureFlagKeylessSchema = true
    23  
    24  // EmptySchema is an instance of a schema with no columns.
    25  var EmptySchema = &schemaImpl{
    26  	pkCols:          EmptyColColl,
    27  	nonPKCols:       EmptyColColl,
    28  	allCols:         EmptyColColl,
    29  	indexCollection: NewIndexCollection(nil),
    30  }
    31  
    32  type schemaImpl struct {
    33  	pkCols, nonPKCols, allCols *ColCollection
    34  	indexCollection            IndexCollection
    35  	checkCollection            CheckCollection
    36  }
    37  
    38  // SchemaFromCols creates a Schema from a collection of columns
    39  func SchemaFromCols(allCols *ColCollection) (Schema, error) {
    40  	var pkCols []Column
    41  	var nonPKCols []Column
    42  
    43  	for _, c := range allCols.cols {
    44  		if c.IsPartOfPK {
    45  			pkCols = append(pkCols, c)
    46  		} else {
    47  			nonPKCols = append(nonPKCols, c)
    48  		}
    49  	}
    50  
    51  	if len(pkCols) == 0 && !FeatureFlagKeylessSchema {
    52  		return nil, ErrNoPrimaryKeyColumns
    53  	}
    54  
    55  	pkColColl := NewColCollection(pkCols...)
    56  	nonPKColColl := NewColCollection(nonPKCols...)
    57  
    58  	return SchemaFromColCollections(allCols, pkColColl, nonPKColColl), nil
    59  }
    60  
    61  func SchemaFromColCollections(allCols, pkColColl, nonPKColColl *ColCollection) Schema {
    62  	return &schemaImpl{
    63  		pkCols:          pkColColl,
    64  		nonPKCols:       nonPKColColl,
    65  		allCols:         allCols,
    66  		indexCollection: NewIndexCollection(allCols),
    67  		checkCollection: NewCheckCollection(),
    68  	}
    69  }
    70  
    71  func MustSchemaFromCols(typedColColl *ColCollection) Schema {
    72  	sch, err := SchemaFromCols(typedColColl)
    73  	if err != nil {
    74  		panic(err)
    75  	}
    76  	return sch
    77  }
    78  
    79  // ValidateForInsert returns an error if the given schema cannot be written to the dolt database.
    80  func ValidateForInsert(allCols *ColCollection) error {
    81  	var seenPkCol bool
    82  	for _, c := range allCols.cols {
    83  		if c.IsPartOfPK {
    84  			seenPkCol = true
    85  			break
    86  		}
    87  	}
    88  
    89  	if !seenPkCol && !FeatureFlagKeylessSchema {
    90  		return ErrNoPrimaryKeyColumns
    91  	}
    92  
    93  	colNames := make(map[string]bool)
    94  	colTags := make(map[uint64]bool)
    95  
    96  	err := allCols.Iter(func(tag uint64, col Column) (stop bool, err error) {
    97  		if _, ok := colTags[tag]; ok {
    98  			return true, ErrColTagCollision
    99  		}
   100  		colTags[tag] = true
   101  
   102  		if _, ok := colNames[strings.ToLower(col.Name)]; ok {
   103  			return true, ErrColNameCollision
   104  		}
   105  		colNames[col.Name] = true
   106  
   107  		return false, nil
   108  	})
   109  
   110  	return err
   111  }
   112  
   113  // UnkeyedSchemaFromCols creates a schema without any primary keys to be used for displaying to users, tests, etc. Such
   114  // unkeyed schemas are not suitable to be inserted into storage.
   115  func UnkeyedSchemaFromCols(allCols *ColCollection) Schema {
   116  	var nonPKCols []Column
   117  
   118  	for _, c := range allCols.cols {
   119  		c.IsPartOfPK = false
   120  		c.Constraints = nil
   121  		nonPKCols = append(nonPKCols, c)
   122  	}
   123  
   124  	pkColColl := NewColCollection()
   125  	nonPKColColl := NewColCollection(nonPKCols...)
   126  
   127  	return &schemaImpl{
   128  		pkCols:          pkColColl,
   129  		nonPKCols:       nonPKColColl,
   130  		allCols:         nonPKColColl,
   131  		indexCollection: NewIndexCollection(nil),
   132  		checkCollection: NewCheckCollection(),
   133  	}
   134  }
   135  
   136  // SchemaFromPKAndNonPKCols creates a Schema from a collection of the key columns, and the non-key columns.
   137  func SchemaFromPKAndNonPKCols(pkCols, nonPKCols *ColCollection) (Schema, error) {
   138  	allCols := make([]Column, pkCols.Size()+nonPKCols.Size())
   139  
   140  	i := 0
   141  	for _, c := range pkCols.cols {
   142  		if !c.IsPartOfPK {
   143  			panic("bug: attempting to add a column to the pk that isn't part of the pk")
   144  		}
   145  
   146  		allCols[i] = c
   147  		i++
   148  	}
   149  
   150  	for _, c := range nonPKCols.cols {
   151  		if c.IsPartOfPK {
   152  			panic("bug: attempting to add a column that is part of the pk to the non-pk columns")
   153  		}
   154  
   155  		allCols[i] = c
   156  		i++
   157  	}
   158  
   159  	allColColl := NewColCollection(allCols...)
   160  	return SchemaFromColCollections(allColColl, pkCols, nonPKCols), nil
   161  }
   162  
   163  // GetAllCols gets the collection of all columns (pk and non-pk)
   164  func (si *schemaImpl) GetAllCols() *ColCollection {
   165  	return si.allCols
   166  }
   167  
   168  // GetNonPKCols gets the collection of columns which are not part of the primary key.
   169  func (si *schemaImpl) GetNonPKCols() *ColCollection {
   170  	return si.nonPKCols
   171  }
   172  
   173  // GetPKCols gets the collection of columns which make the primary key.
   174  func (si *schemaImpl) GetPKCols() *ColCollection {
   175  	return si.pkCols
   176  }
   177  
   178  func (si *schemaImpl) String() string {
   179  	var b strings.Builder
   180  	writeColFn := func(tag uint64, col Column) (stop bool, err error) {
   181  		b.WriteString("tag: ")
   182  		b.WriteString(strconv.FormatUint(tag, 10))
   183  		b.WriteString(", name: ")
   184  		b.WriteString(col.Name)
   185  		b.WriteString(", type: ")
   186  		b.WriteString(col.KindString())
   187  		b.WriteString(",\n")
   188  		return false, nil
   189  	}
   190  	b.WriteString("pkCols: [")
   191  	err := si.pkCols.Iter(writeColFn)
   192  
   193  	if err != nil {
   194  		return err.Error()
   195  	}
   196  
   197  	b.WriteString("]\nnonPkCols: [")
   198  	err = si.nonPKCols.Iter(writeColFn)
   199  
   200  	if err != nil {
   201  		return err.Error()
   202  	}
   203  
   204  	b.WriteString("]")
   205  	return b.String()
   206  }
   207  
   208  func (si *schemaImpl) Indexes() IndexCollection {
   209  	return si.indexCollection
   210  }
   211  
   212  func (si *schemaImpl) Checks() CheckCollection {
   213  	return si.checkCollection
   214  }