Skip to content

Commit 82ab831

Browse files
craig[bot]Dedej-Bergin
andcommitted
Merge #144695
144695: workload/schemachange: add ALTER POLICY to the RSW r=Dedej-Bergin a=Dedej-Bergin Modified the schema changer workload to run `ALTER POLICY` commands. Fixes: #137120 Epic: [CRDB-11724](https://cockroachlabs.atlassian.net/browse/CRDB-11724) Release note: none Co-authored-by: Bergin Dedej <[email protected]>
2 parents df9bf8a + 68d776a commit 82ab831

File tree

3 files changed

+193
-41
lines changed

3 files changed

+193
-41
lines changed

pkg/workload/schemachange/operation_generator.go

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5171,3 +5171,145 @@ func (og *operationGenerator) dropPolicy(ctx context.Context, tx pgx.Tx) (*opStm
51715171

51725172
return opStmt, nil
51735173
}
5174+
5175+
func (og *operationGenerator) alterPolicy(ctx context.Context, tx pgx.Tx) (*opStmt, error) {
5176+
// Try to find an existing policy to alter
5177+
policyWithInfo, policyExists, err := findExistingPolicy(ctx, tx, og)
5178+
if err != nil {
5179+
return nil, err
5180+
}
5181+
5182+
// Variable to track table existence
5183+
var tableExists bool = true
5184+
5185+
if !policyExists {
5186+
// Fall back to random table if no tables with policies were found
5187+
randomTable, err := og.randTable(ctx, tx, og.pctExisting(true), "")
5188+
if err != nil {
5189+
return nil, err
5190+
}
5191+
policyWithInfo.table = *randomTable
5192+
5193+
// If we didn't get a real policy name, generate a random one
5194+
if policyWithInfo.policyName == "" {
5195+
policyWithInfo.policyName = fmt.Sprintf("dummy_policy_%s", og.newUniqueSeqNumSuffix())
5196+
}
5197+
5198+
// Check if table exists to include appropriate expected error
5199+
tableExists, err = og.tableExists(ctx, tx, randomTable)
5200+
if err != nil {
5201+
return nil, err
5202+
}
5203+
}
5204+
5205+
// Determine which ALTER POLICY features to include
5206+
alterType := og.randIntn(4) // 0-3 for different types of alterations
5207+
5208+
var sqlStatement strings.Builder
5209+
sqlStatement.WriteString(fmt.Sprintf("ALTER POLICY %s ON %s", policyWithInfo.policyName, &policyWithInfo.table))
5210+
5211+
usesDummyRole := false
5212+
includeUsing := false
5213+
includeWithCheck := false
5214+
5215+
var columns []string
5216+
5217+
switch alterType {
5218+
case 0: // RENAME TO
5219+
newName := fmt.Sprintf("policy_%s", og.newUniqueSeqNumSuffix())
5220+
sqlStatement.WriteString(fmt.Sprintf(" RENAME TO %s", newName))
5221+
case 1: // TO roles
5222+
// Define roles to grant the policy to
5223+
var roles string
5224+
if og.randIntn(2) == 0 {
5225+
// 50% chance to a real username
5226+
roles, err = og.randUser(ctx, tx)
5227+
if err != nil {
5228+
return nil, err
5229+
}
5230+
} else if og.randIntn(4) == 0 {
5231+
// Fall back to two options: 25% chance for PUBLIC or a 75% for generated dummy role
5232+
roles = "PUBLIC"
5233+
} else {
5234+
// Generate a random role name that doesn't exist
5235+
roles = fmt.Sprintf("dummy_role_%s", og.newUniqueSeqNumSuffix())
5236+
usesDummyRole = true
5237+
}
5238+
5239+
sqlStatement.WriteString(fmt.Sprintf(" TO %s", roles))
5240+
default: // USING and/or WITH CHECK expressions
5241+
// For case 2 and 3, we generate USING, WITH CHECK, or both
5242+
includeUsing = alterType == 2 || og.randIntn(2) == 0
5243+
includeWithCheck = alterType == 3 || og.randIntn(2) == 0
5244+
5245+
// If neither was selected, default to including USING
5246+
if !includeUsing && !includeWithCheck {
5247+
includeUsing = true
5248+
}
5249+
5250+
// Get columns for the table to reference in expressions
5251+
if tableExists {
5252+
columns, err = og.tableColumnsShuffled(ctx, tx, policyWithInfo.table.String())
5253+
if err != nil {
5254+
return nil, err
5255+
}
5256+
}
5257+
5258+
// Generate expressions for USING and WITH CHECK
5259+
if includeUsing {
5260+
usingExpr := og.generatePolicyExpression(columns, -1) // -1 means no preferred column
5261+
sqlStatement.WriteString(fmt.Sprintf(" USING (%s)", usingExpr))
5262+
}
5263+
5264+
if includeWithCheck {
5265+
// Try to use a different column for WITH CHECK if possible
5266+
preferredColIdx := -1
5267+
if len(columns) > 1 && includeUsing {
5268+
preferredColIdx = og.randIntn(len(columns))
5269+
}
5270+
5271+
withCheckExpr := og.generatePolicyExpression(columns, preferredColIdx)
5272+
sqlStatement.WriteString(fmt.Sprintf(" WITH CHECK (%s)", withCheckExpr))
5273+
}
5274+
}
5275+
5276+
// Create the operation statement
5277+
opStmt := makeOpStmt(OpStmtDDL)
5278+
opStmt.sql = sqlStatement.String()
5279+
5280+
opStmt.expectedExecErrors.addAll(codesWithConditions{
5281+
{code: pgcode.UndefinedObject, condition: !policyExists},
5282+
{code: pgcode.UndefinedTable, condition: !tableExists},
5283+
{code: pgcode.UndefinedObject, condition: usesDummyRole},
5284+
})
5285+
5286+
return opStmt, nil
5287+
}
5288+
5289+
// randUser returns a real username from the database.
5290+
// It returns an error if no user is found.
5291+
func (og *operationGenerator) randUser(ctx context.Context, tx pgx.Tx) (string, error) {
5292+
query := "SELECT username FROM [SHOW USERS] ORDER BY random() LIMIT 1"
5293+
rows, err := tx.Query(ctx, query)
5294+
if rows.Err() != nil {
5295+
return "", rows.Err()
5296+
}
5297+
5298+
if err != nil {
5299+
return "", err
5300+
}
5301+
defer rows.Close()
5302+
5303+
var realUser string
5304+
if rows.Next() {
5305+
if err := rows.Scan(&realUser); err != nil {
5306+
return "", err
5307+
}
5308+
og.LogMessage(fmt.Sprintf("Found real user: '%s'", realUser))
5309+
return realUser, nil
5310+
}
5311+
5312+
// This should never happen in a valid CockroachDB instance.
5313+
// There should always be at least one user.
5314+
return "", errors.New("no users found in the database")
5315+
}

pkg/workload/schemachange/optype.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@ const (
8888
alterFunctionRename // ALTER FUNCTION <function> RENAME TO <name>
8989
alterFunctionSetSchema // ALTER FUNCTION <function> SET SCHEMA <schema>
9090

91+
// ALTER POLICY ...
92+
93+
alterPolicy // ALTER POLICY <policy> ON <table> <def>
94+
9195
// ALTER TABLE <table> ...
9296

9397
alterTableAddColumn // ALTER TABLE <table> ADD [COLUMN] <column> <type>
@@ -116,13 +120,13 @@ const (
116120
createTypeEnum // CREATE TYPE <type> ENUM AS <def>
117121
createTypeComposite // CREATE TYPE <type> AS <def>
118122
createIndex // CREATE INDEX <index> ON <table> <def>
123+
createPolicy // CREATE POLICY <policy> ON <table> <def>
119124
createSchema // CREATE SCHEMA <schema>
120125
createSequence // CREATE SEQUENCE <sequence> <def>
121126
createTable // CREATE TABLE <table> <def>
122127
createTableAs // CREATE TABLE <table> AS <def>
123128
createView // CREATE VIEW <view> AS <def>
124129
createFunction // CREATE FUNCTION <function> ...
125-
createPolicy // CREATE POLICY <policy> ON <table> [TO <roles>] [USING (<using_expr>)] [WITH CHECK (<check_expr>)]
126130

127131
// COMMENT ON ...
128132

@@ -132,11 +136,11 @@ const (
132136

133137
dropFunction // DROP FUNCTION <function>
134138
dropIndex // DROP INDEX <index>@<table>
139+
dropPolicy // DROP POLICY [IF EXISTS] <policy> ON <table>
135140
dropSchema // DROP SCHEMA <schema>
136141
dropSequence // DROP SEQUENCE <sequence>
137142
dropTable // DROP TABLE <table>
138143
dropView // DROP VIEW <view>
139-
dropPolicy // DROP POLICY [IF EXISTS] <policy> ON <table>
140144

141145
// Unimplemented operations. TODO(sql-foundations): Audit and/or implement these operations.
142146
// alterDatabaseOwner
@@ -216,6 +220,7 @@ var opFuncs = []func(*operationGenerator, context.Context, pgx.Tx) (*opStmt, err
216220
alterDatabaseSurvivalGoal: (*operationGenerator).survive,
217221
alterFunctionRename: (*operationGenerator).alterFunctionRename,
218222
alterFunctionSetSchema: (*operationGenerator).alterFunctionSetSchema,
223+
alterPolicy: (*operationGenerator).alterPolicy,
219224
alterTableAddColumn: (*operationGenerator).addColumn,
220225
alterTableAddConstraint: (*operationGenerator).addConstraint,
221226
alterTableAddConstraintForeignKey: (*operationGenerator).addForeignKeyConstraint,
@@ -271,6 +276,7 @@ var opWeights = []int{
271276
alterDatabaseSurvivalGoal: 0, // Disabled and tracked with #83831
272277
alterFunctionRename: 1,
273278
alterFunctionSetSchema: 1,
279+
alterPolicy: 1,
274280
alterTableAddColumn: 1,
275281
alterTableAddConstraintForeignKey: 1,
276282
alterTableAddConstraintUnique: 1,
@@ -320,6 +326,7 @@ var opDeclarativeVersion = map[opType]clusterversion.Key{
320326
selectStmt: clusterversion.MinSupported,
321327
validate: clusterversion.MinSupported,
322328

329+
alterPolicy: clusterversion.V25_2,
323330
alterTableAddColumn: clusterversion.MinSupported,
324331
alterTableAddConstraintForeignKey: clusterversion.MinSupported,
325332
alterTableAddConstraintUnique: clusterversion.MinSupported,

pkg/workload/schemachange/optype_string.go

Lines changed: 42 additions & 39 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)