2020-08-06 20:58:47 +03:00
|
|
|
package cli_test
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/csv"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
2020-08-23 13:42:15 +03:00
|
|
|
"github.com/neilotoole/sq/libsq/core/stringz"
|
2020-08-06 20:58:47 +03:00
|
|
|
"github.com/neilotoole/sq/libsq/source"
|
|
|
|
"github.com/neilotoole/sq/testh"
|
|
|
|
"github.com/neilotoole/sq/testh/sakila"
|
|
|
|
)
|
|
|
|
|
2023-03-26 04:20:53 +03:00
|
|
|
// TestCmdSLQ_Insert tests "sq QUERY --insert=@src.tbl".
|
2021-01-04 04:20:05 +03:00
|
|
|
func TestCmdSLQ_Insert_Create(t *testing.T) {
|
|
|
|
th := testh.New(t)
|
|
|
|
originSrc, destSrc := th.Source(sakila.SL3), th.Source(sakila.SL3)
|
|
|
|
srcTbl := sakila.TblActor
|
|
|
|
if th.IsMonotable(originSrc) {
|
|
|
|
srcTbl = source.MonotableName
|
|
|
|
}
|
|
|
|
|
2021-01-04 05:56:22 +03:00
|
|
|
destTbl := stringz.UniqSuffix(sakila.TblActor + "_copy")
|
2021-01-04 04:20:05 +03:00
|
|
|
|
2023-03-19 09:18:54 +03:00
|
|
|
ru := newRun(t, nil).add(*originSrc)
|
2021-01-04 04:20:05 +03:00
|
|
|
if destSrc.Handle != originSrc.Handle {
|
|
|
|
ru.add(*destSrc)
|
|
|
|
}
|
|
|
|
|
|
|
|
insertTo := fmt.Sprintf("%s.%s", destSrc.Handle, destTbl)
|
|
|
|
cols := stringz.PrefixSlice(sakila.TblActorCols(), ".")
|
|
|
|
query := fmt.Sprintf("%s.%s | %s", originSrc.Handle, srcTbl, strings.Join(cols, ", "))
|
|
|
|
|
2023-01-01 06:17:44 +03:00
|
|
|
err := ru.Exec("slq", "--insert="+insertTo, query)
|
2021-01-04 04:20:05 +03:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
sink, err := th.QuerySQL(destSrc, "select * from "+destTbl)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, sakila.TblActorCount, len(sink.Recs))
|
|
|
|
}
|
|
|
|
|
2020-08-07 22:51:30 +03:00
|
|
|
// TestCmdSLQ_Insert tests "sq slq QUERY --insert=dest.tbl".
|
2020-08-06 20:58:47 +03:00
|
|
|
func TestCmdSLQ_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) {
|
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)
|
|
|
|
srcTbl := sakila.TblActor
|
|
|
|
if th.IsMonotable(originSrc) {
|
|
|
|
srcTbl = 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-03-19 09:18:54 +03:00
|
|
|
ru := newRun(t, nil).add(*originSrc)
|
2020-08-06 20:58:47 +03:00
|
|
|
if destSrc.Handle != originSrc.Handle {
|
|
|
|
ru.add(*destSrc)
|
|
|
|
}
|
|
|
|
|
2020-08-13 06:22:53 +03:00
|
|
|
insertTo := fmt.Sprintf("%s.%s", destSrc.Handle, tblName)
|
2020-08-10 18:16:44 +03:00
|
|
|
cols := stringz.PrefixSlice(sakila.TblActorCols(), ".")
|
2020-08-06 20:58:47 +03:00
|
|
|
query := fmt.Sprintf("%s.%s | %s", originSrc.Handle, srcTbl, strings.Join(cols, ", "))
|
|
|
|
|
2023-01-01 06:17:44 +03:00
|
|
|
err := ru.Exec("slq", "--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 TestCmdSLQ_CSV(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
src := testh.New(t).Source(sakila.CSVActor)
|
2023-03-19 09:18:54 +03:00
|
|
|
ru := newRun(t, nil).add(*src)
|
2023-01-01 06:17:44 +03:00
|
|
|
err := ru.Exec("slq", "--header=false", "--csv", fmt.Sprintf("%s.data", src.Handle))
|
2020-08-06 20:58:47 +03:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
recs := ru.mustReadCSV()
|
|
|
|
require.Equal(t, sakila.TblActorCount, len(recs))
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestCmdSLQ_OutputFlag verifies that flag --output=<file> works.
|
|
|
|
func TestCmdSLQ_OutputFlag(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
src := testh.New(t).Source(sakila.SL3)
|
2023-03-19 09:18:54 +03:00
|
|
|
ru := newRun(t, nil).add(*src)
|
2022-12-18 02:11:33 +03:00
|
|
|
outputFile, err := os.CreateTemp("", t.Name())
|
2020-08-06 20:58:47 +03:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
t.Cleanup(func() {
|
|
|
|
assert.NoError(t, outputFile.Close())
|
|
|
|
assert.NoError(t, os.Remove(outputFile.Name()))
|
|
|
|
})
|
|
|
|
|
2023-01-01 06:17:44 +03:00
|
|
|
err = ru.Exec("slq",
|
2020-08-09 16:46:46 +03:00
|
|
|
"--header=false", "--csv", fmt.Sprintf("%s.%s", src.Handle, sakila.TblActor),
|
2020-08-06 20:58:47 +03:00
|
|
|
"--output", outputFile.Name())
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
recs, err := csv.NewReader(outputFile).ReadAll()
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, sakila.TblActorCount, len(recs))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestCmdSLQ_Join(t *testing.T) {
|
2022-12-18 10:18:35 +03:00
|
|
|
const queryTpl = `%s.customer, %s.address | join(.address_id) | .customer_id == %d | .[0] | .customer_id, .email, .city_id` //nolint:lll
|
2020-08-09 17:40:46 +03:00
|
|
|
handles := sakila.SQLAll()
|
2020-08-06 20:58:47 +03:00
|
|
|
|
|
|
|
// Attempt to join every SQL test source against every SQL test source.
|
|
|
|
for _, h1 := range handles {
|
|
|
|
h1 := h1
|
|
|
|
|
|
|
|
t.Run("origin_"+h1, func(t *testing.T) {
|
|
|
|
for _, h2 := range handles {
|
|
|
|
h2 := h2
|
|
|
|
|
|
|
|
t.Run("dest_"+h2, func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
th := testh.New(t)
|
|
|
|
src1, src2 := th.Source(h1), th.Source(h2)
|
|
|
|
|
2023-03-19 09:18:54 +03:00
|
|
|
ru := newRun(t, nil).add(*src1)
|
2020-08-06 20:58:47 +03:00
|
|
|
if src2.Handle != src1.Handle {
|
|
|
|
ru.add(*src2)
|
|
|
|
}
|
|
|
|
|
|
|
|
query := fmt.Sprintf(queryTpl, src1.Handle, src2.Handle, sakila.MillerCustID)
|
|
|
|
|
2023-01-01 06:17:44 +03:00
|
|
|
err := ru.Exec("slq", "--header=false", "--csv", query)
|
2020-08-06 20:58:47 +03:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
recs := ru.mustReadCSV()
|
|
|
|
require.Equal(t, 1, len(recs), "should only be one matching record")
|
|
|
|
require.Equal(t, 3, len(recs[0]), "should have three fields")
|
|
|
|
require.Equal(t, strconv.Itoa(sakila.MillerCustID), recs[0][0])
|
|
|
|
require.Equal(t, sakila.MillerEmail, recs[0][1])
|
|
|
|
require.Equal(t, strconv.Itoa(sakila.MillerCityID), recs[0][2])
|
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestCmdSLQ_ActiveSrcHandle verifies that source.ActiveHandle is
|
|
|
|
// interpreted as the active src in a SLQ query.
|
|
|
|
func TestCmdSLQ_ActiveSrcHandle(t *testing.T) {
|
|
|
|
src := testh.New(t).Source(sakila.SL3)
|
|
|
|
|
|
|
|
// 1. Verify that the query works as expected using the actual src handle
|
2023-03-19 09:18:54 +03:00
|
|
|
ru := newRun(t, nil).add(*src).hush()
|
2020-08-06 20:58:47 +03:00
|
|
|
|
|
|
|
require.Equal(t, src.Handle, ru.rc.Config.Sources.Active().Handle)
|
2023-01-01 06:17:44 +03:00
|
|
|
err := ru.Exec("slq", "--header=false", "--csv", "@sakila_sl3.actor")
|
2020-08-06 20:58:47 +03:00
|
|
|
require.NoError(t, err)
|
|
|
|
recs := ru.mustReadCSV()
|
|
|
|
require.Equal(t, sakila.TblActorCount, len(recs))
|
|
|
|
|
|
|
|
// 2. Verify that it works using source.ActiveHandle as the src handle
|
2023-03-19 09:18:54 +03:00
|
|
|
ru = newRun(t, nil).add(*src).hush()
|
2020-08-06 20:58:47 +03:00
|
|
|
require.Equal(t, src.Handle, ru.rc.Config.Sources.Active().Handle)
|
2023-01-01 06:17:44 +03:00
|
|
|
err = ru.Exec("slq", "--header=false", "--csv", source.ActiveHandle+".actor")
|
2020-08-06 20:58:47 +03:00
|
|
|
require.NoError(t, err)
|
|
|
|
recs = ru.mustReadCSV()
|
|
|
|
require.Equal(t, sakila.TblActorCount, len(recs))
|
|
|
|
}
|