天天看點

Greenplum資料庫外部表——Scan執行節點

Greenplum資料庫外部表執行節點相關函數位于src/backend/executor/nodeExternalscan.c檔案中,用于支援外部表掃描。由以下曆程構成:

INTERFACE ROUTINES
    ExecInitExternalScan      creates and initializes a externalscan node. 
    ExecExternalScan        sequentially scans a relation.
    ExecExternalNext        retrieve next tuple in sequential order.
    ExecEndExternalScan       releases any storage allocated.
    ExecExternalReScan        rescans the relation      

ExecInitExternalScan

Greenplum資料庫外部表——Scan執行節點

​ExecInitExternalScan​

​函數建立并初始化一個externalscan節點,形參ExternalScan代表External Scan node【scan.scanrelid是此節點的外部表的index。filenames是N個字元串節點指針(或NULL)的清單,其中N是數組中的segment數。位置I的指針為NULL或指向包含segment I的檔案名的字元串節點】 。其初始化流程如下所示。

  • 将形參​

    ​ExternalScan *node​

    ​​和​

    ​EState *estate​

    ​關聯到ExternalScanState對應成員中
  • 建立experssion context
  • 初始化child experssions
  • 檢查targetlist或qual是否包含var node引用ctid列
  • 初始化tuple table
  • 擷取Relation和FileScanDesc,設定到​

    ​externalstate->ss.ss_currentRelation​

    ​​和​

    ​externalstate->ess_ScanDesc​

  • 初始化result tuple type和projection info
  • 設定​

    ​externalstate->delayEagerFree​

ExternalScanState *ExecInitExternalScan(ExternalScan *node, EState *estate, int eflags){  
  ExternalScanState *externalstate = makeNode(ExternalScanState); /* create state structure */
  externalstate->ss.ps.plan = (Plan *) node;
  externalstate->ss.ps.state = estate;
  ExecAssignExprContext(estate, &externalstate->ss.ps); /* Miscellaneous initialization: create expression context for node */

  /* initialize child expressions */
  externalstate->ss.ps.targetlist = (List *)ExecInitExpr((Expr *) node->scan.plan.targetlist,(PlanState *) externalstate);
  externalstate->ss.ps.qual = (List *)ExecInitExpr((Expr *) node->scan.plan.qual,(PlanState *) externalstate);

  /* Check if targetlist or qual contains a var node referencing the ctid column */
  externalstate->cdb_want_ctid = contain_ctid_var_reference(&node->scan);
  ItemPointerSetInvalid(&externalstate->cdb_fake_ctid);

  /* tuple table initialization */
  ExecInitResultTupleSlot(estate, &externalstate->ss.ps);
  ExecInitScanTupleSlot(estate, &externalstate->ss);

  /* get the relation object id from the relid'th entry in the range table and open that relation. */
  Relation currentRelation = ExecOpenScanExternalRelation(estate, node->scan.scanrelid);
  FileScanDesc currentScanDesc = external_beginscan(currentRelation, node->scancounter, node->uriList, node->fmtOptString, node->fmtType, node->isMasterOnly, node->rejLimit, node->rejLimitInRows, node->logErrors, node->encoding);
  externalstate->ss.ss_currentRelation = currentRelation;
  externalstate->ess_ScanDesc = currentScanDesc;
  ExecAssignScanType(&externalstate->ss, RelationGetDescr(currentRelation));

  /* Initialize result tuple type and projection info. */
  ExecAssignResultTypeFromTL(&externalstate->ss.ps);
  ExecAssignScanProjectionInfo(&externalstate->ss);

  /* If eflag contains EXEC_FLAG_REWIND or EXEC_FLAG_BACKWARD or EXEC_FLAG_MARK, then this node is not eager free safe. */
  externalstate->delayEagerFree = ((eflags & (EXEC_FLAG_REWIND | EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)) != 0);

  return externalstate;
}      

ExecExternalScan

​ExecExternalScan​

​函數順序掃描外部表,傳回符合要求的元組。其調用ExecScan函數,向其傳遞通路方法。

/* ExecExternalScan(node)   Scans the external relation sequentially and returns the next qualifying tuple. It calls the ExecScan() routine and passes it the access method which retrieve tuples sequentially. */
TupleTableSlot *ExecExternalScan(ExternalScanState *node) { 
  return ExecScan(&node->ss, (ExecScanAccessMtd) ExternalNext, (ExecScanRecheckMtd) ExternalRecheck); /* use SeqNext as access method */
}      
Greenplum資料庫外部表——Scan執行節點
static TupleTableSlot *ExternalNext(ExternalScanState *node) {  
    /* get information from the estate and scan state */
    EState     *estate = node->ss.ps.state;
    FileScanDesc scandesc = node->ess_ScanDesc;
    ScanDirection direction = estate->es_direction;
    TupleTableSlot *slot = node->ss.ss_ScanTupleSlot;
    
    bool        scanNext = true;
    ExternalSelectDesc externalSelectDesc = external_getnext_init(&(node->ss.ps));
    if (gp_external_enable_filter_pushdown) externalSelectDesc->filter_quals = node->ss.ps.plan->qual;
            
    while(scanNext){  /* get the next tuple from the file access methods */
        HeapTuple tuple = external_getnext(scandesc, direction, externalSelectDesc);
        /* save the tuple and the buffer returned to us by the access methods in our scan tuple slot and return the slot.  Note: we pass 'false' because tuples returned by heap_getnext() are pointers onto disk pages and were not created with palloc() and so should not be pfree()'d.  Note also that ExecStoreTuple will increment the refcount of the buffer; the refcount will not be dropped until the tuple table slot is cleared. */ // 将通路方法傳回的元組和緩沖區儲存在掃描元組槽中,并傳回槽。注意:我們傳遞“false”是因為heap_getnext()傳回的元組是指向磁盤頁的指針,不是用palloc()建立的,是以不應該是pfree()。還請注意,ExecStoreTople将增加緩沖區的refcount;在清除元組表槽之前,不會删除refcount。
        if (tuple) {
            ExecStoreHeapTuple(tuple, slot, InvalidBuffer, true);
            if (node->ess_ScanDesc->fs_hasConstraints && !ExternalConstraintCheck(slot, node)){
                ExecClearTuple(slot); continue;
            }
            /* CDB: Label each row with a synthetic ctid if needed for subquery dedup. */
            if (node->cdb_want_ctid && !TupIsNull(slot)) {
                slot_set_ctid_from_fake(slot, &node->cdb_fake_ctid);
            }
        }else{
            ExecClearTuple(slot);
            if (!node->delayEagerFree) { ExecEagerFreeExternalScan(node); }
        }
        scanNext = false;
    }
    pfree(externalSelectDesc);
    return slot;
}