=============================== 指令/命令 =================================================================
mainloop.c 502 行调用 HandleSlashCmds()处理命令 HandleSlashCmds 位于bin/psql/command.c中, 又调用了 exec_command() ,
exec_command()里面就是根据不同的命令调用不同的处理函数
这块有什么特别需要看的吗。。。
169 行 line = gets_interactive()
通过调用 readline() 来获取用户的一行输入(也就是一个指令,比如\a)
384行 psql_scan_setup(scan_state, line, strlen(line),
对用户输入的这一行进行词法解析
397行 scan_result = psql_scan(scan_state, query_buf, &prompt_tmp);
把解析出来的指令保存到 query_buf中
scan_result会被设置为 PSCAN_BACKSLASH
然后就到502行的 HandleSlashCmds() 了
=========================== SQL/QUERY =====================================================================
mainloop中有一个 while大循环,从86行开始,到588行结束,在每一次循环中,接收用户的一行输入,并进行处理
169 行 line = gets_interactive()
通过调用 readline() 来获取用户的一行输入(
但不一定是一条完整的sql语句,可能是一条sql语句中的某一行。
也有可能是多条sql语句放在同一行里面了(用分号分隔)
)
保存到line变量中。
query_buf holds query already accumulated. 也就是用户输入的一部分sql(用户尚未输入分号,所以还没办法执行)
370行,如果query_buf中已经有内容存在了, 就在query_buf尾部附加一个\n,
同时记录下这个\n的位置,放在变量:added_nl_pos中。
如果query_buf目前还是空的,added_nl_pos = -1
384行 psql_scan_setup(scan_state, line, strlen(line),
(准备)对用户输入的这一行进行词法解析
应该还没有开始解析。
下面还有一段while循环,主要是对line进行扫描, 从中找出sql语句的分号,并执行sql语句。
这里是while循环的开始:
397行 scan_result = psql_scan(scan_state, query_buf, &prompt_tmp);
对line进行扫描,并把解析出来的(部分)sql语句 附加到 query_buf尾部
如果line里面是分号分隔的多条语句,那么只把分号之前的部分附加到query_buf尾部(此时query_buf就是一条完整的sql语句了,可以被执行了)。
下次再调用 psql_scan的时候,会处理分号后面的部分。
426 行, 如果词法解析在line里面找到了 分号(PSCAN_SEMICOLON),
或者当前的psql是单行模式(single-line mode: a newline terminates an SQL command, as a semicolon does
单行运行模式,这时每个命令都将由换行符结束),
就要准备执行query_buf中的sql语句了。
435行,把line保存到 history_buf中,以后就可以按键盘的方向键(上和下),来直接显示历史输入到cmd中.
443行的conditional_active()不太理解,可能是指sql里面有if语句?
反正能进入这个if,就表示可以执行sql语句了。
445行 success = SendQuery(query_buf->data); 执行sql语句,这个在后面展开来讲。
如果执行sql语句出错了,并不会影响while循环的执行(会在下次循环中继续扫描sql语句的剩余部分).
530行,清空 query_buf.
562行的意思大概是扫描到了line的结尾,那么就break这个while循环。
如果还没有到line的结尾,那么继续while循环,准备处理line里面剩余的sql语句。
这里就已经出了while循环了,
572行释放line.
这样就完成了一行用户输入的处理。
588行 继续那个while大循环,用户输入下一行内容,然后去进行处理。
================================================================================================
注: 与pipeline相关的代码都没有看,看的全部是conn->pipelineStatus == PQ_PIPELINE_OFF的 这样简单一些。
================================================================================================
接下来再看 SendQuery吧。
bin/psql/common.c 1189行
1190行 SendQuery: send the query string(sql语句) to the backend(后端) and print out results.
后面会混用 query / query string / sql / sql语句 这几个词,都是同一个意思 。
1200 行,检查pset.db(意义是connection to backend)
如果pset.db为空,则表示没有连接到后端的数据库。
那么就不能执行sql语句了。
1206行, 检查是否开启了single-step选项(单步模式运行。
意味着每个查询在发往服务器之前都要提示用户,
用这个选项也可以取消执行。此选项主要用于调试脚本。
)
如果开了这个选项,就输出提示信息。如果此时用户输入x,就可以取消sql语句的执行。输入其它字符就可以开始sql语句的执行。
当然,如果用户这个时候按下了ctrl+c(1219行),也是会取消sql语句执行的。
1228行,如果设置了pset.logfile, 就把sql语句写入日志文件。
// Set cancelConn to point to the current database connection
1237 SetCancelConn(pset.db); 不太理解,先跳过
1239 transaction_status = PQtransactionStatus(pset.db);
获取事务(transaction)状态, 可能的取值有:
PQTRANS_IDLE, /* connection idle */
PQTRANS_ACTIVE, /* command in progress */
PQTRANS_INTRANS, /* idle, within transaction block */
PQTRANS_INERROR, /* idle, within failed transaction */
PQTRANS_UNKNOWN /* cannot determine status */
1241行 if 的意思是 当前不在事务中,并且没有设置autocommit(需要人工commit sql语句),
且 sql语句中没有begin语句,
则自动发送一个begin语句给后端(1245行 PQexec(pset.db, "BEGIN"). PQexec后面再展开讲),。
这样就开始一个事务了。
1257 行的if 意思是如果当前在事务中, 且设置了 PSQL_ERROR_ROLLBACK_ON(sql语句执行出错就自动回滚事务),
就执行query(SAVEPOINT pg_psql_temporary_savepoint) 创建一个事务的恢复点。
如果后面执行事务中的query出错了,就可以回滚到这个恢复点。
1286行的 DescribeQuery() describe the result columns of a query, without executing it
不展开讲, 有需要的话可以再看。
1296行,部分sql语句(不明, 只看到 select语句不会进入)会进入这里: fetch-it-all-and-print mode
1300行,记录当前时间
1303行,执行sql语句 results = PQexec(pset.db, query); PQexec后面展开讲
1306 ResetCancelConn(); 不明
1307 OK = ProcessResult(&results); 处理返回的结果集(主要是当sql语句中包含 COPY FROM STDIN or COPY TO STDOUT 的情况, 不展开讲)
1309 计时结束,统计耗时1。
1318 OK = PrintQueryResults(results); 输出结果集
sql语句执行之后,可以通过 PQresultStatus(results) 来查看其status, 可能的取值有:
PGRES_EMPTY_QUERY = 0, /* empty query string was executed */
PGRES_COMMAND_OK, /* a query command that doesn't return
* anything was executed properly by the
* backend */
PGRES_TUPLES_OK, /* a query command that returns tuples was
* executed properly by the backend, PGresult
* contains the result tuples */
PGRES_COPY_OUT, /* Copy Out data transfer in progress */
PGRES_COPY_IN, /* Copy In data transfer in progress */
PGRES_BAD_RESPONSE, /* an unexpected response was recv'd from the
* backend */
PGRES_NONFATAL_ERROR, /* notice or warning message */
PGRES_FATAL_ERROR, /* query failed */
PGRES_COPY_BOTH, /* Copy In/Out data transfer in progress */
PGRES_SINGLE_TUPLE, /* single tuple from larger resultset */
PGRES_PIPELINE_SYNC, /* pipeline synchronization point */
PGRES_PIPELINE_ABORTED, /* Command didn't run because of an abort
* earlier in a pipeline */
输出结果集的时候,要根据这个status来决定以何种形式来输出。
主要是PGRES_TUPLES_OK, 和 PGRES_COMMAND_OK
update/insert/delete 等语句是 PGRES_TUPLES_OK, 会输出执行结果和status.
命令类型的语句(比如begin rollback declare之类的)是 PGRES_COMMAND_OK, 只会输出status.
1322 Fetch-in-segments mode ExecQueryUsingCursor()
SELECT语句进入这里执行: run a SELECT-like query using a cursor.
后端会把结果集一页一页地返回给前端,避免一下返回太多前端处理不了。
1614行 开启事务
1626行 Send DECLARE CURSOR(调用 PQexec 执行这个语句: DECLARE _psql_cursor NO SCROLL CURSOR FOR + sql语句) 给后端
1686行 PQexec 执行语句:FETCH FORWARD [fetch_count] FROM _psql_cursor. fetch_count是个数字, 不知道是哪里设置的
执行之后,后端会返回一部分数据results set, 如果不是最后一页,那么就返回了fetch_count条数据,
如果是最后一页,那么就会返回少于 fetch_count条数据(也有可能是0条)
于是 1680行就有一个for死循环,直到一次或者多次一共返回了全部数据之后,才结束循环。
这里还有一个pager机制,大概就是类似于linux里面的more/less命令去读取一个很多行的txt文件,
每次只展示一页,然后按空格键往后翻页
1741行 printQuery(results, &my_popt, fout, is_pager, pset.logfile); 输出这一页的result set(可能是个表格的形式).
1798行,后端返回的所有页的记录的总条数保存到 pset.vars.ROW_COUNT中。
1810行,PQexec(CLOSE _psql_cursor) 关闭游标。
1822行,commit 或者 rollback transaction.
1332 如果在事务执行过程中出错,就需要回滚到之前的那个恢复点: ROLLBACK TO pg_psql_temporary_savepoint。
如果事务执行成功的话,就可以删除之前的恢复点了: RELEASE pg_psql_temporary_savepoint。
================================================================================================
PGresult * PQexec(PGconn *conn, const char *query)
interfaces/libpq/fe-exec.c 2142行
send a query to the backend and package up the result in a PGresult
分为三步:
PQexecStart() : prepare to send command
2231行 如果开了pipeline mode, 就无法处理了。
2247行 while循环调用 PQgetResult(后面展开讲) 获取 any prior query result that application didn't eat
PQsendQuery() : Submit a query, but don't wait for it to finish
1281行 又调用了 PQsendQueryInternal()
1295行 PQsendQueryStart(conn, query, bool newQuery=true)
1594行判断了当前的asyncStatus,如果不是空闲状态,则无法send query.
1306 行 entry = pqAllocCmdQueueEntry(conn): Get a command queue entry for caller to fill
这个entry....怎么理解呢?
1311 行 判断pipeline状态,我猜测只有在COPY指令的时候,才会有pipeline,
执行insert/select等常规sql时候,pipelineStatus = PQ_PIPELINE_OFF.
1314 行 连续调用 pqPutMsgStart('Q', conn) pqPuts(query, conn) 和 pqPutMsgEnd(conn):
pqPutMsgStart('Q', conn) begin construction of a message(type: Q) to the server. 填充'Q' 到conn->outBuffer中。
pqPuts(query, conn) 把query填充到 conn->outBuffer中。
pqPutMsgEnd(conn) finish constructing a message and possibly send it:
525行,先计算 message 长度: msgLen = conn->outMsgEnd - conn->outMsgStart
然后把 message 长度填充到 conn->outBuffer中。
conn->outBuffer格式 [message type 可为空][message长度, 固定占用4字节][query]
(这里的message指的就是[message长度] 和 [query]).
544 行,如果conn->outBuffer长度 >= 8192,
调用pqSendSome()发送一部分(这一部分长度为8192的整倍数), 留下另外一部分(这一部分长度小于8192)
pqSendSome()又调用了pqsecure_write()方法来发送数据(这块就不展开讲了,只说一句,如果没有开启SSL和GSS,
那么就调用系统库sys/socket.h 中的send()方法往socket里面发数据)。
剩下的这一部分什么时候发送的呢?后面如果有调用pqFlush()的话,会把剩余的部分发送。
1324行 填充 entry->queryclass 和 entry->query.
1367 pqPipelineFlush(conn) 会去调用 pqFlush()
把 conn->outBuffer中小于 8192的这一部分全部发送出去(pqFlush()会调用pqSendSome())。
1371行 pqAppendCmdQueueEntry(conn, entry) 把entry放到command queue中.
1373行 设置conn->asyncStatus = PGASYNC_BUSY,表示 query in progress.
然后PQsendQueryInternal()就结束了,
PQexecFinish() : wait for command result
2306 while 循环调用 PQgetResult() 获取结果
================================================================================================
PGresult * PQgetResult(PGconn * conn)
interfaces/libpq/fe-exec.c 1926行
Get the next PGresult produced by a query
1234行 parseInput(conn); Parse any available data, if our state permits 只是调用了 pqParseInput3() interfaces/libpq/fe-protocol3.c
pqParseInput3(): parse input data from backend, until input is exhausted or a stopping state is reached
72 行 for 死循环
79行 pqGetc() 获取1个字节的message type. (保存到id变量中)
81行 pqGetInt() 获取4个字节的 message length
145行根据不同的message type做不同的处理。
这里同时还检测了asyncStatus,只有asyncStatus为 PGASYNC_BUSY(query in progress) 的时候,才进行message 的处理。
209行 message type == 'C'的时候,表示 command complete:
210行 pqGets(conn->workBuffer) 获取message的剩余内容保存到workBuffer中。
214行 创建conn->result (也就是PGresult) = PQmakeEmptyPGresult();
注:在PQmakeEmptyPGresult的过程中,会把conn->events复制到conn->result->events里面。后续会用到.
224行 strlcpy(conn->result->cmdStatus, conn->workBuffer.data)
把 workBuffer中的message copy到cmdStatus中。
226行 更新状态 asyncStatus = PGASYNC_READY(query done, waiting for client to fetch result)
382 行 message type == 'D'的时候,表示 Data Row:
387行 调用 getAnotherTuple()
获取一个tuple保存到conn->rowBuf中..这里没有展开看。
233 行 message type == 'Z'的时候,表示 psql这种同步查询的模式
(意思是提交了 sql语句,然后就等待后端返回数据。不是异步查询的形式)
pqCommandQueueAdvance() 调整 command queue:
Remove one query from the command queue, when we receive
all results from the server that pertain to it.
然后 asyncStatus = PGASYNC_IDLE
// 后端返回的message list应该是 DDDDDDDDDCZ (select语句) 或 CZ(insert等语句)
parseInput()结束
1938行 while循环(asyncStatus == PGASYNC_BUSY),
1946行 while循环(pqFlush() and pqWait())发送message的剩余部分给后端(如果有剩余部分的话)。
这里的pqWait()是在等待 需要发送给后端的message
1962行 pqWait() & pqReadData() 等待并接收后端发来的message
pqReadData() 又调用了pqsecure_read()方法来发送数据(这块就不展开讲了,只说一句,如果没有开启SSL和GSS,
那么就调用系统库sys/socket.h 中的recv()方法往socket里面发数据)。
1976行 parseInput(conn) 由于parseInput在处理完成之后,会把asyncStatus设置为PGASYNC_IDLE, 这样就可以跳出while循环了。
1991行 判断conn->asyncStatus的状态,并进行相应的处理
如果状态为PGASYNC_READY(C message):
2018行的if 条件为 false, 因为psql客户端的queryclass就是PGQUERY_SIMPLE.
2021行 res = pqPrepareAsyncResult()创建一个空的res, 其实它就是conn->result.
然后就把asyncStatus = PGASYNC_BUSY,Set the state back to BUSY, allowing parsing to proceed
如果状态为PGASYNC_IDLE(Z message):
query is complete.
2072行,如果在前面一步创建了res,
2076行,对res里面的每一个event调用proc(PGEVT_RESULTCREATE)方法进行处理。
================================================================================================
event是什么呢? 可以看一下 interfaces/libpq/libpq-events.c
40行: PQregisterEventProc()
参考:http://postgres.cn/docs/13/libpq-events.html
================================================================================================
reference:
1) http://postgres.cn/docs/13/libpq.html
2) 如果对前端和后端之间的通讯协议有些忘记的话,也可以参考上次pg8000的文档。