github.com/kaleido-io/firefly@v0.0.0-20210622132723-8b4b6aacb971/internal/database/sqlcommon/data_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 dataColumnsNoValue = []string{ 32 "id", 33 "validator", 34 "namespace", 35 "datatype_name", 36 "datatype_version", 37 "hash", 38 "created", 39 "blobstore", 40 } 41 dataColumnsWithValue = append(append([]string{}, dataColumnsNoValue...), "value") 42 dataFilterTypeMap = map[string]string{ 43 "validator": "validator", 44 "datatype.name": "datatype_name", 45 "datatype.version": "datatype_version", 46 } 47 ) 48 49 func (s *SQLCommon) UpsertData(ctx context.Context, data *fftypes.Data, allowExisting, allowHashUpdate bool) (err error) { 50 ctx, tx, autoCommit, err := s.beginOrUseTx(ctx) 51 if err != nil { 52 return err 53 } 54 defer s.rollbackTx(ctx, tx, autoCommit) 55 56 existing := false 57 if allowExisting { 58 // Do a select within the transaction to detemine if the UUID already exists 59 dataRows, err := s.queryTx(ctx, tx, 60 sq.Select("hash"). 61 From("data"). 62 Where(sq.Eq{"id": data.ID}), 63 ) 64 if err != nil { 65 return err 66 } 67 68 existing = dataRows.Next() 69 if existing && !allowHashUpdate { 70 var hash *fftypes.Bytes32 71 _ = dataRows.Scan(&hash) 72 if !fftypes.SafeHashCompare(hash, data.Hash) { 73 dataRows.Close() 74 log.L(ctx).Errorf("Existing=%s New=%s", hash, data.Hash) 75 return database.HashMismatch 76 } 77 } 78 dataRows.Close() 79 } 80 81 datatype := data.Datatype 82 if datatype == nil { 83 datatype = &fftypes.DatatypeRef{} 84 } 85 86 if existing { 87 // Update the data 88 if err = s.updateTx(ctx, tx, 89 sq.Update("data"). 90 Set("validator", string(data.Validator)). 91 Set("namespace", data.Namespace). 92 Set("datatype_name", datatype.Name). 93 Set("datatype_version", datatype.Version). 94 Set("hash", data.Hash). 95 Set("created", data.Created). 96 Set("blobstore", data.Blobstore). 97 Set("value", data.Value). 98 Where(sq.Eq{"id": data.ID}), 99 ); err != nil { 100 return err 101 } 102 } else { 103 if _, err = s.insertTx(ctx, tx, 104 sq.Insert("data"). 105 Columns(dataColumnsWithValue...). 106 Values( 107 data.ID, 108 string(data.Validator), 109 data.Namespace, 110 datatype.Name, 111 datatype.Version, 112 data.Hash, 113 data.Created, 114 data.Blobstore, 115 data.Value, 116 ), 117 ); err != nil { 118 return err 119 } 120 } 121 122 return s.commitTx(ctx, tx, autoCommit) 123 } 124 125 func (s *SQLCommon) dataResult(ctx context.Context, row *sql.Rows, withValue bool) (*fftypes.Data, error) { 126 data := fftypes.Data{ 127 Datatype: &fftypes.DatatypeRef{}, 128 } 129 results := []interface{}{ 130 &data.ID, 131 &data.Validator, 132 &data.Namespace, 133 &data.Datatype.Name, 134 &data.Datatype.Version, 135 &data.Hash, 136 &data.Created, 137 &data.Blobstore, 138 } 139 if withValue { 140 results = append(results, &data.Value) 141 } 142 err := row.Scan(results...) 143 if data.Datatype.Name == "" && data.Datatype.Version == "" { 144 data.Datatype = nil 145 } 146 if err != nil { 147 return nil, i18n.WrapError(ctx, err, i18n.MsgDBReadErr, "data") 148 } 149 return &data, nil 150 } 151 152 func (s *SQLCommon) GetDataByID(ctx context.Context, id *fftypes.UUID, withValue bool) (message *fftypes.Data, err error) { 153 154 var cols []string 155 if withValue { 156 cols = dataColumnsWithValue 157 } else { 158 cols = dataColumnsNoValue 159 } 160 rows, err := s.query(ctx, 161 sq.Select(cols...). 162 From("data"). 163 Where(sq.Eq{"id": id}), 164 ) 165 if err != nil { 166 return nil, err 167 } 168 defer rows.Close() 169 170 if !rows.Next() { 171 log.L(ctx).Debugf("Data '%s' not found", id) 172 return nil, nil 173 } 174 175 data, err := s.dataResult(ctx, rows, withValue) 176 if err != nil { 177 return nil, err 178 } 179 180 return data, nil 181 } 182 183 func (s *SQLCommon) GetData(ctx context.Context, filter database.Filter) (message []*fftypes.Data, err error) { 184 185 query, err := s.filterSelect(ctx, "", sq.Select(dataColumnsWithValue...).From("data"), filter, dataFilterTypeMap) 186 if err != nil { 187 return nil, err 188 } 189 190 rows, err := s.query(ctx, query) 191 if err != nil { 192 return nil, err 193 } 194 defer rows.Close() 195 196 data := []*fftypes.Data{} 197 for rows.Next() { 198 d, err := s.dataResult(ctx, rows, true) 199 if err != nil { 200 return nil, err 201 } 202 data = append(data, d) 203 } 204 205 return data, err 206 207 } 208 209 func (s *SQLCommon) GetDataRefs(ctx context.Context, filter database.Filter) (message fftypes.DataRefs, err error) { 210 211 query, err := s.filterSelect(ctx, "", sq.Select("id", "hash").From("data"), filter, dataFilterTypeMap) 212 if err != nil { 213 return nil, err 214 } 215 216 rows, err := s.query(ctx, query) 217 if err != nil { 218 return nil, err 219 } 220 defer rows.Close() 221 222 refs := fftypes.DataRefs{} 223 for rows.Next() { 224 ref := fftypes.DataRef{} 225 err := rows.Scan( 226 &ref.ID, 227 &ref.Hash, 228 ) 229 if err != nil { 230 return nil, i18n.WrapError(ctx, err, i18n.MsgDBReadErr, "data") 231 } 232 refs = append(refs, &ref) 233 } 234 235 return refs, err 236 237 } 238 239 func (s *SQLCommon) UpdateData(ctx context.Context, id *fftypes.UUID, update database.Update) (err error) { 240 241 ctx, tx, autoCommit, err := s.beginOrUseTx(ctx) 242 if err != nil { 243 return err 244 } 245 defer s.rollbackTx(ctx, tx, autoCommit) 246 247 query, err := s.buildUpdate(sq.Update("data"), update, dataFilterTypeMap) 248 if err != nil { 249 return err 250 } 251 query = query.Where(sq.Eq{"id": id}) 252 253 err = s.updateTx(ctx, tx, query) 254 if err != nil { 255 return err 256 } 257 258 return s.commitTx(ctx, tx, autoCommit) 259 }