|
| 1 | +/* ------------------------------------------------------------------------ |
| 2 | + * |
| 3 | + * rowmarks_fix.h |
| 4 | + *Hack incorrect RowMark generation due to unset 'RTE->inh' flag |
| 5 | + *NOTE: this code is only useful for vanilla |
| 6 | + * |
| 7 | + * Copyright (c) 2017, Postgres Professional |
| 8 | + * |
| 9 | + * ------------------------------------------------------------------------ |
| 10 | + */ |
| 11 | + |
| 12 | +#include"compat/rowmarks_fix.h" |
| 13 | +#include"planner_tree_modification.h" |
| 14 | + |
| 15 | +#include"access/sysattr.h" |
| 16 | +#include"catalog/pg_type.h" |
| 17 | +#include"nodes/relation.h" |
| 18 | +#include"nodes/nodeFuncs.h" |
| 19 | +#include"utils/builtins.h" |
| 20 | +#include"utils/rel.h" |
| 21 | + |
| 22 | + |
| 23 | +#ifndefNATIVE_PARTITIONING_ROWMARKS |
| 24 | + |
| 25 | +/* Special column name for rowmarks */ |
| 26 | +#defineTABLEOID_STR(subst)( "pathman_tableoid" subst ) |
| 27 | +#defineTABLEOID_STR_BASE_LEN( sizeof(TABLEOID_STR("")) - 1 ) |
| 28 | + |
| 29 | + |
| 30 | +staticvoidlock_rows_visitor(Plan*plan,void*context); |
| 31 | +staticList*get_tableoids_list(List*tlist); |
| 32 | + |
| 33 | + |
| 34 | +/* Final rowmark processing for partitioned tables */ |
| 35 | +void |
| 36 | +postprocess_lock_rows(List*rtable,Plan*plan) |
| 37 | +{ |
| 38 | +plan_tree_walker(plan,lock_rows_visitor,rtable); |
| 39 | +} |
| 40 | + |
| 41 | +/* |
| 42 | + * Add missing 'TABLEOID_STR%u' junk attributes for inherited partitions |
| 43 | + * |
| 44 | + * This is necessary since preprocess_targetlist() heavily |
| 45 | + * depends on the 'inh' flag which we have to unset. |
| 46 | + * |
| 47 | + * postprocess_lock_rows() will later transform 'TABLEOID_STR:Oid' |
| 48 | + * relnames into 'tableoid:rowmarkId'. |
| 49 | + */ |
| 50 | +void |
| 51 | +rowmark_add_tableoids(Query*parse) |
| 52 | +{ |
| 53 | +ListCell*lc; |
| 54 | + |
| 55 | +/* Generate 'tableoid' for partitioned table rowmark */ |
| 56 | +foreach (lc,parse->rowMarks) |
| 57 | +{ |
| 58 | +RowMarkClause*rc= (RowMarkClause*)lfirst(lc); |
| 59 | +Oidparent=getrelid(rc->rti,parse->rtable); |
| 60 | +Var*var; |
| 61 | +TargetEntry*tle; |
| 62 | +charresname[64]; |
| 63 | + |
| 64 | +/* Check that table is partitioned */ |
| 65 | +if (!get_pathman_relation_info(parent)) |
| 66 | +continue; |
| 67 | + |
| 68 | +var=makeVar(rc->rti, |
| 69 | +TableOidAttributeNumber, |
| 70 | +OIDOID, |
| 71 | +-1, |
| 72 | +InvalidOid, |
| 73 | +0); |
| 74 | + |
| 75 | +/* Use parent's Oid as TABLEOID_STR's key (%u) */ |
| 76 | +snprintf(resname,sizeof(resname),TABLEOID_STR("%u"),parent); |
| 77 | + |
| 78 | +tle=makeTargetEntry((Expr*)var, |
| 79 | +list_length(parse->targetList)+1, |
| 80 | +pstrdup(resname), |
| 81 | + true); |
| 82 | + |
| 83 | +/* There's no problem here since new attribute is junk */ |
| 84 | +parse->targetList=lappend(parse->targetList,tle); |
| 85 | +} |
| 86 | +} |
| 87 | + |
| 88 | +/* |
| 89 | + * Extract target entries with resnames beginning with TABLEOID_STR |
| 90 | + * and var->varoattno == TableOidAttributeNumber |
| 91 | + */ |
| 92 | +staticList* |
| 93 | +get_tableoids_list(List*tlist) |
| 94 | +{ |
| 95 | +List*result=NIL; |
| 96 | +ListCell*lc; |
| 97 | + |
| 98 | +foreach (lc,tlist) |
| 99 | +{ |
| 100 | +TargetEntry*te= (TargetEntry*)lfirst(lc); |
| 101 | +Var*var= (Var*)te->expr; |
| 102 | + |
| 103 | +if (!IsA(var,Var)) |
| 104 | +continue; |
| 105 | + |
| 106 | +/* Check that column name begins with TABLEOID_STR & it's tableoid */ |
| 107 | +if (var->varoattno==TableOidAttributeNumber&& |
| 108 | +(te->resname&&strlen(te->resname)>TABLEOID_STR_BASE_LEN)&& |
| 109 | +0==strncmp(te->resname,TABLEOID_STR(""),TABLEOID_STR_BASE_LEN)) |
| 110 | +{ |
| 111 | +result=lappend(result,te); |
| 112 | +} |
| 113 | +} |
| 114 | + |
| 115 | +returnresult; |
| 116 | +} |
| 117 | + |
| 118 | +/* |
| 119 | + * Find 'TABLEOID_STR%u' attributes that were manually |
| 120 | + * created for partitioned tables and replace Oids |
| 121 | + * (used for '%u') with expected rc->rowmarkIds |
| 122 | + */ |
| 123 | +staticvoid |
| 124 | +lock_rows_visitor(Plan*plan,void*context) |
| 125 | +{ |
| 126 | +List*rtable= (List*)context; |
| 127 | +LockRows*lock_rows= (LockRows*)plan; |
| 128 | +Plan*lock_child=outerPlan(plan); |
| 129 | +List*tableoids; |
| 130 | +ListCell*lc; |
| 131 | + |
| 132 | +if (!IsA(lock_rows,LockRows)) |
| 133 | +return; |
| 134 | + |
| 135 | +Assert(rtable&&IsA(rtable,List)&&lock_child); |
| 136 | + |
| 137 | +/* Select tableoid attributes that must be renamed */ |
| 138 | +tableoids=get_tableoids_list(lock_child->targetlist); |
| 139 | +if (!tableoids) |
| 140 | +return;/* this LockRows has nothing to do with partitioned table */ |
| 141 | + |
| 142 | +foreach (lc,lock_rows->rowMarks) |
| 143 | +{ |
| 144 | +PlanRowMark*rc= (PlanRowMark*)lfirst(lc); |
| 145 | +Oidparent_oid=getrelid(rc->rti,rtable); |
| 146 | +ListCell*mark_lc; |
| 147 | +List*finished_tes=NIL;/* postprocessed target entries */ |
| 148 | + |
| 149 | +foreach (mark_lc,tableoids) |
| 150 | +{ |
| 151 | +TargetEntry*te= (TargetEntry*)lfirst(mark_lc); |
| 152 | +constchar*cur_oid_str=&(te->resname[TABLEOID_STR_BASE_LEN]); |
| 153 | +Datumcur_oid_datum; |
| 154 | + |
| 155 | +cur_oid_datum=DirectFunctionCall1(oidin,CStringGetDatum(cur_oid_str)); |
| 156 | + |
| 157 | +if (DatumGetObjectId(cur_oid_datum)==parent_oid) |
| 158 | +{ |
| 159 | +charresname[64]; |
| 160 | + |
| 161 | +/* Replace 'TABLEOID_STR:Oid' with 'tableoid:rowmarkId' */ |
| 162 | +snprintf(resname,sizeof(resname),"tableoid%u",rc->rowmarkId); |
| 163 | +te->resname=pstrdup(resname); |
| 164 | + |
| 165 | +finished_tes=lappend(finished_tes,te); |
| 166 | +} |
| 167 | +} |
| 168 | + |
| 169 | +/* Remove target entries that have been processed in this step */ |
| 170 | +foreach (mark_lc,finished_tes) |
| 171 | +tableoids=list_delete_ptr(tableoids,lfirst(mark_lc)); |
| 172 | + |
| 173 | +if (list_length(tableoids)==0) |
| 174 | +break;/* nothing to do */ |
| 175 | +} |
| 176 | +} |
| 177 | + |
| 178 | +#endif/* NATIVE_PARTITIONING_ROWMARKS */ |