Add GaussDB Database Support (#7508)
* support gaussdb * use github CI * change function name * use gorm.io/driver/gaussdb --------- Co-authored-by: bing.ma <bing.ma@daocloud.io>
This commit is contained in:
parent
751a6dde7a
commit
991c2d4891
68
.github/workflows/tests.yml
vendored
68
.github/workflows/tests.yml
vendored
@ -240,3 +240,71 @@ jobs:
|
||||
|
||||
- name: Tests
|
||||
run: GITHUB_ACTION=true GORM_DIALECT=tidb GORM_DSN="root:@tcp(localhost:9940)/test?charset=utf8&parseTime=True&loc=Local" ./tests/tests_all.sh
|
||||
|
||||
gaussdb:
|
||||
strategy:
|
||||
matrix:
|
||||
dbversion: ['opengauss/opengauss:7.0.0-RC1.B023']
|
||||
go: ['1.23', '1.24']
|
||||
platform: [ubuntu-latest] # can not run in macOS and Windows
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
services:
|
||||
gaussdb:
|
||||
image: ${{ matrix.dbversion }}
|
||||
env:
|
||||
# GaussDB has password limitations
|
||||
GS_PASSWORD: Gaussdb@123
|
||||
TZ: Asia/Shanghai
|
||||
ports:
|
||||
- 9950:5432
|
||||
|
||||
steps:
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ matrix.go }}
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Waiting for GaussDB to be ready
|
||||
run: |
|
||||
container_name=$(docker ps --filter "ancestor=opengauss/opengauss:7.0.0-RC1.B023" --format "{{.Names}}")
|
||||
if [ -z "$container_name" ]; then
|
||||
echo "Error: failed to find a container created from the 'opengauss/opengauss:7.0.0-RC1.B023' image."
|
||||
exit 1
|
||||
fi
|
||||
max_retries=12
|
||||
retry_count=0
|
||||
if [ -t 0 ]; then
|
||||
TTY_FLAG="-t"
|
||||
else
|
||||
TTY_FLAG=""
|
||||
fi
|
||||
while [ $retry_count -lt $max_retries ]; do
|
||||
if docker exec -i "${container_name}" bash -c "su - omm -c 'gsql -U omm -c \"select 1;\"'"
|
||||
then
|
||||
echo "Creating database gorm..."
|
||||
sql_file='/tmp/create_database.sql'
|
||||
echo "CREATE DATABASE gorm DBCOMPATIBILITY 'PG';" > ${sql_file}
|
||||
docker cp "${sql_file}" "${container_name}":"${sql_file}"
|
||||
docker exec -i ${TTY_FLAG} "${container_name}" bash -c "su - omm -c 'gsql -U omm -f ${sql_file}'"
|
||||
echo "Database initialization completed."
|
||||
break
|
||||
fi
|
||||
|
||||
echo "Waiting for database to be ready... (attempt $((retry_count + 1))/$max_retries)"
|
||||
sleep 10
|
||||
((++retry_count))
|
||||
done
|
||||
exit 0
|
||||
|
||||
- name: go mod package cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ matrix.go }}-${{ hashFiles('tests/go.mod') }}
|
||||
|
||||
- name: Tests
|
||||
run: GITHUB_ACTION=true GORM_DIALECT=gaussdb GORM_DSN="user=gaussdb password=Gaussdb@123 dbname=gorm host=localhost port=9950 sslmode=disable TimeZone=Asia/Shanghai" ./tests/tests_all.sh
|
@ -206,9 +206,9 @@ func TestDeleteSliceWithAssociations(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// only sqlite, postgres, sqlserver support returning
|
||||
// only sqlite, postgres, gaussdb, sqlserver support returning
|
||||
func TestSoftDeleteReturning(t *testing.T) {
|
||||
if DB.Dialector.Name() != "sqlite" && DB.Dialector.Name() != "postgres" && DB.Dialector.Name() != "sqlserver" {
|
||||
if DB.Dialector.Name() != "sqlite" && DB.Dialector.Name() != "postgres" && DB.Dialector.Name() != "gaussdb" && DB.Dialector.Name() != "sqlserver" {
|
||||
return
|
||||
}
|
||||
|
||||
@ -233,7 +233,7 @@ func TestSoftDeleteReturning(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteReturning(t *testing.T) {
|
||||
if DB.Dialector.Name() != "sqlite" && DB.Dialector.Name() != "postgres" && DB.Dialector.Name() != "sqlserver" {
|
||||
if DB.Dialector.Name() != "sqlite" && DB.Dialector.Name() != "postgres" && DB.Dialector.Name() != "gaussdb" && DB.Dialector.Name() != "sqlserver" {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -39,7 +39,7 @@ func TestSupportedDialectorWithErrDuplicatedKey(t *testing.T) {
|
||||
t.Fatalf("failed to connect database, got error %v", err)
|
||||
}
|
||||
|
||||
dialectors := map[string]bool{"sqlite": true, "postgres": true, "mysql": true, "sqlserver": true}
|
||||
dialectors := map[string]bool{"sqlite": true, "postgres": true, "gaussdb": true, "mysql": true, "sqlserver": true}
|
||||
if supported, found := dialectors[db.Dialector.Name()]; !(found && supported) {
|
||||
return
|
||||
}
|
||||
@ -81,7 +81,7 @@ func TestSupportedDialectorWithErrForeignKeyViolated(t *testing.T) {
|
||||
t.Fatalf("failed to connect database, got error %v", err)
|
||||
}
|
||||
|
||||
dialectors := map[string]bool{"sqlite": true, "postgres": true, "mysql": true, "sqlserver": true}
|
||||
dialectors := map[string]bool{"sqlite": true, "postgres": true, "gaussdb": true, "mysql": true, "sqlserver": true}
|
||||
if supported, found := dialectors[db.Dialector.Name()]; !(found && supported) {
|
||||
return
|
||||
}
|
||||
|
248
tests/gaussdb_test.go
Normal file
248
tests/gaussdb_test.go
Normal file
@ -0,0 +1,248 @@
|
||||
package tests_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/lib/pq"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
. "gorm.io/gorm/utils/tests"
|
||||
)
|
||||
|
||||
func TestGaussDBReturningIDWhichHasStringType(t *testing.T) {
|
||||
t.Skipf("This test case skipped, because of gaussdb not support pgcrypto extension and gen_random_uuid() function")
|
||||
if DB.Dialector.Name() != "gaussdb" {
|
||||
t.Skip()
|
||||
}
|
||||
|
||||
type Yasuo struct {
|
||||
// TODO: function gen_random_uuid() does not exist
|
||||
ID string `gorm:"default:gen_random_uuid()"`
|
||||
Name string
|
||||
CreatedAt time.Time `gorm:"type:TIMESTAMP WITHOUT TIME ZONE"`
|
||||
UpdatedAt time.Time `gorm:"type:TIMESTAMP WITHOUT TIME ZONE;default:current_timestamp"`
|
||||
}
|
||||
|
||||
if err := DB.Exec("CREATE EXTENSION IF NOT EXISTS pgcrypto;").Error; err != nil {
|
||||
t.Errorf("Failed to create extension pgcrypto, got error %v", err)
|
||||
}
|
||||
|
||||
DB.Migrator().DropTable(&Yasuo{})
|
||||
|
||||
if err := DB.AutoMigrate(&Yasuo{}); err != nil {
|
||||
t.Fatalf("Failed to migrate for uuid default value, got error: %v", err)
|
||||
}
|
||||
|
||||
yasuo := Yasuo{Name: "jinzhu"}
|
||||
if err := DB.Create(&yasuo).Error; err != nil {
|
||||
t.Fatalf("should be able to create data, but got %v", err)
|
||||
}
|
||||
|
||||
if yasuo.ID == "" {
|
||||
t.Fatal("should be able to has ID, but got zero value")
|
||||
}
|
||||
|
||||
var result Yasuo
|
||||
if err := DB.First(&result, "id = ?", yasuo.ID).Error; err != nil || yasuo.Name != "jinzhu" {
|
||||
t.Errorf("No error should happen, but got %v", err)
|
||||
}
|
||||
|
||||
if err := DB.Where("id = $1", yasuo.ID).First(&Yasuo{}).Error; err != nil || yasuo.Name != "jinzhu" {
|
||||
t.Errorf("No error should happen, but got %v", err)
|
||||
}
|
||||
|
||||
yasuo.Name = "jinzhu1"
|
||||
if err := DB.Save(&yasuo).Error; err != nil {
|
||||
t.Errorf("Failed to update date, got error %v", err)
|
||||
}
|
||||
|
||||
if err := DB.First(&result, "id = ?", yasuo.ID).Error; err != nil || yasuo.Name != "jinzhu1" {
|
||||
t.Errorf("No error should happen, but got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGaussDB(t *testing.T) {
|
||||
t.Skipf("This test case skipped, because of gaussdb not support pgcrypto extension and gen_random_uuid() function")
|
||||
if DB.Dialector.Name() != "gaussdb" {
|
||||
t.Skip()
|
||||
}
|
||||
|
||||
type Harumph struct {
|
||||
gorm.Model
|
||||
Name string `gorm:"check:name_checker,name <> ''"`
|
||||
// TODO: function gen_random_uuid() does not exist
|
||||
Test uuid.UUID `gorm:"type:uuid;not null;default:gen_random_uuid()"`
|
||||
CreatedAt time.Time `gorm:"type:TIMESTAMP WITHOUT TIME ZONE"`
|
||||
UpdatedAt time.Time `gorm:"type:TIMESTAMP WITHOUT TIME ZONE;default:current_timestamp"`
|
||||
Things pq.StringArray `gorm:"type:text[]"`
|
||||
}
|
||||
|
||||
if err := DB.Exec("CREATE EXTENSION IF NOT EXISTS pgcrypto;").Error; err != nil {
|
||||
t.Errorf("Failed to create extension pgcrypto, got error %v", err)
|
||||
}
|
||||
|
||||
DB.Migrator().DropTable(&Harumph{})
|
||||
|
||||
if err := DB.AutoMigrate(&Harumph{}); err != nil {
|
||||
t.Fatalf("Failed to migrate for uuid default value, got error: %v", err)
|
||||
}
|
||||
|
||||
harumph := Harumph{}
|
||||
if err := DB.Create(&harumph).Error; err == nil {
|
||||
t.Fatalf("should failed to create data, name can't be blank")
|
||||
}
|
||||
|
||||
harumph = Harumph{Name: "jinzhu"}
|
||||
if err := DB.Create(&harumph).Error; err != nil {
|
||||
t.Fatalf("should be able to create data, but got %v", err)
|
||||
}
|
||||
|
||||
var result Harumph
|
||||
if err := DB.First(&result, "id = ?", harumph.ID).Error; err != nil || harumph.Name != "jinzhu" {
|
||||
t.Errorf("No error should happen, but got %v", err)
|
||||
}
|
||||
|
||||
if err := DB.Where("id = $1", harumph.ID).First(&Harumph{}).Error; err != nil || harumph.Name != "jinzhu" {
|
||||
t.Errorf("No error should happen, but got %v", err)
|
||||
}
|
||||
|
||||
harumph.Name = "jinzhu1"
|
||||
if err := DB.Save(&harumph).Error; err != nil {
|
||||
t.Errorf("Failed to update date, got error %v", err)
|
||||
}
|
||||
|
||||
if err := DB.First(&result, "id = ?", harumph.ID).Error; err != nil || harumph.Name != "jinzhu1" {
|
||||
t.Errorf("No error should happen, but got %v", err)
|
||||
}
|
||||
|
||||
DB.Migrator().DropTable("log_usage")
|
||||
|
||||
if err := DB.Exec(`
|
||||
CREATE TABLE public.log_usage (
|
||||
log_id bigint NOT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE public.log_usage ALTER COLUMN log_id ADD GENERATED BY DEFAULT AS IDENTITY (
|
||||
SEQUENCE NAME public.log_usage_log_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1
|
||||
);
|
||||
`).Error; err != nil {
|
||||
t.Fatalf("failed to create table, got error %v", err)
|
||||
}
|
||||
|
||||
columns, err := DB.Migrator().ColumnTypes("log_usage")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get columns, got error %v", err)
|
||||
}
|
||||
|
||||
hasLogID := false
|
||||
for _, column := range columns {
|
||||
if column.Name() == "log_id" {
|
||||
hasLogID = true
|
||||
autoIncrement, ok := column.AutoIncrement()
|
||||
if !ok || !autoIncrement {
|
||||
t.Fatalf("column log_id should be auto incrementment")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !hasLogID {
|
||||
t.Fatalf("failed to found column log_id")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGaussDBMany2ManyWithDefaultValueUUID(t *testing.T) {
|
||||
t.Skipf("This test case skipped, because of gaussdb does not have 'uuid-ossp' extension")
|
||||
if DB.Dialector.Name() != "gaussdb" {
|
||||
t.Skip()
|
||||
}
|
||||
|
||||
if err := DB.Exec(`create extension if not exists "uuid-ossp"`).Error; err != nil {
|
||||
t.Fatalf("Failed to create 'uuid-ossp' extension, but got error %v", err)
|
||||
}
|
||||
|
||||
DB.Migrator().DropTable(&Post{}, &Category{}, "post_categories")
|
||||
DB.AutoMigrate(&Post{}, &Category{})
|
||||
|
||||
post := Post{
|
||||
Title: "Hello World",
|
||||
Categories: []*Category{
|
||||
{Title: "Coding"},
|
||||
{Title: "Golang"},
|
||||
},
|
||||
}
|
||||
|
||||
if err := DB.Create(&post).Error; err != nil {
|
||||
t.Errorf("Failed, got error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGaussDBOnConstraint(t *testing.T) {
|
||||
t.Skipf("This test case skipped, because of gaussdb not support 'ON CONSTRAINT' statement")
|
||||
if DB.Dialector.Name() != "gaussdb" {
|
||||
t.Skip()
|
||||
}
|
||||
|
||||
type Thing struct {
|
||||
gorm.Model
|
||||
SomeID string
|
||||
OtherID string
|
||||
Data string
|
||||
}
|
||||
|
||||
DB.Migrator().DropTable(&Thing{})
|
||||
DB.Migrator().CreateTable(&Thing{})
|
||||
if err := DB.Exec("ALTER TABLE things ADD CONSTRAINT some_id_other_id_unique UNIQUE (some_id, other_id)").Error; err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
thing := Thing{
|
||||
SomeID: "1234",
|
||||
OtherID: "1234",
|
||||
Data: "something",
|
||||
}
|
||||
|
||||
DB.Create(&thing)
|
||||
|
||||
thing2 := Thing{
|
||||
SomeID: "1234",
|
||||
OtherID: "1234",
|
||||
Data: "something else",
|
||||
}
|
||||
|
||||
result := DB.Clauses(clause.OnConflict{
|
||||
OnConstraint: "some_id_other_id_unique",
|
||||
UpdateAll: true,
|
||||
}).Create(&thing2)
|
||||
if result.Error != nil {
|
||||
t.Errorf("creating second thing: %v", result.Error)
|
||||
}
|
||||
|
||||
var things []Thing
|
||||
if err := DB.Find(&things).Error; err != nil {
|
||||
t.Errorf("Failed, got error: %v", err)
|
||||
}
|
||||
|
||||
if len(things) > 1 {
|
||||
t.Errorf("expected 1 thing got more")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGaussDBAlterColumnDataType(t *testing.T) {
|
||||
if DB.Dialector.Name() != "gaussdb" {
|
||||
t.Skip()
|
||||
}
|
||||
DB.Migrator().DropTable(&Company{})
|
||||
DB.AutoMigrate(Company{})
|
||||
if err := DB.Table("companies").Migrator().AlterColumn(CompanyNew{}, "name"); err != nil {
|
||||
t.Fatalf("failed to alter column from string to int, got error %v", err)
|
||||
}
|
||||
|
||||
DB.AutoMigrate(Company{})
|
||||
}
|
@ -7,6 +7,7 @@ require (
|
||||
github.com/jinzhu/now v1.1.5
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/stretchr/testify v1.10.0
|
||||
gorm.io/driver/gaussdb v0.1.0
|
||||
gorm.io/driver/mysql v1.6.0
|
||||
gorm.io/driver/postgres v1.6.0
|
||||
gorm.io/driver/sqlite v1.6.0
|
||||
@ -16,6 +17,7 @@ require (
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/HuaweiCloudDeveloper/gaussdb-go v1.0.0-rc1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/go-sql-driver/mysql v1.9.2 // indirect
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
||||
@ -30,10 +32,11 @@ require (
|
||||
github.com/microsoft/go-mssqldb v1.8.2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
||||
github.com/tjfoc/gmsm v1.4.1 // indirect
|
||||
golang.org/x/crypto v0.38.0 // indirect
|
||||
golang.org/x/sync v0.14.0 // indirect
|
||||
golang.org/x/text v0.25.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
replace gorm.io/gorm => ../
|
||||
replace gorm.io/gorm => ../
|
@ -12,6 +12,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gorm.io/driver/gaussdb"
|
||||
"gorm.io/driver/postgres"
|
||||
|
||||
"gorm.io/gorm"
|
||||
@ -82,8 +83,8 @@ func TestMigrate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAutoMigrateInt8PG(t *testing.T) {
|
||||
if DB.Dialector.Name() != "postgres" {
|
||||
func TestAutoMigrateInt8PGAndGaussDB(t *testing.T) {
|
||||
if DB.Dialector.Name() != "postgres" && DB.Dialector.Name() != "gaussdb" {
|
||||
return
|
||||
}
|
||||
|
||||
@ -182,7 +183,94 @@ func TestAutoMigrateNullable(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSmartMigrateColumn(t *testing.T) {
|
||||
fullSupported := map[string]bool{"mysql": true, "postgres": true}[DB.Dialector.Name()]
|
||||
fullSupported := map[string]bool{"mysql": true, "postgres": true, "gaussdb": true}[DB.Dialector.Name()]
|
||||
|
||||
type UserMigrateColumn struct {
|
||||
ID uint
|
||||
Name string
|
||||
Salary float64
|
||||
Birthday time.Time `gorm:"precision:4"`
|
||||
}
|
||||
|
||||
DB.Migrator().DropTable(&UserMigrateColumn{})
|
||||
|
||||
DB.AutoMigrate(&UserMigrateColumn{})
|
||||
|
||||
type UserMigrateColumn2 struct {
|
||||
ID uint
|
||||
Name string `gorm:"size:128"`
|
||||
Salary float64 `gorm:"precision:2"`
|
||||
Birthday time.Time `gorm:"precision:2"`
|
||||
NameIgnoreMigration string `gorm:"size:100"`
|
||||
}
|
||||
|
||||
if err := DB.Table("user_migrate_columns").AutoMigrate(&UserMigrateColumn2{}); err != nil {
|
||||
t.Fatalf("failed to auto migrate, got error: %v", err)
|
||||
}
|
||||
|
||||
columnTypes, err := DB.Table("user_migrate_columns").Migrator().ColumnTypes(&UserMigrateColumn{})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get column types, got error: %v", err)
|
||||
}
|
||||
|
||||
for _, columnType := range columnTypes {
|
||||
switch columnType.Name() {
|
||||
case "name":
|
||||
if length, _ := columnType.Length(); (fullSupported || length != 0) && length != 128 {
|
||||
t.Fatalf("name's length should be 128, but got %v", length)
|
||||
}
|
||||
case "salary":
|
||||
if precision, o, _ := columnType.DecimalSize(); (fullSupported || precision != 0) && precision != 2 {
|
||||
t.Fatalf("salary's precision should be 2, but got %v %v", precision, o)
|
||||
}
|
||||
case "birthday":
|
||||
if precision, _, _ := columnType.DecimalSize(); (fullSupported || precision != 0) && precision != 2 {
|
||||
t.Fatalf("birthday's precision should be 2, but got %v", precision)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type UserMigrateColumn3 struct {
|
||||
ID uint
|
||||
Name string `gorm:"size:256"`
|
||||
Salary float64 `gorm:"precision:3"`
|
||||
Birthday time.Time `gorm:"precision:3"`
|
||||
NameIgnoreMigration string `gorm:"size:128;-:migration"`
|
||||
}
|
||||
|
||||
if err := DB.Table("user_migrate_columns").AutoMigrate(&UserMigrateColumn3{}); err != nil {
|
||||
t.Fatalf("failed to auto migrate, got error: %v", err)
|
||||
}
|
||||
|
||||
columnTypes, err = DB.Table("user_migrate_columns").Migrator().ColumnTypes(&UserMigrateColumn{})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get column types, got error: %v", err)
|
||||
}
|
||||
|
||||
for _, columnType := range columnTypes {
|
||||
switch columnType.Name() {
|
||||
case "name":
|
||||
if length, _ := columnType.Length(); (fullSupported || length != 0) && length != 256 {
|
||||
t.Fatalf("name's length should be 128, but got %v", length)
|
||||
}
|
||||
case "salary":
|
||||
if precision, _, _ := columnType.DecimalSize(); (fullSupported || precision != 0) && precision != 3 {
|
||||
t.Fatalf("salary's precision should be 2, but got %v", precision)
|
||||
}
|
||||
case "birthday":
|
||||
if precision, _, _ := columnType.DecimalSize(); (fullSupported || precision != 0) && precision != 3 {
|
||||
t.Fatalf("birthday's precision should be 2, but got %v", precision)
|
||||
}
|
||||
case "name_ignore_migration":
|
||||
if length, _ := columnType.Length(); (fullSupported || length != 0) && length != 100 {
|
||||
t.Fatalf("name_ignore_migration's length should still be 100 but got %v", length)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSmartMigrateColumnGaussDB(t *testing.T) {
|
||||
fullSupported := map[string]bool{"mysql": true, "gaussdb": true}[DB.Dialector.Name()]
|
||||
|
||||
type UserMigrateColumn struct {
|
||||
ID uint
|
||||
@ -850,7 +938,7 @@ func TestMigrateColumnOrder(t *testing.T) {
|
||||
|
||||
// https://github.com/go-gorm/gorm/issues/5047
|
||||
func TestMigrateSerialColumn(t *testing.T) {
|
||||
if DB.Dialector.Name() != "postgres" {
|
||||
if DB.Dialector.Name() != "postgres" && DB.Dialector.Name() != "gaussdb" {
|
||||
return
|
||||
}
|
||||
|
||||
@ -1009,6 +1097,42 @@ func TestPrimarykeyID(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrimarykeyIDGaussDB(t *testing.T) {
|
||||
t.Skipf("This test case skipped, because of gaussdb not support uuid-ossp plugin (SQLSTATE 58P01)")
|
||||
if DB.Dialector.Name() != "gaussdb" {
|
||||
return
|
||||
}
|
||||
|
||||
type MissPKLanguage struct {
|
||||
ID string `gorm:"type:uuid;default:uuid_generate_v4()"`
|
||||
Name string
|
||||
}
|
||||
|
||||
type MissPKUser struct {
|
||||
ID string `gorm:"type:uuid;default:uuid_generate_v4()"`
|
||||
MissPKLanguages []MissPKLanguage `gorm:"many2many:miss_pk_user_languages;"`
|
||||
}
|
||||
|
||||
var err error
|
||||
err = DB.Migrator().DropTable(&MissPKUser{}, &MissPKLanguage{})
|
||||
if err != nil {
|
||||
t.Fatalf("DropTable err:%v", err)
|
||||
}
|
||||
// TODO: ERROR: could not open extension control file: No such file or directory (SQLSTATE 58P01)
|
||||
DB.Exec(`CREATE EXTENSION IF NOT EXISTS "uuid-ossp";`)
|
||||
|
||||
err = DB.AutoMigrate(&MissPKUser{}, &MissPKLanguage{})
|
||||
if err != nil {
|
||||
t.Fatalf("AutoMigrate err:%v", err)
|
||||
}
|
||||
|
||||
// patch
|
||||
err = DB.AutoMigrate(&MissPKUser{}, &MissPKLanguage{})
|
||||
if err != nil {
|
||||
t.Fatalf("AutoMigrate err:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCurrentTimestamp(t *testing.T) {
|
||||
if DB.Dialector.Name() != "mysql" {
|
||||
return
|
||||
@ -1209,6 +1333,43 @@ func TestInvalidCachedPlanSimpleProtocol(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: ERROR: must have at least one column (SQLSTATE 0A000)
|
||||
func TestInvalidCachedPlanSimpleProtocolGaussDB(t *testing.T) {
|
||||
t.Skipf("This test case skipped, because of gaussdb not support creaing empty table(SQLSTATE 0A000)")
|
||||
if DB.Dialector.Name() != "gaussdb" {
|
||||
return
|
||||
}
|
||||
|
||||
db, err := gorm.Open(gaussdb.Open(gaussdbDSN), &gorm.Config{})
|
||||
if err != nil {
|
||||
t.Errorf("Open err:%v", err)
|
||||
}
|
||||
|
||||
type Object1 struct{}
|
||||
type Object2 struct {
|
||||
Field1 string
|
||||
}
|
||||
type Object3 struct {
|
||||
Field2 string
|
||||
}
|
||||
db.Migrator().DropTable("objects")
|
||||
|
||||
err = db.Table("objects").AutoMigrate(&Object1{})
|
||||
if err != nil {
|
||||
t.Errorf("AutoMigrate err:%v", err)
|
||||
}
|
||||
|
||||
err = db.Table("objects").AutoMigrate(&Object2{})
|
||||
if err != nil {
|
||||
t.Errorf("AutoMigrate err:%v", err)
|
||||
}
|
||||
|
||||
err = db.Table("objects").AutoMigrate(&Object3{})
|
||||
if err != nil {
|
||||
t.Errorf("AutoMigrate err:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDifferentTypeWithoutDeclaredLength(t *testing.T) {
|
||||
type DiffType struct {
|
||||
ID uint
|
||||
@ -1249,7 +1410,7 @@ func TestDifferentTypeWithoutDeclaredLength(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMigrateArrayTypeModel(t *testing.T) {
|
||||
if DB.Dialector.Name() != "postgres" {
|
||||
if DB.Dialector.Name() != "postgres" && DB.Dialector.Name() != "gaussdb" {
|
||||
return
|
||||
}
|
||||
|
||||
@ -1571,8 +1732,8 @@ func TestMigrateView(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMigrateExistingBoolColumnPG(t *testing.T) {
|
||||
if DB.Dialector.Name() != "postgres" {
|
||||
func TestMigrateExistingBoolColumnPGAndGaussDB(t *testing.T) {
|
||||
if DB.Dialector.Name() != "postgres" && DB.Dialector.Name() != "gaussdb" {
|
||||
return
|
||||
}
|
||||
|
||||
@ -1965,7 +2126,7 @@ func TestAutoMigrateDecimal(t *testing.T) {
|
||||
`ALTER TABLE "migrate_decimal_columns" ALTER COLUMN "recid3" decimal(9,2) NOT NULL`,
|
||||
}
|
||||
decimalColumnsTest[MigrateDecimalColumn, MigrateDecimalColumn2](t, expectedSql)
|
||||
} else if DB.Dialector.Name() == "postgres" {
|
||||
} else if DB.Dialector.Name() == "postgres" || DB.Dialector.Name() == "gaussdb" {
|
||||
type MigrateDecimalColumn struct {
|
||||
RecID1 int64 `gorm:"column:recid1;type:numeric(9,0);not null" json:"recid1"`
|
||||
RecID2 int64 `gorm:"column:recid2;type:numeric(8);not null" json:"recid2"`
|
||||
|
@ -41,7 +41,7 @@ func TestManyToManyWithMultiPrimaryKeys(t *testing.T) {
|
||||
t.Skip("skip sqlite, sqlserver due to it doesn't support multiple primary keys with auto increment")
|
||||
}
|
||||
|
||||
if name := DB.Dialector.Name(); name == "postgres" || name == "mysql" {
|
||||
if name := DB.Dialector.Name(); name == "postgres" || name == "mysql" || name == "gaussdb" {
|
||||
stmt := gorm.Statement{DB: DB}
|
||||
stmt.Parse(&Blog{})
|
||||
stmt.Schema.LookUpField("ID").Unique = true
|
||||
@ -142,6 +142,9 @@ func TestManyToManyWithCustomizedForeignKeys(t *testing.T) {
|
||||
if name := DB.Dialector.Name(); name == "postgres" {
|
||||
t.Skip("skip postgres due to it only allow unique constraint matching given keys")
|
||||
}
|
||||
if name := DB.Dialector.Name(); name == "gaussdb" {
|
||||
t.Skip("skip gaussdb due to it only allow unique constraint matching given keys")
|
||||
}
|
||||
|
||||
DB.Migrator().DropTable(&Blog{}, &Tag{}, "blog_tags", "locale_blog_tags", "shared_blog_tags")
|
||||
if err := DB.AutoMigrate(&Blog{}, &Tag{}); err != nil {
|
||||
@ -268,6 +271,10 @@ func TestManyToManyWithCustomizedForeignKeys2(t *testing.T) {
|
||||
t.Skip("skip postgres due to it only allow unique constraint matching given keys")
|
||||
}
|
||||
|
||||
if name := DB.Dialector.Name(); name == "gaussdb" {
|
||||
t.Skip("skip gaussdb due to it only allow unique constraint matching given keys")
|
||||
}
|
||||
|
||||
DB.Migrator().DropTable(&Blog{}, &Tag{}, "blog_tags", "locale_blog_tags", "shared_blog_tags")
|
||||
if err := DB.AutoMigrate(&Blog{}, &Tag{}); err != nil {
|
||||
t.Fatalf("Failed to auto migrate, got error: %v", err)
|
||||
|
@ -45,7 +45,7 @@ type SerializerPostgresStruct struct {
|
||||
func (*SerializerPostgresStruct) TableName() string { return "serializer_structs" }
|
||||
|
||||
func adaptorSerializerModel(s *SerializerStruct) interface{} {
|
||||
if DB.Dialector.Name() == "postgres" {
|
||||
if DB.Dialector.Name() == "postgres" || DB.Dialector.Name() == "gaussdb" {
|
||||
sps := SerializerPostgresStruct(*s)
|
||||
return &sps
|
||||
}
|
||||
|
@ -487,7 +487,7 @@ func replaceQuoteInSQL(sql string) string {
|
||||
|
||||
// convert dialect special quote into double quote
|
||||
switch DB.Dialector.Name() {
|
||||
case "postgres":
|
||||
case "postgres", "gaussdb":
|
||||
sql = strings.ReplaceAll(sql, `"`, `"`)
|
||||
case "mysql", "sqlite":
|
||||
sql = strings.ReplaceAll(sql, "`", `"`)
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"gorm.io/driver/gaussdb"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/schema"
|
||||
@ -251,6 +252,82 @@ func TestPostgresTableWithIdentifierLength(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestGaussDBTableWithIdentifierLength(t *testing.T) {
|
||||
if DB.Dialector.Name() != "gaussdb" {
|
||||
return
|
||||
}
|
||||
|
||||
type LongString struct {
|
||||
ThisIsAVeryVeryVeryVeryVeryVeryVeryVeryVeryLongString string `gorm:"unique"`
|
||||
}
|
||||
|
||||
t.Run("default", func(t *testing.T) {
|
||||
db, _ := gorm.Open(gaussdb.Open(gaussdbDSN), &gorm.Config{})
|
||||
user, err := schema.Parse(&LongString{}, &sync.Map{}, db.Config.NamingStrategy)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to parse user unique, got error %v", err)
|
||||
}
|
||||
|
||||
constraints := user.ParseUniqueConstraints()
|
||||
if len(constraints) != 1 {
|
||||
t.Fatalf("failed to find unique constraint, got %v", constraints)
|
||||
}
|
||||
|
||||
for key := range constraints {
|
||||
if len(key) != 63 {
|
||||
t.Errorf("failed to find unique constraint, got %v", constraints)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("naming strategy", func(t *testing.T) {
|
||||
db, _ := gorm.Open(gaussdb.Open(gaussdbDSN), &gorm.Config{
|
||||
NamingStrategy: schema.NamingStrategy{},
|
||||
})
|
||||
|
||||
user, err := schema.Parse(&LongString{}, &sync.Map{}, db.Config.NamingStrategy)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to parse user unique, got error %v", err)
|
||||
}
|
||||
|
||||
constraints := user.ParseUniqueConstraints()
|
||||
if len(constraints) != 1 {
|
||||
t.Fatalf("failed to find unique constraint, got %v", constraints)
|
||||
}
|
||||
|
||||
for key := range constraints {
|
||||
if len(key) != 63 {
|
||||
t.Errorf("failed to find unique constraint, got %v", constraints)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("namer", func(t *testing.T) {
|
||||
uname := "custom_unique_name"
|
||||
db, _ := gorm.Open(gaussdb.Open(gaussdbDSN), &gorm.Config{
|
||||
NamingStrategy: mockUniqueNamingStrategy{
|
||||
UName: uname,
|
||||
},
|
||||
})
|
||||
|
||||
user, err := schema.Parse(&LongString{}, &sync.Map{}, db.Config.NamingStrategy)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to parse user unique, got error %v", err)
|
||||
}
|
||||
|
||||
constraints := user.ParseUniqueConstraints()
|
||||
if len(constraints) != 1 {
|
||||
t.Fatalf("failed to find unique constraint, got %v", constraints)
|
||||
}
|
||||
|
||||
for key := range constraints {
|
||||
if key != uname {
|
||||
t.Errorf("failed to find unique constraint, got %v", constraints)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
type mockUniqueNamingStrategy struct {
|
||||
UName string
|
||||
schema.NamingStrategy
|
||||
|
@ -1,6 +1,6 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
dialects=("sqlite" "mysql" "postgres" "sqlserver" "tidb")
|
||||
dialects=("sqlite" "mysql" "postgres" "gaussdb" "sqlserver" "tidb")
|
||||
|
||||
if [[ $(pwd) == *"gorm/tests"* ]]; then
|
||||
cd ..
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"gorm.io/driver/gaussdb"
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/driver/sqlite"
|
||||
@ -21,6 +22,7 @@ var DB *gorm.DB
|
||||
var (
|
||||
mysqlDSN = "gorm:gorm@tcp(localhost:9910)/gorm?charset=utf8&parseTime=True&loc=Local"
|
||||
postgresDSN = "user=gorm password=gorm dbname=gorm host=localhost port=9920 sslmode=disable TimeZone=Asia/Shanghai"
|
||||
gaussdbDSN = "user=gaussdb password=Gaussdb@123 dbname=gorm host=localhost port=9950 sslmode=disable TimeZone=Asia/Shanghai"
|
||||
sqlserverDSN = "sqlserver://sa:LoremIpsum86@localhost:9930?database=master"
|
||||
tidbDSN = "root:@tcp(localhost:9940)/test?charset=utf8&parseTime=True&loc=Local"
|
||||
)
|
||||
@ -65,6 +67,15 @@ func OpenTestConnection(cfg *gorm.Config) (db *gorm.DB, err error) {
|
||||
DSN: dbDSN,
|
||||
PreferSimpleProtocol: true,
|
||||
}), cfg)
|
||||
case "gaussdb":
|
||||
log.Println("testing gaussdb...")
|
||||
if dbDSN == "" {
|
||||
dbDSN = gaussdbDSN
|
||||
}
|
||||
db, err = gorm.Open(gaussdb.New(gaussdb.Config{
|
||||
DSN: dbDSN,
|
||||
PreferSimpleProtocol: true,
|
||||
}), cfg)
|
||||
case "sqlserver":
|
||||
// go install github.com/microsoft/go-sqlcmd/cmd/sqlcmd@latest
|
||||
// SQLCMDPASSWORD=LoremIpsum86 sqlcmd -U sa -S localhost:9930
|
||||
|
@ -765,9 +765,9 @@ func TestSaveWithPrimaryValue(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// only sqlite, postgres, sqlserver support returning
|
||||
// only sqlite, postgres, gaussdb, sqlserver support returning
|
||||
func TestUpdateReturning(t *testing.T) {
|
||||
if DB.Dialector.Name() != "sqlite" && DB.Dialector.Name() != "postgres" && DB.Dialector.Name() != "sqlserver" {
|
||||
if DB.Dialector.Name() != "sqlite" && DB.Dialector.Name() != "postgres" && DB.Dialector.Name() != "gaussdb" && DB.Dialector.Name() != "sqlserver" {
|
||||
return
|
||||
}
|
||||
|
||||
@ -883,9 +883,9 @@ func TestSaveWithHooks(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// only postgres, sqlserver, sqlite support update from
|
||||
// only postgres, gaussdb, sqlserver, sqlite support update from
|
||||
func TestUpdateFrom(t *testing.T) {
|
||||
if DB.Dialector.Name() != "postgres" && DB.Dialector.Name() != "sqlite" && DB.Dialector.Name() != "sqlserver" {
|
||||
if DB.Dialector.Name() != "postgres" && DB.Dialector.Name() != "gaussdb" && DB.Dialector.Name() != "sqlite" && DB.Dialector.Name() != "sqlserver" {
|
||||
return
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user