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  }