2020-08-06 20:58:47 +03:00
|
|
|
package cli_test
|
|
|
|
|
|
|
|
import (
|
2023-04-22 16:37:07 +03:00
|
|
|
"context"
|
2020-08-06 20:58:47 +03:00
|
|
|
"fmt"
|
|
|
|
"os"
|
2023-04-26 18:16:42 +03:00
|
|
|
"path/filepath"
|
2020-08-06 20:58:47 +03:00
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
|
2023-05-19 17:24:18 +03:00
|
|
|
"github.com/neilotoole/sq/cli/testrun"
|
|
|
|
|
2023-04-26 18:16:42 +03:00
|
|
|
"github.com/neilotoole/sq/cli/flag"
|
2022-12-18 10:18:35 +03:00
|
|
|
"github.com/neilotoole/sq/testh/tutil"
|
|
|
|
|
2020-08-06 20:58:47 +03:00
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
|
|
|
"github.com/neilotoole/sq/drivers/userdriver"
|
|
|
|
"github.com/neilotoole/sq/libsq/source"
|
|
|
|
"github.com/neilotoole/sq/testh"
|
|
|
|
"github.com/neilotoole/sq/testh/proj"
|
|
|
|
"github.com/neilotoole/sq/testh/sakila"
|
|
|
|
"github.com/neilotoole/sq/testh/testsrc"
|
|
|
|
)
|
|
|
|
|
2020-08-07 22:51:30 +03:00
|
|
|
// TestCmdSQL_Insert tests "sq sql QUERY --insert=dest.tbl".
|
2020-08-06 20:58:47 +03:00
|
|
|
func TestCmdSQL_Insert(t *testing.T) {
|
2020-08-09 17:40:46 +03:00
|
|
|
for _, origin := range sakila.SQLLatest() {
|
2020-08-06 20:58:47 +03:00
|
|
|
origin := origin
|
|
|
|
|
|
|
|
t.Run("origin_"+origin, func(t *testing.T) {
|
2022-12-17 05:09:49 +03:00
|
|
|
tutil.SkipShort(t, origin == sakila.XLSX)
|
2020-08-06 20:58:47 +03:00
|
|
|
|
2020-08-09 17:40:46 +03:00
|
|
|
for _, dest := range sakila.SQLLatest() {
|
2020-08-06 20:58:47 +03:00
|
|
|
dest := dest
|
|
|
|
|
|
|
|
t.Run("dest_"+dest, func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
th := testh.New(t)
|
|
|
|
originSrc, destSrc := th.Source(origin), th.Source(dest)
|
|
|
|
originTbl := sakila.TblActor
|
|
|
|
|
|
|
|
if th.IsMonotable(originSrc) {
|
|
|
|
originTbl = source.MonotableName
|
|
|
|
}
|
|
|
|
|
|
|
|
// To avoid dirtying the destination table, we make a copy
|
|
|
|
// of it (without data).
|
2020-08-13 06:22:53 +03:00
|
|
|
tblName := th.CopyTable(true, destSrc, sakila.TblActor, "", false)
|
2020-08-06 20:58:47 +03:00
|
|
|
|
2023-05-19 17:24:18 +03:00
|
|
|
tr := testrun.New(th.Context, t, nil).Add(*originSrc)
|
2020-08-06 20:58:47 +03:00
|
|
|
if destSrc.Handle != originSrc.Handle {
|
2023-05-19 17:24:18 +03:00
|
|
|
tr.Add(*destSrc)
|
2020-08-06 20:58:47 +03:00
|
|
|
}
|
|
|
|
|
2020-08-13 06:22:53 +03:00
|
|
|
insertTo := fmt.Sprintf("%s.%s", destSrc.Handle, tblName)
|
2020-08-10 18:16:44 +03:00
|
|
|
query := fmt.Sprintf("SELECT %s FROM %s", strings.Join(sakila.TblActorCols(), ", "), originTbl)
|
2020-08-06 20:58:47 +03:00
|
|
|
|
2023-05-19 17:24:18 +03:00
|
|
|
err := tr.Exec("sql", "--insert="+insertTo, query)
|
2020-08-06 20:58:47 +03:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
2020-08-13 06:22:53 +03:00
|
|
|
sink, err := th.QuerySQL(destSrc, "select * from "+tblName)
|
2020-08-06 20:58:47 +03:00
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, sakila.TblActorCount, len(sink.Recs))
|
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestCmdSQL_SelectFromUserDriver(t *testing.T) {
|
2022-12-18 10:18:35 +03:00
|
|
|
t.Parallel()
|
|
|
|
|
2020-08-06 20:58:47 +03:00
|
|
|
testCases := map[string][]struct {
|
|
|
|
tblName string
|
|
|
|
wantRows int
|
|
|
|
wantCols int
|
|
|
|
}{
|
|
|
|
testsrc.PplUD: {
|
|
|
|
{tblName: "person", wantRows: 3, wantCols: 7},
|
|
|
|
{tblName: "skill", wantRows: 6, wantCols: 3},
|
|
|
|
},
|
|
|
|
testsrc.RSSNYTLocalUD: {
|
|
|
|
{tblName: "category", wantRows: 251, wantCols: 4},
|
|
|
|
{tblName: "channel", wantRows: 1, wantCols: 7},
|
|
|
|
{tblName: "item", wantRows: 45, wantCols: 9},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for handle, wantTbls := range testCases {
|
|
|
|
for _, wantTbl := range wantTbls {
|
|
|
|
handle, wantTbl := handle, wantTbl
|
|
|
|
t.Run(handle+"__"+wantTbl.tblName, func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
th := testh.New(t)
|
|
|
|
src := th.Source(handle)
|
|
|
|
|
2023-05-19 17:24:18 +03:00
|
|
|
tr := testrun.New(th.Context, t, nil).Add(*src)
|
2020-08-06 20:58:47 +03:00
|
|
|
udDefs := testh.DriverDefsFrom(t, testsrc.PathDriverDefPpl, testsrc.PathDriverDefRSS)
|
2023-04-26 18:16:42 +03:00
|
|
|
require.Len(t, udDefs, 2)
|
2020-08-06 20:58:47 +03:00
|
|
|
for _, udDef := range udDefs {
|
|
|
|
require.Empty(t, userdriver.ValidateDriverDef(udDef))
|
|
|
|
}
|
2023-05-19 17:24:18 +03:00
|
|
|
tr.Run.Config.Ext.UserDrivers = append(tr.Run.Config.Ext.UserDrivers, udDefs...)
|
2020-08-06 20:58:47 +03:00
|
|
|
|
2023-05-19 17:24:18 +03:00
|
|
|
err := tr.Exec("sql", "--csv", "--header=false", "SELECT * FROM "+wantTbl.tblName)
|
2020-08-06 20:58:47 +03:00
|
|
|
require.NoError(t, err)
|
2023-05-19 17:24:18 +03:00
|
|
|
recs := tr.MustReadCSV()
|
2023-04-02 22:49:45 +03:00
|
|
|
require.Equal(t, wantTbl.wantRows, len(recs),
|
|
|
|
"expected %d rows in tbl {%s} but got %d", wantTbl.wantRows,
|
2022-12-18 10:18:35 +03:00
|
|
|
wantTbl, len(recs))
|
2023-04-02 22:49:45 +03:00
|
|
|
require.Equal(t, wantTbl.wantCols, len(recs[0]),
|
|
|
|
"expected %d cols in tbl {%s} but got %d",
|
2022-12-18 10:18:35 +03:00
|
|
|
wantTbl.wantCols, wantTbl, len(recs[0]))
|
2020-08-06 20:58:47 +03:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestCmdSQL_StdinQuery verifies that cmd sql can read from stdin.
|
|
|
|
func TestCmdSQL_StdinQuery(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
testCases := []struct {
|
|
|
|
fpath string
|
|
|
|
tbl string
|
2023-04-26 18:16:42 +03:00
|
|
|
flags map[string]string
|
2020-08-06 20:58:47 +03:00
|
|
|
wantCount int
|
|
|
|
wantErr bool
|
|
|
|
}{
|
2022-12-18 11:35:59 +03:00
|
|
|
{
|
2023-04-26 18:16:42 +03:00
|
|
|
fpath: proj.Abs(sakila.PathCSVActorNoHeader),
|
|
|
|
flags: map[string]string{flag.IngestHeader: "false"},
|
|
|
|
tbl: source.MonotableName,
|
|
|
|
wantCount: sakila.TblActorCount,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
fpath: proj.Abs(sakila.PathCSVActor),
|
|
|
|
flags: map[string]string{flag.IngestHeader: "true"},
|
|
|
|
tbl: source.MonotableName,
|
|
|
|
wantCount: sakila.TblActorCount,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
fpath: proj.Abs(sakila.PathXLSXActorHeader),
|
|
|
|
flags: map[string]string{flag.IngestHeader: "true"},
|
|
|
|
tbl: sakila.TblActor,
|
2022-12-18 11:35:59 +03:00
|
|
|
wantCount: sakila.TblActorCount,
|
|
|
|
},
|
|
|
|
{
|
2023-04-26 18:16:42 +03:00
|
|
|
fpath: proj.Abs(sakila.PathXLSXSubset),
|
|
|
|
flags: map[string]string{flag.IngestHeader: "true"},
|
|
|
|
tbl: sakila.TblActor,
|
2022-12-18 11:35:59 +03:00
|
|
|
wantCount: sakila.TblActorCount,
|
|
|
|
},
|
2023-04-26 18:16:42 +03:00
|
|
|
{
|
|
|
|
fpath: proj.Abs("README.md"),
|
|
|
|
wantErr: true,
|
|
|
|
},
|
2020-08-06 20:58:47 +03:00
|
|
|
}
|
|
|
|
|
2023-04-26 18:16:42 +03:00
|
|
|
for i, tc := range testCases {
|
2020-08-06 20:58:47 +03:00
|
|
|
tc := tc
|
2023-04-26 18:16:42 +03:00
|
|
|
name := tutil.Name(i, filepath.Base(filepath.Dir(tc.fpath)), filepath.Base(tc.fpath))
|
|
|
|
t.Run(name, func(t *testing.T) {
|
2020-08-06 20:58:47 +03:00
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
f, err := os.Open(tc.fpath)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
2023-05-19 17:24:18 +03:00
|
|
|
tr := testrun.New(context.Background(), t, nil).Hush()
|
|
|
|
tr.Run.Stdin = f
|
2020-08-06 20:58:47 +03:00
|
|
|
|
2023-04-26 18:16:42 +03:00
|
|
|
args := []string{"sql", "--header=false"} // Don't print the header in output
|
|
|
|
for k, v := range tc.flags {
|
|
|
|
args = append(args, fmt.Sprintf("--%s=%s", k, v))
|
2020-11-02 20:40:29 +03:00
|
|
|
}
|
2023-04-26 18:16:42 +03:00
|
|
|
args = append(args, "SELECT * FROM "+tc.tbl)
|
2020-11-02 20:40:29 +03:00
|
|
|
|
2023-05-19 17:24:18 +03:00
|
|
|
err = tr.Exec(args...)
|
2020-08-06 20:58:47 +03:00
|
|
|
if tc.wantErr {
|
|
|
|
require.Error(t, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
require.NoError(t, err)
|
2023-05-19 17:24:18 +03:00
|
|
|
results := tr.MustReadCSV()
|
2020-08-06 20:58:47 +03:00
|
|
|
require.Equal(t, tc.wantCount, len(results))
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|