github.com/hyperledger-gerrit-archive/fabric-ca@v2.0.0-alpha.0.20190916143245-4cd4192f0366+incompatible/lib/server/db/sqlite/sqlite.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package sqlite 8 9 import ( 10 "context" 11 "database/sql" 12 "strings" 13 14 "github.com/cloudflare/cfssl/log" 15 "github.com/hyperledger/fabric-ca/lib/server/db" 16 "github.com/jmoiron/sqlx" 17 "github.com/pkg/errors" 18 ) 19 20 //go:generate counterfeiter -o mocks/create.go -fake-name Create . Create 21 22 // Create is interface that defines functions need to create database transaction 23 type Create interface { 24 Exec(funcName, query string, args ...interface{}) (sql.Result, error) 25 Rebind(query string) string 26 Rollback(funcName string) error 27 Commit(funcName string) error 28 } 29 30 // Sqlite defines SQLite database 31 type Sqlite struct { 32 SqlxDB db.FabricCADB 33 CreateTx Create 34 CAName string 35 Metrics *db.Metrics 36 37 datasource string 38 } 39 40 // NewDB creates a SQLite database 41 func NewDB(datasource, caName string, metrics *db.Metrics) *Sqlite { 42 log.Debugf("Using sqlite database, connect to database in home (%s) directory", datasource) 43 return &Sqlite{ 44 datasource: datasource, 45 CAName: caName, 46 Metrics: metrics, 47 } 48 } 49 50 // Connect connects to a SQLite database 51 func (s *Sqlite) Connect() error { 52 var err error 53 log.Debugf("Creating SQLite database (%s) if it does not exist...", s.datasource) 54 sqlxDB, err := sqlx.Connect("sqlite3", s.datasource+"?_busy_timeout=5000") 55 if err != nil { 56 return errors.Wrap(err, "Failed to open sqlite3 DB") 57 } 58 s.SqlxDB = db.New(sqlxDB, s.CAName, s.Metrics) 59 return nil 60 } 61 62 // PingContext pings the database 63 func (s *Sqlite) PingContext(ctx context.Context) error { 64 err := s.SqlxDB.PingContext(ctx) 65 if err != nil { 66 return errors.Wrap(err, "Failed to ping to SQLite database") 67 } 68 return nil 69 } 70 71 // Create creates database and tables 72 func (s *Sqlite) Create() (*db.DB, error) { 73 s.CreateTx = s.SqlxDB.BeginTx() 74 err := s.CreateTables() 75 if err != nil { 76 return nil, err 77 } 78 return s.SqlxDB.(*db.DB), nil 79 } 80 81 // CreateTables creates table 82 func (s *Sqlite) CreateTables() error { 83 err := s.doTransaction("CreateTable", createAllSQLiteTables) 84 if err != nil { 85 return err 86 } 87 88 // Set maximum open connections to one. This is to share one connection 89 // across multiple go routines. This will serialize database operations 90 // with in a single server there by preventing "database is locked" 91 // error under load. The "Database is locked" error is still expected 92 // when multiple servers are accessing the same database (but mitigated 93 // by specifying _busy_timeout to 5 seconds). Since sqlite is 94 // for development and test purposes only, and is not recommended to 95 // be used in a clustered topology, setting max open connections to 96 // 1 is a quick and effective solution 97 // For more info refer to https://github.com/mattn/go-sqlite3/issues/274 98 log.Debug("Successfully opened sqlite3 DB") 99 s.SqlxDB.SetMaxOpenConns(1) 100 101 return nil 102 } 103 104 func createAllSQLiteTables(tx Create, args ...interface{}) error { 105 err := createIdentityTable(tx) 106 if err != nil { 107 return err 108 } 109 err = createAffiliationTable(tx) 110 if err != nil { 111 return err 112 } 113 err = createCertificateTable(tx) 114 if err != nil { 115 return err 116 } 117 err = createCredentialsTable(tx) 118 if err != nil { 119 return err 120 } 121 err = createRevocationComponentTable(tx) 122 if err != nil { 123 return err 124 } 125 err = createNoncesTable(tx) 126 if err != nil { 127 return err 128 } 129 err = createPropertiesTable(tx) 130 if err != nil { 131 return err 132 } 133 return nil 134 } 135 136 func createIdentityTable(tx Create) error { 137 log.Debug("Creating users table if it does not exist") 138 if _, err := tx.Exec("CreateUsersTable", "CREATE TABLE IF NOT EXISTS users (id VARCHAR(255), token bytea, type VARCHAR(256), affiliation VARCHAR(1024), attributes TEXT, state INTEGER, max_enrollments INTEGER, level INTEGER DEFAULT 0, incorrect_password_attempts INTEGER DEFAULT 0)"); err != nil { 139 return errors.Wrap(err, "Error creating users table") 140 } 141 return nil 142 } 143 144 func createAffiliationTable(tx Create) error { 145 log.Debug("Creating affiliations table if it does not exist") 146 if _, err := tx.Exec("CreateAffiliationsTable", "CREATE TABLE IF NOT EXISTS affiliations (name VARCHAR(1024) NOT NULL UNIQUE, prekey VARCHAR(1024), level INTEGER DEFAULT 0)"); err != nil { 147 return errors.Wrap(err, "Error creating affiliations table") 148 } 149 return nil 150 } 151 152 func createCertificateTable(tx Create) error { 153 log.Debug("Creating certificates table if it does not exist") 154 if _, err := tx.Exec("CreateCertificatesTable", "CREATE TABLE IF NOT EXISTS certificates (id VARCHAR(255), serial_number blob NOT NULL, authority_key_identifier blob NOT NULL, ca_label blob, status blob NOT NULL, reason int, expiry timestamp, revoked_at timestamp, pem blob NOT NULL, level INTEGER DEFAULT 0, PRIMARY KEY(serial_number, authority_key_identifier))"); err != nil { 155 return errors.Wrap(err, "Error creating certificates table") 156 } 157 return nil 158 } 159 160 func createCredentialsTable(tx Create) error { 161 log.Debug("Creating credentials table if it does not exist") 162 if _, err := tx.Exec("CreateCredentialsTable", "CREATE TABLE IF NOT EXISTS credentials (id VARCHAR(255), revocation_handle blob NOT NULL, cred blob NOT NULL, ca_label blob, status blob NOT NULL, reason int, expiry timestamp, revoked_at timestamp, level INTEGER DEFAULT 0, PRIMARY KEY(revocation_handle))"); err != nil { 163 return errors.Wrap(err, "Error creating credentials table") 164 } 165 return nil 166 } 167 168 func createRevocationComponentTable(tx Create) error { 169 log.Debug("Creating revocation_authority_info table if it does not exist") 170 if _, err := tx.Exec("CreateRevocationAuthorityTable", "CREATE TABLE IF NOT EXISTS revocation_authority_info (epoch INTEGER, next_handle INTEGER, lasthandle_in_pool INTEGER, level INTEGER DEFAULT 0, PRIMARY KEY(epoch))"); err != nil { 171 return errors.Wrap(err, "Error creating revocation_authority_info table") 172 } 173 return nil 174 } 175 176 func createNoncesTable(tx Create) error { 177 log.Debug("Creating nonces table if it does not exist") 178 if _, err := tx.Exec("CreateNoncesTable", "CREATE TABLE IF NOT EXISTS nonces (val VARCHAR(1024) NOT NULL UNIQUE, expiry timestamp, level INTEGER DEFAULT 0, PRIMARY KEY(val))"); err != nil { 179 return errors.Wrap(err, "Error creating nonces table") 180 } 181 return nil 182 } 183 184 func createPropertiesTable(tx Create) error { 185 log.Debug("Creating properties table if it does not exist") 186 _, err := tx.Exec("CreatePropertiesTable", "CREATE TABLE IF NOT EXISTS properties (property VARCHAR(255), value VARCHAR(256), PRIMARY KEY(property))") 187 if err != nil { 188 return errors.Wrap(err, "Error creating properties table") 189 } 190 _, err = tx.Exec("CreatePropertiesTable", tx.Rebind("INSERT INTO properties (property, value) VALUES ('identity.level', '0'), ('affiliation.level', '0'), ('certificate.level', '0'), ('credential.level', '0'), ('rcinfo.level', '0'), ('nonce.level', '0')")) 191 if err != nil { 192 if !strings.Contains(err.Error(), "UNIQUE constraint failed") { 193 return errors.Wrap(err, "Failed to initialize properties table") 194 } 195 } 196 return nil 197 } 198 199 func (s *Sqlite) doTransaction(funcName string, doit func(tx Create, args ...interface{}) error, args ...interface{}) error { 200 tx := s.CreateTx 201 err := doit(tx, args...) 202 if err != nil { 203 err2 := tx.Rollback(funcName) 204 if err2 != nil { 205 log.Errorf("Error encountered while rolling back transaction: %s", err2) 206 return err 207 } 208 return err 209 } 210 211 err = tx.Commit(funcName) 212 if err != nil { 213 return errors.Wrap(err, "Error encountered while committing transaction") 214 } 215 return nil 216 }