回到上一个层面,继续看 PortalStart的处理:
void PortalStart(Portal portal, ParamListInfo params, int eflags, bool use_active_snapshot) { ... PG_TRY(); { ... /* * Determine the portal execution strategy */ portal->strategy = ChoosePortalStrategy(portal->stmts); /* * Fire her up according to the strategy */ switch (portal->strategy) { case PORTAL_ONE_SELECT: /* Must set snapshot before starting executor. */ if (use_active_snapshot) PushActiveSnapshot(GetActiveSnapshot()); else PushActiveSnapshot(GetTransactionSnapshot()); /* * Create QueryDesc in portal's context; for the moment, set * the destination to DestNone. */ queryDesc = CreateQueryDesc((PlannedStmt *) linitial(portal->stmts, portal->sourceText, GetActiveSnapshot(), InvalidSnapshot, None_Receiver, params, 0); /* * If it's a scrollable cursor, executor needs to support * REWIND and backwards scan, as well as whatever the caller * might've asked for. */ if (portal->cursorOptions & CURSOR_OPT_SCROLL) myeflags = eflags | EXEC_FLAG_REWIND | EXEC_FLAG_BACKWARD; else myeflags = eflags; /* * Call ExecutorStart to prepare the plan for execution */ ExecutorStart(queryDesc, myeflags); /* * This tells PortalCleanup to shut down the executor */ portal->queryDesc = queryDesc; /* * Remember tuple descriptor (computed by ExecutorStart) */ portal->tupDesc = queryDesc->tupDesc; /* * Reset cursor position data to "start of query" */ portal->atStart = true; portal->atEnd = false; /* allow fetches */ portal->portalPos = 0; portal->posOverflow = false; PopActiveSnapshot(); break; case PORTAL_ONE_RETURNING: case PORTAL_ONE_MOD_WITH: ... break; case PORTAL_UTIL_SELECT: ... break; case PORTAL_MULTI_QUERY: /* Need do nothing now */ portal->tupDesc = NULL; break; } } PG_CATCH(); { ... PG_RE_THROW(); } PG_END_TRY(); ... }
由之前的分析可以知道,满足 case PORTAL_ONE_SELECT 的条件,下面再看
use_active_snapshot,回溯上层:
static void exec_simple_query(const char *query_string) { ... foreach(parsetree_item, parsetree_list) { ... /* * Set up a snapshot if parse analysis/planning will need one. */ if (analyze_requires_snapshot(parsetree)) { PushActiveSnapshot(GetTransactionSnapshot()); snapshot_set = true; } ... PortalStart(portal, NULL, 0, snapshot_set); } ... }
可见,snapshot 还是要搞的。简言之,snapshot 是为了MVCC控制:
typedef struct SnapshotData { SnapshotSatisfiesFunc satisfies; /* tuple test function */ /* * The remaining fields are used only for MVCC snapshots, and are normally * just zeroes in special snapshots. (But xmin and xmax are used * specially by HeapTupleSatisfiesDirty.) * * An MVCC snapshot can never see the effects of XIDs >= xmax. It can see * the effects of all older XIDs except those listed in the snapshot. xmin * is stored as an optimization to avoid needing to search the XID arrays * for most tuples. */ TransactionId xmin; /* all XID < xmin are visible to me */ TransactionId xmax; /* all XID >= xmax are invisible to me */ TransactionId *xip; /* array of xact IDs in progress */ uint32 xcnt; /* # of xact ids in xip[] */ /* note: all ids in xip[] satisfy xmin <= xip[i] < xmax */ int32 subxcnt; /* # of xact ids in subxip[] */ TransactionId *subxip; /* array of subxact IDs in progress */ bool suboverflowed; /* has the subxip array overflowed? */ bool takenDuringRecovery; /* recovery-shaped snapshot? */ bool copied; /* false if it's a static snapshot */ /* * note: all ids in subxip[] are >= xmin, but we don't bother filtering * out any that are >= xmax */ CommandId curcid; /* in my xact, CID < curcid are visible */ uint32 active_count; /* refcount on ActiveSnapshot stack */ uint32 regd_count; /* refcount on RegisteredSnapshotList */ } SnapshotData;