schema.go 52 KB

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