Skip to content

Commit 55ca62e

Browse files
committed
Merge branch 'main' of https://github.com/dolthub/go-mysql-server into angela/binding
2 parents 176b7f2 + cf94656 commit 55ca62e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

87 files changed

+3023
-1085
lines changed

enginetest/enginetests.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -793,6 +793,21 @@ func TestQueryPlanWithEngine(t *testing.T, harness Harness, e QueryEngine, tt qu
793793
})
794794
}
795795

796+
func TestQueryPlanScripts(t *testing.T, harness Harness) {
797+
harness.Setup(setup.MydbData)
798+
for _, script := range queries.QueryPlanScriptTests {
799+
if sh, ok := harness.(SkippingHarness); ok {
800+
if sh.SkipQueryTest(script.Name) {
801+
t.Run(script.Name, func(t *testing.T) {
802+
t.Skip(script.Name)
803+
})
804+
continue
805+
}
806+
}
807+
TestScript(t, harness, script)
808+
}
809+
}
810+
796811
func TestOrderByGroupBy(t *testing.T, harness Harness) {
797812
for _, tt := range queries.OrderByGroupByScriptTests {
798813
TestScript(t, harness, tt)

enginetest/evaluation.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,12 +144,20 @@ func TestScriptWithEngine(t *testing.T, e QueryEngine, harness Harness, script q
144144
}
145145
TestQueryWithContext(t, ctx, e, harness, assertion.Query, expected, assertion.ExpectedColumns, assertion.Bindings, nil)
146146
}
147+
147148
if assertion.ExpectedIndexes != nil && !IsServerEngine(e) {
148149
evalIndexTest(t, harness, e, assertion.Query, assertion.ExpectedIndexes, assertion.Skip)
149150
}
150151
if assertion.JoinTypes != nil && !IsServerEngine(e) {
151152
evalJoinTypeTest(t, harness, e, assertion.Query, assertion.JoinTypes, assertion.Skip)
152153
}
154+
155+
if assertion.ExpectedPlan != "" && !IsServerEngine(e) {
156+
options := sql.DescribeOptions{
157+
Debug: true,
158+
}
159+
TestQueryPlanWithName(t, options.String(), harness, e, assertion.Query, assertion.ExpectedPlan, options)
160+
}
153161
})
154162
}
155163
})

enginetest/join_op_tests.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1921,6 +1921,18 @@ SELECT SUM(x) FROM xy WHERE x IN (
19211921
},
19221922
},
19231923
},
1924+
{
1925+
name: "where not exists",
1926+
setup: [][]string{
1927+
setup.XyData[0],
1928+
},
1929+
tests: []JoinOpTests{
1930+
{
1931+
Query: `select * from xy_hasnull x where not exists(select 1 from ab_hasnull a where a.b = x.y)`,
1932+
Expected: []sql.Row{{1, 0}, {3, nil}},
1933+
},
1934+
},
1935+
},
19241936
{
19251937
name: "multi-column merge join",
19261938
setup: [][]string{

enginetest/memory_engine_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,10 @@ func TestQueryPlans(t *testing.T) {
327327
}
328328
}
329329

330+
func TestQueryPlanScripts(t *testing.T) {
331+
enginetest.TestQueryPlanScripts(t, enginetest.NewMemoryHarness("default", 1, testNumPartitions, true, mergableIndexDriver))
332+
}
333+
330334
func TestSingleQueryPlan(t *testing.T) {
331335
t.Skip()
332336
tt := []queries.QueryPlanTest{

enginetest/plangen/cmd/plangen/main.go

Lines changed: 122 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -94,15 +94,9 @@ func ParseSpec(path string) (PlanSpecs, error) {
9494
return res, err
9595
}
9696

97-
func generatePlans(specPath string, srcRoot string) error {
98-
specs, err := ParseSpec(specPath)
99-
if err != nil {
100-
exit(err)
101-
}
102-
for _, spec := range specs.Plans {
103-
var buf bytes.Buffer
104-
fmt.Fprintf(&buf, "// Code generated by plangen.\n\n")
105-
fmt.Fprintf(&buf, `// Copyright 2024 Dolthub, Inc.
97+
func writeHeader(buf *bytes.Buffer, pkg string) {
98+
_, _ = fmt.Fprint(buf, "// Code generated by plangen.\n\n")
99+
_, _ = fmt.Fprint(buf, `// Copyright 2025 Dolthub, Inc.
106100
//
107101
// Licensed under the Apache License, Version 2.0 (the "License");
108102
// you may not use this file except in compliance with the License.
@@ -115,10 +109,24 @@ func generatePlans(specPath string, srcRoot string) error {
115109
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
116110
// See the License for the specific language governing permissions and
117111
// limitations under the License.`)
118-
fmt.Fprintf(&buf, "\n\n")
119-
fmt.Fprintf(&buf, "package %s\n\n", *pkg)
112+
_, _ = fmt.Fprint(buf, "\n\n")
113+
_, _ = fmt.Fprintf(buf, "package %s\n\n", pkg)
114+
}
120115

121-
err = generatePlansForSuite(spec, &buf)
116+
func generatePlans(specPath string, srcRoot string) error {
117+
specs, err := ParseSpec(specPath)
118+
if err != nil {
119+
exit(err)
120+
}
121+
for _, spec := range specs.Plans {
122+
var buf bytes.Buffer
123+
writeHeader(&buf, *pkg)
124+
if spec.Name == "QueryPlanScriptTests" {
125+
_, _ = fmt.Fprint(&buf, "import (\n\t\"github.com/dolthub/go-mysql-server/sql\"\n)\n\n")
126+
err = generatePlansForScriptSuite(spec, &buf)
127+
} else {
128+
err = generatePlansForSuite(spec, &buf)
129+
}
122130
if err != nil {
123131
exit(err)
124132
}
@@ -131,6 +139,33 @@ func generatePlans(specPath string, srcRoot string) error {
131139
return nil
132140
}
133141

142+
func writePlanString(w *bytes.Buffer, planString string) {
143+
for i, line := range strings.Split(planString, "\n") {
144+
if i > 0 {
145+
_, _ = w.WriteString(" + \n")
146+
}
147+
if len(line) > 0 {
148+
_, _ = w.WriteString(fmt.Sprintf(`"%s\n"`, strings.ReplaceAll(line, `"`, `\"`)))
149+
} else {
150+
// final line with comma
151+
_, _ = w.WriteString("\"\",\n")
152+
}
153+
}
154+
}
155+
156+
func analyzeQuery(ctx *sql.Context, engine enginetest.QueryEngine, query string) sql.Node {
157+
binder := planbuilder.New(ctx, engine.EngineAnalyzer().Catalog, engine.EngineEventScheduler(), nil)
158+
parsed, _, _, qFlags, err := binder.Parse(query, nil, false)
159+
if err != nil {
160+
exit(fmt.Errorf("%w\nfailed to parse query: %s", err, query))
161+
}
162+
node, err := engine.EngineAnalyzer().Analyze(ctx, parsed, nil, qFlags)
163+
if err != nil {
164+
exit(fmt.Errorf("%w\nfailed to analyze query: %s", err, query))
165+
}
166+
return node
167+
}
168+
134169
func generatePlansForSuite(spec PlanSpec, w *bytes.Buffer) error {
135170
harness := enginetest.NewMemoryHarness("default", 1, 1, true, nil)
136171
s := specSetup(spec.Name)
@@ -165,49 +200,29 @@ func generatePlansForSuite(spec PlanSpec, w *bytes.Buffer) error {
165200

166201
if !tt.Skip {
167202
ctx := enginetest.NewContext(harness)
168-
binder := planbuilder.New(ctx, engine.EngineAnalyzer().Catalog, engine.EngineEventScheduler(), nil)
169-
parsed, _, _, qFlags, err := binder.Parse(tt.Query, nil, false)
170-
if err != nil {
171-
exit(fmt.Errorf("%w\nfailed to parse query: %s", err, tt.Query))
172-
}
173-
node, err := engine.EngineAnalyzer().Analyze(ctx, parsed, nil, qFlags)
174-
if err != nil {
175-
exit(fmt.Errorf("%w\nfailed to analyze query: %s", err, tt.Query))
176-
}
177-
178-
emitPlanString := func(planString string) {
179-
for i, line := range strings.Split(planString, "\n") {
180-
if i > 0 {
181-
_, _ = w.WriteString(" + \n")
182-
}
183-
if len(line) > 0 {
184-
_, _ = w.WriteString(fmt.Sprintf(`"%s\n"`, strings.ReplaceAll(line, `"`, `\"`)))
185-
} else {
186-
// final line with comma
187-
_, _ = w.WriteString("\"\",\n")
188-
}
189-
}
190-
}
191-
203+
node := analyzeQuery(ctx, engine, tt.Query)
192204
_, _ = w.WriteString(`ExpectedPlan: `)
193-
emitPlanString(sql.Describe(enginetest.ExtractQueryNode(node), sql.DescribeOptions{
205+
planString := sql.Describe(enginetest.ExtractQueryNode(node), sql.DescribeOptions{
194206
Debug: true,
195-
}))
207+
})
208+
writePlanString(w, planString)
196209

197210
if node.IsReadOnly() {
198211
_, _ = w.WriteString(`ExpectedEstimates: `)
199-
emitPlanString(sql.Describe(enginetest.ExtractQueryNode(node), sql.DescribeOptions{
212+
planString = sql.Describe(enginetest.ExtractQueryNode(node), sql.DescribeOptions{
200213
Estimates: true,
201-
}))
214+
})
215+
writePlanString(w, planString)
202216
err = enginetest.ExecuteNode(ctx, engine, node)
203217
if err != nil {
204218
exit(fmt.Errorf("%w\nfailed to execute query: %s", err, tt.Query))
205219
}
206220
_, _ = w.WriteString(`ExpectedAnalysis: `)
207-
emitPlanString(sql.Describe(enginetest.ExtractQueryNode(node), sql.DescribeOptions{
221+
planString = sql.Describe(enginetest.ExtractQueryNode(node), sql.DescribeOptions{
208222
Analyze: true,
209223
Estimates: true,
210-
}))
224+
})
225+
writePlanString(w, planString)
211226
}
212227
} else {
213228
_, _ = w.WriteString(`Skip: true,\n`)
@@ -220,6 +235,71 @@ func generatePlansForSuite(spec PlanSpec, w *bytes.Buffer) error {
220235
return nil
221236
}
222237

238+
func generatePlansForScriptSuite(spec PlanSpec, w *bytes.Buffer) error {
239+
harness := enginetest.NewMemoryHarness("default", 1, 1, true, nil)
240+
harness.Setup(setup.MydbData)
241+
_, _ = fmt.Fprintf(w, "var %s = []ScriptTest{\n", spec.Name)
242+
for _, tt := range queries.QueryPlanScriptTests {
243+
w.WriteString("\t{\n")
244+
if tt.Dialect != "" {
245+
w.WriteString(fmt.Sprintf("\t\tDialect: \"%s\",\n", tt.Dialect))
246+
}
247+
w.WriteString(fmt.Sprintf("\t\tName: \"%s\",\n", tt.Name))
248+
w.WriteString("\t\tSetUpScript: []string{\n")
249+
for _, setupQuery := range tt.SetUpScript {
250+
w.WriteString(fmt.Sprintf("\t\t\t\"%s\",\n", setupQuery))
251+
}
252+
w.WriteString("\t\t},\n")
253+
w.WriteString("\t\tAssertions: []ScriptTestAssertion{\n")
254+
for _, assertion := range tt.Assertions {
255+
w.WriteString("\t\t\t{\n")
256+
if assertion.Skip {
257+
w.WriteString("\t\t\t\tSkip: true,\n")
258+
}
259+
w.WriteString(fmt.Sprintf("\t\t\t\tQuery: \"%s\",\n", assertion.Query))
260+
w.WriteString(fmt.Sprintf("\t\t\t\tExpected: []sql.Row{\n"))
261+
for _, expRow := range assertion.Expected {
262+
w.WriteString(fmt.Sprintf("\t\t\t\t\t%#v,\n", expRow))
263+
}
264+
w.WriteString(fmt.Sprintf("\t\t\t\t},\n"))
265+
if assertion.Skip {
266+
w.WriteString("\t\t\t},\n")
267+
continue
268+
}
269+
270+
engine, err := harness.NewEngine(nil)
271+
if err != nil {
272+
exit(err)
273+
}
274+
ctx := enginetest.NewContext(harness)
275+
for _, setupQuery := range tt.SetUpScript {
276+
ctx = ctx.WithQuery(setupQuery)
277+
_, iter, _, err := engine.Query(ctx, setupQuery)
278+
if err != nil {
279+
exit(fmt.Errorf("%w\nfailed to execute setup query: %s", err, setupQuery))
280+
}
281+
_, err = sql.RowIterToRows(ctx, iter)
282+
if err != nil {
283+
exit(fmt.Errorf("%w\nfailed to execute setup query: %s", err, setupQuery))
284+
}
285+
}
286+
287+
node := analyzeQuery(ctx, engine, assertion.Query)
288+
w.WriteString("\t\t\t\tExpectedPlan: ")
289+
planString := sql.Describe(enginetest.ExtractQueryNode(node), sql.DescribeOptions{
290+
Debug: true,
291+
})
292+
writePlanString(w, planString)
293+
w.WriteString("\t\t\t},\n")
294+
}
295+
w.WriteString("\t\t},\n")
296+
w.WriteString("\t},\n")
297+
}
298+
w.WriteString("}")
299+
300+
return nil
301+
}
302+
223303
func specSetup(name string) [][]setup.SetupScript {
224304
switch name {
225305
case "PlanTests":

enginetest/plangen/testdata/spec.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,6 @@ plans:
1616
- name: GeneratedColumnPlanTests
1717
path: enginetest/queries/generated_column_plans.go
1818
- name: SysbenchPlanTests
19-
path: enginetest/queries/sysbench_plans.go
19+
path: enginetest/queries/sysbench_plans.go
20+
- name: QueryPlanScriptTests
21+
path: enginetest/queries/query_plan_script_tests.go

enginetest/queries/alter_table_queries.go

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1029,12 +1029,58 @@ var AlterTableScripts = []ScriptTest{
10291029
},
10301030
},
10311031
},
1032+
{
1033+
Name: "alter table comment",
1034+
SetUpScript: []string{
1035+
"create table t (i int)",
1036+
"create table tableWithComment (i int) comment = 'comment'",
1037+
},
1038+
Assertions: []ScriptTestAssertion{
1039+
{
1040+
Query: "show create table t",
1041+
Expected: []sql.Row{{
1042+
"t",
1043+
"CREATE TABLE `t` (\n `i` int\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin",
1044+
}},
1045+
},
1046+
{
1047+
Query: "show create table tableWithComment",
1048+
Expected: []sql.Row{{
1049+
"tableWithComment",
1050+
"CREATE TABLE `tableWithComment` (\n `i` int\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin COMMENT='comment'",
1051+
}},
1052+
},
1053+
{
1054+
Query: "alter table t comment = 'new comment'",
1055+
Expected: []sql.Row{{types.NewOkResult(0)}},
1056+
},
1057+
{
1058+
Query: "alter table tableWithComment comment = 'new comment'",
1059+
Expected: []sql.Row{{types.NewOkResult(0)}},
1060+
},
1061+
{
1062+
Query: "show create table t",
1063+
Expected: []sql.Row{{
1064+
"t",
1065+
"CREATE TABLE `t` (\n `i` int\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin COMMENT='new comment'",
1066+
}},
1067+
},
1068+
{
1069+
Query: "show create table tableWithComment",
1070+
Expected: []sql.Row{{
1071+
"tableWithComment",
1072+
"CREATE TABLE `tableWithComment` (\n `i` int\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin COMMENT='new comment'",
1073+
}},
1074+
},
1075+
},
1076+
},
10321077
{
10331078
Name: "alter table comments are escaped",
10341079
SetUpScript: []string{
10351080
"create table t (i int);",
10361081
`alter table t modify column i int comment "newline \n | return \r | backslash \\ | NUL \0 \x00 | ctrlz \Z \x1A"`,
10371082
`alter table t add column j int comment "newline \n | return \r | backslash \\ | NUL \0 \x00 | ctrlz \Z \x1A"`,
1083+
`alter table t comment = "newline \n | return \r | backslash \\ | NUL \0 \x00 | ctrlz \Z \x1A"`,
10381084
},
10391085
Assertions: []ScriptTestAssertion{
10401086
{
@@ -1043,7 +1089,7 @@ var AlterTableScripts = []ScriptTest{
10431089
"t",
10441090
"CREATE TABLE `t` (\n `i` int COMMENT 'newline \\n | return \\r | backslash \\\\ | NUL \\0 x00 | ctrlz \x1A x1A'," +
10451091
"\n `j` int COMMENT 'newline \\n | return \\r | backslash \\\\ | NUL \\0 x00 | ctrlz \x1A x1A'\n" +
1046-
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}},
1092+
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin COMMENT='newline \\n | return \\r | backslash \\\\ | NUL \\0 x00 | ctrlz \u001A x1A'"}},
10471093
},
10481094
},
10491095
},
@@ -2273,6 +2319,20 @@ var ModifyColumnScripts = []ScriptTest{
22732319
},
22742320
},
22752321
},
2322+
{
2323+
// https://github.com/dolthub/dolt/issues/9591
2324+
Name: "Add check constraint when modifying column",
2325+
SetUpScript: []string{
2326+
"create table t(pk int primary key)",
2327+
"alter table t modify column pk int check(pk < 10)",
2328+
},
2329+
Assertions: []ScriptTestAssertion{
2330+
{
2331+
Query: "insert into t values (20)",
2332+
ExpectedErr: sql.ErrCheckConstraintViolated,
2333+
},
2334+
},
2335+
},
22762336
{
22772337
Name: "error cases",
22782338
SetUpScript: []string{},

enginetest/queries/generated_column_plans.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)