Help us shape the future of Go! Take the 2026 Go Developer Survey

sqliteschema

package
v1.9.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 2, 2026 License: MIT Imports: 5 Imported by: 0

Documentation

Overview

Package sqliteschema provides the SQLite table-rebuild procedure used by migrations that must change a column's declared type.

SQLite has no ALTER COLUMN TYPE, so changing a declared type means rebuilding the table: create a replacement, copy every row, drop the original, rename the replacement into place, recreate the indexes.

Why a declared type matters in a dynamically typed database

SQLite stores values, not column types, so a declared type looks cosmetic. It is not. The modernc.org/sqlite driver decides whether to convert a TEXT value back into a time.Time by looking at the column's DECLARED type: only DATE, DATETIME and TIMESTAMP get the conversion (see rows.go, ColumnTypeDatabaseTypeName). A column declared TEXT hands the raw string to the caller.

That is a problem because of how the same driver WRITES a time.Time. Unless the DSN carries _time_format, conn.formatTime falls back to time.Time.String(), which appends the monotonic clock reading:

2026-08-24 18:13:13.095282 -0500 CDT m=+0.241018501

modernc knows how to strip that " m=+…" suffix on read, but only on the DATE/DATETIME/TIMESTAMP path. For a TEXT column the raw string reaches grove's scan/convert.go, whose parseTimeString layouts do not tolerate the suffix, and the read fails:

sql: Scan error on column index 10, name "created_at":
scan: cannot parse "… -0500 CDT m=+0.016649834" as time.Time

So any row written with an ordinary time.Now() into a TEXT-declared timestamp column cannot be read back. Declaring the column TIMESTAMP fixes both halves: the driver strips the suffix and returns a time.Time directly.

The core store hit this first and fixed it in store/sqlite/migrations_timestamps.go (migration 20260601000002). This package generalises that procedure for the plugin schemas, which — unlike the core schema — declare foreign keys.

Foreign keys

The rebuild drops the original table. If foreign key enforcement is on at that moment, SQLite treats the drop as an implicit DELETE FROM and fires every ON DELETE CASCADE pointing at it. Rebuilding authsome_scim_configs that way would silently empty authsome_scim_tokens and authsome_scim_provision_logs.

So the procedure follows https://sqlite.org/lang_altertable.html#otheralter and turns foreign keys off for the duration. Turning them off also settles the other half of the rename semantics: ALTER TABLE … RENAME rewrites REFERENCES clauses in other tables only when foreign keys are ON, so with them OFF each replacement table must spell out its own REFERENCES clauses using the final table names — which is exactly what copying the original CREATE and changing only the timestamp types gives you.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func RebuildTables

func RebuildTables(ctx context.Context, exec migrate.Executor, rebuilds []TableRebuild) error

RebuildTables runs the rebuild procedure for every table in rebuilds, with foreign key enforcement disabled for the duration and restored afterwards.

Each rebuild is self-checked: the replacement's column set must match both the live table and the copy list, and the table's index set must be unchanged once the rename is done. A mismatch aborts before any data moves rather than silently dropping a column added by a later migration.

Types

type TableRebuild

type TableRebuild struct {
	// Table is the existing table name.
	Table string
	// Create is a CREATE TABLE <Table>_new (…) statement: the original
	// definition with the timestamp columns declared TIMESTAMP, every other
	// column and every REFERENCES clause left exactly as it was.
	Create string
	// Columns is the comma-separated column list used as both the INSERT
	// target and the SELECT source, so the copy is order-independent.
	Columns string
	// Indexes holds the CREATE INDEX statements recreated after the rename.
	// Indexes implied by the CREATE (PRIMARY KEY, inline UNIQUE) come back on
	// their own and must not be listed here.
	Indexes string
}

TableRebuild describes the replacement of one table.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL