fix(go.sum): update ResolveSpec dependency to v1.0.87
This commit is contained in:
+21
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013-NOW Jinzhu <wosmvp@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
# GORM SQL Server Driver
|
||||
|
||||
## USAGE
|
||||
|
||||
```go
|
||||
import (
|
||||
"gorm.io/driver/sqlserver"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// github.com/microsoft/go-mssqldb
|
||||
dsn := "sqlserver://gorm:LoremIpsum86@localhost:9930?database=gorm"
|
||||
db, err := gorm.Open(sqlserver.Open(dsn), &gorm.Config{})
|
||||
```
|
||||
|
||||
## Azure AD Auth
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/microsoft/go-mssqldb/azuread"
|
||||
"gorm.io/driver/sqlserver"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// github.com/microsoft/go-mssqldb
|
||||
dsn := "sqlserver://gorm:LoremIpsum86@localhost:9930?database=gorm"
|
||||
dialector := &sqlserver.Dialector{Config: &sqlserver.Config{DSN: dsn, DriverName: azuread.DriverName}}
|
||||
db, err := gorm.Open(dialector, &gorm.Config{})
|
||||
```
|
||||
|
||||
Checkout [https://gorm.io](https://gorm.io) for details.
|
||||
+234
@@ -0,0 +1,234 @@
|
||||
package sqlserver
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/callbacks"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
func Create(db *gorm.DB) {
|
||||
if db.Error != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if db.Statement.Schema != nil && !db.Statement.Unscoped {
|
||||
for _, c := range db.Statement.Schema.CreateClauses {
|
||||
db.Statement.AddClause(c)
|
||||
}
|
||||
}
|
||||
|
||||
hasOutput := false
|
||||
if db.Statement.SQL.String() == "" {
|
||||
var (
|
||||
values = callbacks.ConvertToCreateValues(db.Statement)
|
||||
c = db.Statement.Clauses["ON CONFLICT"]
|
||||
onConflict, hasConflict = c.Expression.(clause.OnConflict)
|
||||
)
|
||||
|
||||
if hasConflict {
|
||||
if len(db.Statement.Schema.PrimaryFields) > 0 {
|
||||
columnsMap := map[string]bool{}
|
||||
for _, column := range values.Columns {
|
||||
columnsMap[column.Name] = true
|
||||
}
|
||||
|
||||
for _, field := range db.Statement.Schema.PrimaryFields {
|
||||
if _, ok := columnsMap[field.DBName]; !ok {
|
||||
hasConflict = false
|
||||
}
|
||||
}
|
||||
} else {
|
||||
hasConflict = false
|
||||
}
|
||||
}
|
||||
|
||||
if hasConflict {
|
||||
hasOutput = MergeCreate(db, onConflict, values)
|
||||
} else {
|
||||
setIdentityInsert := false
|
||||
|
||||
if db.Statement.Schema != nil {
|
||||
if field := db.Statement.Schema.PrioritizedPrimaryField; field != nil && field.AutoIncrement {
|
||||
switch db.Statement.ReflectValue.Kind() {
|
||||
case reflect.Struct:
|
||||
_, isZero := field.ValueOf(db.Statement.Context, db.Statement.ReflectValue)
|
||||
setIdentityInsert = !isZero
|
||||
case reflect.Slice, reflect.Array:
|
||||
for i := 0; i < db.Statement.ReflectValue.Len(); i++ {
|
||||
obj := db.Statement.ReflectValue.Index(i)
|
||||
if reflect.Indirect(obj).Kind() == reflect.Struct {
|
||||
_, isZero := field.ValueOf(db.Statement.Context, db.Statement.ReflectValue.Index(i))
|
||||
setIdentityInsert = !isZero
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if setIdentityInsert {
|
||||
db.Statement.WriteString("SET IDENTITY_INSERT ")
|
||||
db.Statement.WriteQuoted(db.Statement.Table)
|
||||
db.Statement.WriteString(" ON;")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
db.Statement.AddClauseIfNotExists(clause.Insert{})
|
||||
db.Statement.Build("INSERT")
|
||||
db.Statement.WriteByte(' ')
|
||||
|
||||
db.Statement.AddClause(values)
|
||||
if values, ok := db.Statement.Clauses["VALUES"].Expression.(clause.Values); ok {
|
||||
if len(values.Columns) > 0 {
|
||||
db.Statement.WriteByte('(')
|
||||
for idx, column := range values.Columns {
|
||||
if idx > 0 {
|
||||
db.Statement.WriteByte(',')
|
||||
}
|
||||
db.Statement.WriteQuoted(column)
|
||||
}
|
||||
db.Statement.WriteByte(')')
|
||||
|
||||
hasOutput = outputInserted(db)
|
||||
|
||||
db.Statement.WriteString(" VALUES ")
|
||||
|
||||
for idx, value := range values.Values {
|
||||
if idx > 0 {
|
||||
db.Statement.WriteByte(',')
|
||||
}
|
||||
|
||||
db.Statement.WriteByte('(')
|
||||
db.Statement.AddVar(db.Statement, value...)
|
||||
db.Statement.WriteByte(')')
|
||||
}
|
||||
|
||||
db.Statement.WriteString(";")
|
||||
} else {
|
||||
db.Statement.WriteString("DEFAULT VALUES;")
|
||||
}
|
||||
}
|
||||
|
||||
if setIdentityInsert {
|
||||
db.Statement.WriteString("SET IDENTITY_INSERT ")
|
||||
db.Statement.WriteQuoted(db.Statement.Table)
|
||||
db.Statement.WriteString(" OFF;")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !db.DryRun && db.Error == nil {
|
||||
if db.Statement.Schema != nil && hasOutput {
|
||||
rows, err := db.Statement.ConnPool.QueryContext(db.Statement.Context, db.Statement.SQL.String(), db.Statement.Vars...)
|
||||
if db.AddError(err) == nil {
|
||||
defer rows.Close()
|
||||
gorm.Scan(rows, db, gorm.ScanUpdate|gorm.ScanOnConflictDoNothing)
|
||||
if db.Statement.Result != nil {
|
||||
db.Statement.Result.RowsAffected = db.RowsAffected
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result, err := db.Statement.ConnPool.ExecContext(db.Statement.Context, db.Statement.SQL.String(), db.Statement.Vars...)
|
||||
if db.AddError(err) == nil {
|
||||
db.RowsAffected, _ = result.RowsAffected()
|
||||
if db.Statement.Result != nil {
|
||||
db.Statement.Result.Result = result
|
||||
db.Statement.Result.RowsAffected = db.RowsAffected
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func MergeCreate(db *gorm.DB, onConflict clause.OnConflict, values clause.Values) bool {
|
||||
db.Statement.WriteString("MERGE INTO ")
|
||||
db.Statement.WriteQuoted(db.Statement.Table)
|
||||
db.Statement.WriteString(" USING (VALUES")
|
||||
for idx, value := range values.Values {
|
||||
if idx > 0 {
|
||||
db.Statement.WriteByte(',')
|
||||
}
|
||||
|
||||
db.Statement.WriteByte('(')
|
||||
db.Statement.AddVar(db.Statement, value...)
|
||||
db.Statement.WriteByte(')')
|
||||
}
|
||||
|
||||
db.Statement.WriteString(") AS excluded (")
|
||||
for idx, column := range values.Columns {
|
||||
if idx > 0 {
|
||||
db.Statement.WriteByte(',')
|
||||
}
|
||||
db.Statement.WriteQuoted(column.Name)
|
||||
}
|
||||
db.Statement.WriteString(") ON ")
|
||||
|
||||
var where clause.Where
|
||||
for _, field := range db.Statement.Schema.PrimaryFields {
|
||||
where.Exprs = append(where.Exprs, clause.Eq{
|
||||
Column: clause.Column{Table: db.Statement.Table, Name: field.DBName},
|
||||
Value: clause.Column{Table: "excluded", Name: field.DBName},
|
||||
})
|
||||
}
|
||||
where.Build(db.Statement)
|
||||
|
||||
if len(onConflict.DoUpdates) > 0 {
|
||||
db.Statement.WriteString(" WHEN MATCHED THEN UPDATE SET ")
|
||||
onConflict.DoUpdates.Build(db.Statement)
|
||||
}
|
||||
|
||||
db.Statement.WriteString(" WHEN NOT MATCHED THEN INSERT (")
|
||||
|
||||
written := false
|
||||
for _, column := range values.Columns {
|
||||
if db.Statement.Schema.PrioritizedPrimaryField == nil || !db.Statement.Schema.PrioritizedPrimaryField.AutoIncrement || db.Statement.Schema.PrioritizedPrimaryField.DBName != column.Name {
|
||||
if written {
|
||||
db.Statement.WriteByte(',')
|
||||
}
|
||||
written = true
|
||||
db.Statement.WriteQuoted(column.Name)
|
||||
}
|
||||
}
|
||||
|
||||
db.Statement.WriteString(") VALUES (")
|
||||
|
||||
written = false
|
||||
for _, column := range values.Columns {
|
||||
if db.Statement.Schema.PrioritizedPrimaryField == nil || !db.Statement.Schema.PrioritizedPrimaryField.AutoIncrement || db.Statement.Schema.PrioritizedPrimaryField.DBName != column.Name {
|
||||
if written {
|
||||
db.Statement.WriteByte(',')
|
||||
}
|
||||
written = true
|
||||
db.Statement.WriteQuoted(clause.Column{
|
||||
Table: "excluded",
|
||||
Name: column.Name,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
db.Statement.WriteString(")")
|
||||
hasOutput := outputInserted(db)
|
||||
db.Statement.WriteString(";")
|
||||
return hasOutput
|
||||
}
|
||||
|
||||
func outputInserted(db *gorm.DB) (hasOutput bool) {
|
||||
if db.Statement.Schema != nil && len(db.Statement.Schema.FieldsWithDefaultDBValue) > 0 {
|
||||
for _, field := range db.Statement.Schema.FieldsWithDefaultDBValue {
|
||||
if hasOutput {
|
||||
db.Statement.WriteString(",")
|
||||
}
|
||||
if field.Readable {
|
||||
if !hasOutput {
|
||||
db.Statement.WriteString(" OUTPUT INSERTED.")
|
||||
hasOutput = true
|
||||
} else {
|
||||
db.Statement.WriteString(" INSERTED.")
|
||||
}
|
||||
db.Statement.AddVar(db.Statement, clause.Column{Name: field.DBName})
|
||||
}
|
||||
}
|
||||
}
|
||||
return hasOutput
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package sqlserver
|
||||
|
||||
import (
|
||||
"github.com/microsoft/go-mssqldb"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// The error codes to map mssql errors to gorm errors, here is a reference about error codes for mssql https://learn.microsoft.com/en-us/sql/relational-databases/errors-events/database-engine-events-and-errors?view=sql-server-ver16
|
||||
var errCodes = map[int32]error{
|
||||
2627: gorm.ErrDuplicatedKey,
|
||||
2601: gorm.ErrDuplicatedKey,
|
||||
547: gorm.ErrForeignKeyViolated,
|
||||
}
|
||||
|
||||
type ErrMessage struct {
|
||||
Number int32 `json:"Number"`
|
||||
Message string `json:"Message"`
|
||||
}
|
||||
|
||||
// Translate it will translate the error to native gorm errors.
|
||||
func (dialector Dialector) Translate(err error) error {
|
||||
if mssqlErr, ok := err.(mssql.Error); ok {
|
||||
if translatedErr, found := errCodes[mssqlErr.Number]; found {
|
||||
return translatedErr
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
+601
@@ -0,0 +1,601 @@
|
||||
package sqlserver
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
"gorm.io/gorm/logger"
|
||||
"gorm.io/gorm/migrator"
|
||||
"gorm.io/gorm/schema"
|
||||
)
|
||||
|
||||
const indexSQL = `
|
||||
SELECT
|
||||
col.name AS column_name,
|
||||
i.name AS index_name,
|
||||
i.is_unique,
|
||||
i.is_primary_key
|
||||
FROM
|
||||
sys.indexes i
|
||||
LEFT JOIN sys.index_columns ic ON ic.object_id = i.object_id AND ic.index_id = i.index_id
|
||||
LEFT JOIN sys.all_columns col ON col.column_id = ic.column_id AND col.object_id = ic.object_id
|
||||
WHERE
|
||||
i.name IS NOT NULL
|
||||
AND i.is_unique_constraint = 0
|
||||
AND i.object_id = OBJECT_ID(?)
|
||||
`
|
||||
|
||||
type Migrator struct {
|
||||
migrator.Migrator
|
||||
}
|
||||
|
||||
func (m Migrator) GetTables() (tableList []string, err error) {
|
||||
return tableList, m.DB.Raw("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_CATALOG = ?", m.CurrentDatabase()).Scan(&tableList).Error
|
||||
}
|
||||
|
||||
func (m Migrator) CreateTable(values ...interface{}) (err error) {
|
||||
if err = m.Migrator.CreateTable(values...); err != nil {
|
||||
return
|
||||
}
|
||||
for _, value := range m.ReorderModels(values, false) {
|
||||
if err = m.RunWithValue(value, func(stmt *gorm.Statement) (err error) {
|
||||
if stmt.Schema == nil {
|
||||
return
|
||||
}
|
||||
for _, fieldName := range stmt.Schema.DBNames {
|
||||
field := stmt.Schema.FieldsByDBName[fieldName]
|
||||
if _, ok := field.TagSettings["COMMENT"]; !ok {
|
||||
continue
|
||||
}
|
||||
if err = m.setColumnComment(stmt, field, true); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (m Migrator) setColumnComment(stmt *gorm.Statement, field *schema.Field, add bool) error {
|
||||
schemaName := m.getTableSchemaName(stmt.Schema)
|
||||
commentExpr := gorm.Expr(strings.ReplaceAll(field.Comment, "'", "''"))
|
||||
// add field comment
|
||||
if add {
|
||||
return m.DB.Exec(
|
||||
"EXEC sp_addextendedproperty 'MS_Description', N'?', 'SCHEMA', ?, 'TABLE', ?, 'COLUMN', ?",
|
||||
commentExpr, schemaName, stmt.Table, field.DBName,
|
||||
).Error
|
||||
}
|
||||
// update field comment
|
||||
return m.DB.Exec(
|
||||
"EXEC sp_updateextendedproperty 'MS_Description', N'?', 'SCHEMA', ?, 'TABLE', ?, 'COLUMN', ?",
|
||||
commentExpr, schemaName, stmt.Table, field.DBName,
|
||||
).Error
|
||||
}
|
||||
|
||||
func (m Migrator) getTableSchemaName(schema *schema.Schema) string {
|
||||
// return the schema name if it is explicitly provided in the table name
|
||||
// otherwise return default schema name
|
||||
schemaName := getTableSchemaName(schema)
|
||||
if schemaName == "" {
|
||||
schemaName = m.DefaultSchema()
|
||||
}
|
||||
return schemaName
|
||||
}
|
||||
|
||||
func getTableSchemaName(schema *schema.Schema) string {
|
||||
// return the schema name if it is explicitly provided in the table name
|
||||
// otherwise return a sql wildcard -> use any table_schema
|
||||
if schema == nil || !strings.Contains(schema.Table, ".") {
|
||||
return ""
|
||||
}
|
||||
_, schemaName, _ := splitFullQualifiedName(schema.Table)
|
||||
return schemaName
|
||||
}
|
||||
|
||||
func splitFullQualifiedName(name string) (string, string, string) {
|
||||
nameParts := strings.Split(name, ".")
|
||||
if len(nameParts) == 1 { // [table_name]
|
||||
return "", "", nameParts[0]
|
||||
} else if len(nameParts) == 2 { // [table_schema].[table_name]
|
||||
return "", nameParts[0], nameParts[1]
|
||||
} else if len(nameParts) == 3 { // [table_catalog].[table_schema].[table_name]
|
||||
return nameParts[0], nameParts[1], nameParts[2]
|
||||
}
|
||||
return "", "", ""
|
||||
}
|
||||
|
||||
func getFullQualifiedTableName(stmt *gorm.Statement) string {
|
||||
fullQualifiedTableName := stmt.Table
|
||||
if schemaName := getTableSchemaName(stmt.Schema); schemaName != "" {
|
||||
fullQualifiedTableName = schemaName + "." + fullQualifiedTableName
|
||||
}
|
||||
return fullQualifiedTableName
|
||||
}
|
||||
|
||||
func (m Migrator) HasTable(value interface{}) bool {
|
||||
var count int
|
||||
_ = m.RunWithValue(value, func(stmt *gorm.Statement) error {
|
||||
schemaName := getTableSchemaName(stmt.Schema)
|
||||
if schemaName == "" {
|
||||
schemaName = "%"
|
||||
}
|
||||
return m.DB.Raw(
|
||||
"SELECT count(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = ? AND TABLE_CATALOG = ? and TABLE_SCHEMA like ? AND TABLE_TYPE = ?",
|
||||
stmt.Table, m.CurrentDatabase(), schemaName, "BASE TABLE",
|
||||
).Row().Scan(&count)
|
||||
})
|
||||
return count > 0
|
||||
}
|
||||
|
||||
func (m Migrator) DropTable(values ...interface{}) error {
|
||||
values = m.ReorderModels(values, false)
|
||||
for i := len(values) - 1; i >= 0; i-- {
|
||||
tx := m.DB.Session(&gorm.Session{})
|
||||
if err := m.RunWithValue(values[i], func(stmt *gorm.Statement) error {
|
||||
type constraint struct {
|
||||
Name string
|
||||
Parent string
|
||||
}
|
||||
var constraints []constraint
|
||||
err := tx.Raw("SELECT name, OBJECT_NAME(parent_object_id) as parent FROM sys.foreign_keys WHERE referenced_object_id = object_id(?)", getFullQualifiedTableName(stmt)).Scan(&constraints).Error
|
||||
|
||||
for _, c := range constraints {
|
||||
if err == nil {
|
||||
err = tx.Exec("ALTER TABLE ? DROP CONSTRAINT ?;", gorm.Expr(c.Parent), gorm.Expr(c.Name)).Error
|
||||
}
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
err = tx.Exec("DROP TABLE IF EXISTS ?", clause.Table{Name: stmt.Table}).Error
|
||||
}
|
||||
|
||||
return err
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m Migrator) RenameTable(oldName, newName interface{}) error {
|
||||
var oldTable, newTable string
|
||||
if v, ok := oldName.(string); ok {
|
||||
oldTable = v
|
||||
} else {
|
||||
stmt := &gorm.Statement{DB: m.DB}
|
||||
if err := stmt.Parse(oldName); err == nil {
|
||||
oldTable = stmt.Table
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if v, ok := newName.(string); ok {
|
||||
newTable = v
|
||||
} else {
|
||||
stmt := &gorm.Statement{DB: m.DB}
|
||||
if err := stmt.Parse(newName); err == nil {
|
||||
newTable = stmt.Table
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return m.DB.Exec(
|
||||
"sp_rename @objname = ?, @newname = ?;",
|
||||
clause.Table{Name: oldTable}, clause.Table{Name: newTable},
|
||||
).Error
|
||||
}
|
||||
|
||||
func (m Migrator) AddColumn(value interface{}, name string) error {
|
||||
if err := m.Migrator.AddColumn(value, name); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return m.RunWithValue(value, func(stmt *gorm.Statement) (err error) {
|
||||
if stmt.Schema != nil {
|
||||
if field := stmt.Schema.LookUpField(name); field != nil {
|
||||
if _, ok := field.TagSettings["COMMENT"]; !ok {
|
||||
return
|
||||
}
|
||||
if err = m.setColumnComment(stmt, field, true); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
func (m Migrator) HasColumn(value interface{}, field string) bool {
|
||||
var count int64
|
||||
_ = m.RunWithValue(value, func(stmt *gorm.Statement) error {
|
||||
currentDatabase := m.DB.Migrator().CurrentDatabase()
|
||||
name := field
|
||||
if stmt.Schema != nil {
|
||||
if field := stmt.Schema.LookUpField(field); field != nil {
|
||||
name = field.DBName
|
||||
}
|
||||
}
|
||||
|
||||
return m.DB.Raw(
|
||||
"SELECT count(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_CATALOG = ? AND TABLE_NAME = ? AND COLUMN_NAME = ?",
|
||||
currentDatabase, stmt.Table, name,
|
||||
).Row().Scan(&count)
|
||||
})
|
||||
|
||||
return count > 0
|
||||
}
|
||||
|
||||
func (m Migrator) AlterColumn(value interface{}, field string) error {
|
||||
return m.RunWithValue(value, func(stmt *gorm.Statement) error {
|
||||
if stmt.Schema != nil {
|
||||
if field := stmt.Schema.LookUpField(field); field != nil {
|
||||
fieldType := clause.Expr{SQL: m.DataTypeOf(field)}
|
||||
if field.NotNull {
|
||||
fieldType.SQL += " NOT NULL"
|
||||
} else {
|
||||
fieldType.SQL += " NULL"
|
||||
}
|
||||
|
||||
return m.DB.Exec(
|
||||
"ALTER TABLE ? ALTER COLUMN ? ?",
|
||||
clause.Table{Name: getFullQualifiedTableName(stmt)}, clause.Column{Name: field.DBName}, fieldType,
|
||||
).Error
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("failed to look up field with name: %s", field)
|
||||
})
|
||||
}
|
||||
|
||||
func (m Migrator) RenameColumn(value interface{}, oldName, newName string) error {
|
||||
return m.RunWithValue(value, func(stmt *gorm.Statement) error {
|
||||
if stmt.Schema != nil {
|
||||
if field := stmt.Schema.LookUpField(oldName); field != nil {
|
||||
oldName = field.DBName
|
||||
}
|
||||
if field := stmt.Schema.LookUpField(newName); field != nil {
|
||||
newName = field.DBName
|
||||
}
|
||||
}
|
||||
|
||||
return m.DB.Exec(
|
||||
"sp_rename @objname = ?, @newname = ?, @objtype = 'COLUMN';",
|
||||
fmt.Sprintf("%s.%s", stmt.Table, oldName), clause.Column{Name: newName},
|
||||
).Error
|
||||
})
|
||||
}
|
||||
|
||||
func (m Migrator) GetColumnComment(stmt *gorm.Statement, fieldDBName string) (comment sql.NullString) {
|
||||
queryTx := m.DB.Session(&gorm.Session{Logger: m.DB.Logger.LogMode(logger.Warn)})
|
||||
if m.DB.DryRun {
|
||||
queryTx.DryRun = false
|
||||
}
|
||||
queryTx.Raw("SELECT value FROM [?].sys.fn_listextendedproperty('MS_Description', 'SCHEMA', ?, 'TABLE', ?, 'COLUMN', ?)",
|
||||
gorm.Expr(m.CurrentDatabase()), m.getTableSchemaName(stmt.Schema), stmt.Table, fieldDBName).Scan(&comment)
|
||||
return
|
||||
}
|
||||
|
||||
func (m Migrator) MigrateColumn(value interface{}, field *schema.Field, columnType gorm.ColumnType) error {
|
||||
if err := m.Migrator.MigrateColumn(value, field, columnType); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return m.RunWithValue(value, func(stmt *gorm.Statement) (err error) {
|
||||
comment := m.GetColumnComment(stmt, field.DBName)
|
||||
if field.Comment != comment.String {
|
||||
if comment.Valid {
|
||||
err = m.setColumnComment(stmt, field, false)
|
||||
} else {
|
||||
err = m.setColumnComment(stmt, field, true)
|
||||
}
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
var defaultValueTrimRegexp = regexp.MustCompile("^\\('?([^']*)'?\\)$")
|
||||
|
||||
// ColumnTypes return columnTypes []gorm.ColumnType and execErr error
|
||||
func (m Migrator) ColumnTypes(value interface{}) ([]gorm.ColumnType, error) {
|
||||
columnTypes := make([]gorm.ColumnType, 0)
|
||||
execErr := m.RunWithValue(value, func(stmt *gorm.Statement) (err error) {
|
||||
rows, err := m.DB.Session(&gorm.Session{}).Table(getFullQualifiedTableName(stmt)).Limit(1).Rows()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rawColumnTypes, _ := rows.ColumnTypes()
|
||||
_ = rows.Close()
|
||||
|
||||
{
|
||||
_, schemaName, tableName := splitFullQualifiedName(stmt.Table)
|
||||
|
||||
query := strings.TrimSpace(`
|
||||
SELECT COLUMN_NAME, DATA_TYPE, COLUMN_DEFAULT, c.IS_NULLABLE, CHARACTER_MAXIMUM_LENGTH, NUMERIC_PRECISION, NUMERIC_PRECISION_RADIX, NUMERIC_SCALE, DATETIME_PRECISION, AUTO_INCREMENT = c2.is_identity
|
||||
FROM INFORMATION_SCHEMA.COLUMNS c
|
||||
LEFT JOIN sys.tables t ON c.TABLE_NAME = t.[name]
|
||||
LEFT JOIN sys.columns c2 ON t.object_id = c2.object_id AND c2.[name] = c.COLUMN_NAME
|
||||
WHERE TABLE_CATALOG = ? AND TABLE_NAME = ?`)
|
||||
|
||||
queryParameters := []interface{}{m.CurrentDatabase(), tableName}
|
||||
|
||||
if schemaName != "" {
|
||||
query += " AND TABLE_SCHEMA = ?"
|
||||
queryParameters = append(queryParameters, schemaName)
|
||||
}
|
||||
|
||||
var (
|
||||
columnTypeSQL = query
|
||||
columns, rowErr = m.DB.Raw(columnTypeSQL, queryParameters...).Rows()
|
||||
)
|
||||
|
||||
if rowErr != nil {
|
||||
return rowErr
|
||||
}
|
||||
|
||||
for columns.Next() {
|
||||
var (
|
||||
column = migrator.ColumnType{
|
||||
PrimaryKeyValue: sql.NullBool{Valid: true},
|
||||
UniqueValue: sql.NullBool{Valid: true},
|
||||
}
|
||||
datetimePrecision sql.NullInt64
|
||||
radixValue sql.NullInt64
|
||||
nullableValue sql.NullString
|
||||
autoIncrementValue sql.NullBool
|
||||
values = []interface{}{
|
||||
&column.NameValue, &column.ColumnTypeValue, &column.DefaultValueValue, &nullableValue, &column.LengthValue, &column.DecimalSizeValue, &radixValue, &column.ScaleValue, &datetimePrecision, &autoIncrementValue,
|
||||
}
|
||||
)
|
||||
|
||||
if scanErr := columns.Scan(values...); scanErr != nil {
|
||||
return scanErr
|
||||
}
|
||||
|
||||
if nullableValue.Valid {
|
||||
column.NullableValue = sql.NullBool{Bool: strings.EqualFold(nullableValue.String, "YES"), Valid: true}
|
||||
}
|
||||
|
||||
if datetimePrecision.Valid {
|
||||
column.DecimalSizeValue = datetimePrecision
|
||||
}
|
||||
|
||||
if autoIncrementValue.Valid && autoIncrementValue.Bool {
|
||||
column.AutoIncrementValue = autoIncrementValue
|
||||
}
|
||||
|
||||
if column.DefaultValueValue.Valid {
|
||||
matches := defaultValueTrimRegexp.FindStringSubmatch(column.DefaultValueValue.String)
|
||||
for len(matches) > 1 {
|
||||
column.DefaultValueValue.String = matches[1]
|
||||
matches = defaultValueTrimRegexp.FindStringSubmatch(column.DefaultValueValue.String)
|
||||
}
|
||||
}
|
||||
|
||||
for _, c := range rawColumnTypes {
|
||||
if c.Name() == column.NameValue.String {
|
||||
column.SQLColumnType = c
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
columnTypes = append(columnTypes, column)
|
||||
}
|
||||
|
||||
_ = columns.Close()
|
||||
}
|
||||
|
||||
{
|
||||
_, schemaName, tableName := splitFullQualifiedName(stmt.Table)
|
||||
query := "SELECT c.COLUMN_NAME, t.CONSTRAINT_TYPE FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS t JOIN INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE c ON c.CONSTRAINT_NAME=t.CONSTRAINT_NAME WHERE t.CONSTRAINT_TYPE IN ('PRIMARY KEY', 'UNIQUE') AND c.TABLE_CATALOG = ? AND c.TABLE_NAME = ?"
|
||||
|
||||
queryParameters := []interface{}{m.CurrentDatabase(), tableName}
|
||||
|
||||
if schemaName != "" {
|
||||
query += " AND c.TABLE_SCHEMA = ?"
|
||||
queryParameters = append(queryParameters, schemaName)
|
||||
}
|
||||
|
||||
columnTypeRows, err := m.DB.Raw(query, queryParameters...).Rows()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for columnTypeRows.Next() {
|
||||
var name, columnType string
|
||||
_ = columnTypeRows.Scan(&name, &columnType)
|
||||
for idx, c := range columnTypes {
|
||||
mc := c.(migrator.ColumnType)
|
||||
if mc.NameValue.String == name {
|
||||
switch columnType {
|
||||
case "PRIMARY KEY":
|
||||
mc.PrimaryKeyValue = sql.NullBool{Bool: true, Valid: true}
|
||||
case "UNIQUE":
|
||||
mc.UniqueValue = sql.NullBool{Bool: true, Valid: true}
|
||||
}
|
||||
columnTypes[idx] = mc
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ = columnTypeRows.Close()
|
||||
}
|
||||
|
||||
return
|
||||
})
|
||||
|
||||
return columnTypes, execErr
|
||||
}
|
||||
|
||||
func (m Migrator) CreateView(name string, option gorm.ViewOption) error {
|
||||
if option.Query == nil {
|
||||
return gorm.ErrSubQueryRequired
|
||||
}
|
||||
|
||||
sql := new(strings.Builder)
|
||||
sql.WriteString("CREATE ")
|
||||
if option.Replace {
|
||||
sql.WriteString("OR ALTER ")
|
||||
}
|
||||
sql.WriteString("VIEW ")
|
||||
m.QuoteTo(sql, name)
|
||||
sql.WriteString(" AS ")
|
||||
|
||||
m.DB.Statement.AddVar(sql, option.Query)
|
||||
|
||||
if option.CheckOption != "" {
|
||||
sql.WriteString(" ")
|
||||
sql.WriteString(option.CheckOption)
|
||||
}
|
||||
return m.DB.Exec(m.Explain(sql.String(), m.DB.Statement.Vars...)).Error
|
||||
}
|
||||
|
||||
func (m Migrator) CreateIndex(value interface{}, name string) error {
|
||||
return m.RunWithValue(value, func(stmt *gorm.Statement) error {
|
||||
var idx *schema.Index
|
||||
if stmt.Schema != nil {
|
||||
idx = stmt.Schema.LookIndex(name)
|
||||
}
|
||||
if idx == nil {
|
||||
return fmt.Errorf("failed to create index with name %s", name)
|
||||
}
|
||||
|
||||
opts := m.BuildIndexOptions(idx.Fields, stmt)
|
||||
values := []interface{}{clause.Column{Name: idx.Name}, m.CurrentTable(stmt), opts}
|
||||
|
||||
createIndexSQL := "CREATE "
|
||||
if idx.Class != "" {
|
||||
createIndexSQL += idx.Class + " "
|
||||
}
|
||||
createIndexSQL += "INDEX ? ON ??"
|
||||
|
||||
if idx.Where != "" {
|
||||
createIndexSQL += " WHERE " + idx.Where
|
||||
}
|
||||
|
||||
if idx.Option != "" {
|
||||
createIndexSQL += " " + idx.Option
|
||||
}
|
||||
|
||||
return m.DB.Exec(createIndexSQL, values...).Error
|
||||
})
|
||||
}
|
||||
|
||||
func (m Migrator) HasIndex(value interface{}, name string) bool {
|
||||
var count int
|
||||
_ = m.RunWithValue(value, func(stmt *gorm.Statement) error {
|
||||
if stmt.Schema != nil {
|
||||
if idx := stmt.Schema.LookIndex(name); idx != nil {
|
||||
name = idx.Name
|
||||
}
|
||||
}
|
||||
|
||||
return m.DB.Raw(
|
||||
"SELECT count(*) FROM sys.indexes WHERE name=? AND object_id=OBJECT_ID(?)",
|
||||
name, getFullQualifiedTableName(stmt),
|
||||
).Row().Scan(&count)
|
||||
})
|
||||
return count > 0
|
||||
}
|
||||
|
||||
func (m Migrator) RenameIndex(value interface{}, oldName, newName string) error {
|
||||
return m.RunWithValue(value, func(stmt *gorm.Statement) error {
|
||||
|
||||
return m.DB.Exec(
|
||||
"sp_rename @objname = ?, @newname = ?, @objtype = 'INDEX';",
|
||||
fmt.Sprintf("%s.%s", stmt.Table, oldName), clause.Column{Name: newName},
|
||||
).Error
|
||||
})
|
||||
}
|
||||
|
||||
type Index struct {
|
||||
ColumnName string `gorm:"column:column_name"`
|
||||
IndexName string `gorm:"column:index_name"`
|
||||
IsUnique sql.NullBool `gorm:"column:is_unique"`
|
||||
IsPrimaryKey sql.NullBool `gorm:"column:is_primary_key"`
|
||||
}
|
||||
|
||||
func (m Migrator) GetIndexes(value interface{}) ([]gorm.Index, error) {
|
||||
indexes := make([]gorm.Index, 0)
|
||||
err := m.RunWithValue(value, func(stmt *gorm.Statement) error {
|
||||
result := make([]*Index, 0)
|
||||
if err := m.DB.Raw(indexSQL, stmt.Table).Scan(&result).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
indexMap := make(map[string]*migrator.Index)
|
||||
for _, r := range result {
|
||||
idx, ok := indexMap[r.IndexName]
|
||||
if !ok {
|
||||
idx = &migrator.Index{
|
||||
TableName: stmt.Table,
|
||||
NameValue: r.IndexName,
|
||||
ColumnList: nil,
|
||||
PrimaryKeyValue: r.IsPrimaryKey,
|
||||
UniqueValue: r.IsUnique,
|
||||
}
|
||||
}
|
||||
idx.ColumnList = append(idx.ColumnList, r.ColumnName)
|
||||
indexMap[r.IndexName] = idx
|
||||
}
|
||||
for _, idx := range indexMap {
|
||||
indexes = append(indexes, idx)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return indexes, err
|
||||
}
|
||||
|
||||
func (m Migrator) HasConstraint(value interface{}, name string) bool {
|
||||
var count int64
|
||||
_ = m.RunWithValue(value, func(stmt *gorm.Statement) error {
|
||||
constraint, table := m.GuessConstraintInterfaceAndTable(stmt, name)
|
||||
if constraint != nil {
|
||||
name = constraint.GetName()
|
||||
}
|
||||
|
||||
tableCatalog, tableSchema, tableName := splitFullQualifiedName(table)
|
||||
if tableCatalog == "" {
|
||||
tableCatalog = m.CurrentDatabase()
|
||||
}
|
||||
if tableSchema == "" {
|
||||
tableSchema = "%"
|
||||
}
|
||||
|
||||
return m.DB.Raw(
|
||||
`SELECT count(*) FROM (
|
||||
SELECT C.name, T.name as table_name FROM sys.check_constraints as C
|
||||
INNER JOIN sys.tables as T on C.parent_object_id=T.object_id
|
||||
INNER JOIN INFORMATION_SCHEMA.TABLES as I on I.TABLE_NAME = T.name
|
||||
WHERE C.name = ? AND I.TABLE_NAME = ? AND I.TABLE_SCHEMA like ? AND I.TABLE_CATALOG = ?
|
||||
UNION
|
||||
SELECT FK.name, T.name as table_name FROM sys.foreign_keys as FK
|
||||
INNER JOIN sys.tables as T on FK.parent_object_id=T.object_id
|
||||
INNER JOIN INFORMATION_SCHEMA.TABLES as I on I.TABLE_NAME = T.name
|
||||
WHERE FK.name = ? AND I.TABLE_NAME = ? AND I.TABLE_SCHEMA like ? AND I.TABLE_CATALOG = ?
|
||||
) as constraints;`,
|
||||
name, tableName, tableSchema, tableCatalog,
|
||||
name, tableName, tableSchema, tableCatalog,
|
||||
).Row().Scan(&count)
|
||||
})
|
||||
return count > 0
|
||||
}
|
||||
|
||||
func (m Migrator) CurrentDatabase() (name string) {
|
||||
_ = m.DB.Raw("SELECT DB_NAME() AS [Current Database]").Row().Scan(&name)
|
||||
return
|
||||
}
|
||||
|
||||
func (m Migrator) DefaultSchema() (name string) {
|
||||
_ = m.DB.Raw("SELECT SCHEMA_NAME() AS [Default Schema]").Row().Scan(&name)
|
||||
return
|
||||
}
|
||||
+245
@@ -0,0 +1,245 @@
|
||||
package sqlserver
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
_ "github.com/microsoft/go-mssqldb"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/callbacks"
|
||||
"gorm.io/gorm/clause"
|
||||
"gorm.io/gorm/logger"
|
||||
"gorm.io/gorm/migrator"
|
||||
"gorm.io/gorm/schema"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
DriverName string
|
||||
DSN string
|
||||
DefaultStringSize int
|
||||
Conn gorm.ConnPool
|
||||
}
|
||||
|
||||
type Dialector struct {
|
||||
*Config
|
||||
}
|
||||
|
||||
func (dialector Dialector) Name() string {
|
||||
return "sqlserver"
|
||||
}
|
||||
|
||||
func Open(dsn string) gorm.Dialector {
|
||||
return &Dialector{Config: &Config{DSN: dsn}}
|
||||
}
|
||||
|
||||
func New(config Config) gorm.Dialector {
|
||||
return &Dialector{Config: &config}
|
||||
}
|
||||
|
||||
func (dialector Dialector) Initialize(db *gorm.DB) (err error) {
|
||||
// register callbacks
|
||||
callbacks.RegisterDefaultCallbacks(db, &callbacks.Config{
|
||||
CreateClauses: []string{"INSERT", "VALUES", "ON CONFLICT"},
|
||||
QueryClauses: []string{"SELECT", "FROM", "WHERE", "GROUP BY", "ORDER BY", "LIMIT", "FOR"},
|
||||
UpdateClauses: []string{"UPDATE", "SET", "RETURNING", "FROM", "WHERE"},
|
||||
DeleteClauses: []string{"DELETE", "FROM", "RETURNING", "WHERE"},
|
||||
})
|
||||
db.Callback().Create().Replace("gorm:create", Create)
|
||||
db.Callback().Update().Replace("gorm:update", Update)
|
||||
|
||||
if dialector.DriverName == "" {
|
||||
dialector.DriverName = "sqlserver"
|
||||
}
|
||||
|
||||
if dialector.Conn != nil {
|
||||
db.ConnPool = dialector.Conn
|
||||
} else {
|
||||
db.ConnPool, err = sql.Open(dialector.DriverName, dialector.DSN)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for k, v := range dialector.ClauseBuilders() {
|
||||
db.ClauseBuilders[k] = v
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (dialector Dialector) ClauseBuilders() map[string]clause.ClauseBuilder {
|
||||
return map[string]clause.ClauseBuilder{
|
||||
"LIMIT": func(c clause.Clause, builder clause.Builder) {
|
||||
if limit, ok := c.Expression.(clause.Limit); ok {
|
||||
if stmt, ok := builder.(*gorm.Statement); ok {
|
||||
if _, ok := stmt.Clauses["ORDER BY"]; !ok {
|
||||
if stmt.Schema != nil && stmt.Schema.PrioritizedPrimaryField != nil {
|
||||
builder.WriteString("ORDER BY ")
|
||||
builder.WriteQuoted(stmt.Schema.PrioritizedPrimaryField.DBName)
|
||||
builder.WriteByte(' ')
|
||||
} else {
|
||||
builder.WriteString("ORDER BY (SELECT NULL) ")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if limit.Offset > 0 {
|
||||
builder.WriteString("OFFSET ")
|
||||
builder.WriteString(strconv.Itoa(limit.Offset))
|
||||
builder.WriteString(" ROWS")
|
||||
}
|
||||
|
||||
if limit.Limit != nil && *limit.Limit >= 0 {
|
||||
if limit.Offset == 0 {
|
||||
builder.WriteString("OFFSET 0 ROW")
|
||||
}
|
||||
builder.WriteString(" FETCH NEXT ")
|
||||
builder.WriteString(strconv.Itoa(*limit.Limit))
|
||||
builder.WriteString(" ROWS ONLY")
|
||||
}
|
||||
}
|
||||
},
|
||||
"RETURNING": func(c clause.Clause, builder clause.Builder) {
|
||||
if returning, ok := c.Expression.(clause.Returning); ok {
|
||||
if stmt, ok := builder.(*gorm.Statement); ok {
|
||||
var outputTable string
|
||||
if _, ok := stmt.Clauses["UPDATE"]; ok {
|
||||
outputTable = "INSERTED"
|
||||
} else if _, ok := stmt.Clauses["DELETE"]; ok {
|
||||
outputTable = "DELETED"
|
||||
}
|
||||
|
||||
if outputTable != "" {
|
||||
stmt.WriteString("OUTPUT ")
|
||||
|
||||
if len(returning.Columns) > 0 {
|
||||
columns := []clause.Column{}
|
||||
for _, column := range returning.Columns {
|
||||
column.Table = outputTable
|
||||
columns = append(columns, column)
|
||||
}
|
||||
returning.Columns = columns
|
||||
returning.Build(stmt)
|
||||
} else {
|
||||
stmt.WriteString(outputTable + ".*")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (dialector Dialector) DefaultValueOf(field *schema.Field) clause.Expression {
|
||||
return clause.Expr{SQL: "NULL"}
|
||||
}
|
||||
|
||||
func (dialector Dialector) Migrator(db *gorm.DB) gorm.Migrator {
|
||||
return Migrator{migrator.Migrator{Config: migrator.Config{
|
||||
DB: db,
|
||||
Dialector: dialector,
|
||||
CreateIndexAfterCreateTable: true,
|
||||
}}}
|
||||
}
|
||||
|
||||
func (dialector Dialector) BindVarTo(writer clause.Writer, stmt *gorm.Statement, v interface{}) {
|
||||
writer.WriteString("@p")
|
||||
writer.WriteString(strconv.Itoa(len(stmt.Vars)))
|
||||
}
|
||||
|
||||
func (dialector Dialector) QuoteTo(writer clause.Writer, str string) {
|
||||
writer.WriteByte('"')
|
||||
if strings.Contains(str, ".") {
|
||||
for idx, str := range strings.Split(str, ".") {
|
||||
if idx > 0 {
|
||||
writer.WriteString(`."`)
|
||||
}
|
||||
writer.WriteString(str)
|
||||
writer.WriteByte('"')
|
||||
}
|
||||
} else {
|
||||
writer.WriteString(str)
|
||||
writer.WriteByte('"')
|
||||
}
|
||||
}
|
||||
|
||||
var numericPlaceholder = regexp.MustCompile("@p(\\d+)")
|
||||
|
||||
func (dialector Dialector) Explain(sql string, vars ...interface{}) string {
|
||||
for idx, v := range vars {
|
||||
if b, ok := v.(bool); ok {
|
||||
if b {
|
||||
vars[idx] = 1
|
||||
} else {
|
||||
vars[idx] = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return logger.ExplainSQL(sql, numericPlaceholder, `'`, vars...)
|
||||
}
|
||||
|
||||
func (dialector Dialector) DataTypeOf(field *schema.Field) string {
|
||||
switch field.DataType {
|
||||
case schema.Bool:
|
||||
return "bit"
|
||||
case schema.Int, schema.Uint:
|
||||
var sqlType string
|
||||
switch {
|
||||
case field.Size < 16:
|
||||
sqlType = "smallint"
|
||||
case field.Size < 31:
|
||||
sqlType = "int"
|
||||
default:
|
||||
sqlType = "bigint"
|
||||
}
|
||||
|
||||
if field.AutoIncrement {
|
||||
return sqlType + " IDENTITY(1,1)"
|
||||
}
|
||||
return sqlType
|
||||
case schema.Float:
|
||||
if field.Precision > 0 {
|
||||
if field.Scale > 0 {
|
||||
return fmt.Sprintf("decimal(%d, %d)", field.Precision, field.Scale)
|
||||
}
|
||||
return fmt.Sprintf("decimal(%d)", field.Precision)
|
||||
}
|
||||
return "float"
|
||||
case schema.String:
|
||||
size := field.Size
|
||||
hasIndex := field.TagSettings["INDEX"] != "" || field.TagSettings["UNIQUE"] != ""
|
||||
if (field.PrimaryKey || hasIndex) && size == 0 {
|
||||
if dialector.DefaultStringSize > 0 {
|
||||
size = dialector.DefaultStringSize
|
||||
} else {
|
||||
size = 256
|
||||
}
|
||||
}
|
||||
if size > 0 && size <= 4000 {
|
||||
return fmt.Sprintf("nvarchar(%d)", size)
|
||||
}
|
||||
return "nvarchar(MAX)"
|
||||
case schema.Time:
|
||||
if field.Precision > 0 {
|
||||
return fmt.Sprintf("datetimeoffset(%d)", field.Precision)
|
||||
}
|
||||
return "datetimeoffset"
|
||||
case schema.Bytes:
|
||||
return "varbinary(MAX)"
|
||||
}
|
||||
|
||||
return string(field.DataType)
|
||||
}
|
||||
|
||||
func (dialectopr Dialector) SavePoint(tx *gorm.DB, name string) error {
|
||||
tx.Exec("SAVE TRANSACTION " + name)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dialectopr Dialector) RollbackTo(tx *gorm.DB, name string) error {
|
||||
tx.Exec("ROLLBACK TRANSACTION " + name)
|
||||
return nil
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package sqlserver
|
||||
|
||||
import (
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/callbacks"
|
||||
)
|
||||
|
||||
var updateFunc = callbacks.Update(&callbacks.Config{
|
||||
UpdateClauses: []string{"UPDATE", "SET", "RETURNING", "FROM", "WHERE"},
|
||||
})
|
||||
|
||||
func Update(db *gorm.DB) {
|
||||
if db.Statement.Schema != nil && db.Statement.Schema.PrioritizedPrimaryField != nil && db.Statement.Schema.PrioritizedPrimaryField.AutoIncrement {
|
||||
db.Statement.Omits = append(db.Statement.Omits, db.Statement.Schema.PrioritizedPrimaryField.DBName)
|
||||
}
|
||||
|
||||
updateFunc(db)
|
||||
}
|
||||
Reference in New Issue
Block a user