2023-05-19 17:24:18 +03:00
|
|
|
package diff
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"golang.org/x/sync/errgroup"
|
|
|
|
|
|
|
|
"github.com/neilotoole/sq/cli/run"
|
|
|
|
"github.com/neilotoole/sq/libsq/core/errz"
|
2024-01-15 04:45:34 +03:00
|
|
|
"github.com/neilotoole/sq/libsq/driver"
|
2023-05-19 17:24:18 +03:00
|
|
|
"github.com/neilotoole/sq/libsq/source"
|
2023-11-21 00:42:38 +03:00
|
|
|
"github.com/neilotoole/sq/libsq/source/metadata"
|
2023-05-19 17:24:18 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
// ExecTableDiff diffs handle1.table1 and handle2.table2.
|
2024-01-15 04:45:34 +03:00
|
|
|
func ExecTableDiff(ctx context.Context, ru *run.Run, cfg *Config, elems *Elements, //nolint:revive
|
2023-05-22 18:08:14 +03:00
|
|
|
handle1, table1, handle2, table2 string,
|
|
|
|
) error {
|
2023-05-19 17:24:18 +03:00
|
|
|
td1, td2 := &tableData{tblName: table1}, &tableData{tblName: table2}
|
|
|
|
|
2023-05-22 18:08:14 +03:00
|
|
|
var err error
|
|
|
|
td1.src, err = ru.Config.Collection.Get(handle1)
|
|
|
|
if err != nil {
|
2023-05-19 17:24:18 +03:00
|
|
|
return err
|
2023-05-22 18:08:14 +03:00
|
|
|
}
|
|
|
|
td2.src, err = ru.Config.Collection.Get(handle2)
|
|
|
|
if err != nil {
|
2023-05-19 17:24:18 +03:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-05-25 02:36:10 +03:00
|
|
|
if elems.Schema {
|
2023-05-22 18:08:14 +03:00
|
|
|
g, gCtx := errgroup.WithContext(ctx)
|
|
|
|
g.Go(func() error {
|
|
|
|
var gErr error
|
|
|
|
td1.tblMeta, gErr = fetchTableMeta(gCtx, ru, td1.src, table1)
|
|
|
|
return gErr
|
|
|
|
})
|
|
|
|
g.Go(func() error {
|
|
|
|
var gErr error
|
|
|
|
td2.tblMeta, gErr = fetchTableMeta(gCtx, ru, td2.src, table2)
|
|
|
|
return gErr
|
|
|
|
})
|
|
|
|
if err = g.Wait(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var tblDiff *tableDiff
|
2024-01-15 04:45:34 +03:00
|
|
|
tblDiff, err = buildTableStructureDiff(ctx, cfg, elems.RowCount, td1, td2)
|
2023-05-22 18:08:14 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2024-01-15 04:45:34 +03:00
|
|
|
if err = Print(ctx, ru.Out, ru.Writers.Printing, tblDiff.header, tblDiff.diff); err != nil {
|
2023-05-22 18:08:14 +03:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if !elems.Data {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
tblDataDiff, err := buildTableDataDiff(ctx, ru, cfg, td1, td2)
|
2023-05-19 17:24:18 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-05-22 18:08:14 +03:00
|
|
|
if tblDataDiff == nil {
|
|
|
|
return nil
|
2023-05-19 17:24:18 +03:00
|
|
|
}
|
|
|
|
|
2024-01-15 04:45:34 +03:00
|
|
|
return Print(ctx, ru.Out, ru.Writers.Printing, tblDataDiff.header, tblDataDiff.diff)
|
2023-05-19 17:24:18 +03:00
|
|
|
}
|
|
|
|
|
2024-01-15 04:45:34 +03:00
|
|
|
func buildTableStructureDiff(ctx context.Context, cfg *Config, showRowCounts bool,
|
|
|
|
td1, td2 *tableData,
|
|
|
|
) (*tableDiff, error) {
|
2023-05-19 17:24:18 +03:00
|
|
|
var (
|
|
|
|
body1, body2 string
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
|
2023-05-22 18:08:14 +03:00
|
|
|
if body1, err = renderTableMeta2YAML(showRowCounts, td1.tblMeta); err != nil {
|
2023-05-19 17:24:18 +03:00
|
|
|
return nil, err
|
|
|
|
}
|
2023-05-22 18:08:14 +03:00
|
|
|
if body2, err = renderTableMeta2YAML(showRowCounts, td2.tblMeta); err != nil {
|
2023-05-19 17:24:18 +03:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2024-01-15 04:45:34 +03:00
|
|
|
handle1 := td1.src.Handle + "." + td1.tblName
|
|
|
|
handle2 := td2.src.Handle + "." + td2.tblName
|
|
|
|
|
|
|
|
msg := fmt.Sprintf("table schema {%s}", td1.tblName)
|
|
|
|
unified, err := computeUnified(ctx, msg, handle1, handle2, cfg.Lines, body1, body2)
|
2023-05-19 17:24:18 +03:00
|
|
|
if err != nil {
|
2024-01-15 04:45:34 +03:00
|
|
|
return nil, err
|
2023-05-19 17:24:18 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
tblDiff := &tableDiff{
|
|
|
|
td1: td1,
|
|
|
|
td2: td2,
|
|
|
|
header: fmt.Sprintf("sq diff %s.%s %s.%s",
|
|
|
|
td1.src.Handle, td1.tblName, td2.src.Handle, td2.tblName),
|
|
|
|
diff: unified,
|
|
|
|
}
|
|
|
|
|
|
|
|
return tblDiff, nil
|
|
|
|
}
|
|
|
|
|
2023-11-21 00:42:38 +03:00
|
|
|
// fetchTableMeta returns the metadata.Table for table. If the table
|
2023-05-22 18:08:14 +03:00
|
|
|
// does not exist, {nil,nil} is returned.
|
|
|
|
func fetchTableMeta(ctx context.Context, ru *run.Run, src *source.Source, table string) (
|
2023-11-21 00:42:38 +03:00
|
|
|
*metadata.Table, error,
|
2023-05-19 17:24:18 +03:00
|
|
|
) {
|
2024-01-15 04:45:34 +03:00
|
|
|
grip, err := ru.Grips.Open(ctx, src)
|
2023-05-19 17:24:18 +03:00
|
|
|
if err != nil {
|
2023-05-22 18:08:14 +03:00
|
|
|
return nil, err
|
2023-05-19 17:24:18 +03:00
|
|
|
}
|
2024-01-15 04:45:34 +03:00
|
|
|
md, err := grip.TableMetadata(ctx, table)
|
2023-05-19 17:24:18 +03:00
|
|
|
if err != nil {
|
2024-01-15 04:45:34 +03:00
|
|
|
if errz.Has[*driver.NotExistError](err) {
|
2023-05-22 18:08:14 +03:00
|
|
|
return nil, nil //nolint:nilnil
|
|
|
|
}
|
|
|
|
return nil, err
|
2023-05-19 17:24:18 +03:00
|
|
|
}
|
|
|
|
|
2023-05-22 18:08:14 +03:00
|
|
|
return md, nil
|
2023-05-19 17:24:18 +03:00
|
|
|
}
|