Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 44 additions & 2 deletions database/dialects.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import (
// Dialect is the type of database dialect.
type Dialect string

var ErrUnknownDialect = errors.New("unknown dialect")
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're returning the same error in two places, I thought it better to have a sentinel error. I debated having an error type instead, but the only data I could think of it holding is the unknown dialect, but the caller already has this.


const (
DialectCustom Dialect = ""
DialectClickHouse Dialect = "clickhouse"
Expand All @@ -32,7 +34,47 @@ const (
DialectVertica Dialect = "vertica"
)

// NewStore returns a new [Store] implementation for the given dialect.
// ParseDialect returns the corresponding [Dialect], if any, for a given string with external
// origin. A supported [Dialect] value, like [DialectPostgres], may have multiple supported
// aliases, e.g. "postgres" or "pgx". Use this function to ensure that a [Dialect] instance
// passed to any function that accepts a [Dialect] is a valid [Dialect].
func ParseDialect(s string) (d Dialect, err error) {
// Every non-error return value from this function must have an entry in the [NewStore] lookup map.
Copy link
Author

@globalchubby globalchubby Dec 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To really prevent drift, instead of having a comment here, what do you think of having a registry? I drafted up a PR in globalchubby#1.

switch s {
case "postgres", "pgx":
d = DialectPostgres
case "mysql":
d = DialectMySQL
case "sqlite3", "sqlite":
d = DialectSQLite3
case "spanner":
d = DialectSpanner
case "mssql", "azuresql", "sqlserver":
d = DialectMSSQL
case "redshift":
d = DialectRedshift
case "tidb":
d = DialectTiDB
case "clickhouse":
d = DialectClickHouse
case "vertica":
d = DialectVertica
case "ydb":
d = DialectYdB
case "turso":
d = DialectTurso
case "starrocks":
d = DialectStarrocks
case "dsql":
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This case is new. The lookup map in NewStore has an entry for it, so we should support it here as well.

d = DialectAuroraDSQL
default:
err = ErrUnknownDialect
}
return
}

// NewStore returns a new [Store] implementation for the given dialect. The
// dialect must be a valid [Dialect] value; see [ParseDialect].
func NewStore(d Dialect, tableName string) (Store, error) {
if d == DialectCustom {
return nil, errors.New("custom dialect is not supported")
Expand All @@ -54,7 +96,7 @@ func NewStore(d Dialect, tableName string) (Store, error) {
}
querier, ok := lookup[d]
if !ok {
return nil, fmt.Errorf("unknown dialect: %q", d)
return nil, ErrUnknownDialect
}
return NewStoreFromQuerier(tableName, querier)
}
Expand Down
34 changes: 3 additions & 31 deletions dialect.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package goose

import (
"fmt"

"github.com/pressly/goose/v3/database"
"github.com/pressly/goose/v3/internal/legacystore"
)
Expand Down Expand Up @@ -39,36 +37,10 @@ var store legacystore.Store

// SetDialect sets the dialect to use for the goose package.
func SetDialect(s string) error {
var d Dialect
switch s {
case "postgres", "pgx":
d = DialectPostgres
case "mysql":
d = DialectMySQL
case "sqlite3", "sqlite":
d = DialectSQLite3
case "spanner":
d = DialectSpanner
case "mssql", "azuresql", "sqlserver":
d = DialectMSSQL
case "redshift":
d = DialectRedshift
case "tidb":
d = DialectTiDB
case "clickhouse":
d = DialectClickHouse
case "vertica":
d = DialectVertica
case "ydb":
d = DialectYdB
case "turso":
d = DialectTurso
case "starrocks":
d = DialectStarrocks
default:
return fmt.Errorf("%q: unknown dialect", s)
d, err := database.ParseDialect(s)
if err != nil {
return err
}
var err error
store, err = legacystore.NewStore(d)
return err
}
5 changes: 3 additions & 2 deletions provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ type Provider struct {
// The caller is responsible for matching the database dialect with the database/sql driver. For
// example, if the database dialect is "postgres", the database/sql driver could be
// github.com/lib/pq or github.com/jackc/pgx. Each dialect has a corresponding [database.Dialect]
// constant backed by a default [database.Store] implementation. For more advanced use cases, such
// as using a custom table name or supplying a custom store implementation, see [WithStore].
// constant backed by a default [database.Store] implementation. The dialect must also be a supported
// [database.Dialect], use [database.ParseDialect] to get a valid dialect. For more advanced use cases,
// such as using a custom table name or supplying a custom store implementation, see [WithStore].
//
// fsys is the filesystem used to read migration files, but may be nil. Most users will want to use
// [os.DirFS], os.DirFS("path/to/migrations"), to read migrations from the local filesystem.
Expand Down