From 7d3a90c0c1d6c34d864f2a78342bc3c05cc065f0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Aug 2026 06:32:55 +0000 Subject: [PATCH] Bump gorm.io/driver/postgres from 1.6.0 to 1.6.2 Bumps [gorm.io/driver/postgres](https://github.com/go-gorm/postgres) from 1.6.0 to 1.6.2. - [Commits](https://github.com/go-gorm/postgres/compare/v1.6.0...v1.6.2) --- updated-dependencies: - dependency-name: gorm.io/driver/postgres dependency-version: 1.6.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 +- .../driver/postgres/error_translator.go | 5 +- vendor/gorm.io/driver/postgres/migrator.go | 39 +++--- vendor/gorm.io/driver/postgres/postgres.go | 114 +++++++++++++++--- vendor/modules.txt | 4 +- 6 files changed, 126 insertions(+), 42 deletions(-) diff --git a/go.mod b/go.mod index 37c6236d3..d810b91ec 100644 --- a/go.mod +++ b/go.mod @@ -40,7 +40,7 @@ require ( gopkg.in/natefinch/lumberjack.v2 v2.2.1 gorm.io/datatypes v1.2.7 gorm.io/driver/mysql v1.6.0 - gorm.io/driver/postgres v1.6.0 + gorm.io/driver/postgres v1.6.2 gorm.io/driver/sqlite v1.6.0 gorm.io/gorm v1.31.2 ) diff --git a/go.sum b/go.sum index d1d3abf8e..4d239eab0 100644 --- a/go.sum +++ b/go.sum @@ -292,8 +292,8 @@ gorm.io/datatypes v1.2.7 h1:ww9GAhF1aGXZY3EB3cJPJ7//JiuQo7DlQA7NNlVaTdk= gorm.io/datatypes v1.2.7/go.mod h1:M2iO+6S3hhi4nAyYe444Pcb0dcIiOMJ7QHaUXxyiNZY= gorm.io/driver/mysql v1.6.0 h1:eNbLmNTpPpTOVZi8MMxCi2aaIm0ZpInbORNXDwyLGvg= gorm.io/driver/mysql v1.6.0/go.mod h1:D/oCC2GWK3M/dqoLxnOlaNKmXz8WNTfcS9y5ovaSqKo= -gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4= -gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo= +gorm.io/driver/postgres v1.6.2 h1:BvXQ/cNUg63q5TFNg672DmDcowZSFrNLkkA3Xe6GXq4= +gorm.io/driver/postgres v1.6.2/go.mod h1:0c4fQA44XhOklXDkgtuKqysHCycTa5i9e3EIpDGCwXk= gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ= gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8= gorm.io/driver/sqlserver v1.6.0 h1:VZOBQVsVhkHU/NzNhRJKoANt5pZGQAS1Bwc6m6dgfnc= diff --git a/vendor/gorm.io/driver/postgres/error_translator.go b/vendor/gorm.io/driver/postgres/error_translator.go index 5f813501c..b4b8938e9 100644 --- a/vendor/gorm.io/driver/postgres/error_translator.go +++ b/vendor/gorm.io/driver/postgres/error_translator.go @@ -2,6 +2,7 @@ package postgres import ( "encoding/json" + "fmt" "gorm.io/gorm" @@ -27,7 +28,7 @@ type ErrMessage struct { func (dialector Dialector) Translate(err error) error { if pgErr, ok := err.(*pgconn.PgError); ok { if translatedErr, found := errCodes[pgErr.Code]; found { - return translatedErr + return fmt.Errorf("%w: %w", translatedErr, pgErr) } return err } @@ -44,7 +45,7 @@ func (dialector Dialector) Translate(err error) error { } if translatedErr, found := errCodes[errMsg.Code]; found { - return translatedErr + return fmt.Errorf("%w: %s", translatedErr, errMsg.Message) } return err } diff --git a/vendor/gorm.io/driver/postgres/migrator.go b/vendor/gorm.io/driver/postgres/migrator.go index 6b57ce69f..7cbc09fb4 100644 --- a/vendor/gorm.io/driver/postgres/migrator.go +++ b/vendor/gorm.io/driver/postgres/migrator.go @@ -3,10 +3,10 @@ package postgres import ( "database/sql" "fmt" - "github.com/jackc/pgx/v5" "regexp" "strings" + "github.com/jackc/pgx/v5" "gorm.io/gorm" "gorm.io/gorm/clause" "gorm.io/gorm/migrator" @@ -68,6 +68,11 @@ var typeAliasMap = map[string][]string{ "time with time zone": {"timetz"}, } +var ( + autoIncrementValuePattern = regexp.MustCompile(`^nextval\('"?[^']+seq"?'::regclass\)$`) + defaultValueValuePattern = regexp.MustCompile(`^(.*?)(?:::.*)?$`) +) + type Migrator struct { migrator.Migrator } @@ -167,9 +172,10 @@ func (m Migrator) CreateIndex(value interface{}, name string) error { func (m Migrator) RenameIndex(value interface{}, oldName, newName string) error { return m.RunWithValue(value, func(stmt *gorm.Statement) error { + currentSchema, _ := m.CurrentSchema(stmt, stmt.Table) return m.DB.Exec( - "ALTER INDEX ? RENAME TO ?", - clause.Column{Name: oldName}, clause.Column{Name: newName}, + "ALTER INDEX ?.? RENAME TO ?", + currentSchema, clause.Column{Name: oldName}, clause.Column{Name: newName}, ).Error }) } @@ -182,7 +188,8 @@ func (m Migrator) DropIndex(value interface{}, name string) error { } } - return m.DB.Exec("DROP INDEX ?", clause.Column{Name: name}).Error + currentSchema, _ := m.CurrentSchema(stmt, stmt.Table) + return m.DB.Exec("DROP INDEX ?.?", currentSchema, clause.Column{Name: name}).Error }) } @@ -320,15 +327,19 @@ func (m Migrator) AlterColumn(value interface{}, field string) error { err := m.RunWithValue(value, func(stmt *gorm.Statement) error { if stmt.Schema != nil { if field := stmt.Schema.LookUpField(field); field != nil { - var ( - columnTypes, _ = m.DB.Migrator().ColumnTypes(value) - fieldColumnType *migrator.ColumnType - ) + columnTypes, err := m.DB.Migrator().ColumnTypes(value) + if err != nil { + return err + } + var fieldColumnType *migrator.ColumnType for _, columnType := range columnTypes { if columnType.Name() == field.DBName { fieldColumnType, _ = columnType.(*migrator.ColumnType) } } + if fieldColumnType == nil { + return fmt.Errorf("failed to find column type for field %s", field.DBName) + } fileType := clause.Expr{SQL: m.DataTypeOf(field)} // check for typeName and SQL name @@ -501,7 +512,6 @@ func (m Migrator) ColumnTypes(value interface{}) (columnTypes []gorm.ColumnType, column.LengthValue = typeLenValue } - autoIncrementValuePattern := regexp.MustCompile(`^nextval\('"?[^']+seq"?'::regclass\)$`) if autoIncrementValuePattern.MatchString(column.DefaultValueValue.String) || (identityIncrement.Valid && identityIncrement.String != "") { column.AutoIncrementValue = sql.NullBool{Bool: true, Valid: true} column.DefaultValueValue = sql.NullString{} @@ -581,11 +591,7 @@ func (m Migrator) ColumnTypes(value interface{}) (columnTypes []gorm.ColumnType, // check column type { - dataTypeRows, err := m.queryRaw(`SELECT a.attname as column_name, format_type(a.atttypid, a.atttypmod) AS data_type - FROM pg_attribute a JOIN pg_class b ON a.attrelid = b.oid AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname = ?) - WHERE a.attnum > 0 -- hide internal columns - AND NOT a.attisdropped -- hide deleted columns - AND b.relname = ?`, currentSchema, table).Rows() + dataTypeRows, err := m.queryRaw(`SELECT a.attname as column_name, format_type(a.atttypid, a.atttypmod) AS data_type FROM pg_attribute a JOIN pg_class b ON a.attrelid = b.oid AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname = ?) WHERE a.attnum > 0 AND NOT a.attisdropped AND b.relname = ?`, currentSchema, table).Rows() if err != nil { return err } @@ -739,7 +745,8 @@ func (m Migrator) GetIndexes(value interface{}) ([]gorm.Index, error) { err := m.RunWithValue(value, func(stmt *gorm.Statement) error { result := make([]*Index, 0) - scanErr := m.queryRaw(indexSql, stmt.Table).Scan(&result).Error + currentSchema, curTable := m.CurrentSchema(stmt, stmt.Table) + scanErr := m.queryRaw(indexSql+" AND ct.relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = ?)", curTable, currentSchema).Scan(&result).Error if scanErr != nil { return scanErr } @@ -816,6 +823,6 @@ func (m Migrator) RenameColumn(dst interface{}, oldName, field string) error { } func parseDefaultValueValue(defaultValue string) string { - value := regexp.MustCompile(`^(.*?)(?:::.*)?$`).ReplaceAllString(defaultValue, "$1") + value := defaultValueValuePattern.ReplaceAllString(defaultValue, "$1") return strings.Trim(value, "'") } diff --git a/vendor/gorm.io/driver/postgres/postgres.go b/vendor/gorm.io/driver/postgres/postgres.go index 2d8fd9973..94c00c319 100644 --- a/vendor/gorm.io/driver/postgres/postgres.go +++ b/vendor/gorm.io/driver/postgres/postgres.go @@ -31,6 +31,7 @@ type Config struct { PreferSimpleProtocol bool WithoutReturning bool Conn gorm.ConnPool + OptionOpenDB []stdlib.OptionOpenDB } var ( @@ -102,10 +103,9 @@ func (dialector Dialector) Initialize(db *gorm.DB) (err error) { config.DefaultQueryExecMode = pgx.QueryExecModeSimpleProtocol } result := timeZoneMatcher.FindStringSubmatch(dialector.Config.DSN) - var options []stdlib.OptionOpenDB if len(result) > 2 { config.RuntimeParams["timezone"] = result[2] - options = append(options, stdlib.OptionAfterConnect(func(ctx context.Context, conn *pgx.Conn) error { + dialector.OptionOpenDB = append(dialector.OptionOpenDB, stdlib.OptionAfterConnect(func(ctx context.Context, conn *pgx.Conn) error { loc, tzErr := time.LoadLocation(result[2]) if tzErr != nil { return tzErr @@ -118,7 +118,7 @@ func (dialector Dialector) Initialize(db *gorm.DB) (err error) { return nil })) } - db.ConnPool = stdlib.OpenDB(*config, options...) + db.ConnPool = stdlib.OpenDB(*config, dialector.OptionOpenDB...) } return } @@ -208,33 +208,41 @@ func (dialector Dialector) Explain(sql string, vars ...interface{}) string { } func (dialector Dialector) DataTypeOf(field *schema.Field) string { + // PostgreSQL 10+ generated columns. The value-generation strategy is carried + // by the `generated` tag, intentionally kept separate from the column `type`: + // + // `gorm:"generated:identity"` -> GENERATED BY DEFAULT AS IDENTITY + // `gorm:"generated:identity always"` -> GENERATED ALWAYS AS IDENTITY + // `gorm:"generated:price * quantity"` -> GENERATED ALWAYS AS (price * quantity) STORED + // + // https://github.com/go-gorm/gorm/issues/7191 + if gen, ok := generatedColumnOf(field); ok { + if gen.identity { + return dialector.getSchemaIntType(field) + " GENERATED " + gen.mode + " AS IDENTITY" + } + return dialector.getSchemaBaseType(field) + " GENERATED ALWAYS AS (" + gen.expr + ") STORED" + } + + return dialector.getSchemaBaseType(field) +} + +func (dialector Dialector) getSchemaBaseType(field *schema.Field) string { switch field.DataType { case schema.Bool: return "boolean" case schema.Int, schema.Uint: - size := field.Size - if field.DataType == schema.Uint { - size++ - } + intType := dialector.getSchemaIntType(field) if field.AutoIncrement { - switch { - case size <= 16: + switch intType { + case "smallint": return "smallserial" - case size <= 32: + case "integer": return "serial" default: return "bigserial" } - } else { - switch { - case size <= 16: - return "smallint" - case size <= 32: - return "integer" - default: - return "bigint" - } } + return intType case schema.Float: if field.Precision > 0 { if field.Scale > 0 { @@ -260,6 +268,22 @@ func (dialector Dialector) DataTypeOf(field *schema.Field) string { } } +func (dialector Dialector) getSchemaIntType(field *schema.Field) string { + size := field.Size + if field.DataType == schema.Uint { + size++ + } + + switch { + case size <= 16: + return "smallint" + case size <= 32: + return "integer" + default: + return "bigint" + } +} + func (dialector Dialector) getSchemaCustomType(field *schema.Field) string { sqlType := string(field.DataType) @@ -303,3 +327,55 @@ func getSerialDatabaseType(s string) (dbType string, ok bool) { return "", false } } + +// generatedColumn describes a PostgreSQL generated column parsed from a +// `generated` tag: either an identity column or a STORED computed column. +type generatedColumn struct { + identity bool // identity column: GENERATED { mode } AS IDENTITY + mode string // identity generation mode: "BY DEFAULT" or "ALWAYS" + expr string // computed column expression: GENERATED ALWAYS AS (expr) STORED +} + +// generatedColumnOf parses the `generated` tag. The value is either the keyword +// `identity` (optionally combined with the mode `always` / `by default`) for an +// identity column, or any other value, which is taken verbatim as the expression +// of a STORED computed column. +func generatedColumnOf(field *schema.Field) (generatedColumn, bool) { + value, ok := field.TagSettings["GENERATED"] + if !ok { + return generatedColumn{}, false + } + + // Ignore an empty value or a bare `generated` tag, which the tag parser + // stores as the upper-cased key, rather than treating it as an expression. + if value = strings.TrimSpace(value); value == "" || value == "GENERATED" { + return generatedColumn{}, false + } + + if mode, isIdentity := identityMode(value); isIdentity { + return generatedColumn{identity: true, mode: mode}, true + } + + return generatedColumn{expr: value}, true +} + +// identityMode reports whether value describes an identity column and, if so, +// its generation mode. The recognized keywords are `identity`, `always` and +// `by default`, in any order; any other token means value is a computed +// expression rather than an identity specification. +func identityMode(value string) (mode string, ok bool) { + mode = "BY DEFAULT" + for _, token := range strings.Fields(strings.ToLower(value)) { + switch token { + case "identity": + ok = true + case "always": + mode = "ALWAYS" + case "by", "default": + // part of the "by default" mode, which is the default; ignore + default: + return "", false + } + } + return mode, ok +} diff --git a/vendor/modules.txt b/vendor/modules.txt index f38ec99e2..1099f6580 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -485,8 +485,8 @@ gorm.io/datatypes # gorm.io/driver/mysql v1.6.0 ## explicit; go 1.18 gorm.io/driver/mysql -# gorm.io/driver/postgres v1.6.0 -## explicit; go 1.20 +# gorm.io/driver/postgres v1.6.2 +## explicit; go 1.25.0 gorm.io/driver/postgres # gorm.io/driver/sqlite v1.6.0 ## explicit; go 1.20