// TODO(andrei): Figure out the correct codes to send for all the errors // in this file and remove this function. func (c *v3Conn) sendInternalError(errToSend string) error { return c.sendErrorWithCode(pgerror.CodeInternalError, sqlbase.MakeSrcCtx(1), errToSend) }
func (c *v3Conn) serve(authenticationHook func(string, bool) error) error { ctx, cancel := context.WithCancel(context.Background()) defer cancel() if authenticationHook != nil { if err := authenticationHook(c.session.User, true /* public */); err != nil { return c.sendInternalError(err.Error()) } } c.writeBuf.initMsg(serverMsgAuth) c.writeBuf.putInt32(authOK) if err := c.writeBuf.finishMsg(c.wr); err != nil { return err } for key, value := range statusReportParams { c.writeBuf.initMsg(serverMsgParameterStatus) c.writeBuf.writeTerminatedString(key) c.writeBuf.writeTerminatedString(value) if err := c.writeBuf.finishMsg(c.wr); err != nil { return err } } if err := c.wr.Flush(); err != nil { return err } for { if !c.doingExtendedQueryMessage { c.writeBuf.initMsg(serverMsgReady) var txnStatus byte switch c.session.TxnState.State { case sql.Aborted, sql.RestartWait: // We send status "InFailedTransaction" also for state RestartWait // because GO's lib/pq freaks out if we invent a new status. txnStatus = 'E' case sql.Open: txnStatus = 'T' case sql.NoTxn: // We're not in a txn (i.e. the last txn was committed). txnStatus = 'I' case sql.CommitWait: // We need to lie to pgwire and claim that we're still // in a txn. Otherwise drivers freak out. // This state is not part of the Postgres protocol. txnStatus = 'T' } if log.V(2) { log.Infof("pgwire: %s: %q", serverMsgReady, txnStatus) } c.writeBuf.writeByte(txnStatus) if err := c.writeBuf.finishMsg(c.wr); err != nil { return err } // We only flush on every message if not doing an extended query. // If we are, wait for an explicit Flush message. See: // http://www.postgresql.org/docs/current/static/protocol-flow.html#PROTOCOL-FLOW-EXT-QUERY. if err := c.wr.Flush(); err != nil { return err } } typ, n, err := c.readBuf.readTypedMsg(c.rd) c.metrics.bytesInCount.Inc(int64(n)) if err != nil { return err } // When an error occurs handling an extended query message, we have to ignore // any messages until we get a sync. if c.ignoreTillSync && typ != clientMsgSync { if log.V(2) { log.Infof("pgwire: ignoring %s till sync", typ) } continue } if log.V(2) { log.Infof("pgwire: processing %s", typ) } switch typ { case clientMsgSync: c.doingExtendedQueryMessage = false c.ignoreTillSync = false case clientMsgSimpleQuery: c.doingExtendedQueryMessage = false err = c.handleSimpleQuery(ctx, &c.readBuf) case clientMsgTerminate: return nil case clientMsgParse: c.doingExtendedQueryMessage = true err = c.handleParse(ctx, &c.readBuf) case clientMsgDescribe: c.doingExtendedQueryMessage = true err = c.handleDescribe(&c.readBuf) case clientMsgClose: c.doingExtendedQueryMessage = true err = c.handleClose(&c.readBuf) case clientMsgBind: c.doingExtendedQueryMessage = true err = c.handleBind(&c.readBuf) case clientMsgExecute: c.doingExtendedQueryMessage = true err = c.handleExecute(ctx, &c.readBuf) case clientMsgFlush: c.doingExtendedQueryMessage = true err = c.wr.Flush() default: err = c.sendErrorWithCode(pgerror.CodeProtocolViolationError, sqlbase.MakeSrcCtx(0), fmt.Sprintf("unrecognized client message type %s", typ)) } if err != nil { return err } } }