Skip to content

Commit

Permalink
Create DB Performance improvements (#1899)
Browse files Browse the repository at this point in the history
In Multi DB mode, as the number of databases increases, so does the time to create the next new DB.
This is because we create three internal roles for each new DB and internally when run the DB subcommands, multiple calls to roles_is_member_of("sysadmin") is made. Now that output of this list contains all the three roles of every db created. This is the major reason for the perfomance degradation of CREATE DB command.

We fix this in three different places.

1. getAvailDbid - this functions makes a call to nextval function, which by default checks for current user's permission and makes a call to roles_is_member_of. Instead we could call the nextval_internal which is the same function but with the additional option of check permissions flag which we will set to false. To double check we can just ensure that the current user is "sysadmin" when getAvailDbid is called. (Currently we only call this when user is sysadmin)

2. Set temporary user when creating schema - when we create the dbo and guest schema for the new database, the create schema function fetches all the roles that current role is member of (recursively) to check if if current role can actually become the target schema owner role. To bypass this we can assume the newdb_dbo role when creating these schemas. In this case all the roles that newdb_dbo is member of will be fetched, but this list is much smaller than sysadmin.

3. Select best grantor - Select best grantor first fetches the roles_list that sysadmin is member of and then start checking for permissions. But sysadmin is always the first to be checked. That is sysadmin is always top of the roles_list.
We can add a quick check to this. That is, first check if current role is sysadmin and can it give us all the permission needed. If yes, simply return. Note** This does not change any behaviour since this will anyway be done in the first loop after fetching roles_list. We are instead running the first loop before fetching the whole list.

4. Set newdb_dbo user when creating sysdatabases view in new db

Task: BABEL-3869
Signed-off-by: Tanzeel Khan <[email protected]>
  • Loading branch information
tanscorpio7 authored Oct 12, 2023
1 parent 0c1a2c1 commit 08c886f
Showing 1 changed file with 44 additions and 16 deletions.
60 changes: 44 additions & 16 deletions contrib/babelfishpg_tsql/src/dbcmds.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@
#include "pltsql.h"
#include "extendedproperty.h"

Oid sys_babelfish_db_seq_oid = InvalidOid;

static Oid get_sys_babelfish_db_seq_oid(void);
static bool have_createdb_privilege(void);
static List *gen_createdb_subcmds(const char *schema,
const char *dbo,
Expand All @@ -57,6 +60,20 @@ static Oid do_create_bbf_db(const char *dbname, List *options, const char *owner
static void create_bbf_db_internal(const char *dbname, List *options, const char *owner, int16 dbid);
static void drop_related_bbf_namespace_entries(int16 dbid);

static Oid
get_sys_babelfish_db_seq_oid()
{
if(!OidIsValid(sys_babelfish_db_seq_oid))
{
RangeVar *sequence = makeRangeVarFromNameList(stringToQualifiedNameList("sys.babelfish_db_seq"));
Oid seqid = RangeVarGetRelid(sequence, NoLock, false);

Assert(OidIsValid(seqid));
sys_babelfish_db_seq_oid = seqid;
}
return sys_babelfish_db_seq_oid;
}

static bool
have_createdb_privilege(void)
{
Expand Down Expand Up @@ -111,7 +128,6 @@ gen_createdb_subcmds(const char *schema, const char *dbo, const char *db_owner,
/* create sysdatabases under current DB's DBO schema */
appendStringInfo(&query, "CREATE VIEW dummy.sysdatabases AS SELECT * FROM sys.sysdatabases; ");
appendStringInfo(&query, "ALTER VIEW dummy.sysdatabases OWNER TO dummy; ");
appendStringInfo(&query, "GRANT SELECT ON dummy.sysdatabases TO dummy; ");

/* create guest schema in the database. This has to be the last statement */
if (guest)
Expand All @@ -120,9 +136,9 @@ gen_createdb_subcmds(const char *schema, const char *dbo, const char *db_owner,
res = raw_parser(query.data, RAW_PARSE_DEFAULT);

if (guest)
expected_stmt_num = list_length(logins) > 0 ? 10 : 9;
expected_stmt_num = list_length(logins) > 0 ? 9 : 8;
else
expected_stmt_num = 7;
expected_stmt_num = 6;

if (list_length(res) != expected_stmt_num)
ereport(ERROR,
Expand Down Expand Up @@ -166,9 +182,6 @@ gen_createdb_subcmds(const char *schema, const char *dbo, const char *db_owner,
stmt = parsetree_nth_stmt(res, i++);
update_AlterTableStmt(stmt, schema, db_owner);

stmt = parsetree_nth_stmt(res, i++);
update_GrantStmt(stmt, NULL, schema, db_owner);

if (guest)
{
stmt = parsetree_nth_stmt(res, i++);
Expand Down Expand Up @@ -301,9 +314,12 @@ getAvailDbid(void)
int16 dbid;
int16 start = 0;

if(GetUserId() != get_role_oid("sysadmin", true))
return InvalidDbid;

do
{
dbid = DirectFunctionCall1(nextval, CStringGetTextDatum("sys.babelfish_db_seq"));
dbid = nextval_internal(get_sys_babelfish_db_seq_oid(), false);
if (start == 0)
start = dbid;
else if (start == dbid)
Expand Down Expand Up @@ -351,12 +367,8 @@ getDbidForLogicalDbRestore(Oid relid)
* dbid while inserting into sysdatabases catalog.
*/
else
{
RangeVar *sequence = makeRangeVarFromNameList(stringToQualifiedNameList("sys.babelfish_db_seq"));
Oid seqid = RangeVarGetRelid(sequence, NoLock, false);
dbid = DirectFunctionCall1(currval_oid, get_sys_babelfish_db_seq_oid());

dbid = DirectFunctionCall1(currval_oid, seqid);
}
bbf_set_current_user(prev_current_user);

return dbid;
Expand Down Expand Up @@ -408,6 +420,9 @@ create_bbf_db_internal(const char *dbname, List *options, const char *owner, int
const char *guest;
const char *prev_current_user;
int stmt_number = 0;
int save_sec_context;
bool is_set_userid;
Oid save_userid;

/* TODO: Extract options */

Expand Down Expand Up @@ -519,16 +534,24 @@ create_bbf_db_internal(const char *dbname, List *options, const char *owner, int
/* Run all subcommands */
foreach(parsetree_item, parsetree_list)
{
Node *stmt = ((RawStmt *) lfirst(parsetree_item))->stmt;
PlannedStmt *wrapper;
Node *stmt = ((RawStmt *) lfirst(parsetree_item))->stmt;
PlannedStmt *wrapper;
is_set_userid = false;

if(stmt->type == T_CreateSchemaStmt || stmt->type == T_AlterTableStmt
|| stmt->type == T_ViewStmt)
{
GetUserIdAndSecContext(&save_userid, &save_sec_context);
SetUserIdAndSecContext(get_role_oid(dbo_role, true),
save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
is_set_userid = true;
}
/* need to make a wrapper PlannedStmt */
wrapper = makeNode(PlannedStmt);
wrapper->commandType = CMD_UTILITY;
wrapper->canSetTag = false;
wrapper->utilityStmt = stmt;
wrapper->stmt_location = 0;

stmt_number++;
if (guest && list_length(parsetree_list) == stmt_number)
wrapper->stmt_len = 19;
Expand All @@ -545,7 +568,9 @@ create_bbf_db_internal(const char *dbname, List *options, const char *owner, int
None_Receiver,
NULL);

/* make sure later steps can see the object created here */
if(is_set_userid)
SetUserIdAndSecContext(save_userid, save_sec_context);

CommandCounterIncrement();
}
set_cur_db(old_dbid, old_dbname);
Expand All @@ -567,6 +592,9 @@ create_bbf_db_internal(const char *dbname, List *options, const char *owner, int
}
PG_CATCH();
{
if(is_set_userid)
SetUserIdAndSecContext(save_userid, save_sec_context);

/* Clean up. Restore previous state. */
bbf_set_current_user(prev_current_user);
set_cur_db(old_dbid, old_dbname);
Expand Down

0 comments on commit 08c886f

Please sign in to comment.