// See: https://www.postgresql.org/docs/current/static/protocol-flow.html#PROTOCOL-COPY func (c *v3Conn) copyIn(columns []sql.ResultColumn) error { defer c.session.CopyEnd() c.writeBuf.initMsg(serverMsgCopyInResponse) c.writeBuf.writeByte(byte(formatText)) c.writeBuf.putInt16(int16(len(columns))) for range columns { c.writeBuf.putInt16(int16(formatText)) } if err := c.writeBuf.finishMsg(c.wr); err != nil { return err } if err := c.wr.Flush(); err != nil { return err } for { typ, n, err := c.readBuf.readTypedMsg(c.rd) c.metrics.BytesInCount.Inc(int64(n)) if err != nil { return err } var sr sql.StatementResults var done bool switch typ { case clientMsgCopyData: // Note: sql.Executor gets its Context from c.session.context, which // has been bound by v3Conn.setupSession(). sr = c.executor.CopyData(c.session, string(c.readBuf.msg)) case clientMsgCopyDone: // Note: sql.Executor gets its Context from c.session.context, which // has been bound by v3Conn.setupSession(). sr = c.executor.CopyDone(c.session) done = true case clientMsgCopyFail: done = true case clientMsgFlush, clientMsgSync: // Spec says to "ignore Flush and Sync messages received during copy-in mode". default: return c.sendErrorWithCode(pgerror.CodeProtocolViolationError, sqlbase.MakeSrcCtx(0), fmt.Sprintf("unrecognized client message type %s", typ)) } for _, res := range sr.ResultList { if res.Err != nil { return c.sendError(res.Err) } } if done { return nil } } }
// 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(ctx context.Context, reserved mon.BoundAccount) error { 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 } ctx = log.WithLogTagStr(ctx, "user", c.sessionArgs.User) if err := c.setupSession(ctx, reserved); err != nil { return err } defer c.closeSession(ctx) // Once a session has been set up, the underlying net.Conn is switched to // a conn that exits if the session's context is cancelled. c.conn = newReadTimeoutConn(c.conn, c.session.Ctx().Err) c.rd = bufio.NewReader(c.conn) 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(ctx, "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(ctx, "pgwire: ignoring %s till sync", typ) } continue } if log.V(2) { log.Infof(ctx, "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(ctx, &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() case clientMsgCopyData, clientMsgCopyDone, clientMsgCopyFail: // The spec says to drop any extra of these messages. default: err = c.sendErrorWithCode(pgerror.CodeProtocolViolationError, sqlbase.MakeSrcCtx(0), fmt.Sprintf("unrecognized client message type %s", typ)) } if err != nil { return err } } }