=== Applying patches on top of PostgreSQL commit ID fcc0e27f45e2d8dc6046435908a48626c91d3d84 === /etc/rc.d/jail: WARNING: Per-jail configuration via jail_* variables is obsolete. Please consider migrating to /etc/jail.conf. Wed Sep 16 14:52:29 UTC 2026 On branch cf/7239 nothing to commit, working tree clean === using 'git am' to apply patch ./v1-0001-Add-tg_temporal-to-TriggerData.patch === Applying: Add tg_temporal to TriggerData Using index info to reconstruct a base tree... M doc/src/sgml/trigger.sgml M src/backend/commands/trigger.c M src/backend/executor/nodeModifyTable.c M src/backend/parser/analyze.c M src/include/nodes/execnodes.h M src/include/nodes/primnodes.h Falling back to patching base and 3-way merge... Auto-merging src/include/nodes/primnodes.h CONFLICT (content): Merge conflict in src/include/nodes/primnodes.h Auto-merging src/include/nodes/execnodes.h CONFLICT (content): Merge conflict in src/include/nodes/execnodes.h Auto-merging src/backend/parser/analyze.c CONFLICT (content): Merge conflict in src/backend/parser/analyze.c Auto-merging src/backend/executor/nodeModifyTable.c CONFLICT (content): Merge conflict in src/backend/executor/nodeModifyTable.c Auto-merging src/backend/commands/trigger.c Auto-merging doc/src/sgml/trigger.sgml error: Failed to merge in the changes. hint: Use 'git am --show-current-patch=diff' to see the failed patch Patch failed at 0001 Add tg_temporal to TriggerData When you have resolved this problem, run "git am --continue". If you prefer to skip this patch, run "git am --skip" instead. To restore the original branch and stop patching, run "git am --abort". === using patch(1) to apply patch ./v1-0001-Add-tg_temporal-to-TriggerData.patch === patching file doc/src/sgml/trigger.sgml Hunk #1 succeeded at 554 (offset -12 lines). Hunk #2 succeeded at 833 (offset -12 lines). patching file src/backend/commands/trigger.c Hunk #2 succeeded at 2462 (offset 2 lines). Hunk #3 succeeded at 2519 (offset 2 lines). Hunk #4 succeeded at 2624 (offset 2 lines). Hunk #5 succeeded at 2694 (offset 2 lines). Hunk #6 succeeded at 2803 (offset 2 lines). Hunk #7 succeeded at 2905 (offset 2 lines). Hunk #8 succeeded at 2969 (offset 2 lines). Hunk #9 succeeded at 3113 (offset 2 lines). Hunk #10 succeeded at 3276 (offset 2 lines). Hunk #11 succeeded at 3748 (offset 2 lines). Hunk #12 succeeded at 4141 (offset 2 lines). Hunk #13 succeeded at 4320 (offset 2 lines). Hunk #14 succeeded at 4331 (offset 2 lines). Hunk #15 succeeded at 4689 (offset 2 lines). Hunk #16 succeeded at 6788 (offset 2 lines). patching file src/backend/executor/nodeModifyTable.c Hunk #1 FAILED at 5652. Hunk #2 FAILED at 5938. 2 out of 2 hunks FAILED -- saving rejects to file src/backend/executor/nodeModifyTable.c.rej patching file src/backend/parser/analyze.c Hunk #1 FAILED at 1608. 1 out of 1 hunk FAILED -- saving rejects to file src/backend/parser/analyze.c.rej patching file src/include/commands/trigger.h patching file src/include/nodes/execnodes.h Hunk #1 FAILED at 483. 1 out of 1 hunk FAILED -- saving rejects to file src/include/nodes/execnodes.h.rej patching file src/include/nodes/primnodes.h Hunk #1 FAILED at 2453. 1 out of 1 hunk FAILED -- saving rejects to file src/include/nodes/primnodes.h.rej Unstaged changes after reset: M doc/src/sgml/trigger.sgml M src/backend/commands/trigger.c M src/include/commands/trigger.h Removing src/backend/executor/nodeModifyTable.c.rej Removing src/backend/parser/analyze.c.rej Removing src/include/nodes/execnodes.h.rej Removing src/include/nodes/primnodes.h.rej === using 'git apply' to apply patch ./v1-0001-Add-tg_temporal-to-TriggerData.patch === Applied patch to 'doc/src/sgml/trigger.sgml' cleanly. Applied patch to 'src/backend/commands/trigger.c' cleanly. Applied patch to 'src/backend/executor/nodeModifyTable.c' with conflicts. Applied patch to 'src/backend/parser/analyze.c' with conflicts. Applied patch to 'src/include/commands/trigger.h' cleanly. Applied patch to 'src/include/nodes/execnodes.h' with conflicts. Applied patch to 'src/include/nodes/primnodes.h' with conflicts. U src/backend/executor/nodeModifyTable.c U src/backend/parser/analyze.c U src/include/nodes/execnodes.h U src/include/nodes/primnodes.h diff --cc src/backend/executor/nodeModifyTable.c index 2632431ea05,b272c5a9733..00000000000 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@@ -5343,6 -5611,109 +5343,112 @@@ ExecInitModifyTable(ModifyTable *node, } /* ++<<<<<<< ours ++======= + * If needed, initialize the target range for FOR PORTION OF. + */ + if (node->forPortionOf) + { + ResultRelInfo *rootRelInfo; + TupleDesc tupDesc; + ForPortionOfExpr *forPortionOf; + Datum targetRange; + bool isNull; + ExprContext *econtext; + ExprState *exprState; + ForPortionOfState *fpoState; + + rootRelInfo = mtstate->resultRelInfo; + if (rootRelInfo->ri_RootResultRelInfo) + rootRelInfo = rootRelInfo->ri_RootResultRelInfo; + + tupDesc = rootRelInfo->ri_RelationDesc->rd_att; + forPortionOf = (ForPortionOfExpr *) node->forPortionOf; + + /* Eval the FOR PORTION OF target */ + if (mtstate->ps.ps_ExprContext == NULL) + ExecAssignExprContext(estate, &mtstate->ps); + econtext = mtstate->ps.ps_ExprContext; + + exprState = ExecPrepareExpr((Expr *) forPortionOf->targetRange, estate); + targetRange = ExecEvalExpr(exprState, econtext, &isNull); + + /* + * FOR PORTION OF ... TO ... FROM should never give us a NULL target, + * but FOR PORTION OF (...) could. + */ + if (isNull) + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + errmsg("FOR PORTION OF target must not be null"), + executor_errposition(estate, forPortionOf->targetLocation))); + + /* Create state for FOR PORTION OF operation */ + + fpoState = makeNode(ForPortionOfState); + fpoState->fp_rangeName = pstrdup(forPortionOf->range_name); + fpoState->fp_rangeType = forPortionOf->rangeType; + fpoState->fp_rangeAttno = forPortionOf->rangeVar->varattno; + fpoState->fp_targetRange = targetRange; + + /* Initialize slot for the existing tuple */ + + fpoState->fp_Existing = + table_slot_create(rootRelInfo->ri_RelationDesc, + &mtstate->ps.state->es_tupleTable); + + /* Create the tuple slot for INSERTing the temporal leftovers */ + + fpoState->fp_Leftover = + ExecInitExtraTupleSlot(mtstate->ps.state, tupDesc, &TTSOpsVirtual); + + rootRelInfo->ri_forPortionOf = fpoState; + + /* + * Make sure the root relation has the FOR PORTION OF clause too. Each + * partition needs its own TupleTableSlot, since they can have + * different descriptors, so they'll use the root fpoState to + * initialize one if necessary. + */ + if (node->rootRelation > 0) + mtstate->rootResultRelInfo->ri_forPortionOf = fpoState; + + if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && + mtstate->mt_partition_tuple_routing == NULL) + { + /* + * We will need tuple routing to insert temporal leftovers. Since + * we are initializing things before ExecCrossPartitionUpdate + * runs, we must do everything it needs as well. + */ + Relation rootRel = mtstate->rootResultRelInfo->ri_RelationDesc; + MemoryContext oldcxt; + + /* Things built here have to last for the query duration. */ + oldcxt = MemoryContextSwitchTo(estate->es_query_cxt); + + mtstate->mt_partition_tuple_routing = + ExecSetupPartitionTupleRouting(estate, rootRel); + + /* + * Before a partition's tuple can be re-routed, it must first be + * converted to the root's format, so we'll need a slot for + * storing such tuples. + */ + Assert(mtstate->mt_root_tuple_slot == NULL); + mtstate->mt_root_tuple_slot = table_slot_create(rootRel, NULL); + + MemoryContextSwitchTo(oldcxt); + } + + /* + * Don't free the ExprContext here because the result must last for + * the whole query. + */ + } + + /* ++>>>>>>> theirs * If we have any secondary relations in an UPDATE or DELETE, they need to * be treated like non-locked relations in SELECT FOR UPDATE, i.e., the * EvalPlanQual mechanism needs to be told about them. This also goes for @@@ -5535,3 -5906,76 +5641,79 @@@ ExecReScanModifyTable(ModifyTableState */ elog(ERROR, "ExecReScanModifyTable is not implemented"); } ++<<<<<<< ours ++======= + + /* ---------------------------------------------------------------- + * ExecInitForPortionOf + * + * Initializes resultRelInfo->ri_forPortionOf for child tables. + * + * Partitions share the root leftover slot, since they must insert via + * the root relation to get tuple routing. Plain inheritance children + * must keep their own leftover slot and insert back into the child, or + * else child-only column values and physical placement would be lost. + * ---------------------------------------------------------------- + */ + static void + ExecInitForPortionOf(ModifyTableState *mtstate, EState *estate, + ResultRelInfo *resultRelInfo) + { + MemoryContext oldcxt; + ForPortionOfState *leafState; + ResultRelInfo *rootRelInfo = mtstate->rootResultRelInfo; + ForPortionOfState *fpoState; + TupleConversionMap *map; + + if (!rootRelInfo) + elog(ERROR, "no root relation but ri_forPortionOf is uninitialized"); + + fpoState = rootRelInfo->ri_forPortionOf; + Assert(fpoState); + + /* Things built here have to last for the query duration. */ + oldcxt = MemoryContextSwitchTo(estate->es_query_cxt); + + leafState = makeNode(ForPortionOfState); + + leafState->fp_rangeName = pstrdup(fpoState->fp_rangeName); + leafState->fp_rangeType = fpoState->fp_rangeType; + leafState->fp_targetRange = fpoState->fp_targetRange; + map = ExecGetChildToRootMap(resultRelInfo); + + /* + * fp_rangeAttno must match the tuple layout used for reading the old + * range value. The query uses the target relation's attno, so translate + * it to the child attno when the child has a different column layout. + */ + if (map) + leafState->fp_rangeAttno = map->attrMap->attnums[fpoState->fp_rangeAttno - 1]; + else + leafState->fp_rangeAttno = fpoState->fp_rangeAttno; + + /* + * For partitioned tables we must read the leftovers using the child + * table's tuple descriptor, but then insert them into the root table + * (using its tuple descriptor) so we get tuple routing. + * + * For traditional table inheritance, we read and insert directly into + * this resultRelInfo; no tuple routing via the parent is required. + */ + if (rootRelInfo->ri_RelationDesc->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + leafState->fp_Leftover = fpoState->fp_Leftover; + else + leafState->fp_Leftover = + ExecInitExtraTupleSlot(mtstate->ps.state, + RelationGetDescr(resultRelInfo->ri_RelationDesc), + &TTSOpsVirtual); + + /* Each child relation needs a slot matching its tuple descriptor. */ + leafState->fp_Existing = + table_slot_create(resultRelInfo->ri_RelationDesc, + &mtstate->ps.state->es_tupleTable); + + resultRelInfo->ri_forPortionOf = leafState; + + MemoryContextSwitchTo(oldcxt); + } ++>>>>>>> theirs diff --cc src/backend/parser/analyze.c index 08f99dff711,0be92fb7013..00000000000 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@@ -1290,6 -1305,313 +1290,316 @@@ transformOnConflictClause(ParseState *p return result; } ++<<<<<<< ours ++======= + /* + * transformForPortionOfClause + * + * Transforms a ForPortionOfClause in an UPDATE/DELETE statement. + * + * - Look up the range/period requested. + * - Build a compatible range value from the FROM and TO expressions. + * - Build an "overlaps" expression for filtering, used later by the + * rewriter. + * - For UPDATEs, build an "intersects" expression the rewriter can add + * to the targetList to change the temporal bounds. + */ + static ForPortionOfExpr * + transformForPortionOfClause(ParseState *pstate, + int rtindex, + const ForPortionOfClause *forPortionOf, + const Node *whereClause, + bool isUpdate) + { + Relation targetrel = pstate->p_target_relation; + int range_attno = InvalidAttrNumber; + Form_pg_attribute attr; + Oid attbasetype; + Oid opclass; + Oid opfamily; + Oid opcintype; + Oid funcid = InvalidOid; + StrategyNumber strat; + Oid opid; + OpExpr *op; + ForPortionOfExpr *result; + Var *rangeVar; + + /* disallow FOR PORTION OF ... WHERE CURRENT OF */ + if (whereClause && IsA(whereClause, CurrentOfExpr)) + ereport(ERROR, + errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("WHERE CURRENT OF with FOR PORTION OF is not implemented")); + + result = makeNode(ForPortionOfExpr); + + /* Look up the FOR PORTION OF name requested. */ + range_attno = attnameAttNum(targetrel, forPortionOf->range_name, false); + if (range_attno == InvalidAttrNumber) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column \"%s\" of relation \"%s\" does not exist", + forPortionOf->range_name, + RelationGetRelationName(targetrel)), + parser_errposition(pstate, forPortionOf->location))); + attr = TupleDescAttr(targetrel->rd_att, range_attno - 1); + + attbasetype = getBaseType(attr->atttypid); + + rangeVar = makeVar(rtindex, + range_attno, + attr->atttypid, + attr->atttypmod, + attr->attcollation, + 0); + rangeVar->location = forPortionOf->location; + result->rangeVar = rangeVar; + + /* Require SELECT privilege on the application-time column. */ + markVarForSelectPriv(pstate, rangeVar); + + /* + * Use the basetype for the target, which shouldn't be required to follow + * domain rules. The table's column type is in the Var if we need it. + */ + result->rangeType = attbasetype; + result->isDomain = attbasetype != attr->atttypid; + + if (forPortionOf->target) + { + Oid declared_target_type = attbasetype; + Oid actual_target_type; + + /* + * We were already given an expression for the target, so we don't + * have to build anything. We still have to make sure we got the right + * type. NULL will be caught be the executor. + */ + + result->targetRange = transformExpr(pstate, + forPortionOf->target, + EXPR_KIND_FOR_PORTION); + + actual_target_type = exprType(result->targetRange); + + if (!can_coerce_type(1, &actual_target_type, &declared_target_type, COERCION_IMPLICIT)) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("could not coerce FOR PORTION OF target from %s to %s", + format_type_be(actual_target_type), + format_type_be(declared_target_type)), + parser_errposition(pstate, exprLocation(forPortionOf->target)))); + + result->targetRange = coerce_type(pstate, + result->targetRange, + actual_target_type, + declared_target_type, + -1, + COERCION_IMPLICIT, + COERCE_IMPLICIT_CAST, + exprLocation(forPortionOf->target)); + + /* + * XXX: For now we only support ranges and multiranges, so we fail on + * anything else. + */ + if (!type_is_range(attbasetype) && !type_is_multirange(attbasetype)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), + errmsg("column \"%s\" of relation \"%s\" is not a range or multirange type", + forPortionOf->range_name, + RelationGetRelationName(targetrel)), + parser_errposition(pstate, forPortionOf->location))); + + } + else + { + Oid rngsubtype; + Oid declared_arg_types[2]; + Oid actual_arg_types[2]; + List *args; + + /* + * Make sure it's a range column. XXX: We could support this syntax on + * multirange columns too, if we just built a one-range multirange + * from the FROM/TO phrases. + */ + if (!type_is_range(attbasetype)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), + errmsg("column \"%s\" of relation \"%s\" is not a range type", + forPortionOf->range_name, + RelationGetRelationName(targetrel)), + parser_errposition(pstate, forPortionOf->location))); + + rngsubtype = get_range_subtype(attbasetype); + declared_arg_types[0] = rngsubtype; + declared_arg_types[1] = rngsubtype; + + /* + * Build a range from the FROM ... TO ... bounds. This should give a + * constant result, so we accept functions like NOW() but not column + * references, subqueries, etc. + */ + result->targetFrom = transformExpr(pstate, + forPortionOf->target_start, + EXPR_KIND_FOR_PORTION); + result->targetTo = transformExpr(pstate, + forPortionOf->target_end, + EXPR_KIND_FOR_PORTION); + actual_arg_types[0] = exprType(result->targetFrom); + actual_arg_types[1] = exprType(result->targetTo); + args = list_make2(copyObject(result->targetFrom), + copyObject(result->targetTo)); + + /* + * Check the bound types separately, for better error message and + * location + */ + if (!can_coerce_type(1, actual_arg_types, declared_arg_types, COERCION_IMPLICIT)) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("could not coerce FOR PORTION OF %s bound from %s to %s", + "FROM", + format_type_be(actual_arg_types[0]), + format_type_be(declared_arg_types[0])), + parser_errposition(pstate, exprLocation(forPortionOf->target_start)))); + if (!can_coerce_type(1, &actual_arg_types[1], &declared_arg_types[1], COERCION_IMPLICIT)) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("could not coerce FOR PORTION OF %s bound from %s to %s", + "TO", + format_type_be(actual_arg_types[1]), + format_type_be(declared_arg_types[1])), + parser_errposition(pstate, exprLocation(forPortionOf->target_end)))); + + make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types); + result->targetRange = (Node *) makeFuncExpr(get_range_constructor2(attbasetype), + attbasetype, + args, + InvalidOid, InvalidOid, COERCE_EXPLICIT_CALL); + } + + /* + * Build overlapsExpr to use as an extra qual. This means we only hit rows + * matching the FROM & TO bounds. We must look up the overlaps operator + * (usually "&&"). + */ + opclass = GetDefaultOpClass(attr->atttypid, GIST_AM_OID); + if (!OidIsValid(opclass)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("data type %s has no default operator class for access method \"%s\"", + format_type_be(attr->atttypid), "gist"), + errhint("You must define a default operator class for the data type."))); + + /* Look up the operators and functions we need. */ + GetOperatorFromCompareType(opclass, InvalidOid, COMPARE_OVERLAP, &opid, &strat); + op = makeNode(OpExpr); + op->opno = opid; + op->opfuncid = get_opcode(opid); + op->opresulttype = BOOLOID; + op->args = list_make2(copyObject(rangeVar), copyObject(result->targetRange)); + result->overlapsExpr = (Node *) op; + + /* + * Look up the without_portion func. This computes the bounds of temporal + * leftovers. + * + * XXX: Find a more extensible way to look up the function, permitting + * user-defined types. An opclass support function doesn't make sense, + * since there is no index involved. Perhaps a type support function. + */ + if (get_opclass_opfamily_and_input_type(opclass, &opfamily, &opcintype)) + switch (opcintype) + { + case ANYRANGEOID: + result->withoutPortionProc = F_RANGE_MINUS_MULTI; + break; + case ANYMULTIRANGEOID: + result->withoutPortionProc = F_MULTIRANGE_MINUS_MULTI; + break; + default: + elog(ERROR, "unexpected opcintype: %u", opcintype); + } + else + elog(ERROR, "unexpected opclass: %u", opclass); + + if (isUpdate) + { + /* + * Now make sure we update the start/end time of the record. For a + * range col (r) this is `r = r * targetRange` (where * is the + * intersect operator). + */ + Oid intersectoperoid; + List *funcArgs; + Node *rangeTLEExpr; + TargetEntry *tle; + RTEPermissionInfo *target_perminfo = pstate->p_target_nsitem->p_perminfo; + + /* + * Whatever operator is used for intersect by temporal foreign keys, + * we can use its backing procedure for intersects in FOR PORTION OF. + * XXX: Share code with FindFKPeriodOpers? + */ + switch (opcintype) + { + case ANYRANGEOID: + intersectoperoid = OID_RANGE_INTERSECT_RANGE_OP; + break; + case ANYMULTIRANGEOID: + intersectoperoid = OID_MULTIRANGE_INTERSECT_MULTIRANGE_OP; + break; + default: + elog(ERROR, "unexpected opcintype: %u", opcintype); + } + funcid = get_opcode(intersectoperoid); + if (!OidIsValid(funcid)) + ereport(ERROR, + errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("could not identify an intersect function for type %s", + format_type_be(opcintype))); + + funcArgs = list_make2(copyObject(rangeVar), + copyObject(result->targetRange)); + rangeTLEExpr = (Node *) makeFuncExpr(funcid, attbasetype, funcArgs, + InvalidOid, InvalidOid, + COERCE_EXPLICIT_CALL); + + /* + * Coerce to domain if necessary. If we skip this, we will allow + * updating to forbidden values. + */ + rangeTLEExpr = coerce_type(pstate, + rangeTLEExpr, + attbasetype, + attr->atttypid, + -1, + COERCION_IMPLICIT, + COERCE_IMPLICIT_CAST, + exprLocation(forPortionOf->target)); + + /* Make a TLE to set the range column */ + result->rangeTargetList = NIL; + tle = makeTargetEntry((Expr *) rangeTLEExpr, range_attno, + forPortionOf->range_name, false); + result->rangeTargetList = lappend(result->rangeTargetList, tle); + + /* Mark the range column as requiring update permissions */ + target_perminfo->updatedCols = bms_add_member(target_perminfo->updatedCols, + range_attno - FirstLowInvalidHeapAttributeNumber); + } + else + result->rangeTargetList = NIL; + + result->location = forPortionOf->location; + result->targetLocation = forPortionOf->target_location; + result->range_name = forPortionOf->range_name; + + return result; + } ++>>>>>>> theirs /* * BuildOnConflictExcludedTargetlist diff --cc src/include/nodes/execnodes.h index 91bb0bd2e13,524f9beef50..00000000000 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@@ -467,6 -468,25 +467,28 @@@ typedef struct MergeActionStat } MergeActionState; /* ++<<<<<<< ours ++======= + * ForPortionOfState + * + * Executor state of a FOR PORTION OF operation. + */ + typedef struct ForPortionOfState + { + NodeTag type; + + Oid fp_rangeType; /* the base type (not domain) of the FOR + * PORTION OF expression */ + int fp_rangeAttno; /* the attno of the range column */ + Datum fp_targetRange; /* the range/multirange from FOR PORTION OF */ + TypeCacheEntry *fp_leftoverstypcache; /* type cache entry of the range */ + TupleTableSlot *fp_Existing; /* slot to store old tuple */ + TupleTableSlot *fp_Leftover; /* slot to store leftover */ + char *fp_rangeName; /* the column named in FOR PORTION OF */ + } ForPortionOfState; + + /* ++>>>>>>> theirs * ResultRelInfo * * Whenever we update an existing relation, we have to update indexes on the diff --cc src/include/nodes/primnodes.h index 2a832a27f49,e4d6e78e621..00000000000 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@@ -2397,4 -2421,39 +2397,42 @@@ typedef struct OnConflictExp List *exclRelTlist; /* tlist of the EXCLUDED pseudo relation */ } OnConflictExpr; ++<<<<<<< ours ++======= + /*---------- + * ForPortionOfExpr - represents a FOR PORTION OF ... expression + * + * We set up an expression to make a range from the FROM/TO bounds, + * so that we can use range operators with it. + * + * Then we set up an overlaps expression between that and the range column, + * so that we can find the rows we need to update/delete. + * + * If the user used the FROM ... TO ... syntax, we save the individual + * expressions so that we can deparse them. + * + * In the executor we'll also build an intersect expression between the + * targeted range and the range column, so that we can update the start/end + * bounds of the UPDATE'd record. + *---------- + */ + typedef struct ForPortionOfExpr + { + NodeTag type; + Var *rangeVar; /* Range column */ + Node *targetFrom; /* FOR PORTION OF FROM bound, if given */ + Node *targetTo; /* FOR PORTION OF TO bound, if given */ + Node *targetRange; /* FOR PORTION OF bounds as a range/multirange */ + Oid rangeType; /* (base)type of targetRange */ + bool isDomain; /* Is rangeVar a domain? */ + Node *overlapsExpr; /* range && targetRange */ + List *rangeTargetList; /* List of TargetEntrys to set the time + * column(s) */ + Oid withoutPortionProc; /* SRF proc for old_range - target_range */ + ParseLoc location; /* token location, or -1 if unknown */ + ParseLoc targetLocation; /* token location, or -1 if unknown */ + char *range_name; /* Range name */ + } ForPortionOfExpr; + ++>>>>>>> theirs #endif /* PRIMNODES_H */