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

引言

上一篇文章中,我学习了查询执行过程总体的函数调用,并对CreatePortal函数有了初步的了解。在本篇文章里,我将对Portal有关函数继续讲解。

执行过程(续前文)

CreatePortal

1.do{······}while(0)语句块

详解请见前文SQL 查询执行总体流程(1)

2. 内存上下文管理

Portal结构体中的heap字段为MemoryContext(内存上下文)类型。

  • 什么是内存上下文、为什么要使用内存上下文?

    在opengauss数据库查询过程中,需要不断对某些任务分配内存,而这些分配的内存需要显示释放,因此很容易造成内存泄漏,经过时间的累计,存储空间会被耗尽。而内存上下文方法就应运而生了,对于某一个SQL语句,会给这个语句分配一个内存上下文,此SQL语句执行过程中,不再直接调用malloc函数申请内存,而是从内存上下文中申请。当SQL语句执行结束时,直接删除内存上下文,一次性释放所有内存。

  • 内存上下文的逻辑结构

     
      
    1. //代码清单1
    2. typedef struct MemoryContextData* MemoryContext;
    3. typedef struct MemoryContextData {
    4. NodeTag type; /* 上下文类别*/
    5. MemoryContextMethods* methods; /* 虛函数表*/
    6. MemoryContext parent; /* 父上下文。顶级上下文为 NULL*/
    7. MemoryContext firstchild; /* 子上下文的链表头*/
    8. MemoryContext prevchild; /* 前向子上下文 */
    9. MemoryContext nextchild; /* 后向子上下文 */
    10. char* name; /* 上下文名称,方便调试 */
    11. pthread_rwlock_t lock;
    12. bool is_shared; /* 上下文是否在多个线程共享 */
    13. bool isReset; /* 为true时表示复位后还未使用此内存上下文*/
    14. int level; /* 上下文层次级别*/
    15. uint64 session_id; /* 上下所属的会话ID */
    16. ThreadId thread_id; /* 上下所属于的线程ID */
    17. } MemoryContextData;

    由代码清单1中的MemoryContextData定义可知,内存上下文是一个类似n叉树的树型结构。具体结构如图所示。

TopMemoryContext为顶层的内存上下文,其父结点为空。从结构图可以看出,父节点指向一个链表,链表的每一个结点又作为父节点,指向子链表。这样形成了一个链表嵌套链表的树形结构。

当删除某一个内存上下文时,只需要删除自身结点和子链表,并维护内存上下文树即可。

  • 对内存上下文操作的函数

    代码清单1第5行的虚函数表methods中存放对内存上下文操作的函数。

    代码如下:

     
      
    1. //代码清单2
    2. typedef struct MemoryContextMethods {
    3. /*在上下文中分配内存*/
    4. void* (*alloc)(MemoryContext context, Size align, Size size, const char* file, int line);
    5. /* 释放pointer 内存到上下文中*/
    6. void (*free_p)(MemoryContext context, void* pointer);
    7. /*在上下文中重新分配内存*/
    8. void* (*realloc)(MemoryContext context, void* pointer, Size align, Size size, const char* file, int line);
    9. void (*init)(MemoryContext context); /*上下文初始化*/
    10. void (*reset)(MemoryContext context); /*上下文复位*/
    11. void (*delete_context)(MemoryContext context); /*删除上下文 */
    12. Size (*get_chunk_space)(MemoryContext context, void* pointer); /*获取上下文块大小 */
    13. bool (*is_empty)(MemoryContext context); /*上下文是否为空*/
    14. void (*stats)(MemoryContext context, int level); /*上下文信息统计*/
    15. #ifdef MEMORY_CONTEXT_CHECKING
    16. void (*check)(MemoryContext context); /*上下文异常检查*/
    17. #endif
    18. } MemoryContextMethods;

    这些函数在SQL语句执行过程中进行晚期绑定,实际上,这些函数对应于AlignMemoryAllocator类中的AllocSet一族的函数。对应表如下。

    函数指针对应函数
    allocAllocSetAlloc
    free_pAllocSetFree
    reallocAllocSetRealloc
    initAllocSetInit
    resetAllocSetReset
    delete_contextAllocSetDelete
    get_chunk_spaceAllocSetGetChunkSpace
    is_emptyAllocSetIsEmpty
    checkAllocSetCheck

PortalStart

PortalStart函数主要代码如代码清单3所示。

 
  1. //代码清单3
  2. void PortalStart(Portal portal, ParamListInfo params, int eflags, Snapshot snapshot)
  3. {
  4. ············
  5. /* Set up global portal context pointers.*/
  6. saveActivePortal = ActivePortal;
  7. saveResourceOwner = t_thrd.utils_cxt.CurrentResourceOwner;
  8. savePortalContext = t_thrd.mem_cxt.portal_mem_cxt;
  9. PG_TRY();
  10. {
  11. ············
  12. /* Determine the portal execution strategy*/
  13. portal->strategy = ChoosePortalStrategy(portal->stmts);
  14. /* Fire her up according to the strategy*/
  15. switch (portal->strategy) {
  16. case PORTAL_ONE_SELECT: {
  17. ············
  18. /* Create QueryDesc in portal's context; for the moment, set
  19. * the destination to DestNone.
  20. */
  21. queryDesc = CreateQueryDesc(
  22. ps, portal->sourceText, tempSnap, InvalidSnapshot, None_Receiver, params, 0, mot_jit_context);
  23. #else
  24. /* Create QueryDesc in portal's context; for the moment, set
  25. * the destination to DestNone.
  26. */
  27. queryDesc = CreateQueryDesc(
  28. ps, portal->sourceText, GetActiveSnapshot(), InvalidSnapshot, None_Receiver, params, 0);
  29. #endif
  30. /* Call ExecutorStart to prepare the plan for execution*/
  31. ExecutorStart(queryDesc, myeflags);
  32. /* This tells PortalCleanup to shut down the executor*/
  33. portal->queryDesc = queryDesc;
  34. /* Remember tuple descriptor (computed by ExecutorStart)*/
  35. portal->tupDesc = queryDesc->tupDesc;
  36. ············
  37. break;
  38. }
  39. case PORTAL_ONE_RETURNING:
  40. case PORTAL_ONE_MOD_WITH:
  41. {
  42. portal->tupDesc = ExecCleanTypeFromTL(pstmt->planTree->targetlist, false, TAM_HEAP);
  43. }
  44. ············
  45. break;
  46. case PORTAL_UTIL_SELECT:
  47. {
  48. portal->tupDesc = UtilityTupleDescriptor(ustmt);
  49. }
  50. ············
  51. break;
  52. case PORTAL_MULTI_QUERY:
  53. ············
  54. break;
  55. default:
  56. break;
  57. }
  58. portal->stmtMemCost = 0;
  59. }
  60. PG_CATCH();
  61. {
  62. ············
  63. ActivePortal = saveActivePortal;
  64. t_thrd.utils_cxt.CurrentResourceOwner = saveResourceOwner;
  65. t_thrd.mem_cxt.portal_mem_cxt = savePortalContext;
  66. }
  67. PG_END_TRY();
  68. MemoryContextSwitchTo(oldContext);
  69. ············
  70. }

PortalStart函数主要作用是启动一个portal,确保查询所需的资源和状态都已经准备好。

主要功能如下:

  1. 初始化相关变量,然后保存portal上下文相关指针,以便执行异常时可以恢复。
  2. 根据不同的SQL语句,选择不同的执行策略。
    • PORTAL_ONE_SELECT 策略:用于执行只包含一个SELECT语句的情况,并且查询结果不需要修改。
    • PORTAL_ONE_RETURNING 和 PORTAL_ONE_MOD_WITH 策略:用于修改语句(INSERT、UPDATE、DELETE),允许修改数据的同时获取返回的数据。
    • PORTAL_UTIL_SELECT 策略:用于程序语句(EXPLAIN和VACUUM)。
    • PORTAL_MULTI_QUERY 策略:用于执行复杂的查询计划。

其代码逻辑如图所示:

小结

本篇文章里,我学习了内存上下文的逻辑结构及其作用,并学习了PortalStart函数的大致功能。下一篇文章我将继续对Portal部分的PortalRun函数做简要解析。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值