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 }