package pgx

import (
	
	
	
	
	
	
)

// NamedArgs can be used as the first argument to a query method. It will replace every '@' named placeholder with a '$'
// ordinal placeholder and construct the appropriate arguments.
//
// For example, the following two queries are equivalent:
//
//	conn.Query(ctx, "select * from widgets where foo = @foo and bar = @bar", pgx.NamedArgs{"foo": 1, "bar": 2})
//	conn.Query(ctx, "select * from widgets where foo = $1 and bar = $2", 1, 2)
//
// Named placeholders are case sensitive and must start with a letter or underscore. Subsequent characters can be
// letters, numbers, or underscores.

type NamedArgs map[string]any

// RewriteQuery implements the QueryRewriter interface.
func ( NamedArgs) ( context.Context,  *Conn,  string,  []any) ( string,  []any,  error) {
	return rewriteQuery(, , false)
}

// StrictNamedArgs can be used in the same way as NamedArgs, but provided arguments are also checked to include all
// named arguments that the sql query uses, and no extra arguments.
type StrictNamedArgs map[string]any

// RewriteQuery implements the QueryRewriter interface.
func ( StrictNamedArgs) ( context.Context,  *Conn,  string,  []any) ( string,  []any,  error) {
	return rewriteQuery(, , true)
}

type errorQueryRewriter struct {
	err error
}

func ( errorQueryRewriter) ( context.Context,  *Conn,  string,  []any) ( string,  []any,  error) {
	return "", nil, .err
}

// StructArgs converts exported fields of a struct into a QueryRewriter so it can
// be used as the first argument to a query method (e.g. "where id=@id").
//
// Field names are taken from the `db` struct tag if present. Tag values may
// include comma-separated options (e.g. `db:"id,omitempty"`). A `db:"-"` field is
// ignored. If no `db` tag is present, the Go field name is used.
//
// sa may be a struct or a pointer to a struct.
func ( any) QueryRewriter {
	,  := structArgs()
	if  != nil {
		return errorQueryRewriter{err: }
	}
	return NamedArgs()
}

// StrictStructArgs is like StructArgs but uses StrictNamedArgs rewriting
// semantics (i.e. errors if the SQL query references missing arguments or if
// extra arguments are provided).
func ( any) QueryRewriter {
	,  := structArgs()
	if  != nil {
		return errorQueryRewriter{err: }
	}
	return StrictNamedArgs()
}

func ( any) (map[string]any, error) {
	if  == nil {
		return nil, fmt.Errorf("StructArgs requires a struct or pointer to struct, got nil")
	}

	 := reflect.ValueOf()
	 := .Type()

	if .Kind() == reflect.Pointer {
		if .IsNil() {
			return nil, fmt.Errorf("StructArgs requires a non-nil pointer to struct")
		}
		 = .Elem()
		 = .Type()
	}

	if .Kind() != reflect.Struct {
		return nil, fmt.Errorf("StructArgs requires a struct or pointer to struct, got %s", )
	}

	 := make(map[string]any, .NumField())
	for  := 0;  < .NumField(); ++ {
		 := .Field()

		// Ignore unexported fields.
		if .PkgPath != "" {
			continue
		}

		, ,  := dbTagKey()
		if  != nil {
			return nil, 
		}
		if ! {
			continue
		}

		if ,  := [];  {
			return nil, fmt.Errorf("duplicate StructArgs key %q", )
		}

		[] = .Field().Interface()
	}

	return , nil
}

// dbTagKey derives the named-argument key for a struct field. Tag parsing matches
// RowToStructByName* in rows.go (structTagKey, Lookup, comma options, db:"-").
// Anonymous embedded structs are skipped without flattening (unlike row scanning).
func ( reflect.StructField) ( string,  bool,  error) {
	if .Anonymous {
		 := .Type
		if .Kind() == reflect.Pointer {
			 = .Elem()
		}
		if .Kind() == reflect.Struct {
			return "", false, nil
		}
	}

	,  := .Tag.Lookup(structTagKey)
	if  {
		, _, _ = strings.Cut(, ",")
	}
	if  == "-" {
		return "", false, nil
	}
	if  {
		if  == "" {
			return "", false, fmt.Errorf("field %s has empty `%s` tag", .Name, structTagKey)
		}
		return , true, nil
	}

	return .Name, true, nil
}

type namedArg string

type sqlLexer struct {
	src     string
	start   int
	pos     int
	nested  int // multiline comment nesting level.
	stateFn stateFn
	parts   []any

	nameToOrdinal map[namedArg]int
}

type stateFn func(*sqlLexer) stateFn

func ( map[string]any,  string,  bool) ( string,  []any,  error) {
	 := &sqlLexer{
		src:           ,
		stateFn:       rawState,
		nameToOrdinal: make(map[namedArg]int, len()),
	}

	for .stateFn != nil {
		.stateFn = .stateFn()
	}

	 := strings.Builder{}
	for ,  := range .parts {
		switch p := .(type) {
		case string:
			.WriteString()
		case namedArg:
			.WriteRune('$')
			.WriteString(strconv.Itoa(.nameToOrdinal[]))
		}
	}

	 = make([]any, len(.nameToOrdinal))
	for ,  := range .nameToOrdinal {
		var  bool
		[-1],  = [string()]
		if  && ! {
			return "", nil, fmt.Errorf("argument %s found in sql query but not present in StrictNamedArgs", )
		}
	}

	if  {
		for  := range  {
			if ,  := .nameToOrdinal[namedArg()]; ! {
				return "", nil, fmt.Errorf("argument %s of StrictNamedArgs not found in sql query", )
			}
		}
	}

	return .String(), , nil
}

func ( *sqlLexer) stateFn {
	for {
		,  := utf8.DecodeRuneInString(.src[.pos:])
		.pos += 

		switch  {
		case 'e', 'E':
			,  := utf8.DecodeRuneInString(.src[.pos:])
			if  == '\'' {
				.pos += 
				return escapeStringState
			}
		case '\'':
			return singleQuoteState
		case '"':
			return doubleQuoteState
		case '@':
			,  := utf8.DecodeRuneInString(.src[.pos:])
			if isLetter() ||  == '_' {
				if .pos-.start > 0 {
					.parts = append(.parts, .src[.start:.pos-])
				}
				.start = .pos
				return namedArgState
			}
		case '-':
			,  := utf8.DecodeRuneInString(.src[.pos:])
			if  == '-' {
				.pos += 
				return oneLineCommentState
			}
		case '/':
			,  := utf8.DecodeRuneInString(.src[.pos:])
			if  == '*' {
				.pos += 
				return multilineCommentState
			}
		case utf8.RuneError:
			if .pos-.start > 0 {
				.parts = append(.parts, .src[.start:.pos])
				.start = .pos
			}
			return nil
		}
	}
}

func ( rune) bool {
	return ( >= 'a' &&  <= 'z') || ( >= 'A' &&  <= 'Z')
}

func ( *sqlLexer) stateFn {
	for {
		,  := utf8.DecodeRuneInString(.src[.pos:])
		.pos += 

		if  == utf8.RuneError {
			if .pos-.start > 0 {
				 := namedArg(.src[.start:.pos])
				if ,  := .nameToOrdinal[]; ! {
					.nameToOrdinal[] = len(.nameToOrdinal) + 1
				}
				.parts = append(.parts, )
				.start = .pos
			}
			return nil
		} else if !(isLetter() || ( >= '0' &&  <= '9') ||  == '_') {
			.pos -= 
			 := namedArg(.src[.start:.pos])
			if ,  := .nameToOrdinal[]; ! {
				.nameToOrdinal[] = len(.nameToOrdinal) + 1
			}
			.parts = append(.parts, )
			.start = .pos
			return rawState
		}
	}
}

func ( *sqlLexer) stateFn {
	for {
		,  := utf8.DecodeRuneInString(.src[.pos:])
		.pos += 

		switch  {
		case '\'':
			,  := utf8.DecodeRuneInString(.src[.pos:])
			if  != '\'' {
				return rawState
			}
			.pos += 
		case utf8.RuneError:
			if .pos-.start > 0 {
				.parts = append(.parts, .src[.start:.pos])
				.start = .pos
			}
			return nil
		}
	}
}

func ( *sqlLexer) stateFn {
	for {
		,  := utf8.DecodeRuneInString(.src[.pos:])
		.pos += 

		switch  {
		case '"':
			,  := utf8.DecodeRuneInString(.src[.pos:])
			if  != '"' {
				return rawState
			}
			.pos += 
		case utf8.RuneError:
			if .pos-.start > 0 {
				.parts = append(.parts, .src[.start:.pos])
				.start = .pos
			}
			return nil
		}
	}
}

func ( *sqlLexer) stateFn {
	for {
		,  := utf8.DecodeRuneInString(.src[.pos:])
		.pos += 

		switch  {
		case '\\':
			_,  = utf8.DecodeRuneInString(.src[.pos:])
			.pos += 
		case '\'':
			,  := utf8.DecodeRuneInString(.src[.pos:])
			if  != '\'' {
				return rawState
			}
			.pos += 
		case utf8.RuneError:
			if .pos-.start > 0 {
				.parts = append(.parts, .src[.start:.pos])
				.start = .pos
			}
			return nil
		}
	}
}

func ( *sqlLexer) stateFn {
	for {
		,  := utf8.DecodeRuneInString(.src[.pos:])
		.pos += 

		switch  {
		case '\\':
			_,  = utf8.DecodeRuneInString(.src[.pos:])
			.pos += 
		case '\n', '\r':
			return rawState
		case utf8.RuneError:
			if .pos-.start > 0 {
				.parts = append(.parts, .src[.start:.pos])
				.start = .pos
			}
			return nil
		}
	}
}

func ( *sqlLexer) stateFn {
	for {
		,  := utf8.DecodeRuneInString(.src[.pos:])
		.pos += 

		switch  {
		case '/':
			,  := utf8.DecodeRuneInString(.src[.pos:])
			if  == '*' {
				.pos += 
				.nested++
			}
		case '*':
			,  := utf8.DecodeRuneInString(.src[.pos:])
			if  != '/' {
				continue
			}

			.pos += 
			if .nested == 0 {
				return rawState
			}
			.nested--

		case utf8.RuneError:
			if .pos-.start > 0 {
				.parts = append(.parts, .src[.start:.pos])
				.start = .pos
			}
			return nil
		}
	}
}