Skip to content

Commit

Permalink
sql: Support CREATE DATABASE WITH OWNER
Browse files Browse the repository at this point in the history
Release note (sql change): Allow users to specify the owner when creating a database.
			   Similar to postgresql: CREATE DATABASE name
    							[ [ WITH ] [ OWNER [=] user_name ]
  • Loading branch information
Fenil-P committed Jan 20, 2022
1 parent 61a1b93 commit bf237fd
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 6 deletions.
2 changes: 1 addition & 1 deletion docs/generated/sql/bnf/create_database_stmt.bnf
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
create_database_stmt ::=
'CREATE' 'DATABASE' database_name ( 'WITH' | ) opt_template_clause ( 'ENCODING' ( '=' | ) encoding | ) opt_lc_collate_clause opt_lc_ctype_clause ( 'CONNECTION' 'LIMIT' ( '=' | ) limit | ) ( ( 'PRIMARY' 'REGION' ( '=' | ) region_name ) | ) ( ( 'REGIONS' ) ( '=' | ) region_name_list | ) ( ( 'SURVIVE' ( '=' | ) 'REGION' 'FAILURE' | 'SURVIVE' ( '=' | ) 'ZONE' 'FAILURE' ) | )
'CREATE' 'DATABASE' database_name ( 'WITH' | ) opt_template_clause ( 'ENCODING' ( '=' | ) encoding | ) opt_lc_collate_clause opt_lc_ctype_clause ( 'CONNECTION' 'LIMIT' ( '=' | ) limit | ) ( ( 'PRIMARY' 'REGION' ( '=' | ) region_name ) | ) ( ( 'REGIONS' ) ( '=' | ) region_name_list | ) ( ( 'SURVIVE' ( '=' | ) 'REGION' 'FAILURE' | 'SURVIVE' ( '=' | ) 'ZONE' 'FAILURE' ) | ) opt_owner_clause
| 'CREATE' 'DATABASE' 'IF' 'NOT' 'EXISTS' database_name ( 'WITH' | ) opt_template_clause ( 'ENCODING' ( '=' | ) encoding | ) opt_lc_collate_clause opt_lc_ctype_clause ( 'CONNECTION' 'LIMIT' ( '=' | ) limit | ) ( ( 'PRIMARY' 'REGION' ( '=' | ) region_name ) | ) ( ( 'REGIONS' ) ( '=' | ) region_name_list | ) ( ( 'SURVIVE' ( '=' | ) 'REGION' 'FAILURE' | 'SURVIVE' ( '=' | ) 'ZONE' 'FAILURE' ) | )
6 changes: 5 additions & 1 deletion docs/generated/sql/bnf/stmt_block.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -1409,7 +1409,7 @@ for_schedules_clause ::=
| 'FOR' 'SCHEDULE' a_expr

create_database_stmt ::=
'CREATE' 'DATABASE' database_name opt_with opt_template_clause opt_encoding_clause opt_lc_collate_clause opt_lc_ctype_clause opt_connection_limit opt_primary_region_clause opt_regions_list opt_survival_goal_clause
'CREATE' 'DATABASE' database_name opt_with opt_template_clause opt_encoding_clause opt_lc_collate_clause opt_lc_ctype_clause opt_connection_limit opt_primary_region_clause opt_regions_list opt_survival_goal_clause opt_owner_clause
| 'CREATE' 'DATABASE' 'IF' 'NOT' 'EXISTS' database_name opt_with opt_template_clause opt_encoding_clause opt_lc_collate_clause opt_lc_ctype_clause opt_connection_limit opt_primary_region_clause opt_regions_list opt_survival_goal_clause

create_index_stmt ::=
Expand Down Expand Up @@ -1953,6 +1953,10 @@ opt_survival_goal_clause ::=
survival_goal_clause
|

opt_owner_clause ::=
'OWNER' opt_equal role_spec
|

opt_unique ::=
'UNIQUE'
|
Expand Down
15 changes: 14 additions & 1 deletion pkg/sql/descriptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,21 +129,34 @@ func (p *planner) createDatabase(
return nil, false, err
}

owner := p.SessionData().User()
if !database.Owner.Undefined() {
owner, err = database.Owner.ToSQLUsername(p.SessionData(), security.UsernameValidation)
if err != nil {
return nil, true, err
}
}

desc := dbdesc.NewInitial(
id,
string(database.Name),
p.SessionData().User(),
owner,
dbdesc.MaybeWithDatabaseRegionConfig(regionConfig),
dbdesc.WithPublicSchemaID(publicSchemaID),
)

if err := p.checkCanAlterToNewOwner(ctx, desc, owner); err != nil {
return nil, true, err
}

if err := p.createDescriptorWithID(ctx, dKey, id, desc, nil, jobDesc); err != nil {
return nil, true, err
}

// Initialize the multi-region database by creating the multi-region enum and
// database-level zone configuration if there is a region config on the
// descriptor.

if err := p.maybeInitializeMultiRegionDatabase(ctx, desc, regionConfig); err != nil {
return nil, true, err
}
Expand Down
28 changes: 28 additions & 0 deletions pkg/sql/logictest/testdata/logic_test/database
Original file line number Diff line number Diff line change
Expand Up @@ -274,3 +274,31 @@ CREATE TABLE db69713.s.pg_constraintdef_test (

statement ok
DROP DATABASE db69713;

# Ensure user must exist to create with owner.
statement error role/user "fake_user" does not exist
CREATE DATABASE aa with owner fake_user

statement ok
CREATE DATABASE a with owner testuser

query TTTTT colnames
SHOW DATABASES
----
database_name owner primary_region regions survival_goal
a testuser NULL {} NULL
b root NULL {} NULL
c root NULL {} NULL
defaultdb root NULL {} NULL
postgres root NULL {} NULL
system node NULL {} NULL
test root NULL {} NULL

# Non-superusers also must be a member of the new owning role.
statement ok
CREATE USER testuser2

user testuser

statement error permission denied to create database
CREATE DATABASE d WITH OWNER testuser2
19 changes: 16 additions & 3 deletions pkg/sql/parser/sql.y
Original file line number Diff line number Diff line change
Expand Up @@ -1331,7 +1331,7 @@ func (u *sqlSymUnion) setVar() *tree.SetVar {
%type <str> unrestricted_name type_function_name type_function_name_no_crdb_extra
%type <str> non_reserved_word
%type <str> non_reserved_word_or_sconst
%type <tree.RoleSpec> role_spec
%type <tree.RoleSpec> role_spec opt_owner_clause
%type <tree.RoleSpecList> role_spec_list
%type <tree.Expr> zone_value
%type <tree.Expr> string_or_placeholder
Expand Down Expand Up @@ -7433,7 +7433,7 @@ sequence_option_list:
| sequence_option_list sequence_option_elem { $$.val = append($1.seqOpts(), $2.seqOpt()) }

sequence_option_elem:
AS typename {
AS typename {
// Valid option values must be integer types (ex. int2, bigint)
parsedType := $2.colType()
if parsedType.Family() != types.IntFamily {
Expand Down Expand Up @@ -8758,7 +8758,7 @@ transaction_deferrable_mode:
// %Text: CREATE DATABASE [IF NOT EXISTS] <name>
// %SeeAlso: WEBDOCS/create-database.html
create_database_stmt:
CREATE DATABASE database_name opt_with opt_template_clause opt_encoding_clause opt_lc_collate_clause opt_lc_ctype_clause opt_connection_limit opt_primary_region_clause opt_regions_list opt_survival_goal_clause opt_placement_clause
CREATE DATABASE database_name opt_with opt_template_clause opt_encoding_clause opt_lc_collate_clause opt_lc_ctype_clause opt_connection_limit opt_primary_region_clause opt_regions_list opt_survival_goal_clause opt_placement_clause opt_owner_clause
{
$$.val = &tree.CreateDatabase{
Name: tree.Name($3),
Expand All @@ -8771,6 +8771,7 @@ create_database_stmt:
Regions: $11.nameList(),
SurvivalGoal: $12.survivalGoal(),
Placement: $13.dataPlacement(),
Owner: $14.roleSpec(),
}
}
| CREATE DATABASE IF NOT EXISTS database_name opt_with opt_template_clause opt_encoding_clause opt_lc_collate_clause opt_lc_ctype_clause opt_connection_limit opt_primary_region_clause opt_regions_list opt_survival_goal_clause opt_placement_clause
Expand Down Expand Up @@ -8916,6 +8917,18 @@ opt_connection_limit:
$$.val = int32(-1)
}

opt_owner_clause:
OWNER opt_equal role_spec
{
$$ = $3
}
| /* EMPTY */
{
$$.val = tree.RoleSpec{
RoleSpecType: tree.CurrentUser,
}
}

opt_equal:
'=' {}
| /* EMPTY */ {}
Expand Down
6 changes: 6 additions & 0 deletions pkg/sql/sem/tree/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ type CreateDatabase struct {
Regions NameList
SurvivalGoal SurvivalGoal
Placement DataPlacement
Owner RoleSpec
}

// Format implements the NodeFormatter interface.
Expand Down Expand Up @@ -115,6 +116,11 @@ func (node *CreateDatabase) Format(ctx *FmtCtx) {
ctx.WriteString(" ")
ctx.FormatNode(&node.Placement)
}

if node.Owner.Name != "" {
ctx.WriteString(" OWNER = ")
ctx.FormatNode(&node.Owner)
}
}

// IndexElem represents a column with a direction in a CREATE INDEX statement.
Expand Down

0 comments on commit bf237fd

Please sign in to comment.