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.
40 lines
1.1 KiB
Go
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))
|
|
}
|