foreign_key.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543
  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. // foreignKeyReferenceName formats a constraint for a DDL error message. The
  249. // child table is included because an unnamed constraint's label is only unique
  250. // within its table.
  251. func foreignKeyReferenceName(child string, fk ForeignKeyConstraint) string {
  252. return fmt.Sprintf("%s on %s", fk.Label(), child)
  253. }
  254. // ForeignKeyColumnDependency returns the constraints that keep a column in
  255. // place: a FOREIGN KEY this table declares on it, or a FOREIGN KEY (from this
  256. // table or another one) that references it as a parent column. DROP/RENAME
  257. // COLUMN cannot rewrite persisted constraint text, so a non-empty result must
  258. // reject the change instead of silently disabling the constraint.
  259. func (m *SchemaManager) ForeignKeyColumnDependency(table, column string) ([]string, error) {
  260. catalog, err := m.foreignKeyCatalogSnapshot()
  261. if err != nil {
  262. return nil, err
  263. }
  264. schema, err := m.GetSchema(table)
  265. if err != nil {
  266. return nil, err
  267. }
  268. seen := make(map[string]bool)
  269. var labels []string
  270. add := func(child string, fk ForeignKeyConstraint) {
  271. label := foreignKeyReferenceName(child, fk)
  272. if seen[label] {
  273. return
  274. }
  275. seen[label] = true
  276. labels = append(labels, label)
  277. }
  278. for _, fk := range schema.ForeignKeys {
  279. for _, name := range fk.Columns {
  280. if strings.EqualFold(name, column) {
  281. add(schema.Name, fk)
  282. break
  283. }
  284. }
  285. }
  286. for _, ref := range catalog.referencing[strings.ToLower(table)] {
  287. for _, name := range ref.FK.RefColumns {
  288. if strings.EqualFold(name, column) {
  289. add(ref.Table, ref.FK)
  290. break
  291. }
  292. }
  293. }
  294. sort.Strings(labels)
  295. return labels, nil
  296. }
  297. // ForeignKeyTableDependency returns the constraints that name table as their
  298. // referenced parent, including a self-reference. RENAME TABLE cannot rewrite
  299. // them, and DROP TABLE would leave them dangling.
  300. func (m *SchemaManager) ForeignKeyTableDependency(table string) ([]string, error) {
  301. catalog, err := m.foreignKeyCatalogSnapshot()
  302. if err != nil {
  303. return nil, err
  304. }
  305. seen := make(map[string]bool)
  306. var labels []string
  307. for _, ref := range catalog.referencing[strings.ToLower(table)] {
  308. label := foreignKeyReferenceName(ref.Table, ref.FK)
  309. if seen[label] {
  310. continue
  311. }
  312. seen[label] = true
  313. labels = append(labels, label)
  314. }
  315. sort.Strings(labels)
  316. return labels, nil
  317. }
  318. // ForeignKeyParticipants reports whether a write to table can affect referential
  319. // integrity. A table participates when it declares a FOREIGN KEY or when another
  320. // table references it.
  321. func (m *SchemaManager) ForeignKeyParticipants(table string) (bool, error) {
  322. catalog, err := m.foreignKeyCatalogSnapshot()
  323. if err != nil {
  324. return false, err
  325. }
  326. return catalog.participants[strings.ToLower(table)], nil
  327. }
  328. // ForeignKeyClosure returns the tables whose rows must be re-validated when the
  329. // given tables change: the foreign-key-connected component of every changed
  330. // table that participates in a constraint. The result is empty when no changed
  331. // table participates.
  332. func (m *SchemaManager) ForeignKeyClosure(tables []string) (map[string]bool, error) {
  333. catalog, err := m.foreignKeyCatalogSnapshot()
  334. if err != nil {
  335. return nil, err
  336. }
  337. closure := make(map[string]bool)
  338. queue := make([]string, 0, len(tables))
  339. for _, table := range tables {
  340. if catalog.participants[strings.ToLower(table)] {
  341. queue = append(queue, strings.ToLower(table))
  342. }
  343. }
  344. for len(queue) > 0 {
  345. table := queue[0]
  346. queue = queue[1:]
  347. if closure[table] {
  348. continue
  349. }
  350. closure[table] = true
  351. neighbors := make([]string, 0, len(catalog.adjacency[table]))
  352. for neighbor := range catalog.adjacency[table] {
  353. neighbors = append(neighbors, neighbor)
  354. }
  355. sort.Strings(neighbors)
  356. for _, neighbor := range neighbors {
  357. if !closure[neighbor] {
  358. queue = append(queue, neighbor)
  359. }
  360. }
  361. }
  362. return closure, nil
  363. }
  364. // lockForeignKeyWrites serializes writes that can affect referential integrity,
  365. // so a validation scan cannot interleave with a concurrent insert, update, or
  366. // delete on the other side of a constraint. It must be acquired before any
  367. // table gate: commits validate several tables at once, and the canonical order
  368. // (foreign-key gate, then table gates) is what keeps that deadlock-free.
  369. func (m *TableManager) lockForeignKeyWrites(tables ...string) (func(), error) {
  370. for _, table := range tables {
  371. participates, err := m.schema.ForeignKeyParticipants(table)
  372. if err != nil {
  373. return nil, err
  374. }
  375. if participates {
  376. m.foreignKeyMu.Lock()
  377. return m.foreignKeyMu.Unlock, nil
  378. }
  379. }
  380. return func() {}, nil
  381. }
  382. // LockForeignKeyWriteGate takes the foreign-key write gate unconditionally.
  383. // Schema DDL uses it around CREATE TABLE and DROP TABLE: the table that gains or
  384. // loses a constraint is not part of the foreign-key graph yet (or has just been
  385. // removed from it), so the participant check would skip the gate exactly when
  386. // the graph is changing. Callers must take it before any table gate.
  387. func (m *TableManager) LockForeignKeyWriteGate() func() {
  388. m.foreignKeyMu.Lock()
  389. return m.foreignKeyMu.Unlock
  390. }
  391. // foreignKeyStateValidator validates the final row state of a set of tables
  392. // against every FOREIGN KEY declaration among them.
  393. type foreignKeyStateValidator struct {
  394. tables map[string][]Row
  395. keySets map[string]map[string]bool
  396. schemas map[string]*Schema
  397. }
  398. // validateForeignKeyState checks every child row of the given tables against its
  399. // parent rows. tables maps a lowercased table name to its final row set (durable
  400. // rows with staged deletes applied plus staged writes).
  401. func (v *foreignKeyStateValidator) validateForeignKeyState() error {
  402. if v.keySets == nil {
  403. v.keySets = make(map[string]map[string]bool)
  404. }
  405. for table, rows := range v.tables {
  406. schema := v.schemas[table]
  407. if schema == nil {
  408. continue
  409. }
  410. for _, fk := range schema.ForeignKeys {
  411. parent := strings.ToLower(fk.RefTable)
  412. parentRows, ok := v.tables[parent]
  413. if !ok {
  414. return fmt.Errorf("foreign key %s: parent table %s is not available for validation", fk.Label(), fk.RefTable)
  415. }
  416. key := parent + "\x00" + joinForeignKeyValues(fk.RefColumns)
  417. set, ok := v.keySets[key]
  418. if !ok {
  419. set = make(map[string]bool, len(parentRows))
  420. for _, row := range parentRows {
  421. values, ok := foreignKeyValues(row, fk.RefColumns)
  422. if !ok {
  423. continue
  424. }
  425. set[joinForeignKeyValues(values)] = true
  426. }
  427. v.keySets[key] = set
  428. }
  429. for _, row := range rows {
  430. values, ok := foreignKeyValues(row, fk.Columns)
  431. if !ok {
  432. continue
  433. }
  434. if !set[joinForeignKeyValues(values)] {
  435. return &ForeignKeyViolationError{Table: schema.Name, Constraint: fk.Label()}
  436. }
  437. }
  438. }
  439. }
  440. return nil
  441. }
  442. func joinForeignKeyValues(values []string) string {
  443. var b strings.Builder
  444. for _, value := range values {
  445. b.WriteString(strconv.Itoa(len(value)))
  446. b.WriteByte(':')
  447. b.WriteString(value)
  448. }
  449. return b.String()
  450. }
  451. // foreignKeyClosure returns the tables a commit must re-validate, or nil when no
  452. // staged table participates in a foreign key.
  453. func (s *Session) foreignKeyClosure() (map[string]bool, error) {
  454. if len(s.overlay) == 0 {
  455. return nil, nil
  456. }
  457. tables := make([]string, 0, len(s.overlay))
  458. for table := range s.overlay {
  459. tables = append(tables, table)
  460. }
  461. return s.schema.ForeignKeyClosure(tables)
  462. }
  463. // validateForeignKeyFinalState checks the committed row image of every table in
  464. // the constrained component. The caller holds the session lock and the table
  465. // gates of those tables.
  466. func (s *Session) validateForeignKeyFinalState(closure map[string]bool) error {
  467. if len(closure) == 0 {
  468. return nil
  469. }
  470. schemas := make(map[string]*Schema, len(closure))
  471. tables := make(map[string][]Row, len(closure))
  472. for table := range closure {
  473. schema, err := s.schema.GetSchema(table)
  474. if err != nil {
  475. return err
  476. }
  477. rows, err := s.finalRows(table, schema)
  478. if err != nil {
  479. return err
  480. }
  481. schemas[table] = schema
  482. tables[table] = rows
  483. }
  484. validator := foreignKeyStateValidator{tables: tables, schemas: schemas}
  485. return validator.validateForeignKeyState()
  486. }
  487. // finalRows returns the table's durable rows with the transaction overlay
  488. // applied: deleted keys are removed, staged rows are added, and a key that was
  489. // both deleted and re-staged appears once with its staged image. It takes no
  490. // table gate because the caller holds the table's exclusive gate.
  491. func (s *Session) finalRows(table string, schema *Schema) ([]Row, error) {
  492. overlay := s.overlay[strings.ToLower(table)]
  493. rows := make([]Row, 0)
  494. err := s.table.scanRows(table, func(row Row) (bool, error) {
  495. pk, err := schema.PrimaryKeyForRow(row)
  496. if err != nil {
  497. return true, err
  498. }
  499. if _, staged := overlay[s.table.dataKey(table, pk)]; staged {
  500. return false, nil
  501. }
  502. rows = append(rows, row)
  503. return false, nil
  504. })
  505. if err != nil {
  506. return nil, err
  507. }
  508. for _, entry := range overlay {
  509. if entry.absent {
  510. continue
  511. }
  512. rows = append(rows, cloneRow(entry.row))
  513. }
  514. return rows, nil
  515. }