ddl_defaults.go 1.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253
  1. package executor
  2. import (
  3. "fmt"
  4. "github.com/danfragoso/pizzasql-next/pkg/lexer"
  5. "github.com/danfragoso/pizzasql-next/pkg/parser"
  6. "github.com/danfragoso/pizzasql-next/pkg/pgerror"
  7. )
  8. // constantDefaultValue evaluates a DEFAULT expression that cannot depend on the
  9. // row, session clock, or other mutable state.
  10. //
  11. // The schema catalog stores the evaluated value, not the expression SQL, so a
  12. // non-constant default such as CURRENT_TIMESTAMP would be frozen at DDL time and
  13. // every later INSERT would silently receive the same value. Rejecting the
  14. // declaration is safer than accepting it with different semantics. Per-INSERT
  15. // evaluation is not implemented.
  16. func constantDefaultValue(e *Executor, column string, expr parser.Expr) (interface{}, error) {
  17. if !isConstantExpr(expr) {
  18. return nil, pgerror.Unsupported(
  19. "DEFAULT for column %q is not a constant expression: %s; PizzaSQL stores the evaluated default in the schema catalog, so a non-constant default would be frozen at CREATE/ALTER time. Omit the default and supply the value on INSERT",
  20. column, parser.FormatExpr(expr),
  21. )
  22. }
  23. value, err := e.evalExpr(expr, nil)
  24. if err != nil {
  25. return nil, fmt.Errorf("evaluate DEFAULT for column %q: %w", column, err)
  26. }
  27. return value, nil
  28. }
  29. // isConstantExpr reports whether expr is a literal, a unary sign applied to a
  30. // literal, a parenthesized constant, or a cast of a constant. Everything else
  31. // (column references including CURRENT_TIMESTAMP, function calls, subqueries,
  32. // arithmetic) is rejected because its value can change between statements.
  33. func isConstantExpr(expr parser.Expr) bool {
  34. switch ex := expr.(type) {
  35. case *parser.LiteralExpr:
  36. return true
  37. case *parser.UnaryExpr:
  38. if ex.Op != lexer.TokenMinus && ex.Op != lexer.TokenPlus {
  39. return false
  40. }
  41. return isConstantExpr(ex.Operand)
  42. case *parser.ParenExpr:
  43. return isConstantExpr(ex.Expr)
  44. case *parser.CastExpr:
  45. return isConstantExpr(ex.Expr)
  46. default:
  47. return false
  48. }
  49. }