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