diff --git a/internal/app/db-wrapper/mssql.go b/internal/app/db-wrapper/mssql.go index 19ab9a9..b9c86a5 100644 --- a/internal/app/db-wrapper/mssql.go +++ b/internal/app/db-wrapper/mssql.go @@ -4,6 +4,7 @@ import ( "context" "database/sql" "fmt" + "strings" dbdialects "git.ksdemosapps.com/kylesoda/go-migrate/internal/app/db-wrapper/db-dialects" mssql "github.com/microsoft/go-mssqldb" @@ -175,3 +176,72 @@ func (mw *mssqlDbWrapper) SaveMassive(ctx context.Context, schema string, table return rowsAffected, nil } + +func (mw *mssqlDbWrapper) QueryFromObject(ctx context.Context, q ExtractionQuery) (RowsResult, error) { + var sbQuery strings.Builder + + sbQuery.WriteString("SELECT ") + + if len(q.columns) == 0 { + sbQuery.WriteString("*") + } else { + for i, col := range q.columns { + fmt.Fprintf(&sbQuery, "[%s]", col.Name()) + + switch col.Type() { + case "GEOMETRY": + fmt.Fprintf(&sbQuery, ".STAsBinary() AS [%s]", col.Name()) + } + + if i < len(q.columns)-1 { + sbQuery.WriteString(", ") + } + } + } + + fmt.Fprintf(&sbQuery, " FROM [%s].[%s]", q.Schema, q.Table) + + if q.LowerLimit.IsValid || q.UpperLimit.IsValid { + sbQuery.WriteString(" WHERE ") + + if q.LowerLimit.IsValid { + fmt.Fprintf(&sbQuery, "[%s]", q.PrimaryKey) + if q.LowerLimit.IsInclusive { + sbQuery.WriteString(" >=") + } else { + sbQuery.WriteString(" >") + } + sbQuery.WriteString(" @min") + } + + if q.LowerLimit.IsValid && q.UpperLimit.IsValid { + sbQuery.WriteString(" AND ") + } + + if q.UpperLimit.IsValid { + fmt.Fprintf(&sbQuery, "[%s]", q.PrimaryKey) + if q.UpperLimit.IsInclusive { + sbQuery.WriteString(" <=") + } else { + sbQuery.WriteString(" <") + } + sbQuery.WriteString(" @max") + } + } + + fmt.Fprintf(&sbQuery, " ORDER BY [%s] ASC", q.PrimaryKey) + + queryString := sbQuery.String() + + var queryArgs []any + + if q.LowerLimit.IsValid { + queryArgs = append(queryArgs, sql.Named("min", q.LowerLimit.Value)) + } + + if q.UpperLimit.IsValid { + queryArgs = append(queryArgs, sql.Named("max", q.UpperLimit.Value)) + } + + return mw.Query(ctx, queryString, queryArgs...) +} diff --git a/internal/app/db-wrapper/postgres.go b/internal/app/db-wrapper/postgres.go index 5d699f3..1c7e275 100644 --- a/internal/app/db-wrapper/postgres.go +++ b/internal/app/db-wrapper/postgres.go @@ -3,6 +3,8 @@ package dbwrapper import ( "context" "errors" + "fmt" + "strings" dbdialects "git.ksdemosapps.com/kylesoda/go-migrate/internal/app/db-wrapper/db-dialects" "github.com/jackc/pgx/v5" @@ -127,3 +129,75 @@ func (pw *postgresDbWrapper) SaveMassive(ctx context.Context, schema string, tab return affectedRows, nil } + +func (pw *postgresDbWrapper) QueryFromObject(ctx context.Context, q ExtractionQuery) (RowsResult, error) { + var sbQuery strings.Builder + + sbQuery.WriteString("SELECT ") + + if len(q.columns) == 0 { + sbQuery.WriteString("*") + } else { + for i, col := range q.columns { + switch col.Type() { + case "GEOMETRY": + fmt.Fprintf(&sbQuery, `ST_AsEWKB("%s") AS "%s"`, col.Name(), col.Name()) + default: + fmt.Fprintf(&sbQuery, `"%s"`, col.Name()) + } + + if i < len(q.columns)-1 { + sbQuery.WriteString(", ") + } + } + } + + fmt.Fprintf(&sbQuery, ` FROM "%s"."%s"`, q.Schema, q.Table) + + if q.LowerLimit.IsValid || q.UpperLimit.IsValid { + sbQuery.WriteString(" WHERE ") + paramIdx := 1 + + if q.LowerLimit.IsValid { + fmt.Fprintf(&sbQuery, `"%s"`, q.PrimaryKey) + if q.LowerLimit.IsInclusive { + sbQuery.WriteString(" >=") + } else { + sbQuery.WriteString(" >") + } + fmt.Fprintf(&sbQuery, " $%d", paramIdx) + paramIdx++ + } + + if q.LowerLimit.IsValid && q.UpperLimit.IsValid { + sbQuery.WriteString(" AND ") + } + + if q.UpperLimit.IsValid { + fmt.Fprintf(&sbQuery, `"%s"`, q.PrimaryKey) + if q.UpperLimit.IsInclusive { + sbQuery.WriteString(" <=") + } else { + sbQuery.WriteString(" <") + } + fmt.Fprintf(&sbQuery, " $%d", paramIdx) + paramIdx++ + } + } + + fmt.Fprintf(&sbQuery, ` ORDER BY "%s" ASC`, q.PrimaryKey) + + queryString := sbQuery.String() + + var queryArgs []any + + if q.LowerLimit.IsValid { + queryArgs = append(queryArgs, q.LowerLimit.Value) + } + + if q.UpperLimit.IsValid { + queryArgs = append(queryArgs, q.UpperLimit.Value) + } + + return pw.Query(ctx, queryString, queryArgs...) +} diff --git a/internal/app/db-wrapper/types.go b/internal/app/db-wrapper/types.go index 8bd0145..f94fd86 100644 --- a/internal/app/db-wrapper/types.go +++ b/internal/app/db-wrapper/types.go @@ -3,6 +3,8 @@ package dbwrapper import ( "context" "errors" + + "git.ksdemosapps.com/kylesoda/go-migrate/internal/app/models" ) var MethodNotSupported error = errors.New("Method not supported by driver... yet :P") @@ -24,6 +26,21 @@ type RowResult interface { Scan(dest ...any) error } +type ExtractorQueryLimit struct { + IsValid bool + IsInclusive bool + Value int64 +} + +type ExtractionQuery struct { + Schema string + Table string + PrimaryKey string + columns []models.ColumnType + LowerLimit ExtractorQueryLimit + UpperLimit ExtractorQueryLimit +} + type DbWrapper interface { Close() error Connect(ctx context.Context, dbUrl string) error @@ -32,4 +49,5 @@ type DbWrapper interface { Query(ctx context.Context, query string, args ...any) (RowsResult, error) QueryRow(ctx context.Context, query string, args ...any) RowResult SaveMassive(ctx context.Context, schema string, table string, columnNames []string, rows [][]any) (int64, error) + QueryFromObject(ctx context.Context, query ExtractionQuery) (RowsResult, error) }