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

release-23.2: sql: fix statement tags for CREATE and DROP PROCEDURE #112327

Merged
merged 1 commit into from
Oct 17, 2023
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
7 changes: 7 additions & 0 deletions pkg/ccl/logictestccl/tests/3node-tenant/generated_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

56 changes: 56 additions & 0 deletions pkg/sql/logictest/testdata/logic_test/routine_schema_change
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# LogicTest: !local-mixed-23.1

subtest statement-control

statement ok
SET use_declarative_schema_changer = 'on'

# Disable CREATE FUNCTION.
statement ok
SET CLUSTER SETTING sql.schema.force_declarative_statements='!CREATE FUNCTION'

# Verify that it is disabled.
statement error pgcode 0A000 cannot explain a statement which is not supported by the declarative schema changer
EXPLAIN (DDL) CREATE FUNCTION f() RETURNS INT LANGUAGE SQL AS 'SELECT 1'

# Falling-back to the legacy schema changer works.
statement ok
CREATE FUNCTION f() RETURNS INT LANGUAGE SQL AS 'SELECT 1'

# Disable CREATE PROCEDURE.
statement ok
SET CLUSTER SETTING sql.schema.force_declarative_statements='!CREATE PROCEDURE'

# Verify that it is disabled.
statement error pgcode 0A000 cannot explain a statement which is not supported by the declarative schema changer
EXPLAIN (DDL) CREATE PROCEDURE p() LANGUAGE SQL as 'SELECT 1'

# Falling-back to the legacy schema changer works.
statement ok
CREATE PROCEDURE p() LANGUAGE SQL as 'SELECT 1'

# Disable DROP FUNCTION.
statement ok
SET CLUSTER SETTING sql.schema.force_declarative_statements='!DROP FUNCTION'

# Verify that it is disabled.
statement error pgcode 0A000 cannot explain a statement which is not supported by the declarative schema changer
EXPLAIN (DDL) DROP FUNCTION f

# Falling-back to the legacy schema changer works.
statement ok
DROP FUNCTION f

# Disable DROP PROCEDURE.
statement ok
SET CLUSTER SETTING sql.schema.force_declarative_statements='!DROP PROCEDURE'

# Verify that it is disabled.
statement error pgcode 0A000 cannot explain a statement which is not supported by the declarative schema changer
EXPLAIN (DDL) DROP PROCEDURE p

# Falling-back to the legacy schema changer works.
statement ok
DROP PROCEDURE p

subtest end
7 changes: 7 additions & 0 deletions pkg/sql/logictest/tests/fakedist-disk/generated_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions pkg/sql/logictest/tests/fakedist-vec-off/generated_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions pkg/sql/logictest/tests/fakedist/generated_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions pkg/sql/logictest/tests/local-vec-off/generated_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions pkg/sql/logictest/tests/local/generated_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ go_test(
embed = [":scbuildstmt"],
deps = [
"//pkg/settings",
"//pkg/sql/sem/tree",
"@com_github_stretchr_testify//require",
],
)
48 changes: 25 additions & 23 deletions pkg/sql/schemachanger/scbuild/internal/scbuildstmt/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ import (
type supportedStatement struct {
// fn is a function to perform a schema change.
fn interface{}
// statementTag tag for this statement.
statementTag string
// statementTags contains tags for this statement.
statementTags []string
// checks contains a coarse-grained function to filter out most
// unsupported statements.
// It's possible for certain unsupported statements to pass it but will
Expand All @@ -46,26 +46,26 @@ var supportedStatements = map[reflect.Type]supportedStatement{
// Alter table will have commands individually whitelisted via the
// supportedAlterTableStatements list, so wwe will consider it fully supported
// here.
reflect.TypeOf((*tree.AlterTable)(nil)): {fn: AlterTable, statementTag: tree.AlterTableTag, on: true, checks: alterTableChecks},
reflect.TypeOf((*tree.CreateIndex)(nil)): {fn: CreateIndex, statementTag: tree.CreateIndexTag, on: true, checks: isV231Active},
reflect.TypeOf((*tree.DropDatabase)(nil)): {fn: DropDatabase, statementTag: tree.DropDatabaseTag, on: true, checks: nil},
reflect.TypeOf((*tree.DropOwnedBy)(nil)): {fn: DropOwnedBy, statementTag: tree.DropOwnedByTag, on: true, checks: isV222Active},
reflect.TypeOf((*tree.DropSchema)(nil)): {fn: DropSchema, statementTag: tree.DropSchemaTag, on: true, checks: nil},
reflect.TypeOf((*tree.DropSequence)(nil)): {fn: DropSequence, statementTag: tree.DropSequenceTag, on: true, checks: nil},
reflect.TypeOf((*tree.DropTable)(nil)): {fn: DropTable, statementTag: tree.DropTableTag, on: true, checks: nil},
reflect.TypeOf((*tree.DropType)(nil)): {fn: DropType, statementTag: tree.DropTypeTag, on: true, checks: nil},
reflect.TypeOf((*tree.DropView)(nil)): {fn: DropView, statementTag: tree.DropViewTag, on: true, checks: nil},
reflect.TypeOf((*tree.CommentOnConstraint)(nil)): {fn: CommentOnConstraint, statementTag: tree.CommentOnConstraintTag, on: true, checks: isV222Active},
reflect.TypeOf((*tree.CommentOnDatabase)(nil)): {fn: CommentOnDatabase, statementTag: tree.CommentOnDatabaseTag, on: true, checks: isV222Active},
reflect.TypeOf((*tree.CommentOnSchema)(nil)): {fn: CommentOnSchema, statementTag: tree.CommentOnSchemaTag, on: true, checks: isV222Active},
reflect.TypeOf((*tree.CommentOnTable)(nil)): {fn: CommentOnTable, statementTag: tree.CommentOnTableTag, on: true, checks: isV222Active},
reflect.TypeOf((*tree.CommentOnColumn)(nil)): {fn: CommentOnColumn, statementTag: tree.CommentOnColumnTag, on: true, checks: isV222Active},
reflect.TypeOf((*tree.CommentOnIndex)(nil)): {fn: CommentOnIndex, statementTag: tree.CommentOnIndexTag, on: true, checks: isV222Active},
reflect.TypeOf((*tree.DropIndex)(nil)): {fn: DropIndex, statementTag: tree.DropIndexTag, on: true, checks: isV231Active},
reflect.TypeOf((*tree.DropRoutine)(nil)): {fn: DropFunction, statementTag: tree.DropFunctionTag, on: true, checks: isV231Active},
reflect.TypeOf((*tree.CreateRoutine)(nil)): {fn: CreateFunction, statementTag: tree.CreateRoutineTag, on: true, checks: isV231Active},
reflect.TypeOf((*tree.CreateSchema)(nil)): {fn: CreateSchema, statementTag: tree.CreateSchemaTag, on: false, checks: isV232Active},
reflect.TypeOf((*tree.CreateSequence)(nil)): {fn: CreateSequence, statementTag: tree.CreateSequenceTag, on: false, checks: isV232Active},
reflect.TypeOf((*tree.AlterTable)(nil)): {fn: AlterTable, statementTags: []string{tree.AlterTableTag}, on: true, checks: alterTableChecks},
reflect.TypeOf((*tree.CreateIndex)(nil)): {fn: CreateIndex, statementTags: []string{tree.CreateIndexTag}, on: true, checks: isV231Active},
reflect.TypeOf((*tree.DropDatabase)(nil)): {fn: DropDatabase, statementTags: []string{tree.DropDatabaseTag}, on: true, checks: nil},
reflect.TypeOf((*tree.DropOwnedBy)(nil)): {fn: DropOwnedBy, statementTags: []string{tree.DropOwnedByTag}, on: true, checks: isV222Active},
reflect.TypeOf((*tree.DropSchema)(nil)): {fn: DropSchema, statementTags: []string{tree.DropSchemaTag}, on: true, checks: nil},
reflect.TypeOf((*tree.DropSequence)(nil)): {fn: DropSequence, statementTags: []string{tree.DropSequenceTag}, on: true, checks: nil},
reflect.TypeOf((*tree.DropTable)(nil)): {fn: DropTable, statementTags: []string{tree.DropTableTag}, on: true, checks: nil},
reflect.TypeOf((*tree.DropType)(nil)): {fn: DropType, statementTags: []string{tree.DropTypeTag}, on: true, checks: nil},
reflect.TypeOf((*tree.DropView)(nil)): {fn: DropView, statementTags: []string{tree.DropViewTag}, on: true, checks: nil},
reflect.TypeOf((*tree.CommentOnConstraint)(nil)): {fn: CommentOnConstraint, statementTags: []string{tree.CommentOnConstraintTag}, on: true, checks: isV222Active},
reflect.TypeOf((*tree.CommentOnDatabase)(nil)): {fn: CommentOnDatabase, statementTags: []string{tree.CommentOnDatabaseTag}, on: true, checks: isV222Active},
reflect.TypeOf((*tree.CommentOnSchema)(nil)): {fn: CommentOnSchema, statementTags: []string{tree.CommentOnSchemaTag}, on: true, checks: isV222Active},
reflect.TypeOf((*tree.CommentOnTable)(nil)): {fn: CommentOnTable, statementTags: []string{tree.CommentOnTableTag}, on: true, checks: isV222Active},
reflect.TypeOf((*tree.CommentOnColumn)(nil)): {fn: CommentOnColumn, statementTags: []string{tree.CommentOnColumnTag}, on: true, checks: isV222Active},
reflect.TypeOf((*tree.CommentOnIndex)(nil)): {fn: CommentOnIndex, statementTags: []string{tree.CommentOnIndexTag}, on: true, checks: isV222Active},
reflect.TypeOf((*tree.DropIndex)(nil)): {fn: DropIndex, statementTags: []string{tree.DropIndexTag}, on: true, checks: isV231Active},
reflect.TypeOf((*tree.DropRoutine)(nil)): {fn: DropFunction, statementTags: []string{tree.DropFunctionTag, tree.DropProcedureTag}, on: true, checks: isV231Active},
reflect.TypeOf((*tree.CreateRoutine)(nil)): {fn: CreateFunction, statementTags: []string{tree.CreateFunctionTag, tree.CreateProcedureTag}, on: true, checks: isV231Active},
reflect.TypeOf((*tree.CreateSchema)(nil)): {fn: CreateSchema, statementTags: []string{tree.CreateSchemaTag}, on: false, checks: isV232Active},
reflect.TypeOf((*tree.CreateSequence)(nil)): {fn: CreateSequence, statementTags: []string{tree.CreateSequenceTag}, on: false, checks: isV232Active},
}

// supportedStatementTags tracks statement tags which are implemented
Expand Down Expand Up @@ -107,7 +107,9 @@ func init() {
}
// Fetch the statement tag using the statement tag method on the type,
// we can use this as a blacklist of blocked schema changes.
supportedStatementTags[statementEntry.statementTag] = struct{}{}
for _, tag := range statementEntry.statementTags {
supportedStatementTags[tag] = struct{}{}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,19 @@ import (
"testing"

"github.com/cockroachdb/cockroach/pkg/settings"
"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
"github.com/stretchr/testify/require"
)

func TestSupportedStatements(t *testing.T) {
// Some statements n supportedStatements have multiple statementTags. This
// map contains concrete statements that will return each tag, corresponding
// one-to-one with each tag in statementTags.
var multiTagStmts = map[reflect.Type][]tree.Statement{
reflect.TypeOf((*tree.DropRoutine)(nil)): {&tree.DropRoutine{}, &tree.DropRoutine{Procedure: true}},
reflect.TypeOf((*tree.CreateRoutine)(nil)): {&tree.CreateRoutine{}, &tree.CreateRoutine{IsProcedure: true}},
}

sv := &settings.Values{}
// Non-existent tags should error out.
require.Error(t, forceDeclarativeStatements.Validate(sv, "FAKE STATEMENT"))
Expand All @@ -28,25 +37,36 @@ func TestSupportedStatements(t *testing.T) {
noTags := strings.Builder{}
first := true
for typ, stmt := range supportedStatements {
require.Greaterf(t, len(stmt.statementTag), 0, "statement tag is missing %v %v", typ, stmt)
// Validate tags matches the statement tag
typTag, found := typ.MethodByName("StatementTag")
require.True(t, found, "unable to find stmt: %v %v", typ, stmt)
ret := typTag.Func.Call([]reflect.Value{reflect.New(typ.Elem())})
require.Equal(t, ret[0].String(), stmt.statementTag, "statement tag is different in AST")
// Validate all tags are supported.
require.NoError(t, forceDeclarativeStatements.Validate(sv, "+"+stmt.statementTag))
require.NoError(t, forceDeclarativeStatements.Validate(sv, "!"+stmt.statementTag))
// Validate all of them can be specified at once.
if !first {
allTags.WriteString(",")
noTags.WriteString(",")
for i, tag := range stmt.statementTags {
require.Greaterf(t, len(stmt.statementTags), 0, "statement tag is missing %v %v", typ, stmt)

// Validate tags matches the statement tag.
var expectedTag string
if concreteStmts, ok := multiTagStmts[typ]; ok {
expectedTag = concreteStmts[i].StatementTag()
} else {
// Otherwise, build a zero-value statement.
typTag, found := typ.MethodByName("StatementTag")
require.True(t, found, "unable to find stmt: %v %v", typ, stmt)
ret := typTag.Func.Call([]reflect.Value{reflect.New(typ.Elem())})
expectedTag = ret[0].String()
}
require.Equal(t, expectedTag, tag, "statement tag is different in AST")

// Validate all tags are supported.
require.NoError(t, forceDeclarativeStatements.Validate(sv, "+"+tag))
require.NoError(t, forceDeclarativeStatements.Validate(sv, "!"+tag))
// Validate all of them can be specified at once.
if !first {
allTags.WriteString(",")
noTags.WriteString(",")
}
first = false
allTags.WriteString("+")
allTags.WriteString(tag)
noTags.WriteString("!")
noTags.WriteString(tag)
}
first = false
allTags.WriteString("+")
allTags.WriteString(stmt.statementTag)
noTags.WriteString("!")
noTags.WriteString(stmt.statementTag)
}
require.NoError(t, forceDeclarativeStatements.Validate(sv, allTags.String()))
require.NoError(t, forceDeclarativeStatements.Validate(sv, noTags.String()))
Expand Down
21 changes: 16 additions & 5 deletions pkg/sql/sem/tree/stmt.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ const (
AlterTableTag = "ALTER TABLE"
BackupTag = "BACKUP"
CreateIndexTag = "CREATE INDEX"
CreateRoutineTag = "CREATE FUNCTION"
CreateFunctionTag = "CREATE FUNCTION"
CreateProcedureTag = "CREATE PROCEDURE"
CreateSchemaTag = "CREATE SCHEMA"
CreateSequenceTag = "CREATE SEQUENCE"
CommentOnColumnTag = "COMMENT ON COLUMN"
Expand All @@ -104,6 +105,7 @@ const (
CommentOnTableTag = "COMMENT ON TABLE"
DropDatabaseTag = "DROP DATABASE"
DropFunctionTag = "DROP FUNCTION"
DropProcedureTag = "DROP PROCEDURE"
DropIndexTag = "DROP INDEX"
DropOwnedByTag = "DROP OWNED BY"
DropSchemaTag = "DROP SCHEMA"
Expand Down Expand Up @@ -1957,9 +1959,8 @@ func (*ShowRoutines) StatementType() StatementType { return TypeDML }
func (n *ShowRoutines) StatementTag() string {
if n.Procedure {
return "SHOW PROCEDURES"
} else {
return "SHOW FUNCTIONS"
}
return "SHOW FUNCTIONS"
}

// StatementReturnType implements the Statement interface
Expand Down Expand Up @@ -2121,7 +2122,12 @@ func (*CreateRoutine) StatementReturnType() StatementReturnType { return DDL }
func (*CreateRoutine) StatementType() StatementType { return TypeDDL }

// StatementTag returns a short string identifying the type of statement.
func (*CreateRoutine) StatementTag() string { return CreateRoutineTag }
func (n *CreateRoutine) StatementTag() string {
if n.IsProcedure {
return CreateProcedureTag
}
return CreateFunctionTag
}

// StatementReturnType implements the Statement interface.
func (*RoutineReturn) StatementReturnType() StatementReturnType { return Rows }
Expand All @@ -2139,7 +2145,12 @@ func (*DropRoutine) StatementReturnType() StatementReturnType { return DDL }
func (*DropRoutine) StatementType() StatementType { return TypeDDL }

// StatementTag returns a short string identifying the type of statement.
func (*DropRoutine) StatementTag() string { return DropFunctionTag }
func (n *DropRoutine) StatementTag() string {
if n.Procedure {
return DropProcedureTag
}
return DropFunctionTag
}

// StatementReturnType implements the Statement interface.
func (*AlterFunctionOptions) StatementReturnType() StatementReturnType { return DDL }
Expand Down