feat: PostgreSQL driver support
This commit is contained in:
1 parent
eb9bfabed5
commit
dc5d12348c
24 files changed
+1585
-75
No files matched your search
@@ -0,0 +1,108 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
domainUpdate "RenewCMS/internal/domain/update"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
const (
|
||||
DriverPostgres = "postgres"
|
||||
|
||||
postgresTimeZone = "UTC"
|
||||
postgresConnectTimeoutSeconds = 5
|
||||
postgresScheme = "postgres"
|
||||
postgresUTCCallback = "renewcms:postgres_utc"
|
||||
gormQueryCallback = "gorm:query"
|
||||
)
|
||||
|
||||
var timeType = reflect.TypeOf(time.Time{})
|
||||
|
||||
type postgresDriver struct{}
|
||||
|
||||
type PostgresConfig struct {
|
||||
Host string
|
||||
Port string
|
||||
User string
|
||||
Password string
|
||||
Name string
|
||||
SSLMode string
|
||||
}
|
||||
|
||||
func PostgresDSN(config PostgresConfig) string {
|
||||
dsn := url.URL{
|
||||
Scheme: postgresScheme,
|
||||
User: url.UserPassword(config.User, config.Password),
|
||||
Host: net.JoinHostPort(config.Host, config.Port),
|
||||
Path: "/" + config.Name,
|
||||
}
|
||||
query := url.Values{}
|
||||
query.Set("sslmode", config.SSLMode)
|
||||
query.Set("timezone", postgresTimeZone)
|
||||
query.Set("connect_timeout", fmt.Sprint(postgresConnectTimeoutSeconds))
|
||||
dsn.RawQuery = query.Encode()
|
||||
return dsn.String()
|
||||
}
|
||||
|
||||
func (postgresDriver) Open(source string) (*gorm.DB, error) {
|
||||
parsed, err := pgconn.ParseConfig(source)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("the postgres connection settings are invalid")
|
||||
}
|
||||
db, err := gorm.Open(postgres.Open(source), &gorm.Config{})
|
||||
if err != nil {
|
||||
address := net.JoinHostPort(parsed.Host, fmt.Sprint(parsed.Port))
|
||||
return nil, fmt.Errorf("cannot use the postgres server at %s: %s", address, redact(err.Error(), parsed.Password))
|
||||
}
|
||||
if err := db.Callback().Query().After(gormQueryCallback).Register(postgresUTCCallback, readTimesInUTC); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return db, nil
|
||||
}
|
||||
|
||||
func readTimesInUTC(db *gorm.DB) {
|
||||
if db.Error != nil || db.Statement.Dest == nil {
|
||||
return
|
||||
}
|
||||
convertTimesToUTC(reflect.ValueOf(db.Statement.Dest))
|
||||
}
|
||||
|
||||
func convertTimesToUTC(value reflect.Value) {
|
||||
switch value.Kind() {
|
||||
case reflect.Pointer, reflect.Interface:
|
||||
if !value.IsNil() {
|
||||
convertTimesToUTC(value.Elem())
|
||||
}
|
||||
case reflect.Slice, reflect.Array:
|
||||
for index := 0; index < value.Len(); index++ {
|
||||
convertTimesToUTC(value.Index(index))
|
||||
}
|
||||
case reflect.Struct:
|
||||
convertStructTimesToUTC(value)
|
||||
}
|
||||
}
|
||||
|
||||
func convertStructTimesToUTC(value reflect.Value) {
|
||||
if value.Type() == timeType {
|
||||
if value.CanSet() {
|
||||
value.Set(reflect.ValueOf(value.Interface().(time.Time).UTC()))
|
||||
}
|
||||
return
|
||||
}
|
||||
for index := 0; index < value.NumField(); index++ {
|
||||
if value.Type().Field(index).IsExported() {
|
||||
convertTimesToUTC(value.Field(index))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (postgresDriver) NewBackup(db *gorm.DB, directory string, now func() time.Time) domainUpdate.DatabaseBackup {
|
||||
return NewPostgresBackup(db, directory, now)
|
||||
}
|
||||
Reference in new issue
Block a user