2
0

foreign_keys.go 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. package executor
  2. import (
  3. "fmt"
  4. "strings"
  5. "github.com/danfragoso/pizzasql-next/pkg/parser"
  6. "github.com/danfragoso/pizzasql-next/pkg/pgerror"
  7. "github.com/danfragoso/pizzasql-next/pkg/storage"
  8. )
  9. // foreignKeyDeclaration is one parsed FOREIGN KEY (or inline REFERENCES) before
  10. // it is resolved against the parent table.
  11. type foreignKeyDeclaration struct {
  12. name string
  13. columns []string
  14. refTable string
  15. refColumns []string
  16. onDelete parser.ReferentialAction
  17. onUpdate parser.ReferentialAction
  18. deferrable bool
  19. }
  20. // createTableForeignKeys resolves the FOREIGN KEY declarations of a CREATE TABLE
  21. // into durable constraints. The parent table must already exist with its
  22. // unique key in place, except for a self-reference to the table being created;
  23. // a forward reference fails here instead of being stored and silently ignored.
  24. func (e *Executor) createTableForeignKeys(stmt *parser.CreateTableStmt, schema *storage.Schema, uniqueIndexes []*storage.Index) ([]storage.ForeignKeyConstraint, error) {
  25. var declarations []foreignKeyDeclaration
  26. for _, colDef := range stmt.Columns {
  27. for _, constraint := range colDef.Constraints {
  28. if constraint.Type != parser.ConstraintForeignKey {
  29. continue
  30. }
  31. declarations = append(declarations, foreignKeyDeclaration{
  32. name: constraint.Name,
  33. columns: []string{colDef.Name},
  34. refTable: constraint.RefTable,
  35. refColumns: constraint.RefColumns,
  36. onDelete: constraint.OnDelete,
  37. onUpdate: constraint.OnUpdate,
  38. deferrable: constraint.Deferrable,
  39. })
  40. }
  41. }
  42. for _, constraint := range stmt.Constraints {
  43. if constraint.Type != parser.ConstraintForeignKey {
  44. continue
  45. }
  46. declarations = append(declarations, foreignKeyDeclaration{
  47. name: constraint.Name,
  48. columns: constraint.Columns,
  49. refTable: constraint.RefTable,
  50. refColumns: constraint.RefColumns,
  51. onDelete: constraint.OnDelete,
  52. onUpdate: constraint.OnUpdate,
  53. deferrable: constraint.Deferrable,
  54. })
  55. }
  56. foreignKeys := make([]storage.ForeignKeyConstraint, 0, len(declarations))
  57. for _, declaration := range declarations {
  58. foreignKey, err := e.resolveForeignKey(stmt.Table.Name, schema, uniqueIndexes, declaration)
  59. if err != nil {
  60. return nil, err
  61. }
  62. foreignKeys = append(foreignKeys, foreignKey)
  63. }
  64. return foreignKeys, nil
  65. }
  66. // resolveForeignKey validates one declaration and returns its durable form.
  67. func (e *Executor) resolveForeignKey(table string, schema *storage.Schema, uniqueIndexes []*storage.Index, declaration foreignKeyDeclaration) (storage.ForeignKeyConstraint, error) {
  68. label := declaration.label()
  69. if declaration.deferrable {
  70. return storage.ForeignKeyConstraint{}, pgerror.Unsupported(
  71. "foreign key %s on table %s is DEFERRABLE; foreign keys are always enforced immediately and deferred constraints are not implemented",
  72. label, table,
  73. )
  74. }
  75. childColumns := make([]string, 0, len(declaration.columns))
  76. seen := make(map[string]bool, len(declaration.columns))
  77. for _, name := range declaration.columns {
  78. column, ok := schema.GetColumn(name)
  79. if !ok {
  80. return storage.ForeignKeyConstraint{}, pgerror.Unsupported(
  81. "foreign key %s on table %s names column %q, which does not exist",
  82. label, table, name,
  83. )
  84. }
  85. if seen[strings.ToLower(column.Name)] {
  86. return storage.ForeignKeyConstraint{}, pgerror.Unsupported(
  87. "foreign key %s on table %s repeats column %q",
  88. label, table, column.Name,
  89. )
  90. }
  91. seen[strings.ToLower(column.Name)] = true
  92. childColumns = append(childColumns, column.Name)
  93. }
  94. if len(childColumns) == 0 {
  95. return storage.ForeignKeyConstraint{}, pgerror.Unsupported(
  96. "foreign key %s on table %s declares no columns", label, table,
  97. )
  98. }
  99. selfReference := strings.EqualFold(declaration.refTable, table)
  100. var parent *storage.Schema
  101. var parentIndexes []*storage.Index
  102. if selfReference {
  103. parent = schema
  104. parentIndexes = uniqueIndexes
  105. } else {
  106. resolved, err := e.schema.GetSchema(declaration.refTable)
  107. if err != nil {
  108. return storage.ForeignKeyConstraint{}, pgerror.Unsupported(
  109. "foreign key %s on table %s references table %q, which does not exist; create the parent table before the child table",
  110. label, table, declaration.refTable,
  111. )
  112. }
  113. parent = resolved
  114. indexes, err := e.schema.ListTableIndexes(declaration.refTable)
  115. if err != nil {
  116. return storage.ForeignKeyConstraint{}, err
  117. }
  118. parentIndexes = indexes
  119. }
  120. refColumns := append([]string(nil), declaration.refColumns...)
  121. if len(refColumns) == 0 {
  122. if parent.UsesSyntheticRowID() {
  123. return storage.ForeignKeyConstraint{}, pgerror.Unsupported(
  124. "foreign key %s on table %s references table %s without a column list, but %s has no PRIMARY KEY; declare a PRIMARY KEY or list parent columns covered by a UNIQUE index",
  125. label, table, parent.Name, parent.Name,
  126. )
  127. }
  128. refColumns = parent.PrimaryKeyList()
  129. }
  130. if len(childColumns) != len(refColumns) {
  131. return storage.ForeignKeyConstraint{}, pgerror.Unsupported(
  132. "foreign key %s on table %s has %d child column(s) but references %d parent column(s); both sides of a foreign key need the same number of columns",
  133. label, table, len(childColumns), len(refColumns),
  134. )
  135. }
  136. canonical := make([]string, 0, len(refColumns))
  137. for _, name := range refColumns {
  138. column, ok := parent.GetColumn(name)
  139. if !ok {
  140. return storage.ForeignKeyConstraint{}, pgerror.Unsupported(
  141. "foreign key %s on table %s references column %q, which does not exist in table %s",
  142. label, table, name, parent.Name,
  143. )
  144. }
  145. canonical = append(canonical, column.Name)
  146. }
  147. if !foreignKeyTargetIsUnique(parent, canonical, parentIndexes) {
  148. return storage.ForeignKeyConstraint{}, pgerror.Unsupported(
  149. "foreign key mismatch: table %s references %s(%s), but the referenced columns are neither the parent's PRIMARY KEY nor covered by a UNIQUE index; declare UNIQUE (%s) on %s first",
  150. table, parent.Name, strings.Join(canonical, ", "), strings.Join(canonical, ", "), parent.Name,
  151. )
  152. }
  153. return storage.ForeignKeyConstraint{
  154. Name: declaration.name,
  155. Columns: childColumns,
  156. RefTable: parent.Name,
  157. RefColumns: canonical,
  158. OnDelete: declaration.onDelete.String(),
  159. OnUpdate: declaration.onUpdate.String(),
  160. }, nil
  161. }
  162. // label names the declaration in a DDL error before it has been resolved.
  163. func (d foreignKeyDeclaration) label() string {
  164. if d.name != "" {
  165. return d.name
  166. }
  167. if len(d.columns) == 0 {
  168. return "FOREIGN KEY"
  169. }
  170. return fmt.Sprintf("FOREIGN KEY (%s)", strings.Join(d.columns, ", "))
  171. }
  172. // foreignKeyTargetIsUnique reports whether columns is exactly the parent's
  173. // primary key or exactly the column set of a UNIQUE index on the parent.
  174. func foreignKeyTargetIsUnique(parent *storage.Schema, columns []string, indexes []*storage.Index) bool {
  175. if !parent.UsesSyntheticRowID() && parent.PrimaryKeyTargetMatches(columns) {
  176. return true
  177. }
  178. for _, index := range indexes {
  179. if !index.Unique || len(index.Columns) != len(columns) {
  180. continue
  181. }
  182. plain := true
  183. names := make([]string, len(index.Columns))
  184. for i, column := range index.Columns {
  185. if column.Expression != "" {
  186. plain = false
  187. break
  188. }
  189. names[i] = column.Name
  190. }
  191. if plain && sameColumnSet(names, columns) {
  192. return true
  193. }
  194. }
  195. return false
  196. }
  197. // sameColumnSet reports whether both lists name the same columns, in any order.
  198. func sameColumnSet(a, b []string) bool {
  199. if len(a) != len(b) {
  200. return false
  201. }
  202. for _, name := range a {
  203. found := false
  204. for _, other := range b {
  205. if strings.EqualFold(name, other) {
  206. found = true
  207. break
  208. }
  209. }
  210. if !found {
  211. return false
  212. }
  213. }
  214. return true
  215. }
  216. // rejectForeignKeyColumnChange refuses to drop or rename a column that a
  217. // persisted foreign key depends on, on the child side or as a referenced parent
  218. // column. Constraint metadata stores column names verbatim and cannot be
  219. // rewritten, so accepting the change would either silently stop enforcing the
  220. // constraint or leave the catalog naming a missing column.
  221. func (e *Executor) rejectForeignKeyColumnChange(table, column, action string) error {
  222. labels, err := e.schema.ForeignKeyColumnDependency(table, column)
  223. if err != nil {
  224. return err
  225. }
  226. if len(labels) == 0 {
  227. return nil
  228. }
  229. return pgerror.Unsupported(
  230. "cannot %s column %s: foreign key %s depends on it; drop the constrained table (or recreate it without that constraint) first",
  231. action, column, strings.Join(labels, ", "),
  232. )
  233. }
  234. // rejectForeignKeyTableRename refuses to rename a table another constraint (or
  235. // its own constraint) names as the parent. The referenced table name is
  236. // persisted verbatim, so a rename would leave parent-side enforcement pointing
  237. // at a table that no longer exists.
  238. func (e *Executor) rejectForeignKeyTableRename(table string) error {
  239. labels, err := e.schema.ForeignKeyTableDependency(table)
  240. if err != nil {
  241. return err
  242. }
  243. if len(labels) == 0 {
  244. return nil
  245. }
  246. return pgerror.Unsupported(
  247. "cannot rename table %s: foreign key %s references it; drop the constrained table (or recreate it without that constraint) first",
  248. table, strings.Join(labels, ", "),
  249. )
  250. }
  251. // rejectForeignKeyDependentTableDrop refuses to drop a table that a persisted
  252. // foreign key names as its parent, because the constraint would be left dangling
  253. // on the child table.
  254. func (e *Executor) rejectForeignKeyDependentTableDrop(table string) error {
  255. labels, err := e.schema.ForeignKeyTableDependency(table)
  256. if err != nil {
  257. return err
  258. }
  259. if len(labels) == 0 {
  260. return nil
  261. }
  262. return pgerror.Unsupported(
  263. "cannot drop table %s: foreign key %s references it; drop or recreate the table that declares that constraint first",
  264. table, strings.Join(labels, ", "),
  265. )
  266. }