Lint long lines (#115)

* lint config now sorts results

* linted long lines

* linted long lines
This commit is contained in:
Neil O'Toole 2022-12-17 19:43:53 -07:00 committed by GitHub
parent 2831211ae9
commit 540adfac58
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 242 additions and 117 deletions

View File

@ -17,6 +17,9 @@ run:
- cli/output/jsonw/internal/jcolorenc - cli/output/jsonw/internal/jcolorenc
- cli/output/tablew/internal - cli/output/tablew/internal
output:
sort-results: true
# This file contains only configs which differ from defaults. # This file contains only configs which differ from defaults.
# All possible options can be found here https://github.com/golangci/golangci-lint/blob/master/.golangci.reference.yml # All possible options can be found here https://github.com/golangci/golangci-lint/blob/master/.golangci.reference.yml
linters-settings: linters-settings:

View File

@ -557,7 +557,8 @@ type writers struct {
// newWriters returns a writers instance configured per defaults and/or // newWriters returns a writers instance configured per defaults and/or
// flags from cmd. The returned out2/errOut2 values may differ // flags from cmd. The returned out2/errOut2 values may differ
// from the out/errOut args (e.g. decorated to support colorization). // from the out/errOut args (e.g. decorated to support colorization).
func newWriters(log lg.Log, cmd *cobra.Command, defaults config.Defaults, out, errOut io.Writer) (w *writers, out2, errOut2 io.Writer) { func newWriters(log lg.Log, cmd *cobra.Command, defaults config.Defaults, out, errOut io.Writer) (w *writers,
out2, errOut2 io.Writer) {
var fm *output.Formatting var fm *output.Formatting
fm, out2, errOut2 = getWriterFormatting(cmd, out, errOut) fm, out2, errOut2 = getWriterFormatting(cmd, out, errOut)

View File

@ -108,7 +108,8 @@ func execPing(cmd *cobra.Command, args []string) error {
// //
// originally laid down before context.Context was a thing. Thus, // originally laid down before context.Context was a thing. Thus,
// the entire thing could probably be rewritten for simplicity. // the entire thing could probably be rewritten for simplicity.
func pingSources(ctx context.Context, log lg.Log, dp driver.Provider, srcs []*source.Source, w output.PingWriter, timeout time.Duration) error { func pingSources(ctx context.Context, log lg.Log, dp driver.Provider, srcs []*source.Source, w output.PingWriter,
timeout time.Duration) error {
w.Open(srcs) w.Open(srcs)
defer log.WarnIfFuncError(w.Close) defer log.WarnIfFuncError(w.Close)
@ -160,7 +161,8 @@ func pingSources(ctx context.Context, log lg.Log, dp driver.Provider, srcs []*so
// pingSource pings an individual driver.Source. It always returns a // pingSource pings an individual driver.Source. It always returns a
// result on resultCh, even when ctx is done. // result on resultCh, even when ctx is done.
func pingSource(ctx context.Context, dp driver.Provider, src *source.Source, timeout time.Duration, resultCh chan<- pingResult) { func pingSource(ctx context.Context, dp driver.Provider, src *source.Source, timeout time.Duration,
resultCh chan<- pingResult) {
drvr, err := dp.DriverFor(src.Type) drvr, err := dp.DriverFor(src.Type)
if err != nil { if err != nil {
resultCh <- pingResult{src: src, err: err} resultCh <- pingResult{src: src, err: err}

View File

@ -99,7 +99,8 @@ func execTblCopy(cmd *cobra.Command, args []string) error {
sqlDrvr, ok := tblHandles[0].drvr.(driver.SQLDriver) sqlDrvr, ok := tblHandles[0].drvr.(driver.SQLDriver)
if !ok { if !ok {
return errz.Errorf("source type %q (%s) doesn't support dropping tables", tblHandles[0].src.Type, tblHandles[0].src.Handle) return errz.Errorf("source type %q (%s) doesn't support dropping tables", tblHandles[0].src.Type,
tblHandles[0].src.Handle)
} }
copyData := true // copy data by default copyData := true // copy data by default

View File

@ -108,7 +108,8 @@ type handleTableCompleter struct {
} }
// complete is the completionFunc for handleTableCompleter. // complete is the completionFunc for handleTableCompleter.
func (c *handleTableCompleter) complete(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { func (c *handleTableCompleter) complete(cmd *cobra.Command, args []string, toComplete string) ([]string,
cobra.ShellCompDirective) {
rc := RunContextFrom(cmd.Context()) rc := RunContextFrom(cmd.Context())
if err := rc.init(); err != nil { if err := rc.init(); err != nil {
rc.Log.Error(err) rc.Log.Error(err)
@ -152,7 +153,8 @@ func (c *handleTableCompleter) complete(cmd *cobra.Command, args []string, toCom
// completeTableOnly returns suggestions given input beginning with // completeTableOnly returns suggestions given input beginning with
// a period. Effectively this is completion for tables in the // a period. Effectively this is completion for tables in the
// active src. // active src.
func (c *handleTableCompleter) completeTableOnly(ctx context.Context, rc *RunContext, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { func (c *handleTableCompleter) completeTableOnly(ctx context.Context, rc *RunContext, args []string,
toComplete string) ([]string, cobra.ShellCompDirective) {
activeSrc := rc.Config.Sources.Active() activeSrc := rc.Config.Sources.Active()
if activeSrc == nil { if activeSrc == nil {
rc.Log.Error("Active source is nil") rc.Log.Error("Active source is nil")
@ -188,7 +190,8 @@ func (c *handleTableCompleter) completeTableOnly(ctx context.Context, rc *RunCon
// completeHandle returns suggestions given input beginning with // completeHandle returns suggestions given input beginning with
// a '@'. The returned suggestions could be @HANDLE, or @HANDLE.TABLE. // a '@'. The returned suggestions could be @HANDLE, or @HANDLE.TABLE.
func (c *handleTableCompleter) completeHandle(ctx context.Context, rc *RunContext, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { func (c *handleTableCompleter) completeHandle(ctx context.Context, rc *RunContext, args []string,
toComplete string) ([]string, cobra.ShellCompDirective) {
// We're dealing with a handle. // We're dealing with a handle.
// But we could be dealing with just the handle ("@sakila_sl3") // But we could be dealing with just the handle ("@sakila_sl3")
@ -280,7 +283,8 @@ func (c *handleTableCompleter) completeHandle(ctx context.Context, rc *RunContex
return suggestions, cobra.ShellCompDirectiveNoFileComp return suggestions, cobra.ShellCompDirectiveNoFileComp
} }
func (c *handleTableCompleter) completeEither(ctx context.Context, rc *RunContext, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { func (c *handleTableCompleter) completeEither(ctx context.Context, rc *RunContext, args []string,
toComplete string) ([]string, cobra.ShellCompDirective) {
// There's no input yet. // There's no input yet.
// Therefore we want to return a union of all handles // Therefore we want to return a union of all handles
// plus the tables from the active source. // plus the tables from the active source.

View File

@ -56,7 +56,8 @@ func NewRecordWriterAdapter(rw RecordWriter) *RecordWriterAdapter {
} }
// Open implements libsq.RecordWriter. // Open implements libsq.RecordWriter.
func (w *RecordWriterAdapter) Open(ctx context.Context, cancelFn context.CancelFunc, recMeta sqlz.RecordMeta) (chan<- sqlz.Record, <-chan error, error) { func (w *RecordWriterAdapter) Open(ctx context.Context, cancelFn context.CancelFunc,
recMeta sqlz.RecordMeta) (chan<- sqlz.Record, <-chan error, error) {
w.cancelFn = cancelFn w.cancelFn = cancelFn
err := w.rw.Open(recMeta) err := w.rw.Open(recMeta)

View File

@ -156,7 +156,8 @@ func checkStdinSource(ctx context.Context, rc *RunContext) (*source.Source, erro
// newSource creates a new Source instance where the // newSource creates a new Source instance where the
// driver type is known. Opts may be nil. // driver type is known. Opts may be nil.
func newSource(log lg.Log, dp driver.Provider, typ source.Type, handle, location string, opts options.Options) (*source.Source, error) { func newSource(log lg.Log, dp driver.Provider, typ source.Type, handle, location string,
opts options.Options) (*source.Source, error) {
if opts == nil { if opts == nil {
log.Debugf("Create new data source %q [%s] from %q", log.Debugf("Create new data source %q [%s] from %q",
handle, typ, location) handle, typ, location)

View File

@ -224,16 +224,19 @@ var (
) )
// DetectCSV implements source.TypeDetectFunc. // DetectCSV implements source.TypeDetectFunc.
func DetectCSV(ctx context.Context, log lg.Log, openFn source.FileOpenFunc) (detected source.Type, score float32, err error) { func DetectCSV(ctx context.Context, log lg.Log, openFn source.FileOpenFunc) (detected source.Type, score float32,
err error) {
return detectType(ctx, TypeCSV, log, openFn) return detectType(ctx, TypeCSV, log, openFn)
} }
// DetectTSV implements source.TypeDetectFunc. // DetectTSV implements source.TypeDetectFunc.
func DetectTSV(ctx context.Context, log lg.Log, openFn source.FileOpenFunc) (detected source.Type, score float32, err error) { func DetectTSV(ctx context.Context, log lg.Log, openFn source.FileOpenFunc) (detected source.Type,
score float32, err error) {
return detectType(ctx, TypeTSV, log, openFn) return detectType(ctx, TypeTSV, log, openFn)
} }
func detectType(ctx context.Context, typ source.Type, log lg.Log, openFn source.FileOpenFunc) (detected source.Type, score float32, err error) { func detectType(ctx context.Context, typ source.Type, log lg.Log, openFn source.FileOpenFunc) (detected source.Type,
score float32, err error) {
var r io.ReadCloser var r io.ReadCloser
r, err = openFn() r, err = openFn()
if err != nil { if err != nil {

View File

@ -26,7 +26,8 @@ const (
) )
// importCSV loads the src CSV data to scratchDB. // importCSV loads the src CSV data to scratchDB.
func importCSV(ctx context.Context, log lg.Log, src *source.Source, openFn source.FileOpenFunc, scratchDB driver.Database) error { func importCSV(ctx context.Context, log lg.Log, src *source.Source, openFn source.FileOpenFunc,
scratchDB driver.Database) error {
// TODO: optPredictKind should be read from src.Options. // TODO: optPredictKind should be read from src.Options.
const optPredictKind bool = true const optPredictKind bool = true
@ -104,7 +105,8 @@ func importCSV(ctx context.Context, log lg.Log, src *source.Source, openFn sourc
// execInsert inserts the CSV records in readAheadRecs (followed by records // execInsert inserts the CSV records in readAheadRecs (followed by records
// from the csv.Reader) via recw. The caller should wait on recw to complete. // from the csv.Reader) via recw. The caller should wait on recw to complete.
func execInsert(ctx context.Context, recw libsq.RecordWriter, recMeta sqlz.RecordMeta, readAheadRecs [][]string, r *csv.Reader) error { func execInsert(ctx context.Context, recw libsq.RecordWriter, recMeta sqlz.RecordMeta,
readAheadRecs [][]string, r *csv.Reader) error {
ctx, cancelFn := context.WithCancel(ctx) ctx, cancelFn := context.WithCancel(ctx)
recordCh, errCh, err := recw.Open(ctx, cancelFn, recMeta) recordCh, errCh, err := recw.Open(ctx, cancelFn, recMeta)
@ -206,7 +208,8 @@ func createTblDef(tblName string, colNames []string, kinds []kind.Kind) *sqlmode
// kind is excluded from the list of candidate kinds. The first of any // kind is excluded from the list of candidate kinds. The first of any
// remaining candidate kinds for each field is returned, or kind.Text if // remaining candidate kinds for each field is returned, or kind.Text if
// no candidate kinds. // no candidate kinds.
func predictColKinds(expectFieldCount int, r *csv.Reader, readAheadRecs *[][]string, maxExamine int) ([]kind.Kind, error) { func predictColKinds(expectFieldCount int, r *csv.Reader, readAheadRecs *[][]string, maxExamine int) ([]kind.Kind,
error) {
// FIXME: [legacy] this function should switch to using kind.Detector // FIXME: [legacy] this function should switch to using kind.Detector
candidateKinds := newCandidateFieldKinds(expectFieldCount) candidateKinds := newCandidateFieldKinds(expectFieldCount)
@ -215,7 +218,8 @@ func predictColKinds(expectFieldCount int, r *csv.Reader, readAheadRecs *[][]str
// First, read any records from the readAheadRecs buffer // First, read any records from the readAheadRecs buffer
for recIndex := 0; recIndex < len(*readAheadRecs) && examineCount < maxExamine; recIndex++ { for recIndex := 0; recIndex < len(*readAheadRecs) && examineCount < maxExamine; recIndex++ {
for fieldIndex := 0; fieldIndex < expectFieldCount; fieldIndex++ { for fieldIndex := 0; fieldIndex < expectFieldCount; fieldIndex++ {
candidateKinds[fieldIndex] = excludeFieldKinds(candidateKinds[fieldIndex], (*readAheadRecs)[recIndex][fieldIndex]) candidateKinds[fieldIndex] = excludeFieldKinds(candidateKinds[fieldIndex],
(*readAheadRecs)[recIndex][fieldIndex])
} }
examineCount++ examineCount++
} }

View File

@ -259,7 +259,8 @@ func (p *processor) doAddObject(ent *entity, m map[string]any) error {
// Child already exists // Child already exists
if child.isArray { if child.isArray {
// Safety check // Safety check
return errz.Errorf("JSON entity %q previously detected as array, but now detected as object", ent.String()) return errz.Errorf("JSON entity %q previously detected as array, but now detected as object",
ent.String())
} }
} }
@ -423,7 +424,8 @@ type importSchema struct {
entityTbls map[*entity]*sqlmodel.TableDef entityTbls map[*entity]*sqlmodel.TableDef
} }
func execSchemaDelta(ctx context.Context, log lg.Log, drvr driver.SQLDriver, db sqlz.DB, curSchema, newSchema *importSchema) error { func execSchemaDelta(ctx context.Context, log lg.Log, drvr driver.SQLDriver, db sqlz.DB,
curSchema, newSchema *importSchema) error {
var err error var err error
if curSchema == nil { if curSchema == nil {
for _, tblDef := range newSchema.tblDefs { for _, tblDef := range newSchema.tblDefs {

View File

@ -16,7 +16,8 @@ import (
// DetectJSON implements source.TypeDetectFunc. // DetectJSON implements source.TypeDetectFunc.
// The function returns TypeJSON for two varieties of input: // The function returns TypeJSON for two varieties of input:
func DetectJSON(ctx context.Context, log lg.Log, openFn source.FileOpenFunc) (detected source.Type, score float32, err error) { func DetectJSON(ctx context.Context, log lg.Log, openFn source.FileOpenFunc) (detected source.Type, score float32,
err error) {
var r1, r2 io.ReadCloser var r1, r2 io.ReadCloser
r1, err = openFn() r1, err = openFn()
if err != nil { if err != nil {
@ -359,7 +360,8 @@ func (s *objectsInArrayScanner) next() (obj map[string]any, chunk []byte, err er
switch delim { switch delim {
default: default:
// bad input // bad input
return nil, nil, errz.Errorf("invalid JSON: expected comma or right-bracket ']' token but got: %s", formatToken(tok)) return nil, nil, errz.Errorf("invalid JSON: expected comma or right-bracket ']' token but got: %s",
formatToken(tok))
case ']': case ']':
// should be end of input // should be end of input

View File

@ -21,7 +21,8 @@ import (
// DetectJSONA implements source.TypeDetectFunc for TypeJSONA. // DetectJSONA implements source.TypeDetectFunc for TypeJSONA.
// Each line of input must be a valid JSON array. // Each line of input must be a valid JSON array.
func DetectJSONA(ctx context.Context, log lg.Log, openFn source.FileOpenFunc) (detected source.Type, score float32, err error) { func DetectJSONA(ctx context.Context, log lg.Log, openFn source.FileOpenFunc) (detected source.Type, score float32,
err error) {
var r io.ReadCloser var r io.ReadCloser
r, err = openFn() r, err = openFn()
if err != nil { if err != nil {
@ -157,7 +158,8 @@ func importJSONA(ctx context.Context, log lg.Log, job importJob) error {
// startInsertJSONA reads JSON records from r and sends // startInsertJSONA reads JSON records from r and sends
// them on recordCh. // them on recordCh.
func startInsertJSONA(ctx context.Context, recordCh chan<- sqlz.Record, errCh <-chan error, r io.Reader, mungeFns []kind.MungeFunc) error { func startInsertJSONA(ctx context.Context, recordCh chan<- sqlz.Record, errCh <-chan error, r io.Reader,
mungeFns []kind.MungeFunc) error {
defer close(recordCh) defer close(recordCh)
sc := bufio.NewScanner(r) sc := bufio.NewScanner(r)

View File

@ -15,7 +15,8 @@ import (
) )
// DetectJSONL implements source.TypeDetectFunc. // DetectJSONL implements source.TypeDetectFunc.
func DetectJSONL(ctx context.Context, log lg.Log, openFn source.FileOpenFunc) (detected source.Type, score float32, err error) { func DetectJSONL(ctx context.Context, log lg.Log, openFn source.FileOpenFunc) (detected source.Type, score float32,
err error) {
var r io.ReadCloser var r io.ReadCloser
r, err = openFn() r, err = openFn()
if err != nil { if err != nil {

View File

@ -126,7 +126,8 @@ func getNewRecordFunc(rowMeta sqlz.RecordMeta) driver.NewRecordFunc {
} }
// else, we don't know what to do with this col // else, we don't know what to do with this col
return nil, errz.Errorf("column %d %s: unknown type db(%T) with kind(%s), val(%v)", i, rowMeta[i].Name(), rec[i], rowMeta[i].Kind(), rec[i]) return nil, errz.Errorf("column %d %s: unknown type db(%T) with kind(%s), val(%v)", i, rowMeta[i].Name(),
rec[i], rowMeta[i].Kind(), rec[i])
} }
return rec, nil return rec, nil
} }
@ -167,7 +168,8 @@ WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?`
// getColumnMetadata returns column metadata for tblName. // getColumnMetadata returns column metadata for tblName.
func getColumnMetadata(ctx context.Context, log lg.Log, db sqlz.DB, tblName string) ([]*source.ColMetadata, error) { func getColumnMetadata(ctx context.Context, log lg.Log, db sqlz.DB, tblName string) ([]*source.ColMetadata, error) {
const query = `SELECT column_name, data_type, column_type, ordinal_position, column_default, is_nullable, column_key, column_comment, extra const query = `SELECT column_name, data_type, column_type, ordinal_position, column_default,
is_nullable, column_key, column_comment, extra
FROM information_schema.columns cols FROM information_schema.columns cols
WHERE cols.TABLE_SCHEMA = DATABASE() AND cols.TABLE_NAME = ? WHERE cols.TABLE_SCHEMA = DATABASE() AND cols.TABLE_NAME = ?
ORDER BY cols.ordinal_position ASC` ORDER BY cols.ordinal_position ASC`
@ -185,7 +187,8 @@ ORDER BY cols.ordinal_position ASC`
var isNullable, colKey, extra string var isNullable, colKey, extra string
defVal := &sql.NullString{} defVal := &sql.NullString{}
err = rows.Scan(&col.Name, &col.BaseType, &col.ColumnType, &col.Position, defVal, &isNullable, &colKey, &col.Comment, &extra) err = rows.Scan(&col.Name, &col.BaseType, &col.ColumnType, &col.Position, defVal, &isNullable, &colKey,
&col.Comment, &extra)
if err != nil { if err != nil {
return nil, errz.Err(err) return nil, errz.Err(err)
} }
@ -264,7 +267,8 @@ func setSourceSummaryMeta(ctx context.Context, db sqlz.DB, md *source.Metadata)
FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE()) AS size` FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE()) AS size`
var version, versionComment, versionOS, versionArch, schema string var version, versionComment, versionOS, versionArch, schema string
err := db.QueryRowContext(ctx, summaryQuery).Scan(&version, &versionComment, &versionOS, &versionArch, &schema, &md.User, &md.Size) err := db.QueryRowContext(ctx, summaryQuery).Scan(&version, &versionComment, &versionOS, &versionArch, &schema,
&md.User, &md.Size)
if err != nil { if err != nil {
return errz.Err(err) return errz.Err(err)
} }
@ -304,8 +308,10 @@ func getDBVarsMeta(ctx context.Context, log lg.Log, db sqlz.DB) ([]source.DBVar,
// getAllTblMetas returns TableMetadata for each table/view in db. // getAllTblMetas returns TableMetadata for each table/view in db.
func getAllTblMetas(ctx context.Context, log lg.Log, db sqlz.DB) ([]*source.TableMetadata, error) { func getAllTblMetas(ctx context.Context, log lg.Log, db sqlz.DB) ([]*source.TableMetadata, error) {
const query = `SELECT t.TABLE_SCHEMA, t.TABLE_NAME, t.TABLE_TYPE, t.TABLE_COMMENT, (DATA_LENGTH + INDEX_LENGTH) AS table_size, const query = `SELECT t.TABLE_SCHEMA, t.TABLE_NAME, t.TABLE_TYPE, t.TABLE_COMMENT,
c.COLUMN_NAME, c.ORDINAL_POSITION, c.COLUMN_KEY, c.DATA_TYPE, c.COLUMN_TYPE, c.IS_NULLABLE, c.COLUMN_DEFAULT, c.COLUMN_COMMENT, c.EXTRA (DATA_LENGTH + INDEX_LENGTH) AS table_size,
c.COLUMN_NAME, c.ORDINAL_POSITION, c.COLUMN_KEY, c.DATA_TYPE, c.COLUMN_TYPE,
c.IS_NULLABLE, c.COLUMN_DEFAULT, c.COLUMN_COMMENT, c.EXTRA
FROM information_schema.TABLES t FROM information_schema.TABLES t
LEFT JOIN information_schema.COLUMNS c LEFT JOIN information_schema.COLUMNS c
ON c.TABLE_CATALOG = t.TABLE_CATALOG ON c.TABLE_CATALOG = t.TABLE_CATALOG

View File

@ -113,7 +113,8 @@ func (d *driveri) AlterTableAddColumn(ctx context.Context, db *sql.DB, tbl, col
} }
// PrepareInsertStmt implements driver.SQLDriver. // PrepareInsertStmt implements driver.SQLDriver.
func (d *driveri) PrepareInsertStmt(ctx context.Context, db sqlz.DB, destTbl string, destColNames []string, numRows int) (*driver.StmtExecer, error) { func (d *driveri) PrepareInsertStmt(ctx context.Context, db sqlz.DB, destTbl string, destColNames []string,
numRows int) (*driver.StmtExecer, error) {
destColsMeta, err := d.getTableRecordMeta(ctx, db, destTbl, destColNames) destColsMeta, err := d.getTableRecordMeta(ctx, db, destTbl, destColNames)
if err != nil { if err != nil {
return nil, err return nil, err
@ -129,7 +130,8 @@ func (d *driveri) PrepareInsertStmt(ctx context.Context, db sqlz.DB, destTbl str
} }
// PrepareUpdateStmt implements driver.SQLDriver. // PrepareUpdateStmt implements driver.SQLDriver.
func (d *driveri) PrepareUpdateStmt(ctx context.Context, db sqlz.DB, destTbl string, destColNames []string, where string) (*driver.StmtExecer, error) { func (d *driveri) PrepareUpdateStmt(ctx context.Context, db sqlz.DB, destTbl string, destColNames []string,
where string) (*driver.StmtExecer, error) {
destColsMeta, err := d.getTableRecordMeta(ctx, db, destTbl, destColNames) destColsMeta, err := d.getTableRecordMeta(ctx, db, destTbl, destColNames)
if err != nil { if err != nil {
return nil, err return nil, err
@ -204,7 +206,8 @@ func (d *driveri) DropTable(ctx context.Context, db sqlz.DB, tbl string, ifExist
} }
// TableColumnTypes implements driver.SQLDriver. // TableColumnTypes implements driver.SQLDriver.
func (d *driveri) TableColumnTypes(ctx context.Context, db sqlz.DB, tblName string, colNames []string) ([]*sql.ColumnType, error) { func (d *driveri) TableColumnTypes(ctx context.Context, db sqlz.DB, tblName string,
colNames []string) ([]*sql.ColumnType, error) {
const queryTpl = "SELECT %s FROM %s LIMIT 0" const queryTpl = "SELECT %s FROM %s LIMIT 0"
dialect := d.Dialect() dialect := d.Dialect()
@ -243,7 +246,8 @@ func (d *driveri) TableColumnTypes(ctx context.Context, db sqlz.DB, tblName stri
return colTypes, nil return colTypes, nil
} }
func (d *driveri) getTableRecordMeta(ctx context.Context, db sqlz.DB, tblName string, colNames []string) (sqlz.RecordMeta, error) { func (d *driveri) getTableRecordMeta(ctx context.Context, db sqlz.DB, tblName string,
colNames []string) (sqlz.RecordMeta, error) {
colTypes, err := d.TableColumnTypes(ctx, db, tblName, colNames) colTypes, err := d.TableColumnTypes(ctx, db, tblName, colNames)
if err != nil { if err != nil {
return nil, err return nil, err
@ -294,7 +298,8 @@ func (d *driveri) Ping(ctx context.Context, src *source.Source) error {
// Truncate implements driver.SQLDriver. Arg reset is // Truncate implements driver.SQLDriver. Arg reset is
// always ignored: the identity value is always reset by // always ignored: the identity value is always reset by
// the TRUNCATE statement. // the TRUNCATE statement.
func (d *driveri) Truncate(ctx context.Context, src *source.Source, tbl string, reset bool) (affected int64, err error) { func (d *driveri) Truncate(ctx context.Context, src *source.Source, tbl string, reset bool) (affected int64,
err error) {
// https://dev.mysql.com/doc/refman/8.0/en/truncate-table.html // https://dev.mysql.com/doc/refman/8.0/en/truncate-table.html
dsn, err := dsnFromLocation(src, true) dsn, err := dsnFromLocation(src, true)
if err != nil { if err != nil {

View File

@ -314,7 +314,8 @@ AND table_name = $1`
pgTbl := &pgTable{} pgTbl := &pgTable{}
err := db.QueryRowContext(ctx, tablesQuery, tblName). err := db.QueryRowContext(ctx, tablesQuery, tblName).
Scan(&pgTbl.tableCatalog, &pgTbl.tableSchema, &pgTbl.tableName, &pgTbl.tableType, &pgTbl.isInsertable, &pgTbl.rowCount, &pgTbl.size, &pgTbl.oid, &pgTbl.comment) Scan(&pgTbl.tableCatalog, &pgTbl.tableSchema, &pgTbl.tableName, &pgTbl.tableType, &pgTbl.isInsertable,
&pgTbl.rowCount, &pgTbl.size, &pgTbl.oid, &pgTbl.comment)
if err != nil { if err != nil {
return nil, errz.Err(err) return nil, errz.Err(err)
} }
@ -584,9 +585,9 @@ type pgConstraint struct {
columnName string columnName string
ordinalPosition int64 ordinalPosition int64
constraintName sql.NullString constraintName sql.NullString
constraintType sql.NullString constraintType sql.NullString
constraintDef sql.NullString constraintDef sql.NullString
// constraintFKeyTableName holds the name of the table to which // constraintFKeyTableName holds the name of the table to which
// a foreign-key constraint points to. This is null if this // a foreign-key constraint points to. This is null if this
@ -607,7 +608,8 @@ func setTblMetaConstraints(log lg.Log, tblMeta *source.TableMetadata, pgConstrai
colMeta := tblMeta.Column(pgc.columnName) colMeta := tblMeta.Column(pgc.columnName)
if colMeta == nil { if colMeta == nil {
// Shouldn't happen // Shouldn't happen
log.Warnf("No column %s.%s found matching constraint %q", tblMeta.Name, pgc.columnName, pgc.constraintName) log.Warnf("No column %s.%s found matching constraint %q", tblMeta.Name, pgc.columnName,
pgc.constraintName)
continue continue
} }
colMeta.PrimaryKey = true colMeta.PrimaryKey = true

View File

@ -132,7 +132,8 @@ func (d *driveri) Ping(ctx context.Context, src *source.Source) error {
// row count of tbl before executing TRUNCATE. This row count // row count of tbl before executing TRUNCATE. This row count
// query is not part of a transaction with TRUNCATE, although // query is not part of a transaction with TRUNCATE, although
// possibly it should be, as the number of rows may have changed. // possibly it should be, as the number of rows may have changed.
func (d *driveri) Truncate(ctx context.Context, src *source.Source, tbl string, reset bool) (affected int64, err error) { func (d *driveri) Truncate(ctx context.Context, src *source.Source, tbl string, reset bool) (affected int64,
err error) {
// https://www.postgresql.org/docs/9.1/sql-truncate.html // https://www.postgresql.org/docs/9.1/sql-truncate.html
// RESTART IDENTITY and CASCADE/RESTRICT are from pg 8.2 onwards // RESTART IDENTITY and CASCADE/RESTRICT are from pg 8.2 onwards
@ -185,7 +186,8 @@ func (d *driveri) AlterTableAddColumn(ctx context.Context, db *sql.DB, tbl, col
} }
// PrepareInsertStmt implements driver.SQLDriver. // PrepareInsertStmt implements driver.SQLDriver.
func (d *driveri) PrepareInsertStmt(ctx context.Context, db sqlz.DB, destTbl string, destColNames []string, numRows int) (*driver.StmtExecer, error) { func (d *driveri) PrepareInsertStmt(ctx context.Context, db sqlz.DB, destTbl string, destColNames []string,
numRows int) (*driver.StmtExecer, error) {
// Note that the pgx driver doesn't support res.LastInsertId. // Note that the pgx driver doesn't support res.LastInsertId.
// https://github.com/jackc/pgx/issues/411 // https://github.com/jackc/pgx/issues/411
@ -199,12 +201,14 @@ func (d *driveri) PrepareInsertStmt(ctx context.Context, db sqlz.DB, destTbl str
return nil, err return nil, err
} }
execer := driver.NewStmtExecer(stmt, driver.DefaultInsertMungeFunc(destTbl, destColsMeta), newStmtExecFunc(stmt), destColsMeta) execer := driver.NewStmtExecer(stmt, driver.DefaultInsertMungeFunc(destTbl, destColsMeta), newStmtExecFunc(stmt),
destColsMeta)
return execer, nil return execer, nil
} }
// PrepareUpdateStmt implements driver.SQLDriver. // PrepareUpdateStmt implements driver.SQLDriver.
func (d *driveri) PrepareUpdateStmt(ctx context.Context, db sqlz.DB, destTbl string, destColNames []string, where string) (*driver.StmtExecer, error) { func (d *driveri) PrepareUpdateStmt(ctx context.Context, db sqlz.DB, destTbl string, destColNames []string,
where string) (*driver.StmtExecer, error) {
destColsMeta, err := d.getTableRecordMeta(ctx, db, destTbl, destColNames) destColsMeta, err := d.getTableRecordMeta(ctx, db, destTbl, destColNames)
if err != nil { if err != nil {
return nil, err return nil, err
@ -220,7 +224,8 @@ func (d *driveri) PrepareUpdateStmt(ctx context.Context, db sqlz.DB, destTbl str
return nil, err return nil, err
} }
execer := driver.NewStmtExecer(stmt, driver.DefaultInsertMungeFunc(destTbl, destColsMeta), newStmtExecFunc(stmt), destColsMeta) execer := driver.NewStmtExecer(stmt, driver.DefaultInsertMungeFunc(destTbl, destColsMeta), newStmtExecFunc(stmt),
destColsMeta)
return execer, nil return execer, nil
} }
@ -280,7 +285,8 @@ func (d *driveri) DropTable(ctx context.Context, db sqlz.DB, tbl string, ifExist
} }
// TableColumnTypes implements driver.SQLDriver. // TableColumnTypes implements driver.SQLDriver.
func (d *driveri) TableColumnTypes(ctx context.Context, db sqlz.DB, tblName string, colNames []string) ([]*sql.ColumnType, error) { func (d *driveri) TableColumnTypes(ctx context.Context, db sqlz.DB, tblName string,
colNames []string) ([]*sql.ColumnType, error) {
// We have to do some funky stuff to get the column types // We have to do some funky stuff to get the column types
// from when the table has no rows. // from when the table has no rows.
// https://stackoverflow.com/questions/8098795/return-a-value-if-no-record-is-found // https://stackoverflow.com/questions/8098795/return-a-value-if-no-record-is-found
@ -345,7 +351,8 @@ func (d *driveri) TableColumnTypes(ctx context.Context, db sqlz.DB, tblName stri
return colTypes, nil return colTypes, nil
} }
func (d *driveri) getTableRecordMeta(ctx context.Context, db sqlz.DB, tblName string, colNames []string) (sqlz.RecordMeta, error) { func (d *driveri) getTableRecordMeta(ctx context.Context, db sqlz.DB, tblName string,
colNames []string) (sqlz.RecordMeta, error) {
colTypes, err := d.TableColumnTypes(ctx, db, tblName, colNames) colTypes, err := d.TableColumnTypes(ctx, db, tblName, colNames)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -86,7 +86,8 @@ func (d *driveri) Open(ctx context.Context, src *source.Source) (driver.Database
} }
// Truncate implements driver.Driver. // Truncate implements driver.Driver.
func (d *driveri) Truncate(ctx context.Context, src *source.Source, tbl string, reset bool) (affected int64, err error) { func (d *driveri) Truncate(ctx context.Context, src *source.Source, tbl string, reset bool) (affected int64,
err error) {
dsn, err := PathFromLocation(src) dsn, err := PathFromLocation(src)
if err != nil { if err != nil {
return 0, err return 0, err
@ -180,7 +181,8 @@ func (d *driveri) CopyTable(ctx context.Context, db sqlz.DB, fromTable, toTable
// we need to do something more complicated. // we need to do something more complicated.
var originTblCreateStmt string var originTblCreateStmt string
err := db.QueryRowContext(ctx, fmt.Sprintf("SELECT sql FROM sqlite_master WHERE type='table' AND name='%s'", fromTable)).Scan(&originTblCreateStmt) err := db.QueryRowContext(ctx, fmt.Sprintf("SELECT sql FROM sqlite_master WHERE type='table' AND name='%s'",
fromTable)).Scan(&originTblCreateStmt)
if err != nil { if err != nil {
return 0, errz.Err(err) return 0, errz.Err(err)
} }
@ -285,7 +287,8 @@ func (d *driveri) TableExists(ctx context.Context, db sqlz.DB, tbl string) (bool
} }
// PrepareInsertStmt implements driver.SQLDriver. // PrepareInsertStmt implements driver.SQLDriver.
func (d *driveri) PrepareInsertStmt(ctx context.Context, db sqlz.DB, destTbl string, destColNames []string, numRows int) (*driver.StmtExecer, error) { func (d *driveri) PrepareInsertStmt(ctx context.Context, db sqlz.DB, destTbl string, destColNames []string,
numRows int) (*driver.StmtExecer, error) {
destColsMeta, err := d.getTableRecordMeta(ctx, db, destTbl, destColNames) destColsMeta, err := d.getTableRecordMeta(ctx, db, destTbl, destColNames)
if err != nil { if err != nil {
return nil, err return nil, err
@ -336,7 +339,8 @@ func newStmtExecFunc(stmt *sql.Stmt) driver.StmtExecFunc {
} }
// TableColumnTypes implements driver.SQLDriver. // TableColumnTypes implements driver.SQLDriver.
func (d *driveri) TableColumnTypes(ctx context.Context, db sqlz.DB, tblName string, colNames []string) ([]*sql.ColumnType, error) { func (d *driveri) TableColumnTypes(ctx context.Context, db sqlz.DB, tblName string,
colNames []string) ([]*sql.ColumnType, error) {
// Given the dynamic behavior of sqlite's rows.ColumnTypes, // Given the dynamic behavior of sqlite's rows.ColumnTypes,
// this query selects a single row, as that'll give us more // this query selects a single row, as that'll give us more
// accurate column type info than no rows. For other db // accurate column type info than no rows. For other db
@ -394,7 +398,8 @@ func (d *driveri) TableColumnTypes(ctx context.Context, db sqlz.DB, tblName stri
return colTypes, nil return colTypes, nil
} }
func (d *driveri) getTableRecordMeta(ctx context.Context, db sqlz.DB, tblName string, colNames []string) (sqlz.RecordMeta, error) { func (d *driveri) getTableRecordMeta(ctx context.Context, db sqlz.DB, tblName string,
colNames []string) (sqlz.RecordMeta, error) {
colTypes, err := d.TableColumnTypes(ctx, db, tblName, colNames) colTypes, err := d.TableColumnTypes(ctx, db, tblName, colNames)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -148,7 +148,8 @@ GROUP BY database_id) AS total_size_bytes`
if hasErrCode(err, errCodeObjectNotExist) { if hasErrCode(err, errCodeObjectNotExist) {
// This can happen if the table is dropped while // This can happen if the table is dropped while
// we're collecting metadata. We log a warning and continue. // we're collecting metadata. We log a warning and continue.
log.Warnf("table metadata: table %q appears not to exist (continuing regardless): %v", tblNames[i], err) log.Warnf("table metadata: table %q appears not to exist (continuing regardless): %v", tblNames[i],
err)
return nil return nil
} }
return err return err
@ -175,7 +176,8 @@ GROUP BY database_id) AS total_size_bytes`
return md, nil return md, nil
} }
func getTableMetadata(ctx context.Context, log lg.Log, db sqlz.DB, tblCatalog, tblSchema, tblName, tblType string) (*source.TableMetadata, error) { func getTableMetadata(ctx context.Context, log lg.Log, db sqlz.DB,
tblCatalog, tblSchema, tblName, tblType string) (*source.TableMetadata, error) {
const tplTblUsage = `sp_spaceused '%s'` const tplTblUsage = `sp_spaceused '%s'`
tblMeta := &source.TableMetadata{Name: tblName, DBTableType: tblType} tblMeta := &source.TableMetadata{Name: tblName, DBTableType: tblType}
@ -306,7 +308,8 @@ ORDER BY TABLE_NAME ASC, TABLE_TYPE ASC`
return tblNames, tblTypes, nil return tblNames, tblTypes, nil
} }
func getColumnMeta(ctx context.Context, log lg.Log, db sqlz.DB, tblCatalog, tblSchema, tblName string) ([]columnMeta, error) { func getColumnMeta(ctx context.Context, log lg.Log, db sqlz.DB, tblCatalog, tblSchema, tblName string) ([]columnMeta,
error) {
// TODO: sq doesn't use all of these columns, no need to select them all. // TODO: sq doesn't use all of these columns, no need to select them all.
const query = `SELECT const query = `SELECT
@ -335,7 +338,8 @@ func getColumnMeta(ctx context.Context, log lg.Log, db sqlz.DB, tblCatalog, tblS
err = rows.Scan(&c.TableCatalog, &c.TableSchema, &c.TableName, &c.ColumnName, &c.OrdinalPosition, err = rows.Scan(&c.TableCatalog, &c.TableSchema, &c.TableName, &c.ColumnName, &c.OrdinalPosition,
&c.ColumnDefault, &c.Nullable, &c.DataType, &c.CharMaxLength, &c.CharOctetLength, &c.NumericPrecision, &c.ColumnDefault, &c.Nullable, &c.DataType, &c.CharMaxLength, &c.CharOctetLength, &c.NumericPrecision,
&c.NumericPrecisionRadix, &c.NumericScale, &c.DateTimePrecision, &c.CharSetCatalog, &c.CharSetSchema, &c.NumericPrecisionRadix, &c.NumericScale, &c.DateTimePrecision, &c.CharSetCatalog, &c.CharSetSchema,
&c.CharSetName, &c.CollationCatalog, &c.CollationSchema, &c.CollationName, &c.DomainCatalog, &c.DomainSchema, &c.DomainName) &c.CharSetName, &c.CollationCatalog, &c.CollationSchema, &c.CollationName, &c.DomainCatalog,
&c.DomainSchema, &c.DomainName)
if err != nil { if err != nil {
return nil, errz.Err(err) return nil, errz.Err(err)
} }
@ -350,8 +354,10 @@ func getColumnMeta(ctx context.Context, log lg.Log, db sqlz.DB, tblCatalog, tblS
return cols, nil return cols, nil
} }
func getConstraints(ctx context.Context, log lg.Log, db sqlz.DB, tblCatalog, tblSchema, tblName string) ([]constraintMeta, error) { func getConstraints(ctx context.Context, log lg.Log, db sqlz.DB,
const query = `SELECT kcu.TABLE_CATALOG, kcu.TABLE_SCHEMA, kcu.TABLE_NAME, tc.CONSTRAINT_TYPE, kcu.COLUMN_NAME, kcu.CONSTRAINT_NAME tblCatalog, tblSchema, tblName string) ([]constraintMeta, error) {
const query = `SELECT kcu.TABLE_CATALOG, kcu.TABLE_SCHEMA, kcu.TABLE_NAME, tc.CONSTRAINT_TYPE,
kcu.COLUMN_NAME, kcu.CONSTRAINT_NAME
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS tc FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS tc
JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS kcu JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS kcu
ON tc.TABLE_NAME = kcu.TABLE_NAME ON tc.TABLE_NAME = kcu.TABLE_NAME
@ -372,7 +378,8 @@ func getConstraints(ctx context.Context, log lg.Log, db sqlz.DB, tblCatalog, tbl
for rows.Next() { for rows.Next() {
c := constraintMeta{} c := constraintMeta{}
err = rows.Scan(&c.TableCatalog, &c.TableSchema, &c.TableName, &c.ConstraintType, &c.ColumnName, &c.ConstraintName) err = rows.Scan(&c.TableCatalog, &c.TableSchema, &c.TableName, &c.ConstraintType, &c.ColumnName,
&c.ConstraintName)
if err != nil { if err != nil {
return nil, errz.Err(err) return nil, errz.Err(err)
} }

View File

@ -142,7 +142,11 @@ func (d *driveri) Ping(ctx context.Context, src *source.Source) error {
// operation is implemented in two statements. First "DELETE FROM tbl" to // operation is implemented in two statements. First "DELETE FROM tbl" to
// delete all rows. Then, if reset is true, the table sequence counter // delete all rows. Then, if reset is true, the table sequence counter
// is reset via RESEED. // is reset via RESEED.
func (d *driveri) Truncate(ctx context.Context, src *source.Source, tbl string, reset bool) (affected int64, err error) { //
//nolint:lll
func (d *driveri) Truncate(ctx context.Context, src *source.Source, tbl string, reset bool) (affected int64,
err error) {
// https://docs.microsoft.com/en-us/sql/t-sql/statements/truncate-table-transact-sql?view=sql-server-ver15 // https://docs.microsoft.com/en-us/sql/t-sql/statements/truncate-table-transact-sql?view=sql-server-ver15
// When there are foreign key constraints on mssql tables, // When there are foreign key constraints on mssql tables,
@ -175,7 +179,8 @@ func (d *driveri) Truncate(ctx context.Context, src *source.Source, tbl string,
} }
// TableColumnTypes implements driver.SQLDriver. // TableColumnTypes implements driver.SQLDriver.
func (d *driveri) TableColumnTypes(ctx context.Context, db sqlz.DB, tblName string, colNames []string) ([]*sql.ColumnType, error) { func (d *driveri) TableColumnTypes(ctx context.Context, db sqlz.DB, tblName string,
colNames []string) ([]*sql.ColumnType, error) {
// SQLServer has this unusual incantation for its LIMIT equivalent: // SQLServer has this unusual incantation for its LIMIT equivalent:
// //
// SELECT username, email, address_id FROM person // SELECT username, email, address_id FROM person
@ -308,7 +313,8 @@ func (d *driveri) DropTable(ctx context.Context, db sqlz.DB, tbl string, ifExist
} }
// PrepareInsertStmt implements driver.SQLDriver. // PrepareInsertStmt implements driver.SQLDriver.
func (d *driveri) PrepareInsertStmt(ctx context.Context, db sqlz.DB, destTbl string, destColNames []string, numRows int) (*driver.StmtExecer, error) { func (d *driveri) PrepareInsertStmt(ctx context.Context, db sqlz.DB, destTbl string, destColNames []string,
numRows int) (*driver.StmtExecer, error) {
destColsMeta, err := d.getTableColsMeta(ctx, db, destTbl, destColNames) destColsMeta, err := d.getTableColsMeta(ctx, db, destTbl, destColNames)
if err != nil { if err != nil {
return nil, err return nil, err
@ -319,12 +325,14 @@ func (d *driveri) PrepareInsertStmt(ctx context.Context, db sqlz.DB, destTbl str
return nil, err return nil, err
} }
execer := driver.NewStmtExecer(stmt, driver.DefaultInsertMungeFunc(destTbl, destColsMeta), newStmtExecFunc(stmt, db, destTbl), destColsMeta) execer := driver.NewStmtExecer(stmt, driver.DefaultInsertMungeFunc(destTbl, destColsMeta),
newStmtExecFunc(stmt, db, destTbl), destColsMeta)
return execer, nil return execer, nil
} }
// PrepareUpdateStmt implements driver.SQLDriver. // PrepareUpdateStmt implements driver.SQLDriver.
func (d *driveri) PrepareUpdateStmt(ctx context.Context, db sqlz.DB, destTbl string, destColNames []string, where string) (*driver.StmtExecer, error) { func (d *driveri) PrepareUpdateStmt(ctx context.Context, db sqlz.DB, destTbl string, destColNames []string,
where string) (*driver.StmtExecer, error) {
destColsMeta, err := d.getTableColsMeta(ctx, db, destTbl, destColNames) destColsMeta, err := d.getTableColsMeta(ctx, db, destTbl, destColNames)
if err != nil { if err != nil {
return nil, err return nil, err
@ -340,11 +348,13 @@ func (d *driveri) PrepareUpdateStmt(ctx context.Context, db sqlz.DB, destTbl str
return nil, err return nil, err
} }
execer := driver.NewStmtExecer(stmt, driver.DefaultInsertMungeFunc(destTbl, destColsMeta), newStmtExecFunc(stmt, db, destTbl), destColsMeta) execer := driver.NewStmtExecer(stmt, driver.DefaultInsertMungeFunc(destTbl, destColsMeta),
newStmtExecFunc(stmt, db, destTbl), destColsMeta)
return execer, nil return execer, nil
} }
func (d *driveri) getTableColsMeta(ctx context.Context, db sqlz.DB, tblName string, colNames []string) (sqlz.RecordMeta, error) { func (d *driveri) getTableColsMeta(ctx context.Context, db sqlz.DB, tblName string, colNames []string) (sqlz.RecordMeta,
error) {
// SQLServer has this unusual incantation for its LIMIT equivalent: // SQLServer has this unusual incantation for its LIMIT equivalent:
// //
// SELECT username, email, address_id FROM person // SELECT username, email, address_id FROM person
@ -497,8 +507,10 @@ func setIdentityInsert(ctx context.Context, db sqlz.DB, tbl string, on bool) err
} }
// mssql error codes // mssql error codes
// https://docs.microsoft.com/en-us/sql/relational-databases/errors-events/database-engine-events-and-errors?view=sql-server-ver15 //
//nolint:lll
const ( const (
// See: https://docs.microsoft.com/en-us/sql/relational-databases/errors-events/database-engine-events-and-errors?view=sql-server-ver15
errCodeIdentityInsert int32 = 544 errCodeIdentityInsert int32 = 544
errCodeObjectNotExist int32 = 15009 errCodeObjectNotExist int32 = 15009
) )

View File

@ -146,7 +146,8 @@ func (im *importer) execImport(ctx context.Context, r io.Reader, destDB driver.D
col := curRow.tbl.ColBySelector(im.selStack.selector()) col := curRow.tbl.ColBySelector(im.selStack.selector())
if col == nil { if col == nil {
if msg, ok := im.msgOncef("Skip: element %q is not a column of table %q", elem.Name.Local, curRow.tbl.Name); ok { if msg, ok := im.msgOncef("Skip: element %q is not a column of table %q", elem.Name.Local,
curRow.tbl.Name); ok {
im.log.Debug(msg) im.log.Debug(msg)
} }
continue continue
@ -301,7 +302,8 @@ func (im *importer) setForeignColsVals(row *rowState) error {
parts := strings.Split(col.Foreign, "/") parts := strings.Split(col.Foreign, "/")
// parts will look like [ "..", "channel_id" ] // parts will look like [ "..", "channel_id" ]
if len(parts) != 2 || parts[0] != ".." { if len(parts) != 2 || parts[0] != ".." {
return errz.Errorf(`%s.%s: "foreign" field should be of form "../col_name" but was %q`, row.tbl.Name, col.Name, col.Foreign) return errz.Errorf(`%s.%s: "foreign" field should be of form "../col_name" but was %q`, row.tbl.Name,
col.Name, col.Foreign)
} }
fkName := parts[1] fkName := parts[1]
@ -313,7 +315,8 @@ func (im *importer) setForeignColsVals(row *rowState) error {
fkVal, ok := parentRow.savedColVals[fkName] fkVal, ok := parentRow.savedColVals[fkName]
if !ok { if !ok {
return errz.Errorf(`%s.%s: unable to find foreign key value in parent table %q`, row.tbl.Name, col.Name, parentRow.tbl.Name) return errz.Errorf(`%s.%s: unable to find foreign key value in parent table %q`, row.tbl.Name, col.Name,
parentRow.tbl.Name)
} }
row.dirtyColVals[col.Name] = fkVal row.dirtyColVals[col.Name] = fkVal

View File

@ -21,9 +21,11 @@ import (
) )
// xlsxToScratch loads the data in xlFile into scratchDB. // xlsxToScratch loads the data in xlFile into scratchDB.
func xlsxToScratch(ctx context.Context, log lg.Log, src *source.Source, xlFile *xlsx.File, scratchDB driver.Database) error { func xlsxToScratch(ctx context.Context, log lg.Log, src *source.Source, xlFile *xlsx.File,
scratchDB driver.Database) error {
start := time.Now() start := time.Now()
log.Debugf("Beginning import from XLSX %s to %s (%s)...", src.Handle, scratchDB.Source().Handle, scratchDB.Source().RedactedLocation()) log.Debugf("Beginning import from XLSX %s to %s (%s)...", src.Handle, scratchDB.Source().Handle,
scratchDB.Source().RedactedLocation())
hasHeader, _, err := options.HasHeader(src.Options) hasHeader, _, err := options.HasHeader(src.Options)
if err != nil { if err != nil {
@ -72,7 +74,8 @@ func xlsxToScratch(ctx context.Context, log lg.Log, src *source.Source, xlFile *
// importSheetToTable imports sheet's data to its scratch table. // importSheetToTable imports sheet's data to its scratch table.
// The scratch table must already exist. // The scratch table must already exist.
func importSheetToTable(ctx context.Context, log lg.Log, sheet *xlsx.Sheet, hasHeader bool, scratchDB driver.Database, tblDef *sqlmodel.TableDef) error { func importSheetToTable(ctx context.Context, log lg.Log, sheet *xlsx.Sheet, hasHeader bool, scratchDB driver.Database,
tblDef *sqlmodel.TableDef) error {
startTime := time.Now() startTime := time.Now()
conn, err := scratchDB.DB().Conn(ctx) conn, err := scratchDB.DB().Conn(ctx)
@ -154,7 +157,8 @@ func isEmptyRow(row *xlsx.Row) bool {
// buildTblDefsForSheets returns a TableDef for each sheet. If the // buildTblDefsForSheets returns a TableDef for each sheet. If the
// sheet is empty (has no data), the TableDef for that sheet will be nil. // sheet is empty (has no data), the TableDef for that sheet will be nil.
func buildTblDefsForSheets(ctx context.Context, log lg.Log, sheets []*xlsx.Sheet, hasHeader bool) ([]*sqlmodel.TableDef, error) { func buildTblDefsForSheets(ctx context.Context, log lg.Log, sheets []*xlsx.Sheet, hasHeader bool) ([]*sqlmodel.TableDef,
error) {
tblDefs := make([]*sqlmodel.TableDef, len(sheets)) tblDefs := make([]*sqlmodel.TableDef, len(sheets))
g, _ := errgroup.WithContext(ctx) g, _ := errgroup.WithContext(ctx)
@ -341,7 +345,8 @@ func rowToRecord(log lg.Log, destColKinds []kind.Kind, row *xlsx.Row, sheetName
// it's not an int, it's not a float, it's not empty string; // it's not an int, it's not a float, it's not empty string;
// just give up and make it a string. // just give up and make it a string.
log.Warnf("Failed to determine type of numeric cell [%s:%d:%d] from value: %q", sheetName, rowIndex, j, cell.Value) log.Warnf("Failed to determine type of numeric cell [%s:%d:%d] from value: %q", sheetName, rowIndex, j,
cell.Value)
vals[j] = cell.Value vals[j] = cell.Value
// FIXME: prob should return an error here? // FIXME: prob should return an error here?
case xlsx.CellTypeString: case xlsx.CellTypeString:

View File

@ -4,9 +4,10 @@ package xlsx
import ( import (
"context" "context"
"database/sql" "database/sql"
"io"
"github.com/neilotoole/lg" "github.com/neilotoole/lg"
"github.com/tealeg/xlsx/v2" "github.com/tealeg/xlsx/v2"
"io"
"github.com/neilotoole/sq/libsq/core/cleanup" "github.com/neilotoole/sq/libsq/core/cleanup"
"github.com/neilotoole/sq/libsq/core/errz" "github.com/neilotoole/sq/libsq/core/errz"
@ -40,7 +41,8 @@ var _ source.TypeDetectFunc = DetectXLSX
// DetectXLSX implements source.TypeDetectFunc, returning // DetectXLSX implements source.TypeDetectFunc, returning
// TypeXLSX and a score of 1.0 valid XLSX. // TypeXLSX and a score of 1.0 valid XLSX.
func DetectXLSX(ctx context.Context, log lg.Log, openFn source.FileOpenFunc) (detected source.Type, score float32, err error) { func DetectXLSX(ctx context.Context, log lg.Log, openFn source.FileOpenFunc) (detected source.Type, score float32,
err error) {
var r io.ReadCloser var r io.ReadCloser
r, err = openFn() r, err = openFn()
if err != nil { if err != nil {

View File

@ -67,7 +67,8 @@ func (el *antlrErrorListener) String() string {
} }
// SyntaxError implements antlr.ErrorListener. // SyntaxError implements antlr.ErrorListener.
func (el *antlrErrorListener) SyntaxError(recognizer antlr.Recognizer, offendingSymbol any, line, column int, msg string, e antlr.RecognitionException) { func (el *antlrErrorListener) SyntaxError(recognizer antlr.Recognizer, offendingSymbol any, line, column int,
msg string, e antlr.RecognitionException) {
text := fmt.Sprintf("%s: syntax error: [%d:%d] %s", el.name, line, column, msg) text := fmt.Sprintf("%s: syntax error: [%d:%d] %s", el.name, line, column, msg)
el.errs = append(el.errs, text) el.errs = append(el.errs, text)
} }

View File

@ -96,7 +96,8 @@ func (s *Segment) uniformChildren() (bool, error) {
str = append(str, typ) str = append(str, typ)
} }
return false, fmt.Errorf("segment [%d] has more than one element node type: [%s]", s.SegIndex(), strings.Join(str, ", ")) return false, fmt.Errorf("segment [%d] has more than one element node type: [%s]", s.SegIndex(),
strings.Join(str, ", "))
} }
return true, nil return true, nil

View File

@ -168,9 +168,11 @@ func (fb *BaseFragmentBuilder) Join(fnJoin *ast.Join) (string, error) {
return "", errz.Errorf("expected *ColSelector but got %T", joinExpr.Children()[0]) return "", errz.Errorf("expected *ColSelector but got %T", joinExpr.Children()[0])
} }
leftOperand = fmt.Sprintf("%s%s%s.%s%s%s", fb.Quote, fnJoin.LeftTbl().SelValue(), fb.Quote, fb.Quote, colSel.SelValue(), fb.Quote) leftOperand = fmt.Sprintf("%s%s%s.%s%s%s", fb.Quote, fnJoin.LeftTbl().SelValue(), fb.Quote, fb.Quote,
colSel.SelValue(), fb.Quote)
operator = "==" operator = "=="
rightOperand = fmt.Sprintf("%s%s%s.%s%s%s", fb.Quote, fnJoin.RightTbl().SelValue(), fb.Quote, fb.Quote, colSel.SelValue(), fb.Quote) rightOperand = fmt.Sprintf("%s%s%s.%s%s%s", fb.Quote, fnJoin.RightTbl().SelValue(), fb.Quote, fb.Quote,
colSel.SelValue(), fb.Quote)
} else { } else {
var err error var err error
@ -194,7 +196,8 @@ func (fb *BaseFragmentBuilder) Join(fnJoin *ast.Join) (string, error) {
onClause = fmt.Sprintf("ON %s %s %s", leftOperand, operator, rightOperand) onClause = fmt.Sprintf("ON %s %s %s", leftOperand, operator, rightOperand)
} }
sql := fmt.Sprintf("FROM %s%s%s %s %s%s%s", fb.Quote, fnJoin.LeftTbl().SelValue(), fb.Quote, joinType, fb.Quote, fnJoin.RightTbl().SelValue(), fb.Quote) sql := fmt.Sprintf("FROM %s%s%s %s %s%s%s", fb.Quote, fnJoin.LeftTbl().SelValue(), fb.Quote, joinType, fb.Quote,
fnJoin.RightTbl().SelValue(), fb.Quote)
sql = sqlAppend(sql, onClause) sql = sqlAppend(sql, onClause)
return sql, nil return sql, nil

View File

@ -222,11 +222,13 @@ func determineJoinTables(log lg.Log, w *Walker, node Node) error {
fnJoin.leftTbl, ok = prevSeg.Children()[0].(*TblSelector) fnJoin.leftTbl, ok = prevSeg.Children()[0].(*TblSelector)
if !ok { if !ok {
return errorf("JOIN() expected table selector in previous segment, but was %T(%q)", prevSeg.Children()[0], prevSeg.Children()[0].Text()) return errorf("JOIN() expected table selector in previous segment, but was %T(%q)", prevSeg.Children()[0],
prevSeg.Children()[0].Text())
} }
fnJoin.rightTbl, ok = prevSeg.Children()[1].(*TblSelector) fnJoin.rightTbl, ok = prevSeg.Children()[1].(*TblSelector)
if !ok { if !ok {
return errorf("JOIN() expected table selector in previous segment, but was %T(%q)", prevSeg.Children()[1], prevSeg.Children()[1].Text()) return errorf("JOIN() expected table selector in previous segment, but was %T(%q)", prevSeg.Children()[1],
prevSeg.Children()[1].Text())
} }
return nil return nil
} }

View File

@ -67,7 +67,8 @@ func DBWriterCreateTableIfNotExistsHook(destTblName string) DBWriterPreWriteHook
// The writer writes records from recordCh to destTbl // The writer writes records from recordCh to destTbl
// in destDB. The recChSize param controls the size of recordCh // in destDB. The recChSize param controls the size of recordCh
// returned by the writer's Open method. // returned by the writer's Open method.
func NewDBWriter(log lg.Log, destDB driver.Database, destTbl string, recChSize int, preWriteHooks ...DBWriterPreWriteHook) *DBWriter { func NewDBWriter(log lg.Log, destDB driver.Database, destTbl string, recChSize int,
preWriteHooks ...DBWriterPreWriteHook) *DBWriter {
return &DBWriter{ return &DBWriter{
log: log, log: log,
destDB: destDB, destDB: destDB,
@ -85,7 +86,8 @@ func NewDBWriter(log lg.Log, destDB driver.Database, destTbl string, recChSize i
} }
// Open implements RecordWriter. // Open implements RecordWriter.
func (w *DBWriter) Open(ctx context.Context, cancelFn context.CancelFunc, recMeta sqlz.RecordMeta) (chan<- sqlz.Record, <-chan error, error) { func (w *DBWriter) Open(ctx context.Context, cancelFn context.CancelFunc, recMeta sqlz.RecordMeta) (chan<- sqlz.Record,
<-chan error, error) {
w.cancelFn = cancelFn w.cancelFn = cancelFn
// REVISIT: tx could potentially be passed to NewDBWriter? // REVISIT: tx could potentially be passed to NewDBWriter?

View File

@ -112,7 +112,8 @@ type SQLDriver interface {
// //
// Note that db must guarantee a single connection: that is, db // Note that db must guarantee a single connection: that is, db
// must be a sql.Conn or sql.Tx. // must be a sql.Conn or sql.Tx.
PrepareInsertStmt(ctx context.Context, db sqlz.DB, destTbl string, destColNames []string, numRows int) (*StmtExecer, error) PrepareInsertStmt(ctx context.Context, db sqlz.DB, destTbl string, destColNames []string, numRows int) (*StmtExecer,
error)
// PrepareUpdateStmt prepares a statement for updating destColNames in // PrepareUpdateStmt prepares a statement for updating destColNames in
// destTbl, using the supplied where clause (which may be empty). // destTbl, using the supplied where clause (which may be empty).
@ -127,7 +128,8 @@ type SQLDriver interface {
// //
// Note that db must guarantee a single connection: that is, db // Note that db must guarantee a single connection: that is, db
// must be a sql.Conn or sql.Tx. // must be a sql.Conn or sql.Tx.
PrepareUpdateStmt(ctx context.Context, db sqlz.DB, destTbl string, destColNames []string, where string) (*StmtExecer, error) PrepareUpdateStmt(ctx context.Context, db sqlz.DB, destTbl string, destColNames []string,
where string) (*StmtExecer, error)
// CreateTable creates the table defined by tblDef. Some implementations // CreateTable creates the table defined by tblDef. Some implementations
// may not honor all of the fields of tblDef, e.g. an impl might not // may not honor all of the fields of tblDef, e.g. an impl might not

View File

@ -326,7 +326,8 @@ const Comma = ", "
// driver-specific syntax from drvr. numRows specifies // driver-specific syntax from drvr. numRows specifies
// how many rows of values are inserted by each execution of // how many rows of values are inserted by each execution of
// the insert statement (1 row being the prototypical usage). // the insert statement (1 row being the prototypical usage).
func PrepareInsertStmt(ctx context.Context, drvr SQLDriver, db sqlz.Preparer, destTbl string, destCols []string, numRows int) (stmt *sql.Stmt, err error) { func PrepareInsertStmt(ctx context.Context, drvr SQLDriver, db sqlz.Preparer, destTbl string, destCols []string,
numRows int) (stmt *sql.Stmt, err error) {
const stmtTpl = `INSERT INTO %s (%s) VALUES %s` const stmtTpl = `INSERT INTO %s (%s) VALUES %s`
if numRows <= 0 { if numRows <= 0 {
@ -385,7 +386,8 @@ func (bi BatchInsert) Munge(rec []any) error {
// //
// Note that the db arg must guarantee a single connection: that is, // Note that the db arg must guarantee a single connection: that is,
// it must be a sql.Conn or sql.Tx. // it must be a sql.Conn or sql.Tx.
func NewBatchInsert(ctx context.Context, log lg.Log, drvr SQLDriver, db sqlz.DB, destTbl string, destColNames []string, batchSize int) (*BatchInsert, error) { func NewBatchInsert(ctx context.Context, log lg.Log, drvr SQLDriver, db sqlz.DB, destTbl string, destColNames []string,
batchSize int) (*BatchInsert, error) {
err := requireSingleConn(db) err := requireSingleConn(db)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -34,7 +34,8 @@ func (r *Registry) AddProvider(typ source.Type, p Provider) {
defer r.mu.Unlock() defer r.mu.Unlock()
if existingType, ok := r.providers[typ]; ok { if existingType, ok := r.providers[typ]; ok {
r.log.Warnf("failed to add driver provider (%T) for driver type %s: provider (%T) already registered", p, typ, existingType) r.log.Warnf("failed to add driver provider (%T) for driver type %s: provider (%T) already registered", p, typ,
existingType)
return return
} }

View File

@ -173,7 +173,8 @@ func (ng *engine) buildJoinFromClause(ctx context.Context, fnJoin *ast.Join) (fr
return ng.singleSourceJoin(ctx, fnJoin) return ng.singleSourceJoin(ctx, fnJoin)
} }
func (ng *engine) singleSourceJoin(ctx context.Context, fnJoin *ast.Join) (fromClause string, fromDB driver.Database, err error) { func (ng *engine) singleSourceJoin(ctx context.Context, fnJoin *ast.Join) (fromClause string, fromDB driver.Database,
err error) {
src, err := ng.srcs.Get(fnJoin.LeftTbl().DSName) src, err := ng.srcs.Get(fnJoin.LeftTbl().DSName)
if err != nil { if err != nil {
return "", nil, err return "", nil, err
@ -195,10 +196,12 @@ func (ng *engine) singleSourceJoin(ctx context.Context, fnJoin *ast.Join) (fromC
// crossSourceJoin returns a FROM clause that forms part of // crossSourceJoin returns a FROM clause that forms part of
// the SQL SELECT statement against fromDB. // the SQL SELECT statement against fromDB.
func (ng *engine) crossSourceJoin(ctx context.Context, fnJoin *ast.Join) (fromClause string, fromDB driver.Database, err error) { func (ng *engine) crossSourceJoin(ctx context.Context, fnJoin *ast.Join) (fromClause string, fromDB driver.Database,
err error) {
leftTblName, rightTblName := fnJoin.LeftTbl().SelValue(), fnJoin.RightTbl().SelValue() leftTblName, rightTblName := fnJoin.LeftTbl().SelValue(), fnJoin.RightTbl().SelValue()
if leftTblName == rightTblName { if leftTblName == rightTblName {
return "", nil, errz.Errorf("JOIN tables must have distinct names (or use aliases): duplicate tbl name %q", fnJoin.LeftTbl().SelValue()) return "", nil, errz.Errorf("JOIN tables must have distinct names (or use aliases): duplicate tbl name %q",
fnJoin.LeftTbl().SelValue())
} }
leftSrc, err := ng.srcs.Get(fnJoin.LeftTbl().DSName) leftSrc, err := ng.srcs.Get(fnJoin.LeftTbl().DSName)
@ -273,8 +276,10 @@ func (jt *joinCopyTask) executeTask(ctx context.Context, log lg.Log) error {
} }
// execCopyTable performs the work of copying fromDB.fromTblName to destDB.destTblName. // execCopyTable performs the work of copying fromDB.fromTblName to destDB.destTblName.
func execCopyTable(ctx context.Context, log lg.Log, fromDB driver.Database, fromTblName string, destDB driver.Database, destTblName string) error { func execCopyTable(ctx context.Context, log lg.Log, fromDB driver.Database, fromTblName string, destDB driver.Database,
createTblHook := func(ctx context.Context, originRecMeta sqlz.RecordMeta, destDB driver.Database, tx sqlz.DB) error { destTblName string) error {
createTblHook := func(ctx context.Context, originRecMeta sqlz.RecordMeta, destDB driver.Database,
tx sqlz.DB) error {
destColNames := originRecMeta.Names() destColNames := originRecMeta.Names()
destColKinds := originRecMeta.Kinds() destColKinds := originRecMeta.Kinds()
destTblDef := sqlmodel.NewTableDef(destTblName, destColNames, destColKinds) destTblDef := sqlmodel.NewTableDef(destTblName, destColNames, destColKinds)
@ -329,13 +334,15 @@ func buildQueryModel(log lg.Log, a *ast.AST) (*queryModel, error) {
} }
if len(selectableSeg.Children()) != 1 { if len(selectableSeg.Children()) != 1 {
return nil, errz.Errorf("the final selectable segment must have exactly one selectable element, but found %d elements", return nil, errz.Errorf(
"the final selectable segment must have exactly one selectable element, but found %d elements",
len(selectableSeg.Children())) len(selectableSeg.Children()))
} }
selectable, ok := selectableSeg.Children()[0].(ast.Selectable) selectable, ok := selectableSeg.Children()[0].(ast.Selectable)
if !ok { if !ok {
return nil, errz.Errorf("the final selectable segment must have exactly one selectable element, but found element %T(%q)", return nil, errz.Errorf(
"the final selectable segment must have exactly one selectable element, but found element %T(%q)",
selectableSeg.Children()[0], selectableSeg.Children()[0].Text()) selectableSeg.Children()[0], selectableSeg.Children()[0].Text())
} }
@ -346,12 +353,14 @@ func buildQueryModel(log lg.Log, a *ast.AST) (*queryModel, error) {
// Check if the first element of the segment is a row range, if not, just skip // Check if the first element of the segment is a row range, if not, just skip
if rr, ok := seg.Children()[0].(*ast.RowRange); ok { if rr, ok := seg.Children()[0].(*ast.RowRange); ok {
if len(seg.Children()) != 1 { if len(seg.Children()) != 1 {
return nil, errz.Errorf("segment [%d] with row range must have exactly one element, but found %d: %q", return nil, errz.Errorf(
"segment [%d] with row range must have exactly one element, but found %d: %q",
seg.SegIndex(), len(seg.Children()), seg.Text()) seg.SegIndex(), len(seg.Children()), seg.Text())
} }
if qm.Range != nil { if qm.Range != nil {
return nil, errz.Errorf("only one row range permitted, but found %q and %q", qm.Range.Text(), rr.Text()) return nil, errz.Errorf("only one row range permitted, but found %q and %q",
qm.Range.Text(), rr.Text())
} }
log.Debugf("found row range: %q", rr.Text()) log.Debugf("found row range: %q", rr.Text())

View File

@ -62,7 +62,8 @@ type RecordWriter interface {
// construction. This mechanism exists to enable a goroutine to wait // construction. This mechanism exists to enable a goroutine to wait
// on the writer outside of the function that invoked Open, without // on the writer outside of the function that invoked Open, without
// having to pass cancelFn around. // having to pass cancelFn around.
Open(ctx context.Context, cancelFn context.CancelFunc, recMeta sqlz.RecordMeta) (recCh chan<- sqlz.Record, errCh <-chan error, err error) Open(ctx context.Context, cancelFn context.CancelFunc, recMeta sqlz.RecordMeta) (recCh chan<- sqlz.Record,
errCh <-chan error, err error)
// Wait waits for the writer to complete and returns the number of // Wait waits for the writer to complete and returns the number of
// written rows and any error (which may be a multierr). // written rows and any error (which may be a multierr).
@ -73,7 +74,8 @@ type RecordWriter interface {
// ExecuteSLQ executes the slq query, writing the results to recw. // ExecuteSLQ executes the slq query, writing the results to recw.
// The caller is responsible for closing dbases. // The caller is responsible for closing dbases.
func ExecuteSLQ(ctx context.Context, log lg.Log, dbOpener driver.DatabaseOpener, joinDBOpener driver.JoinDatabaseOpener, srcs *source.Set, query string, recw RecordWriter) error { func ExecuteSLQ(ctx context.Context, log lg.Log, dbOpener driver.DatabaseOpener, joinDBOpener driver.JoinDatabaseOpener,
srcs *source.Set, query string, recw RecordWriter) error {
ng, err := newEngine(ctx, log, dbOpener, joinDBOpener, srcs, query) ng, err := newEngine(ctx, log, dbOpener, joinDBOpener, srcs, query)
if err != nil { if err != nil {
return err return err
@ -82,7 +84,8 @@ func ExecuteSLQ(ctx context.Context, log lg.Log, dbOpener driver.DatabaseOpener,
return ng.execute(ctx, recw) return ng.execute(ctx, recw)
} }
func newEngine(ctx context.Context, log lg.Log, dbOpener driver.DatabaseOpener, joinDBOpener driver.JoinDatabaseOpener, srcs *source.Set, query string) (*engine, error) { func newEngine(ctx context.Context, log lg.Log, dbOpener driver.DatabaseOpener, joinDBOpener driver.JoinDatabaseOpener,
srcs *source.Set, query string) (*engine, error) {
a, err := ast.Parse(log, query) a, err := ast.Parse(log, query)
if err != nil { if err != nil {
return nil, err return nil, err
@ -113,7 +116,8 @@ func newEngine(ctx context.Context, log lg.Log, dbOpener driver.DatabaseOpener,
// before recw has finished writing, thus the caller may wish // before recw has finished writing, thus the caller may wish
// to wait for recw to complete. // to wait for recw to complete.
// The caller is responsible for closing dbase. // The caller is responsible for closing dbase.
func QuerySQL(ctx context.Context, log lg.Log, dbase driver.Database, recw RecordWriter, query string, args ...any) error { func QuerySQL(ctx context.Context, log lg.Log, dbase driver.Database, recw RecordWriter, query string,
args ...any) error {
rows, err := dbase.DB().QueryContext(ctx, query, args...) rows, err := dbase.DB().QueryContext(ctx, query, args...)
if err != nil { if err != nil {
return errz.Wrapf(err, `SQL query against %s failed: %s`, dbase.Source().Handle, query) return errz.Wrapf(err, `SQL query against %s failed: %s`, dbase.Source().Handle, query)

View File

@ -48,14 +48,16 @@ type Notifier interface {
// Provider is a factory that returns Notifier instances and generates notification Destinations from user parameters. // Provider is a factory that returns Notifier instances and generates notification Destinations from user parameters.
type Provider interface { type Provider interface {
// Destination returns a notification Destination instance from the supplied parameters. // Destination returns a notification Destination instance from the supplied parameters.
Destination(typ DestType, target string, label string, credentials string, labelAvailable func(label string) bool) (*Destination, error) Destination(typ DestType, target string, label string, credentials string,
labelAvailable func(label string) bool) (*Destination, error)
// Notifier returns a Notifier instance for the given destination. // Notifier returns a Notifier instance for the given destination.
Notifier(dest Destination) (Notifier, error) Notifier(dest Destination) (Notifier, error)
} }
var providers = make(map[DestType]Provider) var providers = make(map[DestType]Provider)
// RegisterProvider should be invoked by notification implementations to indicate that they handle a specific destination type. // RegisterProvider should be invoked by notification implementations to
// indicate that they handle a specific destination type.
func RegisterProvider(typ DestType, p Provider) { func RegisterProvider(typ DestType, p Provider) {
providers[typ] = p providers[typ] = p
} }
@ -135,8 +137,10 @@ var handlePattern = regexp.MustCompile(`\A[a-zA-Z][a-zA-Z0-9_]*$`)
// ValidHandle returns an error if handle is not an acceptable notification destination handle value. // ValidHandle returns an error if handle is not an acceptable notification destination handle value.
func ValidHandle(handle string) error { func ValidHandle(handle string) error {
const msg = `invalid notification destination handle value %q: must begin with a letter, followed by zero or more letters, digits, or underscores, e.g. "slack_devops"` //nolint:lll
if !handlePattern.MatchString(handle) { if !handlePattern.MatchString(handle) {
return errz.Errorf(`invalid notification destination handle value %q: must begin with a letter, followed by zero or more letters, digits, or underscores, e.g. "slack_devops"`, handle) return errz.Errorf(msg, handle)
} }
return nil return nil

View File

@ -22,9 +22,10 @@ var (
// //
// \A@[a-zA-Z][a-zA-Z0-9_]*$ // \A@[a-zA-Z][a-zA-Z0-9_]*$
func VerifyLegalHandle(handle string) error { func VerifyLegalHandle(handle string) error {
const msg = `invalid data source handle %q: must begin with @, followed by a letter, followed by zero or more letters, digits, or underscores, e.g. "@my_db1"` //nolint:lll
matches := handlePattern.MatchString(handle) matches := handlePattern.MatchString(handle)
if !matches { if !matches {
return errz.Errorf(`invalid data source handle %q: must begin with @, followed by a letter, followed by zero or more letters, digits, or underscores, e.g. "@my_db1"`, handle) return errz.Errorf(msg, handle)
} }
return nil return nil
@ -35,9 +36,11 @@ func VerifyLegalHandle(handle string) error {
// //
// \A[a-zA-Z_][a-zA-Z0-9_]*$` // \A[a-zA-Z_][a-zA-Z0-9_]*$`
func verifyLegalTableName(table string) error { func verifyLegalTableName(table string) error {
const msg = `invalid table name %q: must begin a letter or underscore, followed by zero or more letters, digits, or underscores, e.g. "tbl1" or "_tbl2"` //nolint:lll
matches := tablePattern.MatchString(table) matches := tablePattern.MatchString(table)
if !matches { if !matches {
return errz.Errorf(`invalid table name %q: must begin a letter or underscore, followed by zero or more letters, digits, or underscores, e.g. "tbl1" or "_tbl2"`, table) return errz.Errorf(msg, table)
} }
return nil return nil
} }

View File

@ -63,8 +63,10 @@ func mustParseTime(layout, value string) time.Time {
// or withUnknown are set). If isIntBool is // or withUnknown are set). If isIntBool is
// true, kind.Int is returned for "col_bool", otherwise kind.Bool. // true, kind.Int is returned for "col_bool", otherwise kind.Bool.
func ColNamePerKind(isIntBool bool, withNull bool, withUnknown bool) (colNames []string, kinds []kind.Kind) { func ColNamePerKind(isIntBool bool, withNull bool, withUnknown bool) (colNames []string, kinds []kind.Kind) {
colNames = []string{"col_int", "col_float", "col_decimal", "col_bool", "col_text", "col_datetime", "col_date", "col_time", "col_bytes"} colNames = []string{"col_int", "col_float", "col_decimal", "col_bool", "col_text", "col_datetime", "col_date",
kinds = []kind.Kind{kind.Int, kind.Float, kind.Decimal, kind.Bool, kind.Text, kind.Datetime, kind.Date, kind.Time, kind.Bytes} "col_time", "col_bytes"}
kinds = []kind.Kind{kind.Int, kind.Float, kind.Decimal, kind.Bool, kind.Text, kind.Datetime, kind.Date, kind.Time,
kind.Bytes}
if isIntBool { if isIntBool {
kinds[3] = kind.Int kinds[3] = kind.Int

View File

@ -118,19 +118,23 @@ func TblPaymentColKinds() []kind.Kind {
// AllTbls returns all table names. // AllTbls returns all table names.
func AllTbls() []string { func AllTbls() []string {
return []string{"actor", "address", "category", "city", "country", "customer", "film", "film_actor", "film_category", "film_text", "inventory", "language", "payment", "rental", "staff", "store"} return []string{"actor", "address", "category", "city", "country", "customer", "film", "film_actor",
"film_category", "film_text", "inventory", "language", "payment", "rental", "staff", "store"}
} }
// AllTblsViews returns all table AND view names. // AllTblsViews returns all table AND view names.
func AllTblsViews() []string { func AllTblsViews() []string {
return []string{"actor", "address", "category", "city", "country", "customer", "customer_list", "film", "film_actor", "film_category", "film_list", "film_text", "inventory", "language", "payment", "rental", "sales_by_film_category", "sales_by_store", "staff", "staff_list", "store"} return []string{"actor", "address", "category", "city", "country", "customer", "customer_list", "film",
"film_actor", "film_category", "film_list", "film_text", "inventory", "language", "payment", "rental",
"sales_by_film_category", "sales_by_store", "staff", "staff_list", "store"}
} }
// AllTblsExceptFilmText exists because our current postgres image is different // AllTblsExceptFilmText exists because our current postgres image is different
// from the others in that it doesn't have the film_text table. // from the others in that it doesn't have the film_text table.
func AllTblsExceptFilmText() []string { func AllTblsExceptFilmText() []string {
// TODO: delete AllTblsExceptFilmText when postgres image is updated to include film_text. // TODO: delete AllTblsExceptFilmText when postgres image is updated to include film_text.
return []string{"actor", "address", "category", "city", "country", "customer", "film", "film_actor", "film_category", "inventory", "language", "payment", "rental", "staff", "store"} return []string{"actor", "address", "category", "city", "country", "customer", "film", "film_actor",
"film_category", "inventory", "language", "payment", "rental", "staff", "store"}
} }
// URLs for sakila resources. // URLs for sakila resources.

View File

@ -288,7 +288,9 @@ func (h *Helper) SQLDriverFor(src *source.Source) driver.SQLDriver {
drvr, err := reg.DriverFor(src.Type) drvr, err := reg.DriverFor(src.Type)
require.NoError(h.T, err) require.NoError(h.T, err)
sqlDrvr, ok := drvr.(driver.SQLDriver) sqlDrvr, ok := drvr.(driver.SQLDriver)
require.True(h.T, ok, "driver %T is not a driver.SQLDriver: ensure that the src passed to SQLDriverFor implements driver.SQLDriver", drvr) require.True(h.T, ok,
"driver %T is not a driver.SQLDriver: ensure that the src passed to SQLDriverFor implements driver.SQLDriver",
drvr)
return sqlDrvr return sqlDrvr
} }
@ -315,7 +317,8 @@ func (h *Helper) RowCount(src *source.Source, tbl string) int64 {
// CreateTable creates a new table in src, and inserts data, returning // CreateTable creates a new table in src, and inserts data, returning
// the number of data rows inserted. If dropAfter is true, the created // the number of data rows inserted. If dropAfter is true, the created
// table is dropped when t.Cleanup is run. // table is dropped when t.Cleanup is run.
func (h *Helper) CreateTable(dropAfter bool, src *source.Source, tblDef *sqlmodel.TableDef, data ...[]any) (affected int64) { func (h *Helper) CreateTable(dropAfter bool, src *source.Source, tblDef *sqlmodel.TableDef,
data ...[]any) (affected int64) {
dbase := h.openNew(src) dbase := h.openNew(src)
defer h.Log.WarnIfCloseError(dbase) defer h.Log.WarnIfCloseError(dbase)
@ -590,7 +593,8 @@ func (h *Helper) DiffDB(src *source.Source) {
for i, beforeTbl := range beforeMeta.Tables { for i, beforeTbl := range beforeMeta.Tables {
assert.Equal(h.T, beforeTbl.RowCount, afterMeta.Tables[i].RowCount, assert.Equal(h.T, beforeTbl.RowCount, afterMeta.Tables[i].RowCount,
"diffdb: %s: row count for %q is expected to be %d but got %d", src.Handle, beforeTbl.Name, beforeTbl.RowCount, afterMeta.Tables[i].RowCount) "diffdb: %s: row count for %q is expected to be %d but got %d", src.Handle, beforeTbl.Name,
beforeTbl.RowCount, afterMeta.Tables[i].RowCount)
} }
}) })
} }