Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sql: Add RLS support to crdb_internal.create_statements table #141452

Merged
merged 1 commit into from
Feb 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions pkg/ccl/logictestccl/testdata/logic_test/crdb_internal_tenant
Original file line number Diff line number Diff line change
Expand Up @@ -187,10 +187,10 @@ SELECT * FROM crdb_internal.builtin_functions WHERE function = ''
----
function signature category details schema oid

query ITTITTTTTTTTBBBB colnames
query ITTITTTTTTTTTBBBB colnames
SELECT * FROM crdb_internal.create_statements WHERE database_name = ''
----
database_id database_name schema_name descriptor_id descriptor_type descriptor_name create_statement state create_nofks alter_statements validate_statements create_redactable has_partitions is_multi_region is_virtual is_temporary
database_id database_name schema_name descriptor_id descriptor_type descriptor_name create_statement state create_nofks policy_statements alter_statements validate_statements create_redactable has_partitions is_multi_region is_virtual is_temporary

query ITITTBTB colnames
SELECT * FROM crdb_internal.table_columns WHERE descriptor_name = ''
Expand Down
46 changes: 45 additions & 1 deletion pkg/sql/crdb_internal.go
Original file line number Diff line number Diff line change
Expand Up @@ -3967,6 +3967,7 @@ CREATE TABLE crdb_internal.create_statements (
create_statement STRING NOT NULL,
state STRING NOT NULL,
create_nofks STRING NOT NULL,
policy_statements STRING[] NOT NULL,
alter_statements STRING[] NOT NULL,
validate_statements STRING[] NOT NULL,
create_redactable STRING NOT NULL,
Expand All @@ -3991,6 +3992,7 @@ CREATE TABLE crdb_internal.create_statements (
var descType tree.Datum
var stmt, createNofk, createRedactable string
alterStmts := tree.NewDArray(types.String)
policyStmts := tree.NewDArray(types.String)
validateStmts := tree.NewDArray(types.String)
namePrefix := tree.ObjectNamePrefix{SchemaName: tree.Name(sc.GetName()), ExplicitSchema: true}
name := tree.MakeTableNameFromPrefix(namePrefix, tree.Name(table.GetName()))
Expand All @@ -4013,16 +4015,24 @@ CREATE TABLE crdb_internal.create_statements (
} else {
descType = typeTable
displayOptions := ShowCreateDisplayOptions{
FKDisplayMode: OmitFKClausesFromCreate,
FKDisplayMode: OmitFKClausesFromCreate,
IgnoreRLSStatements: true,
}
createNofk, err = ShowCreateTable(ctx, p, &name, contextName, table, lookup, displayOptions)
if err != nil {
return err
}

if err := showAlterStatement(ctx, &name, contextName, lookup, table, alterStmts, validateStmts); err != nil {
return err
}

if err = showPolicyStatements(ctx, &name, table, p.EvalContext(), &p.semaCtx, p.SessionData(), policyStmts); err != nil {
return err
}

displayOptions.FKDisplayMode = IncludeFkClausesInCreate
displayOptions.IgnoreRLSStatements = false
stmt, err = ShowCreateTable(ctx, p, &name, contextName, table, lookup, displayOptions)
if err != nil {
return err
Expand Down Expand Up @@ -4054,6 +4064,7 @@ CREATE TABLE crdb_internal.create_statements (
tree.NewDString(stmt),
tree.NewDString(table.GetState().String()),
tree.NewDString(createNofk),
policyStmts,
alterStmts,
validateStmts,
tree.NewDString(createRedactable),
Expand All @@ -4065,6 +4076,29 @@ CREATE TABLE crdb_internal.create_statements (
},
nil)

// showPolicyStatements adds the RLS policy statements to the policy_statements column.
func showPolicyStatements(
ctx context.Context,
tn *tree.TableName,
table catalog.TableDescriptor,
evalCtx *eval.Context,
semaCtx *tree.SemaContext,
sessionData *sessiondata.SessionData,
policyStmts *tree.DArray,
) error {
for _, policy := range table.GetPolicies() {
if policyStatement, err := showPolicyStatement(ctx, tn, table, evalCtx, semaCtx, sessionData, policy, false); err != nil {
return err
} else if len(policyStatement) != 0 {
if err := policyStmts.Append(tree.NewDString(policyStatement)); err != nil {
return err
}
}
}

return nil
}

func showAlterStatement(
ctx context.Context,
tn *tree.TableName,
Expand Down Expand Up @@ -4108,6 +4142,16 @@ func showAlterStatement(
return err
}
}

// Add the row level security ALTER statements to the alter_statements column.
if alterRLSStatements, err := showRLSAlterStatement(tn, table, false); err != nil {
return err
} else if len(alterRLSStatements) != 0 {
if err = alterStmts.Append(tree.NewDString(alterRLSStatements)); err != nil {
return err
}
}

return nil
}

Expand Down
4 changes: 2 additions & 2 deletions pkg/sql/logictest/testdata/logic_test/crdb_internal
Original file line number Diff line number Diff line change
Expand Up @@ -374,10 +374,10 @@ SELECT * FROM crdb_internal.builtin_functions WHERE function = ''
----
function signature category details schema oid

query ITTITTTTTTTTBBBB colnames
query ITTITTTTTTTTTBBBB colnames
SELECT * FROM crdb_internal.create_statements WHERE database_name = ''
----
database_id database_name schema_name descriptor_id descriptor_type descriptor_name create_statement state create_nofks alter_statements validate_statements create_redactable has_partitions is_multi_region is_virtual is_temporary
database_id database_name schema_name descriptor_id descriptor_type descriptor_name create_statement state create_nofks policy_statements alter_statements validate_statements create_redactable has_partitions is_multi_region is_virtual is_temporary

query ITITTBTB colnames
SELECT * FROM crdb_internal.table_columns WHERE descriptor_name = ''
Expand Down
33 changes: 33 additions & 0 deletions pkg/sql/logictest/testdata/logic_test/row_level_security
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,16 @@ CREATE TABLE public.flying_roaches (
);
CREATE POLICY p1 ON public.flying_roaches AS PERMISSIVE FOR ALL TO public USING ('flying':::public.roach_type = 'crawling':::public.roach_type)

query T
select policy_statements from crdb_internal.create_statements where descriptor_name='flying_roaches'
----
{"CREATE POLICY p1 ON public.flying_roaches AS PERMISSIVE FOR ALL TO public USING ('flying':::public.roach_type = 'crawling':::public.roach_type)"}

query T
select alter_statements from crdb_internal.create_statements where descriptor_name='flying_roaches'
----
{}

statement ok
drop table flying_roaches

Expand Down Expand Up @@ -606,6 +616,19 @@ CREATE TABLE public.roaches (
);
ALTER TABLE public.roaches ENABLE ROW LEVEL SECURITY, FORCE ROW LEVEL SECURITY

query TT
select policy_statements, create_nofks from crdb_internal.create_statements where descriptor_name='roaches'
----
{} CREATE TABLE public.roaches (
rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(),
CONSTRAINT roaches_pkey PRIMARY KEY (rowid ASC)
)

query T
select alter_statements from crdb_internal.create_statements where descriptor_name='roaches'
----
{"ALTER TABLE public.roaches ENABLE ROW LEVEL SECURITY, FORCE ROW LEVEL SECURITY"}

statement ok
ALTER TABLE roaches DISABLE ROW LEVEL SECURITY, NO FORCE ROW LEVEL SECURITY;

Expand All @@ -625,6 +648,16 @@ CREATE TABLE public.roaches (
CONSTRAINT roaches_pkey PRIMARY KEY (rowid ASC)
)

query T
select policy_statements from crdb_internal.create_statements where descriptor_name='roaches'
----
{}

query T
select alter_statements from crdb_internal.create_statements where descriptor_name='roaches'
----
{}

statement ok
DROP TABLE roaches;

Expand Down
6 changes: 3 additions & 3 deletions pkg/sql/logictest/testdata/logic_test/sequences
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,11 @@ CREATE SEQUENCE ignored_options_test NO CYCLE
statement ok
CREATE SEQUENCE show_create_test

query ITTITTTTTTTTBBBB colnames
query ITTITTTTTTTTTBBBB colnames
SELECT * FROM crdb_internal.create_statements WHERE descriptor_name = 'show_create_test'
----
database_id database_name schema_name descriptor_id descriptor_type descriptor_name create_statement state create_nofks alter_statements validate_statements create_redactable has_partitions is_multi_region is_virtual is_temporary
104 test public 111 sequence show_create_test CREATE SEQUENCE public.show_create_test MINVALUE 1 MAXVALUE 9223372036854775807 INCREMENT 1 START 1 PUBLIC CREATE SEQUENCE public.show_create_test MINVALUE 1 MAXVALUE 9223372036854775807 INCREMENT 1 START 1 {} {} CREATE SEQUENCE public.show_create_test MINVALUE 1 MAXVALUE 9223372036854775807 INCREMENT 1 START 1 false false false false
database_id database_name schema_name descriptor_id descriptor_type descriptor_name create_statement state create_nofks policy_statements alter_statements validate_statements create_redactable has_partitions is_multi_region is_virtual is_temporary
104 test public 111 sequence show_create_test CREATE SEQUENCE public.show_create_test MINVALUE 1 MAXVALUE 9223372036854775807 INCREMENT 1 START 1 PUBLIC CREATE SEQUENCE public.show_create_test MINVALUE 1 MAXVALUE 9223372036854775807 INCREMENT 1 START 1 {} {} {} CREATE SEQUENCE public.show_create_test MINVALUE 1 MAXVALUE 9223372036854775807 INCREMENT 1 START 1 false false false false

query TT colnames
SHOW CREATE SEQUENCE show_create_test
Expand Down
95 changes: 55 additions & 40 deletions pkg/sql/show_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ type ShowCreateDisplayOptions struct {
// RedactableValues causes all constants, literals, and other user-provided
// values to be surrounded with redaction markers.
RedactableValues bool
// IgnoreRLSStatements causes all row level security related statements to
// not show up in the SHOW CREATE TABLE output.
IgnoreRLSStatements bool
}

// ShowCreateTable returns a valid SQL representation of the CREATE
Expand Down Expand Up @@ -143,6 +146,7 @@ func ShowCreateTable(
f.WriteString(fkCtx.String())
}
}

for _, idx := range desc.PublicNonPrimaryIndexes() {
// Showing the primary index is handled above.

Expand Down Expand Up @@ -197,13 +201,21 @@ func ShowCreateTable(
return "", err
}

if err := showAlterTableRLS(tn, &f.Buffer, desc.IsRowLevelSecurityEnabled(), desc.IsRowLevelSecurityForced()); err != nil {
return "", err
}

for _, policyDesc := range desc.GetPolicies() {
if err := showCreatePolicies(ctx, desc, p.EvalContext(), &p.semaCtx, p.SessionData(), tn, &f.Buffer, policyDesc); err != nil {
if !displayOptions.IgnoreRLSStatements {
if alterRLSStatements, err := showRLSAlterStatement(tn, desc, true); err != nil {
return "", err
} else {
buf := &f.Buffer
buf.WriteString(alterRLSStatements)
}

for _, policyDesc := range desc.GetPolicies() {
if policyStatements, err := showPolicyStatement(ctx, tn, desc, p.EvalContext(), &p.semaCtx, p.SessionData(), policyDesc, true); err != nil {
return "", err
} else {
buf := &f.Buffer
buf.WriteString(policyStatements)
}
}
}

Expand All @@ -216,106 +228,109 @@ func ShowCreateTable(
return f.CloseAndGetString(), nil
}

// showAlterTableRLS prints out the ALTER TABLE ... ROW LEVEL SECURITY statements
func showAlterTableRLS(tn *tree.TableName, buf *bytes.Buffer, enabled bool, forced bool) error {
if !enabled && !forced {
return nil
// showRLSAlterStatement returns a string of the ALTER TABLE ... ROW LEVEL SECURITY statements
func showRLSAlterStatement(
tn *tree.TableName, table catalog.TableDescriptor, addNewLine bool,
) (string, error) {
if !table.IsRowLevelSecurityEnabled() && !table.IsRowLevelSecurityForced() {
return "", nil
}
f := tree.NewFmtCtx(tree.FmtSimple)
un := tn.ToUnresolvedObjectName()

var cmds []tree.AlterTableCmd

if enabled {
if table.IsRowLevelSecurityEnabled() {
enabledCmd := &tree.AlterTableSetRLSMode{
Mode: tree.TableRLSEnable,
}
cmds = append(cmds, enabledCmd)
}

if forced {
if table.IsRowLevelSecurityForced() {
forcedCmd := &tree.AlterTableSetRLSMode{
Mode: tree.TableRLSForce,
}
cmds = append(cmds, forcedCmd)
}

f.WriteString(";\n")
if addNewLine {
f.WriteString(";\n")
}
f.FormatNode(&tree.AlterTable{
Table: un,
Cmds: cmds,
})

buf.WriteString(f.CloseAndGetString())

return nil
return f.CloseAndGetString(), nil
}

// showCreatePolicies prints out the CREATE POLICY statements
func showCreatePolicies(
// showPolicyStatement returns a string of the row level security POLICY statements
func showPolicyStatement(
ctx context.Context,
desc catalog.TableDescriptor,
tn *tree.TableName,
table catalog.TableDescriptor,
evalCtx *eval.Context,
semaCtx *tree.SemaContext,
sessionData *sessiondata.SessionData,
tn *tree.TableName,
buf *bytes.Buffer,
policyDesc descpb.PolicyDescriptor,
) error {
f := tree.NewFmtCtx(tree.FmtSimple)
policy descpb.PolicyDescriptor,
addNewLine bool,
) (string, error) {
un := tn.ToUnresolvedObjectName()

f := tree.NewFmtCtx(tree.FmtSimple)

var policyRoleSpecList []tree.RoleSpec
for _, roleName := range policyDesc.RoleNames {
for _, roleName := range policy.RoleNames {
policyRoleSpecList = append(policyRoleSpecList, tree.MakeRoleSpecWithRoleName(roleName))
}

// Check if we have a policy using expression
var exprUsing tree.Expr
if len(policyDesc.UsingExpr) != 0 {
if len(policy.UsingExpr) != 0 {
formattedExpr, err := schemaexpr.FormatExprForDisplay(
ctx, desc, policyDesc.UsingExpr, evalCtx, semaCtx, sessionData, tree.FmtParsable,
ctx, table, policy.UsingExpr, evalCtx, semaCtx, sessionData, tree.FmtParsable,
)
if err != nil {
return err
return "", err
}
exprUsing, err = parser.ParseExpr(formattedExpr)
if err != nil {
return err
return "", err
}
}

// Check if we have a policy with check expression
var exprCheck tree.Expr
if len(policyDesc.WithCheckExpr) != 0 {
if len(policy.WithCheckExpr) != 0 {
formattedExpr, err := schemaexpr.FormatExprForDisplay(
ctx, desc, policyDesc.WithCheckExpr, evalCtx, semaCtx, sessionData, tree.FmtParsable,
ctx, table, policy.WithCheckExpr, evalCtx, semaCtx, sessionData, tree.FmtParsable,
)
if err != nil {
return err
return "", err
}
exprCheck, err = parser.ParseExpr(formattedExpr)
if err != nil {
return err
return "", err
}
}

f.WriteString(";\n")
if addNewLine {
f.WriteString(";\n")
}
f.FormatNode(&tree.CreatePolicy{
PolicyName: tree.Name(policyDesc.Name),
PolicyName: tree.Name(policy.Name),
TableName: un,
Type: convertPolicyType(policyDesc.Type),
Cmd: convertPolicyCommand(policyDesc.Command),
Type: convertPolicyType(policy.Type),
Cmd: convertPolicyCommand(policy.Command),
Roles: policyRoleSpecList,
Exprs: tree.PolicyExpressions{
Using: exprUsing,
WithCheck: exprCheck,
},
})

buf.WriteString(f.CloseAndGetString())

return nil
return f.CloseAndGetString(), nil
}

// convertPolicyType will convert from a catpb.PolicyType to a tree.PolicyType
Expand Down
Loading