package logger

import (
	
	
	
	
	
	
	
	

	
)

const (
	tmFmtWithMS = "2006-01-02 15:04:05.999"
	tmFmtZero   = "0000-00-00 00:00:00"
	nullStr     = "NULL"
)

func isPrintable( string) bool {
	for ,  := range  {
		if !unicode.IsPrint() {
			return false
		}
	}
	return true
}

// A list of Go types that should be converted to SQL primitives
var convertibleTypes = []reflect.Type{reflect.TypeOf(time.Time{}), reflect.TypeOf(false), reflect.TypeOf([]byte{})}

// RegEx matches only numeric values
var numericPlaceholderRe = regexp.MustCompile(`\$\d+\$`)

// ExplainSQL generate SQL string with given parameters, the generated SQL is expected to be used in logger, execute it might introduce a SQL injection vulnerability
func ( string,  *regexp.Regexp,  string,  ...interface{}) string {
	var (
		 func(interface{}, int)
		          = make([]string, len())
	)

	 = func( interface{},  int) {
		switch v := .(type) {
		case bool:
			[] = strconv.FormatBool()
		case time.Time:
			if .IsZero() {
				[] =  + tmFmtZero + 
			} else {
				[] =  + .Format(tmFmtWithMS) + 
			}
		case *time.Time:
			if  != nil {
				if .IsZero() {
					[] =  + tmFmtZero + 
				} else {
					[] =  + .Format(tmFmtWithMS) + 
				}
			} else {
				[] = nullStr
			}
		case driver.Valuer:
			 := reflect.ValueOf()
			if  != nil && .IsValid() && ((.Kind() == reflect.Ptr && !.IsNil()) || .Kind() != reflect.Ptr) {
				,  := .Value()
				(, )
			} else {
				[] = nullStr
			}
		case fmt.Stringer:
			 := reflect.ValueOf()
			switch .Kind() {
			case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
				[] = fmt.Sprintf("%d", .Interface())
			case reflect.Float32, reflect.Float64:
				[] = fmt.Sprintf("%.6f", .Interface())
			case reflect.Bool:
				[] = fmt.Sprintf("%t", .Interface())
			case reflect.String:
				[] =  + strings.ReplaceAll(fmt.Sprintf("%v", ), , "\\"+) + 
			default:
				if  != nil && .IsValid() && ((.Kind() == reflect.Ptr && !.IsNil()) || .Kind() != reflect.Ptr) {
					[] =  + strings.ReplaceAll(fmt.Sprintf("%v", ), , "\\"+) + 
				} else {
					[] = nullStr
				}
			}
		case []byte:
			if  := string(); isPrintable() {
				[] =  + strings.ReplaceAll(, , "\\"+) + 
			} else {
				[] =  + "<binary>" + 
			}
		case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
			[] = utils.ToString()
		case float32:
			[] = strconv.FormatFloat(float64(), 'f', -1, 32)
		case float64:
			[] = strconv.FormatFloat(, 'f', -1, 64)
		case string:
			[] =  + strings.ReplaceAll(, , "\\"+) + 
		default:
			 := reflect.ValueOf()
			if  == nil || !.IsValid() || .Kind() == reflect.Ptr && .IsNil() {
				[] = nullStr
			} else if ,  := .(driver.Valuer);  {
				, _ = .Value()
				(, )
			} else if .Kind() == reflect.Ptr && !.IsZero() {
				(reflect.Indirect().Interface(), )
			} else {
				for ,  := range convertibleTypes {
					if .Type().ConvertibleTo() {
						(.Convert().Interface(), )
						return
					}
				}
				[] =  + strings.ReplaceAll(fmt.Sprint(), , "\\"+) + 
			}
		}
	}

	for ,  := range  {
		(, )
	}

	if  == nil {
		var  int
		var  strings.Builder

		for ,  := range []byte() {
			if  == '?' {
				if len() >  {
					.WriteString([])
					++
					continue
				}
			}
			.WriteByte()
		}

		 = .String()
	} else {
		 = .ReplaceAllString(, "$$$1$$")

		 = numericPlaceholderRe.ReplaceAllStringFunc(, func( string) string {
			 := [1 : len()-1]
			,  := strconv.Atoi()

			// position var start from 1 ($1, $2)
			 -= 1
			if  >= 0 &&  <= len()-1 {
				return []
			}
			return 
		})
	}

	return 
}