/* * Evaluate the limit/offset expressions --- done at startup or rescan. * * This is also a handy place to reset the current-position state info. */ static void recompute_limits(limit_ps *node) { expr_ctx_n *econtext; datum_t val; bool isNull; econtext = node->ps.ps_ExprContext; if (node->limitOffset) { val = exec_eval_expr_switch_ctx(node->limitOffset, econtext, &isNull, NULL); /* Interpret NULL offset as no offset */ if (isNull) node->offset = 0; else { node->offset = D_TO_INT64(val); if (node->offset < 0) ereport(ERROR, ( errcode(E_INVALID_ROW_COUNT_IN_RESULT_OFFSET_CLAUSE), errmsg("OFFSET must not be negative"))); } } else { /* No OFFSET supplied */ node->offset = 0; } if (node->limitCount) { val = exec_eval_expr_switch_ctx(node->limitCount, econtext, &isNull, NULL); /* Interpret NULL count as no count (LIMIT ALL) */ if (isNull) { node->count = 0; node->noCount = true; } else { node->count = D_TO_INT64(val); if (node->count < 0) ereport(ERROR, ( errcode(E_INVALID_ROW_COUNT_IN_LIMIT_CLAUSE), errmsg("LIMIT must not be negative"))); node->noCount = false; } } else { /* No COUNT supplied */ node->count = 0; node->noCount = true; } /* Reset position to start-of-scan */ node->position = 0; node->subSlot = NULL; /* Set state-machine state */ node->lstate = LIMIT_RESCAN; /* Notify child node about limit, if useful */ pass_down_bound(node, OUTER_PLAN_STATE(node)); }
/* * If we have a COUNT, and our input is a sort_pl node, notify it that it can * use bounded sort. Also, if our input is a merge_append_pl, we can apply the * same bound to any Sorts that are direct children of the merge_append_pl, * since the merge_append_pl surely need read no more than that many tuples from * any one input. We also have to be prepared to look through a result_pl, * since the planner might stick one atop merge_append_pl for projection purposes. * * This is a bit of a kluge, but we don't have any more-abstract way of * communicating between the two nodes; and it doesn't seem worth trying * to invent one without some more examples of special communication needs. * * Note: it is the responsibility of nodeSort.c to react properly to * changes of these parameters. If we ever do redesign this, it'd be a * good idea to integrate this signaling with the parameter-change mechanism. */ static void pass_down_bound(limit_ps *node, plan_state_n *child_node) { if (IS_A(child_node, SortState)) { sort_ss *sortState; int64 tuples_needed; sortState = (sort_ss *) child_node; tuples_needed = node->count + node->offset; /* negative test checks for overflow in sum */ if (node->noCount || tuples_needed < 0) { /* make sure flag gets reset if needed upon rescan */ sortState->bounded = false; } else { sortState->bounded = true; sortState->bound = tuples_needed; } } else if (IS_A(child_node, MergeAppendState)) { merge_append_ps *maState; int i; maState = (merge_append_ps *) child_node; for (i = 0; i < maState->ms_nplans; i++) pass_down_bound(node, maState->mergeplans[i]); } else if (IS_A(child_node, ResultState)) { /* * An extra consideration here is that if the result_pl is projecting a * targetlist that contains any SRFs, we can't assume that every input * tuple generates an output tuple, so a sort_pl underneath might need to * return more than N tuples to satisfy LIMIT N. So we cannot use * bounded sort. * * If result_pl supported qual checking, we'd have to punt on seeing a * qual, too. Note that having a resconstantqual is not a * showstopper: if that fails we're not getting any rows at all. */ if (OUTER_PLAN_STATE(child_node) && !expr_returns_set((node_n *) child_node->plan->targetlist)) pass_down_bound(node, OUTER_PLAN_STATE(child_node)); } }
/* * If we have a COUNT, and our input is a Sort node, notify it that it can * use bounded sort. Also, if our input is a MergeAppend, we can apply the * same bound to any Sorts that are direct children of the MergeAppend, * since the MergeAppend surely need read no more than that many tuples from * any one input. We also have to be prepared to look through a Result, * since the planner might stick one atop MergeAppend for projection purposes. * * This is a bit of a kluge, but we don't have any more-abstract way of * communicating between the two nodes; and it doesn't seem worth trying * to invent one without some more examples of special communication needs. * * Note: it is the responsibility of nodeSort.c to react properly to * changes of these parameters. If we ever do redesign this, it'd be a * good idea to integrate this signaling with the parameter-change mechanism. */ static void pass_down_bound(LimitState *node, PlanState *child_node) { if (IsA(child_node, SortState)) { SortState *sortState = (SortState *) child_node; int64 tuples_needed = node->count + node->offset; /* negative test checks for overflow in sum */ if (node->noCount || tuples_needed < 0) { /* make sure flag gets reset if needed upon rescan */ sortState->bounded = false; } else { sortState->bounded = true; sortState->bound = tuples_needed; } } else if (IsA(child_node, MergeAppendState)) { MergeAppendState *maState = (MergeAppendState *) child_node; int i; for (i = 0; i < maState->ms_nplans; i++) pass_down_bound(node, maState->mergeplans[i]); } else if (IsA(child_node, ResultState)) { /* * If Result supported qual checking, we'd have to punt on seeing a * qual. Note that having a resconstantqual is not a showstopper: if * that fails we're not getting any rows at all. */ if (outerPlanState(child_node)) pass_down_bound(node, outerPlanState(child_node)); } }
/* * Evaluate the limit/offset expressions --- done at startup or rescan. * * This is also a handy place to reset the current-position state info. */ static void recompute_limits(LimitState *node) { ExprContext *econtext = node->ps.ps_ExprContext; Datum val; bool isNull; if (node->limitOffset) { val = ExecEvalExprSwitchContext(node->limitOffset, econtext, &isNull); /* Interpret NULL offset as no offset */ if (isNull) node->offset = 0; else { node->offset = DatumGetInt64(val); if (node->offset < 0) ereport(ERROR, (errcode(ERRCODE_INVALID_ROW_COUNT_IN_RESULT_OFFSET_CLAUSE), errmsg("OFFSET must not be negative"))); } } else { /* No OFFSET supplied */ node->offset = 0; } if (node->limitCount) { val = ExecEvalExprSwitchContext(node->limitCount, econtext, &isNull); /* Interpret NULL count as no count (LIMIT ALL) */ if (isNull) { node->count = 0; node->noCount = true; } else { node->count = DatumGetInt64(val); if (node->count < 0) ereport(ERROR, (errcode(ERRCODE_INVALID_ROW_COUNT_IN_LIMIT_CLAUSE), errmsg("LIMIT must not be negative"))); node->noCount = false; } } else { /* No COUNT supplied */ node->count = 0; node->noCount = true; } /* Reset position to start-of-scan */ node->position = 0; node->subSlot = NULL; /* Set state-machine state */ node->lstate = LIMIT_RESCAN; /* Notify child node about limit, if useful */ pass_down_bound(node, outerPlanState(node)); }