Files
migrate/sqlite.go
Colin Henry b4529e018f Restructure dialects and add PostgreSQL support
Architecture Changes:
- Split dialect implementations into separate files for better organization
- Move SQLite dialect from dialect.go to sqlite.go
- Keep only Dialect interface in dialect.go with comprehensive documentation
- Each dialect now in its own file following single responsibility principle

New Features:
- Add PostgreSQL dialect support (Postgres() function)
- PostgreSQL uses SERIAL PRIMARY KEY (auto-incrementing integer)
- PostgreSQL uses $1, $2 placeholders instead of ? for parameters
- PostgreSQL uses SELECT 1 for table existence check (more efficient)
- Both dialects implement proper SQL identifier quoting for security

Testing:
- Add comprehensive dialect-specific tests in sqlite_test.go
- Add comprehensive dialect-specific tests in postgres_test.go
- Test SQL generation for all dialect methods
- Test SQL injection protection via identifier escaping
- All tests pass (8 test functions, 10 subtests)

Documentation:
- Update README with PostgreSQL usage example
- Add "Supported Databases" section listing SQLite and PostgreSQL
- Improve code examples with proper imports and error handling
- Document how to implement Dialect interface for other databases

File Structure:
- dialect.go: Interface definition only (18 lines)
- sqlite.go: SQLite dialect implementation (39 lines)
- postgres.go: PostgreSQL dialect implementation (42 lines)
- sqlite_test.go: SQLite dialect tests (67 lines)
- postgres_test.go: PostgreSQL dialect tests (67 lines)

Security:
- Both dialects use quoteIdentifier() for SQL injection protection
- Identifiers are quoted and internal quotes are escaped
- Follows SQL standard quoting mechanism (double quotes for escaping)

This change maintains backward compatibility while adding PostgreSQL support
and improving code organization for future dialect additions.
2026-01-18 01:34:09 -08:00

40 lines
1.1 KiB
Go

package migrate
import (
"fmt"
"strings"
)
// Sqlite3 returns a SQLite dialect implementation
func Sqlite3() Dialect {
return sqlite3{}
}
type sqlite3 struct{}
// quoteIdentifier safely quotes a SQL identifier to prevent SQL injection
func (s sqlite3) quoteIdentifier(identifier string) string {
// Replace any existing quotes with double quotes (SQL escape mechanism)
escaped := strings.ReplaceAll(identifier, `"`, `""`)
return fmt.Sprintf(`"%s"`, escaped)
}
func (s sqlite3) CreateTable(table string) string {
return fmt.Sprintf(`CREATE TABLE %s (
id INTEGER PRIMARY KEY AUTOINCREMENT,
description VARCHAR,
applied TIMESTAMP);`, s.quoteIdentifier(table))
}
func (s sqlite3) TableExists(table string) string {
return fmt.Sprintf("SELECT * FROM %s;", s.quoteIdentifier(table))
}
func (s sqlite3) CheckVersion(table string) string {
return fmt.Sprintf("SELECT id FROM %s ORDER BY id DESC LIMIT 0, 1;", s.quoteIdentifier(table))
}
func (s sqlite3) InsertVersion(table string) string {
return fmt.Sprintf("INSERT INTO %s(description, applied) VALUES (?,?);", s.quoteIdentifier(table))
}