@@ -20,6 +20,7 @@ import (
2020 "fmt"
2121 "io"
2222 "net"
23+ "os"
2324 "reflect"
2425 "strings"
2526 "sync"
@@ -887,26 +888,265 @@ func TestSpatialInsertInto(t *testing.T, harness Harness) {
887888 }
888889}
889890
891+ // setSecureFilePriv sets the secure_file_priv system variable to the current working directory.
892+ func setSecureFilePriv () error {
893+ wd , err := os .Getwd ()
894+ if err != nil {
895+ wd = "./"
896+ }
897+ return sql .SystemVariables .AssignValues (map [string ]interface {}{
898+ "secure_file_priv" : wd ,
899+ })
900+ }
901+
890902func TestLoadData (t * testing.T , harness Harness ) {
891903 harness .Setup (setup .MydbData )
904+
905+ require .NoError (t , setSecureFilePriv ())
906+ TestQuery (t , harness , "select @@secure_file_priv != '';" , []sql.Row {{true }}, nil , nil )
907+
892908 for _ , script := range queries .LoadDataScripts {
893909 TestScript (t , harness , script )
894910 }
895911}
896912
897913func TestLoadDataErrors (t * testing.T , harness Harness ) {
914+ require .NoError (t , setSecureFilePriv ())
915+ TestQuery (t , harness , "select @@secure_file_priv != '';" , []sql.Row {{true }}, nil , nil )
916+
898917 for _ , script := range queries .LoadDataErrorScripts {
899918 TestScript (t , harness , script )
900919 }
901920}
902921
903922func TestLoadDataFailing (t * testing.T , harness Harness ) {
904923 t .Skip ()
924+
925+ require .NoError (t , setSecureFilePriv ())
926+ TestQuery (t , harness , "select @@secure_file_priv != '';" , []sql.Row {{true }}, nil , nil )
927+
905928 for _ , script := range queries .LoadDataFailingScripts {
906929 TestScript (t , harness , script )
907930 }
908931}
909932
933+ func TestSelectIntoFile (t * testing.T , harness Harness ) {
934+ harness .Setup (setup .MydbData , setup .MytableData , setup .EmptytableData , setup .NiltableData )
935+ e := mustNewEngine (t , harness )
936+ defer e .Close ()
937+
938+ ctx := NewContext (harness )
939+ err := CreateNewConnectionForServerEngine (ctx , e )
940+ require .NoError (t , err , nil )
941+
942+ require .NoError (t , setSecureFilePriv ())
943+ TestQuery (t , harness , "select @@secure_file_priv != '';" , []sql.Row {{true }}, nil , nil )
944+
945+ tests := []struct {
946+ file string
947+ query string
948+ exp string
949+ err * errors.Kind
950+ skip bool
951+ }{
952+ {
953+ file : "outfile.txt" ,
954+ query : "select * from mytable into outfile 'outfile.txt';" ,
955+ exp : "" +
956+ "1\t first row\n " +
957+ "2\t second row\n " +
958+ "3\t third row\n " ,
959+ },
960+ {
961+ file : "dumpfile.txt" ,
962+ query : "select * from mytable limit 1 into dumpfile 'dumpfile.txt';" ,
963+ exp : "1first row" ,
964+ },
965+ {
966+ file : "outfile.txt" ,
967+ query : "select * from mytable into outfile 'outfile.txt' fields terminated by ',';" ,
968+ exp : "" +
969+ "1,first row\n " +
970+ "2,second row\n " +
971+ "3,third row\n " ,
972+ },
973+ {
974+ file : "outfile.txt" ,
975+ query : "select * from mytable into outfile 'outfile.txt' fields terminated by '$$';" ,
976+ exp : "" +
977+ "1$$first row\n " +
978+ "2$$second row\n " +
979+ "3$$third row\n " ,
980+ },
981+ {
982+ file : "outfile.txt" ,
983+ query : "select * from mytable into outfile 'outfile.txt' fields terminated by ',' optionally enclosed by '\" ';" ,
984+ exp : "" +
985+ "1,\" first row\" \n " +
986+ "2,\" second row\" \n " +
987+ "3,\" third row\" \n " ,
988+ },
989+ {
990+ file : "outfile.txt" ,
991+ query : "select * from mytable into outfile 'outfile.txt' fields terminated by ',' optionally enclosed by '$$';" ,
992+ err : sql .ErrUnexpectedSeparator ,
993+ },
994+ {
995+ file : "outfile.txt" ,
996+ query : "select * from mytable into outfile 'outfile.txt' fields terminated by ',' escaped by '$$';" ,
997+ err : sql .ErrUnexpectedSeparator ,
998+ },
999+ {
1000+ file : "outfile.txt" ,
1001+ query : "select * from mytable into outfile 'outfile.txt' fields terminated by ',' enclosed by '\" ';" ,
1002+ exp : "" +
1003+ "\" 1\" ,\" first row\" \n " +
1004+ "\" 2\" ,\" second row\" \n " +
1005+ "\" 3\" ,\" third row\" \n " ,
1006+ },
1007+ {
1008+ file : "outfile.txt" ,
1009+ query : "select * from mytable into outfile 'outfile.txt' fields terminated by ',' lines terminated by ';';" ,
1010+ exp : "" +
1011+ "1,first row;" +
1012+ "2,second row;" +
1013+ "3,third row;" ,
1014+ },
1015+ {
1016+ file : "outfile.txt" ,
1017+ query : "select * from mytable into outfile 'outfile.txt' fields terminated by ',' lines terminated by 'r';" ,
1018+ exp : "" +
1019+ "1,fi\\ rst \\ rowr" +
1020+ "2,second \\ rowr" +
1021+ "3,thi\\ rd \\ rowr" ,
1022+ },
1023+ {
1024+ file : "outfile.txt" ,
1025+ query : "select * from mytable into outfile 'outfile.txt' fields terminated by ',' lines starting by 'r';" ,
1026+ exp : "" +
1027+ "r1,first row\n " +
1028+ "r2,second row\n " +
1029+ "r3,third row\n " ,
1030+ },
1031+ {
1032+ file : "outfile.txt" ,
1033+ query : "select * from mytable into outfile 'outfile.txt' fields terminated by '';" ,
1034+ exp : "" +
1035+ "1\t first row\n " +
1036+ "2\t second row\n " +
1037+ "3\t third row\n " ,
1038+ },
1039+ {
1040+ file : "outfile.txt" ,
1041+ query : "select * from mytable into outfile 'outfile.txt' fields terminated by ',' lines terminated by '';" ,
1042+ exp : "" +
1043+ "1,first row" +
1044+ "2,second row" +
1045+ "3,third row" ,
1046+ },
1047+ {
1048+ file : "outfile.txt" ,
1049+ query : "select * from niltable into outfile 'outfile.txt';" ,
1050+ exp : "1\t \\ N\t \\ N\t \\ N\n " +
1051+ "2\t 2\t 1\t \\ N\n " +
1052+ "3\t \\ N\t 0\t \\ N\n " +
1053+ "4\t 4\t \\ N\t 4\n " +
1054+ "5\t \\ N\t 1\t 5\n " +
1055+ "6\t 6\t 0\t 6\n " ,
1056+ },
1057+ {
1058+ file : "outfile.txt" ,
1059+ query : "select * from niltable into outfile 'outfile.txt' fields terminated by ',' enclosed by '\" ';" ,
1060+ exp : "\" 1\" ,\\ N,\\ N,\\ N\n " +
1061+ "\" 2\" ,\" 2\" ,\" 1\" ,\\ N\n " +
1062+ "\" 3\" ,\\ N,\" 0\" ,\\ N\n " +
1063+ "\" 4\" ,\" 4\" ,\\ N,\" 4\" \n " +
1064+ "\" 5\" ,\\ N,\" 1\" ,\" 5\" \n " +
1065+ "\" 6\" ,\" 6\" ,\" 0\" ,\" 6\" \n " ,
1066+ },
1067+ {
1068+ file : "outfile.txt" ,
1069+ query : "select * from niltable into outfile 'outfile.txt' fields terminated by ',' escaped by '$';" ,
1070+ exp : "1,$N,$N,$N\n " +
1071+ "2,2,1,$N\n " +
1072+ "3,$N,0,$N\n " +
1073+ "4,4,$N,4\n " +
1074+ "5,$N,1,5\n " +
1075+ "6,6,0,6\n " ,
1076+ },
1077+ {
1078+ file : "outfile.txt" ,
1079+ query : "select * from niltable into outfile 'outfile.txt' fields terminated by ',' escaped by '';" ,
1080+ exp : "1,NULL,NULL,NULL\n " +
1081+ "2,2,1,NULL\n " +
1082+ "3,NULL,0,NULL\n " +
1083+ "4,4,NULL,4\n " +
1084+ "5,NULL,1,5\n " +
1085+ "6,6,0,6\n " ,
1086+ },
1087+ {
1088+ file : "./subdir/outfile.txt" ,
1089+ query : "select * from mytable into outfile './subdir/outfile.txt';" ,
1090+ exp : "" +
1091+ "1\t first row\n " +
1092+ "2\t second row\n " +
1093+ "3\t third row\n " ,
1094+ },
1095+ {
1096+ file : "../outfile.txt" ,
1097+ query : "select * from mytable into outfile '../outfile.txt';" ,
1098+ err : sql .ErrSecureFilePriv ,
1099+ },
1100+ {
1101+ file : "outfile.txt" ,
1102+ query : "select * from mytable into outfile 'outfile.txt' charset binary;" ,
1103+ err : sql .ErrUnsupportedFeature ,
1104+ },
1105+ }
1106+
1107+ subdir := "subdir"
1108+ if _ , subErr := os .Stat (subdir ); subErr == nil {
1109+ subErr = os .RemoveAll (subdir )
1110+ require .NoError (t , subErr )
1111+ }
1112+ err = os .Mkdir (subdir , 0777 )
1113+ require .NoError (t , err )
1114+ defer os .RemoveAll (subdir )
1115+
1116+ for _ , tt := range tests {
1117+ t .Run (tt .query , func (t * testing.T ) {
1118+ if tt .skip {
1119+ t .Skip ()
1120+ }
1121+ if tt .err != nil {
1122+ AssertErrWithCtx (t , e , harness , ctx , tt .query , tt .err )
1123+ return
1124+ }
1125+ // in case there are any residual files from previous runs
1126+ os .Remove (tt .file )
1127+ TestQueryWithContext (t , ctx , e , harness , tt .query , nil , nil , nil )
1128+ res , err := os .ReadFile (tt .file )
1129+ require .NoError (t , err )
1130+ require .Equal (t , tt .exp , string (res ))
1131+ os .Remove (tt .file )
1132+ })
1133+ }
1134+
1135+ // remove tmp directory from previously failed runs
1136+ exists := "exists.txt"
1137+ if _ , existsErr := os .Stat (exists ); existsErr == nil {
1138+ err = os .Remove (exists )
1139+ require .NoError (t , err )
1140+ }
1141+ file , err := os .Create (exists )
1142+ require .NoError (t , err )
1143+ file .Close ()
1144+ defer os .Remove (exists )
1145+
1146+ AssertErrWithCtx (t , e , harness , ctx , "SELECT * FROM mytable INTO OUTFILE './exists.txt'" , sql .ErrFileExists )
1147+ AssertErrWithCtx (t , e , harness , ctx , "SELECT * FROM mytable LIMIT 1 INTO DUMPFILE './exists.txt'" , sql .ErrFileExists )
1148+ }
1149+
9101150func TestReplaceInto (t * testing.T , harness Harness ) {
9111151 harness .Setup (setup .MydbData , setup .MytableData , setup .Mytable_del_idxData , setup .TypestableData )
9121152 for _ , tt := range queries .ReplaceQueries {
0 commit comments