/* * Create append-only auxiliary relations for target relation rel. * Returns true if they are newly created. If pg_appendonly has already * known those tables, don't create them and returns false. */ bool CreateAOAuxiliaryTable( Relation rel, const char *auxiliaryNamePrefix, char relkind, TupleDesc tupledesc, IndexInfo *indexInfo, Oid *classObjectId, int16 *coloptions) { char aoauxiliary_relname[NAMEDATALEN]; char aoauxiliary_idxname[NAMEDATALEN]; bool shared_relation; Oid relOid, aoauxiliary_relid = InvalidOid; Oid aoauxiliary_idxid = InvalidOid; ObjectAddress baseobject; ObjectAddress aoauxiliaryobject; Assert(RelationIsValid(rel)); Assert(RelationIsAoRows(rel) || RelationIsAoCols(rel)); Assert(auxiliaryNamePrefix); Assert(tupledesc); Assert(classObjectId); if (relkind != RELKIND_AOSEGMENTS) Assert(indexInfo); shared_relation = rel->rd_rel->relisshared; /* * We cannot allow creating an auxiliary table for a shared relation * after initdb (because there's no way to let other databases know * this visibility map. */ if (shared_relation && !IsBootstrapProcessingMode()) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("shared tables cannot have append-only auxiliary relations after initdb"))); relOid = RelationGetRelid(rel); switch(relkind) { case RELKIND_AOVISIMAP: GetAppendOnlyEntryAuxOids(relOid, SnapshotNow, NULL, NULL, NULL, &aoauxiliary_relid, &aoauxiliary_idxid); break; case RELKIND_AOBLOCKDIR: GetAppendOnlyEntryAuxOids(relOid, SnapshotNow, NULL, &aoauxiliary_relid, &aoauxiliary_idxid, NULL, NULL); break; case RELKIND_AOSEGMENTS: GetAppendOnlyEntryAuxOids(relOid, SnapshotNow, &aoauxiliary_relid, NULL, NULL, NULL, NULL); break; default: elog(ERROR, "unsupported auxiliary relkind '%c'", relkind); } /* * Does it have the auxiliary relation? */ if (OidIsValid(aoauxiliary_relid)) { return false; } snprintf(aoauxiliary_relname, sizeof(aoauxiliary_relname), "%s_%u", auxiliaryNamePrefix, relOid); snprintf(aoauxiliary_idxname, sizeof(aoauxiliary_idxname), "%s_%u_index", auxiliaryNamePrefix, relOid); /* * We place auxiliary relation in the pg_aoseg namespace * even if its master relation is a temp table. There cannot be * any naming collision, and the auxiliary relation will be * destroyed when its master is, so there is no need to handle * the aovisimap relation as temp. */ aoauxiliary_relid = heap_create_with_catalog(aoauxiliary_relname, PG_AOSEGMENT_NAMESPACE, rel->rd_rel->reltablespace, InvalidOid, rel->rd_rel->relowner, tupledesc, /* relam */ InvalidOid, relkind, RELSTORAGE_HEAP, shared_relation, true, /* bufferPoolBulkLoad */ false, 0, ONCOMMIT_NOOP, NULL, /* GP Policy */ (Datum) 0, true, /* valid_opts */ false, /* persistentTid */ NULL, /* persistentSerialNum */ NULL); /* Make this table visible, else index creation will fail */ CommandCounterIncrement(); /* Create an index on AO auxiliary tables (like visimap) except for pg_aoseg table */ if (relkind != RELKIND_AOSEGMENTS) { aoauxiliary_idxid = index_create(aoauxiliary_relid, aoauxiliary_idxname, InvalidOid, indexInfo, BTREE_AM_OID, rel->rd_rel->reltablespace, classObjectId, coloptions, (Datum) 0, true, false, true, false, false, NULL); /* Unlock target table -- no one can see it */ UnlockRelationOid(aoauxiliary_relid, ShareLock); /* Unlock the index -- no one can see it anyway */ UnlockRelationOid(aoauxiliary_idxid, AccessExclusiveLock); } /* * Store the auxiliary table's OID in the parent relation's pg_appendonly row. * TODO (How to generalize this?) */ switch (relkind) { case RELKIND_AOVISIMAP: UpdateAppendOnlyEntryAuxOids(relOid, InvalidOid, InvalidOid, InvalidOid, aoauxiliary_relid, aoauxiliary_idxid); break; case RELKIND_AOBLOCKDIR: UpdateAppendOnlyEntryAuxOids(relOid, InvalidOid, aoauxiliary_relid, aoauxiliary_idxid, InvalidOid, InvalidOid); break; case RELKIND_AOSEGMENTS: UpdateAppendOnlyEntryAuxOids(relOid, aoauxiliary_relid, InvalidOid, InvalidOid, InvalidOid, InvalidOid); break; default: elog(ERROR, "unsupported auxiliary relkind '%c'", relkind); } /* * Register dependency from the auxiliary table to the master, so that the * aoseg table will be deleted if the master is. */ baseobject.classId = RelationRelationId; baseobject.objectId = relOid; baseobject.objectSubId = 0; aoauxiliaryobject.classId = RelationRelationId; aoauxiliaryobject.objectId = aoauxiliary_relid; aoauxiliaryobject.objectSubId = 0; recordDependencyOn(&aoauxiliaryobject, &baseobject, DEPENDENCY_INTERNAL); /* * Make changes visible */ CommandCounterIncrement(); return true; }
/* * create_toast_table --- internal workhorse * * rel is already opened and locked * toastOid and toastIndexOid are normally InvalidOid, but during * bootstrap they can be nonzero to specify hand-assigned OIDs */ static bool create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptions, LOCKMODE lockmode, bool check) { Oid relOid = RelationGetRelid(rel); HeapTuple reltup; TupleDesc tupdesc; bool shared_relation; bool mapped_relation; Relation toast_rel; Relation class_rel; Oid toast_relid; Oid toast_typid = InvalidOid; Oid namespaceid; char toast_relname[NAMEDATALEN]; char toast_idxname[NAMEDATALEN]; IndexInfo *indexInfo; Oid collationObjectId[2]; Oid classObjectId[2]; int16 coloptions[2]; ObjectAddress baseobject, toastobject; /* * Toast table is shared if and only if its parent is. * * We cannot allow toasting a shared relation after initdb (because * there's no way to mark it toasted in other databases' pg_class). */ shared_relation = rel->rd_rel->relisshared; if (shared_relation && !IsBootstrapProcessingMode()) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("shared tables cannot be toasted after initdb"))); /* It's mapped if and only if its parent is, too */ mapped_relation = RelationIsMapped(rel); /* * Is it already toasted? */ if (rel->rd_rel->reltoastrelid != InvalidOid) return false; /* * Check to see whether the table actually needs a TOAST table. */ if (!IsBinaryUpgrade) { /* Normal mode, normal check */ if (!needs_toast_table(rel)) return false; } else { /* * In binary-upgrade mode, create a TOAST table if and only if * pg_upgrade told us to (ie, a TOAST table OID has been provided). * * This indicates that the old cluster had a TOAST table for the * current table. We must create a TOAST table to receive the old * TOAST file, even if the table seems not to need one. * * Contrariwise, if the old cluster did not have a TOAST table, we * should be able to get along without one even if the new version's * needs_toast_table rules suggest we should have one. There is a lot * of daylight between where we will create a TOAST table and where * one is really necessary to avoid failures, so small cross-version * differences in the when-to-create heuristic shouldn't be a problem. * If we tried to create a TOAST table anyway, we would have the * problem that it might take up an OID that will conflict with some * old-cluster table we haven't seen yet. */ if (!OidIsValid(binary_upgrade_next_toast_pg_class_oid) || !OidIsValid(binary_upgrade_next_toast_pg_type_oid)) return false; } /* * If requested check lockmode is sufficient. This is a cross check in * case of errors or conflicting decisions in earlier code. */ if (check && lockmode != AccessExclusiveLock) elog(ERROR, "AccessExclusiveLock required to add toast table."); /* * Create the toast table and its index */ snprintf(toast_relname, sizeof(toast_relname), "pg_toast_%u", relOid); snprintf(toast_idxname, sizeof(toast_idxname), "pg_toast_%u_index", relOid); /* this is pretty painful... need a tuple descriptor */ tupdesc = CreateTemplateTupleDesc(3); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "chunk_id", OIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "chunk_seq", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 3, "chunk_data", BYTEAOID, -1, 0); /* * Ensure that the toast table doesn't itself get toasted, or we'll be * toast :-(. This is essential for chunk_data because type bytea is * toastable; hit the other two just to be sure. */ TupleDescAttr(tupdesc, 0)->attstorage = 'p'; TupleDescAttr(tupdesc, 1)->attstorage = 'p'; TupleDescAttr(tupdesc, 2)->attstorage = 'p'; /* * Toast tables for regular relations go in pg_toast; those for temp * relations go into the per-backend temp-toast-table namespace. */ if (isTempOrTempToastNamespace(rel->rd_rel->relnamespace)) namespaceid = GetTempToastNamespace(); else namespaceid = PG_TOAST_NAMESPACE; /* * Use binary-upgrade override for pg_type.oid, if supplied. We might be * in the post-schema-restore phase where we are doing ALTER TABLE to * create TOAST tables that didn't exist in the old cluster. */ if (IsBinaryUpgrade && OidIsValid(binary_upgrade_next_toast_pg_type_oid)) { toast_typid = binary_upgrade_next_toast_pg_type_oid; binary_upgrade_next_toast_pg_type_oid = InvalidOid; } toast_relid = heap_create_with_catalog(toast_relname, namespaceid, rel->rd_rel->reltablespace, toastOid, toast_typid, InvalidOid, rel->rd_rel->relowner, tupdesc, NIL, RELKIND_TOASTVALUE, rel->rd_rel->relpersistence, shared_relation, mapped_relation, ONCOMMIT_NOOP, reloptions, false, true, true, InvalidOid, NULL); Assert(toast_relid != InvalidOid); /* make the toast relation visible, else heap_open will fail */ CommandCounterIncrement(); /* ShareLock is not really needed here, but take it anyway */ toast_rel = heap_open(toast_relid, ShareLock); /* * Create unique index on chunk_id, chunk_seq. * * NOTE: the normal TOAST access routines could actually function with a * single-column index on chunk_id only. However, the slice access * routines use both columns for faster access to an individual chunk. In * addition, we want it to be unique as a check against the possibility of * duplicate TOAST chunk OIDs. The index might also be a little more * efficient this way, since btree isn't all that happy with large numbers * of equal keys. */ indexInfo = makeNode(IndexInfo); indexInfo->ii_NumIndexAttrs = 2; indexInfo->ii_NumIndexKeyAttrs = 2; indexInfo->ii_IndexAttrNumbers[0] = 1; indexInfo->ii_IndexAttrNumbers[1] = 2; indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = NIL; indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; indexInfo->ii_Unique = true; indexInfo->ii_ReadyForInserts = true; indexInfo->ii_Concurrent = false; indexInfo->ii_BrokenHotChain = false; indexInfo->ii_ParallelWorkers = 0; indexInfo->ii_Am = BTREE_AM_OID; indexInfo->ii_AmCache = NULL; indexInfo->ii_Context = CurrentMemoryContext; collationObjectId[0] = InvalidOid; collationObjectId[1] = InvalidOid; classObjectId[0] = OID_BTREE_OPS_OID; classObjectId[1] = INT4_BTREE_OPS_OID; coloptions[0] = 0; coloptions[1] = 0; index_create(toast_rel, toast_idxname, toastIndexOid, InvalidOid, InvalidOid, InvalidOid, indexInfo, list_make2("chunk_id", "chunk_seq"), BTREE_AM_OID, rel->rd_rel->reltablespace, collationObjectId, classObjectId, coloptions, (Datum) 0, INDEX_CREATE_IS_PRIMARY, 0, true, true, NULL); heap_close(toast_rel, NoLock); /* * Store the toast table's OID in the parent relation's pg_class row */ class_rel = heap_open(RelationRelationId, RowExclusiveLock); reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relOid)); if (!HeapTupleIsValid(reltup)) elog(ERROR, "cache lookup failed for relation %u", relOid); ((Form_pg_class) GETSTRUCT(reltup))->reltoastrelid = toast_relid; if (!IsBootstrapProcessingMode()) { /* normal case, use a transactional update */ CatalogTupleUpdate(class_rel, &reltup->t_self, reltup); } else { /* While bootstrapping, we cannot UPDATE, so overwrite in-place */ heap_inplace_update(class_rel, reltup); } heap_freetuple(reltup); heap_close(class_rel, RowExclusiveLock); /* * Register dependency from the toast table to the master, so that the * toast table will be deleted if the master is. Skip this in bootstrap * mode. */ if (!IsBootstrapProcessingMode()) { baseobject.classId = RelationRelationId; baseobject.objectId = relOid; baseobject.objectSubId = 0; toastobject.classId = RelationRelationId; toastobject.objectId = toast_relid; toastobject.objectSubId = 0; recordDependencyOn(&toastobject, &baseobject, DEPENDENCY_INTERNAL); } /* * Make changes visible */ CommandCounterIncrement(); return true; }
/* * create_toast_table --- internal workhorse * * rel is already opened and exclusive-locked * toastOid and toastIndexOid are normally InvalidOid, but during * bootstrap they can be nonzero to specify hand-assigned OIDs */ static bool create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, bool is_part_child) { Oid relOid = RelationGetRelid(rel); HeapTuple reltup; TupleDesc tupdesc; bool shared_relation; Relation class_rel; Oid toast_relid; Oid toast_idxid; Oid namespaceid; char toast_relname[NAMEDATALEN]; char toast_idxname[NAMEDATALEN]; IndexInfo *indexInfo; Oid classObjectId[2]; int16 coloptions[2]; ObjectAddress baseobject, toastobject; /* * Is it already toasted? */ if (rel->rd_rel->reltoastrelid != InvalidOid) return false; /* * Check to see whether the table actually needs a TOAST table. */ if (!RelationNeedsToastTable(rel)) return false; /* * Toast table is shared if and only if its parent is. * * We cannot allow toasting a shared relation after initdb (because * there's no way to mark it toasted in other databases' pg_class). */ shared_relation = rel->rd_rel->relisshared; if (shared_relation && !IsBootstrapProcessingMode()) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("shared tables cannot be toasted after initdb"))); /* * Create the toast table and its index */ snprintf(toast_relname, sizeof(toast_relname), "pg_toast_%u", relOid); snprintf(toast_idxname, sizeof(toast_idxname), "pg_toast_%u_index", relOid); /* this is pretty painful... need a tuple descriptor */ tupdesc = CreateTemplateTupleDesc(3, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "chunk_id", OIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "chunk_seq", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 3, "chunk_data", BYTEAOID, -1, 0); /* * Ensure that the toast table doesn't itself get toasted, or we'll be * toast :-(. This is essential for chunk_data because type bytea is * toastable; hit the other two just to be sure. */ tupdesc->attrs[0]->attstorage = 'p'; tupdesc->attrs[1]->attstorage = 'p'; tupdesc->attrs[2]->attstorage = 'p'; /* * Toast tables for regular relations go in pg_toast; those for temp * relations go into the per-backend temp-toast-table namespace. */ if (rel->rd_istemp) namespaceid = GetTempToastNamespace(); else namespaceid = PG_TOAST_NAMESPACE; /* * XXX would it make sense to apply the master's reloptions to the toast * table? Or maybe some toast-specific reloptions? */ toast_relid = heap_create_with_catalog(toast_relname, namespaceid, rel->rd_rel->reltablespace, toastOid, rel->rd_rel->relowner, tupdesc, /* relam */ InvalidOid, RELKIND_TOASTVALUE, RELSTORAGE_HEAP, shared_relation, true, /* bufferPoolBulkLoad */ false, 0, ONCOMMIT_NOOP, NULL, /* CDB POLICY */ (Datum) 0, true, /* valid_opts */ false, /* persistentTid */ NULL, /* persistentSerialNum */ NULL); /* make the toast relation visible, else index creation will fail */ CommandCounterIncrement(); /* * Create unique index on chunk_id, chunk_seq. * * NOTE: the normal TOAST access routines could actually function with a * single-column index on chunk_id only. However, the slice access * routines use both columns for faster access to an individual chunk. In * addition, we want it to be unique as a check against the possibility of * duplicate TOAST chunk OIDs. The index might also be a little more * efficient this way, since btree isn't all that happy with large numbers * of equal keys. */ indexInfo = makeNode(IndexInfo); indexInfo->ii_NumIndexAttrs = 2; indexInfo->ii_KeyAttrNumbers[0] = 1; indexInfo->ii_KeyAttrNumbers[1] = 2; indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = NIL; indexInfo->ii_PredicateState = NIL; indexInfo->ii_Unique = true; indexInfo->ii_ReadyForInserts = true; indexInfo->ii_Concurrent = false; indexInfo->ii_BrokenHotChain = false; classObjectId[0] = OID_BTREE_OPS_OID; classObjectId[1] = INT4_BTREE_OPS_OID; coloptions[0] = 0; coloptions[1] = 0; toast_idxid = index_create(toast_relid, toast_idxname, toastIndexOid, indexInfo, BTREE_AM_OID, rel->rd_rel->reltablespace, classObjectId, coloptions, (Datum) 0, true, false, true, false, false, NULL); /* * If this is a partitioned child, we can unlock since the master is * already locked. */ if (is_part_child) { UnlockRelationOid(toast_relid, ShareLock); UnlockRelationOid(toast_idxid, AccessExclusiveLock); } /* * Store the toast table's OID in the parent relation's pg_class row */ class_rel = heap_open(RelationRelationId, RowExclusiveLock); reltup = SearchSysCacheCopy(RELOID, ObjectIdGetDatum(relOid), 0, 0, 0); if (!HeapTupleIsValid(reltup)) elog(ERROR, "cache lookup failed for relation %u", relOid); ((Form_pg_class) GETSTRUCT(reltup))->reltoastrelid = toast_relid; if (!IsBootstrapProcessingMode()) { /* normal case, use a transactional update */ simple_heap_update(class_rel, &reltup->t_self, reltup); /* Keep catalog indexes current */ CatalogUpdateIndexes(class_rel, reltup); } else { /* While bootstrapping, we cannot UPDATE, so overwrite in-place */ heap_inplace_update(class_rel, reltup); } heap_freetuple(reltup); heap_close(class_rel, RowExclusiveLock); /* * Register dependency from the toast table to the master, so that the * toast table will be deleted if the master is. Skip this in bootstrap * mode. */ if (!IsBootstrapProcessingMode()) { baseobject.classId = RelationRelationId; baseobject.objectId = relOid; baseobject.objectSubId = 0; toastobject.classId = RelationRelationId; toastobject.objectId = toast_relid; toastobject.objectSubId = 0; recordDependencyOn(&toastobject, &baseobject, DEPENDENCY_INTERNAL); } /* * Make changes visible */ CommandCounterIncrement(); return true; }
/* * create_toast_table --- internal workhorse * * rel is already opened and locked * toastOid and toastIndexOid are normally InvalidOid, but during * bootstrap they can be nonzero to specify hand-assigned OIDs */ static bool create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptions, LOCKMODE lockmode, bool check) { Oid relOid = RelationGetRelid(rel); HeapTuple reltup; TupleDesc tupdesc; bool shared_relation; bool mapped_relation; Relation toast_rel; Relation class_rel; Oid toast_relid; Oid toast_typid = InvalidOid; Oid namespaceid; char toast_relname[NAMEDATALEN]; char toast_idxname[NAMEDATALEN]; IndexInfo *indexInfo; Oid collationObjectId[2]; Oid classObjectId[2]; int16 coloptions[2]; ObjectAddress baseobject, toastobject; /* * Toast table is shared if and only if its parent is. * * We cannot allow toasting a shared relation after initdb (because * there's no way to mark it toasted in other databases' pg_class). */ shared_relation = rel->rd_rel->relisshared; if (shared_relation && !IsBootstrapProcessingMode()) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("shared tables cannot be toasted after initdb"))); /* It's mapped if and only if its parent is, too */ mapped_relation = RelationIsMapped(rel); /* * Is it already toasted? */ if (rel->rd_rel->reltoastrelid != InvalidOid) return false; if (!IsBinaryUpgrade) { if (!needs_toast_table(rel)) return false; } else { /* * Check to see whether the table needs a TOAST table. * * If an update-in-place TOAST relfilenode is specified, force TOAST file * creation even if it seems not to need one. This handles the case * where the old cluster needed a TOAST table but the new cluster * would not normally create one. */ /* * If a TOAST oid is not specified, skip TOAST creation as we will do * it later so we don't create a TOAST table whose OID later conflicts * with a user-supplied OID. This handles cases where the old cluster * didn't need a TOAST table, but the new cluster does. */ if (!OidIsValid(binary_upgrade_next_toast_pg_class_oid)) return false; /* * If a special TOAST value has been passed in, it means we are in * cleanup mode --- we are creating needed TOAST tables after all user * tables with specified OIDs have been created. We let the system * assign a TOAST oid for us. The tables are empty so the missing * TOAST tables were not a problem. */ if (binary_upgrade_next_toast_pg_class_oid == OPTIONALLY_CREATE_TOAST_OID) { /* clear as it is not to be used; it is just a flag */ binary_upgrade_next_toast_pg_class_oid = InvalidOid; if (!needs_toast_table(rel)) return false; } /* both should be set, or not set */ Assert(OidIsValid(binary_upgrade_next_toast_pg_class_oid) == OidIsValid(binary_upgrade_next_toast_pg_type_oid)); } /* * If requested check lockmode is sufficient. This is a cross check in * case of errors or conflicting decisions in earlier code. */ if (check && lockmode != AccessExclusiveLock) elog(ERROR, "AccessExclusiveLock required to add toast table."); /* * Create the toast table and its index */ snprintf(toast_relname, sizeof(toast_relname), "pg_toast_%u", relOid); snprintf(toast_idxname, sizeof(toast_idxname), "pg_toast_%u_index", relOid); /* this is pretty painful... need a tuple descriptor */ tupdesc = CreateTemplateTupleDesc(3, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "chunk_id", OIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "chunk_seq", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 3, "chunk_data", BYTEAOID, -1, 0); /* * Ensure that the toast table doesn't itself get toasted, or we'll be * toast :-(. This is essential for chunk_data because type bytea is * toastable; hit the other two just to be sure. */ tupdesc->attrs[0]->attstorage = 'p'; tupdesc->attrs[1]->attstorage = 'p'; tupdesc->attrs[2]->attstorage = 'p'; /* * Toast tables for regular relations go in pg_toast; those for temp * relations go into the per-backend temp-toast-table namespace. */ if (isTempOrToastNamespace(rel->rd_rel->relnamespace)) namespaceid = GetTempToastNamespace(); else namespaceid = PG_TOAST_NAMESPACE; /* Use binary-upgrade override for pg_type.oid, if supplied. */ if (IsBinaryUpgrade && OidIsValid(binary_upgrade_next_toast_pg_type_oid)) { toast_typid = binary_upgrade_next_toast_pg_type_oid; binary_upgrade_next_toast_pg_type_oid = InvalidOid; } toast_relid = heap_create_with_catalog(toast_relname, namespaceid, rel->rd_rel->reltablespace, toastOid, toast_typid, InvalidOid, rel->rd_rel->relowner, tupdesc, NIL, RELKIND_TOASTVALUE, rel->rd_rel->relpersistence, shared_relation, mapped_relation, true, 0, ONCOMMIT_NOOP, reloptions, false, true, true); Assert(toast_relid != InvalidOid); /* make the toast relation visible, else heap_open will fail */ CommandCounterIncrement(); /* ShareLock is not really needed here, but take it anyway */ toast_rel = heap_open(toast_relid, ShareLock); /* * Create unique index on chunk_id, chunk_seq. * * NOTE: the normal TOAST access routines could actually function with a * single-column index on chunk_id only. However, the slice access * routines use both columns for faster access to an individual chunk. In * addition, we want it to be unique as a check against the possibility of * duplicate TOAST chunk OIDs. The index might also be a little more * efficient this way, since btree isn't all that happy with large numbers * of equal keys. */ indexInfo = makeNode(IndexInfo); indexInfo->ii_NumIndexAttrs = 2; indexInfo->ii_KeyAttrNumbers[0] = 1; indexInfo->ii_KeyAttrNumbers[1] = 2; indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = NIL; indexInfo->ii_PredicateState = NIL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; indexInfo->ii_Unique = true; indexInfo->ii_ReadyForInserts = true; indexInfo->ii_Concurrent = false; indexInfo->ii_BrokenHotChain = false; collationObjectId[0] = InvalidOid; collationObjectId[1] = InvalidOid; classObjectId[0] = OID_BTREE_OPS_OID; classObjectId[1] = INT4_BTREE_OPS_OID; coloptions[0] = 0; coloptions[1] = 0; index_create(toast_rel, toast_idxname, toastIndexOid, InvalidOid, indexInfo, list_make2("chunk_id", "chunk_seq"), BTREE_AM_OID, rel->rd_rel->reltablespace, collationObjectId, classObjectId, coloptions, (Datum) 0, true, false, false, false, true, false, false, true); heap_close(toast_rel, NoLock); /* * Store the toast table's OID in the parent relation's pg_class row */ class_rel = heap_open(RelationRelationId, RowExclusiveLock); reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relOid)); if (!HeapTupleIsValid(reltup)) elog(ERROR, "cache lookup failed for relation %u", relOid); ((Form_pg_class) GETSTRUCT(reltup))->reltoastrelid = toast_relid; if (!IsBootstrapProcessingMode()) { /* normal case, use a transactional update */ simple_heap_update(class_rel, &reltup->t_self, reltup); /* Keep catalog indexes current */ CatalogUpdateIndexes(class_rel, reltup); } else { /* While bootstrapping, we cannot UPDATE, so overwrite in-place */ heap_inplace_update(class_rel, reltup); } heap_freetuple(reltup); heap_close(class_rel, RowExclusiveLock); /* * Register dependency from the toast table to the master, so that the * toast table will be deleted if the master is. Skip this in bootstrap * mode. */ if (!IsBootstrapProcessingMode()) { baseobject.classId = RelationRelationId; baseobject.objectId = relOid; baseobject.objectSubId = 0; toastobject.classId = RelationRelationId; toastobject.objectId = toast_relid; toastobject.objectSubId = 0; recordDependencyOn(&toastobject, &baseobject, DEPENDENCY_INTERNAL); } /* * Make changes visible */ CommandCounterIncrement(); return true; }
void _bitmap_create_lov_heapandindex(Relation rel, Oid lovComptypeOid, Oid *lovHeapOid, Oid *lovIndexOid, Oid lovHeapRelfilenode, Oid lovIndexRelfilenode) { char lovHeapName[NAMEDATALEN]; char lovIndexName[NAMEDATALEN]; TupleDesc tupDesc; IndexInfo *indexInfo; ObjectAddress objAddr, referenced; Oid *classObjectId; int16 *coloptions; Oid heapid; Oid idxid; int indattrs; int i; Oid unusedArrayOid = InvalidOid; Assert(rel != NULL); /* create the new names for the new lov heap and index */ snprintf(lovHeapName, sizeof(lovHeapName), "pg_bm_%u", RelationGetRelid(rel)); snprintf(lovIndexName, sizeof(lovIndexName), "pg_bm_%u_index", RelationGetRelid(rel)); heapid = get_relname_relid(lovHeapName, PG_BITMAPINDEX_NAMESPACE); /* * If heapid exists, then this is happening during re-indexing. * We allocate new relfilenodes for lov heap and lov index. * * XXX Each segment db may have different relfilenodes for lov heap and * lov index, which should not be an issue now. Ideally, we would like each * segment db use the same oids. */ if (OidIsValid(heapid)) { Relation lovHeap; Relation lovIndex; Buffer btree_metabuf; Page btree_metapage; *lovHeapOid = heapid; idxid = get_relname_relid(lovIndexName, PG_BITMAPINDEX_NAMESPACE); Assert(OidIsValid(idxid)); *lovIndexOid = idxid; lovComptypeOid = get_rel_type_id(heapid); Assert(OidIsValid(lovComptypeOid)); lovHeap = heap_open(heapid, AccessExclusiveLock); lovIndex = index_open(idxid, AccessExclusiveLock); if (OidIsValid(lovHeapRelfilenode)) setNewRelfilenodeToOid(lovHeap, lovHeapRelfilenode); else setNewRelfilenode(lovHeap); if (OidIsValid(lovIndexRelfilenode)) setNewRelfilenodeToOid(lovIndex, lovIndexRelfilenode); else setNewRelfilenode(lovIndex); /* * After creating the new relfilenode for a btee index, this is not * a btree anymore. We create the new metapage for this btree. */ btree_metabuf = _bt_getbuf(lovIndex, P_NEW, BT_WRITE); Assert (BTREE_METAPAGE == BufferGetBlockNumber(btree_metabuf)); btree_metapage = BufferGetPage(btree_metabuf); _bt_initmetapage(btree_metapage, P_NONE, 0); /* XLOG the metapage */ if (!XLog_UnconvertedCanBypassWal() && !lovIndex->rd_istemp) { // Fetch gp_persistent_relation_node information that will be added to XLOG record. RelationFetchGpRelationNodeForXLog(lovIndex); _bt_lognewpage(lovIndex, btree_metapage, BufferGetBlockNumber(btree_metabuf)); } /* This cache value is not valid anymore. */ if (lovIndex->rd_amcache) { pfree(lovIndex->rd_amcache); lovIndex->rd_amcache = NULL; } MarkBufferDirty(btree_metabuf); _bt_relbuf(lovIndex, btree_metabuf); index_close(lovIndex, NoLock); heap_close(lovHeap, NoLock); return; } /* * create a new empty heap to store all attribute values with their * corresponding block number and offset in LOV. */ tupDesc = _bitmap_create_lov_heapTupleDesc(rel); Assert(rel->rd_rel != NULL); heapid = heap_create_with_catalog(lovHeapName, PG_BITMAPINDEX_NAMESPACE, rel->rd_rel->reltablespace, *lovHeapOid, rel->rd_rel->relowner, tupDesc, /* relam */ InvalidOid, RELKIND_RELATION, RELSTORAGE_HEAP, rel->rd_rel->relisshared, false, /* bufferPoolBulkLoad */ false, 0, ONCOMMIT_NOOP, NULL /* GP Policy */, (Datum)0, true, /* valid_opts */ true, &lovComptypeOid, &unusedArrayOid, /* persistentTid */ NULL, /* persistentSerialNum */ NULL); Assert(heapid == *lovHeapOid); /* * We must bump the command counter to make the newly-created relation * tuple visible for opening. */ CommandCounterIncrement(); objAddr.classId = RelationRelationId; objAddr.objectId = *lovHeapOid; objAddr.objectSubId = 0 ; referenced.classId = RelationRelationId; referenced.objectId = RelationGetRelid(rel); referenced.objectSubId = 0; recordDependencyOn(&objAddr, &referenced, DEPENDENCY_INTERNAL); /* * create a btree index on the newly-created heap. * The key includes all attributes to be indexed in this bitmap index. */ indattrs = tupDesc->natts - 2; indexInfo = makeNode(IndexInfo); indexInfo->ii_NumIndexAttrs = indattrs; indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = make_ands_implicit(NULL); indexInfo->ii_PredicateState = NIL; indexInfo->ii_Unique = true; indexInfo->opaque = NULL; classObjectId = (Oid *) palloc(indattrs * sizeof(Oid)); coloptions = (int16 *) palloc(indattrs * sizeof(int16)); for (i = 0; i < indattrs; i++) { Oid typid = tupDesc->attrs[i]->atttypid; indexInfo->ii_KeyAttrNumbers[i] = i + 1; classObjectId[i] = GetDefaultOpClass(typid, BTREE_AM_OID); coloptions[i] = 0; } idxid = index_create(*lovHeapOid, lovIndexName, *lovIndexOid, indexInfo, BTREE_AM_OID, rel->rd_rel->reltablespace, classObjectId, coloptions, 0, false, false, (Oid *) NULL, true, false, false, NULL); Assert(idxid == *lovIndexOid); }
/* * create_aoblkdir_table * * rel is already opened and exclusive-locked. * comptypeOid is InvalidOid. */ static bool create_aoblkdir_table(Relation rel, Oid aoblkdirOid, Oid aoblkdirIndexOid, Oid *comptypeOid) { Oid relOid = RelationGetRelid(rel); Oid aoblkdir_relid; Oid aoblkdir_idxid; bool shared_relation = rel->rd_rel->relisshared; char aoblkdir_relname[NAMEDATALEN]; char aoblkdir_idxname[NAMEDATALEN]; TupleDesc tupdesc; IndexInfo *indexInfo; Oid classObjectId[3]; ObjectAddress baseobject; ObjectAddress aoblkdirobject; Oid tablespaceOid = ChooseTablespaceForLimitedObject(rel->rd_rel->reltablespace); if (!RelationIsAoRows(rel)) return false; /* * We cannot allow creating a block directory for a shared relation * after initdb (because there's no way to let other databases know * this block directory. */ if (shared_relation && !IsBootstrapProcessingMode()) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("shared tables cannot have block directory after initdb"))); GetAppendOnlyEntryAuxOids(relOid, SnapshotNow, NULL,NULL, &aoblkdir_relid, &aoblkdir_idxid); /* * Does it have a block directory? */ if (aoblkdir_relid != InvalidOid) { return false; } snprintf(aoblkdir_relname, sizeof(aoblkdir_relname), "pg_aoblkdir_%u", relOid); snprintf(aoblkdir_idxname, sizeof(aoblkdir_idxname), "pg_aoblkdir_%u_index", relOid); /* Create a tuple descriptor */ tupdesc = CreateTemplateTupleDesc(4, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "segno", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "columngroup_no", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 3, "first_row_no", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 4, "minipage", VARBITOID, -1, 0); /* * We don't want any toast columns here. */ tupdesc->attrs[0]->attstorage = 'p'; tupdesc->attrs[1]->attstorage = 'p'; tupdesc->attrs[2]->attstorage = 'p'; tupdesc->attrs[2]->attstorage = 'p'; /* * We place aoblkdir relation in the pg_aoseg namespace * even if its master relation is a temp table. There cannot be * any naming collision, and the aoblkdir relation will be * destroyed when its master is, so there is no need to handle * the aoblkdir relation as temp. */ aoblkdir_relid = heap_create_with_catalog(aoblkdir_relname, PG_AOSEGMENT_NAMESPACE, tablespaceOid, aoblkdirOid, rel->rd_rel->relowner, tupdesc, /* relam */ InvalidOid, RELKIND_AOBLOCKDIR, RELSTORAGE_HEAP, shared_relation, true, /* bufferPoolBulkLoad */ false, 0, ONCOMMIT_NOOP, NULL, /* GP Policy */ (Datum) 0, true, comptypeOid, /* persistentTid */ NULL, /* persistentSerialNum */ NULL); /* Make this table visible, else index creation will fail */ CommandCounterIncrement(); /* * Create index on segno, first_row_no. */ indexInfo = makeNode(IndexInfo); indexInfo->ii_NumIndexAttrs = 3; indexInfo->ii_KeyAttrNumbers[0] = 1; indexInfo->ii_KeyAttrNumbers[1] = 2; indexInfo->ii_KeyAttrNumbers[2] = 3; indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = NIL; indexInfo->ii_PredicateState = NIL; indexInfo->ii_Unique = false; indexInfo->ii_Concurrent = false; classObjectId[0] = INT4_BTREE_OPS_OID; classObjectId[1] = INT4_BTREE_OPS_OID; classObjectId[2] = INT8_BTREE_OPS_OID; aoblkdir_idxid = index_create(aoblkdirOid, aoblkdir_idxname, aoblkdirIndexOid, indexInfo, BTREE_AM_OID, tablespaceOid, classObjectId, (Datum) 0, true, false, (Oid *) NULL, true, false, false, NULL); /* Unlock target table -- no one can see it */ UnlockRelationOid(aoblkdirOid, ShareLock); /* Unlock the index -- no one can see it anyway */ UnlockRelationOid(aoblkdirIndexOid, AccessExclusiveLock); /* * Store the aoblkdir table's OID in the parent relation's pg_appendonly row. */ UpdateAppendOnlyEntryAuxOids(relOid, InvalidOid, InvalidOid, aoblkdir_relid, aoblkdir_idxid); /* * Register dependency from the aoseg table to the master, so that the * aoseg table will be deleted if the master is. */ baseobject.classId = RelationRelationId; baseobject.objectId = relOid; baseobject.objectSubId = 0; aoblkdirobject.classId = RelationRelationId; aoblkdirobject.objectId = aoblkdirOid; aoblkdirobject.objectSubId = 0; recordDependencyOn(&aoblkdirobject, &baseobject, DEPENDENCY_INTERNAL); /* * Make changes visible */ CommandCounterIncrement(); return true; }