/* * ChoosePortalStrategy * Select portal execution strategy given the intended query list. * * See the comments in portal.h. */ PortalStrategy ChoosePortalStrategy(List *parseTrees) { int nSetTag; ListCell *lc; /* * PORTAL_ONE_SELECT and PORTAL_UTIL_SELECT need only consider the * single-Query-struct case, since there are no rewrite rules that can add * auxiliary queries to a SELECT or a utility command. */ if (list_length(parseTrees) == 1) { Query *query = (Query *) linitial(parseTrees); Assert(IsA(query, Query)); if (query->canSetTag) { if (query->commandType == CMD_SELECT && query->into == NULL) return PORTAL_ONE_SELECT; if (query->commandType == CMD_UTILITY && query->utilityStmt != NULL) { if (UtilityReturnsTuples(query->utilityStmt)) return PORTAL_UTIL_SELECT; /* it can't be ONE_RETURNING, so give up */ return PORTAL_MULTI_QUERY; } } } /* * PORTAL_ONE_RETURNING has to allow auxiliary queries added by rewrite. * Choose PORTAL_ONE_RETURNING if there is exactly one canSetTag query and * it has a RETURNING list. */ nSetTag = 0; foreach(lc, parseTrees) { Query *query = (Query *) lfirst(lc); Assert(IsA(query, Query)); if (query->canSetTag) { if (++nSetTag > 1) return PORTAL_MULTI_QUERY; /* no need to look further */ if (query->returningList == NIL) return PORTAL_MULTI_QUERY; /* no need to look further */ } }
/* * ChoosePortalStrategy * Select portal execution strategy given the intended statement list. * * The list elements can be Querys, PlannedStmts, or utility statements. * That's more general than portals need, but plancache.c uses this too. * * See the comments in portal.h. */ PortalStrategy ChoosePortalStrategy(List *stmts) { int nSetTag; ListCell *lc; /* * PORTAL_ONE_SELECT and PORTAL_UTIL_SELECT need only consider the * single-statement case, since there are no rewrite rules that can add * auxiliary queries to a SELECT or a utility command. PORTAL_ONE_MOD_WITH * likewise allows only one top-level statement. */ if (list_length(stmts) == 1) { Node *stmt = (Node *) linitial(stmts); if (IsA(stmt, Query)) { Query *query = (Query *) stmt; if (query->canSetTag) { if (query->commandType == CMD_SELECT && query->utilityStmt == NULL) { if (query->hasModifyingCTE) return PORTAL_ONE_MOD_WITH; else return PORTAL_ONE_SELECT; } if (query->commandType == CMD_UTILITY && query->utilityStmt != NULL) { if (UtilityReturnsTuples(query->utilityStmt)) return PORTAL_UTIL_SELECT; /* it can't be ONE_RETURNING, so give up */ return PORTAL_MULTI_QUERY; } } } else if (IsA(stmt, PlannedStmt)) { PlannedStmt *pstmt = (PlannedStmt *) stmt; if (pstmt->canSetTag) { if (pstmt->commandType == CMD_SELECT && pstmt->utilityStmt == NULL) { if (pstmt->hasModifyingCTE) return PORTAL_ONE_MOD_WITH; else return PORTAL_ONE_SELECT; } } } else { /* must be a utility command; assume it's canSetTag */ if (UtilityReturnsTuples(stmt)) return PORTAL_UTIL_SELECT; /* it can't be ONE_RETURNING, so give up */ return PORTAL_MULTI_QUERY; } } /* * PORTAL_ONE_RETURNING has to allow auxiliary queries added by rewrite. * Choose PORTAL_ONE_RETURNING if there is exactly one canSetTag query and * it has a RETURNING list. */ nSetTag = 0; foreach(lc, stmts) { Node *stmt = (Node *) lfirst(lc); if (IsA(stmt, Query)) { Query *query = (Query *) stmt; if (query->canSetTag) { if (++nSetTag > 1) return PORTAL_MULTI_QUERY; /* no need to look further */ if (query->returningList == NIL) return PORTAL_MULTI_QUERY; /* no need to look further */ } } else if (IsA(stmt, PlannedStmt)) { PlannedStmt *pstmt = (PlannedStmt *) stmt; if (pstmt->canSetTag) { if (++nSetTag > 1) return PORTAL_MULTI_QUERY; /* no need to look further */ if (!pstmt->hasReturning) return PORTAL_MULTI_QUERY; /* no need to look further */ } } /* otherwise, utility command, assumed not canSetTag */ }
/* * SPI_cursor_open() * * Open a prepared SPI plan as a portal */ Portal SPI_cursor_open(const char *name, void *plan, Datum *Values, const char *Nulls, bool read_only) { _SPI_plan *spiplan = (_SPI_plan *) plan; List *qtlist = spiplan->qtlist; List *ptlist = spiplan->ptlist; Query *queryTree; Plan *planTree; ParamListInfo paramLI; Snapshot snapshot; MemoryContext oldcontext; Portal portal; int k; /* Ensure that the plan contains only one query */ if (list_length(ptlist) != 1 || list_length(qtlist) != 1) ereport(ERROR, (errcode(ERRCODE_INVALID_CURSOR_DEFINITION), errmsg("cannot open multi-query plan as cursor"))); queryTree = (Query *) linitial((List *) linitial(qtlist)); planTree = (Plan *) linitial(ptlist); /* Must be a query that returns tuples */ switch (queryTree->commandType) { case CMD_SELECT: if (queryTree->into != NULL) ereport(ERROR, (errcode(ERRCODE_INVALID_CURSOR_DEFINITION), errmsg("cannot open SELECT INTO query as cursor"))); break; case CMD_UTILITY: if (!UtilityReturnsTuples(queryTree->utilityStmt)) ereport(ERROR, (errcode(ERRCODE_INVALID_CURSOR_DEFINITION), errmsg("cannot open non-SELECT query as cursor"))); break; default: ereport(ERROR, (errcode(ERRCODE_INVALID_CURSOR_DEFINITION), errmsg("cannot open non-SELECT query as cursor"))); break; } /* Reset SPI result */ SPI_processed = 0; SPI_tuptable = NULL; _SPI_current->processed = 0; _SPI_current->tuptable = NULL; /* Create the portal */ if (name == NULL || name[0] == '\0') { /* Use a random nonconflicting name */ portal = CreateNewPortal(); } else { /* In this path, error if portal of same name already exists */ portal = CreatePortal(name, false, false); } /* Switch to portals memory and copy the parsetree and plan to there */ oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal)); queryTree = copyObject(queryTree); planTree = copyObject(planTree); /* If the plan has parameters, set them up */ if (spiplan->nargs > 0) { paramLI = (ParamListInfo) palloc0((spiplan->nargs + 1) * sizeof(ParamListInfoData)); for (k = 0; k < spiplan->nargs; k++) { paramLI[k].kind = PARAM_NUM; paramLI[k].id = k + 1; paramLI[k].ptype = spiplan->argtypes[k]; paramLI[k].isnull = (Nulls && Nulls[k] == 'n'); if (paramLI[k].isnull) { /* nulls just copy */ paramLI[k].value = Values[k]; } else { /* pass-by-ref values must be copied into portal context */ int16 paramTypLen; bool paramTypByVal; get_typlenbyval(spiplan->argtypes[k], ¶mTypLen, ¶mTypByVal); paramLI[k].value = datumCopy(Values[k], paramTypByVal, paramTypLen); } } paramLI[k].kind = PARAM_INVALID; } else paramLI = NULL; /* * Set up the portal. */ PortalDefineQuery(portal, NULL, /* unfortunately don't have sourceText */ "SELECT", /* nor the raw parse tree... */ list_make1(queryTree), list_make1(planTree), PortalGetHeapMemory(portal)); MemoryContextSwitchTo(oldcontext); /* * Set up options for portal. */ portal->cursorOptions &= ~(CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL); if (planTree == NULL || ExecSupportsBackwardScan(planTree)) portal->cursorOptions |= CURSOR_OPT_SCROLL; else portal->cursorOptions |= CURSOR_OPT_NO_SCROLL; /* * Set up the snapshot to use. (PortalStart will do CopySnapshot, * so we skip that here.) */ if (read_only) snapshot = ActiveSnapshot; else { CommandCounterIncrement(); snapshot = GetTransactionSnapshot(); } /* * Start portal execution. */ PortalStart(portal, paramLI, snapshot); Assert(portal->strategy == PORTAL_ONE_SELECT || portal->strategy == PORTAL_UTIL_SELECT); /* Return the created portal */ return portal; }
/* * ChoosePortalStrategy * Select portal execution strategy given the intended statement list. * * The list elements can be Querys, PlannedStmts, or utility statements. * That's more general than portals need, but plancache.c uses this too. * * See the comments in portal.h. */ PortalStrategy ChoosePortalStrategy(List *stmts) { int nSetTag; ListCell *lc; /* * PORTAL_ONE_SELECT and PORTAL_UTIL_SELECT need only consider the * single-statement case, since there are no rewrite rules that can add * auxiliary queries to a SELECT or a utility command. PORTAL_ONE_MOD_WITH * likewise allows only one top-level statement. */ if (list_length(stmts) == 1) { Node *stmt = (Node *) linitial(stmts); if (IsA(stmt, Query)) { Query *query = (Query *) stmt; if (query->canSetTag) { if (query->commandType == CMD_SELECT && query->utilityStmt == NULL) { if (query->hasModifyingCTE) return PORTAL_ONE_MOD_WITH; else return PORTAL_ONE_SELECT; } if (query->commandType == CMD_UTILITY && query->utilityStmt != NULL) { if (UtilityReturnsTuples(query->utilityStmt)) return PORTAL_UTIL_SELECT; /* it can't be ONE_RETURNING, so give up */ return PORTAL_MULTI_QUERY; } #ifdef PGXC /* * This is possible with an EXECUTE DIRECT in a SPI. * PGXCTODO: there might be a better way to manage the * cases with EXECUTE DIRECT here like using a special * utility command and redirect it to a correct portal * strategy. * Something like PORTAL_UTIL_SELECT might be far better. */ if (query->commandType == CMD_SELECT && query->utilityStmt != NULL && IsA(query->utilityStmt, RemoteQuery)) { RemoteQuery *step = (RemoteQuery *) stmt; /* * Let's choose PORTAL_ONE_SELECT for now * After adding more PGXC functionality we may have more * sophisticated algorithm of determining portal strategy * * EXECUTE DIRECT is a utility but depending on its inner query * it can return tuples or not depending on the query used. */ if (step->exec_direct_type == EXEC_DIRECT_SELECT || step->exec_direct_type == EXEC_DIRECT_UPDATE || step->exec_direct_type == EXEC_DIRECT_DELETE || step->exec_direct_type == EXEC_DIRECT_INSERT || step->exec_direct_type == EXEC_DIRECT_LOCAL) return PORTAL_ONE_SELECT; else if (step->exec_direct_type == EXEC_DIRECT_UTILITY || step->exec_direct_type == EXEC_DIRECT_LOCAL_UTILITY) return PORTAL_MULTI_QUERY; else return PORTAL_ONE_SELECT; } #endif } } #ifdef PGXC else if (IsA(stmt, RemoteQuery)) { RemoteQuery *step = (RemoteQuery *) stmt; /* * Let's choose PORTAL_ONE_SELECT for now * After adding more PGXC functionality we may have more * sophisticated algorithm of determining portal strategy. * * EXECUTE DIRECT is a utility but depending on its inner query * it can return tuples or not depending on the query used. */ if (step->exec_direct_type == EXEC_DIRECT_SELECT || step->exec_direct_type == EXEC_DIRECT_UPDATE || step->exec_direct_type == EXEC_DIRECT_DELETE || step->exec_direct_type == EXEC_DIRECT_INSERT || step->exec_direct_type == EXEC_DIRECT_LOCAL) return PORTAL_ONE_SELECT; else if (step->exec_direct_type == EXEC_DIRECT_UTILITY || step->exec_direct_type == EXEC_DIRECT_LOCAL_UTILITY) return PORTAL_MULTI_QUERY; else return PORTAL_ONE_SELECT; } #endif else if (IsA(stmt, PlannedStmt)) { PlannedStmt *pstmt = (PlannedStmt *) stmt; if (pstmt->canSetTag) { if (pstmt->commandType == CMD_SELECT && pstmt->utilityStmt == NULL) { if (pstmt->hasModifyingCTE) return PORTAL_ONE_MOD_WITH; else return PORTAL_ONE_SELECT; } } } else { /* must be a utility command; assume it's canSetTag */ if (UtilityReturnsTuples(stmt)) return PORTAL_UTIL_SELECT; /* it can't be ONE_RETURNING, so give up */ return PORTAL_MULTI_QUERY; } } /* * PORTAL_ONE_RETURNING has to allow auxiliary queries added by rewrite. * Choose PORTAL_ONE_RETURNING if there is exactly one canSetTag query and * it has a RETURNING list. */ nSetTag = 0; foreach(lc, stmts) { Node *stmt = (Node *) lfirst(lc); if (IsA(stmt, Query)) { Query *query = (Query *) stmt; if (query->canSetTag) { if (++nSetTag > 1) return PORTAL_MULTI_QUERY; /* no need to look further */ if (query->returningList == NIL) return PORTAL_MULTI_QUERY; /* no need to look further */ } } else if (IsA(stmt, PlannedStmt)) { PlannedStmt *pstmt = (PlannedStmt *) stmt; if (pstmt->canSetTag) { if (++nSetTag > 1) return PORTAL_MULTI_QUERY; /* no need to look further */ if (!pstmt->hasReturning) return PORTAL_MULTI_QUERY; /* no need to look further */ } } /* otherwise, utility command, assumed not canSetTag */ }