static int ReadCommand(StringInfo inBuf) {
int result;
if (whereToSendOutput == DestRemote) result = SocketBackend(inBuf);
else result = InteractiveBackend(inBuf);
return result;
}
ReadCommand 从前端或标准输入读取命令,将其放入 inBuf,并返回消息类型代码(消息的第一个字节)。 如果遇到文件结束则返回 EOF。我们这里只关注SocketBackend函数。SocketBackend() 为前端-后端连接调用。返回消息类型代码,并将消息体数据加载到 inBuf 中。 如果连接丢失,则返回 EOF。
static int SocketBackend(StringInfo inBuf) {
int qtype; int maxmsglen;
/* Get message type code from the frontend. 从前端获取消息类型代码 */
HOLD_CANCEL_INTERRUPTS();
pq_startmsgread();
qtype = pq_getbyte();
if (qtype == EOF){ /* frontend disconnected */
if (IsTransactionState())
ereport(COMMERROR, (errcode(ERRCODE_CONNECTION_FAILURE), errmsg("unexpected EOF on client connection with an open transaction")));
else {
/* Can't send DEBUG log messages to client at this point. Since we're disconnecting right away, we don't need to restore whereToSendOutput. 此时无法向客户端发送 DEBUG 日志消息。 由于我们立即断开连接,因此我们不需要恢复 whereToSendOutput。 */
whereToSendOutput = DestNone;
ereport(DEBUG1, (errcode(ERRCODE_CONNECTION_DOES_NOT_EXIST), errmsg_internal("unexpected EOF on client connection")));
}
return qtype;
}
// 在尝试读取正文之前验证消息类型代码; 如果我们失去了同步,最好说“命令未知”而不是内存不足,因为我们使用垃圾作为长度词。 我们还可以选择一个依赖于类型的限制来确定一个正常长度的单词。 (可以更精细地选择限制,但尚不清楚是否值得大惊小怪。)
//这也为我们提供了一个尽快设置doing_extended_query_message 标志的地方。
/* Validate message type code before trying to read body; if we have lost sync, better to say "command unknown" than to run out of memory because we used garbage as a length word. We can also select a type-dependent limit on what a sane length word could be. (The limit could be chosen more granularly, but it's not clear it's worth fussing over.)
* This also gives us a place to set the doing_extended_query_message flag as soon as possible. */
switch (qtype) {
case 'Q': /* simple query */
maxmsglen = PQ_LARGE_MESSAGE_LIMIT;
doing_extended_query_message = false;
break;
case 'F': /* fastpath function call */
maxmsglen = PQ_LARGE_MESSAGE_LIMIT;
doing_extended_query_message = false;
break;
case 'X': /* terminate */
maxmsglen = PQ_SMALL_MESSAGE_LIMIT;
doing_extended_query_message = false;
ignore_till_sync = false;
break;
case 'B': /* bind */
case 'P': /* parse */
maxmsglen = PQ_LARGE_MESSAGE_LIMIT;
doing_extended_query_message = true;
break;
case 'C': /* close */
case 'D': /* describe */
case 'E': /* execute */
case 'H': /* flush */
maxmsglen = PQ_SMALL_MESSAGE_LIMIT;
doing_extended_query_message = true;
break;
case 'S': /* sync */
maxmsglen = PQ_SMALL_MESSAGE_LIMIT;
/* stop any active skip-till-Sync */
ignore_till_sync = false;
/* mark not-extended, so that a new error doesn't begin skip */
doing_extended_query_message = false;
break;
case 'd': /* copy data */
maxmsglen = PQ_LARGE_MESSAGE_LIMIT;
doing_extended_query_message = false;
break;
case 'c': /* copy done */
case 'f': /* copy fail */
maxmsglen = PQ_SMALL_MESSAGE_LIMIT;
doing_extended_query_message = false;
break;
default: /* Otherwise we got garbage from the frontend. We treat this as fatal because we have probably lost message boundary sync, and there's no good way to recover. 否则我们会从前端得到垃圾。 我们认为这是致命的,因为我们可能丢失了消息边界同步,并且没有好的恢复方法。 */
ereport(FATAL,(errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("invalid frontend message type %d", qtype)));
maxmsglen = 0; /* keep compiler quiet */
break;
}
/* In protocol version 3, all frontend messages have a length word next after the type code; we can read the message contents independently of the type. 在协议版本 3 中,所有前端消息在类型代码之后都有一个长度字; 我们可以独立于类型读取消息内容。 */
if (pq_getmessage(inBuf, maxmsglen))
return EOF; /* suitable message already logged */
RESUME_CANCEL_INTERRUPTS();
return qtype;
}
HOLD_CANCEL_INTERRUPTS
RESUME_CANCEL_INTERRUPTS
为用户交互连接调用 InteractiveBackend(),用户输入的字符串放在它的参数inBuf中,我们就像收到一条Q消息一样。 如果看到文件结尾输入,则返回 EOF,代表是时候关闭了。
static int InteractiveBackend(StringInfo inBuf) {
int c; /* character read from getc() */
/* display a prompt and obtain input from the user */
printf("backend> ");
fflush(stdout);
resetStringInfo(inBuf);
/* Read characters until EOF or the appropriate delimiter is seen. 读取字符,直到看到 EOF 或适当的分隔符 */
while ((c = interactive_getc()) != EOF) {
if (c == '\n') {
if (UseSemiNewlineNewline) {
/* In -j mode, semicolon followed by two newlines ends the command; otherwise treat newline as regular character. 在 -j 模式下,分号后跟两个换行符结束命令; 否则将换行符视为常规字符 */
if (inBuf->len > 1 && inBuf->data[inBuf->len - 1] == '\n' && inBuf->data[inBuf->len - 2] == ';') {
break; /* might as well drop the second newline */
}
}else{ /* In plain mode, newline ends the command unless preceded by backslash. 在普通模式下,换行符结束命令,除非前面有反斜杠。 */
if (inBuf->len > 0 && inBuf->data[inBuf->len - 1] == '\\') {
/* discard backslash from inBuf */
inBuf->data[--inBuf->len] = '\0';
/* discard newline too */
continue;
}else{ /* keep the newline character, but end the command */
appendStringInfoChar(inBuf, '\n');
break;
}
}
}
/* Not newline, or newline treated as regular character 不是换行符,或换行符被视为常规字符 */
appendStringInfoChar(inBuf, (char) c);
}
/* No input before EOF signal means time to quit. 在 EOF 信号之前没有输入意味着要退出 */
if (c == EOF && inBuf->len == 0) return EOF;
/* otherwise we have a user query so process it. */
/* Add '\0' to make it look the same as message case. 添加 '\0' 使其看起来与消息大小写相同 */
appendStringInfoChar(inBuf, (char) '\0');
/* if the query echo flag was given, print the query.. */
if (EchoQuery) printf("statement: %s\n", inBuf->data);
fflush(stdout);
return 'Q';
}