From f69f748e1ba3ccebfe0029dc64d090391699842a Mon Sep 17 00:00:00 2001 From: jawad-arb123 Date: Fri, 10 Oct 2025 11:27:15 +0100 Subject: [PATCH 1/8] added the create and drop type --- oracle/migrator.go | 40 ++++++++++++++++++++++ tests/migrate_test.go | 79 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+) diff --git a/oracle/migrator.go b/oracle/migrator.go index b1d8b54..4a21f08 100644 --- a/oracle/migrator.go +++ b/oracle/migrator.go @@ -484,6 +484,46 @@ func (m Migrator) DropConstraint(value interface{}, name string) error { return m.Migrator.DropConstraint(value, name) } +// CreateOracleType creates or replaces an Oracle user-defined type (UDT). +func (m Migrator) CreateType(typeName, definition string) error { + if typeName == "" || definition == "" { + return fmt.Errorf("CreateOracleType: both typeName and definition are required") + } + + sql := fmt.Sprintf(`CREATE OR REPLACE TYPE "%s" AS %s`, strings.ToLower(typeName), definition) + return m.DB.Exec(sql).Error +} + +// DropOracleType drops an Oracle user-defined type safely. +func (m Migrator) Droptype(typeName string) error { + if typeName == "" { + return fmt.Errorf("DropOracleType: typeName is required") + } + + sql := fmt.Sprintf(` +BEGIN + EXECUTE IMMEDIATE 'DROP TYPE "%s" FORCE'; +EXCEPTION + WHEN OTHERS THEN + IF SQLCODE != -4043 THEN + RAISE; + END IF; +END;`, strings.ToLower(typeName)) + + return m.DB.Exec(sql).Error +} + +// HasType checks whether a user-defined type exists in Oracle. +func (m Migrator) HasType(typeName string) bool { + if typeName == "" { + return false + } + + var count int + err := m.DB.Raw(`SELECT COUNT(*) FROM USER_TYPES WHERE TYPE_NAME = UPPER(?)`, typeName).Scan(&count).Error + return err == nil && count > 0 +} + // DropIndex drops the index with the specified `name` from the table associated with `value` func (m Migrator) DropIndex(value interface{}, name string) error { return m.RunWithValue(value, func(stmt *gorm.Statement) error { diff --git a/tests/migrate_test.go b/tests/migrate_test.go index 2da7589..4ee79a1 100644 --- a/tests/migrate_test.go +++ b/tests/migrate_test.go @@ -50,6 +50,7 @@ import ( "time" + "github.com/oracle-samples/gorm-oracle/oracle" . "github.com/oracle-samples/gorm-oracle/tests/utils" "github.com/stretchr/testify/assert" @@ -1970,6 +1971,84 @@ func TestOracleSequences(t *testing.T) { } } +func TestOracleTypeCreateDrop(t *testing.T) { + if DB.Dialector.Name() != "oracle" { + t.Skip("Skipping Oracle type test: not running on Oracle") + } + + const typeName = "email_list" + const tableName = "email_varray_tab" + + // Assert that DB.Migrator() is an oracle.Migrator (so we can use Oracle-specific methods) + m, ok := DB.Migrator().(oracle.Migrator) + if !ok { + t.Skip("Skipping: current dialect migrator is not Oracle-specific") + } + + // 1️⃣ Drop type if it exists + t.Run("drop_existing_type_if_any", func(t *testing.T) { + err := m.Droptype(typeName) + if err != nil && !strings.Contains(err.Error(), "ORA-04043") { + t.Fatalf("Unexpected error dropping type: %v", err) + } + }) + + // 2️⃣ Create new VARRAY type + t.Run("create_varray_type", func(t *testing.T) { + err := m.CreateType(typeName, "VARRAY(10) OF VARCHAR2(80)") + if err != nil { + t.Fatalf("Failed to create Oracle type: %v", err) + } + + // Verify it exists + var count int + if err := DB.Raw(`SELECT COUNT(*) FROM USER_TYPES WHERE TYPE_NAME = UPPER(?)`, typeName).Scan(&count).Error; err != nil { + t.Fatalf("Failed to verify created type: %v", err) + } + if count == 0 { + t.Fatalf("Expected Oracle type %s to exist", typeName) + } + }) + + // 3️⃣ Create table using the custom type + t.Run("create_table_using_custom_type", func(t *testing.T) { + createTableSQL := fmt.Sprintf(` + CREATE TABLE "%s" ( + "ID" NUMBER PRIMARY KEY, + "EMAILS" "%s" + )`, tableName, typeName) + + if err := DB.Exec(createTableSQL).Error; err != nil { + t.Fatalf("Failed to create table using type %s: %v", typeName, err) + } + + // Verify table exists + if !m.HasTable(tableName) { + t.Fatalf("Expected table %s to exist", tableName) + } + }) + + // 4️⃣ Drop table and type + t.Run("drop_table_and_type", func(t *testing.T) { + if err := m.DropTable(tableName); err != nil { + t.Fatalf("Failed to drop table %s: %v", tableName, err) + } + + if err := m.Droptype(typeName); err != nil { + t.Fatalf("Failed to drop type %s: %v", typeName, err) + } + + // Verify type is gone + var count int + if err := DB.Raw(`SELECT COUNT(*) FROM USER_TYPES WHERE TYPE_NAME = LOWER(?)`, typeName).Scan(&count).Error; err != nil { + t.Fatalf("Failed to verify dropped type: %v", err) + } + if count > 0 { + t.Fatalf("Expected Oracle type %s to be dropped", typeName) + } + }) +} + func TestOracleIndexes(t *testing.T) { if DB.Dialector.Name() != "oracle" { return From 3fa3085504c5cd4ecbb15fa53832063b9b8c27cb Mon Sep 17 00:00:00 2001 From: jawad-arb123 Date: Mon, 13 Oct 2025 15:30:07 +0100 Subject: [PATCH 2/8] code refactoring --- oracle/migrator.go | 22 +++++++--------------- tests/migrate_test.go | 14 +++++++------- 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/oracle/migrator.go b/oracle/migrator.go index 4a21f08..810ab2a 100644 --- a/oracle/migrator.go +++ b/oracle/migrator.go @@ -484,31 +484,23 @@ func (m Migrator) DropConstraint(value interface{}, name string) error { return m.Migrator.DropConstraint(value, name) } -// CreateOracleType creates or replaces an Oracle user-defined type (UDT). -func (m Migrator) CreateType(typeName, definition string) error { - if typeName == "" || definition == "" { +// CreateType creates or replaces an Oracle user-defined type. +func (m Migrator) CreateType(typeName, typeKind, typeof string) error { + if typeName == "" || typeKind == "" || typeof == "" { return fmt.Errorf("CreateOracleType: both typeName and definition are required") } - sql := fmt.Sprintf(`CREATE OR REPLACE TYPE "%s" AS %s`, strings.ToLower(typeName), definition) + sql := fmt.Sprintf(`CREATE OR REPLACE TYPE "%s" AS %s OF %s`, strings.ToLower(typeName), typeKind, typeof) return m.DB.Exec(sql).Error } -// DropOracleType drops an Oracle user-defined type safely. -func (m Migrator) Droptype(typeName string) error { +// DropType drops an Oracle user-defined type safely. +func (m Migrator) DropType(typeName string) error { if typeName == "" { return fmt.Errorf("DropOracleType: typeName is required") } - sql := fmt.Sprintf(` -BEGIN - EXECUTE IMMEDIATE 'DROP TYPE "%s" FORCE'; -EXCEPTION - WHEN OTHERS THEN - IF SQLCODE != -4043 THEN - RAISE; - END IF; -END;`, strings.ToLower(typeName)) + sql := fmt.Sprintf(`DROP TYPE "%s";`, strings.ToLower(typeName)) return m.DB.Exec(sql).Error } diff --git a/tests/migrate_test.go b/tests/migrate_test.go index 4ee79a1..51ba1d6 100644 --- a/tests/migrate_test.go +++ b/tests/migrate_test.go @@ -1985,17 +1985,17 @@ func TestOracleTypeCreateDrop(t *testing.T) { t.Skip("Skipping: current dialect migrator is not Oracle-specific") } - // 1️⃣ Drop type if it exists + // Drop type if it exists t.Run("drop_existing_type_if_any", func(t *testing.T) { - err := m.Droptype(typeName) + err := m.DropType(typeName) if err != nil && !strings.Contains(err.Error(), "ORA-04043") { t.Fatalf("Unexpected error dropping type: %v", err) } }) - // 2️⃣ Create new VARRAY type + // Create new VARRAY type t.Run("create_varray_type", func(t *testing.T) { - err := m.CreateType(typeName, "VARRAY(10) OF VARCHAR2(80)") + err := m.CreateType(typeName, "VARRAY(10)", "VARCHAR2(60)") if err != nil { t.Fatalf("Failed to create Oracle type: %v", err) } @@ -2010,7 +2010,7 @@ func TestOracleTypeCreateDrop(t *testing.T) { } }) - // 3️⃣ Create table using the custom type + // Create table using the custom type t.Run("create_table_using_custom_type", func(t *testing.T) { createTableSQL := fmt.Sprintf(` CREATE TABLE "%s" ( @@ -2028,13 +2028,13 @@ func TestOracleTypeCreateDrop(t *testing.T) { } }) - // 4️⃣ Drop table and type + // Drop table and type t.Run("drop_table_and_type", func(t *testing.T) { if err := m.DropTable(tableName); err != nil { t.Fatalf("Failed to drop table %s: %v", tableName, err) } - if err := m.Droptype(typeName); err != nil { + if err := m.DropType(typeName); err != nil { t.Fatalf("Failed to drop type %s: %v", typeName, err) } From c66768198a62db58ab082c9096041bef79a1ee3d Mon Sep 17 00:00:00 2001 From: jawad-arb123 Date: Mon, 13 Oct 2025 15:39:16 +0100 Subject: [PATCH 3/8] update the drop type syntax --- oracle/migrator.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oracle/migrator.go b/oracle/migrator.go index 810ab2a..3646bab 100644 --- a/oracle/migrator.go +++ b/oracle/migrator.go @@ -500,7 +500,7 @@ func (m Migrator) DropType(typeName string) error { return fmt.Errorf("DropOracleType: typeName is required") } - sql := fmt.Sprintf(`DROP TYPE "%s";`, strings.ToLower(typeName)) + sql := fmt.Sprintf(`DROP TYPE "%s"`, strings.ToLower(typeName)) return m.DB.Exec(sql).Error } From dfa4ed62c32073fc8d0a6813b0622fa2c9622177 Mon Sep 17 00:00:00 2001 From: jawad-arb123 Date: Mon, 13 Oct 2025 15:51:17 +0100 Subject: [PATCH 4/8] update the Drop Type method --- oracle/migrator.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/oracle/migrator.go b/oracle/migrator.go index 3646bab..dc24827 100644 --- a/oracle/migrator.go +++ b/oracle/migrator.go @@ -500,8 +500,7 @@ func (m Migrator) DropType(typeName string) error { return fmt.Errorf("DropOracleType: typeName is required") } - sql := fmt.Sprintf(`DROP TYPE "%s"`, strings.ToLower(typeName)) - + sql := fmt.Sprintf(`DROP TYPE "%s" FORCE`, strings.ToLower(typeName)) return m.DB.Exec(sql).Error } From 6197a05241877bb0a38653a736293cd5e0f8d379 Mon Sep 17 00:00:00 2001 From: jawad-arb123 Date: Mon, 13 Oct 2025 16:14:41 +0100 Subject: [PATCH 5/8] update drop type --- oracle/migrator.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/oracle/migrator.go b/oracle/migrator.go index dc24827..c8b2e6b 100644 --- a/oracle/migrator.go +++ b/oracle/migrator.go @@ -496,11 +496,16 @@ func (m Migrator) CreateType(typeName, typeKind, typeof string) error { // DropType drops an Oracle user-defined type safely. func (m Migrator) DropType(typeName string) error { - if typeName == "" { - return fmt.Errorf("DropOracleType: typeName is required") - } + sql := fmt.Sprintf(` + BEGIN + EXECUTE IMMEDIATE 'DROP TYPE "%s" FORCE'; + EXCEPTION + WHEN OTHERS THEN + IF SQLCODE != -4043 THEN + RAISE; + END IF; + END;`, strings.ToLower(typeName)) - sql := fmt.Sprintf(`DROP TYPE "%s" FORCE`, strings.ToLower(typeName)) return m.DB.Exec(sql).Error } From ed4ce523e41ef7d15db21fbdda3fa82acf5e587b Mon Sep 17 00:00:00 2001 From: jawad-arb123 Date: Mon, 13 Oct 2025 16:19:11 +0100 Subject: [PATCH 6/8] update Upper to Lower while verifiying --- tests/migrate_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/migrate_test.go b/tests/migrate_test.go index 51ba1d6..a8e2d48 100644 --- a/tests/migrate_test.go +++ b/tests/migrate_test.go @@ -2002,7 +2002,7 @@ func TestOracleTypeCreateDrop(t *testing.T) { // Verify it exists var count int - if err := DB.Raw(`SELECT COUNT(*) FROM USER_TYPES WHERE TYPE_NAME = UPPER(?)`, typeName).Scan(&count).Error; err != nil { + if err := DB.Raw(`SELECT COUNT(*) FROM USER_TYPES WHERE TYPE_NAME = LOWER(?)`, typeName).Scan(&count).Error; err != nil { t.Fatalf("Failed to verify created type: %v", err) } if count == 0 { From c5f8b8ec70b79a80053c35298d2b99cee95d99cc Mon Sep 17 00:00:00 2001 From: jawad-arb123 Date: Wed, 15 Oct 2025 11:52:46 +0100 Subject: [PATCH 7/8] refactoring code --- oracle/migrator.go | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/oracle/migrator.go b/oracle/migrator.go index c8b2e6b..e1050f2 100644 --- a/oracle/migrator.go +++ b/oracle/migrator.go @@ -484,32 +484,23 @@ func (m Migrator) DropConstraint(value interface{}, name string) error { return m.Migrator.DropConstraint(value, name) } -// CreateType creates or replaces an Oracle user-defined type. +// CreateType creates or replaces an Oracle user-defined type func (m Migrator) CreateType(typeName, typeKind, typeof string) error { if typeName == "" || typeKind == "" || typeof == "" { - return fmt.Errorf("CreateOracleType: both typeName and definition are required") + return fmt.Errorf("createType: both typeName and definition are required") } sql := fmt.Sprintf(`CREATE OR REPLACE TYPE "%s" AS %s OF %s`, strings.ToLower(typeName), typeKind, typeof) return m.DB.Exec(sql).Error } -// DropType drops an Oracle user-defined type safely. +// DropType drops an Oracle user-defined type func (m Migrator) DropType(typeName string) error { - sql := fmt.Sprintf(` - BEGIN - EXECUTE IMMEDIATE 'DROP TYPE "%s" FORCE'; - EXCEPTION - WHEN OTHERS THEN - IF SQLCODE != -4043 THEN - RAISE; - END IF; - END;`, strings.ToLower(typeName)) - + sql := fmt.Sprintf(`DROP TYPE "%s" FORCE`, strings.ToLower(typeName)) return m.DB.Exec(sql).Error } -// HasType checks whether a user-defined type exists in Oracle. +// HasType checks whether a user-defined type exists func (m Migrator) HasType(typeName string) bool { if typeName == "" { return false From c126f702a66311984b0d78ef1565d564010d5a37 Mon Sep 17 00:00:00 2001 From: jawad-arb123 Date: Tue, 28 Oct 2025 11:59:43 +0100 Subject: [PATCH 8/8] add support for ADT , nested table type & incomplet object type --- oracle/migrator.go | 66 +++++++++++++++++--- tests/migrate_test.go | 140 ++++++++++++++++++++++++++++++++++-------- 2 files changed, 172 insertions(+), 34 deletions(-) diff --git a/oracle/migrator.go b/oracle/migrator.go index e1050f2..10d5aa4 100644 --- a/oracle/migrator.go +++ b/oracle/migrator.go @@ -485,19 +485,67 @@ func (m Migrator) DropConstraint(value interface{}, name string) error { } // CreateType creates or replaces an Oracle user-defined type -func (m Migrator) CreateType(typeName, typeKind, typeof string) error { - if typeName == "" || typeKind == "" || typeof == "" { - return fmt.Errorf("createType: both typeName and definition are required") +func (m Migrator) CreateType(typeName string, args ...string) error { + typeName = strings.TrimSpace(typeName) + if typeName == "" { + return fmt.Errorf("typeName is required") + } + var typeKind, typeOf string + if len(args) > 0 { + typeKind = args[0] + } + if len(args) > 1 { + typeOf = args[1] } - sql := fmt.Sprintf(`CREATE OR REPLACE TYPE "%s" AS %s OF %s`, strings.ToLower(typeName), typeKind, typeof) - return m.DB.Exec(sql).Error + name := strings.ToLower(typeName) + typeKind = strings.TrimSpace(typeKind) + typeOf = strings.TrimSpace(typeOf) + + // Incomplete object type + if typeKind == "" && typeOf == "" { + ddl := fmt.Sprintf(`CREATE TYPE "%s"`, name) + return m.DB.Exec(ddl).Error + } + + k := strings.ToUpper(typeKind) + var ddl string + + switch { + // Standalone varying array (varray) type and Standalone nested table type + case strings.HasPrefix(k, "VARRAY") || strings.HasPrefix(k, "TABLE "): + if typeOf == "" { + return fmt.Errorf("typeof is required for collection types (VARRAY/TABLE)") + } + ddl = fmt.Sprintf(`CREATE OR REPLACE TYPE "%s" AS %s OF %s`, name, typeKind, typeOf) + + // Abstract Data Type (ADT) + case k == "OBJECT" || strings.HasPrefix(k, "OBJECT"): + if typeOf == "" { + return fmt.Errorf("attributes definition is required for OBJECT types") + } + attrs := typeOf + if !strings.HasPrefix(attrs, "(") { + attrs = "(" + attrs + ")" + } + ddl = fmt.Sprintf(`CREATE OR REPLACE TYPE "%s" AS OBJECT %s`, name, attrs) + + default: + // Invalid or unsupported types + return fmt.Errorf("unsupported type kind %q (must be OBJECT, VARRAY, or TABLE)", typeKind) + } + + return m.DB.Exec(ddl).Error } -// DropType drops an Oracle user-defined type +// DropType drops a user-defined type func (m Migrator) DropType(typeName string) error { - sql := fmt.Sprintf(`DROP TYPE "%s" FORCE`, strings.ToLower(typeName)) - return m.DB.Exec(sql).Error + typeName = strings.TrimSpace(typeName) + if typeName == "" { + return fmt.Errorf("dropType: typeName is required") + } + ddl := fmt.Sprintf(`DROP TYPE "%s" FORCE`, strings.ToLower(typeName)) + return m.DB.Exec(ddl).Error } // HasType checks whether a user-defined type exists @@ -507,7 +555,7 @@ func (m Migrator) HasType(typeName string) bool { } var count int - err := m.DB.Raw(`SELECT COUNT(*) FROM USER_TYPES WHERE TYPE_NAME = UPPER(?)`, typeName).Scan(&count).Error + err := m.DB.Raw(`SELECT COUNT(*) FROM USER_TYPES WHERE UPPER(TYPE_NAME) = UPPER(?)`, typeName).Scan(&count).Error return err == nil && count > 0 } diff --git a/tests/migrate_test.go b/tests/migrate_test.go index a8e2d48..3e4f694 100644 --- a/tests/migrate_test.go +++ b/tests/migrate_test.go @@ -1976,8 +1976,16 @@ func TestOracleTypeCreateDrop(t *testing.T) { t.Skip("Skipping Oracle type test: not running on Oracle") } - const typeName = "email_list" - const tableName = "email_varray_tab" + const ( + typeName = "email_list" + tableName = "email_varray_tab" + + objectTypeName = "person_obj" + objectTableName = "person_obj_tab" + + incompleteTypeName = "department_t" + unsupportedTypeName = "unsupported_type_t" + ) // Assert that DB.Migrator() is an oracle.Migrator (so we can use Oracle-specific methods) m, ok := DB.Migrator().(oracle.Migrator) @@ -1985,11 +1993,16 @@ func TestOracleTypeCreateDrop(t *testing.T) { t.Skip("Skipping: current dialect migrator is not Oracle-specific") } - // Drop type if it exists - t.Run("drop_existing_type_if_any", func(t *testing.T) { - err := m.DropType(typeName) - if err != nil && !strings.Contains(err.Error(), "ORA-04043") { - t.Fatalf("Unexpected error dropping type: %v", err) + // Drop types if they exist + t.Run("drop_existing_types_if_any", func(t *testing.T) { + if err := m.DropType(typeName); err != nil && !strings.Contains(err.Error(), "ORA-04043") { + t.Fatalf("Unexpected error dropping type %s: %v", typeName, err) + } + if err := m.DropType(objectTypeName); err != nil && !strings.Contains(err.Error(), "ORA-04043") { + t.Fatalf("Unexpected error dropping type %s: %v", objectTypeName, err) + } + if err := m.DropType(incompleteTypeName); err != nil && !strings.Contains(err.Error(), "ORA-04043") { + t.Fatalf("Unexpected error dropping type %s: %v", incompleteTypeName, err) } }) @@ -1997,21 +2010,17 @@ func TestOracleTypeCreateDrop(t *testing.T) { t.Run("create_varray_type", func(t *testing.T) { err := m.CreateType(typeName, "VARRAY(10)", "VARCHAR2(60)") if err != nil { - t.Fatalf("Failed to create Oracle type: %v", err) + t.Fatalf("Failed to create Oracle VARRAY type: %v", err) } - // Verify it exists - var count int - if err := DB.Raw(`SELECT COUNT(*) FROM USER_TYPES WHERE TYPE_NAME = LOWER(?)`, typeName).Scan(&count).Error; err != nil { - t.Fatalf("Failed to verify created type: %v", err) - } - if count == 0 { - t.Fatalf("Expected Oracle type %s to exist", typeName) + // Verify it exists via HasType + if !m.HasType(typeName) { + t.Fatalf("Expected Oracle VARRAY type %s to exist", typeName) } }) - // Create table using the custom type - t.Run("create_table_using_custom_type", func(t *testing.T) { + // Create table using the VARRAY type + t.Run("create_table_using_varray_type", func(t *testing.T) { createTableSQL := fmt.Sprintf(` CREATE TABLE "%s" ( "ID" NUMBER PRIMARY KEY, @@ -2028,24 +2037,105 @@ func TestOracleTypeCreateDrop(t *testing.T) { } }) - // Drop table and type - t.Run("drop_table_and_type", func(t *testing.T) { + // Create ADT (OBJECT) type + t.Run("create_object_type", func(t *testing.T) { + err := m.CreateType(objectTypeName, "OBJECT", ` + first_name VARCHAR2(50), + last_name VARCHAR2(50), + age NUMBER + `) + if err != nil { + t.Fatalf("Failed to create Oracle OBJECT type: %v", err) + } + + // Verify it exists via HasType + if !m.HasType(objectTypeName) { + t.Fatalf("Expected Oracle OBJECT type %s to exist", objectTypeName) + } + }) + + // Create table using the OBJECT type + t.Run("create_table_using_object_type", func(t *testing.T) { + createTableSQL := fmt.Sprintf(` + CREATE TABLE "%s" ( + "ID" NUMBER PRIMARY KEY, + "PERSON" "%s" + )`, objectTableName, objectTypeName) + + if err := DB.Exec(createTableSQL).Error; err != nil { + t.Fatalf("Failed to create table using object type %s: %v", objectTypeName, err) + } + + // Verify table exists + if !m.HasTable(objectTableName) { + t.Fatalf("Expected table %s to exist", objectTableName) + } + }) + + // Create incomplete type (forward declaration) + t.Run("create_incomplete_type", func(t *testing.T) { + if err := m.CreateType(incompleteTypeName); err != nil { + t.Fatalf("Failed to create incomplete type %s: %v", incompleteTypeName, err) + } + if !m.HasType(incompleteTypeName) { + t.Fatalf("Expected incomplete type %s to exist", incompleteTypeName) + } + if err := m.DropType(incompleteTypeName); err != nil { + t.Fatalf("Failed to drop incomplete type %s: %v", incompleteTypeName, err) + } + if m.HasType(incompleteTypeName) { + t.Fatalf("Expected incomplete type %s to be dropped", incompleteTypeName) + } + }) + + // Unsupported type kinds should return an error and not create anything + t.Run("create_unsupported_type", func(t *testing.T) { + err := m.CreateType(unsupportedTypeName, "Unsupported", "Unsupported") + if err == nil { + t.Fatalf("Expected error when creating unsupported type %s, got nil", unsupportedTypeName) + } + + // Ensure the type was NOT created + if m.HasType(unsupportedTypeName) { + t.Fatalf("Type %s should not exist after failed creation", unsupportedTypeName) + } + + // Also ensure DropType is safe to call (idempotent) + if err := m.DropType(unsupportedTypeName); err != nil { + if !strings.Contains(strings.ToLower(err.Error()), "does not exist") { + t.Fatalf("Unexpected error dropping type %s: %v", unsupportedTypeName, err) + } + } + + if m.HasType(unsupportedTypeName) { + t.Fatalf("Expected type %s to be absent after drop", unsupportedTypeName) + } + }) + + // Drop tables and types + t.Run("drop_tables_and_types", func(t *testing.T) { + if err := m.DropTable(objectTableName); err != nil { + t.Fatalf("Failed to drop table %s: %v", objectTableName, err) + } if err := m.DropTable(tableName); err != nil { t.Fatalf("Failed to drop table %s: %v", tableName, err) } + // Drop types + if err := m.DropType(objectTypeName); err != nil { + t.Fatalf("Failed to drop type %s: %v", objectTypeName, err) + } if err := m.DropType(typeName); err != nil { t.Fatalf("Failed to drop type %s: %v", typeName, err) } - // Verify type is gone - var count int - if err := DB.Raw(`SELECT COUNT(*) FROM USER_TYPES WHERE TYPE_NAME = LOWER(?)`, typeName).Scan(&count).Error; err != nil { - t.Fatalf("Failed to verify dropped type: %v", err) - } - if count > 0 { + // Verify types are gone via HasType + if m.HasType(typeName) { t.Fatalf("Expected Oracle type %s to be dropped", typeName) } + if m.HasType(objectTypeName) { + t.Fatalf("Expected Oracle type %s to be dropped", objectTypeName) + } }) }