Skip to content
This repository was archived by the owner on Jan 28, 2021. It is now read-only.

Commit 98c7c11

Browse files
authored
Fixed INSERT bugs, added UINT64 support, and added tests (#816)
Fixed INSERT bugs, added UINT64 support, and added tests
2 parents 77d8c27 + 7e11c50 commit 98c7c11

File tree

4 files changed

+262
-13
lines changed

4 files changed

+262
-13
lines changed

engine_test.go

Lines changed: 189 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,7 @@ var queries = []struct {
700700
{"bigtable", "InnoDB", "10", "Fixed", int64(0), int64(0), int64(0), int64(0), int64(0), int64(0), int64(0), nil, nil, nil, "utf8_bin", nil, nil},
701701
{"floattable", "InnoDB", "10", "Fixed", int64(0), int64(0), int64(0), int64(0), int64(0), int64(0), int64(0), nil, nil, nil, "utf8_bin", nil, nil},
702702
{"niltable", "InnoDB", "10", "Fixed", int64(0), int64(0), int64(0), int64(0), int64(0), int64(0), int64(0), nil, nil, nil, "utf8_bin", nil, nil},
703+
{"typestable", "InnoDB", "10", "Fixed", int64(0), int64(0), int64(0), int64(0), int64(0), int64(0), int64(0), nil, nil, nil, "utf8_bin", nil, nil},
703704
},
704705
},
705706
{
@@ -710,6 +711,7 @@ var queries = []struct {
710711
{"bigtable", "InnoDB", "10", "Fixed", int64(0), int64(0), int64(0), int64(0), int64(0), int64(0), int64(0), nil, nil, nil, "utf8_bin", nil, nil},
711712
{"floattable", "InnoDB", "10", "Fixed", int64(0), int64(0), int64(0), int64(0), int64(0), int64(0), int64(0), nil, nil, nil, "utf8_bin", nil, nil},
712713
{"niltable", "InnoDB", "10", "Fixed", int64(0), int64(0), int64(0), int64(0), int64(0), int64(0), int64(0), nil, nil, nil, "utf8_bin", nil, nil},
714+
{"typestable", "InnoDB", "10", "Fixed", int64(0), int64(0), int64(0), int64(0), int64(0), int64(0), int64(0), nil, nil, nil, "utf8_bin", nil, nil},
713715
},
714716
},
715717
{
@@ -852,6 +854,7 @@ var queries = []struct {
852854
{"bigtable"},
853855
{"floattable"},
854856
{"niltable"},
857+
{"typestable"},
855858
},
856859
},
857860
{
@@ -881,6 +884,21 @@ var queries = []struct {
881884
{"f64"},
882885
{"b"},
883886
{"f"},
887+
{"id"},
888+
{"i8"},
889+
{"i16"},
890+
{"i32"},
891+
{"i64"},
892+
{"u8"},
893+
{"u16"},
894+
{"u32"},
895+
{"u64"},
896+
{"ti"},
897+
{"da"},
898+
{"te"},
899+
{"bo"},
900+
{"js"},
901+
{"bl"},
884902
},
885903
},
886904
{
@@ -900,6 +918,21 @@ var queries = []struct {
900918
{"f64"},
901919
{"b"},
902920
{"f"},
921+
{"id"},
922+
{"i8"},
923+
{"i16"},
924+
{"i32"},
925+
{"i64"},
926+
{"u8"},
927+
{"u16"},
928+
{"u32"},
929+
{"u64"},
930+
{"ti"},
931+
{"da"},
932+
{"te"},
933+
{"bo"},
934+
{"js"},
935+
{"bl"},
903936
},
904937
},
905938
{
@@ -919,6 +952,21 @@ var queries = []struct {
919952
{"f64"},
920953
{"b"},
921954
{"f"},
955+
{"id"},
956+
{"i8"},
957+
{"i16"},
958+
{"i32"},
959+
{"i64"},
960+
{"u8"},
961+
{"u16"},
962+
{"u32"},
963+
{"u64"},
964+
{"ti"},
965+
{"da"},
966+
{"te"},
967+
{"bo"},
968+
{"js"},
969+
{"bl"},
922970
},
923971
},
924972
{
@@ -955,6 +1003,7 @@ var queries = []struct {
9551003
{"bigtable", "InnoDB", "10", "Fixed", int64(0), int64(0), int64(0), int64(0), int64(0), int64(0), int64(0), nil, nil, nil, "utf8_bin", nil, nil},
9561004
{"floattable", "InnoDB", "10", "Fixed", int64(0), int64(0), int64(0), int64(0), int64(0), int64(0), int64(0), nil, nil, nil, "utf8_bin", nil, nil},
9571005
{"niltable", "InnoDB", "10", "Fixed", int64(0), int64(0), int64(0), int64(0), int64(0), int64(0), int64(0), nil, nil, nil, "utf8_bin", nil, nil},
1006+
{"typestable", "InnoDB", "10", "Fixed", int64(0), int64(0), int64(0), int64(0), int64(0), int64(0), int64(0), nil, nil, nil, "utf8_bin", nil, nil},
9581007
},
9591008
},
9601009
{
@@ -1068,6 +1117,7 @@ var queries = []struct {
10681117
{"bigtable"},
10691118
{"floattable"},
10701119
{"niltable"},
1120+
{"typestable"},
10711121
},
10721122
},
10731123
{
@@ -1079,6 +1129,7 @@ var queries = []struct {
10791129
{"bigtable", "BASE TABLE"},
10801130
{"floattable", "BASE TABLE"},
10811131
{"niltable", "BASE TABLE"},
1132+
{"typestable", "BASE TABLE"},
10821133
},
10831134
},
10841135
{
@@ -1095,6 +1146,7 @@ var queries = []struct {
10951146
{"bigtable"},
10961147
{"floattable"},
10971148
{"niltable"},
1149+
{"typestable"},
10981150
},
10991151
},
11001152
{
@@ -1755,16 +1807,123 @@ func TestOrderByColumns(t *testing.T) {
17551807
}
17561808

17571809
func TestInsertInto(t *testing.T) {
1758-
e := newEngine(t)
1759-
testQuery(t, e,
1760-
"INSERT INTO mytable (s, i) VALUES ('x', 999);",
1761-
[]sql.Row{{int64(1)}},
1762-
)
1810+
timeParse := func(layout string, value string) time.Time {
1811+
t, _ := time.Parse(layout, value)
1812+
return t
1813+
}
17631814

1764-
testQuery(t, e,
1765-
"SELECT i FROM mytable WHERE s = 'x';",
1766-
[]sql.Row{{int64(999)}},
1767-
)
1815+
var insertions = []struct {
1816+
insertQuery string
1817+
expectedInsert []sql.Row
1818+
selectQuery string
1819+
expectedSelect []sql.Row
1820+
}{
1821+
{
1822+
"INSERT INTO mytable (s, i) VALUES ('x', 999);",
1823+
[]sql.Row{{int64(1)}},
1824+
"SELECT i FROM mytable WHERE s = 'x';",
1825+
[]sql.Row{{int64(999)}},
1826+
},
1827+
{
1828+
"INSERT INTO mytable VALUES (999, 'x');",
1829+
[]sql.Row{{int64(1)}},
1830+
"SELECT i FROM mytable WHERE s = 'x';",
1831+
[]sql.Row{{int64(999)}},
1832+
},
1833+
{
1834+
`INSERT INTO typestable VALUES (
1835+
999, 127, 32767, 2147483647, 9223372036854775807,
1836+
255, 65535, 4294967295, 18446744073709551615,
1837+
3.40282346638528859811704183484516925440e+38, 1.797693134862315708145274237317043567981e+308,
1838+
'2132-04-05 12:51:36', '2231-11-07',
1839+
'random text', true, '{"key":"value"}', 'blobdata'
1840+
);`,
1841+
[]sql.Row{{int64(1)}},
1842+
"SELECT * FROM typestable WHERE id = 999;",
1843+
[]sql.Row{{
1844+
int64(999), int64(math.MaxInt8), int64(math.MaxInt16), int64(math.MaxInt32), int64(math.MaxInt64),
1845+
int64(math.MaxUint8), int64(math.MaxUint16), int64(math.MaxUint32), uint64(math.MaxUint64),
1846+
float64(math.MaxFloat32), float64(math.MaxFloat64),
1847+
timeParse(sql.TimestampLayout, "2132-04-05 12:51:36"), timeParse(sql.DateLayout, "2231-11-07"),
1848+
"random text", true, `{"key":"value"}`, "blobdata",
1849+
}},
1850+
},
1851+
{
1852+
`INSERT INTO typestable VALUES (
1853+
999, -128, -32768, -2147483648, -9223372036854775808,
1854+
0, 0, 0, 0,
1855+
1.401298464324817070923729583289916131280e-45, 4.940656458412465441765687928682213723651e-324,
1856+
'0010-04-05 12:51:36', '0101-11-07',
1857+
'', false, '', ''
1858+
);`,
1859+
[]sql.Row{{int64(1)}},
1860+
"SELECT * FROM typestable WHERE id = 999;",
1861+
[]sql.Row{{
1862+
int64(999), int64(-math.MaxInt8-1), int64(-math.MaxInt16-1), int64(-math.MaxInt32-1), int64(-math.MaxInt64-1),
1863+
int64(0), int64(0), int64(0), int64(0),
1864+
float64(math.SmallestNonzeroFloat32), float64(math.SmallestNonzeroFloat64),
1865+
timeParse(sql.TimestampLayout, "0010-04-05 12:51:36"), timeParse(sql.DateLayout, "0101-11-07"),
1866+
"", false, ``, "",
1867+
}},
1868+
},
1869+
{
1870+
`INSERT INTO typestable VALUES (999, null, null, null, null, null, null, null, null,
1871+
null, null, null, null, null, null, null, null);`,
1872+
[]sql.Row{{int64(1)}},
1873+
"SELECT * FROM typestable WHERE id = 999;",
1874+
[]sql.Row{{int64(999), nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil}},
1875+
},
1876+
}
1877+
1878+
for _, insertion := range insertions {
1879+
e := newEngine(t)
1880+
ctx := newCtx()
1881+
testQueryWithContext(ctx, t, e, insertion.insertQuery, insertion.expectedInsert)
1882+
testQueryWithContext(ctx, t, e, insertion.selectQuery, insertion.expectedSelect)
1883+
}
1884+
}
1885+
1886+
func TestInsertIntoErrors(t *testing.T) {
1887+
var expectedFailures = []struct {
1888+
name string
1889+
query string
1890+
}{
1891+
{
1892+
"too few values",
1893+
"INSERT INTO mytable (s, i) VALUES ('x');",
1894+
},
1895+
{
1896+
"too many values one column",
1897+
"INSERT INTO mytable (s) VALUES ('x', 999);",
1898+
},
1899+
{
1900+
"too many values two columns",
1901+
"INSERT INTO mytable (i, s) VALUES (999, 'x', 'y');",
1902+
},
1903+
{
1904+
"too few values no columns specified",
1905+
"INSERT INTO mytable VALUES (999);",
1906+
},
1907+
{
1908+
"too many values no columns specified",
1909+
"INSERT INTO mytable VALUES (999, 'x', 'y');",
1910+
},
1911+
{
1912+
"non-existent column",
1913+
"INSERT INTO mytable (i, s, z) VALUES (999, 'x', 999);",
1914+
},
1915+
{
1916+
"duplicate column",
1917+
"INSERT INTO mytable (i, s, s) VALUES (999, 'x', 'x');",
1918+
},
1919+
}
1920+
1921+
for _, expectedFailure := range expectedFailures {
1922+
t.Run(expectedFailure.name, func(t *testing.T) {
1923+
_, _, err := newEngine(t).Query(newCtx(), expectedFailure.query)
1924+
require.Error(t, err)
1925+
})
1926+
}
17681927
}
17691928

17701929
const testNumPartitions = 5
@@ -2213,13 +2372,34 @@ func newEngineWithParallelism(t *testing.T, parallelism int) *sqle.Engine {
22132372
sql.NewRow(nil, nil, nil),
22142373
)
22152374

2375+
typestable := memory.NewPartitionedTable("typestable", sql.Schema{
2376+
{Name: "id", Type: sql.Int64, Source: "typestable"},
2377+
{Name: "i8", Type: sql.Int8, Source: "typestable", Nullable: true},
2378+
{Name: "i16", Type: sql.Int16, Source: "typestable", Nullable: true},
2379+
{Name: "i32", Type: sql.Int32, Source: "typestable", Nullable: true},
2380+
{Name: "i64", Type: sql.Int64, Source: "typestable", Nullable: true},
2381+
{Name: "u8", Type: sql.Uint8, Source: "typestable", Nullable: true},
2382+
{Name: "u16", Type: sql.Uint16, Source: "typestable", Nullable: true},
2383+
{Name: "u32", Type: sql.Uint32, Source: "typestable", Nullable: true},
2384+
{Name: "u64", Type: sql.Uint64, Source: "typestable", Nullable: true},
2385+
{Name: "f32", Type: sql.Float32, Source: "typestable", Nullable: true},
2386+
{Name: "f64", Type: sql.Float64, Source: "typestable", Nullable: true},
2387+
{Name: "ti", Type: sql.Timestamp, Source: "typestable", Nullable: true},
2388+
{Name: "da", Type: sql.Date, Source: "typestable", Nullable: true},
2389+
{Name: "te", Type: sql.Text, Source: "typestable", Nullable: true},
2390+
{Name: "bo", Type: sql.Boolean, Source: "typestable", Nullable: true},
2391+
{Name: "js", Type: sql.JSON, Source: "typestable", Nullable: true},
2392+
{Name: "bl", Type: sql.Blob, Source: "typestable", Nullable: true},
2393+
}, testNumPartitions)
2394+
22162395
db := memory.NewDatabase("mydb")
22172396
db.AddTable("mytable", table)
22182397
db.AddTable("othertable", table2)
22192398
db.AddTable("tabletest", table3)
22202399
db.AddTable("bigtable", bigtable)
22212400
db.AddTable("floattable", floatTable)
22222401
db.AddTable("niltable", nilTable)
2402+
db.AddTable("typestable", typestable)
22232403

22242404
db2 := memory.NewDatabase("foo")
22252405
db2.AddTable("other_table", table4)

sql/parse/parse.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99
"strconv"
1010
"strings"
1111

12-
opentracing "github.com/opentracing/opentracing-go"
12+
"github.com/opentracing/opentracing-go"
1313
"github.com/src-d/go-mysql-server/sql"
1414
"github.com/src-d/go-mysql-server/sql/expression"
1515
"github.com/src-d/go-mysql-server/sql/expression/function"
@@ -903,7 +903,12 @@ func convertVal(v *sqlparser.SQLVal) (sql.Expression, error) {
903903
//TODO: Use smallest integer representation and widen later.
904904
val, err := strconv.ParseInt(string(v.Val), 10, 64)
905905
if err != nil {
906-
return nil, err
906+
// Might be a uint64 value that is greater than int64 max
907+
val, checkErr := strconv.ParseUint(string(v.Val), 10, 64)
908+
if checkErr != nil {
909+
return nil, err
910+
}
911+
return expression.NewLiteral(val, sql.Uint64), nil
907912
}
908913
return expression.NewLiteral(val, sql.Int64), nil
909914
case sqlparser.FloatVal:

sql/plan/insert.go

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ import (
1111

1212
// ErrInsertIntoNotSupported is thrown when a table doesn't support inserts
1313
var ErrInsertIntoNotSupported = errors.NewKind("table doesn't support INSERT INTO")
14+
var ErrInsertIntoMismatchValueCount =
15+
errors.NewKind("number of values does not match number of columns provided")
16+
var ErrInsertIntoUnsupportedValues = errors.NewKind("%T is unsupported for inserts")
17+
var ErrInsertIntoDuplicateColumn = errors.NewKind("duplicate column name %v")
18+
var ErrInsertIntoNonexistentColumn = errors.NewKind("invalid column name %v")
1419

1520
// InsertInto is a node describing the insertion into some table.
1621
type InsertInto struct {
@@ -67,6 +72,25 @@ func (p *InsertInto) Execute(ctx *sql.Context) (int, error) {
6772

6873
dstSchema := p.Left.Schema()
6974
projExprs := make([]sql.Expression, len(dstSchema))
75+
76+
// If no columns are given, we assume the full schema in order
77+
if len(p.Columns) == 0 {
78+
p.Columns = make([]string, len(dstSchema))
79+
for i, f := range dstSchema {
80+
p.Columns[i] = f.Name
81+
}
82+
} else {
83+
err = p.validateColumns(ctx, dstSchema)
84+
if err != nil {
85+
return 0, err
86+
}
87+
}
88+
89+
err = p.validateValueCount(ctx)
90+
if err != nil {
91+
return 0, err
92+
}
93+
7094
for i, f := range dstSchema {
7195
found := false
7296
for j, col := range p.Columns {
@@ -78,8 +102,7 @@ func (p *InsertInto) Execute(ctx *sql.Context) (int, error) {
78102
}
79103

80104
if !found {
81-
def, _ := f.Type.Convert(nil)
82-
projExprs[i] = expression.NewLiteral(def, f.Type)
105+
projExprs[i] = expression.NewLiteral(nil, f.Type)
83106
}
84107
}
85108

@@ -138,3 +161,36 @@ func (p InsertInto) String() string {
138161
_ = pr.WriteChildren(p.Left.String(), p.Right.String())
139162
return pr.String()
140163
}
164+
165+
func (p *InsertInto) validateValueCount(ctx *sql.Context) error {
166+
switch node := p.Right.(type) {
167+
case *Values:
168+
for _, exprTuple := range node.ExpressionTuples {
169+
if len(exprTuple) != len(p.Columns) {
170+
return ErrInsertIntoMismatchValueCount.New()
171+
}
172+
}
173+
default:
174+
return ErrInsertIntoUnsupportedValues.New(node)
175+
}
176+
return nil
177+
}
178+
179+
func (p *InsertInto) validateColumns(ctx *sql.Context, dstSchema sql.Schema) error {
180+
dstColNames := make(map[string]struct{})
181+
for _, dstCol := range dstSchema {
182+
dstColNames[dstCol.Name] = struct{}{}
183+
}
184+
columnNames := make(map[string]struct{})
185+
for _, columnName := range p.Columns {
186+
if _, exists := dstColNames[columnName]; !exists {
187+
return ErrInsertIntoNonexistentColumn.New(columnName)
188+
}
189+
if _, exists := columnNames[columnName]; !exists {
190+
columnNames[columnName] = struct{}{}
191+
} else {
192+
return ErrInsertIntoDuplicateColumn.New(columnName)
193+
}
194+
}
195+
return nil
196+
}

0 commit comments

Comments
 (0)