feat: Enhance PostgreSQL type handling and migration scripts
- Introduced equivalent base types and variants for PostgreSQL types to normalize type comparisons. - Added functions for normalizing SQL types and retrieving equivalent type variants. - Updated migration writer to handle type alterations with checks for existing types. - Implemented logic to create necessary extensions (e.g., pg_trgm) based on schema requirements. - Enhanced tests to cover new functionality for type normalization and migration handling. - Improved handling of GIN indexes to use appropriate operator classes based on column types.
This commit is contained in:
@@ -116,8 +116,8 @@ func TestWriteDatabase_GinIndexOnTextArrayDoesNotUseTrigramOperatorClass(t *test
|
||||
}
|
||||
|
||||
output := buf.String()
|
||||
if !strings.Contains(output, `USING gin (tags)`) {
|
||||
t.Fatalf("expected GIN index on array column without explicit trigram opclass, got:\n%s", output)
|
||||
if !strings.Contains(output, `USING gin (tags array_ops)`) {
|
||||
t.Fatalf("expected GIN index on array column with array_ops, got:\n%s", output)
|
||||
}
|
||||
if strings.Contains(output, "gin_trgm_ops") {
|
||||
t.Fatalf("did not expect gin_trgm_ops for text[] column, got:\n%s", output)
|
||||
@@ -153,11 +153,51 @@ func TestWriteDatabase_GinIndexOnQuotedTextColumnUsesTrigramOperatorClass(t *tes
|
||||
}
|
||||
|
||||
output := buf.String()
|
||||
if !strings.Contains(output, `CREATE EXTENSION IF NOT EXISTS pg_trgm`) {
|
||||
t.Fatalf("expected trigram extension for text GIN index, got:\n%s", output)
|
||||
}
|
||||
if !strings.Contains(output, `USING gin (name gin_trgm_ops)`) {
|
||||
t.Fatalf("expected quoted text GIN index to include gin_trgm_ops, got:\n%s", output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteDatabase_GinIndexOnJSONBUsesJSONBOperatorClass(t *testing.T) {
|
||||
db := models.InitDatabase("testdb")
|
||||
schema := models.InitSchema("public")
|
||||
|
||||
table := models.InitTable("learnings", "public")
|
||||
|
||||
detailsCol := models.InitColumn("details", "learnings", "public")
|
||||
detailsCol.Type = "jsonb"
|
||||
table.Columns["details"] = detailsCol
|
||||
|
||||
index := &models.Index{
|
||||
Name: "idx_learnings_details",
|
||||
Type: "gin",
|
||||
Columns: []string{"details"},
|
||||
}
|
||||
table.Indexes[index.Name] = index
|
||||
|
||||
schema.Tables = append(schema.Tables, table)
|
||||
db.Schemas = append(db.Schemas, schema)
|
||||
|
||||
var buf bytes.Buffer
|
||||
writer := NewWriter(&writers.WriterOptions{})
|
||||
writer.writer = &buf
|
||||
|
||||
if err := writer.WriteDatabase(db); err != nil {
|
||||
t.Fatalf("WriteDatabase failed: %v", err)
|
||||
}
|
||||
|
||||
output := buf.String()
|
||||
if !strings.Contains(output, `USING gin (details jsonb_ops)`) {
|
||||
t.Fatalf("expected GIN jsonb index to include jsonb_ops, got:\n%s", output)
|
||||
}
|
||||
if strings.Contains(output, "gin_trgm_ops") {
|
||||
t.Fatalf("did not expect gin_trgm_ops for jsonb column, got:\n%s", output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteForeignKeys(t *testing.T) {
|
||||
// Create a test database with two related tables
|
||||
db := models.InitDatabase("testdb")
|
||||
@@ -1018,3 +1058,82 @@ func TestWriteAddColumnStatements(t *testing.T) {
|
||||
t.Errorf("Output missing DO block\nFull output:\n%s", output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteSchema_EmitsGuardedAlterColumnTypeStatements(t *testing.T) {
|
||||
db := models.InitDatabase("testdb")
|
||||
schema := models.InitSchema("public")
|
||||
|
||||
table := models.InitTable("agent_skills", "public")
|
||||
|
||||
nameCol := models.InitColumn("name", "agent_skills", "public")
|
||||
nameCol.Type = "character varying"
|
||||
nameCol.Length = 255
|
||||
table.Columns["name"] = nameCol
|
||||
|
||||
tagsCol := models.InitColumn("tags", "agent_skills", "public")
|
||||
tagsCol.Type = "text[]"
|
||||
table.Columns["tags"] = tagsCol
|
||||
|
||||
schema.Tables = append(schema.Tables, table)
|
||||
db.Schemas = append(db.Schemas, schema)
|
||||
|
||||
var buf bytes.Buffer
|
||||
writer := NewWriter(&writers.WriterOptions{})
|
||||
writer.writer = &buf
|
||||
|
||||
if err := writer.WriteDatabase(db); err != nil {
|
||||
t.Fatalf("WriteDatabase failed: %v", err)
|
||||
}
|
||||
|
||||
output := buf.String()
|
||||
if !strings.Contains(output, "-- Alter column types for schema: public") {
|
||||
t.Fatalf("expected alter column type section, got:\n%s", output)
|
||||
}
|
||||
if !strings.Contains(output, "pg_catalog.format_type") {
|
||||
t.Fatalf("expected guarded live-type check, got:\n%s", output)
|
||||
}
|
||||
if !strings.Contains(output, "ALTER COLUMN name TYPE character varying(255)") {
|
||||
t.Fatalf("expected guarded alter for character varying(255), got:\n%s", output)
|
||||
}
|
||||
if !strings.Contains(output, "ARRAY['varchar(255)', 'character varying(255)']") {
|
||||
t.Fatalf("expected equivalent type spellings for varchar guard, got:\n%s", output)
|
||||
}
|
||||
if !strings.Contains(output, "ALTER COLUMN tags TYPE text[]") {
|
||||
t.Fatalf("expected guarded alter for array type, got:\n%s", output)
|
||||
}
|
||||
if !strings.Contains(output, `ALTER COLUMN tags TYPE text[] USING tags::text[];`) {
|
||||
t.Fatalf("expected guarded alter for array type to include USING cast, got:\n%s", output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteSchema_UsesStorageTypeForSerialAlterStatements(t *testing.T) {
|
||||
db := models.InitDatabase("testdb")
|
||||
schema := models.InitSchema("public")
|
||||
|
||||
table := models.InitTable("learnings", "public")
|
||||
idCol := models.InitColumn("id", "learnings", "public")
|
||||
idCol.Type = "bigserial"
|
||||
table.Columns["id"] = idCol
|
||||
|
||||
schema.Tables = append(schema.Tables, table)
|
||||
db.Schemas = append(db.Schemas, schema)
|
||||
|
||||
var buf bytes.Buffer
|
||||
writer := NewWriter(&writers.WriterOptions{})
|
||||
writer.writer = &buf
|
||||
|
||||
if err := writer.WriteDatabase(db); err != nil {
|
||||
t.Fatalf("WriteDatabase failed: %v", err)
|
||||
}
|
||||
|
||||
output := buf.String()
|
||||
if !strings.Contains(output, "ALTER COLUMN id TYPE bigint") {
|
||||
t.Fatalf("expected serial alter to use bigint storage type, got:\n%s", output)
|
||||
}
|
||||
if strings.Contains(output, "ALTER COLUMN id TYPE bigserial;") {
|
||||
t.Fatalf("did not expect invalid bigserial alter statement, got:\n%s", output)
|
||||
}
|
||||
if !strings.Contains(output, `ALTER COLUMN id TYPE bigint USING id::bigint;`) {
|
||||
t.Fatalf("expected serial alter to include USING cast, got:\n%s", output)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user