constraints.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. package storage
  2. import (
  3. "fmt"
  4. "strings"
  5. )
  6. // PrimaryKeyViolationError preserves duplicate-key identity through wrapped
  7. // executor errors so wire adapters can report integrity SQLSTATE 23505.
  8. type PrimaryKeyViolationError struct {
  9. Table string
  10. Key string
  11. }
  12. func (e *PrimaryKeyViolationError) Error() string {
  13. return fmt.Sprintf("duplicate primary key: %s", e.Key)
  14. }
  15. // NotNullViolationError identifies the final row that violates a required column.
  16. type NotNullViolationError struct {
  17. Table string
  18. Column string
  19. }
  20. func (e *NotNullViolationError) Error() string {
  21. return fmt.Sprintf("NOT NULL constraint failed: %s.%s", e.Table, e.Column)
  22. }
  23. func validateNotNull(schema *Schema, row Row) error {
  24. for _, column := range schema.Columns {
  25. if !column.Nullable && row[column.Name] == nil {
  26. return &NotNullViolationError{Table: schema.Name, Column: column.Name}
  27. }
  28. }
  29. return nil
  30. }
  31. // CheckViolationError identifies the CHECK predicate a write violated. It is a
  32. // distinct type so the protocol layers can report SQLSTATE 23514.
  33. type CheckViolationError struct {
  34. Table string
  35. Constraint string
  36. }
  37. func (e *CheckViolationError) Error() string {
  38. return "CHECK constraint failed: " + e.Constraint
  39. }
  40. // validateChecks evaluates every persisted CHECK predicate against a complete
  41. // row image. The evaluator reports NULL and other unknown results as passing, so
  42. // SQL three-valued logic is preserved: a constraint only rejects a definite
  43. // false.
  44. func (m *SchemaManager) validateChecks(schema *Schema, row Row) error {
  45. if len(schema.Checks) == 0 {
  46. return nil
  47. }
  48. eval := m.checkEvaluator()
  49. if eval == nil {
  50. return fmt.Errorf("cannot validate CHECK constraints on %s: no CHECK evaluator installed", schema.Name)
  51. }
  52. for _, check := range schema.Checks {
  53. holds, err := eval(check.Expression, row)
  54. if err != nil {
  55. return fmt.Errorf("evaluate CHECK constraint %s on %s: %w", check.Label(), schema.Name, err)
  56. }
  57. if !holds {
  58. return &CheckViolationError{Table: schema.Name, Constraint: check.Label()}
  59. }
  60. }
  61. return nil
  62. }
  63. // validateRowConstraints enforces the NOT NULL and CHECK constraints a schema
  64. // declares against a complete row image. Every write path calls it after
  65. // defaults are applied and before the row becomes visible.
  66. func (m *SchemaManager) validateRowConstraints(schema *Schema, row Row) error {
  67. if err := validateNotNull(schema, row); err != nil {
  68. return err
  69. }
  70. return m.validateChecks(schema, row)
  71. }
  72. // applySchemaDefaults completes a row image that was built against an older
  73. // schema: columns the row predates receive their catalog default, and the row is
  74. // then validated against the required columns of the current schema.
  75. //
  76. // Write paths re-run it under the table gate because building a row image and
  77. // publishing it are separate steps. A transaction can normalize an INSERT before
  78. // a concurrent ADD COLUMN publishes a column and commit after it; the commit's
  79. // compare check cannot reject that row, because a new key is only checked for
  80. // absence. Re-applying the current schema keeps such a row from becoming a
  81. // durable NOT NULL violation, and gives it the default every other row received
  82. // from the backfill. An absent key is the only thing filled, so an explicit NULL
  83. // still does not select a default. Callers validate CHECK predicates separately
  84. // through validateRowConstraints/validateChecks so each write site can run them
  85. // against the final row image.
  86. func applySchemaDefaults(schema *Schema, row Row) error {
  87. for _, column := range schema.Columns {
  88. if _, ok := rowColumnValue(row, column.Name); ok {
  89. continue
  90. }
  91. if column.Default != nil {
  92. row[column.Name] = column.Default
  93. }
  94. }
  95. return validateNotNull(schema, row)
  96. }
  97. // rowColumnValue returns the value a row holds for a column, matching the column
  98. // name case-insensitively.
  99. func rowColumnValue(row Row, name string) (interface{}, bool) {
  100. if value, ok := row[name]; ok {
  101. return value, true
  102. }
  103. for key, value := range row {
  104. if strings.EqualFold(key, name) {
  105. return value, true
  106. }
  107. }
  108. return nil, false
  109. }