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  }