// Copyright 2015 ISRG. All rights reserved // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package sa import ( "database/sql" "fmt" "strings" // Load both drivers to allow configuring either _ "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/go-sql-driver/mysql" _ "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/mattn/go-sqlite3" gorp "github.com/letsencrypt/boulder/Godeps/_workspace/src/gopkg.in/gorp.v1" "github.com/letsencrypt/boulder/core" blog "github.com/letsencrypt/boulder/log" ) var dialectMap = map[string]interface{}{ "sqlite3": gorp.SqliteDialect{}, "mysql": gorp.MySQLDialect{Engine: "InnoDB", Encoding: "UTF8"}, "postgres": gorp.PostgresDialect{}, } // NewDbMap creates the root gorp mapping object. Create one of these for each // database schema you wish to map. Each DbMap contains a list of mapped tables. // It automatically maps the tables for the primary parts of Boulder around the // Storage Authority. This may require some further work when we use a disjoint // schema, like that for `certificate-authority-data.go`. func NewDbMap(driver string, dbConnect string) (*gorp.DbMap, error) { logger := blog.GetAuditLogger() // We require this parameter for MySQL, so fail now if it is not present if driver == "mysql" && !strings.Contains(dbConnect, "parseTime=true") { return nil, fmt.Errorf("Database connect string must have parseTime=true") } db, err := sql.Open(driver, dbConnect) if err != nil { return nil, err } if err = db.Ping(); err != nil { return nil, err } logger.Debug(fmt.Sprintf("Connecting to database %s %s", driver, dbConnect)) dialect, ok := dialectMap[driver].(gorp.Dialect) if !ok { err = fmt.Errorf("Couldn't find dialect for %s", driver) return nil, err } logger.Info(fmt.Sprintf("Connected to database %s %s", driver, dbConnect)) dbmap := &gorp.DbMap{Db: db, Dialect: dialect, TypeConverter: BoulderTypeConverter{}} initTables(dbmap) return dbmap, err } // SetSQLDebug enables/disables GORP SQL-level Debugging func SetSQLDebug(dbMap *gorp.DbMap, state bool) { dbMap.TraceOff() if state { // Enable logging dbMap.TraceOn("SQL: ", &SQLLogger{blog.GetAuditLogger()}) } } // SQLLogger adapts the AuditLogger to a format GORP can use. type SQLLogger struct { log *blog.AuditLogger } // Printf adapts the AuditLogger to GORP's interface func (log *SQLLogger) Printf(format string, v ...interface{}) { log.log.Debug(fmt.Sprintf(format, v)) } // initTables constructs the table map for the ORM. If you want to also create // the tables, call CreateTablesIfNotExists on the DbMap. func initTables(dbMap *gorp.DbMap) { regTable := dbMap.AddTableWithName(core.Registration{}, "registrations").SetKeys(true, "ID") regTable.SetVersionCol("LockCol") regTable.ColMap("Key").SetMaxSize(1024).SetNotNull(true).SetUnique(true) pendingAuthzTable := dbMap.AddTableWithName(pendingauthzModel{}, "pending_authz").SetKeys(false, "ID") pendingAuthzTable.SetVersionCol("LockCol") pendingAuthzTable.ColMap("Challenges").SetMaxSize(1536) authzTable := dbMap.AddTableWithName(authzModel{}, "authz").SetKeys(false, "ID") authzTable.ColMap("Challenges").SetMaxSize(1536) dbMap.AddTableWithName(core.Certificate{}, "certificates").SetKeys(false, "Serial") dbMap.AddTableWithName(core.CertificateStatus{}, "certificateStatus").SetKeys(false, "Serial").SetVersionCol("LockCol") dbMap.AddTableWithName(core.OCSPResponse{}, "ocspResponses").SetKeys(true, "ID") dbMap.AddTableWithName(core.CRL{}, "crls").SetKeys(false, "Serial") dbMap.AddTableWithName(core.DeniedCSR{}, "deniedCSRs").SetKeys(true, "ID") }