diff --git a/oracle/common.go b/oracle/common.go index 70db86a..dd42002 100644 --- a/oracle/common.go +++ b/oracle/common.go @@ -39,13 +39,16 @@ package oracle import ( + "bytes" "database/sql" "encoding/json" "fmt" + "math" "reflect" "strings" "time" + "github.com/godror/godror" "github.com/google/uuid" "gorm.io/datatypes" "gorm.io/gorm" @@ -206,6 +209,14 @@ func convertValue(val interface{}) interface{} { return 0 } case string: + if len(v) > math.MaxInt16 { + return godror.Lob{IsClob: true, Reader: strings.NewReader(v)} + } + return v + case []byte: + if len(v) > math.MaxInt16 { + return godror.Lob{IsClob: false, Reader: bytes.NewReader(v)} + } return v default: return val diff --git a/tests/blob_test.go b/tests/blob_test.go index 7bc2c70..dc079e3 100644 --- a/tests/blob_test.go +++ b/tests/blob_test.go @@ -41,6 +41,7 @@ package tests import ( "bytes" "crypto/rand" + "strings" "testing" "time" @@ -48,26 +49,36 @@ import ( ) type BlobTestModel struct { - ID uint `gorm:"primaryKey;autoIncrement"` - Name string `gorm:"size:100;not null"` - Data []byte `gorm:"type:blob"` + ID uint `gorm:"primaryKey;autoIncrement"` + Name string `gorm:"size:100;not null"` + Data []byte `gorm:"type:blob"` OptionalData *[]byte `gorm:"type:blob"` - CreatedAt time.Time - UpdatedAt time.Time + CreatedAt time.Time + UpdatedAt time.Time } type BlobVariantModel struct { - ID uint `gorm:"primaryKey"` + ID uint `gorm:"primaryKey"` SmallBlob []byte `gorm:"type:blob"` LargeBlob []byte `gorm:"type:blob"` } +type BlobOneToManyModel struct { + ID uint `gorm:"primaryKey"` + Children []BlobChildModel `gorm:"foreignKey:ID"` +} + +type BlobChildModel struct { + ID uint `gorm:"primaryKey"` + Data []byte `gorm:"type:blob"` +} + func setupBlobTestTables(t *testing.T) { t.Log("Setting up BLOB test tables") - DB.Migrator().DropTable(&BlobTestModel{}, &BlobVariantModel{}) + DB.Migrator().DropTable(&BlobTestModel{}, &BlobVariantModel{}, &BlobOneToManyModel{}, &BlobChildModel{}) - err := DB.AutoMigrate(&BlobTestModel{}, &BlobVariantModel{}) + err := DB.AutoMigrate(&BlobTestModel{}, &BlobVariantModel{}, &BlobOneToManyModel{}, &BlobChildModel{}) if err != nil { t.Fatalf("Failed to migrate BLOB test tables: %v", err) } @@ -423,6 +434,23 @@ func TestBlobWithReturning(t *testing.T) { } } +func TestBlobOnConflict(t *testing.T) { + setupBlobTestTables(t) + + model := &BlobOneToManyModel{ + ID: 1, + Children: []BlobChildModel{ + { + Data: []byte(strings.Repeat("X", 32768)), + }, + }, + } + err := DB.Create(model).Error + if err != nil { + t.Fatalf("Failed to create BLOB record with ON CONFLICT: %v", err) + } +} + func TestBlobErrorHandling(t *testing.T) { setupBlobTestTables(t) diff --git a/tests/clob_test.go b/tests/clob_test.go new file mode 100644 index 0000000..7ba16f1 --- /dev/null +++ b/tests/clob_test.go @@ -0,0 +1,84 @@ +/* +** Copyright (c) 2025 Oracle and/or its affiliates. +** +** The Universal Permissive License (UPL), Version 1.0 +** +** Subject to the condition set forth below, permission is hereby granted to any +** person obtaining a copy of this software, associated documentation and/or data +** (collectively the "Software"), free of charge and under any and all copyright +** rights in the Software, and any and all patent rights owned or freely +** licensable by each licensor hereunder covering either (i) the unmodified +** Software as contributed to or provided by such licensor, or (ii) the Larger +** Works (as defined below), to deal in both +** +** (a) the Software, and +** (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +** one is included with the Software (each a "Larger Work" to which the Software +** is contributed by such licensors), +** +** without restriction, including without limitation the rights to copy, create +** derivative works of, display, perform, and distribute the Software and make, +** use, sell, offer for sale, import, export, have made, and have sold the +** Software and the Larger Work(s), and to sublicense the foregoing rights on +** either these or other terms. +** +** This license is subject to the following condition: +** The above copyright notice and either this complete permission notice or at +** a minimum a reference to the UPL must be included in all copies or +** substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. + */ + +package tests + +import ( + "strings" + "testing" +) + +type ClobOneToManyModel struct { + ID uint `gorm:"primaryKey"` + Children []ClobChildModel `gorm:"foreignKey:ID"` +} + +type ClobChildModel struct { + ID uint `gorm:"primaryKey"` + Data string `gorm:"type:clob"` +} + +func setupClobTestTables(t *testing.T) { + t.Log("Setting up CLOB test tables") + + DB.Migrator().DropTable(&ClobOneToManyModel{}, &ClobChildModel{}) + + err := DB.AutoMigrate(&ClobOneToManyModel{}, &ClobChildModel{}) + if err != nil { + t.Fatalf("Failed to migrate CLOB test tables: %v", err) + } + + t.Log("CLOB test tables created successfully") +} + +func TestClobOnConflict(t *testing.T) { + setupClobTestTables(t) + + model := &ClobOneToManyModel{ + ID: 1, + Children: []ClobChildModel{ + { + Data: strings.Repeat("X", 32768), + }, + }, + } + err := DB.Create(model).Error + if err != nil { + t.Fatalf("Failed to create BLOB record with ON CONFLICT: %v", err) + } +}