Skip to content

Commit 3395b48

Browse files
committed
feat: add IsSchemaQualified method to skip hint for schema-qualified types
1 parent dcf72de commit 3395b48

File tree

2 files changed

+25
-20
lines changed

2 files changed

+25
-20
lines changed

pkg/migration/file.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@ type MigrationFile struct {
2626
Statements []string
2727
}
2828

29-
var migrateFilePattern = regexp.MustCompile(`^([0-9]+)_(.*)\.sql$`)
29+
var (
30+
migrateFilePattern = regexp.MustCompile(`^([0-9]+)_(.*)\.sql$`)
31+
typeNamePattern = regexp.MustCompile(`type "([^"]+)" does not exist`)
32+
)
3033

3134
func NewMigrationFromFile(path string, fsys fs.FS) (*MigrationFile, error) {
3235
lines, err := parseFile(path, fsys)
@@ -97,17 +100,11 @@ func (m *MigrationFile) ExecBatch(ctx context.Context, conn *pgx.Conn) error {
97100
msg = append(msg, pgErr.Detail)
98101
}
99102
// Provide helpful hint for extension type errors (SQLSTATE 42704: undefined_object)
100-
if pgErr.Code == "42704" && strings.Contains(pgErr.Message, "type") && strings.Contains(pgErr.Message, "does not exist") {
101-
// Extract type name from error message (e.g., 'type "ltree" does not exist')
102-
typeName := extractTypeName(pgErr.Message)
103+
if typeName := extractTypeName(pgErr.Message); len(typeName) > 0 && pgErr.Code == "42704" && !IsSchemaQualified(typeName) {
103104
msg = append(msg, "")
104105
msg = append(msg, "Hint: This type may be defined in a schema that's not in your search_path.")
105106
msg = append(msg, " Use schema-qualified type references to avoid this error:")
106-
if typeName != "" {
107-
msg = append(msg, fmt.Sprintf(" CREATE TABLE example (col extensions.%s);", typeName))
108-
} else {
109-
msg = append(msg, " CREATE TABLE example (col extensions.<type_name>);")
110-
}
107+
msg = append(msg, fmt.Sprintf(" CREATE TABLE example (col extensions.%s);", typeName))
111108
msg = append(msg, " Learn more: supabase migration new --help")
112109
}
113110
}
@@ -137,15 +134,18 @@ func markError(stat string, pos int) string {
137134
// extractTypeName extracts the type name from PostgreSQL error messages like:
138135
// 'type "ltree" does not exist' -> "ltree"
139136
func extractTypeName(errMsg string) string {
140-
// Match pattern: type "typename" does not exist
141-
re := regexp.MustCompile(`type "([^"]+)" does not exist`)
142-
matches := re.FindStringSubmatch(errMsg)
137+
matches := typeNamePattern.FindStringSubmatch(errMsg)
143138
if len(matches) > 1 {
144139
return matches[1]
145140
}
146141
return ""
147142
}
148143

144+
// IsSchemaQualified checks if a type name already contains a schema qualifier (e.g., "extensions.ltree")
145+
func IsSchemaQualified(typeName string) bool {
146+
return strings.Contains(typeName, ".")
147+
}
148+
149149
func (m *MigrationFile) insertVersionSQL(conn *pgx.Conn, batch *pgconn.Batch) error {
150150
value := pgtype.TextArray{}
151151
if err := value.Set(m.Statements); err != nil {

pkg/migration/file_test.go

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -100,24 +100,29 @@ func TestMigrationFile(t *testing.T) {
100100
assert.ErrorContains(t, err, "At statement: 0")
101101
})
102102

103-
t.Run("provides generic hint when type name cannot be extracted", func(t *testing.T) {
103+
t.Run("skips hint for schema-qualified type errors", func(t *testing.T) {
104104
migration := MigrationFile{
105-
Statements: []string{"CREATE TABLE test (id custom_type)"},
105+
Statements: []string{"CREATE TABLE test (path extensions.ltree NOT NULL)"},
106106
Version: "0",
107107
}
108108
// Setup mock postgres
109109
conn := pgtest.NewConn()
110110
defer conn.Close(t)
111111
conn.Query(migration.Statements[0]).
112-
ReplyError("42704", `type does not exist`).
112+
ReplyError("42704", `type "extensions.ltree" does not exist`).
113113
Query(INSERT_MIGRATION_VERSION, "0", "", migration.Statements).
114114
Reply("INSERT 0 1")
115115
// Run test
116116
err := migration.ExecBatch(context.Background(), conn.MockClient(t))
117-
// Check error
118-
assert.ErrorContains(t, err, "type does not exist")
119-
assert.ErrorContains(t, err, "Hint: This type may be defined in a schema")
120-
assert.ErrorContains(t, err, "extensions.<type_name>")
121-
assert.ErrorContains(t, err, "supabase migration new --help")
117+
// Check error - should NOT contain hint since type is already schema-qualified
118+
assert.ErrorContains(t, err, `type "extensions.ltree" does not exist`)
119+
assert.NotContains(t, err.Error(), "Hint: This type may be defined in a schema")
122120
})
123121
}
122+
123+
func TestIsSchemaQualified(t *testing.T) {
124+
assert.True(t, IsSchemaQualified("extensions.ltree"))
125+
assert.True(t, IsSchemaQualified("public.my_type"))
126+
assert.False(t, IsSchemaQualified("ltree"))
127+
assert.False(t, IsSchemaQualified(""))
128+
}

0 commit comments

Comments
 (0)