Compare commits
No commits in common. "main" and "1.0.2011" have entirely different histories.
|
|
@ -1,7 +0,0 @@
|
|||
package consts
|
||||
|
||||
const (
|
||||
// DoNotEditKey is used in generated files,
|
||||
// which marks the files will be overwritten by CLI tool.
|
||||
DoNotEditKey = `DO NOT EDIT`
|
||||
)
|
||||
|
|
@ -28,8 +28,8 @@ import (
|
|||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
|
||||
"git.magicany.cc/black1552/gin-base/cmd/gendao/internal/utility/mlog"
|
||||
"git.magicany.cc/black1552/gin-base/cmd/gendao/internal/utility/utils"
|
||||
"git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/utility/mlog"
|
||||
"git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/utility/utils"
|
||||
)
|
||||
|
||||
type (
|
||||
|
|
@ -10,7 +10,7 @@ import (
|
|||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
|
||||
"git.magicany.cc/black1552/gin-base/cmd/gendao/internal/utility/mlog"
|
||||
"git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/utility/mlog"
|
||||
)
|
||||
|
||||
func doClear(items *CGenDaoInternalGenItems) {
|
||||
|
|
@ -13,7 +13,6 @@ import (
|
|||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"git.magicany.cc/black1552/gin-base/cmd/consts"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
|
||||
"git.magicany.cc/black1552/gin-base/database"
|
||||
|
|
@ -22,8 +21,9 @@ import (
|
|||
"github.com/gogf/gf/v2/os/gview"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
|
||||
"git.magicany.cc/black1552/gin-base/cmd/gendao/internal/utility/mlog"
|
||||
"git.magicany.cc/black1552/gin-base/cmd/gendao/internal/utility/utils"
|
||||
"git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/consts"
|
||||
"git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/utility/mlog"
|
||||
"git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/utility/utils"
|
||||
)
|
||||
|
||||
func generateDao(ctx context.Context, in CGenDaoInternalInput) {
|
||||
|
|
@ -12,14 +12,14 @@ import (
|
|||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"git.magicany.cc/black1552/gin-base/cmd/consts"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gview"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
|
||||
"git.magicany.cc/black1552/gin-base/cmd/gendao/internal/utility/mlog"
|
||||
"git.magicany.cc/black1552/gin-base/cmd/gendao/internal/utility/utils"
|
||||
"git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/consts"
|
||||
"git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/utility/mlog"
|
||||
"git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/utility/utils"
|
||||
)
|
||||
|
||||
func generateDo(ctx context.Context, in CGenDaoInternalInput) {
|
||||
|
|
@ -11,13 +11,13 @@ import (
|
|||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"git.magicany.cc/black1552/gin-base/cmd/consts"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gview"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
|
||||
"git.magicany.cc/black1552/gin-base/cmd/gendao/internal/utility/mlog"
|
||||
"git.magicany.cc/black1552/gin-base/cmd/gendao/internal/utility/utils"
|
||||
"git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/consts"
|
||||
"git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/utility/mlog"
|
||||
"git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/utility/utils"
|
||||
)
|
||||
|
||||
func generateEntity(ctx context.Context, in CGenDaoInternalInput) {
|
||||
|
|
@ -14,15 +14,15 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
"git.magicany.cc/black1552/gin-base/cmd/consts"
|
||||
"git.magicany.cc/black1552/gin-base/database"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gview"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
|
||||
"git.magicany.cc/black1552/gin-base/cmd/gendao/internal/utility/mlog"
|
||||
"git.magicany.cc/black1552/gin-base/cmd/gendao/internal/utility/utils"
|
||||
"git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/consts"
|
||||
"git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/utility/mlog"
|
||||
"git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/utility/utils"
|
||||
)
|
||||
|
||||
// generateTable generates dao files for given tables.
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
module git.magicany.cc/black1552/gin-base/cmd/gf-source
|
||||
|
||||
go 1.25.0
|
||||
|
||||
require (
|
||||
git.magicany.cc/black1552/gin-base v0.0.0
|
||||
github.com/gogf/gf/v2 v2.10.0
|
||||
github.com/olekukonko/tablewriter v1.1.4
|
||||
golang.org/x/mod v0.33.0
|
||||
)
|
||||
|
||||
replace git.magicany.cc/black1552/gin-base => ../..
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package consts
|
||||
|
||||
const (
|
||||
// DoNotEditKey is used in generated files,
|
||||
// which marks the files will be overwritten by CLI tool.
|
||||
DoNotEditKey = `DO NOT EDIT`
|
||||
)
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package consts
|
||||
|
||||
const TemplateGenCtrlControllerEmpty = `
|
||||
// =================================================================================
|
||||
// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
|
||||
// =================================================================================
|
||||
|
||||
package {Module}
|
||||
`
|
||||
|
||||
const TemplateGenCtrlControllerNewEmpty = `
|
||||
// =================================================================================
|
||||
// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
|
||||
// =================================================================================
|
||||
|
||||
package {Module}
|
||||
|
||||
import (
|
||||
{ImportPath}
|
||||
)
|
||||
`
|
||||
|
||||
const TemplateGenCtrlControllerNewFunc = `
|
||||
type {CtrlName} struct{}
|
||||
|
||||
func {NewFuncName}() {InterfaceName} {
|
||||
return &{CtrlName}{}
|
||||
}
|
||||
`
|
||||
|
||||
const TemplateGenCtrlControllerMethodFunc = `
|
||||
package {Module}
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
|
||||
"{ImportPath}"
|
||||
)
|
||||
{MethodComment}
|
||||
func (c *{CtrlName}) {MethodName}(ctx context.Context, req *{Version}.{MethodName}Req) (res *{Version}.{MethodName}Res, err error) {
|
||||
return nil, gerror.NewCode(gcode.CodeNotImplemented)
|
||||
}
|
||||
`
|
||||
|
||||
const TemplateGenCtrlControllerHeader = `
|
||||
package {Module}
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
|
||||
"{ImportPath}"
|
||||
)
|
||||
|
||||
`
|
||||
|
||||
const TemplateGenCtrlControllerMethodFuncMerge = `
|
||||
{MethodComment}
|
||||
func (c *{CtrlName}) {MethodName}(ctx context.Context, req *{Version}.{MethodName}Req) (res *{Version}.{MethodName}Res, err error) {
|
||||
return nil, gerror.NewCode(gcode.CodeNotImplemented)
|
||||
}
|
||||
`
|
||||
|
||||
const TemplateGenCtrlApiInterface = `
|
||||
// =================================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// =================================================================================
|
||||
|
||||
package {Module}
|
||||
|
||||
import (
|
||||
{ImportPaths}
|
||||
)
|
||||
|
||||
{Interfaces}
|
||||
`
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package consts
|
||||
|
||||
const TemplateGenCtrlSdkPkgNew = `
|
||||
// =================================================================================
|
||||
// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
|
||||
// =================================================================================
|
||||
|
||||
package {PkgName}
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/contrib/sdk/httpclient/v2"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
type implementer struct {
|
||||
config httpclient.Config
|
||||
}
|
||||
|
||||
func New(config httpclient.Config) IClient {
|
||||
return &implementer{
|
||||
config: config,
|
||||
}
|
||||
}
|
||||
|
||||
`
|
||||
|
||||
const TemplateGenCtrlSdkIClient = `
|
||||
// =================================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// =================================================================================
|
||||
|
||||
package {PkgName}
|
||||
|
||||
import (
|
||||
)
|
||||
|
||||
type IClient interface {
|
||||
}
|
||||
`
|
||||
|
||||
const TemplateGenCtrlSdkImplementer = `
|
||||
// =================================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// =================================================================================
|
||||
|
||||
package {PkgName}
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/contrib/sdk/httpclient/v2"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
|
||||
{ImportPaths}
|
||||
)
|
||||
|
||||
type implementer{ImplementerName} struct {
|
||||
*httpclient.Client
|
||||
}
|
||||
|
||||
`
|
||||
|
||||
const TemplateGenCtrlSdkImplementerNew = `
|
||||
func (i *implementer) {ImplementerName}() {Module}.I{ImplementerName} {
|
||||
var (
|
||||
client = httpclient.New(i.config)
|
||||
prefix = gstr.TrimRight(i.config.URL, "/") + "{VersionPrefix}"
|
||||
)
|
||||
client.Client = client.Prefix(prefix)
|
||||
return &implementer{ImplementerName}{client}
|
||||
}
|
||||
|
||||
`
|
||||
|
||||
const TemplateGenCtrlSdkImplementerFunc = `{MethodComment}
|
||||
func (i *implementer{ImplementerName}) {MethodName}(ctx context.Context, req *{Version}.{MethodName}Req) (res *{Version}.{MethodName}Res, err error) {
|
||||
err = i.Request(ctx, req, &res)
|
||||
return
|
||||
}
|
||||
`
|
||||
|
|
@ -8,7 +8,7 @@ package consts
|
|||
|
||||
const TemplateGenDaoIndexContent = `
|
||||
// =================================================================================
|
||||
// This file is auto-generated by the Gin-base CLI tool. You may modify it as needed.
|
||||
// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed.
|
||||
// =================================================================================
|
||||
|
||||
package {{.TplPackageName}}
|
||||
|
|
@ -59,7 +59,7 @@ func {{.TplTableNameCamelLowerCase}}ShardingHandler(m *database.Model) *database
|
|||
|
||||
const TemplateGenDaoInternalContent = `
|
||||
// ==========================================================================
|
||||
// Code generated and maintained by Gin-base CLI tool. DO NOT EDIT. {{.TplCreatedAtDatetimeStr}}
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. {{.TplCreatedAtDatetimeStr}}
|
||||
// ==========================================================================
|
||||
|
||||
package internal
|
||||
|
|
@ -8,7 +8,7 @@ package consts
|
|||
|
||||
const TemplateGenDaoDoContent = `
|
||||
// =================================================================================
|
||||
// Code generated and maintained by Gin-base CLI tool. DO NOT EDIT. {{.TplCreatedAtDatetimeStr}}
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. {{.TplCreatedAtDatetimeStr}}
|
||||
// =================================================================================
|
||||
|
||||
package {{.TplPackageName}}
|
||||
|
|
@ -8,7 +8,7 @@ package consts
|
|||
|
||||
const TemplateGenDaoEntityContent = `
|
||||
// =================================================================================
|
||||
// Code generated and maintained by Gin-base CLI tool. DO NOT EDIT. {{.TplCreatedAtDatetimeStr}}
|
||||
// Code generated and maintained by Gen CLI tool. DO NOT EDIT. {{.TplCreatedAtDatetimeStr}}
|
||||
// =================================================================================
|
||||
|
||||
package {{.TplPackageName}}
|
||||
|
|
@ -8,7 +8,7 @@ package consts
|
|||
|
||||
const TemplateGenTableContent = `
|
||||
// =================================================================================
|
||||
// This file is auto-generated by the Gin-base CLI tool. You may modify it as needed.
|
||||
// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed.
|
||||
// =================================================================================
|
||||
|
||||
package {{.TplPackageName}}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package consts
|
||||
|
||||
const TemplateGenEnums = `
|
||||
// ================================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// ================================================================================
|
||||
|
||||
package {PackageName}
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
)
|
||||
|
||||
func init() {
|
||||
gtag.SetGlobalEnums({EnumsJson})
|
||||
}
|
||||
`
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package consts
|
||||
|
||||
const TemplatePbEntityMessageContent = `
|
||||
// ==========================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// ==========================================================================
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package {PackageName};
|
||||
|
||||
option go_package = "{GoPackage}";
|
||||
{OptionContent}
|
||||
{Imports}
|
||||
|
||||
{EntityMessage}
|
||||
`
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package consts
|
||||
|
||||
const TemplateGenServiceContentHead = `
|
||||
// ================================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// You can delete these comments if you wish manually maintain this interface file.
|
||||
// ================================================================================
|
||||
|
||||
package {PackageName}
|
||||
|
||||
{Imports}
|
||||
`
|
||||
|
||||
const TemplateGenServiceContentInterface = `
|
||||
{InterfaceName} interface {
|
||||
{FuncDefinition}
|
||||
}
|
||||
`
|
||||
|
||||
const TemplateGenServiceContentVariable = `
|
||||
local{StructName} {InterfaceName}
|
||||
`
|
||||
|
||||
const TemplateGenServiceContentRegister = `
|
||||
func {StructName}() {InterfaceName} {
|
||||
if local{StructName} == nil {
|
||||
panic("implement not found for interface {InterfaceName}, forgot register?")
|
||||
}
|
||||
return local{StructName}
|
||||
}
|
||||
|
||||
func Register{StructName}(i {InterfaceName}) {
|
||||
local{StructName} = i
|
||||
}
|
||||
`
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package consts
|
||||
|
||||
const TemplateGenServiceLogicContent = `
|
||||
// ==========================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// ==========================================================================
|
||||
|
||||
package {PackageName}
|
||||
|
||||
import(
|
||||
{Imports}
|
||||
)
|
||||
`
|
||||
|
|
@ -1,3 +1,9 @@
|
|||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package mlog
|
||||
|
||||
import (
|
||||
|
|
@ -1,3 +1,9 @@
|
|||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
|
|
@ -11,8 +17,8 @@ import (
|
|||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
|
||||
"git.magicany.cc/black1552/gin-base/cmd/gendao/internal/consts"
|
||||
"git.magicany.cc/black1552/gin-base/cmd/gendao/internal/utility/mlog"
|
||||
"git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/consts"
|
||||
"git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/utility/mlog"
|
||||
)
|
||||
|
||||
// GoFmt formats the source file and adds or removes import statements as necessary.
|
||||
|
|
@ -16,7 +16,7 @@ import (
|
|||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
|
||||
"git.magicany.cc/black1552/gin-base/cmd/gendao/internal/utility/mlog"
|
||||
"git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/utility/mlog"
|
||||
)
|
||||
|
||||
// HTTPDownloadFileWithPercent downloads target url file to local path with percent process printing.
|
||||
|
|
@ -12,7 +12,7 @@ import (
|
|||
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
|
||||
"git.magicany.cc/black1552/gin-base/cmd/gendao/internal/utility/utils"
|
||||
"git.magicany.cc/black1552/gin-base/cmd/gf-source/internal/utility/utils"
|
||||
)
|
||||
|
||||
func Test_GetModPath(t *testing.T) {
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package consts
|
||||
|
||||
const TemplateGenDaoIndexContent = `
|
||||
// =================================================================================
|
||||
// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed.
|
||||
// =================================================================================
|
||||
|
||||
package {{.TplPackageName}}
|
||||
|
||||
import (
|
||||
"{{.TplImportPrefix}}/internal"
|
||||
)
|
||||
|
||||
// {{.TplTableNameCamelLowerCase}}Dao is the data access object for the table {{.TplTableName}}.
|
||||
// You can define custom methods on it to extend its functionality as needed.
|
||||
type {{.TplTableNameCamelLowerCase}}Dao struct {
|
||||
*internal.{{.TplTableNameCamelCase}}Dao
|
||||
}
|
||||
|
||||
var (
|
||||
// {{.TplTableNameCamelCase}} is a globally accessible object for table {{.TplTableName}} operations.
|
||||
{{.TplTableNameCamelCase}} = {{.TplTableNameCamelLowerCase}}Dao{
|
||||
{{- if .TplTableSharding -}}
|
||||
internal.New{{.TplTableNameCamelCase}}Dao({{.TplTableNameCamelLowerCase}}ShardingHandler),
|
||||
{{- else -}}
|
||||
internal.New{{.TplTableNameCamelCase}}Dao(),
|
||||
{{- end -}}
|
||||
}
|
||||
)
|
||||
|
||||
{{if .TplTableSharding -}}
|
||||
// {{.TplTableNameCamelLowerCase}}ShardingHandler is the handler for sharding operations.
|
||||
// You can fill this sharding handler with your custom implementation.
|
||||
func {{.TplTableNameCamelLowerCase}}ShardingHandler(m *database.Model) *database.Model {
|
||||
m = m.Sharding(database.ShardingConfig{
|
||||
Table: database.ShardingTableConfig{
|
||||
Enable: true,
|
||||
Prefix: "{{.TplTableShardingPrefix}}",
|
||||
// Replace Rule field with your custom sharding rule.
|
||||
// Or you can use "&database.DefaultShardingRule{}" for default sharding rule.
|
||||
Rule: nil,
|
||||
},
|
||||
Schema: database.ShardingSchemaConfig{},
|
||||
})
|
||||
return m
|
||||
}
|
||||
{{- end}}
|
||||
|
||||
// Add your custom methods and functionality below.
|
||||
|
||||
`
|
||||
|
||||
const TemplateGenDaoInternalContent = `
|
||||
// ==========================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. {{.TplCreatedAtDatetimeStr}}
|
||||
// ==========================================================================
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.magicany.cc/black1552/gin-base/database"
|
||||
)
|
||||
|
||||
// {{.TplTableNameCamelCase}}Dao is the data access object for the table {{.TplTableName}}.
|
||||
type {{.TplTableNameCamelCase}}Dao struct {
|
||||
table string // table is the underlying table name of the DAO.
|
||||
group string // group is the database configuration group name of the current DAO.
|
||||
columns {{.TplTableNameCamelCase}}Columns // columns contains all the column names of Table for convenient usage.
|
||||
handlers []database.ModelHandler // handlers for customized model modification.
|
||||
}
|
||||
|
||||
// {{.TplTableNameCamelCase}}Columns defines and stores column names for the table {{.TplTableName}}.
|
||||
type {{.TplTableNameCamelCase}}Columns struct {
|
||||
{{.TplColumnDefine}}
|
||||
}
|
||||
|
||||
// {{.TplTableNameCamelLowerCase}}Columns holds the columns for the table {{.TplTableName}}.
|
||||
var {{.TplTableNameCamelLowerCase}}Columns = {{.TplTableNameCamelCase}}Columns{
|
||||
{{.TplColumnNames}}
|
||||
}
|
||||
|
||||
// New{{.TplTableNameCamelCase}}Dao creates and returns a new DAO object for table data access.
|
||||
func New{{.TplTableNameCamelCase}}Dao(handlers ...database.ModelHandler) *{{.TplTableNameCamelCase}}Dao {
|
||||
return &{{.TplTableNameCamelCase}}Dao{
|
||||
group: "{{.TplGroupName}}",
|
||||
table: "{{.TplTableName}}",
|
||||
columns: {{.TplTableNameCamelLowerCase}}Columns,
|
||||
handlers: handlers,
|
||||
}
|
||||
}
|
||||
|
||||
// DB retrieves and returns the underlying raw database management object of the current DAO.
|
||||
func (dao *{{.TplTableNameCamelCase}}Dao) DB() database.DB {
|
||||
return database.Database(dao.group)
|
||||
}
|
||||
|
||||
// Table returns the table name of the current DAO.
|
||||
func (dao *{{.TplTableNameCamelCase}}Dao) Table() string {
|
||||
return dao.table
|
||||
}
|
||||
|
||||
// Columns returns all column names of the current DAO.
|
||||
func (dao *{{.TplTableNameCamelCase}}Dao) Columns() {{.TplTableNameCamelCase}}Columns {
|
||||
return dao.columns
|
||||
}
|
||||
|
||||
// Group returns the database configuration group name of the current DAO.
|
||||
func (dao *{{.TplTableNameCamelCase}}Dao) Group() string {
|
||||
return dao.group
|
||||
}
|
||||
|
||||
// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation.
|
||||
func (dao *{{.TplTableNameCamelCase}}Dao) Ctx(ctx context.Context) *database.Model {
|
||||
model := dao.DB().Model(dao.table)
|
||||
for _, handler := range dao.handlers {
|
||||
model = handler(model)
|
||||
}
|
||||
return model.Safe().Ctx(ctx)
|
||||
}
|
||||
|
||||
// Transaction wraps the transaction logic using function f.
|
||||
// It rolls back the transaction and returns the error if function f returns a non-nil error.
|
||||
// It commits the transaction and returns nil if function f returns nil.
|
||||
//
|
||||
// Note: Do not commit or roll back the transaction in function f,
|
||||
// as it is automatically handled by this function.
|
||||
func (dao *{{.TplTableNameCamelCase}}Dao) Transaction(ctx context.Context, f func(ctx context.Context, tx database.TX) error) (err error) {
|
||||
return dao.Ctx(ctx).Transaction(ctx, f)
|
||||
}
|
||||
`
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package consts
|
||||
|
||||
const TemplateGenDaoDoContent = `
|
||||
// =================================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. {{.TplCreatedAtDatetimeStr}}
|
||||
// =================================================================================
|
||||
|
||||
package {{.TplPackageName}}
|
||||
|
||||
{{.TplPackageImports}}
|
||||
|
||||
// {{.TplTableNameCamelCase}} is the golang structure of table {{.TplTableName}} for DAO operations like Where/Data.
|
||||
{{.TplStructDefine}}
|
||||
`
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package consts
|
||||
|
||||
const TemplateGenDaoEntityContent = `
|
||||
// =================================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. {{.TplCreatedAtDatetimeStr}}
|
||||
// =================================================================================
|
||||
|
||||
package {{.TplPackageName}}
|
||||
|
||||
{{.TplPackageImports}}
|
||||
|
||||
// {{.TplTableNameCamelCase}} is the golang structure for table {{.TplTableName}}.
|
||||
{{.TplStructDefine}}
|
||||
`
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package consts
|
||||
|
||||
const TemplateGenTableContent = `
|
||||
// =================================================================================
|
||||
// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed.
|
||||
// =================================================================================
|
||||
|
||||
package {{.TplPackageName}}
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.magicany.cc/black1552/gin-base/database"
|
||||
)
|
||||
|
||||
// {{.TplTableNameCamelCase}} defines the fields of table "{{.TplTableName}}" with their properties.
|
||||
// This map is used internally by GoFrame ORM to understand table structure.
|
||||
var {{.TplTableNameCamelCase}} = map[string]*database.TableField{
|
||||
{{.TplTableFields}}
|
||||
}
|
||||
|
||||
// Set{{.TplTableNameCamelCase}}TableFields registers the table fields definition to the database instance.
|
||||
// db: database instance that implements database.DB interface.
|
||||
// schema: optional schema/namespace name, especially for databases that support schemas.
|
||||
func Set{{.TplTableNameCamelCase}}TableFields(ctx context.Context, db database.DB, schema ...string) error {
|
||||
return db.GetCore().SetTableFields(ctx, "{{.TplTableName}}", {{.TplTableNameCamelCase}}, schema...)
|
||||
}
|
||||
`
|
||||
|
|
@ -0,0 +1,376 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"git.magicany.cc/black1552/gin-base/config"
|
||||
"git.magicany.cc/black1552/gin-base/database"
|
||||
_ "git.magicany.cc/black1552/gin-base/database/drivers"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx := context.Background()
|
||||
|
||||
// 加载配置
|
||||
cfg := config.GetAllConfig()
|
||||
if cfg == nil {
|
||||
fmt.Println("❌ 错误: 配置为空")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// 检查数据库配置
|
||||
dbConfigMap, ok := cfg["database"].(map[string]any)
|
||||
if !ok || len(dbConfigMap) == 0 {
|
||||
fmt.Println("❌ 错误: 未找到数据库配置")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// 获取默认数据库配置
|
||||
defaultDbConfig, ok := dbConfigMap["default"].(map[string]any)
|
||||
if !ok {
|
||||
fmt.Println("❌ 错误: 未找到 default 数据库配置")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// 提取配置值
|
||||
host := getStringValue(defaultDbConfig, "host", "127.0.0.1")
|
||||
port := getStringValue(defaultDbConfig, "port", "3306")
|
||||
name := getStringValue(defaultDbConfig, "name", "test")
|
||||
dbType := getStringValue(defaultDbConfig, "type", "mysql")
|
||||
|
||||
fmt.Println("=== Gin-Base DAO 代码生成工具 ===")
|
||||
fmt.Printf("📊 数据库: %s\n", name)
|
||||
fmt.Printf("🔧 类型: %s\n", dbType)
|
||||
fmt.Printf("🌐 主机: %s:%s\n\n", host, port)
|
||||
|
||||
// 初始化数据库连接
|
||||
err := initDatabaseFromMap(dbConfigMap)
|
||||
if err != nil {
|
||||
fmt.Printf("❌ 数据库初始化失败: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// 获取数据库实例
|
||||
db := database.Database()
|
||||
|
||||
// 获取所有表
|
||||
tables, err := db.Tables(ctx)
|
||||
if err != nil {
|
||||
fmt.Printf("❌ 获取表列表失败: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("📋 找到 %d 个表:\n", len(tables))
|
||||
for i, table := range tables {
|
||||
fmt.Printf(" %d. %s\n", i+1, table)
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
// 询问用户要生成的表
|
||||
var selectedTables []string
|
||||
if len(os.Args) > 1 {
|
||||
// 从命令行参数获取表名
|
||||
selectedTables = os.Args[1:]
|
||||
} else {
|
||||
// 默认生成所有表
|
||||
selectedTables = tables
|
||||
fmt.Println("💡 提示: 可以通过命令行参数指定要生成的表")
|
||||
fmt.Println(" 例如: gin-dao-gen users orders")
|
||||
}
|
||||
|
||||
// 创建输出目录
|
||||
dirs := []string{
|
||||
"./internal/dao",
|
||||
"./internal/model/do",
|
||||
"./internal/model/entity",
|
||||
"./internal/model/table",
|
||||
}
|
||||
|
||||
for _, dir := range dirs {
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
fmt.Printf("❌ 创建目录失败 %s: %v\n", dir, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// 为每个表生成代码
|
||||
for _, tableName := range selectedTables {
|
||||
fmt.Printf("\n🔨 正在生成表 [%s] 的代码...\n", tableName)
|
||||
|
||||
// 获取表字段信息
|
||||
fields, err := db.TableFields(ctx, tableName)
|
||||
if err != nil {
|
||||
fmt.Printf(" ⚠️ 获取表字段失败: %v\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// 生成 Entity
|
||||
entityName := tableNameToStructName(tableName)
|
||||
generateEntity(tableName, entityName, fields)
|
||||
|
||||
// 生成 DO
|
||||
generateDO(tableName, entityName, fields)
|
||||
|
||||
// 生成 DAO
|
||||
generateDAO(tableName, entityName)
|
||||
|
||||
// 生成 Table
|
||||
generateTable(tableName, entityName, fields)
|
||||
|
||||
fmt.Printf(" ✅ 完成\n")
|
||||
}
|
||||
|
||||
fmt.Println("\n🎉 代码生成完成!")
|
||||
fmt.Println("📁 生成的文件位于:")
|
||||
fmt.Println(" - ./internal/dao/")
|
||||
fmt.Println(" - ./internal/model/do/")
|
||||
fmt.Println(" - ./internal/model/entity/")
|
||||
fmt.Println(" - ./internal/model/table/")
|
||||
}
|
||||
|
||||
// 从 Map 初始化数据库
|
||||
func initDatabaseFromMap(dbConfigMap map[string]any) error {
|
||||
for name, nodeConfig := range dbConfigMap {
|
||||
nodeMap, ok := nodeConfig.(map[string]any)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
configNode := database.ConfigNode{
|
||||
Host: getStringValue(nodeMap, "host", "127.0.0.1"),
|
||||
Port: getStringValue(nodeMap, "port", "3306"),
|
||||
User: getStringValue(nodeMap, "user", "root"),
|
||||
Pass: getStringValue(nodeMap, "pass", ""),
|
||||
Name: getStringValue(nodeMap, "name", ""),
|
||||
Type: getStringValue(nodeMap, "type", "mysql"),
|
||||
Role: database.Role(getStringValue(nodeMap, "role", "master")),
|
||||
Debug: getBoolValue(nodeMap, "debug", false),
|
||||
Prefix: getStringValue(nodeMap, "prefix", ""),
|
||||
Charset: getStringValue(nodeMap, "charset", "utf8"),
|
||||
}
|
||||
|
||||
if err := database.AddConfigNode(name, configNode); err != nil {
|
||||
return fmt.Errorf("add config node %s failed: %w", name, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 辅助函数:从 map 中获取字符串值
|
||||
func getStringValue(m map[string]any, key string, defaultValue string) string {
|
||||
if val, ok := m[key]; ok {
|
||||
if str, ok := val.(string); ok {
|
||||
return str
|
||||
}
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// 辅助函数:从 map 中获取布尔值
|
||||
func getBoolValue(m map[string]any, key string, defaultValue bool) bool {
|
||||
if val, ok := m[key]; ok {
|
||||
if b, ok := val.(bool); ok {
|
||||
return b
|
||||
}
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// 表名转结构体名
|
||||
func tableNameToStructName(tableName string) string {
|
||||
parts := strings.Split(tableName, "_")
|
||||
var result strings.Builder
|
||||
for _, part := range parts {
|
||||
if len(part) > 0 {
|
||||
result.WriteString(strings.ToUpper(part[:1]))
|
||||
result.WriteString(part[1:])
|
||||
}
|
||||
}
|
||||
return result.String()
|
||||
}
|
||||
|
||||
// 生成 Entity 文件
|
||||
func generateEntity(tableName, entityName string, fields map[string]*database.TableField) {
|
||||
filename := fmt.Sprintf("./internal/model/entity/%s.go", tableName)
|
||||
|
||||
var content strings.Builder
|
||||
content.WriteString("package entity\n\n")
|
||||
content.WriteString("// Auto-generated by gin-base gen dao tool\n\n")
|
||||
content.WriteString(fmt.Sprintf("// %s represents the entity for table %s\n", entityName, tableName))
|
||||
content.WriteString(fmt.Sprintf("type %s struct {\n", entityName))
|
||||
|
||||
for _, field := range fields {
|
||||
fieldName := fieldNameToStructName(field.Name)
|
||||
goType := dbTypeToGoType(field.Type)
|
||||
jsonTag := field.Name
|
||||
|
||||
content.WriteString(fmt.Sprintf("\t%s %s `json:\"%s\" description:\"%s\"`\n",
|
||||
fieldName, goType, jsonTag, field.Comment))
|
||||
}
|
||||
|
||||
content.WriteString("}\n")
|
||||
|
||||
if err := os.WriteFile(filename, []byte(content.String()), 0644); err != nil {
|
||||
fmt.Printf(" ❌ 写入文件失败: %v\n", err)
|
||||
} else {
|
||||
fmt.Printf(" 📄 生成 Entity: %s\n", filename)
|
||||
}
|
||||
}
|
||||
|
||||
// 生成 DO 文件
|
||||
func generateDO(tableName, entityName string, fields map[string]*database.TableField) {
|
||||
filename := fmt.Sprintf("./internal/model/do/%s.go", tableName)
|
||||
|
||||
var content strings.Builder
|
||||
content.WriteString("package do\n\n")
|
||||
content.WriteString("import \"github.com/gogf/gf/v2/frame/g\"\n\n")
|
||||
content.WriteString("// Auto-generated by gin-base gen dao tool\n\n")
|
||||
content.WriteString(fmt.Sprintf("// %s represents the data object for table %s\n", entityName, tableName))
|
||||
content.WriteString(fmt.Sprintf("type %s struct {\n\tg.Meta `orm:\"table:%s, do:true\"`\n\n", entityName, tableName))
|
||||
|
||||
for _, field := range fields {
|
||||
fieldName := fieldNameToStructName(field.Name)
|
||||
goType := dbTypeToGoType(field.Type)
|
||||
|
||||
content.WriteString(fmt.Sprintf("\t%s *%s `json:\"%s,omitempty\"`\n",
|
||||
fieldName, goType, field.Name))
|
||||
}
|
||||
|
||||
content.WriteString("}\n")
|
||||
|
||||
if err := os.WriteFile(filename, []byte(content.String()), 0644); err != nil {
|
||||
fmt.Printf(" ❌ 写入文件失败: %v\n", err)
|
||||
} else {
|
||||
fmt.Printf(" 📄 生成 DO: %s\n", filename)
|
||||
}
|
||||
}
|
||||
|
||||
// 生成 DAO 文件
|
||||
func generateDAO(tableName, entityName string) {
|
||||
filename := fmt.Sprintf("./internal/dao/%s.go", tableName)
|
||||
lowerName := strings.ToLower(entityName[:1]) + entityName[1:]
|
||||
|
||||
var content strings.Builder
|
||||
content.WriteString("package dao\n\n")
|
||||
content.WriteString("import (\n")
|
||||
content.WriteString("\t\"git.magicany.cc/black1552/gin-base/database\"\n")
|
||||
content.WriteString(fmt.Sprintf("\t\"git.magicany.cc/black1552/gin-base/internal/model/entity\"\n"))
|
||||
content.WriteString(")\n\n")
|
||||
content.WriteString("// Auto-generated by gin-base gen dao tool\n\n")
|
||||
content.WriteString(fmt.Sprintf("// %s is the DAO for table %s\n", entityName, tableName))
|
||||
content.WriteString(fmt.Sprintf("var %s = New%s()\n\n", lowerName, entityName))
|
||||
content.WriteString(fmt.Sprintf("// %s creates and returns a new DAO instance\n", entityName))
|
||||
content.WriteString(fmt.Sprintf("func New%s() *%sDao {\n", entityName, entityName))
|
||||
content.WriteString(fmt.Sprintf("\treturn &%sDao{\n", entityName))
|
||||
content.WriteString("\t\ttable: \"" + tableName + "\",\n")
|
||||
content.WriteString("\t}\n")
|
||||
content.WriteString("}\n\n")
|
||||
content.WriteString(fmt.Sprintf("// %sDao is the data access object for %s\n", entityName, tableName))
|
||||
content.WriteString(fmt.Sprintf("type %sDao struct {\n", entityName))
|
||||
content.WriteString("\ttable string\n")
|
||||
content.WriteString("}\n\n")
|
||||
content.WriteString("// Table returns the table name\n")
|
||||
content.WriteString(fmt.Sprintf("func (d *%sDao) Table() string {\n", entityName))
|
||||
content.WriteString("\treturn d.table\n")
|
||||
content.WriteString("}\n\n")
|
||||
content.WriteString("// DB returns the database instance\n")
|
||||
content.WriteString("func (d *DB) DB() database.DB {\n")
|
||||
content.WriteString("\treturn database.Database()\n")
|
||||
content.WriteString("}\n")
|
||||
|
||||
if err := os.WriteFile(filename, []byte(content.String()), 0644); err != nil {
|
||||
fmt.Printf(" ❌ 写入文件失败: %v\n", err)
|
||||
} else {
|
||||
fmt.Printf(" 📄 生成 DAO: %s\n", filename)
|
||||
}
|
||||
}
|
||||
|
||||
// 生成 Table 文件
|
||||
func generateTable(tableName, entityName string, fields map[string]*database.TableField) {
|
||||
filename := fmt.Sprintf("./internal/model/table/%s.go", tableName)
|
||||
|
||||
var content strings.Builder
|
||||
content.WriteString("package table\n\n")
|
||||
content.WriteString("// Auto-generated by gin-base gen dao tool\n\n")
|
||||
content.WriteString("const (\n")
|
||||
content.WriteString(fmt.Sprintf("\t// %s is the table name\n", entityName))
|
||||
content.WriteString(fmt.Sprintf("\t%s = \"%s\"\n", entityName, tableName))
|
||||
content.WriteString(")\n\n")
|
||||
content.WriteString("// Columns defines all columns of the table\n")
|
||||
content.WriteString("var Columns = struct {\n")
|
||||
|
||||
for _, field := range fields {
|
||||
fieldName := fieldNameToConstName(field.Name)
|
||||
content.WriteString(fmt.Sprintf("\t%s string\n", fieldName))
|
||||
}
|
||||
content.WriteString("}{\n")
|
||||
|
||||
for _, field := range fields {
|
||||
fieldName := fieldNameToConstName(field.Name)
|
||||
content.WriteString(fmt.Sprintf("\t%s: \"%s\",\n", fieldName, field.Name))
|
||||
}
|
||||
content.WriteString("}\n")
|
||||
|
||||
if err := os.WriteFile(filename, []byte(content.String()), 0644); err != nil {
|
||||
fmt.Printf(" ❌ 写入文件失败: %v\n", err)
|
||||
} else {
|
||||
fmt.Printf(" 📄 生成 Table: %s\n", filename)
|
||||
}
|
||||
}
|
||||
|
||||
// 字段名转结构体字段名
|
||||
func fieldNameToStructName(fieldName string) string {
|
||||
parts := strings.Split(fieldName, "_")
|
||||
var result strings.Builder
|
||||
for _, part := range parts {
|
||||
if len(part) > 0 {
|
||||
result.WriteString(strings.ToUpper(part[:1]))
|
||||
result.WriteString(part[1:])
|
||||
}
|
||||
}
|
||||
return result.String()
|
||||
}
|
||||
|
||||
// 字段名转常量名
|
||||
func fieldNameToConstName(fieldName string) string {
|
||||
parts := strings.Split(fieldName, "_")
|
||||
var result strings.Builder
|
||||
for i, part := range parts {
|
||||
if i > 0 {
|
||||
result.WriteString("_")
|
||||
}
|
||||
result.WriteString(strings.ToUpper(part))
|
||||
}
|
||||
return result.String()
|
||||
}
|
||||
|
||||
// 数据库类型转 Go 类型
|
||||
func dbTypeToGoType(dbType string) string {
|
||||
dbType = strings.ToLower(dbType)
|
||||
|
||||
switch {
|
||||
case strings.Contains(dbType, "int"):
|
||||
if strings.Contains(dbType, "bigint") {
|
||||
return "int64"
|
||||
}
|
||||
return "int"
|
||||
case strings.Contains(dbType, "float"), strings.Contains(dbType, "double"), strings.Contains(dbType, "decimal"):
|
||||
return "float64"
|
||||
case strings.Contains(dbType, "bool"):
|
||||
return "bool"
|
||||
case strings.Contains(dbType, "datetime"), strings.Contains(dbType, "timestamp"):
|
||||
return "*gtime.Time"
|
||||
case strings.Contains(dbType, "date"):
|
||||
return "*gtime.Time"
|
||||
case strings.Contains(dbType, "text"), strings.Contains(dbType, "char"), strings.Contains(dbType, "varchar"):
|
||||
return "string"
|
||||
case strings.Contains(dbType, "blob"), strings.Contains(dbType, "binary"):
|
||||
return "[]byte"
|
||||
default:
|
||||
return "string"
|
||||
}
|
||||
}
|
||||
415
cmd/main.go
415
cmd/main.go
|
|
@ -4,9 +4,10 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"git.magicany.cc/black1552/gin-base/cmd/gendao"
|
||||
"git.magicany.cc/black1552/gin-base/config"
|
||||
"git.magicany.cc/black1552/gin-base/database"
|
||||
_ "git.magicany.cc/black1552/gin-base/database/drivers"
|
||||
)
|
||||
|
||||
|
|
@ -38,102 +39,151 @@ func main() {
|
|||
host := getStringValue(defaultDbConfig, "host", "127.0.0.1")
|
||||
port := getStringValue(defaultDbConfig, "port", "3306")
|
||||
name := getStringValue(defaultDbConfig, "name", "test")
|
||||
user := getStringValue(defaultDbConfig, "user", "root")
|
||||
pass := getStringValue(defaultDbConfig, "pass", "")
|
||||
dbType := getStringValue(defaultDbConfig, "type", "mysql")
|
||||
link := getStringValue(defaultDbConfig, "link", "")
|
||||
|
||||
fmt.Println("=== Gin-Base DAO 代码生成工具 ===")
|
||||
fmt.Printf("📊 数据库: %s\n", name)
|
||||
fmt.Printf("🔧 类型: %s\n", dbType)
|
||||
fmt.Printf("🌐 主机: %s:%s\n\n", host, port)
|
||||
|
||||
// 构建数据库连接字符串
|
||||
var connectionInfo string
|
||||
if link == "" {
|
||||
// 如果没有配置 link,则根据数据库类型构建
|
||||
switch dbType {
|
||||
case "sqlite":
|
||||
// SQLite 使用配置文件中的 link 或默认路径
|
||||
connectionInfo = fmt.Sprintf("📁 数据库文件: %s", link)
|
||||
case "mysql", "mariadb":
|
||||
link = fmt.Sprintf("mysql:%s:%s@tcp(%s:%s)/%s?charset=utf8&parseTime=true&loc=Local",
|
||||
user, pass, host, port, name,
|
||||
)
|
||||
connectionInfo = fmt.Sprintf("📊 数据库: %s\n🌐 主机: %s:%s", name, host, port)
|
||||
case "pgsql", "postgresql":
|
||||
link = fmt.Sprintf("pgsql:%s:%s@tcp(%s:%s)/%s?sslmode=disable",
|
||||
user, pass, host, port, name,
|
||||
)
|
||||
connectionInfo = fmt.Sprintf("📊 数据库: %s\n🌐 主机: %s:%s", name, host, port)
|
||||
case "mssql":
|
||||
link = fmt.Sprintf("mssql:%s:%s@tcp(%s:%s)/%s",
|
||||
user, pass, host, port, name,
|
||||
)
|
||||
connectionInfo = fmt.Sprintf("📊 数据库: %s\n🌐 主机: %s:%s", name, host, port)
|
||||
case "oracle":
|
||||
link = fmt.Sprintf("oracle:%s:%s@%s:%s/%s",
|
||||
user, pass, host, port, name,
|
||||
)
|
||||
connectionInfo = fmt.Sprintf("📊 数据库: %s\n🌐 主机: %s:%s", name, host, port)
|
||||
case "clickhouse":
|
||||
link = fmt.Sprintf("clickhouse:%s:%s@tcp(%s:%s)/%s",
|
||||
user, pass, host, port, name,
|
||||
)
|
||||
connectionInfo = fmt.Sprintf("📊 数据库: %s\n🌐 主机: %s:%s", name, host, port)
|
||||
default:
|
||||
fmt.Printf("⚠️ 警告: 未知的数据库类型 %s,尝试使用配置的 link\n", dbType)
|
||||
if link == "" {
|
||||
fmt.Println("❌ 错误: 未配置数据库连接信息")
|
||||
os.Exit(1)
|
||||
}
|
||||
connectionInfo = "🔗 使用自定义连接"
|
||||
}
|
||||
} else {
|
||||
// 使用配置文件中直接提供的 link
|
||||
connectionInfo = fmt.Sprintf("🔗 连接: %s", link)
|
||||
// 初始化数据库连接
|
||||
err := initDatabaseFromMap(dbConfigMap)
|
||||
if err != nil {
|
||||
fmt.Printf("❌ 数据库初始化失败: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Println(connectionInfo)
|
||||
// 获取数据库实例(使用 default 组)
|
||||
db := database.Database("default")
|
||||
|
||||
// 调试信息:打印当前使用的数据库名称
|
||||
config := db.GetConfig()
|
||||
if config != nil {
|
||||
fmt.Printf("🔍 调试: 当前数据库名 = %s, 类型 = %s\n", config.Name, config.Type)
|
||||
}
|
||||
|
||||
// 获取所有表
|
||||
tables, err := db.Tables(ctx)
|
||||
if err != nil {
|
||||
fmt.Printf("❌ 获取表列表失败: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// 如果没找到表,尝试直接查询验证
|
||||
if len(tables) == 0 {
|
||||
fmt.Println("⚠️ 警告: 未找到任何表,尝试直接查询...")
|
||||
result, err := db.Query(ctx, "SHOW TABLES")
|
||||
if err != nil {
|
||||
fmt.Printf("❌ 直接查询失败: %v\n", err)
|
||||
} else {
|
||||
fmt.Printf("🔍 直接查询结果: %d 行\n", len(result))
|
||||
for _, row := range result {
|
||||
fmt.Printf(" - %v\n", row)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("📋 找到 %d 个表:\n", len(tables))
|
||||
for i, table := range tables {
|
||||
fmt.Printf(" %d. %s\n", i+1, table)
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
// 准备表名参数
|
||||
tablesArg := ""
|
||||
// 询问用户要生成的表
|
||||
var selectedTables []string
|
||||
if len(os.Args) > 1 {
|
||||
tablesArg = joinStrings(os.Args[1:], ",")
|
||||
fmt.Printf("📋 指定生成表: %s\n\n", tablesArg)
|
||||
// 从命令行参数获取表名
|
||||
selectedTables = os.Args[1:]
|
||||
} else {
|
||||
// 默认生成所有表
|
||||
selectedTables = tables
|
||||
fmt.Println("💡 提示: 可以通过命令行参数指定要生成的表")
|
||||
fmt.Println(" 例如: gin-dao-gen users orders\n")
|
||||
fmt.Println(" 例如: gin-dao-gen users orders")
|
||||
}
|
||||
|
||||
// 调用 gendao 的 Dao 函数生成代码
|
||||
genDao := gendao.CGenDao{}
|
||||
input := gendao.CGenDaoInput{
|
||||
Link: link,
|
||||
Tables: tablesArg,
|
||||
Path: "./",
|
||||
DaoPath: "dao",
|
||||
DoPath: "model/do",
|
||||
EntityPath: "model/entity",
|
||||
TablePath: "model/table",
|
||||
Group: "default",
|
||||
JsonCase: "CamelLower",
|
||||
DescriptionTag: true,
|
||||
GenTable: true,
|
||||
// 创建输出目录
|
||||
dirs := []string{
|
||||
"./internal/dao",
|
||||
"./internal/model/do",
|
||||
"./internal/model/entity",
|
||||
"./internal/model/table",
|
||||
}
|
||||
|
||||
fmt.Println("🔨 开始生成代码...")
|
||||
_, err := genDao.Dao(ctx, input)
|
||||
if err != nil {
|
||||
fmt.Printf("\n❌ 代码生成失败: %v\n", err)
|
||||
os.Exit(1)
|
||||
for _, dir := range dirs {
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
fmt.Printf("❌ 创建目录失败 %s: %v\n", dir, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// 为每个表生成代码
|
||||
for _, tableName := range selectedTables {
|
||||
fmt.Printf("\n🔨 正在生成表 [%s] 的代码...\n", tableName)
|
||||
|
||||
// 获取表字段信息
|
||||
fields, err := db.TableFields(ctx, tableName)
|
||||
if err != nil {
|
||||
fmt.Printf(" ⚠️ 获取表字段失败: %v\n", err)
|
||||
return // 使用 return 而不是 continue
|
||||
}
|
||||
|
||||
// 调试:打印字段信息
|
||||
fmt.Printf(" 🔍 调试: 找到 %d 个字段\n", len(fields))
|
||||
for name, field := range fields {
|
||||
fmt.Printf(" - %s: %s (%s)\n", name, field.Type, field.Comment)
|
||||
}
|
||||
|
||||
// 生成 Entity
|
||||
entityName := tableNameToStructName(tableName)
|
||||
generateEntity(tableName, entityName, fields)
|
||||
|
||||
// 生成 DO
|
||||
generateDO(tableName, entityName, fields)
|
||||
|
||||
// 生成 DAO
|
||||
generateDAO(tableName, entityName)
|
||||
|
||||
// 生成 Table
|
||||
generateTable(tableName, entityName, fields)
|
||||
|
||||
fmt.Printf(" ✅ 完成\n")
|
||||
}
|
||||
|
||||
fmt.Println("\n🎉 代码生成完成!")
|
||||
fmt.Println("📁 生成的文件位于:")
|
||||
fmt.Println(" - ./dao/")
|
||||
fmt.Println(" - ./model/do/")
|
||||
fmt.Println(" - ./model/entity/")
|
||||
fmt.Println(" - ./model/table/")
|
||||
fmt.Println(" - ./internal/dao/")
|
||||
fmt.Println(" - ./internal/model/do/")
|
||||
fmt.Println(" - ./internal/model/entity/")
|
||||
fmt.Println(" - ./internal/model/table/")
|
||||
}
|
||||
|
||||
// 从 Map 初始化数据库
|
||||
func initDatabaseFromMap(dbConfigMap map[string]any) error {
|
||||
for name, nodeConfig := range dbConfigMap {
|
||||
nodeMap, ok := nodeConfig.(map[string]any)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
configNode := database.ConfigNode{
|
||||
Host: getStringValue(nodeMap, "host", "127.0.0.1"),
|
||||
Port: getStringValue(nodeMap, "port", "3306"),
|
||||
User: getStringValue(nodeMap, "user", "root"),
|
||||
Pass: getStringValue(nodeMap, "pass", ""),
|
||||
Name: getStringValue(nodeMap, "name", ""),
|
||||
Type: getStringValue(nodeMap, "type", "mysql"),
|
||||
Role: database.Role(getStringValue(nodeMap, "role", "master")),
|
||||
Debug: getBoolValue(nodeMap, "debug", false),
|
||||
Prefix: getStringValue(nodeMap, "prefix", ""),
|
||||
Charset: getStringValue(nodeMap, "charset", "utf8"),
|
||||
}
|
||||
|
||||
if err := database.AddConfigNode(name, configNode); err != nil {
|
||||
return fmt.Errorf("add config node %s failed: %w", name, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 辅助函数:从 map 中获取字符串值
|
||||
|
|
@ -146,14 +196,207 @@ func getStringValue(m map[string]any, key string, defaultValue string) string {
|
|||
return defaultValue
|
||||
}
|
||||
|
||||
// 辅助函数:连接字符串数组
|
||||
func joinStrings(strs []string, sep string) string {
|
||||
if len(strs) == 0 {
|
||||
return ""
|
||||
// 辅助函数:从 map 中获取布尔值
|
||||
func getBoolValue(m map[string]any, key string, defaultValue bool) bool {
|
||||
if val, ok := m[key]; ok {
|
||||
if b, ok := val.(bool); ok {
|
||||
return b
|
||||
}
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// 表名转结构体名
|
||||
func tableNameToStructName(tableName string) string {
|
||||
parts := strings.Split(tableName, "_")
|
||||
var result strings.Builder
|
||||
for _, part := range parts {
|
||||
if len(part) > 0 {
|
||||
result.WriteString(strings.ToUpper(part[:1]))
|
||||
result.WriteString(part[1:])
|
||||
}
|
||||
}
|
||||
return result.String()
|
||||
}
|
||||
|
||||
// 生成 Entity 文件
|
||||
func generateEntity(tableName, entityName string, fields map[string]*database.TableField) {
|
||||
filename := fmt.Sprintf("./internal/model/entity/%s.go", tableName)
|
||||
|
||||
var content strings.Builder
|
||||
content.WriteString("package entity\n\n")
|
||||
content.WriteString("// Auto-generated by gin-base gen dao tool\n\n")
|
||||
content.WriteString(fmt.Sprintf("// %s represents the entity for table %s\n", entityName, tableName))
|
||||
content.WriteString(fmt.Sprintf("type %s struct {\n", entityName))
|
||||
|
||||
for _, field := range fields {
|
||||
fieldName := fieldNameToStructName(field.Name)
|
||||
goType := dbTypeToGoType(field.Type)
|
||||
jsonTag := field.Name
|
||||
|
||||
content.WriteString(fmt.Sprintf("\t%s %s `json:\"%s\" description:\"%s\"`\n",
|
||||
fieldName, goType, jsonTag, field.Comment))
|
||||
}
|
||||
|
||||
content.WriteString("}\n")
|
||||
|
||||
if err := os.WriteFile(filename, []byte(content.String()), 0644); err != nil {
|
||||
fmt.Printf(" ❌ 写入文件失败: %v\n", err)
|
||||
} else {
|
||||
fmt.Printf(" 📄 生成 Entity: %s\n", filename)
|
||||
}
|
||||
}
|
||||
|
||||
// 生成 DO 文件
|
||||
func generateDO(tableName, entityName string, fields map[string]*database.TableField) {
|
||||
filename := fmt.Sprintf("./internal/model/do/%s.go", tableName)
|
||||
|
||||
var content strings.Builder
|
||||
content.WriteString("package do\n\n")
|
||||
content.WriteString("import \"github.com/gogf/gf/v2/frame/g\"\n\n")
|
||||
content.WriteString("// Auto-generated by gin-base gen dao tool\n\n")
|
||||
content.WriteString(fmt.Sprintf("// %s represents the data object for table %s\n", entityName, tableName))
|
||||
content.WriteString(fmt.Sprintf("type %s struct {\n\tg.Meta `orm:\"table:%s, do:true\"`\n\n", entityName, tableName))
|
||||
|
||||
for _, field := range fields {
|
||||
fieldName := fieldNameToStructName(field.Name)
|
||||
goType := dbTypeToGoType(field.Type)
|
||||
|
||||
content.WriteString(fmt.Sprintf("\t%s *%s `json:\"%s,omitempty\"`\n",
|
||||
fieldName, goType, field.Name))
|
||||
}
|
||||
|
||||
content.WriteString("}\n")
|
||||
|
||||
if err := os.WriteFile(filename, []byte(content.String()), 0644); err != nil {
|
||||
fmt.Printf(" ❌ 写入文件失败: %v\n", err)
|
||||
} else {
|
||||
fmt.Printf(" 📄 生成 DO: %s\n", filename)
|
||||
}
|
||||
}
|
||||
|
||||
// 生成 DAO 文件
|
||||
func generateDAO(tableName, entityName string) {
|
||||
filename := fmt.Sprintf("./internal/dao/%s.go", tableName)
|
||||
lowerName := strings.ToLower(entityName[:1]) + entityName[1:]
|
||||
|
||||
var content strings.Builder
|
||||
content.WriteString("package dao\n\n")
|
||||
content.WriteString("import (\n")
|
||||
content.WriteString("\t\"git.magicany.cc/black1552/gin-base/database\"\n")
|
||||
content.WriteString(fmt.Sprintf("\t\"git.magicany.cc/black1552/gin-base/internal/model/entity\"\n"))
|
||||
content.WriteString(")\n\n")
|
||||
content.WriteString("// Auto-generated by gin-base gen dao tool\n\n")
|
||||
content.WriteString(fmt.Sprintf("// %s is the DAO for table %s\n", entityName, tableName))
|
||||
content.WriteString(fmt.Sprintf("var %s = New%s()\n\n", lowerName, entityName))
|
||||
content.WriteString(fmt.Sprintf("// %s creates and returns a new DAO instance\n", entityName))
|
||||
content.WriteString(fmt.Sprintf("func New%s() *%sDao {\n", entityName, entityName))
|
||||
content.WriteString(fmt.Sprintf("\treturn &%sDao{\n", entityName))
|
||||
content.WriteString("\t\ttable: \"" + tableName + "\",\n")
|
||||
content.WriteString("\t}\n")
|
||||
content.WriteString("}\n\n")
|
||||
content.WriteString(fmt.Sprintf("// %sDao is the data access object for %s\n", entityName, tableName))
|
||||
content.WriteString(fmt.Sprintf("type %sDao struct {\n", entityName))
|
||||
content.WriteString("\ttable string\n")
|
||||
content.WriteString("}\n\n")
|
||||
content.WriteString("// Table returns the table name\n")
|
||||
content.WriteString(fmt.Sprintf("func (d *%sDao) Table() string {\n", entityName))
|
||||
content.WriteString("\treturn d.table\n")
|
||||
content.WriteString("}\n\n")
|
||||
content.WriteString("// DB returns the database instance\n")
|
||||
content.WriteString("func (d *DB) DB() database.DB {\n")
|
||||
content.WriteString("\treturn database.Database()\n")
|
||||
content.WriteString("}\n")
|
||||
|
||||
if err := os.WriteFile(filename, []byte(content.String()), 0644); err != nil {
|
||||
fmt.Printf(" ❌ 写入文件失败: %v\n", err)
|
||||
} else {
|
||||
fmt.Printf(" 📄 生成 DAO: %s\n", filename)
|
||||
}
|
||||
}
|
||||
|
||||
// 生成 Table 文件
|
||||
func generateTable(tableName, entityName string, fields map[string]*database.TableField) {
|
||||
filename := fmt.Sprintf("./internal/model/table/%s.go", tableName)
|
||||
|
||||
var content strings.Builder
|
||||
content.WriteString("package table\n\n")
|
||||
content.WriteString("// Auto-generated by gin-base gen dao tool\n\n")
|
||||
content.WriteString("const (\n")
|
||||
content.WriteString(fmt.Sprintf("\t// %s is the table name\n", entityName))
|
||||
content.WriteString(fmt.Sprintf("\t%s = \"%s\"\n", entityName, tableName))
|
||||
content.WriteString(")\n\n")
|
||||
content.WriteString("// Columns defines all columns of the table\n")
|
||||
content.WriteString("var Columns = struct {\n")
|
||||
|
||||
for _, field := range fields {
|
||||
fieldName := fieldNameToConstName(field.Name)
|
||||
content.WriteString(fmt.Sprintf("\t%s string\n", fieldName))
|
||||
}
|
||||
content.WriteString("}{\n")
|
||||
|
||||
for _, field := range fields {
|
||||
fieldName := fieldNameToConstName(field.Name)
|
||||
content.WriteString(fmt.Sprintf("\t%s: \"%s\",\n", fieldName, field.Name))
|
||||
}
|
||||
content.WriteString("}\n")
|
||||
|
||||
if err := os.WriteFile(filename, []byte(content.String()), 0644); err != nil {
|
||||
fmt.Printf(" ❌ 写入文件失败: %v\n", err)
|
||||
} else {
|
||||
fmt.Printf(" 📄 生成 Table: %s\n", filename)
|
||||
}
|
||||
}
|
||||
|
||||
// 字段名转结构体字段名
|
||||
func fieldNameToStructName(fieldName string) string {
|
||||
parts := strings.Split(fieldName, "_")
|
||||
var result strings.Builder
|
||||
for _, part := range parts {
|
||||
if len(part) > 0 {
|
||||
result.WriteString(strings.ToUpper(part[:1]))
|
||||
result.WriteString(part[1:])
|
||||
}
|
||||
}
|
||||
return result.String()
|
||||
}
|
||||
|
||||
// 字段名转常量名
|
||||
func fieldNameToConstName(fieldName string) string {
|
||||
parts := strings.Split(fieldName, "_")
|
||||
var result strings.Builder
|
||||
for i, part := range parts {
|
||||
if i > 0 {
|
||||
result.WriteString("_")
|
||||
}
|
||||
result.WriteString(strings.ToUpper(part))
|
||||
}
|
||||
return result.String()
|
||||
}
|
||||
|
||||
// 数据库类型转 Go 类型
|
||||
func dbTypeToGoType(dbType string) string {
|
||||
dbType = strings.ToLower(dbType)
|
||||
|
||||
switch {
|
||||
case strings.Contains(dbType, "int"):
|
||||
if strings.Contains(dbType, "bigint") {
|
||||
return "int64"
|
||||
}
|
||||
return "int"
|
||||
case strings.Contains(dbType, "float"), strings.Contains(dbType, "double"), strings.Contains(dbType, "decimal"):
|
||||
return "float64"
|
||||
case strings.Contains(dbType, "bool"):
|
||||
return "bool"
|
||||
case strings.Contains(dbType, "datetime"), strings.Contains(dbType, "timestamp"):
|
||||
return "*gtime.Time"
|
||||
case strings.Contains(dbType, "date"):
|
||||
return "*gtime.Time"
|
||||
case strings.Contains(dbType, "text"), strings.Contains(dbType, "char"), strings.Contains(dbType, "varchar"):
|
||||
return "string"
|
||||
case strings.Contains(dbType, "blob"), strings.Contains(dbType, "binary"):
|
||||
return "[]byte"
|
||||
default:
|
||||
return "string"
|
||||
}
|
||||
result := strs[0]
|
||||
for i := 1; i < len(strs); i++ {
|
||||
result += sep + strs[i]
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,13 +61,3 @@ func (d *Driver) injectNeedParsedSql(ctx context.Context) context.Context {
|
|||
}
|
||||
return context.WithValue(ctx, needParsedSqlInCtx, true)
|
||||
}
|
||||
|
||||
// Migration returns a Migration instance for ClickHouse database operations.
|
||||
func (d *Driver) Migration() *Migration {
|
||||
return NewMigration(d)
|
||||
}
|
||||
|
||||
// GetMigration returns a Migration instance implementing the Migration interface.
|
||||
func (d *Driver) GetMigration() database.Migration {
|
||||
return d.Migration()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,321 +0,0 @@
|
|||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package clickhouse
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.magicany.cc/black1552/gin-base/database"
|
||||
)
|
||||
|
||||
// Migration implements database migration operations for ClickHouse.
|
||||
type Migration struct {
|
||||
*database.MigrationCore
|
||||
*database.AutoMigrateCore
|
||||
}
|
||||
|
||||
// NewMigration creates a new ClickHouse Migration instance.
|
||||
func NewMigration(db database.DB) *Migration {
|
||||
return &Migration{
|
||||
MigrationCore: database.NewMigrationCore(db),
|
||||
AutoMigrateCore: database.NewAutoMigrateCore(db),
|
||||
}
|
||||
}
|
||||
|
||||
// CreateTable creates a new table with the given name and column definitions.
|
||||
func (m *Migration) CreateTable(ctx context.Context, table string, columns map[string]*database.ColumnDefinition, options ...database.TableOption) error {
|
||||
if len(columns) == 0 {
|
||||
return fmt.Errorf("cannot create table without columns")
|
||||
}
|
||||
|
||||
var opts database.TableOptions
|
||||
for _, opt := range options {
|
||||
opt(&opts)
|
||||
}
|
||||
|
||||
var sql strings.Builder
|
||||
sql.WriteString("CREATE TABLE ")
|
||||
if opts.IfNotExists {
|
||||
sql.WriteString("IF NOT EXISTS ")
|
||||
}
|
||||
sql.WriteString(database.QuoteIdentifier(table))
|
||||
sql.WriteString(" (\n")
|
||||
|
||||
// Add columns
|
||||
var colDefs []string
|
||||
for name, def := range columns {
|
||||
colDef := m.buildColumnDefinition(name, def)
|
||||
colDefs = append(colDefs, " "+colDef)
|
||||
}
|
||||
|
||||
sql.WriteString(strings.Join(colDefs, ",\n"))
|
||||
sql.WriteString("\n)")
|
||||
|
||||
// Add engine specification (required for ClickHouse)
|
||||
sql.WriteString(" ENGINE = MergeTree()")
|
||||
|
||||
if opts.Comment != "" {
|
||||
sql.WriteString(fmt.Sprintf(" COMMENT '%s'", escapeString(opts.Comment)))
|
||||
}
|
||||
|
||||
return m.ExecuteSQL(ctx, sql.String())
|
||||
}
|
||||
|
||||
// buildColumnDefinition builds column definition for ClickHouse.
|
||||
func (m *Migration) buildColumnDefinition(name string, def *database.ColumnDefinition) string {
|
||||
var parts []string
|
||||
parts = append(parts, database.QuoteIdentifier(name))
|
||||
|
||||
// Handle ClickHouse-specific types
|
||||
dbType := def.Type
|
||||
parts = append(parts, dbType)
|
||||
|
||||
if !def.Null {
|
||||
// ClickHouse uses Nullable type wrapper
|
||||
if !strings.HasPrefix(dbType, "Nullable(") {
|
||||
// Type is already non-nullable by default in ClickHouse
|
||||
}
|
||||
} else {
|
||||
// Make type nullable if needed
|
||||
if !strings.HasPrefix(dbType, "Nullable(") {
|
||||
parts[len(parts)-1] = fmt.Sprintf("Nullable(%s)", dbType)
|
||||
}
|
||||
}
|
||||
|
||||
if def.Default != nil {
|
||||
defaultValue := formatDefaultValue(def.Default)
|
||||
parts = append(parts, fmt.Sprintf("DEFAULT %s", defaultValue))
|
||||
}
|
||||
|
||||
if def.Comment != "" {
|
||||
parts = append(parts, fmt.Sprintf("COMMENT '%s'", escapeString(def.Comment)))
|
||||
}
|
||||
|
||||
return strings.Join(parts, " ")
|
||||
}
|
||||
|
||||
// DropTable drops an existing table from the database.
|
||||
func (m *Migration) DropTable(ctx context.Context, table string, ifExists ...bool) error {
|
||||
sql := "DROP TABLE "
|
||||
if len(ifExists) > 0 && ifExists[0] {
|
||||
sql += "IF EXISTS "
|
||||
}
|
||||
sql += database.QuoteIdentifier(table)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// HasTable checks if a table exists in the database.
|
||||
func (m *Migration) HasTable(ctx context.Context, table string) (bool, error) {
|
||||
schema := m.GetDB().GetSchema()
|
||||
if schema == "" {
|
||||
schema = "currentDatabase()"
|
||||
} else {
|
||||
schema = fmt.Sprintf("'%s'", schema)
|
||||
}
|
||||
|
||||
query := fmt.Sprintf(
|
||||
"SELECT COUNT(*) FROM system.tables WHERE database = %s AND name = '%s'",
|
||||
schema,
|
||||
table,
|
||||
)
|
||||
|
||||
value, err := m.GetDB().GetValue(ctx, query)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return value.Int() > 0, nil
|
||||
}
|
||||
|
||||
// RenameTable renames an existing table from oldName to newName.
|
||||
func (m *Migration) RenameTable(ctx context.Context, oldName, newName string) error {
|
||||
sql := fmt.Sprintf(
|
||||
"RENAME TABLE %s TO %s",
|
||||
database.QuoteIdentifier(oldName),
|
||||
database.QuoteIdentifier(newName),
|
||||
)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// TruncateTable removes all records from a table but keeps the table structure.
|
||||
func (m *Migration) TruncateTable(ctx context.Context, table string) error {
|
||||
sql := fmt.Sprintf("TRUNCATE TABLE %s", database.QuoteIdentifier(table))
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// AddColumn adds a new column to an existing table.
|
||||
func (m *Migration) AddColumn(ctx context.Context, table, column string, definition *database.ColumnDefinition) error {
|
||||
colDef := m.buildColumnDefinition(column, definition)
|
||||
sql := fmt.Sprintf("ALTER TABLE %s ADD COLUMN %s", database.QuoteIdentifier(table), colDef)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// DropColumn removes a column from an existing table.
|
||||
func (m *Migration) DropColumn(ctx context.Context, table, column string) error {
|
||||
sql := fmt.Sprintf(
|
||||
"ALTER TABLE %s DROP COLUMN %s",
|
||||
database.QuoteIdentifier(table),
|
||||
database.QuoteIdentifier(column),
|
||||
)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// RenameColumn renames a column in an existing table.
|
||||
func (m *Migration) RenameColumn(ctx context.Context, table, oldName, newName string) error {
|
||||
sql := fmt.Sprintf(
|
||||
"ALTER TABLE %s RENAME COLUMN %s TO %s",
|
||||
database.QuoteIdentifier(table),
|
||||
database.QuoteIdentifier(oldName),
|
||||
database.QuoteIdentifier(newName),
|
||||
)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// ModifyColumn modifies an existing column's definition.
|
||||
func (m *Migration) ModifyColumn(ctx context.Context, table, column string, definition *database.ColumnDefinition) error {
|
||||
colDef := m.buildColumnDefinition(column, definition)
|
||||
sql := fmt.Sprintf("ALTER TABLE %s MODIFY COLUMN %s", database.QuoteIdentifier(table), colDef)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// HasColumn checks if a column exists in a table.
|
||||
func (m *Migration) HasColumn(ctx context.Context, table, column string) (bool, error) {
|
||||
fields, err := m.GetDB().TableFields(ctx, table)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
_, exists := fields[column]
|
||||
return exists, nil
|
||||
}
|
||||
|
||||
// CreateIndex creates a new index on the specified table and columns.
|
||||
// Note: ClickHouse has limited index support compared to traditional databases.
|
||||
func (m *Migration) CreateIndex(ctx context.Context, table, index string, columns []string, options ...database.IndexOption) error {
|
||||
var opts database.IndexOptions
|
||||
for _, opt := range options {
|
||||
opt(&opts)
|
||||
}
|
||||
|
||||
// ClickHouse uses different indexing mechanisms
|
||||
// For now, we'll use ALTER TABLE ADD INDEX syntax
|
||||
colList := m.BuildIndexColumns(columns)
|
||||
sql := fmt.Sprintf(
|
||||
"ALTER TABLE %s ADD INDEX %s (%s) TYPE minmax GRANULARITY 1",
|
||||
database.QuoteIdentifier(table),
|
||||
database.QuoteIdentifier(index),
|
||||
colList,
|
||||
)
|
||||
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// DropIndex drops an existing index from a table.
|
||||
func (m *Migration) DropIndex(ctx context.Context, table, index string) error {
|
||||
sql := fmt.Sprintf(
|
||||
"ALTER TABLE %s DROP INDEX %s",
|
||||
database.QuoteIdentifier(table),
|
||||
database.QuoteIdentifier(index),
|
||||
)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// HasIndex checks if an index exists on a table.
|
||||
func (m *Migration) HasIndex(ctx context.Context, table, index string) (bool, error) {
|
||||
schema := m.GetDB().GetSchema()
|
||||
if schema == "" {
|
||||
schema = "currentDatabase()"
|
||||
} else {
|
||||
schema = fmt.Sprintf("'%s'", schema)
|
||||
}
|
||||
|
||||
query := fmt.Sprintf(
|
||||
"SELECT COUNT(*) FROM system.data_skipping_indices WHERE database = %s AND table = '%s' AND name = '%s'",
|
||||
schema,
|
||||
table,
|
||||
index,
|
||||
)
|
||||
|
||||
value, err := m.GetDB().GetValue(ctx, query)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return value.Int() > 0, nil
|
||||
}
|
||||
|
||||
// CreateForeignKey is not supported in ClickHouse.
|
||||
func (m *Migration) CreateForeignKey(ctx context.Context, table, constraint string, columns []string, refTable string, refColumns []string, options ...database.ForeignKeyOption) error {
|
||||
// ClickHouse does not support foreign keys
|
||||
return fmt.Errorf("ClickHouse does not support foreign key constraints")
|
||||
}
|
||||
|
||||
// DropForeignKey is not supported in ClickHouse.
|
||||
func (m *Migration) DropForeignKey(ctx context.Context, table, constraint string) error {
|
||||
// ClickHouse does not support foreign keys
|
||||
return fmt.Errorf("ClickHouse does not support foreign key constraints")
|
||||
}
|
||||
|
||||
// HasForeignKey always returns false as ClickHouse doesn't support foreign keys.
|
||||
func (m *Migration) HasForeignKey(ctx context.Context, table, constraint string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// CreateSchema creates a new database schema.
|
||||
func (m *Migration) CreateSchema(ctx context.Context, schema string) error {
|
||||
sql := fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s", database.QuoteIdentifier(schema))
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// DropSchema drops an existing database schema.
|
||||
func (m *Migration) DropSchema(ctx context.Context, schema string, cascade ...bool) error {
|
||||
sql := fmt.Sprintf("DROP DATABASE IF EXISTS %s", database.QuoteIdentifier(schema))
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// HasSchema checks if a schema exists.
|
||||
func (m *Migration) HasSchema(ctx context.Context, schema string) (bool, error) {
|
||||
query := fmt.Sprintf(
|
||||
"SELECT COUNT(*) FROM system.databases WHERE name = '%s'",
|
||||
schema,
|
||||
)
|
||||
|
||||
value, err := m.GetDB().GetValue(ctx, query)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return value.Int() > 0, nil
|
||||
}
|
||||
|
||||
// formatDefaultValue formats the default value for SQL.
|
||||
func formatDefaultValue(value any) string {
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
return fmt.Sprintf("'%s'", escapeString(v))
|
||||
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
|
||||
return fmt.Sprintf("%d", v)
|
||||
case float32, float64:
|
||||
return fmt.Sprintf("%f", v)
|
||||
case bool:
|
||||
if v {
|
||||
return "1"
|
||||
}
|
||||
return "0"
|
||||
case nil:
|
||||
return "NULL"
|
||||
default:
|
||||
return fmt.Sprintf("'%v'", v)
|
||||
}
|
||||
}
|
||||
|
||||
// escapeString escapes special characters in strings for SQL.
|
||||
func escapeString(s string) string {
|
||||
s = strings.ReplaceAll(s, "'", "\\'")
|
||||
return s
|
||||
}
|
||||
|
|
@ -46,13 +46,3 @@ func (d *Driver) New(core *database.Core, node *database.ConfigNode) (database.D
|
|||
func (d *Driver) GetChars() (charLeft string, charRight string) {
|
||||
return quoteChar, quoteChar
|
||||
}
|
||||
|
||||
// Migration returns a Migration instance for MSSQL database operations.
|
||||
func (d *Driver) Migration() *Migration {
|
||||
return NewMigration(d)
|
||||
}
|
||||
|
||||
// GetMigration returns a Migration instance implementing the Migration interface.
|
||||
func (d *Driver) GetMigration() database.Migration {
|
||||
return d.Migration()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,340 +0,0 @@
|
|||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package mssql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.magicany.cc/black1552/gin-base/database"
|
||||
)
|
||||
|
||||
// Migration implements database migration operations for SQL Server.
|
||||
type Migration struct {
|
||||
*database.MigrationCore
|
||||
*database.AutoMigrateCore
|
||||
}
|
||||
|
||||
// NewMigration creates a new SQL Server Migration instance.
|
||||
func NewMigration(db database.DB) *Migration {
|
||||
return &Migration{
|
||||
MigrationCore: database.NewMigrationCore(db),
|
||||
AutoMigrateCore: database.NewAutoMigrateCore(db),
|
||||
}
|
||||
}
|
||||
|
||||
// CreateTable creates a new table with the given name and column definitions.
|
||||
func (m *Migration) CreateTable(ctx context.Context, table string, columns map[string]*database.ColumnDefinition, options ...database.TableOption) error {
|
||||
if len(columns) == 0 {
|
||||
return fmt.Errorf("cannot create table without columns")
|
||||
}
|
||||
|
||||
var opts database.TableOptions
|
||||
for _, opt := range options {
|
||||
opt(&opts)
|
||||
}
|
||||
|
||||
var sql strings.Builder
|
||||
sql.WriteString("CREATE TABLE ")
|
||||
sql.WriteString(database.QuoteIdentifier(table))
|
||||
sql.WriteString(" (\n")
|
||||
|
||||
// Add columns
|
||||
var colDefs []string
|
||||
var primaryKeys []string
|
||||
for name, def := range columns {
|
||||
colDef := m.buildColumnDefinition(name, def)
|
||||
if def.PrimaryKey {
|
||||
primaryKeys = append(primaryKeys, database.QuoteIdentifier(name))
|
||||
}
|
||||
colDefs = append(colDefs, " "+colDef)
|
||||
}
|
||||
|
||||
// Add primary key constraint if needed
|
||||
if len(primaryKeys) > 0 {
|
||||
colDefs = append(colDefs, fmt.Sprintf(" PRIMARY KEY (%s)", strings.Join(primaryKeys, ", ")))
|
||||
}
|
||||
|
||||
sql.WriteString(strings.Join(colDefs, ",\n"))
|
||||
sql.WriteString("\n)")
|
||||
|
||||
return m.ExecuteSQL(ctx, sql.String())
|
||||
}
|
||||
|
||||
// buildColumnDefinition builds column definition for SQL Server.
|
||||
func (m *Migration) buildColumnDefinition(name string, def *database.ColumnDefinition) string {
|
||||
var parts []string
|
||||
parts = append(parts, database.QuoteIdentifier(name))
|
||||
|
||||
// Handle SQL Server-specific types
|
||||
dbType := def.Type
|
||||
if def.AutoIncrement {
|
||||
if dbType == "INT" || dbType == "INTEGER" {
|
||||
dbType = "INT IDENTITY(1,1)"
|
||||
} else if dbType == "BIGINT" {
|
||||
dbType = "BIGINT IDENTITY(1,1)"
|
||||
}
|
||||
}
|
||||
parts = append(parts, dbType)
|
||||
|
||||
if !def.Null {
|
||||
parts = append(parts, "NOT NULL")
|
||||
} else {
|
||||
parts = append(parts, "NULL")
|
||||
}
|
||||
|
||||
if def.Default != nil {
|
||||
defaultValue := formatDefaultValue(def.Default)
|
||||
parts = append(parts, fmt.Sprintf("DEFAULT %s", defaultValue))
|
||||
}
|
||||
|
||||
return strings.Join(parts, " ")
|
||||
}
|
||||
|
||||
// DropTable drops an existing table from the database.
|
||||
func (m *Migration) DropTable(ctx context.Context, table string, ifExists ...bool) error {
|
||||
if len(ifExists) > 0 && ifExists[0] {
|
||||
sql := fmt.Sprintf(
|
||||
"IF OBJECT_ID('%s', 'U') IS NOT NULL DROP TABLE %s",
|
||||
table,
|
||||
database.QuoteIdentifier(table),
|
||||
)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
sql := fmt.Sprintf("DROP TABLE %s", database.QuoteIdentifier(table))
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// HasTable checks if a table exists in the database.
|
||||
func (m *Migration) HasTable(ctx context.Context, table string) (bool, error) {
|
||||
query := fmt.Sprintf(
|
||||
"SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = '%s'",
|
||||
table,
|
||||
)
|
||||
|
||||
value, err := m.GetDB().GetValue(ctx, query)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return value.Int() > 0, nil
|
||||
}
|
||||
|
||||
// RenameTable renames an existing table from oldName to newName.
|
||||
func (m *Migration) RenameTable(ctx context.Context, oldName, newName string) error {
|
||||
sql := fmt.Sprintf(
|
||||
"EXEC sp_rename '%s', '%s'",
|
||||
oldName,
|
||||
newName,
|
||||
)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// TruncateTable removes all records from a table but keeps the table structure.
|
||||
func (m *Migration) TruncateTable(ctx context.Context, table string) error {
|
||||
sql := fmt.Sprintf("TRUNCATE TABLE %s", database.QuoteIdentifier(table))
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// AddColumn adds a new column to an existing table.
|
||||
func (m *Migration) AddColumn(ctx context.Context, table, column string, definition *database.ColumnDefinition) error {
|
||||
colDef := m.buildColumnDefinition(column, definition)
|
||||
sql := fmt.Sprintf("ALTER TABLE %s ADD %s", database.QuoteIdentifier(table), colDef)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// DropColumn removes a column from an existing table.
|
||||
func (m *Migration) DropColumn(ctx context.Context, table, column string) error {
|
||||
sql := fmt.Sprintf(
|
||||
"ALTER TABLE %s DROP COLUMN %s",
|
||||
database.QuoteIdentifier(table),
|
||||
database.QuoteIdentifier(column),
|
||||
)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// RenameColumn renames a column in an existing table.
|
||||
func (m *Migration) RenameColumn(ctx context.Context, table, oldName, newName string) error {
|
||||
sql := fmt.Sprintf(
|
||||
"EXEC sp_rename '%s.%s', '%s', 'COLUMN'",
|
||||
table,
|
||||
oldName,
|
||||
newName,
|
||||
)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// ModifyColumn modifies an existing column's definition.
|
||||
func (m *Migration) ModifyColumn(ctx context.Context, table, column string, definition *database.ColumnDefinition) error {
|
||||
colDef := m.buildColumnDefinition(column, definition)
|
||||
sql := fmt.Sprintf("ALTER TABLE %s ALTER COLUMN %s", database.QuoteIdentifier(table), colDef)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// HasColumn checks if a column exists in a table.
|
||||
func (m *Migration) HasColumn(ctx context.Context, table, column string) (bool, error) {
|
||||
fields, err := m.GetDB().TableFields(ctx, table)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
_, exists := fields[column]
|
||||
return exists, nil
|
||||
}
|
||||
|
||||
// CreateIndex creates a new index on the specified table and columns.
|
||||
func (m *Migration) CreateIndex(ctx context.Context, table, index string, columns []string, options ...database.IndexOption) error {
|
||||
var opts database.IndexOptions
|
||||
for _, opt := range options {
|
||||
opt(&opts)
|
||||
}
|
||||
|
||||
var sql strings.Builder
|
||||
sql.WriteString("CREATE ")
|
||||
|
||||
if opts.Unique {
|
||||
sql.WriteString("UNIQUE ")
|
||||
}
|
||||
|
||||
sql.WriteString("INDEX ")
|
||||
sql.WriteString(database.QuoteIdentifier(index))
|
||||
sql.WriteString(" ON ")
|
||||
sql.WriteString(database.QuoteIdentifier(table))
|
||||
|
||||
colList := m.BuildIndexColumns(columns)
|
||||
sql.WriteString(fmt.Sprintf(" (%s)", colList))
|
||||
|
||||
return m.ExecuteSQL(ctx, sql.String())
|
||||
}
|
||||
|
||||
// DropIndex drops an existing index from a table.
|
||||
func (m *Migration) DropIndex(ctx context.Context, table, index string) error {
|
||||
sql := fmt.Sprintf("DROP INDEX %s ON %s", database.QuoteIdentifier(index), database.QuoteIdentifier(table))
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// HasIndex checks if an index exists on a table.
|
||||
func (m *Migration) HasIndex(ctx context.Context, table, index string) (bool, error) {
|
||||
query := fmt.Sprintf(
|
||||
"SELECT COUNT(*) FROM sys.indexes WHERE name = '%s' AND object_id = OBJECT_ID('%s')",
|
||||
index,
|
||||
table,
|
||||
)
|
||||
|
||||
value, err := m.GetDB().GetValue(ctx, query)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return value.Int() > 0, nil
|
||||
}
|
||||
|
||||
// CreateForeignKey creates a foreign key constraint.
|
||||
func (m *Migration) CreateForeignKey(ctx context.Context, table, constraint string, columns []string, refTable string, refColumns []string, options ...database.ForeignKeyOption) error {
|
||||
var opts database.ForeignKeyOptions
|
||||
for _, opt := range options {
|
||||
opt(&opts)
|
||||
}
|
||||
|
||||
sql := fmt.Sprintf(
|
||||
"ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY %s REFERENCES %s %s",
|
||||
database.QuoteIdentifier(table),
|
||||
database.QuoteIdentifier(constraint),
|
||||
m.BuildForeignKeyColumns(columns),
|
||||
database.QuoteIdentifier(refTable),
|
||||
m.BuildForeignKeyColumns(refColumns),
|
||||
)
|
||||
|
||||
if opts.OnDelete != "" {
|
||||
sql += fmt.Sprintf(" ON DELETE %s", opts.OnDelete)
|
||||
}
|
||||
if opts.OnUpdate != "" {
|
||||
sql += fmt.Sprintf(" ON UPDATE %s", opts.OnUpdate)
|
||||
}
|
||||
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// DropForeignKey drops a foreign key constraint.
|
||||
func (m *Migration) DropForeignKey(ctx context.Context, table, constraint string) error {
|
||||
sql := fmt.Sprintf(
|
||||
"ALTER TABLE %s DROP CONSTRAINT %s",
|
||||
database.QuoteIdentifier(table),
|
||||
database.QuoteIdentifier(constraint),
|
||||
)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// HasForeignKey checks if a foreign key constraint exists.
|
||||
func (m *Migration) HasForeignKey(ctx context.Context, table, constraint string) (bool, error) {
|
||||
query := fmt.Sprintf(
|
||||
"SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_NAME = '%s' AND TABLE_NAME = '%s' AND CONSTRAINT_TYPE = 'FOREIGN KEY'",
|
||||
constraint,
|
||||
table,
|
||||
)
|
||||
|
||||
value, err := m.GetDB().GetValue(ctx, query)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return value.Int() > 0, nil
|
||||
}
|
||||
|
||||
// CreateSchema creates a new database schema.
|
||||
func (m *Migration) CreateSchema(ctx context.Context, schema string) error {
|
||||
sql := fmt.Sprintf("CREATE SCHEMA %s", database.QuoteIdentifier(schema))
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// DropSchema drops an existing database schema.
|
||||
func (m *Migration) DropSchema(ctx context.Context, schema string, cascade ...bool) error {
|
||||
sql := fmt.Sprintf("DROP SCHEMA %s", database.QuoteIdentifier(schema))
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// HasSchema checks if a schema exists.
|
||||
func (m *Migration) HasSchema(ctx context.Context, schema string) (bool, error) {
|
||||
query := fmt.Sprintf(
|
||||
"SELECT COUNT(*) FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = '%s'",
|
||||
schema,
|
||||
)
|
||||
|
||||
value, err := m.GetDB().GetValue(ctx, query)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return value.Int() > 0, nil
|
||||
}
|
||||
|
||||
// formatDefaultValue formats the default value for SQL.
|
||||
func formatDefaultValue(value any) string {
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
return fmt.Sprintf("'%s'", escapeString(v))
|
||||
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
|
||||
return fmt.Sprintf("%d", v)
|
||||
case float32, float64:
|
||||
return fmt.Sprintf("%f", v)
|
||||
case bool:
|
||||
if v {
|
||||
return "1"
|
||||
}
|
||||
return "0"
|
||||
case nil:
|
||||
return "NULL"
|
||||
default:
|
||||
return fmt.Sprintf("'%v'", v)
|
||||
}
|
||||
}
|
||||
|
||||
// escapeString escapes special characters in strings for SQL.
|
||||
func escapeString(s string) string {
|
||||
s = strings.ReplaceAll(s, "'", "''")
|
||||
return s
|
||||
}
|
||||
|
|
@ -52,13 +52,3 @@ func (d *Driver) New(core *database.Core, node *database.ConfigNode) (database.D
|
|||
func (d *Driver) GetChars() (charLeft string, charRight string) {
|
||||
return quoteChar, quoteChar
|
||||
}
|
||||
|
||||
// Migration returns a Migration instance for MySQL database operations.
|
||||
func (d *Driver) Migration() *Migration {
|
||||
return NewMigration(d)
|
||||
}
|
||||
|
||||
// GetMigration returns a Migration instance implementing the Migration interface.
|
||||
func (d *Driver) GetMigration() database.Migration {
|
||||
return d.Migration()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,365 +0,0 @@
|
|||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.magicany.cc/black1552/gin-base/database"
|
||||
)
|
||||
|
||||
// Migration implements database migration operations for MySQL.
|
||||
type Migration struct {
|
||||
*database.MigrationCore
|
||||
*database.AutoMigrateCore
|
||||
}
|
||||
|
||||
// NewMigration creates a new MySQL Migration instance.
|
||||
func NewMigration(db database.DB) *Migration {
|
||||
return &Migration{
|
||||
MigrationCore: database.NewMigrationCore(db),
|
||||
AutoMigrateCore: database.NewAutoMigrateCore(db),
|
||||
}
|
||||
}
|
||||
|
||||
// CreateTable creates a new table with the given name and column definitions.
|
||||
func (m *Migration) CreateTable(ctx context.Context, table string, columns map[string]*database.ColumnDefinition, options ...database.TableOption) error {
|
||||
if len(columns) == 0 {
|
||||
return fmt.Errorf("cannot create table without columns")
|
||||
}
|
||||
|
||||
var opts database.TableOptions
|
||||
for _, opt := range options {
|
||||
opt(&opts)
|
||||
}
|
||||
|
||||
var sql strings.Builder
|
||||
sql.WriteString("CREATE TABLE ")
|
||||
if opts.IfNotExists {
|
||||
sql.WriteString("IF NOT EXISTS ")
|
||||
}
|
||||
sql.WriteString(database.QuoteIdentifier(table))
|
||||
sql.WriteString(" (\n")
|
||||
|
||||
// Add columns
|
||||
var colDefs []string
|
||||
var primaryKeys []string
|
||||
for name, def := range columns {
|
||||
colDef := m.BuildColumnDefinition(name, def)
|
||||
if def.PrimaryKey {
|
||||
primaryKeys = append(primaryKeys, database.QuoteIdentifier(name))
|
||||
}
|
||||
colDefs = append(colDefs, " "+colDef)
|
||||
}
|
||||
|
||||
// Add primary key constraint if needed
|
||||
if len(primaryKeys) > 0 {
|
||||
colDefs = append(colDefs, fmt.Sprintf(" PRIMARY KEY (%s)", strings.Join(primaryKeys, ", ")))
|
||||
}
|
||||
|
||||
sql.WriteString(strings.Join(colDefs, ",\n"))
|
||||
sql.WriteString("\n)")
|
||||
|
||||
// Add table options
|
||||
if opts.Engine != "" {
|
||||
sql.WriteString(fmt.Sprintf(" ENGINE=%s", opts.Engine))
|
||||
}
|
||||
if opts.Charset != "" {
|
||||
sql.WriteString(fmt.Sprintf(" DEFAULT CHARSET=%s", opts.Charset))
|
||||
}
|
||||
if opts.Collation != "" {
|
||||
sql.WriteString(fmt.Sprintf(" COLLATE=%s", opts.Collation))
|
||||
}
|
||||
if opts.Comment != "" {
|
||||
sql.WriteString(fmt.Sprintf(" COMMENT='%s'", escapeString(opts.Comment)))
|
||||
}
|
||||
|
||||
return m.ExecuteSQL(ctx, sql.String())
|
||||
}
|
||||
|
||||
// DropTable drops an existing table from the database.
|
||||
func (m *Migration) DropTable(ctx context.Context, table string, ifExists ...bool) error {
|
||||
sql := "DROP TABLE "
|
||||
if len(ifExists) > 0 && ifExists[0] {
|
||||
sql += "IF EXISTS "
|
||||
}
|
||||
sql += database.QuoteIdentifier(table)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// HasTable checks if a table exists in the database.
|
||||
func (m *Migration) HasTable(ctx context.Context, table string) (bool, error) {
|
||||
schema := m.GetDB().GetSchema()
|
||||
if schema == "" {
|
||||
schema = "DATABASE()"
|
||||
} else {
|
||||
schema = fmt.Sprintf("'%s'", schema)
|
||||
}
|
||||
|
||||
query := fmt.Sprintf(
|
||||
"SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = %s AND table_name = '%s'",
|
||||
schema,
|
||||
table,
|
||||
)
|
||||
|
||||
value, err := m.GetDB().GetValue(ctx, query)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return value.Int() > 0, nil
|
||||
}
|
||||
|
||||
// RenameTable renames an existing table from oldName to newName.
|
||||
func (m *Migration) RenameTable(ctx context.Context, oldName, newName string) error {
|
||||
sql := fmt.Sprintf(
|
||||
"RENAME TABLE %s TO %s",
|
||||
database.QuoteIdentifier(oldName),
|
||||
database.QuoteIdentifier(newName),
|
||||
)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// TruncateTable removes all records from a table but keeps the table structure.
|
||||
func (m *Migration) TruncateTable(ctx context.Context, table string) error {
|
||||
sql := fmt.Sprintf("TRUNCATE TABLE %s", database.QuoteIdentifier(table))
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// AddColumn adds a new column to an existing table.
|
||||
func (m *Migration) AddColumn(ctx context.Context, table, column string, definition *database.ColumnDefinition) error {
|
||||
colDef := m.BuildColumnDefinition(column, definition)
|
||||
sql := fmt.Sprintf("ALTER TABLE %s ADD COLUMN %s", database.QuoteIdentifier(table), colDef)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// DropColumn removes a column from an existing table.
|
||||
func (m *Migration) DropColumn(ctx context.Context, table, column string) error {
|
||||
sql := fmt.Sprintf(
|
||||
"ALTER TABLE %s DROP COLUMN %s",
|
||||
database.QuoteIdentifier(table),
|
||||
database.QuoteIdentifier(column),
|
||||
)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// RenameColumn renames a column in an existing table.
|
||||
func (m *Migration) RenameColumn(ctx context.Context, table, oldName, newName string) error {
|
||||
// MySQL requires the full column definition when renaming
|
||||
fields, err := m.GetDB().TableFields(ctx, table)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
field, ok := fields[oldName]
|
||||
if !ok {
|
||||
return fmt.Errorf("column %s does not exist in table %s", oldName, table)
|
||||
}
|
||||
|
||||
def := &database.ColumnDefinition{
|
||||
Type: field.Type,
|
||||
Null: field.Null,
|
||||
Default: field.Default,
|
||||
Comment: field.Comment,
|
||||
AutoIncrement: strings.Contains(field.Extra, "auto_increment"),
|
||||
}
|
||||
|
||||
colDef := m.BuildColumnDefinition(newName, def)
|
||||
sql := fmt.Sprintf(
|
||||
"ALTER TABLE %s CHANGE COLUMN %s %s",
|
||||
database.QuoteIdentifier(table),
|
||||
database.QuoteIdentifier(oldName),
|
||||
colDef,
|
||||
)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// ModifyColumn modifies an existing column's definition.
|
||||
func (m *Migration) ModifyColumn(ctx context.Context, table, column string, definition *database.ColumnDefinition) error {
|
||||
colDef := m.BuildColumnDefinition(column, definition)
|
||||
sql := fmt.Sprintf("ALTER TABLE %s MODIFY COLUMN %s", database.QuoteIdentifier(table), colDef)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// HasColumn checks if a column exists in a table.
|
||||
func (m *Migration) HasColumn(ctx context.Context, table, column string) (bool, error) {
|
||||
fields, err := m.GetDB().TableFields(ctx, table)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
_, exists := fields[column]
|
||||
return exists, nil
|
||||
}
|
||||
|
||||
// CreateIndex creates a new index on the specified table and columns.
|
||||
func (m *Migration) CreateIndex(ctx context.Context, table, index string, columns []string, options ...database.IndexOption) error {
|
||||
var opts database.IndexOptions
|
||||
for _, opt := range options {
|
||||
opt(&opts)
|
||||
}
|
||||
|
||||
var sql strings.Builder
|
||||
sql.WriteString("CREATE ")
|
||||
|
||||
if opts.Unique {
|
||||
sql.WriteString("UNIQUE ")
|
||||
}
|
||||
if opts.FullText {
|
||||
sql.WriteString("FULLTEXT ")
|
||||
}
|
||||
if opts.Spatial {
|
||||
sql.WriteString("SPATIAL ")
|
||||
}
|
||||
|
||||
sql.WriteString("INDEX ")
|
||||
sql.WriteString(database.QuoteIdentifier(index))
|
||||
sql.WriteString(" ON ")
|
||||
sql.WriteString(database.QuoteIdentifier(table))
|
||||
|
||||
colList := m.BuildIndexColumns(columns)
|
||||
sql.WriteString(fmt.Sprintf(" (%s)", colList))
|
||||
|
||||
if opts.Using != "" {
|
||||
sql.WriteString(fmt.Sprintf(" USING %s", opts.Using))
|
||||
}
|
||||
|
||||
return m.ExecuteSQL(ctx, sql.String())
|
||||
}
|
||||
|
||||
// DropIndex drops an existing index from a table.
|
||||
func (m *Migration) DropIndex(ctx context.Context, table, index string) error {
|
||||
sql := fmt.Sprintf(
|
||||
"DROP INDEX %s ON %s",
|
||||
database.QuoteIdentifier(index),
|
||||
database.QuoteIdentifier(table),
|
||||
)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// HasIndex checks if an index exists on a table.
|
||||
func (m *Migration) HasIndex(ctx context.Context, table, index string) (bool, error) {
|
||||
schema := m.GetDB().GetSchema()
|
||||
if schema == "" {
|
||||
schema = "DATABASE()"
|
||||
} else {
|
||||
schema = fmt.Sprintf("'%s'", schema)
|
||||
}
|
||||
|
||||
query := fmt.Sprintf(
|
||||
"SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = %s AND table_name = '%s' AND index_name = '%s'",
|
||||
schema,
|
||||
table,
|
||||
index,
|
||||
)
|
||||
|
||||
value, err := m.GetDB().GetValue(ctx, query)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return value.Int() > 0, nil
|
||||
}
|
||||
|
||||
// CreateForeignKey creates a foreign key constraint.
|
||||
func (m *Migration) CreateForeignKey(ctx context.Context, table, constraint string, columns []string, refTable string, refColumns []string, options ...database.ForeignKeyOption) error {
|
||||
var opts database.ForeignKeyOptions
|
||||
for _, opt := range options {
|
||||
opt(&opts)
|
||||
}
|
||||
|
||||
sql := fmt.Sprintf(
|
||||
"ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY %s REFERENCES %s %s",
|
||||
database.QuoteIdentifier(table),
|
||||
database.QuoteIdentifier(constraint),
|
||||
m.BuildForeignKeyColumns(columns),
|
||||
database.QuoteIdentifier(refTable),
|
||||
m.BuildForeignKeyColumns(refColumns),
|
||||
)
|
||||
|
||||
if opts.OnDelete != "" {
|
||||
sql += fmt.Sprintf(" ON DELETE %s", opts.OnDelete)
|
||||
}
|
||||
if opts.OnUpdate != "" {
|
||||
sql += fmt.Sprintf(" ON UPDATE %s", opts.OnUpdate)
|
||||
}
|
||||
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// DropForeignKey drops a foreign key constraint.
|
||||
func (m *Migration) DropForeignKey(ctx context.Context, table, constraint string) error {
|
||||
sql := fmt.Sprintf(
|
||||
"ALTER TABLE %s DROP FOREIGN KEY %s",
|
||||
database.QuoteIdentifier(table),
|
||||
database.QuoteIdentifier(constraint),
|
||||
)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// HasForeignKey checks if a foreign key constraint exists.
|
||||
func (m *Migration) HasForeignKey(ctx context.Context, table, constraint string) (bool, error) {
|
||||
schema := m.GetDB().GetSchema()
|
||||
if schema == "" {
|
||||
schema = "DATABASE()"
|
||||
} else {
|
||||
schema = fmt.Sprintf("'%s'", schema)
|
||||
}
|
||||
|
||||
query := fmt.Sprintf(
|
||||
"SELECT COUNT(*) FROM information_schema.table_constraints WHERE constraint_schema = %s AND table_name = '%s' AND constraint_name = '%s' AND constraint_type = 'FOREIGN KEY'",
|
||||
schema,
|
||||
table,
|
||||
constraint,
|
||||
)
|
||||
|
||||
value, err := m.GetDB().GetValue(ctx, query)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return value.Int() > 0, nil
|
||||
}
|
||||
|
||||
// CreateSchema creates a new database schema.
|
||||
func (m *Migration) CreateSchema(ctx context.Context, schema string) error {
|
||||
sql := fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s", database.QuoteIdentifier(schema))
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// DropSchema drops an existing database schema.
|
||||
func (m *Migration) DropSchema(ctx context.Context, schema string, cascade ...bool) error {
|
||||
sql := "DROP DATABASE "
|
||||
if len(cascade) > 0 && cascade[0] {
|
||||
sql += "IF EXISTS "
|
||||
}
|
||||
sql += database.QuoteIdentifier(schema)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// HasSchema checks if a schema exists.
|
||||
func (m *Migration) HasSchema(ctx context.Context, schema string) (bool, error) {
|
||||
query := fmt.Sprintf(
|
||||
"SELECT COUNT(*) FROM information_schema.schemata WHERE schema_name = '%s'",
|
||||
schema,
|
||||
)
|
||||
|
||||
value, err := m.GetDB().GetValue(ctx, query)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return value.Int() > 0, nil
|
||||
}
|
||||
|
||||
// escapeString escapes special characters in strings for SQL.
|
||||
func escapeString(s string) string {
|
||||
s = strings.ReplaceAll(s, "'", "''")
|
||||
s = strings.ReplaceAll(s, "\\", "\\\\")
|
||||
return s
|
||||
}
|
||||
|
|
@ -44,13 +44,3 @@ func (d *Driver) New(core *database.Core, node *database.ConfigNode) (database.D
|
|||
func (d *Driver) GetChars() (charLeft string, charRight string) {
|
||||
return quoteChar, quoteChar
|
||||
}
|
||||
|
||||
// Migration returns a Migration instance for Oracle database operations.
|
||||
func (d *Driver) Migration() *Migration {
|
||||
return NewMigration(d)
|
||||
}
|
||||
|
||||
// GetMigration returns a Migration instance implementing the Migration interface.
|
||||
func (d *Driver) GetMigration() database.Migration {
|
||||
return d.Migration()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,351 +0,0 @@
|
|||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package oracle
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.magicany.cc/black1552/gin-base/database"
|
||||
)
|
||||
|
||||
// Migration implements database migration operations for Oracle.
|
||||
type Migration struct {
|
||||
*database.MigrationCore
|
||||
*database.AutoMigrateCore
|
||||
}
|
||||
|
||||
// NewMigration creates a new Oracle Migration instance.
|
||||
func NewMigration(db database.DB) *Migration {
|
||||
return &Migration{
|
||||
MigrationCore: database.NewMigrationCore(db),
|
||||
AutoMigrateCore: database.NewAutoMigrateCore(db),
|
||||
}
|
||||
}
|
||||
|
||||
// CreateTable creates a new table with the given name and column definitions.
|
||||
func (m *Migration) CreateTable(ctx context.Context, table string, columns map[string]*database.ColumnDefinition, options ...database.TableOption) error {
|
||||
if len(columns) == 0 {
|
||||
return fmt.Errorf("cannot create table without columns")
|
||||
}
|
||||
|
||||
var opts database.TableOptions
|
||||
for _, opt := range options {
|
||||
opt(&opts)
|
||||
}
|
||||
|
||||
var sql strings.Builder
|
||||
sql.WriteString("CREATE TABLE ")
|
||||
sql.WriteString(database.QuoteIdentifierDouble(table))
|
||||
sql.WriteString(" (\n")
|
||||
|
||||
// Add columns
|
||||
var colDefs []string
|
||||
var primaryKeys []string
|
||||
for name, def := range columns {
|
||||
colDef := m.buildColumnDefinition(name, def)
|
||||
if def.PrimaryKey {
|
||||
primaryKeys = append(primaryKeys, database.QuoteIdentifierDouble(name))
|
||||
}
|
||||
colDefs = append(colDefs, " "+colDef)
|
||||
}
|
||||
|
||||
// Add primary key constraint if needed
|
||||
if len(primaryKeys) > 0 {
|
||||
colDefs = append(colDefs, fmt.Sprintf(" PRIMARY KEY (%s)", strings.Join(primaryKeys, ", ")))
|
||||
}
|
||||
|
||||
sql.WriteString(strings.Join(colDefs, ",\n"))
|
||||
sql.WriteString("\n)")
|
||||
|
||||
return m.ExecuteSQL(ctx, sql.String())
|
||||
}
|
||||
|
||||
// buildColumnDefinition builds column definition for Oracle.
|
||||
func (m *Migration) buildColumnDefinition(name string, def *database.ColumnDefinition) string {
|
||||
var parts []string
|
||||
parts = append(parts, database.QuoteIdentifierDouble(name))
|
||||
|
||||
// Handle Oracle-specific types
|
||||
dbType := def.Type
|
||||
parts = append(parts, dbType)
|
||||
|
||||
if !def.Null {
|
||||
parts = append(parts, "NOT NULL")
|
||||
}
|
||||
|
||||
if def.Default != nil {
|
||||
defaultValue := formatDefaultValue(def.Default)
|
||||
parts = append(parts, fmt.Sprintf("DEFAULT %s", defaultValue))
|
||||
}
|
||||
|
||||
return strings.Join(parts, " ")
|
||||
}
|
||||
|
||||
// DropTable drops an existing table from the database.
|
||||
func (m *Migration) DropTable(ctx context.Context, table string, ifExists ...bool) error {
|
||||
if len(ifExists) > 0 && ifExists[0] {
|
||||
sql := fmt.Sprintf(
|
||||
"BEGIN EXECUTE IMMEDIATE 'DROP TABLE %s'; EXCEPTION WHEN OTHERS THEN IF SQLCODE != -942 THEN RAISE; END IF; END;",
|
||||
database.QuoteIdentifierDouble(table),
|
||||
)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
sql := fmt.Sprintf("DROP TABLE %s", database.QuoteIdentifierDouble(table))
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// HasTable checks if a table exists in the database.
|
||||
func (m *Migration) HasTable(ctx context.Context, table string) (bool, error) {
|
||||
query := fmt.Sprintf(
|
||||
"SELECT COUNT(*) FROM USER_TABLES WHERE TABLE_NAME = '%s'",
|
||||
strings.ToUpper(table),
|
||||
)
|
||||
|
||||
value, err := m.GetDB().GetValue(ctx, query)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return value.Int() > 0, nil
|
||||
}
|
||||
|
||||
// RenameTable renames an existing table from oldName to newName.
|
||||
func (m *Migration) RenameTable(ctx context.Context, oldName, newName string) error {
|
||||
sql := fmt.Sprintf(
|
||||
"ALTER TABLE %s RENAME TO %s",
|
||||
database.QuoteIdentifierDouble(oldName),
|
||||
database.QuoteIdentifierDouble(newName),
|
||||
)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// TruncateTable removes all records from a table but keeps the table structure.
|
||||
func (m *Migration) TruncateTable(ctx context.Context, table string) error {
|
||||
sql := fmt.Sprintf("TRUNCATE TABLE %s", database.QuoteIdentifierDouble(table))
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// AddColumn adds a new column to an existing table.
|
||||
func (m *Migration) AddColumn(ctx context.Context, table, column string, definition *database.ColumnDefinition) error {
|
||||
colDef := m.buildColumnDefinition(column, definition)
|
||||
sql := fmt.Sprintf("ALTER TABLE %s ADD %s", database.QuoteIdentifierDouble(table), colDef)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// DropColumn removes a column from an existing table.
|
||||
func (m *Migration) DropColumn(ctx context.Context, table, column string) error {
|
||||
sql := fmt.Sprintf(
|
||||
"ALTER TABLE %s DROP COLUMN %s",
|
||||
database.QuoteIdentifierDouble(table),
|
||||
database.QuoteIdentifierDouble(column),
|
||||
)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// RenameColumn renames a column in an existing table.
|
||||
func (m *Migration) RenameColumn(ctx context.Context, table, oldName, newName string) error {
|
||||
sql := fmt.Sprintf(
|
||||
"ALTER TABLE %s RENAME COLUMN %s TO %s",
|
||||
database.QuoteIdentifierDouble(table),
|
||||
database.QuoteIdentifierDouble(oldName),
|
||||
database.QuoteIdentifierDouble(newName),
|
||||
)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// ModifyColumn modifies an existing column's definition.
|
||||
func (m *Migration) ModifyColumn(ctx context.Context, table, column string, definition *database.ColumnDefinition) error {
|
||||
colDef := m.buildColumnDefinition(column, definition)
|
||||
sql := fmt.Sprintf("ALTER TABLE %s MODIFY %s", database.QuoteIdentifierDouble(table), colDef)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// HasColumn checks if a column exists in a table.
|
||||
func (m *Migration) HasColumn(ctx context.Context, table, column string) (bool, error) {
|
||||
fields, err := m.GetDB().TableFields(ctx, table)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
_, exists := fields[column]
|
||||
return exists, nil
|
||||
}
|
||||
|
||||
// CreateIndex creates a new index on the specified table and columns.
|
||||
func (m *Migration) CreateIndex(ctx context.Context, table, index string, columns []string, options ...database.IndexOption) error {
|
||||
var opts database.IndexOptions
|
||||
for _, opt := range options {
|
||||
opt(&opts)
|
||||
}
|
||||
|
||||
var sql strings.Builder
|
||||
sql.WriteString("CREATE ")
|
||||
|
||||
if opts.Unique {
|
||||
sql.WriteString("UNIQUE ")
|
||||
}
|
||||
|
||||
sql.WriteString("INDEX ")
|
||||
sql.WriteString(database.QuoteIdentifierDouble(index))
|
||||
sql.WriteString(" ON ")
|
||||
sql.WriteString(database.QuoteIdentifierDouble(table))
|
||||
|
||||
colList := m.BuildIndexColumnsWithDouble(columns)
|
||||
sql.WriteString(fmt.Sprintf(" (%s)", colList))
|
||||
|
||||
return m.ExecuteSQL(ctx, sql.String())
|
||||
}
|
||||
|
||||
// BuildIndexColumnsWithDouble builds the column list for index creation using double quotes.
|
||||
func (m *Migration) BuildIndexColumnsWithDouble(columns []string) string {
|
||||
quoted := make([]string, len(columns))
|
||||
for i, col := range columns {
|
||||
quoted[i] = database.QuoteIdentifierDouble(col)
|
||||
}
|
||||
return strings.Join(quoted, ", ")
|
||||
}
|
||||
|
||||
// DropIndex drops an existing index from a table.
|
||||
func (m *Migration) DropIndex(ctx context.Context, table, index string) error {
|
||||
sql := fmt.Sprintf("DROP INDEX %s", database.QuoteIdentifierDouble(index))
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// HasIndex checks if an index exists on a table.
|
||||
func (m *Migration) HasIndex(ctx context.Context, table, index string) (bool, error) {
|
||||
query := fmt.Sprintf(
|
||||
"SELECT COUNT(*) FROM USER_INDEXES WHERE INDEX_NAME = '%s' AND TABLE_NAME = '%s'",
|
||||
strings.ToUpper(index),
|
||||
strings.ToUpper(table),
|
||||
)
|
||||
|
||||
value, err := m.GetDB().GetValue(ctx, query)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return value.Int() > 0, nil
|
||||
}
|
||||
|
||||
// CreateForeignKey creates a foreign key constraint.
|
||||
func (m *Migration) CreateForeignKey(ctx context.Context, table, constraint string, columns []string, refTable string, refColumns []string, options ...database.ForeignKeyOption) error {
|
||||
var opts database.ForeignKeyOptions
|
||||
for _, opt := range options {
|
||||
opt(&opts)
|
||||
}
|
||||
|
||||
sql := fmt.Sprintf(
|
||||
"ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY %s REFERENCES %s %s",
|
||||
database.QuoteIdentifierDouble(table),
|
||||
database.QuoteIdentifierDouble(constraint),
|
||||
m.BuildForeignKeyColumnsWithDouble(columns),
|
||||
database.QuoteIdentifierDouble(refTable),
|
||||
m.BuildForeignKeyColumnsWithDouble(refColumns),
|
||||
)
|
||||
|
||||
if opts.OnDelete != "" {
|
||||
sql += fmt.Sprintf(" ON DELETE %s", opts.OnDelete)
|
||||
}
|
||||
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// BuildForeignKeyColumnsWithDouble builds the column list for foreign key using double quotes.
|
||||
func (m *Migration) BuildForeignKeyColumnsWithDouble(columns []string) string {
|
||||
quoted := make([]string, len(columns))
|
||||
for i, col := range columns {
|
||||
quoted[i] = database.QuoteIdentifierDouble(col)
|
||||
}
|
||||
return "(" + strings.Join(quoted, ", ") + ")"
|
||||
}
|
||||
|
||||
// DropForeignKey drops a foreign key constraint.
|
||||
func (m *Migration) DropForeignKey(ctx context.Context, table, constraint string) error {
|
||||
sql := fmt.Sprintf(
|
||||
"ALTER TABLE %s DROP CONSTRAINT %s",
|
||||
database.QuoteIdentifierDouble(table),
|
||||
database.QuoteIdentifierDouble(constraint),
|
||||
)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// HasForeignKey checks if a foreign key constraint exists.
|
||||
func (m *Migration) HasForeignKey(ctx context.Context, table, constraint string) (bool, error) {
|
||||
query := fmt.Sprintf(
|
||||
"SELECT COUNT(*) FROM USER_CONSTRAINTS WHERE CONSTRAINT_NAME = '%s' AND TABLE_NAME = '%s' AND CONSTRAINT_TYPE = 'R'",
|
||||
strings.ToUpper(constraint),
|
||||
strings.ToUpper(table),
|
||||
)
|
||||
|
||||
value, err := m.GetDB().GetValue(ctx, query)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return value.Int() > 0, nil
|
||||
}
|
||||
|
||||
// CreateSchema creates a new database schema (user in Oracle).
|
||||
func (m *Migration) CreateSchema(ctx context.Context, schema string) error {
|
||||
// In Oracle, schema is equivalent to user
|
||||
sql := fmt.Sprintf("CREATE USER %s IDENTIFIED BY password", database.QuoteIdentifierDouble(schema))
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// DropSchema drops an existing database schema (user in Oracle).
|
||||
func (m *Migration) DropSchema(ctx context.Context, schema string, cascade ...bool) error {
|
||||
sql := "DROP USER "
|
||||
if len(cascade) > 0 && cascade[0] {
|
||||
sql += database.QuoteIdentifierDouble(schema) + " CASCADE"
|
||||
} else {
|
||||
sql += database.QuoteIdentifierDouble(schema)
|
||||
}
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// HasSchema checks if a schema exists.
|
||||
func (m *Migration) HasSchema(ctx context.Context, schema string) (bool, error) {
|
||||
query := fmt.Sprintf(
|
||||
"SELECT COUNT(*) FROM ALL_USERS WHERE USERNAME = '%s'",
|
||||
strings.ToUpper(schema),
|
||||
)
|
||||
|
||||
value, err := m.GetDB().GetValue(ctx, query)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return value.Int() > 0, nil
|
||||
}
|
||||
|
||||
// formatDefaultValue formats the default value for SQL.
|
||||
func formatDefaultValue(value any) string {
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
return fmt.Sprintf("'%s'", escapeString(v))
|
||||
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
|
||||
return fmt.Sprintf("%d", v)
|
||||
case float32, float64:
|
||||
return fmt.Sprintf("%f", v)
|
||||
case bool:
|
||||
if v {
|
||||
return "1"
|
||||
}
|
||||
return "0"
|
||||
case nil:
|
||||
return "NULL"
|
||||
default:
|
||||
return fmt.Sprintf("'%v'", v)
|
||||
}
|
||||
}
|
||||
|
||||
// escapeString escapes special characters in strings for SQL.
|
||||
func escapeString(s string) string {
|
||||
s = strings.ReplaceAll(s, "'", "''")
|
||||
return s
|
||||
}
|
||||
|
|
@ -48,13 +48,3 @@ func (d *Driver) New(core *database.Core, node *database.ConfigNode) (database.D
|
|||
func (d *Driver) GetChars() (charLeft string, charRight string) {
|
||||
return quoteChar, quoteChar
|
||||
}
|
||||
|
||||
// Migration returns a Migration instance for PostgreSQL database operations.
|
||||
func (d *Driver) Migration() *Migration {
|
||||
return NewMigration(d)
|
||||
}
|
||||
|
||||
// GetMigration returns a Migration instance implementing the Migration interface.
|
||||
func (d *Driver) GetMigration() database.Migration {
|
||||
return d.Migration()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,481 +0,0 @@
|
|||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package pgsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.magicany.cc/black1552/gin-base/database"
|
||||
)
|
||||
|
||||
// Migration implements database migration operations for PostgreSQL.
|
||||
type Migration struct {
|
||||
*database.MigrationCore
|
||||
*database.AutoMigrateCore
|
||||
}
|
||||
|
||||
// NewMigration creates a new PostgreSQL Migration instance.
|
||||
func NewMigration(db database.DB) *Migration {
|
||||
return &Migration{
|
||||
MigrationCore: database.NewMigrationCore(db),
|
||||
AutoMigrateCore: database.NewAutoMigrateCore(db),
|
||||
}
|
||||
}
|
||||
|
||||
// CreateTable creates a new table with the given name and column definitions.
|
||||
func (m *Migration) CreateTable(ctx context.Context, table string, columns map[string]*database.ColumnDefinition, options ...database.TableOption) error {
|
||||
if len(columns) == 0 {
|
||||
return fmt.Errorf("cannot create table without columns")
|
||||
}
|
||||
|
||||
var opts database.TableOptions
|
||||
for _, opt := range options {
|
||||
opt(&opts)
|
||||
}
|
||||
|
||||
var sql strings.Builder
|
||||
sql.WriteString("CREATE TABLE ")
|
||||
if opts.IfNotExists {
|
||||
sql.WriteString("IF NOT EXISTS ")
|
||||
}
|
||||
sql.WriteString(database.QuoteIdentifierDouble(table))
|
||||
sql.WriteString(" (\n")
|
||||
|
||||
// Add columns
|
||||
var colDefs []string
|
||||
var primaryKeys []string
|
||||
for name, def := range columns {
|
||||
colDef := m.buildColumnDefinition(name, def)
|
||||
if def.PrimaryKey {
|
||||
primaryKeys = append(primaryKeys, database.QuoteIdentifierDouble(name))
|
||||
}
|
||||
colDefs = append(colDefs, " "+colDef)
|
||||
}
|
||||
|
||||
// Add primary key constraint if needed
|
||||
if len(primaryKeys) > 0 {
|
||||
colDefs = append(colDefs, fmt.Sprintf(" PRIMARY KEY (%s)", strings.Join(primaryKeys, ", ")))
|
||||
}
|
||||
|
||||
sql.WriteString(strings.Join(colDefs, ",\n"))
|
||||
sql.WriteString("\n)")
|
||||
|
||||
// Add table comment if provided
|
||||
if opts.Comment != "" {
|
||||
commentSQL := fmt.Sprintf(
|
||||
"COMMENT ON TABLE %s IS '%s'",
|
||||
database.QuoteIdentifierDouble(table),
|
||||
escapeString(opts.Comment),
|
||||
)
|
||||
if err := m.ExecuteSQL(ctx, commentSQL); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return m.ExecuteSQL(ctx, sql.String())
|
||||
}
|
||||
|
||||
// buildColumnDefinition builds column definition for PostgreSQL.
|
||||
func (m *Migration) buildColumnDefinition(name string, def *database.ColumnDefinition) string {
|
||||
var parts []string
|
||||
parts = append(parts, database.QuoteIdentifierDouble(name))
|
||||
|
||||
// Handle PostgreSQL-specific types
|
||||
dbType := def.Type
|
||||
if def.AutoIncrement {
|
||||
if dbType == "INT" || dbType == "INTEGER" {
|
||||
dbType = "SERIAL"
|
||||
} else if dbType == "BIGINT" {
|
||||
dbType = "BIGSERIAL"
|
||||
}
|
||||
}
|
||||
parts = append(parts, dbType)
|
||||
|
||||
if def.PrimaryKey {
|
||||
// Primary key is handled separately
|
||||
} else {
|
||||
if !def.Null {
|
||||
parts = append(parts, "NOT NULL")
|
||||
}
|
||||
|
||||
if def.Unique {
|
||||
parts = append(parts, "UNIQUE")
|
||||
}
|
||||
|
||||
if def.Default != nil {
|
||||
defaultValue := formatDefaultValue(def.Default)
|
||||
parts = append(parts, fmt.Sprintf("DEFAULT %s", defaultValue))
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Join(parts, " ")
|
||||
}
|
||||
|
||||
// DropTable drops an existing table from the database.
|
||||
func (m *Migration) DropTable(ctx context.Context, table string, ifExists ...bool) error {
|
||||
sql := "DROP TABLE "
|
||||
if len(ifExists) > 0 && ifExists[0] {
|
||||
sql += "IF EXISTS "
|
||||
}
|
||||
sql += database.QuoteIdentifierDouble(table)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// HasTable checks if a table exists in the database.
|
||||
func (m *Migration) HasTable(ctx context.Context, table string) (bool, error) {
|
||||
schema := m.GetDB().GetSchema()
|
||||
if schema == "" {
|
||||
schema = "current_schema()"
|
||||
} else {
|
||||
schema = fmt.Sprintf("'%s'", schema)
|
||||
}
|
||||
|
||||
query := fmt.Sprintf(
|
||||
"SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = %s AND table_name = '%s'",
|
||||
schema,
|
||||
table,
|
||||
)
|
||||
|
||||
value, err := m.GetDB().GetValue(ctx, query)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return value.Int() > 0, nil
|
||||
}
|
||||
|
||||
// RenameTable renames an existing table from oldName to newName.
|
||||
func (m *Migration) RenameTable(ctx context.Context, oldName, newName string) error {
|
||||
sql := fmt.Sprintf(
|
||||
"ALTER TABLE %s RENAME TO %s",
|
||||
database.QuoteIdentifierDouble(oldName),
|
||||
database.QuoteIdentifierDouble(newName),
|
||||
)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// TruncateTable removes all records from a table but keeps the table structure.
|
||||
func (m *Migration) TruncateTable(ctx context.Context, table string) error {
|
||||
sql := fmt.Sprintf("TRUNCATE TABLE %s", database.QuoteIdentifierDouble(table))
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// AddColumn adds a new column to an existing table.
|
||||
func (m *Migration) AddColumn(ctx context.Context, table, column string, definition *database.ColumnDefinition) error {
|
||||
colDef := m.buildColumnDefinition(column, definition)
|
||||
sql := fmt.Sprintf("ALTER TABLE %s ADD COLUMN %s", database.QuoteIdentifierDouble(table), colDef)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// DropColumn removes a column from an existing table.
|
||||
func (m *Migration) DropColumn(ctx context.Context, table, column string) error {
|
||||
sql := fmt.Sprintf(
|
||||
"ALTER TABLE %s DROP COLUMN %s",
|
||||
database.QuoteIdentifierDouble(table),
|
||||
database.QuoteIdentifierDouble(column),
|
||||
)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// RenameColumn renames a column in an existing table.
|
||||
func (m *Migration) RenameColumn(ctx context.Context, table, oldName, newName string) error {
|
||||
sql := fmt.Sprintf(
|
||||
"ALTER TABLE %s RENAME COLUMN %s TO %s",
|
||||
database.QuoteIdentifierDouble(table),
|
||||
database.QuoteIdentifierDouble(oldName),
|
||||
database.QuoteIdentifierDouble(newName),
|
||||
)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// ModifyColumn modifies an existing column's definition.
|
||||
func (m *Migration) ModifyColumn(ctx context.Context, table, column string, definition *database.ColumnDefinition) error {
|
||||
// PostgreSQL requires multiple ALTER statements for different modifications
|
||||
var statements []string
|
||||
|
||||
if definition.Type != "" {
|
||||
statements = append(statements, fmt.Sprintf(
|
||||
"ALTER TABLE %s ALTER COLUMN %s TYPE %s",
|
||||
database.QuoteIdentifierDouble(table),
|
||||
database.QuoteIdentifierDouble(column),
|
||||
definition.Type,
|
||||
))
|
||||
}
|
||||
|
||||
if definition.Default != nil {
|
||||
defaultValue := formatDefaultValue(definition.Default)
|
||||
statements = append(statements, fmt.Sprintf(
|
||||
"ALTER TABLE %s ALTER COLUMN %s SET DEFAULT %s",
|
||||
database.QuoteIdentifierDouble(table),
|
||||
database.QuoteIdentifierDouble(column),
|
||||
defaultValue,
|
||||
))
|
||||
} else {
|
||||
statements = append(statements, fmt.Sprintf(
|
||||
"ALTER TABLE %s ALTER COLUMN %s DROP DEFAULT",
|
||||
database.QuoteIdentifierDouble(table),
|
||||
database.QuoteIdentifierDouble(column),
|
||||
))
|
||||
}
|
||||
|
||||
if !definition.Null {
|
||||
statements = append(statements, fmt.Sprintf(
|
||||
"ALTER TABLE %s ALTER COLUMN %s SET NOT NULL",
|
||||
database.QuoteIdentifierDouble(table),
|
||||
database.QuoteIdentifierDouble(column),
|
||||
))
|
||||
} else {
|
||||
statements = append(statements, fmt.Sprintf(
|
||||
"ALTER TABLE %s ALTER COLUMN %s DROP NOT NULL",
|
||||
database.QuoteIdentifierDouble(table),
|
||||
database.QuoteIdentifierDouble(column),
|
||||
))
|
||||
}
|
||||
|
||||
// Execute all statements
|
||||
for _, stmt := range statements {
|
||||
if err := m.ExecuteSQL(ctx, stmt); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// HasColumn checks if a column exists in a table.
|
||||
func (m *Migration) HasColumn(ctx context.Context, table, column string) (bool, error) {
|
||||
fields, err := m.GetDB().TableFields(ctx, table)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
_, exists := fields[column]
|
||||
return exists, nil
|
||||
}
|
||||
|
||||
// CreateIndex creates a new index on the specified table and columns.
|
||||
func (m *Migration) CreateIndex(ctx context.Context, table, index string, columns []string, options ...database.IndexOption) error {
|
||||
var opts database.IndexOptions
|
||||
for _, opt := range options {
|
||||
opt(&opts)
|
||||
}
|
||||
|
||||
var sql strings.Builder
|
||||
sql.WriteString("CREATE ")
|
||||
|
||||
if opts.Unique {
|
||||
sql.WriteString("UNIQUE ")
|
||||
}
|
||||
|
||||
sql.WriteString("INDEX ")
|
||||
sql.WriteString(database.QuoteIdentifierDouble(index))
|
||||
sql.WriteString(" ON ")
|
||||
sql.WriteString(database.QuoteIdentifierDouble(table))
|
||||
|
||||
colList := m.BuildIndexColumnsWithDouble(columns)
|
||||
sql.WriteString(fmt.Sprintf(" (%s)", colList))
|
||||
|
||||
if opts.Using != "" {
|
||||
sql.WriteString(fmt.Sprintf(" USING %s", opts.Using))
|
||||
}
|
||||
|
||||
if opts.Comment != "" {
|
||||
// Comment will be added after index creation
|
||||
}
|
||||
|
||||
err := m.ExecuteSQL(ctx, sql.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Add comment if provided
|
||||
if opts.Comment != "" {
|
||||
commentSQL := fmt.Sprintf(
|
||||
"COMMENT ON INDEX %s IS '%s'",
|
||||
database.QuoteIdentifierDouble(index),
|
||||
escapeString(opts.Comment),
|
||||
)
|
||||
return m.ExecuteSQL(ctx, commentSQL)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// BuildIndexColumnsWithDouble builds the column list for index creation using double quotes.
|
||||
func (m *Migration) BuildIndexColumnsWithDouble(columns []string) string {
|
||||
quoted := make([]string, len(columns))
|
||||
for i, col := range columns {
|
||||
quoted[i] = database.QuoteIdentifierDouble(col)
|
||||
}
|
||||
return strings.Join(quoted, ", ")
|
||||
}
|
||||
|
||||
// DropIndex drops an existing index from a table.
|
||||
func (m *Migration) DropIndex(ctx context.Context, table, index string) error {
|
||||
sql := fmt.Sprintf("DROP INDEX %s", database.QuoteIdentifierDouble(index))
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// HasIndex checks if an index exists on a table.
|
||||
func (m *Migration) HasIndex(ctx context.Context, table, index string) (bool, error) {
|
||||
schema := m.GetDB().GetSchema()
|
||||
if schema == "" {
|
||||
schema = "current_schema()"
|
||||
} else {
|
||||
schema = fmt.Sprintf("'%s'", schema)
|
||||
}
|
||||
|
||||
query := fmt.Sprintf(
|
||||
"SELECT COUNT(*) FROM pg_indexes WHERE schemaname = %s AND tablename = '%s' AND indexname = '%s'",
|
||||
schema,
|
||||
table,
|
||||
index,
|
||||
)
|
||||
|
||||
value, err := m.GetDB().GetValue(ctx, query)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return value.Int() > 0, nil
|
||||
}
|
||||
|
||||
// CreateForeignKey creates a foreign key constraint.
|
||||
func (m *Migration) CreateForeignKey(ctx context.Context, table, constraint string, columns []string, refTable string, refColumns []string, options ...database.ForeignKeyOption) error {
|
||||
var opts database.ForeignKeyOptions
|
||||
for _, opt := range options {
|
||||
opt(&opts)
|
||||
}
|
||||
|
||||
sql := fmt.Sprintf(
|
||||
"ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY %s REFERENCES %s %s",
|
||||
database.QuoteIdentifierDouble(table),
|
||||
database.QuoteIdentifierDouble(constraint),
|
||||
m.BuildForeignKeyColumnsWithDouble(columns),
|
||||
database.QuoteIdentifierDouble(refTable),
|
||||
m.BuildForeignKeyColumnsWithDouble(refColumns),
|
||||
)
|
||||
|
||||
if opts.OnDelete != "" {
|
||||
sql += fmt.Sprintf(" ON DELETE %s", opts.OnDelete)
|
||||
}
|
||||
if opts.OnUpdate != "" {
|
||||
sql += fmt.Sprintf(" ON UPDATE %s", opts.OnUpdate)
|
||||
}
|
||||
if opts.Deferrable {
|
||||
sql += " DEFERRABLE"
|
||||
if opts.InitiallyDeferred {
|
||||
sql += " INITIALLY DEFERRED"
|
||||
}
|
||||
}
|
||||
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// BuildForeignKeyColumnsWithDouble builds the column list for foreign key using double quotes.
|
||||
func (m *Migration) BuildForeignKeyColumnsWithDouble(columns []string) string {
|
||||
quoted := make([]string, len(columns))
|
||||
for i, col := range columns {
|
||||
quoted[i] = database.QuoteIdentifierDouble(col)
|
||||
}
|
||||
return "(" + strings.Join(quoted, ", ") + ")"
|
||||
}
|
||||
|
||||
// DropForeignKey drops a foreign key constraint.
|
||||
func (m *Migration) DropForeignKey(ctx context.Context, table, constraint string) error {
|
||||
sql := fmt.Sprintf(
|
||||
"ALTER TABLE %s DROP CONSTRAINT %s",
|
||||
database.QuoteIdentifierDouble(table),
|
||||
database.QuoteIdentifierDouble(constraint),
|
||||
)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// HasForeignKey checks if a foreign key constraint exists.
|
||||
func (m *Migration) HasForeignKey(ctx context.Context, table, constraint string) (bool, error) {
|
||||
schema := m.GetDB().GetSchema()
|
||||
if schema == "" {
|
||||
schema = "current_schema()"
|
||||
} else {
|
||||
schema = fmt.Sprintf("'%s'", schema)
|
||||
}
|
||||
|
||||
query := fmt.Sprintf(
|
||||
"SELECT COUNT(*) FROM information_schema.table_constraints WHERE constraint_schema = %s AND table_name = '%s' AND constraint_name = '%s' AND constraint_type = 'FOREIGN KEY'",
|
||||
schema,
|
||||
table,
|
||||
constraint,
|
||||
)
|
||||
|
||||
value, err := m.GetDB().GetValue(ctx, query)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return value.Int() > 0, nil
|
||||
}
|
||||
|
||||
// CreateSchema creates a new database schema.
|
||||
func (m *Migration) CreateSchema(ctx context.Context, schema string) error {
|
||||
sql := fmt.Sprintf("CREATE SCHEMA IF NOT EXISTS %s", database.QuoteIdentifierDouble(schema))
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// DropSchema drops an existing database schema.
|
||||
func (m *Migration) DropSchema(ctx context.Context, schema string, cascade ...bool) error {
|
||||
sql := "DROP SCHEMA "
|
||||
if len(cascade) > 0 && cascade[0] {
|
||||
sql += "IF EXISTS "
|
||||
sql += database.QuoteIdentifierDouble(schema) + " CASCADE"
|
||||
} else {
|
||||
sql += "IF EXISTS " + database.QuoteIdentifierDouble(schema)
|
||||
}
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// HasSchema checks if a schema exists.
|
||||
func (m *Migration) HasSchema(ctx context.Context, schema string) (bool, error) {
|
||||
query := fmt.Sprintf(
|
||||
"SELECT COUNT(*) FROM information_schema.schemata WHERE schema_name = '%s'",
|
||||
schema,
|
||||
)
|
||||
|
||||
value, err := m.GetDB().GetValue(ctx, query)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return value.Int() > 0, nil
|
||||
}
|
||||
|
||||
// formatDefaultValue formats the default value for SQL.
|
||||
func formatDefaultValue(value any) string {
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
return fmt.Sprintf("'%s'", escapeString(v))
|
||||
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
|
||||
return fmt.Sprintf("%d", v)
|
||||
case float32, float64:
|
||||
return fmt.Sprintf("%f", v)
|
||||
case bool:
|
||||
if v {
|
||||
return "TRUE"
|
||||
}
|
||||
return "FALSE"
|
||||
case nil:
|
||||
return "NULL"
|
||||
default:
|
||||
return fmt.Sprintf("'%v'", v)
|
||||
}
|
||||
}
|
||||
|
||||
// escapeString escapes special characters in strings for SQL.
|
||||
func escapeString(s string) string {
|
||||
s = strings.ReplaceAll(s, "'", "''")
|
||||
return s
|
||||
}
|
||||
|
|
@ -45,13 +45,3 @@ func (d *Driver) New(core *database.Core, node *database.ConfigNode) (database.D
|
|||
func (d *Driver) GetChars() (charLeft string, charRight string) {
|
||||
return quoteChar, quoteChar
|
||||
}
|
||||
|
||||
// Migration returns a Migration instance for SQLite database operations.
|
||||
func (d *Driver) Migration() *Migration {
|
||||
return NewMigration(d)
|
||||
}
|
||||
|
||||
// GetMigration returns a Migration instance implementing the Migration interface.
|
||||
func (d *Driver) GetMigration() database.Migration {
|
||||
return d.Migration()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,321 +0,0 @@
|
|||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package sqlite
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.magicany.cc/black1552/gin-base/database"
|
||||
)
|
||||
|
||||
// Migration implements database migration operations for SQLite.
|
||||
type Migration struct {
|
||||
*database.MigrationCore
|
||||
*database.AutoMigrateCore
|
||||
}
|
||||
|
||||
// NewMigration creates a new SQLite Migration instance.
|
||||
func NewMigration(db database.DB) *Migration {
|
||||
return &Migration{
|
||||
MigrationCore: database.NewMigrationCore(db),
|
||||
AutoMigrateCore: database.NewAutoMigrateCore(db),
|
||||
}
|
||||
}
|
||||
|
||||
// CreateTable creates a new table with the given name and column definitions.
|
||||
func (m *Migration) CreateTable(ctx context.Context, table string, columns map[string]*database.ColumnDefinition, options ...database.TableOption) error {
|
||||
if len(columns) == 0 {
|
||||
return fmt.Errorf("cannot create table without columns")
|
||||
}
|
||||
|
||||
var opts database.TableOptions
|
||||
for _, opt := range options {
|
||||
opt(&opts)
|
||||
}
|
||||
|
||||
var sql strings.Builder
|
||||
sql.WriteString("CREATE TABLE ")
|
||||
if opts.IfNotExists {
|
||||
sql.WriteString("IF NOT EXISTS ")
|
||||
}
|
||||
sql.WriteString(database.QuoteIdentifier(table))
|
||||
sql.WriteString(" (\n")
|
||||
|
||||
// Add columns
|
||||
var colDefs []string
|
||||
var primaryKeys []string
|
||||
for name, def := range columns {
|
||||
colDef := m.buildColumnDefinition(name, def)
|
||||
// Only add to primaryKeys if it's not an auto-increment column
|
||||
// (auto-increment columns already have PRIMARY KEY in their definition)
|
||||
if def.PrimaryKey && !def.AutoIncrement {
|
||||
primaryKeys = append(primaryKeys, database.QuoteIdentifier(name))
|
||||
}
|
||||
colDefs = append(colDefs, " "+colDef)
|
||||
}
|
||||
|
||||
// Add composite primary key constraint if needed (for non-auto-increment keys)
|
||||
if len(primaryKeys) > 1 {
|
||||
colDefs = append(colDefs, fmt.Sprintf(" PRIMARY KEY (%s)", strings.Join(primaryKeys, ", ")))
|
||||
}
|
||||
|
||||
sql.WriteString(strings.Join(colDefs, ",\n"))
|
||||
sql.WriteString("\n)")
|
||||
|
||||
return m.ExecuteSQL(ctx, sql.String())
|
||||
}
|
||||
|
||||
// buildColumnDefinition builds column definition for SQLite.
|
||||
func (m *Migration) buildColumnDefinition(name string, def *database.ColumnDefinition) string {
|
||||
var parts []string
|
||||
parts = append(parts, database.QuoteIdentifier(name))
|
||||
|
||||
// Handle SQLite-specific types for auto-increment primary key
|
||||
if def.PrimaryKey && def.AutoIncrement {
|
||||
// SQLite requires INTEGER type for AUTOINCREMENT
|
||||
parts = append(parts, "INTEGER PRIMARY KEY AUTOINCREMENT")
|
||||
return strings.Join(parts, " ")
|
||||
}
|
||||
|
||||
// Regular column definition
|
||||
parts = append(parts, def.Type)
|
||||
|
||||
if !def.Null {
|
||||
parts = append(parts, "NOT NULL")
|
||||
}
|
||||
|
||||
if def.Unique && !def.PrimaryKey {
|
||||
parts = append(parts, "UNIQUE")
|
||||
}
|
||||
|
||||
if def.Default != nil {
|
||||
defaultValue := formatDefaultValue(def.Default)
|
||||
parts = append(parts, fmt.Sprintf("DEFAULT %s", defaultValue))
|
||||
}
|
||||
|
||||
return strings.Join(parts, " ")
|
||||
}
|
||||
|
||||
// DropTable drops an existing table from the database.
|
||||
func (m *Migration) DropTable(ctx context.Context, table string, ifExists ...bool) error {
|
||||
sql := "DROP TABLE "
|
||||
if len(ifExists) > 0 && ifExists[0] {
|
||||
sql += "IF EXISTS "
|
||||
}
|
||||
sql += database.QuoteIdentifier(table)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// HasTable checks if a table exists in the database.
|
||||
func (m *Migration) HasTable(ctx context.Context, table string) (bool, error) {
|
||||
query := fmt.Sprintf(
|
||||
"SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='%s'",
|
||||
table,
|
||||
)
|
||||
|
||||
value, err := m.GetDB().GetValue(ctx, query)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return value.Int() > 0, nil
|
||||
}
|
||||
|
||||
// RenameTable renames an existing table from oldName to newName.
|
||||
func (m *Migration) RenameTable(ctx context.Context, oldName, newName string) error {
|
||||
sql := fmt.Sprintf(
|
||||
"ALTER TABLE %s RENAME TO %s",
|
||||
database.QuoteIdentifier(oldName),
|
||||
database.QuoteIdentifier(newName),
|
||||
)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// TruncateTable removes all records from a table but keeps the table structure.
|
||||
func (m *Migration) TruncateTable(ctx context.Context, table string) error {
|
||||
sql := fmt.Sprintf("DELETE FROM %s", database.QuoteIdentifier(table))
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// AddColumn adds a new column to an existing table.
|
||||
func (m *Migration) AddColumn(ctx context.Context, table, column string, definition *database.ColumnDefinition) error {
|
||||
colDef := m.buildColumnDefinition(column, definition)
|
||||
sql := fmt.Sprintf("ALTER TABLE %s ADD COLUMN %s", database.QuoteIdentifier(table), colDef)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// DropColumn removes a column from an existing table.
|
||||
// Note: SQLite has limited ALTER TABLE support. This may require table recreation.
|
||||
func (m *Migration) DropColumn(ctx context.Context, table, column string) error {
|
||||
// SQLite 3.35.0+ supports DROP COLUMN
|
||||
sql := fmt.Sprintf(
|
||||
"ALTER TABLE %s DROP COLUMN %s",
|
||||
database.QuoteIdentifier(table),
|
||||
database.QuoteIdentifier(column),
|
||||
)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// RenameColumn renames a column in an existing table.
|
||||
func (m *Migration) RenameColumn(ctx context.Context, table, oldName, newName string) error {
|
||||
sql := fmt.Sprintf(
|
||||
"ALTER TABLE %s RENAME COLUMN %s TO %s",
|
||||
database.QuoteIdentifier(table),
|
||||
database.QuoteIdentifier(oldName),
|
||||
database.QuoteIdentifierDouble(newName),
|
||||
)
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// ModifyColumn modifies an existing column's definition.
|
||||
// Note: SQLite requires table recreation for most column modifications.
|
||||
func (m *Migration) ModifyColumn(ctx context.Context, table, column string, definition *database.ColumnDefinition) error {
|
||||
// SQLite has very limited ALTER TABLE support
|
||||
// This would typically require recreating the table
|
||||
return fmt.Errorf("SQLite does not support MODIFY COLUMN directly, table recreation required")
|
||||
}
|
||||
|
||||
// HasColumn checks if a column exists in a table.
|
||||
func (m *Migration) HasColumn(ctx context.Context, table, column string) (bool, error) {
|
||||
fields, err := m.GetDB().TableFields(ctx, table)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
_, exists := fields[column]
|
||||
return exists, nil
|
||||
}
|
||||
|
||||
// CreateIndex creates a new index on the specified table and columns.
|
||||
func (m *Migration) CreateIndex(ctx context.Context, table, index string, columns []string, options ...database.IndexOption) error {
|
||||
var opts database.IndexOptions
|
||||
for _, opt := range options {
|
||||
opt(&opts)
|
||||
}
|
||||
|
||||
var sql strings.Builder
|
||||
sql.WriteString("CREATE ")
|
||||
|
||||
if opts.Unique {
|
||||
sql.WriteString("UNIQUE ")
|
||||
}
|
||||
|
||||
sql.WriteString("INDEX ")
|
||||
if opts.IfNotExists {
|
||||
sql.WriteString("IF NOT EXISTS ")
|
||||
}
|
||||
sql.WriteString(database.QuoteIdentifier(index))
|
||||
sql.WriteString(" ON ")
|
||||
sql.WriteString(database.QuoteIdentifier(table))
|
||||
|
||||
colList := m.BuildIndexColumns(columns)
|
||||
sql.WriteString(fmt.Sprintf(" (%s)", colList))
|
||||
|
||||
return m.ExecuteSQL(ctx, sql.String())
|
||||
}
|
||||
|
||||
// DropIndex drops an existing index from a table.
|
||||
func (m *Migration) DropIndex(ctx context.Context, table, index string) error {
|
||||
sql := fmt.Sprintf("DROP INDEX IF EXISTS %s", database.QuoteIdentifier(index))
|
||||
return m.ExecuteSQL(ctx, sql)
|
||||
}
|
||||
|
||||
// HasIndex checks if an index exists on a table.
|
||||
func (m *Migration) HasIndex(ctx context.Context, table, index string) (bool, error) {
|
||||
query := fmt.Sprintf(
|
||||
"SELECT COUNT(*) FROM sqlite_master WHERE type='index' AND name='%s' AND tbl_name='%s'",
|
||||
index,
|
||||
table,
|
||||
)
|
||||
|
||||
value, err := m.GetDB().GetValue(ctx, query)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return value.Int() > 0, nil
|
||||
}
|
||||
|
||||
// CreateForeignKey creates a foreign key constraint.
|
||||
// Note: SQLite requires foreign keys to be enabled with PRAGMA foreign_keys = ON
|
||||
func (m *Migration) CreateForeignKey(ctx context.Context, table, constraint string, columns []string, refTable string, refColumns []string, options ...database.ForeignKeyOption) error {
|
||||
// SQLite doesn't support adding foreign keys to existing tables
|
||||
// Foreign keys must be defined during table creation
|
||||
return fmt.Errorf("SQLite does not support adding foreign keys to existing tables")
|
||||
}
|
||||
|
||||
// DropForeignKey drops a foreign key constraint.
|
||||
func (m *Migration) DropForeignKey(ctx context.Context, table, constraint string) error {
|
||||
// SQLite doesn't support dropping foreign keys from existing tables
|
||||
return fmt.Errorf("SQLite does not support dropping foreign keys from existing tables")
|
||||
}
|
||||
|
||||
// HasForeignKey checks if a foreign key constraint exists.
|
||||
func (m *Migration) HasForeignKey(ctx context.Context, table, constraint string) (bool, error) {
|
||||
// Query pragma to check foreign keys
|
||||
query := fmt.Sprintf("PRAGMA foreign_key_list(%s)", database.QuoteIdentifier(table))
|
||||
|
||||
result, err := m.GetDB().GetAll(ctx, query)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Check if constraint exists in the result
|
||||
for _, row := range result {
|
||||
if id, ok := row["id"]; ok && id.String() == constraint {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// CreateSchema is not applicable for SQLite (single database file).
|
||||
func (m *Migration) CreateSchema(ctx context.Context, schema string) error {
|
||||
// SQLite doesn't support schemas
|
||||
return fmt.Errorf("SQLite does not support schemas")
|
||||
}
|
||||
|
||||
// DropSchema is not applicable for SQLite (single database file).
|
||||
func (m *Migration) DropSchema(ctx context.Context, schema string, cascade ...bool) error {
|
||||
// SQLite doesn't support schemas
|
||||
return fmt.Errorf("SQLite does not support schemas")
|
||||
}
|
||||
|
||||
// HasSchema checks if a schema exists (always returns false for SQLite).
|
||||
func (m *Migration) HasSchema(ctx context.Context, schema string) (bool, error) {
|
||||
// SQLite doesn't support schemas
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// formatDefaultValue formats the default value for SQL.
|
||||
func formatDefaultValue(value any) string {
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
return fmt.Sprintf("'%s'", escapeString(v))
|
||||
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
|
||||
return fmt.Sprintf("%d", v)
|
||||
case float32, float64:
|
||||
return fmt.Sprintf("%f", v)
|
||||
case bool:
|
||||
if v {
|
||||
return "1"
|
||||
}
|
||||
return "0"
|
||||
case nil:
|
||||
return "NULL"
|
||||
default:
|
||||
return fmt.Sprintf("'%v'", v)
|
||||
}
|
||||
}
|
||||
|
||||
// escapeString escapes special characters in strings for SQL.
|
||||
func escapeString(s string) string {
|
||||
s = strings.ReplaceAll(s, "'", "''")
|
||||
return s
|
||||
}
|
||||
|
|
@ -50,9 +50,3 @@ func (d *Driver) New(core *database.Core, node *database.ConfigNode) (database.D
|
|||
func (d *Driver) GetChars() (charLeft string, charRight string) {
|
||||
return quoteChar, quoteChar
|
||||
}
|
||||
|
||||
// GetMigration returns a Migration instance implementing the Migration interface.
|
||||
// Note: sqlitecgo driver does not have migration support yet, returns nil.
|
||||
func (d *Driver) GetMigration() database.Migration {
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -319,10 +319,6 @@ type DB interface {
|
|||
// If no schema is specified, it uses the default schema.
|
||||
Tables(ctx context.Context, schema ...string) (tables []string, err error)
|
||||
|
||||
// HasTable checks if a table exists in the database.
|
||||
// It returns true if the table exists, false otherwise.
|
||||
HasTable(ctx context.Context, table string) (bool, error)
|
||||
|
||||
// TableFields returns detailed information about all fields in the specified table.
|
||||
// The returned map keys are field names and values contain field metadata.
|
||||
TableFields(ctx context.Context, table string, schema ...string) (map[string]*TableField, error)
|
||||
|
|
@ -349,14 +345,6 @@ type DB interface {
|
|||
// OrderRandomFunction returns the SQL function for random ordering.
|
||||
// The implementation is database-specific (e.g., RAND() for MySQL).
|
||||
OrderRandomFunction() string
|
||||
|
||||
// ===========================================================================
|
||||
// Migration support.
|
||||
// ===========================================================================
|
||||
|
||||
// GetMigration returns a Migration instance for database schema operations.
|
||||
// The returned Migration can be used to create, alter, and drop tables, columns, indexes, etc.
|
||||
GetMigration() Migration
|
||||
}
|
||||
|
||||
// TX defines the interfaces for ORM transaction operations.
|
||||
|
|
|
|||
|
|
@ -735,7 +735,7 @@ func (c *Core) writeSqlToLogger(sql *Sql) {
|
|||
}
|
||||
|
||||
// HasTable determine whether the table name exists in the database.
|
||||
func (c *Core) HasTable(ctx context.Context, name string) (bool, error) {
|
||||
func (c *Core) HasTable(name string) (bool, error) {
|
||||
tables, err := c.GetTablesWithCache()
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
|
|
|||
|
|
@ -44,8 +44,3 @@ func (d *DriverDefault) PingMaster() error {
|
|||
func (d *DriverDefault) PingSlave() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetMigration returns a Migration instance. For default driver, it returns nil.
|
||||
func (d *DriverDefault) GetMigration() Migration {
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -527,7 +527,7 @@ func formatWhereHolder(ctx context.Context, db DB, in formatWhereHolderInput) (n
|
|||
)
|
||||
// If `Prefix` is given, it checks and retrieves the table name.
|
||||
if in.Prefix != "" {
|
||||
hasTable, _ := db.GetCore().HasTable(ctx, in.Prefix)
|
||||
hasTable, _ := db.GetCore().HasTable(in.Prefix)
|
||||
if hasTable {
|
||||
in.Table = in.Prefix
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,304 +0,0 @@
|
|||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// Migration defines the interface for database migration operations.
|
||||
// It provides methods for creating, altering, and dropping database schema objects.
|
||||
type Migration interface {
|
||||
// ===========================================================================
|
||||
// Auto Migration based on Entity Structs
|
||||
// ===========================================================================
|
||||
|
||||
// AutoMigrate automatically creates or updates tables based on entity structs.
|
||||
// It analyzes the struct fields and their tags to determine column definitions.
|
||||
// The entities parameter accepts struct instances or pointers to structs.
|
||||
AutoMigrate(ctx context.Context, entities ...any) error
|
||||
|
||||
// ===========================================================================
|
||||
// Table Operations
|
||||
// ===========================================================================
|
||||
|
||||
// CreateTable creates a new table with the given name and column definitions.
|
||||
// The columns parameter is a map where keys are column names and values are column definitions.
|
||||
CreateTable(ctx context.Context, table string, columns map[string]*ColumnDefinition, options ...TableOption) error
|
||||
|
||||
// DropTable drops an existing table from the database.
|
||||
// If ifExists is true, it won't return an error if the table doesn't exist.
|
||||
DropTable(ctx context.Context, table string, ifExists ...bool) error
|
||||
|
||||
// HasTable checks if a table exists in the database.
|
||||
HasTable(ctx context.Context, table string) (bool, error)
|
||||
|
||||
// RenameTable renames an existing table from oldName to newName.
|
||||
RenameTable(ctx context.Context, oldName, newName string) error
|
||||
|
||||
// TruncateTable removes all records from a table but keeps the table structure.
|
||||
TruncateTable(ctx context.Context, table string) error
|
||||
|
||||
// ===========================================================================
|
||||
// Column Operations
|
||||
// ===========================================================================
|
||||
|
||||
// AddColumn adds a new column to an existing table.
|
||||
AddColumn(ctx context.Context, table, column string, definition *ColumnDefinition) error
|
||||
|
||||
// DropColumn removes a column from an existing table.
|
||||
DropColumn(ctx context.Context, table, column string) error
|
||||
|
||||
// RenameColumn renames a column in an existing table.
|
||||
RenameColumn(ctx context.Context, table, oldName, newName string) error
|
||||
|
||||
// ModifyColumn modifies an existing column's definition.
|
||||
ModifyColumn(ctx context.Context, table, column string, definition *ColumnDefinition) error
|
||||
|
||||
// HasColumn checks if a column exists in a table.
|
||||
HasColumn(ctx context.Context, table, column string) (bool, error)
|
||||
|
||||
// ===========================================================================
|
||||
// Index Operations
|
||||
// ===========================================================================
|
||||
|
||||
// CreateIndex creates a new index on the specified table and columns.
|
||||
CreateIndex(ctx context.Context, table, index string, columns []string, options ...IndexOption) error
|
||||
|
||||
// DropIndex drops an existing index from a table.
|
||||
DropIndex(ctx context.Context, table, index string) error
|
||||
|
||||
// HasIndex checks if an index exists on a table.
|
||||
HasIndex(ctx context.Context, table, index string) (bool, error)
|
||||
|
||||
// ===========================================================================
|
||||
// Foreign Key Operations
|
||||
// ===========================================================================
|
||||
|
||||
// CreateForeignKey creates a foreign key constraint.
|
||||
CreateForeignKey(ctx context.Context, table, constraint string, columns []string, refTable string, refColumns []string, options ...ForeignKeyOption) error
|
||||
|
||||
// DropForeignKey drops a foreign key constraint.
|
||||
DropForeignKey(ctx context.Context, table, constraint string) error
|
||||
|
||||
// HasForeignKey checks if a foreign key constraint exists.
|
||||
HasForeignKey(ctx context.Context, table, constraint string) (bool, error)
|
||||
|
||||
// ===========================================================================
|
||||
// Schema Operations
|
||||
// ===========================================================================
|
||||
|
||||
// CreateSchema creates a new database schema (namespace).
|
||||
CreateSchema(ctx context.Context, schema string) error
|
||||
|
||||
// DropSchema drops an existing database schema.
|
||||
DropSchema(ctx context.Context, schema string, cascade ...bool) error
|
||||
|
||||
// HasSchema checks if a schema exists.
|
||||
HasSchema(ctx context.Context, schema string) (bool, error)
|
||||
}
|
||||
|
||||
// ColumnDefinition defines the structure and properties of a database column.
|
||||
type ColumnDefinition struct {
|
||||
// Type specifies the database column type (e.g., "VARCHAR(255)", "INT", "TEXT").
|
||||
Type string
|
||||
|
||||
// Null indicates whether the column can contain NULL values.
|
||||
Null bool
|
||||
|
||||
// Default sets the default value for the column.
|
||||
Default any
|
||||
|
||||
// Comment adds a comment to the column.
|
||||
Comment string
|
||||
|
||||
// AutoIncrement enables auto-increment for numeric columns.
|
||||
AutoIncrement bool
|
||||
|
||||
// PrimaryKey marks this column as part of the primary key.
|
||||
PrimaryKey bool
|
||||
|
||||
// Unique marks this column as having unique values.
|
||||
Unique bool
|
||||
|
||||
// Length specifies the length for string types.
|
||||
Length int
|
||||
|
||||
// Precision specifies the precision for decimal/numeric types.
|
||||
Precision int
|
||||
|
||||
// Scale specifies the scale for decimal/numeric types.
|
||||
Scale int
|
||||
}
|
||||
|
||||
// TableOption defines additional options for table creation.
|
||||
type TableOption func(*TableOptions)
|
||||
|
||||
// TableOptions defines additional options for table creation.
|
||||
type TableOptions struct {
|
||||
// Engine specifies the storage engine (MySQL specific).
|
||||
Engine string
|
||||
|
||||
// Charset specifies the character set.
|
||||
Charset string
|
||||
|
||||
// Collation specifies the collation.
|
||||
Collation string
|
||||
|
||||
// Comment adds a comment to the table.
|
||||
Comment string
|
||||
|
||||
// IfNotExists prevents error if table already exists.
|
||||
IfNotExists bool
|
||||
}
|
||||
|
||||
// WithEngine sets the storage engine for the table (MySQL specific).
|
||||
func WithEngine(engine string) TableOption {
|
||||
return func(opts *TableOptions) {
|
||||
opts.Engine = engine
|
||||
}
|
||||
}
|
||||
|
||||
// WithCharset sets the character set for the table.
|
||||
func WithCharset(charset string) TableOption {
|
||||
return func(opts *TableOptions) {
|
||||
opts.Charset = charset
|
||||
}
|
||||
}
|
||||
|
||||
// WithCollation sets the collation for the table.
|
||||
func WithCollation(collation string) TableOption {
|
||||
return func(opts *TableOptions) {
|
||||
opts.Collation = collation
|
||||
}
|
||||
}
|
||||
|
||||
// WithTableComment adds a comment to the table.
|
||||
func WithTableComment(comment string) TableOption {
|
||||
return func(opts *TableOptions) {
|
||||
opts.Comment = comment
|
||||
}
|
||||
}
|
||||
|
||||
// WithIfNotExists prevents error if table already exists.
|
||||
func WithIfNotExists() TableOption {
|
||||
return func(opts *TableOptions) {
|
||||
opts.IfNotExists = true
|
||||
}
|
||||
}
|
||||
|
||||
// IndexOption defines additional options for index creation.
|
||||
type IndexOption func(*IndexOptions)
|
||||
|
||||
// IndexOptions defines additional options for index creation.
|
||||
type IndexOptions struct {
|
||||
// Unique marks the index as unique.
|
||||
Unique bool
|
||||
|
||||
// FullText creates a full-text index (MySQL specific).
|
||||
FullText bool
|
||||
|
||||
// Spatial creates a spatial index (MySQL specific).
|
||||
Spatial bool
|
||||
|
||||
// Using specifies the index method (e.g., BTREE, HASH).
|
||||
Using string
|
||||
|
||||
// Comment adds a comment to the index.
|
||||
Comment string
|
||||
|
||||
// IfNotExists prevents error if index already exists.
|
||||
IfNotExists bool
|
||||
}
|
||||
|
||||
// WithUniqueIndex creates a unique index.
|
||||
func WithUniqueIndex() IndexOption {
|
||||
return func(opts *IndexOptions) {
|
||||
opts.Unique = true
|
||||
}
|
||||
}
|
||||
|
||||
// WithFullTextIndex creates a full-text index (MySQL specific).
|
||||
func WithFullTextIndex() IndexOption {
|
||||
return func(opts *IndexOptions) {
|
||||
opts.FullText = true
|
||||
}
|
||||
}
|
||||
|
||||
// WithSpatialIndex creates a spatial index (MySQL specific).
|
||||
func WithSpatialIndex() IndexOption {
|
||||
return func(opts *IndexOptions) {
|
||||
opts.Spatial = true
|
||||
}
|
||||
}
|
||||
|
||||
// WithIndexUsing specifies the index method.
|
||||
func WithIndexUsing(method string) IndexOption {
|
||||
return func(opts *IndexOptions) {
|
||||
opts.Using = method
|
||||
}
|
||||
}
|
||||
|
||||
// WithIndexComment adds a comment to the index.
|
||||
func WithIndexComment(comment string) IndexOption {
|
||||
return func(opts *IndexOptions) {
|
||||
opts.Comment = comment
|
||||
}
|
||||
}
|
||||
|
||||
// WithIndexIfNotExists prevents error if index already exists.
|
||||
func WithIndexIfNotExists() IndexOption {
|
||||
return func(opts *IndexOptions) {
|
||||
opts.IfNotExists = true
|
||||
}
|
||||
}
|
||||
|
||||
// ForeignKeyOption defines additional options for foreign key creation.
|
||||
type ForeignKeyOption func(*ForeignKeyOptions)
|
||||
|
||||
// ForeignKeyOptions defines additional options for foreign key creation.
|
||||
type ForeignKeyOptions struct {
|
||||
// OnDelete specifies the action when referenced row is deleted.
|
||||
OnDelete string
|
||||
|
||||
// OnUpdate specifies the action when referenced row is updated.
|
||||
OnUpdate string
|
||||
|
||||
// Deferrable makes the constraint deferrable (PostgreSQL specific).
|
||||
Deferrable bool
|
||||
|
||||
// InitiallyDeferred sets the constraint to be initially deferred (PostgreSQL specific).
|
||||
InitiallyDeferred bool
|
||||
}
|
||||
|
||||
// WithOnDelete sets the ON DELETE action for foreign key.
|
||||
func WithOnDelete(action string) ForeignKeyOption {
|
||||
return func(opts *ForeignKeyOptions) {
|
||||
opts.OnDelete = action
|
||||
}
|
||||
}
|
||||
|
||||
// WithOnUpdate sets the ON UPDATE action for foreign key.
|
||||
func WithOnUpdate(action string) ForeignKeyOption {
|
||||
return func(opts *ForeignKeyOptions) {
|
||||
opts.OnUpdate = action
|
||||
}
|
||||
}
|
||||
|
||||
// WithDeferrable makes the foreign key constraint deferrable (PostgreSQL specific).
|
||||
func WithDeferrable() ForeignKeyOption {
|
||||
return func(opts *ForeignKeyOptions) {
|
||||
opts.Deferrable = true
|
||||
}
|
||||
}
|
||||
|
||||
// WithInitiallyDeferred sets the constraint to be initially deferred (PostgreSQL specific).
|
||||
func WithInitiallyDeferred() ForeignKeyOption {
|
||||
return func(opts *ForeignKeyOptions) {
|
||||
opts.InitiallyDeferred = true
|
||||
}
|
||||
}
|
||||
|
|
@ -1,499 +0,0 @@
|
|||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// AutoMigrateCore provides automatic migration functionality based on entity structs.
|
||||
type AutoMigrateCore struct {
|
||||
db DB
|
||||
}
|
||||
|
||||
// NewAutoMigrateCore creates a new AutoMigrateCore instance.
|
||||
func NewAutoMigrateCore(db DB) *AutoMigrateCore {
|
||||
return &AutoMigrateCore{db: db}
|
||||
}
|
||||
|
||||
// AutoMigrate automatically creates or updates tables based on entity structs.
|
||||
func (am *AutoMigrateCore) AutoMigrate(ctx context.Context, entities ...any) error {
|
||||
for _, entity := range entities {
|
||||
if err := am.migrateEntity(ctx, entity); err != nil {
|
||||
return fmt.Errorf("failed to migrate entity %T: %w", entity, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// migrateEntity migrates a single entity to database.
|
||||
func (am *AutoMigrateCore) migrateEntity(ctx context.Context, entity any) error {
|
||||
// Get table name and columns from entity
|
||||
tableName, columns, err := am.parseEntity(entity)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(columns) == 0 {
|
||||
return fmt.Errorf("no columns found for table %s", tableName)
|
||||
}
|
||||
|
||||
// For SQLite, ensure the database file directory exists
|
||||
if am.db.GetConfig().Type == "sqlite" {
|
||||
if err := am.ensureSQLiteDirectory(); err != nil {
|
||||
return fmt.Errorf("failed to prepare SQLite database: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Check if table exists
|
||||
hasTable, err := am.db.HasTable(ctx, tableName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check table existence: %w", err)
|
||||
}
|
||||
|
||||
if !hasTable {
|
||||
// Create table
|
||||
return am.createTableFromColumns(ctx, tableName, columns)
|
||||
}
|
||||
|
||||
// Update table structure
|
||||
return am.updateTableStructure(ctx, tableName, columns)
|
||||
}
|
||||
|
||||
// parseEntity parses an entity struct and returns table name and column definitions.
|
||||
func (am *AutoMigrateCore) parseEntity(entity any) (string, map[string]*ColumnDefinition, error) {
|
||||
val := reflect.ValueOf(entity)
|
||||
|
||||
// Handle pointer
|
||||
if val.Kind() == reflect.Ptr {
|
||||
val = val.Elem()
|
||||
}
|
||||
|
||||
if val.Kind() != reflect.Struct {
|
||||
return "", nil, fmt.Errorf("entity must be a struct or pointer to struct")
|
||||
}
|
||||
|
||||
typ := val.Type()
|
||||
|
||||
// Get table name from orm tag or struct name
|
||||
tableName := am.getTableName(typ)
|
||||
|
||||
// Parse columns
|
||||
columns := make(map[string]*ColumnDefinition)
|
||||
for i := 0; i < val.NumField(); i++ {
|
||||
field := typ.Field(i)
|
||||
fieldValue := val.Field(i)
|
||||
|
||||
// Skip unexported fields
|
||||
if !field.IsExported() {
|
||||
continue
|
||||
}
|
||||
|
||||
// Parse field to column definition
|
||||
colName, colDef, err := am.parseField(field, fieldValue)
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("failed to parse field %s: %w", field.Name, err)
|
||||
}
|
||||
|
||||
if colName != "" && colDef != nil {
|
||||
columns[colName] = colDef
|
||||
}
|
||||
}
|
||||
|
||||
return tableName, columns, nil
|
||||
}
|
||||
|
||||
// getTableName extracts table name from struct tags or generates from struct name.
|
||||
func (am *AutoMigrateCore) getTableName(typ reflect.Type) string {
|
||||
// Check for orm tag
|
||||
if tag, ok := typ.FieldByName("Meta"); ok {
|
||||
ormTag := tag.Tag.Get("orm")
|
||||
if ormTag != "" {
|
||||
// Parse table:name from orm tag
|
||||
parts := strings.Split(ormTag, ",")
|
||||
for _, part := range parts {
|
||||
part = strings.TrimSpace(part)
|
||||
if strings.HasPrefix(part, "table:") {
|
||||
return strings.TrimPrefix(part, "table:")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for table method (commented out as it requires instance)
|
||||
// method, hasMethod := typ.MethodByName("TableName")
|
||||
// if hasMethod {
|
||||
// // Try to call TableName method if it exists
|
||||
// }
|
||||
|
||||
// Convert struct name to snake_case table name
|
||||
return camelToSnake(typ.Name())
|
||||
}
|
||||
|
||||
// parseField parses a struct field into a column definition.
|
||||
func (am *AutoMigrateCore) parseField(field reflect.StructField, fieldValue reflect.Value) (string, *ColumnDefinition, error) {
|
||||
// Check orm tag
|
||||
ormTag := field.Tag.Get("orm")
|
||||
if ormTag == "-" {
|
||||
// Skip field
|
||||
return "", nil, nil
|
||||
}
|
||||
|
||||
// Get column name
|
||||
colName := am.getColumnName(field, ormTag)
|
||||
|
||||
// Build column definition
|
||||
colDef := &ColumnDefinition{
|
||||
Type: am.getFieldType(field, fieldValue),
|
||||
Null: true, // Default to nullable
|
||||
}
|
||||
|
||||
// Parse orm tag options
|
||||
if ormTag != "" {
|
||||
am.parseOrmTag(colDef, ormTag)
|
||||
}
|
||||
|
||||
// Check for gorm/tag compatibility
|
||||
if gormTag := field.Tag.Get("gorm"); gormTag != "" {
|
||||
am.parseGormTag(colDef, gormTag)
|
||||
}
|
||||
|
||||
// Check json tag for field presence
|
||||
jsonTag := field.Tag.Get("json")
|
||||
if jsonTag == "-" {
|
||||
return "", nil, nil
|
||||
}
|
||||
|
||||
return colName, colDef, nil
|
||||
}
|
||||
|
||||
// getColumnName extracts column name from field name or tags.
|
||||
func (am *AutoMigrateCore) getColumnName(field reflect.StructField, ormTag string) string {
|
||||
// Check orm tag for explicit column name
|
||||
if ormTag != "" {
|
||||
parts := strings.Split(ormTag, ",")
|
||||
namePart := strings.TrimSpace(parts[0])
|
||||
if namePart != "" && !strings.Contains(namePart, ":") {
|
||||
return namePart
|
||||
}
|
||||
}
|
||||
|
||||
// Use field name converted to snake_case
|
||||
return camelToSnake(field.Name)
|
||||
}
|
||||
|
||||
// getFieldType determines the database type for a Go field type.
|
||||
func (am *AutoMigrateCore) getFieldType(field reflect.StructField, fieldValue reflect.Value) string {
|
||||
// Check for explicit type in orm tag
|
||||
ormTag := field.Tag.Get("orm")
|
||||
if ormTag != "" {
|
||||
parts := strings.Split(ormTag, ",")
|
||||
for _, part := range parts {
|
||||
part = strings.TrimSpace(part)
|
||||
if strings.HasPrefix(part, "type:") {
|
||||
return strings.TrimPrefix(part, "type:")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Infer type from Go type
|
||||
switch fieldValue.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return "BIGINT"
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return "BIGINT UNSIGNED"
|
||||
case reflect.Float32:
|
||||
return "FLOAT"
|
||||
case reflect.Float64:
|
||||
return "DOUBLE"
|
||||
case reflect.Bool:
|
||||
return "BOOLEAN"
|
||||
case reflect.String:
|
||||
// Check for length specification
|
||||
length := am.getStringLength(field)
|
||||
if length > 0 {
|
||||
return fmt.Sprintf("VARCHAR(%d)", length)
|
||||
}
|
||||
return "TEXT"
|
||||
case reflect.Struct:
|
||||
// Handle special types
|
||||
typeName := fieldValue.Type().String()
|
||||
switch {
|
||||
case strings.Contains(typeName, "time.Time"):
|
||||
return "TIMESTAMP"
|
||||
default:
|
||||
return "TEXT"
|
||||
}
|
||||
case reflect.Slice:
|
||||
elemKind := fieldValue.Type().Elem().Kind()
|
||||
if elemKind == reflect.Uint8 {
|
||||
return "BLOB"
|
||||
}
|
||||
return "JSON"
|
||||
default:
|
||||
return "TEXT"
|
||||
}
|
||||
}
|
||||
|
||||
// getStringLength gets the specified string length from tags.
|
||||
func (am *AutoMigrateCore) getStringLength(field reflect.StructField) int {
|
||||
// Check orm tag
|
||||
ormTag := field.Tag.Get("orm")
|
||||
if ormTag != "" {
|
||||
parts := strings.Split(ormTag, ",")
|
||||
for _, part := range parts {
|
||||
part = strings.TrimSpace(part)
|
||||
if strings.HasPrefix(part, "length:") {
|
||||
var length int
|
||||
fmt.Sscanf(strings.TrimPrefix(part, "length:"), "%d", &length)
|
||||
return length
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check gorm tag
|
||||
gormTag := field.Tag.Get("gorm")
|
||||
if gormTag != "" {
|
||||
parts := strings.Split(gormTag, ";")
|
||||
for _, part := range parts {
|
||||
part = strings.TrimSpace(part)
|
||||
if strings.HasPrefix(part, "size:") {
|
||||
var size int
|
||||
fmt.Sscanf(strings.TrimPrefix(part, "size:"), "%d", &size)
|
||||
return size
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// parseOrmTag parses orm tag options.
|
||||
func (am *AutoMigrateCore) parseOrmTag(colDef *ColumnDefinition, ormTag string) {
|
||||
parts := strings.Split(ormTag, ",")
|
||||
for _, part := range parts {
|
||||
part = strings.TrimSpace(part)
|
||||
switch {
|
||||
case part == "pk" || part == "primary_key":
|
||||
colDef.PrimaryKey = true
|
||||
colDef.Null = false
|
||||
case part == "auto_increment":
|
||||
colDef.AutoIncrement = true
|
||||
case part == "not_null":
|
||||
colDef.Null = false
|
||||
case part == "unique":
|
||||
colDef.Unique = true
|
||||
case strings.HasPrefix(part, "default:"):
|
||||
defaultVal := strings.TrimPrefix(part, "default:")
|
||||
colDef.Default = defaultVal
|
||||
case strings.HasPrefix(part, "comment:"):
|
||||
colDef.Comment = strings.TrimPrefix(part, "comment:")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// parseGormTag parses gorm tag options for compatibility.
|
||||
func (am *AutoMigrateCore) parseGormTag(colDef *ColumnDefinition, gormTag string) {
|
||||
parts := strings.Split(gormTag, ";")
|
||||
for _, part := range parts {
|
||||
part = strings.TrimSpace(part)
|
||||
lowerPart := strings.ToLower(part)
|
||||
switch {
|
||||
case lowerPart == "primarykey" || lowerPart == "primaryKey":
|
||||
colDef.PrimaryKey = true
|
||||
colDef.Null = false
|
||||
case lowerPart == "autoincrement":
|
||||
colDef.AutoIncrement = true
|
||||
case lowerPart == "not null":
|
||||
colDef.Null = false
|
||||
case lowerPart == "unique":
|
||||
colDef.Unique = true
|
||||
case strings.HasPrefix(lowerPart, "default:"):
|
||||
defaultVal := strings.TrimPrefix(part, "default:")
|
||||
colDef.Default = defaultVal
|
||||
case strings.HasPrefix(lowerPart, "comment:"):
|
||||
colDef.Comment = strings.TrimPrefix(part, "comment:")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// createTableFromColumns creates a table from column definitions.
|
||||
func (am *AutoMigrateCore) createTableFromColumns(ctx context.Context, table string, columns map[string]*ColumnDefinition) error {
|
||||
// Get the migration instance based on database type
|
||||
migration := am.getMigrationInstance()
|
||||
if migration == nil {
|
||||
return fmt.Errorf("failed to get migration instance for database type %s", am.db.GetConfig().Type)
|
||||
}
|
||||
|
||||
return migration.CreateTable(ctx, table, columns)
|
||||
}
|
||||
|
||||
// updateTableStructure updates table structure by comparing with existing columns.
|
||||
func (am *AutoMigrateCore) updateTableStructure(ctx context.Context, table string, newColumns map[string]*ColumnDefinition) error {
|
||||
// Get existing columns
|
||||
existingFields, err := am.db.TableFields(ctx, table)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get table fields: %w", err)
|
||||
}
|
||||
|
||||
// Add missing columns
|
||||
for colName, colDef := range newColumns {
|
||||
if _, exists := existingFields[colName]; !exists {
|
||||
// Column doesn't exist, add it
|
||||
if err := am.addColumn(ctx, table, colName, colDef); err != nil {
|
||||
return fmt.Errorf("failed to add column %s: %w", colName, err)
|
||||
}
|
||||
} else {
|
||||
// Column exists, check if modification is needed
|
||||
if err := am.modifyColumnIfNeeded(ctx, table, colName, colDef, existingFields[colName]); err != nil {
|
||||
return fmt.Errorf("failed to modify column %s: %w", colName, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// addColumn adds a new column to table.
|
||||
func (am *AutoMigrateCore) addColumn(ctx context.Context, table, column string, definition *ColumnDefinition) error {
|
||||
migration := am.getMigrationInstance()
|
||||
if migration == nil {
|
||||
return fmt.Errorf("failed to get migration instance")
|
||||
}
|
||||
return migration.AddColumn(ctx, table, column, definition)
|
||||
}
|
||||
|
||||
// modifyColumnIfNeeded checks if column needs modification and applies it.
|
||||
func (am *AutoMigrateCore) modifyColumnIfNeeded(ctx context.Context, table, column string, newDef *ColumnDefinition, existingField *TableField) error {
|
||||
// Compare and modify if needed
|
||||
needsModification := false
|
||||
|
||||
// Check type
|
||||
if !strings.EqualFold(existingField.Type, newDef.Type) {
|
||||
needsModification = true
|
||||
}
|
||||
|
||||
// Check nullability
|
||||
if existingField.Null != newDef.Null {
|
||||
needsModification = true
|
||||
}
|
||||
|
||||
if needsModification {
|
||||
return am.modifyColumn(ctx, table, column, newDef)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// modifyColumn modifies an existing column.
|
||||
func (am *AutoMigrateCore) modifyColumn(ctx context.Context, table, column string, definition *ColumnDefinition) error {
|
||||
migration := am.getMigrationInstance()
|
||||
if migration == nil {
|
||||
return fmt.Errorf("failed to get migration instance")
|
||||
}
|
||||
return migration.ModifyColumn(ctx, table, column, definition)
|
||||
}
|
||||
|
||||
// getMigrationInstance returns the appropriate Migration instance based on database type.
|
||||
func (am *AutoMigrateCore) getMigrationInstance() Migration {
|
||||
return am.db.GetMigration()
|
||||
}
|
||||
|
||||
// camelToSnake converts CamelCase to snake_case.
|
||||
func camelToSnake(s string) string {
|
||||
var result strings.Builder
|
||||
for i, r := range s {
|
||||
if r >= 'A' && r <= 'Z' {
|
||||
// Add underscore before uppercase letter if:
|
||||
// 1. It's not the first character
|
||||
// 2. The previous character is lowercase or the next character is lowercase
|
||||
if i > 0 {
|
||||
prevRune := rune(s[i-1])
|
||||
if (prevRune >= 'a' && prevRune <= 'z') ||
|
||||
(i+1 < len(s) && rune(s[i+1]) >= 'a' && rune(s[i+1]) <= 'z') {
|
||||
result.WriteRune('_')
|
||||
}
|
||||
}
|
||||
result.WriteRune(r + 32)
|
||||
} else {
|
||||
result.WriteRune(r)
|
||||
}
|
||||
}
|
||||
return result.String()
|
||||
}
|
||||
|
||||
// FormatTime formats time for default values.
|
||||
func FormatTime(t time.Time) string {
|
||||
return t.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
|
||||
// IsZeroValue checks if a value is zero value.
|
||||
func IsZeroValue(v any) bool {
|
||||
if v == nil {
|
||||
return true
|
||||
}
|
||||
rv := reflect.ValueOf(v)
|
||||
switch rv.Kind() {
|
||||
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
|
||||
return rv.Len() == 0
|
||||
case reflect.Bool:
|
||||
return !rv.Bool()
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return rv.Int() == 0
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return rv.Uint() == 0
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return rv.Float() == 0
|
||||
case reflect.Interface, reflect.Ptr:
|
||||
return rv.IsNil()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ensureSQLiteDirectory ensures the SQLite database file directory exists.
|
||||
func (am *AutoMigrateCore) ensureSQLiteDirectory() error {
|
||||
config := am.db.GetConfig()
|
||||
if config.Type != "sqlite" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get the database file path from config.Name or config.Link
|
||||
dbPath := config.Name
|
||||
if dbPath == "" && config.Link != "" {
|
||||
// Parse link format: sqlite::@file(./data/company.db)
|
||||
if strings.Contains(config.Link, "@file(") {
|
||||
start := strings.Index(config.Link, "@file(") + 6
|
||||
end := strings.Index(config.Link[start:], ")")
|
||||
if end > 0 {
|
||||
dbPath = config.Link[start : start+end]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if dbPath == "" {
|
||||
return fmt.Errorf("SQLite database path is empty")
|
||||
}
|
||||
|
||||
// Get directory path
|
||||
dir := filepath.Dir(dbPath)
|
||||
|
||||
// Check if directory exists
|
||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||
// Create directory with all parents
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
return fmt.Errorf("failed to create directory %s: %w", dir, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,135 +0,0 @@
|
|||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// MigrationCore provides the base implementation for migration operations.
|
||||
// It serves as a foundation that database-specific drivers can extend.
|
||||
type MigrationCore struct {
|
||||
db DB // Database interface for executing SQL statements
|
||||
}
|
||||
|
||||
// NewMigrationCore creates a new MigrationCore instance.
|
||||
func NewMigrationCore(db DB) *MigrationCore {
|
||||
return &MigrationCore{db: db}
|
||||
}
|
||||
|
||||
// GetDB returns the underlying database interface.
|
||||
func (m *MigrationCore) GetDB() DB {
|
||||
return m.db
|
||||
}
|
||||
|
||||
// ExecuteSQL executes a raw SQL statement.
|
||||
func (m *MigrationCore) ExecuteSQL(ctx context.Context, sql string, args ...any) error {
|
||||
_, err := m.db.Exec(ctx, sql, args...)
|
||||
return err
|
||||
}
|
||||
|
||||
// BuildColumnDefinition builds the column definition SQL string from ColumnDefinition.
|
||||
func (m *MigrationCore) BuildColumnDefinition(name string, def *ColumnDefinition) string {
|
||||
var parts []string
|
||||
parts = append(parts, QuoteIdentifier(name))
|
||||
parts = append(parts, def.Type)
|
||||
|
||||
if !def.Null {
|
||||
parts = append(parts, "NOT NULL")
|
||||
}
|
||||
|
||||
if def.AutoIncrement {
|
||||
parts = append(parts, "AUTO_INCREMENT")
|
||||
}
|
||||
|
||||
if def.PrimaryKey {
|
||||
parts = append(parts, "PRIMARY KEY")
|
||||
}
|
||||
|
||||
if def.Unique && !def.PrimaryKey {
|
||||
parts = append(parts, "UNIQUE")
|
||||
}
|
||||
|
||||
if def.Default != nil {
|
||||
defaultValue := formatDefaultValue(def.Default)
|
||||
parts = append(parts, fmt.Sprintf("DEFAULT %s", defaultValue))
|
||||
}
|
||||
|
||||
return strings.Join(parts, " ")
|
||||
}
|
||||
|
||||
// formatDefaultValue formats the default value for SQL.
|
||||
func formatDefaultValue(value any) string {
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
return fmt.Sprintf("'%s'", escapeString(v))
|
||||
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
|
||||
return fmt.Sprintf("%d", v)
|
||||
case float32, float64:
|
||||
return fmt.Sprintf("%f", v)
|
||||
case bool:
|
||||
if v {
|
||||
return "TRUE"
|
||||
}
|
||||
return "FALSE"
|
||||
case nil:
|
||||
return "NULL"
|
||||
default:
|
||||
return fmt.Sprintf("'%v'", v)
|
||||
}
|
||||
}
|
||||
|
||||
// escapeString escapes special characters in strings for SQL.
|
||||
func escapeString(s string) string {
|
||||
s = strings.ReplaceAll(s, "'", "''")
|
||||
s = strings.ReplaceAll(s, "\\", "\\\\")
|
||||
return s
|
||||
}
|
||||
|
||||
// QuoteIdentifier quotes an identifier (table name, column name, etc.) with backticks.
|
||||
func QuoteIdentifier(name string) string {
|
||||
if strings.Contains(name, ".") {
|
||||
parts := strings.Split(name, ".")
|
||||
for i, part := range parts {
|
||||
parts[i] = fmt.Sprintf("`%s`", part)
|
||||
}
|
||||
return strings.Join(parts, ".")
|
||||
}
|
||||
return fmt.Sprintf("`%s`", name)
|
||||
}
|
||||
|
||||
// QuoteIdentifierDouble quotes an identifier with double quotes (for PostgreSQL, Oracle, etc.).
|
||||
func QuoteIdentifierDouble(name string) string {
|
||||
if strings.Contains(name, ".") {
|
||||
parts := strings.Split(name, ".")
|
||||
for i, part := range parts {
|
||||
parts[i] = fmt.Sprintf(`"%s"`, part)
|
||||
}
|
||||
return strings.Join(parts, ".")
|
||||
}
|
||||
return fmt.Sprintf(`"%s"`, name)
|
||||
}
|
||||
|
||||
// BuildIndexColumns builds the column list for index creation.
|
||||
func (m *MigrationCore) BuildIndexColumns(columns []string) string {
|
||||
quoted := make([]string, len(columns))
|
||||
for i, col := range columns {
|
||||
quoted[i] = QuoteIdentifier(col)
|
||||
}
|
||||
return strings.Join(quoted, ", ")
|
||||
}
|
||||
|
||||
// BuildForeignKeyColumns builds the column list for foreign key.
|
||||
func (m *MigrationCore) BuildForeignKeyColumns(columns []string) string {
|
||||
quoted := make([]string, len(columns))
|
||||
for i, col := range columns {
|
||||
quoted[i] = QuoteIdentifier(col)
|
||||
}
|
||||
return "(" + strings.Join(quoted, ", ") + ")"
|
||||
}
|
||||
|
|
@ -66,7 +66,7 @@ func (m *Model) getModel() *Model {
|
|||
func (m *Model) mappingAndFilterToTableFields(table string, fields []any, filter bool) []any {
|
||||
var fieldsTable = table
|
||||
if fieldsTable != "" {
|
||||
hasTable, _ := m.db.GetCore().HasTable(m.GetCtx(), fieldsTable)
|
||||
hasTable, _ := m.db.GetCore().HasTable(fieldsTable)
|
||||
if !hasTable {
|
||||
if fieldsTable != m.tablesInit {
|
||||
// Table/alias unknown (e.g., FieldsPrefix called before LeftJoin), skip filtering.
|
||||
|
|
|
|||
62
go.mod
62
go.mod
|
|
@ -4,37 +4,26 @@ go 1.25.0
|
|||
|
||||
require (
|
||||
git.magicany.cc/black1552/gf-common v1.0.1018
|
||||
github.com/ClickHouse/clickhouse-go/v2 v2.0.15
|
||||
github.com/eclipse/paho.mqtt.golang v1.5.1
|
||||
github.com/fsnotify/fsnotify v1.9.0
|
||||
github.com/gin-gonic/gin v1.12.0
|
||||
github.com/glebarez/go-sqlite v1.22.0
|
||||
github.com/go-sql-driver/mysql v1.9.3
|
||||
github.com/glebarez/sqlite v1.11.0
|
||||
github.com/gogf/gf/v2 v2.10.0
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/lib/pq v1.12.0
|
||||
github.com/mattn/go-sqlite3 v1.14.38
|
||||
github.com/microsoft/go-mssqldb v1.7.1
|
||||
github.com/olekukonko/tablewriter v1.1.4
|
||||
github.com/schollz/progressbar/v3 v3.19.0
|
||||
github.com/shopspring/decimal v1.3.1
|
||||
github.com/sijms/go-ora/v2 v2.7.10
|
||||
github.com/spf13/viper v1.21.0
|
||||
github.com/stretchr/testify v1.11.1
|
||||
go.opentelemetry.io/otel v1.42.0
|
||||
go.opentelemetry.io/otel/trace v1.42.0
|
||||
golang.org/x/crypto v0.49.0
|
||||
golang.org/x/mod v0.34.0
|
||||
golang.org/x/tools v0.43.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
gorm.io/driver/mysql v1.6.0
|
||||
gorm.io/gorm v1.31.1
|
||||
)
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.2.0 // indirect
|
||||
gitee.com/chunanyong/dm v1.8.12 // indirect
|
||||
github.com/BurntSushi/toml v1.6.0 // indirect
|
||||
github.com/ClickHouse/clickhouse-go/v2 v2.0.15 // indirect
|
||||
github.com/VictoriaMetrics/easyproto v0.1.4 // indirect
|
||||
github.com/bytedance/gopkg v0.1.4 // indirect
|
||||
github.com/bytedance/sonic v1.15.0 // indirect
|
||||
github.com/bytedance/sonic/loader v0.5.1 // indirect
|
||||
|
|
@ -43,66 +32,95 @@ require (
|
|||
github.com/clipperhouse/displaywidth v0.10.0 // indirect
|
||||
github.com/clipperhouse/uax29/v2 v2.6.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.6 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/denisenkom/go-mssqldb v0.12.3 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/emirpasic/gods/v2 v2.0.0-alpha // indirect
|
||||
github.com/fatih/color v1.19.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.13 // indirect
|
||||
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||
github.com/glebarez/sqlite v1.11.0 // indirect
|
||||
github.com/glebarez/go-sqlite v1.22.0 // indirect
|
||||
github.com/go-logfmt/logfmt v0.6.0 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.30.1 // indirect
|
||||
github.com/go-sql-driver/mysql v1.9.3 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
|
||||
github.com/goccy/go-json v0.10.6 // indirect
|
||||
github.com/goccy/go-yaml v1.19.2 // indirect
|
||||
github.com/godror/godror v0.50.0 // indirect
|
||||
github.com/godror/knownpb v0.3.0 // indirect
|
||||
github.com/gogf/gf/contrib/drivers/clickhouse/v2 v2.10.0 // indirect
|
||||
github.com/gogf/gf/contrib/drivers/dm/v2 v2.10.0 // indirect
|
||||
github.com/gogf/gf/contrib/drivers/mariadb/v2 v2.10.0 // indirect
|
||||
github.com/gogf/gf/contrib/drivers/mssql/v2 v2.10.0 // indirect
|
||||
github.com/gogf/gf/contrib/drivers/mysql/v2 v2.10.0 // indirect
|
||||
github.com/gogf/gf/contrib/drivers/oracle/v2 v2.10.0 // indirect
|
||||
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.10.0 // indirect
|
||||
github.com/gogf/gf/contrib/drivers/sqlite/v2 v2.10.0 // indirect
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
||||
github.com/golang-sql/sqlexp v0.1.0 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/grokify/html-strip-tags-go v0.1.0 // indirect
|
||||
github.com/ibmdb/go_ibm_db v0.5.4 // indirect
|
||||
github.com/ibmruntimes/go-recordio/v2 v2.0.0-20240416213906-ae0ad556db70 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.17.6 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/lib/pq v1.12.0 // indirect
|
||||
github.com/magiconair/properties v1.8.10 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.21 // indirect
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.38 // indirect
|
||||
github.com/microsoft/go-mssqldb v1.7.1 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/montanaflynn/stats v0.7.1 // indirect
|
||||
github.com/ncruces/go-strftime v1.0.0 // indirect
|
||||
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect
|
||||
github.com/olekukonko/errors v1.2.0 // indirect
|
||||
github.com/olekukonko/ll v0.1.8 // indirect
|
||||
github.com/olekukonko/tablewriter v1.1.4 // indirect
|
||||
github.com/paulmach/orb v0.7.1 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.3.0 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.14 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/quic-go/qpack v0.6.0 // indirect
|
||||
github.com/quic-go/quic-go v0.59.0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/sagikazarmark/locafero v0.12.0 // indirect
|
||||
github.com/shopspring/decimal v1.3.1 // indirect
|
||||
github.com/sijms/go-ora/v2 v2.7.10 // indirect
|
||||
github.com/spf13/afero v1.15.0 // indirect
|
||||
github.com/spf13/cast v1.10.0 // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.3.1 // indirect
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||
github.com/xdg-go/scram v1.2.0 // indirect
|
||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||
go.mongodb.org/mongo-driver v1.17.9 // indirect
|
||||
go.mongodb.org/mongo-driver/v2 v2.5.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/otel v1.42.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.42.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.42.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.42.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/arch v0.25.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa // indirect
|
||||
golang.org/x/net v0.52.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
golang.org/x/sys v0.42.0 // indirect
|
||||
golang.org/x/term v0.41.0 // indirect
|
||||
golang.org/x/text v0.35.0 // indirect
|
||||
golang.org/x/tools v0.43.0 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
modernc.org/libc v1.70.0 // indirect
|
||||
|
|
|
|||
154
go.sum
154
go.sum
|
|
@ -1,18 +1,18 @@
|
|||
filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo=
|
||||
filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc=
|
||||
git.magicany.cc/black1552/gf-common v1.0.1018 h1:57w/5ha1+EkZ/UC+0Bxwd8GJikb0qr2+h5C3mcYdUcI=
|
||||
git.magicany.cc/black1552/gf-common v1.0.1017 h1:KP0e32CSOzIYg8Nfqj7zwGakO6Co9HYTuiDZupK7LsU=
|
||||
git.magicany.cc/black1552/gf-common v1.0.1017/go.mod h1:ln6bd5oXxPNsktr8xI3itmsqpVBn1j+4W7iaS0g7S0Q=
|
||||
git.magicany.cc/black1552/gf-common v1.0.1018/go.mod h1:ln6bd5oXxPNsktr8xI3itmsqpVBn1j+4W7iaS0g7S0Q=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1 h1:lGlwhPtrX6EVml1hO0ivjkUxsSyl4dsiw9qcA1k/3IQ=
|
||||
gitee.com/chunanyong/dm v1.8.12 h1:WupbFZL0MRNIIiCPaLDHgFi5jkdkjzjPReuWPaInGwk=
|
||||
gitee.com/chunanyong/dm v1.8.12/go.mod h1:EPRJnuPFgbyOFgJ0TRYCTGzhq+ZT4wdyaj/GW/LLcNg=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1/go.mod h1:RKUqNu35KJYcVG/fqTRqmuXJZYNhYkBrnC/hX7yGbTA=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 h1:sO0/P7g68FrryJzljemN+6GTssUXdANk6aJ7T1ZxnsQ=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1/go.mod h1:h8hyGFDsU5HMivxiS2iYFZsgDbU9OnnJ163x5UGVKYo=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1 h1:6oNBlSdi1QqM1PNW7FPA6xOGA5UNsXnkaYZz9vdPGhA=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1/go.mod h1:s4kgfzA0covAXNicZHDMN58jExvcng2mC/DepXiF1EI=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1 h1:MyVTgWR8qd/Jw1Le0NZebGBUCLbtak3bJ3z1OlqZBpw=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1/go.mod h1:GpPjLhVR9dnUoJMyHWSPy71xY9/lcmpzIPZXmF0FCVY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0 h1:D3occbWoio4EBLkbkevetNMAVX197GkzbUMtqjGWn80=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0/go.mod h1:bTSOgj05NGRuHHhQwAdPnYr9TOdNmKlZTgGLL6nyAdI=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1 h1:DzHpqpoJVaCgOUdVHxE8QB52S6NiVdDQvGlny1qvPqA=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
||||
github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
|
||||
github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
|
|
@ -20,6 +20,9 @@ github.com/ClickHouse/clickhouse-go v1.5.4/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHg
|
|||
github.com/ClickHouse/clickhouse-go/v2 v2.0.15 h1:lLAZliqrZEygkxosLaW1qHyeTb4Ho7fVCZ0WKCpLocU=
|
||||
github.com/ClickHouse/clickhouse-go/v2 v2.0.15/go.mod h1:Z21o82zD8FFqefOQDg93c0XITlxGbTsWQuRm588Azkk=
|
||||
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||
github.com/UNO-SOFT/zlog v0.8.1/go.mod h1:yqFOjn3OhvJ4j7ArJqQNA+9V+u6t9zSAyIZdWdMweWc=
|
||||
github.com/VictoriaMetrics/easyproto v0.1.4 h1:r8cNvo8o6sR4QShBXQd1bKw/VVLSQma/V2KhTBPf+Sc=
|
||||
github.com/VictoriaMetrics/easyproto v0.1.4/go.mod h1:QlGlzaJnDfFd8Lk6Ci/fuLxfTo3/GThPs2KH23mv710=
|
||||
github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4=
|
||||
github.com/bytedance/gopkg v0.1.4 h1:oZnQwnX82KAIWb7033bEwtxvTqXcYMxDBaQxo5JJHWM=
|
||||
github.com/bytedance/gopkg v0.1.4/go.mod h1:v1zWfPm21Fb+OsyXN2VAHdL6TBb2L88anLQgdyje6R4=
|
||||
|
|
@ -29,20 +32,25 @@ github.com/bytedance/sonic/loader v0.5.1 h1:Ygpfa9zwRCCKSlrp5bBP/b/Xzc3VxsAW+5NI
|
|||
github.com/bytedance/sonic/loader v0.5.1/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chengxilo/virtualterm v1.0.4 h1:Z6IpERbRVlfB8WkOmtbHiDbBANU7cimRIof7mk9/PwM=
|
||||
github.com/chengxilo/virtualterm v1.0.4/go.mod h1:DyxxBZz/x1iqJjFxTFcr6/x+jSpqN0iwWCOK1q10rlY=
|
||||
github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME=
|
||||
github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
|
||||
github.com/clipperhouse/displaywidth v0.10.0 h1:GhBG8WuerxjFQQYeuZAeVTuyxuX+UraiZGD4HJQ3Y8g=
|
||||
github.com/clipperhouse/displaywidth v0.10.0/go.mod h1:XqJajYsaiEwkxOj4bowCTMcT1SgvHo9flfF3jQasdbs=
|
||||
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
|
||||
github.com/clipperhouse/uax29/v2 v2.6.0 h1:z0cDbUV+aPASdFb2/ndFnS9ts/WNXgTNNGFoKXuhpos=
|
||||
github.com/clipperhouse/uax29/v2 v2.6.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
|
||||
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80=
|
||||
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
|
||||
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/denisenkom/go-mssqldb v0.12.3 h1:pBSGx9Tq67pBOTLmxNuirNTeB8Vjmf886Kx+8Y+8shw=
|
||||
github.com/denisenkom/go-mssqldb v0.12.3/go.mod h1:k0mtMFOnU+AihqFxPMiF05rtiDrorD1Vrm1KEz5hxDo=
|
||||
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
|
||||
github.com/duke-git/lancet/v2 v2.3.7/go.mod h1:zGa2R4xswg6EG9I6WnyubDbFO/+A/RROxIbXcwryTsc=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/eclipse/paho.mqtt.golang v1.5.1 h1:/VSOv3oDLlpqR2Epjn1Q7b2bSTplJIeV2ISgCl2W7nE=
|
||||
|
|
@ -65,6 +73,8 @@ github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec
|
|||
github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc=
|
||||
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
|
||||
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
|
||||
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.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
|
|
@ -90,16 +100,45 @@ github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
|
|||
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
|
||||
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
github.com/godror/godror v0.50.0 h1:c0ZnGSDFT12E8HJfQwxtqcmybaIkbqACNk4lIfkkESc=
|
||||
github.com/godror/godror v0.50.0/go.mod h1:kTMcxZzRw73RT5kn9v3JkBK4kHI6dqowHotqV72ebU8=
|
||||
github.com/godror/knownpb v0.3.0 h1:+caUdy8hTtl7X05aPl3tdL540TvCcaQA6woZQroLZMw=
|
||||
github.com/godror/knownpb v0.3.0/go.mod h1:PpTyfJwiOEAzQl7NtVCM8kdPCnp3uhxsZYIzZ5PV4zU=
|
||||
github.com/gogf/gf/contrib/drivers/clickhouse/v2 v2.10.0 h1:9PTchr92xIJej4tq5c+HOHSU7LGOHr3YfD7tuf23LW4=
|
||||
github.com/gogf/gf/contrib/drivers/clickhouse/v2 v2.10.0/go.mod h1:eKtLMs9uccxFvmoKOUCRQ/Se3nxhzEZwF0Ir13qbk5g=
|
||||
github.com/gogf/gf/contrib/drivers/dm/v2 v2.10.0 h1:w4vPUIdPRLM3zxi0QGyP5YgdTy6fV9L0QOk9bWk5tig=
|
||||
github.com/gogf/gf/contrib/drivers/dm/v2 v2.10.0/go.mod h1:cWfwD+df3eeRDx4B+Rsk2Qe5ljNlBeS3rY2iwpPvPKU=
|
||||
github.com/gogf/gf/contrib/drivers/mariadb/v2 v2.10.0 h1:Dmg8d2sfX2AxETLFB9C0pj3ZjZSziOk6hN/78A7P9AA=
|
||||
github.com/gogf/gf/contrib/drivers/mariadb/v2 v2.10.0/go.mod h1:W8L0w4cDyvZddYWJsZ5ys2mxOls5+u4zut/9tPcT0T4=
|
||||
github.com/gogf/gf/contrib/drivers/mssql/v2 v2.10.0 h1:mBs6XpNM34IdZPZv4Kv3LA8yhP2UisbONMLfnQVFvKM=
|
||||
github.com/gogf/gf/contrib/drivers/mssql/v2 v2.10.0/go.mod h1:mChbF9FrmiYMSE2rG3zdxI/oSTwaHsR5KbINAgt3KcY=
|
||||
github.com/gogf/gf/contrib/drivers/mysql/v2 v2.10.0 h1:UvqxwinkelKxwdwnKUfdy51/ls4RL7MCeJqAZOVAy0I=
|
||||
github.com/gogf/gf/contrib/drivers/mysql/v2 v2.10.0/go.mod h1:6v7oGBF9wv59WERJIOJxXmLhkUcxwON3tPYW3AZ7wbY=
|
||||
github.com/gogf/gf/contrib/drivers/oracle/v2 v2.10.0 h1:MvhoMaz8YYj4WJuYzKGDdzJYiieiYiqp0vjoOshfOF4=
|
||||
github.com/gogf/gf/contrib/drivers/oracle/v2 v2.10.0/go.mod h1:vb2fx33RGhjhOaocOTEFvlEuBSGHss5S0lZ4sS3XK6E=
|
||||
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.10.0 h1:39+jbTenm7KBj4hO2C8ANAxVHpX/7OuRDs1VcGC9ylA=
|
||||
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.10.0/go.mod h1:B0s0fVzn0W220E8UTpSGzrrGKsop5KcB90twBeLCiz0=
|
||||
github.com/gogf/gf/contrib/drivers/sqlite/v2 v2.10.0 h1:OyAH7Ls2c9Un7CJiAq7G6eY1jWIICRkN8C5SyM94rnY=
|
||||
github.com/gogf/gf/contrib/drivers/sqlite/v2 v2.10.0/go.mod h1:fwhAMG0qZpeHbbP2JE78rJRfV7eBbu9jXkxTMM1lwyo=
|
||||
github.com/gogf/gf/contrib/registry/etcd/v2 v2.10.0/go.mod h1:ezkHf1r7YxkFYis7Y1807D1tr+3nXolfL8IsY816Cuw=
|
||||
github.com/gogf/gf/contrib/registry/file/v2 v2.10.0/go.mod h1:ZzGWiTbQ9nAmDynrmzaRJlqYHD/F3MeMwCvHrkv/LP4=
|
||||
github.com/gogf/gf/contrib/rpc/grpcx/v2 v2.10.0/go.mod h1:zGZdjS08IqiSGhKfy69/qgcYKUYUL/VsOx5fuT++TVE=
|
||||
github.com/gogf/gf/v2 v2.10.0 h1:rzDROlyqGMe/eM6dCalSR8dZOuMIdLhmxKSH1DGhbFs=
|
||||
github.com/gogf/gf/v2 v2.10.0/go.mod h1:Svl1N+E8G/QshU2DUbh/3J/AJauqCgUnxHurXWR4Qx0=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
|
||||
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
|
|
@ -116,24 +155,38 @@ github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aN
|
|||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grokify/html-strip-tags-go v0.1.0 h1:03UrQLjAny8xci+R+qjCce/MYnpNXCtgzltlQbOBae4=
|
||||
github.com/grokify/html-strip-tags-go v0.1.0/go.mod h1:ZdzgfHEzAfz9X6Xe5eBLVblWIxXfYSQ40S/VKrAOGpc=
|
||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/ibmdb/go_ibm_db v0.5.4 h1:cveEOt1J2PoQivQdxIQB0f8ugDJYKaSmh7RUKAaJyAE=
|
||||
github.com/ibmdb/go_ibm_db v0.5.4/go.mod h1:BA12Alfe+h5BMGZGE+b0pqP4leILZkpoxe5qr/iMoHw=
|
||||
github.com/ibmruntimes/go-recordio/v2 v2.0.0-20240416213906-ae0ad556db70 h1:muF5XqVkHnMdbMDXusPdKtuT8qWzefBgSuLH1JVHcC4=
|
||||
github.com/ibmruntimes/go-recordio/v2 v2.0.0-20240416213906-ae0ad556db70/go.mod h1:NSpUK0x9IyEoM1EjTp2/S8ErxZfRHoA2DfwiYobFSkc=
|
||||
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
|
||||
github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=
|
||||
github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo=
|
||||
github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg=
|
||||
github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=
|
||||
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
|
||||
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/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
|
||||
github.com/jordanlewis/gcassert v0.0.0-20250430164644-389ef753e22e/go.mod h1:ZybsQk6DWyN5t7An1MuPm1gtSZ1xDaTXS9ZjIOxvQrk=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI=
|
||||
github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
|
|
@ -153,16 +206,19 @@ github.com/mattn/go-sqlite3 v1.14.38 h1:tDUzL85kMvOrvpCt8P64SbGgVFtJB11GPi2AdmIT
|
|||
github.com/mattn/go-sqlite3 v1.14.38/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/microsoft/go-mssqldb v1.7.1 h1:KU/g8aWeM3Hx7IMOFpiwYiUkU+9zeISb4+tx3ScVfsM=
|
||||
github.com/microsoft/go-mssqldb v1.7.1/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA=
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
|
||||
github.com/mkevac/debugcharts v0.0.0-20191222103121-ae1c48aa8615/go.mod h1:Ad7oeElCZqA1Ufj0U9/liOF4BtVepxRcTvr2ey7zTvM=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
|
||||
github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
|
||||
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
|
||||
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
|
||||
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||
github.com/oklog/ulid/v2 v2.0.2/go.mod h1:mtBL0Qe/0HAx6/a4Z30qxVIAL1eQDweXq5lxOEiwQ68=
|
||||
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj4EljqMiZsIcE09mmF8XsD5AYOJc=
|
||||
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0=
|
||||
github.com/olekukonko/errors v1.2.0 h1:10Zcn4GeV59t/EGqJc8fUjtFT/FuUh5bTMzZ1XwmCRo=
|
||||
|
|
@ -171,6 +227,7 @@ github.com/olekukonko/ll v0.1.8 h1:ysHCJRGHYKzmBSdz9w5AySztx7lG8SQY+naTGYUbsz8=
|
|||
github.com/olekukonko/ll v0.1.8/go.mod h1:RPRC6UcscfFZgjo1nulkfMH5IM0QAYim0LfnMvUuozw=
|
||||
github.com/olekukonko/tablewriter v1.1.4 h1:ORUMI3dXbMnRlRggJX3+q7OzQFDdvgbN9nVWj1drm6I=
|
||||
github.com/olekukonko/tablewriter v1.1.4/go.mod h1:+kedxuyTtgoZLwif3P1Em4hARJs+mVnzKxmsCL/C5RY=
|
||||
github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0/go.mod h1:F/7q8/HZz+TXjlsoZQQKVYvXTZaFH4QRa3y+j1p7MS0=
|
||||
github.com/paulmach/orb v0.7.1 h1:Zha++Z5OX/l168sqHK3k4z18LDvr+YAO/VjK0ReQ9rU=
|
||||
github.com/paulmach/orb v0.7.1/go.mod h1:FWRlTgl88VI1RBx/MkrwWDRhQ96ctqMCh8boXhmqB/A=
|
||||
github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY=
|
||||
|
|
@ -179,8 +236,10 @@ github.com/pelletier/go-toml/v2 v2.3.0/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8
|
|||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
|
||||
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/planetscale/vtprotobuf v0.6.0/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
|
||||
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/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
|
||||
|
|
@ -189,14 +248,13 @@ github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SA
|
|||
github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk=
|
||||
github.com/richardlehane/msoleps v1.0.4/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4=
|
||||
github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI=
|
||||
github.com/schollz/progressbar/v3 v3.19.0 h1:Ea18xuIRQXLAUidVDox3AbwfUhD0/1IvohyTutOIFoc=
|
||||
github.com/schollz/progressbar/v3 v3.19.0/go.mod h1:IsO3lpbaGuzh8zIMzgY3+J8l4C8GjO0Y9S69eFvNsec=
|
||||
github.com/shirou/gopsutil v2.19.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
|
||||
|
|
@ -204,6 +262,7 @@ github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5g
|
|||
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/sijms/go-ora/v2 v2.7.10 h1:GSLdj0PYYgSndhsnm7b6p32OqgnwnUZSkFb3j+htfhI=
|
||||
github.com/sijms/go-ora/v2 v2.7.10/go.mod h1:EHxlY6x7y9HAsdfumurRfTd+v8NrEOTR3Xl4FWlH6xk=
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
|
||||
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
|
||||
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
||||
|
|
@ -218,6 +277,7 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE
|
|||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
|
|
@ -227,15 +287,33 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
|
|||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
github.com/tiendc/go-deepcopy v1.6.0/go.mod h1:toXoeQoUqXOOS/X4sKuiAoSk6elIdqc0pN7MTgOOo2I=
|
||||
github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
|
||||
github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
|
||||
github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||
github.com/xdg-go/scram v1.2.0 h1:bYKF2AEwG5rqd1BumT4gAnvwU/M9nBp2pTSxeZw7Wvs=
|
||||
github.com/xdg-go/scram v1.2.0/go.mod h1:3dlrS0iBaWKYVt2ZfA4cj48umJZ+cAEbR6/SjLA88I8=
|
||||
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
|
||||
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
||||
github.com/xuri/efp v0.0.1/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
|
||||
github.com/xuri/excelize/v2 v2.9.1/go.mod h1:x7L6pKz2dvo9ejrRuD8Lnl98z4JLt0TGAwjhW+EiP8s=
|
||||
github.com/xuri/nfp v0.0.1/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.etcd.io/etcd/api/v3 v3.5.17/go.mod h1:d1hvkRuXkts6PmaYk2Vrgqbv7H4ADfAKhyJqHNLJCB4=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.17/go.mod h1:4DqK1TKacp/86nJk4FLQqo6Mn2vvQFBmruW3pP14H/w=
|
||||
go.etcd.io/etcd/client/v3 v3.5.17/go.mod h1:j2d4eXTHWkT2ClBgnnEPm/Wuu7jsqku41v9DZ3OtjQo=
|
||||
go.mongodb.org/mongo-driver v1.17.9 h1:IexDdCuuNJ3BHrELgBlyaH9p60JXAvdzWR128q+U5tU=
|
||||
go.mongodb.org/mongo-driver v1.17.9/go.mod h1:LlOhpH5NUEfhxcAwG0UEkMqwYcc4JU18gtCdGudk/tQ=
|
||||
go.mongodb.org/mongo-driver/v2 v2.5.0 h1:yXUhImUjjAInNcpTcAlPHiT7bIXhshCTL3jVBkF3xaE=
|
||||
go.mongodb.org/mongo-driver/v2 v2.5.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
|
|
@ -252,10 +330,13 @@ go.opentelemetry.io/otel/sdk/metric v1.42.0/go.mod h1:Ua6AAlDKdZ7tdvaQKfSmnFTdHx
|
|||
go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU=
|
||||
go.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4LenLmOYY=
|
||||
go.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
|
||||
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/arch v0.25.0 h1:qnk6Ksugpi5Bz32947rkUgDt9/s5qvqDPl/gBKdMJLE=
|
||||
|
|
@ -263,21 +344,32 @@ golang.org/x/arch v0.25.0/go.mod h1:0X+GdSIP+kL5wPmpK7sdkEVTt2XoYP0cSjQSbZBwOi8=
|
|||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
|
||||
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
|
||||
golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa h1:Zt3DZoOFFYkKhDT3v7Lm9FDMEV06GpzjG2jrqW+QTE0=
|
||||
golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
|
||||
golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
||||
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
|
@ -285,27 +377,45 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191220220014-0732a990476f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220429233432-b5fbb4746d32/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU=
|
||||
golang.org/x/telemetry v0.0.0-20260311193753-579e4da9a98c/go.mod h1:TpUTTEp9frx7rTdLpC9gFG9kdI7zVLFTFFlqaH2Cncw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
|
||||
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s=
|
||||
golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0=
|
||||
golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY=
|
||||
golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250804133106-a7a43d27e69b/go.mod h1:oDOGiMSXHL4sDTJvFvIB9nRQCGdLP1o/iVaqQK8zB+M=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250804133106-a7a43d27e69b/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
|
|
@ -316,13 +426,22 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV
|
|||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
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/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8=
|
||||
gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg=
|
||||
gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
|
||||
lukechampine.com/uint128 v1.3.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||
modernc.org/cc/v3 v3.41.0/go.mod h1:Ni4zjJYJ04CDOhG7dn640WGfwBzfE0ecX8TyMB0Fv0Y=
|
||||
modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis=
|
||||
modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
modernc.org/ccgo/v3 v3.16.15/go.mod h1:yT7B+/E2m43tmMOT51GMoM98/MtHIcQQSleGnddkUNI=
|
||||
modernc.org/ccgo/v4 v4.32.0 h1:hjG66bI/kqIPX1b2yT6fr/jt+QedtP2fqojG2VrFuVw=
|
||||
modernc.org/ccgo/v4 v4.32.0/go.mod h1:6F08EBCx5uQc38kMGl+0Nm0oWczoo1c7cgpzEry7Uc0=
|
||||
modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM=
|
||||
|
|
@ -349,3 +468,4 @@ modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
|||
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
|
|
|
|||
|
|
@ -1,397 +0,0 @@
|
|||
package session
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// SessionManager Session管理器,负责Session的创建、获取和销毁
|
||||
type SessionManager struct {
|
||||
sessions map[string]*Session // 存储所有活跃的Session,key为SessionID
|
||||
mutex sync.RWMutex // 读写锁,保证并发安全
|
||||
maxAge time.Duration // Session最大存活时间
|
||||
}
|
||||
|
||||
// Session 单个Session对象,用于存储用户数据
|
||||
type Session struct {
|
||||
ID string // Session唯一标识符
|
||||
Data map[string]interface{} // 存储的数据,键值对形式
|
||||
CreateTime time.Time // 创建时间
|
||||
LastAccess time.Time // 最后访问时间
|
||||
mutex sync.RWMutex // 读写锁,保证并发安全
|
||||
}
|
||||
|
||||
var (
|
||||
// defaultManager 默认的Session管理器实例
|
||||
defaultManager *SessionManager
|
||||
// once 确保只初始化一次
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
// init 包初始化时自动创建默认管理器
|
||||
func init() {
|
||||
InitDefaultManager(30 * time.Minute) // 默认30分钟过期
|
||||
}
|
||||
|
||||
// InitDefaultManager 初始化默认Session管理器
|
||||
// 参数 duration: Session的最大存活时间
|
||||
func InitDefaultManager(duration time.Duration) {
|
||||
once.Do(func() {
|
||||
defaultManager = NewSessionManager(duration)
|
||||
})
|
||||
}
|
||||
|
||||
// NewSessionManager 创建一个新的Session管理器
|
||||
// 参数 duration: Session的最大存活时间
|
||||
// 返回值: Session管理器指针
|
||||
func NewSessionManager(duration time.Duration) *SessionManager {
|
||||
sm := &SessionManager{
|
||||
sessions: make(map[string]*Session),
|
||||
maxAge: duration,
|
||||
}
|
||||
|
||||
// 启动定时清理任务,每5分钟清理一次过期的Session
|
||||
go sm.startCleanupTicker(5 * time.Minute)
|
||||
|
||||
return sm
|
||||
}
|
||||
|
||||
// startCleanupTicker 启动定时清理过期Session的任务
|
||||
// 参数 interval: 清理间隔时间
|
||||
func (sm *SessionManager) startCleanupTicker(interval time.Duration) {
|
||||
ticker := time.NewTicker(interval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for range ticker.C {
|
||||
sm.CleanupExpiredSessions()
|
||||
}
|
||||
}
|
||||
|
||||
// CreateSession 创建新的Session并返回Session ID
|
||||
// 参数 c: Gin上下文对象
|
||||
// 返回值: Session ID字符串
|
||||
func (sm *SessionManager) CreateSession(c *gin.Context) string {
|
||||
sessionID := uuid.New().String()
|
||||
|
||||
session := &Session{
|
||||
ID: sessionID,
|
||||
Data: make(map[string]interface{}),
|
||||
CreateTime: time.Now(),
|
||||
LastAccess: time.Now(),
|
||||
}
|
||||
|
||||
sm.mutex.Lock()
|
||||
sm.sessions[sessionID] = session
|
||||
sm.mutex.Unlock()
|
||||
|
||||
// 设置Cookie,保存Session ID
|
||||
c.SetCookie(
|
||||
"session_id", // Cookie名称
|
||||
sessionID, // Cookie值
|
||||
int(sm.maxAge.Seconds()), // 过期时间(秒)
|
||||
"/", // 路径
|
||||
"", // 域名(空表示当前域名)
|
||||
false, // 是否仅HTTPS
|
||||
true, // 是否HttpOnly(防止XSS攻击)
|
||||
)
|
||||
|
||||
return sessionID
|
||||
}
|
||||
|
||||
// GetSession 根据Gin上下文获取Session对象
|
||||
// 参数 c: Gin上下文对象
|
||||
// 返回值: Session对象指针,如果不存在则返回nil
|
||||
func (sm *SessionManager) GetSession(c *gin.Context) *Session {
|
||||
sessionID, err := c.Cookie("session_id")
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
sm.mutex.RLock()
|
||||
session, exists := sm.sessions[sessionID]
|
||||
sm.mutex.RUnlock()
|
||||
|
||||
if !exists {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 检查Session是否过期
|
||||
if time.Since(session.LastAccess) > sm.maxAge {
|
||||
sm.DestroySession(sessionID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 更新最后访问时间
|
||||
session.mutex.Lock()
|
||||
session.LastAccess = time.Now()
|
||||
session.mutex.Unlock()
|
||||
|
||||
return session
|
||||
}
|
||||
|
||||
// GetSessionByID 根据Session ID获取Session对象
|
||||
// 参数 sessionID: Session的唯一标识符
|
||||
// 返回值: Session对象指针,如果不存在则返回nil
|
||||
func (sm *SessionManager) GetSessionByID(sessionID string) *Session {
|
||||
sm.mutex.RLock()
|
||||
session, exists := sm.sessions[sessionID]
|
||||
sm.mutex.RUnlock()
|
||||
|
||||
if !exists {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 检查Session是否过期
|
||||
if time.Since(session.LastAccess) > sm.maxAge {
|
||||
sm.DestroySession(sessionID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 更新最后访问时间
|
||||
session.mutex.Lock()
|
||||
session.LastAccess = time.Now()
|
||||
session.mutex.Unlock()
|
||||
|
||||
return session
|
||||
}
|
||||
|
||||
// DestroySession 销毁指定的Session
|
||||
// 参数 sessionID: Session的唯一标识符
|
||||
func (sm *SessionManager) DestroySession(sessionID string) {
|
||||
sm.mutex.Lock()
|
||||
delete(sm.sessions, sessionID)
|
||||
sm.mutex.Unlock()
|
||||
}
|
||||
|
||||
// DestroySessionByContext 根据Gin上下文销毁Session
|
||||
// 参数 c: Gin上下文对象
|
||||
func (sm *SessionManager) DestroySessionByContext(c *gin.Context) {
|
||||
sessionID, err := c.Cookie("session_id")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
sm.DestroySession(sessionID)
|
||||
|
||||
// 清除Cookie
|
||||
c.SetCookie(
|
||||
"session_id",
|
||||
"",
|
||||
-1, // 立即过期
|
||||
"/",
|
||||
"",
|
||||
false,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
// CleanupExpiredSessions 清理所有过期的Session
|
||||
func (sm *SessionManager) CleanupExpiredSessions() {
|
||||
now := time.Now()
|
||||
expiredIDs := make([]string, 0)
|
||||
|
||||
sm.mutex.RLock()
|
||||
for id, session := range sm.sessions {
|
||||
session.mutex.RLock()
|
||||
if now.Sub(session.LastAccess) > sm.maxAge {
|
||||
expiredIDs = append(expiredIDs, id)
|
||||
}
|
||||
session.mutex.RUnlock()
|
||||
}
|
||||
sm.mutex.RUnlock()
|
||||
|
||||
// 删除过期的Session
|
||||
sm.mutex.Lock()
|
||||
for _, id := range expiredIDs {
|
||||
delete(sm.sessions, id)
|
||||
}
|
||||
sm.mutex.Unlock()
|
||||
}
|
||||
|
||||
// GetSessionCount 获取当前活跃的Session数量
|
||||
// 返回值: Session数量
|
||||
func (sm *SessionManager) GetSessionCount() int {
|
||||
sm.mutex.RLock()
|
||||
defer sm.mutex.RUnlock()
|
||||
return len(sm.sessions)
|
||||
}
|
||||
|
||||
// Set 在Session中设置键值对
|
||||
// 参数 key: 键名
|
||||
// 参数 value: 值
|
||||
func (s *Session) Set(key string, value interface{}) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
s.Data[key] = value
|
||||
}
|
||||
|
||||
// Get 从Session中获取值
|
||||
// 参数 key: 键名
|
||||
// 返回值: 对应的值,如果不存在则返回nil
|
||||
func (s *Session) Get(key string) interface{} {
|
||||
s.mutex.RLock()
|
||||
defer s.mutex.RUnlock()
|
||||
return s.Data[key]
|
||||
}
|
||||
|
||||
// GetString 从Session中获取字符串类型的值
|
||||
// 参数 key: 键名
|
||||
// 返回值: 字符串值,如果不存在或类型不匹配则返回空字符串
|
||||
func (s *Session) GetString(key string) string {
|
||||
s.mutex.RLock()
|
||||
defer s.mutex.RUnlock()
|
||||
if val, ok := s.Data[key]; ok {
|
||||
if str, ok := val.(string); ok {
|
||||
return str
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetInt 从Session中获取整数类型的值
|
||||
// 参数 key: 键名
|
||||
// 返回值: 整数值,如果不存在或类型不匹配则返回0
|
||||
func (s *Session) GetInt(key string) int {
|
||||
s.mutex.RLock()
|
||||
defer s.mutex.RUnlock()
|
||||
if val, ok := s.Data[key]; ok {
|
||||
switch v := val.(type) {
|
||||
case int:
|
||||
return v
|
||||
case float64: // JSON解码时数字会变成float64
|
||||
return int(v)
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetFloat64 从Session中获取浮点数类型的值
|
||||
// 参数 key: 键名
|
||||
// 返回值: 浮点数值,如果不存在或类型不匹配则返回0
|
||||
func (s *Session) GetFloat64(key string) float64 {
|
||||
s.mutex.RLock()
|
||||
defer s.mutex.RUnlock()
|
||||
if val, ok := s.Data[key]; ok {
|
||||
switch v := val.(type) {
|
||||
case float64:
|
||||
return v
|
||||
case int:
|
||||
return float64(v)
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetBool 从Session中获取布尔类型的值
|
||||
// 参数 key: 键名
|
||||
// 返回值: 布尔值,如果不存在或类型不匹配则返回false
|
||||
func (s *Session) GetBool(key string) bool {
|
||||
s.mutex.RLock()
|
||||
defer s.mutex.RUnlock()
|
||||
if val, ok := s.Data[key]; ok {
|
||||
if b, ok := val.(bool); ok {
|
||||
return b
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Delete 从Session中删除指定的键
|
||||
// 参数 key: 要删除的键名
|
||||
func (s *Session) Delete(key string) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
delete(s.Data, key)
|
||||
}
|
||||
|
||||
// Clear 清空Session中的所有数据
|
||||
func (s *Session) Clear() {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
s.Data = make(map[string]interface{})
|
||||
}
|
||||
|
||||
// Has 检查Session中是否存在指定的键
|
||||
// 参数 key: 键名
|
||||
// 返回值: 如果存在返回true,否则返回false
|
||||
func (s *Session) Has(key string) bool {
|
||||
s.mutex.RLock()
|
||||
defer s.mutex.RUnlock()
|
||||
_, exists := s.Data[key]
|
||||
return exists
|
||||
}
|
||||
|
||||
// GetAll 获取Session中的所有数据
|
||||
// 返回值: 包含所有数据的map副本
|
||||
func (s *Session) GetAll() map[string]interface{} {
|
||||
s.mutex.RLock()
|
||||
defer s.mutex.RUnlock()
|
||||
|
||||
// 返回副本,避免外部修改
|
||||
result := make(map[string]interface{}, len(s.Data))
|
||||
for k, v := range s.Data {
|
||||
result[k] = v
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// ToJSON 将Session数据转换为JSON字符串
|
||||
// 返回值: JSON字符串,如果转换失败则返回错误
|
||||
func (s *Session) ToJSON() (string, error) {
|
||||
s.mutex.RLock()
|
||||
defer s.mutex.RUnlock()
|
||||
|
||||
data, err := json.Marshal(s.Data)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("序列化Session数据失败: %w", err)
|
||||
}
|
||||
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
// FromJSON 从JSON字符串恢复Session数据
|
||||
// 参数 jsonStr: JSON字符串
|
||||
// 返回值: 如果解析失败则返回错误
|
||||
func (s *Session) FromJSON(jsonStr string) error {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
var data map[string]interface{}
|
||||
if err := json.Unmarshal([]byte(jsonStr), &data); err != nil {
|
||||
return fmt.Errorf("反序列化Session数据失败: %w", err)
|
||||
}
|
||||
|
||||
s.Data = data
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetDefaultManager 获取默认的Session管理器
|
||||
// 返回值: 默认Session管理器指针
|
||||
func GetDefaultManager() *SessionManager {
|
||||
return defaultManager
|
||||
}
|
||||
|
||||
// CreateSession 使用默认管理器创建Session
|
||||
// 参数 c: Gin上下文对象
|
||||
// 返回值: Session ID字符串
|
||||
func CreateSession(c *gin.Context) string {
|
||||
return defaultManager.CreateSession(c)
|
||||
}
|
||||
|
||||
// GetSession 使用默认管理器获取Session
|
||||
// 参数 c: Gin上下文对象
|
||||
// 返回值: Session对象指针
|
||||
func GetSession(c *gin.Context) *Session {
|
||||
return defaultManager.GetSession(c)
|
||||
}
|
||||
|
||||
// DestroySession 使用默认管理器销毁Session
|
||||
// 参数 c: Gin上下文对象
|
||||
func DestroySession(c *gin.Context) {
|
||||
defaultManager.DestroySessionByContext(c)
|
||||
}
|
||||
Loading…
Reference in New Issue