Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

executor: migrate test-infra to testify for select_into_test.go #32415

Merged
merged 4 commits into from
Feb 17, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 72 additions & 58 deletions executor/select_into_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,73 +19,77 @@ import (
"os"
"path/filepath"
"strings"
"testing"
"time"

. "github.com/pingcap/check"
"github.com/pingcap/tidb/executor"
"github.com/pingcap/tidb/parser/mysql"
"github.com/pingcap/tidb/testkit"
"github.com/pingcap/tidb/types"
"github.com/pingcap/tidb/util/testkit"
"github.com/stretchr/testify/require"
)

func cmpAndRm(expected, outfile string, c *C) {
func cmpAndRm(expected, outfile string, t *testing.T) {
content, err := os.ReadFile(outfile)
c.Assert(err, IsNil)
c.Assert(string(content), Equals, expected)
c.Assert(os.Remove(outfile), IsNil)
require.NoError(t, err)
require.Equal(t, expected, string(content))
require.NoError(t, os.Remove(outfile))
}

func randomSelectFilePath(testName string) string {
return filepath.Join(os.TempDir(), fmt.Sprintf("select-into-%v-%v.data", testName, time.Now().Nanosecond()))
}

func (s *testSuite1) TestSelectIntoFileExists(c *C) {
func TestSelectIntoFileExists(t *testing.T) {
outfile := randomSelectFilePath("TestSelectIntoFileExists")
defer func() {
c.Assert(os.Remove(outfile), IsNil)
require.NoError(t, os.Remove(outfile))
}()
tk := testkit.NewTestKit(c, s.store)
store, clean := testkit.CreateMockStore(t)
defer clean()
tk := testkit.NewTestKit(t, store)
sql := fmt.Sprintf("select 1 into outfile %q", outfile)
tk.MustExec(sql)
err := tk.ExecToErr(sql)
c.Assert(err, NotNil)
c.Assert(strings.Contains(err.Error(), "already exists") ||
strings.Contains(err.Error(), "file exists"), IsTrue, Commentf("err: %v", err))
c.Assert(strings.Contains(err.Error(), outfile), IsTrue)
require.Error(t, err)
require.Truef(t, strings.Contains(err.Error(), "already exists") || strings.Contains(err.Error(), "file exists"), "err: %v", err)
require.True(t, strings.Contains(err.Error(), outfile))
}

func (s *testSuite1) TestSelectIntoOutfileTypes(c *C) {
func TestSelectIntoOutfileTypes(t *testing.T) {
outfile := randomSelectFilePath("TestSelectIntoOutfileTypes")
tk := testkit.NewTestKit(c, s.store)
store, clean := testkit.CreateMockStore(t)
defer clean()
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")

tk.MustExec("drop table if exists t")
tk.MustExec("CREATE TABLE `t` ( `a` bit(10) DEFAULT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;")
tk.MustExec("INSERT INTO `t` VALUES (_binary '\\0'), (_binary '\\1'), (_binary '\\2'), (_binary '\\3');")
tk.MustExec(fmt.Sprintf("SELECT * FROM t INTO OUTFILE %q", outfile))
cmpAndRm("\x00\x00\n\x001\n\x002\n\x003\n", outfile, c)
cmpAndRm("\x00\x00\n\x001\n\x002\n\x003\n", outfile, t)

tk.MustExec("drop table if exists t")
tk.MustExec("CREATE TABLE `t` (col ENUM ('value1','value2','value3'));")
tk.MustExec("INSERT INTO t values ('value1'), ('value2');")
tk.MustExec(fmt.Sprintf("SELECT * FROM t INTO OUTFILE %q", outfile))
cmpAndRm("value1\nvalue2\n", outfile, c)
cmpAndRm("value1\nvalue2\n", outfile, t)

tk.MustExec("drop table if exists t")
tk.MustExec("create table t ( v json);")
tk.MustExec(`insert into t values ('{"id": 1, "name": "aaa"}'), ('{"id": 2, "name": "xxx"}');`)
tk.MustExec(fmt.Sprintf("SELECT * FROM t INTO OUTFILE %q", outfile))
cmpAndRm(`{"id": 1, "name": "aaa"}
{"id": 2, "name": "xxx"}
`, outfile, c)
`, outfile, t)

tk.MustExec("drop table if exists t")
tk.MustExec("create table t (v tinyint unsigned)")
tk.MustExec("insert into t values (0), (1)")
tk.MustExec(fmt.Sprintf("SELECT * FROM t INTO OUTFILE %q", outfile))
cmpAndRm(`0
1
`, outfile, c)
`, outfile, t)

tk.MustExec("drop table if exists t")
tk.MustExec("create table t (id float(16,2)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")
Expand All @@ -95,12 +99,14 @@ func (s *testSuite1) TestSelectIntoOutfileTypes(c *C) {
2.00
3.40
10.10
`, outfile, c)
`, outfile, t)
}

func (s *testSuite1) TestSelectIntoOutfileFromTable(c *C) {
func TestSelectIntoOutfileFromTable(t *testing.T) {
outfile := randomSelectFilePath("TestSelectIntoOutfileFromTable")
tk := testkit.NewTestKit(c, s.store)
store, clean := testkit.CreateMockStore(t)
defer clean()
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")

tk.MustExec("drop table if exists t")
Expand All @@ -115,84 +121,88 @@ func (s *testSuite1) TestSelectIntoOutfileFromTable(c *C) {
2 2.2 0.20000 b 2000-02-02 00:00:00 2002-02-02 00:00:00 02:02:02 [1, 2]
\N \N \N \N 2000-03-03 00:00:00 2003-03-03 00:00:00 03:03:03 [1, 2, 3]
4 4.4 0.40000 d \N \N \N \N
`, outfile, c)
c.Assert(tk.Se.GetSessionVars().StmtCtx.AffectedRows(), Equals, uint64(4))
`, outfile, t)
require.Equal(t, uint64(4), tk.Session().GetSessionVars().StmtCtx.AffectedRows())

tk.MustExec(fmt.Sprintf("select * from t into outfile %q fields terminated by ',' enclosed by '\"' escaped by '#'", outfile))
cmpAndRm(`"1","1.1","0.10000","a","2000-01-01 00:00:00","2001-01-01 00:00:00","01:01:01","[1]"
"2","2.2","0.20000","b","2000-02-02 00:00:00","2002-02-02 00:00:00","02:02:02","[1, 2]"
#N,#N,#N,#N,"2000-03-03 00:00:00","2003-03-03 00:00:00","03:03:03","[1, 2, 3]"
"4","4.4","0.40000","d",#N,#N,#N,#N
`, outfile, c)
c.Assert(tk.Se.GetSessionVars().StmtCtx.AffectedRows(), Equals, uint64(4))
`, outfile, t)
require.Equal(t, uint64(4), tk.Session().GetSessionVars().StmtCtx.AffectedRows())

tk.MustExec(fmt.Sprintf("select * from t into outfile %q fields terminated by ',' optionally enclosed by '\"' escaped by '#'", outfile))
cmpAndRm(`1,1.1,0.10000,"a","2000-01-01 00:00:00","2001-01-01 00:00:00","01:01:01","[1]"
2,2.2,0.20000,"b","2000-02-02 00:00:00","2002-02-02 00:00:00","02:02:02","[1, 2]"
#N,#N,#N,#N,"2000-03-03 00:00:00","2003-03-03 00:00:00","03:03:03","[1, 2, 3]"
4,4.4,0.40000,"d",#N,#N,#N,#N
`, outfile, c)
c.Assert(tk.Se.GetSessionVars().StmtCtx.AffectedRows(), Equals, uint64(4))
`, outfile, t)
require.Equal(t, uint64(4), tk.Session().GetSessionVars().StmtCtx.AffectedRows())

tk.MustExec(fmt.Sprintf("select * from t into outfile %q fields terminated by ',' optionally enclosed by '\"' escaped by '#' lines terminated by '<<<\n'", outfile))
cmpAndRm(`1,1.1,0.10000,"a","2000-01-01 00:00:00","2001-01-01 00:00:00","01:01:01","[1]"<<<
2,2.2,0.20000,"b","2000-02-02 00:00:00","2002-02-02 00:00:00","02:02:02","[1, 2]"<<<
#N,#N,#N,#N,"2000-03-03 00:00:00","2003-03-03 00:00:00","03:03:03","[1, 2, 3]"<<<
4,4.4,0.40000,"d",#N,#N,#N,#N<<<
`, outfile, c)
c.Assert(tk.Se.GetSessionVars().StmtCtx.AffectedRows(), Equals, uint64(4))
`, outfile, t)
require.Equal(t, uint64(4), tk.Session().GetSessionVars().StmtCtx.AffectedRows())
}

func (s *testSuite1) TestSelectIntoOutfileConstant(c *C) {
func TestSelectIntoOutfileConstant(t *testing.T) {
outfile := randomSelectFilePath("TestSelectIntoOutfileConstant")
tk := testkit.NewTestKit(c, s.store)
store, clean := testkit.CreateMockStore(t)
defer clean()
tk := testkit.NewTestKit(t, store)
// On windows the outfile name looks like "C:\Users\genius\AppData\Local\Temp\select-into-outfile.data",
// fmt.Sprintf("%q") is used otherwise the string become
// "C:UsersgeniusAppDataLocalTempselect-into-outfile.data".
tk.MustExec(fmt.Sprintf("select 1, 2, 3, '4', '5', '6', 7.7, 8.8, 9.9, null into outfile %q", outfile)) // test constants
cmpAndRm(`1 2 3 4 5 6 7.7 8.8 9.9 \N
`, outfile, c)
`, outfile, t)

tk.MustExec(fmt.Sprintf("select 1e10, 1e20, 1.234567e8, 0.000123e3, 1.01234567890123456789, 123456789e-10 into outfile %q", outfile))
cmpAndRm(`10000000000 1e20 123456700 0.123 1.01234567890123456789 0.0123456789
`, outfile, c)
`, outfile, t)
}

func (s *testSuite1) TestDeliminators(c *C) {
func TestDeliminators(t *testing.T) {
outfile := randomSelectFilePath("TestDeliminators")
tk := testkit.NewTestKit(c, s.store)
store, clean := testkit.CreateMockStore(t)
defer clean()
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")

tk.MustExec("CREATE TABLE `tx` (`a` varbinary(20) DEFAULT NULL,`b` int DEFAULT NULL)")
err := tk.ExecToErr(fmt.Sprintf("select * from `tx` into outfile %q fields enclosed by '\"\"'", outfile))
// enclosed by must be a single character
c.Check(err, NotNil)
c.Assert(strings.Contains(err.Error(), "Field separator argument is not what is expected"), IsTrue, Commentf("err: %v", err))
require.Error(t, err)
require.Truef(t, strings.Contains(err.Error(), "Field separator argument is not what is expected"), "err: %v", err)
err = tk.ExecToErr(fmt.Sprintf("select * from `tx` into outfile %q fields escaped by 'gg'", outfile))
// so does escaped by
c.Check(err, NotNil)
c.Assert(strings.Contains(err.Error(), "Field separator argument is not what is expected"), IsTrue, Commentf("err: %v", err))
require.Error(t, err)
require.Truef(t, strings.Contains(err.Error(), "Field separator argument is not what is expected"), "err: %v", err)

// since the above two test cases failed, it should not has outfile remained on disk
_, err = os.Stat(outfile)
c.Check(os.IsNotExist(err), IsTrue, Commentf("err: %v", err))
require.Truef(t, os.IsNotExist(err), "err: %v", err)

tk.MustExec("insert into tx values (NULL, NULL);\n")
tk.MustExec(fmt.Sprintf("select * from `tx` into outfile %q fields escaped by ''", outfile))
// if escaped by is set as empty, then NULL should not be escaped
cmpAndRm("NULL\tNULL\n", outfile, c)
cmpAndRm("NULL\tNULL\n", outfile, t)

tk.MustExec("delete from tx")
tk.MustExec("insert into tx values ('d\",\"e\",', 3), ('\\\\', 2)")
tk.MustExec(fmt.Sprintf("select * from `tx` into outfile %q FIELDS TERMINATED BY ',' ENCLOSED BY '\"' LINES TERMINATED BY '\\n'", outfile))
// enclosed by character & escaped by characters should be escaped, no matter what
cmpAndRm("\"d\\\",\\\"e\\\",\",\"3\"\n\"\\\\\",\"2\"\n", outfile, c)
cmpAndRm("\"d\\\",\\\"e\\\",\",\"3\"\n\"\\\\\",\"2\"\n", outfile, t)

tk.MustExec("delete from tx")
tk.MustExec("insert into tx values ('a\tb', 1)")
tk.MustExec(fmt.Sprintf("select * from `tx` into outfile %q FIELDS TERMINATED BY ',' ENCLOSED BY '\"' escaped by '\t' LINES TERMINATED BY '\\n'", outfile))
// enclosed by character & escaped by characters should be escaped, no matter what
cmpAndRm("\"a\t\tb\",\"1\"\n", outfile, c)
cmpAndRm("\"a\t\tb\",\"1\"\n", outfile, t)

tk.MustExec("delete from tx")
tk.MustExec(`insert into tx values ('d","e",', 1)`)
Expand All @@ -201,34 +211,34 @@ func (s *testSuite1) TestDeliminators(c *C) {
tk.MustExec(`insert into tx values (null, 4)`)
tk.MustExec(fmt.Sprintf("select * from `tx` into outfile %q FIELDS TERMINATED BY ',' ENCLOSED BY '\"' LINES TERMINATED BY '\\n'", outfile))
// line terminator will be escaped
cmpAndRm("\"d\\\",\\\"e\\\",\",\"1\"\n"+"\"\\0\",\"2\"\n"+"\"\r\\\n\b\032\t\",\"3\"\n"+"\\N,\"4\"\n", outfile, c)
cmpAndRm("\"d\\\",\\\"e\\\",\",\"1\"\n"+"\"\\0\",\"2\"\n"+"\"\r\\\n\b\032\t\",\"3\"\n"+"\\N,\"4\"\n", outfile, t)

tk.MustExec("create table tb (s char(10), b bit(48), bb blob(6))")
tk.MustExec("insert into tb values ('\\0\\b\\n\\r\\t\\Z', _binary '\\0\\b\\n\\r\\t\\Z', unhex('00080A0D091A'))")
tk.MustExec(fmt.Sprintf("select * from tb into outfile %q", outfile))
// bit type won't be escaped (verified on MySQL)
cmpAndRm("\\0\b\\\n\r\\\t\032\t"+"\000\b\n\r\t\032\t"+"\\0\b\\\n\r\\\t\032\n", outfile, c)
cmpAndRm("\\0\b\\\n\r\\\t\032\t"+"\000\b\n\r\t\032\t"+"\\0\b\\\n\r\\\t\032\n", outfile, t)

tk.MustExec("create table zero (a varchar(10), b varchar(10), c varchar(10))")
tk.MustExec("insert into zero values (unhex('00'), _binary '\\0', '\\0')")
tk.MustExec(fmt.Sprintf("select * from zero into outfile %q", outfile))
// zero will always be escaped
cmpAndRm("\\0\t\\0\t\\0\n", outfile, c)
cmpAndRm("\\0\t\\0\t\\0\n", outfile, t)
tk.MustExec(fmt.Sprintf("select * from zero into outfile %q fields enclosed by '\"'", outfile))
// zero will always be escaped, including when being enclosed
cmpAndRm("\"\\0\"\t\"\\0\"\t\"\\0\"\n", outfile, c)
cmpAndRm("\"\\0\"\t\"\\0\"\t\"\\0\"\n", outfile, t)

tk.MustExec("create table tt (a char(10), b char(10), c char(10))")
tk.MustExec("insert into tt values ('abcd', 'abcd', 'abcd')")
tk.MustExec(fmt.Sprintf("select * from tt into outfile %q fields terminated by 'a-' lines terminated by 'b--'", outfile))
// when not escaped, the first character of both terminators will be escaped
cmpAndRm("\\a\\bcda-\\a\\bcda-\\a\\bcdb--", outfile, c)
cmpAndRm("\\a\\bcda-\\a\\bcda-\\a\\bcdb--", outfile, t)
tk.MustExec(fmt.Sprintf("select * from tt into outfile %q fields terminated by 'a-' enclosed by '\"' lines terminated by 'b--'", outfile))
// when escaped, only line terminator's first character will be escaped
cmpAndRm("\"a\\bcd\"a-\"a\\bcd\"a-\"a\\bcd\"b--", outfile, c)
cmpAndRm("\"a\\bcd\"a-\"a\\bcd\"a-\"a\\bcd\"b--", outfile, t)
}

func (s *testSuite1) TestDumpReal(c *C) {
func TestDumpReal(t *testing.T) {
cases := []struct {
val float64
dec int
Expand All @@ -247,13 +257,15 @@ func (s *testSuite1) TestDumpReal(c *C) {
tp := types.NewFieldType(mysql.TypeDouble)
tp.Decimal = testCase.dec
_, buf := executor.DumpRealOutfile(nil, nil, testCase.val, tp)
c.Assert(string(buf), Equals, testCase.result)
require.Equal(t, testCase.result, string(buf))
}
}

func (s *testSuite1) TestEscapeType(c *C) {
func TestEscapeType(t *testing.T) {
outfile := randomSelectFilePath("TestEscapeType")
tk := testkit.NewTestKit(c, s.store)
store, clean := testkit.CreateMockStore(t)
defer clean()
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("drop table if exists t")
tk.MustExec(`create table t (
Expand All @@ -268,18 +280,20 @@ func (s *testSuite1) TestEscapeType(c *C) {

tk.MustExec(fmt.Sprintf("select * from t into outfile '%v' fields terminated by ',' escaped by '1'", outfile))
cmpAndRm(`1,1,11,11,{"key": 11},11,11
`, outfile, c)
`, outfile, t)
}

func (s *testSuite1) TestYearType(c *C) {
func TestYearType(t *testing.T) {
outfile := randomSelectFilePath("TestYearType")
tk := testkit.NewTestKit(c, s.store)
store, clean := testkit.CreateMockStore(t)
defer clean()
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("drop table if exists t")
tk.MustExec("create table t(time1 year(4) default '2030');")
tk.MustExec("insert into t values (2010), (2011), (2012);")
tk.MustExec("insert into t values ();")

tk.MustExec(fmt.Sprintf("select * from t into outfile '%v' fields terminated by ',' optionally enclosed by '\"' lines terminated by '\\n';", outfile))
cmpAndRm("2010\n2011\n2012\n2030\n", outfile, c)
cmpAndRm("2010\n2011\n2012\n2030\n", outfile, t)
}