@@ -494,6 +494,28 @@ func isValidGenerationError(code string) bool {
494
494
return getValidGenerationErrors ().contains (pgCode )
495
495
}
496
496
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
+
497
519
// validateGeneratedExpressionsForInsert goes through generated expressions and
498
520
// detects if a valid value can be generated with a given insert row.
499
521
func (og * operationGenerator ) validateGeneratedExpressionsForInsert (
@@ -514,6 +536,12 @@ func (og *operationGenerator) validateGeneratedExpressionsForInsert(
514
536
isInvalidInsert = true
515
537
}
516
538
}()
539
+
540
+ hasBeforeInsertTrigger , err := og .tableHasBeforeInsertTrigger (ctx , tx , tableName )
541
+ if err != nil {
542
+ return false , nil , nil , err
543
+ }
544
+
517
545
// Put values to be inserted into a column name to value map to simplify lookups.
518
546
columnsToValues := map [tree.Name ]string {}
519
547
for i := 0 ; i < len (nonGeneratedColNames ); i ++ {
@@ -601,7 +629,14 @@ func (og *operationGenerator) validateGeneratedExpressionsForInsert(
601
629
}
602
630
if isNull && ! isNullable && ! nullViolationAdded {
603
631
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
+ }
605
640
}
606
641
// Re-run the another variant in case we have NULL values in arithmetic
607
642
// of expression, the evaluation order can differ depending on how variables
0 commit comments