package postgres

import (
	
	
	
	
	

	
	
	
	
	
	
	
	
)

type Dialector struct {
	*Config
}

type Config struct {
	DriverName           string
	DSN                  string
	WithoutQuotingCheck  bool
	PreferSimpleProtocol bool
	WithoutReturning     bool
	Conn                 gorm.ConnPool
}

func ( string) gorm.Dialector {
	return &Dialector{&Config{DSN: }}
}

func ( Config) gorm.Dialector {
	return &Dialector{Config: &}
}

func ( Dialector) () string {
	return "postgres"
}

var timeZoneMatcher = regexp.MustCompile("(time_zone|TimeZone)=(.*?)($|&| )")

func ( Dialector) ( *gorm.DB) ( error) {
	 := &callbacks.Config{
		CreateClauses: []string{"INSERT", "VALUES", "ON CONFLICT"},
		UpdateClauses: []string{"UPDATE", "SET", "FROM", "WHERE"},
		DeleteClauses: []string{"DELETE", "FROM", "WHERE"},
	}
	// register callbacks
	if !.WithoutReturning {
		.CreateClauses = append(.CreateClauses, "RETURNING")
		.UpdateClauses = append(.UpdateClauses, "RETURNING")
		.DeleteClauses = append(.DeleteClauses, "RETURNING")
	}
	callbacks.RegisterDefaultCallbacks(, )

	if .Conn != nil {
		.ConnPool = .Conn
	} else if .DriverName != "" {
		.ConnPool,  = sql.Open(.DriverName, .Config.DSN)
	} else {
		var  *pgx.ConnConfig

		,  = pgx.ParseConfig(.Config.DSN)
		if  != nil {
			return
		}
		if .Config.PreferSimpleProtocol {
			.DefaultQueryExecMode = pgx.QueryExecModeSimpleProtocol
		}
		 := timeZoneMatcher.FindStringSubmatch(.Config.DSN)
		if len() > 2 {
			.RuntimeParams["timezone"] = [2]
		}
		.ConnPool = stdlib.OpenDB(*)
	}
	return
}

func ( Dialector) ( *gorm.DB) gorm.Migrator {
	return Migrator{migrator.Migrator{Config: migrator.Config{
		DB:                          ,
		Dialector:                   ,
		CreateIndexAfterCreateTable: true,
	}}}
}

func ( Dialector) ( *schema.Field) clause.Expression {
	return clause.Expr{SQL: "DEFAULT"}
}

func ( Dialector) ( clause.Writer,  *gorm.Statement,  interface{}) {
	.WriteByte('$')
	.WriteString(strconv.Itoa(len(.Vars)))
}

func ( Dialector) ( clause.Writer,  string) {
	if .WithoutQuotingCheck {
		.WriteString()
		return
	}

	var (
		,  bool
		      int8
		          int8
	)

	for ,  := range []byte() {
		switch  {
		case '"':
			++
			if  == 2 {
				.WriteString(`""`)
				 = 0
			}
		case '.':
			if  > 0 || ! {
				 = 0
				 = false
				 = 0
				.WriteByte('"')
			}
			.WriteByte()
			continue
		default:
			if - <= 0 && ! {
				.WriteByte('"')
				 = true
				if  =  > 0;  {
					 -= 1
				}
			}

			for ;  > 0;  -= 1 {
				.WriteString(`""`)
			}

			.WriteByte()
		}
		++
	}

	if  > 0 && ! {
		.WriteString(`""`)
	}
	.WriteByte('"')
}

var numericPlaceholder = regexp.MustCompile(`\$(\d+)`)

func ( Dialector) ( string,  ...interface{}) string {
	return logger.ExplainSQL(, numericPlaceholder, `'`, ...)
}

func ( Dialector) ( *schema.Field) string {
	switch .DataType {
	case schema.Bool:
		return "boolean"
	case schema.Int, schema.Uint:
		 := .Size
		if .DataType == schema.Uint {
			++
		}
		if .AutoIncrement {
			switch {
			case  <= 16:
				return "smallserial"
			case  <= 32:
				return "serial"
			default:
				return "bigserial"
			}
		} else {
			switch {
			case  <= 16:
				return "smallint"
			case  <= 32:
				return "integer"
			default:
				return "bigint"
			}
		}
	case schema.Float:
		if .Precision > 0 {
			if .Scale > 0 {
				return fmt.Sprintf("numeric(%d, %d)", .Precision, .Scale)
			}
			return fmt.Sprintf("numeric(%d)", .Precision)
		}
		return "decimal"
	case schema.String:
		if .Size > 0 {
			return fmt.Sprintf("varchar(%d)", .Size)
		}
		return "text"
	case schema.Time:
		if .Precision > 0 {
			return fmt.Sprintf("timestamptz(%d)", .Precision)
		}
		return "timestamptz"
	case schema.Bytes:
		return "bytea"
	default:
		return .getSchemaCustomType()
	}
}

func ( Dialector) ( *schema.Field) string {
	 := string(.DataType)

	if .AutoIncrement && !strings.Contains(strings.ToLower(), "serial") {
		 := .Size
		if .GORMDataType == schema.Uint {
			++
		}
		switch {
		case  <= 16:
			 = "smallserial"
		case  <= 32:
			 = "serial"
		default:
			 = "bigserial"
		}
	}

	return 
}

func ( Dialector) ( *gorm.DB,  string) error {
	.Exec("SAVEPOINT " + )
	return nil
}

func ( Dialector) ( *gorm.DB,  string) error {
	.Exec("ROLLBACK TO SAVEPOINT " + )
	return nil
}

func getSerialDatabaseType( string) ( string,  bool) {
	switch  {
	case "smallserial":
		return "smallint", true
	case "serial":
		return "integer", true
	case "bigserial":
		return "bigint", true
	default:
		return "", false
	}
}