7.3 KiB
SQL Schema Updates in LND
This document provides guidance for adding new SQL schema migrations in the LND codebase. When adding a new SQL schema, multiple files need to be updated to ensure consistency across different build configurations and environments.
Overview
LND uses a dual approach for database migrations:
- SQL Schema Migrations: Standard SQL files handled by golang-migrate
- Custom Application-Level Migrations: Go functions that perform data migration or complex logic
The migration system supports both development and production configurations through build tags.
Required Updates Checklist
When adding a new SQL schema migration, you MUST update the following locations:
1. SQL Migration Files
Create the migration SQL files in the appropriate directory:
sqldb/sqlc/migrations/
├── 000XXX_your_migration_name.up.sql # Schema changes
└── 000XXX_your_migration_name.down.sql # Rollback changes
2. Migration Configuration
Add the migration entry to ONE of these files based on your target environment:
For Production Releases:
File: sqldb/migrations.go
migrationConfig = append([]MigrationConfig{
// ... existing migrations
{
Name: "000XXX_your_migration_name",
Version: X,
SchemaVersion: X,
// MigrationFn: customMigrationFunction, // Only if custom logic needed
},
For Development/Testing:
File: sqldb/migrations_dev.go
//go:build test_db_postgres || test_db_sqlite || test_native_sql
var migrationAdditions = []MigrationConfig{
// ... existing migrations
{
Name: "000XXX_your_migration_name",
Version: X,
SchemaVersion: X,
// MigrationFn: customMigrationFunction, // Only if custom logic needed
},
}
3. Custom Migration Functions (If Needed)
If your migration requires custom application logic (data transformation, complex operations), you need to:
⚠️ Important: Custom migrations do NOT have corresponding SQL files in the sqlc/migrations/
directory. They are purely application-level migrations defined in Go code.
3a. Add Custom Migration Entry to Migration Configuration
same as in section 2 without the number prefix. See the example for more information.
3b. Add Migration Function Reference
Add the migration function to the appropriate config files:
For Production: config_builder.go
func (d *DefaultDatabaseBuilder) BuildDatabase(
ctx context.Context) (*DatabaseInstances, func(), error) {
invoiceMig := func(tx *sqlc.Queries) error {
err := invoices.MigrateInvoicesToSQL(
ctx, dbs.ChanStateDB.Backend,
dbs.ChanStateDB, tx,
invoiceMigrationBatchSize,
)
if err != nil {
return fmt.Errorf("failed to migrate "+
"invoices to SQL: %w", err)
}
// Set the invoice bucket tombstone to indicate
// that the migration has been completed.
d.logger.Debugf("Setting invoice bucket " +
"tombstone")
return dbs.ChanStateDB.SetInvoiceBucketTombstone() //nolint:ll
For Testing: config_test_native_sql.go
func (d *DefaultDatabaseBuilder) getSQLMigration(ctx context.Context,
version int, kvBackend kvdb.Backend) (func(tx *sqlc.Queries) error, bool) {
switch version {
case X: // Use the version number from your migrationAdditions
return func(tx *sqlc.Queries) error {
// Your custom migration logic here
return nil
}, true
}
return nil, false
}
3b. Implement Migration Function
Create the actual migration function (typically in the same package that owns the data):
func yourCustomMigrationFunction(tx *sqlc.Queries) error {
// Your custom migration logic here
// Example: data transformation, index creation, etc.
return nil
}
Important Guidelines
Version Numbering
- Schema Version: Must match the SQL file number (e.g.,
000008
→SchemaVersion: 8
) - Migration Version: Must be sequential and unique across all migrations
- File Naming: Use format
000XXX_descriptive_name.{up|down}.sql
Build Tags
- Production builds (default): Use
migrations_prod.go
andconfig_prod.go
- Test builds: Use
migrations_dev.go
andconfig_test_native_sql.go
- The build system automatically includes the appropriate files based on build tags
Custom Migrations
- No SQL files: Custom migrations do NOT have
.up.sql
or.down.sql
files - they are pure Go code - Custom KV→SQL migrations should use the same
SchemaVersion
as their corresponding SQL migration - KV migration
Version
should be higher than the SQL migration version - Custom migrations only require config entries in
migrationAdditions
and implementation ingetSQLMigration
Examples
Example 1: Simple Schema Migration - 000008_graph
The 000008_graph
migration is a real example from the codebase:
-
SQL files exist:
-- sqldb/sqlc/migrations/000008_graph.up.sql -- (Contains graph schema creation SQL)
-
Migration config in migrations_dev.go:
var migrationAdditions = []MigrationConfig{ { Name: "000008_graph", Version: 9, SchemaVersion: 8, }, }
Example 2: Schema Migration with Custom Logic - 000008_graph
+ kv_graph_migration
This shows how 000008_graph
(SQL schema) works together with kv_graph_migration
(custom logic):
-
SQL files for schema:
-- sqldb/sqlc/migrations/000008_graph.up.sql -- Creates graph tables and indexes
-
Schema migration in migrations_dev.go:
var migrationAdditions = []MigrationConfig{ { Name: "000008_graph", Version: 9, SchemaVersion: 8, }, { Name: "kv_graph_migration", Version: 10, SchemaVersion: 8, // Same schema version as 000008_graph }, }
-
Custom migration logic in config_test_native_sql.go:
const graphSQLMigration = 10 func (d *DefaultDatabaseBuilder) getSQLMigration(ctx context.Context, version int, kvBackend kvdb.Backend) (func(tx *sqlc.Queries) error, bool) { switch version { case graphSQLMigration: // version 10 (kv_graph_migration) return func(tx *sqlc.Queries) error { err := graphdb.MigrateGraphToSQL(ctx, cfg, kvBackend, tx) if err != nil { return fmt.Errorf("failed to migrate graph to SQL: %w", err) } return nil }, true } return nil, false }
This pattern shows:
- Version 9: SQL schema creation (
000008_graph
) - Version 10: Data migration from KV to SQL (
kv_graph_migration
)
Common Mistakes to Avoid
- Forgetting migration config entry: Every SQL file must have a corresponding config entry
- Version number mismatch: Schema version must match SQL file number
- Missing custom migration reference: If using
MigrationFn
, must update appropriate config file - Wrong build tag file: Use prod files for releases, dev files for testing
- Non-sequential versions: Migration versions must be sequential without gaps
- Inconsistent cross-environment: Ensure both prod and dev configurations are updated if needed