From dc32026d840c84c9bcb82350378320a9727ff97e Mon Sep 17 00:00:00 2001 From: James Cor Date: Thu, 5 Dec 2024 10:40:11 -0800 Subject: [PATCH] return ok result for `select into` statements (#2781) --- enginetest/enginetests.go | 99 +++++++++++++++++----------- enginetest/queries/script_queries.go | 9 ++- sql/plan/into.go | 3 +- sql/rowexec/rel.go | 8 +-- 4 files changed, 70 insertions(+), 49 deletions(-) diff --git a/enginetest/enginetests.go b/enginetest/enginetests.go index fab5b3c37f..81a617ecdc 100644 --- a/enginetest/enginetests.go +++ b/enginetest/enginetests.go @@ -1017,44 +1017,50 @@ func TestSelectIntoFile(t *testing.T, harness Harness) { }) tests := []struct { - file string - query string - exp string - err *errors.Kind - skip bool + file string + query string + exp string + expRows []sql.Row + err *errors.Kind + skip bool }{ { - file: "outfile.txt", - query: "select * from mytable into outfile 'outfile.txt';", + file: "outfile.txt", + query: "select * from mytable into outfile 'outfile.txt';", + expRows: []sql.Row{{types.NewOkResult(3)}}, exp: "" + "1\tfirst row\n" + "2\tsecond row\n" + "3\tthird row\n", }, { - file: "dumpfile.txt", - query: "select * from mytable limit 1 into dumpfile 'dumpfile.txt';", - exp: "1first row", + file: "dumpfile.txt", + query: "select * from mytable limit 1 into dumpfile 'dumpfile.txt';", + expRows: []sql.Row{{types.NewOkResult(1)}}, + exp: "1first row", }, { - file: "outfile.txt", - query: "select * from mytable into outfile 'outfile.txt' fields terminated by ',';", + file: "outfile.txt", + query: "select * from mytable into outfile 'outfile.txt' fields terminated by ',';", + expRows: []sql.Row{{types.NewOkResult(3)}}, exp: "" + "1,first row\n" + "2,second row\n" + "3,third row\n", }, { - file: "outfile.txt", - query: "select * from mytable into outfile 'outfile.txt' fields terminated by '$$';", + file: "outfile.txt", + query: "select * from mytable into outfile 'outfile.txt' fields terminated by '$$';", + expRows: []sql.Row{{types.NewOkResult(3)}}, exp: "" + "1$$first row\n" + "2$$second row\n" + "3$$third row\n", }, { - file: "outfile.txt", - query: "select * from mytable into outfile 'outfile.txt' fields terminated by ',' optionally enclosed by '\"';", + file: "outfile.txt", + query: "select * from mytable into outfile 'outfile.txt' fields terminated by ',' optionally enclosed by '\"';", + expRows: []sql.Row{{types.NewOkResult(3)}}, exp: "" + "1,\"first row\"\n" + "2,\"second row\"\n" + @@ -1071,56 +1077,63 @@ func TestSelectIntoFile(t *testing.T, harness Harness) { err: sql.ErrUnexpectedSeparator, }, { - file: "outfile.txt", - query: "select * from mytable into outfile 'outfile.txt' fields terminated by ',' enclosed by '\"';", + file: "outfile.txt", + query: "select * from mytable into outfile 'outfile.txt' fields terminated by ',' enclosed by '\"';", + expRows: []sql.Row{{types.NewOkResult(3)}}, exp: "" + "\"1\",\"first row\"\n" + "\"2\",\"second row\"\n" + "\"3\",\"third row\"\n", }, { - file: "outfile.txt", - query: "select * from mytable into outfile 'outfile.txt' fields terminated by ',' lines terminated by ';';", + file: "outfile.txt", + query: "select * from mytable into outfile 'outfile.txt' fields terminated by ',' lines terminated by ';';", + expRows: []sql.Row{{types.NewOkResult(3)}}, exp: "" + "1,first row;" + "2,second row;" + "3,third row;", }, { - file: "outfile.txt", - query: "select * from mytable into outfile 'outfile.txt' fields terminated by ',' lines terminated by 'r';", + file: "outfile.txt", + query: "select * from mytable into outfile 'outfile.txt' fields terminated by ',' lines terminated by 'r';", + expRows: []sql.Row{{types.NewOkResult(3)}}, exp: "" + "1,fi\\rst \\rowr" + "2,second \\rowr" + "3,thi\\rd \\rowr", }, { - file: "outfile.txt", - query: "select * from mytable into outfile 'outfile.txt' fields terminated by ',' lines starting by 'r';", + file: "outfile.txt", + query: "select * from mytable into outfile 'outfile.txt' fields terminated by ',' lines starting by 'r';", + expRows: []sql.Row{{types.NewOkResult(3)}}, exp: "" + "r1,first row\n" + "r2,second row\n" + "r3,third row\n", }, { - file: "outfile.txt", - query: "select * from mytable into outfile 'outfile.txt' fields terminated by '';", + file: "outfile.txt", + query: "select * from mytable into outfile 'outfile.txt' fields terminated by '';", + expRows: []sql.Row{{types.NewOkResult(3)}}, exp: "" + "1\tfirst row\n" + "2\tsecond row\n" + "3\tthird row\n", }, { - file: "outfile.txt", - query: "select * from mytable into outfile 'outfile.txt' fields terminated by ',' lines terminated by '';", + file: "outfile.txt", + query: "select * from mytable into outfile 'outfile.txt' fields terminated by ',' lines terminated by '';", + expRows: []sql.Row{{types.NewOkResult(3)}}, exp: "" + "1,first row" + "2,second row" + "3,third row", }, { - file: "outfile.txt", - query: "select * from niltable into outfile 'outfile.txt';", + file: "outfile.txt", + query: "select * from niltable into outfile 'outfile.txt';", + expRows: []sql.Row{{types.NewOkResult(6)}}, exp: "1\t\\N\t\\N\t\\N\n" + "2\t2\t1\t\\N\n" + "3\t\\N\t0\t\\N\n" + @@ -1129,8 +1142,9 @@ func TestSelectIntoFile(t *testing.T, harness Harness) { "6\t6\t0\t6\n", }, { - file: "outfile.txt", - query: "select * from niltable into outfile 'outfile.txt' fields terminated by ',' enclosed by '\"';", + file: "outfile.txt", + query: "select * from niltable into outfile 'outfile.txt' fields terminated by ',' enclosed by '\"';", + expRows: []sql.Row{{types.NewOkResult(6)}}, exp: "\"1\",\\N,\\N,\\N\n" + "\"2\",\"2\",\"1\",\\N\n" + "\"3\",\\N,\"0\",\\N\n" + @@ -1139,8 +1153,9 @@ func TestSelectIntoFile(t *testing.T, harness Harness) { "\"6\",\"6\",\"0\",\"6\"\n", }, { - file: "outfile.txt", - query: "select * from niltable into outfile 'outfile.txt' fields terminated by ',' escaped by '$';", + file: "outfile.txt", + query: "select * from niltable into outfile 'outfile.txt' fields terminated by ',' escaped by '$';", + expRows: []sql.Row{{types.NewOkResult(6)}}, exp: "1,$N,$N,$N\n" + "2,2,1,$N\n" + "3,$N,0,$N\n" + @@ -1149,8 +1164,9 @@ func TestSelectIntoFile(t *testing.T, harness Harness) { "6,6,0,6\n", }, { - file: "outfile.txt", - query: "select * from niltable into outfile 'outfile.txt' fields terminated by ',' escaped by '';", + file: "outfile.txt", + query: "select * from niltable into outfile 'outfile.txt' fields terminated by ',' escaped by '';", + expRows: []sql.Row{{types.NewOkResult(6)}}, exp: "1,NULL,NULL,NULL\n" + "2,2,1,NULL\n" + "3,NULL,0,NULL\n" + @@ -1159,8 +1175,9 @@ func TestSelectIntoFile(t *testing.T, harness Harness) { "6,6,0,6\n", }, { - file: "./subdir/outfile.txt", - query: "select * from mytable into outfile './subdir/outfile.txt';", + file: "./subdir/outfile.txt", + query: "select * from mytable into outfile './subdir/outfile.txt';", + expRows: []sql.Row{{types.NewOkResult(3)}}, exp: "" + "1\tfirst row\n" + "2\tsecond row\n" + @@ -1198,7 +1215,11 @@ func TestSelectIntoFile(t *testing.T, harness Harness) { } // in case there are any residual files from previous runs os.Remove(tt.file) - TestQueryWithContext(t, ctx, e, harness, tt.query, nil, nil, nil, nil) + var expected = tt.expRows + if IsServerEngine(e) { + expected = nil + } + TestQueryWithContext(t, ctx, e, harness, tt.query, expected, types.OkResultSchema, nil, nil) res, err := os.ReadFile(tt.file) require.NoError(t, err) require.Equal(t, tt.exp, string(res)) diff --git a/enginetest/queries/script_queries.go b/enginetest/queries/script_queries.go index 2bb5cde6bb..15993d6a47 100644 --- a/enginetest/queries/script_queries.go +++ b/enginetest/queries/script_queries.go @@ -1623,11 +1623,10 @@ CREATE TABLE tab3 ( }, Assertions: []ScriptTestAssertion{ { - // SELECT INTO has an empty result schema - // /~https://github.com/dolthub/dolt/issues/6105 - Query: `SELECT 1 INTO @abc`, - Expected: []sql.Row{{}}, - ExpectedColumns: nil, + Query: `SELECT 1 INTO @abc`, + SkipResultCheckOnServerEngine: true, + Expected: []sql.Row{{types.NewOkResult(1)}}, + ExpectedColumns: nil, }, { Query: `SELECT @abc`, diff --git a/sql/plan/into.go b/sql/plan/into.go index 85761ede23..2943eef2b2 100644 --- a/sql/plan/into.go +++ b/sql/plan/into.go @@ -19,6 +19,7 @@ import ( "strings" "github.com/dolthub/go-mysql-server/sql" + "github.com/dolthub/go-mysql-server/sql/types" ) // Into is a node to wrap the top-level node in a query plan so that any result will set user-defined or others @@ -79,7 +80,7 @@ var emptySch = make(sql.Schema, 0) func (i *Into) Schema() sql.Schema { // SELECT INTO does not return results directly (only through SQL vars or files), // so it's result schema is always empty. - return emptySch + return types.OkResultSchema } func (i *Into) IsReadOnly() bool { diff --git a/sql/rowexec/rel.go b/sql/rowexec/rel.go index 17bb1c7f37..6c15ba28ac 100644 --- a/sql/rowexec/rel.go +++ b/sql/rowexec/rel.go @@ -630,7 +630,7 @@ func (b *BaseBuilder) buildInto(ctx *sql.Context, n *plan.Into, row sql.Row) (sq } file.WriteString(n.LinesTerminatedBy) } - return sql.RowsToRowIter(sql.Row{}), nil + return sql.RowsToRowIter(sql.Row{types.NewOkResult(len(rows))}), nil } rowNum := len(rows) @@ -652,12 +652,12 @@ func (b *BaseBuilder) buildInto(ctx *sql.Context, n *plan.Into, row sql.Row) (sq file.WriteString(fmt.Sprintf("%v", val)) } } - return sql.RowsToRowIter(sql.Row{}), nil + return sql.RowsToRowIter(sql.Row{types.NewOkResult(rowNum)}), nil } if rowNum == 0 { // a warning with error code 1329 occurs (No data), and make no change to variables - return sql.RowsToRowIter(sql.Row{}), nil + return sql.RowsToRowIter(sql.Row{types.NewOkResult(0)}), nil } if len(rows[0]) != len(n.IntoVars) { return nil, sql.ErrColumnNumberDoesNotMatch.New() @@ -684,7 +684,7 @@ func (b *BaseBuilder) buildInto(ctx *sql.Context, n *plan.Into, row sql.Row) (sq } } - return sql.RowsToRowIter(sql.Row{}), nil + return sql.RowsToRowIter(sql.Row{types.NewOkResult(1)}), nil } func (b *BaseBuilder) buildExternalProcedure(ctx *sql.Context, n *plan.ExternalProcedure, row sql.Row) (sql.RowIter, error) {