Skip to content

Commit e7363a1

Browse files
Merge pull request #24 from oracle-samples/tinglwan-fix-bulk-insert
Fix records mismatch when doing bulk insert in TestReturningWithNullToZeroValues
2 parents 479e802 + 2af5293 commit e7363a1

File tree

6 files changed

+52
-17
lines changed

6 files changed

+52
-17
lines changed

oracle/common.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,50 @@ func writeQuotedIdentifier(builder *strings.Builder, identifier string) {
424424
builder.WriteByte('"')
425425
}
426426

427+
// writeTableRecordCollectionDecl writes the PL/SQL declarations needed to
428+
// define a custom record type and a collection of that record type,
429+
// based on the schema of the given table.
430+
//
431+
// Specifically, it generates:
432+
// - A RECORD type (`t_record`) with fields corresponding to the table's columns.
433+
// - A nested TABLE type (`t_records`) of `t_record`.
434+
//
435+
// The declarations are written into the provided strings.Builder in the
436+
// correct PL/SQL syntax, so they can be used as part of a larger PL/SQL block.
437+
//
438+
// Example output:
439+
//
440+
// TYPE t_record IS RECORD (
441+
// "id" "users"."id"%TYPE,
442+
// "created_at" "users"."created_at"%TYPE,
443+
// ...
444+
// );
445+
// TYPE t_records IS TABLE OF t_record;
446+
//
447+
// Parameters:
448+
// - plsqlBuilder: The builder to write the PL/SQL code into.
449+
// - dbNames: The slice containing the column names.
450+
// - table: The table name
451+
func writeTableRecordCollectionDecl(plsqlBuilder *strings.Builder, dbNames []string, table string) {
452+
// Declare a record where each element has the same structure as a row from the given table
453+
plsqlBuilder.WriteString(" TYPE t_record IS RECORD (\n")
454+
for i, field := range dbNames {
455+
if i > 0 {
456+
plsqlBuilder.WriteString(",\n")
457+
}
458+
plsqlBuilder.WriteString(" ")
459+
writeQuotedIdentifier(plsqlBuilder, field)
460+
plsqlBuilder.WriteString(" ")
461+
writeQuotedIdentifier(plsqlBuilder, table)
462+
plsqlBuilder.WriteString(".")
463+
writeQuotedIdentifier(plsqlBuilder, field)
464+
plsqlBuilder.WriteString("%TYPE")
465+
}
466+
plsqlBuilder.WriteString("\n")
467+
plsqlBuilder.WriteString(" );\n")
468+
plsqlBuilder.WriteString(" TYPE t_records IS TABLE OF t_record;\n")
469+
}
470+
427471
// Helper function to check if a value represents NULL
428472
func isNullValue(value interface{}) bool {
429473
if value == nil {

oracle/create.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -285,9 +285,7 @@ func buildBulkMergePLSQL(db *gorm.DB, createValues clause.Values, onConflictClau
285285

286286
// Start PL/SQL block
287287
plsqlBuilder.WriteString("DECLARE\n")
288-
plsqlBuilder.WriteString(" TYPE t_records IS TABLE OF ")
289-
writeQuotedIdentifier(&plsqlBuilder, stmt.Table)
290-
plsqlBuilder.WriteString("%ROWTYPE;\n")
288+
writeTableRecordCollectionDecl(&plsqlBuilder, stmt.Schema.DBNames, stmt.Table)
291289
plsqlBuilder.WriteString(" l_affected_records t_records;\n")
292290

293291
// Create array types and variables for each column
@@ -526,9 +524,7 @@ func buildBulkInsertOnlyPLSQL(db *gorm.DB, createValues clause.Values) {
526524

527525
// Start PL/SQL block
528526
plsqlBuilder.WriteString("DECLARE\n")
529-
plsqlBuilder.WriteString(" TYPE t_records IS TABLE OF ")
530-
writeQuotedIdentifier(&plsqlBuilder, stmt.Table)
531-
plsqlBuilder.WriteString("%ROWTYPE;\n")
527+
writeTableRecordCollectionDecl(&plsqlBuilder, stmt.Schema.DBNames, stmt.Table)
532528
plsqlBuilder.WriteString(" l_inserted_records t_records;\n")
533529

534530
// Create array types and variables for each column

oracle/delete.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -239,9 +239,7 @@ func buildBulkDeletePLSQL(db *gorm.DB) {
239239

240240
// Start PL/SQL block
241241
plsqlBuilder.WriteString("DECLARE\n")
242-
plsqlBuilder.WriteString(" TYPE t_records IS TABLE OF ")
243-
writeQuotedIdentifier(&plsqlBuilder, stmt.Table)
244-
plsqlBuilder.WriteString("%ROWTYPE;\n")
242+
writeTableRecordCollectionDecl(&plsqlBuilder, stmt.Schema.DBNames, stmt.Table)
245243
plsqlBuilder.WriteString(" l_deleted_records t_records;\n")
246244
plsqlBuilder.WriteString("BEGIN\n")
247245

oracle/update.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -476,9 +476,7 @@ func buildUpdatePLSQL(db *gorm.DB) {
476476

477477
// Start PL/SQL block
478478
plsqlBuilder.WriteString("DECLARE\n")
479-
plsqlBuilder.WriteString(" TYPE t_records IS TABLE OF ")
480-
writeQuotedIdentifier(&plsqlBuilder, stmt.Table)
481-
plsqlBuilder.WriteString("%ROWTYPE;\n")
479+
writeTableRecordCollectionDecl(&plsqlBuilder, stmt.Schema.DBNames, stmt.Table)
482480
plsqlBuilder.WriteString(" l_updated_records t_records;\n")
483481
plsqlBuilder.WriteString("BEGIN\n")
484482

tests/gorm_test.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ func TestOpen(t *testing.T) {
5454
}
5555

5656
func TestReturningWithNullToZeroValues(t *testing.T) {
57-
t.Skip()
5857
// This user struct will leverage the existing users table, but override
5958
// the Name field to default to null.
6059
type user struct {
@@ -72,7 +71,7 @@ func TestReturningWithNullToZeroValues(t *testing.T) {
7271
}
7372

7473
got := user{}
75-
results := DB.First(&got, "id = ?", u1.ID)
74+
results := DB.First(&got, "\"id\" = ?", u1.ID)
7675
if results.Error != nil {
7776
t.Fatalf("errors happened on first: %v", results.Error)
7877
} else if results.RowsAffected != 1 {
@@ -81,7 +80,7 @@ func TestReturningWithNullToZeroValues(t *testing.T) {
8180
t.Fatalf("first expects: %v, got %v", u1, got)
8281
}
8382

84-
results = DB.Select("id, name").Find(&got)
83+
results = DB.Select("\"id\", \"name\"").Find(&got)
8584
if results.Error != nil {
8685
t.Fatalf("errors happened on first: %v", results.Error)
8786
} else if results.RowsAffected != 1 {
@@ -112,7 +111,7 @@ func TestReturningWithNullToZeroValues(t *testing.T) {
112111
}
113112

114113
var gotUsers []user
115-
results = DB.Where("id in (?, ?)", u1.ID, u2.ID).Order("id asc").Select("id, name").Find(&gotUsers)
114+
results = DB.Where("\"id\" in (?, ?)", u1.ID, u2.ID).Order("\"id\" asc").Select("\"id\", \"name\"").Find(&gotUsers)
116115
if results.Error != nil {
117116
t.Fatalf("errors happened on first: %v", results.Error)
118117
} else if results.RowsAffected != 2 {

tests/passed-tests.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ TestGenericsReuse
124124
TestGenericsWithTransaction
125125
TestGenericsToSQL
126126
TestOpen
127-
#TestReturningWithNullToZeroValues
127+
TestReturningWithNullToZeroValues
128128
TestGroupBy
129129
TestRunCallbacks
130130
TestCallbacksWithErrors

0 commit comments

Comments
 (0)