mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
0d3b7703f7
Change-Id: Ic33bc383d00d0e354c25a0dd6080a4307d9862b6 Signed-off-by: Thomas Kosiewski <tk@coder.com>
6.2 KiB
6.2 KiB
Database Development Patterns
Database Work Overview
Database Generation Process
- Modify SQL files in
coderd/database/queries/ - Run
make gen - If errors about audit table, update
enterprise/audit/table.go - Run
make genagain - Run
make lintto catch any remaining issues
Migration Guidelines
Creating Migration Files
Location: coderd/database/migrations/
Format: {number}_{description}.{up|down}.sql
- Number must be unique and sequential
- Always include both up and down migrations
Helper Scripts
| Script | Purpose |
|---|---|
./coderd/database/migrations/create_migration.sh "migration name" |
Creates new migration files |
./coderd/database/migrations/fix_migration_numbers.sh |
Renumbers migrations to avoid conflicts |
./coderd/database/migrations/create_fixture.sh "fixture name" |
Creates test fixtures for migrations |
Database Query Organization
- MUST DO: Any changes to database - adding queries, modifying queries should be done in the
coderd/database/queries/*.sqlfiles - MUST DO: Queries are grouped in files relating to context - e.g.
prebuilds.sql,users.sql,oauth2.sql - After making changes to any
coderd/database/queries/*.sqlfiles you must runmake gento generate respective ORM changes
Handling Nullable Fields
Use sql.NullString, sql.NullBool, etc. for optional database fields:
CodeChallenge: sql.NullString{
String: params.codeChallenge,
Valid: params.codeChallenge != "",
}
Set .Valid = true when providing values.
Audit Table Updates
If adding fields to auditable types:
- Update
enterprise/audit/table.go - Add each new field with appropriate action:
ActionTrack: Field should be tracked in audit logsActionIgnore: Field should be ignored in audit logsActionSecret: Field contains sensitive data
- Run
make gento verify no audit errors
Database Architecture
Core Components
- PostgreSQL 13+ recommended for production
- Migrations managed with
migrate - Database authorization through
dbauthzpackage
Authorization Patterns
// Public endpoints needing system access (OAuth2 registration)
app, err := api.Database.GetOAuth2ProviderAppByClientID(dbauthz.AsSystemRestricted(ctx), clientID)
// Authenticated endpoints with user context
app, err := api.Database.GetOAuth2ProviderAppByClientID(ctx, clientID)
// System operations in middleware
roles, err := db.GetAuthorizationUserRoles(dbauthz.AsSystemRestricted(ctx), userID)
Common Database Issues
Migration Issues
- Migration conflicts: Use
fix_migration_numbers.shto renumber - Missing down migration: Always create both up and down files
- Schema inconsistencies: Verify against existing schema
Field Handling Issues
- Nullable field errors: Use
sql.Null*types consistently - Missing audit entries: Update
enterprise/audit/table.go
Query Issues
- Query organization: Group related queries in appropriate files
- Generated code errors: Run
make genafter query changes - Performance issues: Add appropriate indexes in migrations
Database Testing
Test Database Setup
func TestDatabaseFunction(t *testing.T) {
db := dbtestutil.NewDB(t)
// Test with real database
result, err := db.GetSomething(ctx, param)
require.NoError(t, err)
require.Equal(t, expected, result)
}
Best Practices
Schema Design
- Use appropriate data types: VARCHAR for strings, TIMESTAMP for times
- Add constraints: NOT NULL, UNIQUE, FOREIGN KEY as appropriate
- Create indexes: For frequently queried columns
- Consider performance: Normalize appropriately but avoid over-normalization
Query Writing
- Use parameterized queries: Prevent SQL injection
- Handle errors appropriately: Check for specific error types
- Use transactions: For related operations that must succeed together
- Optimize queries: Use EXPLAIN to understand query performance
Migration Writing
- Make migrations reversible: Always include down migration
- Test migrations: On copy of production data if possible
- Keep migrations small: One logical change per migration
- Document complex changes: Add comments explaining rationale
Advanced Patterns
Complex Queries
-- Example: Complex join with aggregation
SELECT
u.id,
u.username,
COUNT(w.id) as workspace_count
FROM users u
LEFT JOIN workspaces w ON u.id = w.owner_id
WHERE u.created_at > $1
GROUP BY u.id, u.username
ORDER BY workspace_count DESC;
Conditional Queries
-- Example: Dynamic filtering
SELECT * FROM oauth2_provider_apps
WHERE
($1::text IS NULL OR name ILIKE '%' || $1 || '%')
AND ($2::uuid IS NULL OR organization_id = $2)
ORDER BY created_at DESC;
Audit Patterns
// Example: Auditable database operation
func (q *sqlQuerier) UpdateUser(ctx context.Context, arg UpdateUserParams) (User, error) {
// Implementation here
// Audit the change
if auditor := audit.FromContext(ctx); auditor != nil {
auditor.Record(audit.UserUpdate{
UserID: arg.ID,
Old: oldUser,
New: newUser,
})
}
return newUser, nil
}
Debugging Database Issues
Common Debug Commands
# Check database connection
make test-postgres
# Run specific database tests
go test ./coderd/database/... -run TestSpecificFunction
# Check query generation
make gen
# Verify audit table
make lint
Debug Techniques
- Enable query logging: Set appropriate log levels
- Use database tools: pgAdmin, psql for direct inspection
- Check constraints: UNIQUE, FOREIGN KEY violations
- Analyze performance: Use EXPLAIN ANALYZE for slow queries
Troubleshooting Checklist
- Migration files exist (both up and down)
make genrun after query changes- Audit table updated for new fields
- Nullable fields use
sql.Null*types - Authorization context appropriate for endpoint type