static bool const_record_walker(Node *node, pgssConstLocations *jstate) { bool result; if (node == NULL) return false; if (IsA(node, A_Const)) { RecordConstLocation(jstate, castNode(A_Const, node)->location); } else if (IsA(node, ParamRef)) { /* Track the highest ParamRef number */ if (((ParamRef *) node)->number > jstate->highest_extern_param_id) jstate->highest_extern_param_id = castNode(ParamRef, node)->number; } else if (IsA(node, DefElem)) { return const_record_walker((Node *) ((DefElem *) node)->arg, jstate); } else if (IsA(node, RawStmt)) { return const_record_walker((Node *) ((RawStmt *) node)->stmt, jstate); } else if (IsA(node, VariableSetStmt)) { return const_record_walker((Node *) ((VariableSetStmt *) node)->args, jstate); } else if (IsA(node, CopyStmt)) { return const_record_walker((Node *) ((CopyStmt *) node)->query, jstate); } else if (IsA(node, ExplainStmt)) { return const_record_walker((Node *) ((ExplainStmt *) node)->query, jstate); } else if (IsA(node, AlterRoleStmt)) { return const_record_walker((Node *) ((AlterRoleStmt *) node)->options, jstate); } else if (IsA(node, DeclareCursorStmt)) { return const_record_walker((Node *) ((DeclareCursorStmt *) node)->query, jstate); } PG_TRY(); { result = raw_expression_tree_walker(node, const_record_walker, (void*) jstate); } PG_CATCH(); { FlushErrorState(); result = false; } PG_END_TRY(); return result; }
/* ---------------------------------------------------------------- * ExecSetOp * ---------------------------------------------------------------- */ static TupleTableSlot * /* return: a tuple or NULL */ ExecSetOp(PlanState *pstate) { SetOpState *node = castNode(SetOpState, pstate); SetOp *plannode = (SetOp *) node->ps.plan; TupleTableSlot *resultTupleSlot = node->ps.ps_ResultTupleSlot; CHECK_FOR_INTERRUPTS(); /* * If the previously-returned tuple needs to be returned more than once, * keep returning it. */ if (node->numOutput > 0) { node->numOutput--; return resultTupleSlot; } /* Otherwise, we're done if we are out of groups */ if (node->setop_done) return NULL; /* Fetch the next tuple group according to the correct strategy */ if (plannode->strategy == SETOP_HASHED) { if (!node->table_filled) setop_fill_hash_table(node); return setop_retrieve_hash_table(node); } else return setop_retrieve_direct(node); }
/* ---------------------------------------------------------------- * ExecSeqScan(node) * * Scans the relation sequentially and returns the next qualifying * tuple. * We call the ExecScan() routine and pass it the appropriate * access method functions. * ---------------------------------------------------------------- */ static TupleTableSlot * ExecSeqScan(PlanState *pstate) { SeqScanState *node = castNode(SeqScanState, pstate); return ExecScan(&node->ss, (ExecScanAccessMtd) SeqNext, (ExecScanRecheckMtd) SeqRecheck); }
/* ---------------------------------------------------------------- * ExecCteScan(node) * * Scans the CTE sequentially and returns the next qualifying tuple. * We call the ExecScan() routine and pass it the appropriate * access method functions. * ---------------------------------------------------------------- */ static TupleTableSlot * ExecCteScan(PlanState *pstate) { CteScanState *node = castNode(CteScanState, pstate); return ExecScan(&node->ss, (ExecScanAccessMtd) CteScanNext, (ExecScanRecheckMtd) CteScanRecheck); }
/* ---------------------------------------------------------------- * ExecSubqueryScan(node) * * Scans the subquery sequentially and returns the next qualifying * tuple. * We call the ExecScan() routine and pass it the appropriate * access method functions. * ---------------------------------------------------------------- */ static TupleTableSlot * ExecSubqueryScan(PlanState *pstate) { SubqueryScanState *node = castNode(SubqueryScanState, pstate); return ExecScan(&node->ss, (ExecScanAccessMtd) SubqueryNext, (ExecScanRecheckMtd) SubqueryRecheck); }
/* ---------------------------------------------------------------- * ExecBitmapHeapScan(node) * ---------------------------------------------------------------- */ static TupleTableSlot * ExecBitmapHeapScan(PlanState *pstate) { BitmapHeapScanState *node = castNode(BitmapHeapScanState, pstate); return ExecScan(&node->ss, (ExecScanAccessMtd) BitmapHeapNext, (ExecScanRecheckMtd) BitmapHeapRecheck); }
/* ---------------------------------------------------------------- * ExecWorkTableScan(node) * * Scans the worktable sequentially and returns the next qualifying tuple. * We call the ExecScan() routine and pass it the appropriate * access method functions. * ---------------------------------------------------------------- */ static TupleTableSlot * ExecWorkTableScan(PlanState *pstate) { WorkTableScanState *node = castNode(WorkTableScanState, pstate); /* * On the first call, find the ancestor RecursiveUnion's state via the * Param slot reserved for it. (We can't do this during node init because * there are corner cases where we'll get the init call before the * RecursiveUnion does.) */ if (node->rustate == NULL) { WorkTableScan *plan = (WorkTableScan *) node->ss.ps.plan; EState *estate = node->ss.ps.state; ParamExecData *param; param = &(estate->es_param_exec_vals[plan->wtParam]); Assert(param->execPlan == NULL); Assert(!param->isnull); node->rustate = castNode(RecursiveUnionState, DatumGetPointer(param->value)); Assert(node->rustate); /* * The scan tuple type (ie, the rowtype we expect to find in the work * table) is the same as the result rowtype of the ancestor * RecursiveUnion node. Note this depends on the assumption that * RecursiveUnion doesn't allow projection. */ ExecAssignScanType(&node->ss, ExecGetResultType(&node->rustate->ps)); /* * Now we can initialize the projection info. This must be completed * before we can call ExecScan(). */ ExecAssignScanProjectionInfo(&node->ss); } return ExecScan(&node->ss, (ExecScanAccessMtd) WorkTableScanNext, (ExecScanRecheckMtd) WorkTableScanRecheck); }
/* ---------------------------------------------------------------- * ExecIndexOnlyScan(node) * ---------------------------------------------------------------- */ static TupleTableSlot * ExecIndexOnlyScan(PlanState *pstate) { IndexOnlyScanState *node = castNode(IndexOnlyScanState, pstate); /* * If we have runtime keys and they've not already been set up, do it now. */ if (node->ioss_NumRuntimeKeys != 0 && !node->ioss_RuntimeKeysReady) ExecReScan((PlanState *) node); return ExecScan(&node->ss, (ExecScanAccessMtd) IndexOnlyNext, (ExecScanRecheckMtd) IndexOnlyRecheck); }
/* * callback function in case a SetExprState needs to be shut down before it * has been run to completion */ static void ShutdownSetExpr(Datum arg) { SetExprState *sexpr = castNode(SetExprState, DatumGetPointer(arg)); /* If we have a slot, make sure it's let go of any tuplestore pointer */ if (sexpr->funcResultSlot) ExecClearTuple(sexpr->funcResultSlot); /* Release any open tuplestore */ if (sexpr->funcResultStore) tuplestore_end(sexpr->funcResultStore); sexpr->funcResultStore = NULL; /* Clear any active set-argument state */ sexpr->setArgsValid = false; /* execUtils will deregister the callback... */ sexpr->shutdown_reg = false; }
/* * Set up the data structures that we'll need for Gather Merge. * * We allocate these once on the basis of gm->num_workers, which is an * upper bound for the number of workers we'll actually have. During * a rescan, we reset the structures to empty. This approach simplifies * not leaking memory across rescans. * * In the gm_slots[] array, index 0 is for the leader, and indexes 1 to n * are for workers. The values placed into gm_heap correspond to indexes * in gm_slots[]. The gm_tuple_buffers[] array, however, is indexed from * 0 to n-1; it has no entry for the leader. */ static void gather_merge_setup(GatherMergeState *gm_state) { GatherMerge *gm = castNode(GatherMerge, gm_state->ps.plan); int nreaders = gm->num_workers; int i; /* * Allocate gm_slots for the number of workers + one more slot for leader. * Slot 0 is always for the leader. Leader always calls ExecProcNode() to * read the tuple, and then stores it directly into its gm_slots entry. * For other slots, code below will call ExecInitExtraTupleSlot() to * create a slot for the worker's results. Note that during any single * scan, we might have fewer than num_workers available workers, in which * case the extra array entries go unused. */ gm_state->gm_slots = (TupleTableSlot **) palloc0((nreaders + 1) * sizeof(TupleTableSlot *)); /* Allocate the tuple slot and tuple array for each worker */ gm_state->gm_tuple_buffers = (GMReaderTupleBuffer *) palloc0(nreaders * sizeof(GMReaderTupleBuffer)); for (i = 0; i < nreaders; i++) { /* Allocate the tuple array with length MAX_TUPLE_STORE */ gm_state->gm_tuple_buffers[i].tuple = (HeapTuple *) palloc0(sizeof(HeapTuple) * MAX_TUPLE_STORE); /* Initialize tuple slot for worker */ gm_state->gm_slots[i + 1] = ExecInitExtraTupleSlot(gm_state->ps.state, gm_state->tupDesc, &TTSOpsHeapTuple); } /* Allocate the resources for the merge */ gm_state->gm_heap = binaryheap_allocate(nreaders + 1, heap_compare_slots, gm_state); }
/* ---------------------------------------------------------------- * ExecResult(node) * * returns the tuples from the outer plan which satisfy the * qualification clause. Since result nodes with right * subtrees are never planned, we ignore the right subtree * entirely (for now).. -cim 10/7/89 * * The qualification containing only constant clauses are * checked first before any processing is done. It always returns * 'nil' if the constant qualification is not satisfied. * ---------------------------------------------------------------- */ static TupleTableSlot * ExecResult(PlanState *pstate) { ResultState *node = castNode(ResultState, pstate); TupleTableSlot *outerTupleSlot; PlanState *outerPlan; ExprContext *econtext; CHECK_FOR_INTERRUPTS(); econtext = node->ps.ps_ExprContext; /* * check constant qualifications like (2 > 1), if not already done */ if (node->rs_checkqual) { bool qualResult = ExecQual(node->resconstantqual, econtext); node->rs_checkqual = false; if (!qualResult) { node->rs_done = true; return NULL; } } /* * Reset per-tuple memory context to free any expression evaluation * storage allocated in the previous tuple cycle. */ ResetExprContext(econtext); /* * if rs_done is true then it means that we were asked to return a * constant tuple and we already did the last time ExecResult() was * called, OR that we failed the constant qual check. Either way, now we * are through. */ while (!node->rs_done) { outerPlan = outerPlanState(node); if (outerPlan != NULL) { /* * retrieve tuples from the outer plan until there are no more. */ outerTupleSlot = ExecProcNode(outerPlan); if (TupIsNull(outerTupleSlot)) return NULL; /* * prepare to compute projection expressions, which will expect to * access the input tuples as varno OUTER. */ econtext->ecxt_outertuple = outerTupleSlot; } else { /* * if we don't have an outer plan, then we are just generating the * results from a constant target list. Do it only once. */ node->rs_done = true; } /* form the result tuple using ExecProject(), and return it */ return ExecProject(node->ps.ps_ProjInfo); } return NULL; }
/* ---------------------------------------------------------------- * ExecInitCteScan * ---------------------------------------------------------------- */ CteScanState * ExecInitCteScan(CteScan *node, EState *estate, int eflags) { CteScanState *scanstate; ParamExecData *prmdata; /* check for unsupported flags */ Assert(!(eflags & EXEC_FLAG_MARK)); /* * For the moment we have to force the tuplestore to allow REWIND, because * we might be asked to rescan the CTE even though upper levels didn't * tell us to be prepared to do it efficiently. Annoying, since this * prevents truncation of the tuplestore. XXX FIXME * * Note: if we are in an EPQ recheck plan tree, it's likely that no access * to the tuplestore is needed at all, making this even more annoying. * It's not worth improving that as long as all the read pointers would * have REWIND anyway, but if we ever improve this logic then that aspect * should be considered too. */ eflags |= EXEC_FLAG_REWIND; /* * CteScan should not have any children. */ Assert(outerPlan(node) == NULL); Assert(innerPlan(node) == NULL); /* * create new CteScanState for node */ scanstate = makeNode(CteScanState); scanstate->ss.ps.plan = (Plan *) node; scanstate->ss.ps.state = estate; scanstate->ss.ps.ExecProcNode = ExecCteScan; scanstate->eflags = eflags; scanstate->cte_table = NULL; scanstate->eof_cte = false; /* * Find the already-initialized plan for the CTE query. */ scanstate->cteplanstate = (PlanState *) list_nth(estate->es_subplanstates, node->ctePlanId - 1); /* * The Param slot associated with the CTE query is used to hold a pointer * to the CteState of the first CteScan node that initializes for this * CTE. This node will be the one that holds the shared state for all the * CTEs, particularly the shared tuplestore. */ prmdata = &(estate->es_param_exec_vals[node->cteParam]); Assert(prmdata->execPlan == NULL); Assert(!prmdata->isnull); scanstate->leader = castNode(CteScanState, DatumGetPointer(prmdata->value)); if (scanstate->leader == NULL) { /* I am the leader */ prmdata->value = PointerGetDatum(scanstate); scanstate->leader = scanstate; scanstate->cte_table = tuplestore_begin_heap(true, false, work_mem); tuplestore_set_eflags(scanstate->cte_table, scanstate->eflags); scanstate->readptr = 0; } else { /* Not the leader */ /* Create my own read pointer, and ensure it is at start */ scanstate->readptr = tuplestore_alloc_read_pointer(scanstate->leader->cte_table, scanstate->eflags); tuplestore_select_read_pointer(scanstate->leader->cte_table, scanstate->readptr); tuplestore_rescan(scanstate->leader->cte_table); } /* * Miscellaneous initialization * * create expression context for node */ ExecAssignExprContext(estate, &scanstate->ss.ps); /* * The scan tuple type (ie, the rowtype we expect to find in the work * table) is the same as the result rowtype of the CTE query. */ ExecInitScanTupleSlot(estate, &scanstate->ss, ExecGetResultType(scanstate->cteplanstate), &TTSOpsMinimalTuple); /* * Initialize result type and projection. */ ExecInitResultTypeTL(&scanstate->ss.ps); ExecAssignScanProjectionInfo(&scanstate->ss); /* * initialize child expressions */ scanstate->ss.ps.qual = ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate); return scanstate; }
/* ---------------------------------------------------------------- * ExecGatherMerge(node) * * Scans the relation via multiple workers and returns * the next qualifying tuple. * ---------------------------------------------------------------- */ static TupleTableSlot * ExecGatherMerge(PlanState *pstate) { GatherMergeState *node = castNode(GatherMergeState, pstate); TupleTableSlot *slot; ExprContext *econtext; CHECK_FOR_INTERRUPTS(); /* * As with Gather, we don't launch workers until this node is actually * executed. */ if (!node->initialized) { EState *estate = node->ps.state; GatherMerge *gm = castNode(GatherMerge, node->ps.plan); /* * Sometimes we might have to run without parallelism; but if parallel * mode is active then we can try to fire up some workers. */ if (gm->num_workers > 0 && estate->es_use_parallel_mode) { ParallelContext *pcxt; /* Initialize, or re-initialize, shared state needed by workers. */ if (!node->pei) node->pei = ExecInitParallelPlan(node->ps.lefttree, estate, gm->initParam, gm->num_workers, node->tuples_needed); else ExecParallelReinitialize(node->ps.lefttree, node->pei, gm->initParam); /* Try to launch workers. */ pcxt = node->pei->pcxt; LaunchParallelWorkers(pcxt); /* We save # workers launched for the benefit of EXPLAIN */ node->nworkers_launched = pcxt->nworkers_launched; /* Set up tuple queue readers to read the results. */ if (pcxt->nworkers_launched > 0) { ExecParallelCreateReaders(node->pei); /* Make a working array showing the active readers */ node->nreaders = pcxt->nworkers_launched; node->reader = (TupleQueueReader **) palloc(node->nreaders * sizeof(TupleQueueReader *)); memcpy(node->reader, node->pei->reader, node->nreaders * sizeof(TupleQueueReader *)); } else { /* No workers? Then never mind. */ node->nreaders = 0; node->reader = NULL; } } /* allow leader to participate if enabled or no choice */ if (parallel_leader_participation || node->nreaders == 0) node->need_to_scan_locally = true; node->initialized = true; } /* * Reset per-tuple memory context to free any expression evaluation * storage allocated in the previous tuple cycle. */ econtext = node->ps.ps_ExprContext; ResetExprContext(econtext); /* * Get next tuple, either from one of our workers, or by running the plan * ourselves. */ slot = gather_merge_getnext(node); if (TupIsNull(slot)) return NULL; /* If no projection is required, we're done. */ if (node->ps.ps_ProjInfo == NULL) return slot; /* * Form the result tuple using ExecProject(), and return it. */ econtext->ecxt_outertuple = slot; return ExecProject(node->ps.ps_ProjInfo); }
/* * PerformCursorOpen * Execute SQL DECLARE CURSOR command. */ void PerformCursorOpen(DeclareCursorStmt *cstmt, ParamListInfo params, const char *queryString, bool isTopLevel) { Query *query = castNode(Query, cstmt->query); List *rewritten; PlannedStmt *plan; Portal portal; MemoryContext oldContext; /* * Disallow empty-string cursor name (conflicts with protocol-level * unnamed portal). */ if (!cstmt->portalname || cstmt->portalname[0] == '\0') ereport(ERROR, (errcode(ERRCODE_INVALID_CURSOR_NAME), errmsg("invalid cursor name: must not be empty"))); /* * If this is a non-holdable cursor, we require that this statement has * been executed inside a transaction block (or else, it would have no * user-visible effect). */ if (!(cstmt->options & CURSOR_OPT_HOLD)) RequireTransactionChain(isTopLevel, "DECLARE CURSOR"); /* * Parse analysis was done already, but we still have to run the rule * rewriter. We do not do AcquireRewriteLocks: we assume the query either * came straight from the parser, or suitable locks were acquired by * plancache.c. * * Because the rewriter and planner tend to scribble on the input, we make * a preliminary copy of the source querytree. This prevents problems in * the case that the DECLARE CURSOR is in a portal or plpgsql function and * is executed repeatedly. (See also the same hack in EXPLAIN and * PREPARE.) XXX FIXME someday. */ rewritten = QueryRewrite((Query *) copyObject(query)); /* SELECT should never rewrite to more or less than one query */ if (list_length(rewritten) != 1) elog(ERROR, "non-SELECT statement in DECLARE CURSOR"); query = castNode(Query, linitial(rewritten)); if (query->commandType != CMD_SELECT) elog(ERROR, "non-SELECT statement in DECLARE CURSOR"); /* Plan the query, applying the specified options */ plan = pg_plan_query(query, cstmt->options, params); /* * Create a portal and copy the plan and queryString into its memory. */ portal = CreatePortal(cstmt->portalname, false, false); oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal)); plan = copyObject(plan); queryString = pstrdup(queryString); PortalDefineQuery(portal, NULL, queryString, "SELECT", /* cursor's query is always a SELECT */ list_make1(plan), NULL); /*---------- * Also copy the outer portal's parameter list into the inner portal's * memory context. We want to pass down the parameter values in case we * had a command like * DECLARE c CURSOR FOR SELECT ... WHERE foo = $1 * This will have been parsed using the outer parameter set and the * parameter value needs to be preserved for use when the cursor is * executed. *---------- */ params = copyParamList(params); MemoryContextSwitchTo(oldContext); /* * Set up options for portal. * * If the user didn't specify a SCROLL type, allow or disallow scrolling * based on whether it would require any additional runtime overhead to do * so. Also, we disallow scrolling for FOR UPDATE cursors. */ portal->cursorOptions = cstmt->options; if (!(portal->cursorOptions & (CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL))) { if (plan->rowMarks == NIL && ExecSupportsBackwardScan(plan->planTree)) portal->cursorOptions |= CURSOR_OPT_SCROLL; else portal->cursorOptions |= CURSOR_OPT_NO_SCROLL; } /* * Start execution, inserting parameters if any. */ PortalStart(portal, params, 0, GetActiveSnapshot()); Assert(portal->strategy == PORTAL_ONE_SELECT); /* * We're done; the query won't actually be run until PerformPortalFetch is * called. */ }
/* ---------------------------------------------------------------- * ExecMaterial * * As long as we are at the end of the data collected in the tuplestore, * we collect one new row from the subplan on each call, and stash it * aside in the tuplestore before returning it. The tuplestore is * only read if we are asked to scan backwards, rescan, or mark/restore. * * ---------------------------------------------------------------- */ static TupleTableSlot * /* result tuple from subplan */ ExecMaterial(PlanState *pstate) { MaterialState *node = castNode(MaterialState, pstate); EState *estate; ScanDirection dir; bool forward; Tuplestorestate *tuplestorestate; bool eof_tuplestore; TupleTableSlot *slot; CHECK_FOR_INTERRUPTS(); /* * get state info from node */ estate = node->ss.ps.state; dir = estate->es_direction; forward = ScanDirectionIsForward(dir); tuplestorestate = node->tuplestorestate; /* * If first time through, and we need a tuplestore, initialize it. */ if (tuplestorestate == NULL && node->eflags != 0) { tuplestorestate = tuplestore_begin_heap(true, false, work_mem); tuplestore_set_eflags(tuplestorestate, node->eflags); if (node->eflags & EXEC_FLAG_MARK) { /* * Allocate a second read pointer to serve as the mark. We know it * must have index 1, so needn't store that. */ int ptrno PG_USED_FOR_ASSERTS_ONLY; ptrno = tuplestore_alloc_read_pointer(tuplestorestate, node->eflags); Assert(ptrno == 1); } node->tuplestorestate = tuplestorestate; } /* * If we are not at the end of the tuplestore, or are going backwards, try * to fetch a tuple from tuplestore. */ eof_tuplestore = (tuplestorestate == NULL) || tuplestore_ateof(tuplestorestate); if (!forward && eof_tuplestore) { if (!node->eof_underlying) { /* * When reversing direction at tuplestore EOF, the first * gettupleslot call will fetch the last-added tuple; but we want * to return the one before that, if possible. So do an extra * fetch. */ if (!tuplestore_advance(tuplestorestate, forward)) return NULL; /* the tuplestore must be empty */ } eof_tuplestore = false; } /* * If we can fetch another tuple from the tuplestore, return it. */ slot = node->ss.ps.ps_ResultTupleSlot; if (!eof_tuplestore) { if (tuplestore_gettupleslot(tuplestorestate, forward, false, slot)) return slot; if (forward) eof_tuplestore = true; } /* * If necessary, try to fetch another row from the subplan. * * Note: the eof_underlying state variable exists to short-circuit further * subplan calls. It's not optional, unfortunately, because some plan * node types are not robust about being called again when they've already * returned NULL. */ if (eof_tuplestore && !node->eof_underlying) { PlanState *outerNode; TupleTableSlot *outerslot; /* * We can only get here with forward==true, so no need to worry about * which direction the subplan will go. */ outerNode = outerPlanState(node); outerslot = ExecProcNode(outerNode); if (TupIsNull(outerslot)) { node->eof_underlying = true; return NULL; } /* * Append a copy of the returned tuple to tuplestore. NOTE: because * the tuplestore is certainly in EOF state, its read position will * move forward over the added tuple. This is what we want. */ if (tuplestorestate) tuplestore_puttupleslot(tuplestorestate, outerslot); ExecCopySlot(slot, outerslot); return slot; } /* * Nothing left ... */ return ExecClearTuple(slot); }
/* * ExecGroup - * * Return one tuple for each group of matching input tuples. */ static TupleTableSlot * ExecGroup(PlanState *pstate) { GroupState *node = castNode(GroupState, pstate); ExprContext *econtext; int numCols; AttrNumber *grpColIdx; TupleTableSlot *firsttupleslot; TupleTableSlot *outerslot; CHECK_FOR_INTERRUPTS(); /* * get state info from node */ if (node->grp_done) return NULL; econtext = node->ss.ps.ps_ExprContext; numCols = ((Group *) node->ss.ps.plan)->numCols; grpColIdx = ((Group *) node->ss.ps.plan)->grpColIdx; /* * The ScanTupleSlot holds the (copied) first tuple of each group. */ firsttupleslot = node->ss.ss_ScanTupleSlot; /* * We need not call ResetExprContext here because execTuplesMatch will * reset the per-tuple memory context once per input tuple. */ /* * If first time through, acquire first input tuple and determine whether * to return it or not. */ if (TupIsNull(firsttupleslot)) { outerslot = ExecProcNode(outerPlanState(node)); if (TupIsNull(outerslot)) { /* empty input, so return nothing */ node->grp_done = true; return NULL; } /* Copy tuple into firsttupleslot */ ExecCopySlot(firsttupleslot, outerslot); /* * Set it up as input for qual test and projection. The expressions * will access the input tuple as varno OUTER. */ econtext->ecxt_outertuple = firsttupleslot; /* * Check the qual (HAVING clause); if the group does not match, ignore * it and fall into scan loop. */ if (ExecQual(node->ss.ps.qual, econtext)) { /* * Form and return a projection tuple using the first input tuple. */ return ExecProject(node->ss.ps.ps_ProjInfo); } else InstrCountFiltered1(node, 1); } /* * This loop iterates once per input tuple group. At the head of the * loop, we have finished processing the first tuple of the group and now * need to scan over all the other group members. */ for (;;) { /* * Scan over all remaining tuples that belong to this group */ for (;;) { outerslot = ExecProcNode(outerPlanState(node)); if (TupIsNull(outerslot)) { /* no more groups, so we're done */ node->grp_done = true; return NULL; } /* * Compare with first tuple and see if this tuple is of the same * group. If so, ignore it and keep scanning. */ if (!execTuplesMatch(firsttupleslot, outerslot, numCols, grpColIdx, node->eqfunctions, econtext->ecxt_per_tuple_memory)) break; } /* * We have the first tuple of the next input group. See if we want to * return it. */ /* Copy tuple, set up as input for qual test and projection */ ExecCopySlot(firsttupleslot, outerslot); econtext->ecxt_outertuple = firsttupleslot; /* * Check the qual (HAVING clause); if the group does not match, ignore * it and loop back to scan the rest of the group. */ if (ExecQual(node->ss.ps.qual, econtext)) { /* * Form and return a projection tuple using the first input tuple. */ return ExecProject(node->ss.ps.ps_ProjInfo); } else InstrCountFiltered1(node, 1); } }
/* ---------------------------------------------------------------- * ExecLimit * * This is a very simple node which just performs LIMIT/OFFSET * filtering on the stream of tuples returned by a subplan. * ---------------------------------------------------------------- */ static TupleTableSlot * /* return: a tuple or NULL */ ExecLimit(PlanState *pstate) { LimitState *node = castNode(LimitState, pstate); ScanDirection direction; TupleTableSlot *slot; PlanState *outerPlan; CHECK_FOR_INTERRUPTS(); /* * get information from the node */ direction = node->ps.state->es_direction; outerPlan = outerPlanState(node); /* * The main logic is a simple state machine. */ switch (node->lstate) { case LIMIT_INITIAL: /* * First call for this node, so compute limit/offset. (We can't do * this any earlier, because parameters from upper nodes will not * be set during ExecInitLimit.) This also sets position = 0 and * changes the state to LIMIT_RESCAN. */ recompute_limits(node); /* FALL THRU */ case LIMIT_RESCAN: /* * If backwards scan, just return NULL without changing state. */ if (!ScanDirectionIsForward(direction)) return NULL; /* * Check for empty window; if so, treat like empty subplan. */ if (node->count <= 0 && !node->noCount) { node->lstate = LIMIT_EMPTY; return NULL; } /* * Fetch rows from subplan until we reach position > offset. */ for (;;) { slot = ExecProcNode(outerPlan); if (TupIsNull(slot)) { /* * The subplan returns too few tuples for us to produce * any output at all. */ node->lstate = LIMIT_EMPTY; return NULL; } node->subSlot = slot; if (++node->position > node->offset) break; } /* * Okay, we have the first tuple of the window. */ node->lstate = LIMIT_INWINDOW; break; case LIMIT_EMPTY: /* * The subplan is known to return no tuples (or not more than * OFFSET tuples, in general). So we return no tuples. */ return NULL; case LIMIT_INWINDOW: if (ScanDirectionIsForward(direction)) { /* * Forwards scan, so check for stepping off end of window. If * we are at the end of the window, return NULL without * advancing the subplan or the position variable; but change * the state machine state to record having done so. */ if (!node->noCount && node->position - node->offset >= node->count) { node->lstate = LIMIT_WINDOWEND; return NULL; } /* * Get next tuple from subplan, if any. */ slot = ExecProcNode(outerPlan); if (TupIsNull(slot)) { node->lstate = LIMIT_SUBPLANEOF; return NULL; } node->subSlot = slot; node->position++; } else { /* * Backwards scan, so check for stepping off start of window. * As above, change only state-machine status if so. */ if (node->position <= node->offset + 1) { node->lstate = LIMIT_WINDOWSTART; return NULL; } /* * Get previous tuple from subplan; there should be one! */ slot = ExecProcNode(outerPlan); if (TupIsNull(slot)) elog(ERROR, "LIMIT subplan failed to run backwards"); node->subSlot = slot; node->position--; } break; case LIMIT_SUBPLANEOF: if (ScanDirectionIsForward(direction)) return NULL; /* * Backing up from subplan EOF, so re-fetch previous tuple; there * should be one! Note previous tuple must be in window. */ slot = ExecProcNode(outerPlan); if (TupIsNull(slot)) elog(ERROR, "LIMIT subplan failed to run backwards"); node->subSlot = slot; node->lstate = LIMIT_INWINDOW; /* position does not change 'cause we didn't advance it before */ break; case LIMIT_WINDOWEND: if (ScanDirectionIsForward(direction)) return NULL; /* * Backing up from window end: simply re-return the last tuple * fetched from the subplan. */ slot = node->subSlot; node->lstate = LIMIT_INWINDOW; /* position does not change 'cause we didn't advance it before */ break; case LIMIT_WINDOWSTART: if (!ScanDirectionIsForward(direction)) return NULL; /* * Advancing after having backed off window start: simply * re-return the last tuple fetched from the subplan. */ slot = node->subSlot; node->lstate = LIMIT_INWINDOW; /* position does not change 'cause we didn't change it before */ break; default: elog(ERROR, "impossible LIMIT state: %d", (int) node->lstate); slot = NULL; /* keep compiler quiet */ break; } /* Return the current tuple */ Assert(!TupIsNull(slot)); return slot; }
/* * Initialize the Gather Merge. * * Reset data structures to ensure they're empty. Then pull at least one * tuple from leader + each worker (or set its "done" indicator), and set up * the heap. */ static void gather_merge_init(GatherMergeState *gm_state) { int nreaders = gm_state->nreaders; bool nowait = true; int i; /* Assert that gather_merge_setup made enough space */ Assert(nreaders <= castNode(GatherMerge, gm_state->ps.plan)->num_workers); /* Reset leader's tuple slot to empty */ gm_state->gm_slots[0] = NULL; /* Reset the tuple slot and tuple array for each worker */ for (i = 0; i < nreaders; i++) { /* Reset tuple array to empty */ gm_state->gm_tuple_buffers[i].nTuples = 0; gm_state->gm_tuple_buffers[i].readCounter = 0; /* Reset done flag to not-done */ gm_state->gm_tuple_buffers[i].done = false; /* Ensure output slot is empty */ ExecClearTuple(gm_state->gm_slots[i + 1]); } /* Reset binary heap to empty */ binaryheap_reset(gm_state->gm_heap); /* * First, try to read a tuple from each worker (including leader) in * nowait mode. After this, if not all workers were able to produce a * tuple (or a "done" indication), then re-read from remaining workers, * this time using wait mode. Add all live readers (those producing at * least one tuple) to the heap. */ reread: for (i = 0; i <= nreaders; i++) { CHECK_FOR_INTERRUPTS(); /* skip this source if already known done */ if ((i == 0) ? gm_state->need_to_scan_locally : !gm_state->gm_tuple_buffers[i - 1].done) { if (TupIsNull(gm_state->gm_slots[i])) { /* Don't have a tuple yet, try to get one */ if (gather_merge_readnext(gm_state, i, nowait)) binaryheap_add_unordered(gm_state->gm_heap, Int32GetDatum(i)); } else { /* * We already got at least one tuple from this worker, but * might as well see if it has any more ready by now. */ load_tuple_array(gm_state, i); } } } /* need not recheck leader, since nowait doesn't matter for it */ for (i = 1; i <= nreaders; i++) { if (!gm_state->gm_tuple_buffers[i - 1].done && TupIsNull(gm_state->gm_slots[i])) { nowait = false; goto reread; } } /* Now heapify the heap. */ binaryheap_build(gm_state->gm_heap); gm_state->gm_initialized = true; }
/* ---------------------------------------------------------------- * ExecSort * * Sorts tuples from the outer subtree of the node using tuplesort, * which saves the results in a temporary file or memory. After the * initial call, returns a tuple from the file with each call. * * Conditions: * -- none. * * Initial States: * -- the outer child is prepared to return the first tuple. * ---------------------------------------------------------------- */ static TupleTableSlot * ExecSort(PlanState *pstate) { SortState *node = castNode(SortState, pstate); EState *estate; ScanDirection dir; Tuplesortstate *tuplesortstate; TupleTableSlot *slot; CHECK_FOR_INTERRUPTS(); /* * get state info from node */ SO1_printf("ExecSort: %s\n", "entering routine"); estate = node->ss.ps.state; dir = estate->es_direction; tuplesortstate = (Tuplesortstate *) node->tuplesortstate; /* * If first time through, read all tuples from outer plan and pass them to * tuplesort.c. Subsequent calls just fetch tuples from tuplesort. */ if (!node->sort_Done) { Sort *plannode = (Sort *) node->ss.ps.plan; PlanState *outerNode; TupleDesc tupDesc; SO1_printf("ExecSort: %s\n", "sorting subplan"); /* * Want to scan subplan in the forward direction while creating the * sorted data. */ estate->es_direction = ForwardScanDirection; /* * Initialize tuplesort module. */ SO1_printf("ExecSort: %s\n", "calling tuplesort_begin"); outerNode = outerPlanState(node); tupDesc = ExecGetResultType(outerNode); tuplesortstate = tuplesort_begin_heap(tupDesc, plannode->numCols, plannode->sortColIdx, plannode->sortOperators, plannode->collations, plannode->nullsFirst, work_mem, NULL, node->randomAccess); if (node->bounded) tuplesort_set_bound(tuplesortstate, node->bound); node->tuplesortstate = (void *) tuplesortstate; /* * Scan the subplan and feed all the tuples to tuplesort. */ for (;;) { slot = ExecProcNode(outerNode); if (TupIsNull(slot)) break; tuplesort_puttupleslot(tuplesortstate, slot); } /* * Complete the sort. */ tuplesort_performsort(tuplesortstate); /* * restore to user specified direction */ estate->es_direction = dir; /* * finally set the sorted flag to true */ node->sort_Done = true; node->bounded_Done = node->bounded; node->bound_Done = node->bound; if (node->shared_info && node->am_worker) { TuplesortInstrumentation *si; Assert(IsParallelWorker()); Assert(ParallelWorkerNumber <= node->shared_info->num_workers); si = &node->shared_info->sinstrument[ParallelWorkerNumber]; tuplesort_get_stats(tuplesortstate, si); } SO1_printf("ExecSort: %s\n", "sorting done"); } SO1_printf("ExecSort: %s\n", "retrieving tuple from tuplesort"); /* * Get the first or next tuple from tuplesort. Returns NULL if no more * tuples. Note that we only rely on slot tuple remaining valid until the * next fetch from the tuplesort. */ slot = node->ss.ps.ps_ResultTupleSlot; (void) tuplesort_gettupleslot(tuplesortstate, ScanDirectionIsForward(dir), false, slot, NULL); return slot; }
/* ---------------------------------------------------------------- * ExecGatherMerge(node) * * Scans the relation via multiple workers and returns * the next qualifying tuple. * ---------------------------------------------------------------- */ static TupleTableSlot * ExecGatherMerge(PlanState *pstate) { GatherMergeState *node = castNode(GatherMergeState, pstate); TupleTableSlot *slot; ExprContext *econtext; int i; CHECK_FOR_INTERRUPTS(); /* * As with Gather, we don't launch workers until this node is actually * executed. */ if (!node->initialized) { EState *estate = node->ps.state; GatherMerge *gm = (GatherMerge *) node->ps.plan; /* * Sometimes we might have to run without parallelism; but if parallel * mode is active then we can try to fire up some workers. */ if (gm->num_workers > 0 && IsInParallelMode()) { ParallelContext *pcxt; /* Initialize data structures for workers. */ if (!node->pei) node->pei = ExecInitParallelPlan(node->ps.lefttree, estate, gm->num_workers); /* Try to launch workers. */ pcxt = node->pei->pcxt; LaunchParallelWorkers(pcxt); node->nworkers_launched = pcxt->nworkers_launched; /* Set up tuple queue readers to read the results. */ if (pcxt->nworkers_launched > 0) { node->nreaders = 0; node->reader = palloc(pcxt->nworkers_launched * sizeof(TupleQueueReader *)); Assert(gm->numCols); for (i = 0; i < pcxt->nworkers_launched; ++i) { shm_mq_set_handle(node->pei->tqueue[i], pcxt->worker[i].bgwhandle); node->reader[node->nreaders++] = CreateTupleQueueReader(node->pei->tqueue[i], node->tupDesc); } } else { /* No workers? Then never mind. */ ExecShutdownGatherMergeWorkers(node); } } /* always allow leader to participate */ node->need_to_scan_locally = true; node->initialized = true; } /* * Reset per-tuple memory context to free any expression evaluation * storage allocated in the previous tuple cycle. */ econtext = node->ps.ps_ExprContext; ResetExprContext(econtext); /* * Get next tuple, either from one of our workers, or by running the plan * ourselves. */ slot = gather_merge_getnext(node); if (TupIsNull(slot)) return NULL; /* * form the result tuple using ExecProject(), and return it --- unless the * projection produces an empty set, in which case we must loop back * around for another tuple */ econtext->ecxt_outertuple = slot; return ExecProject(node->ps.ps_ProjInfo); }
/* ---------------------------------------------------------------- * ExecGather(node) * * Scans the relation via multiple workers and returns * the next qualifying tuple. * ---------------------------------------------------------------- */ static TupleTableSlot * ExecGather(PlanState *pstate) { GatherState *node = castNode(GatherState, pstate); TupleTableSlot *slot; ExprContext *econtext; CHECK_FOR_INTERRUPTS(); /* * Initialize the parallel context and workers on first execution. We do * this on first execution rather than during node initialization, as it * needs to allocate a large dynamic segment, so it is better to do it * only if it is really needed. */ if (!node->initialized) { EState *estate = node->ps.state; Gather *gather = (Gather *) node->ps.plan; /* * Sometimes we might have to run without parallelism; but if parallel * mode is active then we can try to fire up some workers. */ if (gather->num_workers > 0 && estate->es_use_parallel_mode) { ParallelContext *pcxt; /* Initialize, or re-initialize, shared state needed by workers. */ if (!node->pei) node->pei = ExecInitParallelPlan(node->ps.lefttree, estate, gather->initParam, gather->num_workers, node->tuples_needed); else ExecParallelReinitialize(node->ps.lefttree, node->pei, gather->initParam); /* * Register backend workers. We might not get as many as we * requested, or indeed any at all. */ pcxt = node->pei->pcxt; LaunchParallelWorkers(pcxt); /* We save # workers launched for the benefit of EXPLAIN */ node->nworkers_launched = pcxt->nworkers_launched; /* Set up tuple queue readers to read the results. */ if (pcxt->nworkers_launched > 0) { ExecParallelCreateReaders(node->pei); /* Make a working array showing the active readers */ node->nreaders = pcxt->nworkers_launched; node->reader = (TupleQueueReader **) palloc(node->nreaders * sizeof(TupleQueueReader *)); memcpy(node->reader, node->pei->reader, node->nreaders * sizeof(TupleQueueReader *)); } else { /* No workers? Then never mind. */ node->nreaders = 0; node->reader = NULL; } node->nextreader = 0; } /* Run plan locally if no workers or enabled and not single-copy. */ node->need_to_scan_locally = (node->nreaders == 0) || (!gather->single_copy && parallel_leader_participation); node->initialized = true; } /* * Reset per-tuple memory context to free any expression evaluation * storage allocated in the previous tuple cycle. */ econtext = node->ps.ps_ExprContext; ResetExprContext(econtext); /* * Get next tuple, either from one of our workers, or by running the plan * ourselves. */ slot = gather_getnext(node); if (TupIsNull(slot)) return NULL; /* If no projection is required, we're done. */ if (node->ps.ps_ProjInfo == NULL) return slot; /* * Form the result tuple using ExecProject(), and return it. */ econtext->ecxt_outertuple = slot; return ExecProject(node->ps.ps_ProjInfo); }
/* ---------------------------------------------------------------- * ProcedureCreate * * Note: allParameterTypes, parameterModes, parameterNames, trftypes, and proconfig * are either arrays of the proper types or NULL. We declare them Datum, * not "ArrayType *", to avoid importing array.h into pg_proc.h. * ---------------------------------------------------------------- */ ObjectAddress ProcedureCreate(const char *procedureName, Oid procNamespace, bool replace, bool returnsSet, Oid returnType, Oid proowner, Oid languageObjectId, Oid languageValidator, const char *prosrc, const char *probin, char prokind, bool security_definer, bool isLeakProof, bool isStrict, char volatility, char parallel, oidvector *parameterTypes, Datum allParameterTypes, Datum parameterModes, Datum parameterNames, List *parameterDefaults, Datum trftypes, Datum proconfig, float4 procost, float4 prorows) { Oid retval; int parameterCount; int allParamCount; Oid *allParams; char *paramModes = NULL; bool genericInParam = false; bool genericOutParam = false; bool anyrangeInParam = false; bool anyrangeOutParam = false; bool internalInParam = false; bool internalOutParam = false; Oid variadicType = InvalidOid; Acl *proacl = NULL; Relation rel; HeapTuple tup; HeapTuple oldtup; bool nulls[Natts_pg_proc]; Datum values[Natts_pg_proc]; bool replaces[Natts_pg_proc]; NameData procname; TupleDesc tupDesc; bool is_update; ObjectAddress myself, referenced; int i; Oid trfid; /* * sanity checks */ Assert(PointerIsValid(prosrc)); parameterCount = parameterTypes->dim1; if (parameterCount < 0 || parameterCount > FUNC_MAX_ARGS) ereport(ERROR, (errcode(ERRCODE_TOO_MANY_ARGUMENTS), errmsg_plural("functions cannot have more than %d argument", "functions cannot have more than %d arguments", FUNC_MAX_ARGS, FUNC_MAX_ARGS))); /* note: the above is correct, we do NOT count output arguments */ /* Deconstruct array inputs */ if (allParameterTypes != PointerGetDatum(NULL)) { /* * We expect the array to be a 1-D OID array; verify that. We don't * need to use deconstruct_array() since the array data is just going * to look like a C array of OID values. */ ArrayType *allParamArray = (ArrayType *) DatumGetPointer(allParameterTypes); allParamCount = ARR_DIMS(allParamArray)[0]; if (ARR_NDIM(allParamArray) != 1 || allParamCount <= 0 || ARR_HASNULL(allParamArray) || ARR_ELEMTYPE(allParamArray) != OIDOID) elog(ERROR, "allParameterTypes is not a 1-D Oid array"); allParams = (Oid *) ARR_DATA_PTR(allParamArray); Assert(allParamCount >= parameterCount); /* we assume caller got the contents right */ } else { allParamCount = parameterCount; allParams = parameterTypes->values; } if (parameterModes != PointerGetDatum(NULL)) { /* * We expect the array to be a 1-D CHAR array; verify that. We don't * need to use deconstruct_array() since the array data is just going * to look like a C array of char values. */ ArrayType *modesArray = (ArrayType *) DatumGetPointer(parameterModes); if (ARR_NDIM(modesArray) != 1 || ARR_DIMS(modesArray)[0] != allParamCount || ARR_HASNULL(modesArray) || ARR_ELEMTYPE(modesArray) != CHAROID) elog(ERROR, "parameterModes is not a 1-D char array"); paramModes = (char *) ARR_DATA_PTR(modesArray); } /* * Detect whether we have polymorphic or INTERNAL arguments. The first * loop checks input arguments, the second output arguments. */ for (i = 0; i < parameterCount; i++) { switch (parameterTypes->values[i]) { case ANYARRAYOID: case ANYELEMENTOID: case ANYNONARRAYOID: case ANYENUMOID: genericInParam = true; break; case ANYRANGEOID: genericInParam = true; anyrangeInParam = true; break; case INTERNALOID: internalInParam = true; break; } } if (allParameterTypes != PointerGetDatum(NULL)) { for (i = 0; i < allParamCount; i++) { if (paramModes == NULL || paramModes[i] == PROARGMODE_IN || paramModes[i] == PROARGMODE_VARIADIC) continue; /* ignore input-only params */ switch (allParams[i]) { case ANYARRAYOID: case ANYELEMENTOID: case ANYNONARRAYOID: case ANYENUMOID: genericOutParam = true; break; case ANYRANGEOID: genericOutParam = true; anyrangeOutParam = true; break; case INTERNALOID: internalOutParam = true; break; } } } /* * Do not allow polymorphic return type unless at least one input argument * is polymorphic. ANYRANGE return type is even stricter: must have an * ANYRANGE input (since we can't deduce the specific range type from * ANYELEMENT). Also, do not allow return type INTERNAL unless at least * one input argument is INTERNAL. */ if ((IsPolymorphicType(returnType) || genericOutParam) && !genericInParam) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot determine result data type"), errdetail("A function returning a polymorphic type must have at least one polymorphic argument."))); if ((returnType == ANYRANGEOID || anyrangeOutParam) && !anyrangeInParam) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot determine result data type"), errdetail("A function returning \"anyrange\" must have at least one \"anyrange\" argument."))); if ((returnType == INTERNALOID || internalOutParam) && !internalInParam) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("unsafe use of pseudo-type \"internal\""), errdetail("A function returning \"internal\" must have at least one \"internal\" argument."))); if (paramModes != NULL) { /* * Only the last input parameter can be variadic; if it is, save its * element type. Errors here are just elog since caller should have * checked this already. */ for (i = 0; i < allParamCount; i++) { switch (paramModes[i]) { case PROARGMODE_IN: case PROARGMODE_INOUT: if (OidIsValid(variadicType)) elog(ERROR, "variadic parameter must be last"); break; case PROARGMODE_OUT: case PROARGMODE_TABLE: /* okay */ break; case PROARGMODE_VARIADIC: if (OidIsValid(variadicType)) elog(ERROR, "variadic parameter must be last"); switch (allParams[i]) { case ANYOID: variadicType = ANYOID; break; case ANYARRAYOID: variadicType = ANYELEMENTOID; break; default: variadicType = get_element_type(allParams[i]); if (!OidIsValid(variadicType)) elog(ERROR, "variadic parameter is not an array"); break; } break; default: elog(ERROR, "invalid parameter mode '%c'", paramModes[i]); break; } } } /* * All seems OK; prepare the data to be inserted into pg_proc. */ for (i = 0; i < Natts_pg_proc; ++i) { nulls[i] = false; values[i] = (Datum) 0; replaces[i] = true; } namestrcpy(&procname, procedureName); values[Anum_pg_proc_proname - 1] = NameGetDatum(&procname); values[Anum_pg_proc_pronamespace - 1] = ObjectIdGetDatum(procNamespace); values[Anum_pg_proc_proowner - 1] = ObjectIdGetDatum(proowner); values[Anum_pg_proc_prolang - 1] = ObjectIdGetDatum(languageObjectId); values[Anum_pg_proc_procost - 1] = Float4GetDatum(procost); values[Anum_pg_proc_prorows - 1] = Float4GetDatum(prorows); values[Anum_pg_proc_provariadic - 1] = ObjectIdGetDatum(variadicType); values[Anum_pg_proc_protransform - 1] = ObjectIdGetDatum(InvalidOid); values[Anum_pg_proc_prokind - 1] = CharGetDatum(prokind); values[Anum_pg_proc_prosecdef - 1] = BoolGetDatum(security_definer); values[Anum_pg_proc_proleakproof - 1] = BoolGetDatum(isLeakProof); values[Anum_pg_proc_proisstrict - 1] = BoolGetDatum(isStrict); values[Anum_pg_proc_proretset - 1] = BoolGetDatum(returnsSet); values[Anum_pg_proc_provolatile - 1] = CharGetDatum(volatility); values[Anum_pg_proc_proparallel - 1] = CharGetDatum(parallel); values[Anum_pg_proc_pronargs - 1] = UInt16GetDatum(parameterCount); values[Anum_pg_proc_pronargdefaults - 1] = UInt16GetDatum(list_length(parameterDefaults)); values[Anum_pg_proc_prorettype - 1] = ObjectIdGetDatum(returnType); values[Anum_pg_proc_proargtypes - 1] = PointerGetDatum(parameterTypes); if (allParameterTypes != PointerGetDatum(NULL)) values[Anum_pg_proc_proallargtypes - 1] = allParameterTypes; else nulls[Anum_pg_proc_proallargtypes - 1] = true; if (parameterModes != PointerGetDatum(NULL)) values[Anum_pg_proc_proargmodes - 1] = parameterModes; else nulls[Anum_pg_proc_proargmodes - 1] = true; if (parameterNames != PointerGetDatum(NULL)) values[Anum_pg_proc_proargnames - 1] = parameterNames; else nulls[Anum_pg_proc_proargnames - 1] = true; if (parameterDefaults != NIL) values[Anum_pg_proc_proargdefaults - 1] = CStringGetTextDatum(nodeToString(parameterDefaults)); else nulls[Anum_pg_proc_proargdefaults - 1] = true; if (trftypes != PointerGetDatum(NULL)) values[Anum_pg_proc_protrftypes - 1] = trftypes; else nulls[Anum_pg_proc_protrftypes - 1] = true; values[Anum_pg_proc_prosrc - 1] = CStringGetTextDatum(prosrc); if (probin) values[Anum_pg_proc_probin - 1] = CStringGetTextDatum(probin); else nulls[Anum_pg_proc_probin - 1] = true; if (proconfig != PointerGetDatum(NULL)) values[Anum_pg_proc_proconfig - 1] = proconfig; else nulls[Anum_pg_proc_proconfig - 1] = true; /* proacl will be determined later */ rel = table_open(ProcedureRelationId, RowExclusiveLock); tupDesc = RelationGetDescr(rel); /* Check for pre-existing definition */ oldtup = SearchSysCache3(PROCNAMEARGSNSP, PointerGetDatum(procedureName), PointerGetDatum(parameterTypes), ObjectIdGetDatum(procNamespace)); if (HeapTupleIsValid(oldtup)) { /* There is one; okay to replace it? */ Form_pg_proc oldproc = (Form_pg_proc) GETSTRUCT(oldtup); Datum proargnames; bool isnull; const char *dropcmd; if (!replace) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_FUNCTION), errmsg("function \"%s\" already exists with same argument types", procedureName))); if (!pg_proc_ownercheck(oldproc->oid, proowner)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION, procedureName); /* Not okay to change routine kind */ if (oldproc->prokind != prokind) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot change routine kind"), (oldproc->prokind == PROKIND_AGGREGATE ? errdetail("\"%s\" is an aggregate function.", procedureName) : oldproc->prokind == PROKIND_FUNCTION ? errdetail("\"%s\" is a function.", procedureName) : oldproc->prokind == PROKIND_PROCEDURE ? errdetail("\"%s\" is a procedure.", procedureName) : oldproc->prokind == PROKIND_WINDOW ? errdetail("\"%s\" is a window function.", procedureName) : 0))); dropcmd = (prokind == PROKIND_PROCEDURE ? "DROP PROCEDURE" : "DROP FUNCTION"); /* * Not okay to change the return type of the existing proc, since * existing rules, views, etc may depend on the return type. * * In case of a procedure, a changing return type means that whether * the procedure has output parameters was changed. Since there is no * user visible return type, we produce a more specific error message. */ if (returnType != oldproc->prorettype || returnsSet != oldproc->proretset) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), prokind == PROKIND_PROCEDURE ? errmsg("cannot change whether a procedure has output parameters") : errmsg("cannot change return type of existing function"), /* translator: first %s is DROP FUNCTION or DROP PROCEDURE */ errhint("Use %s %s first.", dropcmd, format_procedure(oldproc->oid)))); /* * If it returns RECORD, check for possible change of record type * implied by OUT parameters */ if (returnType == RECORDOID) { TupleDesc olddesc; TupleDesc newdesc; olddesc = build_function_result_tupdesc_t(oldtup); newdesc = build_function_result_tupdesc_d(prokind, allParameterTypes, parameterModes, parameterNames); if (olddesc == NULL && newdesc == NULL) /* ok, both are runtime-defined RECORDs */ ; else if (olddesc == NULL || newdesc == NULL || !equalTupleDescs(olddesc, newdesc)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot change return type of existing function"), errdetail("Row type defined by OUT parameters is different."), /* translator: first %s is DROP FUNCTION or DROP PROCEDURE */ errhint("Use %s %s first.", dropcmd, format_procedure(oldproc->oid)))); } /* * If there were any named input parameters, check to make sure the * names have not been changed, as this could break existing calls. We * allow adding names to formerly unnamed parameters, though. */ proargnames = SysCacheGetAttr(PROCNAMEARGSNSP, oldtup, Anum_pg_proc_proargnames, &isnull); if (!isnull) { Datum proargmodes; char **old_arg_names; char **new_arg_names; int n_old_arg_names; int n_new_arg_names; int j; proargmodes = SysCacheGetAttr(PROCNAMEARGSNSP, oldtup, Anum_pg_proc_proargmodes, &isnull); if (isnull) proargmodes = PointerGetDatum(NULL); /* just to be sure */ n_old_arg_names = get_func_input_arg_names(proargnames, proargmodes, &old_arg_names); n_new_arg_names = get_func_input_arg_names(parameterNames, parameterModes, &new_arg_names); for (j = 0; j < n_old_arg_names; j++) { if (old_arg_names[j] == NULL) continue; if (j >= n_new_arg_names || new_arg_names[j] == NULL || strcmp(old_arg_names[j], new_arg_names[j]) != 0) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot change name of input parameter \"%s\"", old_arg_names[j]), /* translator: first %s is DROP FUNCTION or DROP PROCEDURE */ errhint("Use %s %s first.", dropcmd, format_procedure(oldproc->oid)))); } } /* * If there are existing defaults, check compatibility: redefinition * must not remove any defaults nor change their types. (Removing a * default might cause a function to fail to satisfy an existing call. * Changing type would only be possible if the associated parameter is * polymorphic, and in such cases a change of default type might alter * the resolved output type of existing calls.) */ if (oldproc->pronargdefaults != 0) { Datum proargdefaults; List *oldDefaults; ListCell *oldlc; ListCell *newlc; if (list_length(parameterDefaults) < oldproc->pronargdefaults) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot remove parameter defaults from existing function"), /* translator: first %s is DROP FUNCTION or DROP PROCEDURE */ errhint("Use %s %s first.", dropcmd, format_procedure(oldproc->oid)))); proargdefaults = SysCacheGetAttr(PROCNAMEARGSNSP, oldtup, Anum_pg_proc_proargdefaults, &isnull); Assert(!isnull); oldDefaults = castNode(List, stringToNode(TextDatumGetCString(proargdefaults))); Assert(list_length(oldDefaults) == oldproc->pronargdefaults); /* new list can have more defaults than old, advance over 'em */ newlc = list_head(parameterDefaults); for (i = list_length(parameterDefaults) - oldproc->pronargdefaults; i > 0; i--) newlc = lnext(newlc); foreach(oldlc, oldDefaults) { Node *oldDef = (Node *) lfirst(oldlc); Node *newDef = (Node *) lfirst(newlc); if (exprType(oldDef) != exprType(newDef)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot change data type of existing parameter default value"), /* translator: first %s is DROP FUNCTION or DROP PROCEDURE */ errhint("Use %s %s first.", dropcmd, format_procedure(oldproc->oid)))); newlc = lnext(newlc); } }
/* * Transform the subscript expressions. */ foreach(idx, indirection) { A_Indices *ai = castNode(A_Indices, lfirst(idx)); Node *subexpr; if (isSlice) { if (ai->lidx) { subexpr = transformExpr(pstate, ai->lidx, pstate->p_expr_kind); /* If it's not int4 already, try to coerce */ subexpr = coerce_to_target_type(pstate, subexpr, exprType(subexpr), INT4OID, -1, COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST, -1); if (subexpr == NULL) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("array subscript must have type integer"), parser_errposition(pstate, exprLocation(ai->lidx)))); } else if (!ai->is_slice) { /* Make a constant 1 */ subexpr = (Node *) makeConst(INT4OID, -1, InvalidOid, sizeof(int32), Int32GetDatum(1), false, true); /* pass by value */ } else { /* Slice with omitted lower bound, put NULL into the list */ subexpr = NULL; } lowerIndexpr = lappend(lowerIndexpr, subexpr); } else Assert(ai->lidx == NULL && !ai->is_slice); if (ai->uidx) { subexpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind); /* If it's not int4 already, try to coerce */ subexpr = coerce_to_target_type(pstate, subexpr, exprType(subexpr), INT4OID, -1, COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST, -1); if (subexpr == NULL) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("array subscript must have type integer"), parser_errposition(pstate, exprLocation(ai->uidx)))); } else { /* Slice with omitted upper bound, put NULL into the list */ Assert(isSlice && ai->is_slice); subexpr = NULL; } upperIndexpr = lappend(upperIndexpr, subexpr); }