postgres-master/src/bin/psql/mainloop.c


===============================  指令/命令 =================================================================
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的文档。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值