diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index abee8ac57..fb70ff46a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -12,17 +12,17 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version: "1.25.x" + go-version: "1.26.x" - name: golangci-lint uses: golangci/golangci-lint-action@v9 with: - version: v2.6.2 + version: v2.11.3 test: runs-on: ubuntu-latest strategy: matrix: - go: ["1.24.x", "1.25.x"] + go: ["1.25.x", "1.26.x"] steps: - uses: actions/checkout@v4 diff --git a/Makefile b/Makefile index 8e23a43c7..9591bca1f 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ SOURCE ?= file go_bindata github github_ee bitbucket aws_s3 google_cloud_storage godoc_vfs gitlab -DATABASE ?= postgres mysql redshift cassandra spanner cockroachdb yugabytedb clickhouse mongodb sqlserver firebird neo4j pgx pgx5 rqlite +DATABASE ?= postgres mysql redshift cassandra spanner cockroachdb yugabytedb clickhouse mongodb sqlserver firebird neo4j pgx pgx5 rqlite hana DATABASE_TEST ?= $(DATABASE) sqlite sqlite3 sqlcipher VERSION ?= $(shell git describe --tags 2>/dev/null | cut -c 2-) TEST_FLAGS ?= diff --git a/README.md b/README.md index 9b5b4b69e..a154be33c 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Coverage Status](https://img.shields.io/coveralls/github/golang-migrate/migrate/master.svg)](https://coveralls.io/github/golang-migrate/migrate?branch=master) [![packagecloud.io](https://img.shields.io/badge/deb-packagecloud.io-844fec.svg)](https://packagecloud.io/golang-migrate/migrate?filter=debs) [![Docker Pulls](https://img.shields.io/docker/pulls/migrate/migrate.svg)](https://hub.docker.com/r/migrate/migrate/) -![Supported Go Versions](https://img.shields.io/badge/Go-1.24%2C%201.25-lightgrey.svg) +![Supported Go Versions](https://img.shields.io/badge/Go-1.25%2C%201.26-lightgrey.svg) [![GitHub Release](https://img.shields.io/github/release/golang-migrate/migrate.svg)](https://github.com/golang-migrate/migrate/releases) [![Go Report Card](https://goreportcard.com/badge/github.com/golang-migrate/migrate/v4)](https://goreportcard.com/report/github.com/golang-migrate/migrate/v4) diff --git a/database/hana/README.md b/database/hana/README.md new file mode 100644 index 000000000..9a3d73618 --- /dev/null +++ b/database/hana/README.md @@ -0,0 +1,12 @@ +# SAP HANA + +`hdb://user:password@host:port?TLSServerName=host&x-migrations-schema=MYSCHEMA` + +## URL Parameters + +| URL Query | WithInstance Config | Description | +|------------------------|----------------------|-------------| +| `x-migrations-schema` | | **Required.** The schema in which the migrations table is created and migrations are applied. | +| `x-migrations-table` | `MigrationsTable` | Name of the migrations table. (default: `schema_migrations`) | +| `x-statement-timeout` | `StatementTimeout` | Abort any statement that takes longer than this duration (e.g. `30s`, `1m`). | +| `x-isolation-level` | `IsolationLevel` | Transaction isolation level as an integer corresponding to [`sql.IsolationLevel`](https://pkg.go.dev/database/sql#IsolationLevel). (default: `0`) | diff --git a/database/hana/examples/migrations/1_init.down.sql b/database/hana/examples/migrations/1_init.down.sql new file mode 100644 index 000000000..13f94f96d --- /dev/null +++ b/database/hana/examples/migrations/1_init.down.sql @@ -0,0 +1 @@ +DROP TABLE TEST; diff --git a/database/hana/examples/migrations/1_init.up.sql b/database/hana/examples/migrations/1_init.up.sql new file mode 100644 index 000000000..524f66c7a --- /dev/null +++ b/database/hana/examples/migrations/1_init.up.sql @@ -0,0 +1,4 @@ +CREATE ROW TABLE TEST ( + id INTEGER PRIMARY KEY, + name NVARCHAR(255) NOT NULL +); diff --git a/database/hana/hana.go b/database/hana/hana.go new file mode 100644 index 000000000..431f34baf --- /dev/null +++ b/database/hana/hana.go @@ -0,0 +1,286 @@ +package hana + +import ( + "context" + "database/sql" + "errors" + "fmt" + "io" + nurl "net/url" + "strconv" + "sync/atomic" + "time" + + hdbDriver "github.com/SAP/go-hdb/driver" + "github.com/golang-migrate/migrate/v4" + "github.com/golang-migrate/migrate/v4/database" +) + +func init() { + database.Register("hdb", &Hana{}) +} + +var _ database.Driver = (*Hana)(nil) + +var ( + DefaultMigrationsTable = "schema_migrations" + + ErrNilConfig = fmt.Errorf("no config") + ErrNoSchemaName = fmt.Errorf("no schema name") + ErrInvalidStatementTimeout = fmt.Errorf("invalid x-statement-timeout") + ErrInvalidIsolationLevel = fmt.Errorf("invalid x-isolation-level") +) + +type Config struct { + SchemaName string + MigrationsTable string + StatementTimeout time.Duration + IsolationLevel sql.IsolationLevel +} + +type Hana struct { + db *sql.DB + config *Config + isLocked atomic.Bool +} + +func (h *Hana) Open(url string) (database.Driver, error) { + purl, err := nurl.Parse(url) + if err != nil { + return nil, err + } + + schemaName := purl.Query().Get("x-migrations-schema") + if schemaName == "" { + return nil, ErrNoSchemaName + } + + migrationsTable := purl.Query().Get("x-migrations-table") + + statementTimeoutParam := purl.Query().Get("x-statement-timeout") + var statementTimeout time.Duration + if statementTimeoutParam != "" { + statementTimeout, err = time.ParseDuration(statementTimeoutParam) + if err != nil { + return nil, fmt.Errorf("%w: %w", ErrInvalidStatementTimeout, err) + } + } + + isolationLevel := sql.LevelDefault + if isolationLevelParam := purl.Query().Get("x-isolation-level"); isolationLevelParam != "" { + isolationLevelInt, err := strconv.Atoi(isolationLevelParam) + if err != nil { + return nil, fmt.Errorf("could not parse x-isolation-level: %w", err) + } + + if isolationLevelInt < int(sql.LevelDefault) || isolationLevelInt > int(sql.LevelLinearizable) { + return nil, fmt.Errorf("%w: %d", ErrInvalidIsolationLevel, isolationLevelInt) + } + + isolationLevel = sql.IsolationLevel(isolationLevelInt) + } + + dsn := migrate.FilterCustomQuery(purl).String() + connector, err := hdbDriver.NewDSNConnector(dsn) + if err != nil { + return nil, err + } + + connector.SetDefaultSchema(schemaName) + db := sql.OpenDB(connector) + + return WithInstance(db, &Config{ + MigrationsTable: migrationsTable, + SchemaName: schemaName, + StatementTimeout: statementTimeout, + IsolationLevel: isolationLevel, + }) +} + +func (h *Hana) Close() error { + return h.db.Close() +} + +func (h *Hana) Lock() error { + if !h.isLocked.CompareAndSwap(false, true) { + return database.ErrLocked + } + + return nil +} + +func (h *Hana) Unlock() error { + if !h.isLocked.CompareAndSwap(true, false) { + return database.ErrNotLocked + } + + return nil +} + +func (h *Hana) Run(migration io.Reader) error { + migr, err := io.ReadAll(migration) + if err != nil { + return err + } + + ctx := context.Background() + if h.config.StatementTimeout != 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, h.config.StatementTimeout) + defer cancel() + } + + if _, err := h.db.ExecContext(ctx, string(migr)); err != nil { + return database.Error{OrigErr: err, Err: "migration failed", Query: migr} + } + + return nil +} + +func (h *Hana) SetVersion(version int, dirty bool) error { + tx, err := h.db.BeginTx(context.Background(), &sql.TxOptions{Isolation: h.config.IsolationLevel}) + if err != nil { + return &database.Error{OrigErr: err, Err: "transaction start failed"} + } + + query := `DELETE FROM "` + h.config.SchemaName + `"."` + h.config.MigrationsTable + `"` + if _, err := tx.ExecContext(context.Background(), query); err != nil { + if errRollback := tx.Rollback(); errRollback != nil { + err = errors.Join(err, errRollback) + } + + return &database.Error{OrigErr: err, Query: []byte(query)} + } + + // Also re-write the schema version for nil dirty versions to prevent + // empty schema version for failed down migration on the first migration. + // See: https://github.com/golang-migrate/migrate/issues/330 + if version >= 0 || (version == database.NilVersion && dirty) { + query = `INSERT INTO "` + h.config.SchemaName + `"."` + h.config.MigrationsTable + `" (version, dirty) VALUES (?, ?)` + if _, err := tx.ExecContext(context.Background(), query, version, dirty); err != nil { + if errRollback := tx.Rollback(); errRollback != nil { + err = errors.Join(err, errRollback) + } + + return &database.Error{OrigErr: err, Query: []byte(query)} + } + } + + if err := tx.Commit(); err != nil { + return &database.Error{OrigErr: err, Err: "transaction commit failed"} + } + + return nil +} + +func (h *Hana) Version() (version int, dirty bool, err error) { + query := `SELECT version, dirty FROM "` + h.config.SchemaName + `"."` + h.config.MigrationsTable + `" LIMIT 1` + + err = h.db.QueryRowContext(context.Background(), query).Scan(&version, &dirty) + switch { + case err == sql.ErrNoRows: + return database.NilVersion, false, nil + case err != nil: + return 0, false, &database.Error{OrigErr: err, Query: []byte(query)} + default: + return version, dirty, nil + } +} + +func (h *Hana) Drop() (err error) { + query := `SELECT TABLE_NAME FROM SYS.TABLES WHERE SCHEMA_NAME = ?` + + tables, err := h.db.QueryContext(context.Background(), query, h.config.SchemaName) + if err != nil { + return &database.Error{OrigErr: err, Query: []byte(query)} + } + + defer func() { + if errClose := tables.Close(); errClose != nil { + err = errors.Join(err, errClose) + } + }() + + tableNames := make([]string, 0) + for tables.Next() { + var tableName string + if err := tables.Scan(&tableName); err != nil { + return err + } + + if tableName != "" { + tableNames = append(tableNames, tableName) + } + } + + if err := tables.Err(); err != nil { + return &database.Error{OrigErr: err, Query: []byte(query)} + } + + for _, t := range tableNames { + query = `DROP TABLE "` + h.config.SchemaName + `"."` + t + `"` + if _, err := h.db.ExecContext(context.Background(), query); err != nil { + return &database.Error{OrigErr: err, Query: []byte(query)} + } + } + + return nil +} + +func WithInstance(instance *sql.DB, config *Config) (database.Driver, error) { + if config == nil { + return nil, ErrNilConfig + } + + if err := instance.PingContext(context.Background()); err != nil { + return nil, err + } + + hx := &Hana{ + db: instance, + config: config, + } + + if config.MigrationsTable == "" { + config.MigrationsTable = DefaultMigrationsTable + } + + if err := hx.ensureVersionTable(); err != nil { + return nil, err + } + + return hx, nil +} + +// ensureVersionTable checks if the migrations table exists and creates it if not. +func (h *Hana) ensureVersionTable() (err error) { + if err = h.Lock(); err != nil { + return err + } + + defer func() { + if e := h.Unlock(); e != nil { + err = errors.Join(err, e) + } + }() + + var count int + query := `SELECT COUNT(*) FROM SYS.TABLES WHERE SCHEMA_NAME = ? AND TABLE_NAME = ?` + if err := h.db.QueryRowContext(context.Background(), query, + h.config.SchemaName, + h.config.MigrationsTable, + ).Scan(&count); err != nil { + return &database.Error{OrigErr: err, Query: []byte(query)} + } + + if count == 1 { + return nil + } + + query = `CREATE ROW TABLE "` + h.config.SchemaName + `"."` + h.config.MigrationsTable + `" (version BIGINT NOT NULL PRIMARY KEY, dirty BOOLEAN NOT NULL)` + if _, err = h.db.ExecContext(context.Background(), query); err != nil { + return &database.Error{OrigErr: err, Query: []byte(query)} + } + + return nil +} diff --git a/database/hana/hana_int_test.go b/database/hana/hana_int_test.go new file mode 100644 index 000000000..945a8de1c --- /dev/null +++ b/database/hana/hana_int_test.go @@ -0,0 +1,45 @@ +package hana + +import ( + "os" + "testing" + + "github.com/golang-migrate/migrate/v4" + dt "github.com/golang-migrate/migrate/v4/database/testing" + _ "github.com/golang-migrate/migrate/v4/source/file" +) + +const envKey = "HANA_DATABASE_URL" + +func TestMigrate(t *testing.T) { + url := getURL(t) + + p := &Hana{} + d, err := p.Open(url) + if err != nil { + t.Fatal(err) + } + + defer func() { + if err := d.Close(); err != nil { + t.Error(err) + } + }() + + m, err := migrate.NewWithDatabaseInstance("file://./examples/migrations", "hdb", d) + if err != nil { + t.Fatal(err) + } + + dt.TestMigrate(t, m) +} + +func getURL(t *testing.T) string { + t.Helper() + url := os.Getenv(envKey) + if url == "" { + t.Skipf("skipping integration test: %s not set", envKey) + } + + return url +} diff --git a/database/hana/hana_test.go b/database/hana/hana_test.go new file mode 100644 index 000000000..41c49e0c6 --- /dev/null +++ b/database/hana/hana_test.go @@ -0,0 +1,75 @@ +package hana + +import ( + "database/sql" + "errors" + "testing" + + _ "github.com/golang-migrate/migrate/v4/source/file" +) + +func TestOpen(t *testing.T) { + t.Parallel() + + testcases := []struct { + name string + inputURL string + expectedErr error + }{ + { + name: "missing schema", + inputURL: "hdb://user:pass@localhost:443", + expectedErr: ErrNoSchemaName, + }, + { + name: "invalid statement timeout", + inputURL: "hdb://user:pass@localhost:443?x-migrations-schema=TEST&x-statement-timeout=INVALID", + expectedErr: ErrInvalidStatementTimeout, + }, + { + name: "invalid isolation level", + inputURL: "hdb://user:pass@localhost:443?x-migrations-schema=TEST&x-isolation-level=999", + expectedErr: ErrInvalidIsolationLevel, + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + _, err := (&Hana{}).Open(tc.inputURL) + if !errors.Is(err, tc.expectedErr) { + t.Fatalf("expected %v, got %v", tc.expectedErr, err) + } + }) + } +} + +func TestWithInstance(t *testing.T) { + t.Parallel() + + testcases := []struct { + name string + db *sql.DB + config *Config + expectedErr error + }{ + { + name: "nil config", + db: nil, + config: nil, + expectedErr: ErrNilConfig, + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + _, err := WithInstance(tc.db, tc.config) + if !errors.Is(err, tc.expectedErr) { + t.Fatalf("expected %v, got %v", tc.expectedErr, err) + } + }) + } +} diff --git a/go.mod b/go.mod index eacffe3e0..77bf2c0d7 100644 --- a/go.mod +++ b/go.mod @@ -1,12 +1,13 @@ module github.com/golang-migrate/migrate/v4 -go 1.24.0 +go 1.25.0 require ( cloud.google.com/go/spanner v1.85.0 cloud.google.com/go/storage v1.56.0 github.com/Azure/go-autorest/autorest/adal v0.9.16 github.com/ClickHouse/clickhouse-go v1.4.3 + github.com/SAP/go-hdb v1.15.2 github.com/aws/aws-sdk-go v1.49.6 github.com/cenkalti/backoff/v4 v4.1.2 github.com/cockroachdb/cockroach-go/v2 v2.1.1 @@ -75,8 +76,8 @@ require ( go.opentelemetry.io/otel/sdk/metric v1.40.0 // indirect go.opentelemetry.io/otel/trace v1.40.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect - golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8 // indirect - golang.org/x/tools v0.38.0 // indirect + golang.org/x/telemetry v0.0.0-20260109210033-bd525da824e2 // indirect + golang.org/x/tools v0.41.0 // indirect ) require ( @@ -182,14 +183,14 @@ require ( github.com/zeebo/xxh3 v1.0.2 // indirect gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b // indirect go.opencensus.io v0.24.0 // indirect - golang.org/x/crypto v0.45.0 // indirect + golang.org/x/crypto v0.47.0 // indirect golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0 // indirect - golang.org/x/mod v0.29.0 // indirect - golang.org/x/net v0.47.0 // indirect - golang.org/x/sync v0.18.0 // indirect + golang.org/x/mod v0.32.0 // indirect + golang.org/x/net v0.49.0 // indirect + golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.40.0 // indirect - golang.org/x/term v0.37.0 // indirect - golang.org/x/text v0.31.0 // indirect + golang.org/x/term v0.39.0 // indirect + golang.org/x/text v0.34.0 // indirect golang.org/x/time v0.12.0 // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect google.golang.org/genproto v0.0.0-20250603155806-513f23925822 // indirect diff --git a/go.sum b/go.sum index 039672531..565712189 100644 --- a/go.sum +++ b/go.sum @@ -671,6 +671,8 @@ github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0 github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/SAP/go-hdb v1.15.2 h1:IwR5GnJGfef6lVFGKy2xDLHnXtMGZAFhMC798Ce2TfM= +github.com/SAP/go-hdb v1.15.2/go.mod h1:B5yJ0qHcAkBxQQorbiRxgr2dpalPVOx9zSDgXo82fvI= github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY= github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= @@ -1420,8 +1422,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= -golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= +golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= +golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1481,8 +1483,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= -golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= +golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c= +golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1549,8 +1551,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= -golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= -golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= +golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= +golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= golang.org/x/oauth2 v0.0.0-20180227000427-d7d64896b5ff/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1600,8 +1602,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180224232135-f6cff0780e54/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1699,8 +1701,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8 h1:LvzTn0GQhWuvKH/kVRS3R3bVAsdQWI7hvfLHGgh9+lU= -golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8/go.mod h1:Pi4ztBfryZoJEkyFTI5/Ocsu2jXyDr6iSdgJiYE/uwE= +golang.org/x/telemetry v0.0.0-20260109210033-bd525da824e2 h1:O1cMQHRfwNpDfDJerqRoE2oD+AFlyid87D40L/OkkJo= +golang.org/x/telemetry v0.0.0-20260109210033-bd525da824e2/go.mod h1:b7fPSJ0pKZ3ccUh8gnTONJxhn3c/PS6tyzQvyqw4iA8= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1710,8 +1712,8 @@ golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= -golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= -golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= +golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY= +golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1728,8 +1730,8 @@ golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= +golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1808,8 +1810,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= -golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= -golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= +golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= +golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= golang.org/x/tools/godoc v0.1.0-deprecated h1:o+aZ1BOj6Hsx/GBdJO/s815sqftjSnrZZwyYTHODvtk= golang.org/x/tools/godoc v0.1.0-deprecated/go.mod h1:qM63CriJ961IHWmnWa9CjZnBndniPt4a3CK0PVB9bIg= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/internal/cli/build_hana.go b/internal/cli/build_hana.go new file mode 100644 index 000000000..b7eb124a7 --- /dev/null +++ b/internal/cli/build_hana.go @@ -0,0 +1,7 @@ +//go:build hana + +package cli + +import ( + _ "github.com/golang-migrate/migrate/v4/database/hana" +)