foreign_key.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  1. package storage
  2. import (
  3. "fmt"
  4. "sort"
  5. "strconv"
  6. "strings"
  7. )
  8. // ForeignKeyViolationError reports a row that would leave a foreign key
  9. // dangling. It is a distinct type so the protocol layers can report SQLSTATE
  10. // 23503 instead of a generic execution error.
  11. type ForeignKeyViolationError struct {
  12. // Table is the child table whose row cannot be written, or whose rows would
  13. // be orphaned by the parent change.
  14. Table string
  15. // Constraint is the label of the violated FOREIGN KEY declaration.
  16. Constraint string
  17. }
  18. func (e *ForeignKeyViolationError) Error() string {
  19. if e.Constraint != "" {
  20. return fmt.Sprintf("FOREIGN KEY constraint failed: %s", e.Constraint)
  21. }
  22. return "FOREIGN KEY constraint failed: " + e.Table
  23. }
  24. // foreignKeyValues returns the formatted values a row holds for the given
  25. // columns, and reports false when any component is NULL or missing. MATCH
  26. // SIMPLE semantics: a partially NULL key is never matched against the parent, so
  27. // the constraint does not apply to that row.
  28. func foreignKeyValues(row Row, columns []string) ([]string, bool) {
  29. values := make([]string, len(columns))
  30. for i, name := range columns {
  31. value, ok := rowColumnValue(row, name)
  32. if !ok || value == nil {
  33. return nil, false
  34. }
  35. values[i] = formatIndexValue(value)
  36. }
  37. return values, true
  38. }
  39. // foreignKeyMatches reports whether row's columns equal want.
  40. func foreignKeyMatches(row Row, columns, want []string) bool {
  41. if len(columns) != len(want) {
  42. return false
  43. }
  44. got, ok := foreignKeyValues(row, columns)
  45. if !ok {
  46. return false
  47. }
  48. for i := range want {
  49. if got[i] != want[i] {
  50. return false
  51. }
  52. }
  53. return true
  54. }
  55. func foreignKeyValuesEqual(a, b []string) bool {
  56. if len(a) != len(b) {
  57. return false
  58. }
  59. for i := range a {
  60. if a[i] != b[i] {
  61. return false
  62. }
  63. }
  64. return true
  65. }
  66. // parentRowExists reports whether table holds a row whose columns match want.
  67. // extra supplies rows that are not durable yet (the rows of the current
  68. // statement), so a self-referential row can reference itself or another row of
  69. // the same multi-row insert. The scan deliberately takes no table gate: callers
  70. // hold the table's write gate already, and re-entering it would deadlock on a
  71. // self-referential constraint.
  72. func (m *TableManager) parentRowExists(table string, extra []Row, columns, want []string) (bool, error) {
  73. for _, row := range extra {
  74. if foreignKeyMatches(row, columns, want) {
  75. return true, nil
  76. }
  77. }
  78. found := false
  79. err := m.scanRows(table, func(row Row) (bool, error) {
  80. if foreignKeyMatches(row, columns, want) {
  81. found = true
  82. return true, nil
  83. }
  84. return false, nil
  85. })
  86. return found, err
  87. }
  88. // validateInsertForeignKeys enforces every FOREIGN KEY declared on schema for
  89. // rows that are about to be written. rows holds all rows of the statement so a
  90. // self-referential insert can reference a sibling row in the same batch.
  91. func (m *TableManager) validateInsertForeignKeys(schema *Schema, rows []Row) error {
  92. if len(schema.ForeignKeys) == 0 {
  93. return nil
  94. }
  95. for _, fk := range schema.ForeignKeys {
  96. for _, row := range rows {
  97. want, ok := foreignKeyValues(row, fk.Columns)
  98. if !ok {
  99. continue
  100. }
  101. var extra []Row
  102. if strings.EqualFold(fk.RefTable, schema.Name) {
  103. extra = rows
  104. }
  105. found, err := m.parentRowExists(fk.RefTable, extra, fk.RefColumns, want)
  106. if err != nil {
  107. return fmt.Errorf("validate foreign key %s: %w", fk.Label(), err)
  108. }
  109. if !found {
  110. return &ForeignKeyViolationError{Table: schema.Name, Constraint: fk.Label()}
  111. }
  112. }
  113. }
  114. return nil
  115. }
  116. // validateParentKeyRemoval rejects a parent change that would orphan an existing
  117. // child row. oldRow is the durable parent image; newRow is the replacement image
  118. // for an UPDATE (nil for a DELETE). A change that leaves every referenced value
  119. // untouched cannot orphan anything and is not checked.
  120. //
  121. // A self-referential table needs no special exemption for a row that references
  122. // itself: the row being deleted (or re-keyed) is skipped in the child scan and
  123. // its replacement image is evaluated instead, so deleting a self-referencing row
  124. // works while re-keying it into a dangling reference still fails.
  125. func (m *TableManager) validateParentKeyRemoval(table string, oldRow, newRow Row) error {
  126. referencing, err := m.schema.referencingForeignKeys(table)
  127. if err != nil {
  128. return err
  129. }
  130. selfPK := ""
  131. var selfSchema *Schema
  132. if len(referencing) > 0 {
  133. schema, err := m.schema.GetSchema(table)
  134. if err != nil {
  135. return err
  136. }
  137. selfSchema = schema
  138. if pk, err := schema.PrimaryKeyForRow(oldRow); err == nil {
  139. selfPK = pk
  140. }
  141. }
  142. for _, ref := range referencing {
  143. oldKey, ok := foreignKeyValues(oldRow, ref.FK.RefColumns)
  144. if !ok {
  145. continue
  146. }
  147. if newRow != nil {
  148. if newKey, ok := foreignKeyValues(newRow, ref.FK.RefColumns); ok && foreignKeyValuesEqual(oldKey, newKey) {
  149. continue
  150. }
  151. }
  152. selfTable := strings.EqualFold(ref.Table, table)
  153. found := false
  154. err := m.scanRows(ref.Table, func(row Row) (bool, error) {
  155. if selfTable && selfPK != "" {
  156. if pk, err := selfSchema.PrimaryKeyForRow(row); err == nil && pk == selfPK {
  157. return false, nil
  158. }
  159. }
  160. if foreignKeyMatches(row, ref.FK.Columns, oldKey) {
  161. found = true
  162. return true, nil
  163. }
  164. return false, nil
  165. })
  166. if err != nil {
  167. return fmt.Errorf("validate foreign key %s: %w", ref.FK.Label(), err)
  168. }
  169. if !found && selfTable && newRow != nil && foreignKeyMatches(newRow, ref.FK.Columns, oldKey) {
  170. found = true
  171. }
  172. if found {
  173. return &ForeignKeyViolationError{Table: ref.Table, Constraint: ref.FK.Label()}
  174. }
  175. }
  176. return nil
  177. }
  178. // referencingForeignKey is one FOREIGN KEY declaration that targets a table.
  179. type referencingForeignKey struct {
  180. Table string
  181. FK ForeignKeyConstraint
  182. }
  183. // foreignKeyCatalog is the cached foreign-key metadata of the database. It is
  184. // keyed by the schema manager version so a DDL change rebuilds it.
  185. type foreignKeyCatalog struct {
  186. version uint64
  187. participants map[string]bool
  188. referencing map[string][]referencingForeignKey
  189. adjacency map[string]map[string]bool
  190. }
  191. // foreignKeyCatalogSnapshot returns the current foreign-key metadata, rebuilding
  192. // it when the schema version changed.
  193. func (m *SchemaManager) foreignKeyCatalogSnapshot() (*foreignKeyCatalog, error) {
  194. version := m.Version()
  195. m.fkCatalogMu.Lock()
  196. cached := m.fkCatalog
  197. m.fkCatalogMu.Unlock()
  198. if cached != nil && cached.version == version {
  199. return cached, nil
  200. }
  201. tables, err := m.ListTables()
  202. if err != nil {
  203. return nil, err
  204. }
  205. catalog := &foreignKeyCatalog{
  206. version: version,
  207. participants: make(map[string]bool),
  208. referencing: make(map[string][]referencingForeignKey),
  209. adjacency: make(map[string]map[string]bool),
  210. }
  211. link := func(a, b string) {
  212. if catalog.adjacency[a] == nil {
  213. catalog.adjacency[a] = make(map[string]bool)
  214. }
  215. catalog.adjacency[a][b] = true
  216. }
  217. for _, name := range tables {
  218. schema, err := m.GetSchema(name)
  219. if err != nil {
  220. return nil, err
  221. }
  222. child := strings.ToLower(schema.Name)
  223. for _, fk := range schema.ForeignKeys {
  224. parent := strings.ToLower(fk.RefTable)
  225. catalog.participants[child] = true
  226. catalog.participants[parent] = true
  227. catalog.referencing[parent] = append(catalog.referencing[parent], referencingForeignKey{Table: schema.Name, FK: fk})
  228. link(child, parent)
  229. link(parent, child)
  230. }
  231. }
  232. m.fkCatalogMu.Lock()
  233. if m.Version() == version {
  234. m.fkCatalog = catalog
  235. }
  236. m.fkCatalogMu.Unlock()
  237. return catalog, nil
  238. }
  239. // referencingForeignKeys returns every FOREIGN KEY declaration whose parent is
  240. // table.
  241. func (m *SchemaManager) referencingForeignKeys(table string) ([]referencingForeignKey, error) {
  242. catalog, err := m.foreignKeyCatalogSnapshot()
  243. if err != nil {
  244. return nil, err
  245. }
  246. return catalog.referencing[strings.ToLower(table)], nil
  247. }
  248. // ForeignKeyParticipants reports whether a write to table can affect referential
  249. // integrity. A table participates when it declares a FOREIGN KEY or when another
  250. // table references it.
  251. func (m *SchemaManager) ForeignKeyParticipants(table string) (bool, error) {
  252. catalog, err := m.foreignKeyCatalogSnapshot()
  253. if err != nil {
  254. return false, err
  255. }
  256. return catalog.participants[strings.ToLower(table)], nil
  257. }
  258. // ForeignKeyClosure returns the tables whose rows must be re-validated when the
  259. // given tables change: the foreign-key-connected component of every changed
  260. // table that participates in a constraint. The result is empty when no changed
  261. // table participates.
  262. func (m *SchemaManager) ForeignKeyClosure(tables []string) (map[string]bool, error) {
  263. catalog, err := m.foreignKeyCatalogSnapshot()
  264. if err != nil {
  265. return nil, err
  266. }
  267. closure := make(map[string]bool)
  268. queue := make([]string, 0, len(tables))
  269. for _, table := range tables {
  270. if catalog.participants[strings.ToLower(table)] {
  271. queue = append(queue, strings.ToLower(table))
  272. }
  273. }
  274. for len(queue) > 0 {
  275. table := queue[0]
  276. queue = queue[1:]
  277. if closure[table] {
  278. continue
  279. }
  280. closure[table] = true
  281. neighbors := make([]string, 0, len(catalog.adjacency[table]))
  282. for neighbor := range catalog.adjacency[table] {
  283. neighbors = append(neighbors, neighbor)
  284. }
  285. sort.Strings(neighbors)
  286. for _, neighbor := range neighbors {
  287. if !closure[neighbor] {
  288. queue = append(queue, neighbor)
  289. }
  290. }
  291. }
  292. return closure, nil
  293. }
  294. // lockForeignKeyWrites serializes writes that can affect referential integrity,
  295. // so a validation scan cannot interleave with a concurrent insert, update, or
  296. // delete on the other side of a constraint. It must be acquired before any
  297. // table gate: commits validate several tables at once, and the canonical order
  298. // (foreign-key gate, then table gates) is what keeps that deadlock-free.
  299. func (m *TableManager) lockForeignKeyWrites(tables ...string) (func(), error) {
  300. for _, table := range tables {
  301. participates, err := m.schema.ForeignKeyParticipants(table)
  302. if err != nil {
  303. return nil, err
  304. }
  305. if participates {
  306. m.foreignKeyMu.Lock()
  307. return m.foreignKeyMu.Unlock, nil
  308. }
  309. }
  310. return func() {}, nil
  311. }
  312. // foreignKeyStateValidator validates the final row state of a set of tables
  313. // against every FOREIGN KEY declaration among them.
  314. type foreignKeyStateValidator struct {
  315. tables map[string][]Row
  316. keySets map[string]map[string]bool
  317. schemas map[string]*Schema
  318. }
  319. // validateForeignKeyState checks every child row of the given tables against its
  320. // parent rows. tables maps a lowercased table name to its final row set (durable
  321. // rows with staged deletes applied plus staged writes).
  322. func (v *foreignKeyStateValidator) validateForeignKeyState() error {
  323. if v.keySets == nil {
  324. v.keySets = make(map[string]map[string]bool)
  325. }
  326. for table, rows := range v.tables {
  327. schema := v.schemas[table]
  328. if schema == nil {
  329. continue
  330. }
  331. for _, fk := range schema.ForeignKeys {
  332. parent := strings.ToLower(fk.RefTable)
  333. parentRows, ok := v.tables[parent]
  334. if !ok {
  335. return fmt.Errorf("foreign key %s: parent table %s is not available for validation", fk.Label(), fk.RefTable)
  336. }
  337. key := parent + "\x00" + joinForeignKeyValues(fk.RefColumns)
  338. set, ok := v.keySets[key]
  339. if !ok {
  340. set = make(map[string]bool, len(parentRows))
  341. for _, row := range parentRows {
  342. values, ok := foreignKeyValues(row, fk.RefColumns)
  343. if !ok {
  344. continue
  345. }
  346. set[joinForeignKeyValues(values)] = true
  347. }
  348. v.keySets[key] = set
  349. }
  350. for _, row := range rows {
  351. values, ok := foreignKeyValues(row, fk.Columns)
  352. if !ok {
  353. continue
  354. }
  355. if !set[joinForeignKeyValues(values)] {
  356. return &ForeignKeyViolationError{Table: schema.Name, Constraint: fk.Label()}
  357. }
  358. }
  359. }
  360. }
  361. return nil
  362. }
  363. func joinForeignKeyValues(values []string) string {
  364. var b strings.Builder
  365. for _, value := range values {
  366. b.WriteString(strconv.Itoa(len(value)))
  367. b.WriteByte(':')
  368. b.WriteString(value)
  369. }
  370. return b.String()
  371. }
  372. // foreignKeyClosure returns the tables a commit must re-validate, or nil when no
  373. // staged table participates in a foreign key.
  374. func (s *Session) foreignKeyClosure() (map[string]bool, error) {
  375. if len(s.overlay) == 0 {
  376. return nil, nil
  377. }
  378. tables := make([]string, 0, len(s.overlay))
  379. for table := range s.overlay {
  380. tables = append(tables, table)
  381. }
  382. return s.schema.ForeignKeyClosure(tables)
  383. }
  384. // validateForeignKeyFinalState checks the committed row image of every table in
  385. // the constrained component. The caller holds the session lock and the table
  386. // gates of those tables.
  387. func (s *Session) validateForeignKeyFinalState(closure map[string]bool) error {
  388. if len(closure) == 0 {
  389. return nil
  390. }
  391. schemas := make(map[string]*Schema, len(closure))
  392. tables := make(map[string][]Row, len(closure))
  393. for table := range closure {
  394. schema, err := s.schema.GetSchema(table)
  395. if err != nil {
  396. return err
  397. }
  398. rows, err := s.finalRows(table, schema)
  399. if err != nil {
  400. return err
  401. }
  402. schemas[table] = schema
  403. tables[table] = rows
  404. }
  405. validator := foreignKeyStateValidator{tables: tables, schemas: schemas}
  406. return validator.validateForeignKeyState()
  407. }
  408. // finalRows returns the table's durable rows with the transaction overlay
  409. // applied: deleted keys are removed, staged rows are added, and a key that was
  410. // both deleted and re-staged appears once with its staged image. It takes no
  411. // table gate because the caller holds the table's exclusive gate.
  412. func (s *Session) finalRows(table string, schema *Schema) ([]Row, error) {
  413. overlay := s.overlay[strings.ToLower(table)]
  414. rows := make([]Row, 0)
  415. err := s.table.scanRows(table, func(row Row) (bool, error) {
  416. pk, err := schema.PrimaryKeyForRow(row)
  417. if err != nil {
  418. return true, err
  419. }
  420. if _, staged := overlay[s.table.dataKey(table, pk)]; staged {
  421. return false, nil
  422. }
  423. rows = append(rows, row)
  424. return false, nil
  425. })
  426. if err != nil {
  427. return nil, err
  428. }
  429. for _, entry := range overlay {
  430. if entry.absent {
  431. continue
  432. }
  433. rows = append(rows, cloneRow(entry.row))
  434. }
  435. return rows, nil
  436. }