1 /*------------------------------------------------------------------------- 2 * 3 * partition.c 4 * Partitioning related data structures and functions. 5 * 6 * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group 7 * Portions Copyright (c) 1994, Regents of the University of California 8 * 9 * 10 * IDENTIFICATION 11 * src/backend/catalog/partition.c 12 * 13 *------------------------------------------------------------------------- 14 */ 15 #include "postgres.h" 16 17 #include "access/genam.h" 18 #include "access/htup_details.h" 19 #include "access/table.h" 20 #include "access/tupconvert.h" 21 #include "access/sysattr.h" 22 #include "catalog/indexing.h" 23 #include "catalog/partition.h" 24 #include "catalog/pg_inherits.h" 25 #include "catalog/pg_partitioned_table.h" 26 #include "nodes/makefuncs.h" 27 #include "optimizer/optimizer.h" 28 #include "partitioning/partbounds.h" 29 #include "rewrite/rewriteManip.h" 30 #include "utils/fmgroids.h" 31 #include "utils/partcache.h" 32 #include "utils/rel.h" 33 #include "utils/syscache.h" 34 35 36 static Oid get_partition_parent_worker(Relation inhRel, Oid relid); 37 static void get_partition_ancestors_worker(Relation inhRel, Oid relid, 38 List **ancestors); 39 40 /* 41 * get_partition_parent 42 * Obtain direct parent of given relation 43 * 44 * Returns inheritance parent of a partition by scanning pg_inherits 45 * 46 * Note: Because this function assumes that the relation whose OID is passed 47 * as an argument will have precisely one parent, it should only be called 48 * when it is known that the relation is a partition. 49 */ 50 Oid 51 get_partition_parent(Oid relid) 52 { 53 Relation catalogRelation; 54 Oid result; 55 56 catalogRelation = table_open(InheritsRelationId, AccessShareLock); 57 58 result = get_partition_parent_worker(catalogRelation, relid); 59 60 if (!OidIsValid(result)) 61 elog(ERROR, "could not find tuple for parent of relation %u", relid); 62 63 table_close(catalogRelation, AccessShareLock); 64 65 return result; 66 } 67 68 /* 69 * get_partition_parent_worker 70 * Scan the pg_inherits relation to return the OID of the parent of the 71 * given relation 72 */ 73 static Oid 74 get_partition_parent_worker(Relation inhRel, Oid relid) 75 { 76 SysScanDesc scan; 77 ScanKeyData key[2]; 78 Oid result = InvalidOid; 79 HeapTuple tuple; 80 81 ScanKeyInit(&key[0], 82 Anum_pg_inherits_inhrelid, 83 BTEqualStrategyNumber, F_OIDEQ, 84 ObjectIdGetDatum(relid)); 85 ScanKeyInit(&key[1], 86 Anum_pg_inherits_inhseqno, 87 BTEqualStrategyNumber, F_INT4EQ, 88 Int32GetDatum(1)); 89 90 scan = systable_beginscan(inhRel, InheritsRelidSeqnoIndexId, true, 91 NULL, 2, key); 92 tuple = systable_getnext(scan); 93 if (HeapTupleIsValid(tuple)) 94 { 95 Form_pg_inherits form = (Form_pg_inherits) GETSTRUCT(tuple); 96 97 result = form->inhparent; 98 } 99 100 systable_endscan(scan); 101 102 return result; 103 } 104 105 /* 106 * get_partition_ancestors 107 * Obtain ancestors of given relation 108 * 109 * Returns a list of ancestors of the given relation. 110 * 111 * Note: Because this function assumes that the relation whose OID is passed 112 * as an argument and each ancestor will have precisely one parent, it should 113 * only be called when it is known that the relation is a partition. 114 */ 115 List * 116 get_partition_ancestors(Oid relid) 117 { 118 List *result = NIL; 119 Relation inhRel; 120 121 inhRel = table_open(InheritsRelationId, AccessShareLock); 122 123 get_partition_ancestors_worker(inhRel, relid, &result); 124 125 table_close(inhRel, AccessShareLock); 126 127 return result; 128 } 129 130 /* 131 * get_partition_ancestors_worker 132 * recursive worker for get_partition_ancestors 133 */ 134 static void 135 get_partition_ancestors_worker(Relation inhRel, Oid relid, List **ancestors) 136 { 137 Oid parentOid; 138 139 /* Recursion ends at the topmost level, ie., when there's no parent */ 140 parentOid = get_partition_parent_worker(inhRel, relid); 141 if (parentOid == InvalidOid) 142 return; 143 144 *ancestors = lappend_oid(*ancestors, parentOid); SimpleLruShmemSize(int nslots,int nlsns)145 get_partition_ancestors_worker(inhRel, parentOid, ancestors); 146 } 147 148 /* 149 * index_get_partition 150 * Return the OID of index of the given partition that is a child 151 * of the given index, or InvalidOid if there isn't one. 152 */ 153 Oid 154 index_get_partition(Relation partition, Oid indexId) 155 { 156 List *idxlist = RelationGetIndexList(partition); 157 ListCell *l; 158 159 foreach(l, idxlist) 160 { 161 Oid partIdx = lfirst_oid(l); 162 HeapTuple tup; 163 Form_pg_class classForm; 164 bool ispartition; SimpleLruInit(SlruCtl ctl,const char * name,int nslots,int nlsns,LWLock * ctllock,const char * subdir,int tranche_id)165 166 tup = SearchSysCache1(RELOID, ObjectIdGetDatum(partIdx)); 167 if (!HeapTupleIsValid(tup)) 168 elog(ERROR, "cache lookup failed for relation %u", partIdx); 169 classForm = (Form_pg_class) GETSTRUCT(tup); 170 ispartition = classForm->relispartition; 171 ReleaseSysCache(tup); 172 if (!ispartition) 173 continue; 174 if (get_partition_parent(partIdx) == indexId) 175 { 176 list_free(idxlist); 177 return partIdx; 178 } 179 } 180 181 list_free(idxlist); 182 return InvalidOid; 183 } 184 185 /* 186 * map_partition_varattnos - maps varattno of any Vars in expr from the 187 * attno's of 'from_rel' to the attno's of 'to_rel' partition, each of which 188 * may be either a leaf partition or a partitioned table, but both of which 189 * must be from the same partitioning hierarchy. 190 * 191 * Even though all of the same column names must be present in all relations 192 * in the hierarchy, and they must also have the same types, the attnos may 193 * be different. 194 * 195 * If found_whole_row is not NULL, *found_whole_row returns whether a 196 * whole-row variable was found in the input expression. 197 * 198 * Note: this will work on any node tree, so really the argument and result 199 * should be declared "Node *". But a substantial majority of the callers 200 * are working on Lists, so it's less messy to do the casts internally. 201 */ 202 List * 203 map_partition_varattnos(List *expr, int fromrel_varno, 204 Relation to_rel, Relation from_rel, 205 bool *found_whole_row) 206 { 207 bool my_found_whole_row = false; 208 209 if (expr != NIL) 210 { 211 AttrNumber *part_attnos; 212 213 part_attnos = convert_tuples_by_name_map(RelationGetDescr(to_rel), 214 RelationGetDescr(from_rel), 215 gettext_noop("could not convert row type")); 216 expr = (List *) map_variable_attnos((Node *) expr, 217 fromrel_varno, 0, 218 part_attnos, 219 RelationGetDescr(from_rel)->natts, 220 RelationGetForm(to_rel)->reltype, 221 &my_found_whole_row); 222 } 223 224 if (found_whole_row) 225 *found_whole_row = my_found_whole_row; 226 227 return expr; 228 } 229 230 /* 231 * Checks if any of the 'attnums' is a partition key attribute for rel 232 * 233 * Sets *used_in_expr if any of the 'attnums' is found to be referenced in some 234 * partition key expression. It's possible for a column to be both used 235 * directly and as part of an expression; if that happens, *used_in_expr may 236 * end up as either true or false. That's OK for current uses of this 237 * function, because *used_in_expr is only used to tailor the error message 238 * text. 239 */ 240 bool 241 has_partition_attrs(Relation rel, Bitmapset *attnums, bool *used_in_expr) 242 { 243 PartitionKey key; 244 int partnatts; 245 List *partexprs; 246 ListCell *partexprs_item; 247 int i; 248 249 if (attnums == NULL || rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) 250 return false; 251 252 key = RelationGetPartitionKey(rel); 253 partnatts = get_partition_natts(key); 254 partexprs = get_partition_exprs(key); 255 256 partexprs_item = list_head(partexprs); 257 for (i = 0; i < partnatts; i++) 258 { 259 AttrNumber partattno = get_partition_col_attnum(key, i); 260 261 if (partattno != 0) 262 { SimpleLruZeroPage(SlruCtl ctl,int pageno)263 if (bms_is_member(partattno - FirstLowInvalidHeapAttributeNumber, 264 attnums)) 265 { 266 if (used_in_expr) 267 *used_in_expr = false; 268 return true; 269 } 270 } 271 else 272 { 273 /* Arbitrary expression */ 274 Node *expr = (Node *) lfirst(partexprs_item); 275 Bitmapset *expr_attrs = NULL; 276 277 /* Find all attributes referenced */ 278 pull_varattnos(expr, 1, &expr_attrs); 279 partexprs_item = lnext(partexprs_item); 280 281 if (bms_overlap(attnums, expr_attrs)) 282 { 283 if (used_in_expr) 284 *used_in_expr = true; 285 return true; 286 } 287 } 288 } 289 290 return false; 291 } 292 293 /* 294 * get_default_partition_oid 295 * 296 * Given a relation OID, return the OID of the default partition, if one 297 * exists. Use get_default_oid_from_partdesc where possible, for 298 * efficiency. 299 */ 300 Oid 301 get_default_partition_oid(Oid parentId) 302 { 303 HeapTuple tuple; SimpleLruZeroLSNs(SlruCtl ctl,int slotno)304 Oid defaultPartId = InvalidOid; 305 306 tuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(parentId)); 307 308 if (HeapTupleIsValid(tuple)) 309 { 310 Form_pg_partitioned_table part_table_form; 311 312 part_table_form = (Form_pg_partitioned_table) GETSTRUCT(tuple); 313 defaultPartId = part_table_form->partdefid; 314 ReleaseSysCache(tuple); 315 } 316 317 return defaultPartId; 318 } 319 320 /* SimpleLruWaitIO(SlruCtl ctl,int slotno)321 * update_default_partition_oid 322 * 323 * Update pg_partitioned_table.partdefid with a new default partition OID. 324 */ 325 void 326 update_default_partition_oid(Oid parentId, Oid defaultPartId) 327 { 328 HeapTuple tuple; 329 Relation pg_partitioned_table; 330 Form_pg_partitioned_table part_table_form; 331 332 pg_partitioned_table = table_open(PartitionedRelationId, RowExclusiveLock); 333 334 tuple = SearchSysCacheCopy1(PARTRELID, ObjectIdGetDatum(parentId)); 335 336 if (!HeapTupleIsValid(tuple)) 337 elog(ERROR, "cache lookup failed for partition key of relation %u", 338 parentId); 339 340 part_table_form = (Form_pg_partitioned_table) GETSTRUCT(tuple); 341 part_table_form->partdefid = defaultPartId; 342 CatalogTupleUpdate(pg_partitioned_table, &tuple->t_self, tuple); 343 344 heap_freetuple(tuple); 345 table_close(pg_partitioned_table, RowExclusiveLock); 346 } 347 348 /* 349 * get_proposed_default_constraint 350 * 351 * This function returns the negation of new_part_constraints, which 352 * would be an integral part of the default partition constraints after 353 * addition of the partition to which the new_part_constraints belongs. 354 */ 355 List * 356 get_proposed_default_constraint(List *new_part_constraints) 357 { 358 Expr *defPartConstraint; 359 360 defPartConstraint = make_ands_explicit(new_part_constraints); 361 362 /* 363 * Derive the partition constraints of default partition by negating the 364 * given partition constraints. The partition constraint never evaluates 365 * to NULL, so negating it like this is safe. 366 */ 367 defPartConstraint = makeBoolExpr(NOT_EXPR, 368 list_make1(defPartConstraint), 369 -1); 370 371 /* Simplify, to put the negated expression into canonical form */ 372 defPartConstraint = 373 (Expr *) eval_const_expressions(NULL, 374 (Node *) defPartConstraint); SimpleLruReadPage(SlruCtl ctl,int pageno,bool write_ok,TransactionId xid)375 defPartConstraint = canonicalize_qual(defPartConstraint, true); 376 377 return make_ands_implicit(defPartConstraint); 378 } 379