github.com/adecaro/fabric-ca@v2.0.0-alpha+incompatible/lib/server/db/db.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package db
     8  
     9  import (
    10  	"context"
    11  	"database/sql"
    12  	"time"
    13  
    14  	"github.com/cloudflare/cfssl/certdb"
    15  	"github.com/hyperledger/fabric-ca/lib/server/db/util"
    16  	"github.com/hyperledger/fabric/common/metrics"
    17  	"github.com/jmoiron/sqlx"
    18  )
    19  
    20  //go:generate counterfeiter -o mocks/fabricCaDb.go -fake-name FabricCADB . FabricCADB
    21  //go:generate mockery -name FabricCADB -output ../idemix/mocks -case underscore
    22  
    23  // FabricCADB is the interface that wrapper off SqlxDB
    24  type FabricCADB interface {
    25  	IsInitialized() bool
    26  	SetDBInitialized(bool)
    27  	// BeginTx has same behavior as MustBegin except it returns FabricCATx
    28  	// instead of *sqlx.Tx
    29  	BeginTx() FabricCATx
    30  	DriverName() string
    31  
    32  	Select(funcName string, dest interface{}, query string, args ...interface{}) error
    33  	Exec(funcName, query string, args ...interface{}) (sql.Result, error)
    34  	NamedExec(funcName, query string, arg interface{}) (sql.Result, error)
    35  	Get(funcName string, dest interface{}, query string, args ...interface{}) error
    36  	Queryx(funcName, query string, args ...interface{}) (*sqlx.Rows, error)
    37  	Rebind(query string) string
    38  	MustBegin() *sqlx.Tx
    39  	Close() error
    40  	SetMaxOpenConns(n int)
    41  	PingContext(ctx context.Context) error
    42  }
    43  
    44  //go:generate counterfeiter -o mocks/sqlxDB.go -fake-name SqlxDB . SqlxDB
    45  
    46  // SqlxDB is the interface with functions implemented by sqlx.DB
    47  // object that are used by Fabric CA server
    48  type SqlxDB interface {
    49  	DriverName() string
    50  	Select(dest interface{}, query string, args ...interface{}) error
    51  	Exec(query string, args ...interface{}) (sql.Result, error)
    52  	NamedExec(query string, arg interface{}) (sql.Result, error)
    53  	Get(dest interface{}, query string, args ...interface{}) error
    54  	Queryx(query string, args ...interface{}) (*sqlx.Rows, error)
    55  	Rebind(query string) string
    56  	MustBegin() *sqlx.Tx
    57  	Close() error
    58  	SetMaxOpenConns(n int)
    59  	PingContext(ctx context.Context) error
    60  }
    61  
    62  // CertRecord extends CFSSL CertificateRecord by adding an enrollment ID to the record
    63  type CertRecord struct {
    64  	ID    string `db:"id"`
    65  	Level int    `db:"level"`
    66  	certdb.CertificateRecord
    67  }
    68  
    69  // AffiliationRecord defines the properties of an affiliation
    70  type AffiliationRecord struct {
    71  	ID     int    `db:"id"`
    72  	Name   string `db:"name"`
    73  	Prekey string `db:"prekey"`
    74  	Level  int    `db:"level"`
    75  }
    76  
    77  // DB is an adapter for sqlx.DB and implements FabricCADB interface
    78  type DB struct {
    79  	DB SqlxDB
    80  	// Indicates if database was successfully initialized
    81  	IsDBInitialized bool
    82  
    83  	CAName string
    84  
    85  	Metrics Metrics
    86  }
    87  
    88  // New creates an instance of DB
    89  func New(db SqlxDB, caName string, metricsProvider metrics.Provider) *DB {
    90  	return &DB{
    91  		DB:     db,
    92  		CAName: caName,
    93  		Metrics: Metrics{
    94  			APICounter:  metricsProvider.NewCounter(APICounterOpts),
    95  			APIDuration: metricsProvider.NewHistogram(APIDurationOpts),
    96  		},
    97  	}
    98  }
    99  
   100  // IsInitialized returns true if db is intialized, else false
   101  func (db *DB) IsInitialized() bool {
   102  	return db.IsDBInitialized
   103  }
   104  
   105  // SetDBInitialized sets the value for Isdbinitialized
   106  func (db *DB) SetDBInitialized(b bool) {
   107  	db.IsDBInitialized = b
   108  }
   109  
   110  // BeginTx implements BeginTx method of FabricCADB interface
   111  func (db *DB) BeginTx() FabricCATx {
   112  	return &TX{
   113  		TX:     db.DB.MustBegin(),
   114  		Record: db,
   115  	}
   116  }
   117  
   118  // Select performs select sql statement
   119  func (db *DB) Select(funcName string, dest interface{}, query string, args ...interface{}) error {
   120  	startTime := time.Now()
   121  	err := db.DB.Select(dest, query, args...)
   122  	db.recordMetric(startTime, funcName, "Select")
   123  	return err
   124  }
   125  
   126  // Exec executes query
   127  func (db *DB) Exec(funcName, query string, args ...interface{}) (sql.Result, error) {
   128  	startTime := time.Now()
   129  	res, err := db.DB.Exec(query, args...)
   130  	db.recordMetric(startTime, funcName, "Exec")
   131  	return res, err
   132  }
   133  
   134  // NamedExec executes query
   135  func (db *DB) NamedExec(funcName, query string, args interface{}) (sql.Result, error) {
   136  	startTime := time.Now()
   137  	res, err := db.DB.NamedExec(query, args)
   138  	db.recordMetric(startTime, funcName, "NamedExec")
   139  	return res, err
   140  }
   141  
   142  // Get executes query
   143  func (db *DB) Get(funcName string, dest interface{}, query string, args ...interface{}) error {
   144  	startTime := time.Now()
   145  	err := db.DB.Get(dest, query, args...)
   146  	db.recordMetric(startTime, funcName, "Get")
   147  	return err
   148  }
   149  
   150  // Queryx executes query
   151  func (db *DB) Queryx(funcName, query string, args ...interface{}) (*sqlx.Rows, error) {
   152  	startTime := time.Now()
   153  	rows, err := db.DB.Queryx(query, args...)
   154  	db.recordMetric(startTime, funcName, "Queryx")
   155  	return rows, err
   156  }
   157  
   158  // MustBegin starts a transaction
   159  func (db *DB) MustBegin() *sqlx.Tx {
   160  	return db.DB.MustBegin()
   161  }
   162  
   163  // DriverName returns database driver name
   164  func (db *DB) DriverName() string {
   165  	return db.DB.DriverName()
   166  }
   167  
   168  // Rebind parses query to properly format query
   169  func (db *DB) Rebind(query string) string {
   170  	return db.DB.Rebind(query)
   171  }
   172  
   173  // Close closes db
   174  func (db *DB) Close() error {
   175  	return db.DB.Close()
   176  }
   177  
   178  // SetMaxOpenConns sets number of max open connections
   179  func (db *DB) SetMaxOpenConns(n int) {
   180  	db.DB.SetMaxOpenConns(n)
   181  }
   182  
   183  // PingContext pings the database
   184  func (db *DB) PingContext(ctx context.Context) error {
   185  	return db.DB.PingContext(ctx)
   186  }
   187  
   188  func (db *DB) recordMetric(startTime time.Time, funcName, dbapiName string) {
   189  	db.Metrics.APICounter.With("ca_name", db.CAName, "func_name", funcName, "dbapi_name", dbapiName).Add(1)
   190  	db.Metrics.APIDuration.With("ca_name", db.CAName, "func_name", funcName, "dbapi_name", dbapiName).Observe(time.Since(startTime).Seconds())
   191  }
   192  
   193  // CurrentDBLevels returns current levels from the database
   194  func CurrentDBLevels(db FabricCADB) (*util.Levels, error) {
   195  	var err error
   196  	var identityLevel, affiliationLevel, certificateLevel, credentialLevel, rcinfoLevel, nonceLevel int
   197  
   198  	err = getProperty(db, "identity.level", &identityLevel)
   199  	if err != nil {
   200  		return nil, err
   201  	}
   202  	err = getProperty(db, "affiliation.level", &affiliationLevel)
   203  	if err != nil {
   204  		return nil, err
   205  	}
   206  	err = getProperty(db, "certificate.level", &certificateLevel)
   207  	if err != nil {
   208  		return nil, err
   209  	}
   210  	err = getProperty(db, "credential.level", &credentialLevel)
   211  	if err != nil {
   212  		return nil, err
   213  	}
   214  	err = getProperty(db, "rcinfo.level", &rcinfoLevel)
   215  	if err != nil {
   216  		return nil, err
   217  	}
   218  	err = getProperty(db, "nonce.level", &nonceLevel)
   219  	if err != nil {
   220  		return nil, err
   221  	}
   222  	return &util.Levels{
   223  		Identity:    identityLevel,
   224  		Affiliation: affiliationLevel,
   225  		Certificate: certificateLevel,
   226  		Credential:  credentialLevel,
   227  		RAInfo:      rcinfoLevel,
   228  		Nonce:       nonceLevel,
   229  	}, nil
   230  }
   231  
   232  func getProperty(db FabricCADB, propName string, val *int) error {
   233  	err := db.Get("GetProperty", val, db.Rebind("Select value FROM properties WHERE (property = ?)"), propName)
   234  	if err != nil && err.Error() == "sql: no rows in result set" {
   235  		return nil
   236  	}
   237  	return err
   238  }