SQL 查询执行总体流程(3)

引言

上一篇文章中,我们学习了内存上下文的只是,并且对PortalStart函数做了简要的了解。今天这篇文章,我将学习PortalRun函数的有关内容。

执行过程(续前文)

PortalRun

 
  1. //代码清单1
  2. bool PortalRun(
  3. Portal portal, long count, bool isTopLevel, DestReceiver* dest, DestReceiver* altdest, char* completionTag)
  4. {
  5. ············
  6. /*
  7. * Check for improper portal use, and mark portal active.
  8. */
  9. MarkPortalActive(portal);
  10. QueryDesc* queryDesc = portal->queryDesc;
  11. PotalSetIoState(portal);
  12. ············
  13. PG_TRY();
  14. {
  15. switch (portal->strategy) {
  16. case PORTAL_ONE_SELECT:
  17. case PORTAL_ONE_RETURNING:
  18. case PORTAL_ONE_MOD_WITH:
  19. case PORTAL_UTIL_SELECT:
  20. /*
  21. * If we have not yet run the command, do so, storing its
  22. * results in the portal's tuplestore. But we don't do that
  23. * for the PORTAL_ONE_SELECT case.
  24. */
  25. if (portal->strategy != PORTAL_ONE_SELECT && !portal->holdStore) {
  26. /* DestRemoteExecute can not send T message automatically */
  27. if (strcmp(portal->commandTag, "EXPLAIN") == 0 && dest->mydest != DestRemote)
  28. t_thrd.explain_cxt.explain_perf_mode = EXPLAIN_NORMAL;
  29. FillPortalStore(portal, isTopLevel);
  30. }
  31. /*
  32. * Now fetch desired portion of results.
  33. */
  34. nprocessed = PortalRunSelect(portal, true, count, dest);
  35. /*
  36. * If the portal result contains a command tag and the caller
  37. * gave us a pointer to store it, copy it. Patch the "SELECT"
  38. * tag to also provide the rowcount.
  39. */
  40. if (completionTag != NULL) {
  41. if (strcmp(portal->commandTag, "SELECT") == 0) {
  42. errorno = snprintf_s(completionTag,
  43. COMPLETION_TAG_BUFSIZE,
  44. COMPLETION_TAG_BUFSIZE - 1,
  45. "SELECT %lu",
  46. nprocessed);
  47. securec_check_ss(errorno, "\0", "\0");
  48. } else {
  49. errorno = strcpy_s(completionTag, COMPLETION_TAG_BUFSIZE,
  50. portal->commandTag);
  51. securec_check(errorno, "\0", "\0");
  52. }
  53. }
  54. /* Mark portal not active */
  55. portal->status = PORTAL_READY;
  56. /*
  57. * Since it's a forward fetch, say DONE iff atEnd is now true.
  58. */
  59. result = portal->atEnd;
  60. break;
  61. case PORTAL_MULTI_QUERY:
  62. PortalRunMulti(portal, isTopLevel, dest, altdest, completionTag);
  63. /* Prevent portal's commands from being re-executed */
  64. MarkPortalDone(portal);
  65. /* Always complete at end of RunMulti */
  66. result = true;
  67. break;
  68. default:
  69. ereport(ERROR,
  70. (errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
  71. errmodule(MOD_EXECUTOR),
  72. errmsg("Unrecognized portal strategy: %d", (int)portal->strategy)));
  73. result = false; /* keep compiler quiet */
  74. break;
  75. }
  76. }
  77. PG_CATCH();
  78. {
  79. }
  80. PG_END_TRY();
  81. u_sess->plsql_cxt.portal_depth = savePortalDepth;
  82. stp_reset_xact_state_and_err_msg(savedisAllowCommitRollback, needResetErrMsg);
  83. ············
  84. /* update unique sql stat */
  85. if (is_instr_top_portal() && is_unique_sql_enabled() && is_local_unique_sql()) {
  86. /* Instrumentation: update unique sql returned rows(SELECT) */
  87. // only CN can update this counter
  88. if (portal->queryDesc != NULL && portal->queryDesc->estate && portal->queryDesc->estate->es_plannedstmt &&
  89. portal->queryDesc->estate->es_plannedstmt->commandType == CMD_SELECT) {
  90. ereport(DEBUG1,
  91. (errmodule(MOD_INSTR),
  92. errmsg("[UniqueSQL]"
  93. "unique id: %lu , select returned rows: %lu",
  94. u_sess->unique_sql_cxt.unique_sql_id,
  95. portal->queryDesc->estate->es_processed)));
  96. UniqueSQLStatCountReturnedRows(portal->queryDesc->estate->es_processed);
  97. }
  98. /* PortalRun using unique_sql_start_time as unique sql elapse start time */
  99. if (IsNeedUpdateUniqueSQLStat(portal) && IS_UNIQUE_SQL_TRACK_TOP && IsTopUniqueSQL()) {
  100. UpdateUniqueSQLStat(NULL, NULL, u_sess->unique_sql_cxt.unique_sql_start_time);
  101. }
  102. if (u_sess->unique_sql_cxt.unique_sql_start_time != 0) {
  103. int64 duration = GetCurrentTimestamp() - u_sess->unique_sql_cxt.unique_sql_start_time;
  104. if (IS_SINGLE_NODE) {
  105. pgstat_update_responstime_singlenode(
  106. u_sess->unique_sql_cxt.unique_sql_id, u_sess->unique_sql_cxt.unique_sql_start_time, duration);
  107. } else {
  108. pgstat_report_sql_rt(
  109. u_sess->unique_sql_cxt.unique_sql_id, u_sess->unique_sql_cxt.unique_sql_start_time, duration);
  110. }
  111. }
  112. }
  113. decrease_instr_portal_nesting_level();
  114. gstrace_exit(GS_TRC_ID_PortalRun);
  115. u_sess->pcache_cxt.cur_stmt_name = old_stmt_name;
  116. return result;
  117. }

此函数的主要功能为:

  1. 调用MarkPortalActive函数,使portal从Ready状态转为Active状态

  2. 执行Switch-Case语句,根据不同语句,选择不同的执行方法。

    • PORTAL_ONE_SELECT 、 PORTAL_ONE_RETURNING 、 PORTAL_ONE_MOD_WITH 和 PORTAL_UTIL_SELECT:

      如果portal对应的语句还没有被执行过(portal->holdStore字段为空),并且不是PORTAL_ONE_SELECT语句,则调用FillPortalStore函数执行查询并将结果加载到portal的存储元组中。

      然后调用PortalRunSelect函数获取结果。

      把portal更新为Ready状态,准备下一次执行。

    • PORTAL_MULTI_QUERY:

      调用PortalRunMulti函数执行此语句(一般为多个查询或者非查询语句,例如对表的创建和删除等)。并把portal更新为Done状态。

  3. 执行后后续的一些恢复及其他操作,例如恢复原来的内存上下文,记录和更新一些时间和资源的统计信息等。

在学习PortalRun函数的过程中,我发现了两个陌生的点,并对其深入拓展学习了一番。

1.Portal对象的持久化连接和非持久化连接

在代码清单1中,第28行中出现了holdStore(持久化存储)字段,通过引申学习,我发现Portal对象有两种连接方式。

  • 非持久化连接

    如果查询是一个简单的SELECT语句,那么查询结果不会存储在任何地方,而是直接返回给客户端。此时Portal对象是非持久化连接,即当事务结束时就释放内存。

  • 持久化连接

    如果查询是一个带有游标(CURSOR)的SELECT语句,那么查询中间结果会储存在Portal对象中。此时Portal对象是持久化连接,可以让Portal对象在多个请求——响应事务中保持有效,而不是每次都重新创建和销毁。

2.UNIQUE SQL

在代码清单1中的第98行~128行代码中,我发现unique_sql字符串频繁出现,我查找了相关资料,发现opengauss数据库执行过程的一个精妙之处。

在opengauss代码架构中,拓扑结构相同的SQL语句会赋给一个相同ID。

例如,有如下两条SQL语句:

 
  1. select* from t where id=1
  2. select* from t where id=2

这两条SQL语句生成的查询树的拓扑结构是相同的,只有选择算子的输入不同,因此属于同一个UNIQUE SQL。

引入UNIQUE SQL后SQL语句的执行流程如下图所示。

可以看出,当UNIQUE ID存在时,SQL可以省去创建UNIQUE SQL,也即创建查询树的过程。这样对代码的执行性能又有了一个提升。

小结

本篇文章对PortalRun函数做了简要的分析,学习了Portal对象的持久性和非持久性连接,并对UNIQUE SQL有了一定了解。下一篇文章中,我将学习Portal相关的最后一个函数PortalDrop。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值