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  }