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 }