From 61cc152321458ccf61c7491b8f61447e429dec3e Mon Sep 17 00:00:00 2001 From: Jayant Shrivastava Date: Wed, 28 Oct 2020 11:18:40 -0400 Subject: [PATCH 01/11] workload/schemachange: screen for errors in renameTable op Release note: None --- pkg/workload/schemachange/error_screening.go | 17 ++++++++++ .../schemachange/operation_generator.go | 34 ++++++++++++++++++- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/pkg/workload/schemachange/error_screening.go b/pkg/workload/schemachange/error_screening.go index 21971d4909d6..62352b475e29 100644 --- a/pkg/workload/schemachange/error_screening.go +++ b/pkg/workload/schemachange/error_screening.go @@ -74,3 +74,20 @@ func schemaExists(tx *pgx.Tx, schemaName string) (bool, error) { WHERE schema_name = $1 )`, schemaName) } + +func tableHasDependencies(tx *pgx.Tx, tableName *tree.TableName) (bool, error) { + return scanBool(tx, ` + SELECT EXISTS( + SELECT fd.descriptor_name + FROM crdb_internal.forward_dependencies AS fd + WHERE fd.descriptor_id + = ( + SELECT c.oid + FROM pg_catalog.pg_class AS c + JOIN pg_catalog.pg_namespace AS ns ON + ns.oid = c.relnamespace + WHERE c.relname = $1 AND ns.nspname = $2 + ) + ) + `, tableName.Object(), tableName.Schema()) +} diff --git a/pkg/workload/schemachange/operation_generator.go b/pkg/workload/schemachange/operation_generator.go index f183b90920db..b0aba0ac482f 100644 --- a/pkg/workload/schemachange/operation_generator.go +++ b/pkg/workload/schemachange/operation_generator.go @@ -72,10 +72,13 @@ type opType int // opsWithErrorScreening stores ops which currently check for exec // errors and update expectedExecErrors in the op generator state var opsWithExecErrorScreening = map[opType]bool{ - addColumn: true, + addColumn: true, + createTable: true, createTableAs: true, createView: true, + + renameTable: true, } func opScreensForExecErrors(op opType) bool { @@ -733,6 +736,35 @@ func (og *operationGenerator) renameTable(tx *pgx.Tx) (string, error) { return "", err } + srcTableExists, err := tableExists(tx, srcTableName) + if err != nil { + return "", err + } + + destSchemaExists, err := schemaExists(tx, destTableName.Schema()) + if err != nil { + return "", err + } + + destTableExists, err := tableExists(tx, destTableName) + if err != nil { + return "", err + } + + srcTableHasDependencies, err := tableHasDependencies(tx, srcTableName) + if err != nil { + return "", err + } + + srcEqualsDest := destTableName.String() == srcTableName.String() + codesWithConditions{ + {code: pgcode.UndefinedTable, condition: !srcTableExists}, + {code: pgcode.UndefinedSchema, condition: !destSchemaExists}, + {code: pgcode.DuplicateRelation, condition: !srcEqualsDest && destTableExists}, + {code: pgcode.DependentObjectsStillExist, condition: srcTableHasDependencies}, + {code: pgcode.InvalidName, condition: srcTableName.Schema() != destTableName.Schema()}, + }.add(og.expectedExecErrors) + return fmt.Sprintf(`ALTER TABLE %s RENAME TO %s`, srcTableName, destTableName), nil } From 87652c786b7b615edbb90bf0d77865de0e1d27eb Mon Sep 17 00:00:00 2001 From: Jayant Shrivastava Date: Wed, 28 Oct 2020 18:41:12 -0400 Subject: [PATCH 02/11] workload/schemachange: screen for errors in dropColumn op Release note: None --- pkg/workload/schemachange/error_screening.go | 65 +++++++++++++++++++ .../schemachange/operation_generator.go | 31 +++++++++ 2 files changed, 96 insertions(+) diff --git a/pkg/workload/schemachange/error_screening.go b/pkg/workload/schemachange/error_screening.go index 62352b475e29..c55cfaca5cf9 100644 --- a/pkg/workload/schemachange/error_screening.go +++ b/pkg/workload/schemachange/error_screening.go @@ -91,3 +91,68 @@ func tableHasDependencies(tx *pgx.Tx, tableName *tree.TableName) (bool, error) { ) `, tableName.Object(), tableName.Schema()) } + +func columnIsDependedOn(tx *pgx.Tx, tableName *tree.TableName, columnName string) (bool, error) { + // To see if a column is depended on, the ordinal_position of the column is looked up in + // information_schema.columns. Then, this position is used to see if that column has view dependencies + // or foreign key dependencies which would be stored in crdb_internal.forward_dependencies and + // pg_catalog.pg_constraint respectively. + // + // crdb_internal.forward_dependencies.dependedonby_details is an array of ordinal positions + // stored as a list of numbers in a string, so SQL functions are used to parse these values + // into arrays. unnest is used to flatten rows with this column of array type into multiple rows, + // so performing unions and joins is easier. + return scanBool(tx, `SELECT EXISTS( + SELECT source.column_id + FROM ( + SELECT DISTINCT column_id + FROM ( + SELECT unnest( + string_to_array( + rtrim( + ltrim( + fd.dependedonby_details, + 'Columns: [' + ), + ']' + ), + ' ' + )::INT8[] + ) AS column_id + FROM crdb_internal.forward_dependencies + AS fd + WHERE fd.descriptor_id + = $1::REGCLASS + ) + UNION ( + SELECT unnest(confkey) AS column_id + FROM pg_catalog.pg_constraint + WHERE confrelid = $1::REGCLASS + ) + ) AS cons + INNER JOIN ( + SELECT ordinal_position AS column_id + FROM information_schema.columns + WHERE table_schema = $2 + AND table_name = $3 + AND column_name = $4 + ) AS source ON source.column_id = cons.column_id +)`, tableName.String(), tableName.Schema(), tableName.Object(), columnName) +} + +func colIsPrimaryKey(tx *pgx.Tx, tableName *tree.TableName, columnName string) (bool, error) { + return scanBool(tx, ` + SELECT EXISTS( + SELECT column_name + FROM information_schema.table_constraints AS c + JOIN information_schema.constraint_column_usage + AS ccu ON ccu.table_name = c.table_name + AND ccu.table_schema = c.table_schema + AND ccu.constraint_name = c.constraint_name + WHERE c.table_schema = $1 + AND c.table_name = $2 + AND ccu.column_name = $3 + AND c.constraint_type = 'PRIMARY KEY' + ); + `, tableName.Schema(), tableName.Object(), columnName) +} diff --git a/pkg/workload/schemachange/operation_generator.go b/pkg/workload/schemachange/operation_generator.go index b0aba0ac482f..f4dadaa84ebe 100644 --- a/pkg/workload/schemachange/operation_generator.go +++ b/pkg/workload/schemachange/operation_generator.go @@ -78,6 +78,8 @@ var opsWithExecErrorScreening = map[opType]bool{ createTableAs: true, createView: true, + dropColumn: true, + renameTable: true, } @@ -572,10 +574,38 @@ func (og *operationGenerator) dropColumn(tx *pgx.Tx) (string, error) { return "", err } + tableExists, err := tableExists(tx, tableName) + if err != nil { + return "", err + } + if !tableExists { + og.expectedExecErrors.add(pgcode.UndefinedTable) + return fmt.Sprintf(`ALTER TABLE %s DROP COLUMN "IrrelevantColumnName"`, tableName), nil + } + columnName, err := og.randColumn(tx, *tableName, og.pctExisting(true)) if err != nil { return "", err } + columnExists, err := columnExistsOnTable(tx, tableName, columnName) + if err != nil { + return "", err + } + colIsPrimaryKey, err := colIsPrimaryKey(tx, tableName, columnName) + if err != nil { + return "", err + } + columnIsDependedOn, err := columnIsDependedOn(tx, tableName, columnName) + if err != nil { + return "", err + } + + codesWithConditions{ + {code: pgcode.UndefinedColumn, condition: !columnExists}, + {code: pgcode.InvalidColumnReference, condition: colIsPrimaryKey}, + {code: pgcode.DependentObjectsStillExist, condition: columnIsDependedOn}, + }.add(og.expectedExecErrors) + return fmt.Sprintf(`ALTER TABLE %s DROP COLUMN "%s"`, tableName, columnName), nil } @@ -946,6 +976,7 @@ func (og *operationGenerator) randColumn( q := fmt.Sprintf(` SELECT column_name FROM [SHOW COLUMNS FROM %s] + WHERE column_name != 'rowid' ORDER BY random() LIMIT 1; `, tableName.String()) From e9241ffca6c34f5b8febd8f9cda9b62ba738abe9 Mon Sep 17 00:00:00 2001 From: Jayant Shrivastava Date: Thu, 29 Oct 2020 15:14:05 -0400 Subject: [PATCH 03/11] workload/schemachange: update createView and createTableAs column logic Duplicate and/or ambiguous columns can exist in createView and createTableAs statements because tables and views can now be generated from others which may have columns in common. This commit adds error handling for duplicate columns and also qualifies column names in createTableAs or createView statements to remove ambiguity. Release note: None --- .../schemachange/operation_generator.go | 48 +++++++++++++++++-- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/pkg/workload/schemachange/operation_generator.go b/pkg/workload/schemachange/operation_generator.go index f4dadaa84ebe..c320136de7c6 100644 --- a/pkg/workload/schemachange/operation_generator.go +++ b/pkg/workload/schemachange/operation_generator.go @@ -375,9 +375,14 @@ func (og *operationGenerator) createTableAs(tx *pgx.Tx) (string, error) { sourceTableNames := make([]tree.TableExpr, numSourceTables) sourceTableExistence := make([]bool, numSourceTables) + // uniqueTableNames and duplicateSourceTables are used to track unique + // tables. If there are any duplicates, then a pgcode.DuplicateAlias error + // is expected on execution. uniqueTableNames := map[string]bool{} duplicateSourceTables := false + // Collect a random set of size numSourceTables that contains tables and views + // from which to use columns. for i := 0; i < numSourceTables; i++ { var tableName *tree.TableName var err error @@ -418,10 +423,17 @@ func (og *operationGenerator) createTableAs(tx *pgx.Tx) (string, error) { From: tree.From{Tables: sourceTableNames}, } + // uniqueColumnNames and duplicateColumns are used to track unique + // columns. If there are any duplicates, then a pgcode.DuplicateColumn error + // is expected on execution. + uniqueColumnNames := map[string]bool{} + duplicateColumns := false for i := 0; i < numSourceTables; i++ { tableName := sourceTableNames[i] tableExists := sourceTableExistence[i] + // If the table does not exist, columns cannot be fetched from it. For this reason, the placeholder + // "IrrelevantColumnName" is used, and a pgcode.UndefinedTable error is expected on execution. if tableExists { columnNamesForTable, err := og.tableColumnsShuffled(tx, tableName.(*tree.TableName).String()) if err != nil { @@ -429,11 +441,18 @@ func (og *operationGenerator) createTableAs(tx *pgx.Tx) (string, error) { } columnNamesForTable = columnNamesForTable[:1+og.randIntn(len(columnNamesForTable))] - for i := range columnNamesForTable { + for j := range columnNamesForTable { colItem := tree.ColumnItem{ - ColumnName: tree.Name(columnNamesForTable[i]), + TableName: tableName.(*tree.TableName).ToUnresolvedObjectName(), + ColumnName: tree.Name(columnNamesForTable[j]), } selectStatement.Exprs = append(selectStatement.Exprs, tree.SelectExpr{Expr: &colItem}) + + if _, exists := uniqueColumnNames[columnNamesForTable[j]]; exists { + duplicateColumns = true + } else { + uniqueColumnNames[columnNamesForTable[j]] = true + } } } else { og.expectedExecErrors.add(pgcode.UndefinedTable) @@ -462,6 +481,7 @@ func (og *operationGenerator) createTableAs(tx *pgx.Tx) (string, error) { {code: pgcode.DuplicateRelation, condition: tableExists}, {code: pgcode.Syntax, condition: len(selectStatement.Exprs) == 0}, {code: pgcode.DuplicateAlias, condition: duplicateSourceTables}, + {code: pgcode.DuplicateColumn, condition: duplicateColumns}, }.add(og.expectedExecErrors) return fmt.Sprintf(`CREATE TABLE %s AS %s`, @@ -475,9 +495,14 @@ func (og *operationGenerator) createView(tx *pgx.Tx) (string, error) { sourceTableNames := make([]tree.TableExpr, numSourceTables) sourceTableExistence := make([]bool, numSourceTables) + // uniqueTableNames and duplicateSourceTables are used to track unique + // tables. If there are any duplicates, then a pgcode.DuplicateColumn error + // is expected on execution. uniqueTableNames := map[string]bool{} duplicateSourceTables := false + // Collect a random set of size numSourceTables that contains tables and views + // from which to use columns. for i := 0; i < numSourceTables; i++ { var tableName *tree.TableName var err error @@ -518,10 +543,17 @@ func (og *operationGenerator) createView(tx *pgx.Tx) (string, error) { From: tree.From{Tables: sourceTableNames}, } + // uniqueColumnNames and duplicateColumns are used to track unique + // columns. If there are any duplicates, then a pgcode.DuplicateColumn error + // is expected on execution. + uniqueColumnNames := map[string]bool{} + duplicateColumns := false for i := 0; i < numSourceTables; i++ { tableName := sourceTableNames[i] tableExists := sourceTableExistence[i] + // If the table does not exist, columns cannot be fetched from it. For this reason, the placeholder + // "IrrelevantColumnName" is used, and a pgcode.UndefinedTable error is expected on execution. if tableExists { columnNamesForTable, err := og.tableColumnsShuffled(tx, tableName.(*tree.TableName).String()) if err != nil { @@ -529,11 +561,18 @@ func (og *operationGenerator) createView(tx *pgx.Tx) (string, error) { } columnNamesForTable = columnNamesForTable[:1+og.randIntn(len(columnNamesForTable))] - for i := range columnNamesForTable { + for j := range columnNamesForTable { colItem := tree.ColumnItem{ - ColumnName: tree.Name(columnNamesForTable[i]), + TableName: tableName.(*tree.TableName).ToUnresolvedObjectName(), + ColumnName: tree.Name(columnNamesForTable[j]), } selectStatement.Exprs = append(selectStatement.Exprs, tree.SelectExpr{Expr: &colItem}) + + if _, exists := uniqueColumnNames[columnNamesForTable[j]]; exists { + duplicateColumns = true + } else { + uniqueColumnNames[columnNamesForTable[j]] = true + } } } else { og.expectedExecErrors.add(pgcode.UndefinedTable) @@ -562,6 +601,7 @@ func (og *operationGenerator) createView(tx *pgx.Tx) (string, error) { {code: pgcode.DuplicateRelation, condition: viewExists}, {code: pgcode.Syntax, condition: len(selectStatement.Exprs) == 0}, {code: pgcode.DuplicateAlias, condition: duplicateSourceTables}, + {code: pgcode.DuplicateColumn, condition: duplicateColumns}, }.add(og.expectedExecErrors) return fmt.Sprintf(`CREATE VIEW %s AS %s`, From 6ed9c3f229c10ad7e1cdb583cbe7e1dc75fe1e18 Mon Sep 17 00:00:00 2001 From: Jayant Shrivastava Date: Thu, 29 Oct 2020 15:29:54 -0400 Subject: [PATCH 04/11] workload/schemachange: screen for errors in renameView op Release note: None --- .../schemachange/operation_generator.go | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/pkg/workload/schemachange/operation_generator.go b/pkg/workload/schemachange/operation_generator.go index c320136de7c6..226221f47413 100644 --- a/pkg/workload/schemachange/operation_generator.go +++ b/pkg/workload/schemachange/operation_generator.go @@ -81,6 +81,7 @@ var opsWithExecErrorScreening = map[opType]bool{ dropColumn: true, renameTable: true, + renameView: true, } func opScreensForExecErrors(op opType) bool { @@ -854,6 +855,35 @@ func (og *operationGenerator) renameView(tx *pgx.Tx) (string, error) { return "", err } + srcViewExists, err := viewExists(tx, srcViewName) + if err != nil { + return "", err + } + + destSchemaExists, err := schemaExists(tx, destViewName.Schema()) + if err != nil { + return "", err + } + + destViewExists, err := viewExists(tx, destViewName) + if err != nil { + return "", err + } + + srcTableHasDependencies, err := tableHasDependencies(tx, srcViewName) + if err != nil { + return "", err + } + + srcEqualsDest := destViewName.String() == srcViewName.String() + codesWithConditions{ + {code: pgcode.UndefinedTable, condition: !srcViewExists}, + {code: pgcode.UndefinedSchema, condition: !destSchemaExists}, + {code: pgcode.DuplicateRelation, condition: !srcEqualsDest && destViewExists}, + {code: pgcode.DependentObjectsStillExist, condition: srcTableHasDependencies}, + {code: pgcode.InvalidName, condition: srcViewName.Schema() != destViewName.Schema()}, + }.add(og.expectedExecErrors) + return fmt.Sprintf(`ALTER VIEW %s RENAME TO %s`, srcViewName, destViewName), nil } From cbc5755e32d559f249baeaee1515edd30c7d53ee Mon Sep 17 00:00:00 2001 From: Jayant Shrivastava Date: Thu, 29 Oct 2020 15:52:00 -0400 Subject: [PATCH 05/11] workload/schemachange: screen for errors in createEnum op Release note: None --- pkg/workload/schemachange/operation_generator.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pkg/workload/schemachange/operation_generator.go b/pkg/workload/schemachange/operation_generator.go index 226221f47413..dfb9eb38dc93 100644 --- a/pkg/workload/schemachange/operation_generator.go +++ b/pkg/workload/schemachange/operation_generator.go @@ -77,6 +77,7 @@ var opsWithExecErrorScreening = map[opType]bool{ createTable: true, createTableAs: true, createView: true, + createEnum: true, dropColumn: true, @@ -366,6 +367,17 @@ func (og *operationGenerator) createEnum(tx *pgx.Tx) (string, error) { if err != nil { return "", err } + typ, err := typName.ToUnresolvedObjectName(tree.NoAnnotation) + if err != nil { + return "", err + } + typeExists, err := typeExists(tx, typ) + if err != nil { + return "", err + } + if typeExists { + og.expectedExecErrors.add(pgcode.DuplicateObject) + } stmt := rowenc.RandCreateType(og.params.rng, typName.String(), "asdf") return tree.Serialize(stmt), nil } From e0b202249600fccf44a2c2a540a30b9929a99954 Mon Sep 17 00:00:00 2001 From: Jayant Shrivastava Date: Fri, 30 Oct 2020 11:10:00 -0400 Subject: [PATCH 06/11] workload/schemachange: screen for errors in dropColumnDefault op Release note: None --- .../schemachange/operation_generator.go | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/pkg/workload/schemachange/operation_generator.go b/pkg/workload/schemachange/operation_generator.go index dfb9eb38dc93..dbde3a0cdd8f 100644 --- a/pkg/workload/schemachange/operation_generator.go +++ b/pkg/workload/schemachange/operation_generator.go @@ -79,7 +79,8 @@ var opsWithExecErrorScreening = map[opType]bool{ createView: true, createEnum: true, - dropColumn: true, + dropColumn: true, + dropColumnDefault: true, renameTable: true, renameView: true, @@ -667,10 +668,25 @@ func (og *operationGenerator) dropColumnDefault(tx *pgx.Tx) (string, error) { if err != nil { return "", err } + tableExists, err := tableExists(tx, tableName) + if err != nil { + return "", err + } + if !tableExists { + og.expectedExecErrors.add(pgcode.UndefinedTable) + return fmt.Sprintf(`ALTER TABLE %s ALTER COLUMN "IrrelevantColumnName" DROP DEFAULT`, tableName), nil + } columnName, err := og.randColumn(tx, *tableName, og.pctExisting(true)) if err != nil { return "", err } + columnExists, err := columnExistsOnTable(tx, tableName, columnName) + if err != nil { + return "", err + } + if !columnExists { + og.expectedExecErrors.add(pgcode.UndefinedColumn) + } return fmt.Sprintf(`ALTER TABLE %s ALTER COLUMN "%s" DROP DEFAULT`, tableName, columnName), nil } From a4d8d8cf67e87185568b8d875c01bf4e28e3c246 Mon Sep 17 00:00:00 2001 From: Jayant Shrivastava Date: Fri, 30 Oct 2020 11:27:19 -0400 Subject: [PATCH 07/11] workload/schemachange: screen for errors in dropColumnNotNull op Release note: None --- .../schemachange/operation_generator.go | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/pkg/workload/schemachange/operation_generator.go b/pkg/workload/schemachange/operation_generator.go index dbde3a0cdd8f..d2acfda6bd64 100644 --- a/pkg/workload/schemachange/operation_generator.go +++ b/pkg/workload/schemachange/operation_generator.go @@ -81,6 +81,7 @@ var opsWithExecErrorScreening = map[opType]bool{ dropColumn: true, dropColumnDefault: true, + dropColumnNotNull: true, renameTable: true, renameView: true, @@ -695,10 +696,34 @@ func (og *operationGenerator) dropColumnNotNull(tx *pgx.Tx) (string, error) { if err != nil { return "", err } + tableExists, err := tableExists(tx, tableName) + if err != nil { + return "", err + } + if !tableExists { + og.expectedExecErrors.add(pgcode.UndefinedTable) + return fmt.Sprintf(`ALTER TABLE %s ALTER COLUMN "IrrelevantColumnName" DROP NOT NULL`, tableName), nil + } columnName, err := og.randColumn(tx, *tableName, og.pctExisting(true)) if err != nil { return "", err } + columnExists, err := columnExistsOnTable(tx, tableName, columnName) + if err != nil { + return "", err + } + colIsPrimaryKey, err := colIsPrimaryKey(tx, tableName, columnName) + if err != nil { + return "", err + } + + codesWithConditions{ + {pgcode.UndefinedColumn, !columnExists}, + {pgcode.InvalidTableDefinition, colIsPrimaryKey}, + }.add(og.expectedExecErrors) + if !columnExists { + og.expectedExecErrors.add(pgcode.UndefinedColumn) + } return fmt.Sprintf(`ALTER TABLE %s ALTER COLUMN "%s" DROP NOT NULL`, tableName, columnName), nil } From b07c9d1680e2023f5befa18d137ce5fb902fed40 Mon Sep 17 00:00:00 2001 From: Jayant Shrivastava Date: Sat, 31 Oct 2020 15:27:32 -0400 Subject: [PATCH 08/11] workload/schemachange: screen for schema change after write Release note: None --- .../schemachange/operation_generator.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/pkg/workload/schemachange/operation_generator.go b/pkg/workload/schemachange/operation_generator.go index d2acfda6bd64..fa200228d316 100644 --- a/pkg/workload/schemachange/operation_generator.go +++ b/pkg/workload/schemachange/operation_generator.go @@ -45,6 +45,10 @@ type operationGenerator struct { screenForExecErrors bool expectedExecErrors errorCodeSet expectedCommitErrors errorCodeSet + + // opsInTxn is a list of previous ops in the current transaction implemented + // as a map for fast lookups. + opsInTxn map[opType]bool } func makeOperationGenerator(params *operationGeneratorParams) *operationGenerator { @@ -52,6 +56,7 @@ func makeOperationGenerator(params *operationGeneratorParams) *operationGenerato params: params, expectedExecErrors: makeExpectedErrorSet(), expectedCommitErrors: makeExpectedErrorSet(), + opsInTxn: map[opType]bool{}, } } @@ -64,6 +69,10 @@ func (og *operationGenerator) resetOpState() { // Reset internal state used per transaction func (og *operationGenerator) resetTxnState() { og.expectedCommitErrors.reset() + + for k := range og.opsInTxn { + delete(og.opsInTxn, k) + } } //go:generate stringer -type=opType @@ -223,7 +232,16 @@ func (og *operationGenerator) randOp(tx *pgx.Tx) (string, string, error) { if opScreensForExecErrors(op) { og.screenForExecErrors = true + + // Screen for schema change after write in the same transaction. + if op != insertRow { + if _, previous := og.opsInTxn[insertRow]; previous { + og.expectedExecErrors.add(pgcode.FeatureNotSupported) + } + } } + + og.opsInTxn[op] = true return stmt, log.String(), err } } From 26ed047dba7b80db710e66b84b5f28f829cd8606 Mon Sep 17 00:00:00 2001 From: Jayant Shrivastava Date: Sat, 31 Oct 2020 16:41:38 -0400 Subject: [PATCH 09/11] workload/schemachange: screen for errors in renameColumn op Release note: None --- .../schemachange/operation_generator.go | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/pkg/workload/schemachange/operation_generator.go b/pkg/workload/schemachange/operation_generator.go index fa200228d316..b395b1b6e1bd 100644 --- a/pkg/workload/schemachange/operation_generator.go +++ b/pkg/workload/schemachange/operation_generator.go @@ -92,8 +92,9 @@ var opsWithExecErrorScreening = map[opType]bool{ dropColumnDefault: true, dropColumnNotNull: true, - renameTable: true, - renameView: true, + renameColumn: true, + renameTable: true, + renameView: true, } func opScreensForExecErrors(op opType) bool { @@ -814,6 +815,16 @@ func (og *operationGenerator) renameColumn(tx *pgx.Tx) (string, error) { return "", err } + srcTableExists, err := tableExists(tx, tableName) + if err != nil { + return "", err + } + if !srcTableExists { + og.expectedExecErrors.add(pgcode.UndefinedTable) + return fmt.Sprintf(`ALTER TABLE %s RENAME COLUMN "IrrelevantColumnName" TO "OtherIrrelevantName"`, + tableName), nil + } + srcColumnName, err := og.randColumn(tx, *tableName, og.pctExisting(true)) if err != nil { return "", err @@ -824,6 +835,25 @@ func (og *operationGenerator) renameColumn(tx *pgx.Tx) (string, error) { return "", err } + srcColumnExists, err := columnExistsOnTable(tx, tableName, srcColumnName) + if err != nil { + return "", err + } + destColumnExists, err := columnExistsOnTable(tx, tableName, destColumnName) + if err != nil { + return "", err + } + columnIsDependedOn, err := columnIsDependedOn(tx, tableName, srcColumnName) + if err != nil { + return "", err + } + + codesWithConditions{ + {pgcode.UndefinedColumn, !srcColumnExists}, + {pgcode.DuplicateColumn, destColumnExists && srcColumnName != destColumnName}, + {pgcode.DependentObjectsStillExist, columnIsDependedOn}, + }.add(og.expectedExecErrors) + return fmt.Sprintf(`ALTER TABLE %s RENAME COLUMN "%s" TO "%s"`, tableName, srcColumnName, destColumnName), nil } From 79046d3e76ba1b11f003bb54ae5b58a755074db3 Mon Sep 17 00:00:00 2001 From: Jayant Shrivastava Date: Sun, 1 Nov 2020 17:26:47 -0500 Subject: [PATCH 10/11] workload/schemachange: screen for errors in createSchema op Release note: None --- pkg/workload/schemachange/operation_generator.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/pkg/workload/schemachange/operation_generator.go b/pkg/workload/schemachange/operation_generator.go index b395b1b6e1bd..7fdf0eef1319 100644 --- a/pkg/workload/schemachange/operation_generator.go +++ b/pkg/workload/schemachange/operation_generator.go @@ -87,6 +87,7 @@ var opsWithExecErrorScreening = map[opType]bool{ createTableAs: true, createView: true, createEnum: true, + createSchema: true, dropColumn: true, dropColumnDefault: true, @@ -1432,9 +1433,18 @@ func (og *operationGenerator) createSchema(tx *pgx.Tx) (string, error) { if err != nil { return "", err } + ifNotExists := og.randIntn(2) == 0 + + schemaExists, err := schemaExists(tx, schemaName) + if err != nil { + return "", err + } + if schemaExists && !ifNotExists { + og.expectedExecErrors.add(pgcode.DuplicateSchema) + } // TODO(jayshrivastava): Support authorization - stmt := rowenc.MakeSchemaName(og.randIntn(2) == 0, schemaName, security.RootUserName()) + stmt := rowenc.MakeSchemaName(ifNotExists, schemaName, security.RootUserName()) return tree.Serialize(stmt), nil } From 6ebfb07d0092b6526e4349b83bc1501b99b05b76 Mon Sep 17 00:00:00 2001 From: Jayant Shrivastava Date: Sun, 1 Nov 2020 17:47:33 -0500 Subject: [PATCH 11/11] workload/schemachange: screen for errors in dropSchema op Release note: None --- pkg/workload/schemachange/operation_generator.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pkg/workload/schemachange/operation_generator.go b/pkg/workload/schemachange/operation_generator.go index 7fdf0eef1319..1f25aba5b1a8 100644 --- a/pkg/workload/schemachange/operation_generator.go +++ b/pkg/workload/schemachange/operation_generator.go @@ -92,6 +92,7 @@ var opsWithExecErrorScreening = map[opType]bool{ dropColumn: true, dropColumnDefault: true, dropColumnNotNull: true, + dropSchema: true, renameColumn: true, renameTable: true, @@ -1473,6 +1474,16 @@ func (og *operationGenerator) dropSchema(tx *pgx.Tx) (string, error) { if err != nil { return "", err } + + schemaExists, err := schemaExists(tx, schemaName) + if err != nil { + return "", err + } + codesWithConditions{ + {pgcode.UndefinedSchema, !schemaExists}, + {pgcode.InvalidSchemaName, schemaName == tree.PublicSchema}, + }.add(og.expectedExecErrors) + return fmt.Sprintf(`DROP SCHEMA "%s" CASCADE`, schemaName), nil }