summaryrefslogtreecommitdiff
path: root/vendor/github.com/uptrace/bun/migrate/sqlschema
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/uptrace/bun/migrate/sqlschema')
-rw-r--r--vendor/github.com/uptrace/bun/migrate/sqlschema/column.go75
-rw-r--r--vendor/github.com/uptrace/bun/migrate/sqlschema/database.go127
-rw-r--r--vendor/github.com/uptrace/bun/migrate/sqlschema/inspector.go241
-rw-r--r--vendor/github.com/uptrace/bun/migrate/sqlschema/migrator.go49
-rw-r--r--vendor/github.com/uptrace/bun/migrate/sqlschema/table.go60
5 files changed, 552 insertions, 0 deletions
diff --git a/vendor/github.com/uptrace/bun/migrate/sqlschema/column.go b/vendor/github.com/uptrace/bun/migrate/sqlschema/column.go
new file mode 100644
index 000000000..60f7ea8a6
--- /dev/null
+++ b/vendor/github.com/uptrace/bun/migrate/sqlschema/column.go
@@ -0,0 +1,75 @@
+package sqlschema
+
+import (
+ "fmt"
+
+ "github.com/uptrace/bun/schema"
+)
+
+type Column interface {
+ GetName() string
+ GetSQLType() string
+ GetVarcharLen() int
+ GetDefaultValue() string
+ GetIsNullable() bool
+ GetIsAutoIncrement() bool
+ GetIsIdentity() bool
+ AppendQuery(schema.Formatter, []byte) ([]byte, error)
+}
+
+var _ Column = (*BaseColumn)(nil)
+
+// BaseColumn is a base column definition that stores various attributes of a column.
+//
+// Dialects and only dialects can use it to implement the Column interface.
+// Other packages must use the Column interface.
+type BaseColumn struct {
+ Name string
+ SQLType string
+ VarcharLen int
+ DefaultValue string
+ IsNullable bool
+ IsAutoIncrement bool
+ IsIdentity bool
+ // TODO: add Precision and Cardinality for timestamps/bit-strings/floats and arrays respectively.
+}
+
+func (cd BaseColumn) GetName() string {
+ return cd.Name
+}
+
+func (cd BaseColumn) GetSQLType() string {
+ return cd.SQLType
+}
+
+func (cd BaseColumn) GetVarcharLen() int {
+ return cd.VarcharLen
+}
+
+func (cd BaseColumn) GetDefaultValue() string {
+ return cd.DefaultValue
+}
+
+func (cd BaseColumn) GetIsNullable() bool {
+ return cd.IsNullable
+}
+
+func (cd BaseColumn) GetIsAutoIncrement() bool {
+ return cd.IsAutoIncrement
+}
+
+func (cd BaseColumn) GetIsIdentity() bool {
+ return cd.IsIdentity
+}
+
+// AppendQuery appends full SQL data type.
+func (c *BaseColumn) AppendQuery(fmter schema.Formatter, b []byte) (_ []byte, err error) {
+ b = append(b, c.SQLType...)
+ if c.VarcharLen == 0 {
+ return b, nil
+ }
+ b = append(b, "("...)
+ b = append(b, fmt.Sprint(c.VarcharLen)...)
+ b = append(b, ")"...)
+ return b, nil
+}
diff --git a/vendor/github.com/uptrace/bun/migrate/sqlschema/database.go b/vendor/github.com/uptrace/bun/migrate/sqlschema/database.go
new file mode 100644
index 000000000..cdc5b2d50
--- /dev/null
+++ b/vendor/github.com/uptrace/bun/migrate/sqlschema/database.go
@@ -0,0 +1,127 @@
+package sqlschema
+
+import (
+ "slices"
+ "strings"
+
+ "github.com/uptrace/bun/schema"
+ orderedmap "github.com/wk8/go-ordered-map/v2"
+)
+
+type Database interface {
+ GetTables() *orderedmap.OrderedMap[string, Table]
+ GetForeignKeys() map[ForeignKey]string
+}
+
+var _ Database = (*BaseDatabase)(nil)
+
+// BaseDatabase is a base database definition.
+//
+// Dialects and only dialects can use it to implement the Database interface.
+// Other packages must use the Database interface.
+type BaseDatabase struct {
+ Tables *orderedmap.OrderedMap[string, Table]
+ ForeignKeys map[ForeignKey]string
+}
+
+func (ds BaseDatabase) GetTables() *orderedmap.OrderedMap[string, Table] {
+ return ds.Tables
+}
+
+func (ds BaseDatabase) GetForeignKeys() map[ForeignKey]string {
+ return ds.ForeignKeys
+}
+
+type ForeignKey struct {
+ From ColumnReference
+ To ColumnReference
+}
+
+func NewColumnReference(tableName string, columns ...string) ColumnReference {
+ return ColumnReference{
+ TableName: tableName,
+ Column: NewColumns(columns...),
+ }
+}
+
+func (fk ForeignKey) DependsOnTable(tableName string) bool {
+ return fk.From.TableName == tableName || fk.To.TableName == tableName
+}
+
+func (fk ForeignKey) DependsOnColumn(tableName string, column string) bool {
+ return fk.DependsOnTable(tableName) &&
+ (fk.From.Column.Contains(column) || fk.To.Column.Contains(column))
+}
+
+// Columns is a hashable representation of []string used to define schema constraints that depend on multiple columns.
+// Although having duplicated column references in these constraints is illegal, Columns neither validates nor enforces this constraint on the caller.
+type Columns string
+
+// NewColumns creates a composite column from a slice of column names.
+func NewColumns(columns ...string) Columns {
+ slices.Sort(columns)
+ return Columns(strings.Join(columns, ","))
+}
+
+func (c *Columns) String() string {
+ return string(*c)
+}
+
+func (c *Columns) AppendQuery(fmter schema.Formatter, b []byte) ([]byte, error) {
+ return schema.Safe(*c).AppendQuery(fmter, b)
+}
+
+// Split returns a slice of column names that make up the composite.
+func (c *Columns) Split() []string {
+ return strings.Split(c.String(), ",")
+}
+
+// ContainsColumns checks that columns in "other" are a subset of current colums.
+func (c *Columns) ContainsColumns(other Columns) bool {
+ columns := c.Split()
+Outer:
+ for _, check := range other.Split() {
+ for _, column := range columns {
+ if check == column {
+ continue Outer
+ }
+ }
+ return false
+ }
+ return true
+}
+
+// Contains checks that a composite column contains the current column.
+func (c *Columns) Contains(other string) bool {
+ return c.ContainsColumns(Columns(other))
+}
+
+// Replace renames a column if it is part of the composite.
+// If a composite consists of multiple columns, only one column will be renamed.
+func (c *Columns) Replace(oldColumn, newColumn string) bool {
+ columns := c.Split()
+ for i, column := range columns {
+ if column == oldColumn {
+ columns[i] = newColumn
+ *c = NewColumns(columns...)
+ return true
+ }
+ }
+ return false
+}
+
+// Unique represents a unique constraint defined on 1 or more columns.
+type Unique struct {
+ Name string
+ Columns Columns
+}
+
+// Equals checks that two unique constraint are the same, assuming both are defined for the same table.
+func (u Unique) Equals(other Unique) bool {
+ return u.Columns == other.Columns
+}
+
+type ColumnReference struct {
+ TableName string
+ Column Columns
+}
diff --git a/vendor/github.com/uptrace/bun/migrate/sqlschema/inspector.go b/vendor/github.com/uptrace/bun/migrate/sqlschema/inspector.go
new file mode 100644
index 000000000..fc9af06fc
--- /dev/null
+++ b/vendor/github.com/uptrace/bun/migrate/sqlschema/inspector.go
@@ -0,0 +1,241 @@
+package sqlschema
+
+import (
+ "context"
+ "fmt"
+ "strconv"
+ "strings"
+
+ "github.com/uptrace/bun"
+ "github.com/uptrace/bun/schema"
+ orderedmap "github.com/wk8/go-ordered-map/v2"
+)
+
+type InspectorDialect interface {
+ schema.Dialect
+
+ // Inspector returns a new instance of Inspector for the dialect.
+ // Dialects MAY set their default InspectorConfig values in constructor
+ // but MUST apply InspectorOptions to ensure they can be overriden.
+ //
+ // Use ApplyInspectorOptions to reduce boilerplate.
+ NewInspector(db *bun.DB, options ...InspectorOption) Inspector
+
+ // CompareType returns true if col1 and co2 SQL types are equivalent,
+ // i.e. they might use dialect-specifc type aliases (SERIAL ~ SMALLINT)
+ // or specify the same VARCHAR length differently (VARCHAR(255) ~ VARCHAR).
+ CompareType(Column, Column) bool
+}
+
+// InspectorConfig controls the scope of migration by limiting the objects Inspector should return.
+// Inspectors SHOULD use the configuration directly instead of copying it, or MAY choose to embed it,
+// to make sure options are always applied correctly.
+type InspectorConfig struct {
+ // SchemaName limits inspection to tables in a particular schema.
+ SchemaName string
+
+ // ExcludeTables from inspection.
+ ExcludeTables []string
+}
+
+// Inspector reads schema state.
+type Inspector interface {
+ Inspect(ctx context.Context) (Database, error)
+}
+
+func WithSchemaName(schemaName string) InspectorOption {
+ return func(cfg *InspectorConfig) {
+ cfg.SchemaName = schemaName
+ }
+}
+
+// WithExcludeTables works in append-only mode, i.e. tables cannot be re-included.
+func WithExcludeTables(tables ...string) InspectorOption {
+ return func(cfg *InspectorConfig) {
+ cfg.ExcludeTables = append(cfg.ExcludeTables, tables...)
+ }
+}
+
+// NewInspector creates a new database inspector, if the dialect supports it.
+func NewInspector(db *bun.DB, options ...InspectorOption) (Inspector, error) {
+ dialect, ok := (db.Dialect()).(InspectorDialect)
+ if !ok {
+ return nil, fmt.Errorf("%s does not implement sqlschema.Inspector", db.Dialect().Name())
+ }
+ return &inspector{
+ Inspector: dialect.NewInspector(db, options...),
+ }, nil
+}
+
+func NewBunModelInspector(tables *schema.Tables, options ...InspectorOption) *BunModelInspector {
+ bmi := &BunModelInspector{
+ tables: tables,
+ }
+ ApplyInspectorOptions(&bmi.InspectorConfig, options...)
+ return bmi
+}
+
+type InspectorOption func(*InspectorConfig)
+
+func ApplyInspectorOptions(cfg *InspectorConfig, options ...InspectorOption) {
+ for _, opt := range options {
+ opt(cfg)
+ }
+}
+
+// inspector is opaque pointer to a database inspector.
+type inspector struct {
+ Inspector
+}
+
+// BunModelInspector creates the current project state from the passed bun.Models.
+// Do not recycle BunModelInspector for different sets of models, as older models will not be de-registerred before the next run.
+type BunModelInspector struct {
+ InspectorConfig
+ tables *schema.Tables
+}
+
+var _ Inspector = (*BunModelInspector)(nil)
+
+func (bmi *BunModelInspector) Inspect(ctx context.Context) (Database, error) {
+ state := BunModelSchema{
+ BaseDatabase: BaseDatabase{
+ ForeignKeys: make(map[ForeignKey]string),
+ },
+ Tables: orderedmap.New[string, Table](),
+ }
+ for _, t := range bmi.tables.All() {
+ if t.Schema != bmi.SchemaName {
+ continue
+ }
+
+ columns := orderedmap.New[string, Column]()
+ for _, f := range t.Fields {
+
+ sqlType, length, err := parseLen(f.CreateTableSQLType)
+ if err != nil {
+ return nil, fmt.Errorf("parse length in %q: %w", f.CreateTableSQLType, err)
+ }
+ columns.Set(f.Name, &BaseColumn{
+ Name: f.Name,
+ SQLType: strings.ToLower(sqlType), // TODO(dyma): maybe this is not necessary after Column.Eq()
+ VarcharLen: length,
+ DefaultValue: exprToLower(f.SQLDefault),
+ IsNullable: !f.NotNull,
+ IsAutoIncrement: f.AutoIncrement,
+ IsIdentity: f.Identity,
+ })
+ }
+
+ var unique []Unique
+ for name, group := range t.Unique {
+ // Create a separate unique index for single-column unique constraints
+ // let each dialect apply the default naming convention.
+ if name == "" {
+ for _, f := range group {
+ unique = append(unique, Unique{Columns: NewColumns(f.Name)})
+ }
+ continue
+ }
+
+ // Set the name if it is a "unique group", in which case the user has provided the name.
+ var columns []string
+ for _, f := range group {
+ columns = append(columns, f.Name)
+ }
+ unique = append(unique, Unique{Name: name, Columns: NewColumns(columns...)})
+ }
+
+ var pk *PrimaryKey
+ if len(t.PKs) > 0 {
+ var columns []string
+ for _, f := range t.PKs {
+ columns = append(columns, f.Name)
+ }
+ pk = &PrimaryKey{Columns: NewColumns(columns...)}
+ }
+
+ // In cases where a table is defined in a non-default schema in the `bun:table` tag,
+ // schema.Table only extracts the name of the schema, but passes the entire tag value to t.Name
+ // for backwads-compatibility. For example, a bun model like this:
+ // type Model struct { bun.BaseModel `bun:"table:favourite.books` }
+ // produces
+ // schema.Table{ Schema: "favourite", Name: "favourite.books" }
+ tableName := strings.TrimPrefix(t.Name, t.Schema+".")
+ state.Tables.Set(tableName, &BunTable{
+ BaseTable: BaseTable{
+ Schema: t.Schema,
+ Name: tableName,
+ Columns: columns,
+ UniqueConstraints: unique,
+ PrimaryKey: pk,
+ },
+ Model: t.ZeroIface,
+ })
+
+ for _, rel := range t.Relations {
+ // These relations are nominal and do not need a foreign key to be declared in the current table.
+ // They will be either expressed as N:1 relations in an m2m mapping table, or will be referenced by the other table if it's a 1:N.
+ if rel.Type == schema.ManyToManyRelation ||
+ rel.Type == schema.HasManyRelation {
+ continue
+ }
+
+ var fromCols, toCols []string
+ for _, f := range rel.BasePKs {
+ fromCols = append(fromCols, f.Name)
+ }
+ for _, f := range rel.JoinPKs {
+ toCols = append(toCols, f.Name)
+ }
+
+ target := rel.JoinTable
+ state.ForeignKeys[ForeignKey{
+ From: NewColumnReference(t.Name, fromCols...),
+ To: NewColumnReference(target.Name, toCols...),
+ }] = ""
+ }
+ }
+ return state, nil
+}
+
+func parseLen(typ string) (string, int, error) {
+ paren := strings.Index(typ, "(")
+ if paren == -1 {
+ return typ, 0, nil
+ }
+ length, err := strconv.Atoi(typ[paren+1 : len(typ)-1])
+ if err != nil {
+ return typ, 0, err
+ }
+ return typ[:paren], length, nil
+}
+
+// exprToLower converts string to lowercase, if it does not contain a string literal 'lit'.
+// Use it to ensure that user-defined default values in the models are always comparable
+// to those returned by the database inspector, regardless of the case convention in individual drivers.
+func exprToLower(s string) string {
+ if strings.HasPrefix(s, "'") && strings.HasSuffix(s, "'") {
+ return s
+ }
+ return strings.ToLower(s)
+}
+
+// BunModelSchema is the schema state derived from bun table models.
+type BunModelSchema struct {
+ BaseDatabase
+
+ Tables *orderedmap.OrderedMap[string, Table]
+}
+
+func (ms BunModelSchema) GetTables() *orderedmap.OrderedMap[string, Table] {
+ return ms.Tables
+}
+
+// BunTable provides additional table metadata that is only accessible from scanning bun models.
+type BunTable struct {
+ BaseTable
+
+ // Model stores the zero interface to the underlying Go struct.
+ Model interface{}
+}
diff --git a/vendor/github.com/uptrace/bun/migrate/sqlschema/migrator.go b/vendor/github.com/uptrace/bun/migrate/sqlschema/migrator.go
new file mode 100644
index 000000000..00500061b
--- /dev/null
+++ b/vendor/github.com/uptrace/bun/migrate/sqlschema/migrator.go
@@ -0,0 +1,49 @@
+package sqlschema
+
+import (
+ "fmt"
+
+ "github.com/uptrace/bun"
+ "github.com/uptrace/bun/schema"
+)
+
+type MigratorDialect interface {
+ schema.Dialect
+ NewMigrator(db *bun.DB, schemaName string) Migrator
+}
+
+type Migrator interface {
+ AppendSQL(b []byte, operation interface{}) ([]byte, error)
+}
+
+// migrator is a dialect-agnostic wrapper for sqlschema.MigratorDialect.
+type migrator struct {
+ Migrator
+}
+
+func NewMigrator(db *bun.DB, schemaName string) (Migrator, error) {
+ md, ok := db.Dialect().(MigratorDialect)
+ if !ok {
+ return nil, fmt.Errorf("%q dialect does not implement sqlschema.Migrator", db.Dialect().Name())
+ }
+ return &migrator{
+ Migrator: md.NewMigrator(db, schemaName),
+ }, nil
+}
+
+// BaseMigrator can be embeded by dialect's Migrator implementations to re-use some of the existing bun queries.
+type BaseMigrator struct {
+ db *bun.DB
+}
+
+func NewBaseMigrator(db *bun.DB) *BaseMigrator {
+ return &BaseMigrator{db: db}
+}
+
+func (m *BaseMigrator) AppendCreateTable(b []byte, model interface{}) ([]byte, error) {
+ return m.db.NewCreateTable().Model(model).AppendQuery(m.db.Formatter(), b)
+}
+
+func (m *BaseMigrator) AppendDropTable(b []byte, schemaName, tableName string) ([]byte, error) {
+ return m.db.NewDropTable().TableExpr("?.?", bun.Ident(schemaName), bun.Ident(tableName)).AppendQuery(m.db.Formatter(), b)
+}
diff --git a/vendor/github.com/uptrace/bun/migrate/sqlschema/table.go b/vendor/github.com/uptrace/bun/migrate/sqlschema/table.go
new file mode 100644
index 000000000..a805ba780
--- /dev/null
+++ b/vendor/github.com/uptrace/bun/migrate/sqlschema/table.go
@@ -0,0 +1,60 @@
+package sqlschema
+
+import (
+ orderedmap "github.com/wk8/go-ordered-map/v2"
+)
+
+type Table interface {
+ GetSchema() string
+ GetName() string
+ GetColumns() *orderedmap.OrderedMap[string, Column]
+ GetPrimaryKey() *PrimaryKey
+ GetUniqueConstraints() []Unique
+}
+
+var _ Table = (*BaseTable)(nil)
+
+// BaseTable is a base table definition.
+//
+// Dialects and only dialects can use it to implement the Table interface.
+// Other packages must use the Table interface.
+type BaseTable struct {
+ Schema string
+ Name string
+
+ // ColumnDefinitions map each column name to the column definition.
+ Columns *orderedmap.OrderedMap[string, Column]
+
+ // PrimaryKey holds the primary key definition.
+ // A nil value means that no primary key is defined for the table.
+ PrimaryKey *PrimaryKey
+
+ // UniqueConstraints defined on the table.
+ UniqueConstraints []Unique
+}
+
+// PrimaryKey represents a primary key constraint defined on 1 or more columns.
+type PrimaryKey struct {
+ Name string
+ Columns Columns
+}
+
+func (td *BaseTable) GetSchema() string {
+ return td.Schema
+}
+
+func (td *BaseTable) GetName() string {
+ return td.Name
+}
+
+func (td *BaseTable) GetColumns() *orderedmap.OrderedMap[string, Column] {
+ return td.Columns
+}
+
+func (td *BaseTable) GetPrimaryKey() *PrimaryKey {
+ return td.PrimaryKey
+}
+
+func (td *BaseTable) GetUniqueConstraints() []Unique {
+ return td.UniqueConstraints
+}