Skip to content

Commit b13e228

Browse files
committed
workload/schemachange: handle triggers in NULL constraint validation
Previously, the schemachange workload would expect a NOT NULL violation error when inserting NULL values into NOT NULL columns. However, if a table has a BEFORE INSERT trigger that returns NULL, the insertion is silently cancelled without raising any error, causing test failures. This change detects when a table has BEFORE INSERT triggers and treats NOT NULL violations as potential errors rather than expected errors in such cases. Fixes #152815 Release note: None Epic: None
1 parent dd1a00f commit b13e228

File tree

1 file changed

+36
-1
lines changed

1 file changed

+36
-1
lines changed

pkg/workload/schemachange/error_screening.go

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,28 @@ func isValidGenerationError(code string) bool {
494494
return getValidGenerationErrors().contains(pgCode)
495495
}
496496

497+
// tableHasBeforeInsertTrigger checks if the table has any BEFORE INSERT triggers
498+
// that could affect the insertion behavior.
499+
func (og *operationGenerator) tableHasBeforeInsertTrigger(
500+
ctx context.Context, tx pgx.Tx, tableName *tree.TableName,
501+
) (bool, error) {
502+
query := `
503+
SELECT EXISTS (
504+
SELECT 1 FROM pg_trigger t
505+
JOIN pg_class c ON t.tgrelid = c.oid
506+
JOIN pg_namespace n ON c.relnamespace = n.oid
507+
WHERE n.nspname = $1
508+
AND c.relname = $2
509+
AND t.tgenabled != 'D'
510+
AND (t.tgtype & 2) = 2 -- BEFORE trigger
511+
AND (t.tgtype & 4) = 4 -- INSERT trigger
512+
)`
513+
514+
var hasTrigger bool
515+
err := tx.QueryRow(ctx, query, tableName.Schema(), tableName.Object()).Scan(&hasTrigger)
516+
return hasTrigger, err
517+
}
518+
497519
// validateGeneratedExpressionsForInsert goes through generated expressions and
498520
// detects if a valid value can be generated with a given insert row.
499521
func (og *operationGenerator) validateGeneratedExpressionsForInsert(
@@ -514,6 +536,12 @@ func (og *operationGenerator) validateGeneratedExpressionsForInsert(
514536
isInvalidInsert = true
515537
}
516538
}()
539+
540+
hasBeforeInsertTrigger, err := og.tableHasBeforeInsertTrigger(ctx, tx, tableName)
541+
if err != nil {
542+
return false, nil, nil, err
543+
}
544+
517545
// Put values to be inserted into a column name to value map to simplify lookups.
518546
columnsToValues := map[tree.Name]string{}
519547
for i := 0; i < len(nonGeneratedColNames); i++ {
@@ -601,7 +629,14 @@ func (og *operationGenerator) validateGeneratedExpressionsForInsert(
601629
}
602630
if isNull && !isNullable && !nullViolationAdded {
603631
nullViolationAdded = true
604-
expectedErrCodes = expectedErrCodes.append(pgcode.NotNullViolation)
632+
// If there is a trigger, then we cannot be sure if the NULL value
633+
// will actually be inserted. It may be replaced or the row itself could
634+
// be cleared.
635+
if hasBeforeInsertTrigger {
636+
potentialErrCodes = potentialErrCodes.append(pgcode.NotNullViolation)
637+
} else {
638+
expectedErrCodes = expectedErrCodes.append(pgcode.NotNullViolation)
639+
}
605640
}
606641
// Re-run the another variant in case we have NULL values in arithmetic
607642
// of expression, the evaluation order can differ depending on how variables

0 commit comments

Comments
 (0)