Esempio n. 1
0
char *
get_relation_name(Oid relid)
{
	return quote_qualified_identifier(
		get_namespace_name(get_rel_namespace(relid)),
		get_rel_name(relid));
}
Esempio n. 2
0
/*
 * Auto-stats employs this sub-routine to issue an analyze on a specific relation.
 */
static void
autostats_issue_analyze(Oid relationOid)
{
	VacuumStmt *analyzeStmt = NULL;
	RangeVar   *relation = NULL;

	/*
	 * If this user does not own the table, then auto-stats will not issue the
	 * analyze.
	 */
	if (!(pg_class_ownercheck(relationOid, GetUserId()) ||
		  (pg_database_ownercheck(MyDatabaseId, GetUserId()) && !IsSharedRelation(relationOid))))
	{
		elog(DEBUG3, "Auto-stats did not issue ANALYZE on tableoid %d since the user does not have table-owner level permissions.",
			 relationOid);

		return;
	}

	relation = makeRangeVar(get_namespace_name(get_rel_namespace(relationOid)), get_rel_name(relationOid), -1);
	analyzeStmt = makeNode(VacuumStmt);
	/* Set up command parameters */
	analyzeStmt->vacuum = false;
	analyzeStmt->full = false;
	analyzeStmt->analyze = true;
	analyzeStmt->freeze_min_age = -1;
	analyzeStmt->verbose = false;
	analyzeStmt->rootonly = false;
	analyzeStmt->relation = relation;	/* not used since we pass relids list */
	analyzeStmt->va_cols = NIL;
	vacuum(analyzeStmt, NIL, NULL, false, false);
	pfree(analyzeStmt);
}
Esempio n. 3
0
File: lmgr.c Progetto: AnLingm/gpdb
/*
 * LockTagIsTemp
 *		Determine whether a locktag is for a lock on a temporary object
 *
 * We need this because 2PC cannot deal with temp objects
 */
bool
LockTagIsTemp(const LOCKTAG *tag)
{
	switch (tag->locktag_type)
	{
		case LOCKTAG_RELATION:
		case LOCKTAG_RELATION_EXTEND:
		case LOCKTAG_PAGE:
		case LOCKTAG_TUPLE:
		case LOCKTAG_RELATION_RESYNCHRONIZE:
		case LOCKTAG_RELATION_APPENDONLY_SEGMENT_FILE:
			/* check for lock on a temp relation */
			/* field1 is dboid, field2 is reloid for all of these */
			if ((Oid) tag->locktag_field1 == InvalidOid)
				return false;	/* shared, so not temp */
			if (isTempNamespace(get_rel_namespace((Oid) tag->locktag_field2)))
				return true;
			break;
		case LOCKTAG_TRANSACTION:
			/* there are no temp transactions */
			break;
		case LOCKTAG_OBJECT:
			/* there are currently no non-table temp objects */
			break;
		case LOCKTAG_USERLOCK:
		case LOCKTAG_ADVISORY:
			/* assume these aren't temp */
			break;
	}
	return false;				/* default case */
}
Esempio n. 4
0
/*
 * GetTableCreationCommands takes in a relationId, and returns the list of DDL
 * commands needed to reconstruct the relation, excluding indexes and
 * constraints.
 */
List *
GetTableCreationCommands(Oid relationId, bool includeSequenceDefaults)
{
	List *tableDDLEventList = NIL;
	char tableType = 0;
	char *tableSchemaDef = NULL;
	char *tableColumnOptionsDef = NULL;
	char *createSchemaCommand = NULL;
	Oid schemaId = InvalidOid;

	/*
	 * Set search_path to NIL so that all objects outside of pg_catalog will be
	 * schema-prefixed. pg_catalog will be added automatically when we call
	 * PushOverrideSearchPath(), since we set addCatalog to true;
	 */
	OverrideSearchPath *overridePath = GetOverrideSearchPath(CurrentMemoryContext);
	overridePath->schemas = NIL;
	overridePath->addCatalog = true;
	PushOverrideSearchPath(overridePath);

	/* if foreign table, fetch extension and server definitions */
	tableType = get_rel_relkind(relationId);
	if (tableType == RELKIND_FOREIGN_TABLE)
	{
		char *extensionDef = pg_get_extensiondef_string(relationId);
		char *serverDef = pg_get_serverdef_string(relationId);

		if (extensionDef != NULL)
		{
			tableDDLEventList = lappend(tableDDLEventList, extensionDef);
		}
		tableDDLEventList = lappend(tableDDLEventList, serverDef);
	}

	/* create schema if the table is not in the default namespace (public) */
	schemaId = get_rel_namespace(relationId);
	createSchemaCommand = CreateSchemaDDLCommand(schemaId);
	if (createSchemaCommand != NULL)
	{
		tableDDLEventList = lappend(tableDDLEventList, createSchemaCommand);
	}

	/* fetch table schema and column option definitions */
	tableSchemaDef = pg_get_tableschemadef_string(relationId, includeSequenceDefaults);
	tableColumnOptionsDef = pg_get_tablecolumnoptionsdef_string(relationId);

	tableDDLEventList = lappend(tableDDLEventList, tableSchemaDef);
	if (tableColumnOptionsDef != NULL)
	{
		tableDDLEventList = lappend(tableDDLEventList, tableColumnOptionsDef);
	}

	/* revert back to original search_path */
	PopOverrideSearchPath();

	return tableDDLEventList;
}
Esempio n. 5
0
Datum
pg_relation_size(PG_FUNCTION_ARGS)
{
	Oid			relOid = PG_GETARG_OID(0);
	text	   *forkName = PG_GETARG_TEXT_P(1);
	Relation	rel;
	int64		size = 0;

	/**
	 * This function is peculiar in that it does its own dispatching.
	 * It does not work on entry db since we do not support dispatching
	 * from entry-db currently.
	 */
	if (Gp_role == GP_ROLE_EXECUTE && Gp_segment == -1)
		elog(ERROR, "This query is not currently supported by GPDB.");

	rel = try_relation_open(relOid, AccessShareLock, false);

	/*
	 * While we scan pg_class with an MVCC snapshot,
 	 * someone else might drop the table. It's better to return NULL for
	 * already-dropped tables than throw an error and abort the whole query.
	 */
	if (!RelationIsValid(rel))
  		PG_RETURN_NULL();

	if (relOid == 0 || rel->rd_node.relNode == 0)
		size = 0;
	else
		size = calculate_relation_size(rel,
									   forkname_to_number(text_to_cstring(forkName)));

	if (Gp_role == GP_ROLE_DISPATCH)
	{
		StringInfoData buffer;
		char *schemaName;
		char *relName;

		schemaName = get_namespace_name(get_rel_namespace(relOid));
		if (schemaName == NULL)
			elog(ERROR, "Cannot find schema for oid %d", relOid);
		relName = get_rel_name(relOid);
		if (relName == NULL)
			elog(ERROR, "Cannot find relation for oid %d", relOid);

		initStringInfo(&buffer);

		appendStringInfo(&buffer, "select sum(pg_relation_size('%s.%s'))::int8 from gp_dist_random('gp_id');", quote_identifier(schemaName), quote_identifier(relName));

		size += get_size_from_segDBs(buffer.data);
	}

	relation_close(rel, AccessShareLock);

	PG_RETURN_INT64(size);
}
Esempio n. 6
0
/*
 * Print a relation name into the StringInfo provided by caller.
 */
static void
print_relname(StringInfo s, Relation rel)
{
	Form_pg_class	class_form = RelationGetForm(rel);

	appendStringInfoString(s,
		quote_qualified_identifier(
				get_namespace_name(
						   get_rel_namespace(RelationGetRelid(rel))),
			NameStr(class_form->relname)));
}
/*
 * GenerateAttachShardPartitionCommand generates command to attach a child table
 * table to its parent in a partitioning hierarchy.
 */
char *
GenerateAttachShardPartitionCommand(ShardInterval *shardInterval)
{
	Oid schemaId = get_rel_namespace(shardInterval->relationId);
	char *schemaName = get_namespace_name(schemaId);
	char *escapedSchemaName = quote_literal_cstr(schemaName);

	char *command = GenerateAlterTableAttachPartitionCommand(shardInterval->relationId);
	char *escapedCommand = quote_literal_cstr(command);
	int shardIndex = ShardIndex(shardInterval);

	Oid parentSchemaId = InvalidOid;
	char *parentSchemaName = NULL;
	char *escapedParentSchemaName = NULL;
	uint64 parentShardId = INVALID_SHARD_ID;

	StringInfo attachPartitionCommand = makeStringInfo();

	Oid parentRelationId = PartitionParentOid(shardInterval->relationId);
	if (parentRelationId == InvalidOid)
	{
		ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
						errmsg("cannot attach partition"),
						errdetail("Referenced relation cannot be found.")));
	}

	parentSchemaId = get_rel_namespace(parentRelationId);
	parentSchemaName = get_namespace_name(parentSchemaId);
	escapedParentSchemaName = quote_literal_cstr(parentSchemaName);
	parentShardId = ColocatedShardIdInRelation(parentRelationId, shardIndex);

	appendStringInfo(attachPartitionCommand,
					 WORKER_APPLY_INTER_SHARD_DDL_COMMAND, parentShardId,
					 escapedParentSchemaName, shardInterval->shardId,
					 escapedSchemaName, escapedCommand);

	return attachPartitionCommand->data;
}
Esempio n. 8
0
/* Gpmon helpers. */
char * GetScanRelNameGpmon(Oid relid, char schema_rel_name[SCAN_REL_NAME_BUF_SIZE])
{
	if (relid > 0)
	{
		char *relname = get_rel_name(relid);
		char *schemaname = get_namespace_name(get_rel_namespace(relid));
		snprintf(schema_rel_name, SCAN_REL_NAME_BUF_SIZE, "%s.%s", schemaname, relname);
		if (relname)
			pfree(relname);
		if (schemaname)
			pfree(schemaname);
	}
	return schema_rel_name;
}
Esempio n. 9
0
static char *
get_relation_name(Oid relid)
{
    Oid		nsp = get_rel_namespace(relid);
    char   *nspname;

    /* Qualify the name if not visible in search path */
    if (RelationIsVisible(relid))
        nspname = NULL;
    else
        nspname = get_namespace_name(nsp);

    return quote_qualified_identifier(nspname, get_rel_name(relid));
}
Esempio n. 10
0
static char *
get_relation_name(Oid relid)
{
	Oid		nsp = get_rel_namespace(relid);
	char   *nspname;
	char   *strver;
	int ver;

	/* Get the version of the running server (PG_VERSION_NUM would return
	 * the version we compiled the extension with) */
	strver = GetConfigOptionByName("server_version_num", NULL
#if PG_VERSION_NUM >= 90600
		, false	    /* missing_ok */
#endif
	);

	ver = atoi(strver);
	pfree(strver);

	/*
	 * Relation names given by PostgreSQL core are always
	 * qualified since some minor releases. Note that this change
	 * wasn't introduced in PostgreSQL 9.2 and 9.1 releases.
	 */
	if ((ver >= 100000 && ver < 100003) ||
		(ver >= 90600 && ver < 90608) ||
		(ver >= 90500 && ver < 90512) ||
		(ver >= 90400 && ver < 90417) ||
		(ver >= 90300 && ver < 90322) ||
		(ver >= 90200 && ver < 90300) ||
		(ver >= 90100 && ver < 90200))
	{
		/* Qualify the name if not visible in search path */
		if (RelationIsVisible(relid))
			nspname = NULL;
		else
			nspname = get_namespace_name(nsp);
	}
	else
	{
		/* Always qualify the name */
		if (OidIsValid(nsp))
			nspname = get_namespace_name(nsp);
		else
			nspname = NULL;
	}

	return quote_qualified_identifier(nspname, get_rel_name(relid));
}
Esempio n. 11
0
/*
 * CheckTableSchemaNameForDrop errors out if the current user does not
 * have permission to undistribute the given relation, taking into
 * account that it may be called from the drop trigger. If the table exists,
 * the function rewrites the given table and schema name.
 */
void
CheckTableSchemaNameForDrop(Oid relationId, char **schemaName, char **tableName)
{
	char *tempTableName = get_rel_name(relationId);

	if (tempTableName != NULL)
	{
		/* ensure proper values are used if the table exists */
		Oid schemaId = get_rel_namespace(relationId);
		(*schemaName) = get_namespace_name(schemaId);
		(*tableName) = tempTableName;

		EnsureTableOwner(relationId);
	}
}
Esempio n. 12
0
/**
 * Method determines if a relation is master-only or distributed among segments.
 * Input:
 * 	relationOid
 * Output:
 * 	true if masteronly
 */
bool
isMasterOnly(Oid relationOid)
{
	Assert(relationOid != InvalidOid);
	Oid				schemaOid = get_rel_namespace(relationOid);
	GpPolicy		*distributionPolicy = GpPolicyFetch(CurrentMemoryContext, relationOid);
	
	bool masterOnly = (Gp_role == GP_ROLE_UTILITY 
			|| IsSystemNamespace(schemaOid)
			|| IsToastNamespace(schemaOid)
			|| IsAoSegmentNamespace(schemaOid)
			|| (distributionPolicy == NULL)
			|| (distributionPolicy->ptype == POLICYTYPE_ENTRY));
	
	return masterOnly;
}
Esempio n. 13
0
/*
 * truncate relation
 */
void
TruncateTable(Oid relid)
{
	TruncateStmt	stmt;
	RangeVar	   *heap;

	Assert(OidIsValid(relid));

	heap = makeRangeVar(get_namespace_name(get_rel_namespace(relid)),
						get_rel_name(relid), -1);

	memset(&stmt, 0, sizeof(stmt));
	stmt.type = T_TruncateStmt;
	stmt.relations = list_make1(heap);
	stmt.behavior = DROP_RESTRICT;
	ExecuteTruncate(&stmt);

	CommandCounterIncrement();
}
Esempio n. 14
0
/*
 * master_apply_delete_command takes in a delete command, finds shards that
 * match the criteria defined in the delete command, drops the found shards from
 * the worker nodes, and updates the corresponding metadata on the master node.
 * This function drops a shard if and only if all rows in the shard satisfy
 * the conditions in the delete command. Note that this function only accepts
 * conditions on the partition key and if no condition is provided then all
 * shards are deleted.
 *
 * We mark shard placements that we couldn't drop as to be deleted later. If a
 * shard satisfies the given conditions, we delete it from shard metadata table
 * even though related shard placements are not deleted.
 */
Datum
master_apply_delete_command(PG_FUNCTION_ARGS)
{
	text *queryText = PG_GETARG_TEXT_P(0);
	char *queryString = text_to_cstring(queryText);
	char *relationName = NULL;
	char *schemaName = NULL;
	Oid relationId = InvalidOid;
	List *shardIntervalList = NIL;
	List *deletableShardIntervalList = NIL;
	List *queryTreeList = NIL;
	Query *deleteQuery = NULL;
	Node *whereClause = NULL;
	Node *deleteCriteria = NULL;
	Node *queryTreeNode = NULL;
	DeleteStmt *deleteStatement = NULL;
	int droppedShardCount = 0;
	LOCKMODE lockMode = 0;
	char partitionMethod = 0;
	bool failOK = false;
#if (PG_VERSION_NUM >= 100000)
	RawStmt *rawStmt = (RawStmt *) ParseTreeRawStmt(queryString);
	queryTreeNode = rawStmt->stmt;
#else
	queryTreeNode = ParseTreeNode(queryString);
#endif

	EnsureCoordinator();
	CheckCitusVersion(ERROR);

	if (!IsA(queryTreeNode, DeleteStmt))
	{
		ereport(ERROR, (errmsg("query \"%s\" is not a delete statement",
							   queryString)));
	}

	deleteStatement = (DeleteStmt *) queryTreeNode;

	schemaName = deleteStatement->relation->schemaname;
	relationName = deleteStatement->relation->relname;

	/*
	 * We take an exclusive lock while dropping shards to prevent concurrent
	 * writes. We don't want to block SELECTs, which means queries might fail
	 * if they access a shard that has just been dropped.
	 */
	lockMode = ExclusiveLock;

	relationId = RangeVarGetRelid(deleteStatement->relation, lockMode, failOK);

	/* schema-prefix if it is not specified already */
	if (schemaName == NULL)
	{
		Oid schemaId = get_rel_namespace(relationId);
		schemaName = get_namespace_name(schemaId);
	}

	CheckDistributedTable(relationId);
	EnsureTablePermissions(relationId, ACL_DELETE);

#if (PG_VERSION_NUM >= 100000)
	queryTreeList = pg_analyze_and_rewrite(rawStmt, queryString, NULL, 0, NULL);
#else
	queryTreeList = pg_analyze_and_rewrite(queryTreeNode, queryString, NULL, 0);
#endif
	deleteQuery = (Query *) linitial(queryTreeList);
	CheckTableCount(deleteQuery);

	/* get where clause and flatten it */
	whereClause = (Node *) deleteQuery->jointree->quals;
	deleteCriteria = eval_const_expressions(NULL, whereClause);

	partitionMethod = PartitionMethod(relationId);
	if (partitionMethod == DISTRIBUTE_BY_HASH)
	{
		ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						errmsg("cannot delete from hash distributed table with this "
							   "command"),
						errdetail("Delete statements on hash-partitioned tables "
								  "are not supported with master_apply_delete_command."),
						errhint("Use master_modify_multiple_shards command instead.")));
	}
	else if (partitionMethod == DISTRIBUTE_BY_NONE)
	{
		ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						errmsg("cannot delete from distributed table"),
						errdetail("Delete statements on reference tables "
								  "are not supported.")));
	}


	CheckDeleteCriteria(deleteCriteria);
	CheckPartitionColumn(relationId, deleteCriteria);

	shardIntervalList = LoadShardIntervalList(relationId);

	/* drop all shards if where clause is not present */
	if (deleteCriteria == NULL)
	{
		deletableShardIntervalList = shardIntervalList;
		ereport(DEBUG2, (errmsg("dropping all shards for \"%s\"", relationName)));
	}
	else
	{
		deletableShardIntervalList = ShardsMatchingDeleteCriteria(relationId,
																  shardIntervalList,
																  deleteCriteria);
	}

	droppedShardCount = DropShards(relationId, schemaName, relationName,
								   deletableShardIntervalList);

	PG_RETURN_INT32(droppedShardCount);
}
Esempio n. 15
0
/*
 * exec_object_restorecon
 *
 * This routine is a helper called by sepgsql_restorecon; it set up
 * initial security labels of database objects within the supplied
 * catalog OID.
 */
static void
exec_object_restorecon(struct selabel_handle *sehnd, Oid catalogId)
{
	Relation		rel;
	SysScanDesc		sscan;
	HeapTuple		tuple;
	char		   *database_name = get_database_name(MyDatabaseId);
	char		   *namespace_name;
	Oid				namespace_id;
	char		   *relation_name;

	/*
	 * Open the target catalog. We don't want to allow writable
	 * accesses by other session during initial labeling.
	 */
	rel = heap_open(catalogId, AccessShareLock);

	sscan = systable_beginscan(rel, InvalidOid, false,
							   SnapshotNow, 0, NULL);
	while (HeapTupleIsValid(tuple = systable_getnext(sscan)))
	{
		Form_pg_namespace	nspForm;
		Form_pg_class		relForm;
		Form_pg_attribute	attForm;
		Form_pg_proc		proForm;
		char			   *objname;
		int					objtype = 1234;
		ObjectAddress		object;
		security_context_t	context;

		/*
		 * The way to determine object name depends on object classes.
		 * So, any branches set up `objtype', `objname' and `object' here.
		 */
		switch (catalogId)
		{
			case NamespaceRelationId:
				nspForm = (Form_pg_namespace) GETSTRUCT(tuple);

				objtype = SELABEL_DB_SCHEMA;

				objname = quote_object_name(database_name,
											NameStr(nspForm->nspname),
											NULL, NULL);

				object.classId = NamespaceRelationId;
				object.objectId = HeapTupleGetOid(tuple);
				object.objectSubId = 0;
				break;

			case RelationRelationId:
				relForm = (Form_pg_class) GETSTRUCT(tuple);

				if (relForm->relkind == RELKIND_RELATION)
					objtype = SELABEL_DB_TABLE;
				else if (relForm->relkind == RELKIND_SEQUENCE)
					objtype = SELABEL_DB_SEQUENCE;
				else if (relForm->relkind == RELKIND_VIEW)
					objtype = SELABEL_DB_VIEW;
				else
					continue;	/* no need to assign security label */

				namespace_name = get_namespace_name(relForm->relnamespace);
				objname = quote_object_name(database_name,
											namespace_name,
											NameStr(relForm->relname),
											NULL);
				pfree(namespace_name);

				object.classId = RelationRelationId;
				object.objectId = HeapTupleGetOid(tuple);
				object.objectSubId = 0;
				break;

			case AttributeRelationId:
				attForm = (Form_pg_attribute) GETSTRUCT(tuple);

				if (get_rel_relkind(attForm->attrelid) != RELKIND_RELATION)
					continue;	/* no need to assign security label */

				objtype = SELABEL_DB_COLUMN;

				namespace_id = get_rel_namespace(attForm->attrelid);
				namespace_name = get_namespace_name(namespace_id);
				relation_name = get_rel_name(attForm->attrelid);
				objname = quote_object_name(database_name,
											namespace_name,
											relation_name,
											NameStr(attForm->attname));
				pfree(namespace_name);
				pfree(relation_name);

				object.classId = RelationRelationId;
				object.objectId = attForm->attrelid;
				object.objectSubId = attForm->attnum;
				break;

			case ProcedureRelationId:
				proForm = (Form_pg_proc) GETSTRUCT(tuple);

				objtype = SELABEL_DB_PROCEDURE;

				namespace_name = get_namespace_name(proForm->pronamespace);
				objname = quote_object_name(database_name,
											namespace_name,
											NameStr(proForm->proname),
											NULL);
				pfree(namespace_name);

				object.classId = ProcedureRelationId;
				object.objectId = HeapTupleGetOid(tuple);
				object.objectSubId = 0;
				break;

			default:
				elog(ERROR, "unexpected catalog id: %u", catalogId);
				objname = NULL;		/* for compiler quiet */
				break;
		}

		if (selabel_lookup_raw(sehnd, &context, objname, objtype) == 0)
		{
			PG_TRY();
			{
				/*
				 * Check SELinux permission to relabel the fetched object,
				 * then do the actual relabeling.
				 */
				sepgsql_object_relabel(&object, context);

				SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, context);
			}
			PG_CATCH();
			{
				freecon(context);
				PG_RE_THROW();
			}
			PG_END_TRY();
			freecon(context);
		}
		else if (errno == ENOENT)
			ereport(WARNING,
					(errmsg("SELinux: no initial label assigned for %s (type=%d), skipping",
							objname, objtype)));
		else
			ereport(ERROR,
					(errcode(ERRCODE_INTERNAL_ERROR),
					 errmsg("SELinux: could not determine initial security label for %s (type=%d): %m", objname, objtype)));

		pfree(objname);
	}
	systable_endscan(sscan);

	heap_close(rel, NoLock);
}
Esempio n. 16
0
/*
 * sepgsql_relation_drop
 *
 * It checks privileges to drop the supplied relation.
 */
void
sepgsql_relation_drop(Oid relOid)
{
	ObjectAddress object;
	char	   *audit_name;
	uint16_t	tclass;
	char		relkind;

	relkind = get_rel_relkind(relOid);
	switch (relkind)
	{
		case RELKIND_RELATION:
			tclass = SEPG_CLASS_DB_TABLE;
			break;
		case RELKIND_SEQUENCE:
			tclass = SEPG_CLASS_DB_SEQUENCE;
			break;
		case RELKIND_VIEW:
			tclass = SEPG_CLASS_DB_VIEW;
			break;
		case RELKIND_INDEX:
			/* ignore indexes on toast tables */
			if (get_rel_namespace(relOid) == PG_TOAST_NAMESPACE)
				return;
			/* other indexes are handled specially below; no need for tclass */
			break;
		default:
			/* ignore other relkinds */
			return;
	}

	/*
	 * check db_schema:{remove_name} permission
	 */
	object.classId = NamespaceRelationId;
	object.objectId = get_rel_namespace(relOid);
	object.objectSubId = 0;
	audit_name = getObjectDescription(&object);

	sepgsql_avc_check_perms(&object,
							SEPG_CLASS_DB_SCHEMA,
							SEPG_DB_SCHEMA__REMOVE_NAME,
							audit_name,
							true);
	pfree(audit_name);

	/* deal with indexes specially */
	if (relkind == RELKIND_INDEX)
	{
		sepgsql_index_modify(relOid);
		return;
	}

	/*
	 * check db_table/sequence/view:{drop} permission
	 */
	object.classId = RelationRelationId;
	object.objectId = relOid;
	object.objectSubId = 0;
	audit_name = getObjectDescription(&object);

	sepgsql_avc_check_perms(&object,
							tclass,
							SEPG_DB_TABLE__DROP,
							audit_name,
							true);
	pfree(audit_name);

	/*
	 * check db_column:{drop} permission
	 */
	if (relkind == RELKIND_RELATION)
	{
		Form_pg_attribute attForm;
		CatCList   *attrList;
		HeapTuple	atttup;
		int			i;

		attrList = SearchSysCacheList1(ATTNUM, ObjectIdGetDatum(relOid));
		for (i = 0; i < attrList->n_members; i++)
		{
			atttup = &attrList->members[i]->tuple;
			attForm = (Form_pg_attribute) GETSTRUCT(atttup);

			if (attForm->attisdropped)
				continue;

			object.classId = RelationRelationId;
			object.objectId = relOid;
			object.objectSubId = attForm->attnum;
			audit_name = getObjectDescription(&object);

			sepgsql_avc_check_perms(&object,
									SEPG_CLASS_DB_COLUMN,
									SEPG_DB_COLUMN__DROP,
									audit_name,
									true);
			pfree(audit_name);
		}
		ReleaseCatCacheList(attrList);
	}
}
Esempio n. 17
0
/*
 * sepgsql_relation_drop
 *
 * It checks privileges to drop the supplied relation.
 */
void
sepgsql_relation_drop(Oid relOid)
{
	ObjectAddress object;
	char	   *audit_name;
	uint16_t	tclass = 0;
	char		relkind;

	relkind = get_rel_relkind(relOid);
	if (relkind == RELKIND_RELATION)
		tclass = SEPG_CLASS_DB_TABLE;
	else if (relkind == RELKIND_SEQUENCE)
		tclass = SEPG_CLASS_DB_SEQUENCE;
	else if (relkind == RELKIND_VIEW)
		tclass = SEPG_CLASS_DB_VIEW;
	else
		return;

	/*
	 * check db_schema:{remove_name} permission
	 */
	object.classId = NamespaceRelationId;
	object.objectId = get_rel_namespace(relOid);
	object.objectSubId = 0;
	audit_name = getObjectDescription(&object);

	sepgsql_avc_check_perms(&object,
							SEPG_CLASS_DB_SCHEMA,
							SEPG_DB_SCHEMA__REMOVE_NAME,
							audit_name,
							true);
	pfree(audit_name);

	/*
	 * check db_table/sequence/view:{drop} permission
	 */
	object.classId = RelationRelationId;
	object.objectId = relOid;
	object.objectSubId = 0;
	audit_name = getObjectDescription(&object);

	sepgsql_avc_check_perms(&object,
							tclass,
							SEPG_DB_TABLE__DROP,
							audit_name,
							true);
	pfree(audit_name);

	/*
	 * check db_column:{drop} permission
	 */
	if (relkind == RELKIND_RELATION)
	{
		Form_pg_attribute attForm;
		CatCList   *attrList;
		HeapTuple	atttup;
		int			i;

		attrList = SearchSysCacheList1(ATTNUM, ObjectIdGetDatum(relOid));
		for (i = 0; i < attrList->n_members; i++)
		{
			atttup = &attrList->members[i]->tuple;
			attForm = (Form_pg_attribute) GETSTRUCT(atttup);

			if (attForm->attisdropped)
				continue;

			object.classId = RelationRelationId;
			object.objectId = relOid;
			object.objectSubId = attForm->attnum;
			audit_name = getObjectDescription(&object);

			sepgsql_avc_check_perms(&object,
									SEPG_CLASS_DB_COLUMN,
									SEPG_DB_COLUMN__DROP,
									audit_name,
									true);
			pfree(audit_name);
		}
		ReleaseCatCacheList(attrList);
	}
}
Esempio n. 18
0
static const char *
get_quoted_nspname(Oid oid)
{
    return quote_identifier(get_namespace_name(get_rel_namespace(oid)));
}
Esempio n. 19
0
/*
 * GetTableDDLEvents takes in a relationId, and returns the list of DDL commands
 * needed to reconstruct the relation. These DDL commands are all palloced; and
 * include the table's schema definition, optional column storage and statistics
 * definitions, and index and constraint defitions.
 */
List *
GetTableDDLEvents(Oid relationId)
{
	List *tableDDLEventList = NIL;
	char tableType = 0;
	List *sequenceIdlist = getOwnedSequences(relationId);
	ListCell *sequenceIdCell;
	char *tableSchemaDef = NULL;
	char *tableColumnOptionsDef = NULL;
	char *schemaName = NULL;
	Oid schemaId = InvalidOid;

	Relation pgIndex = NULL;
	SysScanDesc scanDescriptor = NULL;
	ScanKeyData scanKey[1];
	int scanKeyCount = 1;
	HeapTuple heapTuple = NULL;

	/*
	 * Set search_path to NIL so that all objects outside of pg_catalog will be
	 * schema-prefixed. pg_catalog will be added automatically when we call
	 * PushOverrideSearchPath(), since we set addCatalog to true;
	 */
	OverrideSearchPath *overridePath = GetOverrideSearchPath(CurrentMemoryContext);
	overridePath->schemas = NIL;
	overridePath->addCatalog = true;
	PushOverrideSearchPath(overridePath);

	/* if foreign table, fetch extension and server definitions */
	tableType = get_rel_relkind(relationId);
	if (tableType == RELKIND_FOREIGN_TABLE)
	{
		char *extensionDef = pg_get_extensiondef_string(relationId);
		char *serverDef = pg_get_serverdef_string(relationId);

		if (extensionDef != NULL)
		{
			tableDDLEventList = lappend(tableDDLEventList, extensionDef);
		}
		tableDDLEventList = lappend(tableDDLEventList, serverDef);
	}

	/* create schema if the table is not in the default namespace (public) */
	schemaId = get_rel_namespace(relationId);
	schemaName = get_namespace_name(schemaId);
	if (strncmp(schemaName, "public", NAMEDATALEN) != 0)
	{
		StringInfo schemaNameDef = makeStringInfo();
		appendStringInfo(schemaNameDef, CREATE_SCHEMA_COMMAND, schemaName);

		tableDDLEventList = lappend(tableDDLEventList, schemaNameDef->data);
	}

	/* create sequences if needed */
	foreach(sequenceIdCell, sequenceIdlist)
	{
		Oid sequenceRelid = lfirst_oid(sequenceIdCell);
		char *sequenceDef = pg_get_sequencedef_string(sequenceRelid);

		tableDDLEventList = lappend(tableDDLEventList, sequenceDef);
	}
Esempio n. 20
0
/*
 * check_relation_privileges
 *
 * It actually checks required permissions on a certain relation
 * and its columns.
 */
static bool
check_relation_privileges(Oid relOid,
						  Bitmapset *selected,
						  Bitmapset *inserted,
						  Bitmapset *updated,
						  uint32 required,
						  bool abort_on_violation)
{
	ObjectAddress object;
	char	   *audit_name;
	Bitmapset  *columns;
	int			index;
	char		relkind = get_rel_relkind(relOid);
	bool		result = true;

	/*
	 * Hardwired Policies: SE-PostgreSQL enforces - clients cannot modify
	 * system catalogs using DMLs - clients cannot reference/modify toast
	 * relations using DMLs
	 */
	if (sepgsql_getenforce() > 0)
	{
		Oid			relnamespace = get_rel_namespace(relOid);

		if (IsSystemNamespace(relnamespace) &&
			(required & (SEPG_DB_TABLE__UPDATE |
						 SEPG_DB_TABLE__INSERT |
						 SEPG_DB_TABLE__DELETE)) != 0)
			ereport(ERROR,
					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
					 errmsg("SELinux: hardwired security policy violation")));

		if (relkind == RELKIND_TOASTVALUE)
			ereport(ERROR,
					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
					 errmsg("SELinux: hardwired security policy violation")));
	}

	/*
	 * Check permissions on the relation
	 */
	object.classId = RelationRelationId;
	object.objectId = relOid;
	object.objectSubId = 0;
	audit_name = getObjectIdentity(&object);
	switch (relkind)
	{
		case RELKIND_RELATION:
		case RELKIND_PARTITIONED_TABLE:
			result = sepgsql_avc_check_perms(&object,
											 SEPG_CLASS_DB_TABLE,
											 required,
											 audit_name,
											 abort_on_violation);
			break;

		case RELKIND_SEQUENCE:
			Assert((required & ~SEPG_DB_TABLE__SELECT) == 0);

			if (required & SEPG_DB_TABLE__SELECT)
				result = sepgsql_avc_check_perms(&object,
												 SEPG_CLASS_DB_SEQUENCE,
												 SEPG_DB_SEQUENCE__GET_VALUE,
												 audit_name,
												 abort_on_violation);
			break;

		case RELKIND_VIEW:
			result = sepgsql_avc_check_perms(&object,
											 SEPG_CLASS_DB_VIEW,
											 SEPG_DB_VIEW__EXPAND,
											 audit_name,
											 abort_on_violation);
			break;

		default:
			/* nothing to be checked */
			break;
	}
	pfree(audit_name);

	/*
	 * Only columns owned by relations shall be checked
	 */
	if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE)
		return true;

	/*
	 * Check permissions on the columns
	 */
	selected = fixup_whole_row_references(relOid, selected);
	inserted = fixup_whole_row_references(relOid, inserted);
	updated = fixup_whole_row_references(relOid, updated);
	columns = bms_union(selected, bms_union(inserted, updated));

	while ((index = bms_first_member(columns)) >= 0)
	{
		AttrNumber	attnum;
		uint32		column_perms = 0;

		if (bms_is_member(index, selected))
			column_perms |= SEPG_DB_COLUMN__SELECT;
		if (bms_is_member(index, inserted))
		{
			if (required & SEPG_DB_TABLE__INSERT)
				column_perms |= SEPG_DB_COLUMN__INSERT;
		}
		if (bms_is_member(index, updated))
		{
			if (required & SEPG_DB_TABLE__UPDATE)
				column_perms |= SEPG_DB_COLUMN__UPDATE;
		}
		if (column_perms == 0)
			continue;

		/* obtain column's permission */
		attnum = index + FirstLowInvalidHeapAttributeNumber;

		object.classId = RelationRelationId;
		object.objectId = relOid;
		object.objectSubId = attnum;
		audit_name = getObjectDescription(&object);

		result = sepgsql_avc_check_perms(&object,
										 SEPG_CLASS_DB_COLUMN,
										 column_perms,
										 audit_name,
										 abort_on_violation);
		pfree(audit_name);

		if (!result)
			return result;
	}
	return true;
}
Esempio n. 21
0
/*
 * Creates a sample table with data from a PXF table.
 * We need to create a copy of the PXF table, in order to pass the sampling
 * parameters pxf_sample_ratio and pxf_max_fragments as attributes,
 * and to create a segment reject limit of 25 percent.
 *
 * The new PXF table is sampled and the results are saved in the returned sample table.
 * Note that ANALYZE can be executed only by the database owner.
 * It is safe to assume that the database owner has permissions to create temp tables.
 * The sampling is done by uniformly sampling pxf_sample_ratio records of each fragments,
 * up to pxf_max_fragments.
 *
 * Input:
 * 	relationOid 	- relation to be sampled
 * 	sampleTableName - sample table name, moderately unique
 * 	lAttributeNames - attributes to be included in the sample
 * 	relTuples		- estimated size of relation
 * 	relFrags		- estimated number of fragments in relation
 * 	requestedSampleSize - as determined by attribute statistics requirements.
 * 	sampleTableRelTuples	- limit on size of the sample.
 * Output:
 * 	sampleTableRelTuples - number of tuples in the sample table created.
 */
Oid buildPxfSampleTable(Oid relationOid,
		char* sampleTableName,
		List *lAttributeNames,
		float4	relTuples,
		float4  relFrags,
		float4 	requestedSampleSize,
		float4 *sampleTableRelTuples)
{
	const char *schemaName = get_namespace_name(get_rel_namespace(relationOid)); /* must be pfreed */
	const char *tableName = get_rel_name(relationOid); /* must be pfreed */
	char	*sampleSchemaName = pstrdup("pg_temp");
	char	*pxfSampleTable = temporarySampleTableName(relationOid, "pg_analyze_pxf"); /* must be pfreed */
	Oid			sampleTableOid = InvalidOid;
	Oid			pxfSampleTableOid = InvalidOid;
	RangeVar 	*rangeVar = NULL;
	float4 pxfSamplingRatio = 0.0;

	Assert(requestedSampleSize > 0.0);
	Assert(relTuples > 0.0);
	Assert(relFrags > 0.0);

	/* calculate pxf_sample_ratio */
	pxfSamplingRatio = calculateSamplingRatio(relTuples, relFrags, requestedSampleSize);

	/* build copy of original pxf table */
	buildPxfTableCopy(relationOid,
					  pxfSamplingRatio,
					  pxf_stat_max_fragments,
					  schemaName, tableName,
					  sampleSchemaName, pxfSampleTable);

	rangeVar = makeRangeVar(NULL /*catalogname*/, sampleSchemaName, pxfSampleTable, -1);
	pxfSampleTableOid = RangeVarGetRelid(rangeVar, true /* failOK */, false /*allowHcatalog*/);

	buildSampleFromPxf(sampleSchemaName, sampleTableName, pxfSampleTable,
					   lAttributeNames, sampleTableRelTuples);

	rangeVar = makeRangeVar(NULL /*catalogname*/, sampleSchemaName, sampleTableName, -1);
	sampleTableOid = RangeVarGetRelid(rangeVar, true /* failOK */, false /*allowHcatalog*/);

	Assert(sampleTableOid != InvalidOid);

	/**
	 * MPP-10723: Very rarely, we may be unlucky and generate an empty sample table. We error out in this case rather than
	 * generate bad statistics.
	 */

	if (*sampleTableRelTuples < 1.0)
	{
		elog(ERROR, "ANALYZE unable to generate accurate statistics on table %s.%s. Try lowering gp_analyze_relative_error",
				quote_identifier(schemaName),
				quote_identifier(tableName));
	}

	if (pxfSampleTableOid != InvalidOid)
	{
		elog(DEBUG2, "ANALYZE dropping PXF sample table");
		dropSampleTable(pxfSampleTableOid, true);
	}

	pfree((void *) rangeVar);
	pfree((void *) pxfSampleTable);
	pfree((void *) tableName);
	pfree((void *) schemaName);
	pfree((void *) sampleSchemaName);
	return sampleTableOid;
}
Esempio n. 22
0
/*
 * Executes an ALTER OBJECT / RENAME TO statement.	Based on the object
 * type, the function appropriate to that type is executed.
 */
void
ExecRenameStmt(RenameStmt *stmt)
{
	switch (stmt->renameType)
	{
		case OBJECT_AGGREGATE:
			RenameAggregate(stmt->object, stmt->objarg, stmt->newname);
			break;

		case OBJECT_CONVERSION:
			RenameConversion(stmt->object, stmt->newname);
			break;

		case OBJECT_DATABASE:
			RenameDatabase(stmt->subname, stmt->newname);
			break;

		case OBJECT_EXTPROTOCOL:
			RenameExtProtocol(stmt->subname, stmt->newname);
			break;

		case OBJECT_FUNCTION:
			RenameFunction(stmt->object, stmt->objarg, stmt->newname);
			break;

		case OBJECT_LANGUAGE:
			RenameLanguage(stmt->subname, stmt->newname);
			break;

		case OBJECT_OPCLASS:
			RenameOpClass(stmt->object, stmt->subname, stmt->newname);
			break;

		case OBJECT_OPFAMILY:
			RenameOpFamily(stmt->object, stmt->subname, stmt->newname);
			break;

		case OBJECT_ROLE:
			RenameRole(stmt->subname, stmt->newname);
			break;

		case OBJECT_SCHEMA:
			RenameSchema(stmt->subname, stmt->newname);
			break;

		case OBJECT_TABLESPACE:
			RenameTableSpace(stmt->subname, stmt->newname);
			break;

		case OBJECT_FILESPACE:
			RenameFileSpace(stmt->subname, stmt->newname);
			break;

		case OBJECT_TABLE:
		case OBJECT_SEQUENCE:
		case OBJECT_VIEW:
		case OBJECT_INDEX:
		{
			if (Gp_role == GP_ROLE_DISPATCH)
			{
				CheckRelationOwnership(stmt->relation, true);
				stmt->objid = RangeVarGetRelid(stmt->relation, false);
			}

			/*
			 * RENAME TABLE requires that we (still) hold
			 * CREATE rights on the containing namespace, as
			 * well as ownership of the table.
			 */
			Oid			namespaceId = get_rel_namespace(stmt->objid);
			AclResult	aclresult;

			aclresult = pg_namespace_aclcheck(namespaceId,
											  GetUserId(),
											  ACL_CREATE);
			if (aclresult != ACLCHECK_OK)
				aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
							   get_namespace_name(namespaceId));

			renamerel(stmt->objid, stmt->newname, stmt->renameType, stmt);
			break;
		}

		case OBJECT_COLUMN:
		case OBJECT_TRIGGER:
			{
				Oid			relid;

				CheckRelationOwnership(stmt->relation, true);

				relid = RangeVarGetRelid(stmt->relation, false);

				switch (stmt->renameType)
				{
					case OBJECT_COLUMN:
						renameatt(relid,
								  stmt->subname,		/* old att name */
								  stmt->newname,		/* new att name */
								  interpretInhOption(stmt->relation->inhOpt),	/* recursive? */
								  false);		/* recursing already? */
						break;
					case OBJECT_TRIGGER:
						renametrig(relid,
								   stmt->subname,		/* old att name */
								   stmt->newname);		/* new att name */
						break;
					default:
						 /* can't happen */ ;
				}
				break;
			}

		case OBJECT_TSPARSER:
			RenameTSParser(stmt->object, stmt->newname);
			break;

		case OBJECT_TSDICTIONARY:
			RenameTSDictionary(stmt->object, stmt->newname);
			break;

		case OBJECT_TSTEMPLATE:
			RenameTSTemplate(stmt->object, stmt->newname);
			break;

		case OBJECT_TSCONFIGURATION:
			RenameTSConfiguration(stmt->object, stmt->newname);
			break;

		default:
			elog(ERROR, "unrecognized rename stmt type: %d",
				 (int) stmt->renameType);
	}
	if (Gp_role == GP_ROLE_DISPATCH)
	{
		CdbDispatchUtilityStatement((Node *) stmt, "ExecRenameStmt");
	}

}
Esempio n. 23
0
static const char *
get_quoted_nspname(Oid oid)
{
	const char *nspname = get_namespace_name(get_rel_namespace(oid));
	return (nspname ? quote_identifier(nspname) : NULL);
}
Esempio n. 24
0
static void pg_decode_change(LogicalDecodingContext* ctx, ReorderBufferTXN* txn, Relation relation, ReorderBufferChange* change) {
  DecodingJsonData* data;
  Form_pg_class class_form;
  TupleDesc  tupdesc;
  HeapTuple tuple;
  MemoryContext old;

  data = ctx->output_plugin_private;

  data->xact_wrote_changes = true;

  class_form = RelationGetForm(relation);
  tupdesc = RelationGetDescr(relation);

  old = MemoryContextSwitchTo(data->context);

  OutputPluginPrepareWrite(ctx, true);

  appendStringInfoString(ctx->out, "{\"type\":\"table\"");
  appendStringInfo(
    ctx->out,
    ",\"schema\":\"%s\"",
    get_namespace_name(
      get_rel_namespace(
        RelationGetRelid(relation)
      )
    )
  );
  appendStringInfo(ctx->out, ",\"name\":\"%s\"", NameStr(class_form->relname));
  appendStringInfo(
    ctx->out,
    ",\"change\":\"%s\"",
    change->action == REORDER_BUFFER_CHANGE_INSERT
      ? "INSERT"
      : change->action == REORDER_BUFFER_CHANGE_UPDATE
        ? "UPDATE"
        : change->action == REORDER_BUFFER_CHANGE_DELETE
          ? "DELETE"
          : "FIXME"
  );

  if (change->action == REORDER_BUFFER_CHANGE_UPDATE || change->action == REORDER_BUFFER_CHANGE_DELETE) {
    appendStringInfoString(ctx->out, ",\"key\":{");
    RelationGetIndexList(relation);
    if (OidIsValid(relation->rd_replidindex)) {
      int i;
      Relation index = index_open(relation->rd_replidindex, ShareLock);
      tuple =
        change->data.tp.oldtuple
          ? &change->data.tp.oldtuple->tuple
          : &change->data.tp.newtuple->tuple;
      for (i = 0; i < index->rd_index->indnatts; i++) {
        int j = index->rd_index->indkey.values[i];
        Form_pg_attribute attr = tupdesc->attrs[j - 1];
        if (i > 0) appendStringInfoChar(ctx->out, ',');
        appendStringInfo(ctx->out, "\"%s\":", NameStr(attr->attname));
        print_value(ctx->out, tupdesc, tuple, j - 1);
      }
      index_close(index, NoLock);
    } else {
      appendStringInfoString(ctx->out, "\"***FIXME***\"");
    }
    appendStringInfoChar(ctx->out, '}');
  }

  if (change->action == REORDER_BUFFER_CHANGE_UPDATE || change->action == REORDER_BUFFER_CHANGE_INSERT) {
    appendStringInfoString(ctx->out, ",\"data\":{");
    tuple_to_stringinfo(ctx->out, tupdesc, &change->data.tp.newtuple->tuple, false);
    appendStringInfoChar(ctx->out, '}');
  }
  appendStringInfoChar(ctx->out, '}');

  MemoryContextSwitchTo(old);
  MemoryContextReset(data->context);

  OutputPluginWrite(ctx, true);
}
Esempio n. 25
0
/*
 * Executes an ALTER OBJECT / RENAME TO statement.	Based on the object
 * type, the function appropriate to that type is executed.
 */
void
ExecRenameStmt(RenameStmt *stmt)
{
    switch (stmt->renameType)
    {
    case OBJECT_AGGREGATE:
        RenameAggregate(stmt->object, stmt->objarg, stmt->newname);
        break;

    case OBJECT_CONVERSION:
        RenameConversion(stmt->object, stmt->newname);
        break;

    case OBJECT_DATABASE:
        RenameDatabase(stmt->subname, stmt->newname);
        break;

    case OBJECT_FUNCTION:
        RenameFunction(stmt->object, stmt->objarg, stmt->newname);
        break;

    case OBJECT_LANGUAGE:
        RenameLanguage(stmt->subname, stmt->newname);
        break;

    case OBJECT_OPCLASS:
        RenameOpClass(stmt->object, stmt->subname, stmt->newname);
        break;

    case OBJECT_ROLE:
        RenameRole(stmt->subname, stmt->newname);
        break;

    case OBJECT_SCHEMA:
        RenameSchema(stmt->subname, stmt->newname);
        break;

    case OBJECT_TABLESPACE:
        RenameTableSpace(stmt->subname, stmt->newname);
        break;

    case OBJECT_TABLE:
    case OBJECT_INDEX:
    case OBJECT_COLUMN:
    case OBJECT_TRIGGER:
    {
        Oid			relid;

        CheckRelationOwnership(stmt->relation, true);

        relid = RangeVarGetRelid(stmt->relation, false);

        switch (stmt->renameType)
        {
        case OBJECT_TABLE:
        case OBJECT_INDEX:
        {
            /*
             * RENAME TABLE requires that we (still) hold
             * CREATE rights on the containing namespace, as
             * well as ownership of the table.
             */
            Oid			namespaceId = get_rel_namespace(relid);
            AclResult	aclresult;

            aclresult = pg_namespace_aclcheck(namespaceId,
                                              GetUserId(),
                                              ACL_CREATE);
            if (aclresult != ACLCHECK_OK)
                aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
                               get_namespace_name(namespaceId));

            renamerel(relid, stmt->newname);
            break;
        }
        case OBJECT_COLUMN:
            renameatt(relid,
                      stmt->subname,		/* old att name */
                      stmt->newname,		/* new att name */
                      interpretInhOption(stmt->relation->inhOpt),	/* recursive? */
                      false);		/* recursing already? */
            break;
        case OBJECT_TRIGGER:
            renametrig(relid,
                       stmt->subname,		/* old att name */
                       stmt->newname);		/* new att name */
            break;
        default:
            /* can't happen */
            ;
        }
        break;
    }

    default:
        elog(ERROR, "unrecognized rename stmt type: %d",
             (int) stmt->renameType);
    }
}
Esempio n. 26
0
/*
 * master_append_table_to_shard appends the given table's contents to the given
 * shard, and updates shard metadata on the master node. If the function fails
 * to append table data to all shard placements, it doesn't update any metadata
 * and errors out. Else if the function fails to append table data to some of
 * the shard placements, it marks those placements as invalid. These invalid
 * placements will get cleaned up during shard rebalancing.
 */
Datum
master_append_table_to_shard(PG_FUNCTION_ARGS)
{
	uint64 shardId = PG_GETARG_INT64(0);
	text *sourceTableNameText = PG_GETARG_TEXT_P(1);
	text *sourceNodeNameText = PG_GETARG_TEXT_P(2);
	uint32 sourceNodePort = PG_GETARG_UINT32(3);

	char *sourceTableName = text_to_cstring(sourceTableNameText);
	char *sourceNodeName = text_to_cstring(sourceNodeNameText);

	Oid shardSchemaOid = 0;
	char *shardSchemaName = NULL;
	char *shardTableName = NULL;
	char *shardQualifiedName = NULL;
	List *shardPlacementList = NIL;
	List *succeededPlacementList = NIL;
	List *failedPlacementList = NIL;
	ListCell *shardPlacementCell = NULL;
	ListCell *failedPlacementCell = NULL;
	uint64 newShardSize = 0;
	uint64 shardMaxSizeInBytes = 0;
	float4 shardFillLevel = 0.0;
	char partitionMethod = 0;

	ShardInterval *shardInterval = LoadShardInterval(shardId);
	Oid relationId = shardInterval->relationId;
	bool cstoreTable = CStoreTable(relationId);

	char storageType = shardInterval->storageType;

	EnsureTablePermissions(relationId, ACL_INSERT);

	if (storageType != SHARD_STORAGE_TABLE && !cstoreTable)
	{
		ereport(ERROR, (errmsg("cannot append to shardId " UINT64_FORMAT, shardId),
						errdetail("The underlying shard is not a regular table")));
	}

	partitionMethod = PartitionMethod(relationId);
	if (partitionMethod == DISTRIBUTE_BY_HASH)
	{
		ereport(ERROR, (errmsg("cannot append to shardId " UINT64_FORMAT, shardId),
						errdetail("We currently don't support appending to shards "
								  "in hash-partitioned tables")));
	}

	/*
	 * 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 appends to a shard happen in a serial manner.
	 */
	LockShardResource(shardId, AccessExclusiveLock);

	/* get schame name of the target shard */
	shardSchemaOid = get_rel_namespace(relationId);
	shardSchemaName = get_namespace_name(shardSchemaOid);

	/* Build shard table name. */
	shardTableName = get_rel_name(relationId);
	AppendShardIdToName(&shardTableName, shardId);

	shardQualifiedName = quote_qualified_identifier(shardSchemaName, shardTableName);

	shardPlacementList = FinalizedShardPlacementList(shardId);
	if (shardPlacementList == NIL)
	{
		ereport(ERROR, (errmsg("could not find any shard placements for shardId "
							   UINT64_FORMAT, shardId),
						errhint("Try running master_create_empty_shard() first")));
	}

	/* issue command to append table to each shard placement */
	foreach(shardPlacementCell, shardPlacementList)
	{
		ShardPlacement *shardPlacement = (ShardPlacement *) lfirst(shardPlacementCell);
		char *workerName = shardPlacement->nodeName;
		uint32 workerPort = shardPlacement->nodePort;
		List *queryResultList = NIL;

		StringInfo workerAppendQuery = makeStringInfo();
		appendStringInfo(workerAppendQuery, WORKER_APPEND_TABLE_TO_SHARD,
						 quote_literal_cstr(shardQualifiedName),
						 quote_literal_cstr(sourceTableName),
						 quote_literal_cstr(sourceNodeName), sourceNodePort);

		/* inserting data should be performed by the current user */
		queryResultList = ExecuteRemoteQuery(workerName, workerPort, NULL,
											 workerAppendQuery);
		if (queryResultList != NIL)
		{
			succeededPlacementList = lappend(succeededPlacementList, shardPlacement);
		}
		else
		{
			failedPlacementList = lappend(failedPlacementList, shardPlacement);
		}
	}