github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/doltcore/schema/alterschema/addcolumn_test.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 alterschema 16 17 import ( 18 "context" 19 "fmt" 20 "testing" 21 22 "github.com/google/uuid" 23 "github.com/stretchr/testify/assert" 24 "github.com/stretchr/testify/require" 25 26 "github.com/dolthub/dolt/go/libraries/doltcore/dtestutils" 27 "github.com/dolthub/dolt/go/libraries/doltcore/row" 28 "github.com/dolthub/dolt/go/libraries/doltcore/schema" 29 "github.com/dolthub/dolt/go/libraries/doltcore/schema/typeinfo" 30 "github.com/dolthub/dolt/go/store/types" 31 ) 32 33 const tableName = "people" 34 35 func TestAddColumnToTable(t *testing.T) { 36 tests := []struct { 37 name string 38 tag uint64 39 newColName string 40 colKind types.NomsKind 41 nullable Nullable 42 defaultVal string 43 order *ColumnOrder 44 expectedSchema schema.Schema 45 expectedRows []row.Row 46 expectedErr string 47 }{ 48 { 49 name: "string column no default", 50 tag: dtestutils.NextTag, 51 newColName: "newCol", 52 colKind: types.StringKind, 53 nullable: Null, 54 expectedSchema: dtestutils.AddColumnToSchema(dtestutils.TypedSchema, 55 schema.NewColumn("newCol", dtestutils.NextTag, types.StringKind, false)), 56 expectedRows: dtestutils.TypedRows, 57 }, 58 { 59 name: "int column no default", 60 tag: dtestutils.NextTag, 61 newColName: "newCol", 62 colKind: types.IntKind, 63 nullable: Null, 64 expectedSchema: dtestutils.AddColumnToSchema(dtestutils.TypedSchema, 65 schema.NewColumn("newCol", dtestutils.NextTag, types.IntKind, false)), 66 expectedRows: dtestutils.TypedRows, 67 }, 68 { 69 name: "uint column no default", 70 tag: dtestutils.NextTag, 71 newColName: "newCol", 72 colKind: types.UintKind, 73 nullable: Null, 74 expectedSchema: dtestutils.AddColumnToSchema(dtestutils.TypedSchema, 75 schema.NewColumn("newCol", dtestutils.NextTag, types.UintKind, false)), 76 expectedRows: dtestutils.TypedRows, 77 }, 78 { 79 name: "float column no default", 80 tag: dtestutils.NextTag, 81 newColName: "newCol", 82 colKind: types.FloatKind, 83 nullable: Null, 84 expectedSchema: dtestutils.AddColumnToSchema(dtestutils.TypedSchema, 85 schema.NewColumn("newCol", dtestutils.NextTag, types.FloatKind, false)), 86 expectedRows: dtestutils.TypedRows, 87 }, 88 { 89 name: "bool column no default", 90 tag: dtestutils.NextTag, 91 newColName: "newCol", 92 colKind: types.BoolKind, 93 nullable: Null, 94 expectedSchema: dtestutils.AddColumnToSchema(dtestutils.TypedSchema, 95 schema.NewColumn("newCol", dtestutils.NextTag, types.BoolKind, false)), 96 expectedRows: dtestutils.TypedRows, 97 }, 98 { 99 name: "uuid column no default", 100 tag: dtestutils.NextTag, 101 newColName: "newCol", 102 colKind: types.UUIDKind, 103 nullable: Null, 104 expectedSchema: dtestutils.AddColumnToSchema(dtestutils.TypedSchema, 105 schema.NewColumn("newCol", dtestutils.NextTag, types.UUIDKind, false)), 106 expectedRows: dtestutils.TypedRows, 107 }, 108 { 109 name: "string column with default", 110 tag: dtestutils.NextTag, 111 newColName: "newCol", 112 colKind: types.StringKind, 113 nullable: NotNull, 114 defaultVal: `("default")`, 115 expectedSchema: dtestutils.AddColumnToSchema(dtestutils.TypedSchema, 116 schemaNewColumn("newCol", dtestutils.NextTag, types.StringKind, false, `("default")`, schema.NotNullConstraint{})), 117 expectedRows: dtestutils.AddColToRows(t, dtestutils.TypedRows, dtestutils.NextTag, types.String("default")), 118 }, 119 { 120 name: "int column with default", 121 tag: dtestutils.NextTag, 122 newColName: "newCol", 123 colKind: types.IntKind, 124 nullable: NotNull, 125 defaultVal: "42", 126 expectedSchema: dtestutils.AddColumnToSchema(dtestutils.TypedSchema, 127 schemaNewColumn("newCol", dtestutils.NextTag, types.IntKind, false, "42", schema.NotNullConstraint{})), 128 expectedRows: dtestutils.AddColToRows(t, dtestutils.TypedRows, dtestutils.NextTag, types.Int(42)), 129 }, 130 { 131 name: "uint column with default", 132 tag: dtestutils.NextTag, 133 newColName: "newCol", 134 colKind: types.UintKind, 135 nullable: NotNull, 136 defaultVal: "64", 137 expectedSchema: dtestutils.AddColumnToSchema(dtestutils.TypedSchema, 138 schemaNewColumn("newCol", dtestutils.NextTag, types.UintKind, false, "64", schema.NotNullConstraint{})), 139 expectedRows: dtestutils.AddColToRows(t, dtestutils.TypedRows, dtestutils.NextTag, types.Uint(64)), 140 }, 141 { 142 name: "float column with default", 143 tag: dtestutils.NextTag, 144 newColName: "newCol", 145 colKind: types.FloatKind, 146 nullable: NotNull, 147 defaultVal: "33.33", 148 expectedSchema: dtestutils.AddColumnToSchema(dtestutils.TypedSchema, 149 schemaNewColumn("newCol", dtestutils.NextTag, types.FloatKind, false, "33.33", schema.NotNullConstraint{})), 150 expectedRows: dtestutils.AddColToRows(t, dtestutils.TypedRows, dtestutils.NextTag, types.Float(33.33)), 151 }, 152 { 153 name: "bool column with default", 154 tag: dtestutils.NextTag, 155 newColName: "newCol", 156 colKind: types.BoolKind, 157 nullable: NotNull, 158 defaultVal: "true", 159 expectedSchema: dtestutils.AddColumnToSchema(dtestutils.TypedSchema, 160 schemaNewColumn("newCol", dtestutils.NextTag, types.BoolKind, false, "true", schema.NotNullConstraint{})), 161 expectedRows: dtestutils.AddColToRows(t, dtestutils.TypedRows, dtestutils.NextTag, types.Bool(true)), 162 }, 163 { 164 name: "uuid column with default", 165 tag: dtestutils.NextTag, 166 newColName: "newCol", 167 colKind: types.UUIDKind, 168 nullable: NotNull, 169 defaultVal: `"00000000-0000-0000-0000-000000000000"`, 170 expectedSchema: dtestutils.AddColumnToSchema(dtestutils.TypedSchema, 171 schemaNewColumn("newCol", dtestutils.NextTag, types.UUIDKind, false, `"00000000-0000-0000-0000-000000000000"`, schema.NotNullConstraint{})), 172 expectedRows: dtestutils.AddColToRows(t, 173 dtestutils.TypedRows, dtestutils.NextTag, types.UUID(uuid.MustParse("00000000-0000-0000-0000-000000000000"))), 174 }, 175 { 176 name: "nullable with nil default", 177 tag: dtestutils.NextTag, 178 newColName: "newCol", 179 colKind: types.IntKind, 180 nullable: Null, 181 defaultVal: "", 182 expectedSchema: dtestutils.AddColumnToSchema(dtestutils.TypedSchema, 183 schemaNewColumn("newCol", dtestutils.NextTag, types.IntKind, false, "")), 184 expectedRows: dtestutils.TypedRows, 185 }, 186 { 187 name: "nullable with non-nil default", 188 tag: dtestutils.NextTag, 189 newColName: "newCol", 190 colKind: types.IntKind, 191 nullable: Null, 192 defaultVal: "42", 193 expectedSchema: dtestutils.AddColumnToSchema(dtestutils.TypedSchema, 194 schemaNewColumn("newCol", dtestutils.NextTag, types.IntKind, false, "42")), 195 expectedRows: dtestutils.AddColToRows(t, dtestutils.TypedRows, dtestutils.NextTag, types.Int(42)), 196 }, 197 { 198 name: "first order", 199 tag: dtestutils.NextTag, 200 newColName: "newCol", 201 colKind: types.IntKind, 202 nullable: Null, 203 defaultVal: "42", 204 order: &ColumnOrder{First: true}, 205 expectedSchema: dtestutils.CreateSchema( 206 schemaNewColumn("newCol", dtestutils.NextTag, types.IntKind, false, "42"), 207 schema.NewColumn("id", dtestutils.IdTag, types.UUIDKind, true, schema.NotNullConstraint{}), 208 schema.NewColumn("name", dtestutils.NameTag, types.StringKind, false, schema.NotNullConstraint{}), 209 schema.NewColumn("age", dtestutils.AgeTag, types.UintKind, false, schema.NotNullConstraint{}), 210 schema.NewColumn("is_married", dtestutils.IsMarriedTag, types.BoolKind, false, schema.NotNullConstraint{}), 211 schema.NewColumn("title", dtestutils.TitleTag, types.StringKind, false), 212 ), 213 expectedRows: dtestutils.AddColToRows(t, dtestutils.TypedRows, dtestutils.NextTag, types.Int(42)), 214 }, 215 { 216 name: "middle order", 217 tag: dtestutils.NextTag, 218 newColName: "newCol", 219 colKind: types.IntKind, 220 nullable: Null, 221 defaultVal: "42", 222 order: &ColumnOrder{After: "age"}, 223 expectedSchema: dtestutils.CreateSchema( 224 schema.NewColumn("id", dtestutils.IdTag, types.UUIDKind, true, schema.NotNullConstraint{}), 225 schema.NewColumn("name", dtestutils.NameTag, types.StringKind, false, schema.NotNullConstraint{}), 226 schema.NewColumn("age", dtestutils.AgeTag, types.UintKind, false, schema.NotNullConstraint{}), 227 schemaNewColumn("newCol", dtestutils.NextTag, types.IntKind, false, "42"), 228 schema.NewColumn("is_married", dtestutils.IsMarriedTag, types.BoolKind, false, schema.NotNullConstraint{}), 229 schema.NewColumn("title", dtestutils.TitleTag, types.StringKind, false), 230 ), 231 expectedRows: dtestutils.AddColToRows(t, dtestutils.TypedRows, dtestutils.NextTag, types.Int(42)), 232 }, 233 { 234 name: "tag collision", 235 tag: dtestutils.AgeTag, 236 newColName: "newCol", 237 colKind: types.IntKind, 238 nullable: NotNull, 239 defaultVal: "", 240 expectedErr: fmt.Sprintf("Cannot create column newCol, the tag %d was already used in table people", dtestutils.AgeTag), 241 }, 242 { 243 name: "name collision", 244 tag: dtestutils.NextTag, 245 newColName: "age", 246 colKind: types.IntKind, 247 nullable: NotNull, 248 defaultVal: "10", 249 expectedErr: "A column with the name age already exists", 250 }, 251 } 252 253 for _, tt := range tests { 254 t.Run(tt.name, func(t *testing.T) { 255 dEnv := dtestutils.CreateEnvWithSeedData(t) 256 ctx := context.Background() 257 258 root, err := dEnv.WorkingRoot(ctx) 259 assert.NoError(t, err) 260 tbl, _, err := root.GetTable(ctx, tableName) 261 assert.NoError(t, err) 262 263 updatedTable, err := AddColumnToTable(ctx, root, tbl, tableName, tt.tag, tt.newColName, typeinfo.FromKind(tt.colKind), tt.nullable, tt.defaultVal, "", tt.order) 264 if len(tt.expectedErr) > 0 { 265 require.Error(t, err) 266 assert.Contains(t, err.Error(), tt.expectedErr) 267 return 268 } else { 269 require.NoError(t, err) 270 require.NoError(t, err) 271 } 272 273 sch, err := updatedTable.GetSchema(ctx) 274 require.NoError(t, err) 275 index := sch.Indexes().GetByName(dtestutils.IndexName) 276 assert.NotNil(t, index) 277 tt.expectedSchema.Indexes().AddIndex(index) 278 require.Equal(t, tt.expectedSchema, sch) 279 280 rowData, err := updatedTable.GetRowData(ctx) 281 require.NoError(t, err) 282 283 var foundRows []row.Row 284 err = rowData.Iter(ctx, func(key, value types.Value) (stop bool, err error) { 285 tpl, err := row.FromNoms(tt.expectedSchema, key.(types.Tuple), value.(types.Tuple)) 286 287 if err != nil { 288 return false, err 289 } 290 291 foundRows = append(foundRows, tpl) 292 return false, nil 293 }) 294 295 assert.NoError(t, err) 296 assert.Equal(t, tt.expectedRows, foundRows) 297 298 indexRowData, err := updatedTable.GetIndexRowData(ctx, dtestutils.IndexName) 299 require.NoError(t, err) 300 assert.Greater(t, indexRowData.Len(), uint64(0)) 301 }) 302 } 303 } 304 305 func schemaNewColumn(name string, tag uint64, kind types.NomsKind, partOfPK bool, defaultVal string, constraints ...schema.ColConstraint) schema.Column { 306 col := schema.NewColumn(name, tag, kind, partOfPK, constraints...) 307 col.Default = defaultVal 308 return col 309 }