/*
 * CREATE SCHEMA
 */
Oid
CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString)
{
	const char *schemaName = stmt->schemaname;
	Oid			namespaceId;
	OverrideSearchPath *overridePath;
	List	   *parsetree_list;
	ListCell   *parsetree_item;
	Oid			owner_uid;
	Oid			saved_uid;
	int			save_sec_context;
	AclResult	aclresult;
	ObjectAddress address;

	GetUserIdAndSecContext(&saved_uid, &save_sec_context);

	/*
	 * Who is supposed to own the new schema?
	 */
	if (stmt->authrole)
		owner_uid = get_rolespec_oid(stmt->authrole, false);
	else
		owner_uid = saved_uid;

	/* fill schema name with the user name if not specified */
	if (!schemaName)
	{
		HeapTuple	tuple;

		tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(owner_uid));
		if (!HeapTupleIsValid(tuple))
			elog(ERROR, "cache lookup failed for role %u", owner_uid);
		schemaName =
			pstrdup(NameStr(((Form_pg_authid) GETSTRUCT(tuple))->rolname));
		ReleaseSysCache(tuple);
	}

	/*
	 * To create a schema, must have schema-create privilege on the current
	 * database and must be able to become the target role (this does not
	 * imply that the target role itself must have create-schema privilege).
	 * The latter provision guards against "giveaway" attacks.  Note that a
	 * superuser will always have both of these privileges a fortiori.
	 */
	aclresult = pg_database_aclcheck(MyDatabaseId, saved_uid, ACL_CREATE);
	if (aclresult != ACLCHECK_OK)
		aclcheck_error(aclresult, ACL_KIND_DATABASE,
					   get_database_name(MyDatabaseId));

	check_is_member_of_role(saved_uid, owner_uid);

	/* Additional check to protect reserved schema names */
	if (!allowSystemTableMods && IsReservedName(schemaName))
		ereport(ERROR,
				(errcode(ERRCODE_RESERVED_NAME),
				 errmsg("unacceptable schema name \"%s\"", schemaName),
		   errdetail("The prefix \"pg_\" is reserved for system schemas.")));

	/*
	 * If if_not_exists was given and the schema already exists, bail out.
	 * (Note: we needn't check this when not if_not_exists, because
	 * NamespaceCreate will complain anyway.)  We could do this before making
	 * the permissions checks, but since CREATE TABLE IF NOT EXISTS makes its
	 * creation-permission check first, we do likewise.
	 */
	if (stmt->if_not_exists &&
		SearchSysCacheExists1(NAMESPACENAME, PointerGetDatum(schemaName)))
	{
		ereport(NOTICE,
				(errcode(ERRCODE_DUPLICATE_SCHEMA),
				 errmsg("schema \"%s\" already exists, skipping",
						schemaName)));
		return InvalidOid;
	}

	/*
	 * If the requested authorization is different from the current user,
	 * temporarily set the current user so that the object(s) will be created
	 * with the correct ownership.
	 *
	 * (The setting will be restored at the end of this routine, or in case of
	 * error, transaction abort will clean things up.)
	 */
	if (saved_uid != owner_uid)
		SetUserIdAndSecContext(owner_uid,
							save_sec_context | SECURITY_LOCAL_USERID_CHANGE);

	/* Create the schema's namespace */
	namespaceId = NamespaceCreate(schemaName, owner_uid, false);

	/* Advance cmd counter to make the namespace visible */
	CommandCounterIncrement();

	/*
	 * Temporarily make the new namespace be the front of the search path, as
	 * well as the default creation target namespace.  This will be undone at
	 * the end of this routine, or upon error.
	 */
	overridePath = GetOverrideSearchPath(CurrentMemoryContext);
	overridePath->schemas = lcons_oid(namespaceId, overridePath->schemas);
	/* XXX should we clear overridePath->useTemp? */
	PushOverrideSearchPath(overridePath);

	/*
	 * Report the new schema to possibly interested event triggers.  Note we
	 * must do this here and not in ProcessUtilitySlow because otherwise the
	 * objects created below are reported before the schema, which would be
	 * wrong.
	 */
	ObjectAddressSet(address, NamespaceRelationId, namespaceId);
	EventTriggerCollectSimpleCommand(address, InvalidObjectAddress,
									 (Node *) stmt);

	/*
	 * Examine the list of commands embedded in the CREATE SCHEMA command, and
	 * reorganize them into a sequentially executable order with no forward
	 * references.  Note that the result is still a list of raw parsetrees ---
	 * we cannot, in general, run parse analysis on one statement until we
	 * have actually executed the prior ones.
	 */
	parsetree_list = transformCreateSchemaStmt(stmt);

	/*
	 * Execute each command contained in the CREATE SCHEMA.  Since the grammar
	 * allows only utility commands in CREATE SCHEMA, there is no need to pass
	 * them through parse_analyze() or the rewriter; we can just hand them
	 * straight to ProcessUtility.
	 */
	foreach(parsetree_item, parsetree_list)
	{
		Node	   *stmt = (Node *) lfirst(parsetree_item);

		/* do this step */
		ProcessUtility(stmt,
					   queryString,
					   PROCESS_UTILITY_SUBCOMMAND,
					   NULL,
					   None_Receiver,
					   NULL);
		/* make sure later steps can see the object created here */
		CommandCounterIncrement();
	}
Example #2
0
CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString)
#endif
{
	const char *schemaName = stmt->schemaname;
	const char *authId = stmt->authid;
	Oid			namespaceId;
	OverrideSearchPath *overridePath;
	List	   *parsetree_list;
	ListCell   *parsetree_item;
	Oid			owner_uid;
	Oid			saved_uid;
	int			save_sec_context;
	AclResult	aclresult;

	GetUserIdAndSecContext(&saved_uid, &save_sec_context);

	/*
	 * Who is supposed to own the new schema?
	 */
	if (authId)
		owner_uid = get_role_oid(authId, false);
	else
		owner_uid = saved_uid;

	/*
	 * To create a schema, must have schema-create privilege on the current
	 * database and must be able to become the target role (this does not
	 * imply that the target role itself must have create-schema privilege).
	 * The latter provision guards against "giveaway" attacks.	Note that a
	 * superuser will always have both of these privileges a fortiori.
	 */
	aclresult = pg_database_aclcheck(MyDatabaseId, saved_uid, ACL_CREATE);
	if (aclresult != ACLCHECK_OK)
		aclcheck_error(aclresult, ACL_KIND_DATABASE,
					   get_database_name(MyDatabaseId));

	check_is_member_of_role(saved_uid, owner_uid);

	/* Additional check to protect reserved schema names */
	if (!allowSystemTableMods && IsReservedName(schemaName))
		ereport(ERROR,
				(errcode(ERRCODE_RESERVED_NAME),
				 errmsg("unacceptable schema name \"%s\"", schemaName),
		   errdetail("The prefix \"pg_\" is reserved for system schemas.")));

	/*
	 * If the requested authorization is different from the current user,
	 * temporarily set the current user so that the object(s) will be created
	 * with the correct ownership.
	 *
	 * (The setting will be restored at the end of this routine, or in case of
	 * error, transaction abort will clean things up.)
	 */
	if (saved_uid != owner_uid)
		SetUserIdAndSecContext(owner_uid,
							save_sec_context | SECURITY_LOCAL_USERID_CHANGE);

	/* Create the schema's namespace */
	namespaceId = NamespaceCreate(schemaName, owner_uid, false);

	/* Advance cmd counter to make the namespace visible */
	CommandCounterIncrement();

	/*
	 * Temporarily make the new namespace be the front of the search path, as
	 * well as the default creation target namespace.  This will be undone at
	 * the end of this routine, or upon error.
	 */
	overridePath = GetOverrideSearchPath(CurrentMemoryContext);
	overridePath->schemas = lcons_oid(namespaceId, overridePath->schemas);
	/* XXX should we clear overridePath->useTemp? */
	PushOverrideSearchPath(overridePath);

	/*
	 * Examine the list of commands embedded in the CREATE SCHEMA command, and
	 * reorganize them into a sequentially executable order with no forward
	 * references.	Note that the result is still a list of raw parsetrees ---
	 * we cannot, in general, run parse analysis on one statement until we
	 * have actually executed the prior ones.
	 */
	parsetree_list = transformCreateSchemaStmt(stmt);

#ifdef PGXC
	/*
	 * Add a RemoteQuery node for a query at top level on a remote Coordinator,
	 * if not done already.
	 */
	if (!sentToRemote)
		parsetree_list = AddRemoteQueryNode(parsetree_list, queryString,
											EXEC_ON_ALL_NODES, false);
#endif

	/*
	 * Execute each command contained in the CREATE SCHEMA.  Since the grammar
	 * allows only utility commands in CREATE SCHEMA, there is no need to pass
	 * them through parse_analyze() or the rewriter; we can just hand them
	 * straight to ProcessUtility.
	 */
	foreach(parsetree_item, parsetree_list)
	{
		Node	   *stmt = (Node *) lfirst(parsetree_item);

		/* do this step */
		ProcessUtility(stmt,
					   queryString,
					   NULL,
					   false,	/* not top level */
					   None_Receiver,
#ifdef PGXC
					   true,
#endif /* PGXC */
					   NULL);
		/* make sure later steps can see the object created here */
		CommandCounterIncrement();
	}
/*
 * CREATE SCHEMA
 */
void
CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString)
{
	const char *schemaName = stmt->schemaname;
	const char *authId = stmt->authid;
	const bool  istemp = stmt->istemp;
	Oid			namespaceId = 0;
	List	   *parsetree_list;
	ListCell   *parsetree_item;
	Oid			owner_uid;
	Oid			saved_uid;
	AclResult	aclresult;
	bool        saved_secdefcxt;
/*	bool		shouldDispatch = (Gp_role == GP_ROLE_DISPATCH &&
								  !IsBootstrapProcessingMode());*/

	GetUserIdAndContext(&saved_uid, &saved_secdefcxt);

	/*
	 * Who is supposed to own the new schema?
	 */
	if (authId)
		owner_uid = get_roleid_checked(authId);
	else
		owner_uid = saved_uid;

	/* 
	 * If we are creating a temporary schema then we can skip a 
	 * bunch of checks that we would otherwise make.
	 */
	if (istemp)
	{
		/*
		 * CDB: Delete old temp schema.
		 *
		 * Remove any vestigages of old temporary schema, if any.  This can
		 * happen when an old session crashes and doesn't run normal session
		 * shutdown.  
		 *
		 * In postgres they try to reuse existing schemas in this case, 
		 * however that does not work well for us since the schemas may exist 
		 * on a segment by segment basis and we want to keep them syncronized
		 * on oid.  The best way of dealing with this is to just delete the
		 * old schemas.
		 */
		RemoveSchema_internal(schemaName, DROP_CASCADE, true, true);
	}
	else
	{
		/*
		 * To create a schema, must have schema-create privilege on the current
		 * database and must be able to become the target role (this does not
		 * imply that the target role itself must have create-schema privilege).
		 * The latter provision guards against "giveaway" attacks. Note that a
		 * superuser will always have both of these privileges a fortiori.
		 */
		aclresult = pg_database_aclcheck(MyDatabaseId, saved_uid, ACL_CREATE);
		if (aclresult != ACLCHECK_OK)
			aclcheck_error(aclresult, ACL_KIND_DATABASE,
						   get_database_name(MyDatabaseId));

		check_is_member_of_role(saved_uid, owner_uid);

		/* Additional check to protect reserved schema names */
		if (!allowSystemTableModsDDL &&
			(IsReservedName(schemaName) || strcmp(schemaName, "madlib") == 0))
		{
			ereport(ERROR,
					(errcode(ERRCODE_RESERVED_NAME),
					 errmsg("unacceptable schema name \"%s\"", schemaName),
					 errdetail("The prefix \"%s\" is reserved for system schemas.",
							   GetReservedPrefix(schemaName))));
		}
	}

	/*
	 * If the requested authorization is different from the current user,
	 * temporarily set the current user so that the object(s) will be created
	 * with the correct ownership.
	 *
	 * (The setting will be restored at the end of this routine, or in case
	 * of error, transaction abort will clean things up.)
	 */
	if (saved_uid != owner_uid)
		SetUserIdAndContext(owner_uid, true);

	/*
	 * in hawq, should be only called on master except
	 * UPGRADE model.
	 */
	Assert(gp_upgrade_mode || (Gp_role != GP_ROLE_EXECUTE));
	/* Create the schema's namespace */
    namespaceId = NamespaceCreate(schemaName, owner_uid, 0);

    /* MPP-6929: metadata tracking */
    if (Gp_role == GP_ROLE_DISPATCH && !istemp)
        MetaTrackAddObject(NamespaceRelationId,
                           namespaceId,
                           saved_uid,
                           "CREATE", "SCHEMA"
                );

	/* Advance cmd counter to make the namespace visible */
	CommandCounterIncrement();

	/* If this is the temporary namespace we must mark it specially */
	if (istemp)
		SetTempNamespace(namespaceId);

	/*
	 * Temporarily make the new namespace be the front of the search path, as
	 * well as the default creation target namespace.  This will be undone at
	 * the end of this routine, or upon error.
	 */
	PushSpecialNamespace(namespaceId);

	/*
	 * Examine the list of commands embedded in the CREATE SCHEMA command, and
	 * reorganize them into a sequentially executable order with no forward
	 * references.	Note that the result is still a list of raw parsetrees in
	 * need of parse analysis --- we cannot, in general, run analyze.c on one
	 * statement until we have actually executed the prior ones.
	 */
	parsetree_list = analyzeCreateSchemaStmt(stmt);

	/*
	 * Analyze and execute each command contained in the CREATE SCHEMA
	 */
	foreach(parsetree_item, parsetree_list)
	{
		Node	   *parsetree = (Node *) lfirst(parsetree_item);
		List	   *querytree_list;
		ListCell   *querytree_item;

		querytree_list = parse_analyze(parsetree, NULL, NULL, 0);

		foreach(querytree_item, querytree_list)
		{
			Query	   *querytree = (Query *) lfirst(querytree_item);

			/* schemas should contain only utility stmts */
			Assert(querytree->commandType == CMD_UTILITY);
			/* do this step */
			ProcessUtility(querytree->utilityStmt, 
						   queryString,
						   NULL, 
						   false, /* not top level */
						   None_Receiver, 
						   NULL);
			/* make sure later steps can see the object created here */
			CommandCounterIncrement();
		}
Example #4
0
/*
 * CREATE SCHEMA
 */
void
CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString)
{
    const char *schemaName = stmt->schemaname;
    const char *authId = stmt->authid;
    Oid			namespaceId;
    OverrideSearchPath *overridePath;
    List	   *parsetree_list;
    ListCell   *parsetree_item;
    Oid			owner_uid;
    Oid			saved_uid;
    int			save_sec_context;
    AclResult	aclresult;
    bool		shouldDispatch = (Gp_role == GP_ROLE_DISPATCH &&
                                  !IsBootstrapProcessingMode());

    /*
     * GPDB: Creation of temporary namespaces is a special case. This statement
     * is dispatched by the dispatcher node the first time a temporary table is
     * created. It bypasses all the normal checks and logic of schema creation,
     * and is routed to the internal routine for creating temporary namespaces,
     * instead.
     */
    if (stmt->istemp)
    {
        Assert(Gp_role == GP_ROLE_EXECUTE);

        Assert(stmt->schemaname == InvalidOid);
        Assert(stmt->authid == NULL);
        Assert(stmt->schemaElts == NIL);
        Assert(stmt->schemaOid != InvalidOid);
        Assert(stmt->toastSchemaOid != InvalidOid);

        InitTempTableNamespaceWithOids(stmt->schemaOid,
                                       stmt->toastSchemaOid);
        return;
    }

    GetUserIdAndSecContext(&saved_uid, &save_sec_context);

    /*
     * Who is supposed to own the new schema?
     */
    if (authId)
        owner_uid = get_roleid_checked(authId);
    else
        owner_uid = saved_uid;

    /*
     * To create a schema, must have schema-create privilege on the current
     * database and must be able to become the target role (this does not
     * imply that the target role itself must have create-schema privilege).
     * The latter provision guards against "giveaway" attacks.	Note that a
     * superuser will always have both of these privileges a fortiori.
     */
    aclresult = pg_database_aclcheck(MyDatabaseId, saved_uid, ACL_CREATE);
    if (aclresult != ACLCHECK_OK)
        aclcheck_error(aclresult, ACL_KIND_DATABASE,
                       get_database_name(MyDatabaseId));

    check_is_member_of_role(saved_uid, owner_uid);

    /* Additional check to protect reserved schema names */
    if (!allowSystemTableModsDDL && IsReservedName(schemaName))
    {
        ereport(ERROR,
                (errcode(ERRCODE_RESERVED_NAME),
                 errmsg("unacceptable schema name \"%s\"", schemaName),
                 errdetail("The prefix \"%s\" is reserved for system schemas.",
                           GetReservedPrefix(schemaName))));
    }

    /*
     * If the requested authorization is different from the current user,
     * temporarily set the current user so that the object(s) will be created
     * with the correct ownership.
     *
     * (The setting will be restored at the end of this routine, or in case
     * of error, transaction abort will clean things up.)
     */
    if (saved_uid != owner_uid)
        SetUserIdAndSecContext(owner_uid,
                               save_sec_context | SECURITY_LOCAL_USERID_CHANGE);

    /* Create the schema's namespace */
    if (shouldDispatch || Gp_role != GP_ROLE_EXECUTE)
    {
        namespaceId = NamespaceCreate(schemaName, owner_uid, 0);

        if (shouldDispatch)
        {
            elog(DEBUG5, "shouldDispatch = true, namespaceOid = %d", namespaceId);

            Assert(stmt->schemaOid == 0);
            stmt->schemaOid = namespaceId;

            /*
             * Dispatch the command to all primary and mirror segment dbs.
             * Starts a global transaction and reconfigures cluster if needed.
             * Waits for QEs to finish.  Exits via ereport(ERROR,...) if error.
             */
            CdbDispatchUtilityStatement((Node *) stmt,
                                        DF_CANCEL_ON_ERROR |
                                        DF_WITH_SNAPSHOT |
                                        DF_NEED_TWO_PHASE,
                                        NULL);
        }

        /* MPP-6929: metadata tracking */
        if (Gp_role == GP_ROLE_DISPATCH)
            MetaTrackAddObject(NamespaceRelationId,
                               namespaceId,
                               saved_uid,
                               "CREATE", "SCHEMA"
                              );
    }
    else
    {
        namespaceId = NamespaceCreate(schemaName, owner_uid, stmt->schemaOid);
    }

    /* Advance cmd counter to make the namespace visible */
    CommandCounterIncrement();

    /*
     * Temporarily make the new namespace be the front of the search path, as
     * well as the default creation target namespace.  This will be undone at
     * the end of this routine, or upon error.
     */
    overridePath = GetOverrideSearchPath(CurrentMemoryContext);
    overridePath->schemas = lcons_oid(namespaceId, overridePath->schemas);
    /* XXX should we clear overridePath->useTemp? */
    PushOverrideSearchPath(overridePath);

    /*
     * Examine the list of commands embedded in the CREATE SCHEMA command, and
     * reorganize them into a sequentially executable order with no forward
     * references.	Note that the result is still a list of raw parsetrees ---
     * we cannot, in general, run parse analysis on one statement until we
     * have actually executed the prior ones.
     */
    parsetree_list = transformCreateSchemaStmt(stmt);

    /*
     * Execute each command contained in the CREATE SCHEMA.  Since the grammar
     * allows only utility commands in CREATE SCHEMA, there is no need to pass
     * them through parse_analyze() or the rewriter; we can just hand them
     * straight to ProcessUtility.
     */
    foreach(parsetree_item, parsetree_list)
    {
        Node	   *stmt = (Node *) lfirst(parsetree_item);

        /* do this step */
        ProcessUtility(stmt,
                       queryString,
                       NULL,
                       false,	/* not top level */
                       None_Receiver,
                       NULL);
        /* make sure later steps can see the object created here */
        CommandCounterIncrement();
    }