| 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); |
| 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; |
| 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(lfirst_oid(l)) == indexId) |
| 175 | { |
| 176 | list_free(idxlist); |
| 177 | return partIdx; |
| 178 | } |
| 179 | } |
| 180 | |
| 181 | return InvalidOid; |
| 182 | } |
| 183 | |
| 184 | /* |
| 185 | * map_partition_varattnos - maps varattno of any Vars in expr from the |
| 186 | * attno's of 'from_rel' to the attno's of 'to_rel' partition, each of which |
| 187 | * may be either a leaf partition or a partitioned table, but both of which |
| 188 | * must be from the same partitioning hierarchy. |
| 189 | * |
| 190 | * Even though all of the same column names must be present in all relations |
| 191 | * in the hierarchy, and they must also have the same types, the attnos may |
| 192 | * be different. |
| 193 | * |
| 194 | * If found_whole_row is not NULL, *found_whole_row returns whether a |
| 195 | * whole-row variable was found in the input expression. |
| 196 | * |
| 197 | * Note: this will work on any node tree, so really the argument and result |
| 198 | * should be declared "Node *". But a substantial majority of the callers |
| 199 | * are working on Lists, so it's less messy to do the casts internally. |
| 200 | */ |
| 201 | List * |
| 202 | map_partition_varattnos(List *expr, int fromrel_varno, |
| 203 | Relation to_rel, Relation from_rel, |
| 204 | bool *found_whole_row) |
| 205 | { |
| 206 | bool my_found_whole_row = false; |
| 207 | |
| 208 | if (expr != NIL) |
| 209 | { |
| 210 | AttrNumber *part_attnos; |
| 211 | |
| 212 | part_attnos = convert_tuples_by_name_map(RelationGetDescr(to_rel), |
| 213 | RelationGetDescr(from_rel), |
| 214 | gettext_noop("could not convert row type" )); |
| 215 | expr = (List *) map_variable_attnos((Node *) expr, |
| 216 | fromrel_varno, 0, |
| 217 | part_attnos, |
| 218 | RelationGetDescr(from_rel)->natts, |
| 219 | RelationGetForm(to_rel)->reltype, |
| 220 | &my_found_whole_row); |
| 221 | } |
| 222 | |
| 223 | if (found_whole_row) |
| 224 | *found_whole_row = my_found_whole_row; |
| 225 | |
| 226 | return expr; |
| 227 | } |
| 228 | |
| 229 | /* |
| 230 | * Checks if any of the 'attnums' is a partition key attribute for rel |
| 231 | * |
| 232 | * Sets *used_in_expr if any of the 'attnums' is found to be referenced in some |
| 233 | * partition key expression. It's possible for a column to be both used |
| 234 | * directly and as part of an expression; if that happens, *used_in_expr may |
| 235 | * end up as either true or false. That's OK for current uses of this |
| 236 | * function, because *used_in_expr is only used to tailor the error message |
| 237 | * text. |
| 238 | */ |
| 239 | bool |
| 240 | has_partition_attrs(Relation rel, Bitmapset *attnums, bool *used_in_expr) |
| 241 | { |
| 242 | PartitionKey key; |
| 243 | int partnatts; |
| 244 | List *partexprs; |
| 245 | ListCell *partexprs_item; |
| 246 | int i; |
| 247 | |
| 248 | if (attnums == NULL || rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) |
| 249 | return false; |
| 250 | |
| 251 | key = RelationGetPartitionKey(rel); |
| 252 | partnatts = get_partition_natts(key); |
| 253 | partexprs = get_partition_exprs(key); |
| 254 | |
| 255 | partexprs_item = list_head(partexprs); |
| 256 | for (i = 0; i < partnatts; i++) |
| 257 | { |
| 258 | AttrNumber partattno = get_partition_col_attnum(key, i); |
| 259 | |
| 260 | if (partattno != 0) |
| 261 | { |
| 262 | if (bms_is_member(partattno - FirstLowInvalidHeapAttributeNumber, |
| 263 | attnums)) |
| 264 | { |
| 265 | if (used_in_expr) |
| 266 | *used_in_expr = false; |
| 267 | return true; |
| 268 | } |
| 269 | } |
| 270 | else |
| 271 | { |
| 272 | /* Arbitrary expression */ |
| 273 | Node *expr = (Node *) lfirst(partexprs_item); |
| 274 | Bitmapset *expr_attrs = NULL; |
| 275 | |
| 276 | /* Find all attributes referenced */ |
| 277 | pull_varattnos(expr, 1, &expr_attrs); |
| 278 | partexprs_item = lnext(partexprs_item); |
| 279 | |
| 280 | if (bms_overlap(attnums, expr_attrs)) |
| 281 | { |
| 282 | if (used_in_expr) |
| 283 | *used_in_expr = true; |
| 284 | return true; |
| 285 | } |
| 286 | } |
| 287 | } |
| 288 | |
| 289 | return false; |
| 290 | } |
| 291 | |
| 292 | /* |
| 293 | * get_default_partition_oid |
| 294 | * |
| 295 | * Given a relation OID, return the OID of the default partition, if one |
| 296 | * exists. Use get_default_oid_from_partdesc where possible, for |
| 297 | * efficiency. |
| 298 | */ |
| 299 | Oid |
| 300 | get_default_partition_oid(Oid parentId) |
| 301 | { |
| 302 | HeapTuple tuple; |
| 303 | Oid defaultPartId = InvalidOid; |
| 304 | |
| 305 | tuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(parentId)); |
| 306 | |
| 307 | if (HeapTupleIsValid(tuple)) |
| 308 | { |
| 309 | Form_pg_partitioned_table part_table_form; |
| 310 | |
| 311 | part_table_form = (Form_pg_partitioned_table) GETSTRUCT(tuple); |
| 312 | defaultPartId = part_table_form->partdefid; |
| 313 | ReleaseSysCache(tuple); |
| 314 | } |
| 315 | |
| 316 | return defaultPartId; |
| 317 | } |
| 318 | |
| 319 | /* |
| 320 | * update_default_partition_oid |
| 321 | * |
| 322 | * Update pg_partitioned_table.partdefid with a new default partition OID. |
| 323 | */ |
| 324 | void |
| 325 | update_default_partition_oid(Oid parentId, Oid defaultPartId) |
| 326 | { |
| 327 | HeapTuple tuple; |
| 328 | Relation pg_partitioned_table; |
| 329 | Form_pg_partitioned_table part_table_form; |
| 330 | |
| 331 | pg_partitioned_table = table_open(PartitionedRelationId, RowExclusiveLock); |
| 332 | |
| 333 | tuple = SearchSysCacheCopy1(PARTRELID, ObjectIdGetDatum(parentId)); |
| 334 | |
| 335 | if (!HeapTupleIsValid(tuple)) |
| 336 | elog(ERROR, "cache lookup failed for partition key of relation %u" , |
| 337 | parentId); |
| 338 | |
| 339 | part_table_form = (Form_pg_partitioned_table) GETSTRUCT(tuple); |
| 340 | part_table_form->partdefid = defaultPartId; |
| 341 | CatalogTupleUpdate(pg_partitioned_table, &tuple->t_self, tuple); |
| 342 | |
| 343 | heap_freetuple(tuple); |
| 344 | table_close(pg_partitioned_table, RowExclusiveLock); |
| 345 | } |
| 346 | |
| 347 | /* |
| 348 | * get_proposed_default_constraint |
| 349 | * |
| 350 | * This function returns the negation of new_part_constraints, which |
| 351 | * would be an integral part of the default partition constraints after |
| 352 | * addition of the partition to which the new_part_constraints belongs. |
| 353 | */ |
| 354 | List * |
| 355 | get_proposed_default_constraint(List *new_part_constraints) |
| 356 | { |
| 357 | Expr *defPartConstraint; |
| 358 | |
| 359 | defPartConstraint = make_ands_explicit(new_part_constraints); |
| 360 | |
| 361 | /* |
| 362 | * Derive the partition constraints of default partition by negating the |
| 363 | * given partition constraints. The partition constraint never evaluates |
| 364 | * to NULL, so negating it like this is safe. |
| 365 | */ |
| 366 | defPartConstraint = makeBoolExpr(NOT_EXPR, |
| 367 | list_make1(defPartConstraint), |
| 368 | -1); |
| 369 | |
| 370 | /* Simplify, to put the negated expression into canonical form */ |
| 371 | defPartConstraint = |
| 372 | (Expr *) eval_const_expressions(NULL, |
| 373 | (Node *) defPartConstraint); |
| 374 | defPartConstraint = canonicalize_qual(defPartConstraint, true); |
| 375 | |
| 376 | return make_ands_implicit(defPartConstraint); |
| 377 | } |
| 378 | |