github.com/kaleido-io/firefly@v0.0.0-20210622132723-8b4b6aacb971/internal/database/sqlcommon/group_sql.go (about) 1 // Copyright © 2021 Kaleido, Inc. 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 17 package sqlcommon 18 19 import ( 20 "context" 21 "database/sql" 22 23 sq "github.com/Masterminds/squirrel" 24 "github.com/kaleido-io/firefly/internal/i18n" 25 "github.com/kaleido-io/firefly/internal/log" 26 "github.com/kaleido-io/firefly/pkg/database" 27 "github.com/kaleido-io/firefly/pkg/fftypes" 28 ) 29 30 var ( 31 groupColumns = []string{ 32 "message_id", 33 "namespace", 34 "name", 35 "ledger", 36 "hash", 37 "created", 38 } 39 groupFilterTypeMap = map[string]string{ 40 "message": "message_id", 41 } 42 ) 43 44 func (s *SQLCommon) UpsertGroup(ctx context.Context, group *fftypes.Group, allowExisting bool) (err error) { 45 ctx, tx, autoCommit, err := s.beginOrUseTx(ctx) 46 if err != nil { 47 return err 48 } 49 defer s.rollbackTx(ctx, tx, autoCommit) 50 51 existing := false 52 if allowExisting { 53 // Do a select within the transaction to detemine if the UUID already exists 54 groupRows, err := s.queryTx(ctx, tx, 55 sq.Select("hash"). 56 From("groups"). 57 Where(sq.Eq{"hash": group.Hash}), 58 ) 59 if err != nil { 60 return err 61 } 62 existing = groupRows.Next() 63 groupRows.Close() 64 } 65 66 if existing { 67 68 // Update the group 69 if err = s.updateTx(ctx, tx, 70 sq.Update("groups"). 71 Set("message_id", group.Message). 72 Set("namespace", group.Namespace). 73 Set("name", group.Name). 74 Set("ledger", group.Ledger). 75 Set("hash", group.Hash). 76 Set("created", group.Created). 77 Where(sq.Eq{"hash": group.Hash}), 78 ); err != nil { 79 return err 80 } 81 } else { 82 _, err := s.insertTx(ctx, tx, 83 sq.Insert("groups"). 84 Columns(groupColumns...). 85 Values( 86 group.Message, 87 group.Namespace, 88 group.Name, 89 group.Ledger, 90 group.Hash, 91 group.Created, 92 ), 93 ) 94 if err != nil { 95 return err 96 } 97 98 } 99 100 if err = s.updateMembers(ctx, tx, group, existing); err != nil { 101 return err 102 } 103 104 return s.commitTx(ctx, tx, autoCommit) 105 } 106 107 func (s *SQLCommon) updateMembers(ctx context.Context, tx *txWrapper, group *fftypes.Group, existing bool) error { 108 109 if existing { 110 if err := s.deleteTx(ctx, tx, 111 sq.Delete("members"). 112 Where(sq.And{ 113 sq.Eq{"group_hash": group.Hash}, 114 }), 115 ); err != nil { 116 return err 117 } 118 } 119 120 // Run through the ones in the group, finding ones that already exist, and ones that need to be created 121 for requiredIdx, requiredMember := range group.Members { 122 if requiredMember == nil || requiredMember.Identity == "" { 123 return i18n.NewError(ctx, i18n.MsgEmptyMemberIdentity, requiredIdx) 124 } 125 if requiredMember.Node == nil { 126 return i18n.NewError(ctx, i18n.MsgEmptyMemberNode, requiredIdx) 127 } 128 if _, err := s.insertTx(ctx, tx, 129 sq.Insert("members"). 130 Columns( 131 "group_hash", 132 "identity", 133 "node_id", 134 "idx", 135 ). 136 Values( 137 group.Hash, 138 requiredMember.Identity, 139 requiredMember.Node, 140 requiredIdx, 141 ), 142 ); err != nil { 143 return err 144 } 145 } 146 147 return nil 148 149 } 150 151 func (s *SQLCommon) loadMembers(ctx context.Context, groups []*fftypes.Group) error { 152 153 groupIDs := make([]string, len(groups)) 154 for i, m := range groups { 155 if m != nil { 156 groupIDs[i] = m.Hash.String() 157 } 158 } 159 160 members, err := s.query(ctx, 161 sq.Select( 162 "group_hash", 163 "identity", 164 "node_id", 165 "idx", 166 ). 167 From("members"). 168 Where(sq.Eq{"group_hash": groupIDs}). 169 OrderBy("idx"), 170 ) 171 if err != nil { 172 return err 173 } 174 defer members.Close() 175 176 for members.Next() { 177 var groupID fftypes.Bytes32 178 member := &fftypes.Member{} 179 var idx int 180 if err = members.Scan(&groupID, &member.Identity, &member.Node, &idx); err != nil { 181 return i18n.WrapError(ctx, err, i18n.MsgDBReadErr, "members") 182 } 183 for _, g := range groups { 184 if g.Hash.Equals(&groupID) { 185 g.Members = append(g.Members, member) 186 } 187 } 188 } 189 // Ensure we return an empty array if no entries, and a consistent order for the data 190 for _, g := range groups { 191 if g.Members == nil { 192 g.Members = fftypes.Members{} 193 } 194 } 195 196 return nil 197 } 198 199 func (s *SQLCommon) groupResult(ctx context.Context, row *sql.Rows) (*fftypes.Group, error) { 200 var group fftypes.Group 201 err := row.Scan( 202 &group.Message, 203 &group.Namespace, 204 &group.Name, 205 &group.Ledger, 206 &group.Hash, 207 &group.Created, 208 ) 209 if err != nil { 210 return nil, i18n.WrapError(ctx, err, i18n.MsgDBReadErr, "groups") 211 } 212 return &group, nil 213 } 214 215 func (s *SQLCommon) GetGroupByHash(ctx context.Context, hash *fftypes.Bytes32) (group *fftypes.Group, err error) { 216 217 rows, err := s.query(ctx, 218 sq.Select(groupColumns...). 219 From("groups"). 220 Where(sq.Eq{"hash": hash}), 221 ) 222 if err != nil { 223 return nil, err 224 } 225 defer rows.Close() 226 227 if !rows.Next() { 228 log.L(ctx).Debugf("Group '%s' not found", hash) 229 return nil, nil 230 } 231 232 group, err = s.groupResult(ctx, rows) 233 if err != nil { 234 return nil, err 235 } 236 237 rows.Close() 238 if err = s.loadMembers(ctx, []*fftypes.Group{group}); err != nil { 239 return nil, err 240 } 241 242 return group, nil 243 } 244 245 func (s *SQLCommon) getGroupsQuery(ctx context.Context, query sq.SelectBuilder) (group []*fftypes.Group, err error) { 246 rows, err := s.query(ctx, query) 247 if err != nil { 248 return nil, err 249 } 250 defer rows.Close() 251 252 groups := []*fftypes.Group{} 253 for rows.Next() { 254 group, err := s.groupResult(ctx, rows) 255 if err != nil { 256 return nil, err 257 } 258 groups = append(groups, group) 259 } 260 261 rows.Close() 262 if len(groups) > 0 { 263 if err = s.loadMembers(ctx, groups); err != nil { 264 return nil, err 265 } 266 } 267 268 return groups, err 269 } 270 271 func (s *SQLCommon) GetGroups(ctx context.Context, filter database.Filter) (group []*fftypes.Group, err error) { 272 query, err := s.filterSelect(ctx, "", sq.Select(groupColumns...).From("groups"), filter, groupFilterTypeMap) 273 if err != nil { 274 return nil, err 275 } 276 return s.getGroupsQuery(ctx, query) 277 } 278 279 func (s *SQLCommon) UpdateGroup(ctx context.Context, hash *fftypes.Bytes32, update database.Update) (err error) { 280 return s.UpdateGroups(ctx, database.GroupQueryFactory.NewFilter(ctx).Eq("hash", hash), update) 281 } 282 283 func (s *SQLCommon) UpdateGroups(ctx context.Context, filter database.Filter, update database.Update) (err error) { 284 285 ctx, tx, autoCommit, err := s.beginOrUseTx(ctx) 286 if err != nil { 287 return err 288 } 289 defer s.rollbackTx(ctx, tx, autoCommit) 290 291 query, err := s.buildUpdate(sq.Update("groups"), update, groupFilterTypeMap) 292 if err != nil { 293 return err 294 } 295 296 query, err = s.filterUpdate(ctx, "", query, filter, opFilterTypeMap) 297 if err != nil { 298 return err 299 } 300 301 err = s.updateTx(ctx, tx, query) 302 if err != nil { 303 return err 304 } 305 306 return s.commitTx(ctx, tx, autoCommit) 307 }