2024-01-15 04:45:34 +03:00
|
|
|
package sqlserver
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"database/sql"
|
|
|
|
"log/slog"
|
|
|
|
|
|
|
|
"github.com/neilotoole/sq/libsq/core/lg/lga"
|
|
|
|
"github.com/neilotoole/sq/libsq/core/lg/lgm"
|
|
|
|
"github.com/neilotoole/sq/libsq/core/progress"
|
|
|
|
"github.com/neilotoole/sq/libsq/driver"
|
|
|
|
"github.com/neilotoole/sq/libsq/source"
|
|
|
|
"github.com/neilotoole/sq/libsq/source/metadata"
|
|
|
|
)
|
|
|
|
|
|
|
|
// grip implements driver.Grip.
|
|
|
|
type grip struct {
|
|
|
|
log *slog.Logger
|
|
|
|
drvr *driveri
|
|
|
|
db *sql.DB
|
|
|
|
src *source.Source
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ driver.Grip = (*grip)(nil)
|
|
|
|
|
|
|
|
// DB implements driver.Grip.
|
|
|
|
func (g *grip) DB(context.Context) (*sql.DB, error) {
|
|
|
|
return g.db, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// SQLDriver implements driver.Grip.
|
|
|
|
func (g *grip) SQLDriver() driver.SQLDriver {
|
|
|
|
return g.drvr
|
|
|
|
}
|
|
|
|
|
|
|
|
// Source implements driver.Grip.
|
|
|
|
func (g *grip) Source() *source.Source {
|
|
|
|
return g.src
|
|
|
|
}
|
|
|
|
|
|
|
|
// TableMetadata implements driver.Grip.
|
|
|
|
func (g *grip) TableMetadata(ctx context.Context, tblName string) (*metadata.Table, error) {
|
|
|
|
bar := progress.FromContext(ctx).NewUnitCounter(g.Source().Handle+"."+tblName+": read schema", "item")
|
|
|
|
defer bar.Stop()
|
|
|
|
ctx = progress.NewBarContext(ctx, bar)
|
|
|
|
|
|
|
|
const query = `SELECT TABLE_CATALOG, TABLE_SCHEMA, TABLE_TYPE
|
|
|
|
FROM INFORMATION_SCHEMA.TABLES
|
|
|
|
WHERE TABLE_NAME = @p1`
|
|
|
|
|
|
|
|
var catalog, schema, tblType string
|
|
|
|
err := g.db.QueryRowContext(ctx, query, tblName).Scan(&catalog, &schema, &tblType)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errw(err)
|
|
|
|
}
|
|
|
|
progress.Incr(ctx, 1)
|
2024-01-15 06:56:54 +03:00
|
|
|
progress.DebugSleep(ctx)
|
2024-01-15 04:45:34 +03:00
|
|
|
|
|
|
|
// TODO: getTableMetadata can cause deadlock in the DB. Needs further investigation.
|
|
|
|
// But a quick hack would be to use retry on a deadlock error.
|
|
|
|
return getTableMetadata(ctx, g.db, catalog, schema, tblName, tblType)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SourceMetadata implements driver.Grip.
|
|
|
|
func (g *grip) SourceMetadata(ctx context.Context, noSchema bool) (*metadata.Source, error) {
|
|
|
|
bar := progress.FromContext(ctx).NewUnitCounter(g.Source().Handle+": read schema", "item")
|
|
|
|
defer bar.Stop()
|
|
|
|
ctx = progress.NewBarContext(ctx, bar)
|
|
|
|
|
|
|
|
return getSourceMetadata(ctx, g.src, g.db, noSchema)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Close implements driver.Grip.
|
|
|
|
func (g *grip) Close() error {
|
|
|
|
g.log.Debug(lgm.CloseDB, lga.Handle, g.src.Handle)
|
|
|
|
|
|
|
|
return errw(g.db.Close())
|
|
|
|
}
|