diff --git a/pkg/common/recursive_crud.go b/pkg/common/recursive_crud.go index fb620b2..461293a 100644 --- a/pkg/common/recursive_crud.go +++ b/pkg/common/recursive_crud.go @@ -143,6 +143,10 @@ func (p *NestedCUDProcessor) ProcessNestedCUD( case "update", "change": // Only perform update if we have data to update + if reflection.IsEmptyValue(data[pkName]) { + logger.Warn("Skipping update for %s - no primary key", tableName) + return result, nil + } if hasData { rows, err := p.processUpdate(ctx, regularData, tableName, data[pkName]) if err != nil { @@ -171,10 +175,15 @@ func (p *NestedCUDProcessor) ProcessNestedCUD( } case "delete": + if reflection.IsEmptyValue(data[pkName]) { + logger.Warn("Skipping delete for %s - no primary key", tableName) + return result, nil + } + // Process child relations first (for referential integrity) if err := p.processChildRelations(ctx, "delete", data[pkName], relationFields, result.RelationData, modelType, parentIDs); err != nil { logger.Error("Failed to process child relations before delete: table=%s, id=%v, relations=%+v, error=%v", tableName, data[pkName], relationFields, err) - return nil, nil + return nil, fmt.Errorf("failed to process child relations: %w", err) } rows, err := p.processDelete(ctx, tableName, data[pkName]) diff --git a/pkg/reflection/helpers.go b/pkg/reflection/helpers.go index 9740c3e..71a1850 100644 --- a/pkg/reflection/helpers.go +++ b/pkg/reflection/helpers.go @@ -51,6 +51,31 @@ func ExtractTableNameOnly(fullName string) string { return fullName[startIndex:] } +// IsEmptyValue reports whether v is nil, an empty string, or a zero number. +func IsEmptyValue(v any) bool { + if v == nil { + return true + } + rv := reflect.ValueOf(v) + if rv.Kind() == reflect.Ptr { + if rv.IsNil() { + return true + } + rv = rv.Elem() + } + switch rv.Kind() { + case reflect.String: + return rv.String() == "" + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return rv.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return rv.Uint() == 0 + case reflect.Float32, reflect.Float64: + return rv.Float() == 0 + } + return false +} + // GetPointerElement returns the element type if the provided reflect.Type is a pointer. // If the type is a slice of pointers, it returns the element type of the pointer within the slice. // If neither condition is met, it returns the original type.