static TupleDesc compute_result_tupledesc(List *stmt_list) { Query *query; switch (ChoosePortalStrategy(stmt_list)) { case PORTAL_ONE_SELECT: case PORTAL_ONE_MOD_WITH: query = (Query *) linitial(stmt_list); Assert(IsA(query, Query)); return ExecCleanTypeFromTL(query->targetList, false); case PORTAL_ONE_RETURNING: query = (Query *) PortalListGetPrimaryStmt(stmt_list); Assert(IsA(query, Query)); Assert(query->returningList); return ExecCleanTypeFromTL(query->returningList, false); case PORTAL_UTIL_SELECT: query = (Query *) linitial(stmt_list); Assert(IsA(query, Query)); Assert(query->utilityStmt); return UtilityTupleDescriptor(query->utilityStmt); case PORTAL_MULTI_QUERY: /* will not return tuples */ break; } return NULL; }
/* * ExecInitJunkFilter * * Initialize the Junk filter. * * The source targetlist is passed in. The output tuple descriptor is * built from the non-junk tlist entries, plus the passed specification * of whether to include room for an OID or not. * An optional resultSlot can be passed as well. */ JunkFilter * ExecInitJunkFilter(List *targetList, bool hasoid, TupleTableSlot *slot) { JunkFilter *junkfilter; TupleDesc cleanTupType; int cleanLength; AttrNumber *cleanMap; ListCell *t; AttrNumber cleanResno; /* * Compute the tuple descriptor for the cleaned tuple. */ cleanTupType = ExecCleanTypeFromTL(targetList, hasoid); /* * Use the given slot, or make a new slot if we weren't given one. */ if (slot) ExecSetSlotDescriptor(slot, cleanTupType); else slot = MakeSingleTupleTableSlot(cleanTupType); /* * Now calculate the mapping between the original tuple's attributes and * the "clean" tuple's attributes. * * The "map" is an array of "cleanLength" attribute numbers, i.e. one * entry for every attribute of the "clean" tuple. The value of this entry * is the attribute number of the corresponding attribute of the * "original" tuple. (Zero indicates a NULL output attribute, but we do * not use that feature in this routine.) */ cleanLength = cleanTupType->natts; if (cleanLength > 0) { cleanMap = (AttrNumber *) palloc(cleanLength * sizeof(AttrNumber)); cleanResno = 1; foreach(t, targetList) { TargetEntry *tle = lfirst(t); if (!tle->resjunk) { cleanMap[cleanResno - 1] = tle->resno; cleanResno++; } }
/* * multi_ExecutorStart is a hook called at at the beginning of any execution * of any query plan. * * If a distributed relation is the target of the query, perform some validity * checks. If a legal statement, start the distributed execution. After that * the to-be-executed query is replaced with the portion executing solely on * the master. */ void multi_ExecutorStart(QueryDesc *queryDesc, int eflags) { PlannedStmt *planStatement = queryDesc->plannedstmt; if (HasCitusToplevelNode(planStatement)) { MultiPlan *multiPlan = GetMultiPlan(planStatement); MultiExecutorType executorType = MULTI_EXECUTOR_INVALID_FIRST; Job *workerJob = multiPlan->workerJob; ExecCheckRTPerms(planStatement->rtable, true); executorType = JobExecutorType(multiPlan); if (executorType == MULTI_EXECUTOR_ROUTER) { Task *task = NULL; List *taskList = workerJob->taskList; TupleDesc tupleDescriptor = ExecCleanTypeFromTL( planStatement->planTree->targetlist, false); List *dependendJobList PG_USED_FOR_ASSERTS_ONLY = workerJob->dependedJobList; /* router executor can only execute distributed plans with a single task */ Assert(list_length(taskList) == 1); Assert(dependendJobList == NIL); task = (Task *) linitial(taskList); /* we need to set tupleDesc in executorStart */ queryDesc->tupDesc = tupleDescriptor; /* drop into the router executor */ RouterExecutorStart(queryDesc, eflags, task); } else { PlannedStmt *masterSelectPlan = MasterNodeSelectPlan(multiPlan); CreateStmt *masterCreateStmt = MasterNodeCreateStatement(multiPlan); List *masterCopyStmtList = MasterNodeCopyStatementList(multiPlan); RangeTblEntry *masterRangeTableEntry = NULL; StringInfo jobDirectoryName = NULL; /* * We create a directory on the master node to keep task execution results. * We also register this directory for automatic cleanup on portal delete. */ jobDirectoryName = JobDirectoryName(workerJob->jobId); CreateDirectory(jobDirectoryName); ResourceOwnerEnlargeJobDirectories(CurrentResourceOwner); ResourceOwnerRememberJobDirectory(CurrentResourceOwner, workerJob->jobId); /* pick distributed executor to use */ if (eflags & EXEC_FLAG_EXPLAIN_ONLY) { /* skip distributed query execution for EXPLAIN commands */ } else if (executorType == MULTI_EXECUTOR_REAL_TIME) { MultiRealTimeExecute(workerJob); } else if (executorType == MULTI_EXECUTOR_TASK_TRACKER) { MultiTaskTrackerExecute(workerJob); } /* then create the result relation */ ProcessUtility((Node *) masterCreateStmt, "(temp table creation)", PROCESS_UTILITY_QUERY, NULL, None_Receiver, NULL); /* make the temporary table visible */ CommandCounterIncrement(); if (!(eflags & EXEC_FLAG_EXPLAIN_ONLY)) { CopyQueryResults(masterCopyStmtList); } /* * Update the QueryDesc's snapshot so it sees the table. That's not * particularly pretty, but we don't have much of a choice. One might * think we could unregister the snapshot, push a new active one, * update it, register it, and be happy. That only works if it's only * registered once though... */ queryDesc->snapshot->curcid = GetCurrentCommandId(false); /* * Set the OID of the RTE used in the master select statement to point * to the now created (and filled) temporary table. The target * relation's oid is only known now. */ masterRangeTableEntry = (RangeTblEntry *) linitial(masterSelectPlan->rtable); masterRangeTableEntry->relid = RelnameGetRelid(masterRangeTableEntry->eref->aliasname); /* * Replace to-be-run query with the master select query. As the * planned statement is now replaced we can't call GetMultiPlan() in * the later hooks, so we set a flag marking this as a distributed * statement running on the master. That e.g. allows us to drop the * temp table later. * * We copy the original statement's queryId, to allow * pg_stat_statements and similar extension to associate the * statement with the toplevel statement. */ masterSelectPlan->queryId = queryDesc->plannedstmt->queryId; queryDesc->plannedstmt = masterSelectPlan; eflags |= EXEC_FLAG_CITUS_MASTER_SELECT; } } /* if the execution is not done for router executor, drop into standard executor */ if (queryDesc->estate == NULL || !(queryDesc->estate->es_top_eflags & EXEC_FLAG_CITUS_ROUTER_EXECUTOR)) { standard_ExecutorStart(queryDesc, eflags); } }