good morning!!!!

Skip to content
Snippets Groups Projects
query.go 6.63 KiB
Newer Older
  • Learn to ignore specific revisions
  • Garet Halliday's avatar
    Garet Halliday committed
    	"log"
    
    Garet Halliday's avatar
    Garet Halliday committed
    	"strings"
    
    Garet Halliday's avatar
    Garet Halliday committed
    
    
    Garet Halliday's avatar
    Garet Halliday committed
    	"gfx.cafe/gfx/pggat/lib/fed"
    
    Garet Halliday's avatar
    Garet Halliday committed
    	packets "gfx.cafe/gfx/pggat/lib/fed/packets/v3.0"
    	"gfx.cafe/gfx/pggat/lib/util/strutil"
    
    Garet Halliday's avatar
    Garet Halliday committed
    func copyIn(ctx *context) error {
    
    Garet Halliday's avatar
    Garet Halliday committed
    	ctx.PeerWrite()
    
    Garet Halliday's avatar
    Garet Halliday committed
    
    	for {
    
    Garet Halliday's avatar
    Garet Halliday committed
    		if !ctx.PeerRead() {
    
    Garet Halliday's avatar
    Garet Halliday committed
    			copyFail := packets.CopyFail("peer failed")
    			ctx.Packet = &copyFail
    
    Garet Halliday's avatar
    Garet Halliday committed
    			return ctx.ServerWrite()
    
    Garet Halliday's avatar
    Garet Halliday committed
    		}
    
    
    Garet Halliday's avatar
    Garet Halliday committed
    		switch ctx.Packet.Type() {
    
    Garet Halliday's avatar
    Garet Halliday committed
    		case packets.TypeCopyData:
    
    Garet Halliday's avatar
    Garet Halliday committed
    			if err := ctx.ServerWrite(); err != nil {
    
    Garet Halliday's avatar
    Garet Halliday committed
    				return err
    			}
    		case packets.TypeCopyDone, packets.TypeCopyFail:
    
    Garet Halliday's avatar
    Garet Halliday committed
    			return ctx.ServerWrite()
    
    Garet Halliday's avatar
    Garet Halliday committed
    		default:
    
    Garet Halliday's avatar
    Garet Halliday committed
    			ctx.PeerFail(ctx.ErrUnexpectedPacket())
    
    Garet Halliday's avatar
    Garet Halliday committed
    		}
    	}
    }
    
    
    Garet Halliday's avatar
    Garet Halliday committed
    func copyOut(ctx *context) error {
    
    Garet Halliday's avatar
    Garet Halliday committed
    	ctx.PeerWrite()
    
    Garet Halliday's avatar
    Garet Halliday committed
    
    	for {
    
    Garet Halliday's avatar
    Garet Halliday committed
    		err := ctx.ServerRead()
    
    Garet Halliday's avatar
    Garet Halliday committed
    		if err != nil {
    
    Garet Halliday's avatar
    Garet Halliday committed
    			return err
    		}
    
    
    Garet Halliday's avatar
    Garet Halliday committed
    		switch ctx.Packet.Type() {
    
    Garet Halliday's avatar
    Garet Halliday committed
    		case packets.TypeCopyData,
    			packets.TypeNoticeResponse,
    			packets.TypeParameterStatus,
    			packets.TypeNotificationResponse:
    
    Garet Halliday's avatar
    Garet Halliday committed
    			ctx.PeerWrite()
    
    Garet Halliday's avatar
    Garet Halliday committed
    		case packets.TypeCopyDone, packets.TypeErrorResponse:
    
    Garet Halliday's avatar
    Garet Halliday committed
    			ctx.PeerWrite()
    
    Garet Halliday's avatar
    Garet Halliday committed
    			return nil
    		default:
    
    Garet Halliday's avatar
    Garet Halliday committed
    			return ctx.ErrUnexpectedPacket()
    
    Garet Halliday's avatar
    Garet Halliday committed
    		}
    	}
    }
    
    
    Garet Halliday's avatar
    Garet Halliday committed
    func query(ctx *context) error {
    
    Garet Halliday's avatar
    Garet Halliday committed
    	if err := ctx.ServerWrite(); err != nil {
    
    Garet Halliday's avatar
    Garet Halliday committed
    		err := ctx.ServerRead()
    
    Garet Halliday's avatar
    Garet Halliday committed
    		switch ctx.Packet.Type() {
    
    Garet Halliday's avatar
    Garet Halliday committed
    		case packets.TypeCommandComplete,
    			packets.TypeRowDescription,
    			packets.TypeDataRow,
    			packets.TypeEmptyQueryResponse,
    			packets.TypeErrorResponse,
    			packets.TypeNoticeResponse,
    			packets.TypeParameterStatus,
    			packets.TypeNotificationResponse:
    
    Garet Halliday's avatar
    Garet Halliday committed
    			ctx.PeerWrite()
    
    Garet Halliday's avatar
    Garet Halliday committed
    		case packets.TypeCopyInResponse:
    
    Garet Halliday's avatar
    Garet Halliday committed
    			if err = copyIn(ctx); err != nil {
    
    Garet Halliday's avatar
    Garet Halliday committed
    		case packets.TypeCopyOutResponse:
    
    Garet Halliday's avatar
    Garet Halliday committed
    			if err = copyOut(ctx); err != nil {
    
    Garet Halliday's avatar
    Garet Halliday committed
    				return err
    
    Garet Halliday's avatar
    Garet Halliday committed
    		case packets.TypeReadyForQuery:
    
    Garet Halliday's avatar
    Garet Halliday committed
    			var p packets.ReadyForQuery
    			err = fed.ToConcrete(&p, ctx.Packet)
    
    Garet Halliday's avatar
    Garet Halliday committed
    			if err != nil {
    				return err
    
    Garet Halliday's avatar
    Garet Halliday committed
    			}
    
    Garet Halliday's avatar
    Garet Halliday committed
    			ctx.Packet = &p
    			ctx.TxState = byte(p)
    
    Garet Halliday's avatar
    Garet Halliday committed
    			ctx.PeerWrite()
    
    Garet Halliday's avatar
    Garet Halliday committed
    			return nil
    		default:
    
    Garet Halliday's avatar
    Garet Halliday committed
    			return ctx.ErrUnexpectedPacket()
    
    Garet Halliday's avatar
    Garet Halliday committed
    		}
    	}
    }
    
    
    Garet Halliday's avatar
    Garet Halliday committed
    func queryString(ctx *context, q string) error {
    	qq := packets.Query(q)
    
    Garet Halliday's avatar
    Garet Halliday committed
    	ctx.Packet = &qq
    
    Garet Halliday's avatar
    Garet Halliday committed
    	return query(ctx)
    
    Garet Halliday's avatar
    Garet Halliday committed
    }
    
    
    Garet Halliday's avatar
    Garet Halliday committed
    func QueryString(server, peer *fed.Conn, query string) (err, peerError error) {
    
    Garet Halliday's avatar
    Garet Halliday committed
    	ctx := context{
    		Server: server,
    		Peer:   peer,
    	}
    	err = queryString(&ctx, query)
    	peerError = ctx.PeerError
    	return
    }
    
    
    Garet Halliday's avatar
    Garet Halliday committed
    func SetParameter(server, peer *fed.Conn, name strutil.CIString, value string) (err, peerError error) {
    
    Garet Halliday's avatar
    Garet Halliday committed
    	var q strings.Builder
    	escapedName := strutil.Escape(name.String(), '"')
    	escapedValue := strutil.Escape(value, '\'')
    	q.Grow(len(`SET "" = ''`) + len(escapedName) + len(escapedValue))
    	q.WriteString(`SET "`)
    	q.WriteString(escapedName)
    	q.WriteString(`" = '`)
    	q.WriteString(escapedValue)
    	q.WriteString(`'`)
    
    
    Garet Halliday's avatar
    Garet Halliday committed
    	return QueryString(
    
    Garet Halliday's avatar
    Garet Halliday committed
    		server,
    		peer,
    
    Garet Halliday's avatar
    Garet Halliday committed
    		q.String(),
    
    Garet Halliday's avatar
    Garet Halliday committed
    	)
    
    Garet Halliday's avatar
    Garet Halliday committed
    func functionCall(ctx *context) error {
    
    Garet Halliday's avatar
    Garet Halliday committed
    	if err := ctx.ServerWrite(); err != nil {
    
    Garet Halliday's avatar
    Garet Halliday committed
    		return err
    	}
    
    	for {
    
    Garet Halliday's avatar
    Garet Halliday committed
    		err := ctx.ServerRead()
    
    Garet Halliday's avatar
    Garet Halliday committed
    		if err != nil {
    
    Garet Halliday's avatar
    Garet Halliday committed
    			return err
    		}
    
    
    Garet Halliday's avatar
    Garet Halliday committed
    		switch ctx.Packet.Type() {
    
    Garet Halliday's avatar
    Garet Halliday committed
    		case packets.TypeErrorResponse,
    			packets.TypeFunctionCallResponse,
    			packets.TypeNoticeResponse,
    			packets.TypeParameterStatus,
    			packets.TypeNotificationResponse:
    
    Garet Halliday's avatar
    Garet Halliday committed
    			ctx.PeerWrite()
    
    Garet Halliday's avatar
    Garet Halliday committed
    		case packets.TypeReadyForQuery:
    
    Garet Halliday's avatar
    Garet Halliday committed
    			var p packets.ReadyForQuery
    			err = fed.ToConcrete(&p, ctx.Packet)
    
    Garet Halliday's avatar
    Garet Halliday committed
    			if err != nil {
    				return err
    
    Garet Halliday's avatar
    Garet Halliday committed
    			}
    
    Garet Halliday's avatar
    Garet Halliday committed
    			ctx.Packet = &p
    			ctx.TxState = byte(p)
    
    Garet Halliday's avatar
    Garet Halliday committed
    			ctx.PeerWrite()
    
    Garet Halliday's avatar
    Garet Halliday committed
    			return nil
    		default:
    
    Garet Halliday's avatar
    Garet Halliday committed
    			return ctx.ErrUnexpectedPacket()
    
    Garet Halliday's avatar
    Garet Halliday committed
    		}
    	}
    }
    
    
    Garet Halliday's avatar
    Garet Halliday committed
    func sync(ctx *context) (bool, error) {
    
    Garet Halliday's avatar
    Garet Halliday committed
    	if err := ctx.ServerWrite(); err != nil {
    
    Garet Halliday's avatar
    Garet Halliday committed
    		return false, err
    
    Garet Halliday's avatar
    Garet Halliday committed
    	}
    
    
    Garet Halliday's avatar
    Garet Halliday committed
    	var i int
    
    Garet Halliday's avatar
    Garet Halliday committed
    	for {
    
    Garet Halliday's avatar
    Garet Halliday committed
    		i++
    
    
    Garet Halliday's avatar
    Garet Halliday committed
    		err := ctx.ServerRead()
    
    Garet Halliday's avatar
    Garet Halliday committed
    		if err != nil {
    
    Garet Halliday's avatar
    Garet Halliday committed
    			return false, err
    
    Garet Halliday's avatar
    Garet Halliday committed
    		}
    
    
    Garet Halliday's avatar
    Garet Halliday committed
    		if i > 10000 {
    			log.Printf("long sync, packet %c", ctx.Packet.Type())
    		}
    
    
    Garet Halliday's avatar
    Garet Halliday committed
    		switch ctx.Packet.Type() {
    
    Garet Halliday's avatar
    Garet Halliday committed
    		case packets.TypeParseComplete,
    			packets.TypeBindComplete,
    
    			packets.TypeCloseComplete,
    
    Garet Halliday's avatar
    Garet Halliday committed
    			packets.TypeErrorResponse,
    			packets.TypeRowDescription,
    			packets.TypeNoData,
    			packets.TypeParameterDescription,
    
    Garet Halliday's avatar
    Garet Halliday committed
    
    
    Garet Halliday's avatar
    Garet Halliday committed
    			packets.TypeCommandComplete,
    			packets.TypeDataRow,
    			packets.TypeEmptyQueryResponse,
    			packets.TypePortalSuspended,
    
    Garet Halliday's avatar
    Garet Halliday committed
    
    
    Garet Halliday's avatar
    Garet Halliday committed
    			packets.TypeNoticeResponse,
    			packets.TypeParameterStatus,
    			packets.TypeNotificationResponse:
    
    Garet Halliday's avatar
    Garet Halliday committed
    			ctx.PeerWrite()
    
    Garet Halliday's avatar
    Garet Halliday committed
    		case packets.TypeCopyInResponse:
    
    Garet Halliday's avatar
    Garet Halliday committed
    			if err = copyIn(ctx); err != nil {
    
    Garet Halliday's avatar
    Garet Halliday committed
    				return false, err
    
    Garet Halliday's avatar
    Garet Halliday committed
    			}
    
    Garet Halliday's avatar
    Garet Halliday committed
    			// why
    			return false, nil
    
    Garet Halliday's avatar
    Garet Halliday committed
    		case packets.TypeCopyOutResponse:
    
    Garet Halliday's avatar
    Garet Halliday committed
    			if err = copyOut(ctx); err != nil {
    
    Garet Halliday's avatar
    Garet Halliday committed
    				return false, err
    
    Garet Halliday's avatar
    Garet Halliday committed
    			}
    
    Garet Halliday's avatar
    Garet Halliday committed
    		case packets.TypeReadyForQuery:
    
    Garet Halliday's avatar
    Garet Halliday committed
    			var p packets.ReadyForQuery
    			err = fed.ToConcrete(&p, ctx.Packet)
    
    Garet Halliday's avatar
    Garet Halliday committed
    			if err != nil {
    				return false, err
    
    Garet Halliday's avatar
    Garet Halliday committed
    			ctx.Packet = &p
    			ctx.TxState = byte(p)
    
    Garet Halliday's avatar
    Garet Halliday committed
    			ctx.PeerWrite()
    
    Garet Halliday's avatar
    Garet Halliday committed
    			return true, nil
    
    Garet Halliday's avatar
    Garet Halliday committed
    			return false, ctx.ErrUnexpectedPacket()
    
    Garet Halliday's avatar
    Garet Halliday committed
    
    
    Garet Halliday's avatar
    Garet Halliday committed
    func Sync(server, peer *fed.Conn) (err, peerErr error) {
    
    Garet Halliday's avatar
    Garet Halliday committed
    	ctx := context{
    		Server: server,
    		Peer:   peer,
    
    Garet Halliday's avatar
    Garet Halliday committed
    		Packet: &packets.Sync{},
    
    Garet Halliday's avatar
    Garet Halliday committed
    	}
    	_, err = sync(&ctx)
    	peerErr = ctx.PeerError
    	return
    }
    
    func eqp(ctx *context) error {
    
    Garet Halliday's avatar
    Garet Halliday committed
    	if err := ctx.ServerWrite(); err != nil {
    
    Garet Halliday's avatar
    Garet Halliday committed
    		return err
    	}
    
    	for {
    
    Garet Halliday's avatar
    Garet Halliday committed
    		if !ctx.PeerRead() {
    
    Garet Halliday's avatar
    Garet Halliday committed
    			for {
    
    Garet Halliday's avatar
    Garet Halliday committed
    				ctx.Packet = &packets.Sync{}
    
    Garet Halliday's avatar
    Garet Halliday committed
    				ok, err := sync(ctx)
    
    Garet Halliday's avatar
    Garet Halliday committed
    				if err != nil {
    					return err
    				}
    				if ok {
    					return nil
    				}
    			}
    
    Garet Halliday's avatar
    Garet Halliday committed
    		}
    
    
    Garet Halliday's avatar
    Garet Halliday committed
    		switch ctx.Packet.Type() {
    
    Garet Halliday's avatar
    Garet Halliday committed
    		case packets.TypeSync:
    
    Garet Halliday's avatar
    Garet Halliday committed
    			ok, err := sync(ctx)
    
    Garet Halliday's avatar
    Garet Halliday committed
    			if err != nil {
    				return err
    			}
    			if ok {
    				return nil
    			}
    
    Garet Halliday's avatar
    Garet Halliday committed
    		case packets.TypeParse, packets.TypeBind, packets.TypeClose, packets.TypeDescribe, packets.TypeExecute, packets.TypeFlush:
    
    Garet Halliday's avatar
    Garet Halliday committed
    			if err := ctx.ServerWrite(); err != nil {
    
    Garet Halliday's avatar
    Garet Halliday committed
    				return err
    			}
    		default:
    
    Garet Halliday's avatar
    Garet Halliday committed
    			ctx.PeerFail(ctx.ErrUnexpectedPacket())
    
    Garet Halliday's avatar
    Garet Halliday committed
    		}
    	}
    }
    
    
    Garet Halliday's avatar
    Garet Halliday committed
    func transaction(ctx *context) error {
    
    Garet Halliday's avatar
    Garet Halliday committed
    	for {
    
    Garet Halliday's avatar
    Garet Halliday committed
    		switch ctx.Packet.Type() {
    
    Garet Halliday's avatar
    Garet Halliday committed
    		case packets.TypeQuery:
    
    Garet Halliday's avatar
    Garet Halliday committed
    			if err := query(ctx); err != nil {
    
    Garet Halliday's avatar
    Garet Halliday committed
    				return err
    			}
    
    Garet Halliday's avatar
    Garet Halliday committed
    		case packets.TypeFunctionCall:
    
    Garet Halliday's avatar
    Garet Halliday committed
    			if err := functionCall(ctx); err != nil {
    
    Garet Halliday's avatar
    Garet Halliday committed
    				return err
    			}
    
    Garet Halliday's avatar
    Garet Halliday committed
    		case packets.TypeSync:
    
    Garet Halliday's avatar
    Garet Halliday committed
    			// phony sync call, we can just reply with a fake ReadyForQuery(TxState)
    
    Garet Halliday's avatar
    Garet Halliday committed
    			rfq := packets.ReadyForQuery(ctx.TxState)
    
    Garet Halliday's avatar
    Garet Halliday committed
    			ctx.Packet = &rfq
    
    Garet Halliday's avatar
    Garet Halliday committed
    			ctx.PeerWrite()
    
    Garet Halliday's avatar
    Garet Halliday committed
    		case packets.TypeParse, packets.TypeBind, packets.TypeClose, packets.TypeDescribe, packets.TypeExecute, packets.TypeFlush:
    
    Garet Halliday's avatar
    Garet Halliday committed
    			if err := eqp(ctx); err != nil {
    
    Garet Halliday's avatar
    Garet Halliday committed
    				return err
    			}
    		default:
    
    Garet Halliday's avatar
    Garet Halliday committed
    			ctx.PeerFail(ctx.ErrUnexpectedPacket())
    
    Garet Halliday's avatar
    Garet Halliday committed
    		}
    
    
    Garet Halliday's avatar
    Garet Halliday committed
    		if ctx.TxState == 'I' {
    
    Garet Halliday's avatar
    Garet Halliday committed
    			return nil
    		}
    
    
    Garet Halliday's avatar
    Garet Halliday committed
    		if !ctx.PeerRead() {
    
    Garet Halliday's avatar
    Garet Halliday committed
    			// abort tx
    
    Garet Halliday's avatar
    Garet Halliday committed
    			err := queryString(ctx, "ABORT;")
    
    Garet Halliday's avatar
    Garet Halliday committed
    			if err != nil {
    				return err
    			}
    
    			if ctx.TxState != 'I' {
    
    Garet Halliday's avatar
    Garet Halliday committed
    				return ErrExpectedIdle
    
    Garet Halliday's avatar
    Garet Halliday committed
    			}
    			return nil
    		}
    	}
    }
    
    Garet Halliday's avatar
    Garet Halliday committed
    
    
    Garet Halliday's avatar
    Garet Halliday committed
    func Transaction(server, peer *fed.Conn, initialPacket fed.Packet) (err, peerError error) {
    
    Garet Halliday's avatar
    Garet Halliday committed
    	ctx := context{
    		Server: server,
    		Peer:   peer,
    		Packet: initialPacket,
    	}
    	err = transaction(&ctx)
    	peerError = ctx.PeerError
    	return
    }