github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/doltcore/sqle/sqlfmt/row_fmt_test.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 sqlfmt
    16  
    17  import (
    18  	"testing"
    19  
    20  	"github.com/google/uuid"
    21  	"github.com/stretchr/testify/assert"
    22  	"github.com/stretchr/testify/require"
    23  
    24  	"github.com/dolthub/dolt/go/libraries/doltcore/dtestutils"
    25  	"github.com/dolthub/dolt/go/libraries/doltcore/row"
    26  	"github.com/dolthub/dolt/go/libraries/doltcore/schema"
    27  	"github.com/dolthub/dolt/go/libraries/doltcore/schema/typeinfo"
    28  	"github.com/dolthub/dolt/go/store/types"
    29  )
    30  
    31  const expectedDropSql = "DROP TABLE `table_name`;"
    32  const expectedDropIfExistsSql = "DROP TABLE IF EXISTS `table_name`;"
    33  const expectedAddColSql = "ALTER TABLE `table_name` ADD `c0` BIGINT NOT NULL;"
    34  const expectedDropColSql = "ALTER TABLE `table_name` DROP `first_name`;"
    35  const expectedRenameColSql = "ALTER TABLE `table_name` RENAME COLUMN `id` TO `pk`;"
    36  const expectedRenameTableSql = "RENAME TABLE `table_name` TO `new_table_name`;"
    37  
    38  type test struct {
    39  	name           string
    40  	row            row.Row
    41  	sch            schema.Schema
    42  	expectedOutput string
    43  }
    44  
    45  func TestTableDropStmt(t *testing.T) {
    46  	stmt := DropTableStmt("table_name")
    47  
    48  	assert.Equal(t, expectedDropSql, stmt)
    49  }
    50  
    51  func TestTableDropIfExistsStmt(t *testing.T) {
    52  	stmt := DropTableIfExistsStmt("table_name")
    53  
    54  	assert.Equal(t, expectedDropIfExistsSql, stmt)
    55  }
    56  
    57  func TestAlterTableAddColStmt(t *testing.T) {
    58  	newColDef := "`c0` BIGINT NOT NULL"
    59  	stmt := AlterTableAddColStmt("table_name", newColDef)
    60  
    61  	assert.Equal(t, expectedAddColSql, stmt)
    62  }
    63  
    64  func TestAlterTableDropColStmt(t *testing.T) {
    65  	stmt := AlterTableDropColStmt("table_name", "first_name")
    66  
    67  	assert.Equal(t, expectedDropColSql, stmt)
    68  }
    69  
    70  func TestAlterTableRenameColStmt(t *testing.T) {
    71  	stmt := AlterTableRenameColStmt("table_name", "id", "pk")
    72  
    73  	assert.Equal(t, expectedRenameColSql, stmt)
    74  }
    75  
    76  func TestRenameTableStmt(t *testing.T) {
    77  	stmt := RenameTableStmt("table_name", "new_table_name")
    78  
    79  	assert.Equal(t, expectedRenameTableSql, stmt)
    80  }
    81  
    82  func TestRowAsInsertStmt(t *testing.T) {
    83  	id := uuid.MustParse("00000000-0000-0000-0000-000000000000")
    84  	tableName := "people"
    85  
    86  	tests := []test{
    87  		{
    88  			name:           "simple row",
    89  			row:            dtestutils.NewTypedRow(id, "some guy", 100, false, strPointer("normie")),
    90  			sch:            dtestutils.TypedSchema,
    91  			expectedOutput: "INSERT INTO `people` (`id`,`name`,`age`,`is_married`,`title`) VALUES ('00000000-0000-0000-0000-000000000000','some guy',100,FALSE,'normie');",
    92  		},
    93  		{
    94  			name:           "embedded quotes",
    95  			row:            dtestutils.NewTypedRow(id, `It's "Mister Perfect" to you`, 100, false, strPointer("normie")),
    96  			sch:            dtestutils.TypedSchema,
    97  			expectedOutput: "INSERT INTO `people` (`id`,`name`,`age`,`is_married`,`title`) VALUES ('00000000-0000-0000-0000-000000000000','It\\'s \\\"Mister Perfect\\\" to you',100,FALSE,'normie');",
    98  		},
    99  		{
   100  			name:           "null values",
   101  			row:            dtestutils.NewTypedRow(id, "some guy", 100, false, nil),
   102  			sch:            dtestutils.TypedSchema,
   103  			expectedOutput: "INSERT INTO `people` (`id`,`name`,`age`,`is_married`,`title`) VALUES ('00000000-0000-0000-0000-000000000000','some guy',100,FALSE,NULL);",
   104  		},
   105  	}
   106  
   107  	trickySch := dtestutils.CreateSchema(
   108  		schema.NewColumn("a name with spaces", 0, types.FloatKind, false),
   109  		schema.NewColumn("anotherColumn", 1, types.IntKind, true),
   110  	)
   111  
   112  	tests = append(tests, test{
   113  		name:           "negative values and columns with spaces",
   114  		row:            dtestutils.NewRow(trickySch, types.Float(-3.14), types.Int(-42)),
   115  		sch:            trickySch,
   116  		expectedOutput: "INSERT INTO `people` (`a name with spaces`,`anotherColumn`) VALUES (-3.14,-42);",
   117  	})
   118  
   119  	for _, tt := range tests {
   120  		t.Run(tt.name, func(t *testing.T) {
   121  			stmt, err := RowAsInsertStmt(tt.row, tableName, tt.sch)
   122  			assert.NoError(t, err)
   123  			assert.Equal(t, tt.expectedOutput, stmt)
   124  		})
   125  	}
   126  }
   127  
   128  func TestRowAsDeleteStmt(t *testing.T) {
   129  	tableName := "tricky"
   130  	trickySch := dtestutils.CreateSchema(
   131  		schema.NewColumn("anotherCol", 0, types.FloatKind, false),
   132  		schema.NewColumn("a name with spaces", 1, types.IntKind, true),
   133  	)
   134  
   135  	tests := []test{
   136  		{
   137  			name:           "negative values and columns with spaces",
   138  			row:            dtestutils.NewRow(trickySch, types.Float(-3.14), types.Int(-42)),
   139  			sch:            trickySch,
   140  			expectedOutput: "DELETE FROM `tricky` WHERE (`a name with spaces`=-42);",
   141  		},
   142  	}
   143  
   144  	for _, tt := range tests {
   145  		t.Run(tt.name, func(t *testing.T) {
   146  			stmt, err := RowAsDeleteStmt(tt.row, tableName, tt.sch)
   147  			assert.NoError(t, err)
   148  			assert.Equal(t, tt.expectedOutput, stmt)
   149  		})
   150  	}
   151  }
   152  
   153  func TestRowAsUpdateStmt(t *testing.T) {
   154  	id := uuid.MustParse("00000000-0000-0000-0000-000000000000")
   155  	tableName := "people"
   156  
   157  	tests := []test{
   158  		{
   159  			name:           "simple row",
   160  			row:            dtestutils.NewTypedRow(id, "some guy", 100, false, strPointer("normie")),
   161  			sch:            dtestutils.TypedSchema,
   162  			expectedOutput: "UPDATE `people` SET `name`='some guy',`age`=100,`is_married`=FALSE,`title`='normie' WHERE (`id`='00000000-0000-0000-0000-000000000000');",
   163  		},
   164  		{
   165  			name:           "embedded quotes",
   166  			row:            dtestutils.NewTypedRow(id, `It's "Mister Perfect" to you`, 100, false, strPointer("normie")),
   167  			sch:            dtestutils.TypedSchema,
   168  			expectedOutput: "UPDATE `people` SET `name`='It\\'s \\\"Mister Perfect\\\" to you',`age`=100,`is_married`=FALSE,`title`='normie' WHERE (`id`='00000000-0000-0000-0000-000000000000');",
   169  		},
   170  		{
   171  			name:           "null values",
   172  			row:            dtestutils.NewTypedRow(id, "some guy", 100, false, nil),
   173  			sch:            dtestutils.TypedSchema,
   174  			expectedOutput: "UPDATE `people` SET `name`='some guy',`age`=100,`is_married`=FALSE,`title`=NULL WHERE (`id`='00000000-0000-0000-0000-000000000000');",
   175  		},
   176  	}
   177  
   178  	trickySch := dtestutils.CreateSchema(
   179  		schema.NewColumn("a name with spaces", 0, types.FloatKind, false),
   180  		schema.NewColumn("anotherColumn", 1, types.IntKind, true),
   181  	)
   182  
   183  	tests = append(tests, test{
   184  		name:           "negative values and columns with spaces",
   185  		row:            dtestutils.NewRow(trickySch, types.Float(-3.14), types.Int(-42)),
   186  		sch:            trickySch,
   187  		expectedOutput: "UPDATE `people` SET `a name with spaces`=-3.14 WHERE (`anotherColumn`=-42);",
   188  	})
   189  
   190  	for _, tt := range tests {
   191  		t.Run(tt.name, func(t *testing.T) {
   192  			stmt, err := RowAsUpdateStmt(tt.row, tableName, tt.sch)
   193  			assert.NoError(t, err)
   194  			assert.Equal(t, tt.expectedOutput, stmt)
   195  		})
   196  	}
   197  }
   198  
   199  func TestValueAsSqlString(t *testing.T) {
   200  	tu, _ := uuid.Parse("00000000-0000-0000-0000-000000000000")
   201  
   202  	tests := []struct {
   203  		name string
   204  		val  types.Value
   205  		ti   typeinfo.TypeInfo
   206  		exp  string
   207  	}{
   208  		{
   209  			name: "bool(true)",
   210  			val:  types.Bool(true),
   211  			ti:   typeinfo.BoolType,
   212  			exp:  "TRUE",
   213  		},
   214  		{
   215  			name: "bool(false)",
   216  			val:  types.Bool(false),
   217  			ti:   typeinfo.BoolType,
   218  			exp:  "FALSE",
   219  		},
   220  		{
   221  			name: "uuid",
   222  			val:  types.UUID(tu),
   223  			ti:   typeinfo.UuidType,
   224  			exp:  "'00000000-0000-0000-0000-000000000000'",
   225  		},
   226  		{
   227  			name: "string",
   228  			val:  types.String("leviosa"),
   229  			ti:   typeinfo.StringDefaultType,
   230  			exp:  "'leviosa'",
   231  		},
   232  		{
   233  			// borrowed from vitess
   234  			name: "escape string",
   235  			val:  types.String("\x00'\"\b\n\r\t\x1A\\"),
   236  			ti:   typeinfo.StringDefaultType,
   237  			exp:  "'\\0\\'\\\"\\b\\n\\r\\t\\Z\\\\'",
   238  		},
   239  	}
   240  
   241  	for _, test := range tests {
   242  		t.Run(test.name, func(t *testing.T) {
   243  			act, err := valueAsSqlString(test.ti, test.val)
   244  			require.NoError(t, err)
   245  			assert.Equal(t, test.exp, act)
   246  		})
   247  	}
   248  }
   249  
   250  func strPointer(s string) *string {
   251  	return &s
   252  }