github.com/kaleido-io/firefly@v0.0.0-20210622132723-8b4b6aacb971/internal/database/sqlcommon/transaction_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  	transactionColumns = []string{
    32  		"id",
    33  		"ttype",
    34  		"namespace",
    35  		"ref",
    36  		"signer",
    37  		"hash",
    38  		"created",
    39  		"protocol_id",
    40  		"status",
    41  		"info",
    42  	}
    43  	transactionFilterTypeMap = map[string]string{
    44  		"type":       "ttype",
    45  		"protocolid": "protocol_id",
    46  		"reference":  "ref",
    47  	}
    48  )
    49  
    50  func (s *SQLCommon) UpsertTransaction(ctx context.Context, transaction *fftypes.Transaction, allowExisting, allowHashUpdate bool) (err error) {
    51  	ctx, tx, autoCommit, err := s.beginOrUseTx(ctx)
    52  	if err != nil {
    53  		return err
    54  	}
    55  	defer s.rollbackTx(ctx, tx, autoCommit)
    56  
    57  	existing := false
    58  	if allowExisting {
    59  		// Do a select within the transaction to detemine if the UUID already exists
    60  		transactionRows, err := s.queryTx(ctx, tx,
    61  			sq.Select("hash").
    62  				From("transactions").
    63  				Where(sq.Eq{"id": transaction.ID}),
    64  		)
    65  		if err != nil {
    66  			return err
    67  		}
    68  		existing = transactionRows.Next()
    69  
    70  		if existing && !allowHashUpdate {
    71  			var hash *fftypes.Bytes32
    72  			_ = transactionRows.Scan(&hash)
    73  			if !fftypes.SafeHashCompare(hash, transaction.Hash) {
    74  				transactionRows.Close()
    75  				log.L(ctx).Errorf("Existing=%s New=%s", hash, transaction.Hash)
    76  				return database.HashMismatch
    77  			}
    78  		}
    79  		transactionRows.Close()
    80  	}
    81  
    82  	if existing {
    83  
    84  		// Update the transaction
    85  		if err = s.updateTx(ctx, tx,
    86  			sq.Update("transactions").
    87  				Set("ttype", string(transaction.Subject.Type)).
    88  				Set("namespace", transaction.Subject.Namespace).
    89  				Set("ref", transaction.Subject.Reference).
    90  				Set("signer", transaction.Subject.Signer).
    91  				Set("hash", transaction.Hash).
    92  				Set("created", transaction.Created).
    93  				Set("protocol_id", transaction.ProtocolID).
    94  				Set("status", transaction.Status).
    95  				Set("info", transaction.Info).
    96  				Where(sq.Eq{"id": transaction.ID}),
    97  		); err != nil {
    98  			return err
    99  		}
   100  	} else {
   101  
   102  		if _, err = s.insertTx(ctx, tx,
   103  			sq.Insert("transactions").
   104  				Columns(transactionColumns...).
   105  				Values(
   106  					transaction.ID,
   107  					string(transaction.Subject.Type),
   108  					transaction.Subject.Namespace,
   109  					transaction.Subject.Reference,
   110  					transaction.Subject.Signer,
   111  					transaction.Hash,
   112  					transaction.Created,
   113  					transaction.ProtocolID,
   114  					transaction.Status,
   115  					transaction.Info,
   116  				),
   117  		); err != nil {
   118  			return err
   119  		}
   120  	}
   121  
   122  	return s.commitTx(ctx, tx, autoCommit)
   123  }
   124  
   125  func (s *SQLCommon) transactionResult(ctx context.Context, row *sql.Rows) (*fftypes.Transaction, error) {
   126  	var transaction fftypes.Transaction
   127  	err := row.Scan(
   128  		&transaction.ID,
   129  		&transaction.Subject.Type,
   130  		&transaction.Subject.Namespace,
   131  		&transaction.Subject.Reference,
   132  		&transaction.Subject.Signer,
   133  		&transaction.Hash,
   134  		&transaction.Created,
   135  		&transaction.ProtocolID,
   136  		&transaction.Status,
   137  		&transaction.Info,
   138  		// Must be added to the list of columns in all selects
   139  		&transaction.Sequence,
   140  	)
   141  	if err != nil {
   142  		return nil, i18n.WrapError(ctx, err, i18n.MsgDBReadErr, "transactions")
   143  	}
   144  	return &transaction, nil
   145  }
   146  
   147  func (s *SQLCommon) GetTransactionByID(ctx context.Context, id *fftypes.UUID) (message *fftypes.Transaction, err error) {
   148  
   149  	cols := append([]string{}, transactionColumns...)
   150  	cols = append(cols, s.provider.SequenceField(""))
   151  	rows, err := s.query(ctx,
   152  		sq.Select(cols...).
   153  			From("transactions").
   154  			Where(sq.Eq{"id": id}),
   155  	)
   156  	if err != nil {
   157  		return nil, err
   158  	}
   159  	defer rows.Close()
   160  
   161  	if !rows.Next() {
   162  		log.L(ctx).Debugf("Transaction '%s' not found", id)
   163  		return nil, nil
   164  	}
   165  
   166  	transaction, err := s.transactionResult(ctx, rows)
   167  	if err != nil {
   168  		return nil, err
   169  	}
   170  
   171  	return transaction, nil
   172  }
   173  
   174  func (s *SQLCommon) GetTransactions(ctx context.Context, filter database.Filter) (message []*fftypes.Transaction, err error) {
   175  
   176  	cols := append([]string{}, transactionColumns...)
   177  	cols = append(cols, s.provider.SequenceField(""))
   178  	query, err := s.filterSelect(ctx, "", sq.Select(cols...).From("transactions"), filter, transactionFilterTypeMap)
   179  	if err != nil {
   180  		return nil, err
   181  	}
   182  
   183  	rows, err := s.query(ctx, query)
   184  	if err != nil {
   185  		return nil, err
   186  	}
   187  	defer rows.Close()
   188  
   189  	transactions := []*fftypes.Transaction{}
   190  	for rows.Next() {
   191  		transaction, err := s.transactionResult(ctx, rows)
   192  		if err != nil {
   193  			return nil, err
   194  		}
   195  		transactions = append(transactions, transaction)
   196  	}
   197  
   198  	return transactions, err
   199  
   200  }
   201  
   202  func (s *SQLCommon) UpdateTransaction(ctx context.Context, id *fftypes.UUID, update database.Update) (err error) {
   203  
   204  	ctx, tx, autoCommit, err := s.beginOrUseTx(ctx)
   205  	if err != nil {
   206  		return err
   207  	}
   208  	defer s.rollbackTx(ctx, tx, autoCommit)
   209  
   210  	query, err := s.buildUpdate(sq.Update("transactions"), update, transactionFilterTypeMap)
   211  	if err != nil {
   212  		return err
   213  	}
   214  	query = query.Where(sq.Eq{"id": id})
   215  
   216  	err = s.updateTx(ctx, tx, query)
   217  	if err != nil {
   218  		return err
   219  	}
   220  
   221  	return s.commitTx(ctx, tx, autoCommit)
   222  }