foreign_key_cascade.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. package storage
  2. import (
  3. "fmt"
  4. "strings"
  5. "time"
  6. )
  7. // ReferentialAction is one persisted FOREIGN KEY ON DELETE/ON UPDATE action.
  8. // The catalog stores the SQL spelling so sqlite_master can reproduce the
  9. // declaration; an omitted clause and NO ACTION behave identically.
  10. type ReferentialAction string
  11. const (
  12. ReferentialActionNoAction ReferentialAction = "NO ACTION"
  13. ReferentialActionRestrict ReferentialAction = "RESTRICT"
  14. ReferentialActionCascade ReferentialAction = "CASCADE"
  15. ReferentialActionSetNull ReferentialAction = "SET NULL"
  16. ReferentialActionSetDefault ReferentialAction = "SET DEFAULT"
  17. )
  18. // DeleteAction returns the action applied when the parent row is deleted.
  19. func (fk ForeignKeyConstraint) DeleteAction() ReferentialAction {
  20. return parseReferentialAction(fk.OnDelete)
  21. }
  22. // UpdateAction returns the action applied when a referenced parent key changes.
  23. func (fk ForeignKeyConstraint) UpdateAction() ReferentialAction {
  24. return parseReferentialAction(fk.OnUpdate)
  25. }
  26. // parseReferentialAction maps the persisted spelling to its action. Anything
  27. // unrecognized is NO ACTION, the default the catalog uses when the clause was
  28. // omitted, so a hand-edited catalog cannot silently disable enforcement.
  29. func parseReferentialAction(text string) ReferentialAction {
  30. switch strings.ToUpper(strings.TrimSpace(text)) {
  31. case "CASCADE":
  32. return ReferentialActionCascade
  33. case "SET NULL":
  34. return ReferentialActionSetNull
  35. case "SET DEFAULT":
  36. return ReferentialActionSetDefault
  37. case "RESTRICT":
  38. return ReferentialActionRestrict
  39. default:
  40. return ReferentialActionNoAction
  41. }
  42. }
  43. // rewritesReferencingRows reports whether the action changes or removes the
  44. // child rows instead of rejecting the parent change.
  45. func (a ReferentialAction) rewritesReferencingRows() bool {
  46. switch a {
  47. case ReferentialActionCascade, ReferentialActionSetNull, ReferentialActionSetDefault:
  48. return true
  49. default:
  50. return false
  51. }
  52. }
  53. // HasCascadingReference reports whether table is the referenced parent of a
  54. // constraint whose DELETE action (forDelete) or UPDATE action rewrites the
  55. // referencing rows. Callers use it to choose the transactional write path, so a
  56. // statement whose only actions are NO ACTION/RESTRICT keeps the optimized
  57. // point-write path.
  58. func (m *SchemaManager) HasCascadingReference(table string, forDelete bool) (bool, error) {
  59. refs, err := m.referencingForeignKeys(table)
  60. if err != nil {
  61. return false, err
  62. }
  63. for _, ref := range refs {
  64. action := ref.FK.UpdateAction()
  65. if forDelete {
  66. action = ref.FK.DeleteAction()
  67. }
  68. if action.rewritesReferencingRows() {
  69. return true, nil
  70. }
  71. }
  72. return false, nil
  73. }
  74. // foreignKeyRawValues returns the unformatted values a row holds for the given
  75. // columns, and reports false when a column is missing. A NULL component is
  76. // returned as nil: on a parent-side update the cascading action copies the new
  77. // value, including a NULL that clears the reference.
  78. func foreignKeyRawValues(row Row, columns []string) ([]interface{}, bool) {
  79. values := make([]interface{}, len(columns))
  80. for i, name := range columns {
  81. value, ok := rowColumnValue(row, name)
  82. if !ok {
  83. return nil, false
  84. }
  85. values[i] = value
  86. }
  87. return values, true
  88. }
  89. // foreignKeyActionKey formats a row's values for the cascade event key, keeping
  90. // a NULL component distinguishable so a key that becomes NULL still changes the
  91. // event identity.
  92. func foreignKeyActionKey(row Row, columns []string) ([]string, bool) {
  93. values := make([]string, len(columns))
  94. for i, name := range columns {
  95. value, ok := rowColumnValue(row, name)
  96. if !ok {
  97. return nil, false
  98. }
  99. if value == nil {
  100. values[i] = "\x00NULL"
  101. continue
  102. }
  103. values[i] = formatIndexValue(value)
  104. }
  105. return values, true
  106. }
  107. // cascadeExpansion tracks the referential actions one statement already applied.
  108. // It is the termination guard for self-references and constraint cycles: a row
  109. // delete is applied at most once, and one (row, constraint, old key, new key)
  110. // child update at most once. Because the applied event includes the key values
  111. // involved, a cycle that revisits a row with a different key still terminates:
  112. // the set of values a cascade can copy is bounded by the rows it starts from.
  113. type cascadeExpansion struct {
  114. applied map[string]bool
  115. // now is the statement instant used to evaluate a SET DEFAULT expression
  116. // default, so every cascaded row of one statement sees the same value.
  117. now time.Time
  118. }
  119. func newCascadeExpansion(schema *SchemaManager) *cascadeExpansion {
  120. return &cascadeExpansion{applied: make(map[string]bool), now: schema.defaultNow()}
  121. }
  122. func (e *cascadeExpansion) markDelete(table, key string) bool {
  123. id := "D\x00" + strings.ToLower(table) + "\x00" + key
  124. if e.applied[id] {
  125. return false
  126. }
  127. e.applied[id] = true
  128. return true
  129. }
  130. func (e *cascadeExpansion) markUpdate(table, key string, fk ForeignKeyConstraint, oldKey, newKey []string) bool {
  131. id := "U\x00" + strings.ToLower(table) + "\x00" + key + "\x00" + strings.ToLower(fk.RefTable) + "\x00" + joinForeignKeyValues(fk.Columns) + "\x00" + joinForeignKeyValues(oldKey) + "\x00" + joinForeignKeyValues(newKey)
  132. if e.applied[id] {
  133. return false
  134. }
  135. e.applied[id] = true
  136. return true
  137. }
  138. // referencingRows returns the effective child rows (durable rows with the
  139. // transaction overlay applied) that reference key through ref. The caller holds
  140. // the session lock, and selectLocked registers the read so a concurrent write on
  141. // the child side invalidates the commit instead of interleaving.
  142. func (s *Session) referencingRows(ref referencingForeignKey, key []string) ([]Row, error) {
  143. return s.selectLocked(ref.Table, func(row Row) bool {
  144. return foreignKeyMatches(row, ref.FK.Columns, key)
  145. })
  146. }
  147. // stageCascadeDelete stages a row delete plus the ON DELETE actions of every
  148. // constraint that names its table as the parent. The row is staged before the
  149. // child scan, so a self-referential row is not visited as its own child.
  150. func (s *Session) stageCascadeDelete(table, key string, row Row, exp *cascadeExpansion) error {
  151. if !exp.markDelete(table, key) {
  152. return nil
  153. }
  154. if entry, ok := s.overlay[strings.ToLower(table)][key]; ok && entry.absent {
  155. return nil
  156. }
  157. s.stageDelete(table, key, row)
  158. return s.cascadeAfterDelete(table, row, exp)
  159. }
  160. // cascadeAfterDelete applies the ON DELETE actions triggered by removing row
  161. // from table.
  162. func (s *Session) cascadeAfterDelete(table string, row Row, exp *cascadeExpansion) error {
  163. refs, err := s.schema.referencingForeignKeys(table)
  164. if err != nil {
  165. return err
  166. }
  167. for _, ref := range refs {
  168. action := ref.FK.DeleteAction()
  169. if !action.rewritesReferencingRows() {
  170. continue
  171. }
  172. key, ok := foreignKeyValues(row, ref.FK.RefColumns)
  173. if !ok {
  174. continue
  175. }
  176. children, err := s.referencingRows(ref, key)
  177. if err != nil {
  178. return err
  179. }
  180. for _, child := range children {
  181. if err := s.applyDeleteAction(ref, child, action, exp); err != nil {
  182. return err
  183. }
  184. }
  185. }
  186. return nil
  187. }
  188. // applyDeleteAction performs one row's ON DELETE action. CASCADE removes the
  189. // child and recurses through its own constraints; SET NULL/SET DEFAULT rewrite
  190. // the child's foreign-key columns and then propagate as an update, because those
  191. // columns can themselves be a key another table references.
  192. func (s *Session) applyDeleteAction(ref referencingForeignKey, child Row, action ReferentialAction, exp *cascadeExpansion) error {
  193. schema, err := s.schema.GetSchema(ref.Table)
  194. if err != nil {
  195. return err
  196. }
  197. pk, err := schema.PrimaryKeyForRow(child)
  198. if err != nil {
  199. return err
  200. }
  201. key := s.table.dataKey(ref.Table, pk)
  202. if action == ReferentialActionCascade {
  203. return s.stageCascadeDelete(ref.Table, key, child, exp)
  204. }
  205. updated, err := s.applySetAction(schema, ref.FK, child, action, exp)
  206. if err != nil {
  207. return err
  208. }
  209. return s.stageCascadeUpdate(ref.Table, child, updated, exp)
  210. }
  211. // applySetAction returns the child row with its foreign-key columns replaced by
  212. // NULL or by the column defaults. A column without a default receives NULL, the
  213. // SQL default, which then fails NOT NULL if the column is required.
  214. func (s *Session) applySetAction(schema *Schema, fk ForeignKeyConstraint, child Row, action ReferentialAction, exp *cascadeExpansion) (Row, error) {
  215. updated := cloneRow(child)
  216. for _, name := range fk.Columns {
  217. if action == ReferentialActionSetNull {
  218. updated[name] = nil
  219. continue
  220. }
  221. column, ok := schema.GetColumn(name)
  222. if !ok {
  223. return nil, fmt.Errorf("foreign key %s: column %s is missing from table %s", fk.Label(), name, schema.Name)
  224. }
  225. value, hasDefault, err := s.schema.columnDefaultValue(*column, exp.now)
  226. if err != nil {
  227. return nil, err
  228. }
  229. if !hasDefault {
  230. value = nil
  231. }
  232. updated[name] = value
  233. }
  234. return updated, nil
  235. }
  236. // cascadeAfterUpdate applies the ON UPDATE actions triggered by changing a
  237. // parent row whose referenced key moved from oldRow to newRow.
  238. func (s *Session) cascadeAfterUpdate(table string, oldRow, newRow Row, exp *cascadeExpansion) error {
  239. refs, err := s.schema.referencingForeignKeys(table)
  240. if err != nil {
  241. return err
  242. }
  243. for _, ref := range refs {
  244. action := ref.FK.UpdateAction()
  245. if !action.rewritesReferencingRows() {
  246. continue
  247. }
  248. oldKey, ok := foreignKeyValues(oldRow, ref.FK.RefColumns)
  249. if !ok {
  250. continue
  251. }
  252. newKey, newOK := foreignKeyActionKey(newRow, ref.FK.RefColumns)
  253. if newOK && foreignKeyValuesEqual(oldKey, newKey) {
  254. continue
  255. }
  256. parentValues, ok := foreignKeyRawValues(newRow, ref.FK.RefColumns)
  257. if !ok {
  258. continue
  259. }
  260. children, err := s.referencingRows(ref, oldKey)
  261. if err != nil {
  262. return err
  263. }
  264. childSchema, err := s.schema.GetSchema(ref.Table)
  265. if err != nil {
  266. return err
  267. }
  268. for _, child := range children {
  269. childPK, err := childSchema.PrimaryKeyForRow(child)
  270. if err != nil {
  271. return err
  272. }
  273. childKey := s.table.dataKey(ref.Table, childPK)
  274. if !exp.markUpdate(ref.Table, childKey, ref.FK, oldKey, newKey) {
  275. continue
  276. }
  277. var updated Row
  278. switch action {
  279. case ReferentialActionCascade:
  280. updated = cloneRow(child)
  281. for i, column := range ref.FK.Columns {
  282. updated[column] = parentValues[i]
  283. }
  284. case ReferentialActionSetNull, ReferentialActionSetDefault:
  285. updated, err = s.applySetAction(childSchema, ref.FK, child, action, exp)
  286. if err != nil {
  287. return err
  288. }
  289. }
  290. if err := s.stageCascadeUpdate(ref.Table, child, updated, exp); err != nil {
  291. return err
  292. }
  293. }
  294. }
  295. return nil
  296. }
  297. // stageCascadeUpdate stages one cascaded row rewrite. The row keeps its key
  298. // unless a cascaded foreign-key column is part of it, in which case the staged
  299. // image moves and a collision with another row fails like any primary-key move.
  300. // Required columns and CHECK predicates are validated immediately so a SET NULL
  301. // on a NOT NULL column fails at the statement with 23502 instead of surfacing at
  302. // commit; FK and UNIQUE validity are validated against the transaction's final
  303. // state at commit.
  304. func (s *Session) stageCascadeUpdate(table string, oldRow, newRow Row, exp *cascadeExpansion) error {
  305. schema, err := s.schema.GetSchema(table)
  306. if err != nil {
  307. return err
  308. }
  309. if err := s.schema.validateRowConstraints(schema, newRow); err != nil {
  310. return err
  311. }
  312. oldPK, err := schema.PrimaryKeyForRow(oldRow)
  313. if err != nil {
  314. return err
  315. }
  316. newPK, err := schema.PrimaryKeyForRow(newRow)
  317. if err != nil {
  318. return err
  319. }
  320. oldKey := s.table.dataKey(table, oldPK)
  321. newKey := s.table.dataKey(table, newPK)
  322. if oldKey != newKey {
  323. if _, gerr := s.getByPKLocked(table, newPK); gerr == nil {
  324. return &PrimaryKeyViolationError{Table: table, Key: newPK}
  325. } else if gerr != ErrKeyNotFound {
  326. return gerr
  327. }
  328. s.stageDelete(table, oldKey, oldRow)
  329. }
  330. s.stagePut(table, newKey, newRow)
  331. return s.cascadeAfterUpdate(table, oldRow, newRow, exp)
  332. }
  333. // rollbackToLocked discards mutations after a savepoint position. The caller
  334. // holds the session lock.
  335. func (s *Session) rollbackToLocked(pos int) {
  336. for i := len(s.log) - 1; i >= pos && i >= 0; i-- {
  337. m := s.log[i]
  338. if m.prev == nil {
  339. delete(s.overlay[m.table], m.key)
  340. if len(s.overlay[m.table]) == 0 {
  341. delete(s.overlay, m.table)
  342. }
  343. } else {
  344. if s.overlay[m.table] == nil {
  345. s.overlay[m.table] = make(map[string]*overlayEntry)
  346. }
  347. s.overlay[m.table][m.key] = m.prev
  348. }
  349. }
  350. if pos < len(s.log) {
  351. s.log = s.log[:pos]
  352. }
  353. }