Filepath and colorization issues with the v0.12.1 release on Windows (#50)

- Windows filepath handling
- Windows colorization handling
- Minor code tidy up and refactor
This commit is contained in:
Neil O'Toole 2020-08-07 13:51:30 -06:00 committed by GitHub
parent 58d8e301e0
commit 062e2dea88
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 155 additions and 98 deletions

View File

@ -348,6 +348,8 @@ func (rc *RunContext) preRunE() error {
} }
rc.wrtr = newWriters(rc.Log, rc.Cmd, rc.Config.Options, rc.Out, rc.ErrOut) rc.wrtr = newWriters(rc.Log, rc.Cmd, rc.Config.Options, rc.Out, rc.ErrOut)
rc.Out = rc.wrtr.out
rc.ErrOut = rc.wrtr.errOut
var scratchSrcFunc driver.ScratchSrcFunc var scratchSrcFunc driver.ScratchSrcFunc
@ -456,6 +458,8 @@ func (rc *RunContext) databases() *driver.Databases {
// writers is a container for the various output writer types. // writers is a container for the various output writer types.
type writers struct { type writers struct {
out io.Writer
errOut io.Writer
fmt *output.Formatting fmt *output.Formatting
recordw output.RecordWriter recordw output.RecordWriter
metaw output.MetadataWriter metaw output.MetadataWriter
@ -491,6 +495,8 @@ func newWriters(log lg.Log, cmd *cobra.Command, opts config.Options, out, errOut
// flags and set the various writer fields depending upon which // flags and set the various writer fields depending upon which
// writers the format implements. // writers the format implements.
w := &writers{ w := &writers{
out: out,
errOut: errOut,
fmt: fm, fmt: fm,
recordw: tablew.NewRecordWriter(out, fm, hasHeader), recordw: tablew.NewRecordWriter(out, fm, hasHeader),
metaw: tablew.NewMetadataWriter(out, fm), metaw: tablew.NewMetadataWriter(out, fm),

View File

@ -149,8 +149,8 @@ func execSrcAdd(rc *RunContext, cmd *cobra.Command, args []string) error {
// //
// The second form is particularly nice for bash completion etc. // The second form is particularly nice for bash completion etc.
if typ == sqlite3.Type { if typ == sqlite3.Type {
if !strings.HasPrefix(loc, "sqlite3:") { if !strings.HasPrefix(loc, sqlite3.Prefix) {
loc = "sqlite3:" + loc loc = sqlite3.Prefix + loc
} }
} }

View File

@ -181,8 +181,8 @@ func newNotifyAddHipChatCmd() (*cobra.Command, runFunc) {
} }
func execNotifyAddHipChat(rc *RunContext, cmd *cobra.Command, args []string) error { func execNotifyAddHipChat(rc *RunContext, cmd *cobra.Command, args []string) error {
fmt.Println("Add HipChat room") fmt.Fprintln(rc.Out, "Add HipChat room")
fmt.Println(strings.Join(args, " | ")) fmt.Fprintln(rc.Out, strings.Join(args, " | "))
var label string var label string
var err error var err error
@ -194,7 +194,7 @@ func execNotifyAddHipChat(rc *RunContext, cmd *cobra.Command, args []string) err
} }
if label != "" { if label != "" {
fmt.Printf("Label: %s", label) fmt.Fprintf(rc.Out, "Label: %s", label)
} }
return nil return nil

View File

@ -18,24 +18,15 @@ import (
"github.com/neilotoole/sq/testh/sakila" "github.com/neilotoole/sq/testh/sakila"
) )
func TestCmdSLQ_Insert_LONG(t *testing.T) { // TestCmdSLQ_Insert tests "sq slq QUERY --insert=dest.tbl".
testh.SkipShort(t, true)
testCmdSLQ_Insert(t, sakila.All, sakila.SQLAll)
}
func TestCmdSLQ_Insert(t *testing.T) { func TestCmdSLQ_Insert(t *testing.T) {
testCmdSLQ_Insert(t, sakila.SQLLatest, sakila.SQLLatest) for _, origin := range sakila.SQLLatest {
}
// testCmdSLQ_Insert tests "sq slq QUERY --insert=dest.tbl".
func testCmdSLQ_Insert(t *testing.T, origins, dests []string) {
for _, origin := range origins {
origin := origin origin := origin
t.Run("origin_"+origin, func(t *testing.T) { t.Run("origin_"+origin, func(t *testing.T) {
testh.SkipShort(t, origin == sakila.XLSX) testh.SkipShort(t, origin == sakila.XLSX)
for _, dest := range dests { for _, dest := range sakila.SQLLatest {
dest := dest dest := dest
t.Run("dest_"+dest, func(t *testing.T) { t.Run("dest_"+dest, func(t *testing.T) {

View File

@ -16,23 +16,15 @@ import (
"github.com/neilotoole/sq/testh/testsrc" "github.com/neilotoole/sq/testh/testsrc"
) )
func TestCmdSQL_Insert_LONG(t *testing.T) { // TestCmdSQL_Insert tests "sq sql QUERY --insert=dest.tbl".
testh.SkipShort(t, true)
testCmdSQL_Insert(t, sakila.All, sakila.All)
}
func TestCmdSQL_Insert(t *testing.T) { func TestCmdSQL_Insert(t *testing.T) {
testCmdSQL_Insert(t, sakila.SQLLatest, sakila.SQLLatest) for _, origin := range sakila.SQLLatest {
}
// testCmdSQL_Insert tests "sq sql QUERY --insert=dest.tbl".
func testCmdSQL_Insert(t *testing.T, origins, dests []string) {
for _, origin := range origins {
origin := origin origin := origin
t.Run("origin_"+origin, func(t *testing.T) { t.Run("origin_"+origin, func(t *testing.T) {
testh.SkipShort(t, origin == sakila.XLSX) testh.SkipShort(t, origin == sakila.XLSX)
for _, dest := range dests { for _, dest := range sakila.SQLLatest {
dest := dest dest := dest
t.Run("dest_"+dest, func(t *testing.T) { t.Run("dest_"+dest, func(t *testing.T) {
@ -125,8 +117,8 @@ func TestCmdSQL_StdinQuery(t *testing.T) {
wantCount int wantCount int
wantErr bool wantErr bool
}{ }{
{fpath: proj.Abs(sakila.PathCSVActor), tbl: source.MonotableName, wantCount: sakila.TblActorCount + 1}, // +1 is for the header row {fpath: proj.Abs(sakila.PathCSVActorNoHeader), tbl: source.MonotableName, wantCount: sakila.TblActorCount},
{fpath: proj.Abs(sakila.PathXLSXSubset), tbl: sakila.TblActor, wantCount: sakila.TblActorCount + 1}, {fpath: proj.Abs(sakila.PathXLSXSubset), tbl: sakila.TblActor, wantCount: sakila.TblActorCount + 1}, // +1 is for the header row in the XLSX file
{fpath: proj.Abs("README.md"), wantErr: true}, {fpath: proj.Abs("README.md"), wantErr: true},
} }
@ -140,9 +132,10 @@ func TestCmdSQL_StdinQuery(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
ru := newRun(t).hush() ru := newRun(t).hush()
//ru := newRun(t)
ru.rc.Stdin = f ru.rc.Stdin = f
err = ru.exec("sql", "SELECT * FROM "+tc.tbl) err = ru.exec("sql", "--no-header", "SELECT * FROM "+tc.tbl)
if tc.wantErr { if tc.wantErr {
require.Error(t, err) require.Error(t, err)
return return

View File

@ -23,19 +23,19 @@ func execVersion(rc *RunContext, cmd *cobra.Command, args []string) error {
// If buildinfo.Version is not set (building without ldflags), // If buildinfo.Version is not set (building without ldflags),
// then we set a dummy version. // then we set a dummy version.
if version == "" { if version == "" {
version = "0.0.0.dev" version = "0.0.0-dev"
} }
rc.wrtr.fmt.Hilite.Fprintf(rc.Out, "sq %s", version) rc.wrtr.fmt.Hilite.Fprintf(rc.Out, "sq %s", version)
if len(buildinfo.Commit) > 0 { if len(buildinfo.Commit) > 0 {
fmt.Fprintf(rc.Out, " ") fmt.Fprint(rc.Out, " ")
rc.wrtr.fmt.Faint.Fprintf(rc.Out, "#"+buildinfo.Commit) rc.wrtr.fmt.Faint.Fprint(rc.Out, "#"+buildinfo.Commit)
} }
if len(buildinfo.Timestamp) > 0 { if len(buildinfo.Timestamp) > 0 {
fmt.Fprintf(rc.Out, " ") fmt.Fprint(rc.Out, " ")
rc.wrtr.fmt.Faint.Fprintf(rc.Out, buildinfo.Timestamp) rc.wrtr.fmt.Faint.Fprint(rc.Out, buildinfo.Timestamp)
} }
fmt.Fprintln(rc.Out) fmt.Fprintln(rc.Out)

View File

@ -349,7 +349,7 @@ func (t *Table) printHeading() {
pad := ConditionString(i == end && !t.borders.Left, Space, t.pColumn) pad := ConditionString(i == end && !t.borders.Left, Space, t.pColumn)
head := t.headerTrans(fmt.Sprintf("%s %s ", padFunc(h, Space, v), pad)) head := t.headerTrans(fmt.Sprintf("%s %s ", padFunc(h, Space, v), pad))
fmt.Print(head) fmt.Fprint(t.out, head)
} }
// Next line // Next line

View File

@ -46,7 +46,7 @@ func (d *database) SourceMetadata(ctx context.Context) (*source.Metadata, error)
meta := &source.Metadata{Handle: d.src.Handle, SourceType: Type, DBDriverType: dbDrvr} meta := &source.Metadata{Handle: d.src.Handle, SourceType: Type, DBDriverType: dbDrvr}
dsn, err := PathFromSourceLocation(d.src) dsn, err := PathFromLocation(d.src)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -28,6 +28,9 @@ const (
// dbDrvr is the backing sqlite3 SQL driver impl name. // dbDrvr is the backing sqlite3 SQL driver impl name.
dbDrvr = "sqlite3" dbDrvr = "sqlite3"
// Prefix is the scheme+separator value "sqlite3://".
Prefix = "sqlite3://"
) )
// Provider is the SQLite3 implementation of driver.Provider. // Provider is the SQLite3 implementation of driver.Provider.
@ -63,7 +66,7 @@ func (d *Driver) DriverMetadata() driver.Metadata {
func (d *Driver) Open(ctx context.Context, src *source.Source) (driver.Database, error) { func (d *Driver) Open(ctx context.Context, src *source.Source) (driver.Database, error) {
d.log.Debug("Opening data source: ", src) d.log.Debug("Opening data source: ", src)
dsn, err := PathFromSourceLocation(src) dsn, err := PathFromLocation(src)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -77,7 +80,7 @@ func (d *Driver) Open(ctx context.Context, src *source.Source) (driver.Database,
// Truncate implements driver.Driver. // Truncate implements driver.Driver.
func (d *Driver) Truncate(ctx context.Context, src *source.Source, tbl string, reset bool) (affected int64, err error) { func (d *Driver) Truncate(ctx context.Context, src *source.Source, tbl string, reset bool) (affected int64, err error) {
dsn, err := PathFromSourceLocation(src) dsn, err := PathFromLocation(src)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -118,7 +121,7 @@ func (d *Driver) ValidateSource(src *source.Source) (*source.Source, error) {
// Ping implements driver.Driver. // Ping implements driver.Driver.
func (d *Driver) Ping(ctx context.Context, src *source.Source) error { func (d *Driver) Ping(ctx context.Context, src *source.Source) error {
dbase, err := d.Open(context.TODO(), src) dbase, err := d.Open(ctx, src)
if err != nil { if err != nil {
return err return err
} }
@ -378,24 +381,24 @@ func NewScratchSource(log lg.Log, name string) (src *source.Source, clnup func()
src = &source.Source{ src = &source.Source{
Type: Type, Type: Type,
Handle: source.ScratchHandle, Handle: source.ScratchHandle,
Location: dbDrvr + "://" + f.Name(), Location: Prefix + f.Name(),
} }
return src, cleanFn, nil return src, cleanFn, nil
} }
// PathFromSourceLocation returns absolute file path // PathFromLocation returns the absolute file path
// from the source location, which typically has the "sqlite3:" prefix. // from the source location, which should have the "sqlite3://" prefix.
func PathFromSourceLocation(src *source.Source) (string, error) { func PathFromLocation(src *source.Source) (string, error) {
if src.Type != Type { if src.Type != Type {
return "", errz.Errorf("driver %q does not support %q", Type, src.Type) return "", errz.Errorf("driver %q does not support %q", Type, src.Type)
} }
if !strings.HasPrefix(src.Location, dbDrvr+":") { if !strings.HasPrefix(src.Location, Prefix) {
return "", errz.Errorf("sqlite3 source location must begin with %q but was: %s", Type, src.RedactedLocation()) return "", errz.Errorf("sqlite3 source location must begin with %q but was: %s", Prefix, src.RedactedLocation())
} }
loc := strings.TrimPrefix(src.Location, dbDrvr+":") loc := strings.TrimPrefix(src.Location, Prefix)
if len(loc) < 2 { if len(loc) < 2 {
return "", errz.Errorf("sqlite3 source location is too short: %s", src.RedactedLocation()) return "", errz.Errorf("sqlite3 source location is too short: %s", src.RedactedLocation())
} }

View File

@ -6,6 +6,8 @@ import (
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/neilotoole/sq/drivers/sqlite3"
"github.com/neilotoole/sq/libsq/source"
"github.com/neilotoole/sq/libsq/sqlmodel" "github.com/neilotoole/sq/libsq/sqlmodel"
"github.com/neilotoole/sq/libsq/sqlz" "github.com/neilotoole/sq/libsq/sqlz"
"github.com/neilotoole/sq/libsq/stringz" "github.com/neilotoole/sq/libsq/stringz"
@ -160,3 +162,36 @@ func TestDriver_CreateTable_NotNullDefault(t *testing.T) {
require.NotNil(t, sink.Recs[0][i]) require.NotNil(t, sink.Recs[0][i])
} }
} }
func TestPathFromLocation(t *testing.T) {
testCases := []struct {
loc string
want string
wantErr bool
}{
{loc: "sqlite3:///test.db", want: "/test.db"},
{loc: "postgres:///test.db", wantErr: true},
{loc: `sqlite3://C:\dir\sakila.db`, want: `C:\dir\sakila.db`},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.loc, func(t *testing.T) {
src := &source.Source{
Handle: "@h1",
Type: sqlite3.Type,
Location: tc.loc,
}
got, err := sqlite3.PathFromLocation(src)
if tc.wantErr {
require.Error(t, err)
return
}
require.NoError(t, err)
require.Equal(t, tc.want, got)
})
}
}

View File

@ -214,7 +214,8 @@ func TestSQLDriver_PrepareUpdateStmt(t *testing.T) {
func TestDriver_Ping(t *testing.T) { func TestDriver_Ping(t *testing.T) {
t.Parallel() t.Parallel()
testCases := append(sakila.All, sakila.CSVActor, sakila.CSVActorHTTP) testCases := sakila.All
testCases = append(testCases, sakila.CSVActor, sakila.CSVActorHTTP)
for _, handle := range testCases { for _, handle := range testCases {
handle := handle handle := handle
@ -235,7 +236,8 @@ func TestDriver_Ping(t *testing.T) {
func TestDriver_Open(t *testing.T) { func TestDriver_Open(t *testing.T) {
t.Parallel() t.Parallel()
testCases := append(sakila.All, sakila.CSVActor, sakila.CSVActorHTTP) testCases := sakila.All
testCases = append(testCases, sakila.CSVActor, sakila.CSVActorHTTP)
for _, handle := range testCases { for _, handle := range testCases {
handle := handle handle := handle

View File

@ -455,8 +455,11 @@ func AbsLocation(loc string) string {
return loc return loc
} }
// isFpath returns true if loc is a file path.
func isFpath(loc string) (fpath string, ok bool) { func isFpath(loc string) (fpath string, ok bool) {
if strings.ContainsRune(loc, ':') { // This is not exactly an industrial-strength algorithm...
if strings.Contains(loc, ":/") {
// Excludes "http:/" etc
return "", false return "", false
} }

View File

@ -3,6 +3,7 @@ package source
import ( import (
"context" "context"
"io/ioutil" "io/ioutil"
"runtime"
"testing" "testing"
"github.com/neilotoole/lg/testlg" "github.com/neilotoole/lg/testlg"
@ -55,15 +56,18 @@ func TestParseLoc(t *testing.T) {
loc string loc string
want parsedLoc want parsedLoc
wantErr bool wantErr bool
windows bool
}{ }{
{loc: "/path/to/sakila.xlsx", want: parsedLoc{name: "sakila", ext: ".xlsx"}}, {loc: "/path/to/sakila.xlsx", want: parsedLoc{name: "sakila", ext: ".xlsx"}},
{loc: "relative/path/to/sakila.xlsx", want: parsedLoc{name: "sakila", ext: ".xlsx"}}, {loc: "relative/path/to/sakila.xlsx", want: parsedLoc{name: "sakila", ext: ".xlsx"}},
{loc: "./relative/path/to/sakila.xlsx", want: parsedLoc{name: "sakila", ext: ".xlsx"}}, {loc: "./relative/path/to/sakila.xlsx", want: parsedLoc{name: "sakila", ext: ".xlsx"}},
{loc: "https://server:8080/path/to/sakila.xlsx", want: parsedLoc{scheme: "https", hostname: "server", port: 8080, name: "sakila", ext: ".xlsx"}}, {loc: "https://server:8080/path/to/sakila.xlsx", want: parsedLoc{scheme: "https", hostname: "server", port: 8080, name: "sakila", ext: ".xlsx"}},
{loc: "http://server/path/to/sakila.xlsx?param=val&param2=val2", want: parsedLoc{scheme: "http", hostname: "server", name: "sakila", ext: ".xlsx"}}, {loc: "http://server/path/to/sakila.xlsx?param=val&param2=val2", want: parsedLoc{scheme: "http", hostname: "server", name: "sakila", ext: ".xlsx"}},
{loc: "sqlite3:/path/to/sakila.db", want: parsedLoc{typ: typeSL3, scheme: "sqlite3", name: "sakila", ext: ".db", dsn: "/path/to/sakila.db"}}, {loc: "sqlite3:/path/to/sakila.db", wantErr: true}, // the scheme is malformed (should be "sqlite3://...")
{loc: "sqlite3:/path/to/sakila.sqlite", want: parsedLoc{typ: typeSL3, scheme: "sqlite3", name: "sakila", ext: ".sqlite", dsn: "/path/to/sakila.sqlite"}}, {loc: "sqlite3:///path/to/sakila.sqlite", want: parsedLoc{typ: typeSL3, scheme: "sqlite3", name: "sakila", ext: ".sqlite", dsn: "/path/to/sakila.sqlite"}},
{loc: "sqlite3:/path/to/sakila", want: parsedLoc{typ: typeSL3, scheme: "sqlite3", name: "sakila", dsn: "/path/to/sakila"}}, {loc: `sqlite3://C:\path\to\sakila.sqlite`, windows: true, want: parsedLoc{typ: typeSL3, scheme: "sqlite3", name: "sakila", ext: ".sqlite", dsn: `C:\path\to\sakila.sqlite`}},
{loc: `sqlite3://C:\path\to\sakila.sqlite?param=val`, windows: true, want: parsedLoc{typ: typeSL3, scheme: "sqlite3", name: "sakila", ext: ".sqlite", dsn: `C:\path\to\sakila.sqlite?param=val`}},
{loc: "sqlite3:///path/to/sakila", want: parsedLoc{typ: typeSL3, scheme: "sqlite3", name: "sakila", dsn: "/path/to/sakila"}},
{loc: "sqlite3://path/to/sakila.db", want: parsedLoc{typ: typeSL3, scheme: "sqlite3", name: "sakila", ext: ".db", dsn: "path/to/sakila.db"}}, {loc: "sqlite3://path/to/sakila.db", want: parsedLoc{typ: typeSL3, scheme: "sqlite3", name: "sakila", ext: ".db", dsn: "path/to/sakila.db"}},
{loc: "sqlite3:///path/to/sakila.db", want: parsedLoc{typ: typeSL3, scheme: "sqlite3", name: "sakila", ext: ".db", dsn: "/path/to/sakila.db"}}, {loc: "sqlite3:///path/to/sakila.db", want: parsedLoc{typ: typeSL3, scheme: "sqlite3", name: "sakila", ext: ".db", dsn: "/path/to/sakila.db"}},
{loc: "sqlserver://sakila:p_ssW0rd@localhost?database=sakila", want: parsedLoc{typ: typeMS, scheme: "sqlserver", user: dbuser, pass: dbpass, hostname: "localhost", name: "sakila", dsn: "Password=p_ssW0rd;Server=localhost;User ID=sakila;database=sakila"}}, {loc: "sqlserver://sakila:p_ssW0rd@localhost?database=sakila", want: parsedLoc{typ: typeMS, scheme: "sqlserver", user: dbuser, pass: dbpass, hostname: "localhost", name: "sakila", dsn: "Password=p_ssW0rd;Server=localhost;User ID=sakila;database=sakila"}},
@ -77,6 +81,10 @@ func TestParseLoc(t *testing.T) {
for _, tc := range testCases { for _, tc := range testCases {
tc := tc tc := tc
t.Run(tc.loc, func(t *testing.T) { t.Run(tc.loc, func(t *testing.T) {
if tc.windows && runtime.GOOS != "windows" {
return
}
tc.want.loc = tc.loc // set this here rather than verbosely in the setup tc.want.loc = tc.loc // set this here rather than verbosely in the setup
got, gotErr := parseLoc(tc.loc) got, gotErr := parseLoc(tc.loc)
if tc.wantErr { if tc.wantErr {

View File

@ -150,8 +150,13 @@ type parsedLoc struct {
func parseLoc(loc string) (*parsedLoc, error) { func parseLoc(loc string) (*parsedLoc, error) {
ploc := &parsedLoc{loc: loc} ploc := &parsedLoc{loc: loc}
if !strings.ContainsRune(loc, ':') { if !strings.Contains(loc, "://") {
// no scheme: it's just a file path if strings.Contains(loc, ":/") {
// malformed location, such as "sqlite3:/path/to/file"
return nil, errz.Errorf("parse location: invalid scheme: %q", loc)
}
// no scheme: it's just a regular file path for a document such as an Excel file
name := filepath.Base(loc) name := filepath.Base(loc)
ploc.ext = filepath.Ext(name) ploc.ext = filepath.Ext(name)
if ploc.ext != "" { if ploc.ext != "" {
@ -184,20 +189,22 @@ func parseLoc(loc string) (*parsedLoc, error) {
return ploc, nil return ploc, nil
} }
u, err := dburl.Parse(loc)
if err != nil {
return nil, errz.Err(err)
}
ploc.scheme = u.OriginalScheme
ploc.dsn = u.DSN
ploc.user = u.User.Username()
ploc.pass, _ = u.User.Password()
// sqlite3 is a special case, handle it now // sqlite3 is a special case, handle it now
if ploc.scheme == "sqlite3" { const sqlitePrefix = "sqlite3://"
if strings.HasPrefix(loc, sqlitePrefix) {
fpath := strings.TrimPrefix(loc, sqlitePrefix)
ploc.scheme = "sqlite3"
ploc.typ = typeSL3 ploc.typ = typeSL3
name := path.Base(u.DSN) ploc.dsn = fpath
// fpath could include params, e.g. "sqlite3://C:\sakila.db?param=val"
if i := strings.IndexRune(fpath, '?'); i >= 0 {
// Snip off the params
fpath = fpath[:i]
}
name := filepath.Base(fpath)
ploc.ext = filepath.Ext(name) ploc.ext = filepath.Ext(name)
if ploc.ext != "" { if ploc.ext != "" {
name = name[:len(name)-len(ploc.ext)] name = name[:len(name)-len(ploc.ext)]
@ -207,6 +214,15 @@ func parseLoc(loc string) (*parsedLoc, error) {
return ploc, nil return ploc, nil
} }
u, err := dburl.Parse(loc)
if err != nil {
return nil, errz.Err(err)
}
ploc.scheme = u.OriginalScheme
ploc.dsn = u.DSN
ploc.user = u.User.Username()
ploc.pass, _ = u.User.Password()
ploc.hostname = u.Hostname() ploc.hostname = u.Hostname()
if u.Port() != "" { if u.Port() != "" {
ploc.port, err = strconv.Atoi(u.Port()) ploc.port, err = strconv.Atoi(u.Port())

View File

@ -29,90 +29,90 @@ sources:
location: sqlserver://sakila:p_ssW0rd@${SQ_TEST_SRC__SAKILA_MS17}?database=sakila location: sqlserver://sakila:p_ssW0rd@${SQ_TEST_SRC__SAKILA_MS17}?database=sakila
- handle: '@sakila_xlsx' - handle: '@sakila_xlsx'
type: xlsx type: xlsx
location: "${SQ_ROOT}/drivers/xlsx/testdata/sakila.xlsx" location: '${SQ_ROOT}/drivers/xlsx/testdata/sakila.xlsx'
options: options:
header: header:
- "true" - 'true'
- handle: '@sakila_xlsx_subset' - handle: '@sakila_xlsx_subset'
type: xlsx type: xlsx
location: "${SQ_ROOT}/drivers/xlsx/testdata/sakila_subset.xlsx" location: '${SQ_ROOT}/drivers/xlsx/testdata/sakila_subset.xlsx'
options: options:
header: header:
- "true" - 'true'
- handle: '@sakila_xlsx_noheader' - handle: '@sakila_xlsx_noheader'
type: xlsx type: xlsx
location: "${SQ_ROOT}/drivers/xlsx/testdata/sakila_noheader.xlsx" location: '${SQ_ROOT}/drivers/xlsx/testdata/sakila_noheader.xlsx'
options: options:
header: header:
- "false" - 'false'
- handle: '@sakila_csv_actor' - handle: '@sakila_csv_actor'
type: csv type: csv
location: "${SQ_ROOT}/drivers/csv/testdata/sakila-csv/actor.csv" location: '${SQ_ROOT}/drivers/csv/testdata/sakila-csv/actor.csv'
options: options:
header: header:
- "true" - 'true'
- handle: '@sakila_csv_actor_http' - handle: '@sakila_csv_actor_http'
type: csv type: csv
location: "https://sq.io/testdata/actor.csv" location: 'https://sq.io/testdata/actor.csv'
options: options:
header: header:
- "true" - 'true'
- handle: '@sakila_csv_actor_noheader' - handle: '@sakila_csv_actor_noheader'
type: csv type: csv
location: "${SQ_ROOT}/drivers/csv/testdata/sakila-csv-noheader/actor.csv" location: '${SQ_ROOT}/drivers/csv/testdata/sakila-csv-noheader/actor.csv'
options: options:
header: header:
- "false" - 'false'
- handle: '@sakila_tsv_actor' - handle: '@sakila_tsv_actor'
type: tsv type: tsv
location: "${SQ_ROOT}/drivers/csv/testdata/sakila-tsv/actor.tsv" location: '${SQ_ROOT}/drivers/csv/testdata/sakila-tsv/actor.tsv'
options: options:
header: header:
- "true" - 'true'
- handle: '@sakila_tsv_actor_noheader' - handle: '@sakila_tsv_actor_noheader'
type: tsv type: tsv
location: "${SQ_ROOT}/drivers/csv/testdata/sakila-tsv-noheader/actor.tsv" location: '${SQ_ROOT}/drivers/csv/testdata/sakila-tsv-noheader/actor.tsv'
options: options:
header: header:
- "false" - 'false'
- handle: '@csv_person' - handle: '@csv_person'
type: csv type: csv
location: "${SQ_ROOT}/drivers/csv/testdata/person.csv" location: '${SQ_ROOT}/drivers/csv/testdata/person.csv'
- handle: '@csv_person_big' - handle: '@csv_person_big'
options: options:
header: header:
- "true" - 'true'
type: csv type: csv
location: "${SQ_ROOT}/drivers/csv/testdata/person_big.csv" location: '${SQ_ROOT}/drivers/csv/testdata/person_big.csv'
- handle: '@csv_person_noheader' - handle: '@csv_person_noheader'
type: csv type: csv
location: "${SQ_ROOT}/drivers/csv/testdata/person_noheader.csv" location: '${SQ_ROOT}/drivers/csv/testdata/person_noheader.csv'
- handle: '@tsv_person' - handle: '@tsv_person'
type: tsv type: tsv
location: "${SQ_ROOT}/sq/drivers/csv/testdata/person.tsv" location: '${SQ_ROOT}/sq/drivers/csv/testdata/person.tsv'
- handle: '@tsv_person_noheader' - handle: '@tsv_person_noheader'
type: tsv type: tsv
location: "${SQ_ROOT}/drivers/csv/testdata/person_noheader.tsv" location: '${SQ_ROOT}/drivers/csv/testdata/person_noheader.tsv'
- handle: '@tsv_person_noheader_cols' - handle: '@tsv_person_noheader_cols'
type: tsv type: tsv
location: "${SQ_ROOT}/drivers/csv/testdata/person_noheader.tsv" location: '${SQ_ROOT}/drivers/csv/testdata/person_noheader.tsv'
options: options:
cols: cols:
- uid,username,email - uid,username,email
- handle: '@xl_header' - handle: '@xl_header'
type: xlsx type: xlsx
location: "${SQ_ROOT}/drivers/xlsx/testdata/test_header.xlsx" location: '${SQ_ROOT}/drivers/xlsx/testdata/test_header.xlsx'
- handle: '@xl_noheader' - handle: '@xl_noheader'
type: xlsx type: xlsx
location: "${SQ_ROOT}/drivers/xlsx/testdata/test_noheader.xlsx" location: '${SQ_ROOT}/drivers/xlsx/testdata/test_noheader.xlsx'
- handle: '@ud_ppl' - handle: '@ud_ppl'
type: ppl type: ppl
location: "${SQ_ROOT}/drivers/userdriver/xmlud/testdata/people.xml" location: '${SQ_ROOT}/drivers/userdriver/xmlud/testdata/people.xml'
- handle: '@ud_rss_nytimes_local' - handle: '@ud_rss_nytimes_local'
type: rss type: rss
location: "${SQ_ROOT}/drivers/userdriver/xmlud/testdata/nytimes_local.rss.xml" location: '${SQ_ROOT}/drivers/userdriver/xmlud/testdata/nytimes_local.rss.xml'
- handle: '@miscdb' - handle: '@miscdb'
type: sqlite3 type: sqlite3
location: "sqlite3://${SQ_ROOT}/drivers/sqlite3/testdata/misc.db" location: 'sqlite3://${SQ_ROOT}/drivers/sqlite3/testdata/misc.db'

View File

@ -150,7 +150,7 @@ func (h *Helper) Source(handle string) *source.Source {
switch src.Type { switch src.Type {
case sqlite3.Type: case sqlite3.Type:
// This could be easily generalized for CSV/XLSX etc. // This could be easily generalized for CSV/XLSX etc.
fpath, err := sqlite3.PathFromSourceLocation(src) fpath, err := sqlite3.PathFromLocation(src)
require.NoError(t, err) require.NoError(t, err)
srcFile, err := os.Open(fpath) srcFile, err := os.Open(fpath)
@ -167,7 +167,7 @@ func (h *Helper) Source(handle string) *source.Source {
_, err = io.Copy(destFile, srcFile) _, err = io.Copy(destFile, srcFile)
require.NoError(t, err) require.NoError(t, err)
src.Location = "sqlite3:" + destFile.Name() src.Location = sqlite3.Prefix + destFile.Name()
} }
h.srcCache[handle] = src h.srcCache[handle] = src
return src return src