package orm
import (
)
const (
beforeScanHookFlag = uint16(1) << iota
afterScanHookFlag
afterSelectHookFlag
beforeInsertHookFlag
afterInsertHookFlag
beforeUpdateHookFlag
afterUpdateHookFlag
beforeDeleteHookFlag
afterDeleteHookFlag
discardUnknownColumnsFlag
)
var (
timeType = reflect.TypeOf((*time.Time)(nil)).Elem()
nullTimeType = reflect.TypeOf((*types.NullTime)(nil)).Elem()
sqlNullTimeType = reflect.TypeOf((*sql.NullTime)(nil)).Elem()
ipType = reflect.TypeOf((*net.IP)(nil)).Elem()
ipNetType = reflect.TypeOf((*net.IPNet)(nil)).Elem()
scannerType = reflect.TypeOf((*sql.Scanner)(nil)).Elem()
nullBoolType = reflect.TypeOf((*sql.NullBool)(nil)).Elem()
nullFloatType = reflect.TypeOf((*sql.NullFloat64)(nil)).Elem()
nullIntType = reflect.TypeOf((*sql.NullInt64)(nil)).Elem()
nullStringType = reflect.TypeOf((*sql.NullString)(nil)).Elem()
jsonRawMessageType = reflect.TypeOf((*json.RawMessage)(nil)).Elem()
)
var tableNameInflector = inflection.Plural
func ( func(string) string) {
tableNameInflector =
}
type Table struct {
Type reflect.Type
zeroStruct reflect.Value
TypeName string
Alias types.Safe
ModelName string
SQLName types.Safe
SQLNameForSelects types.Safe
Tablespace types.Safe
PartitionBy string
allFields []*Field
skippedFields []*Field
Fields []*Field
PKs []*Field
DataFields []*Field
fieldsMapMu sync.RWMutex
FieldsMap map[string]*Field
Methods map[string]*Method
Relations map[string]*Relation
Unique map[string][]*Field
SoftDeleteField *Field
SetSoftDeleteField func(fv reflect.Value) error
flags uint16
}
func ( reflect.Type) *Table {
:= new(Table)
.Type =
.zeroStruct = reflect.New(.Type).Elem()
.TypeName = internal.ToExported(.Type.Name())
.ModelName = internal.Underscore(.Type.Name())
:= tableNameInflector(.ModelName)
.setName(quoteIdent())
.Alias = quoteIdent(.ModelName)
= reflect.PtrTo(.Type)
if .Implements(beforeScanHookType) {
.setFlag(beforeScanHookFlag)
}
if .Implements(afterScanHookType) {
.setFlag(afterScanHookFlag)
}
if .Implements(afterSelectHookType) {
.setFlag(afterSelectHookFlag)
}
if .Implements(beforeInsertHookType) {
.setFlag(beforeInsertHookFlag)
}
if .Implements(afterInsertHookType) {
.setFlag(afterInsertHookFlag)
}
if .Implements(beforeUpdateHookType) {
.setFlag(beforeUpdateHookFlag)
}
if .Implements(afterUpdateHookType) {
.setFlag(afterUpdateHookFlag)
}
if .Implements(beforeDeleteHookType) {
.setFlag(beforeDeleteHookFlag)
}
if .Implements(afterDeleteHookType) {
.setFlag(afterDeleteHookFlag)
}
return
}
func ( *Table) () {
.initFields()
.initMethods()
}
func ( *Table) () {
.initInlines()
.initRelations()
.skippedFields = nil
}
func ( *Table) ( types.Safe) {
.SQLName =
.SQLNameForSelects =
if .Alias == "" {
.Alias =
}
}
func ( *Table) () string {
return "model=" + .TypeName
}
func ( *Table) ( uint16) {
.flags |=
}
func ( *Table) ( uint16) bool {
if == nil {
return false
}
return .flags& != 0
}
func ( *Table) () error {
if len(.PKs) == 0 {
return fmt.Errorf("pg: %s does not have primary keys", )
}
return nil
}
func ( *Table) () error {
if .SoftDeleteField == nil {
return fmt.Errorf("pg: %s does not support soft deletes", )
}
return nil
}
func ( *Table) ( *Field) {
.Fields = append(.Fields, )
if .hasFlag(PrimaryKeyFlag) {
.PKs = append(.PKs, )
} else {
.DataFields = append(.DataFields, )
}
.FieldsMap[.SQLName] =
}
func ( *Table) ( *Field) {
.Fields = removeField(.Fields, )
if .hasFlag(PrimaryKeyFlag) {
.PKs = removeField(.PKs, )
} else {
.DataFields = removeField(.DataFields, )
}
delete(.FieldsMap, .SQLName)
}
func ( []*Field, *Field) []*Field {
for , := range {
if == {
= append([:], [+1:]...)
}
}
return
}
func ( *Table) ( string) *Field {
.fieldsMapMu.RLock()
:= .FieldsMap[]
.fieldsMapMu.RUnlock()
return
}
func ( *Table) ( string) bool {
, := .FieldsMap[]
return
}
func ( *Table) ( string) (*Field, error) {
, := .FieldsMap[]
if ! {
return nil, fmt.Errorf("pg: %s does not have column=%s", , )
}
return , nil
}
func ( *Table) ( []byte, reflect.Value, string) ([]byte, bool) {
, := .FieldsMap[]
if {
= .AppendValue(, , 1)
return , true
}
, := .Methods[]
if {
= .AppendValue(, .Addr(), 1)
return , true
}
return , false
}
func ( *Table) () {
.Fields = make([]*Field, 0, .Type.NumField())
.FieldsMap = make(map[string]*Field, .Type.NumField())
.addFields(.Type, nil)
}
func ( *Table) ( reflect.Type, []int) {
for := 0; < .NumField(); ++ {
:= .Field()
:= make([]int, len())
copy(, )
if .Anonymous {
if .Tag.Get("sql") == "-" || .Tag.Get("pg") == "-" {
continue
}
:= indirectType(.Type)
if .Kind() != reflect.Struct {
continue
}
.(, append(, .Index...))
:= tagparser.Parse(.Tag.Get("pg"))
if , := .Options["inherit"]; {
:= _tables.get(, true)
.TypeName = .TypeName
.SQLName = .SQLName
.SQLNameForSelects = .SQLNameForSelects
.Alias = .Alias
.ModelName = .ModelName
}
continue
}
:= .newField(, )
if != nil {
.AddField()
}
}
}
func ( *Table) ( reflect.StructField, []int) *Field {
:= tagparser.Parse(.Tag.Get("pg"))
switch .Name {
case "tableName":
if len() > 0 {
return nil
}
if isKnownTableOption(.Name) {
internal.Warn.Printf(
"%s.%s tag name %q is also an option name; is it a mistake?",
.TypeName, .Name, .Name,
)
}
for := range .Options {
if !isKnownTableOption() {
internal.Warn.Printf("%s.%s has unknown tag option: %q", .TypeName, .Name, )
}
}
if , := .Options["tablespace"]; {
, := tagparser.Unquote()
.Tablespace = quoteIdent()
}
, := .Options["partition_by"]
if ! {
, = .Options["partitionBy"]
if {
internal.Deprecated.Printf("partitionBy is renamed to partition_by")
}
}
if {
, := tagparser.Unquote()
.PartitionBy =
}
if .Name == "_" {
.setName("")
} else if .Name != "" {
, := tagparser.Unquote(.Name)
.setName(types.Safe(quoteTableName()))
}
if , := .Options["select"]; {
, _ = tagparser.Unquote()
.SQLNameForSelects = types.Safe(quoteTableName())
}
if , := .Options["alias"]; {
, _ = tagparser.Unquote()
.Alias = quoteIdent()
}
:= tagparser.Parse(.Tag.Get("pg"))
if , := .Options["discard_unknown_columns"]; {
.setFlag(discardUnknownColumnsFlag)
}
return nil
}
if .PkgPath != "" {
return nil
}
:= internal.Underscore(.Name)
if .Name != && isKnownFieldOption(.Name) {
internal.Warn.Printf(
"%s.%s tag name %q is also an option name; is it a mistake?",
.TypeName, .Name, .Name,
)
}
for := range .Options {
if !isKnownFieldOption() {
internal.Warn.Printf("%s.%s has unknown tag option: %q", .TypeName, .Name, )
}
}
:= .Name == "-"
if ! && .Name != "" {
= .Name
}
= append(, .Index...)
if := .getField(); != nil {
if indexEqual(.Index, ) {
return
}
.RemoveField()
}
:= &Field{
Field: ,
Type: indirectType(.Type),
GoName: .Name,
SQLName: ,
Column: quoteIdent(),
Index: ,
}
if , := .Options["notnull"]; {
.setFlag(NotNullFlag)
}
if , := .Options["unique"]; {
if == "" {
.setFlag(UniqueFlag)
}
, _ = tagparser.Unquote()
for , := range strings.Split(, ",") {
if .Unique == nil {
.Unique = make(map[string][]*Field)
}
.Unique[] = append(.Unique[], )
}
}
if , := .Options["default"]; {
, = tagparser.Unquote()
if {
.Default = types.Safe(types.AppendString(nil, , 1))
} else {
.Default = types.Safe()
}
}
if , := .Options["pk"]; {
.setFlag(PrimaryKeyFlag)
} else if strings.HasSuffix(.SQLName, "_id") ||
strings.HasSuffix(.SQLName, "_uuid") {
.setFlag(ForeignKeyFlag)
} else if strings.HasPrefix(.SQLName, "fk_") {
.setFlag(ForeignKeyFlag)
} else if len(.PKs) == 0 && !.HasOption("nopk") {
switch .SQLName {
case "id", "uuid", "pk_" + .ModelName:
.setFlag(PrimaryKeyFlag)
}
}
if , := .Options["use_zero"]; {
.setFlag(UseZeroFlag)
}
if , := .Options["array"]; {
.setFlag(ArrayFlag)
}
.SQLType = fieldSQLType(, )
if strings.HasSuffix(.SQLType, "[]") {
.setFlag(ArrayFlag)
}
if , := .Options["on_delete"]; {
.OnDelete =
}
if , := .Options["on_update"]; {
.OnUpdate =
}
if , := .Options["composite"]; {
.append = compositeAppender(.Type)
.scan = compositeScanner(.Type)
} else if , := .Options["json_use_number"]; {
.append = types.Appender(.Type)
.scan = scanJSONValue
} else if .hasFlag(ArrayFlag) {
.append = types.ArrayAppender(.Type)
.scan = types.ArrayScanner(.Type)
} else if , := .Options["hstore"]; {
.append = types.HstoreAppender(.Type)
.scan = types.HstoreScanner(.Type)
} else if .SQLType == pgTypeBigint && .Type.Kind() == reflect.Uint64 {
if .Type.Kind() == reflect.Ptr {
.append = appendUintPtrAsInt
} else {
.append = appendUintAsInt
}
.scan = types.Scanner(.Type)
} else if , := .Options["msgpack"]; {
.append = msgpackAppender(.Type)
.scan = msgpackScanner(.Type)
} else {
.append = types.Appender(.Type)
.scan = types.Scanner(.Type)
}
.isZero = zerochecker.Checker(.Type)
if , := .Options["alias"]; {
, _ = tagparser.Unquote()
.FieldsMap[] =
}
.allFields = append(.allFields, )
if {
.skippedFields = append(.skippedFields, )
.FieldsMap[.SQLName] =
return nil
}
if , := .Options["soft_delete"]; {
.SetSoftDeleteField = setSoftDeleteFieldFunc(.Type)
if .SetSoftDeleteField == nil {
:= fmt.Errorf(
"pg: soft_delete is only supported for time.Time, pg.NullTime, sql.NullInt64, and int64 (or implement ValueScanner that scans time)")
panic()
}
.SoftDeleteField =
}
return
}
func ( *Table) () {
.Methods = make(map[string]*Method)
:= reflect.PtrTo(.Type)
for := 0; < .NumMethod(); ++ {
:= .Method()
if .PkgPath != "" {
continue
}
if .Type.NumIn() > 1 {
continue
}
if .Type.NumOut() != 1 {
continue
}
:= .Type.Out(0)
.Methods[.Name] = &Method{
Index: .Index,
appender: types.Appender(),
}
}
}
func ( *Table) () {
for , := range .skippedFields {
if .Type.Kind() == reflect.Struct {
.inlineFields(, nil)
}
}
}
func ( *Table) () {
for := 0; < len(.Fields); {
:= .Fields[]
if .tryRelation() {
.Fields = removeField(.Fields, )
.DataFields = removeField(.DataFields, )
} else {
++
}
if .Type.Kind() == reflect.Struct {
.inlineFields(, nil)
}
}
}
func ( *Table) ( *Field) bool {
:= tagparser.Parse(.Field.Tag.Get("pg"))
if , := .Options["rel"]; {
return .tryRelationType(, , )
}
if , := .Options["many2many"]; {
return .tryRelationType(, "many2many", )
}
if .UserSQLType != "" || isScanner(.Type) {
return false
}
switch .Type.Kind() {
case reflect.Slice:
return .tryRelationSlice(, )
case reflect.Struct:
return .tryRelationStruct(, )
}
return false
}
func ( *Table) ( *Field, string, *tagparser.Tag) bool {
switch {
case "has-one":
return .mustHasOneRelation(, )
case "belongs-to":
return .mustBelongsToRelation(, )
case "has-many":
return .mustHasManyRelation(, )
case "many2many":
return .mustM2MRelation(, )
default:
panic(fmt.Errorf("pg: unknown relation=%s on field=%s", , .GoName))
}
}
func ( *Table) ( *Field, *tagparser.Tag) bool {
:= _tables.get(.Type, true)
:= .PKs
, := .Options["join_fk"]
if := .checkPKs(); != nil && ! {
panic()
}
if {
:= .getField()
if == nil {
panic(fmt.Errorf(
"pg: %s has-one %s: field %s specified by join_fk doesn't exist on %s",
.TypeName, .GoName, , .TypeName,
))
}
= []*Field{}
}
, := .Options["fk"]
if && len() == 1 {
:= .getField()
if == nil {
panic(fmt.Errorf(
"pg: %s has-one %s: %s must have column %s "+
"(use fk:custom_column tag on %s field to specify custom column)",
.TypeName, .GoName, .TypeName, , .GoName,
))
}
.addRelation(&Relation{
Type: HasOneRelation,
Field: ,
JoinTable: ,
BaseFKs: []*Field{},
JoinFKs: ,
})
return true
}
if ! {
= internal.Underscore(.GoName) + "_"
}
:= make([]*Field, 0, len())
for , := range {
:= + .SQLName
if := .getField(); != nil {
= append(, )
continue
}
if := .getField(.SQLName); != nil {
= append(, )
continue
}
panic(fmt.Errorf(
"pg: %s has-one %s: %s must have column %s "+
"(use fk:custom_column tag on %s field to specify custom column)",
.TypeName, .GoName, .TypeName, , .GoName,
))
}
.addRelation(&Relation{
Type: HasOneRelation,
Field: ,
JoinTable: ,
BaseFKs: ,
JoinFKs: ,
})
return true
}
func ( *Table) ( *Field, *tagparser.Tag) bool {
if := .checkPKs(); != nil {
panic()
}
:= _tables.get(.Type, true)
, := .Options["join_fk"]
if && len(.PKs) == 1 {
:= .getField()
if == nil {
panic(fmt.Errorf(
"pg: %s belongs-to %s: %s must have column %s "+
"(use join_fk:custom_column tag on %s field to specify custom column)",
.GoName, .TypeName, .TypeName, , .GoName,
))
}
.addRelation(&Relation{
Type: BelongsToRelation,
Field: ,
JoinTable: ,
BaseFKs: .PKs,
JoinFKs: []*Field{},
})
return true
}
if ! {
= internal.Underscore(.ModelName) + "_"
}
:= make([]*Field, 0, len(.PKs))
for , := range .PKs {
:= + .SQLName
if := .getField(); != nil {
= append(, )
continue
}
if := .getField(.SQLName); != nil {
= append(, )
continue
}
panic(fmt.Errorf(
"pg: %s belongs-to %s: %s must have column %s "+
"(use join_fk:custom_column tag on %s field to specify custom column)",
.GoName, .TypeName, .TypeName, , .GoName,
))
}
.addRelation(&Relation{
Type: BelongsToRelation,
Field: ,
JoinTable: ,
BaseFKs: .PKs,
JoinFKs: ,
})
return true
}
func ( *Table) ( *Field, *tagparser.Tag) bool {
if := .checkPKs(); != nil {
panic()
}
if .Type.Kind() != reflect.Slice {
panic(fmt.Errorf(
"pg: %s.%s has-many relation requires slice, got %q",
.TypeName, .GoName, .Type.Kind(),
))
}
:= _tables.get(indirectType(.Type.Elem()), true)
, := .Options["join_fk"]
, := .Options["polymorphic"]
if && ! && len(.PKs) == 1 {
:= .getField()
if == nil {
panic(fmt.Errorf(
"pg: %s has-many %s: %s must have column %s "+
"(use join_fk:custom_column tag on %s field to specify custom column)",
.TypeName, .GoName, .TypeName, , .GoName,
))
}
.addRelation(&Relation{
Type: HasManyRelation,
Field: ,
JoinTable: ,
BaseFKs: .PKs,
JoinFKs: []*Field{},
})
return true
}
if ! {
= internal.Underscore(.ModelName) + "_"
}
:= make([]*Field, 0, len(.PKs))
for , := range .PKs {
:= + .SQLName
if := .getField(); != nil {
= append(, )
continue
}
if := .getField(.SQLName); != nil {
= append(, )
continue
}
panic(fmt.Errorf(
"pg: %s has-many %s: %s must have column %s "+
"(use join_fk:custom_column tag on %s field to specify custom column)",
.TypeName, .GoName, .TypeName, , .GoName,
))
}
var *Field
if {
:= + "type"
= .getField()
if == nil {
panic(fmt.Errorf(
"pg: %s has-many %s: %s must have polymorphic column %s",
.TypeName, .GoName, .TypeName, ,
))
}
}
.addRelation(&Relation{
Type: HasManyRelation,
Field: ,
JoinTable: ,
BaseFKs: .PKs,
JoinFKs: ,
Polymorphic: ,
})
return true
}
func ( *Table) ( *Field, *tagparser.Tag) bool {
if .Type.Kind() != reflect.Slice {
panic(fmt.Errorf(
"pg: %s.%s many2many relation requires slice, got %q",
.TypeName, .GoName, .Type.Kind(),
))
}
:= _tables.get(indirectType(.Type.Elem()), true)
if := .checkPKs(); != nil {
panic()
}
if := .checkPKs(); != nil {
panic()
}
, := .Options["many2many"]
if ! {
panic(fmt.Errorf("pg: %s must have many2many tag option", .GoName))
}
:= quoteTableName()
:= _tables.getByName()
if == nil {
panic(fmt.Errorf(
"pg: can't find %s table (use orm.RegisterTable to register the model)",
,
))
}
var []string
var []string
{
, := .Options["fk"]
if ! {
= internal.Underscore(.ModelName) + "_"
}
if && len(.PKs) == 1 {
if .getField() == nil {
panic(fmt.Errorf(
"pg: %s many2many %s: %s must have column %s "+
"(use fk:custom_column tag on %s field to specify custom column)",
.TypeName, .GoName, .TypeName, , .GoName,
))
}
= []string{}
} else {
for , := range .PKs {
:= + .SQLName
if .getField() != nil {
= append(, )
continue
}
if .getField(.SQLName) != nil {
= append(, .SQLName)
continue
}
panic(fmt.Errorf(
"pg: %s many2many %s: %s must have column %s "+
"(use fk:custom_column tag on %s field to specify custom column)",
.TypeName, .GoName, .TypeName, , .GoName,
))
}
}
}
{
, := .Options["join_fk"]
if ! {
= internal.Underscore(.ModelName) + "_"
}
if && len(.PKs) == 1 {
if .getField() == nil {
panic(fmt.Errorf(
"pg: %s many2many %s: %s must have column %s "+
"(use join_fk:custom_column tag on %s field to specify custom column)",
.TypeName, .GoName, .TypeName, , .GoName,
))
}
= []string{}
} else {
for , := range .PKs {
:= + .SQLName
if .getField() != nil {
= append(, )
continue
}
if .getField(.SQLName) != nil {
= append(, .SQLName)
continue
}
panic(fmt.Errorf(
"pg: %s many2many %s: %s must have column %s "+
"(use join_fk:custom_column tag on %s field to specify custom column)",
.TypeName, .GoName, .TypeName, , .GoName,
))
}
}
}
.addRelation(&Relation{
Type: Many2ManyRelation,
Field: ,
JoinTable: ,
M2MTableName: ,
M2MTableAlias: .Alias,
M2MBaseFKs: ,
M2MJoinFKs: ,
})
return true
}
func ( *Table) ( *Field, *tagparser.Tag) bool {
if .tryM2MRelation(, ) {
internal.Deprecated.Printf(
`add pg:"rel:many2many" to %s.%s field tag`, .TypeName, .GoName)
return true
}
if .tryHasManyRelation(, ) {
internal.Deprecated.Printf(
`add pg:"rel:has-many" to %s.%s field tag`, .TypeName, .GoName)
return true
}
return false
}
func ( *Table) ( *Field, *tagparser.Tag) bool {
:= indirectType(.Type.Elem())
if .Kind() != reflect.Struct {
return false
}
:= _tables.get(, true)
, := .Options["fk"]
if {
if == "-" {
return false
}
= tryUnderscorePrefix()
}
:= .Options["many2many"]
if == "" {
return false
}
:= _tables.getByName(quoteIdent())
var types.Safe
if != nil {
= .Alias
} else if := strings.IndexByte(, '.'); >= 0 {
= quoteIdent([+1:])
} else {
= quoteIdent()
}
var []string
if ! {
= .ModelName + "_"
}
if != nil {
:= foreignKeys(, , , )
if len() == 0 {
return false
}
for , := range {
= append(, .SQLName)
}
} else {
if && len(.PKs) == 1 {
= append(, )
} else {
for , := range .PKs {
= append(, +.SQLName)
}
}
}
, := .Options["join_fk"]
if ! {
, = .Options["joinFK"]
if {
internal.Deprecated.Printf("joinFK is renamed to join_fk")
}
}
if {
= tryUnderscorePrefix()
} else {
= .ModelName + "_"
}
var []string
if != nil {
:= foreignKeys(, , , )
if len() == 0 {
return false
}
for , := range {
= append(, .SQLName)
}
} else {
if && len(.PKs) == 1 {
= append(, )
} else {
for , := range .PKs {
= append(, +.SQLName)
}
}
}
.addRelation(&Relation{
Type: Many2ManyRelation,
Field: ,
JoinTable: ,
M2MTableName: quoteIdent(),
M2MTableAlias: ,
M2MBaseFKs: ,
M2MJoinFKs: ,
})
return true
}
func ( *Table) ( *Field, *tagparser.Tag) bool {
:= indirectType(.Type.Elem())
if .Kind() != reflect.Struct {
return false
}
:= _tables.get(, true)
, := .Options["fk"]
if {
if == "-" {
return false
}
= tryUnderscorePrefix()
}
, := .Options["polymorphic"]
var *Field
if {
= tryUnderscorePrefix()
= .getField( + "type")
if == nil {
return false
}
} else if ! {
= .ModelName + "_"
}
:= foreignKeys(, , , || )
if len() == 0 {
return false
}
var []*Field
, := .Options["fk_value"]
if {
if len() > 1 {
panic(fmt.Errorf("got fk_value, but there are %d fks", len()))
}
:= .getField()
if == nil {
panic(fmt.Errorf("fk_value=%q not found in %s", , ))
}
= append(, )
} else {
= .PKs
}
if len() != len() {
panic("len(fks) != len(fkValues)")
}
if len() > 0 {
.addRelation(&Relation{
Type: HasManyRelation,
Field: ,
JoinTable: ,
BaseFKs: ,
JoinFKs: ,
Polymorphic: ,
})
return true
}
return false
}
func ( *Table) ( *Field, *tagparser.Tag) bool {
:= _tables.get(.Type, true)
if len(.allFields) == 0 {
return false
}
if .tryHasOne(, , ) {
internal.Deprecated.Printf(
`add pg:"rel:has-one" to %s.%s field tag`, .TypeName, .GoName)
.inlineFields(, nil)
return true
}
if .tryBelongsToOne(, , ) {
internal.Deprecated.Printf(
`add pg:"rel:belongs-to" to %s.%s field tag`, .TypeName, .GoName)
.inlineFields(, nil)
return true
}
.inlineFields(, nil)
return false
}
func ( *Table) ( *Field, map[reflect.Type]struct{}) {
if == nil {
= map[reflect.Type]struct{}{
.Type: {},
}
}
if , := [.Type]; {
return
}
[.Type] = struct{}{}
:= _tables.get(.Type, true)
for , := range .allFields {
= .Clone()
.GoName = .GoName + "_" + .GoName
.SQLName = .SQLName + "__" + .SQLName
.Column = quoteIdent(.SQLName)
.Index = appendNew(.Index, .Index...)
.fieldsMapMu.Lock()
if , := .FieldsMap[.SQLName]; ! {
.FieldsMap[.SQLName] =
}
.fieldsMapMu.Unlock()
if .Type.Kind() != reflect.Struct {
continue
}
if , := [.Type]; ! {
.(, )
}
}
}
func ( []int, ...int) []int {
:= make([]int, len()+len())
copy(, )
copy([len():], )
return
}
func ( reflect.Type) bool {
return .Implements(scannerType) || reflect.PtrTo().Implements(scannerType)
}
func ( *Field, *tagparser.Tag) string {
if , := .Options["type"]; {
, _ = tagparser.Unquote()
.UserSQLType =
= normalizeSQLType()
return
}
if , := .Options["composite"]; {
, _ = tagparser.Unquote()
return
}
if , := .Options["hstore"]; {
return "hstore"
} else if , := .Options["hstore"]; {
return "hstore"
}
if .hasFlag(ArrayFlag) {
switch .Type.Kind() {
case reflect.Slice, reflect.Array:
:= sqlType(.Type.Elem())
return + "[]"
}
}
:= sqlType(.Type)
return
}
func ( reflect.Type) string {
switch {
case timeType, nullTimeType, sqlNullTimeType:
return pgTypeTimestampTz
case ipType:
return pgTypeInet
case ipNetType:
return pgTypeCidr
case nullBoolType:
return pgTypeBoolean
case nullFloatType:
return pgTypeDoublePrecision
case nullIntType:
return pgTypeBigint
case nullStringType:
return pgTypeText
case jsonRawMessageType:
return pgTypeJSONB
}
switch .Kind() {
case reflect.Int8, reflect.Uint8, reflect.Int16:
return pgTypeSmallint
case reflect.Uint16, reflect.Int32:
return pgTypeInteger
case reflect.Uint32, reflect.Int64, reflect.Int:
return pgTypeBigint
case reflect.Uint, reflect.Uint64:
return pgTypeBigint
case reflect.Float32:
return pgTypeReal
case reflect.Float64:
return pgTypeDoublePrecision
case reflect.Bool:
return pgTypeBoolean
case reflect.String:
return pgTypeText
case reflect.Map, reflect.Struct:
return pgTypeJSONB
case reflect.Array, reflect.Slice:
if .Elem().Kind() == reflect.Uint8 {
return pgTypeBytea
}
return pgTypeJSONB
default:
return .Kind().String()
}
}
func ( string) string {
switch {
case "int2":
return pgTypeSmallint
case "int4", "int", "serial":
return pgTypeInteger
case "int8", pgTypeBigserial:
return pgTypeBigint
case "float4":
return pgTypeReal
case "float8":
return pgTypeDoublePrecision
}
return
}
func (, string) bool {
return ==
}
func ( *Table) ( *Table, *Field, *tagparser.Tag) bool {
, := .Options["fk"]
if {
if == "-" {
return false
}
= tryUnderscorePrefix()
} else {
= internal.Underscore(.GoName) + "_"
}
:= foreignKeys(, , , )
if len() > 0 {
.addRelation(&Relation{
Type: HasOneRelation,
Field: ,
JoinTable: ,
BaseFKs: ,
JoinFKs: .PKs,
})
return true
}
return false
}
func ( *Table) ( *Table, *Field, *tagparser.Tag) bool {
, := .Options["fk"]
if {
if == "-" {
return false
}
= tryUnderscorePrefix()
} else {
= internal.Underscore(.TypeName) + "_"
}
:= foreignKeys(, , , )
if len() > 0 {
.addRelation(&Relation{
Type: BelongsToRelation,
Field: ,
JoinTable: ,
BaseFKs: .PKs,
JoinFKs: ,
})
return true
}
return false
}
func ( *Table) ( *Relation) {
if .Relations == nil {
.Relations = make(map[string]*Relation)
}
, := .Relations[.Field.GoName]
if {
panic(fmt.Errorf("%s already has %s", , ))
}
.Relations[.Field.GoName] =
}
func (, *Table, string, bool) []*Field {
var []*Field
for , := range .PKs {
:= + .SQLName
:= .getField()
if != nil && sqlTypeEqual(.SQLType, .SQLType) {
= append(, )
continue
}
if strings.IndexByte(.SQLName, '_') == -1 {
continue
}
= .getField(.SQLName)
if != nil && sqlTypeEqual(.SQLType, .SQLType) {
= append(, )
continue
}
}
if len() > 0 && len() == len(.PKs) {
return
}
= nil
for , := range .PKs {
if !strings.HasPrefix(.SQLName, "pk_") {
continue
}
:= "fk_" + .SQLName[3:]
:= .getField()
if != nil && sqlTypeEqual(.SQLType, .SQLType) {
= append(, )
}
}
if len() > 0 && len() == len(.PKs) {
return
}
if == "" || len(.PKs) != 1 {
return nil
}
if {
:= .getField()
if != nil && sqlTypeEqual(.PKs[0].SQLType, .SQLType) {
return []*Field{}
}
}
for , := range []string{"id", "uuid"} {
:= .getField( + )
if != nil && sqlTypeEqual(.PKs[0].SQLType, .SQLType) {
return []*Field{}
}
}
return nil
}
func ( reflect.Value, types.Reader, int) error {
.Set(reflect.New(.Type()).Elem())
if == -1 {
return nil
}
:= pgjson.NewDecoder()
.UseNumber()
return .Decode(.Addr().Interface())
}
func ( []byte, reflect.Value, int) []byte {
return strconv.AppendInt(, int64(.Uint()), 10)
}
func ( []byte, reflect.Value, int) []byte {
return strconv.AppendInt(, int64(.Elem().Uint()), 10)
}
func ( string) string {
if == "" {
return
}
if := [0]; internal.IsUpper() {
return internal.Underscore() + "_"
}
return
}
func ( string) types.Safe {
if strings.IndexByte(, '?') >= 0 ||
strings.IndexByte(, '(') >= 0 && strings.IndexByte(, ')') >= 0 {
return types.Safe()
}
return quoteIdent()
}
func ( string) types.Safe {
return types.Safe(types.AppendIdent(nil, , 1))
}
func ( reflect.Type) func( reflect.Value) error {
switch {
case timeType:
return func( reflect.Value) error {
:= .Addr().Interface().(*time.Time)
* = time.Now()
return nil
}
case nullTimeType:
return func( reflect.Value) error {
:= .Addr().Interface().(*types.NullTime)
* = types.NullTime{Time: time.Now()}
return nil
}
case nullIntType:
return func( reflect.Value) error {
:= .Addr().Interface().(*sql.NullInt64)
* = sql.NullInt64{Int64: time.Now().UnixNano()}
return nil
}
}
switch .Kind() {
case reflect.Int64:
return func( reflect.Value) error {
:= .Addr().Interface().(*int64)
* = time.Now().UnixNano()
return nil
}
case reflect.Ptr:
break
default:
return setSoftDeleteFallbackFunc()
}
:=
= .Elem()
switch {
case timeType:
return func( reflect.Value) error {
:= time.Now()
.Set(reflect.ValueOf(&))
return nil
}
}
switch .Kind() {
case reflect.Int64:
return func( reflect.Value) error {
:= time.Now().UnixNano()
.Set(reflect.ValueOf(&))
return nil
}
}
return setSoftDeleteFallbackFunc()
}
func ( reflect.Type) func( reflect.Value) error {
:= types.Scanner()
if == nil {
return nil
}
return func( reflect.Value) error {
var int
:= types.AppendTime(nil, time.Now(), )
return (, pool.NewBytesReader(), len())
}
}
func ( string) bool {
switch {
case "alias",
"select",
"tablespace",
"partition_by",
"discard_unknown_columns":
return true
}
return false
}
func ( string) bool {
switch {
case "alias",
"type",
"array",
"hstore",
"composite",
"json_use_number",
"msgpack",
"notnull",
"use_zero",
"default",
"unique",
"soft_delete",
"on_delete",
"on_update",
"pk",
"nopk",
"rel",
"fk",
"join_fk",
"many2many",
"polymorphic":
return true
}
return false
}