feat(pgsql): support vector and PostGIS indexes with extensions
* Add handling for pgvector and PostGIS extensions in migration scripts * Implement operator class and storage parameters for vector indexes * Update tests to validate new index behaviors and extension creation
This commit is contained in:
@@ -852,3 +852,93 @@ func TestWriteMigration_NilCurrentTreatsDatabaseAsEmpty(t *testing.T) {
|
||||
t.Fatalf("expected CREATE TABLE in migration output, got:\n%s", output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteMigration_VectorAndPostGISIndexes(t *testing.T) {
|
||||
current := models.InitDatabase("testdb")
|
||||
current.Schemas = append(current.Schemas, models.InitSchema("public"))
|
||||
|
||||
model := models.InitDatabase("testdb")
|
||||
modelSchema := models.InitSchema("public")
|
||||
|
||||
table := models.InitTable("documents", "public")
|
||||
|
||||
embedding := models.InitColumn("embedding", "documents", "public")
|
||||
embedding.Type = "vector(1536)"
|
||||
table.Columns["embedding"] = embedding
|
||||
|
||||
location := models.InitColumn("location", "documents", "public")
|
||||
location.Type = "geometry(Point,4326)"
|
||||
table.Columns["location"] = location
|
||||
|
||||
table.Indexes["idx_documents_embedding"] = &models.Index{
|
||||
Name: "idx_documents_embedding",
|
||||
Type: "ivfflat",
|
||||
Columns: []string{"embedding"},
|
||||
Comment: "opclass=vector_cosine_ops; with (lists=100)",
|
||||
}
|
||||
table.Indexes["idx_documents_location"] = &models.Index{
|
||||
Name: "idx_documents_location",
|
||||
Type: "gist",
|
||||
Columns: []string{"location"},
|
||||
}
|
||||
|
||||
modelSchema.Tables = append(modelSchema.Tables, table)
|
||||
model.Schemas = append(model.Schemas, modelSchema)
|
||||
|
||||
var buf bytes.Buffer
|
||||
writer, err := NewMigrationWriter(&writers.WriterOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create writer: %v", err)
|
||||
}
|
||||
writer.writer = &buf
|
||||
|
||||
if err := writer.WriteMigration(model, current); err != nil {
|
||||
t.Fatalf("WriteMigration failed: %v", err)
|
||||
}
|
||||
|
||||
output := buf.String()
|
||||
for _, want := range []string{
|
||||
"CREATE EXTENSION IF NOT EXISTS postgis;",
|
||||
"CREATE EXTENSION IF NOT EXISTS vector;",
|
||||
"vector(1536)",
|
||||
"geometry(Point,4326)",
|
||||
"USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100)",
|
||||
"USING gist (location)",
|
||||
} {
|
||||
if !strings.Contains(output, want) {
|
||||
t.Fatalf("expected migration to contain %q, got:\n%s", want, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIndexesEqual_OperatorClassAndStorageParameters(t *testing.T) {
|
||||
newIndex := func(comment string) *models.Index {
|
||||
return &models.Index{
|
||||
Name: "idx_documents_embedding",
|
||||
Type: "hnsw",
|
||||
Columns: []string{"embedding"},
|
||||
Comment: comment,
|
||||
}
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
comment1 string
|
||||
comment2 string
|
||||
wantEqual bool
|
||||
}{
|
||||
{"identical hints", "opclass=vector_l2_ops", "opclass=vector_l2_ops", true},
|
||||
{"different operator class", "opclass=vector_l2_ops", "opclass=vector_cosine_ops", false},
|
||||
{"different storage parameters", "with (m=16)", "with (m=32)", false},
|
||||
{"unspecified hint on one side", "", "opclass=vector_l2_ops; with (m=16)", true},
|
||||
{"unrelated comments", "primary lookup index", "primary lookup index", true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := indexesEqual(newIndex(tt.comment1), newIndex(tt.comment2)); got != tt.wantEqual {
|
||||
t.Errorf("indexesEqual() = %v, want %v", got, tt.wantEqual)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user