/* * RemoveConversionById * * Remove a tuple from pg_conversion by Oid. This function is solely * called inside catalog/dependency.c */ void RemoveConversionById(Oid conversionOid) { Relation rel; HeapTuple tuple; HeapScanDesc scan; ScanKeyData scanKeyData; ScanKeyInit(&scanKeyData, ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(conversionOid)); /* open pg_conversion */ rel = heap_open(ConversionRelationId, RowExclusiveLock); scan = heap_beginscan_catalog(rel, 1, &scanKeyData); /* search for the target tuple */ if (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection))) simple_heap_delete(rel, &tuple->t_self); else elog(ERROR, "could not find tuple for conversion %u", conversionOid); heap_endscan(scan); heap_close(rel, RowExclusiveLock); }
/* * Gets list of all relation published by FOR ALL TABLES publication(s). */ List * GetAllTablesPublicationRelations(void) { Relation classRel; ScanKeyData key[1]; HeapScanDesc scan; HeapTuple tuple; List *result = NIL; classRel = heap_open(RelationRelationId, AccessShareLock); ScanKeyInit(&key[0], Anum_pg_class_relkind, BTEqualStrategyNumber, F_CHAREQ, CharGetDatum(RELKIND_RELATION)); scan = heap_beginscan_catalog(classRel, 1, key); while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple); Oid relid = relForm->oid; if (is_publishable_class(relid, relForm)) result = lappend_oid(result, relid); } heap_endscan(scan); heap_close(classRel, AccessShareLock); return result; }
/* * get_next_id * * Gets the smallest possible id to assign to the next continuous view. * We keep this minimal so that we can minimize the size of bitmaps used * to tag stream buffer events with. */ static int32 get_next_id(Relation rel) { HeapScanDesc scandesc; HeapTuple tup; int32 id = 1; List *idsList = NIL; ListCell *lc; scandesc = heap_beginscan_catalog(rel, 0, NULL); while ((tup = heap_getnext(scandesc, ForwardScanDirection)) != NULL) { Form_pipeline_query row = (Form_pipeline_query) GETSTRUCT(tup); idsList = lappend_int(idsList, row->id); } heap_endscan(scandesc); if (idsList != NIL) { int32 ids[idsList->length]; int i = 0; foreach(lc, idsList) { ids[i] = lfirst_int(lc); i++; }
/** * @brief Get the pg class tuple * @param tuple relevant tuple if it exists, NULL otherwise */ HeapTuple Bridge::GetPGClassTupleForRelationName(const char *relation_name) { LOG_WARN("Do not use bridge function(%s) in Peloton !!! ", __func__); Relation pg_class_rel; HeapTuple tuple = NULL; HeapScanDesc scan; // Open pg_class table pg_class_rel = heap_open(RelationRelationId, AccessShareLock); // Search the pg_class table with given relation name scan = heap_beginscan_catalog(pg_class_rel, 0, NULL); while (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection))) { Form_pg_class pgclass = (Form_pg_class)GETSTRUCT(tuple); if (pgclass->relnamespace == PG_PUBLIC_NAMESPACE) { if (strcmp(NameStr(pgclass->relname), relation_name) == 0) { // We need to end scan and close heap break; } } } heap_endscan(scan); heap_close(pg_class_rel, AccessShareLock); return tuple; }
/* * worker_cleanup_job_schema_cache walks over all schemas in the database, and * removes schemas whose names start with the job schema prefix. Note that this * function does not perform any locking; we expect it to be called at process * start-up time before any merge tasks are run. Further note that this function * runs within the scope of a particular database (template1, postgres) and can * only delete schemas within that database. */ Datum worker_cleanup_job_schema_cache(PG_FUNCTION_ARGS) { Relation pgNamespace = NULL; HeapScanDesc scanDescriptor = NULL; ScanKey scanKey = NULL; int scanKeyCount = 0; HeapTuple heapTuple = NULL; pgNamespace = heap_open(NamespaceRelationId, AccessExclusiveLock); scanDescriptor = heap_beginscan_catalog(pgNamespace, scanKeyCount, scanKey); heapTuple = heap_getnext(scanDescriptor, ForwardScanDirection); while (HeapTupleIsValid(heapTuple)) { Form_pg_namespace schemaForm = (Form_pg_namespace) GETSTRUCT(heapTuple); char *schemaName = NameStr(schemaForm->nspname); char *jobSchemaFound = strstr(schemaName, JOB_SCHEMA_PREFIX); if (jobSchemaFound != NULL) { StringInfo jobSchemaName = makeStringInfo(); appendStringInfoString(jobSchemaName, schemaName); RemoveJobSchema(jobSchemaName); } heapTuple = heap_getnext(scanDescriptor, ForwardScanDirection); } heap_endscan(scanDescriptor); heap_close(pgNamespace, AccessExclusiveLock); PG_RETURN_VOID(); }
/* * get_database_oids * * Returns a list of all database OIDs found in pg_database. */ static List * get_database_list(void) { List *dbs = NIL; Relation rel; HeapScanDesc scan; HeapTuple tup; MemoryContext resultcxt; /* This is the context that we will allocate our output data in */ resultcxt = CurrentMemoryContext; /* * Start a transaction so we can access pg_database, and get a snapshot. * We don't have a use for the snapshot itself, but we're interested in * the secondary effect that it sets RecentGlobalXmin. (This is critical * for anything that reads heap pages, because HOT may decide to prune * them even if the process doesn't attempt to modify any tuples.) */ StartTransactionCommand(); (void) GetTransactionSnapshot(); /* We take a AccessExclusiveLock so we don't conflict with any DATABASE commands */ rel = heap_open(DatabaseRelationId, AccessExclusiveLock); scan = heap_beginscan_catalog(rel, 0, NULL); while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection))) { MemoryContext oldcxt; Form_pg_database pgdatabase = (Form_pg_database) GETSTRUCT(tup); DatabaseEntry *db_entry; /* Ignore template databases or ones that don't allow connections. */ if (pgdatabase->datistemplate || !pgdatabase->datallowconn) continue; /* * Allocate our results in the caller's context, not the * transaction's. We do this inside the loop, and restore the original * context at the end, so that leaky things like heap_getnext() are * not called in a potentially long-lived context. */ oldcxt = MemoryContextSwitchTo(resultcxt); db_entry = palloc0(sizeof(DatabaseEntry)); db_entry->oid = HeapTupleGetOid(tup); StrNCpy(NameStr(db_entry->name), NameStr(pgdatabase->datname), NAMEDATALEN); dbs = lappend(dbs, db_entry); MemoryContextSwitchTo(oldcxt); } heap_endscan(scan); heap_close(rel, AccessExclusiveLock); CommitTransactionCommand(); return dbs; }
/* * Find rule oid, given only a rule name but no rel OID. * * If there's more than one, it's an error. If there aren't any, that's an * error, too. In general, this should be avoided - it is provided to support * syntax that is compatible with pre-7.3 versions of PG, where rule names * were unique across the entire database. */ Oid get_rewrite_oid_without_relid(const char *rulename, Oid *reloid, bool missing_ok) { Relation RewriteRelation; HeapScanDesc scanDesc; ScanKeyData scanKeyData; HeapTuple htup; Oid ruleoid; /* Search pg_rewrite for such a rule */ ScanKeyInit(&scanKeyData, Anum_pg_rewrite_rulename, BTEqualStrategyNumber, F_NAMEEQ, CStringGetDatum(rulename)); RewriteRelation = heap_open(RewriteRelationId, AccessShareLock); scanDesc = heap_beginscan_catalog(RewriteRelation, 1, &scanKeyData); htup = heap_getnext(scanDesc, ForwardScanDirection); if (!HeapTupleIsValid(htup)) { if (!missing_ok) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("rule \"%s\" does not exist", rulename))); ruleoid = InvalidOid; } else { ruleoid = HeapTupleGetOid(htup); if (reloid != NULL) *reloid = ((Form_pg_rewrite) GETSTRUCT(htup))->ev_class; htup = heap_getnext(scanDesc, ForwardScanDirection); if (HeapTupleIsValid(htup)) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("there are multiple rules named \"%s\"", rulename), errhint("Specify a relation name as well as a rule name."))); } heap_endscan(scanDesc); heap_close(RewriteRelation, AccessShareLock); return ruleoid; }
/* * Returns true if at least one role is defined in this database cluster. */ static bool ThereIsAtLeastOneRole(void) { Relation pg_authid_rel; HeapScanDesc scan; bool result; pg_authid_rel = heap_open(AuthIdRelationId, AccessShareLock); scan = heap_beginscan_catalog(pg_authid_rel, 0, NULL); result = (heap_getnext(scan, ForwardScanDirection) != NULL); heap_endscan(scan); heap_close(pg_authid_rel, AccessShareLock); return result; }
/** * @brief Print all databases using catalog table pg_database */ void Bridge::GetDatabaseList(void) { LOG_WARN("Do not use bridge function(%s) in Peloton !!! ", __func__); Relation pg_database_rel; HeapScanDesc scan; HeapTuple tup; // Scan pg database table pg_database_rel = heap_open(DatabaseRelationId, AccessShareLock); scan = heap_beginscan_catalog(pg_database_rel, 0, NULL); while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection))) { Form_pg_database pg_database = (Form_pg_database)GETSTRUCT(tup); Oid database_oid = HeapTupleHeaderGetOid(tup->t_data); elog(LOG, "pgdatabase->datname :: %s oid %d ", NameStr(pg_database->datname), (int)database_oid); } heap_endscan(scan); heap_close(pg_database_rel, AccessShareLock); }
/* * Drop some settings from the catalog. These can be for a particular * database, or for a particular role. (It is of course possible to do both * too, but it doesn't make sense for current uses.) */ void DropSetting(Oid databaseid, Oid roleid) { Relation relsetting; HeapScanDesc scan; ScanKeyData keys[2]; HeapTuple tup; int numkeys = 0; relsetting = heap_open(DbRoleSettingRelationId, RowExclusiveLock); if (OidIsValid(databaseid)) { ScanKeyInit(&keys[numkeys], Anum_pg_db_role_setting_setdatabase, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(databaseid)); numkeys++; } if (OidIsValid(roleid)) { ScanKeyInit(&keys[numkeys], Anum_pg_db_role_setting_setrole, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(roleid)); numkeys++; } scan = heap_beginscan_catalog(relsetting, numkeys, keys); while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection))) { simple_heap_delete(relsetting, &tup->t_self); } heap_endscan(scan); heap_close(relsetting, RowExclusiveLock); }
/* * Drop subscription relation mapping. These can be for a particular * subscription, or for a particular relation, or both. */ void RemoveSubscriptionRel(Oid subid, Oid relid) { Relation rel; HeapScanDesc scan; ScanKeyData skey[2]; HeapTuple tup; int nkeys = 0; rel = heap_open(SubscriptionRelRelationId, RowExclusiveLock); if (OidIsValid(subid)) { ScanKeyInit(&skey[nkeys++], Anum_pg_subscription_rel_srsubid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(subid)); } if (OidIsValid(relid)) { ScanKeyInit(&skey[nkeys++], Anum_pg_subscription_rel_srrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relid)); } /* Do the search and delete what we found. */ scan = heap_beginscan_catalog(rel, nkeys, skey); while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection))) { CatalogTupleDelete(rel, &tup->t_self); } heap_endscan(scan); heap_close(rel, RowExclusiveLock); }
/** * @brief Print all tables in *current* database using catalog table pg_class */ void Bridge::GetTableList(bool catalog_only) { LOG_WARN("Do not use bridge function(%s) in Peloton !!! ", __func__); Relation pg_class_rel; HeapScanDesc scan; HeapTuple tuple; // Scan pg class table pg_class_rel = heap_open(RelationRelationId, AccessShareLock); scan = heap_beginscan_catalog(pg_class_rel, 0, NULL); while (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection))) { Form_pg_class pgclass = (Form_pg_class)GETSTRUCT(tuple); // Check if we only need catalog tables or not ? if (catalog_only == false) { elog(LOG, "pgclass->relname :: %s ", NameStr(pgclass->relname)); } else if (pgclass->relnamespace == PG_PUBLIC_NAMESPACE) { elog(LOG, "pgclass->relname :: %s ", NameStr(pgclass->relname)); } } heap_endscan(scan); heap_close(pg_class_rel, AccessShareLock); }
/* * get_next_id * * Gets the smallest possible id to assign to the next continuous view. * We keep this minimal so that we can minimize the size of bitmaps used * to tag stream buffer events with. */ static Oid get_next_id(Relation rel) { HeapScanDesc scandesc; HeapTuple tup; List *ids_list = NIL; int num_ids; Assert(MAX_CQS % 32 == 0); scandesc = heap_beginscan_catalog(rel, 0, NULL); while ((tup = heap_getnext(scandesc, ForwardScanDirection)) != NULL) { Form_pipeline_query row = (Form_pipeline_query) GETSTRUCT(tup); ids_list = lappend_oid(ids_list, row->id); } heap_endscan(scandesc); num_ids = list_length(ids_list); if (num_ids) { Oid ids[num_ids]; int counts_per_combiner[continuous_query_num_combiners]; int i = 0; Oid max; ListCell *lc; int j; int target_combiner; List *potential_ids; MemSet(counts_per_combiner, 0, sizeof(counts_per_combiner)); foreach(lc, ids_list) { ids[i] = lfirst_oid(lc); counts_per_combiner[ids[i] % continuous_query_num_combiners] += 1; i++; } qsort(ids, num_ids, sizeof(Oid), &compare_oid); if (num_ids == MAX_CQS - 1) /* -1 because 0 is an invalid id */ ereport(ERROR, (errcode(ERRCODE_TOO_MANY_CONTINUOUS_VIEWS), errmsg("maximum number of continuous views exceeded"), errhint("Please drop a existing continuous view before trying to create a new one."))); max = ids[num_ids - 1]; Assert(max >= num_ids); /* * FIXME(usmanm): We do some randomization of ID generation here to make sure that CQs that * are created and dropped in quick succession don't read an event that was not for them. */ /* * Collect any unused ids in [1, max]. */ list_free(ids_list); ids_list = NIL; for (i = 1, j = 0; j < num_ids; i++) { if (ids[j] > i) ids_list = lappend_oid(ids_list, (Oid) i); else j++; } /* * Add all IDs between max and the next multiple of 32. */ j = Min((max / 32 + 1) * 32, MAX_CQS); for (i = max + 1; i < j; i++) ids_list = lappend_oid(ids_list, (Oid) i); /* * Less than 16 options? Throw in some more. */ if (list_length(ids_list) < 16 && j < MAX_CQS) for (i = j; i < j + 32; i++) ids_list = lappend_oid(ids_list, (Oid) i); /* * Figure out the target combiner (one with least IDs allocated) and try to allocate * an ID that belongs to it. */ target_combiner = 0; for (i = 0; i < continuous_query_num_combiners; i++) if (counts_per_combiner[i] < counts_per_combiner[target_combiner]) target_combiner = i; potential_ids = NIL; foreach(lc, ids_list) { Oid id = lfirst_oid(lc); if (id % continuous_query_num_combiners == target_combiner) potential_ids = lappend_oid(potential_ids, id); }
/* * Alter table space move * * Allows a user to move all of their objects in a given tablespace in the * current database to another tablespace. Only objects which the user is * considered to be an owner of are moved and the user must have CREATE rights * on the new tablespace. These checks should mean that ALTER TABLE will never * fail due to permissions, but note that permissions will also be checked at * that level. Objects can be ALL, TABLES, INDEXES, or MATERIALIZED VIEWS. * * All to-be-moved objects are locked first. If NOWAIT is specified and the * lock can't be acquired then we ereport(ERROR). */ Oid AlterTableSpaceMove(AlterTableSpaceMoveStmt *stmt) { List *relations = NIL; ListCell *l; ScanKeyData key[1]; Relation rel; HeapScanDesc scan; HeapTuple tuple; Oid orig_tablespaceoid; Oid new_tablespaceoid; List *role_oids = roleNamesToIds(stmt->roles); /* Ensure we were not asked to move something we can't */ if (!stmt->move_all && stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX && stmt->objtype != OBJECT_MATVIEW) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("only tables, indexes, and materialized views exist in tablespaces"))); /* Get the orig and new tablespace OIDs */ orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false); new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false); /* Can't move shared relations in to or out of pg_global */ /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */ if (orig_tablespaceoid == GLOBALTABLESPACE_OID || new_tablespaceoid == GLOBALTABLESPACE_OID) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("cannot move relations in to or out of pg_global tablespace"))); /* * Must have CREATE rights on the new tablespace, unless it is the * database default tablespace (which all users implicitly have CREATE * rights on). */ if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace) { AclResult aclresult; aclresult = pg_tablespace_aclcheck(new_tablespaceoid, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_TABLESPACE, get_tablespace_name(new_tablespaceoid)); } /* * Now that the checks are done, check if we should set either to * InvalidOid because it is our database's default tablespace. */ if (orig_tablespaceoid == MyDatabaseTableSpace) orig_tablespaceoid = InvalidOid; if (new_tablespaceoid == MyDatabaseTableSpace) new_tablespaceoid = InvalidOid; /* no-op */ if (orig_tablespaceoid == new_tablespaceoid) return new_tablespaceoid; /* * Walk the list of objects in the tablespace and move them. This will * only find objects in our database, of course. */ ScanKeyInit(&key[0], Anum_pg_class_reltablespace, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(orig_tablespaceoid)); rel = heap_open(RelationRelationId, AccessShareLock); scan = heap_beginscan_catalog(rel, 1, key); while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { Oid relOid = HeapTupleGetOid(tuple); Form_pg_class relForm; relForm = (Form_pg_class) GETSTRUCT(tuple); /* * Do not move objects in pg_catalog as part of this, if an admin * really wishes to do so, they can issue the individual ALTER * commands directly. * * Also, explicitly avoid any shared tables, temp tables, or TOAST * (TOAST will be moved with the main table). */ if (IsSystemNamespace(relForm->relnamespace) || relForm->relisshared || isAnyTempNamespace(relForm->relnamespace) || relForm->relnamespace == PG_TOAST_NAMESPACE) continue; /* Only consider objects which live in tablespaces */ if (relForm->relkind != RELKIND_RELATION && relForm->relkind != RELKIND_INDEX && relForm->relkind != RELKIND_MATVIEW) continue; /* Check if we were asked to only move a certain type of object */ if (!stmt->move_all && ((stmt->objtype == OBJECT_TABLE && relForm->relkind != RELKIND_RELATION) || (stmt->objtype == OBJECT_INDEX && relForm->relkind != RELKIND_INDEX) || (stmt->objtype == OBJECT_MATVIEW && relForm->relkind != RELKIND_MATVIEW))) continue; /* Check if we are only moving objects owned by certain roles */ if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner)) continue; /* * Handle permissions-checking here since we are locking the tables * and also to avoid doing a bunch of work only to fail part-way. * Note that permissions will also be checked by AlterTableInternal(). * * Caller must be considered an owner on the table to move it. */ if (!pg_class_ownercheck(relOid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, NameStr(relForm->relname)); if (stmt->nowait && !ConditionalLockRelationOid(relOid, AccessExclusiveLock)) ereport(ERROR, (errcode(ERRCODE_OBJECT_IN_USE), errmsg("aborting due to \"%s\".\"%s\" --- lock not available", get_namespace_name(relForm->relnamespace), NameStr(relForm->relname)))); else LockRelationOid(relOid, AccessExclusiveLock); /* Add to our list of objects to move */ relations = lappend_oid(relations, relOid); } heap_endscan(scan); heap_close(rel, AccessShareLock); if (relations == NIL) ereport(NOTICE, (errcode(ERRCODE_NO_DATA_FOUND), errmsg("no matching relations in tablespace \"%s\" found", orig_tablespaceoid == InvalidOid ? "(database default)" : get_tablespace_name(orig_tablespaceoid)))); /* Everything is locked, loop through and move all of the relations. */ foreach(l, relations) { List *cmds = NIL; AlterTableCmd *cmd = makeNode(AlterTableCmd); cmd->subtype = AT_SetTableSpace; cmd->name = stmt->new_tablespacename; cmds = lappend(cmds, cmd); AlterTableInternal(lfirst_oid(l), cmds, false); }
/* * Alter table space options */ Oid AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt) { Relation rel; ScanKeyData entry[1]; HeapScanDesc scandesc; HeapTuple tup; Oid tablespaceoid; Datum datum; Datum newOptions; Datum repl_val[Natts_pg_tablespace]; bool isnull; bool repl_null[Natts_pg_tablespace]; bool repl_repl[Natts_pg_tablespace]; HeapTuple newtuple; /* Search pg_tablespace */ rel = heap_open(TableSpaceRelationId, RowExclusiveLock); ScanKeyInit(&entry[0], Anum_pg_tablespace_spcname, BTEqualStrategyNumber, F_NAMEEQ, CStringGetDatum(stmt->tablespacename)); scandesc = heap_beginscan_catalog(rel, 1, entry); tup = heap_getnext(scandesc, ForwardScanDirection); if (!HeapTupleIsValid(tup)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("tablespace \"%s\" does not exist", stmt->tablespacename))); tablespaceoid = HeapTupleGetOid(tup); /* Must be owner of the existing object */ if (!pg_tablespace_ownercheck(HeapTupleGetOid(tup), GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TABLESPACE, stmt->tablespacename); /* Generate new proposed spcoptions (text array) */ datum = heap_getattr(tup, Anum_pg_tablespace_spcoptions, RelationGetDescr(rel), &isnull); newOptions = transformRelOptions(isnull ? (Datum) 0 : datum, stmt->options, NULL, NULL, false, stmt->isReset); (void) tablespace_reloptions(newOptions, true); /* Build new tuple. */ memset(repl_null, false, sizeof(repl_null)); memset(repl_repl, false, sizeof(repl_repl)); if (newOptions != (Datum) 0) repl_val[Anum_pg_tablespace_spcoptions - 1] = newOptions; else repl_null[Anum_pg_tablespace_spcoptions - 1] = true; repl_repl[Anum_pg_tablespace_spcoptions - 1] = true; newtuple = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null, repl_repl); /* Update system catalog. */ simple_heap_update(rel, &newtuple->t_self, newtuple); CatalogUpdateIndexes(rel, newtuple); InvokeObjectPostAlterHook(TableSpaceRelationId, HeapTupleGetOid(tup), 0); heap_freetuple(newtuple); /* Conclude heap scan. */ heap_endscan(scandesc); heap_close(rel, NoLock); return tablespaceoid; }
/* * Rename a tablespace */ ObjectAddress RenameTableSpace(const char *oldname, const char *newname) { Oid tspId; Relation rel; ScanKeyData entry[1]; HeapScanDesc scan; HeapTuple tup; HeapTuple newtuple; Form_pg_tablespace newform; ObjectAddress address; /* Search pg_tablespace */ rel = heap_open(TableSpaceRelationId, RowExclusiveLock); ScanKeyInit(&entry[0], Anum_pg_tablespace_spcname, BTEqualStrategyNumber, F_NAMEEQ, CStringGetDatum(oldname)); scan = heap_beginscan_catalog(rel, 1, entry); tup = heap_getnext(scan, ForwardScanDirection); if (!HeapTupleIsValid(tup)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("tablespace \"%s\" does not exist", oldname))); tspId = HeapTupleGetOid(tup); newtuple = heap_copytuple(tup); newform = (Form_pg_tablespace) GETSTRUCT(newtuple); heap_endscan(scan); /* Must be owner */ if (!pg_tablespace_ownercheck(HeapTupleGetOid(newtuple), GetUserId())) aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_TABLESPACE, oldname); /* Validate new name */ if (!allowSystemTableMods && IsReservedName(newname)) ereport(ERROR, (errcode(ERRCODE_RESERVED_NAME), errmsg("unacceptable tablespace name \"%s\"", newname), errdetail("The prefix \"pg_\" is reserved for system tablespaces."))); /* Make sure the new name doesn't exist */ ScanKeyInit(&entry[0], Anum_pg_tablespace_spcname, BTEqualStrategyNumber, F_NAMEEQ, CStringGetDatum(newname)); scan = heap_beginscan_catalog(rel, 1, entry); tup = heap_getnext(scan, ForwardScanDirection); if (HeapTupleIsValid(tup)) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("tablespace \"%s\" already exists", newname))); heap_endscan(scan); /* OK, update the entry */ namestrcpy(&(newform->spcname), newname); simple_heap_update(rel, &newtuple->t_self, newtuple); CatalogUpdateIndexes(rel, newtuple); InvokeObjectPostAlterHook(TableSpaceRelationId, tspId, 0); ObjectAddressSet(address, TableSpaceRelationId, tspId); heap_close(rel, NoLock); return address; }
/* * Drop a table space * * Be careful to check that the tablespace is empty. */ void DropTableSpace(DropTableSpaceStmt *stmt) { #ifdef HAVE_SYMLINK char *tablespacename = stmt->tablespacename; HeapScanDesc scandesc; Relation rel; HeapTuple tuple; ScanKeyData entry[1]; Oid tablespaceoid; /* * Find the target tuple */ rel = heap_open(TableSpaceRelationId, RowExclusiveLock); ScanKeyInit(&entry[0], Anum_pg_tablespace_spcname, BTEqualStrategyNumber, F_NAMEEQ, CStringGetDatum(tablespacename)); scandesc = heap_beginscan_catalog(rel, 1, entry); tuple = heap_getnext(scandesc, ForwardScanDirection); if (!HeapTupleIsValid(tuple)) { if (!stmt->missing_ok) { ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("tablespace \"%s\" does not exist", tablespacename))); } else { ereport(NOTICE, (errmsg("tablespace \"%s\" does not exist, skipping", tablespacename))); /* XXX I assume I need one or both of these next two calls */ heap_endscan(scandesc); heap_close(rel, NoLock); } return; } tablespaceoid = HeapTupleGetOid(tuple); /* Must be tablespace owner */ if (!pg_tablespace_ownercheck(tablespaceoid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TABLESPACE, tablespacename); /* Disallow drop of the standard tablespaces, even by superuser */ if (tablespaceoid == GLOBALTABLESPACE_OID || tablespaceoid == DEFAULTTABLESPACE_OID) aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_TABLESPACE, tablespacename); /* DROP hook for the tablespace being removed */ InvokeObjectDropHook(TableSpaceRelationId, tablespaceoid, 0); /* * Remove the pg_tablespace tuple (this will roll back if we fail below) */ simple_heap_delete(rel, &tuple->t_self); heap_endscan(scandesc); /* * Remove any comments or security labels on this tablespace. */ DeleteSharedComments(tablespaceoid, TableSpaceRelationId); DeleteSharedSecurityLabel(tablespaceoid, TableSpaceRelationId); /* * Remove dependency on owner. */ deleteSharedDependencyRecordsFor(TableSpaceRelationId, tablespaceoid, 0); /* * Acquire TablespaceCreateLock to ensure that no TablespaceCreateDbspace * is running concurrently. */ LWLockAcquire(TablespaceCreateLock, LW_EXCLUSIVE); /* * Try to remove the physical infrastructure. */ if (!destroy_tablespace_directories(tablespaceoid, false)) { /* * Not all files deleted? However, there can be lingering empty files * in the directories, left behind by for example DROP TABLE, that * have been scheduled for deletion at next checkpoint (see comments * in mdunlink() for details). We could just delete them immediately, * but we can't tell them apart from important data files that we * mustn't delete. So instead, we force a checkpoint which will clean * out any lingering files, and try again. * * XXX On Windows, an unlinked file persists in the directory listing * until no process retains an open handle for the file. The DDL * commands that schedule files for unlink send invalidation messages * directing other PostgreSQL processes to close the files. DROP * TABLESPACE should not give up on the tablespace becoming empty * until all relevant invalidation processing is complete. */ RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_FORCE | CHECKPOINT_WAIT); if (!destroy_tablespace_directories(tablespaceoid, false)) { /* Still not empty, the files must be important then */ ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("tablespace \"%s\" is not empty", tablespacename))); } } /* Record the filesystem change in XLOG */ { xl_tblspc_drop_rec xlrec; xlrec.ts_id = tablespaceoid; XLogBeginInsert(); XLogRegisterData((char *) &xlrec, sizeof(xl_tblspc_drop_rec)); (void) XLogInsert(RM_TBLSPC_ID, XLOG_TBLSPC_DROP); } /* * Note: because we checked that the tablespace was empty, there should be * no need to worry about flushing shared buffers or free space map * entries for relations in the tablespace. */ /* * Force synchronous commit, to minimize the window between removing the * files on-disk and marking the transaction committed. It's not great * that there is any window at all, but definitely we don't want to make * it larger than necessary. */ ForceSyncCommit(); /* * Allow TablespaceCreateDbspace again. */ LWLockRelease(TablespaceCreateLock); /* We keep the lock on pg_tablespace until commit */ heap_close(rel, NoLock); #else /* !HAVE_SYMLINK */ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("tablespaces are not supported on this platform"))); #endif /* HAVE_SYMLINK */ }