package pgtype

import (
	
	
	
	
	
	
	
	

	
)
Information on the internals of PostgreSQL arrays can be found in src/include/utils/array.h and src/backend/utils/adt/arrayfuncs.c. Of particular interest is the array_send function.

type ArrayHeader struct {
	ContainsNull bool
	ElementOID   int32
	Dimensions   []ArrayDimension
}

type ArrayDimension struct {
	Length     int32
	LowerBound int32
}

func ( *ArrayHeader) ( *ConnInfo,  []byte) (int, error) {
	if len() < 12 {
		return 0, fmt.Errorf("array header too short: %d", len())
	}

	 := 0

	 := int(binary.BigEndian.Uint32([:]))
	 += 4

	.ContainsNull = binary.BigEndian.Uint32([:]) == 1
	 += 4

	.ElementOID = int32(binary.BigEndian.Uint32([:]))
	 += 4

	if  > 0 {
		.Dimensions = make([]ArrayDimension, )
	}
	if len() < 12+*8 {
		return 0, fmt.Errorf("array header too short for %d dimensions: %d", , len())
	}
	for  := range .Dimensions {
		.Dimensions[].Length = int32(binary.BigEndian.Uint32([:]))
		 += 4

		.Dimensions[].LowerBound = int32(binary.BigEndian.Uint32([:]))
		 += 4
	}

	return , nil
}

func ( ArrayHeader) ( *ConnInfo,  []byte) []byte {
	 = pgio.AppendInt32(, int32(len(.Dimensions)))

	var  int32
	if .ContainsNull {
		 = 1
	}
	 = pgio.AppendInt32(, )

	 = pgio.AppendInt32(, .ElementOID)

	for  := range .Dimensions {
		 = pgio.AppendInt32(, .Dimensions[].Length)
		 = pgio.AppendInt32(, .Dimensions[].LowerBound)
	}

	return 
}

type UntypedTextArray struct {
	Elements   []string
	Quoted     []bool
	Dimensions []ArrayDimension
}

func ( string) (*UntypedTextArray, error) {
	 := &UntypedTextArray{}

	 := bytes.NewBufferString()

	skipWhitespace()

	, ,  := .ReadRune()
	if  != nil {
		return nil, fmt.Errorf("invalid array: %v", )
	}

	var  []ArrayDimension
Array has explicit dimensions
	if  == '[' {
		.UnreadRune()

		for {
			, _,  = .ReadRune()
			if  != nil {
				return nil, fmt.Errorf("invalid array: %v", )
			}

			if  == '=' {
				break
			} else if  != '[' {
				return nil, fmt.Errorf("invalid array, expected '[' or '=' got %v", )
			}

			,  := arrayParseInteger()
			if  != nil {
				return nil, fmt.Errorf("invalid array: %v", )
			}

			, _,  = .ReadRune()
			if  != nil {
				return nil, fmt.Errorf("invalid array: %v", )
			}

			if  != ':' {
				return nil, fmt.Errorf("invalid array, expected ':' got %v", )
			}

			,  := arrayParseInteger()
			if  != nil {
				return nil, fmt.Errorf("invalid array: %v", )
			}

			, _,  = .ReadRune()
			if  != nil {
				return nil, fmt.Errorf("invalid array: %v", )
			}

			if  != ']' {
				return nil, fmt.Errorf("invalid array, expected ']' got %v", )
			}

			 = append(, ArrayDimension{LowerBound: , Length:  -  + 1})
		}

		, _,  = .ReadRune()
		if  != nil {
			return nil, fmt.Errorf("invalid array: %v", )
		}
	}

	if  != '{' {
		return nil, fmt.Errorf("invalid array, expected '{': %v", )
	}

	 := []ArrayDimension{{LowerBound: 1, Length: 0}}
Consume all initial opening brackets. This provides number of dimensions.
	for {
		, _,  = .ReadRune()
		if  != nil {
			return nil, fmt.Errorf("invalid array: %v", )
		}

		if  == '{' {
			[len()-1].Length = 1
			 = append(, ArrayDimension{LowerBound: 1})
		} else {
			.UnreadRune()
			break
		}
	}
	 := len() - 1
	 := 

	for {
		, _,  = .ReadRune()
		if  != nil {
			return nil, fmt.Errorf("invalid array: %v", )
		}

		switch  {
		case '{':
			if  ==  {
				[].Length++
			}
			++
		case ',':
		case '}':
			--
			if  <  {
				 = 
			}
		default:
			.UnreadRune()
			, ,  := arrayParseValue()
			if  != nil {
				return nil, fmt.Errorf("invalid array value: %v", )
			}
			if  ==  {
				[].Length++
			}
			.Quoted = append(.Quoted, )
			.Elements = append(.Elements, )
		}

		if  < 0 {
			break
		}
	}

	skipWhitespace()

	if .Len() > 0 {
		return nil, fmt.Errorf("unexpected trailing data: %v", .String())
	}

	if len(.Elements) == 0 {
		.Dimensions = nil
	} else if len() > 0 {
		.Dimensions = 
	} else {
		.Dimensions = 
	}

	return , nil
}

func ( *bytes.Buffer) {
	var  rune
	var  error
	for , _, _ = .ReadRune(); unicode.IsSpace(); , _, _ = .ReadRune() {
	}

	if  != io.EOF {
		.UnreadRune()
	}
}

func ( *bytes.Buffer) (string, bool, error) {
	, ,  := .ReadRune()
	if  != nil {
		return "", false, 
	}
	if  == '"' {
		return arrayParseQuotedValue()
	}
	.UnreadRune()

	 := &bytes.Buffer{}

	for {
		, ,  := .ReadRune()
		if  != nil {
			return "", false, 
		}

		switch  {
		case ',', '}':
			.UnreadRune()
			return .String(), false, nil
		}

		.WriteRune()
	}
}

func ( *bytes.Buffer) (string, bool, error) {
	 := &bytes.Buffer{}

	for {
		, ,  := .ReadRune()
		if  != nil {
			return "", false, 
		}

		switch  {
		case '\\':
			, _,  = .ReadRune()
			if  != nil {
				return "", false, 
			}
		case '"':
			, _,  = .ReadRune()
			if  != nil {
				return "", false, 
			}
			.UnreadRune()
			return .String(), true, nil
		}
		.WriteRune()
	}
}

func ( *bytes.Buffer) (int32, error) {
	 := &bytes.Buffer{}

	for {
		, ,  := .ReadRune()
		if  != nil {
			return 0, 
		}

		if '0' <=  &&  <= '9' {
			.WriteRune()
		} else {
			.UnreadRune()
			,  := strconv.ParseInt(.String(), 10, 32)
			if  != nil {
				return 0, 
			}
			return int32(), nil
		}
	}
}

func ( []byte,  []ArrayDimension) []byte {
	var  bool
	for ,  := range  {
		if .LowerBound != 1 {
			 = true
		}
	}

	if ! {
		return 
	}

	for ,  := range  {
		 = append(, '[')
		 = append(, strconv.FormatInt(int64(.LowerBound), 10)...)
		 = append(, ':')
		 = append(, strconv.FormatInt(int64(.LowerBound+.Length-1), 10)...)
		 = append(, ']')
	}

	return append(, '=')
}

var quoteArrayReplacer = strings.NewReplacer(`\`, `\\`, `"`, `\"`)

func ( string) string {
	return `"` + quoteArrayReplacer.Replace() + `"`
}

see https://github.com/postgres/postgres/blob/REL_12_STABLE/src/backend/parser/scansup.c#L224
	return  == ' ' ||  == '\t' ||  == '\n' ||  == '\r' ||  == '\f'
}

func ( string) string {
	if  == "" || (len() == 4 && strings.ToLower() == "null") || isSpace([0]) || isSpace([len()-1]) || strings.ContainsAny(, `{},"\`) {
		return quoteArrayElement()
	}
	return 
}

func ( reflect.Value,  []ArrayDimension,  int) ([]ArrayDimension, int, bool) {
	switch .Kind() {
	case reflect.Array:
		fallthrough
	case reflect.Slice:
		 := .Len()
		if 0 ==  {
			 = 
		} else {
			 *= 
		}
		 = append(, ArrayDimension{Length: int32(), LowerBound: 1})
		for  := 0;  < ; ++ {
			if , ,  := (.Index(), , );  {
				return , , true
			}
		}
	}
	return , , true