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 }