From 2d3723822bb397008649cb5164dae49dc062b4de Mon Sep 17 00:00:00 2001 From: Charlie <3140647@qq.com> Date: Fri, 10 May 2024 11:28:44 +0800 Subject: [PATCH] oracle 11g --- create.go | 8 +-- go.sum | 48 ----------------- migrator.go | 8 +-- namer.go | 22 ++++++-- oracle.go | 148 +++++++++++++++++++++++++++++++++++++++------------- 5 files changed, 141 insertions(+), 93 deletions(-) delete mode 100644 go.sum diff --git a/create.go b/create.go index b5656be..6025e4d 100644 --- a/create.go +++ b/create.go @@ -16,13 +16,15 @@ import ( func Create(db *gorm.DB) { stmt := db.Statement + if stmt == nil { + return + } schema := stmt.Schema - boundVars := make(map[string]int) - - if stmt == nil || schema == nil { + if schema == nil { return } + boundVars := make(map[string]int) hasDefaultValues := len(schema.FieldsWithDefaultDBValue) > 0 if !stmt.Unscoped { diff --git a/go.sum b/go.sum deleted file mode 100644 index 3145790..0000000 --- a/go.sum +++ /dev/null @@ -1,48 +0,0 @@ -github.com/UNO-SOFT/zlog v0.8.1 h1:TEFkGJHtUfTRgMkLZiAjLSHALjwSBdw6/zByMC5GJt4= -github.com/UNO-SOFT/zlog v0.8.1/go.mod h1:yqFOjn3OhvJ4j7ArJqQNA+9V+u6t9zSAyIZdWdMweWc= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= -github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= -github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= -github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/godror/godror v0.43.0 h1:qMbQwG0ejJnKma3bBvrJg1rkiyP5b4v6uxvx9zDrKJw= -github.com/godror/godror v0.43.0/go.mod h1:82Uc/HdjsFVnzR5c9Yf6IkTBalK80jzm/U6xojbTo94= -github.com/godror/knownpb v0.1.1 h1:A4J7jdx7jWBhJm18NntafzSC//iZDHkDi1+juwQ5pTI= -github.com/godror/knownpb v0.1.1/go.mod h1:4nRFbQo1dDuwKnblRXDxrfCFYeT4hjg3GjMqef58eRE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= -github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= -github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= -github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= -github.com/oklog/ulid/v2 v2.0.2 h1:r4fFzBm+bv0wNKNh5eXTwU7i85y5x+uwkxCUTNVQqLc= -github.com/oklog/ulid/v2 v2.0.2/go.mod h1:mtBL0Qe/0HAx6/a4Z30qxVIAL1eQDweXq5lxOEiwQ68= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/thoas/go-funk v0.9.3 h1:7+nAEx3kn5ZJcnDm2Bh23N2yOtweO14bi//dvRtgLpw= -github.com/thoas/go-funk v0.9.3/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q= -golang.org/x/exp v0.0.0-20240318143956-a85f2c67cd81 h1:6R2FC06FonbXQ8pK11/PDFY6N6LWlf9KlzibaCapmqc= -golang.org/x/exp v0.0.0-20240318143956-a85f2c67cd81/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ= -golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= -golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= -golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 h1:w8s32wxx3sY+OjLlv9qltkLU5yvJzxjjgiHWLjdIcw4= -golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= -golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s= -gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= diff --git a/migrator.go b/migrator.go index 97d73ae..93b4c1d 100644 --- a/migrator.go +++ b/migrator.go @@ -3,9 +3,10 @@ package oracle import ( "database/sql" "fmt" - "gorm.io/gorm/schema" "strings" + "gorm.io/gorm/schema" + "gorm.io/gorm" "gorm.io/gorm/clause" "gorm.io/gorm/migrator" @@ -275,11 +276,10 @@ func (m Migrator) HasIndex(value interface{}, name string) bool { // https://docs.oracle.com/database/121/SPATL/alter-index-rename.htm func (m Migrator) RenameIndex(value interface{}, oldName, newName string) error { - panic("TODO") return m.RunWithValue(value, func(stmt *gorm.Statement) error { return m.DB.Exec( - "ALTER INDEX ?.? RENAME TO ?", // wat - clause.Table{Name: stmt.Table}, clause.Column{Name: oldName}, clause.Column{Name: newName}, + "ALTER INDEX ? RENAME TO ?", // wat + clause.Column{Name: oldName}, clause.Column{Name: newName}, ).Error }) } diff --git a/namer.go b/namer.go index a7dd323..e803ba4 100644 --- a/namer.go +++ b/namer.go @@ -1,12 +1,15 @@ package oracle import ( - "gorm.io/gorm/schema" + "fmt" "strings" + + "gorm.io/gorm/schema" ) type Namer struct { - schema.NamingStrategy + NamingStrategy schema.Namer + DBName string } func ConvertNameToFormat(x string) string { @@ -14,7 +17,12 @@ func ConvertNameToFormat(x string) string { } func (n Namer) TableName(table string) (name string) { - return ConvertNameToFormat(n.NamingStrategy.TableName(table)) + tableName := ConvertNameToFormat(n.NamingStrategy.TableName(table)) + if len(n.DBName) > 0 { + return fmt.Sprintf("%s.%s", n.DBName, tableName) + } + + return tableName } func (n Namer) ColumnName(table, column string) (name string) { @@ -36,3 +44,11 @@ func (n Namer) CheckerName(table, column string) (name string) { func (n Namer) IndexName(table, column string) (name string) { return ConvertNameToFormat(n.NamingStrategy.IndexName(table, column)) } + +func (n Namer) SchemaName(table string) string { + return ConvertNameToFormat(n.NamingStrategy.SchemaName(table)) +} + +func (n Namer) UniqueName(table, column string) string { + return ConvertNameToFormat(n.NamingStrategy.UniqueName(table, column)) +} diff --git a/oracle.go b/oracle.go index d747c4c..236d70d 100644 --- a/oracle.go +++ b/oracle.go @@ -4,7 +4,6 @@ import ( "context" "database/sql" "fmt" - "log" "regexp" "strconv" "strings" @@ -21,6 +20,8 @@ import ( "gorm.io/gorm/schema" ) +const RowNumberAliasForOracle11 = "ROW_NUM" + type Config struct { DriverName string DSN string @@ -51,7 +52,11 @@ func (d Dialector) Name() string { } func (d Dialector) Initialize(db *gorm.DB) (err error) { - db.NamingStrategy = Namer{db.NamingStrategy.(schema.NamingStrategy)} + + db.NamingStrategy = Namer{ + NamingStrategy: db.NamingStrategy, + DBName: d.DBName, + } d.DefaultStringSize = 1024 // register callbacks @@ -64,10 +69,15 @@ func (d Dialector) Initialize(db *gorm.DB) (err error) { d.DriverName = "godror" + // godror.Batch + if d.Conn != nil { db.ConnPool = d.Conn } else { db.ConnPool, err = sql.Open(d.DriverName, d.DSN) + if err != nil { + return + } } err = db.ConnPool.QueryRowContext(context.Background(), "select version from product_component_version where rownum = 1").Scan(&d.DBVer) if err != nil { @@ -96,7 +106,6 @@ func (d Dialector) ClauseBuilders() map[string]clause.ClauseBuilder { "LIMIT": d.RewriteLimit, } } - } func (d Dialector) RewriteLimit(c clause.Clause, builder clause.Builder) { @@ -121,9 +130,14 @@ func (d Dialector) RewriteLimit(c clause.Clause, builder clause.Builder) { builder.WriteString(strconv.Itoa(offset)) builder.WriteString(" ROWS") } - if limit := limit.Limit; *limit > 0 { + + v := 0 + if limit.Limit != nil { + v = *limit.Limit + } + if v > 0 { builder.WriteString(" FETCH NEXT ") - builder.WriteString(strconv.Itoa(*limit)) + builder.WriteString(strconv.Itoa(v)) builder.WriteString(" ROWS ONLY") } } @@ -131,34 +145,93 @@ func (d Dialector) RewriteLimit(c clause.Clause, builder clause.Builder) { // Oracle11 Limit func (d Dialector) RewriteLimit11(c clause.Clause, builder clause.Builder) { - if limit, ok := c.Expression.(clause.Limit); ok { - if stmt, ok := builder.(*gorm.Statement); ok { - limitsql := strings.Builder{} - if limit := limit.Limit; *limit > 0 { - if _, ok := stmt.Clauses["WHERE"]; !ok { - limitsql.WriteString(" WHERE ") - } else { - limitsql.WriteString(" AND ") - } - limitsql.WriteString("ROWNUM <= ") - limitsql.WriteString(strconv.Itoa(*limit)) - } - if _, ok := stmt.Clauses["ORDER BY"]; !ok { - builder.WriteString(limitsql.String()) - } else { - // "ORDER BY" before insert - sqltmp := strings.Builder{} - sqlold := stmt.SQL.String() - orderindx := strings.Index(sqlold, "ORDER BY") - 1 - sqltmp.WriteString(sqlold[:orderindx]) - sqltmp.WriteString(limitsql.String()) - sqltmp.WriteString(sqlold[orderindx:]) - log.Println(sqltmp.String()) - stmt.SQL = sqltmp - } - } + limit, ok := c.Expression.(clause.Limit) + if !ok { + return + } + offsetRows := limit.Offset + hasOffset := offsetRows > 0 + limitRows, hasLimit := d.getLimitRows(limit) + if !hasOffset && !hasLimit { + return + } + + var stmt *gorm.Statement + if stmt, ok = builder.(*gorm.Statement); !ok { + return + } + + if hasLimit && hasOffset { + subQuerySQL := fmt.Sprintf( + "SELECT * FROM (SELECT T.*, ROW_NUMBER() OVER (ORDER BY %s) AS %s FROM (%s) T) WHERE %s BETWEEN %d AND %d", + d.getOrderByColumns(stmt), + RowNumberAliasForOracle11, + strings.TrimSpace(stmt.SQL.String()), + RowNumberAliasForOracle11, + offsetRows+1, + offsetRows+limitRows, + ) + + stmt.SQL.Reset() + stmt.SQL.WriteString(subQuerySQL) + } else if hasLimit { + // 只有 Limit 的情况 + subQuerySQL := fmt.Sprintf( + "SELECT * FROM (%s) WHERE ROWNUM <= %d", + strings.TrimSpace(stmt.SQL.String()), + limitRows, + ) + // d.rewriteRownumStmt(stmt, builder, " <= ", limitRows) + + stmt.SQL.Reset() + stmt.SQL.WriteString(subQuerySQL) + } else { + // 只有 Offset 的情况 + // 偏移后取剩余所有记录 + subQuerySQL := fmt.Sprintf( + "SELECT * FROM (SELECT T.*, ROW_NUMBER() OVER (ORDER BY %s) AS %s FROM (%s) T) WHERE %s > %d", + d.getOrderByColumns(stmt), + RowNumberAliasForOracle11, + strings.TrimSpace(stmt.SQL.String()), + RowNumberAliasForOracle11, + offsetRows+1, + ) + + stmt.SQL.Reset() + stmt.SQL.WriteString(subQuerySQL) + + // d.rewriteRownumStmt(stmt, builder, " > ", offsetRows) } } + +func (d Dialector) getOrderByColumns(stmt *gorm.Statement) string { + if orderByClause, ok := stmt.Clauses["ORDER BY"]; ok { + var orderBy clause.OrderBy + if orderBy, ok = orderByClause.Expression.(clause.OrderBy); ok && len(orderBy.Columns) > 0 { + orderByBuilder := strings.Builder{} + for i, column := range orderBy.Columns { + if i > 0 { + orderByBuilder.WriteString(", ") + } + orderByBuilder.WriteString(column.Column.Name) + if column.Desc { + orderByBuilder.WriteString(" DESC") + } + } + return orderByBuilder.String() + } + } + return "NULL" +} + +func (d Dialector) getLimitRows(limit clause.Limit) (limitRows int, hasLimit bool) { + if l := limit.Limit; l != nil { + limitRows = *l + hasLimit = limitRows > 0 + } + return +} + func (d Dialector) DefaultValueOf(*schema.Field) clause.Expression { return clause.Expr{SQL: "VALUES (DEFAULT)"} } @@ -181,7 +254,14 @@ func (d Dialector) BindVarTo(writer clause.Writer, stmt *gorm.Statement, v inter } func (d Dialector) QuoteTo(writer clause.Writer, str string) { - writer.WriteString(str) + if str != "" && IsReservedWord(str) { + writer.WriteByte('"') + writer.WriteString(str) + writer.WriteByte('"') + } else { + + writer.WriteString(str) + } } var numericPlaceholder = regexp.MustCompile(`:(\d+)`) @@ -201,9 +281,7 @@ func (d Dialector) Explain(sql string, vars ...interface{}) string { } func (d Dialector) DataTypeOf(field *schema.Field) string { - if _, found := field.TagSettings["RESTRICT"]; found { - delete(field.TagSettings, "RESTRICT") - } + delete(field.TagSettings, "RESTRICT") var sqlType string