Skip to content

Commit 8999cb4

Browse files
authored
Merge pull request #3249 from dolthub/elian/9916
dolthub/dolt#9916: Add TRUNCATE(X,D) Support
2 parents 70e32de + 05f9044 commit 8999cb4

File tree

8 files changed

+752
-9
lines changed

8 files changed

+752
-9
lines changed

enginetest/enginetests.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ func TestQueries(t *testing.T, harness Harness) {
8383
if IsServerEngine(e) && tt.SkipServerEngine {
8484
t.Skip("skipping for server engine")
8585
}
86-
TestQueryWithContext(t, ctx, e, harness, tt.Query, tt.Expected, tt.ExpectedColumns, nil, nil)
86+
TestQueryWithEngine(t, harness, e, tt)
8787
})
8888
}
8989

enginetest/evaluation.go

Lines changed: 94 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,27 @@ func TestQueryWithEngine(t *testing.T, harness Harness, e QueryEngine, tt querie
407407
}
408408

409409
ctx := NewContext(harness)
410-
TestQueryWithContext(t, ctx, e, harness, tt.Query, tt.Expected, tt.ExpectedColumns, tt.Bindings, nil)
410+
411+
if tt.ExpectedErr != nil {
412+
AssertErr(t, e, harness, tt.Query, tt.Bindings, tt.ExpectedErr)
413+
} else if tt.ExpectedErrStr != "" {
414+
AssertErrWithCtx(t, e, harness, ctx, tt.Query, tt.Bindings, nil, tt.ExpectedErrStr)
415+
} else if tt.ExpectedWarning != 0 {
416+
if IsServerEngine(e) && tt.SkipServerEngine {
417+
t.Skip()
418+
}
419+
AssertWarningAndTestQuery(t, e, ctx, harness,
420+
tt.Query,
421+
tt.Expected,
422+
tt.ExpectedColumns,
423+
tt.ExpectedWarning,
424+
tt.ExpectedWarningsCount,
425+
tt.ExpectedWarningMessageSubstring,
426+
false,
427+
)
428+
} else {
429+
TestQueryWithContext(t, ctx, e, harness, tt.Query, tt.Expected, tt.ExpectedColumns, tt.Bindings, nil)
430+
}
411431
})
412432
}
413433

@@ -539,7 +559,17 @@ func TestPreparedQueryWithEngine(t *testing.T, harness Harness, e QueryEngine, t
539559
}
540560
}
541561
ctx := NewContext(harness)
542-
TestPreparedQueryWithContext(t, ctx, e, harness, tt.Query, tt.Expected, tt.ExpectedColumns, nil, false)
562+
563+
if tt.ExpectedErr != nil {
564+
AssertErr(t, e, harness, tt.Query, tt.Bindings, tt.ExpectedErr)
565+
} else if tt.ExpectedErrStr != "" {
566+
AssertErrWithCtx(t, e, harness, ctx, tt.Query, tt.Bindings, nil, tt.ExpectedErrStr)
567+
} else if tt.ExpectedWarning != 0 {
568+
AssertWarningAndTestPreparedQuery(t, e, ctx, harness, tt.Query, tt.Expected, tt.ExpectedColumns,
569+
tt.ExpectedWarning, tt.ExpectedWarningsCount, tt.ExpectedWarningMessageSubstring, false)
570+
} else {
571+
TestPreparedQueryWithContext(t, ctx, e, harness, tt.Query, tt.Expected, tt.ExpectedColumns, nil, false)
572+
}
543573
})
544574
}
545575

@@ -1136,6 +1166,68 @@ func AssertWarningAndTestQuery(
11361166
validateEngine(t, ctx, harness, e)
11371167
}
11381168

1169+
// AssertWarningAndTestPreparedQuery is similar to AssertWarningAndTestQuery but works with prepared statements
1170+
func AssertWarningAndTestPreparedQuery(
1171+
t *testing.T,
1172+
e QueryEngine,
1173+
ctx *sql.Context,
1174+
harness Harness,
1175+
query string,
1176+
expected []sql.Row,
1177+
expectedCols []*sql.Column,
1178+
expectedCode int,
1179+
expectedWarningsCount int,
1180+
expectedWarningMessageSubstring string,
1181+
skipResultsCheck bool,
1182+
) {
1183+
req := require.New(t)
1184+
if ctx == nil {
1185+
ctx = NewContext(harness)
1186+
}
1187+
ctx.ClearWarnings()
1188+
ctx = ctx.WithQuery(query)
1189+
1190+
sch, iter, _, err := e.QueryWithBindings(ctx, query, nil, nil, nil)
1191+
req.NoError(err, "Unexpected error for query %s", query)
1192+
1193+
rows, err := sql.RowIterToRows(ctx, iter)
1194+
req.NoError(err, "Unexpected error for query %s", query)
1195+
1196+
if !IsServerEngine(e) {
1197+
// check warnings depend on context, which ServerEngine does not depend on
1198+
if expectedWarningsCount > 0 {
1199+
assert.Equal(t, expectedWarningsCount, len(ctx.Warnings()))
1200+
// Verify that if warnings are expected, we also configured a specific value check.
1201+
if expectedCode == 0 && len(expectedWarningMessageSubstring) == 0 {
1202+
req.Fail("Invalid test setup. Warning expected, but no value validation was configured.")
1203+
}
1204+
} else {
1205+
if expectedCode != 0 || len(expectedWarningMessageSubstring) != 0 {
1206+
req.Fail("Invalid test setup. No warnings expected, but value validation was configured")
1207+
}
1208+
assert.Zero(t, len(ctx.Warnings()), "Unexpected warnings")
1209+
}
1210+
1211+
if expectedCode > 0 {
1212+
// Not ideal. We are only supporting all warning codes being identical in a given test.
1213+
for _, warning := range ctx.Warnings() {
1214+
assert.Equal(t, expectedCode, warning.Code, "Unexpected warning code")
1215+
}
1216+
}
1217+
if len(expectedWarningMessageSubstring) > 0 {
1218+
// Not ideal. All messages must have the same substring for a given test.
1219+
for _, warning := range ctx.Warnings() {
1220+
assert.Contains(t, warning.Message, expectedWarningMessageSubstring, "Unexpected warning message")
1221+
}
1222+
}
1223+
}
1224+
1225+
if !skipResultsCheck {
1226+
CheckResults(ctx, t, harness, expected, expectedCols, sch, rows, query, e)
1227+
}
1228+
validateEngine(t, ctx, harness, e)
1229+
}
1230+
11391231
func assertSchemasEqualWithDefaults(t *testing.T, expected, actual sql.Schema) bool {
11401232
if len(expected) != len(actual) {
11411233
return assert.Equal(t, expected, actual)

enginetest/queries/function_queries.go

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,224 @@
1515
package queries
1616

1717
import (
18+
"fmt"
1819
"time"
1920

21+
"github.com/dolthub/vitess/go/mysql"
22+
2023
"github.com/dolthub/go-mysql-server/sql"
24+
"github.com/dolthub/go-mysql-server/sql/expression/function"
2125
"github.com/dolthub/go-mysql-server/sql/types"
2226
)
2327

2428
// FunctionQueryTests contains queries that primarily test SQL function calls
2529
var FunctionQueryTests = []QueryTest{
30+
// Truncate function https://github.com/dolthub/dolt/issues/9916
31+
{
32+
Query: "SELECT TRUNCATE(1.223,1)",
33+
Expected: []sql.Row{
34+
{"1.2"},
35+
},
36+
},
37+
{
38+
Query: "SELECT TRUNCATE(1.999,1)",
39+
Expected: []sql.Row{
40+
{"1.9"},
41+
},
42+
},
43+
{
44+
Query: "SELECT TRUNCATE(1.999,0)",
45+
Expected: []sql.Row{
46+
{"1"},
47+
},
48+
},
49+
{
50+
Query: "SELECT TRUNCATE(-1.999,1)",
51+
Expected: []sql.Row{
52+
{"-1.9"},
53+
},
54+
},
55+
{
56+
Query: "SELECT TRUNCATE(122,-2)",
57+
Expected: []sql.Row{
58+
{100},
59+
},
60+
},
61+
{
62+
Query: "SELECT TRUNCATE(10.28*100,0)",
63+
Expected: []sql.Row{
64+
{"1028"},
65+
},
66+
},
67+
{
68+
Query: "SELECT TRUNCATE(NULL,1)",
69+
Expected: []sql.Row{
70+
{nil},
71+
},
72+
},
73+
{
74+
Query: "SELECT TRUNCATE(1.223,NULL)",
75+
Expected: []sql.Row{
76+
{nil},
77+
},
78+
},
79+
{
80+
Query: "SELECT TRUNCATE(0.5,0)",
81+
Expected: []sql.Row{
82+
{"0"},
83+
},
84+
},
85+
{
86+
Query: "SELECT TRUNCATE(-0.5,0)",
87+
Expected: []sql.Row{
88+
{"0"},
89+
},
90+
},
91+
{
92+
Query: "SELECT TRUNCATE(1.223,100)",
93+
Expected: []sql.Row{
94+
{"1.223"},
95+
},
96+
},
97+
{
98+
Query: "SELECT TRUNCATE(1.223,-100)",
99+
Expected: []sql.Row{
100+
{"0"},
101+
},
102+
},
103+
{
104+
Query: "SELECT TRUNCATE('abc',1)",
105+
Expected: []sql.Row{
106+
{0.0},
107+
},
108+
},
109+
{
110+
Query: "SELECT TRUNCATE(1.223,'xyz')",
111+
Expected: []sql.Row{
112+
{"1"},
113+
},
114+
},
115+
{
116+
Query: "SELECT TRUNCATE(1.223,1.5)",
117+
Expected: []sql.Row{
118+
{"1.22"},
119+
},
120+
},
121+
{
122+
Query: "SELECT TRUNCATE(1.223,1.7)",
123+
Expected: []sql.Row{
124+
{"1.22"},
125+
},
126+
},
127+
{
128+
Query: "SELECT TRUNCATE(1.223,0.1)",
129+
Expected: []sql.Row{
130+
{"1"},
131+
},
132+
},
133+
{
134+
Query: "SELECT TRUNCATE(1.223,0.9)",
135+
Expected: []sql.Row{
136+
{"1.2"},
137+
},
138+
},
139+
{
140+
Query: "SELECT TRUNCATE(1.223,-0.5)",
141+
Expected: []sql.Row{
142+
{"0"},
143+
},
144+
},
145+
{
146+
Query: "SELECT TRUNCATE(1.223,-0.9)",
147+
Expected: []sql.Row{
148+
{"0"},
149+
},
150+
},
151+
{
152+
Dialect: "mysql",
153+
Query: "SELECT TRUNCATE('123abc',1)",
154+
Expected: []sql.Row{
155+
{123.0},
156+
},
157+
ExpectedWarning: mysql.ERTruncatedWrongValue,
158+
ExpectedWarningsCount: 1,
159+
ExpectedWarningMessageSubstring: fmt.Sprintf(sql.ErrTruncatedIncorrect.Message, types.Float64.String(), "123abc"),
160+
},
161+
{
162+
Dialect: "mysql",
163+
Query: "SELECT TRUNCATE('1.5abc',1)",
164+
Expected: []sql.Row{
165+
{1.5},
166+
},
167+
ExpectedWarning: mysql.ERTruncatedWrongValue,
168+
ExpectedWarningsCount: 1,
169+
ExpectedWarningMessageSubstring: fmt.Sprintf(sql.ErrTruncatedIncorrect.Message, types.Float64.String(), "1.5abc"),
170+
},
171+
{
172+
Dialect: "mysql",
173+
Query: "SELECT TRUNCATE('999xyz',2)",
174+
Expected: []sql.Row{
175+
{999.0},
176+
},
177+
ExpectedWarning: mysql.ERTruncatedWrongValue,
178+
ExpectedWarningsCount: 1,
179+
ExpectedWarningMessageSubstring: fmt.Sprintf(sql.ErrTruncatedIncorrect.Message, types.Float64.String(), "999xyz"),
180+
},
181+
{
182+
Dialect: "mysql",
183+
Query: "SELECT TRUNCATE(1.223,'1.5abc')",
184+
Expected: []sql.Row{
185+
{"1.2"},
186+
},
187+
ExpectedWarning: mysql.ERTruncatedWrongValue,
188+
ExpectedWarningsCount: 2, // Both input and precision conversions generate warnings
189+
ExpectedWarningMessageSubstring: fmt.Sprintf(sql.ErrTruncatedIncorrect.Message, types.Int32.String(), "1.5abc"),
190+
},
191+
{
192+
Dialect: "mysql",
193+
Query: "SELECT TRUNCATE(1.223,'0.5')",
194+
Expected: []sql.Row{
195+
{"1"},
196+
},
197+
ExpectedWarning: mysql.ERTruncatedWrongValue,
198+
ExpectedWarningsCount: 2, // Both input and precision conversions generate warnings
199+
ExpectedWarningMessageSubstring: fmt.Sprintf(sql.ErrTruncatedIncorrect.Message, types.Int32.String(), "0.5"),
200+
},
201+
{
202+
Dialect: "mysql",
203+
Query: "SELECT TRUNCATE(1.223,'2.7')",
204+
Expected: []sql.Row{
205+
{"1.22"},
206+
},
207+
ExpectedWarning: mysql.ERTruncatedWrongValue,
208+
ExpectedWarningsCount: 2, // Both input and precision conversions generate warnings
209+
ExpectedWarningMessageSubstring: fmt.Sprintf(sql.ErrTruncatedIncorrect.Message, types.Int32.String(), "2.7"),
210+
},
211+
{
212+
Dialect: "mysql",
213+
Query: "SELECT TRUNCATE(1.223,'invalid_precision')",
214+
Expected: []sql.Row{
215+
{"1"},
216+
},
217+
ExpectedWarning: mysql.ERTruncatedWrongValue,
218+
ExpectedWarningsCount: 2, // Both input and precision conversions generate warnings
219+
ExpectedWarningMessageSubstring: fmt.Sprintf(sql.ErrTruncatedIncorrect.Message, types.Int32.String(), "invalid_precision"),
220+
},
221+
{
222+
Query: "SELECT TRUNCATE()",
223+
ExpectedErr: sql.ErrInvalidArgumentNumber,
224+
ExpectedErrStr: fmt.Sprintf(sql.ErrInvalidArgumentNumber.Message, function.TruncateFunctionName, 2, 0),
225+
},
226+
{
227+
Query: "SELECT TRUNCATE(1)",
228+
ExpectedErr: sql.ErrInvalidArgumentNumber,
229+
ExpectedErrStr: fmt.Sprintf(sql.ErrInvalidArgumentNumber.Message, function.TruncateFunctionName, 2, 1),
230+
},
231+
{
232+
Query: "SELECT TRUNCATE(1,2,3)",
233+
ExpectedErr: sql.ErrInvalidArgumentNumber,
234+
ExpectedErrStr: fmt.Sprintf(sql.ErrInvalidArgumentNumber.Message, function.TruncateFunctionName, 2, 3),
235+
},
26236
// String Functions
27237
{
28238
Query: `SELECT CONCAT("a", "b", "c")`,

enginetest/queries/queries.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,20 @@ type QueryTest struct {
4343
// Query is the query string to execute
4444
Query string
4545
// Expected is the expected result of the query
46-
Expected []sql.Row
46+
Expected []sql.Row
47+
ExpectedErr *errors.Kind
48+
// ExpectedErrStr should be set for tests that expect a specific error string this is not linked to a custom error.
49+
// In most cases, errors should be linked to a custom error, however there are exceptions where this is not possible,
50+
// such as the use of the SIGNAL statement.
51+
ExpectedErrStr string
52+
// ExpectedWarning contains the expected warning code when a query generates warnings but not errors.
53+
ExpectedWarning int
54+
// ExpectedWarningsCount is used to test the expected number of warnings generated by a query.
55+
// The ExpectedWarning field must be set for warning counts to be checked.
56+
ExpectedWarningsCount int
57+
// ExpectedWarningMessageSubstring is used to test the contents of warning messages generated by a
58+
// query. The ExpectedWarning field must be set for warning messages to be checked.
59+
ExpectedWarningMessageSubstring string
4760
// ExpectedColumns is the set of expected column names for the query results, if specified.
4861
// Only the Name and Type matter of the columns are checked.
4962
ExpectedColumns sql.Schema

0 commit comments

Comments
 (0)