Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to use UPDATE update statement in Postgres

2025-04-01 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/01 Report--

This article mainly introduces how to use the UPDATE update sentence in Postgres. It is very detailed and has a certain reference value. Friends who are interested must finish it!

Analysis of UPDATE Source Code in PG

This article mainly describes the source code analysis of UPDATE statements in SQL, the code is the PG13.3 version.

Overall process analysis

Use update dtea set id = 1; this simplest Update statement for source code analysis (dtea is not a partitioned table, does not consider parallelism, etc., and does not build any indexes) to help us understand the general flow of update.

The SQL process is as follows:

Parser (parsing, generating parsing tree UpdateStmt, checking for syntactic errors)

Analyze (semantic analysis, UpdateStmt is converted to query tree Query, and system tables are checked to see if there are semantic errors)

Rewrite (rule rewriting, rewriting query tree Query according to rule rules, rewriting according to the rules stored in the system table beforehand, no rewriting if not, plus, the implementation of the view is implemented according to the rule system, which also needs to be processed here)

Optimizer (optimizer: logical optimization, physical optimization, generation of execution plan, Query generates corresponding execution plan PlannedStmt, cost-based optimizer, best path Path generates best execution plan Plan)

Executor (actuators, there will be various operators, processed according to the execution plan, volcanic model, one tuple at a time)

Storage (Storage engine). There are also transactions in the middle. The code for the transaction part is not analyzed here so as not to complicate the problem. The storage engine part is not analyzed, focusing on parsing, optimization, and execution.

Corresponding code:

Exec_simple_query (const char * query_string) / /-Parser section-> pg_parse_query (query_string); / / spanning syntax parsing tree-> pg_analyze_and_rewrite (parsetree, query_string,NULL, 0, NULL); / / spanning query tree Query-> parse_analyze (parsetree, query_string, paramTypes, numParams,queryEnv) / / semantic analysis-- > pg_rewrite_query (query); / / Rule rewriting / /-Optimizer-> pg_plan_queries () / /-Actuator-> PortalStart (portal, NULL, 0, InvalidSnapshot);-- > PortalRun (portal,FETCH_ALL,true,true,receiver,receiver,&qc) / / executor executes-- > PortalDrop (portal, false); parsing part-- spanning syntax parsing tree UpdateStmt

Key data structures: UpdateStmt, RangeVar, ResTarget:

/ * Update Statement * / typedef struct UpdateStmt {NodeTag type; RangeVar * relation; / * relation to update * / List * targetList; / * the target list (of ResTarget) * / / set id = 0 in the corresponding statement; the information is Node * whereClause; / * qualifications * / List * fromClause; / * optional from clause for more tables * / List * returningList; / * list of expressions to return * / WithClause * withClause; / * WITH clause * /} UpdateStmt / / dtea table typedef struct RangeVar {NodeTag type; char * catalogname; / * the catalog (database) name, or NULL * / char * schemaname; / * the schemaname, or NULL * / char * relname; / * the relation/sequence name * / bool inh; / * expand rel by inheritance? Recursively act * on children? * / char relpersistence; / * see RELPERSISTENCE_* in pg_class.h * / Alias * alias; / * table alias & optional column aliases * / int location; / * token location, or-1 if unknown * /} RangeVar;// set id = 0; after transformTargetList ()-> transformTargetEntry, it will be converted to TargetEntrytypedef struct ResTarget {NodeTag type; char * name; / * column name or NULL * / id column List * indirection / * subscripts, field names, and'*', or NIL * / Node * val; / * the value expression to compute or assign * / / = 1 expression node exists here int location; / * token location, or-1 if unknown * /} ResTarget

The update statement update dtea set id = 1 entered by the user changes from a string to an internal data structure syntax parsing tree UpdateStmt that can be understood by the database. The execution logic is in pg_parse_query (query_string);, you need to understand flex and bison.

Definition of Update syntax in gram.y:

/ * QUERY: * UpdateStmt (UPDATE) * * * * / combine this statement to analyze update dtea set id = 0 UpdateStmt: opt_with_clause UPDATE relation_expr_opt_alias SET set_clause_list from_clause where_or_current_clause returning_clause {UpdateStmt * n = makeNode (UpdateStmt); n-> relation = $3; n-> targetList = $5; n-> fromClause = $6; n-> whereClause = $7; n-> returningList = $8; n-> withClause = $1; $= (Node *) n;}; set_clause_list: set_clause {$= $1 | | set_clause_list', 'set_clause {$$= list_concat;}; / / corresponding to set id = 0set_clause: / / id = 0set_ target' = 'a_expr {$1-> val = (Node *) $3; $= list_make1 ($1);} |' ('set_target_list')'= 'a_expr {int ncolumns = list_length ($2) | Int I = 1; ListCell * col_cell; foreach (col_cell, $2) / * Create a MultiAssignRef source foreach target * / {ResTarget * res_col = (ResTarget *) lfirst (col_cell); MultiAssignRef * r = makeNode (MultiAssignRef); r-> source = (Node *) $5; r-> colno = I; r-> ncolumns = ncolumns; res_col- > val = (Node *) r } $$= $2;}; set_target: ColId opt_indirection {$$= makeNode (ResTarget); $$- > name = $1; $$- > indirection = check_indirection ($2, yyscanner); $- > val = NULL; / * upper production sets this * / $$- > location = @ 1;}; set_target_list: set_target {$$= list_make1 ($1) } | set_target_list', 'set_target {$$= lappend;}; parsing part-generate query tree Query

After the UpdateStmt is generated, the query tree Query is generated through parse_analyze semantic analysis, which is used by the subsequent optimizer to generate the execution plan. The main code is in src/backent/parser/analyze.c

Analyze.c: transform the raw parse tree into a query tree

Parse_analyze ()-- > transformTopLevelStmt (pstate, parseTree);-- > transformOptionalSelectInto (pstate, parseTree- > stmt);-- > transformStmt (pstate, parseTree); / / transforms an update statement-- > transformUpdateStmt (pstate, (UpdateStmt *) parseTree); / / the handler actually converted from UpdateStmt to Query

Specifically, let's take a look at the transformUpdateStmt function implementation:

/ * transformUpdateStmt-transforms an update statement * / static Query * transformUpdateStmt (ParseState * pstate, UpdateStmt * stmt) {Query * qry = makeNode (Query); ParseNamespaceItem * nsitem; Node * qual; qry- > commandType = CMD_UPDATE; pstate- > p_is_insert = false; / * process the WITH clause independently of all else * / if (stmt- > withClause) {qry- > hasRecursive = stmt- > withClause- > recursive; qry- > cteList = transformWithClause (pstate, stmt- > withClause); qry- > hasModifyingCTE = pstate- > pstate- } qry- > resultRelation = setTargetTable (pstate, stmt- > relation, stmt- > relation- > inh, true, ACL_UPDATE); nsitem = pstate- > pawnsitem; / * subqueries in FROM cannot access the result relation * / nsitem- > p_lateral_only = true; nsitem- > p_lateral_ok = false; / * the FROM clause is non-standard SQL syntax. We used to be able to do this with REPLACE in POSTQUEL so we keep the feature.*/ transformFromClause (pstate, stmt- > fromClause); / * remaining clauses can reference the result relation normally * / nsitem- > p_lateral_only = false; nsitem- > p_lateral_ok = true; qual = transformWhereClause (pstate, stmt- > whereClause,EXPR_KIND_WHERE, "WHERE"); qry- > returningList = transformReturningList (pstate, stmt- > returningList) / * Now we are done with SELECT-like processing, and can get on with * transforming the target list to match the UPDATE target columns.*/ qry- > targetList = transformUpdateTargetList (pstate, stmt- > targetList); / / deal with set id = 1 qry- > rtable = pstate- > proomrtable in the SQL statement; qry- > jointree = makeFromExpr (pstate- > p_joinlist, qual); qry- > hasTargetSRFs = pstate- > pairhasTargetSRFs; qry- > hasSubLinks = pstate- > pairhasSubLinks; assign_query_collations (pstate, qry); return qry;}

The focus here is on transformTargetList, which turns the ResTarget in the abstract syntax tree into the TargetEntry of the querier.

Typedef struct TargetEntry {Expr xpr; Expr * expr; / * expression to evaluate * / AttrNumber resno; / * attribute number (see notes above) * / char * resname; / * name of the column (could be NULL) * / Index ressortgroupref; / * nonzero if referenced by a sort/group clause * / Oid resorigtbl; / * OID of column's source table * / AttrNumber resorigcol; / * column's number in source table * / bool resjunk; / * set to true to eliminate the attribute from final target list * /} TargetEntry

For its internal processing, please refer to the relevant processing in the source code src/backend/parser, which will not be described in detail here. Need to focus on reading all the README in the README,PG source code is very good information, be sure to read it carefully.

Optimizer-generate execution plan

There is a lot of content in this block, and the main logic is to optimize the logic first, such as subquery, sublink, constant expression, selection push-down, and so on, because the statement we want to analyze is very simple. So this part of logic optimization is not involved. Physical optimization involves selection rate, cost estimation, index scanning or sequential scanning, choosing which connection mode, using dynamic programming or genetic algorithm, choosing nestloop-join, merge-join or hash-join, etc. Because our table is not indexed and updating a single table does not involve multi-table joins, physical optimization does not involve much. The path is generated, the best path is generated, and then the execution plan is generated by the best path.

In the path generation, the most basic is the scanning mode of the table, such as sequential scan, index scan, and then the connection mode, which is used, and then the path is generated from bottom to top, such as sorting, Limit and so on. The statement we want to analyze is very simple, there is no other processing, just scan it sequentially and update it.

Let's not consider parallel execution plans here. Let's first take a look at the results of its implementation plan:

Postgres@postgres=# explain update dtea set id = 0 QUERY PLAN-Update on dtea (cost=0.00..19.00 rows=900 width=68)-> Seq Scan on dtea (cost=0.00..19.00 rows=900 width=68) (2 rows)

Let's analyze the generation process of its execution plan:

/ / query tree Query-- > Path-- > Plan (PlannedStmt) pg_plan_queries ()-- > pg_plan_query ()-- > planner ()-- > standard_planner (Query * parse, const char * query_string, int cursorOptions,ParamListInfo boundParams) / / by Query--- > PlannerInfo-- > subquery_planner (glob, parse, NULL,false, tuple_fraction) / / A lot of logic optimizations are not listed-- > pull_up_sublinks (root);-- > pull_up_subqueries (root); / / only a few important logic optimizations are listed here, and others are not listed. / / if the update/delete partition table inherits the table, use inheritance_planner () For other cases, go to grouping_planner ()-- > inheritance_planner () / / update/delete partition table inheritance table-- > grouping_planner ()-- > grouping_planner () / / non-partition table, inheritance table case-- > preprocess_targetlist (root) / / update updates only one column, but when inserting a new tuple, you need to know other column information. -- > rewriteTargetListUD (parse, target_rte, target_relation);-- > expand_targetlist ()-- > query_planner (root, standard_qp_callback, & qp_extra); / / important-- > add_base_rels_to_query ()-> deconstruct_jointree (root) -- > add_other_rels_to_query (root); / / expand the partition table to the relevant fields in PlannerInfo-- > expand_inherited_rtentry ()-- > expand_planner_arrays (root, num_live_parts);-- > make_one_rel (root, joinlist) -- > set_base_rel_sizes (root);-- > set_rel_size ();-- > set_append_rel_size (root, rel, rti, rte); / / if it is a partition table or inherits it here, otherwise go below-- > set_rel_size (root, childrel, childRTindex, childRTE) / / process sub-partition tables-- > set_plain_rel_size (root, rel, rte) -- > set_plain_rel_size () / / if it is not a partition table or inheritance-- > set_baserel_size_estimates ()-- > set_base_rel_pathlists (root) -- > set_rel_pathlist (root, rel, rti, root- > simple_rte_ array [RTI]);-- > set_append_rel_pathlist (root, rel, rti, rte); / / generate the access path of each partition table-- > make_rel_from_joinlist (root, joinlist) / / dynamic programming or genetic planning-- > standard_join_search () / / dynamic planning-- > geqo () / / genetic planning and dynamic planning choose one of the two-- > apply_scanjoin_target_to_paths ()-- > create_modifytable_path () / / by PlannerInfo--- > RelOptInfo-- > fetch_upper_rel (root UPPERREL_FINAL, NULL) / / by RelOptInfo--- > Path-- > get_cheapest_fractional_path (final_rel, tuple_fraction); / by PlannerInfo+Path-- > Plan-- > create_plan (root, best_path); / / subsequent processing, by Plan-- > PlannedStmt

Core data structures: PlannedStmt, PlannerInfo, RelOptInfo (storage access path and its cost), Path

Path: all paths inherit from Path, so this is important.

Typedef struct Path {NodeTag type; NodeTag pathtype; / * tag identifying scan/join method * / RelOptInfo * parent; / * the relation this path can build * / PathTarget * pathtarget; / * list of Vars/Exprs, cost, width * / ParamPathInfo * param_info; / * parameterization info, or NULL if none * / bool parallel_aware; / * engage parallel-aware logic? * / bool parallel_safe; / * OK to use as part of parallel plan? * / int parallel_workers; / * desired # of workers 0 = not parallel * / * estimated size/costs for path (see costsize.c for more info) * / double rows; / * estimated number of result tuples * / Cost startup_cost; / * cost expended before fetching any tuples * / Cost total_cost; / * total cost (assuming all tuples fetched) * / List * pathkeys; / * sort ordering of path's output * / / * pathkeys is a List of PathKey nodes; see above * /} Path / * ModifyTablePath represents performing INSERT/UPDATE/DELETE modifications * We represent most things that will be in the ModifyTable plan node * literally, except we have child Path (s) not Plan (s) But analysis of the * OnConflictExpr is deferred to createplan.c, as is collection of FDW data. * / typedef struct ModifyTablePath {Path path; / / you can see that ModifyTablePath inherits from Path CmdType operation; / * INSERT, UPDATE, or DELETE * / bool canSetTag; / * do we set the command tag/es_processed? * / Index nominalRelation; / * Parent RT index for use of EXPLAIN * / Index rootRelation; / * Root RT index, if target is partitioned * / bool partColsUpdated; / * some part key in hierarchy updated * / List * resultRelations; / * integer list of RT indexes * / List * subpaths / * Path (s) producing source data * / List * subroots; / * per-target-table PlannerInfos * / List * withCheckOptionLists; / * per-target-table WCO lists * / List * returningLists; / * per-target-table RETURNING tlists * / List * rowMarks; / * PlanRowMarks (non-locking only) * / OnConflictExpr * onconflict; / * ON CONFLICT clause, or NULL * / int epqParam; / * ID of Param for EvalPlanQual re-eval * /} ModifyTablePath

To generate the update execution path, the final step is to generate ModifyTablePath. In this case, the path generation process is Path-- > ProjectionPath-- > ModifyTablePath, that is, the table is scanned sequentially and then modified. The execution plan is generated later by the path.

/ * create_modifytable_path * Creates a pathnode that represents performing INSERT/UPDATE/DELETE mods * * 'rel' is the parent relation associated with the result *' resultRelations' is an integer list of actual RT indexes of target rel (s) * subpaths' is a list of Path (s) producing source data (one per rel) * 'subroots' is a list of PlannerInfo structs (one per rel) * / ModifyTablePath * create_modifytable_path (PlannerInfo * root, RelOptInfo * rel, CmdType operation, bool canSetTag, Index nominalRelation, Index rootRelation Bool partColsUpdated, List * resultRelations, List * subpaths, List * subroots, List * withCheckOptionLists, List * returningLists, List * rowMarks, OnConflictExpr * onconflict, int epqParam) {ModifyTablePath * pathnode = makeNode (ModifyTablePath) Double total_size; ListCell * lc; Assert (list_length (resultRelations) = = list_length (subpaths)); Assert (list_length (resultRelations) = = list_length (subroots)); Assert (withCheckOptionLists = = NIL | | list_length (resultRelations) = = list_length (withCheckOptionLists)); Assert (returningLists = = NIL | list_length (resultRelations) = = list_length (returningLists)); pathnode- > path.pathtype = Tunable ModifyTable; pathnode- > path.parent = rel; pathnode- > path.pathtarget = rel- > reltarget / * pathtarget is not interesting, just make it minimally valid * / * For now, assume we are above any joins, so no parameterization * / pathnode- > path.param_info = NULL; pathnode- > path.parallel_aware = false; pathnode- > path.parallel_safe = false; pathnode- > path.parallel_workers = 0; pathnode- > path.pathkeys = NIL; / * * Compute cost & rowcount assum of subpath costs & rowcounts. * * Currently, we don't charge anything extra for the actual table * modification work, nor for the WITH CHECK OPTIONS or RETURNING * expressions if any. It would only be window dressing, since * ModifyTable is always a top-level node and there is no way for the * costs to change any higher-level planning choices. But we might want * to make it look better sometime.*/ pathnode- > path.startup_cost = 0; pathnode- > path.total_cost = 0; pathnode- > path.rows = 0; total_size = 0; foreach (lc, subpaths) {Path * subpath = (Path *) lfirst (lc); if (lc = = list_head (subpaths)) / * first node? * / pathnode- > path.startup_cost = subpath- > startup_cost; pathnode- > path.total_cost + = subpath- > total_cost Pathnode- > path.rows + = subpath- > rows; total_size + = subpath- > pathtarget- > width * subpath- > rows;} / * Set width to the average width of the subpath outputs. XXX this is * totally wrong: we should report zero if no RETURNING, else an average * of the RETURNING tlist widths. But it's what happened historically, * and improving it is a task for another day.*/ if (pathnode- > path.rows > 0) total_size / = pathnode- > path.rows; pathnode- > path.pathtarget- > width = rint (total_size); pathnode- > operation = operation; pathnode- > canSetTag = canSetTag; pathnode- > nominalRelation = nominalRelation; pathnode- > rootRelation = rootRelation; pathnode- > partColsUpdated = partColsUpdated; pathnode- > resultRelations = resultRelations; pathnode- > subpaths = subpaths; pathnode- > subroots = subroots; pathnode- > withCheckOptionLists = withCheckOptionLists; pathnode- > returningLists = returningLists; pathnode- > rowMarks = rowMarks; pathnode- > onconflict = onconflict Pathnode- > epqParam = epqParam; return pathnode;}

Now that we have generated the optimal update path, we need to generate the execution plan from the path:

Plan * create_plan (PlannerInfo * root, Path * best_path) {Plan * plan; Assert (root- > plan_params = = NIL); / * plan_params should not be in use in current query level * / / * Initialize this module's workspace in PlannerInfo * / root- > curOuterRels = NULL; root- > curOuterParams = NIL; / * Recursively process the path tree, demanding the correct tlist result * / plan = create_plan_recurse (root, best_path, CP_EXACT_TLIST) / / the actual implementation is here / * * Make sure the topmost plan node's targetlist exposes the original * column names and other decorative info. Targetlists generated within * the planner don't bother with that stuff, but we must have it on the * top-level tlist seen at execution time. However, ModifyTable plan * nodes don't have a tlist matching the querytree targetlist.*/ if (! IsA (plan, ModifyTable)) apply_tlist_labeling (plan- > targetlist, root- > processed_tlist); / * Attach any initPlans created in this query level to the topmost plan * node. In principle the initplans could go in any plan node at or * above where they're referenced, but there seems no reason to put them * any lower than the topmost node for the query level. Also, see * comments for SS_finalize_plan before you try to change this.) * / SS_attach_initplans (root, plan); / * Check we successfully assigned all NestLoopParams to plan nodes * / if (root- > curOuterParams! = NIL) elog (ERROR, "failed to assign all NestLoopParams to plan nodes"); / * Reset plan_params to ensure param IDs used for nestloop params are not re-used later*/ root- > plan_params = NIL; return plan } / / generate the best execution plan static ModifyTable * create_modifytable_plan (PlannerInfo * root, ModifyTablePath * best_path) {ModifyTable * plan; List * subplans = NIL; ListCell * subpaths, * subroots; / * Build the plan for each input path * / forboth (subpaths, best_path- > subpaths, subroots, best_path- > subroots) {Path * subpath = (Path *) lfirst (subpaths); PlannerInfo * subroot = (PlannerInfo *) lfirst (subroots); Plan * subplan / * In an inherited UPDATE/DELETE, reference the per-child modified * subroot while creating Plans from Paths for the child rel. This is * a kluge, but otherwise it's too hard to ensure that Plan creation * functions (particularly in FDWs) don't depend on the contents of * "root" matching what they saw at Path creation time. The main * downside is that creation functions for Plans that might appear * below a ModifyTable cannot expect to modify the contents of "root" * and have it "stick" for subsequent processing such as setrefs.c * That's not great, but it seems better than the alternative.*/ subplan = create_plan_recurse (subroot, subpath, CP_EXACT_TLIST); / * Transfer resname/resjunk labeling, too, to keep executor happy * / apply_tlist_labeling (subplan- > targetlist, subroot- > processed_tlist); subplans = lappend (subplans, subplan) } plan = make_modifytable (root,best_path- > operation,best_path- > canSetTag, best_path- > nominalRelation,best_path- > rootRelation, best_path- > partColsUpdated,best_path- > resultRelations, subplans,best_path- > subroots,best_path- > withCheckOptionLists, best_path- > returningLists,best_path- > rowMarks, best_path- > onconflict,best_path- > epqParam); copy_generic_path_info (& plan- > plan, & best_path- > path); return plan;}

The final execution plan is ModifyTable:

/ *-* ModifyTable node-* Apply rows produced by subplan (s) to result table (s), * by inserting, updating, or deleting. * * If the originally named target table is a partitioned table, both * nominalRelation and rootRelation contain the RT index of the partition * root, which is not otherwise mentioned in the plan. Otherwise rootRelation * is zero. However, nominalRelation will always be set, as it's the rel that * EXPLAIN should claim is the INSERT/UPDATE/DELETE target. * * Note that rowMarks and epqParam are presumed to be valid for all the * subplan (s); they can't contain any info that varies across subplans. *-* / typedef struct ModifyTable {Plan plan; CmdType operation; / * INSERT, UPDATE, or DELETE * / bool canSetTag; / * do we set the command tag/es_processed? * / Index nominalRelation; / * Parent RT index for use of EXPLAIN * / Index rootRelation; / * Root RT index, if target is partitioned * / bool partColsUpdated; / * some part key in hierarchy updated * / List * resultRelations; / * integer list of RT indexes * / int resultRelIndex / * index of first resultRel in plan's list * / int rootResultRelIndex; / * index of the partitioned table root * / List * plans; / * plan (s) producing source data * / List * withCheckOptionLists; / * per-target-table WCO lists * / List * returningLists; / * per-target-table RETURNING tlists * / List * fdwPrivLists; / * per-target-table FDW private data lists * / Bitmapset * fdwDirectModifyPlans; / * indices of FDW DM plans * / List * rowMarks / * PlanRowMarks (non-locking only) * / int epqParam; / * ID of Param for EvalPlanQual re-eval * / OnConflictAction onConflictAction; / * ON CONFLICT action * / List * arbiterIndexes; / * List of ON CONFLICT arbiter index OIDs * / List * onConflictSet; / * SET for INSERT ON CONFLICT DO UPDATE * / Node * onConflictWhere; / * WHERE for ON CONFLICT UPDATE * / Index exclRelRTI; / * RTI of the EXCLUDED pseudo relation * / List * exclRelTlist; / * tlist of the EXCLUDED pseudo relation * /} ModifyTable; Actuator

According to the above execution plan, execute it. It is mainly the implementation of various operators, in which to understand the operation principle of the actuator, mainly the volcanic model, one tuple at a time. Let's take a look at the calling process.

CreatePortal (", true,true); PortalDefineQuery (portal,NULL,query_string,commandTag,plantree_list,NULL); PortalStart (portal,NULL, 0, InvalidSnapshot); PortalRun (portal,FETCH_ALL,true,true,receiver,receiver,&qc);-- > PortalRunMulti ()-- > ProcessQuery ()-- > ExecutorStart (queryDesc, 0);-- > standard_ExecutorStart ()-> estate = CreateExecutorState (); / / create EState-> estate- > es_output_cid = GetCurrentCommandId (true) / / to get cid, use-- > InitPlan (queryDesc, eflags);-- > ExecInitNode (plan, estate, eflags);-- > ExecInitModifyTable () / / initialize ModifyTableState-- > ExecutorRun (queryDesc, ForwardScanDirection, 0L, true);-- > standard_ExecutorRun ()-- > ExecutePlan ()-- > ExecProcNode (planstate) / / one-tuple volcano model-- > node- > ExecProcNode (node);-- > ExecProcNodeFirst (PlanState * node)-- > node- > ExecProcNode (node) -- > ExecModifyTable (PlanState * pstate)-- > ExecUpdate ()-- > table_tuple_update (Relation rel,.)-- > rel- > rd_tableam- > tuple_update ()-- > heapam_tuple_update (Relation relation,.)-- > heap_update (relation, otid, tuple, cid .)-- > ExecutorFinish (queryDesc) -- > ExecutorEnd (queryDesc); PortalDrop (portal, false)

Key data structures:

/ / ModifyTableState informationtypedef struct ModifyTableState {PlanState ps; / * its first field is NodeTag * / CmdType operation; / * INSERT, UPDATE, or DELETE * / bool canSetTag; / * do we set the command tag/es_processed? * / bool mt_done; / * are we done? * / PlanState * * mt_plans; / * subplans (one per target rel) * / int mt_nplans; / * number of plans in the array * / int mt_whichplan / * which one is being executed (0..n-1) * / TupleTableSlot * * mt_scans; / * input tuple corresponding to underlying * plans * / ResultRelInfo * resultRelInfo; / * per-subplan target relations * / ResultRelInfo * rootResultRelInfo; / * root target relation (partitioned * table root) * / List * * mt_arowmarks; / * per-subplan ExecAuxRowMark lists * / EPQState mt_epqstate; / * for evaluating EvalPlanQual rechecks * / bool fireBSTriggers / * do we need to fire stmt triggers? * / * Slot for storing tuples in the root partitioned table's rowtype during * an UPDATE of a partitioned table. * / TupleTableSlot * mt_root_tuple_slot; struct PartitionTupleRouting * mt_partition_tuple_routing; / * Tuple-routing support info * / struct TransitionCaptureState * mt_transition_capture; / * controls transition table population for specified operation * / / * controls transition table population for INSERT...ON CONFLICT UPDATE * / struct TransitionCaptureState * mt_oc_transition_capture; / * Per plan map for tuple conversion from child to root * / TupleConversionMap * * mt_per_subplan_tupconv_maps;} ModifyTableState

Core execution operator implementation:

/ *-* ExecModifyTable * * Perform table modifications as required, and return RETURNING results * if needed. *-* / static TupleTableSlot * ExecModifyTable (PlanState * pstate) {ModifyTableState * node = castNode (ModifyTableState, pstate); PartitionTupleRouting * proute = node- > mt_partition_tuple_routing; EState * estate = node- > ps.state; CmdType operation = node- > operation; ResultRelInfo * saved_resultRelInfo ResultRelInfo * resultRelInfo; PlanState * subplanstate; JunkFilter * junkfilter; TupleTableSlot * slot; TupleTableSlot * planSlot; ItemPointer tupleid; ItemPointerData tuple_ctid; HeapTupleData oldtupdata; HeapTuple oldtuple; CHECK_FOR_INTERRUPTS (); / * This should NOT get called during EvalPlanQual; we should have passed a * subplan tree to EvalPlanQual, instead. Use a runtime test not just * Assert because this condition is easy to miss in testing. * / if (estate- > es_epq_active! = NULL) elog (ERROR, "ModifyTable should not be called during EvalPlanQual"); / * If we've already completed processing, don't try to do more. We need * this test because ExecPostprocessPlan might call us an extra time, and * our subplan's nodes aren't necessarily robust against being called * extra times.*/ if (node- > mt_done) return NULL; / * On first call, fire BEFORE STATEMENT triggers before proceeding.*/ if (node- > fireBSTriggers) {fireBSTriggers (node); node- > fireBSTriggers = false;} / * Preload local variables * / resultRelInfo = node- > resultRelInfo + node- > mt_whichplan; subplanstate = node- > mt_ plans [node-> mt_whichplan]; junkfilter = resultRelInfo- > ri_junkFilter / * es_result_relation_info must point to the currently active result relation while we are within this ModifyTable node. * Even though ModifyTable nodes can't be nested statically, they can be nested * dynamically (since our subplan could include a reference to a modifying * CTE). So we have to save and restore the caller's value.*/ saved_resultRelInfo = estate- > es_result_relation_info; estate- > es_result_relation_info = resultRelInfo; / * Fetch rows from subplan (s), and execute the required table modification for each row.*/ for (;;) {/ * Reset the per-output-tuple exprcontext. This is needed because * triggers expect to use that context as workspace. It's a bit ugly * to do this below the top level of the plan, however. We might need to rethink this later.*/ ResetPerTupleExprContext (estate); / * Reset per-tuple memory context used for processing on conflict and * returning clauses, to free any expression evaluation storage allocated in the previous cycle. * / if (pstate- > ps_ExprContext) ResetExprContext (pstate- > ps_ExprContext); planSlot = ExecProcNode (subplanstate); if (TupIsNull (planSlot)) {/ * advance to next subplan if any * / node- > mt_whichplan++; / / update of the partition table, each partition distribution corresponds to a subplan. Execute the next partition if (node- > mt_whichplan) after one partition is executed.

< node->

< mtstate->

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report