/* * ConversionDrop * * Drop a conversion after doing permission checks. */ void ConversionDrop(Oid conversionOid, DropBehavior behavior) { HeapTuple tuple; ObjectAddress object; tuple = SearchSysCache(CONOID, ObjectIdGetDatum(conversionOid), 0, 0, 0); if (!HeapTupleIsValid(tuple)) elog(ERROR, "cache lookup failed for conversion %u", conversionOid); if (!superuser() && ((Form_pg_conversion) GETSTRUCT(tuple))->conowner != GetUserId()) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION, NameStr(((Form_pg_conversion) GETSTRUCT(tuple))->conname)); ReleaseSysCache(tuple); /* * Do the deletion */ object.classId = ConversionRelationId; object.objectId = conversionOid; object.objectSubId = 0; performDeletion(&object, behavior); }
/* * ConversionDrop * * Drop a conversion after doing permission checks. */ void ConversionDrop(Oid conversionOid, DropBehavior behavior) { HeapTuple tuple; ObjectAddress object; cqContext *pcqCtx; pcqCtx = caql_beginscan( NULL, cql("SELECT * FROM pg_conversion " " WHERE oid = :1 ", ObjectIdGetDatum(conversionOid))); tuple = caql_getnext(pcqCtx); if (!HeapTupleIsValid(tuple)) elog(ERROR, "cache lookup failed for conversion %u", conversionOid); if (!superuser() && ((Form_pg_conversion) GETSTRUCT(tuple))->conowner != GetUserId()) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION, NameStr(((Form_pg_conversion) GETSTRUCT(tuple))->conname)); caql_endscan(pcqCtx); /* * Do the deletion */ object.classId = ConversionRelationId; object.objectId = conversionOid; object.objectSubId = 0; performDeletion(&object, behavior); }
/* --------------------------------------------------------------------- * DROP PROCEDURAL LANGUAGE * --------------------------------------------------------------------- */ void DropProceduralLanguage(DropPLangStmt *stmt) { char *languageName; HeapTuple langTup; ObjectAddress object; /* * Translate the language name, check that the language exists */ languageName = case_translate_language_name(stmt->plname); langTup = SearchSysCache(LANGNAME, CStringGetDatum(languageName), 0, 0, 0); if (!HeapTupleIsValid(langTup)) { if (!stmt->missing_ok) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("language \"%s\" does not exist", languageName))); else ereport(NOTICE, (errmsg("language \"%s\" does not exist, skipping", languageName))); return; } /* * Check permission */ if (!pg_language_ownercheck(HeapTupleGetOid(langTup), GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_LANGUAGE, languageName); object.classId = LanguageRelationId; object.objectId = HeapTupleGetOid(langTup); object.objectSubId = 0; ReleaseSysCache(langTup); /* * Do the deletion */ performDeletion(&object, stmt->behavior); if (Gp_role == GP_ROLE_DISPATCH) { CdbDispatchUtilityStatement((Node *) stmt, DF_CANCEL_ON_ERROR| DF_WITH_SNAPSHOT| DF_NEED_TWO_PHASE, NIL, NULL); } }
/* * RemoveRewriteRule * * Delete a rule given its name. */ void RemoveRewriteRule(Oid owningRel, const char *ruleName, DropBehavior behavior, bool missing_ok) { HeapTuple tuple; Oid eventRelationOid; ObjectAddress object; /* * Find the tuple for the target rule. */ tuple = SearchSysCache(RULERELNAME, ObjectIdGetDatum(owningRel), PointerGetDatum(ruleName), 0, 0); /* * complain if no rule with such name exists */ if (!HeapTupleIsValid(tuple)) { if (!missing_ok) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("rule \"%s\" for relation \"%s\" does not exist", ruleName, get_rel_name(owningRel)))); else ereport(NOTICE, (errmsg("rule \"%s\" for relation \"%s\" does not exist, skipping", ruleName, get_rel_name(owningRel)))); return; } /* * Verify user has appropriate permissions. */ eventRelationOid = ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_class; Assert(eventRelationOid == owningRel); if (!pg_class_ownercheck(eventRelationOid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, get_rel_name(eventRelationOid)); /* * Do the deletion */ object.classId = RewriteRelationId; object.objectId = HeapTupleGetOid(tuple); object.objectSubId = 0; ReleaseSysCache(tuple); performDeletion(&object, behavior); }
/* --------------------------------------------------------------------- * DROP PROCEDURAL LANGUAGE * --------------------------------------------------------------------- */ void DropProceduralLanguage(DropPLangStmt *stmt) { char *languageName; HeapTuple langTup; ObjectAddress object; /* * Check permission */ if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to drop procedural language"))); /* * Translate the language name, check that the language exists */ languageName = case_translate_language_name(stmt->plname); langTup = SearchSysCache(LANGNAME, CStringGetDatum(languageName), 0, 0, 0); if (!HeapTupleIsValid(langTup)) { if (!stmt->missing_ok) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("language \"%s\" does not exist", languageName))); else ereport(NOTICE, (errmsg("language \"%s\" does not exist, skipping", languageName))); return; } object.classId = LanguageRelationId; object.objectId = HeapTupleGetOid(langTup); object.objectSubId = 0; ReleaseSysCache(langTup); /* * Do the deletion */ performDeletion(&object, stmt->behavior); }
/* Removes the schema and all tables within the schema, if the schema exists. */ void RemoveJobSchema(StringInfo schemaName) { Datum schemaNameDatum = CStringGetDatum(schemaName->data); Oid schemaId = InvalidOid; schemaId = GetSysCacheOid(NAMESPACENAME, schemaNameDatum, 0, 0, 0); if (OidIsValid(schemaId)) { ObjectAddress schemaObject = { 0, 0, 0 }; bool showNotices = false; bool permissionsOK = pg_namespace_ownercheck(schemaId, GetUserId()); if (!permissionsOK) { aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE, schemaName->data); } schemaObject.classId = NamespaceRelationId; schemaObject.objectId = schemaId; schemaObject.objectSubId = 0; /* * We first delete all tables in this schema. Rather than relying on the * schema command, we call the dependency mechanism directly so that we * can suppress notice messages that are typically displayed during * cascading deletes. */ deleteWhatDependsOn(&schemaObject, showNotices); CommandCounterIncrement(); /* drop the empty schema */ performDeletion(&schemaObject, DROP_RESTRICT, 0); CommandCounterIncrement(); } else { ereport(DEBUG2, (errmsg("schema \"%s\" does not exist, skipping", schemaName->data))); } }
/* * Destroys an existing large object (not to be confused with a descriptor!) * * returns -1 if failed */ int inv_drop(Oid lobjId) { ObjectAddress object; /* * Delete any comments and dependencies on the large object */ object.classId = LargeObjectRelationId; object.objectId = lobjId; object.objectSubId = 0; performDeletion(&object, DROP_CASCADE, 0); /* * Advance command counter so that tuple removal will be seen by later * large-object operations in this transaction. */ CommandCounterIncrement(); return 1; }
/* --------------------------------------------------------------------- * DROP PROCEDURAL LANGUAGE * --------------------------------------------------------------------- */ void DropProceduralLanguage(DropPLangStmt *stmt) { char *languageName; Oid oid; ObjectAddress object; /* * Translate the language name, check that the language exists */ languageName = case_translate_language_name(stmt->plname); oid = get_language_oid(languageName, stmt->missing_ok); if (!OidIsValid(oid)) { ereport(NOTICE, (errmsg("language \"%s\" does not exist, skipping", languageName))); return; } /* * Check permission */ if (!pg_language_ownercheck(oid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_LANGUAGE, languageName); object.classId = LanguageRelationId; object.objectId = oid; object.objectSubId = 0; /* * Do the deletion */ performDeletion(&object, stmt->behavior); }
/* * RemoveRewriteRule * * Delete a rule given its name. */ void RemoveRewriteRule(Oid owningRel, const char *ruleName, DropBehavior behavior, bool missing_ok) { HeapTuple tuple; Oid eventRelationOid; ObjectAddress object; cqContext *pcqCtx; /* * Find the tuple for the target rule. */ pcqCtx = caql_beginscan( NULL, cql("SELECT * FROM pg_rewrite " " WHERE ev_class = :1 " " AND rulename = :2 " " FOR UPDATE ", ObjectIdGetDatum(owningRel), CStringGetDatum((char *) ruleName))); tuple = caql_getnext(pcqCtx); /* * complain if no rule with such name exists */ if (!HeapTupleIsValid(tuple)) { if (!missing_ok) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("rule \"%s\" for relation \"%s\" does not exist", ruleName, get_rel_name(owningRel)))); else ereport(NOTICE, (errmsg("rule \"%s\" for relation \"%s\" does not exist, skipping", ruleName, get_rel_name(owningRel)))); caql_endscan(pcqCtx); return; } /* * Verify user has appropriate permissions. */ eventRelationOid = ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_class; Assert(eventRelationOid == owningRel); if (!pg_class_ownercheck(eventRelationOid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, get_rel_name(eventRelationOid)); /* * Do the deletion */ object.classId = RewriteRelationId; object.objectId = HeapTupleGetOid(tuple); object.objectSubId = 0; caql_endscan(pcqCtx); performDeletion(&object, behavior); }
/* * FetchTableCommon executes common logic that wraps around the actual data * fetching function. This common logic includes ensuring that only one process * tries to fetch this table at any given time, and that data fetch operations * are retried in case of node failures. */ static void FetchTableCommon(text *tableNameText, uint64 remoteTableSize, ArrayType *nodeNameObject, ArrayType *nodePortObject, bool (*FetchTableFunction)(const char *, uint32, const char *)) { uint64 shardId = INVALID_SHARD_ID; Oid relationId = InvalidOid; List *relationNameList = NIL; RangeVar *relation = NULL; uint32 nodeIndex = 0; bool tableFetched = false; char *tableName = text_to_cstring(tableNameText); Datum *nodeNameArray = DeconstructArrayObject(nodeNameObject); Datum *nodePortArray = DeconstructArrayObject(nodePortObject); int32 nodeNameCount = ArrayObjectCount(nodeNameObject); int32 nodePortCount = ArrayObjectCount(nodePortObject); /* we should have the same number of node names and port numbers */ if (nodeNameCount != nodePortCount) { ereport(ERROR, (errmsg("node name array size: %d and node port array size: %d" " do not match", nodeNameCount, nodePortCount))); } /* * We lock on the shardId, but do not unlock. When the function returns, and * the transaction for this function commits, this lock will automatically * be released. This ensures that concurrent caching commands will see the * newly created table when they acquire the lock (in read committed mode). */ shardId = ExtractShardId(tableName); LockShardResource(shardId, AccessExclusiveLock); relationNameList = textToQualifiedNameList(tableNameText); relation = makeRangeVarFromNameList(relationNameList); relationId = RangeVarGetRelid(relation, NoLock, true); /* check if we already fetched the table */ if (relationId != InvalidOid) { uint64 localTableSize = 0; if (!ExpireCachedShards) { return; } /* * Check if the cached shard has the same size on disk as it has as on * the placement (is up to date). * * Note 1: performing updates or deletes on the original shard leads to * inconsistent sizes between different databases in which case the data * would be fetched every time, or worse, the placement would get into * a deadlock when it tries to fetch from itself while holding the lock. * Therefore, this option is disabled by default. * * Note 2: when appending data to a shard, the size on disk only * increases when a new page is added (the next 8kB block). */ localTableSize = LocalTableSize(relationId); if (remoteTableSize > localTableSize) { /* table is not up to date, drop the table */ ObjectAddress tableObject = { InvalidOid, InvalidOid, 0 }; tableObject.classId = RelationRelationId; tableObject.objectId = relationId; tableObject.objectSubId = 0; performDeletion(&tableObject, DROP_RESTRICT, PERFORM_DELETION_INTERNAL); } else { /* table is up to date */ return; } } /* loop until we fetch the table or try all nodes */ while (!tableFetched && (nodeIndex < nodeNameCount)) { Datum nodeNameDatum = nodeNameArray[nodeIndex]; Datum nodePortDatum = nodePortArray[nodeIndex]; char *nodeName = TextDatumGetCString(nodeNameDatum); uint32 nodePort = DatumGetUInt32(nodePortDatum); tableFetched = (*FetchTableFunction)(nodeName, nodePort, tableName); nodeIndex++; } /* error out if we tried all nodes and could not fetch the table */ if (!tableFetched) { ereport(ERROR, (errmsg("could not fetch relation: \"%s\"", tableName))); } }