schema_tx.go 47 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589
  1. package storage
  2. import (
  3. "fmt"
  4. "sort"
  5. "strings"
  6. "time"
  7. "github.com/goccy/go-json"
  8. )
  9. // rowRewrite is one pending ALTER TABLE row transformation. It reports whether
  10. // it changed the row and must be idempotent: it runs again on every read of the
  11. // table while the transaction is open and once more on each durable row when
  12. // the transaction commits.
  13. type rowRewrite func(row Row) (bool, error)
  14. // SchemaTx is the transaction-scoped catalog overlay of one Session. DDL inside
  15. // an explicit transaction records its catalog and row effects here instead of
  16. // publishing them: the owning session reads through the overlay, every other
  17. // session keeps reading the committed catalog, ROLLBACK discards it, and COMMIT
  18. // merges the overlay into the session's single compare-and-swap batch write, so
  19. // a multi-statement migration becomes durable atomically or not at all.
  20. //
  21. // The overlay starts out empty and reads through to the durable catalog while
  22. // recording the LSN each touched key was observed at. Those LSNs become the
  23. // commit's compare checks, so a concurrent DDL that committed after the
  24. // transaction derived its catalog makes the commit fail with ErrSerialization
  25. // instead of overwriting the concurrent change.
  26. //
  27. // Every method assumes the owning session lock is held: a Session executes one
  28. // statement at a time, so the overlay needs no lock of its own and can share the
  29. // session's row overlay directly.
  30. type SchemaTx struct {
  31. base *SchemaManager
  32. sess *Session
  33. // staged holds the transaction's definition of every touched table, keyed
  34. // by lowercased name. A nil entry means the name is not visible to this
  35. // transaction: it was dropped, or moved by RENAME TABLE.
  36. staged map[string]*Schema
  37. // stagedIndexes holds the transaction's definition of every touched index,
  38. // keyed by lowercased name; nil means dropped.
  39. stagedIndexes map[string]*Index
  40. // committedSchemas and committedIndexes cache the definitions read from
  41. // durable storage so the executor's repeated catalog lookups inside one
  42. // statement do not re-read them. An entry is dropped when the transaction
  43. // stages a replacement for the same name.
  44. committedSchemas map[string]*Schema
  45. committedIndexes map[string]*Index
  46. // tables and indexList are the staged catalog lists. They are loaded from
  47. // the committed catalog on first use and rewritten in place afterwards.
  48. tables []string
  49. indexList []string
  50. // catalogDirty and indexListDirty report that the corresponding list is
  51. // part of the commit.
  52. catalogDirty bool
  53. indexListDirty bool
  54. // reads records the durable LSN every staged catalog key was derived from
  55. // (0 for a key that was absent). commitChecks turns them into compare
  56. // checks.
  57. reads map[string]uint64
  58. // rewrites holds pending row transformations keyed by the lowercased table
  59. // name whose rows they apply to, in statement order.
  60. rewrites map[string][]rowRewrite
  61. // dataSources maps a staged table name to the durable table name whose data
  62. // prefix still holds its rows: RENAME TABLE moves the definition without
  63. // touching rows, so a later ALTER or DROP must look under the old prefix.
  64. // An entry with an empty source is a table created by this transaction.
  65. dataSources map[string]string
  66. // drops holds durable table names whose data prefix is deleted at commit.
  67. drops map[string]bool
  68. // renames maps a durable table name to the staged name its rows move to at
  69. // commit.
  70. renames map[string]string
  71. // rekeys records the overlay re-keyings this transaction performed, in
  72. // order, so a savepoint can undo them.
  73. rekeys []rekeyPair
  74. version uint64
  75. }
  76. type rekeyPair struct {
  77. from string
  78. to string
  79. }
  80. // schemaTxSnapshot is the transaction catalog state at a savepoint.
  81. type schemaTxSnapshot struct {
  82. version uint64
  83. staged map[string]*Schema
  84. stagedIndexes map[string]*Index
  85. tables []string
  86. indexList []string
  87. catalogDirty bool
  88. indexListDirty bool
  89. rewrites map[string][]rowRewrite
  90. dataSources map[string]string
  91. drops map[string]bool
  92. renames map[string]string
  93. rekeys int
  94. }
  95. func newSchemaTx(s *Session) *SchemaTx {
  96. return &SchemaTx{
  97. base: s.schema,
  98. sess: s,
  99. staged: make(map[string]*Schema),
  100. stagedIndexes: make(map[string]*Index),
  101. committedSchemas: make(map[string]*Schema),
  102. committedIndexes: make(map[string]*Index),
  103. reads: make(map[string]uint64),
  104. rewrites: make(map[string][]rowRewrite),
  105. dataSources: make(map[string]string),
  106. drops: make(map[string]bool),
  107. renames: make(map[string]string),
  108. version: s.schema.Version(),
  109. }
  110. }
  111. // snapshot captures the catalog overlay. The maps are copied shallowly: staged
  112. // definitions are cloned before they are mutated, so the captured pointers stay
  113. // immutable.
  114. func (tx *SchemaTx) snapshot() schemaTxSnapshot {
  115. snap := schemaTxSnapshot{
  116. version: tx.version,
  117. staged: make(map[string]*Schema, len(tx.staged)),
  118. stagedIndexes: make(map[string]*Index, len(tx.stagedIndexes)),
  119. tables: append([]string(nil), tx.tables...),
  120. indexList: append([]string(nil), tx.indexList...),
  121. catalogDirty: tx.catalogDirty,
  122. indexListDirty: tx.indexListDirty,
  123. rewrites: make(map[string][]rowRewrite, len(tx.rewrites)),
  124. dataSources: make(map[string]string, len(tx.dataSources)),
  125. drops: make(map[string]bool, len(tx.drops)),
  126. renames: make(map[string]string, len(tx.renames)),
  127. rekeys: len(tx.rekeys),
  128. }
  129. for k, v := range tx.staged {
  130. snap.staged[k] = v
  131. }
  132. for k, v := range tx.stagedIndexes {
  133. snap.stagedIndexes[k] = v
  134. }
  135. for k, v := range tx.rewrites {
  136. snap.rewrites[k] = append([]rowRewrite(nil), v...)
  137. }
  138. for k, v := range tx.dataSources {
  139. snap.dataSources[k] = v
  140. }
  141. for k, v := range tx.drops {
  142. snap.drops[k] = v
  143. }
  144. for k, v := range tx.renames {
  145. snap.renames[k] = v
  146. }
  147. return snap
  148. }
  149. // restore returns the overlay to a snapshot and undoes the overlay re-keyings
  150. // performed after it. The caller holds the session lock.
  151. func (tx *SchemaTx) restore(snap schemaTxSnapshot) {
  152. for i := len(tx.rekeys) - 1; i >= snap.rekeys; i-- {
  153. tx.sess.rekeyOverlayLocked(tx.rekeys[i].to, tx.rekeys[i].from)
  154. }
  155. tx.rekeys = tx.rekeys[:snap.rekeys]
  156. tx.version = snap.version
  157. tx.staged = snap.staged
  158. tx.stagedIndexes = snap.stagedIndexes
  159. tx.tables = snap.tables
  160. tx.indexList = snap.indexList
  161. tx.catalogDirty = snap.catalogDirty
  162. tx.indexListDirty = snap.indexListDirty
  163. tx.rewrites = snap.rewrites
  164. tx.dataSources = snap.dataSources
  165. tx.drops = snap.drops
  166. tx.renames = snap.renames
  167. }
  168. // bump advances the transaction's catalog version so the executor's analyzer
  169. // catalog resynchronizes after every staged change.
  170. func (tx *SchemaTx) bump() {
  171. tx.version++
  172. }
  173. func (tx *SchemaTx) recordRead(key string, lsn uint64) {
  174. if _, ok := tx.reads[key]; !ok {
  175. tx.reads[key] = lsn
  176. }
  177. }
  178. // refreshCommitted drops the cached committed definitions before the commit's
  179. // final validation. Another session's DDL may have committed while this
  180. // transaction was open, so the rows it stages must be validated against the
  181. // catalog that is current under the commit gates; the staged overlay itself is
  182. // kept. The catalog lists keep their recorded LSN: a staged list write must
  183. // still fail its compare check rather than overwrite the concurrent change.
  184. func (tx *SchemaTx) refreshCommitted() {
  185. tx.committedSchemas = make(map[string]*Schema)
  186. tx.committedIndexes = make(map[string]*Index)
  187. if !tx.indexListDirty {
  188. tx.indexList = nil
  189. }
  190. }
  191. // getSchema returns the transaction's view of a table.
  192. func (tx *SchemaTx) getSchema(name string) (*Schema, error) {
  193. lower := strings.ToLower(name)
  194. if schema, ok := tx.staged[lower]; ok {
  195. if schema == nil {
  196. return nil, fmt.Errorf("table not found: %s", name)
  197. }
  198. return cloneSchema(schema), nil
  199. }
  200. return tx.loadSchema(name)
  201. }
  202. // loadSchema reads the committed definition and records the LSN it observed.
  203. func (tx *SchemaTx) loadSchema(name string) (*Schema, error) {
  204. lower := strings.ToLower(name)
  205. if schema, ok := tx.committedSchemas[lower]; ok {
  206. return cloneSchema(schema), nil
  207. }
  208. key := tx.base.schemaKey(name)
  209. data, lsn, found, err := tx.base.readDurable(key)
  210. if err != nil {
  211. return nil, err
  212. }
  213. tx.recordRead(key, lsn)
  214. if !found {
  215. return nil, fmt.Errorf("table not found: %s", name)
  216. }
  217. var schema Schema
  218. if err := json.Unmarshal([]byte(data), &schema); err != nil {
  219. return nil, fmt.Errorf("failed to parse schema: %w", err)
  220. }
  221. schema.normalizePrimaryKey()
  222. tx.committedSchemas[lower] = &schema
  223. return cloneSchema(&schema), nil
  224. }
  225. func (tx *SchemaTx) tableExists(name string) bool {
  226. _, err := tx.getSchema(name)
  227. return err == nil
  228. }
  229. // listTables returns the staged table catalog in creation order.
  230. func (tx *SchemaTx) listTables() ([]string, error) {
  231. if tx.tables == nil {
  232. tables, lsn, err := tx.base.catalogTablesDurable()
  233. if err != nil {
  234. return nil, err
  235. }
  236. tx.recordRead(tx.base.catalogKey(), lsn)
  237. tx.tables = tables
  238. }
  239. return append([]string(nil), tx.tables...), nil
  240. }
  241. func (tx *SchemaTx) stageTableList(tables []string) {
  242. tx.tables = tables
  243. tx.catalogDirty = true
  244. }
  245. // getIndex returns the transaction's view of an index definition.
  246. func (tx *SchemaTx) getIndex(name string) (*Index, error) {
  247. lower := strings.ToLower(name)
  248. if index, ok := tx.stagedIndexes[lower]; ok {
  249. if index == nil {
  250. return nil, fmt.Errorf("%w: %s", ErrIndexNotFound, name)
  251. }
  252. return cloneIndex(index), nil
  253. }
  254. if index, ok := tx.committedIndexes[lower]; ok {
  255. return cloneIndex(index), nil
  256. }
  257. key := tx.base.indexKey(name)
  258. data, lsn, found, err := tx.base.readDurable(key)
  259. if err != nil {
  260. return nil, err
  261. }
  262. tx.recordRead(key, lsn)
  263. if !found {
  264. return nil, fmt.Errorf("%w: %s", ErrIndexNotFound, name)
  265. }
  266. var index Index
  267. if err := json.Unmarshal([]byte(data), &index); err != nil {
  268. return nil, fmt.Errorf("failed to parse index: %w", err)
  269. }
  270. tx.committedIndexes[lower] = &index
  271. return cloneIndex(&index), nil
  272. }
  273. func (tx *SchemaTx) indexExists(name string) bool {
  274. _, err := tx.getIndex(name)
  275. return err == nil
  276. }
  277. func (tx *SchemaTx) listIndexes() ([]string, error) {
  278. if tx.indexList == nil {
  279. indexes, lsn, err := tx.base.indexListDurable()
  280. if err != nil {
  281. return nil, err
  282. }
  283. tx.recordRead(tx.base.indexListKey(), lsn)
  284. tx.indexList = indexes
  285. }
  286. return append([]string(nil), tx.indexList...), nil
  287. }
  288. func (tx *SchemaTx) stageIndexList(indexes []string) {
  289. tx.indexList = indexes
  290. tx.indexListDirty = true
  291. }
  292. func (tx *SchemaTx) listTableIndexes(table string) ([]*Index, error) {
  293. names, err := tx.listIndexes()
  294. if err != nil {
  295. return nil, err
  296. }
  297. var result []*Index
  298. for _, name := range names {
  299. idx, err := tx.getIndex(name)
  300. if err != nil {
  301. if err == ErrIndexNotFound || strings.Contains(err.Error(), ErrIndexNotFound.Error()) {
  302. continue
  303. }
  304. return nil, err
  305. }
  306. if strings.EqualFold(idx.Table, table) {
  307. result = append(result, idx)
  308. }
  309. }
  310. return result, nil
  311. }
  312. // stageSchema records a replacement table definition.
  313. func (tx *SchemaTx) stageSchema(schema *Schema) {
  314. lower := strings.ToLower(schema.Name)
  315. delete(tx.committedSchemas, lower)
  316. tx.staged[lower] = cloneSchema(schema)
  317. if _, ok := tx.dataSources[lower]; !ok {
  318. tx.dataSources[lower] = lower
  319. }
  320. tx.recordRead(tx.base.schemaKey(schema.Name), 0)
  321. tx.bump()
  322. }
  323. // stageDroppedTable records that a name stopped being addressable.
  324. func (tx *SchemaTx) stageDroppedTable(lower string) {
  325. delete(tx.committedSchemas, lower)
  326. tx.staged[lower] = nil
  327. tx.recordRead(tx.base.schemaKey(lower), 0)
  328. tx.bump()
  329. }
  330. // createTable stages CREATE TABLE.
  331. func (tx *SchemaTx) createTable(schema *Schema) error {
  332. if tx.tableExists(schema.Name) {
  333. return fmt.Errorf("table already exists: %s", schema.Name)
  334. }
  335. lower := strings.ToLower(schema.Name)
  336. schema = cloneSchema(schema)
  337. schema.CreatedAt = time.Now()
  338. schema.normalizePrimaryKey()
  339. tx.staged[lower] = schema
  340. tx.dataSources[lower] = ""
  341. tables, err := tx.listTables()
  342. if err != nil {
  343. return err
  344. }
  345. for _, name := range tables {
  346. if strings.EqualFold(name, schema.Name) {
  347. return fmt.Errorf("table already exists: %s", schema.Name)
  348. }
  349. }
  350. tx.stageTableList(append(tables, schema.Name))
  351. tx.bump()
  352. return nil
  353. }
  354. // createIndex stages CREATE INDEX.
  355. func (tx *SchemaTx) createIndex(index *Index) error {
  356. if tx.indexExists(index.Name) {
  357. return fmt.Errorf("index already exists: %s", index.Name)
  358. }
  359. if !tx.tableExists(index.Table) {
  360. return fmt.Errorf("table not found: %s", index.Table)
  361. }
  362. index = cloneIndex(index)
  363. index.CreatedAt = time.Now()
  364. delete(tx.committedIndexes, strings.ToLower(index.Name))
  365. tx.stagedIndexes[strings.ToLower(index.Name)] = index
  366. names, err := tx.listIndexes()
  367. if err != nil {
  368. return err
  369. }
  370. tx.stageIndexList(append(names, index.Name))
  371. tx.recordRead(tx.base.indexKey(index.Name), 0)
  372. tx.bump()
  373. return nil
  374. }
  375. // dropIndex stages DROP INDEX.
  376. func (tx *SchemaTx) dropIndex(name string) error {
  377. index, err := tx.getIndex(name)
  378. if err != nil {
  379. return fmt.Errorf("index not found: %s", name)
  380. }
  381. lower := strings.ToLower(name)
  382. delete(tx.committedIndexes, lower)
  383. tx.stagedIndexes[lower] = nil
  384. names, err := tx.listIndexes()
  385. if err != nil {
  386. return err
  387. }
  388. filtered := make([]string, 0, len(names))
  389. for _, existing := range names {
  390. if !strings.EqualFold(existing, name) {
  391. filtered = append(filtered, existing)
  392. }
  393. }
  394. tx.stageIndexList(filtered)
  395. tx.recordRead(tx.base.indexKey(index.Name), 0)
  396. tx.bump()
  397. return nil
  398. }
  399. // dropTable stages DROP TABLE. The durable rows are removed by the commit; the
  400. // session's staged rows for the table are discarded immediately because the
  401. // table is no longer visible to this transaction.
  402. func (tx *SchemaTx) dropTable(name string) error {
  403. lower := strings.ToLower(name)
  404. if _, err := tx.getSchema(name); err != nil {
  405. return fmt.Errorf("table not found: %s", name)
  406. }
  407. source := tx.durableSource(lower)
  408. if source != "" {
  409. tx.drops[source] = true
  410. }
  411. delete(tx.rewrites, lower)
  412. delete(tx.dataSources, lower)
  413. // A name that was renamed inside this transaction has no durable record of
  414. // its own; the rename already deleted the original key at commit time.
  415. if _, renamed := tx.renames[lower]; !renamed {
  416. tx.recordRead(tx.base.schemaKey(lower), 0)
  417. }
  418. tx.stageDroppedTable(lower)
  419. for _, idx := range tx.tableIndexes(lower) {
  420. if err := tx.dropIndex(idx.Name); err != nil {
  421. return err
  422. }
  423. }
  424. tables, err := tx.listTables()
  425. if err != nil {
  426. return err
  427. }
  428. filtered := make([]string, 0, len(tables))
  429. for _, existing := range tables {
  430. if !strings.EqualFold(existing, name) {
  431. filtered = append(filtered, existing)
  432. }
  433. }
  434. tx.stageTableList(filtered)
  435. tx.sess.dropOverlayTableLocked(lower)
  436. tx.sess.markTableScanned(lower)
  437. return nil
  438. }
  439. // durableSource returns the committed table name whose data prefix holds the
  440. // rows of a staged table, or "" when the table was created by this transaction.
  441. func (tx *SchemaTx) durableSource(lower string) string {
  442. if source, ok := tx.dataSources[lower]; ok {
  443. return source
  444. }
  445. return lower
  446. }
  447. // tableIndexes returns the staged index definitions of a table.
  448. func (tx *SchemaTx) tableIndexes(lower string) []*Index {
  449. names, err := tx.listIndexes()
  450. if err != nil {
  451. return nil
  452. }
  453. var result []*Index
  454. for _, name := range names {
  455. idx, err := tx.getIndex(name)
  456. if err != nil || !strings.EqualFold(idx.Table, lower) {
  457. continue
  458. }
  459. result = append(result, idx)
  460. }
  461. return result
  462. }
  463. // addColumn stages ALTER TABLE ADD COLUMN. The new column, its CHECK
  464. // predicates, its UNIQUE index, and the default backfill of existing rows are
  465. // validated against the rows this session sees (durable rows plus its own
  466. // staged writes) before anything is staged, so a rejected DDL leaves the
  467. // transaction's catalog untouched.
  468. func (tx *SchemaTx) addColumn(table string, column Column, checks []CheckConstraint, unique *Index) error {
  469. schema, err := tx.getSchema(table)
  470. if err != nil {
  471. return err
  472. }
  473. if err := checkColumnAbsent(schema, column.Name); err != nil {
  474. return err
  475. }
  476. if unique != nil {
  477. if !strings.EqualFold(unique.Table, schema.Name) {
  478. return fmt.Errorf("index %s belongs to table %s, not %s", unique.Name, unique.Table, schema.Name)
  479. }
  480. if tx.indexExists(unique.Name) {
  481. return fmt.Errorf("index already exists: %s", unique.Name)
  482. }
  483. }
  484. now := tx.base.defaultNow()
  485. // Validation covers the committed rows, mirroring the autocommit DDL path:
  486. // rows this transaction staged are re-validated against the staged schema at
  487. // commit (applyOverlaySchemaDefaults), because a staged row may still be
  488. // rewritten or removed before then.
  489. rows, err := tx.durableRows(table)
  490. if err != nil {
  491. return err
  492. }
  493. if !column.Nullable && !columnHasDefault(column) && len(rows) > 0 {
  494. return &NotNullViolationError{Table: schema.Name, Column: column.Name}
  495. }
  496. eval := tx.base.checkEvaluator()
  497. if len(checks) > 0 && eval == nil {
  498. return fmt.Errorf("cannot validate CHECK constraints on %s: no CHECK evaluator installed", table)
  499. }
  500. seen := make(map[string]struct{})
  501. for _, row := range rows {
  502. value, present := rowColumnValue(row, column.Name)
  503. if !present {
  504. if fallback, has, defErr := tx.base.columnDefaultValue(column, now); defErr != nil {
  505. return defErr
  506. } else if has {
  507. value = fallback
  508. present = true
  509. }
  510. }
  511. if len(checks) > 0 {
  512. probe := cloneRow(row)
  513. if present {
  514. probe[column.Name] = value
  515. }
  516. for _, check := range checks {
  517. holds, evalErr := eval(check.Expression, probe)
  518. if evalErr != nil {
  519. return fmt.Errorf("evaluate CHECK constraint %s on %s: %w", check.Label(), table, evalErr)
  520. }
  521. if !holds {
  522. return &CheckViolationError{Table: table, Constraint: check.Label()}
  523. }
  524. }
  525. }
  526. if unique != nil && value != nil {
  527. encoded := encodeUniqueScalar(value)
  528. if _, duplicate := seen[encoded]; duplicate {
  529. return &UniqueViolationError{Constraint: unique.Name}
  530. }
  531. seen[encoded] = struct{}{}
  532. }
  533. }
  534. schema = cloneSchema(schema)
  535. schema.Columns = append(schema.Columns, column)
  536. schema.Checks = append(schema.Checks, checks...)
  537. tx.stageSchema(schema)
  538. if unique != nil {
  539. tx.stagedIndexes[strings.ToLower(unique.Name)] = cloneIndex(unique)
  540. names, err := tx.listIndexes()
  541. if err != nil {
  542. return err
  543. }
  544. tx.stageIndexList(append(names, unique.Name))
  545. tx.recordRead(tx.base.indexKey(unique.Name), 0)
  546. }
  547. if columnHasDefault(column) {
  548. value, has, err := tx.base.columnDefaultValue(column, now)
  549. if err != nil {
  550. return err
  551. }
  552. if has {
  553. name := column.Name
  554. tx.addRewrite(table, func(row Row) (bool, error) {
  555. if _, ok := rowColumnValue(row, name); ok {
  556. return false, nil
  557. }
  558. row[name] = value
  559. return true, nil
  560. })
  561. }
  562. }
  563. tx.sess.markTableScanned(strings.ToLower(table))
  564. return nil
  565. }
  566. // durableRows returns the committed rows of a table, ignoring this
  567. // transaction's staged rows and pending rewrites.
  568. func (tx *SchemaTx) durableRows(table string) ([]Row, error) {
  569. var rows []Row
  570. prefix := tx.sess.table.dataPrefix(tx.durableSource(strings.ToLower(table)))
  571. err := tx.sess.table.scanRowsAtPrefix(prefix, func(_ string, row Row, _ uint64) (bool, error) {
  572. rows = append(rows, row)
  573. return false, nil
  574. })
  575. return rows, err
  576. }
  577. // dropColumn stages ALTER TABLE DROP COLUMN.
  578. func (tx *SchemaTx) dropColumn(table, columnName string) error {
  579. schema, err := tx.getSchema(table)
  580. if err != nil {
  581. return err
  582. }
  583. if schema.IsPrimaryKeyColumn(columnName) {
  584. return fmt.Errorf("cannot drop primary key column: %s", columnName)
  585. }
  586. columns := make([]Column, 0, len(schema.Columns)-1)
  587. found := false
  588. for _, column := range schema.Columns {
  589. if strings.EqualFold(column.Name, columnName) {
  590. found = true
  591. continue
  592. }
  593. columns = append(columns, column)
  594. }
  595. if !found {
  596. return fmt.Errorf("column not found: %s", columnName)
  597. }
  598. schema = cloneSchema(schema)
  599. schema.Columns = columns
  600. tx.stageSchema(schema)
  601. tx.addRewrite(table, func(row Row) (bool, error) {
  602. for key := range row {
  603. if strings.EqualFold(key, columnName) {
  604. delete(row, key)
  605. return true, nil
  606. }
  607. }
  608. return false, nil
  609. })
  610. tx.sess.markTableScanned(strings.ToLower(table))
  611. return nil
  612. }
  613. // renameColumn stages ALTER TABLE RENAME COLUMN, including the column
  614. // references of the table's index definitions.
  615. func (tx *SchemaTx) renameColumn(table, oldName, newName string) error {
  616. schema, err := tx.getSchema(table)
  617. if err != nil {
  618. return err
  619. }
  620. for _, column := range schema.Columns {
  621. if strings.EqualFold(column.Name, newName) {
  622. return fmt.Errorf("column already exists: %s", newName)
  623. }
  624. }
  625. schema = cloneSchema(schema)
  626. found := false
  627. for i := range schema.Columns {
  628. if !strings.EqualFold(schema.Columns[i].Name, oldName) {
  629. continue
  630. }
  631. schema.Columns[i].Name = newName
  632. found = true
  633. for j, pk := range schema.PrimaryKeyColumns {
  634. if strings.EqualFold(pk, oldName) {
  635. schema.PrimaryKeyColumns[j] = newName
  636. }
  637. }
  638. if strings.EqualFold(schema.PrimaryKey, oldName) {
  639. schema.PrimaryKey = newName
  640. }
  641. break
  642. }
  643. if !found {
  644. return fmt.Errorf("column not found: %s", oldName)
  645. }
  646. // CHECK predicates persist SQL text and are not rewritten; the executor
  647. // rejects a rename that one depends on, matching the autocommit path.
  648. tx.stageSchema(schema)
  649. for _, idx := range tx.tableIndexes(strings.ToLower(table)) {
  650. changed := false
  651. updated := cloneIndex(idx)
  652. for i := range updated.Columns {
  653. if updated.Columns[i].Expression == "" && strings.EqualFold(updated.Columns[i].Name, oldName) {
  654. updated.Columns[i].Name = newName
  655. changed = true
  656. }
  657. }
  658. if !changed {
  659. continue
  660. }
  661. tx.stagedIndexes[strings.ToLower(updated.Name)] = updated
  662. tx.recordRead(tx.base.indexKey(updated.Name), 0)
  663. }
  664. tx.addRewrite(table, func(row Row) (bool, error) {
  665. for key, value := range row {
  666. if strings.EqualFold(key, oldName) {
  667. delete(row, key)
  668. row[newName] = value
  669. return true, nil
  670. }
  671. }
  672. return false, nil
  673. })
  674. tx.sess.markTableScanned(strings.ToLower(table))
  675. return nil
  676. }
  677. // renameTable stages ALTER TABLE RENAME TABLE. Rows stay under their committed
  678. // data prefix until commit; the session overlay is re-keyed immediately so the
  679. // transaction reads its own writes under the new name.
  680. func (tx *SchemaTx) renameTable(oldName, newName string) error {
  681. schema, err := tx.getSchema(oldName)
  682. if err != nil {
  683. return err
  684. }
  685. if tx.tableExists(newName) {
  686. return fmt.Errorf("table already exists: %s", newName)
  687. }
  688. oldLower := strings.ToLower(oldName)
  689. newLower := strings.ToLower(newName)
  690. source := tx.durableSource(oldLower)
  691. schema = cloneSchema(schema)
  692. schema.Name = newName
  693. delete(tx.committedSchemas, oldLower)
  694. delete(tx.committedSchemas, newLower)
  695. tx.staged[oldLower] = nil
  696. tx.staged[newLower] = schema
  697. if source == "" {
  698. tx.dataSources[newLower] = ""
  699. } else {
  700. tx.dataSources[newLower] = source
  701. tx.renames[source] = newName
  702. }
  703. delete(tx.dataSources, oldLower)
  704. // A drop staged before the rename keeps its durable source; the rename
  705. // moves that source instead, so the drop no longer applies to it.
  706. delete(tx.drops, newLower)
  707. if rewrites, ok := tx.rewrites[oldLower]; ok {
  708. delete(tx.rewrites, oldLower)
  709. tx.rewrites[newLower] = rewrites
  710. }
  711. for _, idx := range tx.tableIndexes(oldLower) {
  712. updated := cloneIndex(idx)
  713. updated.Table = newName
  714. tx.stagedIndexes[strings.ToLower(updated.Name)] = updated
  715. tx.recordRead(tx.base.indexKey(updated.Name), 0)
  716. }
  717. tables, err := tx.listTables()
  718. if err != nil {
  719. return err
  720. }
  721. found := false
  722. for i, name := range tables {
  723. if strings.EqualFold(name, oldName) {
  724. tables[i] = newName
  725. found = true
  726. break
  727. }
  728. }
  729. if !found {
  730. return fmt.Errorf("table not found in catalog: %s", oldName)
  731. }
  732. tx.stageTableList(tables)
  733. tx.rekeys = append(tx.rekeys, rekeyPair{from: oldLower, to: newLower})
  734. tx.sess.rekeyOverlayLocked(oldLower, newLower)
  735. source = tx.durableSource(newLower)
  736. if source != "" {
  737. tx.sess.markTableScanned(source)
  738. }
  739. tx.bump()
  740. return nil
  741. }
  742. // addRewrite appends a row transformation for a table.
  743. func (tx *SchemaTx) addRewrite(table string, rewrite rowRewrite) {
  744. lower := strings.ToLower(table)
  745. tx.rewrites[lower] = append(tx.rewrites[lower], rewrite)
  746. }
  747. // rewriteRow applies every pending transformation of a table to a row.
  748. func (tx *SchemaTx) rewriteRow(table string, row Row) (bool, error) {
  749. rewrites, ok := tx.rewrites[strings.ToLower(table)]
  750. if !ok {
  751. return false, nil
  752. }
  753. changed := false
  754. for _, rewrite := range rewrites {
  755. applied, err := rewrite(row)
  756. if err != nil {
  757. return changed, err
  758. }
  759. changed = changed || applied
  760. }
  761. return changed, nil
  762. }
  763. // commitCatalogOps returns the durable catalog writes of the transaction and
  764. // the compare checks that keep them from overwriting a concurrent commit. The
  765. // caller holds the session lock and the affected table gates.
  766. func (tx *SchemaTx) commitCatalogOps() ([]BatchOp, []CompareCheck, error) {
  767. var ops []BatchOp
  768. keys := make([]string, 0, len(tx.staged)+len(tx.stagedIndexes)+2)
  769. for lower, schema := range tx.staged {
  770. if schema == nil {
  771. if _, renamed := tx.renames[lower]; renamed {
  772. // RENAME TABLE names the source through renames; the delete of
  773. // the old schema record is emitted below.
  774. continue
  775. }
  776. ops = append(ops,
  777. BatchOp{Op: batchDelete, Key: []byte(tx.base.schemaKey(lower))},
  778. BatchOp{Op: batchDelete, Key: []byte(tx.base.rowIDKey(lower))},
  779. )
  780. // The ROWID key is an in-memory allocator input, not catalog state a
  781. // concurrent statement can change, so its delete needs no compare.
  782. keys = append(keys, tx.base.schemaKey(lower))
  783. continue
  784. }
  785. data, err := json.Marshal(schema)
  786. if err != nil {
  787. return nil, nil, fmt.Errorf("failed to serialize schema: %w", err)
  788. }
  789. key := tx.base.schemaKey(schema.Name)
  790. ops = append(ops, BatchOp{Op: batchPut, Key: []byte(key), Value: data})
  791. keys = append(keys, key)
  792. }
  793. // A staged table definition whose name replaced an older one deletes the
  794. // old record in the same batch.
  795. for source, newName := range tx.renames {
  796. ops = append(ops, BatchOp{Op: batchDelete, Key: []byte(tx.base.schemaKey(source))})
  797. keys = append(keys, tx.base.schemaKey(source))
  798. if !strings.EqualFold(source, newName) {
  799. ops = append(ops, BatchOp{Op: batchDelete, Key: []byte(tx.base.rowIDKey(source))})
  800. }
  801. }
  802. for lower, index := range tx.stagedIndexes {
  803. if index == nil {
  804. ops = append(ops, BatchOp{Op: batchDelete, Key: []byte(tx.base.indexKey(lower))})
  805. keys = append(keys, tx.base.indexKey(lower))
  806. continue
  807. }
  808. data, err := json.Marshal(index)
  809. if err != nil {
  810. return nil, nil, fmt.Errorf("failed to serialize index: %w", err)
  811. }
  812. key := tx.base.indexKey(index.Name)
  813. ops = append(ops, BatchOp{Op: batchPut, Key: []byte(key), Value: data})
  814. keys = append(keys, key)
  815. }
  816. if tx.catalogDirty {
  817. data, err := json.Marshal(tx.tables)
  818. if err != nil {
  819. return nil, nil, fmt.Errorf("failed to serialize catalog: %w", err)
  820. }
  821. ops = append(ops, BatchOp{Op: batchPut, Key: []byte(tx.base.catalogKey()), Value: data})
  822. keys = append(keys, tx.base.catalogKey())
  823. }
  824. if tx.indexListDirty {
  825. data, err := json.Marshal(tx.indexList)
  826. if err != nil {
  827. return nil, nil, fmt.Errorf("failed to serialize index list: %w", err)
  828. }
  829. ops = append(ops, BatchOp{Op: batchPut, Key: []byte(tx.base.indexListKey()), Value: data})
  830. keys = append(keys, tx.base.indexListKey())
  831. }
  832. checks := make([]CompareCheck, 0, len(keys))
  833. seen := make(map[string]bool, len(keys))
  834. for _, key := range keys {
  835. if seen[key] {
  836. continue
  837. }
  838. seen[key] = true
  839. checks = append(checks, CompareCheck{Key: []byte(key), LSN: tx.reads[key]})
  840. }
  841. return ops, checks, nil
  842. }
  843. // commitDataOps returns the durable row writes of the transaction: dropped
  844. // tables lose every row, renamed tables move to their new prefix, and ALTER
  845. // TABLE transformations are applied to the committed row images. Rows this
  846. // session staged in its overlay are published from the overlay instead, so they
  847. // are skipped here. The caller holds the session lock and the exclusive table
  848. // gates of every affected table.
  849. func (tx *SchemaTx) commitDataOps() ([]BatchOp, map[string]map[string]*overlayEntry, error) {
  850. entries := make(map[string]map[string]*overlayEntry)
  851. var ops []BatchOp
  852. if len(tx.drops) == 0 && len(tx.renames) == 0 && len(tx.rewrites) == 0 {
  853. return nil, nil, nil
  854. }
  855. affected := make(map[string]bool)
  856. for table := range tx.drops {
  857. affected[table] = true
  858. }
  859. for table := range tx.renames {
  860. affected[table] = true
  861. }
  862. for table := range tx.rewrites {
  863. if source := tx.durableSource(table); source != "" && !tx.drops[source] {
  864. affected[source] = true
  865. }
  866. }
  867. names := make([]string, 0, len(affected))
  868. for table := range affected {
  869. names = append(names, table)
  870. }
  871. sort.Strings(names)
  872. for _, source := range names {
  873. if tx.drops[source] {
  874. dropped, err := tx.scanRowsForDDL(source, func(key string, row Row) (bool, Row, error) {
  875. if tx.sess.overlayHolds(strings.ToLower(source), key) {
  876. return false, nil, nil
  877. }
  878. return true, nil, nil
  879. })
  880. if err != nil {
  881. return nil, nil, err
  882. }
  883. ops = append(ops, dropped...)
  884. continue
  885. }
  886. newName, renamed := tx.renames[source]
  887. if renamed {
  888. updated, err := tx.scanRowsForDDL(source, func(key string, row Row) (bool, Row, error) {
  889. if _, err := tx.rewriteRow(newName, row); err != nil {
  890. return false, nil, err
  891. }
  892. newKey := tx.sess.table.dataKey(newName, mustRowPK(tx, newName, row))
  893. if tx.sess.overlayHolds(strings.ToLower(newName), newKey) {
  894. return false, nil, nil
  895. }
  896. return true, row, nil
  897. })
  898. if err != nil {
  899. return nil, nil, err
  900. }
  901. lower := strings.ToLower(newName)
  902. if entries[lower] == nil {
  903. entries[lower] = make(map[string]*overlayEntry)
  904. }
  905. for _, op := range updated {
  906. oldKey := op.Key
  907. suffix := strings.TrimPrefix(string(oldKey), tx.sess.table.dataPrefix(source))
  908. newKey := []byte(tx.sess.table.dataPrefix(newName) + suffix)
  909. row, err := decodeRow(op.Value)
  910. if err != nil {
  911. return nil, nil, err
  912. }
  913. entries[lower][string(newKey)] = &overlayEntry{row: row}
  914. ops = append(ops,
  915. BatchOp{Op: batchPut, Key: newKey, Value: op.Value},
  916. BatchOp{Op: batchDelete, Key: oldKey},
  917. )
  918. }
  919. continue
  920. }
  921. rewrites := tx.rewrites[source]
  922. if len(rewrites) == 0 {
  923. continue
  924. }
  925. updated, err := tx.scanRowsForDDL(source, func(key string, row Row) (bool, Row, error) {
  926. if tx.sess.overlayHolds(strings.ToLower(source), key) {
  927. return false, nil, nil
  928. }
  929. changed, err := tx.rewriteRow(source, row)
  930. if err != nil {
  931. return false, nil, err
  932. }
  933. return changed, row, nil
  934. })
  935. if err != nil {
  936. return nil, nil, err
  937. }
  938. lower := strings.ToLower(source)
  939. if entries[lower] == nil {
  940. entries[lower] = make(map[string]*overlayEntry)
  941. }
  942. for _, op := range updated {
  943. row, err := decodeRow(op.Value)
  944. if err != nil {
  945. return nil, nil, err
  946. }
  947. entries[lower][string(op.Key)] = &overlayEntry{row: row}
  948. ops = append(ops, op)
  949. }
  950. }
  951. return ops, entries, nil
  952. }
  953. // mustRowPK derives the primary-key string of a row the transaction's staged
  954. // schema describes.
  955. func mustRowPK(tx *SchemaTx, table string, row Row) string {
  956. schema, err := tx.getSchema(table)
  957. if err != nil {
  958. return ""
  959. }
  960. pk, err := schema.PrimaryKeyForRow(row)
  961. if err != nil {
  962. return ""
  963. }
  964. return pk
  965. }
  966. // scanRowsForDDL streams a durable table's rows through transform. When
  967. // transform reports a change the row is re-encoded and returned as a put under
  968. // its existing key; when it reports removal the key is returned as a delete.
  969. func (tx *SchemaTx) scanRowsForDDL(table string, transform func(key string, row Row) (write bool, replacement Row, err error)) ([]BatchOp, error) {
  970. var ops []BatchOp
  971. prefix := []byte(tx.sess.table.dataPrefix(table))
  972. err := tx.base.pool.WithClient(func(client *KVClient) (retErr error) {
  973. cursor, err := client.Scan(prefix)
  974. if err != nil {
  975. return err
  976. }
  977. defer func() {
  978. if cerr := cursor.Close(); retErr == nil {
  979. retErr = cerr
  980. }
  981. }()
  982. for {
  983. entries, done, err := cursor.Next()
  984. if err != nil {
  985. return err
  986. }
  987. for _, entry := range entries {
  988. row, err := decodeRow(entry.Value)
  989. if err != nil {
  990. return err
  991. }
  992. write, replacement, err := transform(string(entry.Key), row)
  993. if err != nil {
  994. return err
  995. }
  996. if !write {
  997. continue
  998. }
  999. if replacement == nil {
  1000. ops = append(ops, BatchOp{Op: batchDelete, Key: append([]byte(nil), entry.Key...)})
  1001. continue
  1002. }
  1003. value, err := encodeRow(replacement)
  1004. if err != nil {
  1005. return err
  1006. }
  1007. ops = append(ops, BatchOp{Op: batchPut, Key: append([]byte(nil), entry.Key...), Value: value})
  1008. }
  1009. if done {
  1010. return nil
  1011. }
  1012. }
  1013. })
  1014. return ops, err
  1015. }
  1016. // touchedTables lists the lowercased table names whose caches the commit must
  1017. // invalidate, including the source names rows moved away from.
  1018. func (tx *SchemaTx) touchedTables() []string {
  1019. set := make(map[string]bool)
  1020. for lower, schema := range tx.staged {
  1021. set[lower] = true
  1022. if schema != nil {
  1023. set[strings.ToLower(schema.Name)] = true
  1024. if source, ok := tx.dataSources[lower]; ok && source != "" {
  1025. set[source] = true
  1026. }
  1027. }
  1028. }
  1029. for table := range tx.drops {
  1030. set[table] = true
  1031. }
  1032. for table := range tx.renames {
  1033. set[table] = true
  1034. }
  1035. for table := range tx.rewrites {
  1036. set[table] = true
  1037. if source := tx.durableSource(table); source != "" {
  1038. set[source] = true
  1039. }
  1040. }
  1041. names := make([]string, 0, len(set))
  1042. for table := range set {
  1043. names = append(names, table)
  1044. }
  1045. sort.Strings(names)
  1046. return names
  1047. }
  1048. // touchedIndexes lists the lowercased index names whose caches the commit must
  1049. // reset.
  1050. func (tx *SchemaTx) touchedIndexes() []string {
  1051. names := make([]string, 0, len(tx.stagedIndexes))
  1052. for lower := range tx.stagedIndexes {
  1053. names = append(names, lower)
  1054. }
  1055. sort.Strings(names)
  1056. return names
  1057. }
  1058. // hasDataEffects reports whether the commit must rewrite or remove durable rows.
  1059. func (tx *SchemaTx) hasDataEffects() bool {
  1060. return len(tx.drops) > 0 || len(tx.renames) > 0 || len(tx.rewrites) > 0
  1061. }
  1062. // hasDDL reports whether the transaction staged a catalog change that the
  1063. // commit has to publish.
  1064. func (tx *SchemaTx) hasDDL() bool {
  1065. return len(tx.staged) > 0 || len(tx.stagedIndexes) > 0 || tx.catalogDirty || tx.indexListDirty || tx.hasDataEffects()
  1066. }
  1067. // Session catalog access
  1068. //
  1069. // A Session is the catalog view of one SQL connection. Outside an explicit
  1070. // transaction every accessor delegates to the shared SchemaManager; while a
  1071. // transaction is open the accessors resolve against its SchemaTx overlay, so
  1072. // the session reads its own DDL without publishing it.
  1073. // GetSchema returns the table definition this session observes.
  1074. func (s *Session) GetSchema(name string) (*Schema, error) {
  1075. s.mu.Lock()
  1076. defer s.mu.Unlock()
  1077. return s.getSchemaLocked(name)
  1078. }
  1079. // getSchemaLocked resolves a table definition (caller holds the session lock).
  1080. func (s *Session) getSchemaLocked(name string) (*Schema, error) {
  1081. if s.schemaTx != nil {
  1082. return s.schemaTx.getSchema(name)
  1083. }
  1084. return s.schema.GetSchema(name)
  1085. }
  1086. // TableExists reports whether the session can resolve a table.
  1087. func (s *Session) TableExists(name string) bool {
  1088. _, err := s.GetSchema(name)
  1089. return err == nil
  1090. }
  1091. // ListTables returns the table catalog this session observes.
  1092. func (s *Session) ListTables() ([]string, error) {
  1093. s.mu.Lock()
  1094. defer s.mu.Unlock()
  1095. if s.schemaTx != nil {
  1096. return s.schemaTx.listTables()
  1097. }
  1098. return s.schema.ListTables()
  1099. }
  1100. // GetIndex returns an index definition this session observes.
  1101. func (s *Session) GetIndex(name string) (*Index, error) {
  1102. s.mu.Lock()
  1103. defer s.mu.Unlock()
  1104. if s.schemaTx != nil {
  1105. return s.schemaTx.getIndex(name)
  1106. }
  1107. return s.schema.GetIndex(name)
  1108. }
  1109. // IndexExists reports whether the session can resolve an index.
  1110. func (s *Session) IndexExists(name string) bool {
  1111. _, err := s.GetIndex(name)
  1112. return err == nil
  1113. }
  1114. // ListIndexes returns the index catalog this session observes.
  1115. func (s *Session) ListIndexes() ([]string, error) {
  1116. s.mu.Lock()
  1117. defer s.mu.Unlock()
  1118. if s.schemaTx != nil {
  1119. return s.schemaTx.listIndexes()
  1120. }
  1121. return s.schema.ListIndexes()
  1122. }
  1123. // ListTableIndexes returns the index definitions of a table as this session
  1124. // observes them.
  1125. func (s *Session) ListTableIndexes(table string) ([]*Index, error) {
  1126. s.mu.Lock()
  1127. defer s.mu.Unlock()
  1128. if s.schemaTx != nil {
  1129. return s.schemaTx.listTableIndexes(table)
  1130. }
  1131. return s.schema.ListTableIndexes(table)
  1132. }
  1133. // Version identifies the catalog generation this session observes, including
  1134. // staged DDL, so the executor resynchronizes its analyzer catalog after a
  1135. // transactional DDL statement and again after the overlay is discarded.
  1136. func (s *Session) Version() uint64 {
  1137. s.mu.Lock()
  1138. defer s.mu.Unlock()
  1139. return s.versionLocked()
  1140. }
  1141. func (s *Session) versionLocked() uint64 {
  1142. if s.schemaTx != nil {
  1143. return s.schemaTx.version
  1144. }
  1145. return s.schema.Version()
  1146. }
  1147. // GetNextRowID allocates from the shared per-table ROWID counter. The counter is
  1148. // derived from durable rows and is not part of the catalog, so a transaction
  1149. // uses the same allocator as autocommit.
  1150. func (s *Session) GetNextRowID(table string) (int64, error) {
  1151. return s.schema.GetNextRowID(table)
  1152. }
  1153. // UpdateMaxRowID raises the shared per-table ROWID counter.
  1154. func (s *Session) UpdateMaxRowID(table string, rowid int64) error {
  1155. return s.schema.UpdateMaxRowID(table, rowid)
  1156. }
  1157. // CreateTable stages CREATE TABLE inside a transaction and publishes it
  1158. // immediately otherwise.
  1159. func (s *Session) CreateTable(schema *Schema) error {
  1160. s.mu.Lock()
  1161. defer s.mu.Unlock()
  1162. if s.schemaTx == nil {
  1163. return s.schema.CreateTable(schema)
  1164. }
  1165. return s.stageDDL(func(tx *SchemaTx) error { return tx.createTable(schema) })
  1166. }
  1167. // DropTable stages DROP TABLE inside a transaction and publishes it otherwise.
  1168. func (s *Session) DropTable(name string) error {
  1169. s.mu.Lock()
  1170. defer s.mu.Unlock()
  1171. if s.schemaTx == nil {
  1172. return s.schema.DropTable(name)
  1173. }
  1174. return s.stageDDL(func(tx *SchemaTx) error { return tx.dropTable(name) })
  1175. }
  1176. // CreateIndex stages CREATE INDEX inside a transaction and publishes it
  1177. // otherwise. A UNIQUE index is validated against the rows the session observes,
  1178. // so a transaction that staged duplicate rows fails the statement, not the
  1179. // commit.
  1180. func (s *Session) CreateIndex(index *Index) error {
  1181. s.mu.Lock()
  1182. defer s.mu.Unlock()
  1183. if s.schemaTx == nil {
  1184. if index.Unique {
  1185. return s.table.CreateUniqueIndex(index)
  1186. }
  1187. return s.schema.CreateIndex(index)
  1188. }
  1189. return s.stageDDL(func(tx *SchemaTx) error {
  1190. if err := tx.createIndex(index); err != nil {
  1191. return err
  1192. }
  1193. if !index.Unique {
  1194. return nil
  1195. }
  1196. return s.validateUniqueIndexLocked(index)
  1197. })
  1198. }
  1199. // validateUniqueIndexLocked rejects a staged UNIQUE index whose indexed values
  1200. // already collide in the rows this session observes (caller holds the session
  1201. // lock).
  1202. func (s *Session) validateUniqueIndexLocked(index *Index) error {
  1203. rows, err := s.selectLocked(index.Table, nil)
  1204. if err != nil {
  1205. return err
  1206. }
  1207. seen := make(map[string]struct{}, len(rows))
  1208. for _, row := range rows {
  1209. encoded, isNull, err := s.table.encodeUniqueValue(row, index.Columns)
  1210. if err != nil {
  1211. return err
  1212. }
  1213. if isNull {
  1214. continue
  1215. }
  1216. if _, duplicate := seen[encoded]; duplicate {
  1217. return &UniqueViolationError{Constraint: index.Name}
  1218. }
  1219. seen[encoded] = struct{}{}
  1220. }
  1221. return nil
  1222. }
  1223. // DropIndex stages DROP INDEX inside a transaction and publishes it otherwise.
  1224. func (s *Session) DropIndex(name string) error {
  1225. s.mu.Lock()
  1226. defer s.mu.Unlock()
  1227. if s.schemaTx == nil {
  1228. return s.schema.DropIndex(name)
  1229. }
  1230. return s.stageDDL(func(tx *SchemaTx) error { return tx.dropIndex(name) })
  1231. }
  1232. // AddColumn stages ALTER TABLE ADD COLUMN inside a transaction and publishes it
  1233. // otherwise.
  1234. func (s *Session) AddColumn(table string, column Column, checks []CheckConstraint, unique *Index) error {
  1235. s.mu.Lock()
  1236. defer s.mu.Unlock()
  1237. if s.schemaTx == nil {
  1238. return s.schema.AddColumn(table, column, checks, unique)
  1239. }
  1240. return s.stageDDL(func(tx *SchemaTx) error { return tx.addColumn(table, column, checks, unique) })
  1241. }
  1242. // DropColumn stages ALTER TABLE DROP COLUMN inside a transaction and publishes
  1243. // it otherwise.
  1244. func (s *Session) DropColumn(table, columnName string) error {
  1245. s.mu.Lock()
  1246. defer s.mu.Unlock()
  1247. if s.schemaTx == nil {
  1248. return s.schema.DropColumn(table, columnName)
  1249. }
  1250. return s.stageDDL(func(tx *SchemaTx) error { return tx.dropColumn(table, columnName) })
  1251. }
  1252. // RenameTable stages ALTER TABLE RENAME TABLE inside a transaction and
  1253. // publishes it otherwise.
  1254. func (s *Session) RenameTable(oldName, newName string) error {
  1255. s.mu.Lock()
  1256. defer s.mu.Unlock()
  1257. if s.schemaTx == nil {
  1258. return s.schema.RenameTable(oldName, newName)
  1259. }
  1260. return s.stageDDL(func(tx *SchemaTx) error { return tx.renameTable(oldName, newName) })
  1261. }
  1262. // RenameColumn stages ALTER TABLE RENAME COLUMN inside a transaction and
  1263. // publishes it otherwise.
  1264. func (s *Session) RenameColumn(table, oldName, newName string) error {
  1265. s.mu.Lock()
  1266. defer s.mu.Unlock()
  1267. if s.schemaTx == nil {
  1268. return s.schema.RenameColumn(table, oldName, newName)
  1269. }
  1270. return s.stageDDL(func(tx *SchemaTx) error { return tx.renameColumn(table, oldName, newName) })
  1271. }
  1272. // stageDDL runs one DDL statement against the transaction overlay and restores
  1273. // the catalog, overlay, and read set if it fails, so a rejected statement
  1274. // leaves the transaction exactly as it was (caller holds the session lock).
  1275. func (s *Session) stageDDL(apply func(tx *SchemaTx) error) error {
  1276. savepoint := s.savepointLocked()
  1277. if err := apply(s.schemaTx); err != nil {
  1278. s.rollbackToSavepointLocked(savepoint)
  1279. return err
  1280. }
  1281. return nil
  1282. }
  1283. // overlayHolds reports whether the session staged the given durable key of a
  1284. // table.
  1285. func (s *Session) overlayHolds(lowerTable, key string) bool {
  1286. _, ok := s.overlay[lowerTable][key]
  1287. return ok
  1288. }
  1289. // markTableScanned pins the table's current generation as a read so the commit
  1290. // fails with a serialization error if another session writes the table between
  1291. // the DDL statement and the commit.
  1292. func (s *Session) markTableScanned(table string) {
  1293. lower := strings.ToLower(table)
  1294. if _, ok := s.scanGens[lower]; !ok {
  1295. s.scanGens[lower] = s.table.generation(lower)
  1296. }
  1297. }
  1298. // dropOverlayTableLocked discards the staged rows of a table the transaction
  1299. // dropped, including their mutation-log entries (caller holds the session lock).
  1300. func (s *Session) dropOverlayTableLocked(lower string) {
  1301. delete(s.overlay, lower)
  1302. log := s.log[:0]
  1303. for _, mutation := range s.log {
  1304. if mutation.table != lower {
  1305. log = append(log, mutation)
  1306. }
  1307. }
  1308. s.log = log
  1309. delete(s.scanGens, lower)
  1310. for key, predicate := range s.predicateGens {
  1311. if predicate.table == lower {
  1312. delete(s.predicateGens, key)
  1313. }
  1314. }
  1315. prefix := s.table.dataPrefix(lower)
  1316. for key := range s.reads {
  1317. if strings.HasPrefix(key, prefix) {
  1318. delete(s.reads, key)
  1319. }
  1320. }
  1321. }
  1322. // rekeyOverlayLocked moves the staged rows of a renamed table to the new data
  1323. // prefix so the transaction reads its own rows back under the new name
  1324. // (caller holds the session lock).
  1325. func (s *Session) rekeyOverlayLocked(oldTable, newTable string) {
  1326. oldLower := strings.ToLower(oldTable)
  1327. newLower := strings.ToLower(newTable)
  1328. if oldLower == newLower {
  1329. return
  1330. }
  1331. oldPrefix := s.table.dataPrefix(oldLower)
  1332. newPrefix := s.table.dataPrefix(newLower)
  1333. if entries, ok := s.overlay[oldLower]; ok {
  1334. delete(s.overlay, oldLower)
  1335. moved := make(map[string]*overlayEntry, len(entries))
  1336. for key, entry := range entries {
  1337. moved[newPrefix+strings.TrimPrefix(key, oldPrefix)] = entry
  1338. }
  1339. s.overlay[newLower] = moved
  1340. }
  1341. for i := range s.log {
  1342. if s.log[i].table != oldLower {
  1343. continue
  1344. }
  1345. s.log[i].table = newLower
  1346. s.log[i].key = newPrefix + strings.TrimPrefix(s.log[i].key, oldPrefix)
  1347. }
  1348. if gen, ok := s.scanGens[oldLower]; ok {
  1349. delete(s.scanGens, oldLower)
  1350. s.scanGens[newLower] = gen
  1351. }
  1352. reads := make(map[string]uint64, len(s.reads))
  1353. for key, lsn := range s.reads {
  1354. if strings.HasPrefix(key, oldPrefix) {
  1355. reads[newPrefix+strings.TrimPrefix(key, oldPrefix)] = lsn
  1356. continue
  1357. }
  1358. reads[key] = lsn
  1359. }
  1360. s.reads = reads
  1361. predicates := make(map[string]predicateRead, len(s.predicateGens))
  1362. for key, predicate := range s.predicateGens {
  1363. if predicate.table == oldLower {
  1364. predicate.table = newLower
  1365. }
  1366. if strings.HasPrefix(key, oldPrefix) {
  1367. key = newPrefix + strings.TrimPrefix(key, oldPrefix)
  1368. }
  1369. predicates[key] = predicate
  1370. }
  1371. s.predicateGens = predicates
  1372. }
  1373. // ForeignKeyParticipants reports whether a write to table can affect referential
  1374. // integrity as this session observes the catalog.
  1375. func (s *Session) ForeignKeyParticipants(table string) (bool, error) {
  1376. s.mu.Lock()
  1377. defer s.mu.Unlock()
  1378. if s.schemaTx == nil {
  1379. return s.schema.ForeignKeyParticipants(table)
  1380. }
  1381. catalog, err := s.foreignKeyCatalogLocked()
  1382. if err != nil {
  1383. return false, err
  1384. }
  1385. return catalog.participants[strings.ToLower(table)], nil
  1386. }
  1387. // ForeignKeyColumnDependency returns the constraints that keep a column in
  1388. // place, as this session observes the catalog.
  1389. func (s *Session) ForeignKeyColumnDependency(table, column string) ([]string, error) {
  1390. s.mu.Lock()
  1391. defer s.mu.Unlock()
  1392. if s.schemaTx == nil {
  1393. return s.schema.ForeignKeyColumnDependency(table, column)
  1394. }
  1395. catalog, err := s.foreignKeyCatalogLocked()
  1396. if err != nil {
  1397. return nil, err
  1398. }
  1399. schema, err := s.getSchemaLocked(table)
  1400. if err != nil {
  1401. return nil, err
  1402. }
  1403. seen := make(map[string]bool)
  1404. var labels []string
  1405. add := func(child string, fk ForeignKeyConstraint) {
  1406. label := foreignKeyReferenceName(child, fk)
  1407. if seen[label] {
  1408. return
  1409. }
  1410. seen[label] = true
  1411. labels = append(labels, label)
  1412. }
  1413. for _, fk := range schema.ForeignKeys {
  1414. for _, name := range fk.Columns {
  1415. if strings.EqualFold(name, column) {
  1416. add(schema.Name, fk)
  1417. break
  1418. }
  1419. }
  1420. }
  1421. for _, ref := range catalog.referencing[strings.ToLower(table)] {
  1422. for _, name := range ref.FK.RefColumns {
  1423. if strings.EqualFold(name, column) {
  1424. add(ref.Table, ref.FK)
  1425. break
  1426. }
  1427. }
  1428. }
  1429. sort.Strings(labels)
  1430. return labels, nil
  1431. }
  1432. // ForeignKeyTableDependency returns the constraints that name table as their
  1433. // referenced parent, as this session observes the catalog.
  1434. func (s *Session) ForeignKeyTableDependency(table string) ([]string, error) {
  1435. s.mu.Lock()
  1436. defer s.mu.Unlock()
  1437. if s.schemaTx == nil {
  1438. return s.schema.ForeignKeyTableDependency(table)
  1439. }
  1440. catalog, err := s.foreignKeyCatalogLocked()
  1441. if err != nil {
  1442. return nil, err
  1443. }
  1444. seen := make(map[string]bool)
  1445. var labels []string
  1446. for _, ref := range catalog.referencing[strings.ToLower(table)] {
  1447. label := foreignKeyReferenceName(ref.Table, ref.FK)
  1448. if seen[label] {
  1449. continue
  1450. }
  1451. seen[label] = true
  1452. labels = append(labels, label)
  1453. }
  1454. sort.Strings(labels)
  1455. return labels, nil
  1456. }
  1457. // ForeignKeyClosure returns the constrained component of the given tables as
  1458. // this session observes the catalog.
  1459. func (s *Session) ForeignKeyClosure(tables []string) (map[string]bool, error) {
  1460. s.mu.Lock()
  1461. defer s.mu.Unlock()
  1462. if s.schemaTx == nil {
  1463. return s.schema.ForeignKeyClosure(tables)
  1464. }
  1465. catalog, err := s.foreignKeyCatalogLocked()
  1466. if err != nil {
  1467. return nil, err
  1468. }
  1469. return catalog.closure(tables), nil
  1470. }
  1471. // foreignKeyCatalogLocked builds the foreign-key metadata of the session's
  1472. // catalog view (caller holds the session lock).
  1473. func (s *Session) foreignKeyCatalogLocked() (*foreignKeyCatalog, error) {
  1474. version := s.versionLocked()
  1475. if s.fkCatalog != nil && s.fkVersion == version {
  1476. return s.fkCatalog, nil
  1477. }
  1478. tables, err := s.schemaTx.listTables()
  1479. if err != nil {
  1480. return nil, err
  1481. }
  1482. catalog, err := buildForeignKeyCatalog(version, tables, func(name string) (*Schema, error) {
  1483. return s.getSchemaLocked(name)
  1484. })
  1485. if err != nil {
  1486. return nil, err
  1487. }
  1488. s.fkCatalog = catalog
  1489. s.fkVersion = version
  1490. return catalog, nil
  1491. }