schema.go 61 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119
  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. func (m *SchemaManager) invalidateCommittedDDL() {
  352. m.mu.Lock()
  353. defer m.mu.Unlock()
  354. m.cache = make(map[string]*Schema)
  355. m.indexCache = make(map[string]*Index)
  356. m.indexListCache = nil
  357. m.indexListCached = false
  358. m.rowIDMu.Lock()
  359. m.rowIDAlloc = make(map[string]*rowIDAllocator)
  360. m.rowIDMu.Unlock()
  361. m.fkCatalogMu.Lock()
  362. m.fkCatalog = nil
  363. m.fkCatalogMu.Unlock()
  364. m.bumpVersionLocked()
  365. }
  366. // CreateTable creates a new table.
  367. func (m *SchemaManager) CreateTable(schema *Schema) error {
  368. m.mu.Lock()
  369. defer m.mu.Unlock()
  370. // Check if table already exists
  371. key := m.schemaKey(schema.Name)
  372. err := m.pool.WithClient(func(c *KVClient) error {
  373. _, err := c.Read(key)
  374. return err
  375. })
  376. if err == nil {
  377. return fmt.Errorf("table already exists: %s", schema.Name)
  378. }
  379. // Keep the cached schema private so callers cannot mutate a published
  380. // catalog snapshot after this operation returns.
  381. schema = cloneSchema(schema)
  382. schema.CreatedAt = time.Now()
  383. // Determine the primary key if not set. A schema with no declared key uses
  384. // the synthetic _rowid_ so user columns remain unconstrained and can hold
  385. // duplicate or NULL values.
  386. schema.normalizePrimaryKey()
  387. // Serialize schema
  388. data, err := json.Marshal(schema)
  389. if err != nil {
  390. return fmt.Errorf("failed to serialize schema: %w", err)
  391. }
  392. // Write schema
  393. err = m.pool.WithClient(func(c *KVClient) error {
  394. return c.Write(key, string(data))
  395. })
  396. if err != nil {
  397. return fmt.Errorf("failed to write schema: %w", err)
  398. }
  399. // Update catalog
  400. if err := m.addToCatalog(schema.Name); err != nil {
  401. // Rollback schema write
  402. m.pool.WithClient(func(c *KVClient) error {
  403. return c.Delete(key)
  404. })
  405. return err
  406. }
  407. // Update cache
  408. m.cache[strings.ToLower(schema.Name)] = schema
  409. m.bumpVersionLocked()
  410. return nil
  411. }
  412. // DropTable drops a table.
  413. func (m *SchemaManager) DropTable(name string) error {
  414. tableLock := m.tableLock(name)
  415. tableLock.Lock()
  416. defer tableLock.Unlock()
  417. m.mu.Lock()
  418. defer m.mu.Unlock()
  419. key := m.schemaKey(name)
  420. // Check if table exists
  421. err := m.pool.WithClient(func(c *KVClient) error {
  422. _, err := c.Read(key)
  423. return err
  424. })
  425. if err != nil {
  426. return fmt.Errorf("table not found: %s", name)
  427. }
  428. // Delete all rows by scanning their actual keys and batch-deleting them, so
  429. // a table drop no longer leaks durable rows.
  430. if err := m.deleteKeysWithPrefix([]byte(fmt.Sprintf("%s:_data:%s:", m.database, strings.ToLower(name)))); err != nil {
  431. return err
  432. }
  433. // Delete schema
  434. err = m.pool.WithClient(func(c *KVClient) error {
  435. return c.Delete(key)
  436. })
  437. if err != nil {
  438. return fmt.Errorf("failed to delete schema: %w", err)
  439. }
  440. // Delete ROWID state.
  441. m.pool.WithClient(func(c *KVClient) error {
  442. return c.Delete(m.rowIDKey(name))
  443. })
  444. // Update catalog
  445. if err := m.removeFromCatalog(name); err != nil {
  446. return err
  447. }
  448. // Update cache
  449. tableLower := strings.ToLower(name)
  450. delete(m.cache, tableLower)
  451. m.rowIDMu.Lock()
  452. delete(m.rowIDAlloc, tableLower)
  453. m.rowIDMu.Unlock()
  454. m.bumpVersionLocked()
  455. return nil
  456. }
  457. // GetSchema retrieves a table schema.
  458. func (m *SchemaManager) GetSchema(name string) (*Schema, error) {
  459. m.mu.RLock()
  460. if schema, ok := m.cache[strings.ToLower(name)]; ok {
  461. // Clone while still holding the read lock: the cached schema's
  462. // NextRowID field is mutated under the write lock, so cloning outside
  463. // the lock races with that mutation.
  464. cloned := cloneSchema(schema)
  465. m.mu.RUnlock()
  466. return cloned, nil
  467. }
  468. m.mu.RUnlock()
  469. m.mu.Lock()
  470. defer m.mu.Unlock()
  471. // Double-check after acquiring write lock
  472. if schema, ok := m.cache[strings.ToLower(name)]; ok {
  473. return cloneSchema(schema), nil
  474. }
  475. key := m.schemaKey(name)
  476. var data string
  477. err := m.pool.WithClient(func(c *KVClient) error {
  478. var err error
  479. data, err = c.Read(key)
  480. return err
  481. })
  482. if err != nil {
  483. if err == ErrKeyNotFound {
  484. return nil, fmt.Errorf("table not found: %s", name)
  485. }
  486. return nil, err
  487. }
  488. var schema Schema
  489. if err := json.Unmarshal([]byte(data), &schema); err != nil {
  490. return nil, fmt.Errorf("failed to parse schema: %w", err)
  491. }
  492. schema.normalizePrimaryKey()
  493. m.cache[strings.ToLower(name)] = &schema
  494. return cloneSchema(&schema), nil
  495. }
  496. func cloneSchema(schema *Schema) *Schema {
  497. if schema == nil {
  498. return nil
  499. }
  500. cloned := *schema
  501. cloned.Columns = append([]Column(nil), schema.Columns...)
  502. cloned.Checks = append([]CheckConstraint(nil), schema.Checks...)
  503. cloned.PrimaryKeyColumns = append([]string(nil), schema.PrimaryKeyColumns...)
  504. if schema.ForeignKeys != nil {
  505. cloned.ForeignKeys = make([]ForeignKeyConstraint, len(schema.ForeignKeys))
  506. for i, fk := range schema.ForeignKeys {
  507. cloned.ForeignKeys[i] = ForeignKeyConstraint{
  508. Name: fk.Name,
  509. Columns: append([]string(nil), fk.Columns...),
  510. RefTable: fk.RefTable,
  511. RefColumns: append([]string(nil), fk.RefColumns...),
  512. OnDelete: fk.OnDelete,
  513. OnUpdate: fk.OnUpdate,
  514. }
  515. }
  516. }
  517. return &cloned
  518. }
  519. func cloneIndex(index *Index) *Index {
  520. if index == nil {
  521. return nil
  522. }
  523. cloned := *index
  524. cloned.Columns = append([]IndexColumn(nil), index.Columns...)
  525. return &cloned
  526. }
  527. // TableExists checks if a table exists.
  528. func (m *SchemaManager) TableExists(name string) bool {
  529. _, err := m.GetSchema(name)
  530. return err == nil
  531. }
  532. // ListTables returns all table names.
  533. func (m *SchemaManager) ListTables() ([]string, error) {
  534. var data string
  535. err := m.pool.WithClient(func(c *KVClient) error {
  536. var err error
  537. data, err = c.Read(m.catalogKey())
  538. return err
  539. })
  540. if err != nil {
  541. if err == ErrKeyNotFound {
  542. return nil, nil
  543. }
  544. return nil, err
  545. }
  546. var tables []string
  547. if err := json.Unmarshal([]byte(data), &tables); err != nil {
  548. return nil, fmt.Errorf("failed to parse catalog: %w", err)
  549. }
  550. return tables, nil
  551. }
  552. // addToCatalog adds a table to the catalog.
  553. func (m *SchemaManager) addToCatalog(name string) error {
  554. tables, err := m.ListTables()
  555. if err != nil && err != ErrKeyNotFound {
  556. return err
  557. }
  558. // Check if already exists
  559. lowerName := strings.ToLower(name)
  560. for _, t := range tables {
  561. if strings.ToLower(t) == lowerName {
  562. return nil
  563. }
  564. }
  565. tables = append(tables, name)
  566. data, err := json.Marshal(tables)
  567. if err != nil {
  568. return err
  569. }
  570. return m.pool.WithClient(func(c *KVClient) error {
  571. return c.Write(m.catalogKey(), string(data))
  572. })
  573. }
  574. // removeFromCatalog removes a table from the catalog.
  575. func (m *SchemaManager) removeFromCatalog(name string) error {
  576. tables, err := m.ListTables()
  577. if err != nil {
  578. return err
  579. }
  580. lowerName := strings.ToLower(name)
  581. newTables := make([]string, 0, len(tables))
  582. for _, t := range tables {
  583. if strings.ToLower(t) != lowerName {
  584. newTables = append(newTables, t)
  585. }
  586. }
  587. data, err := json.Marshal(newTables)
  588. if err != nil {
  589. return err
  590. }
  591. return m.pool.WithClient(func(c *KVClient) error {
  592. return c.Write(m.catalogKey(), string(data))
  593. })
  594. }
  595. // InvalidateCache clears the cache for a table.
  596. func (m *SchemaManager) InvalidateCache(name string) {
  597. m.mu.Lock()
  598. defer m.mu.Unlock()
  599. tableLower := strings.ToLower(name)
  600. delete(m.cache, tableLower)
  601. m.rowIDMu.Lock()
  602. delete(m.rowIDAlloc, tableLower)
  603. m.rowIDMu.Unlock()
  604. }
  605. // ToAnalyzerTableInfo converts a Schema to analyzer.TableInfo.
  606. func (s *Schema) ToAnalyzerTableInfo() *analyzer.TableInfo {
  607. info := &analyzer.TableInfo{
  608. Name: s.Name,
  609. }
  610. for _, col := range s.Columns {
  611. info.Columns = append(info.Columns, analyzer.ColumnInfo{
  612. Name: col.Name,
  613. Type: analyzer.TypeFromName(col.Type),
  614. Nullable: col.Nullable,
  615. PrimaryKey: col.PrimaryKey,
  616. TableName: s.Name,
  617. Generated: col.GeneratedExpr != "",
  618. })
  619. }
  620. return info
  621. }
  622. // normalizePrimaryKey derives the ordered primary-key column list from the
  623. // legacy single-column field or from the columns marked PRIMARY KEY, and keeps
  624. // both representations consistent. Primary-key columns are implicitly NOT NULL.
  625. func (s *Schema) normalizePrimaryKey() {
  626. if len(s.PrimaryKeyColumns) == 0 {
  627. if s.PrimaryKey != "" {
  628. s.PrimaryKeyColumns = []string{s.PrimaryKey}
  629. } else {
  630. for _, col := range s.Columns {
  631. if col.PrimaryKey {
  632. s.PrimaryKeyColumns = append(s.PrimaryKeyColumns, col.Name)
  633. }
  634. }
  635. if len(s.PrimaryKeyColumns) == 0 {
  636. s.PrimaryKeyColumns = []string{"_rowid_"}
  637. }
  638. }
  639. }
  640. if len(s.PrimaryKeyColumns) == 1 {
  641. // A single-column key keeps the historical catalog shape: only the
  642. // scalar field, so existing schema bytes stay unchanged.
  643. s.PrimaryKey = s.PrimaryKeyColumns[0]
  644. s.PrimaryKeyColumns = nil
  645. } else {
  646. s.PrimaryKey = ""
  647. }
  648. for i := range s.Columns {
  649. if s.IsPrimaryKeyColumn(s.Columns[i].Name) {
  650. s.Columns[i].PrimaryKey = true
  651. s.Columns[i].Nullable = false
  652. }
  653. }
  654. }
  655. // PrimaryKeyList returns the ordered primary-key columns. A schema always has
  656. // at least one: an explicit key, or the synthetic _rowid_.
  657. func (s *Schema) PrimaryKeyList() []string {
  658. if len(s.PrimaryKeyColumns) > 0 {
  659. return append([]string(nil), s.PrimaryKeyColumns...)
  660. }
  661. if s.PrimaryKey != "" {
  662. return []string{s.PrimaryKey}
  663. }
  664. return []string{"_rowid_"}
  665. }
  666. // UsesSyntheticRowID reports whether the table declares no primary key, so rows
  667. // are stored under the engine-assigned ROWID.
  668. func (s *Schema) UsesSyntheticRowID() bool {
  669. columns := s.PrimaryKeyList()
  670. return len(columns) == 1 && strings.EqualFold(columns[0], "_rowid_")
  671. }
  672. // IsCompositePrimaryKey reports whether the primary key spans more than one
  673. // column.
  674. func (s *Schema) IsCompositePrimaryKey() bool {
  675. return len(s.PrimaryKeyList()) > 1
  676. }
  677. // IsSingleColumnPrimaryKey reports whether the primary key is exactly one
  678. // column, which is the only shape a durable point lookup may address with one
  679. // value.
  680. func (s *Schema) IsSingleColumnPrimaryKey() bool {
  681. return len(s.PrimaryKeyList()) == 1
  682. }
  683. // IsPrimaryKeyColumn reports whether name is one of the primary-key columns.
  684. func (s *Schema) IsPrimaryKeyColumn(name string) bool {
  685. for _, column := range s.PrimaryKeyList() {
  686. if strings.EqualFold(column, name) {
  687. return true
  688. }
  689. }
  690. return false
  691. }
  692. // PrimaryKeyTargetMatches reports whether columns names the complete
  693. // primary-key column set. Order is not significant: ON CONFLICT (b, a) on a
  694. // PRIMARY KEY (a, b) addresses the same key, because the encoded key is built
  695. // in declaration order either way. SQLite instead requires the declaration
  696. // order of a constraint; this engine already accepts arbitrary conflict-target
  697. // lists, so it matches the set rather than raising.
  698. func (s *Schema) PrimaryKeyTargetMatches(columns []string) bool {
  699. primary := s.PrimaryKeyList()
  700. if len(primary) != len(columns) {
  701. return false
  702. }
  703. for _, column := range primary {
  704. found := false
  705. for _, candidate := range columns {
  706. if strings.EqualFold(column, candidate) {
  707. found = true
  708. break
  709. }
  710. }
  711. if !found {
  712. return false
  713. }
  714. }
  715. return true
  716. }
  717. // PrimaryKeyForRow returns the row's primary key encoded the way the table's
  718. // durable row keys encode it. A single-column key keeps the historical
  719. // "<value>" form, so existing databases are read and written unchanged. A
  720. // composite key length-prefixes each component ("<len>:<value>" repeated), a
  721. // prefix-free encoding, so ("ab","c") and ("a","bc") cannot collide. A NULL or
  722. // missing component is a NOT NULL violation: no rowid is auto-assigned.
  723. func (s *Schema) PrimaryKeyForRow(row Row) (string, error) {
  724. columns := s.PrimaryKeyList()
  725. parts := make([]string, len(columns))
  726. for i, column := range columns {
  727. value, ok := rowColumnValue(row, column)
  728. if !ok || value == nil {
  729. return "", &NotNullViolationError{Table: s.Name, Column: column}
  730. }
  731. parts[i] = fmt.Sprintf("%v", value)
  732. }
  733. if len(parts) == 1 {
  734. return parts[0], nil
  735. }
  736. var b strings.Builder
  737. for _, part := range parts {
  738. b.WriteString(strconv.Itoa(len(part)))
  739. b.WriteByte(':')
  740. b.WriteString(part)
  741. }
  742. return b.String(), nil
  743. }
  744. // GetColumn returns a column by name.
  745. func (s *Schema) GetColumn(name string) (*Column, bool) {
  746. lowerName := strings.ToLower(name)
  747. for i := range s.Columns {
  748. if strings.ToLower(s.Columns[i].Name) == lowerName {
  749. return &s.Columns[i], true
  750. }
  751. }
  752. return nil, false
  753. }
  754. // GetNextRowID gets and increments the next ROWID for a table. Allocation is
  755. // serialized per table via the table's own allocator so inserts on different
  756. // tables never contend, and no global SchemaManager lock is held across the
  757. // durable derivation scan.
  758. func (m *SchemaManager) GetNextRowID(table string) (int64, error) {
  759. alloc := m.rowIDAllocatorFor(table)
  760. alloc.mu.Lock()
  761. defer alloc.mu.Unlock()
  762. next, err := m.nextRowIDLocked(alloc, table)
  763. if err != nil {
  764. return 0, err
  765. }
  766. alloc.next = next + 1
  767. return next, nil
  768. }
  769. // UpdateMaxRowID updates the next ROWID if the provided value is higher.
  770. func (m *SchemaManager) UpdateMaxRowID(table string, rowid int64) error {
  771. alloc := m.rowIDAllocatorFor(table)
  772. alloc.mu.Lock()
  773. defer alloc.mu.Unlock()
  774. next, err := m.nextRowIDLocked(alloc, table)
  775. if err != nil {
  776. return err
  777. }
  778. if rowid >= next {
  779. alloc.next = rowid + 1
  780. }
  781. return nil
  782. }
  783. // nextRowIDLocked returns the current next ROWID, deriving it from durable rows
  784. // on first use (must hold the per-table allocator lock).
  785. func (m *SchemaManager) nextRowIDLocked(alloc *rowIDAllocator, table string) (int64, error) {
  786. if alloc.init {
  787. return alloc.next, nil
  788. }
  789. next, err := m.deriveNextRowID(table)
  790. if err != nil {
  791. return 0, err
  792. }
  793. if next < 1 {
  794. next = 1
  795. }
  796. alloc.next = next
  797. alloc.init = true
  798. return alloc.next, nil
  799. }
  800. // deriveNextRowID scans durable row keys to recover max(rowid)+1, streaming
  801. // each page through decodeRow instead of materializing every value.
  802. func (m *SchemaManager) deriveNextRowID(table string) (int64, error) {
  803. prefix := []byte(fmt.Sprintf("%s:_data:%s:", m.database, strings.ToLower(table)))
  804. var maxRowID int64
  805. err := m.pool.WithClient(func(client *KVClient) (retErr error) {
  806. cursor, err := client.Scan(prefix)
  807. if err != nil {
  808. return err
  809. }
  810. defer func() {
  811. if err := cursor.Close(); retErr == nil {
  812. retErr = err
  813. }
  814. }()
  815. for {
  816. entries, done, err := cursor.Next()
  817. if err != nil {
  818. return err
  819. }
  820. for _, e := range entries {
  821. row, err := decodeRow(e.Value)
  822. if err != nil {
  823. return fmt.Errorf("failed to parse row while deriving ROWID: %w", err)
  824. }
  825. if rowid, ok := valueAsInt64(row["_rowid_"]); ok && rowid > maxRowID {
  826. maxRowID = rowid
  827. }
  828. }
  829. if done {
  830. return nil
  831. }
  832. }
  833. })
  834. if err != nil {
  835. return 0, err
  836. }
  837. return maxRowID + 1, nil
  838. }
  839. // deleteKeysWithPrefix streams the keys with the given prefix one page at a
  840. // time and atomically batch-deletes them, so a bulk operation never leaves
  841. // durable rows behind.
  842. func (m *SchemaManager) deleteKeysWithPrefix(prefix []byte) error {
  843. return m.pool.WithClient(func(client *KVClient) (retErr error) {
  844. cursor, err := client.ScanKeys(prefix)
  845. if err != nil {
  846. return err
  847. }
  848. defer func() {
  849. if err := cursor.Close(); retErr == nil {
  850. retErr = err
  851. }
  852. }()
  853. ops := make([]BatchOp, 0, scanPageSize)
  854. batchBytes := 8
  855. flush := func() error {
  856. if len(ops) == 0 {
  857. return nil
  858. }
  859. if _, err := client.BatchWrite(ops, nil); err != nil {
  860. return err
  861. }
  862. ops = ops[:0]
  863. batchBytes = 8
  864. return nil
  865. }
  866. for {
  867. entries, done, err := cursor.Next()
  868. if err != nil {
  869. return err
  870. }
  871. for _, e := range entries {
  872. opBytes := 12 + len(e.Key)
  873. if len(ops) == maxOperations || batchBytes+opBytes > bulkBatchByteBudget {
  874. if err := flush(); err != nil {
  875. return err
  876. }
  877. }
  878. ops = append(ops, BatchOp{Op: batchDelete, Key: append([]byte(nil), e.Key...)})
  879. batchBytes += opBytes
  880. }
  881. if done {
  882. return flush()
  883. }
  884. }
  885. })
  886. }
  887. func valueAsInt64(value interface{}) (int64, bool) {
  888. switch v := value.(type) {
  889. case int64:
  890. return v, true
  891. case int:
  892. return int64(v), true
  893. case float64:
  894. return int64(v), true
  895. default:
  896. return 0, false
  897. }
  898. }
  899. // getSchemaLocked retrieves schema (must hold lock).
  900. func (m *SchemaManager) getSchemaLocked(name string) (*Schema, error) {
  901. if schema, ok := m.cache[strings.ToLower(name)]; ok {
  902. return schema, nil
  903. }
  904. key := m.schemaKey(name)
  905. var data string
  906. err := m.pool.WithClient(func(c *KVClient) error {
  907. var err error
  908. data, err = c.Read(key)
  909. return err
  910. })
  911. if err != nil {
  912. if err == ErrKeyNotFound {
  913. return nil, fmt.Errorf("table not found: %s", name)
  914. }
  915. return nil, err
  916. }
  917. var schema Schema
  918. if err := json.Unmarshal([]byte(data), &schema); err != nil {
  919. return nil, fmt.Errorf("failed to parse schema: %w", err)
  920. }
  921. schema.normalizePrimaryKey()
  922. m.cache[strings.ToLower(name)] = &schema
  923. return &schema, nil
  924. }
  925. // saveSchemaLocked saves schema (must hold lock).
  926. func (m *SchemaManager) saveSchemaLocked(schema *Schema) error {
  927. data, err := json.Marshal(schema)
  928. if err != nil {
  929. return fmt.Errorf("failed to serialize schema: %w", err)
  930. }
  931. key := m.schemaKey(schema.Name)
  932. err = m.pool.WithClient(func(c *KVClient) error {
  933. return c.Write(key, string(data))
  934. })
  935. if err != nil {
  936. return fmt.Errorf("failed to write schema: %w", err)
  937. }
  938. m.cache[strings.ToLower(schema.Name)] = schema
  939. return nil
  940. }
  941. // Index management methods
  942. // indexKey returns the key for an index.
  943. func (m *SchemaManager) indexKey(name string) string {
  944. return fmt.Sprintf("%s:index:%s", m.database, strings.ToLower(name))
  945. }
  946. // indexListKey returns the key for the index list.
  947. func (m *SchemaManager) indexListKey() string {
  948. return fmt.Sprintf("%s:indexes", m.database)
  949. }
  950. // CreateIndex creates a new index.
  951. func (m *SchemaManager) CreateIndex(index *Index) error {
  952. m.mu.Lock()
  953. defer m.mu.Unlock()
  954. // Check if index already exists
  955. key := m.indexKey(index.Name)
  956. err := m.pool.WithClient(func(c *KVClient) error {
  957. _, err := c.Read(key)
  958. return err
  959. })
  960. if err == nil {
  961. return fmt.Errorf("index already exists: %s", index.Name)
  962. }
  963. // Verify table exists
  964. if _, err := m.getSchemaLocked(index.Table); err != nil {
  965. return fmt.Errorf("table not found: %s", index.Table)
  966. }
  967. // Save index
  968. index.CreatedAt = time.Now()
  969. data, err := json.Marshal(index)
  970. if err != nil {
  971. return fmt.Errorf("failed to serialize index: %w", err)
  972. }
  973. err = m.pool.WithClient(func(c *KVClient) error {
  974. return c.Write(key, string(data))
  975. })
  976. if err != nil {
  977. return fmt.Errorf("failed to write index: %w", err)
  978. }
  979. // Add to index list
  980. if err := m.addToIndexList(index.Name); err != nil {
  981. return err
  982. }
  983. m.indexCache[strings.ToLower(index.Name)] = cloneIndex(index)
  984. m.bumpVersionLocked()
  985. return nil
  986. }
  987. // DropIndex drops an index.
  988. func (m *SchemaManager) DropIndex(name string) error {
  989. m.mu.Lock()
  990. defer m.mu.Unlock()
  991. key := m.indexKey(name)
  992. err := m.pool.WithClient(func(c *KVClient) error {
  993. return c.Delete(key)
  994. })
  995. if err != nil {
  996. return fmt.Errorf("failed to delete index: %w", err)
  997. }
  998. if err := m.removeFromIndexList(name); err != nil {
  999. return err
  1000. }
  1001. delete(m.indexCache, strings.ToLower(name))
  1002. m.bumpVersionLocked()
  1003. return nil
  1004. }
  1005. // IndexExists checks if an index exists.
  1006. func (m *SchemaManager) IndexExists(name string) bool {
  1007. m.mu.RLock()
  1008. defer m.mu.RUnlock()
  1009. return m.indexExistsUnsafe(name)
  1010. }
  1011. // indexExistsUnsafe reports whether an index definition is durable (m.mu must be
  1012. // held).
  1013. func (m *SchemaManager) indexExistsUnsafe(name string) bool {
  1014. if _, ok := m.indexCache[strings.ToLower(name)]; ok {
  1015. return true
  1016. }
  1017. key := m.indexKey(name)
  1018. err := m.pool.WithClient(func(c *KVClient) error {
  1019. _, err := c.Read(key)
  1020. return err
  1021. })
  1022. return err == nil
  1023. }
  1024. // GetIndex retrieves an index by name.
  1025. func (m *SchemaManager) GetIndex(name string) (*Index, error) {
  1026. m.mu.Lock()
  1027. defer m.mu.Unlock()
  1028. cacheKey := strings.ToLower(name)
  1029. if index, ok := m.indexCache[cacheKey]; ok {
  1030. return cloneIndex(index), nil
  1031. }
  1032. key := m.indexKey(name)
  1033. var data string
  1034. err := m.pool.WithClient(func(c *KVClient) error {
  1035. var err error
  1036. data, err = c.Read(key)
  1037. return err
  1038. })
  1039. if err != nil {
  1040. if err == ErrKeyNotFound {
  1041. return nil, fmt.Errorf("%w: %s", ErrIndexNotFound, name)
  1042. }
  1043. return nil, err
  1044. }
  1045. var index Index
  1046. if err := json.Unmarshal([]byte(data), &index); err != nil {
  1047. return nil, fmt.Errorf("failed to parse index: %w", err)
  1048. }
  1049. m.indexCache[cacheKey] = &index
  1050. return cloneIndex(&index), nil
  1051. }
  1052. // ListIndexes returns all index names.
  1053. func (m *SchemaManager) ListIndexes() ([]string, error) {
  1054. m.mu.Lock()
  1055. defer m.mu.Unlock()
  1056. return m.indexListUnsafe()
  1057. }
  1058. // indexListUnsafe returns all index names, loading them from storage on first
  1059. // use (m.mu must be held).
  1060. func (m *SchemaManager) indexListUnsafe() ([]string, error) {
  1061. if m.indexListCached {
  1062. return append([]string(nil), m.indexListCache...), nil
  1063. }
  1064. key := m.indexListKey()
  1065. var data string
  1066. err := m.pool.WithClient(func(c *KVClient) error {
  1067. var err error
  1068. data, err = c.Read(key)
  1069. return err
  1070. })
  1071. if err == ErrKeyNotFound {
  1072. m.indexListCache = nil
  1073. m.indexListCached = true
  1074. return []string{}, nil
  1075. }
  1076. if err != nil {
  1077. return nil, err
  1078. }
  1079. var indexes []string
  1080. if err := json.Unmarshal([]byte(data), &indexes); err != nil {
  1081. return nil, fmt.Errorf("failed to parse index list: %w", err)
  1082. }
  1083. m.indexListCache = append([]string(nil), indexes...)
  1084. m.indexListCached = true
  1085. return indexes, nil
  1086. }
  1087. // ListTableIndexes returns all indexes for a table.
  1088. func (m *SchemaManager) ListTableIndexes(table string) ([]*Index, error) {
  1089. indexes, err := m.ListIndexes()
  1090. if err != nil {
  1091. return nil, err
  1092. }
  1093. var result []*Index
  1094. for _, name := range indexes {
  1095. idx, err := m.GetIndex(name)
  1096. if err != nil {
  1097. // An index dropped concurrently is simply absent; any other error
  1098. // (storage/IO or corrupt data) must not be silently swallowed.
  1099. if errors.Is(err, ErrIndexNotFound) {
  1100. continue
  1101. }
  1102. return nil, err
  1103. }
  1104. if strings.EqualFold(idx.Table, table) {
  1105. result = append(result, idx)
  1106. }
  1107. }
  1108. return result, nil
  1109. }
  1110. // addToIndexList adds an index name to the list.
  1111. func (m *SchemaManager) addToIndexList(name string) error {
  1112. key := m.indexListKey()
  1113. var indexes []string
  1114. var data string
  1115. err := m.pool.WithClient(func(c *KVClient) error {
  1116. var err error
  1117. data, err = c.Read(key)
  1118. return err
  1119. })
  1120. if err == nil {
  1121. json.Unmarshal([]byte(data), &indexes)
  1122. }
  1123. indexes = append(indexes, name)
  1124. newData, _ := json.Marshal(indexes)
  1125. err = m.pool.WithClient(func(c *KVClient) error {
  1126. return c.Write(key, string(newData))
  1127. })
  1128. if err == nil {
  1129. m.indexListCache = append([]string(nil), indexes...)
  1130. m.indexListCached = true
  1131. }
  1132. return err
  1133. }
  1134. // removeFromIndexList removes an index name from the list.
  1135. func (m *SchemaManager) removeFromIndexList(name string) error {
  1136. key := m.indexListKey()
  1137. var indexes []string
  1138. var data string
  1139. err := m.pool.WithClient(func(c *KVClient) error {
  1140. var err error
  1141. data, err = c.Read(key)
  1142. return err
  1143. })
  1144. if err != nil {
  1145. return nil
  1146. }
  1147. json.Unmarshal([]byte(data), &indexes)
  1148. var newIndexes []string
  1149. for _, idx := range indexes {
  1150. if !strings.EqualFold(idx, name) {
  1151. newIndexes = append(newIndexes, idx)
  1152. }
  1153. }
  1154. newData, _ := json.Marshal(newIndexes)
  1155. err = m.pool.WithClient(func(c *KVClient) error {
  1156. return c.Write(key, string(newData))
  1157. })
  1158. if err == nil {
  1159. m.indexListCache = append([]string(nil), newIndexes...)
  1160. m.indexListCached = true
  1161. }
  1162. return err
  1163. }
  1164. // AddColumn adds a new column to a table. When unique is non-nil the column's
  1165. // UNIQUE index is registered as part of the same statement, and checks are
  1166. // persisted with the column in the same schema snapshot.
  1167. //
  1168. // A DEFAULT is materialized into every existing row before the schema is
  1169. // published. NOT NULL, UNIQUE, CHECK, and index validation all read persisted
  1170. // row values, so a default that only existed in the catalog would be invisible
  1171. // to them: a UNIQUE index over the new column could not see the duplicate
  1172. // values every existing row shares, a CHECK predicate could not see a violating
  1173. // default, and an UPDATE to NULL would look non-null. The rewrite is the same
  1174. // shape as DropColumn: the table's exclusive gate keeps point writes and CAS
  1175. // commits out while rows are scanned, validated, and rewritten, the transform
  1176. // is idempotent, and the schema is saved last so an interrupted operation
  1177. // resumes against the old schema.
  1178. //
  1179. // The gate also makes the validation authoritative: NOT NULL without a default,
  1180. // duplicate UNIQUE values, and rows a new CHECK predicate rejects are rejected
  1181. // before the column, its checks, and its index are published, and they are
  1182. // published together in one batch write so a crash cannot leave the column
  1183. // visible without the constraints that validate it. A constant default is
  1184. // deterministic, so its UNIQUE prediction can run as a cheap pre-scan. A
  1185. // clock-reading default is evaluated at one instant sampled for this ALTER
  1186. // statement, and that instant is passed to every validation scan and to the
  1187. // rewrite, so a clock crossing between them cannot publish a row that violates
  1188. // the constraint the statement adds; the UNIQUE duplicate set is still
  1189. // collected during the rewrite so it covers exactly the values the index will
  1190. // see. A nullable column without a default needs no rewrite because a missing
  1191. // row field already reads as NULL. Like DROP COLUMN, the statement holds the
  1192. // catalog lock for the whole scan, so the cost is one table scan per validating
  1193. // constraint plus a table-wide stall per ADD COLUMN that has a default, an
  1194. // index, or a check.
  1195. func (m *SchemaManager) AddColumn(table string, column Column, checks []CheckConstraint, unique *Index) error {
  1196. tableLock := m.tableLock(table)
  1197. tableLock.Lock()
  1198. defer tableLock.Unlock()
  1199. m.mu.Lock()
  1200. defer m.mu.Unlock()
  1201. // One instant for the whole ALTER statement: every validation scan and the
  1202. // row rewrite must observe the same value, or a clock crossing between them
  1203. // could publish rows that violate the CHECK or UNIQUE constraint this
  1204. // statement adds.
  1205. now := m.defaultNow()
  1206. schema, err := m.getSchemaUnsafe(table)
  1207. if err != nil {
  1208. return err
  1209. }
  1210. if err := checkColumnAbsent(schema, column.Name); err != nil {
  1211. return err
  1212. }
  1213. if unique != nil {
  1214. if !strings.EqualFold(unique.Table, schema.Name) {
  1215. return fmt.Errorf("index %s belongs to table %s, not %s", unique.Name, unique.Table, schema.Name)
  1216. }
  1217. if m.indexExistsUnsafe(unique.Name) {
  1218. return fmt.Errorf("index already exists: %s", unique.Name)
  1219. }
  1220. // A clock-reading default is validated during the rewrite below so the
  1221. // duplicate set covers exactly the values this statement writes. The
  1222. // pre-scan is kept for constant and absent defaults, which are
  1223. // deterministic.
  1224. if column.DefaultExpr == "" {
  1225. if err := m.validateAddColumnUnique(table, column, unique, now); err != nil {
  1226. return err
  1227. }
  1228. }
  1229. }
  1230. if err := m.validateAddColumnChecks(table, column, checks, now); err != nil {
  1231. return err
  1232. }
  1233. // A required column with nothing to write into rows that already exist
  1234. // cannot be declared on a populated table.
  1235. if !column.Nullable && !columnHasDefault(column) {
  1236. hasRows, err := m.tableHasRows(table)
  1237. if err != nil {
  1238. return err
  1239. }
  1240. if hasRows {
  1241. return &NotNullViolationError{Table: schema.Name, Column: column.Name}
  1242. }
  1243. }
  1244. // An expression default is evaluated at the ALTER statement's instant. When
  1245. // the column is also UNIQUE, collect the values this rewrite writes and
  1246. // abort on a duplicate, so the index is validated against exactly the values
  1247. // it will see.
  1248. var seen map[string]struct{}
  1249. if unique != nil && column.DefaultExpr != "" {
  1250. seen = make(map[string]struct{})
  1251. }
  1252. if columnHasDefault(column) {
  1253. if err := m.applyRows(table, func(row Row) (bool, error) {
  1254. value, present := rowColumnValue(row, column.Name)
  1255. if !present {
  1256. var err error
  1257. value, _, err = m.columnDefaultValue(column, now)
  1258. if err != nil {
  1259. return false, err
  1260. }
  1261. row[column.Name] = value
  1262. }
  1263. if seen != nil {
  1264. if value == nil {
  1265. return !present, nil
  1266. }
  1267. encoded := encodeUniqueScalar(value)
  1268. if _, duplicate := seen[encoded]; duplicate {
  1269. return false, &UniqueViolationError{Constraint: unique.Name}
  1270. }
  1271. seen[encoded] = struct{}{}
  1272. }
  1273. return !present, nil
  1274. }); err != nil {
  1275. return err
  1276. }
  1277. }
  1278. return m.publishAddedColumn(schema, column, checks, unique)
  1279. }
  1280. // publishAddedColumn publishes a column, its checks, and, when present, its
  1281. // unique index in one atomic batch write (m.mu must be held): the schema
  1282. // snapshot, the index definition, and the index list always become durable
  1283. // together or not at all.
  1284. func (m *SchemaManager) publishAddedColumn(schema *Schema, column Column, checks []CheckConstraint, unique *Index) error {
  1285. // Publish a fresh snapshot instead of mutating readers' shared pointer.
  1286. schema = cloneSchema(schema)
  1287. schema.Columns = append(schema.Columns, column)
  1288. schema.Checks = append(schema.Checks, checks...)
  1289. schemaData, err := json.Marshal(schema)
  1290. if err != nil {
  1291. return fmt.Errorf("failed to serialize schema: %w", err)
  1292. }
  1293. ops := []BatchOp{{Op: batchPut, Key: []byte(m.schemaKey(schema.Name)), Value: schemaData}}
  1294. var indexList []string
  1295. if unique != nil {
  1296. unique.CreatedAt = time.Now()
  1297. indexData, err := json.Marshal(unique)
  1298. if err != nil {
  1299. return fmt.Errorf("failed to serialize index: %w", err)
  1300. }
  1301. indexList, err = m.indexListUnsafe()
  1302. if err != nil {
  1303. return err
  1304. }
  1305. indexList = append(indexList, unique.Name)
  1306. listData, err := json.Marshal(indexList)
  1307. if err != nil {
  1308. return fmt.Errorf("failed to serialize index list: %w", err)
  1309. }
  1310. ops = append(ops,
  1311. BatchOp{Op: batchPut, Key: []byte(m.indexKey(unique.Name)), Value: indexData},
  1312. BatchOp{Op: batchPut, Key: []byte(m.indexListKey()), Value: listData},
  1313. )
  1314. }
  1315. if err := m.pool.WithClient(func(c *KVClient) error {
  1316. _, err := c.BatchWrite(ops, nil)
  1317. return err
  1318. }); err != nil {
  1319. return fmt.Errorf("failed to publish column %s: %w", column.Name, err)
  1320. }
  1321. // Publish in-memory state only after the durable write.
  1322. m.cache[strings.ToLower(schema.Name)] = schema
  1323. if unique != nil {
  1324. m.indexCache[strings.ToLower(unique.Name)] = cloneIndex(unique)
  1325. m.indexListCache = append([]string(nil), indexList...)
  1326. m.indexListCached = true
  1327. }
  1328. m.bumpVersionLocked()
  1329. return nil
  1330. }
  1331. func checkColumnAbsent(schema *Schema, name string) error {
  1332. for _, col := range schema.Columns {
  1333. if strings.EqualFold(col.Name, name) {
  1334. return fmt.Errorf("column already exists: %s", name)
  1335. }
  1336. }
  1337. return nil
  1338. }
  1339. // DropColumn removes a column from a table.
  1340. func (m *SchemaManager) DropColumn(table, columnName string) error {
  1341. tableLock := m.tableLock(table)
  1342. tableLock.Lock()
  1343. defer tableLock.Unlock()
  1344. m.mu.Lock()
  1345. defer m.mu.Unlock()
  1346. schema, err := m.getSchemaUnsafe(table)
  1347. if err != nil {
  1348. return err
  1349. }
  1350. // Cannot drop primary key column
  1351. if schema.IsPrimaryKeyColumn(columnName) {
  1352. return fmt.Errorf("cannot drop primary key column: %s", columnName)
  1353. }
  1354. // Find and remove column
  1355. newColumns := make([]Column, 0, len(schema.Columns)-1)
  1356. found := false
  1357. for _, col := range schema.Columns {
  1358. if strings.EqualFold(col.Name, columnName) {
  1359. found = true
  1360. continue
  1361. }
  1362. newColumns = append(newColumns, col)
  1363. }
  1364. if !found {
  1365. return fmt.Errorf("column not found: %s", columnName)
  1366. }
  1367. schema = cloneSchema(schema)
  1368. schema.Columns = newColumns
  1369. if err := m.rewriteRows(table, func(row Row) bool {
  1370. for key := range row {
  1371. if strings.EqualFold(key, columnName) {
  1372. delete(row, key)
  1373. return true
  1374. }
  1375. }
  1376. return false
  1377. }); err != nil {
  1378. return err
  1379. }
  1380. // Update schema
  1381. return m.updateSchemaUnsafe(schema)
  1382. }
  1383. // RenameTable renames a table.
  1384. func (m *SchemaManager) RenameTable(oldName, newName string) error {
  1385. m.mu.Lock()
  1386. defer m.mu.Unlock()
  1387. // Check if old table exists
  1388. schema, err := m.getSchemaUnsafe(oldName)
  1389. if err != nil {
  1390. return err
  1391. }
  1392. // Check if new table name already exists
  1393. _, err = m.getSchemaUnsafe(newName)
  1394. if err == nil {
  1395. return fmt.Errorf("table already exists: %s", newName)
  1396. }
  1397. schema = cloneSchema(schema)
  1398. // Update schema name
  1399. schema.Name = newName
  1400. // Move durable rows from the old table's data prefix to the new one. Without
  1401. // this, ALTER TABLE ... RENAME leaves every row under the old key and the
  1402. // renamed table appears empty.
  1403. if err := m.renameDataKeys(oldName, newName); err != nil {
  1404. return err
  1405. }
  1406. // Repoint indexes that belonged to the old table.
  1407. if err := m.repointIndexesLocked(oldName, newName); err != nil {
  1408. return err
  1409. }
  1410. // Build the replacement catalog before switching schema names. The schema,
  1411. // catalog, and stale ROWID state change in one batch, so a crash cannot
  1412. // leave neither table name addressable after all row chunks have moved.
  1413. tables, err := m.ListTables()
  1414. if err != nil {
  1415. return err
  1416. }
  1417. foundOld := false
  1418. for i, name := range tables {
  1419. if strings.EqualFold(name, oldName) {
  1420. tables[i] = newName
  1421. foundOld = true
  1422. break
  1423. }
  1424. }
  1425. if !foundOld {
  1426. return fmt.Errorf("table not found in catalog: %s", oldName)
  1427. }
  1428. catalogData, err := json.Marshal(tables)
  1429. if err != nil {
  1430. return err
  1431. }
  1432. schemaData, err := json.Marshal(schema)
  1433. if err != nil {
  1434. return err
  1435. }
  1436. oldKey := m.schemaKey(oldName)
  1437. newKey := m.schemaKey(newName)
  1438. err = m.pool.WithClient(func(c *KVClient) error {
  1439. _, err := c.BatchWrite([]BatchOp{
  1440. {Op: batchPut, Key: []byte(newKey), Value: schemaData},
  1441. {Op: batchDelete, Key: []byte(oldKey)},
  1442. {Op: batchDelete, Key: []byte(m.rowIDKey(oldName))},
  1443. {Op: batchPut, Key: []byte(m.catalogKey()), Value: catalogData},
  1444. }, nil)
  1445. return err
  1446. })
  1447. if err != nil {
  1448. return err
  1449. }
  1450. // Update cache
  1451. oldLower := strings.ToLower(oldName)
  1452. newLower := strings.ToLower(newName)
  1453. delete(m.cache, oldLower)
  1454. m.rowIDMu.Lock()
  1455. delete(m.rowIDAlloc, oldLower)
  1456. m.rowIDMu.Unlock()
  1457. // Update cache
  1458. m.cache[newLower] = schema
  1459. m.bumpVersionLocked()
  1460. return nil
  1461. }
  1462. // renameDataKeys moves every durable row of a table from the old name's data
  1463. // prefix to the new name's prefix in a single atomic batch per page.
  1464. func (m *SchemaManager) renameDataKeys(oldName, newName string) error {
  1465. oldPrefix := []byte(fmt.Sprintf("%s:_data:%s:", m.database, strings.ToLower(oldName)))
  1466. newPrefix := fmt.Sprintf("%s:_data:%s:", m.database, strings.ToLower(newName))
  1467. return m.pool.WithClient(func(client *KVClient) (retErr error) {
  1468. cursor, err := client.Scan(oldPrefix)
  1469. if err != nil {
  1470. return err
  1471. }
  1472. defer func() {
  1473. if cerr := cursor.Close(); retErr == nil {
  1474. retErr = cerr
  1475. }
  1476. }()
  1477. ops := make([]BatchOp, 0, scanPageSize*2)
  1478. batchBytes := 8
  1479. flush := func() error {
  1480. if len(ops) == 0 {
  1481. return nil
  1482. }
  1483. if _, err := client.BatchWrite(ops, nil); err != nil {
  1484. return err
  1485. }
  1486. ops = ops[:0]
  1487. batchBytes = 8
  1488. return nil
  1489. }
  1490. for {
  1491. entries, done, err := cursor.Next()
  1492. if err != nil {
  1493. return err
  1494. }
  1495. for _, e := range entries {
  1496. suffix := string(e.Key[len(oldPrefix):])
  1497. newKey := []byte(newPrefix + suffix)
  1498. opBytes := 24 + len(newKey) + len(e.Value) + len(e.Key)
  1499. if len(ops)+2 > maxOperations || batchBytes+opBytes > bulkBatchByteBudget {
  1500. if err := flush(); err != nil {
  1501. return err
  1502. }
  1503. }
  1504. ops = append(ops,
  1505. BatchOp{Op: batchPut, Key: newKey, Value: e.Value},
  1506. BatchOp{Op: batchDelete, Key: append([]byte(nil), e.Key...)},
  1507. )
  1508. batchBytes += opBytes
  1509. }
  1510. if done {
  1511. return flush()
  1512. }
  1513. }
  1514. })
  1515. }
  1516. // updateIndexesOfTableLocked applies mutate to the durable definition of every
  1517. // index on table and persists the definitions it changed. mutate reports
  1518. // whether it modified the definition; unmodified indexes are left untouched.
  1519. // The changed definitions are written in one batch, so a table's indexes never
  1520. // split between the old and new shape. The caller holds m.mu; it reads the
  1521. // durable index list directly rather than calling the self-locking ListIndexes
  1522. // helper.
  1523. func (m *SchemaManager) updateIndexesOfTableLocked(table string, mutate func(*Index) bool) error {
  1524. var names []string
  1525. if m.indexListCached {
  1526. names = append([]string(nil), m.indexListCache...)
  1527. } else {
  1528. var data string
  1529. err := m.pool.WithClient(func(c *KVClient) error {
  1530. var rerr error
  1531. data, rerr = c.Read(m.indexListKey())
  1532. return rerr
  1533. })
  1534. if err == ErrKeyNotFound {
  1535. return nil
  1536. }
  1537. if err != nil {
  1538. return err
  1539. }
  1540. if err := json.Unmarshal([]byte(data), &names); err != nil {
  1541. return err
  1542. }
  1543. }
  1544. type indexUpdate struct {
  1545. index Index
  1546. key string
  1547. value []byte
  1548. }
  1549. var updates []indexUpdate
  1550. for _, name := range names {
  1551. key := m.indexKey(name)
  1552. var data string
  1553. err := m.pool.WithClient(func(c *KVClient) error {
  1554. var rerr error
  1555. data, rerr = c.Read(key)
  1556. return rerr
  1557. })
  1558. if err != nil {
  1559. if err == ErrKeyNotFound {
  1560. continue
  1561. }
  1562. return err
  1563. }
  1564. var idx Index
  1565. if err := json.Unmarshal([]byte(data), &idx); err != nil {
  1566. return err
  1567. }
  1568. if !strings.EqualFold(idx.Table, table) {
  1569. continue
  1570. }
  1571. if !mutate(&idx) {
  1572. continue
  1573. }
  1574. updated, err := json.Marshal(&idx)
  1575. if err != nil {
  1576. return err
  1577. }
  1578. updates = append(updates, indexUpdate{index: idx, key: key, value: updated})
  1579. }
  1580. if len(updates) == 0 {
  1581. return nil
  1582. }
  1583. ops := make([]BatchOp, 0, len(updates))
  1584. for _, update := range updates {
  1585. ops = append(ops, BatchOp{Op: batchPut, Key: []byte(update.key), Value: update.value})
  1586. }
  1587. if err := m.pool.WithClient(func(c *KVClient) error {
  1588. _, err := c.BatchWrite(ops, nil)
  1589. return err
  1590. }); err != nil {
  1591. return fmt.Errorf("update index definitions for table %s: %w", table, err)
  1592. }
  1593. for _, update := range updates {
  1594. if cached, ok := m.indexCache[strings.ToLower(update.index.Name)]; ok {
  1595. *cached = update.index
  1596. }
  1597. }
  1598. return nil
  1599. }
  1600. // repointIndexesLocked updates indexes whose table was renamed.
  1601. func (m *SchemaManager) repointIndexesLocked(oldName, newName string) error {
  1602. return m.updateIndexesOfTableLocked(oldName, func(idx *Index) bool {
  1603. idx.Table = newName
  1604. return true
  1605. })
  1606. }
  1607. // renameIndexColumnsLocked rewrites the stored definition of every index on
  1608. // table so a reference to oldName becomes newName. Index names stay unchanged:
  1609. // they are independent identifiers that SQLite does not derive from column
  1610. // names, and callers may have chosen them explicitly. A rename to an existing
  1611. // column name is rejected before this runs. Expression columns keep their SQL
  1612. // text; callers must reject a rename that such an expression depends on,
  1613. // because the text is not rewritten.
  1614. func (m *SchemaManager) renameIndexColumnsLocked(table, oldName, newName string) error {
  1615. return m.updateIndexesOfTableLocked(table, func(idx *Index) bool {
  1616. changed := false
  1617. for i := range idx.Columns {
  1618. // Expression columns persist SQL text, not a column reference; their
  1619. // text is left for the caller to drop and recreate the index.
  1620. if idx.Columns[i].Expression == "" && strings.EqualFold(idx.Columns[i].Name, oldName) {
  1621. idx.Columns[i].Name = newName
  1622. changed = true
  1623. }
  1624. }
  1625. return changed
  1626. })
  1627. }
  1628. // RenameColumn renames a column in a table. Column references in index
  1629. // definitions are rewritten; expression-index text is not, so a SQL caller must
  1630. // reject a rename that an expression depends on.
  1631. func (m *SchemaManager) RenameColumn(table, oldName, newName string) error {
  1632. tableLock := m.tableLock(table)
  1633. tableLock.Lock()
  1634. defer tableLock.Unlock()
  1635. m.mu.Lock()
  1636. defer m.mu.Unlock()
  1637. schema, err := m.getSchemaUnsafe(table)
  1638. if err != nil {
  1639. return err
  1640. }
  1641. // Check if new column name already exists
  1642. for _, col := range schema.Columns {
  1643. if strings.EqualFold(col.Name, newName) {
  1644. return fmt.Errorf("column already exists: %s", newName)
  1645. }
  1646. }
  1647. schema = cloneSchema(schema)
  1648. // Find and rename column
  1649. found := false
  1650. for i, col := range schema.Columns {
  1651. if strings.EqualFold(col.Name, oldName) {
  1652. schema.Columns[i].Name = newName
  1653. found = true
  1654. // Update primary key references if needed. Renaming keeps the column
  1655. // order, so a composite key's encoding is unchanged.
  1656. for i, pkColumn := range schema.PrimaryKeyColumns {
  1657. if strings.EqualFold(pkColumn, oldName) {
  1658. schema.PrimaryKeyColumns[i] = newName
  1659. }
  1660. }
  1661. if strings.EqualFold(schema.PrimaryKey, oldName) {
  1662. schema.PrimaryKey = newName
  1663. }
  1664. break
  1665. }
  1666. }
  1667. if !found {
  1668. return fmt.Errorf("column not found: %s", oldName)
  1669. }
  1670. if err := m.rewriteRows(table, func(row Row) bool {
  1671. for key, value := range row {
  1672. if strings.EqualFold(key, oldName) {
  1673. delete(row, key)
  1674. row[newName] = value
  1675. return true
  1676. }
  1677. }
  1678. return false
  1679. }); err != nil {
  1680. return err
  1681. }
  1682. // Publish the renamed schema before the index definitions: reads of the new
  1683. // column name resolve against rewritten rows as soon as the schema lands.
  1684. // Autocommit index DDL is not covered by this write, so a failure here leaves
  1685. // stale index metadata for the caller to drop and recreate; inside a
  1686. // transaction the whole statement is staged and published atomically.
  1687. if err := m.updateSchemaUnsafe(schema); err != nil {
  1688. return err
  1689. }
  1690. // Index metadata drives secondary-index values, UNIQUE enforcement,
  1691. // ON CONFLICT target resolution, and catalog introspection, so it must name
  1692. // the renamed column before any later statement uses the table.
  1693. return m.renameIndexColumnsLocked(table, oldName, newName)
  1694. }
  1695. // rewriteRows applies an idempotent name-keyed row transformation in bounded
  1696. // batches. Column DDL updates the schema only after all rows are rewritten, so
  1697. // an interrupted operation can safely resume against the old schema.
  1698. func (m *SchemaManager) rewriteRows(table string, transform func(Row) bool) error {
  1699. return m.applyRows(table, func(row Row) (bool, error) {
  1700. return transform(row), nil
  1701. })
  1702. }
  1703. // applyRows scans a table once and applies transform to every row. Rows for
  1704. // which transform reports a change are re-encoded and written in bounded
  1705. // batches; an error from transform aborts the scan. Callers hold the table's
  1706. // exclusive gate, so no writer can interleave between the scan and the schema
  1707. // update that follows it. Aborting after a batch was flushed is safe only
  1708. // because the DDL transforms are idempotent.
  1709. func (m *SchemaManager) applyRows(table string, transform func(Row) (bool, error)) error {
  1710. prefix := []byte(fmt.Sprintf("%s:_data:%s:", m.database, strings.ToLower(table)))
  1711. return m.pool.WithClient(func(client *KVClient) (retErr error) {
  1712. cursor, err := client.Scan(prefix)
  1713. if err != nil {
  1714. return err
  1715. }
  1716. defer func() {
  1717. if cerr := cursor.Close(); retErr == nil {
  1718. retErr = cerr
  1719. }
  1720. }()
  1721. ops := make([]BatchOp, 0, scanPageSize)
  1722. batchBytes := 8
  1723. flush := func() error {
  1724. if len(ops) == 0 {
  1725. return nil
  1726. }
  1727. if _, err := client.BatchWrite(ops, nil); err != nil {
  1728. return err
  1729. }
  1730. ops = ops[:0]
  1731. batchBytes = 8
  1732. return nil
  1733. }
  1734. for {
  1735. entries, done, err := cursor.Next()
  1736. if err != nil {
  1737. return err
  1738. }
  1739. for _, entry := range entries {
  1740. row, err := decodeRow(entry.Value)
  1741. if err != nil {
  1742. return err
  1743. }
  1744. changed, err := transform(row)
  1745. if err != nil {
  1746. return err
  1747. }
  1748. if !changed {
  1749. continue
  1750. }
  1751. value, err := encodeRow(row)
  1752. if err != nil {
  1753. return err
  1754. }
  1755. opBytes := 12 + len(entry.Key) + len(value)
  1756. if len(ops) == maxOperations || batchBytes+opBytes > bulkBatchByteBudget {
  1757. if err := flush(); err != nil {
  1758. return err
  1759. }
  1760. }
  1761. ops = append(ops, BatchOp{Op: batchPut, Key: append([]byte(nil), entry.Key...), Value: value})
  1762. batchBytes += opBytes
  1763. }
  1764. if done {
  1765. return flush()
  1766. }
  1767. }
  1768. })
  1769. }
  1770. // validateAddColumnUnique rejects an ADD COLUMN ... UNIQUE whose backfilled
  1771. // values would duplicate across existing rows. It reads rows as they will look
  1772. // after the rewrite (a row that already carries the field from an interrupted
  1773. // earlier attempt keeps its value) and mirrors ValidateUniqueIndex for a single
  1774. // plain column: NULLs do not collide. It is used only for a constant or absent
  1775. // default; a clock-reading default is validated by the rewrite itself because
  1776. // its values cannot be predicted. The caller holds the table's exclusive gate,
  1777. // so no writer can insert a conflicting row between this check and the
  1778. // publication of the index.
  1779. func (m *SchemaManager) validateAddColumnUnique(table string, column Column, index *Index, now time.Time) error {
  1780. seen := make(map[string]struct{})
  1781. return m.applyRows(table, func(row Row) (bool, error) {
  1782. value, ok := rowColumnValue(row, column.Name)
  1783. if !ok {
  1784. var err error
  1785. value, _, err = m.columnDefaultValue(column, now)
  1786. if err != nil {
  1787. return false, err
  1788. }
  1789. }
  1790. if value == nil {
  1791. return false, nil
  1792. }
  1793. encoded := encodeUniqueScalar(value)
  1794. if _, duplicate := seen[encoded]; duplicate {
  1795. return false, &UniqueViolationError{Constraint: index.Name}
  1796. }
  1797. seen[encoded] = struct{}{}
  1798. return false, nil
  1799. })
  1800. }
  1801. // columnHasDefault reports whether the column contributes a value to rows that
  1802. // omit it, whether that default is a stored constant or an expression.
  1803. func columnHasDefault(column Column) bool {
  1804. return column.Default != nil || column.DefaultExpr != ""
  1805. }
  1806. // validateAddColumnChecks rejects an ADD COLUMN ... CHECK whose predicate is
  1807. // already false for an existing row. Like validateAddColumnUnique it reads rows
  1808. // as they will look after the default backfill, and it runs before that rewrite
  1809. // so a rejected DDL leaves no row carrying the new field. The default is
  1810. // resolved through columnDefaultValue at the instant sampled once for the
  1811. // ALTER statement, so a clock-reading default reaches the predicate as the value
  1812. // the backfilled row will receive instead of NULL. The caller holds the table's
  1813. // exclusive gate, so no writer can insert a violating row between this
  1814. // validation and the publication of the check.
  1815. func (m *SchemaManager) validateAddColumnChecks(table string, column Column, checks []CheckConstraint, now time.Time) error {
  1816. if len(checks) == 0 {
  1817. return nil
  1818. }
  1819. eval := m.checkEvaluator()
  1820. if eval == nil {
  1821. return fmt.Errorf("cannot validate CHECK constraints on %s: no CHECK evaluator installed", table)
  1822. }
  1823. return m.applyRows(table, func(row Row) (bool, error) {
  1824. if _, ok := rowColumnValue(row, column.Name); !ok {
  1825. value, hasDefault, err := m.columnDefaultValue(column, now)
  1826. if err != nil {
  1827. return false, err
  1828. }
  1829. if hasDefault {
  1830. row[column.Name] = value
  1831. }
  1832. }
  1833. for _, check := range checks {
  1834. holds, err := eval(check.Expression, row)
  1835. if err != nil {
  1836. return false, fmt.Errorf("evaluate CHECK constraint %s on %s: %w", check.Label(), table, err)
  1837. }
  1838. if !holds {
  1839. return false, &CheckViolationError{Table: table, Constraint: check.Label()}
  1840. }
  1841. }
  1842. return false, nil
  1843. })
  1844. }
  1845. // tableHasRows reports whether a table holds at least one durable row.
  1846. func (m *SchemaManager) tableHasRows(table string) (bool, error) {
  1847. prefix := []byte(fmt.Sprintf("%s:_data:%s:", m.database, strings.ToLower(table)))
  1848. found := false
  1849. err := m.pool.WithClient(func(client *KVClient) (retErr error) {
  1850. cursor, err := client.ScanWithLimit(prefix, 1)
  1851. if err != nil {
  1852. return err
  1853. }
  1854. defer func() {
  1855. if cerr := cursor.Close(); retErr == nil {
  1856. retErr = cerr
  1857. }
  1858. }()
  1859. entries, _, err := cursor.Next()
  1860. if err != nil {
  1861. return err
  1862. }
  1863. found = len(entries) > 0
  1864. return nil
  1865. })
  1866. return found, err
  1867. }
  1868. // getSchemaUnsafe gets a schema without locking (internal use).
  1869. func (m *SchemaManager) getSchemaUnsafe(table string) (*Schema, error) {
  1870. tableLower := strings.ToLower(table)
  1871. // Check cache
  1872. if schema, ok := m.cache[tableLower]; ok {
  1873. return schema, nil
  1874. }
  1875. // Read from storage
  1876. key := m.schemaKey(table)
  1877. var data string
  1878. err := m.pool.WithClient(func(c *KVClient) error {
  1879. var err error
  1880. data, err = c.Read(key)
  1881. return err
  1882. })
  1883. if err != nil {
  1884. return nil, fmt.Errorf("table not found: %s", table)
  1885. }
  1886. var schema Schema
  1887. if err := json.Unmarshal([]byte(data), &schema); err != nil {
  1888. return nil, err
  1889. }
  1890. schema.normalizePrimaryKey()
  1891. m.cache[tableLower] = &schema
  1892. return &schema, nil
  1893. }
  1894. // updateSchemaUnsafe updates a schema without locking (internal use).
  1895. func (m *SchemaManager) updateSchemaUnsafe(schema *Schema) error {
  1896. key := m.schemaKey(schema.Name)
  1897. data, _ := json.Marshal(schema)
  1898. err := m.pool.WithClient(func(c *KVClient) error {
  1899. return c.Write(key, string(data))
  1900. })
  1901. if err != nil {
  1902. return err
  1903. }
  1904. // Update cache
  1905. m.cache[strings.ToLower(schema.Name)] = schema
  1906. m.bumpVersionLocked()
  1907. return nil
  1908. }