1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
|
package ovsdb
import (
"fmt"
)
// ExpandNamedUUIDs replaces named UUIDs in columns that contain UUID types
// throughout the operation. The caller must ensure each input operation has
// a valid UUID, which may be replaced if a previous operation created a
// matching named UUID mapping. Returns the updated operations or an error.
func ExpandNamedUUIDs(ops []Operation, schema *DatabaseSchema) ([]Operation, error) {
uuidMap := make(map[string]string)
// Pass 1: replace the named UUID with a real UUID for each operation and
// build the substitution map
for i := range ops {
op := &ops[i]
if op.Op != OperationInsert {
// Only Insert operations can specify a Named UUID
continue
}
if err := ValidateUUID(op.UUID); err != nil {
return nil, fmt.Errorf("operation UUID %q invalid: %v", op.UUID, err)
}
if op.UUIDName != "" {
if uuid, ok := uuidMap[op.UUIDName]; ok {
if op.UUID != "" && op.UUID != uuid {
return nil, fmt.Errorf("named UUID %q maps to UUID %q but found existing UUID %q",
op.UUIDName, uuid, op.UUID)
}
// If there's already a mapping for this named UUID use it
op.UUID = uuid
} else {
uuidMap[op.UUIDName] = op.UUID
}
op.UUIDName = ""
}
}
// Pass 2: replace named UUIDs in operation fields with the real UUID
for i := range ops {
op := &ops[i]
tableSchema := schema.Table(op.Table)
if tableSchema == nil {
return nil, fmt.Errorf("table %q not found in schema %q", op.Table, schema.Name)
}
for i, condition := range op.Where {
newVal, err := expandColumnNamedUUIDs(tableSchema, op.Table, condition.Column, condition.Value, uuidMap)
if err != nil {
return nil, err
}
op.Where[i].Value = newVal
}
for i, mutation := range op.Mutations {
newVal, err := expandColumnNamedUUIDs(tableSchema, op.Table, mutation.Column, mutation.Value, uuidMap)
if err != nil {
return nil, err
}
op.Mutations[i].Value = newVal
}
for _, row := range op.Rows {
for k, v := range row {
newVal, err := expandColumnNamedUUIDs(tableSchema, op.Table, k, v, uuidMap)
if err != nil {
return nil, err
}
row[k] = newVal
}
}
for k, v := range op.Row {
newVal, err := expandColumnNamedUUIDs(tableSchema, op.Table, k, v, uuidMap)
if err != nil {
return nil, err
}
op.Row[k] = newVal
}
}
return ops, nil
}
func expandColumnNamedUUIDs(tableSchema *TableSchema, tableName, columnName string, value any, uuidMap map[string]string) (any, error) {
column := tableSchema.Column(columnName)
if column == nil {
return nil, fmt.Errorf("column %q not found in table %q", columnName, tableName)
}
return expandNamedUUID(column, value, uuidMap), nil
}
func expandNamedUUID(column *ColumnSchema, value any, namedUUIDs map[string]string) any {
var keyType, valType ExtendedType
switch column.Type {
case TypeUUID:
keyType = column.Type
case TypeSet:
keyType = column.TypeObj.Key.Type
case TypeMap:
keyType = column.TypeObj.Key.Type
valType = column.TypeObj.Value.Type
}
if valType == TypeUUID {
if m, ok := value.(OvsMap); ok {
for k, v := range m.GoMap {
if newUUID, ok := expandNamedUUIDAtomic(keyType, k, namedUUIDs); ok {
m.GoMap[newUUID] = m.GoMap[k]
delete(m.GoMap, k)
k = newUUID
}
if newUUID, ok := expandNamedUUIDAtomic(valType, v, namedUUIDs); ok {
m.GoMap[k] = newUUID
}
}
}
} else if keyType == TypeUUID {
if ovsSet, ok := value.(OvsSet); ok {
for i, s := range ovsSet.GoSet {
if newUUID, ok := expandNamedUUIDAtomic(keyType, s, namedUUIDs); ok {
ovsSet.GoSet[i] = newUUID
}
}
return value
} else if strSet, ok := value.([]string); ok {
for i, s := range strSet {
if newUUID, ok := expandNamedUUIDAtomic(keyType, s, namedUUIDs); ok {
strSet[i] = newUUID.(string)
}
}
return value
} else if uuidSet, ok := value.([]UUID); ok {
for i, s := range uuidSet {
if newUUID, ok := expandNamedUUIDAtomic(keyType, s, namedUUIDs); ok {
uuidSet[i] = newUUID.(UUID)
}
}
return value
}
if newUUID, ok := expandNamedUUIDAtomic(keyType, value, namedUUIDs); ok {
return newUUID
}
}
// No expansion required; return original value
return value
}
func expandNamedUUIDAtomic(valueType ExtendedType, value any, namedUUIDs map[string]string) (any, bool) {
if valueType == TypeUUID {
if uuid, ok := value.(UUID); ok {
if newUUID, ok := namedUUIDs[uuid.GoUUID]; ok {
return UUID{GoUUID: newUUID}, true
}
} else if uuid, ok := value.(string); ok {
if newUUID, ok := namedUUIDs[uuid]; ok {
return newUUID, true
}
}
}
return value, false
}
|