Skip to content

Commit b4669b9

Browse files
authored
Merge pull request #155977 from rafiss/backport25.4-155795
release-25.4: workload/schemachange: remove FK violation prediction for INSERT operations
2 parents 9294fe2 + e6e78fd commit b4669b9

File tree

2 files changed

+2
-261
lines changed

2 files changed

+2
-261
lines changed

pkg/workload/schemachange/error_screening.go

Lines changed: 0 additions & 242 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ package schemachange
88
import (
99
"context"
1010
"fmt"
11-
"regexp"
1211
"slices"
1312
"strings"
1413

@@ -789,36 +788,6 @@ func (og *operationGenerator) scanStringArrayNullableRows(
789788
return results, nil
790789
}
791790

792-
func (og *operationGenerator) scanStringArrayRows(
793-
ctx context.Context, tx pgx.Tx, query string, args ...interface{},
794-
) ([][]string, error) {
795-
rows, err := tx.Query(ctx, query, args...)
796-
if err != nil {
797-
return nil, errors.Wrapf(err, "scanStringArrayRows: %q %q", query, args)
798-
}
799-
defer rows.Close()
800-
801-
var results [][]string
802-
for rows.Next() {
803-
var columnNames []string
804-
err := rows.Scan(&columnNames)
805-
if err != nil {
806-
return nil, errors.Wrapf(err, "scan: %q, args %q, scanArgs %q", query, columnNames, args)
807-
}
808-
results = append(results, columnNames)
809-
}
810-
811-
if rows.Err() != nil {
812-
return nil, rows.Err()
813-
}
814-
815-
og.LogQueryResults(
816-
query,
817-
results,
818-
args...)
819-
return results, nil
820-
}
821-
822791
func (og *operationGenerator) indexExists(
823792
ctx context.Context, tx pgx.Tx, tableName *tree.TableName, indexName string,
824793
) (bool, error) {
@@ -1051,188 +1020,6 @@ func (og *operationGenerator) constraintExists(
10511020
)`, string(tableName), string(constraintName))
10521021
}
10531022

1054-
var (
1055-
// regexpUnknownSchemaErr matches unknown schema errors with
1056-
// a descriptor ID, which will have the form: unknown schema "[123]"
1057-
regexpUnknownSchemaErr = regexp.MustCompile(`unknown schema "\[\d+]"`)
1058-
)
1059-
1060-
// checkAndAdjustForUnknownSchemaErrors in certain contexts we will attempt to
1061-
// bind descriptors without leasing them, since we are using crdb_internal tables,
1062-
// so it's possible for said descriptor to be dropped before we bind it. This
1063-
// method will allow for "unknown schema [xx]" in those contexts.
1064-
func (og *operationGenerator) checkAndAdjustForUnknownSchemaErrors(err error) error {
1065-
if pgErr := new(pgconn.PgError); errors.As(err, &pgErr) &&
1066-
pgcode.MakeCode(pgErr.Code) == pgcode.InvalidSchemaName {
1067-
if regexpUnknownSchemaErr.MatchString(pgErr.Message) {
1068-
og.LogMessage(fmt.Sprintf("Rolling back due to unknown schema error %v",
1069-
err))
1070-
// Force a rollback and log inside the operation generator.
1071-
return errors.Mark(err, errRunInTxnRbkSentinel)
1072-
}
1073-
}
1074-
return err
1075-
}
1076-
1077-
// violatesFkConstraints checks if the rows to be inserted will result in a foreign key violation.
1078-
func (og *operationGenerator) violatesFkConstraints(
1079-
ctx context.Context,
1080-
tx pgx.Tx,
1081-
tableName *tree.TableName,
1082-
nonGeneratedColNames []tree.Name,
1083-
rows [][]string,
1084-
) (expectedViolation, potentialViolation bool, err error) {
1085-
// TODO(annie): readd the join on active constraints once #120702 is resolved.
1086-
//
1087-
// N.B. We add random noise to column names that makes it hard to just directly call on these names. This is
1088-
// not the case with table/schema names; thus, only column names are quote_ident'ed to ensure that they get
1089-
// referenced properly.
1090-
fkConstraints, err := og.scanStringArrayRows(ctx, tx, fmt.Sprintf(`
1091-
SELECT array[
1092-
parent.table_schema,
1093-
parent.table_name,
1094-
parent.column_name,
1095-
parent.is_generated,
1096-
child.column_name,
1097-
child.is_generated
1098-
]
1099-
FROM (
1100-
SELECT conname, conkey, confkey, conrelid, confrelid
1101-
FROM pg_constraint
1102-
WHERE contype = 'f'
1103-
AND conrelid = '%s'::REGCLASS::INT8
1104-
) AS con
1105-
JOIN (
1106-
SELECT column_name, ordinal_position, column_default, is_generated
1107-
FROM information_schema.columns
1108-
WHERE table_schema = '%s'
1109-
AND table_name = '%s'
1110-
) AS child ON conkey[1] = child.ordinal_position
1111-
JOIN (
1112-
SELECT pc.oid,
1113-
cols.table_schema,
1114-
cols.table_name,
1115-
cols.column_name,
1116-
cols.ordinal_position,
1117-
cols.is_generated
1118-
FROM pg_class AS pc
1119-
JOIN pg_namespace AS pn ON pc.relnamespace = pn.oid
1120-
JOIN information_schema.columns AS cols ON (pc.relname = cols.table_name AND pn.nspname = cols.table_schema)
1121-
) AS parent ON (
1122-
con.confkey[1] = parent.ordinal_position
1123-
AND con.confrelid = parent.oid
1124-
)
1125-
WHERE child.column_name != 'rowid';
1126-
`, tableName.String(), tableName.Schema(), tableName.Object()))
1127-
if err != nil {
1128-
return false, false, og.checkAndAdjustForUnknownSchemaErrors(err)
1129-
}
1130-
1131-
// Maps a non-generated column name to its index. This way, the value of a
1132-
// column in a row can be looked up using row[colToIndexMap["columnName"]] = "valueForColumn"
1133-
nonGeneratedColumnNameToIndexMap := map[tree.Name]int{}
1134-
1135-
for i, name := range nonGeneratedColNames {
1136-
nonGeneratedColumnNameToIndexMap[name] = i
1137-
}
1138-
1139-
for _, row := range rows {
1140-
for _, constraint := range fkConstraints {
1141-
parentTableSchema := tree.Name(constraint[0])
1142-
parentTableName := tree.Name(constraint[1])
1143-
parentColumnName := tree.Name(constraint[2])
1144-
parentIsGenerated := constraint[3] == "ALWAYS"
1145-
childColumnName := tree.Name(constraint[4])
1146-
childIsGenerated := constraint[5] == "ALWAYS"
1147-
1148-
// If self referential, there cannot be a violation.
1149-
parentAndChildAreSame := parentTableSchema == tableName.SchemaName && parentTableName == tableName.ObjectName
1150-
if parentAndChildAreSame && parentColumnName == childColumnName {
1151-
continue
1152-
}
1153-
1154-
// If the foreign key involves any generated columns, skip detailed FK checking
1155-
// and conservatively assume a violation may occur. This avoids the complexity
1156-
// of reasoning about values that are automatically computed by the database.
1157-
if parentIsGenerated || childIsGenerated {
1158-
return false, true, nil
1159-
}
1160-
1161-
violation, err := og.violatesFkConstraintsHelper(
1162-
ctx, tx, nonGeneratedColumnNameToIndexMap, parentTableSchema, parentTableName, parentColumnName, childColumnName, tableName, parentAndChildAreSame, row, rows,
1163-
)
1164-
if err != nil {
1165-
return false, false, err
1166-
}
1167-
if violation {
1168-
return true, false, nil
1169-
}
1170-
}
1171-
}
1172-
1173-
return false, false, nil
1174-
}
1175-
1176-
// violatesFkConstraintsHelper checks if inserting a single row would violate a
1177-
// foreign key constraint between the given child and parent columns.
1178-
//
1179-
// This function assumes that neither the parent nor child columns are generated.
1180-
// The caller must ensure this; generated columns are not supported.
1181-
func (og *operationGenerator) violatesFkConstraintsHelper(
1182-
ctx context.Context,
1183-
tx pgx.Tx,
1184-
nonGeneratedColumnNameToIndexMap map[tree.Name]int,
1185-
parentTableSchema, parentTableName, parentColumn, childColumn tree.Name,
1186-
childTableName *tree.TableName,
1187-
parentAndChildAreSameTable bool,
1188-
rowToInsert []string,
1189-
allRows [][]string,
1190-
) (bool, error) {
1191-
1192-
childIndex, ok := nonGeneratedColumnNameToIndexMap[childColumn]
1193-
if !ok {
1194-
return false, errors.Newf("child column %s does not exist in table %s", childColumn, childTableName)
1195-
}
1196-
childValue := rowToInsert[childIndex]
1197-
// If the value to insert in the child column is NULL and the column default is NULL, then it is not possible to have a fk violation.
1198-
if childValue == "NULL" {
1199-
return false, nil
1200-
}
1201-
// If the parent and child are the same table, then any rows in an existing
1202-
// insert may satisfy the same constraint.
1203-
var parentAndChildSameQueryColumns []string
1204-
if parentAndChildAreSameTable {
1205-
for _, otherRow := range allRows {
1206-
parentIdx, ok := nonGeneratedColumnNameToIndexMap[parentColumn]
1207-
if !ok {
1208-
return false, errors.Newf("parent column %s does not exist in table %s", parentColumn, childTableName)
1209-
}
1210-
parentValueInSameInsert := otherRow[parentIdx]
1211-
1212-
// Skip over NULL values.
1213-
if parentValueInSameInsert == "NULL" {
1214-
continue
1215-
}
1216-
parentAndChildSameQueryColumns = append(parentAndChildSameQueryColumns,
1217-
fmt.Sprintf("%s = %s", parentValueInSameInsert, childValue))
1218-
}
1219-
}
1220-
checkSharedParentChildRows := ""
1221-
if len(parentAndChildSameQueryColumns) > 0 {
1222-
// Check if none of the rows being inserted satisfy the foreign key constraint,
1223-
// since the foreign key constraints refers to the same table. So, anything in
1224-
// the insert batch can satisfy the constraint.
1225-
checkSharedParentChildRows = fmt.Sprintf("NOT (true = ANY (ARRAY [%s])) AND",
1226-
strings.Join(parentAndChildSameQueryColumns, ","))
1227-
}
1228-
q := fmt.Sprintf(`
1229-
SELECT %s count(*) = 0 from %s.%s
1230-
WHERE %s = (%s)
1231-
`,
1232-
checkSharedParentChildRows, parentTableSchema.String(), parentTableName.String(), parentColumn.String(), childValue)
1233-
return og.scanBool(ctx, tx, q)
1234-
}
1235-
12361023
func (og *operationGenerator) columnIsInAddingOrDroppingIndex(
12371024
ctx context.Context, tx pgx.Tx, tableName *tree.TableName, columnName tree.Name,
12381025
) (bool, error) {
@@ -1765,32 +1552,3 @@ func (og *operationGenerator) tableHasUniqueConstraintMutation(
17651552
AND (m->'index'->>'unique')::BOOL IS TRUE
17661553
);`, tableName)
17671554
}
1768-
1769-
// tableHasForeignKeyMutation determines if a table has any foreign key constraint
1770-
// mutation ongoing. This means either being added or dropped.
1771-
func (og *operationGenerator) tableHasForeignKeyMutation(
1772-
ctx context.Context, tx pgx.Tx, tableName *tree.TableName,
1773-
) (bool, error) {
1774-
return og.scanBool(ctx, tx, `
1775-
WITH table_desc AS (
1776-
SELECT crdb_internal.pb_to_json(
1777-
'desc',
1778-
descriptor,
1779-
false
1780-
)->'table' as d
1781-
FROM system.descriptor
1782-
WHERE id = $1::REGCLASS
1783-
)
1784-
SELECT EXISTS (
1785-
SELECT * FROM (
1786-
SELECT jsonb_array_elements(
1787-
CASE WHEN d->'mutations' IS NULL
1788-
THEN '[]'::JSONB
1789-
ELSE d->'mutations'
1790-
END
1791-
) as m
1792-
FROM table_desc)
1793-
WHERE (m->>'direction')::STRING IN ('ADD', 'DROP')
1794-
AND (m->'constraint'->>'foreign_key') IS NOT NULL
1795-
);`, tableName)
1796-
}

pkg/workload/schemachange/operation_generator.go

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3134,9 +3134,6 @@ func (og *operationGenerator) insertRow(ctx context.Context, tx pgx.Tx) (stmt *o
31343134
// we will need to evaluate generated expressions below.
31353135
hasUniqueConstraints := false
31363136
hasUniqueConstraintsMutations := false
3137-
hasForeignKeyMutations := false
3138-
expectedFkViolation := false
3139-
potentialFkViolation := false
31403137
if !anyInvalidInserts {
31413138
// Verify if the new row may violate unique constraints by checking the
31423139
// constraints in the database.
@@ -3151,31 +3148,17 @@ func (og *operationGenerator) insertRow(ctx context.Context, tx pgx.Tx) (stmt *o
31513148
if err != nil {
31523149
return nil, err
31533150
}
3154-
// Verify if the new row will violate fk constraints by checking the constraints and rows
3155-
// in the database.
3156-
expectedFkViolation, potentialFkViolation, err = og.violatesFkConstraints(ctx, tx, tableName, nonGeneratedColNames, rows)
3157-
if err != nil {
3158-
return nil, err
3159-
}
3160-
// Determine if a foreign key mutation exists.
3161-
hasForeignKeyMutations, err = og.tableHasForeignKeyMutation(ctx, tx, tableName)
3162-
if err != nil {
3163-
return nil, err
3164-
}
31653151
}
31663152

31673153
stmt.potentialExecErrors.addAll(codesWithConditions{
31683154
{code: pgcode.UniqueViolation, condition: hasUniqueConstraints || hasUniqueConstraintsMutations},
3169-
{code: pgcode.ForeignKeyViolation, condition: expectedFkViolation || potentialFkViolation || hasForeignKeyMutations},
3155+
{code: pgcode.ForeignKeyViolation, condition: true},
31703156
{code: pgcode.NotNullViolation, condition: true},
31713157
{code: pgcode.CheckViolation, condition: true},
31723158
{code: pgcode.InsufficientPrivilege, condition: true}, // For RLS violations
31733159
})
3174-
og.expectedCommitErrors.addAll(codesWithConditions{
3175-
{code: pgcode.ForeignKeyViolation, condition: expectedFkViolation && !hasForeignKeyMutations},
3176-
})
31773160
og.potentialCommitErrors.addAll(codesWithConditions{
3178-
{code: pgcode.ForeignKeyViolation, condition: potentialFkViolation || hasForeignKeyMutations},
3161+
{code: pgcode.ForeignKeyViolation, condition: true},
31793162
})
31803163

31813164
var formattedRows []string

0 commit comments

Comments
 (0)