2
0

schema.go 61 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126
  1. package storage
  2. import (
  3. "errors"
  4. "fmt"
  5. "strconv"
  6. "strings"
  7. "sync"
  8. "time"
  9. "github.com/goccy/go-json"
  10. "github.com/danfragoso/pizzasql-next/pkg/analyzer"
  11. )
  12. // ErrIndexNotFound is returned by GetIndex when an index is absent. It is
  13. // distinct from a storage/IO error so callers like ListTableIndexes can skip a
  14. // concurrently-dropped index without swallowing real read failures.
  15. var ErrIndexNotFound = errors.New("index not found")
  16. // Schema represents a table schema.
  17. type Schema struct {
  18. Name string `json:"name"`
  19. Columns []Column `json:"columns"`
  20. // PrimaryKey is the single primary-key column. It stays the source of truth
  21. // for pre-existing catalogs that do not carry PrimaryKeyColumns, and it is
  22. // empty for a composite key so a caller cannot mistake one member for the
  23. // whole key. Use PrimaryKeyList instead of reading either field directly.
  24. PrimaryKey string `json:"primary_key"`
  25. // PrimaryKeyColumns is the ordered primary-key column list for a composite
  26. // key. It is omitted for single-column keys so the catalog keeps its
  27. // historical shape; normalizePrimaryKey fills it in when a schema is loaded.
  28. PrimaryKeyColumns []string `json:"primary_key_columns,omitempty"`
  29. CreatedAt time.Time `json:"created_at"`
  30. NextRowID int64 `json:"next_rowid"`
  31. AutoIncrement bool `json:"autoincrement"`
  32. // Checks holds the row predicates declared with CHECK, at column and table
  33. // level alike. Inline and table-level declarations are equivalent once
  34. // parsed, so both live in this one list.
  35. Checks []CheckConstraint `json:"checks,omitempty"`
  36. // ForeignKeys holds the FOREIGN KEY declarations. Each constraint is
  37. // enforced on every write and re-validated at transaction commit; a
  38. // cascading action rewrites or removes the referencing rows as part of the
  39. // same statement.
  40. ForeignKeys []ForeignKeyConstraint `json:"foreign_keys,omitempty"`
  41. }
  42. // ForeignKeyConstraint is a persisted FOREIGN KEY declaration. Columns are the
  43. // child columns in declaration order; RefColumns are the parent columns they are
  44. // matched against. Both sides have the same length, and the parent side is
  45. // validated at DDL to be the parent's PRIMARY KEY or a UNIQUE index.
  46. type ForeignKeyConstraint struct {
  47. Name string `json:"name,omitempty"`
  48. Columns []string `json:"columns"`
  49. RefTable string `json:"ref_table"`
  50. RefColumns []string `json:"ref_columns"`
  51. // OnDelete and OnUpdate hold the declared referential action text (NO
  52. // ACTION, RESTRICT, CASCADE, SET NULL, or SET DEFAULT). They are empty when
  53. // the clause was omitted and are kept so sqlite_master can reproduce the
  54. // declaration.
  55. OnDelete string `json:"on_delete,omitempty"`
  56. OnUpdate string `json:"on_update,omitempty"`
  57. }
  58. // Label identifies the constraint in error messages: the declared name when
  59. // there is one, otherwise the child column list.
  60. func (fk ForeignKeyConstraint) Label() string {
  61. if fk.Name != "" {
  62. return fk.Name
  63. }
  64. return strings.Join(fk.Columns, ", ") + " -> " + fk.RefTable
  65. }
  66. // CheckConstraint is a persisted CHECK predicate. The predicate is stored as
  67. // SQL text and re-parsed by the evaluator installed on the SchemaManager, so a
  68. // durable reload keeps enforcing it.
  69. type CheckConstraint struct {
  70. Name string `json:"name,omitempty"`
  71. Expression string `json:"expression"`
  72. }
  73. // Label identifies the constraint in error messages and the catalog: the
  74. // declared name when there is one, otherwise the expression text.
  75. func (c CheckConstraint) Label() string {
  76. if c.Name != "" {
  77. return c.Name
  78. }
  79. return c.Expression
  80. }
  81. // Column represents a column definition.
  82. type Column struct {
  83. Name string `json:"name"`
  84. Type string `json:"type"`
  85. Nullable bool `json:"nullable"`
  86. // Default holds the evaluated constant DEFAULT. DefaultExpr holds the SQL
  87. // text of a non-constant DEFAULT (CURRENT_TIMESTAMP and the other
  88. // clock-reading forms) so it is evaluated on every write instead of being
  89. // frozen at DDL time. Only one of the two is set; DefaultExpr takes
  90. // precedence if a hand-edited catalog sets both.
  91. Default interface{} `json:"default,omitempty"`
  92. DefaultExpr string `json:"default_expr,omitempty"`
  93. PrimaryKey bool `json:"primary_key"`
  94. // GeneratedExpr holds the SQL text of a GENERATED ALWAYS AS (expr) column.
  95. // GeneratedStored reports whether the value is materialized on write
  96. // (STORED, the only form this engine persists) versus computed on read.
  97. GeneratedExpr string `json:"generated_expr,omitempty"`
  98. GeneratedStored bool `json:"generated_stored,omitempty"`
  99. }
  100. // DefaultLiteral returns the catalog representation of the column's default:
  101. // the evaluated constant value, or the SQL text of a non-constant default.
  102. // DefaultExpr takes precedence when a hand-edited catalog sets both.
  103. func (c Column) DefaultLiteral() interface{} {
  104. if c.DefaultExpr != "" {
  105. return c.DefaultExpr
  106. }
  107. if c.Default != nil {
  108. return c.Default
  109. }
  110. return nil
  111. }
  112. // Index represents an index definition.
  113. type Index struct {
  114. Name string `json:"name"`
  115. Table string `json:"table"`
  116. Columns []IndexColumn `json:"columns"`
  117. Unique bool `json:"unique"`
  118. CreatedAt time.Time `json:"created_at"`
  119. // OnConflict is the default conflict resolution declared for this index via
  120. // a UNIQUE(...) ON CONFLICT clause. Empty means the SQLite default (ABORT).
  121. OnConflict string `json:"on_conflict,omitempty"`
  122. }
  123. // IndexColumn represents a column in an index. Expression is set for expression
  124. // indexes (e.g. lower(email)); a plain column index leaves it empty and uses
  125. // Name.
  126. type IndexColumn struct {
  127. Name string `json:"name"`
  128. Desc bool `json:"desc"`
  129. Expression string `json:"expression,omitempty"`
  130. }
  131. // rowIDAllocator owns the next-ROWID state for a single table. It is a separate
  132. // mutex per table so allocating a ROWID on one table never serializes against
  133. // another table, and never contends with the SchemaManager catalog lock.
  134. type rowIDAllocator struct {
  135. mu sync.Mutex
  136. next int64
  137. init bool
  138. }
  139. // SchemaManager manages table schemas.
  140. type SchemaManager struct {
  141. pool *KVPool
  142. database string
  143. cache map[string]*Schema
  144. indexCache map[string]*Index
  145. indexListCache []string
  146. indexListCached bool
  147. version uint64
  148. mu sync.RWMutex
  149. tableLocksMu sync.Mutex
  150. tableLocks map[string]*sync.RWMutex
  151. // checkMu guards checkEval, the pluggable evaluator for persisted CHECK
  152. // predicates. It is set by the SQL executor so storage stays independent of
  153. // the SQL front end.
  154. checkMu sync.RWMutex
  155. checkEval CheckEvaluator
  156. rowIDMu sync.Mutex
  157. rowIDAlloc map[string]*rowIDAllocator
  158. // defaultEval evaluates a persisted non-constant DEFAULT expression. The
  159. // SQL executor registers one so storage can backfill ADD COLUMN rows and
  160. // complete row images without importing the parser or executor.
  161. // defaultClock supplies the instant a DDL statement samples once for every
  162. // expression default it touches.
  163. defaultEvalMu sync.RWMutex
  164. defaultEval DefaultEvaluator
  165. defaultClock DefaultClock
  166. // fkCatalogMu guards fkCatalog, the cached foreign-key metadata used to find
  167. // referencing tables without re-reading every schema on every write.
  168. fkCatalogMu sync.Mutex
  169. fkCatalog *foreignKeyCatalog
  170. }
  171. // DefaultEvaluator evaluates a persisted non-constant DEFAULT expression, such
  172. // as CURRENT_TIMESTAMP, at the supplied instant. Expression defaults are never
  173. // frozen at DDL time; the caller samples one instant per statement so every row
  174. // it touches sees the same value.
  175. type DefaultEvaluator func(expression string, now time.Time) (interface{}, error)
  176. // DefaultClock supplies the instant a statement's expression defaults use. The
  177. // SQL executor registers a clock backed by the session so a storage-side ADD
  178. // COLUMN backfill matches statement-time now().
  179. type DefaultClock func() time.Time
  180. // SetDefaultEvaluator installs the evaluator used for expression defaults.
  181. func (m *SchemaManager) SetDefaultEvaluator(eval DefaultEvaluator) {
  182. m.defaultEvalMu.Lock()
  183. m.defaultEval = eval
  184. m.defaultEvalMu.Unlock()
  185. }
  186. // SetDefaultClock installs the clock sampled once per DDL statement for
  187. // expression defaults.
  188. func (m *SchemaManager) SetDefaultClock(clock DefaultClock) {
  189. m.defaultEvalMu.Lock()
  190. m.defaultClock = clock
  191. m.defaultEvalMu.Unlock()
  192. }
  193. func (m *SchemaManager) defaultEvaluator() DefaultEvaluator {
  194. m.defaultEvalMu.RLock()
  195. defer m.defaultEvalMu.RUnlock()
  196. return m.defaultEval
  197. }
  198. // defaultNow samples the registered clock, falling back to the wall clock.
  199. // Callers sample it once per statement and pass the instant down so validation
  200. // and the rewrite observe the same value.
  201. func (m *SchemaManager) defaultNow() time.Time {
  202. m.defaultEvalMu.RLock()
  203. clock := m.defaultClock
  204. m.defaultEvalMu.RUnlock()
  205. if clock != nil {
  206. return clock().UTC()
  207. }
  208. return time.Now().UTC()
  209. }
  210. // CheckEvaluator reports whether a persisted CHECK expression holds for a row.
  211. // A false result is a violation; NULL and other unknown results must be
  212. // reported as true, matching SQL three-valued logic. The SQL executor registers
  213. // one so the storage layer can enforce CHECK without importing the parser.
  214. type CheckEvaluator func(expression string, row Row) (bool, error)
  215. // SetCheckEvaluator installs the evaluator used to enforce persisted CHECK
  216. // predicates.
  217. func (m *SchemaManager) SetCheckEvaluator(eval CheckEvaluator) {
  218. m.checkMu.Lock()
  219. m.checkEval = eval
  220. m.checkMu.Unlock()
  221. }
  222. func (m *SchemaManager) checkEvaluator() CheckEvaluator {
  223. m.checkMu.RLock()
  224. defer m.checkMu.RUnlock()
  225. return m.checkEval
  226. }
  227. // BeginTransaction is retained for API compatibility. Buffered per-session
  228. // transactions no longer take a database-wide transaction lock; staged writes
  229. // are validated and committed atomically with CompareBatchWrite instead.
  230. func (m *SchemaManager) BeginTransaction() {}
  231. // EndTransaction is retained for API compatibility.
  232. func (m *SchemaManager) EndTransaction() {}
  233. // LockStatement is retained for API compatibility. Statement execution is now
  234. // serialized through per-table locks and optimistic validation, so no global
  235. // statement lock is required.
  236. func (m *SchemaManager) LockStatement() {}
  237. // UnlockStatement is retained for API compatibility.
  238. func (m *SchemaManager) UnlockStatement() {}
  239. // NewSchemaManager creates a new schema manager.
  240. func NewSchemaManager(pool *KVPool, database string) *SchemaManager {
  241. return &SchemaManager{
  242. pool: pool,
  243. database: database,
  244. cache: make(map[string]*Schema),
  245. indexCache: make(map[string]*Index),
  246. tableLocks: make(map[string]*sync.RWMutex),
  247. rowIDAlloc: make(map[string]*rowIDAllocator),
  248. }
  249. }
  250. // rowIDAllocatorFor returns (creating if needed) the per-table ROWID allocator.
  251. func (m *SchemaManager) rowIDAllocatorFor(table string) *rowIDAllocator {
  252. key := strings.ToLower(table)
  253. m.rowIDMu.Lock()
  254. a, ok := m.rowIDAlloc[key]
  255. if !ok {
  256. a = &rowIDAllocator{}
  257. m.rowIDAlloc[key] = a
  258. }
  259. m.rowIDMu.Unlock()
  260. return a
  261. }
  262. func (m *SchemaManager) tableLock(table string) *sync.RWMutex {
  263. key := strings.ToLower(table)
  264. m.tableLocksMu.Lock()
  265. lock, ok := m.tableLocks[key]
  266. if !ok {
  267. lock = &sync.RWMutex{}
  268. m.tableLocks[key] = lock
  269. }
  270. m.tableLocksMu.Unlock()
  271. return lock
  272. }
  273. // GetDatabaseName returns the database name.
  274. func (m *SchemaManager) GetDatabaseName() string {
  275. return m.database
  276. }
  277. // GetPool returns the KV pool.
  278. func (m *SchemaManager) GetPool() *KVPool {
  279. return m.pool
  280. }
  281. // Version returns the in-process schema catalog version. It is incremented for
  282. // schema/index definition changes so cached executors can resync their analyzer
  283. // catalogs without scanning storage on every query.
  284. func (m *SchemaManager) Version() uint64 {
  285. m.mu.RLock()
  286. defer m.mu.RUnlock()
  287. return m.version
  288. }
  289. func (m *SchemaManager) bumpVersionLocked() {
  290. m.version++
  291. }
  292. // schemaKey returns the key for a table schema.
  293. func (m *SchemaManager) schemaKey(table string) string {
  294. return fmt.Sprintf("%s:_schema:%s", m.database, strings.ToLower(table))
  295. }
  296. // catalogKey returns the key for the table catalog.
  297. func (m *SchemaManager) catalogKey() string {
  298. return fmt.Sprintf("%s:_sys:tables", m.database)
  299. }
  300. // rowIDKey returns the key for a table's next ROWID counter.
  301. func (m *SchemaManager) rowIDKey(table string) string {
  302. return fmt.Sprintf("%s:_sys:rowid:%s", m.database, strings.ToLower(table))
  303. }
  304. // readDurable reads a catalog key straight from durable storage, bypassing the
  305. // in-memory caches, and reports the LSN the store observed it at. Transactional
  306. // DDL uses it so a staged catalog change is pinned to the exact durable version
  307. // it was derived from, and a concurrent committed DDL fails the commit instead
  308. // of being silently overwritten.
  309. func (m *SchemaManager) readDurable(key string) (value string, lsn uint64, found bool, err error) {
  310. err = m.pool.WithClient(func(c *KVClient) error {
  311. var rerr error
  312. value, lsn, found, rerr = c.ReadWithLSN(key)
  313. return rerr
  314. })
  315. return value, lsn, found, err
  316. }
  317. // catalogTablesDurable returns the committed table catalog and its LSN.
  318. func (m *SchemaManager) catalogTablesDurable() ([]string, uint64, error) {
  319. data, lsn, found, err := m.readDurable(m.catalogKey())
  320. if err != nil {
  321. return nil, 0, err
  322. }
  323. if !found {
  324. return nil, lsn, nil
  325. }
  326. var tables []string
  327. if err := json.Unmarshal([]byte(data), &tables); err != nil {
  328. return nil, 0, fmt.Errorf("failed to parse catalog: %w", err)
  329. }
  330. return tables, lsn, nil
  331. }
  332. // indexListDurable returns the committed index catalog and its LSN.
  333. func (m *SchemaManager) indexListDurable() ([]string, uint64, error) {
  334. data, lsn, found, err := m.readDurable(m.indexListKey())
  335. if err != nil {
  336. return nil, 0, err
  337. }
  338. if !found {
  339. return nil, lsn, nil
  340. }
  341. var indexes []string
  342. if err := json.Unmarshal([]byte(data), &indexes); err != nil {
  343. return nil, 0, fmt.Errorf("failed to parse index list: %w", err)
  344. }
  345. return indexes, lsn, nil
  346. }
  347. // invalidateCommittedDDL publishes a committed transactional DDL change to
  348. // readers: every cache derived from the catalog is dropped and the version is
  349. // bumped so the foreign-key metadata and session analyzers reload. The caches
  350. // are rebuilt lazily from the durable catalog, which the commit already wrote.
  351. //
  352. // Only the touched tables lose their ROWID allocator. Wiping every allocator
  353. // would let a concurrent transaction that staged a TEXT/composite-key insert
  354. // (whose ROWID is not durable yet) derive the same ROWID again and publish a
  355. // duplicate, so untouched tables keep their derived counters.
  356. func (m *SchemaManager) invalidateCommittedDDL(tables []string) {
  357. m.mu.Lock()
  358. defer m.mu.Unlock()
  359. m.cache = make(map[string]*Schema)
  360. m.indexCache = make(map[string]*Index)
  361. m.indexListCache = nil
  362. m.indexListCached = false
  363. m.rowIDMu.Lock()
  364. for _, table := range tables {
  365. delete(m.rowIDAlloc, strings.ToLower(table))
  366. }
  367. m.rowIDMu.Unlock()
  368. m.fkCatalogMu.Lock()
  369. m.fkCatalog = nil
  370. m.fkCatalogMu.Unlock()
  371. m.bumpVersionLocked()
  372. }
  373. // CreateTable creates a new table.
  374. func (m *SchemaManager) CreateTable(schema *Schema) error {
  375. m.mu.Lock()
  376. defer m.mu.Unlock()
  377. // Check if table already exists
  378. key := m.schemaKey(schema.Name)
  379. err := m.pool.WithClient(func(c *KVClient) error {
  380. _, err := c.Read(key)
  381. return err
  382. })
  383. if err == nil {
  384. return fmt.Errorf("table already exists: %s", schema.Name)
  385. }
  386. // Keep the cached schema private so callers cannot mutate a published
  387. // catalog snapshot after this operation returns.
  388. schema = cloneSchema(schema)
  389. schema.CreatedAt = time.Now()
  390. // Determine the primary key if not set. A schema with no declared key uses
  391. // the synthetic _rowid_ so user columns remain unconstrained and can hold
  392. // duplicate or NULL values.
  393. schema.normalizePrimaryKey()
  394. // Serialize schema
  395. data, err := json.Marshal(schema)
  396. if err != nil {
  397. return fmt.Errorf("failed to serialize schema: %w", err)
  398. }
  399. // Write schema
  400. err = m.pool.WithClient(func(c *KVClient) error {
  401. return c.Write(key, string(data))
  402. })
  403. if err != nil {
  404. return fmt.Errorf("failed to write schema: %w", err)
  405. }
  406. // Update catalog
  407. if err := m.addToCatalog(schema.Name); err != nil {
  408. // Rollback schema write
  409. m.pool.WithClient(func(c *KVClient) error {
  410. return c.Delete(key)
  411. })
  412. return err
  413. }
  414. // Update cache
  415. m.cache[strings.ToLower(schema.Name)] = schema
  416. m.bumpVersionLocked()
  417. return nil
  418. }
  419. // DropTable drops a table.
  420. func (m *SchemaManager) DropTable(name string) error {
  421. tableLock := m.tableLock(name)
  422. tableLock.Lock()
  423. defer tableLock.Unlock()
  424. m.mu.Lock()
  425. defer m.mu.Unlock()
  426. key := m.schemaKey(name)
  427. // Check if table exists
  428. err := m.pool.WithClient(func(c *KVClient) error {
  429. _, err := c.Read(key)
  430. return err
  431. })
  432. if err != nil {
  433. return fmt.Errorf("table not found: %s", name)
  434. }
  435. // Delete all rows by scanning their actual keys and batch-deleting them, so
  436. // a table drop no longer leaks durable rows.
  437. if err := m.deleteKeysWithPrefix([]byte(fmt.Sprintf("%s:_data:%s:", m.database, strings.ToLower(name)))); err != nil {
  438. return err
  439. }
  440. // Delete schema
  441. err = m.pool.WithClient(func(c *KVClient) error {
  442. return c.Delete(key)
  443. })
  444. if err != nil {
  445. return fmt.Errorf("failed to delete schema: %w", err)
  446. }
  447. // Delete ROWID state.
  448. m.pool.WithClient(func(c *KVClient) error {
  449. return c.Delete(m.rowIDKey(name))
  450. })
  451. // Update catalog
  452. if err := m.removeFromCatalog(name); err != nil {
  453. return err
  454. }
  455. // Update cache
  456. tableLower := strings.ToLower(name)
  457. delete(m.cache, tableLower)
  458. m.rowIDMu.Lock()
  459. delete(m.rowIDAlloc, tableLower)
  460. m.rowIDMu.Unlock()
  461. m.bumpVersionLocked()
  462. return nil
  463. }
  464. // GetSchema retrieves a table schema.
  465. func (m *SchemaManager) GetSchema(name string) (*Schema, error) {
  466. m.mu.RLock()
  467. if schema, ok := m.cache[strings.ToLower(name)]; ok {
  468. // Clone while still holding the read lock: the cached schema's
  469. // NextRowID field is mutated under the write lock, so cloning outside
  470. // the lock races with that mutation.
  471. cloned := cloneSchema(schema)
  472. m.mu.RUnlock()
  473. return cloned, nil
  474. }
  475. m.mu.RUnlock()
  476. m.mu.Lock()
  477. defer m.mu.Unlock()
  478. // Double-check after acquiring write lock
  479. if schema, ok := m.cache[strings.ToLower(name)]; ok {
  480. return cloneSchema(schema), nil
  481. }
  482. key := m.schemaKey(name)
  483. var data string
  484. err := m.pool.WithClient(func(c *KVClient) error {
  485. var err error
  486. data, err = c.Read(key)
  487. return err
  488. })
  489. if err != nil {
  490. if err == ErrKeyNotFound {
  491. return nil, fmt.Errorf("table not found: %s", name)
  492. }
  493. return nil, err
  494. }
  495. var schema Schema
  496. if err := json.Unmarshal([]byte(data), &schema); err != nil {
  497. return nil, fmt.Errorf("failed to parse schema: %w", err)
  498. }
  499. schema.normalizePrimaryKey()
  500. m.cache[strings.ToLower(name)] = &schema
  501. return cloneSchema(&schema), nil
  502. }
  503. func cloneSchema(schema *Schema) *Schema {
  504. if schema == nil {
  505. return nil
  506. }
  507. cloned := *schema
  508. cloned.Columns = append([]Column(nil), schema.Columns...)
  509. cloned.Checks = append([]CheckConstraint(nil), schema.Checks...)
  510. cloned.PrimaryKeyColumns = append([]string(nil), schema.PrimaryKeyColumns...)
  511. if schema.ForeignKeys != nil {
  512. cloned.ForeignKeys = make([]ForeignKeyConstraint, len(schema.ForeignKeys))
  513. for i, fk := range schema.ForeignKeys {
  514. cloned.ForeignKeys[i] = ForeignKeyConstraint{
  515. Name: fk.Name,
  516. Columns: append([]string(nil), fk.Columns...),
  517. RefTable: fk.RefTable,
  518. RefColumns: append([]string(nil), fk.RefColumns...),
  519. OnDelete: fk.OnDelete,
  520. OnUpdate: fk.OnUpdate,
  521. }
  522. }
  523. }
  524. return &cloned
  525. }
  526. func cloneIndex(index *Index) *Index {
  527. if index == nil {
  528. return nil
  529. }
  530. cloned := *index
  531. cloned.Columns = append([]IndexColumn(nil), index.Columns...)
  532. return &cloned
  533. }
  534. // TableExists checks if a table exists.
  535. func (m *SchemaManager) TableExists(name string) bool {
  536. _, err := m.GetSchema(name)
  537. return err == nil
  538. }
  539. // ListTables returns all table names.
  540. func (m *SchemaManager) ListTables() ([]string, error) {
  541. var data string
  542. err := m.pool.WithClient(func(c *KVClient) error {
  543. var err error
  544. data, err = c.Read(m.catalogKey())
  545. return err
  546. })
  547. if err != nil {
  548. if err == ErrKeyNotFound {
  549. return nil, nil
  550. }
  551. return nil, err
  552. }
  553. var tables []string
  554. if err := json.Unmarshal([]byte(data), &tables); err != nil {
  555. return nil, fmt.Errorf("failed to parse catalog: %w", err)
  556. }
  557. return tables, nil
  558. }
  559. // addToCatalog adds a table to the catalog.
  560. func (m *SchemaManager) addToCatalog(name string) error {
  561. tables, err := m.ListTables()
  562. if err != nil && err != ErrKeyNotFound {
  563. return err
  564. }
  565. // Check if already exists
  566. lowerName := strings.ToLower(name)
  567. for _, t := range tables {
  568. if strings.ToLower(t) == lowerName {
  569. return nil
  570. }
  571. }
  572. tables = append(tables, name)
  573. data, err := json.Marshal(tables)
  574. if err != nil {
  575. return err
  576. }
  577. return m.pool.WithClient(func(c *KVClient) error {
  578. return c.Write(m.catalogKey(), string(data))
  579. })
  580. }
  581. // removeFromCatalog removes a table from the catalog.
  582. func (m *SchemaManager) removeFromCatalog(name string) error {
  583. tables, err := m.ListTables()
  584. if err != nil {
  585. return err
  586. }
  587. lowerName := strings.ToLower(name)
  588. newTables := make([]string, 0, len(tables))
  589. for _, t := range tables {
  590. if strings.ToLower(t) != lowerName {
  591. newTables = append(newTables, t)
  592. }
  593. }
  594. data, err := json.Marshal(newTables)
  595. if err != nil {
  596. return err
  597. }
  598. return m.pool.WithClient(func(c *KVClient) error {
  599. return c.Write(m.catalogKey(), string(data))
  600. })
  601. }
  602. // InvalidateCache clears the cache for a table.
  603. func (m *SchemaManager) InvalidateCache(name string) {
  604. m.mu.Lock()
  605. defer m.mu.Unlock()
  606. tableLower := strings.ToLower(name)
  607. delete(m.cache, tableLower)
  608. m.rowIDMu.Lock()
  609. delete(m.rowIDAlloc, tableLower)
  610. m.rowIDMu.Unlock()
  611. }
  612. // ToAnalyzerTableInfo converts a Schema to analyzer.TableInfo.
  613. func (s *Schema) ToAnalyzerTableInfo() *analyzer.TableInfo {
  614. info := &analyzer.TableInfo{
  615. Name: s.Name,
  616. }
  617. for _, col := range s.Columns {
  618. info.Columns = append(info.Columns, analyzer.ColumnInfo{
  619. Name: col.Name,
  620. Type: analyzer.TypeFromName(col.Type),
  621. Nullable: col.Nullable,
  622. PrimaryKey: col.PrimaryKey,
  623. TableName: s.Name,
  624. Generated: col.GeneratedExpr != "",
  625. })
  626. }
  627. return info
  628. }
  629. // normalizePrimaryKey derives the ordered primary-key column list from the
  630. // legacy single-column field or from the columns marked PRIMARY KEY, and keeps
  631. // both representations consistent. Primary-key columns are implicitly NOT NULL.
  632. func (s *Schema) normalizePrimaryKey() {
  633. if len(s.PrimaryKeyColumns) == 0 {
  634. if s.PrimaryKey != "" {
  635. s.PrimaryKeyColumns = []string{s.PrimaryKey}
  636. } else {
  637. for _, col := range s.Columns {
  638. if col.PrimaryKey {
  639. s.PrimaryKeyColumns = append(s.PrimaryKeyColumns, col.Name)
  640. }
  641. }
  642. if len(s.PrimaryKeyColumns) == 0 {
  643. s.PrimaryKeyColumns = []string{"_rowid_"}
  644. }
  645. }
  646. }
  647. if len(s.PrimaryKeyColumns) == 1 {
  648. // A single-column key keeps the historical catalog shape: only the
  649. // scalar field, so existing schema bytes stay unchanged.
  650. s.PrimaryKey = s.PrimaryKeyColumns[0]
  651. s.PrimaryKeyColumns = nil
  652. } else {
  653. s.PrimaryKey = ""
  654. }
  655. for i := range s.Columns {
  656. if s.IsPrimaryKeyColumn(s.Columns[i].Name) {
  657. s.Columns[i].PrimaryKey = true
  658. s.Columns[i].Nullable = false
  659. }
  660. }
  661. }
  662. // PrimaryKeyList returns the ordered primary-key columns. A schema always has
  663. // at least one: an explicit key, or the synthetic _rowid_.
  664. func (s *Schema) PrimaryKeyList() []string {
  665. if len(s.PrimaryKeyColumns) > 0 {
  666. return append([]string(nil), s.PrimaryKeyColumns...)
  667. }
  668. if s.PrimaryKey != "" {
  669. return []string{s.PrimaryKey}
  670. }
  671. return []string{"_rowid_"}
  672. }
  673. // UsesSyntheticRowID reports whether the table declares no primary key, so rows
  674. // are stored under the engine-assigned ROWID.
  675. func (s *Schema) UsesSyntheticRowID() bool {
  676. columns := s.PrimaryKeyList()
  677. return len(columns) == 1 && strings.EqualFold(columns[0], "_rowid_")
  678. }
  679. // IsCompositePrimaryKey reports whether the primary key spans more than one
  680. // column.
  681. func (s *Schema) IsCompositePrimaryKey() bool {
  682. return len(s.PrimaryKeyList()) > 1
  683. }
  684. // IsSingleColumnPrimaryKey reports whether the primary key is exactly one
  685. // column, which is the only shape a durable point lookup may address with one
  686. // value.
  687. func (s *Schema) IsSingleColumnPrimaryKey() bool {
  688. return len(s.PrimaryKeyList()) == 1
  689. }
  690. // IsPrimaryKeyColumn reports whether name is one of the primary-key columns.
  691. func (s *Schema) IsPrimaryKeyColumn(name string) bool {
  692. for _, column := range s.PrimaryKeyList() {
  693. if strings.EqualFold(column, name) {
  694. return true
  695. }
  696. }
  697. return false
  698. }
  699. // PrimaryKeyTargetMatches reports whether columns names the complete
  700. // primary-key column set. Order is not significant: ON CONFLICT (b, a) on a
  701. // PRIMARY KEY (a, b) addresses the same key, because the encoded key is built
  702. // in declaration order either way. SQLite instead requires the declaration
  703. // order of a constraint; this engine already accepts arbitrary conflict-target
  704. // lists, so it matches the set rather than raising.
  705. func (s *Schema) PrimaryKeyTargetMatches(columns []string) bool {
  706. primary := s.PrimaryKeyList()
  707. if len(primary) != len(columns) {
  708. return false
  709. }
  710. for _, column := range primary {
  711. found := false
  712. for _, candidate := range columns {
  713. if strings.EqualFold(column, candidate) {
  714. found = true
  715. break
  716. }
  717. }
  718. if !found {
  719. return false
  720. }
  721. }
  722. return true
  723. }
  724. // PrimaryKeyForRow returns the row's primary key encoded the way the table's
  725. // durable row keys encode it. A single-column key keeps the historical
  726. // "<value>" form, so existing databases are read and written unchanged. A
  727. // composite key length-prefixes each component ("<len>:<value>" repeated), a
  728. // prefix-free encoding, so ("ab","c") and ("a","bc") cannot collide. A NULL or
  729. // missing component is a NOT NULL violation: no rowid is auto-assigned.
  730. func (s *Schema) PrimaryKeyForRow(row Row) (string, error) {
  731. columns := s.PrimaryKeyList()
  732. parts := make([]string, len(columns))
  733. for i, column := range columns {
  734. value, ok := rowColumnValue(row, column)
  735. if !ok || value == nil {
  736. return "", &NotNullViolationError{Table: s.Name, Column: column}
  737. }
  738. parts[i] = fmt.Sprintf("%v", value)
  739. }
  740. if len(parts) == 1 {
  741. return parts[0], nil
  742. }
  743. var b strings.Builder
  744. for _, part := range parts {
  745. b.WriteString(strconv.Itoa(len(part)))
  746. b.WriteByte(':')
  747. b.WriteString(part)
  748. }
  749. return b.String(), nil
  750. }
  751. // GetColumn returns a column by name.
  752. func (s *Schema) GetColumn(name string) (*Column, bool) {
  753. lowerName := strings.ToLower(name)
  754. for i := range s.Columns {
  755. if strings.ToLower(s.Columns[i].Name) == lowerName {
  756. return &s.Columns[i], true
  757. }
  758. }
  759. return nil, false
  760. }
  761. // GetNextRowID gets and increments the next ROWID for a table. Allocation is
  762. // serialized per table via the table's own allocator so inserts on different
  763. // tables never contend, and no global SchemaManager lock is held across the
  764. // durable derivation scan.
  765. func (m *SchemaManager) GetNextRowID(table string) (int64, error) {
  766. alloc := m.rowIDAllocatorFor(table)
  767. alloc.mu.Lock()
  768. defer alloc.mu.Unlock()
  769. next, err := m.nextRowIDLocked(alloc, table)
  770. if err != nil {
  771. return 0, err
  772. }
  773. alloc.next = next + 1
  774. return next, nil
  775. }
  776. // UpdateMaxRowID updates the next ROWID if the provided value is higher.
  777. func (m *SchemaManager) UpdateMaxRowID(table string, rowid int64) error {
  778. alloc := m.rowIDAllocatorFor(table)
  779. alloc.mu.Lock()
  780. defer alloc.mu.Unlock()
  781. next, err := m.nextRowIDLocked(alloc, table)
  782. if err != nil {
  783. return err
  784. }
  785. if rowid >= next {
  786. alloc.next = rowid + 1
  787. }
  788. return nil
  789. }
  790. // nextRowIDLocked returns the current next ROWID, deriving it from durable rows
  791. // on first use (must hold the per-table allocator lock).
  792. func (m *SchemaManager) nextRowIDLocked(alloc *rowIDAllocator, table string) (int64, error) {
  793. if alloc.init {
  794. return alloc.next, nil
  795. }
  796. next, err := m.deriveNextRowID(table)
  797. if err != nil {
  798. return 0, err
  799. }
  800. if next < 1 {
  801. next = 1
  802. }
  803. alloc.next = next
  804. alloc.init = true
  805. return alloc.next, nil
  806. }
  807. // deriveNextRowID scans durable row keys to recover max(rowid)+1, streaming
  808. // each page through decodeRow instead of materializing every value.
  809. func (m *SchemaManager) deriveNextRowID(table string) (int64, error) {
  810. prefix := []byte(fmt.Sprintf("%s:_data:%s:", m.database, strings.ToLower(table)))
  811. var maxRowID int64
  812. err := m.pool.WithClient(func(client *KVClient) (retErr error) {
  813. cursor, err := client.Scan(prefix)
  814. if err != nil {
  815. return err
  816. }
  817. defer func() {
  818. if err := cursor.Close(); retErr == nil {
  819. retErr = err
  820. }
  821. }()
  822. for {
  823. entries, done, err := cursor.Next()
  824. if err != nil {
  825. return err
  826. }
  827. for _, e := range entries {
  828. row, err := decodeRow(e.Value)
  829. if err != nil {
  830. return fmt.Errorf("failed to parse row while deriving ROWID: %w", err)
  831. }
  832. if rowid, ok := valueAsInt64(row["_rowid_"]); ok && rowid > maxRowID {
  833. maxRowID = rowid
  834. }
  835. }
  836. if done {
  837. return nil
  838. }
  839. }
  840. })
  841. if err != nil {
  842. return 0, err
  843. }
  844. return maxRowID + 1, nil
  845. }
  846. // deleteKeysWithPrefix streams the keys with the given prefix one page at a
  847. // time and atomically batch-deletes them, so a bulk operation never leaves
  848. // durable rows behind.
  849. func (m *SchemaManager) deleteKeysWithPrefix(prefix []byte) error {
  850. return m.pool.WithClient(func(client *KVClient) (retErr error) {
  851. cursor, err := client.ScanKeys(prefix)
  852. if err != nil {
  853. return err
  854. }
  855. defer func() {
  856. if err := cursor.Close(); retErr == nil {
  857. retErr = err
  858. }
  859. }()
  860. ops := make([]BatchOp, 0, scanPageSize)
  861. batchBytes := 8
  862. flush := func() error {
  863. if len(ops) == 0 {
  864. return nil
  865. }
  866. if _, err := client.BatchWrite(ops, nil); err != nil {
  867. return err
  868. }
  869. ops = ops[:0]
  870. batchBytes = 8
  871. return nil
  872. }
  873. for {
  874. entries, done, err := cursor.Next()
  875. if err != nil {
  876. return err
  877. }
  878. for _, e := range entries {
  879. opBytes := 12 + len(e.Key)
  880. if len(ops) == maxOperations || batchBytes+opBytes > bulkBatchByteBudget {
  881. if err := flush(); err != nil {
  882. return err
  883. }
  884. }
  885. ops = append(ops, BatchOp{Op: batchDelete, Key: append([]byte(nil), e.Key...)})
  886. batchBytes += opBytes
  887. }
  888. if done {
  889. return flush()
  890. }
  891. }
  892. })
  893. }
  894. func valueAsInt64(value interface{}) (int64, bool) {
  895. switch v := value.(type) {
  896. case int64:
  897. return v, true
  898. case int:
  899. return int64(v), true
  900. case float64:
  901. return int64(v), true
  902. default:
  903. return 0, false
  904. }
  905. }
  906. // getSchemaLocked retrieves schema (must hold lock).
  907. func (m *SchemaManager) getSchemaLocked(name string) (*Schema, error) {
  908. if schema, ok := m.cache[strings.ToLower(name)]; ok {
  909. return schema, nil
  910. }
  911. key := m.schemaKey(name)
  912. var data string
  913. err := m.pool.WithClient(func(c *KVClient) error {
  914. var err error
  915. data, err = c.Read(key)
  916. return err
  917. })
  918. if err != nil {
  919. if err == ErrKeyNotFound {
  920. return nil, fmt.Errorf("table not found: %s", name)
  921. }
  922. return nil, err
  923. }
  924. var schema Schema
  925. if err := json.Unmarshal([]byte(data), &schema); err != nil {
  926. return nil, fmt.Errorf("failed to parse schema: %w", err)
  927. }
  928. schema.normalizePrimaryKey()
  929. m.cache[strings.ToLower(name)] = &schema
  930. return &schema, nil
  931. }
  932. // saveSchemaLocked saves schema (must hold lock).
  933. func (m *SchemaManager) saveSchemaLocked(schema *Schema) error {
  934. data, err := json.Marshal(schema)
  935. if err != nil {
  936. return fmt.Errorf("failed to serialize schema: %w", err)
  937. }
  938. key := m.schemaKey(schema.Name)
  939. err = m.pool.WithClient(func(c *KVClient) error {
  940. return c.Write(key, string(data))
  941. })
  942. if err != nil {
  943. return fmt.Errorf("failed to write schema: %w", err)
  944. }
  945. m.cache[strings.ToLower(schema.Name)] = schema
  946. return nil
  947. }
  948. // Index management methods
  949. // indexKey returns the key for an index.
  950. func (m *SchemaManager) indexKey(name string) string {
  951. return fmt.Sprintf("%s:index:%s", m.database, strings.ToLower(name))
  952. }
  953. // indexListKey returns the key for the index list.
  954. func (m *SchemaManager) indexListKey() string {
  955. return fmt.Sprintf("%s:indexes", m.database)
  956. }
  957. // CreateIndex creates a new index.
  958. func (m *SchemaManager) CreateIndex(index *Index) error {
  959. m.mu.Lock()
  960. defer m.mu.Unlock()
  961. // Check if index already exists
  962. key := m.indexKey(index.Name)
  963. err := m.pool.WithClient(func(c *KVClient) error {
  964. _, err := c.Read(key)
  965. return err
  966. })
  967. if err == nil {
  968. return fmt.Errorf("index already exists: %s", index.Name)
  969. }
  970. // Verify table exists
  971. if _, err := m.getSchemaLocked(index.Table); err != nil {
  972. return fmt.Errorf("table not found: %s", index.Table)
  973. }
  974. // Save index
  975. index.CreatedAt = time.Now()
  976. data, err := json.Marshal(index)
  977. if err != nil {
  978. return fmt.Errorf("failed to serialize index: %w", err)
  979. }
  980. err = m.pool.WithClient(func(c *KVClient) error {
  981. return c.Write(key, string(data))
  982. })
  983. if err != nil {
  984. return fmt.Errorf("failed to write index: %w", err)
  985. }
  986. // Add to index list
  987. if err := m.addToIndexList(index.Name); err != nil {
  988. return err
  989. }
  990. m.indexCache[strings.ToLower(index.Name)] = cloneIndex(index)
  991. m.bumpVersionLocked()
  992. return nil
  993. }
  994. // DropIndex drops an index.
  995. func (m *SchemaManager) DropIndex(name string) error {
  996. m.mu.Lock()
  997. defer m.mu.Unlock()
  998. key := m.indexKey(name)
  999. err := m.pool.WithClient(func(c *KVClient) error {
  1000. return c.Delete(key)
  1001. })
  1002. if err != nil {
  1003. return fmt.Errorf("failed to delete index: %w", err)
  1004. }
  1005. if err := m.removeFromIndexList(name); err != nil {
  1006. return err
  1007. }
  1008. delete(m.indexCache, strings.ToLower(name))
  1009. m.bumpVersionLocked()
  1010. return nil
  1011. }
  1012. // IndexExists checks if an index exists.
  1013. func (m *SchemaManager) IndexExists(name string) bool {
  1014. m.mu.RLock()
  1015. defer m.mu.RUnlock()
  1016. return m.indexExistsUnsafe(name)
  1017. }
  1018. // indexExistsUnsafe reports whether an index definition is durable (m.mu must be
  1019. // held).
  1020. func (m *SchemaManager) indexExistsUnsafe(name string) bool {
  1021. if _, ok := m.indexCache[strings.ToLower(name)]; ok {
  1022. return true
  1023. }
  1024. key := m.indexKey(name)
  1025. err := m.pool.WithClient(func(c *KVClient) error {
  1026. _, err := c.Read(key)
  1027. return err
  1028. })
  1029. return err == nil
  1030. }
  1031. // GetIndex retrieves an index by name.
  1032. func (m *SchemaManager) GetIndex(name string) (*Index, error) {
  1033. m.mu.Lock()
  1034. defer m.mu.Unlock()
  1035. cacheKey := strings.ToLower(name)
  1036. if index, ok := m.indexCache[cacheKey]; ok {
  1037. return cloneIndex(index), nil
  1038. }
  1039. key := m.indexKey(name)
  1040. var data string
  1041. err := m.pool.WithClient(func(c *KVClient) error {
  1042. var err error
  1043. data, err = c.Read(key)
  1044. return err
  1045. })
  1046. if err != nil {
  1047. if err == ErrKeyNotFound {
  1048. return nil, fmt.Errorf("%w: %s", ErrIndexNotFound, name)
  1049. }
  1050. return nil, err
  1051. }
  1052. var index Index
  1053. if err := json.Unmarshal([]byte(data), &index); err != nil {
  1054. return nil, fmt.Errorf("failed to parse index: %w", err)
  1055. }
  1056. m.indexCache[cacheKey] = &index
  1057. return cloneIndex(&index), nil
  1058. }
  1059. // ListIndexes returns all index names.
  1060. func (m *SchemaManager) ListIndexes() ([]string, error) {
  1061. m.mu.Lock()
  1062. defer m.mu.Unlock()
  1063. return m.indexListUnsafe()
  1064. }
  1065. // indexListUnsafe returns all index names, loading them from storage on first
  1066. // use (m.mu must be held).
  1067. func (m *SchemaManager) indexListUnsafe() ([]string, error) {
  1068. if m.indexListCached {
  1069. return append([]string(nil), m.indexListCache...), nil
  1070. }
  1071. key := m.indexListKey()
  1072. var data string
  1073. err := m.pool.WithClient(func(c *KVClient) error {
  1074. var err error
  1075. data, err = c.Read(key)
  1076. return err
  1077. })
  1078. if err == ErrKeyNotFound {
  1079. m.indexListCache = nil
  1080. m.indexListCached = true
  1081. return []string{}, nil
  1082. }
  1083. if err != nil {
  1084. return nil, err
  1085. }
  1086. var indexes []string
  1087. if err := json.Unmarshal([]byte(data), &indexes); err != nil {
  1088. return nil, fmt.Errorf("failed to parse index list: %w", err)
  1089. }
  1090. m.indexListCache = append([]string(nil), indexes...)
  1091. m.indexListCached = true
  1092. return indexes, nil
  1093. }
  1094. // ListTableIndexes returns all indexes for a table.
  1095. func (m *SchemaManager) ListTableIndexes(table string) ([]*Index, error) {
  1096. indexes, err := m.ListIndexes()
  1097. if err != nil {
  1098. return nil, err
  1099. }
  1100. var result []*Index
  1101. for _, name := range indexes {
  1102. idx, err := m.GetIndex(name)
  1103. if err != nil {
  1104. // An index dropped concurrently is simply absent; any other error
  1105. // (storage/IO or corrupt data) must not be silently swallowed.
  1106. if errors.Is(err, ErrIndexNotFound) {
  1107. continue
  1108. }
  1109. return nil, err
  1110. }
  1111. if strings.EqualFold(idx.Table, table) {
  1112. result = append(result, idx)
  1113. }
  1114. }
  1115. return result, nil
  1116. }
  1117. // addToIndexList adds an index name to the list.
  1118. func (m *SchemaManager) addToIndexList(name string) error {
  1119. key := m.indexListKey()
  1120. var indexes []string
  1121. var data string
  1122. err := m.pool.WithClient(func(c *KVClient) error {
  1123. var err error
  1124. data, err = c.Read(key)
  1125. return err
  1126. })
  1127. if err == nil {
  1128. json.Unmarshal([]byte(data), &indexes)
  1129. }
  1130. indexes = append(indexes, name)
  1131. newData, _ := json.Marshal(indexes)
  1132. err = m.pool.WithClient(func(c *KVClient) error {
  1133. return c.Write(key, string(newData))
  1134. })
  1135. if err == nil {
  1136. m.indexListCache = append([]string(nil), indexes...)
  1137. m.indexListCached = true
  1138. }
  1139. return err
  1140. }
  1141. // removeFromIndexList removes an index name from the list.
  1142. func (m *SchemaManager) removeFromIndexList(name string) error {
  1143. key := m.indexListKey()
  1144. var indexes []string
  1145. var data string
  1146. err := m.pool.WithClient(func(c *KVClient) error {
  1147. var err error
  1148. data, err = c.Read(key)
  1149. return err
  1150. })
  1151. if err != nil {
  1152. return nil
  1153. }
  1154. json.Unmarshal([]byte(data), &indexes)
  1155. var newIndexes []string
  1156. for _, idx := range indexes {
  1157. if !strings.EqualFold(idx, name) {
  1158. newIndexes = append(newIndexes, idx)
  1159. }
  1160. }
  1161. newData, _ := json.Marshal(newIndexes)
  1162. err = m.pool.WithClient(func(c *KVClient) error {
  1163. return c.Write(key, string(newData))
  1164. })
  1165. if err == nil {
  1166. m.indexListCache = append([]string(nil), newIndexes...)
  1167. m.indexListCached = true
  1168. }
  1169. return err
  1170. }
  1171. // AddColumn adds a new column to a table. When unique is non-nil the column's
  1172. // UNIQUE index is registered as part of the same statement, and checks are
  1173. // persisted with the column in the same schema snapshot.
  1174. //
  1175. // A DEFAULT is materialized into every existing row before the schema is
  1176. // published. NOT NULL, UNIQUE, CHECK, and index validation all read persisted
  1177. // row values, so a default that only existed in the catalog would be invisible
  1178. // to them: a UNIQUE index over the new column could not see the duplicate
  1179. // values every existing row shares, a CHECK predicate could not see a violating
  1180. // default, and an UPDATE to NULL would look non-null. The rewrite is the same
  1181. // shape as DropColumn: the table's exclusive gate keeps point writes and CAS
  1182. // commits out while rows are scanned, validated, and rewritten, the transform
  1183. // is idempotent, and the schema is saved last so an interrupted operation
  1184. // resumes against the old schema.
  1185. //
  1186. // The gate also makes the validation authoritative: NOT NULL without a default,
  1187. // duplicate UNIQUE values, and rows a new CHECK predicate rejects are rejected
  1188. // before the column, its checks, and its index are published, and they are
  1189. // published together in one batch write so a crash cannot leave the column
  1190. // visible without the constraints that validate it. A constant default is
  1191. // deterministic, so its UNIQUE prediction can run as a cheap pre-scan. A
  1192. // clock-reading default is evaluated at one instant sampled for this ALTER
  1193. // statement, and that instant is passed to every validation scan and to the
  1194. // rewrite, so a clock crossing between them cannot publish a row that violates
  1195. // the constraint the statement adds; the UNIQUE duplicate set is still
  1196. // collected during the rewrite so it covers exactly the values the index will
  1197. // see. A nullable column without a default needs no rewrite because a missing
  1198. // row field already reads as NULL. Like DROP COLUMN, the statement holds the
  1199. // catalog lock for the whole scan, so the cost is one table scan per validating
  1200. // constraint plus a table-wide stall per ADD COLUMN that has a default, an
  1201. // index, or a check.
  1202. func (m *SchemaManager) AddColumn(table string, column Column, checks []CheckConstraint, unique *Index) error {
  1203. tableLock := m.tableLock(table)
  1204. tableLock.Lock()
  1205. defer tableLock.Unlock()
  1206. m.mu.Lock()
  1207. defer m.mu.Unlock()
  1208. // One instant for the whole ALTER statement: every validation scan and the
  1209. // row rewrite must observe the same value, or a clock crossing between them
  1210. // could publish rows that violate the CHECK or UNIQUE constraint this
  1211. // statement adds.
  1212. now := m.defaultNow()
  1213. schema, err := m.getSchemaUnsafe(table)
  1214. if err != nil {
  1215. return err
  1216. }
  1217. if err := checkColumnAbsent(schema, column.Name); err != nil {
  1218. return err
  1219. }
  1220. if unique != nil {
  1221. if !strings.EqualFold(unique.Table, schema.Name) {
  1222. return fmt.Errorf("index %s belongs to table %s, not %s", unique.Name, unique.Table, schema.Name)
  1223. }
  1224. if m.indexExistsUnsafe(unique.Name) {
  1225. return fmt.Errorf("index already exists: %s", unique.Name)
  1226. }
  1227. // A clock-reading default is validated during the rewrite below so the
  1228. // duplicate set covers exactly the values this statement writes. The
  1229. // pre-scan is kept for constant and absent defaults, which are
  1230. // deterministic.
  1231. if column.DefaultExpr == "" {
  1232. if err := m.validateAddColumnUnique(table, column, unique, now); err != nil {
  1233. return err
  1234. }
  1235. }
  1236. }
  1237. if err := m.validateAddColumnChecks(table, column, checks, now); err != nil {
  1238. return err
  1239. }
  1240. // A required column with nothing to write into rows that already exist
  1241. // cannot be declared on a populated table.
  1242. if !column.Nullable && !columnHasDefault(column) {
  1243. hasRows, err := m.tableHasRows(table)
  1244. if err != nil {
  1245. return err
  1246. }
  1247. if hasRows {
  1248. return &NotNullViolationError{Table: schema.Name, Column: column.Name}
  1249. }
  1250. }
  1251. // An expression default is evaluated at the ALTER statement's instant. When
  1252. // the column is also UNIQUE, collect the values this rewrite writes and
  1253. // abort on a duplicate, so the index is validated against exactly the values
  1254. // it will see.
  1255. var seen map[string]struct{}
  1256. if unique != nil && column.DefaultExpr != "" {
  1257. seen = make(map[string]struct{})
  1258. }
  1259. if columnHasDefault(column) {
  1260. if err := m.applyRows(table, func(row Row) (bool, error) {
  1261. value, present := rowColumnValue(row, column.Name)
  1262. if !present {
  1263. var err error
  1264. value, _, err = m.columnDefaultValue(column, now)
  1265. if err != nil {
  1266. return false, err
  1267. }
  1268. row[column.Name] = value
  1269. }
  1270. if seen != nil {
  1271. if value == nil {
  1272. return !present, nil
  1273. }
  1274. encoded := encodeUniqueScalar(value)
  1275. if _, duplicate := seen[encoded]; duplicate {
  1276. return false, &UniqueViolationError{Constraint: unique.Name}
  1277. }
  1278. seen[encoded] = struct{}{}
  1279. }
  1280. return !present, nil
  1281. }); err != nil {
  1282. return err
  1283. }
  1284. }
  1285. return m.publishAddedColumn(schema, column, checks, unique)
  1286. }
  1287. // publishAddedColumn publishes a column, its checks, and, when present, its
  1288. // unique index in one atomic batch write (m.mu must be held): the schema
  1289. // snapshot, the index definition, and the index list always become durable
  1290. // together or not at all.
  1291. func (m *SchemaManager) publishAddedColumn(schema *Schema, column Column, checks []CheckConstraint, unique *Index) error {
  1292. // Publish a fresh snapshot instead of mutating readers' shared pointer.
  1293. schema = cloneSchema(schema)
  1294. schema.Columns = append(schema.Columns, column)
  1295. schema.Checks = append(schema.Checks, checks...)
  1296. schemaData, err := json.Marshal(schema)
  1297. if err != nil {
  1298. return fmt.Errorf("failed to serialize schema: %w", err)
  1299. }
  1300. ops := []BatchOp{{Op: batchPut, Key: []byte(m.schemaKey(schema.Name)), Value: schemaData}}
  1301. var indexList []string
  1302. if unique != nil {
  1303. unique.CreatedAt = time.Now()
  1304. indexData, err := json.Marshal(unique)
  1305. if err != nil {
  1306. return fmt.Errorf("failed to serialize index: %w", err)
  1307. }
  1308. indexList, err = m.indexListUnsafe()
  1309. if err != nil {
  1310. return err
  1311. }
  1312. indexList = append(indexList, unique.Name)
  1313. listData, err := json.Marshal(indexList)
  1314. if err != nil {
  1315. return fmt.Errorf("failed to serialize index list: %w", err)
  1316. }
  1317. ops = append(ops,
  1318. BatchOp{Op: batchPut, Key: []byte(m.indexKey(unique.Name)), Value: indexData},
  1319. BatchOp{Op: batchPut, Key: []byte(m.indexListKey()), Value: listData},
  1320. )
  1321. }
  1322. if err := m.pool.WithClient(func(c *KVClient) error {
  1323. _, err := c.BatchWrite(ops, nil)
  1324. return err
  1325. }); err != nil {
  1326. return fmt.Errorf("failed to publish column %s: %w", column.Name, err)
  1327. }
  1328. // Publish in-memory state only after the durable write.
  1329. m.cache[strings.ToLower(schema.Name)] = schema
  1330. if unique != nil {
  1331. m.indexCache[strings.ToLower(unique.Name)] = cloneIndex(unique)
  1332. m.indexListCache = append([]string(nil), indexList...)
  1333. m.indexListCached = true
  1334. }
  1335. m.bumpVersionLocked()
  1336. return nil
  1337. }
  1338. func checkColumnAbsent(schema *Schema, name string) error {
  1339. for _, col := range schema.Columns {
  1340. if strings.EqualFold(col.Name, name) {
  1341. return fmt.Errorf("column already exists: %s", name)
  1342. }
  1343. }
  1344. return nil
  1345. }
  1346. // DropColumn removes a column from a table.
  1347. func (m *SchemaManager) DropColumn(table, columnName string) error {
  1348. tableLock := m.tableLock(table)
  1349. tableLock.Lock()
  1350. defer tableLock.Unlock()
  1351. m.mu.Lock()
  1352. defer m.mu.Unlock()
  1353. schema, err := m.getSchemaUnsafe(table)
  1354. if err != nil {
  1355. return err
  1356. }
  1357. // Cannot drop primary key column
  1358. if schema.IsPrimaryKeyColumn(columnName) {
  1359. return fmt.Errorf("cannot drop primary key column: %s", columnName)
  1360. }
  1361. // Find and remove column
  1362. newColumns := make([]Column, 0, len(schema.Columns)-1)
  1363. found := false
  1364. for _, col := range schema.Columns {
  1365. if strings.EqualFold(col.Name, columnName) {
  1366. found = true
  1367. continue
  1368. }
  1369. newColumns = append(newColumns, col)
  1370. }
  1371. if !found {
  1372. return fmt.Errorf("column not found: %s", columnName)
  1373. }
  1374. schema = cloneSchema(schema)
  1375. schema.Columns = newColumns
  1376. if err := m.rewriteRows(table, func(row Row) bool {
  1377. for key := range row {
  1378. if strings.EqualFold(key, columnName) {
  1379. delete(row, key)
  1380. return true
  1381. }
  1382. }
  1383. return false
  1384. }); err != nil {
  1385. return err
  1386. }
  1387. // Update schema
  1388. return m.updateSchemaUnsafe(schema)
  1389. }
  1390. // RenameTable renames a table.
  1391. func (m *SchemaManager) RenameTable(oldName, newName string) error {
  1392. m.mu.Lock()
  1393. defer m.mu.Unlock()
  1394. // Check if old table exists
  1395. schema, err := m.getSchemaUnsafe(oldName)
  1396. if err != nil {
  1397. return err
  1398. }
  1399. // Check if new table name already exists
  1400. _, err = m.getSchemaUnsafe(newName)
  1401. if err == nil {
  1402. return fmt.Errorf("table already exists: %s", newName)
  1403. }
  1404. schema = cloneSchema(schema)
  1405. // Update schema name
  1406. schema.Name = newName
  1407. // Move durable rows from the old table's data prefix to the new one. Without
  1408. // this, ALTER TABLE ... RENAME leaves every row under the old key and the
  1409. // renamed table appears empty.
  1410. if err := m.renameDataKeys(oldName, newName); err != nil {
  1411. return err
  1412. }
  1413. // Repoint indexes that belonged to the old table.
  1414. if err := m.repointIndexesLocked(oldName, newName); err != nil {
  1415. return err
  1416. }
  1417. // Build the replacement catalog before switching schema names. The schema,
  1418. // catalog, and stale ROWID state change in one batch, so a crash cannot
  1419. // leave neither table name addressable after all row chunks have moved.
  1420. tables, err := m.ListTables()
  1421. if err != nil {
  1422. return err
  1423. }
  1424. foundOld := false
  1425. for i, name := range tables {
  1426. if strings.EqualFold(name, oldName) {
  1427. tables[i] = newName
  1428. foundOld = true
  1429. break
  1430. }
  1431. }
  1432. if !foundOld {
  1433. return fmt.Errorf("table not found in catalog: %s", oldName)
  1434. }
  1435. catalogData, err := json.Marshal(tables)
  1436. if err != nil {
  1437. return err
  1438. }
  1439. schemaData, err := json.Marshal(schema)
  1440. if err != nil {
  1441. return err
  1442. }
  1443. oldKey := m.schemaKey(oldName)
  1444. newKey := m.schemaKey(newName)
  1445. err = m.pool.WithClient(func(c *KVClient) error {
  1446. _, err := c.BatchWrite([]BatchOp{
  1447. {Op: batchPut, Key: []byte(newKey), Value: schemaData},
  1448. {Op: batchDelete, Key: []byte(oldKey)},
  1449. {Op: batchDelete, Key: []byte(m.rowIDKey(oldName))},
  1450. {Op: batchPut, Key: []byte(m.catalogKey()), Value: catalogData},
  1451. }, nil)
  1452. return err
  1453. })
  1454. if err != nil {
  1455. return err
  1456. }
  1457. // Update cache
  1458. oldLower := strings.ToLower(oldName)
  1459. newLower := strings.ToLower(newName)
  1460. delete(m.cache, oldLower)
  1461. m.rowIDMu.Lock()
  1462. delete(m.rowIDAlloc, oldLower)
  1463. m.rowIDMu.Unlock()
  1464. // Update cache
  1465. m.cache[newLower] = schema
  1466. m.bumpVersionLocked()
  1467. return nil
  1468. }
  1469. // renameDataKeys moves every durable row of a table from the old name's data
  1470. // prefix to the new name's prefix in a single atomic batch per page.
  1471. func (m *SchemaManager) renameDataKeys(oldName, newName string) error {
  1472. oldPrefix := []byte(fmt.Sprintf("%s:_data:%s:", m.database, strings.ToLower(oldName)))
  1473. newPrefix := fmt.Sprintf("%s:_data:%s:", m.database, strings.ToLower(newName))
  1474. return m.pool.WithClient(func(client *KVClient) (retErr error) {
  1475. cursor, err := client.Scan(oldPrefix)
  1476. if err != nil {
  1477. return err
  1478. }
  1479. defer func() {
  1480. if cerr := cursor.Close(); retErr == nil {
  1481. retErr = cerr
  1482. }
  1483. }()
  1484. ops := make([]BatchOp, 0, scanPageSize*2)
  1485. batchBytes := 8
  1486. flush := func() error {
  1487. if len(ops) == 0 {
  1488. return nil
  1489. }
  1490. if _, err := client.BatchWrite(ops, nil); err != nil {
  1491. return err
  1492. }
  1493. ops = ops[:0]
  1494. batchBytes = 8
  1495. return nil
  1496. }
  1497. for {
  1498. entries, done, err := cursor.Next()
  1499. if err != nil {
  1500. return err
  1501. }
  1502. for _, e := range entries {
  1503. suffix := string(e.Key[len(oldPrefix):])
  1504. newKey := []byte(newPrefix + suffix)
  1505. opBytes := 24 + len(newKey) + len(e.Value) + len(e.Key)
  1506. if len(ops)+2 > maxOperations || batchBytes+opBytes > bulkBatchByteBudget {
  1507. if err := flush(); err != nil {
  1508. return err
  1509. }
  1510. }
  1511. ops = append(ops,
  1512. BatchOp{Op: batchPut, Key: newKey, Value: e.Value},
  1513. BatchOp{Op: batchDelete, Key: append([]byte(nil), e.Key...)},
  1514. )
  1515. batchBytes += opBytes
  1516. }
  1517. if done {
  1518. return flush()
  1519. }
  1520. }
  1521. })
  1522. }
  1523. // updateIndexesOfTableLocked applies mutate to the durable definition of every
  1524. // index on table and persists the definitions it changed. mutate reports
  1525. // whether it modified the definition; unmodified indexes are left untouched.
  1526. // The changed definitions are written in one batch, so a table's indexes never
  1527. // split between the old and new shape. The caller holds m.mu; it reads the
  1528. // durable index list directly rather than calling the self-locking ListIndexes
  1529. // helper.
  1530. func (m *SchemaManager) updateIndexesOfTableLocked(table string, mutate func(*Index) bool) error {
  1531. var names []string
  1532. if m.indexListCached {
  1533. names = append([]string(nil), m.indexListCache...)
  1534. } else {
  1535. var data string
  1536. err := m.pool.WithClient(func(c *KVClient) error {
  1537. var rerr error
  1538. data, rerr = c.Read(m.indexListKey())
  1539. return rerr
  1540. })
  1541. if err == ErrKeyNotFound {
  1542. return nil
  1543. }
  1544. if err != nil {
  1545. return err
  1546. }
  1547. if err := json.Unmarshal([]byte(data), &names); err != nil {
  1548. return err
  1549. }
  1550. }
  1551. type indexUpdate struct {
  1552. index Index
  1553. key string
  1554. value []byte
  1555. }
  1556. var updates []indexUpdate
  1557. for _, name := range names {
  1558. key := m.indexKey(name)
  1559. var data string
  1560. err := m.pool.WithClient(func(c *KVClient) error {
  1561. var rerr error
  1562. data, rerr = c.Read(key)
  1563. return rerr
  1564. })
  1565. if err != nil {
  1566. if err == ErrKeyNotFound {
  1567. continue
  1568. }
  1569. return err
  1570. }
  1571. var idx Index
  1572. if err := json.Unmarshal([]byte(data), &idx); err != nil {
  1573. return err
  1574. }
  1575. if !strings.EqualFold(idx.Table, table) {
  1576. continue
  1577. }
  1578. if !mutate(&idx) {
  1579. continue
  1580. }
  1581. updated, err := json.Marshal(&idx)
  1582. if err != nil {
  1583. return err
  1584. }
  1585. updates = append(updates, indexUpdate{index: idx, key: key, value: updated})
  1586. }
  1587. if len(updates) == 0 {
  1588. return nil
  1589. }
  1590. ops := make([]BatchOp, 0, len(updates))
  1591. for _, update := range updates {
  1592. ops = append(ops, BatchOp{Op: batchPut, Key: []byte(update.key), Value: update.value})
  1593. }
  1594. if err := m.pool.WithClient(func(c *KVClient) error {
  1595. _, err := c.BatchWrite(ops, nil)
  1596. return err
  1597. }); err != nil {
  1598. return fmt.Errorf("update index definitions for table %s: %w", table, err)
  1599. }
  1600. for _, update := range updates {
  1601. if cached, ok := m.indexCache[strings.ToLower(update.index.Name)]; ok {
  1602. *cached = update.index
  1603. }
  1604. }
  1605. return nil
  1606. }
  1607. // repointIndexesLocked updates indexes whose table was renamed.
  1608. func (m *SchemaManager) repointIndexesLocked(oldName, newName string) error {
  1609. return m.updateIndexesOfTableLocked(oldName, func(idx *Index) bool {
  1610. idx.Table = newName
  1611. return true
  1612. })
  1613. }
  1614. // renameIndexColumnsLocked rewrites the stored definition of every index on
  1615. // table so a reference to oldName becomes newName. Index names stay unchanged:
  1616. // they are independent identifiers that SQLite does not derive from column
  1617. // names, and callers may have chosen them explicitly. A rename to an existing
  1618. // column name is rejected before this runs. Expression columns keep their SQL
  1619. // text; callers must reject a rename that such an expression depends on,
  1620. // because the text is not rewritten.
  1621. func (m *SchemaManager) renameIndexColumnsLocked(table, oldName, newName string) error {
  1622. return m.updateIndexesOfTableLocked(table, func(idx *Index) bool {
  1623. changed := false
  1624. for i := range idx.Columns {
  1625. // Expression columns persist SQL text, not a column reference; their
  1626. // text is left for the caller to drop and recreate the index.
  1627. if idx.Columns[i].Expression == "" && strings.EqualFold(idx.Columns[i].Name, oldName) {
  1628. idx.Columns[i].Name = newName
  1629. changed = true
  1630. }
  1631. }
  1632. return changed
  1633. })
  1634. }
  1635. // RenameColumn renames a column in a table. Column references in index
  1636. // definitions are rewritten; expression-index text is not, so a SQL caller must
  1637. // reject a rename that an expression depends on.
  1638. func (m *SchemaManager) RenameColumn(table, oldName, newName string) error {
  1639. tableLock := m.tableLock(table)
  1640. tableLock.Lock()
  1641. defer tableLock.Unlock()
  1642. m.mu.Lock()
  1643. defer m.mu.Unlock()
  1644. schema, err := m.getSchemaUnsafe(table)
  1645. if err != nil {
  1646. return err
  1647. }
  1648. // Check if new column name already exists
  1649. for _, col := range schema.Columns {
  1650. if strings.EqualFold(col.Name, newName) {
  1651. return fmt.Errorf("column already exists: %s", newName)
  1652. }
  1653. }
  1654. schema = cloneSchema(schema)
  1655. // Find and rename column
  1656. found := false
  1657. for i, col := range schema.Columns {
  1658. if strings.EqualFold(col.Name, oldName) {
  1659. schema.Columns[i].Name = newName
  1660. found = true
  1661. // Update primary key references if needed. Renaming keeps the column
  1662. // order, so a composite key's encoding is unchanged.
  1663. for i, pkColumn := range schema.PrimaryKeyColumns {
  1664. if strings.EqualFold(pkColumn, oldName) {
  1665. schema.PrimaryKeyColumns[i] = newName
  1666. }
  1667. }
  1668. if strings.EqualFold(schema.PrimaryKey, oldName) {
  1669. schema.PrimaryKey = newName
  1670. }
  1671. break
  1672. }
  1673. }
  1674. if !found {
  1675. return fmt.Errorf("column not found: %s", oldName)
  1676. }
  1677. if err := m.rewriteRows(table, func(row Row) bool {
  1678. for key, value := range row {
  1679. if strings.EqualFold(key, oldName) {
  1680. delete(row, key)
  1681. row[newName] = value
  1682. return true
  1683. }
  1684. }
  1685. return false
  1686. }); err != nil {
  1687. return err
  1688. }
  1689. // Publish the renamed schema before the index definitions: reads of the new
  1690. // column name resolve against rewritten rows as soon as the schema lands.
  1691. // Autocommit index DDL is not covered by this write, so a failure here leaves
  1692. // stale index metadata for the caller to drop and recreate; inside a
  1693. // transaction the whole statement is staged and published atomically.
  1694. if err := m.updateSchemaUnsafe(schema); err != nil {
  1695. return err
  1696. }
  1697. // Index metadata drives secondary-index values, UNIQUE enforcement,
  1698. // ON CONFLICT target resolution, and catalog introspection, so it must name
  1699. // the renamed column before any later statement uses the table.
  1700. return m.renameIndexColumnsLocked(table, oldName, newName)
  1701. }
  1702. // rewriteRows applies an idempotent name-keyed row transformation in bounded
  1703. // batches. Column DDL updates the schema only after all rows are rewritten, so
  1704. // an interrupted operation can safely resume against the old schema.
  1705. func (m *SchemaManager) rewriteRows(table string, transform func(Row) bool) error {
  1706. return m.applyRows(table, func(row Row) (bool, error) {
  1707. return transform(row), nil
  1708. })
  1709. }
  1710. // applyRows scans a table once and applies transform to every row. Rows for
  1711. // which transform reports a change are re-encoded and written in bounded
  1712. // batches; an error from transform aborts the scan. Callers hold the table's
  1713. // exclusive gate, so no writer can interleave between the scan and the schema
  1714. // update that follows it. Aborting after a batch was flushed is safe only
  1715. // because the DDL transforms are idempotent.
  1716. func (m *SchemaManager) applyRows(table string, transform func(Row) (bool, error)) error {
  1717. prefix := []byte(fmt.Sprintf("%s:_data:%s:", m.database, strings.ToLower(table)))
  1718. return m.pool.WithClient(func(client *KVClient) (retErr error) {
  1719. cursor, err := client.Scan(prefix)
  1720. if err != nil {
  1721. return err
  1722. }
  1723. defer func() {
  1724. if cerr := cursor.Close(); retErr == nil {
  1725. retErr = cerr
  1726. }
  1727. }()
  1728. ops := make([]BatchOp, 0, scanPageSize)
  1729. batchBytes := 8
  1730. flush := func() error {
  1731. if len(ops) == 0 {
  1732. return nil
  1733. }
  1734. if _, err := client.BatchWrite(ops, nil); err != nil {
  1735. return err
  1736. }
  1737. ops = ops[:0]
  1738. batchBytes = 8
  1739. return nil
  1740. }
  1741. for {
  1742. entries, done, err := cursor.Next()
  1743. if err != nil {
  1744. return err
  1745. }
  1746. for _, entry := range entries {
  1747. row, err := decodeRow(entry.Value)
  1748. if err != nil {
  1749. return err
  1750. }
  1751. changed, err := transform(row)
  1752. if err != nil {
  1753. return err
  1754. }
  1755. if !changed {
  1756. continue
  1757. }
  1758. value, err := encodeRow(row)
  1759. if err != nil {
  1760. return err
  1761. }
  1762. opBytes := 12 + len(entry.Key) + len(value)
  1763. if len(ops) == maxOperations || batchBytes+opBytes > bulkBatchByteBudget {
  1764. if err := flush(); err != nil {
  1765. return err
  1766. }
  1767. }
  1768. ops = append(ops, BatchOp{Op: batchPut, Key: append([]byte(nil), entry.Key...), Value: value})
  1769. batchBytes += opBytes
  1770. }
  1771. if done {
  1772. return flush()
  1773. }
  1774. }
  1775. })
  1776. }
  1777. // validateAddColumnUnique rejects an ADD COLUMN ... UNIQUE whose backfilled
  1778. // values would duplicate across existing rows. It reads rows as they will look
  1779. // after the rewrite (a row that already carries the field from an interrupted
  1780. // earlier attempt keeps its value) and mirrors ValidateUniqueIndex for a single
  1781. // plain column: NULLs do not collide. It is used only for a constant or absent
  1782. // default; a clock-reading default is validated by the rewrite itself because
  1783. // its values cannot be predicted. The caller holds the table's exclusive gate,
  1784. // so no writer can insert a conflicting row between this check and the
  1785. // publication of the index.
  1786. func (m *SchemaManager) validateAddColumnUnique(table string, column Column, index *Index, now time.Time) error {
  1787. seen := make(map[string]struct{})
  1788. return m.applyRows(table, func(row Row) (bool, error) {
  1789. value, ok := rowColumnValue(row, column.Name)
  1790. if !ok {
  1791. var err error
  1792. value, _, err = m.columnDefaultValue(column, now)
  1793. if err != nil {
  1794. return false, err
  1795. }
  1796. }
  1797. if value == nil {
  1798. return false, nil
  1799. }
  1800. encoded := encodeUniqueScalar(value)
  1801. if _, duplicate := seen[encoded]; duplicate {
  1802. return false, &UniqueViolationError{Constraint: index.Name}
  1803. }
  1804. seen[encoded] = struct{}{}
  1805. return false, nil
  1806. })
  1807. }
  1808. // columnHasDefault reports whether the column contributes a value to rows that
  1809. // omit it, whether that default is a stored constant or an expression.
  1810. func columnHasDefault(column Column) bool {
  1811. return column.Default != nil || column.DefaultExpr != ""
  1812. }
  1813. // validateAddColumnChecks rejects an ADD COLUMN ... CHECK whose predicate is
  1814. // already false for an existing row. Like validateAddColumnUnique it reads rows
  1815. // as they will look after the default backfill, and it runs before that rewrite
  1816. // so a rejected DDL leaves no row carrying the new field. The default is
  1817. // resolved through columnDefaultValue at the instant sampled once for the
  1818. // ALTER statement, so a clock-reading default reaches the predicate as the value
  1819. // the backfilled row will receive instead of NULL. The caller holds the table's
  1820. // exclusive gate, so no writer can insert a violating row between this
  1821. // validation and the publication of the check.
  1822. func (m *SchemaManager) validateAddColumnChecks(table string, column Column, checks []CheckConstraint, now time.Time) error {
  1823. if len(checks) == 0 {
  1824. return nil
  1825. }
  1826. eval := m.checkEvaluator()
  1827. if eval == nil {
  1828. return fmt.Errorf("cannot validate CHECK constraints on %s: no CHECK evaluator installed", table)
  1829. }
  1830. return m.applyRows(table, func(row Row) (bool, error) {
  1831. if _, ok := rowColumnValue(row, column.Name); !ok {
  1832. value, hasDefault, err := m.columnDefaultValue(column, now)
  1833. if err != nil {
  1834. return false, err
  1835. }
  1836. if hasDefault {
  1837. row[column.Name] = value
  1838. }
  1839. }
  1840. for _, check := range checks {
  1841. holds, err := eval(check.Expression, row)
  1842. if err != nil {
  1843. return false, fmt.Errorf("evaluate CHECK constraint %s on %s: %w", check.Label(), table, err)
  1844. }
  1845. if !holds {
  1846. return false, &CheckViolationError{Table: table, Constraint: check.Label()}
  1847. }
  1848. }
  1849. return false, nil
  1850. })
  1851. }
  1852. // tableHasRows reports whether a table holds at least one durable row.
  1853. func (m *SchemaManager) tableHasRows(table string) (bool, error) {
  1854. prefix := []byte(fmt.Sprintf("%s:_data:%s:", m.database, strings.ToLower(table)))
  1855. found := false
  1856. err := m.pool.WithClient(func(client *KVClient) (retErr error) {
  1857. cursor, err := client.ScanWithLimit(prefix, 1)
  1858. if err != nil {
  1859. return err
  1860. }
  1861. defer func() {
  1862. if cerr := cursor.Close(); retErr == nil {
  1863. retErr = cerr
  1864. }
  1865. }()
  1866. entries, _, err := cursor.Next()
  1867. if err != nil {
  1868. return err
  1869. }
  1870. found = len(entries) > 0
  1871. return nil
  1872. })
  1873. return found, err
  1874. }
  1875. // getSchemaUnsafe gets a schema without locking (internal use).
  1876. func (m *SchemaManager) getSchemaUnsafe(table string) (*Schema, error) {
  1877. tableLower := strings.ToLower(table)
  1878. // Check cache
  1879. if schema, ok := m.cache[tableLower]; ok {
  1880. return schema, nil
  1881. }
  1882. // Read from storage
  1883. key := m.schemaKey(table)
  1884. var data string
  1885. err := m.pool.WithClient(func(c *KVClient) error {
  1886. var err error
  1887. data, err = c.Read(key)
  1888. return err
  1889. })
  1890. if err != nil {
  1891. return nil, fmt.Errorf("table not found: %s", table)
  1892. }
  1893. var schema Schema
  1894. if err := json.Unmarshal([]byte(data), &schema); err != nil {
  1895. return nil, err
  1896. }
  1897. schema.normalizePrimaryKey()
  1898. m.cache[tableLower] = &schema
  1899. return &schema, nil
  1900. }
  1901. // updateSchemaUnsafe updates a schema without locking (internal use).
  1902. func (m *SchemaManager) updateSchemaUnsafe(schema *Schema) error {
  1903. key := m.schemaKey(schema.Name)
  1904. data, _ := json.Marshal(schema)
  1905. err := m.pool.WithClient(func(c *KVClient) error {
  1906. return c.Write(key, string(data))
  1907. })
  1908. if err != nil {
  1909. return err
  1910. }
  1911. // Update cache
  1912. m.cache[strings.ToLower(schema.Name)] = schema
  1913. m.bumpVersionLocked()
  1914. return nil
  1915. }