| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156 |
- package storage
- import (
- "fmt"
- "strings"
- "time"
- )
- // PrimaryKeyViolationError preserves duplicate-key identity through wrapped
- // executor errors so wire adapters can report integrity SQLSTATE 23505.
- type PrimaryKeyViolationError struct {
- Table string
- Key string
- }
- func (e *PrimaryKeyViolationError) Error() string {
- return fmt.Sprintf("duplicate primary key: %s", e.Key)
- }
- // NotNullViolationError identifies the final row that violates a required column.
- type NotNullViolationError struct {
- Table string
- Column string
- }
- func (e *NotNullViolationError) Error() string {
- return fmt.Sprintf("NOT NULL constraint failed: %s.%s", e.Table, e.Column)
- }
- func validateNotNull(schema *Schema, row Row) error {
- for _, column := range schema.Columns {
- if !column.Nullable && row[column.Name] == nil {
- return &NotNullViolationError{Table: schema.Name, Column: column.Name}
- }
- }
- return nil
- }
- // CheckViolationError identifies the CHECK predicate a write violated. It is a
- // distinct type so the protocol layers can report SQLSTATE 23514.
- type CheckViolationError struct {
- Table string
- Constraint string
- }
- func (e *CheckViolationError) Error() string {
- return "CHECK constraint failed: " + e.Constraint
- }
- // validateChecks evaluates every persisted CHECK predicate against a complete
- // row image. The evaluator reports NULL and other unknown results as passing, so
- // SQL three-valued logic is preserved: a constraint only rejects a definite
- // false.
- func (m *SchemaManager) validateChecks(schema *Schema, row Row) error {
- if len(schema.Checks) == 0 {
- return nil
- }
- eval := m.checkEvaluator()
- if eval == nil {
- return fmt.Errorf("cannot validate CHECK constraints on %s: no CHECK evaluator installed", schema.Name)
- }
- for _, check := range schema.Checks {
- holds, err := eval(check.Expression, row)
- if err != nil {
- return fmt.Errorf("evaluate CHECK constraint %s on %s: %w", check.Label(), schema.Name, err)
- }
- if !holds {
- return &CheckViolationError{Table: schema.Name, Constraint: check.Label()}
- }
- }
- return nil
- }
- // validateRowConstraints enforces the NOT NULL and CHECK constraints a schema
- // declares against a complete row image. Every write path calls it after
- // defaults are applied and before the row becomes visible.
- func (m *SchemaManager) validateRowConstraints(schema *Schema, row Row) error {
- if err := validateNotNull(schema, row); err != nil {
- return err
- }
- return m.validateChecks(schema, row)
- }
- // applySchemaDefaults completes a row image that was built against an older
- // schema: columns the row predates receive their catalog default, and the row is
- // then validated against the required columns of the current schema.
- //
- // Write paths re-run it under the table gate because building a row image and
- // publishing it are separate steps. A transaction can normalize an INSERT before
- // a concurrent ADD COLUMN publishes a column and commit after it; the commit's
- // compare check cannot reject that row, because a new key is only checked for
- // absence. Re-applying the current schema keeps such a row from becoming a
- // durable NOT NULL violation, and gives it the default every other row received
- // from the backfill. An absent key is the only thing filled, so an explicit NULL
- // still does not select a default. Callers validate CHECK predicates separately
- // through validateRowConstraints/validateChecks so each write site can run them
- // against the final row image.
- //
- // A constant default is copied from the catalog. An expression default is
- // evaluated here instead, so a row that reaches storage without one (for
- // example a staged transaction row whose column was published later) receives a
- // value rather than freezing the DDL-time one. Ordinary INSERT/UPDATE resolve
- // expression defaults in the executor before the write; this is the fallback
- // for every other path.
- func (m *SchemaManager) applySchemaDefaults(schema *Schema, row Row) error {
- for _, column := range schema.Columns {
- if _, ok := rowColumnValue(row, column.Name); ok {
- continue
- }
- value, ok, err := m.columnDefaultValue(column, m.defaultNow())
- if err != nil {
- return err
- }
- if ok {
- row[column.Name] = value
- }
- }
- return validateNotNull(schema, row)
- }
- // columnDefaultValue returns the value a row receives for a column it omits.
- // A column with no default reports ok=false. DefaultExpr takes precedence over
- // Default; the two are never both set by DDL, which keeps constant defaults in
- // the catalog as values and clock-reading defaults as SQL text. now is the
- // instant the caller sampled for its statement; a constant default ignores it.
- func (m *SchemaManager) columnDefaultValue(column Column, now time.Time) (interface{}, bool, error) {
- if column.DefaultExpr != "" {
- eval := m.defaultEvaluator()
- if eval == nil {
- return nil, false, fmt.Errorf("column %q has a DEFAULT expression but no evaluator is installed", column.Name)
- }
- value, err := eval(column.DefaultExpr, now)
- if err != nil {
- return nil, false, fmt.Errorf("evaluate DEFAULT for column %q: %w", column.Name, err)
- }
- return value, true, nil
- }
- if column.Default != nil {
- return column.Default, true, nil
- }
- return nil, false, nil
- }
- // rowColumnValue returns the value a row holds for a column, matching the column
- // name case-insensitively.
- func rowColumnValue(row Row, name string) (interface{}, bool) {
- if value, ok := row[name]; ok {
- return value, true
- }
- for key, value := range row {
- if strings.EqualFold(key, name) {
- return value, true
- }
- }
- return nil, false
- }
|