foreign_key.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610
  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, err := buildForeignKeyCatalog(version, tables, m.GetSchema)
  206. if err != nil {
  207. return nil, err
  208. }
  209. m.fkCatalogMu.Lock()
  210. if m.Version() == version {
  211. m.fkCatalog = catalog
  212. }
  213. m.fkCatalogMu.Unlock()
  214. return catalog, nil
  215. }
  216. // buildForeignKeyCatalog derives the foreign-key metadata of a catalog view from
  217. // its table list and definitions. Both the shared SchemaManager cache and a
  218. // transaction's staged catalog build it, so a session validates against the
  219. // constraints it can see.
  220. func buildForeignKeyCatalog(version uint64, tables []string, getSchema func(string) (*Schema, error)) (*foreignKeyCatalog, error) {
  221. catalog := &foreignKeyCatalog{
  222. version: version,
  223. participants: make(map[string]bool),
  224. referencing: make(map[string][]referencingForeignKey),
  225. adjacency: make(map[string]map[string]bool),
  226. }
  227. link := func(a, b string) {
  228. if catalog.adjacency[a] == nil {
  229. catalog.adjacency[a] = make(map[string]bool)
  230. }
  231. catalog.adjacency[a][b] = true
  232. }
  233. for _, name := range tables {
  234. schema, err := getSchema(name)
  235. if err != nil {
  236. return nil, err
  237. }
  238. child := strings.ToLower(schema.Name)
  239. for _, fk := range schema.ForeignKeys {
  240. parent := strings.ToLower(fk.RefTable)
  241. catalog.participants[child] = true
  242. catalog.participants[parent] = true
  243. catalog.referencing[parent] = append(catalog.referencing[parent], referencingForeignKey{Table: schema.Name, FK: fk})
  244. link(child, parent)
  245. link(parent, child)
  246. }
  247. }
  248. return catalog, nil
  249. }
  250. // closure returns the foreign-key-connected component of the given tables.
  251. func (c *foreignKeyCatalog) closure(tables []string) map[string]bool {
  252. closure := make(map[string]bool)
  253. queue := make([]string, 0, len(tables))
  254. for _, table := range tables {
  255. if c.participants[strings.ToLower(table)] {
  256. queue = append(queue, strings.ToLower(table))
  257. }
  258. }
  259. for len(queue) > 0 {
  260. table := queue[0]
  261. queue = queue[1:]
  262. if closure[table] {
  263. continue
  264. }
  265. closure[table] = true
  266. neighbors := make([]string, 0, len(c.adjacency[table]))
  267. for neighbor := range c.adjacency[table] {
  268. neighbors = append(neighbors, neighbor)
  269. }
  270. sort.Strings(neighbors)
  271. for _, neighbor := range neighbors {
  272. if !closure[neighbor] {
  273. queue = append(queue, neighbor)
  274. }
  275. }
  276. }
  277. return closure
  278. }
  279. // referencingForeignKeys returns every FOREIGN KEY declaration whose parent is
  280. // table.
  281. func (m *SchemaManager) referencingForeignKeys(table string) ([]referencingForeignKey, error) {
  282. catalog, err := m.foreignKeyCatalogSnapshot()
  283. if err != nil {
  284. return nil, err
  285. }
  286. return catalog.referencing[strings.ToLower(table)], nil
  287. }
  288. // foreignKeyReferenceName formats a constraint for a DDL error message. The
  289. // child table is included because an unnamed constraint's label is only unique
  290. // within its table.
  291. func foreignKeyReferenceName(child string, fk ForeignKeyConstraint) string {
  292. return fmt.Sprintf("%s on %s", fk.Label(), child)
  293. }
  294. // ForeignKeyColumnDependency returns the constraints that keep a column in
  295. // place: a FOREIGN KEY this table declares on it, or a FOREIGN KEY (from this
  296. // table or another one) that references it as a parent column. DROP/RENAME
  297. // COLUMN cannot rewrite persisted constraint text, so a non-empty result must
  298. // reject the change instead of silently disabling the constraint.
  299. func (m *SchemaManager) ForeignKeyColumnDependency(table, column string) ([]string, error) {
  300. catalog, err := m.foreignKeyCatalogSnapshot()
  301. if err != nil {
  302. return nil, err
  303. }
  304. schema, err := m.GetSchema(table)
  305. if err != nil {
  306. return nil, err
  307. }
  308. seen := make(map[string]bool)
  309. var labels []string
  310. add := func(child string, fk ForeignKeyConstraint) {
  311. label := foreignKeyReferenceName(child, fk)
  312. if seen[label] {
  313. return
  314. }
  315. seen[label] = true
  316. labels = append(labels, label)
  317. }
  318. for _, fk := range schema.ForeignKeys {
  319. for _, name := range fk.Columns {
  320. if strings.EqualFold(name, column) {
  321. add(schema.Name, fk)
  322. break
  323. }
  324. }
  325. }
  326. for _, ref := range catalog.referencing[strings.ToLower(table)] {
  327. for _, name := range ref.FK.RefColumns {
  328. if strings.EqualFold(name, column) {
  329. add(ref.Table, ref.FK)
  330. break
  331. }
  332. }
  333. }
  334. sort.Strings(labels)
  335. return labels, nil
  336. }
  337. // ForeignKeyTableDependency returns the constraints that name table as their
  338. // referenced parent, including a self-reference. RENAME TABLE cannot rewrite
  339. // them, and DROP TABLE would leave them dangling.
  340. func (m *SchemaManager) ForeignKeyTableDependency(table string) ([]string, error) {
  341. catalog, err := m.foreignKeyCatalogSnapshot()
  342. if err != nil {
  343. return nil, err
  344. }
  345. seen := make(map[string]bool)
  346. var labels []string
  347. for _, ref := range catalog.referencing[strings.ToLower(table)] {
  348. label := foreignKeyReferenceName(ref.Table, ref.FK)
  349. if seen[label] {
  350. continue
  351. }
  352. seen[label] = true
  353. labels = append(labels, label)
  354. }
  355. sort.Strings(labels)
  356. return labels, nil
  357. }
  358. // ForeignKeyParticipants reports whether a write to table can affect referential
  359. // integrity. A table participates when it declares a FOREIGN KEY or when another
  360. // table references it.
  361. func (m *SchemaManager) ForeignKeyParticipants(table string) (bool, error) {
  362. catalog, err := m.foreignKeyCatalogSnapshot()
  363. if err != nil {
  364. return false, err
  365. }
  366. return catalog.participants[strings.ToLower(table)], nil
  367. }
  368. // ForeignKeyClosure returns the tables whose rows must be re-validated when the
  369. // given tables change: the foreign-key-connected component of every changed
  370. // table that participates in a constraint. The result is empty when no changed
  371. // table participates.
  372. func (m *SchemaManager) ForeignKeyClosure(tables []string) (map[string]bool, error) {
  373. catalog, err := m.foreignKeyCatalogSnapshot()
  374. if err != nil {
  375. return nil, err
  376. }
  377. return catalog.closure(tables), nil
  378. }
  379. // lockForeignKeyWrites serializes writes that can affect referential integrity,
  380. // so a validation scan cannot interleave with a concurrent insert, update, or
  381. // delete on the other side of a constraint. It must be acquired before any
  382. // table gate: commits validate several tables at once, and the canonical order
  383. // (foreign-key gate, then table gates) is what keeps that deadlock-free.
  384. func (m *TableManager) lockForeignKeyWrites(tables ...string) (func(), error) {
  385. for _, table := range tables {
  386. participates, err := m.schema.ForeignKeyParticipants(table)
  387. if err != nil {
  388. return nil, err
  389. }
  390. if participates {
  391. m.foreignKeyMu.Lock()
  392. return m.foreignKeyMu.Unlock, nil
  393. }
  394. }
  395. return func() {}, nil
  396. }
  397. // LockForeignKeyWriteGate takes the foreign-key write gate unconditionally.
  398. // Schema DDL uses it around CREATE TABLE and DROP TABLE: the table that gains or
  399. // loses a constraint is not part of the foreign-key graph yet (or has just been
  400. // removed from it), so the participant check would skip the gate exactly when
  401. // the graph is changing. Callers must take it before any table gate.
  402. func (m *TableManager) LockForeignKeyWriteGate() func() {
  403. m.foreignKeyMu.Lock()
  404. return m.foreignKeyMu.Unlock
  405. }
  406. // foreignKeyStateValidator validates the final row state of a set of tables
  407. // against every FOREIGN KEY declaration among them.
  408. type foreignKeyStateValidator struct {
  409. tables map[string][]Row
  410. keySets map[string]map[string]bool
  411. schemas map[string]*Schema
  412. }
  413. // validateForeignKeyState checks every child row of the given tables against its
  414. // parent rows. tables maps a lowercased table name to its final row set (durable
  415. // rows with staged deletes applied plus staged writes).
  416. func (v *foreignKeyStateValidator) validateForeignKeyState() error {
  417. if v.keySets == nil {
  418. v.keySets = make(map[string]map[string]bool)
  419. }
  420. for table, rows := range v.tables {
  421. schema := v.schemas[table]
  422. if schema == nil {
  423. continue
  424. }
  425. for _, fk := range schema.ForeignKeys {
  426. parent := strings.ToLower(fk.RefTable)
  427. parentRows, ok := v.tables[parent]
  428. if !ok {
  429. return fmt.Errorf("foreign key %s: parent table %s is not available for validation", fk.Label(), fk.RefTable)
  430. }
  431. key := parent + "\x00" + joinForeignKeyValues(fk.RefColumns)
  432. set, ok := v.keySets[key]
  433. if !ok {
  434. set = make(map[string]bool, len(parentRows))
  435. for _, row := range parentRows {
  436. values, ok := foreignKeyValues(row, fk.RefColumns)
  437. if !ok {
  438. continue
  439. }
  440. set[joinForeignKeyValues(values)] = true
  441. }
  442. v.keySets[key] = set
  443. }
  444. for _, row := range rows {
  445. values, ok := foreignKeyValues(row, fk.Columns)
  446. if !ok {
  447. continue
  448. }
  449. if !set[joinForeignKeyValues(values)] {
  450. return &ForeignKeyViolationError{Table: schema.Name, Constraint: fk.Label()}
  451. }
  452. }
  453. }
  454. }
  455. return nil
  456. }
  457. func joinForeignKeyValues(values []string) string {
  458. var b strings.Builder
  459. for _, value := range values {
  460. b.WriteString(strconv.Itoa(len(value)))
  461. b.WriteByte(':')
  462. b.WriteString(value)
  463. }
  464. return b.String()
  465. }
  466. // foreignKeyClosure returns the tables a commit must re-validate, or nil when no
  467. // staged table participates in a foreign key.
  468. func (s *Session) foreignKeyClosure() (map[string]bool, error) {
  469. tables := make([]string, 0, len(s.overlay))
  470. seen := make(map[string]bool, len(s.overlay))
  471. for table := range s.overlay {
  472. if seen[table] {
  473. continue
  474. }
  475. seen[table] = true
  476. tables = append(tables, table)
  477. }
  478. // DDL staged by this transaction can add or remove a constraint without
  479. // staging a row, so its tables join the validation component too.
  480. if s.schemaTx != nil {
  481. for table, schema := range s.schemaTx.staged {
  482. if schema == nil || seen[table] {
  483. continue
  484. }
  485. seen[table] = true
  486. tables = append(tables, table)
  487. }
  488. }
  489. if len(tables) == 0 {
  490. return nil, nil
  491. }
  492. return s.foreignKeyClosureLocked(tables)
  493. }
  494. // foreignKeyClosureLocked computes the constrained component of tables against
  495. // the catalog this session observes (caller holds the session lock).
  496. func (s *Session) foreignKeyClosureLocked(tables []string) (map[string]bool, error) {
  497. if s.schemaTx == nil {
  498. return s.schema.ForeignKeyClosure(tables)
  499. }
  500. catalog, err := s.foreignKeyCatalogLocked()
  501. if err != nil {
  502. return nil, err
  503. }
  504. return catalog.closure(tables), nil
  505. }
  506. // referencingForeignKeysLocked returns the FOREIGN KEY declarations whose parent
  507. // is table, as this session observes the catalog.
  508. func (s *Session) referencingForeignKeysLocked(table string) ([]referencingForeignKey, error) {
  509. if s.schemaTx == nil {
  510. return s.schema.referencingForeignKeys(table)
  511. }
  512. catalog, err := s.foreignKeyCatalogLocked()
  513. if err != nil {
  514. return nil, err
  515. }
  516. return catalog.referencing[strings.ToLower(table)], nil
  517. }
  518. // validateForeignKeyFinalState checks the committed row image of every table in
  519. // the constrained component. The caller holds the session lock and the table
  520. // gates of those tables.
  521. func (s *Session) validateForeignKeyFinalState(closure map[string]bool) error {
  522. if len(closure) == 0 {
  523. return nil
  524. }
  525. schemas := make(map[string]*Schema, len(closure))
  526. tables := make(map[string][]Row, len(closure))
  527. for table := range closure {
  528. schema, err := s.getSchemaLocked(table)
  529. if err != nil {
  530. return err
  531. }
  532. rows, err := s.finalRows(table, schema)
  533. if err != nil {
  534. return err
  535. }
  536. schemas[table] = schema
  537. tables[table] = rows
  538. }
  539. validator := foreignKeyStateValidator{tables: tables, schemas: schemas}
  540. return validator.validateForeignKeyState()
  541. }
  542. // finalRows returns the table's durable rows with the transaction overlay
  543. // applied: deleted keys are removed, staged rows are added, and a key that was
  544. // both deleted and re-staged appears once with its staged image. It takes no
  545. // table gate because the caller holds the table's exclusive gate.
  546. func (s *Session) finalRows(table string, schema *Schema) ([]Row, error) {
  547. overlay := s.overlay[strings.ToLower(table)]
  548. source := s.durableSourceLocked(table)
  549. rows := make([]Row, 0)
  550. err := s.table.scanRowsAtPrefix(s.table.dataPrefix(source), func(_ string, row Row, _ uint64) (bool, error) {
  551. pk, err := schema.PrimaryKeyForRow(row)
  552. if err != nil {
  553. return true, err
  554. }
  555. if _, staged := overlay[s.table.dataKey(table, pk)]; staged {
  556. return false, nil
  557. }
  558. if err := s.applyStagedRewritesLocked(table, row); err != nil {
  559. return true, err
  560. }
  561. rows = append(rows, row)
  562. return false, nil
  563. })
  564. if err != nil {
  565. return nil, err
  566. }
  567. for _, entry := range overlay {
  568. if entry.absent {
  569. continue
  570. }
  571. row := cloneRow(entry.row)
  572. if err := s.applyStagedRewritesLocked(table, row); err != nil {
  573. return nil, err
  574. }
  575. rows = append(rows, row)
  576. }
  577. return rows, nil
  578. }