package schema

import (
	
	
	
	
	
	

	

	
	
)

const (
	beforeScanHookFlag internal.Flag = 1 << iota
	afterScanHookFlag
)

var (
	baseModelType      = reflect.TypeOf((*BaseModel)(nil)).Elem()
	tableNameInflector = inflection.Plural
)

type BaseModel struct{}

// SetTableNameInflector overrides the default func that pluralizes
// model name to get table name, e.g. my_article becomes my_articles.
func ( func(string) string) {
	tableNameInflector = 
}

// Table represents a SQL table created from Go struct.
type Table struct {
	dialect Dialect

	Type      reflect.Type
	ZeroValue reflect.Value // reflect.Struct
	ZeroIface interface{}   // struct pointer

	TypeName  string
	ModelName string

	Name              string
	SQLName           Safe
	SQLNameForSelects Safe
	Alias             string
	SQLAlias          Safe

	Fields     []*Field // PKs + DataFields
	PKs        []*Field
	DataFields []*Field

	fieldsMapMu sync.RWMutex
	FieldMap    map[string]*Field

	Relations map[string]*Relation
	Unique    map[string][]*Field

	SoftDeleteField       *Field
	UpdateSoftDeleteField func(fv reflect.Value, tm time.Time) error

	allFields []*Field // read only

	flags internal.Flag
}

func ( Dialect,  reflect.Type) *Table {
	 := new(Table)
	.dialect = 
	.Type = 
	.ZeroValue = reflect.New(.Type).Elem()
	.ZeroIface = reflect.New(.Type).Interface()
	.TypeName = internal.ToExported(.Type.Name())
	.ModelName = internal.Underscore(.Type.Name())
	 := tableNameInflector(.ModelName)
	.setName()
	.Alias = .ModelName
	.SQLAlias = .quoteIdent(.ModelName)

	 := []struct {
		  reflect.Type
		 internal.Flag
	}{
		{beforeScanHookType, beforeScanHookFlag},
		{afterScanHookType, afterScanHookFlag},
	}

	 = reflect.PtrTo(.Type)
	for ,  := range  {
		if .Implements(.) {
			.flags = .flags.Set(.)
		}
	}

	return 
}

func ( *Table) () {
	.initFields()
}

func ( *Table) () {
	.initRelations()
}

func ( *Table) ( string) {
	.Name = 
	.SQLName = .quoteIdent()
	.SQLNameForSelects = .quoteIdent()
	if .SQLAlias == "" {
		.Alias = 
		.SQLAlias = .quoteIdent()
	}
}

func ( *Table) () string {
	return "model=" + .TypeName
}

func ( *Table) () error {
	if len(.PKs) == 0 {
		return fmt.Errorf("bun: %s does not have primary keys", )
	}
	return nil
}

func ( *Table) ( *Field) {
	.Fields = append(.Fields, )
	if .IsPK {
		.PKs = append(.PKs, )
	} else {
		.DataFields = append(.DataFields, )
	}
	.FieldMap[.Name] = 
}

func ( *Table) ( *Field) {
	.Fields = removeField(.Fields, )
	if .IsPK {
		.PKs = removeField(.PKs, )
	} else {
		.DataFields = removeField(.DataFields, )
	}
	delete(.FieldMap, .Name)
}

func ( *Table) ( string) *Field {
	.fieldsMapMu.RLock()
	 := .FieldMap[]
	.fieldsMapMu.RUnlock()
	return 
}

func ( *Table) ( string) bool {
	,  := .FieldMap[]
	return 
}

func ( *Table) ( string) (*Field, error) {
	,  := .FieldMap[]
	if ! {
		return nil, fmt.Errorf("bun: %s does not have column=%s", , )
	}
	return , nil
}

func ( *Table) ( string) *Field {
	for ,  := range .allFields {
		if .GoName ==  {
			return 
		}
	}
	return nil
}

func ( *Table) () {
	.Fields = make([]*Field, 0, .Type.NumField())
	.FieldMap = make(map[string]*Field, .Type.NumField())
	.addFields(.Type, nil)

	if len(.PKs) == 0 {
		for ,  := range []string{"id", "uuid", "pk_" + .ModelName} {
			if ,  := .FieldMap[];  {
				.markAsPK()
				.PKs = []*Field{}
				.DataFields = removeField(.DataFields, )
				break
			}
		}
	}

	if len(.PKs) == 1 {
		 := .PKs[0]
		if .SQLDefault != "" {
			return
		}

		switch .IndirectType.Kind() {
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
			reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
			.AutoIncrement = true
		}
	}
}

func ( *Table) ( reflect.Type,  []int) {
	for  := 0;  < .NumField(); ++ {
		 := .Field()
		 := .PkgPath != ""

		if  && !.Anonymous { // unexported
			continue
		}
		if .Tag.Get("bun") == "-" {
			continue
		}

		// Make a copy so the slice is not shared between fields.
		 := make([]int, len())
		copy(, )

		if .Anonymous {
			if .Name == "BaseModel" && .Type == baseModelType {
				if len() == 0 {
					.processBaseModelField()
				}
				continue
			}

			 := indirectType(.Type)
			if .Kind() != reflect.Struct {
				continue
			}
			.(, append(, .Index...))

			 := tagparser.Parse(.Tag.Get("bun"))
			if ,  := .Options["inherit"];  {
				 := .dialect.Tables().Ref()
				.TypeName = .TypeName
				.SQLName = .SQLName
				.SQLNameForSelects = .SQLNameForSelects
				.Alias = .Alias
				.SQLAlias = .SQLAlias
				.ModelName = .ModelName
			}

			continue
		}

		if  := .newField(, );  != nil {
			.addField()
		}
	}
}

func ( *Table) ( reflect.StructField) {
	 := tagparser.Parse(.Tag.Get("bun"))

	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 .Name != "" {
		.setName(.Name)
	}

	if ,  := .Options["select"];  {
		.SQLNameForSelects = .quoteTableName()
	}

	if ,  := .Options["alias"];  {
		.Alias = 
		.SQLAlias = .quoteIdent()
	}
}

//nolint
func ( *Table) ( reflect.StructField,  []int) *Field {
	 := tagparser.Parse(.Tag.Get("bun"))

	 := internal.Underscore(.Name)
	if .Name != "" {
		 = .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, )
		}
	}

	 = append(, .Index...)
	if  := .fieldWithLock();  != nil {
		if indexEqual(.Index, ) {
			return 
		}
		.removeField()
	}

	 := &Field{
		StructField: ,

		Tag:          ,
		IndirectType: indirectType(.Type),
		Index:        ,

		Name:    ,
		GoName:  .Name,
		SQLName: .quoteIdent(),
	}

	.NotNull = .HasOption("notnull")
	.NullZero = .HasOption("nullzero")
	.AutoIncrement = .HasOption("autoincrement")
	if .HasOption("pk") {
		.markAsPK()
	}
	if .HasOption("allowzero") {
		if .HasOption("nullzero") {
			internal.Warn.Printf(
				"%s.%s: nullzero and allowzero options are mutually exclusive",
				.TypeName, .Name,
			)
		}
		.NullZero = false
	}

	if ,  := .Options["unique"];  {
		// Split the value by comma, this will allow multiple names to be specified.
		// We can use this to create multiple named unique constraints where a single column
		// might be included in multiple constraints.
		for ,  := range strings.Split(, ",") {
			if .Unique == nil {
				.Unique = make(map[string][]*Field)
			}
			.Unique[] = append(.Unique[], )
		}
	}
	if ,  := .Options["default"];  {
		.SQLDefault = 
	}
	if ,  := .Tag.Options["type"];  {
		.UserSQLType = 
	}
	.DiscoveredSQLType = DiscoverSQLType(.IndirectType)
	.Append = .dialect.FieldAppender()
	.Scan = FieldScanner(.dialect, )
	.IsZero = zeroChecker(.StructField.Type)

	if ,  := .Options["alt"];  {
		.FieldMap[] = 
	}

	.allFields = append(.allFields, )
	if .HasOption("scanonly") {
		.FieldMap[.Name] = 
		if .IndirectType.Kind() == reflect.Struct {
			.inlineFields(, nil)
		}
		return nil
	}

	if ,  := .Options["soft_delete"];  {
		.NullZero = true
		.SoftDeleteField = 
		.UpdateSoftDeleteField = softDeleteFieldUpdater()
	}

	return 
}

//---------------------------------------------------------------------------------------

func ( *Table) () {
	for  := 0;  < len(.Fields); {
		 := .Fields[]
		if .tryRelation() {
			.Fields = removeField(.Fields, )
			.DataFields = removeField(.DataFields, )
		} else {
			++
		}

		if .IndirectType.Kind() == reflect.Struct {
			.inlineFields(, nil)
		}
	}
}

func ( *Table) ( *Field) bool {
	if ,  := .Tag.Options["rel"];  {
		.initRelation(, )
		return true
	}
	if .Tag.HasOption("m2m") {
		.addRelation(.m2mRelation())
		return true
	}

	if .Tag.HasOption("join") {
		internal.Warn.Printf(
			`%s.%s option "join" requires a relation type`,
			.TypeName, .GoName,
		)
	}

	return false
}

func ( *Table) ( *Field,  string) {
	switch  {
	case "belongs-to":
		.addRelation(.belongsToRelation())
	case "has-one":
		.addRelation(.hasOneRelation())
	case "has-many":
		.addRelation(.hasManyRelation())
	default:
		panic(fmt.Errorf("bun: unknown relation=%s on field=%s", , .GoName))
	}
}

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) ( *Field) *Relation {
	 := .dialect.Tables().Ref(.IndirectType)
	if  := .CheckPKs();  != nil {
		panic()
	}

	 := &Relation{
		Type:      HasOneRelation,
		Field:     ,
		JoinTable: ,
	}

	if ,  := .Tag.Options["join"];  {
		,  := parseRelationJoin()
		for ,  := range  {
			 := []

			if  := .fieldWithLock();  != nil {
				.BaseFields = append(.BaseFields, )
			} else {
				panic(fmt.Errorf(
					"bun: %s belongs-to %s: %s must have column %s",
					.TypeName, .GoName, .TypeName, ,
				))
			}

			if  := .fieldWithLock();  != nil {
				.JoinFields = append(.JoinFields, )
			} else {
				panic(fmt.Errorf(
					"bun: %s belongs-to %s: %s must have column %s",
					.TypeName, .GoName, .TypeName, ,
				))
			}
		}
		return 
	}

	.JoinFields = .PKs
	 := internal.Underscore(.GoName) + "_"
	for ,  := range .PKs {
		 :=  + .Name
		if  := .fieldWithLock();  != nil {
			.BaseFields = append(.BaseFields, )
			continue
		}

		if  := .fieldWithLock(.Name);  != nil {
			.BaseFields = append(.BaseFields, )
			continue
		}

		panic(fmt.Errorf(
			"bun: %s belongs-to %s: %s must have column %s "+
				"(to override, use join:base_column=join_column tag on %s field)",
			.TypeName, .GoName, .TypeName, , .GoName,
		))
	}
	return 
}

func ( *Table) ( *Field) *Relation {
	if  := .CheckPKs();  != nil {
		panic()
	}

	 := .dialect.Tables().Ref(.IndirectType)
	 := &Relation{
		Type:      BelongsToRelation,
		Field:     ,
		JoinTable: ,
	}

	if ,  := .Tag.Options["join"];  {
		,  := parseRelationJoin()
		for ,  := range  {
			if  := .fieldWithLock();  != nil {
				.BaseFields = append(.BaseFields, )
			} else {
				panic(fmt.Errorf(
					"bun: %s has-one %s: %s must have column %s",
					.GoName, .TypeName, .TypeName, ,
				))
			}

			 := []
			if  := .fieldWithLock();  != nil {
				.JoinFields = append(.JoinFields, )
			} else {
				panic(fmt.Errorf(
					"bun: %s has-one %s: %s must have column %s",
					.GoName, .TypeName, .TypeName, ,
				))
			}
		}
		return 
	}

	.BaseFields = .PKs
	 := internal.Underscore(.ModelName) + "_"
	for ,  := range .PKs {
		 :=  + .Name
		if  := .fieldWithLock();  != nil {
			.JoinFields = append(.JoinFields, )
			continue
		}

		if  := .fieldWithLock(.Name);  != nil {
			.JoinFields = append(.JoinFields, )
			continue
		}

		panic(fmt.Errorf(
			"bun: %s has-one %s: %s must have column %s "+
				"(to override, use join:base_column=join_column tag on %s field)",
			.GoName, .TypeName, .TypeName, , .GoName,
		))
	}
	return 
}

func ( *Table) ( *Field) *Relation {
	if  := .CheckPKs();  != nil {
		panic()
	}
	if .IndirectType.Kind() != reflect.Slice {
		panic(fmt.Errorf(
			"bun: %s.%s has-many relation requires slice, got %q",
			.TypeName, .GoName, .IndirectType.Kind(),
		))
	}

	 := .dialect.Tables().Ref(indirectType(.IndirectType.Elem()))
	,  := .Tag.Options["polymorphic"]
	 := &Relation{
		Type:      HasManyRelation,
		Field:     ,
		JoinTable: ,
	}
	var  string

	if ,  := .Tag.Options["join"];  {
		,  := parseRelationJoin()
		for ,  := range  {
			 := []

			if  &&  == "type" {
				 = 
				continue
			}

			if  := .fieldWithLock();  != nil {
				.BaseFields = append(.BaseFields, )
			} else {
				panic(fmt.Errorf(
					"bun: %s has-one %s: %s must have column %s",
					.TypeName, .GoName, .TypeName, ,
				))
			}

			if  := .fieldWithLock();  != nil {
				.JoinFields = append(.JoinFields, )
			} else {
				panic(fmt.Errorf(
					"bun: %s has-one %s: %s must have column %s",
					.TypeName, .GoName, .TypeName, ,
				))
			}
		}
	} else {
		.BaseFields = .PKs
		 := internal.Underscore(.ModelName) + "_"
		if  {
			 =  + "type"
		}

		for ,  := range .PKs {
			 :=  + .Name
			if  := .fieldWithLock();  != nil {
				.JoinFields = append(.JoinFields, )
				continue
			}

			if  := .fieldWithLock(.Name);  != nil {
				.JoinFields = append(.JoinFields, )
				continue
			}

			panic(fmt.Errorf(
				"bun: %s has-many %s: %s must have column %s "+
					"(to override, use join:base_column=join_column tag on the field %s)",
				.TypeName, .GoName, .TypeName, , .GoName,
			))
		}
	}

	if  {
		.PolymorphicField = .fieldWithLock()
		if .PolymorphicField == nil {
			panic(fmt.Errorf(
				"bun: %s has-many %s: %s must have polymorphic column %s",
				.TypeName, .GoName, .TypeName, ,
			))
		}

		if  == "" {
			 = .ModelName
		}
		.PolymorphicValue = 
	}

	return 
}

func ( *Table) ( *Field) *Relation {
	if .IndirectType.Kind() != reflect.Slice {
		panic(fmt.Errorf(
			"bun: %s.%s m2m relation requires slice, got %q",
			.TypeName, .GoName, .IndirectType.Kind(),
		))
	}
	 := .dialect.Tables().Ref(indirectType(.IndirectType.Elem()))

	if  := .CheckPKs();  != nil {
		panic()
	}
	if  := .CheckPKs();  != nil {
		panic()
	}

	,  := .Tag.Options["m2m"]
	if ! {
		panic(fmt.Errorf("bun: %s must have m2m tag option", .GoName))
	}

	 := .dialect.Tables().ByName()
	if  == nil {
		panic(fmt.Errorf(
			"bun: can't find m2m %s table (use db.RegisterModel)",
			,
		))
	}

	 := &Relation{
		Type:      ManyToManyRelation,
		Field:     ,
		JoinTable: ,
		M2MTable:  ,
	}
	var ,  string

	if ,  := .Tag.Options["join"];  {
		,  := parseRelationJoin()
		 = [0]
		 = [0]
	} else {
		 = .TypeName
		 = .TypeName
	}

	 := .fieldByGoName()
	if  == nil {
		panic(fmt.Errorf(
			"bun: %s many-to-many %s: %s must have field %s "+
				"(to override, use tag join:LeftField=RightField on field %s.%s",
			.TypeName, .GoName, .TypeName, , .TypeName, .GoName,
		))
	}

	 := .fieldByGoName()
	if  == nil {
		panic(fmt.Errorf(
			"bun: %s many-to-many %s: %s must have field %s "+
				"(to override, use tag join:LeftField=RightField on field %s.%s",
			.TypeName, .GoName, .TypeName, , .TypeName, .GoName,
		))
	}

	 := .belongsToRelation()
	.BaseFields = .JoinFields
	.M2MBaseFields = .BaseFields

	 := .belongsToRelation()
	.JoinFields = .JoinFields
	.M2MJoinFields = .BaseFields

	return 
}

func ( *Table) ( *Field,  map[reflect.Type]struct{}) {
	if  == nil {
		 = map[reflect.Type]struct{}{.Type: {}}
	}

	if ,  := [.IndirectType];  {
		return
	}
	[.IndirectType] = struct{}{}

	 := .dialect.Tables().Ref(.IndirectType)
	for ,  := range .allFields {
		 = .Clone()
		.GoName = .GoName + "_" + .GoName
		.Name = .Name + "__" + .Name
		.SQLName = .quoteIdent(.Name)
		.Index = appendNew(.Index, .Index...)

		.fieldsMapMu.Lock()
		if ,  := .FieldMap[.Name]; ! {
			.FieldMap[.Name] = 
		}
		.fieldsMapMu.Unlock()

		if .IndirectType.Kind() != reflect.Struct {
			continue
		}

		if ,  := [.IndirectType]; ! {
			.(, )
		}
	}
}

//------------------------------------------------------------------------------

func ( *Table) () Dialect        { return .dialect }
func ( *Table) () bool { return .flags.Has(beforeScanHookFlag) }
func ( *Table) () bool  { return .flags.Has(afterScanHookFlag) }

//------------------------------------------------------------------------------

func ( *Table) (
	 Formatter,  []byte,  string,  reflect.Value,
) ([]byte, bool) {
	if ,  := .FieldMap[];  {
		return .appendArg(, .Value().Interface()), true
	}
	return , false
}

func ( *Table) ( string) Safe {
	// Don't quote if table name contains placeholder (?) or parentheses.
	if strings.IndexByte(, '?') >= 0 ||
		strings.IndexByte(, '(') >= 0 ||
		strings.IndexByte(, ')') >= 0 {
		return Safe()
	}
	return .quoteIdent()
}

func ( *Table) ( string) Safe {
	return Safe(NewFormatter(.dialect).AppendIdent(nil, ))
}

func ( []int,  ...int) []int {
	 := make([]int, len()+len())
	copy(, )
	copy([len():], )
	return 
}

func ( string) bool {
	switch  {
	case "alias", "select":
		return true
	}
	return false
}

func ( string) bool {
	switch  {
	case "alias",
		"type",
		"array",
		"hstore",
		"composite",
		"json_use_number",
		"msgpack",
		"notnull",
		"nullzero",
		"allowzero",
		"default",
		"unique",
		"soft_delete",
		"scanonly",

		"pk",
		"autoincrement",
		"rel",
		"join",
		"m2m",
		"polymorphic":
		return true
	}
	return false
}

func ( []*Field,  *Field) []*Field {
	for ,  := range  {
		if  ==  {
			return append([:], [+1:]...)
		}
	}
	return 
}

func ( string) ([]string, []string) {
	 := strings.Split(, ",")
	 := make([]string, len())
	 := make([]string, len())
	for ,  := range  {
		 := strings.Split(strings.TrimSpace(), "=")
		if len() != 2 {
			panic(fmt.Errorf("can't parse relation join: %q", ))
		}
		[] = [0]
		[] = [1]
	}
	return , 
}

//------------------------------------------------------------------------------

func ( *Field) func( reflect.Value,  time.Time) error {
	 := .StructField.Type

	switch  {
	case timeType:
		return func( reflect.Value,  time.Time) error {
			 := .Addr().Interface().(*time.Time)
			* = 
			return nil
		}
	case nullTimeType:
		return func( reflect.Value,  time.Time) error {
			 := .Addr().Interface().(*sql.NullTime)
			* = sql.NullTime{Time: }
			return nil
		}
	case nullIntType:
		return func( reflect.Value,  time.Time) error {
			 := .Addr().Interface().(*sql.NullInt64)
			* = sql.NullInt64{Int64: .UnixNano()}
			return nil
		}
	}

	switch .IndirectType.Kind() {
	case reflect.Int64:
		return func( reflect.Value,  time.Time) error {
			 := .Addr().Interface().(*int64)
			* = .UnixNano()
			return nil
		}
	case reflect.Ptr:
		 = .Elem()
	default:
		return softDeleteFieldUpdaterFallback()
	}

	switch  { //nolint:gocritic
	case timeType:
		return func( reflect.Value,  time.Time) error {
			.Set(reflect.ValueOf(&))
			return nil
		}
	}

	switch .Kind() { //nolint:gocritic
	case reflect.Int64:
		return func( reflect.Value,  time.Time) error {
			 := .UnixNano()
			.Set(reflect.ValueOf(&))
			return nil
		}
	}

	return softDeleteFieldUpdaterFallback()
}

func ( *Field) func( reflect.Value,  time.Time) error {
	return func( reflect.Value,  time.Time) error {
		return .ScanWithCheck(, )
	}
}