It's borpin' time! (#6982)
This change replaces [gorp] with [borp]. The changes consist of a mass renaming of the import and comments / doc fixups, plus modifications of many call sites to provide a context.Context everywhere, since gorp newly requires this (this was one of the motivating factors for the borp fork). This also refactors `github.com/letsencrypt/boulder/db.WrappedMap` and `github.com/letsencrypt/boulder/db.Transaction` to not embed their underlying gorp/borp objects, but to have them as plain fields. This ensures that we can only call methods on them that are specifically implemented in `github.com/letsencrypt/boulder/db`, so we don't miss wrapping any. This required introducing a `NewWrappedMap` method along with accessors `SQLDb()` and `BorpDB()` to get at the internal fields during metrics and logging setup. Fixes #6944
This commit is contained in:
parent
0981768793
commit
7d66d67054
|
@ -167,7 +167,7 @@ func (r *revoker) revokeCertificate(ctx context.Context, certObj core.Certificat
|
|||
}
|
||||
|
||||
func (r *revoker) revokeBySerial(ctx context.Context, serial string, reasonCode revocation.Reason, skipBlockKey bool) error {
|
||||
certObj, err := sa.SelectPrecertificate(r.dbMap, serial)
|
||||
certObj, err := sa.SelectPrecertificate(ctx, r.dbMap, serial)
|
||||
if err != nil {
|
||||
if db.IsNoRows(err) {
|
||||
return berrors.NotFoundError("precertificate with serial %q not found", serial)
|
||||
|
@ -224,7 +224,7 @@ func (r *revoker) revokeSerialBatchFile(ctx context.Context, serialPath string,
|
|||
// Finding relevant accounts will be very slow because it does not use an index.
|
||||
func (r *revoker) clearEmailAddress(ctx context.Context, email string) error {
|
||||
r.log.AuditInfof("Scanning database for accounts with email addresses matching %q in order to clear the email addresses.", email)
|
||||
regIDs, err := r.getRegIDsMatchingEmail(email)
|
||||
regIDs, err := r.getRegIDsMatchingEmail(ctx, email)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -233,7 +233,7 @@ func (r *revoker) clearEmailAddress(ctx context.Context, email string) error {
|
|||
|
||||
failures := 0
|
||||
for _, regID := range regIDs {
|
||||
err := sa.ClearEmail(r.dbMap, ctx, regID, email)
|
||||
err := sa.ClearEmail(ctx, r.dbMap, regID, email)
|
||||
if err != nil {
|
||||
// Log, but don't fail, because it took a long time to find the relevant registration IDs
|
||||
// and we don't want to have to redo that work.
|
||||
|
@ -297,7 +297,7 @@ func (r *revoker) revokeByReg(ctx context.Context, regID int64, reasonCode revoc
|
|||
return fmt.Errorf("couldn't fetch registration: %w", err)
|
||||
}
|
||||
|
||||
certObjs, err := sa.SelectPrecertificates(r.dbMap, "WHERE registrationID = :regID", map[string]interface{}{"regID": regID})
|
||||
certObjs, err := sa.SelectPrecertificates(ctx, r.dbMap, "WHERE registrationID = :regID", map[string]interface{}{"regID": regID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -372,7 +372,7 @@ func (r *revoker) revokeByPrivateKey(ctx context.Context, privateKey string) err
|
|||
return err
|
||||
}
|
||||
|
||||
matches, err := r.getCertsMatchingSPKIHash(spkiHash)
|
||||
matches, err := r.getCertsMatchingSPKIHash(ctx, spkiHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -408,9 +408,9 @@ func (r *revoker) revokeByPrivateKey(ctx context.Context, privateKey string) err
|
|||
return nil
|
||||
}
|
||||
|
||||
func (r *revoker) spkiHashInBlockedKeys(spkiHash []byte) (bool, error) {
|
||||
func (r *revoker) spkiHashInBlockedKeys(ctx context.Context, spkiHash []byte) (bool, error) {
|
||||
var count int
|
||||
err := r.dbMap.SelectOne(&count, "SELECT COUNT(*) as count FROM blockedKeys WHERE keyHash = ?", spkiHash)
|
||||
err := r.dbMap.SelectOne(ctx, &count, "SELECT COUNT(*) as count FROM blockedKeys WHERE keyHash = ?", spkiHash)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -421,9 +421,9 @@ func (r *revoker) spkiHashInBlockedKeys(spkiHash []byte) (bool, error) {
|
|||
return false, nil
|
||||
}
|
||||
|
||||
func (r *revoker) countCertsMatchingSPKIHash(spkiHash []byte) (int, error) {
|
||||
func (r *revoker) countCertsMatchingSPKIHash(ctx context.Context, spkiHash []byte) (int, error) {
|
||||
var count int
|
||||
err := r.dbMap.SelectOne(&count, "SELECT COUNT(*) as count FROM keyHashToSerial WHERE keyHash = ?", spkiHash)
|
||||
err := r.dbMap.SelectOne(ctx, &count, "SELECT COUNT(*) as count FROM keyHashToSerial WHERE keyHash = ?", spkiHash)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
@ -434,11 +434,11 @@ func (r *revoker) countCertsMatchingSPKIHash(spkiHash []byte) (int, error) {
|
|||
// contains the given email address. Since this uses a substring match, it is important
|
||||
// to subsequently parse the JSON list of addresses and look for exact matches.
|
||||
// Note: Since this does not use an index, it is very slow.
|
||||
func (r *revoker) getRegIDsMatchingEmail(email string) ([]int64, error) {
|
||||
func (r *revoker) getRegIDsMatchingEmail(ctx context.Context, email string) ([]int64, error) {
|
||||
// We use SQL `CONCAT` rather than interpolating with `+` or `%s` because we want to
|
||||
// use a `?` placeholder for the email, which prevents SQL injection.
|
||||
var regIDs []int64
|
||||
_, err := r.dbMap.Select(®IDs, "SELECT id FROM registrations WHERE contact LIKE CONCAT('%\"mailto:', ?, '\"%')", email)
|
||||
_, err := r.dbMap.Select(ctx, ®IDs, "SELECT id FROM registrations WHERE contact LIKE CONCAT('%\"mailto:', ?, '\"%')", email)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -447,9 +447,9 @@ func (r *revoker) getRegIDsMatchingEmail(email string) ([]int64, error) {
|
|||
|
||||
// TODO(#5899) Use an non-wrapped sql.Db client to iterate over results and
|
||||
// return them on a channel.
|
||||
func (r *revoker) getCertsMatchingSPKIHash(spkiHash []byte) ([]string, error) {
|
||||
func (r *revoker) getCertsMatchingSPKIHash(ctx context.Context, spkiHash []byte) ([]string, error) {
|
||||
var h []string
|
||||
_, err := r.dbMap.Select(&h, "SELECT certSerial FROM keyHashToSerial WHERE keyHash = ?", spkiHash)
|
||||
_, err := r.dbMap.Select(ctx, &h, "SELECT certSerial FROM keyHashToSerial WHERE keyHash = ?", spkiHash)
|
||||
if err != nil {
|
||||
if db.IsNoRows(err) {
|
||||
return nil, berrors.NotFoundError("no certificates with a matching SPKI hash were found")
|
||||
|
@ -466,8 +466,8 @@ func (rc revocationCodes) Len() int { return len(rc) }
|
|||
func (rc revocationCodes) Less(i, j int) bool { return rc[i] < rc[j] }
|
||||
func (rc revocationCodes) Swap(i, j int) { rc[i], rc[j] = rc[j], rc[i] }
|
||||
|
||||
func privateKeyBlock(r *revoker, dryRun bool, comment string, count int, spkiHash []byte, keyPath string) error {
|
||||
keyExists, err := r.spkiHashInBlockedKeys(spkiHash)
|
||||
func privateKeyBlock(ctx context.Context, r *revoker, dryRun bool, comment string, count int, spkiHash []byte, keyPath string) error {
|
||||
keyExists, err := r.spkiHashInBlockedKeys(ctx, spkiHash)
|
||||
if err != nil {
|
||||
return fmt.Errorf("while checking if the provided key already exists in the 'blockedKeys' table: %s", err)
|
||||
}
|
||||
|
@ -640,12 +640,12 @@ func main() {
|
|||
spkiHash, err := getPublicKeySPKIHash(publicKey)
|
||||
cmd.FailOnError(err, "While obtaining the SPKI hash for the provided key")
|
||||
|
||||
count, err := r.countCertsMatchingSPKIHash(spkiHash)
|
||||
count, err := r.countCertsMatchingSPKIHash(ctx, spkiHash)
|
||||
cmd.FailOnError(err, "While retrieving a count of certificates matching the provided key")
|
||||
r.log.AuditInfof("Found %d certificates matching the provided key", count)
|
||||
|
||||
if command == "private-key-block" {
|
||||
err := privateKeyBlock(r, *dryRun, *comment, count, spkiHash, keyPath)
|
||||
err := privateKeyBlock(ctx, r, *dryRun, *comment, count, spkiHash, keyPath)
|
||||
cmd.FailOnError(err, "")
|
||||
}
|
||||
|
||||
|
|
|
@ -93,7 +93,10 @@ func TestRevokeIncidentTableSerials(t *testing.T) {
|
|||
test.Assert(t, len(testCtx.log.GetAllMatching("No serials found in incident table")) > 0, "Expected log output not found")
|
||||
testCtx.log.Clear()
|
||||
|
||||
_, err = testIncidentsDbMap.Exec(
|
||||
ctx := context.Background()
|
||||
|
||||
_, err = testIncidentsDbMap.ExecContext(
|
||||
ctx,
|
||||
fmt.Sprintf("INSERT INTO incident_foo (%s) VALUES ('%s', %d, %d, '%s')",
|
||||
"serial, registrationID, orderID, lastNoticeSent",
|
||||
core.SerialToString(entries[0].serial),
|
||||
|
@ -104,14 +107,14 @@ func TestRevokeIncidentTableSerials(t *testing.T) {
|
|||
)
|
||||
test.AssertNotError(t, err, "while inserting row into incident table")
|
||||
|
||||
err = testCtx.revoker.revokeIncidentTableSerials(context.Background(), "incident_foo", 0, 1)
|
||||
err = testCtx.revoker.revokeIncidentTableSerials(ctx, "incident_foo", 0, 1)
|
||||
test.AssertNotError(t, err, "revokeIncidentTableSerials failed")
|
||||
|
||||
// Ensure that a populated incident table results in the expected log output.
|
||||
test.AssertNotError(t, err, "revokeIncidentTableSerials failed")
|
||||
test.Assert(t, len(testCtx.log.GetAllMatching("No serials found in incident table")) <= 0, "Expected log output not found")
|
||||
|
||||
status, err := testCtx.ssa.GetCertificateStatus(context.Background(), &sapb.Serial{Serial: core.SerialToString(entries[0].serial)})
|
||||
status, err := testCtx.ssa.GetCertificateStatus(ctx, &sapb.Serial{Serial: core.SerialToString(entries[0].serial)})
|
||||
test.AssertNotError(t, err, "failed to retrieve certificate status")
|
||||
test.AssertEquals(t, core.OCSPStatus(status.Status), core.OCSPStatusRevoked)
|
||||
}
|
||||
|
@ -143,9 +146,11 @@ func TestBlockAndRevokeByPrivateKey(t *testing.T) {
|
|||
spkiHash, err := getPublicKeySPKIHash(&duplicateEntry.testKey.PublicKey)
|
||||
test.AssertNotError(t, err, "Failed to get SPKI hash for dupe.")
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// Ensure that the SPKI hash hasn't already been added to the blockedKeys
|
||||
// table.
|
||||
keyExists, err := testCtx.revoker.spkiHashInBlockedKeys(spkiHash)
|
||||
keyExists, err := testCtx.revoker.spkiHashInBlockedKeys(ctx, spkiHash)
|
||||
test.AssertNotError(t, err, "countCertsMatchingSPKIHash for dupe failed")
|
||||
test.Assert(t, !keyExists, "SPKI hash should not be in blockedKeys")
|
||||
|
||||
|
@ -155,19 +160,19 @@ func TestBlockAndRevokeByPrivateKey(t *testing.T) {
|
|||
switch e.names[0] {
|
||||
case uniqueEntries[0].names[0]:
|
||||
// example-1337.com
|
||||
count, err := testCtx.revoker.countCertsMatchingSPKIHash(e.spkiHash)
|
||||
count, err := testCtx.revoker.countCertsMatchingSPKIHash(ctx, e.spkiHash)
|
||||
test.AssertNotError(t, err, "countCertsMatchingSPKIHash for entry failed")
|
||||
test.AssertEquals(t, count, 2)
|
||||
|
||||
case uniqueEntries[1].names[0]:
|
||||
// example-1338.com
|
||||
count, err := testCtx.revoker.countCertsMatchingSPKIHash(e.spkiHash)
|
||||
count, err := testCtx.revoker.countCertsMatchingSPKIHash(ctx, e.spkiHash)
|
||||
test.AssertNotError(t, err, "countCertsMatchingSPKIHash for entry failed")
|
||||
test.AssertEquals(t, count, 1)
|
||||
|
||||
case uniqueEntries[2].names[0]:
|
||||
// example-1339.com
|
||||
count, err := testCtx.revoker.countCertsMatchingSPKIHash(e.spkiHash)
|
||||
count, err := testCtx.revoker.countCertsMatchingSPKIHash(ctx, e.spkiHash)
|
||||
test.AssertNotError(t, err, "countCertsMatchingSPKIHash for entry failed")
|
||||
test.AssertEquals(t, count, 1)
|
||||
}
|
||||
|
@ -184,7 +189,7 @@ func TestBlockAndRevokeByPrivateKey(t *testing.T) {
|
|||
test.AssertNotError(t, err, "While attempting to revoke certificates for the provided key")
|
||||
|
||||
// Ensure that the key is not blocked, yet.
|
||||
keyExists, err = testCtx.revoker.spkiHashInBlockedKeys(spkiHash)
|
||||
keyExists, err = testCtx.revoker.spkiHashInBlockedKeys(ctx, spkiHash)
|
||||
test.AssertNotError(t, err, "countCertsMatchingSPKIHash for dupe failed")
|
||||
test.Assert(t, !keyExists, "SPKI hash should not be in blockedKeys")
|
||||
|
||||
|
@ -193,7 +198,7 @@ func TestBlockAndRevokeByPrivateKey(t *testing.T) {
|
|||
test.AssertNotError(t, err, "While attempting to block issuance for the provided key")
|
||||
|
||||
// Ensure that the key is now blocked.
|
||||
keyExists, err = testCtx.revoker.spkiHashInBlockedKeys(spkiHash)
|
||||
keyExists, err = testCtx.revoker.spkiHashInBlockedKeys(ctx, spkiHash)
|
||||
test.AssertNotError(t, err, "countCertsMatchingSPKIHash for dupe failed")
|
||||
test.Assert(t, keyExists, "SPKI hash should not be in blockedKeys")
|
||||
|
||||
|
@ -203,6 +208,7 @@ func TestBlockAndRevokeByPrivateKey(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestPrivateKeyBlock(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
testCtx := setup(t)
|
||||
defer testCtx.cleanUp()
|
||||
|
||||
|
@ -231,35 +237,35 @@ func TestPrivateKeyBlock(t *testing.T) {
|
|||
|
||||
// Query the 'keyHashToSerial' table for certificates with a matching SPKI
|
||||
// hash. We expect that since this key was re-used we'll find 2 matches.
|
||||
count, err := testCtx.revoker.countCertsMatchingSPKIHash(duplicateKeySPKI)
|
||||
count, err := testCtx.revoker.countCertsMatchingSPKIHash(ctx, duplicateKeySPKI)
|
||||
test.AssertNotError(t, err, "countCertsMatchingSPKIHash for dupe failed")
|
||||
test.AssertEquals(t, count, 2)
|
||||
|
||||
// With dryRun=true this should not block the key.
|
||||
err = privateKeyBlock(&testCtx.revoker, true, "", count, duplicateKeySPKI, duplicateKeyFile.Name())
|
||||
err = privateKeyBlock(ctx, &testCtx.revoker, true, "", count, duplicateKeySPKI, duplicateKeyFile.Name())
|
||||
test.AssertNotError(t, err, "While attempting to block issuance for the provided key")
|
||||
|
||||
// Ensure that the key is not blocked, yet.
|
||||
keyExists, err := testCtx.revoker.spkiHashInBlockedKeys(duplicateKeySPKI)
|
||||
keyExists, err := testCtx.revoker.spkiHashInBlockedKeys(ctx, duplicateKeySPKI)
|
||||
test.AssertNotError(t, err, "countCertsMatchingSPKIHash for dupe failed")
|
||||
test.Assert(t, !keyExists, "SPKI hash should not be in blockedKeys")
|
||||
|
||||
// With dryRun=false this should block the key.
|
||||
comment := "key blocked as part of test"
|
||||
err = privateKeyBlock(&testCtx.revoker, false, comment, count, duplicateKeySPKI, duplicateKeyFile.Name())
|
||||
err = privateKeyBlock(ctx, &testCtx.revoker, false, comment, count, duplicateKeySPKI, duplicateKeyFile.Name())
|
||||
test.AssertNotError(t, err, "While attempting to block issuance for the provided key")
|
||||
|
||||
// With dryRun=false this should result in an error as the key is already blocked.
|
||||
err = privateKeyBlock(&testCtx.revoker, false, "", count, duplicateKeySPKI, duplicateKeyFile.Name())
|
||||
err = privateKeyBlock(ctx, &testCtx.revoker, false, "", count, duplicateKeySPKI, duplicateKeyFile.Name())
|
||||
test.AssertError(t, err, "Attempting to block a key which is already blocked should have failed.")
|
||||
|
||||
// Ensure that the key is now blocked.
|
||||
keyExists, err = testCtx.revoker.spkiHashInBlockedKeys(duplicateKeySPKI)
|
||||
keyExists, err = testCtx.revoker.spkiHashInBlockedKeys(ctx, duplicateKeySPKI)
|
||||
test.AssertNotError(t, err, "countCertsMatchingSPKIHash for dupe failed")
|
||||
test.Assert(t, keyExists, "SPKI hash should not be in blockedKeys")
|
||||
|
||||
// Ensure that the comment was set as expected
|
||||
commentFromDB, err := testCtx.dbMap.SelectStr("SELECT comment from blockedKeys WHERE keyHash = ?", duplicateKeySPKI)
|
||||
commentFromDB, err := testCtx.dbMap.SelectStr(ctx, "SELECT comment from blockedKeys WHERE keyHash = ?", duplicateKeySPKI)
|
||||
test.AssertNotError(t, err, "Failed to get comment from database")
|
||||
u, err := user.Current()
|
||||
test.AssertNotError(t, err, "Failed to get current user")
|
||||
|
@ -268,6 +274,7 @@ func TestPrivateKeyBlock(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestPrivateKeyRevoke(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
testCtx := setup(t)
|
||||
defer testCtx.cleanUp()
|
||||
|
||||
|
@ -296,7 +303,7 @@ func TestPrivateKeyRevoke(t *testing.T) {
|
|||
|
||||
// Query the 'keyHashToSerial' table for certificates with a matching SPKI
|
||||
// hash. We expect that since this key was re-used we'll find 2 matches.
|
||||
count, err := testCtx.revoker.countCertsMatchingSPKIHash(duplicateKeySPKI)
|
||||
count, err := testCtx.revoker.countCertsMatchingSPKIHash(ctx, duplicateKeySPKI)
|
||||
test.AssertNotError(t, err, "countCertsMatchingSPKIHash for dupe failed")
|
||||
test.AssertEquals(t, count, 2)
|
||||
|
||||
|
@ -305,7 +312,7 @@ func TestPrivateKeyRevoke(t *testing.T) {
|
|||
test.AssertNotError(t, err, "While attempting to block issuance for the provided key")
|
||||
|
||||
// Ensure that the key is not blocked, yet.
|
||||
keyExists, err := testCtx.revoker.spkiHashInBlockedKeys(duplicateKeySPKI)
|
||||
keyExists, err := testCtx.revoker.spkiHashInBlockedKeys(ctx, duplicateKeySPKI)
|
||||
test.AssertNotError(t, err, "spkiHashInBlockedKeys failed for key that shouldn't be blocked yet")
|
||||
test.Assert(t, !keyExists, "SPKI hash should not be in blockedKeys")
|
||||
|
||||
|
@ -315,12 +322,12 @@ func TestPrivateKeyRevoke(t *testing.T) {
|
|||
test.AssertNotError(t, err, "While attempting to block issuance for the provided key")
|
||||
|
||||
// Ensure that the key is now blocked.
|
||||
keyExists, err = testCtx.revoker.spkiHashInBlockedKeys(duplicateKeySPKI)
|
||||
keyExists, err = testCtx.revoker.spkiHashInBlockedKeys(ctx, duplicateKeySPKI)
|
||||
test.AssertNotError(t, err, "spkiHashInBlockedKeys failed for key that should now be blocked")
|
||||
test.Assert(t, keyExists, "SPKI hash should not be in blockedKeys")
|
||||
|
||||
// Ensure that the comment was set as expected
|
||||
commentFromDB, err := testCtx.dbMap.SelectStr("SELECT comment from blockedKeys WHERE keyHash = ?", duplicateKeySPKI)
|
||||
commentFromDB, err := testCtx.dbMap.SelectStr(ctx, "SELECT comment from blockedKeys WHERE keyHash = ?", duplicateKeySPKI)
|
||||
test.AssertNotError(t, err, "Failed to get comment from database")
|
||||
u, err := user.Current()
|
||||
test.AssertNotError(t, err, "Failed to get current user")
|
||||
|
|
|
@ -82,9 +82,10 @@ func (ubk uncheckedBlockedKey) String() string {
|
|||
ubk.RevokedBy, ubk.KeyHash)
|
||||
}
|
||||
|
||||
func (bkr *badKeyRevoker) countUncheckedKeys() (int, error) {
|
||||
func (bkr *badKeyRevoker) countUncheckedKeys(ctx context.Context) (int, error) {
|
||||
var count int
|
||||
err := bkr.dbMap.SelectOne(
|
||||
ctx,
|
||||
&count,
|
||||
`SELECT COUNT(*)
|
||||
FROM (SELECT 1 FROM blockedKeys
|
||||
|
@ -95,9 +96,10 @@ func (bkr *badKeyRevoker) countUncheckedKeys() (int, error) {
|
|||
return count, err
|
||||
}
|
||||
|
||||
func (bkr *badKeyRevoker) selectUncheckedKey() (uncheckedBlockedKey, error) {
|
||||
func (bkr *badKeyRevoker) selectUncheckedKey(ctx context.Context) (uncheckedBlockedKey, error) {
|
||||
var row uncheckedBlockedKey
|
||||
err := bkr.dbMap.SelectOne(
|
||||
ctx,
|
||||
&row,
|
||||
`SELECT keyHash, revokedBy
|
||||
FROM blockedKeys
|
||||
|
@ -125,7 +127,7 @@ func (uc unrevokedCertificate) String() string {
|
|||
// findUnrevoked looks for all unexpired, currently valid certificates which have a specific SPKI hash,
|
||||
// by looking first at the keyHashToSerial table and then the certificateStatus and certificates tables.
|
||||
// If the number of certificates it finds is larger than bkr.maxRevocations it'll error out.
|
||||
func (bkr *badKeyRevoker) findUnrevoked(unchecked uncheckedBlockedKey) ([]unrevokedCertificate, error) {
|
||||
func (bkr *badKeyRevoker) findUnrevoked(ctx context.Context, unchecked uncheckedBlockedKey) ([]unrevokedCertificate, error) {
|
||||
var unrevokedCerts []unrevokedCertificate
|
||||
initialID := 0
|
||||
for {
|
||||
|
@ -134,6 +136,7 @@ func (bkr *badKeyRevoker) findUnrevoked(unchecked uncheckedBlockedKey) ([]unrevo
|
|||
CertSerial string
|
||||
}
|
||||
_, err := bkr.dbMap.Select(
|
||||
ctx,
|
||||
&batch,
|
||||
"SELECT id, certSerial FROM keyHashToSerial WHERE keyHash = ? AND id > ? AND certNotAfter > ? ORDER BY id LIMIT ?",
|
||||
unchecked.KeyHash,
|
||||
|
@ -155,6 +158,7 @@ func (bkr *badKeyRevoker) findUnrevoked(unchecked uncheckedBlockedKey) ([]unrevo
|
|||
// possible we could get multiple results for a single serial number, but they
|
||||
// would be duplicates.
|
||||
err = bkr.dbMap.SelectOne(
|
||||
ctx,
|
||||
&unrevokedCert,
|
||||
`SELECT cs.id, cs.serial, c.registrationID, c.der, cs.status, cs.isExpired
|
||||
FROM certificateStatus AS cs
|
||||
|
@ -181,19 +185,19 @@ func (bkr *badKeyRevoker) findUnrevoked(unchecked uncheckedBlockedKey) ([]unrevo
|
|||
|
||||
// markRowChecked updates a row in the blockedKeys table to mark a keyHash
|
||||
// as having been checked for extant unrevoked certificates.
|
||||
func (bkr *badKeyRevoker) markRowChecked(unchecked uncheckedBlockedKey) error {
|
||||
_, err := bkr.dbMap.Exec("UPDATE blockedKeys SET extantCertificatesChecked = true WHERE keyHash = ?", unchecked.KeyHash)
|
||||
func (bkr *badKeyRevoker) markRowChecked(ctx context.Context, unchecked uncheckedBlockedKey) error {
|
||||
_, err := bkr.dbMap.ExecContext(ctx, "UPDATE blockedKeys SET extantCertificatesChecked = true WHERE keyHash = ?", unchecked.KeyHash)
|
||||
return err
|
||||
}
|
||||
|
||||
// resolveContacts builds a map of id -> email addresses
|
||||
func (bkr *badKeyRevoker) resolveContacts(ids []int64) (map[int64][]string, error) {
|
||||
func (bkr *badKeyRevoker) resolveContacts(ctx context.Context, ids []int64) (map[int64][]string, error) {
|
||||
idToEmail := map[int64][]string{}
|
||||
for _, id := range ids {
|
||||
var emails struct {
|
||||
Contact []string
|
||||
}
|
||||
err := bkr.dbMap.SelectOne(&emails, "SELECT contact FROM registrations WHERE id = ?", id)
|
||||
err := bkr.dbMap.SelectOne(ctx, &emails, "SELECT contact FROM registrations WHERE id = ?", id)
|
||||
if err != nil {
|
||||
// ErrNoRows is not acceptable here since there should always be a
|
||||
// row for the registration, even if there are no contacts
|
||||
|
@ -289,9 +293,9 @@ func (bkr *badKeyRevoker) revokeCerts(revokerEmails []string, emailToCerts map[s
|
|||
|
||||
// invoke processes a single key in the blockedKeys table and returns whether
|
||||
// there were any rows to process or not.
|
||||
func (bkr *badKeyRevoker) invoke() (bool, error) {
|
||||
func (bkr *badKeyRevoker) invoke(ctx context.Context) (bool, error) {
|
||||
// Gather a count of rows to be processed.
|
||||
uncheckedCount, err := bkr.countUncheckedKeys()
|
||||
uncheckedCount, err := bkr.countUncheckedKeys(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -307,7 +311,7 @@ func (bkr *badKeyRevoker) invoke() (bool, error) {
|
|||
}
|
||||
|
||||
// select a row to process
|
||||
unchecked, err := bkr.selectUncheckedKey()
|
||||
unchecked, err := bkr.selectUncheckedKey(ctx)
|
||||
if err != nil {
|
||||
if db.IsNoRows(err) {
|
||||
return true, nil
|
||||
|
@ -317,7 +321,7 @@ func (bkr *badKeyRevoker) invoke() (bool, error) {
|
|||
bkr.logger.AuditInfo(fmt.Sprintf("found unchecked block key to work on: %s", unchecked))
|
||||
|
||||
// select all unrevoked, unexpired serials associated with the blocked key hash
|
||||
unrevokedCerts, err := bkr.findUnrevoked(unchecked)
|
||||
unrevokedCerts, err := bkr.findUnrevoked(ctx, unchecked)
|
||||
if err != nil {
|
||||
bkr.logger.AuditInfo(fmt.Sprintf("finding unrevoked certificates related to %s: %s",
|
||||
unchecked, err))
|
||||
|
@ -326,7 +330,7 @@ func (bkr *badKeyRevoker) invoke() (bool, error) {
|
|||
if len(unrevokedCerts) == 0 {
|
||||
bkr.logger.AuditInfo(fmt.Sprintf("found no certificates that need revoking related to %s, marking row as checked", unchecked))
|
||||
// mark row as checked
|
||||
err = bkr.markRowChecked(unchecked)
|
||||
err = bkr.markRowChecked(ctx, unchecked)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -352,7 +356,7 @@ func (bkr *badKeyRevoker) invoke() (bool, error) {
|
|||
ids = append(ids, unchecked.RevokedBy)
|
||||
}
|
||||
// get contact addresses for the list of IDs
|
||||
idToEmails, err := bkr.resolveContacts(ids)
|
||||
idToEmails, err := bkr.resolveContacts(ctx, ids)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -377,7 +381,7 @@ func (bkr *badKeyRevoker) invoke() (bool, error) {
|
|||
}
|
||||
|
||||
// mark the key as checked
|
||||
err = bkr.markRowChecked(unchecked)
|
||||
err = bkr.markRowChecked(ctx, unchecked)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -523,7 +527,7 @@ func main() {
|
|||
|
||||
// Run bad-key-revoker in a loop. Backoff if no work or errors.
|
||||
for {
|
||||
noWork, err := bkr.invoke()
|
||||
noWork, err := bkr.invoke(context.Background())
|
||||
if err != nil {
|
||||
keysProcessed.WithLabelValues("error").Inc()
|
||||
logger.AuditErrf("failed to process blockedKeys row: %s", err)
|
||||
|
|
|
@ -34,7 +34,7 @@ func randHash(t *testing.T) []byte {
|
|||
|
||||
func insertBlockedRow(t *testing.T, dbMap *db.WrappedMap, fc clock.Clock, hash []byte, by int64, checked bool) {
|
||||
t.Helper()
|
||||
_, err := dbMap.Exec(`INSERT INTO blockedKeys
|
||||
_, err := dbMap.ExecContext(context.Background(), `INSERT INTO blockedKeys
|
||||
(keyHash, added, source, revokedBy, extantCertificatesChecked)
|
||||
VALUES
|
||||
(?, ?, ?, ?, ?)`,
|
||||
|
@ -48,6 +48,8 @@ func insertBlockedRow(t *testing.T, dbMap *db.WrappedMap, fc clock.Clock, hash [
|
|||
}
|
||||
|
||||
func TestSelectUncheckedRows(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
dbMap, err := sa.DBMapForTest(vars.DBConnSAFullPerms)
|
||||
test.AssertNotError(t, err, "failed setting up db client")
|
||||
defer test.ResetBoulderTestDatabase(t)()
|
||||
|
@ -62,18 +64,18 @@ func TestSelectUncheckedRows(t *testing.T) {
|
|||
|
||||
hashA, hashB, hashC := randHash(t), randHash(t), randHash(t)
|
||||
insertBlockedRow(t, dbMap, fc, hashA, 1, true)
|
||||
count, err := bkr.countUncheckedKeys()
|
||||
count, err := bkr.countUncheckedKeys(ctx)
|
||||
test.AssertNotError(t, err, "countUncheckedKeys failed")
|
||||
test.AssertEquals(t, count, 0)
|
||||
_, err = bkr.selectUncheckedKey()
|
||||
_, err = bkr.selectUncheckedKey(ctx)
|
||||
test.AssertError(t, err, "selectUncheckedKey didn't fail with no rows to process")
|
||||
test.Assert(t, db.IsNoRows(err), "returned error is not sql.ErrNoRows")
|
||||
insertBlockedRow(t, dbMap, fc, hashB, 1, false)
|
||||
insertBlockedRow(t, dbMap, fc, hashC, 1, false)
|
||||
count, err = bkr.countUncheckedKeys()
|
||||
count, err = bkr.countUncheckedKeys(ctx)
|
||||
test.AssertNotError(t, err, "countUncheckedKeys failed")
|
||||
test.AssertEquals(t, count, 2)
|
||||
row, err := bkr.selectUncheckedKey()
|
||||
row, err := bkr.selectUncheckedKey(ctx)
|
||||
test.AssertNotError(t, err, "selectUncheckKey failed")
|
||||
test.AssertByteEquals(t, row.KeyHash, hashB)
|
||||
test.AssertEquals(t, row.RevokedBy, int64(1))
|
||||
|
@ -92,7 +94,8 @@ func insertRegistration(t *testing.T, dbMap *db.WrappedMap, fc clock.Clock, addr
|
|||
}
|
||||
contactStr = fmt.Sprintf("[%s]", strings.Join(contacts, ","))
|
||||
}
|
||||
res, err := dbMap.Exec(
|
||||
res, err := dbMap.ExecContext(
|
||||
context.Background(),
|
||||
"INSERT INTO registrations (jwk, jwk_sha256, contact, agreement, initialIP, createdAt, status, LockCol) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
[]byte{},
|
||||
fmt.Sprintf("%x", jwkHash),
|
||||
|
@ -124,13 +127,15 @@ func insertGoodCert(t *testing.T, dbMap *db.WrappedMap, fc clock.Clock, keyHash
|
|||
|
||||
func insertCert(t *testing.T, dbMap *db.WrappedMap, fc clock.Clock, keyHash []byte, serial string, regID int64, expiredStatus ExpiredStatus, status core.OCSPStatus) {
|
||||
t.Helper()
|
||||
ctx := context.Background()
|
||||
|
||||
expiresOffset := 0 * time.Second
|
||||
if !expiredStatus {
|
||||
expiresOffset = 90*24*time.Hour - 1*time.Second // 90 days exclusive
|
||||
}
|
||||
|
||||
_, err := dbMap.Exec(
|
||||
_, err := dbMap.ExecContext(
|
||||
ctx,
|
||||
`INSERT IGNORE INTO keyHashToSerial
|
||||
(keyHash, certNotAfter, certSerial) VALUES
|
||||
(?, ?, ?)`,
|
||||
|
@ -140,7 +145,8 @@ func insertCert(t *testing.T, dbMap *db.WrappedMap, fc clock.Clock, keyHash []by
|
|||
)
|
||||
test.AssertNotError(t, err, "failed to insert test keyHashToSerial row")
|
||||
|
||||
_, err = dbMap.Exec(
|
||||
_, err = dbMap.ExecContext(
|
||||
ctx,
|
||||
"INSERT INTO certificateStatus (serial, status, isExpired, ocspLastUpdated, revokedDate, revokedReason, lastExpirationNagSent) VALUES (?, ?, ?, ?, ?, ?, ?)",
|
||||
serial,
|
||||
status,
|
||||
|
@ -152,7 +158,8 @@ func insertCert(t *testing.T, dbMap *db.WrappedMap, fc clock.Clock, keyHash []by
|
|||
)
|
||||
test.AssertNotError(t, err, "failed to insert test certificateStatus row")
|
||||
|
||||
_, err = dbMap.Exec(
|
||||
_, err = dbMap.ExecContext(
|
||||
ctx,
|
||||
"INSERT INTO precertificates (serial, registrationID, der, issued, expires) VALUES (?, ?, ?, ?, ?)",
|
||||
serial,
|
||||
regID,
|
||||
|
@ -162,7 +169,8 @@ func insertCert(t *testing.T, dbMap *db.WrappedMap, fc clock.Clock, keyHash []by
|
|||
)
|
||||
test.AssertNotError(t, err, "failed to insert test certificateStatus row")
|
||||
|
||||
_, err = dbMap.Exec(
|
||||
_, err = dbMap.ExecContext(
|
||||
ctx,
|
||||
"INSERT INTO certificates (serial, registrationID, der, digest, issued, expires) VALUES (?, ?, ?, ?, ?, ?)",
|
||||
serial,
|
||||
regID,
|
||||
|
@ -178,6 +186,8 @@ func insertCert(t *testing.T, dbMap *db.WrappedMap, fc clock.Clock, keyHash []by
|
|||
// does not have a corresponding entry in the certificateStatus and
|
||||
// precertificates table.
|
||||
func TestFindUnrevokedNoRows(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
dbMap, err := sa.DBMapForTest(vars.DBConnSAFullPerms)
|
||||
test.AssertNotError(t, err, "failed setting up db client")
|
||||
defer test.ResetBoulderTestDatabase(t)()
|
||||
|
@ -185,7 +195,8 @@ func TestFindUnrevokedNoRows(t *testing.T) {
|
|||
fc := clock.NewFake()
|
||||
|
||||
hashA := randHash(t)
|
||||
_, err = dbMap.Exec(
|
||||
_, err = dbMap.ExecContext(
|
||||
ctx,
|
||||
"INSERT INTO keyHashToSerial (keyHash, certNotAfter, certSerial) VALUES (?, ?, ?)",
|
||||
hashA,
|
||||
fc.Now().Add(90*24*time.Hour-1*time.Second), // 90 days exclusive
|
||||
|
@ -194,11 +205,13 @@ func TestFindUnrevokedNoRows(t *testing.T) {
|
|||
test.AssertNotError(t, err, "failed to insert test keyHashToSerial row")
|
||||
|
||||
bkr := &badKeyRevoker{dbMap: dbMap, serialBatchSize: 1, maxRevocations: 10, clk: fc}
|
||||
_, err = bkr.findUnrevoked(uncheckedBlockedKey{KeyHash: hashA})
|
||||
_, err = bkr.findUnrevoked(ctx, uncheckedBlockedKey{KeyHash: hashA})
|
||||
test.Assert(t, db.IsNoRows(err), "expected NoRows error")
|
||||
}
|
||||
|
||||
func TestFindUnrevoked(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
dbMap, err := sa.DBMapForTest(vars.DBConnSAFullPerms)
|
||||
test.AssertNotError(t, err, "failed setting up db client")
|
||||
defer test.ResetBoulderTestDatabase(t)()
|
||||
|
@ -219,7 +232,7 @@ func TestFindUnrevoked(t *testing.T) {
|
|||
// insert revoked
|
||||
insertCert(t, dbMap, fc, hashA, "dd", regID, Unexpired, Revoked)
|
||||
|
||||
rows, err := bkr.findUnrevoked(uncheckedBlockedKey{KeyHash: hashA})
|
||||
rows, err := bkr.findUnrevoked(ctx, uncheckedBlockedKey{KeyHash: hashA})
|
||||
test.AssertNotError(t, err, "findUnrevoked failed")
|
||||
test.AssertEquals(t, len(rows), 1)
|
||||
test.AssertEquals(t, rows[0].Serial, "ff")
|
||||
|
@ -227,7 +240,7 @@ func TestFindUnrevoked(t *testing.T) {
|
|||
test.AssertByteEquals(t, rows[0].DER, []byte{1, 2, 3})
|
||||
|
||||
bkr.maxRevocations = 0
|
||||
_, err = bkr.findUnrevoked(uncheckedBlockedKey{KeyHash: hashA})
|
||||
_, err = bkr.findUnrevoked(ctx, uncheckedBlockedKey{KeyHash: hashA})
|
||||
test.AssertError(t, err, "findUnrevoked didn't fail with 0 maxRevocations")
|
||||
test.AssertEquals(t, err.Error(), fmt.Sprintf("too many certificates to revoke associated with %x: got 1, max 0", hashA))
|
||||
}
|
||||
|
@ -246,7 +259,7 @@ func TestResolveContacts(t *testing.T) {
|
|||
regIDC := insertRegistration(t, dbMap, fc, "example.com")
|
||||
regIDD := insertRegistration(t, dbMap, fc, "example-2.com")
|
||||
|
||||
idToEmail, err := bkr.resolveContacts([]int64{regIDA, regIDB, regIDC, regIDD})
|
||||
idToEmail, err := bkr.resolveContacts(context.Background(), []int64{regIDA, regIDB, regIDC, regIDD})
|
||||
test.AssertNotError(t, err, "resolveContacts failed")
|
||||
test.AssertDeepEquals(t, idToEmail, map[int64][]string{
|
||||
regIDA: {""},
|
||||
|
@ -308,6 +321,8 @@ func TestRevokeCerts(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCertificateAbsent(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
dbMap, err := sa.DBMapForTest(vars.DBConnSAFullPerms)
|
||||
test.AssertNotError(t, err, "failed setting up db client")
|
||||
defer test.ResetBoulderTestDatabase(t)()
|
||||
|
@ -321,7 +336,8 @@ func TestCertificateAbsent(t *testing.T) {
|
|||
|
||||
// Add an entry to keyHashToSerial but not to certificateStatus or certificate
|
||||
// status, and expect an error.
|
||||
_, err = dbMap.Exec(
|
||||
_, err = dbMap.ExecContext(
|
||||
ctx,
|
||||
"INSERT INTO keyHashToSerial (keyHash, certNotAfter, certSerial) VALUES (?, ?, ?)",
|
||||
hashA,
|
||||
fc.Now().Add(90*24*time.Hour-1*time.Second), // 90 days exclusive
|
||||
|
@ -340,11 +356,13 @@ func TestCertificateAbsent(t *testing.T) {
|
|||
logger: blog.NewMock(),
|
||||
clk: fc,
|
||||
}
|
||||
_, err = bkr.invoke()
|
||||
_, err = bkr.invoke(ctx)
|
||||
test.AssertError(t, err, "expected error when row in keyHashToSerial didn't have a matching cert")
|
||||
}
|
||||
|
||||
func TestInvoke(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
dbMap, err := sa.DBMapForTest(vars.DBConnSAFullPerms)
|
||||
test.AssertNotError(t, err, "failed setting up db client")
|
||||
defer test.ResetBoulderTestDatabase(t)()
|
||||
|
@ -377,7 +395,7 @@ func TestInvoke(t *testing.T) {
|
|||
insertGoodCert(t, dbMap, fc, hashA, "dd", regIDC)
|
||||
insertGoodCert(t, dbMap, fc, hashA, "cc", regIDD)
|
||||
|
||||
noWork, err := bkr.invoke()
|
||||
noWork, err := bkr.invoke(ctx)
|
||||
test.AssertNotError(t, err, "invoke failed")
|
||||
test.AssertEquals(t, noWork, false)
|
||||
test.AssertEquals(t, mr.revoked, 4)
|
||||
|
@ -388,7 +406,7 @@ func TestInvoke(t *testing.T) {
|
|||
var checked struct {
|
||||
ExtantCertificatesChecked bool
|
||||
}
|
||||
err = dbMap.SelectOne(&checked, "SELECT extantCertificatesChecked FROM blockedKeys WHERE keyHash = ?", hashA)
|
||||
err = dbMap.SelectOne(ctx, &checked, "SELECT extantCertificatesChecked FROM blockedKeys WHERE keyHash = ?", hashA)
|
||||
test.AssertNotError(t, err, "failed to select row from blockedKeys")
|
||||
test.AssertEquals(t, checked.ExtantCertificatesChecked, true)
|
||||
|
||||
|
@ -397,16 +415,16 @@ func TestInvoke(t *testing.T) {
|
|||
insertBlockedRow(t, dbMap, fc, hashB, regIDC, false)
|
||||
insertCert(t, dbMap, fc, hashB, "bb", regIDA, Expired, Revoked)
|
||||
|
||||
noWork, err = bkr.invoke()
|
||||
noWork, err = bkr.invoke(ctx)
|
||||
test.AssertNotError(t, err, "invoke failed")
|
||||
test.AssertEquals(t, noWork, false)
|
||||
|
||||
checked.ExtantCertificatesChecked = false
|
||||
err = dbMap.SelectOne(&checked, "SELECT extantCertificatesChecked FROM blockedKeys WHERE keyHash = ?", hashB)
|
||||
err = dbMap.SelectOne(ctx, &checked, "SELECT extantCertificatesChecked FROM blockedKeys WHERE keyHash = ?", hashB)
|
||||
test.AssertNotError(t, err, "failed to select row from blockedKeys")
|
||||
test.AssertEquals(t, checked.ExtantCertificatesChecked, true)
|
||||
|
||||
noWork, err = bkr.invoke()
|
||||
noWork, err = bkr.invoke(ctx)
|
||||
test.AssertNotError(t, err, "invoke failed")
|
||||
test.AssertEquals(t, noWork, true)
|
||||
}
|
||||
|
@ -450,7 +468,7 @@ func TestInvokeRevokerHasNoExtantCerts(t *testing.T) {
|
|||
insertGoodCert(t, dbMap, fc, hashA, "cc", regIDC)
|
||||
insertGoodCert(t, dbMap, fc, hashA, "bb", regIDC)
|
||||
|
||||
noWork, err := bkr.invoke()
|
||||
noWork, err := bkr.invoke(context.Background())
|
||||
test.AssertNotError(t, err, "invoke failed")
|
||||
test.AssertEquals(t, noWork, false)
|
||||
test.AssertEquals(t, mr.revoked, 4)
|
||||
|
|
|
@ -82,12 +82,12 @@ type reportEntry struct {
|
|||
Problems []string `json:"problems,omitempty"`
|
||||
}
|
||||
|
||||
// certDB is an interface collecting the gorp.saDbMap functions that the various
|
||||
// certDB is an interface collecting the borp.DbMap functions that the various
|
||||
// parts of cert-checker rely on. Using this adapter shim allows tests to swap
|
||||
// out the saDbMap implementation.
|
||||
type certDB interface {
|
||||
Select(i interface{}, query string, args ...interface{}) ([]interface{}, error)
|
||||
SelectNullInt(query string, args ...interface{}) (sql.NullInt64, error)
|
||||
Select(ctx context.Context, i interface{}, query string, args ...interface{}) ([]interface{}, error)
|
||||
SelectNullInt(ctx context.Context, query string, args ...interface{}) (sql.NullInt64, error)
|
||||
}
|
||||
|
||||
type certChecker struct {
|
||||
|
@ -125,7 +125,7 @@ func newChecker(saDbMap certDB,
|
|||
}
|
||||
}
|
||||
|
||||
func (c *certChecker) getCerts(unexpiredOnly bool) error {
|
||||
func (c *certChecker) getCerts(ctx context.Context, unexpiredOnly bool) error {
|
||||
c.issuedReport.end = c.clock.Now()
|
||||
c.issuedReport.begin = c.issuedReport.end.Add(-c.checkPeriod)
|
||||
|
||||
|
@ -139,6 +139,7 @@ func (c *certChecker) getCerts(unexpiredOnly bool) error {
|
|||
var retries int
|
||||
for {
|
||||
sni, err = c.dbMap.SelectNullInt(
|
||||
ctx,
|
||||
"SELECT MIN(id) FROM certificates WHERE issued >= :issued AND expires >= :now",
|
||||
args,
|
||||
)
|
||||
|
@ -170,6 +171,7 @@ func (c *certChecker) getCerts(unexpiredOnly bool) error {
|
|||
|
||||
for {
|
||||
certs, err := sa.SelectCertificates(
|
||||
ctx,
|
||||
c.dbMap,
|
||||
"WHERE id > :id AND issued >= :issued AND expires >= :now ORDER BY id LIMIT :limit",
|
||||
args,
|
||||
|
@ -199,9 +201,9 @@ func (c *certChecker) getCerts(unexpiredOnly bool) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *certChecker) processCerts(wg *sync.WaitGroup, badResultsOnly bool, ignoredLints map[string]bool) {
|
||||
func (c *certChecker) processCerts(ctx context.Context, wg *sync.WaitGroup, badResultsOnly bool, ignoredLints map[string]bool) {
|
||||
for cert := range c.certs {
|
||||
dnsNames, problems := c.checkCert(cert, ignoredLints)
|
||||
dnsNames, problems := c.checkCert(ctx, cert, ignoredLints)
|
||||
valid := len(problems) == 0
|
||||
c.rMu.Lock()
|
||||
if !badResultsOnly || (badResultsOnly && !valid) {
|
||||
|
@ -245,8 +247,8 @@ var expectedExtensionContent = map[string][]byte{
|
|||
// likely valid at the time the certificate was issued. Authorizations with
|
||||
// status = "deactivated" are counted for this, so long as their validatedAt
|
||||
// is before the issuance and expiration is after.
|
||||
func (c *certChecker) checkValidations(cert core.Certificate, dnsNames []string) error {
|
||||
authzs, err := sa.SelectAuthzsMatchingIssuance(c.dbMap, cert.RegistrationID, cert.Issued, dnsNames)
|
||||
func (c *certChecker) checkValidations(ctx context.Context, cert core.Certificate, dnsNames []string) error {
|
||||
authzs, err := sa.SelectAuthzsMatchingIssuance(ctx, c.dbMap, cert.RegistrationID, cert.Issued, dnsNames)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error checking authzs for certificate %s: %w", cert.Serial, err)
|
||||
}
|
||||
|
@ -277,7 +279,7 @@ func (c *certChecker) checkValidations(cert core.Certificate, dnsNames []string)
|
|||
}
|
||||
|
||||
// checkCert returns a list of DNS names in the certificate and a list of problems with the certificate.
|
||||
func (c *certChecker) checkCert(cert core.Certificate, ignoredLints map[string]bool) ([]string, []string) {
|
||||
func (c *certChecker) checkCert(ctx context.Context, cert core.Certificate, ignoredLints map[string]bool) ([]string, []string) {
|
||||
var dnsNames []string
|
||||
var problems []string
|
||||
|
||||
|
@ -386,13 +388,13 @@ func (c *certChecker) checkCert(cert core.Certificate, ignoredLints map[string]b
|
|||
if err != nil {
|
||||
problems = append(problems, fmt.Sprintf("Couldn't parse stored certificate: %s", err))
|
||||
}
|
||||
err = c.kp.GoodKey(context.Background(), p.PublicKey)
|
||||
err = c.kp.GoodKey(ctx, p.PublicKey)
|
||||
if err != nil {
|
||||
problems = append(problems, fmt.Sprintf("Key Policy isn't willing to issue for public key: %s", err))
|
||||
}
|
||||
|
||||
if features.Enabled(features.CertCheckerChecksValidations) {
|
||||
err = c.checkValidations(cert, parsedCert.DNSNames)
|
||||
err = c.checkValidations(ctx, cert, parsedCert.DNSNames)
|
||||
if err != nil {
|
||||
if features.Enabled(features.CertCheckerRequiresValidations) {
|
||||
problems = append(problems, err.Error())
|
||||
|
@ -531,7 +533,7 @@ func main() {
|
|||
// is finished it will close the certificate channel which allows the range
|
||||
// loops in checker.processCerts to break
|
||||
go func() {
|
||||
err := checker.getCerts(config.CertChecker.UnexpiredOnly)
|
||||
err := checker.getCerts(context.TODO(), config.CertChecker.UnexpiredOnly)
|
||||
cmd.FailOnError(err, "Batch retrieval of certificates failed")
|
||||
}()
|
||||
|
||||
|
@ -541,7 +543,7 @@ func main() {
|
|||
wg.Add(1)
|
||||
go func() {
|
||||
s := checker.clock.Now()
|
||||
checker.processCerts(wg, config.CertChecker.BadResultsOnly, ignoredLintsMap)
|
||||
checker.processCerts(context.TODO(), wg, config.CertChecker.BadResultsOnly, ignoredLintsMap)
|
||||
checkerLatency.Observe(checker.clock.Since(s).Seconds())
|
||||
}()
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ func BenchmarkCheckCert(b *testing.B) {
|
|||
}
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
checker.checkCert(cert, nil)
|
||||
checker.checkCert(context.Background(), cert, nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,7 +130,7 @@ func TestCheckWildcardCert(t *testing.T) {
|
|||
Issued: parsed.NotBefore,
|
||||
DER: wildcardCertDer,
|
||||
}
|
||||
_, problems := checker.checkCert(cert, nil)
|
||||
_, problems := checker.checkCert(context.Background(), cert, nil)
|
||||
for _, p := range problems {
|
||||
t.Errorf(p)
|
||||
}
|
||||
|
@ -163,7 +163,7 @@ func TestCheckCertReturnsDNSNames(t *testing.T) {
|
|||
DER: block.Bytes,
|
||||
}
|
||||
|
||||
names, problems := checker.checkCert(cert, nil)
|
||||
names, problems := checker.checkCert(context.Background(), cert, nil)
|
||||
if !reflect.DeepEqual(names, []string{"quite_invalid.com", "al--so--wr--ong.com"}) {
|
||||
t.Errorf("didn't get expected DNS names. other problems: %s", strings.Join(problems, "\n"))
|
||||
}
|
||||
|
@ -268,7 +268,7 @@ func TestCheckCert(t *testing.T) {
|
|||
Expires: goodExpiry.AddDate(0, 0, 2), // Expiration doesn't match
|
||||
}
|
||||
|
||||
_, problems := checker.checkCert(cert, nil)
|
||||
_, problems := checker.checkCert(context.Background(), cert, nil)
|
||||
|
||||
problemsMap := map[string]int{
|
||||
"Stored digest doesn't match certificate digest": 1,
|
||||
|
@ -294,7 +294,7 @@ func TestCheckCert(t *testing.T) {
|
|||
|
||||
// Same settings as above, but the stored serial number in the DB is invalid.
|
||||
cert.Serial = "not valid"
|
||||
_, problems = checker.checkCert(cert, nil)
|
||||
_, problems = checker.checkCert(context.Background(), cert, nil)
|
||||
foundInvalidSerialProblem := false
|
||||
for _, p := range problems {
|
||||
if p == "Stored serial is invalid" {
|
||||
|
@ -319,7 +319,7 @@ func TestCheckCert(t *testing.T) {
|
|||
cert.DER = goodCertDer
|
||||
cert.Expires = parsed.NotAfter
|
||||
cert.Issued = parsed.NotBefore
|
||||
_, problems = checker.checkCert(cert, nil)
|
||||
_, problems = checker.checkCert(context.Background(), cert, nil)
|
||||
test.AssertEquals(t, len(problems), 0)
|
||||
})
|
||||
}
|
||||
|
@ -365,12 +365,12 @@ func TestGetAndProcessCerts(t *testing.T) {
|
|||
}
|
||||
|
||||
batchSize = 2
|
||||
err = checker.getCerts(false)
|
||||
err = checker.getCerts(context.Background(), false)
|
||||
test.AssertNotError(t, err, "Failed to retrieve certificates")
|
||||
test.AssertEquals(t, len(checker.certs), 5)
|
||||
wg := new(sync.WaitGroup)
|
||||
wg.Add(1)
|
||||
checker.processCerts(wg, false, nil)
|
||||
checker.processCerts(context.Background(), wg, false, nil)
|
||||
test.AssertEquals(t, checker.issuedReport.BadCerts, int64(5))
|
||||
test.AssertEquals(t, len(checker.issuedReport.Entries), 5)
|
||||
}
|
||||
|
@ -383,7 +383,7 @@ type mismatchedCountDB struct{}
|
|||
// `getCerts` calls `SelectInt` first to determine how many rows there are
|
||||
// matching the `getCertsCountQuery` criteria. For this mock we return
|
||||
// a non-zero number
|
||||
func (db mismatchedCountDB) SelectNullInt(_ string, _ ...interface{}) (sql.NullInt64, error) {
|
||||
func (db mismatchedCountDB) SelectNullInt(_ context.Context, _ string, _ ...interface{}) (sql.NullInt64, error) {
|
||||
return sql.NullInt64{
|
||||
Int64: 99999,
|
||||
Valid: true,
|
||||
|
@ -393,7 +393,7 @@ func (db mismatchedCountDB) SelectNullInt(_ string, _ ...interface{}) (sql.NullI
|
|||
|
||||
// `getCerts` then calls `Select` to retrieve the Certificate rows. We pull
|
||||
// a dastardly switch-a-roo here and return an empty set
|
||||
func (db mismatchedCountDB) Select(output interface{}, _ string, _ ...interface{}) ([]interface{}, error) {
|
||||
func (db mismatchedCountDB) Select(_ context.Context, output interface{}, _ string, _ ...interface{}) ([]interface{}, error) {
|
||||
// But actually return nothing
|
||||
outputPtr, _ := output.(*[]sa.CertWithID)
|
||||
*outputPtr = []sa.CertWithID{}
|
||||
|
@ -425,7 +425,7 @@ func TestGetCertsEmptyResults(t *testing.T) {
|
|||
checker.dbMap = mismatchedCountDB{}
|
||||
|
||||
batchSize = 3
|
||||
err = checker.getCerts(false)
|
||||
err = checker.getCerts(context.Background(), false)
|
||||
test.AssertNotError(t, err, "Failed to retrieve certificates")
|
||||
}
|
||||
|
||||
|
@ -437,7 +437,7 @@ type emptyDB struct {
|
|||
|
||||
// SelectNullInt is a method that returns a false sql.NullInt64 struct to
|
||||
// mock a null DB response
|
||||
func (db emptyDB) SelectNullInt(_ string, _ ...interface{}) (sql.NullInt64, error) {
|
||||
func (db emptyDB) SelectNullInt(_ context.Context, _ string, _ ...interface{}) (sql.NullInt64, error) {
|
||||
return sql.NullInt64{Valid: false},
|
||||
nil
|
||||
}
|
||||
|
@ -452,7 +452,7 @@ func TestGetCertsNullResults(t *testing.T) {
|
|||
checker := newChecker(saDbMap, clock.NewFake(), pa, kp, time.Hour, testValidityDurations, blog.NewMock())
|
||||
checker.dbMap = emptyDB{}
|
||||
|
||||
err = checker.getCerts(false)
|
||||
err = checker.getCerts(context.Background(), false)
|
||||
test.AssertError(t, err, "Should have gotten error from empty DB")
|
||||
}
|
||||
|
||||
|
@ -592,13 +592,13 @@ func TestIgnoredLint(t *testing.T) {
|
|||
|
||||
// Check the certificate with a nil ignore map. This should return the
|
||||
// expected zlint problems.
|
||||
_, problems := checker.checkCert(cert, nil)
|
||||
_, problems := checker.checkCert(context.Background(), cert, nil)
|
||||
sort.Strings(problems)
|
||||
test.AssertDeepEquals(t, problems, expectedProblems)
|
||||
|
||||
// Check the certificate again with an ignore map that excludes the affected
|
||||
// lints. This should return no problems.
|
||||
_, problems = checker.checkCert(cert, map[string]bool{
|
||||
_, problems = checker.checkCert(context.Background(), cert, map[string]bool{
|
||||
"e_sub_cert_aia_does_not_contain_ocsp_url": true,
|
||||
"n_subject_common_name_included": true,
|
||||
"w_ct_sct_policy_count_unsatisfied": true,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package notmain
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
@ -68,8 +69,8 @@ func validateContacts(id int64, createdAt string, contacts []string) error {
|
|||
|
||||
// beginAuditQuery executes the audit query and returns a cursor used to
|
||||
// stream the results.
|
||||
func (c contactAuditor) beginAuditQuery() (*sql.Rows, error) {
|
||||
rows, err := c.db.Query(`
|
||||
func (c contactAuditor) beginAuditQuery(ctx context.Context) (*sql.Rows, error) {
|
||||
rows, err := c.db.QueryContext(ctx, `
|
||||
SELECT DISTINCT id, contact, createdAt
|
||||
FROM registrations
|
||||
WHERE contact NOT IN ('[]', 'null');`)
|
||||
|
@ -98,9 +99,9 @@ func (c contactAuditor) writeResults(result string) {
|
|||
// run retrieves a cursor from `beginAuditQuery` and then audits the
|
||||
// `contact` column of all returned rows for abnormalities or policy
|
||||
// violations.
|
||||
func (c contactAuditor) run(resChan chan *result) error {
|
||||
func (c contactAuditor) run(ctx context.Context, resChan chan *result) error {
|
||||
c.logger.Infof("Beginning database query")
|
||||
rows, err := c.beginAuditQuery()
|
||||
rows, err := c.beginAuditQuery(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -193,7 +194,7 @@ func main() {
|
|||
|
||||
logger.Info("Running contact-auditor")
|
||||
|
||||
err = auditor.run(nil)
|
||||
err = auditor.run(context.TODO(), nil)
|
||||
cmd.FailOnError(err, "Audit was interrupted, results may be incomplete")
|
||||
|
||||
logger.Info("Audit finished successfully")
|
||||
|
|
|
@ -41,7 +41,7 @@ func TestContactAuditor(t *testing.T) {
|
|||
testCtx.addRegistrations(t)
|
||||
|
||||
resChan := make(chan *result, 10)
|
||||
err := testCtx.c.run(resChan)
|
||||
err := testCtx.c.run(context.Background(), resChan)
|
||||
test.AssertNotError(t, err, "received error")
|
||||
|
||||
// We should get back A, B, C, and D
|
||||
|
|
|
@ -296,7 +296,7 @@ func (m *mailer) updateLastNagTimestampsChunk(ctx context.Context, certs []*x509
|
|||
)
|
||||
params[0] = m.clk.Now()
|
||||
|
||||
_, err := m.dbMap.WithContext(ctx).Exec(query, params...)
|
||||
_, err := m.dbMap.ExecContext(ctx, query, params...)
|
||||
if err != nil {
|
||||
m.log.AuditErrf("Error updating certificate status for %d certs: %s", len(certs), err)
|
||||
m.stats.errorCount.With(prometheus.Labels{"type": "UpdateCertificateStatus"}).Inc()
|
||||
|
@ -307,7 +307,8 @@ func (m *mailer) certIsRenewed(ctx context.Context, names []string, issued time.
|
|||
namehash := sa.HashNames(names)
|
||||
|
||||
var present bool
|
||||
err := m.dbMap.WithContext(ctx).SelectOne(
|
||||
err := m.dbMap.SelectOne(
|
||||
ctx,
|
||||
&present,
|
||||
`SELECT EXISTS (SELECT id FROM fqdnSets WHERE setHash = ? AND issued > ? LIMIT 1)`,
|
||||
namehash,
|
||||
|
@ -530,7 +531,8 @@ func (m *mailer) getCertsWithJoin(ctx context.Context, left, right time.Time, ex
|
|||
// sequentially fetch the certificate details. This avoids an expensive
|
||||
// JOIN.
|
||||
var certs []certDERWithRegID
|
||||
_, err := m.dbMap.WithContext(ctx).Select(
|
||||
_, err := m.dbMap.Select(
|
||||
ctx,
|
||||
&certs,
|
||||
`SELECT
|
||||
cert.der as der, cert.registrationID as regID
|
||||
|
@ -564,7 +566,8 @@ func (m *mailer) getCerts(ctx context.Context, left, right time.Time, expiresIn
|
|||
// sequentially fetch the certificate details. This avoids an expensive
|
||||
// JOIN.
|
||||
var serials []string
|
||||
_, err := m.dbMap.WithContext(ctx).Select(
|
||||
_, err := m.dbMap.Select(
|
||||
ctx,
|
||||
&serials,
|
||||
`SELECT
|
||||
cs.serial
|
||||
|
@ -596,7 +599,7 @@ func (m *mailer) getCerts(ctx context.Context, left, right time.Time, expiresIn
|
|||
return nil, ctx.Err()
|
||||
}
|
||||
var cert core.Certificate
|
||||
cert, err := sa.SelectCertificate(m.dbMap.WithContext(ctx), serial)
|
||||
cert, err := sa.SelectCertificate(ctx, m.dbMap, serial)
|
||||
if err != nil {
|
||||
// We can get a NoRowsErr when processing a serial number corresponding
|
||||
// to a precertificate with no final certificate. Since this certificate
|
||||
|
|
|
@ -329,6 +329,8 @@ func TestNoContactCertIsNotRenewed(t *testing.T) {
|
|||
// An account with no contact info has a certificate that is expiring but has been renewed.
|
||||
// We should only examine that certificate once.
|
||||
func TestNoContactCertIsRenewed(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
testCtx := setup(t, []time.Duration{time.Hour * 24 * 7})
|
||||
|
||||
reg, err := makeRegistration(testCtx.ssa, 1, jsonKeyA, []string{})
|
||||
|
@ -350,7 +352,7 @@ func TestNoContactCertIsRenewed(t *testing.T) {
|
|||
|
||||
setupDBMap, err := sa.DBMapForTest(vars.DBConnSAFullPerms)
|
||||
test.AssertNotError(t, err, "setting up DB")
|
||||
err = setupDBMap.Insert(&core.FQDNSet{
|
||||
err = setupDBMap.Insert(ctx, &core.FQDNSet{
|
||||
SetHash: sa.HashNames(names),
|
||||
Serial: core.SerialToString(serial2),
|
||||
Issued: testCtx.fc.Now().Add(time.Hour),
|
||||
|
@ -358,7 +360,7 @@ func TestNoContactCertIsRenewed(t *testing.T) {
|
|||
})
|
||||
test.AssertNotError(t, err, "inserting FQDNSet for renewal")
|
||||
|
||||
err = testCtx.m.findExpiringCertificates(context.Background())
|
||||
err = testCtx.m.findExpiringCertificates(ctx)
|
||||
test.AssertNotError(t, err, "finding expired certificates")
|
||||
|
||||
// We should have examined exactly one certificate
|
||||
|
@ -370,7 +372,7 @@ func TestNoContactCertIsRenewed(t *testing.T) {
|
|||
|
||||
// Run findExpiringCertificates again. The count of examined certificates
|
||||
// should not increase again.
|
||||
err = testCtx.m.findExpiringCertificates(context.Background())
|
||||
err = testCtx.m.findExpiringCertificates(ctx)
|
||||
test.AssertNotError(t, err, "finding expired certificates")
|
||||
test.AssertMetricWithLabelsEquals(t, certsExamined, prometheus.Labels{}, 1.0)
|
||||
test.AssertMetricWithLabelsEquals(t, certsAlreadyRenewed, prometheus.Labels{}, 1.0)
|
||||
|
@ -498,6 +500,8 @@ func makeCertificate(regID int64, serial *big.Int, dnsNames []string, expires ti
|
|||
}
|
||||
|
||||
func insertCertificate(cert certDERWithRegID, lastNagSent time.Time) error {
|
||||
ctx := context.Background()
|
||||
|
||||
parsedCert, err := x509.ParseCertificate(cert.DER)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -507,7 +511,7 @@ func insertCertificate(cert certDERWithRegID, lastNagSent time.Time) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = setupDBMap.Insert(&core.Certificate{
|
||||
err = setupDBMap.Insert(ctx, &core.Certificate{
|
||||
RegistrationID: cert.RegID,
|
||||
Serial: core.SerialToString(parsedCert.SerialNumber),
|
||||
Issued: parsedCert.NotBefore,
|
||||
|
@ -518,7 +522,7 @@ func insertCertificate(cert certDERWithRegID, lastNagSent time.Time) error {
|
|||
return fmt.Errorf("inserting certificate: %w", err)
|
||||
}
|
||||
|
||||
return setupDBMap.Insert(&core.CertificateStatus{
|
||||
return setupDBMap.Insert(ctx, &core.CertificateStatus{
|
||||
Serial: core.SerialToString(parsedCert.SerialNumber),
|
||||
LastExpirationNagSent: lastNagSent,
|
||||
Status: core.OCSPStatusGood,
|
||||
|
@ -599,9 +603,9 @@ func addExpiringCerts(t *testing.T, ctx *testCtx) []certDERWithRegID {
|
|||
|
||||
setupDBMap, err := sa.DBMapForTest(vars.DBConnSAFullPerms)
|
||||
test.AssertNotError(t, err, "setting up DB")
|
||||
err = setupDBMap.Insert(fqdnStatusD)
|
||||
err = setupDBMap.Insert(context.Background(), fqdnStatusD)
|
||||
test.AssertNotError(t, err, "Couldn't add fqdnStatusD")
|
||||
err = setupDBMap.Insert(fqdnStatusDRenewed)
|
||||
err = setupDBMap.Insert(context.Background(), fqdnStatusDRenewed)
|
||||
test.AssertNotError(t, err, "Couldn't add fqdnStatusDRenewed")
|
||||
return []certDERWithRegID{certA, certB, certC, certD}
|
||||
}
|
||||
|
@ -752,7 +756,7 @@ func TestCertIsRenewed(t *testing.T) {
|
|||
err = insertCertificate(certDERWithRegID{DER: certDer, RegID: reg.Id}, time.Time{})
|
||||
test.AssertNotError(t, err, fmt.Sprintf("Couldn't add cert %s", testData.stringSerial))
|
||||
|
||||
err = setupDBMap.Insert(fqdnStatus)
|
||||
err = setupDBMap.Insert(context.Background(), fqdnStatus)
|
||||
test.AssertNotError(t, err, fmt.Sprintf("Couldn't add fqdnStatus %s", testData.stringSerial))
|
||||
}
|
||||
|
||||
|
@ -858,13 +862,15 @@ func TestDontFindRevokedCert(t *testing.T) {
|
|||
err = insertCertificate(certA, time.Time{})
|
||||
test.AssertNotError(t, err, "inserting certificate")
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
setupDBMap, err := sa.DBMapForTest(vars.DBConnSAFullPerms)
|
||||
test.AssertNotError(t, err, "sa.NewDbMap failed")
|
||||
_, err = setupDBMap.Exec("UPDATE certificateStatus SET status = ? WHERE serial = ?",
|
||||
_, err = setupDBMap.ExecContext(ctx, "UPDATE certificateStatus SET status = ? WHERE serial = ?",
|
||||
string(core.OCSPStatusRevoked), core.SerialToString(serial1))
|
||||
test.AssertNotError(t, err, "revoking certificate")
|
||||
|
||||
err = testCtx.m.findExpiringCertificates(context.Background())
|
||||
err = testCtx.m.findExpiringCertificates(ctx)
|
||||
test.AssertNotError(t, err, "err from findExpiringCertificates")
|
||||
|
||||
if len(testCtx.mc.Messages) != 0 {
|
||||
|
|
|
@ -2,6 +2,7 @@ package notmain
|
|||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
|
@ -68,9 +69,10 @@ func (i *idExporterResults) writeToFile(outfile string) error {
|
|||
}
|
||||
|
||||
// findIDs gathers all registration IDs with unexpired certificates.
|
||||
func (c idExporter) findIDs() (idExporterResults, error) {
|
||||
func (c idExporter) findIDs(ctx context.Context) (idExporterResults, error) {
|
||||
var holder idExporterResults
|
||||
_, err := c.dbMap.Select(
|
||||
ctx,
|
||||
&holder,
|
||||
`SELECT DISTINCT r.id
|
||||
FROM registrations AS r
|
||||
|
@ -89,9 +91,10 @@ func (c idExporter) findIDs() (idExporterResults, error) {
|
|||
|
||||
// findIDsWithExampleHostnames gathers all registration IDs with
|
||||
// unexpired certificates and a corresponding example hostname.
|
||||
func (c idExporter) findIDsWithExampleHostnames() (idExporterResults, error) {
|
||||
func (c idExporter) findIDsWithExampleHostnames(ctx context.Context) (idExporterResults, error) {
|
||||
var holder idExporterResults
|
||||
_, err := c.dbMap.Select(
|
||||
ctx,
|
||||
&holder,
|
||||
`SELECT SQL_BIG_RESULT
|
||||
cert.registrationID AS id,
|
||||
|
@ -116,13 +119,14 @@ func (c idExporter) findIDsWithExampleHostnames() (idExporterResults, error) {
|
|||
|
||||
// findIDsForHostnames gathers all registration IDs with unexpired
|
||||
// certificates for each `hostnames` entry.
|
||||
func (c idExporter) findIDsForHostnames(hostnames []string) (idExporterResults, error) {
|
||||
func (c idExporter) findIDsForHostnames(ctx context.Context, hostnames []string) (idExporterResults, error) {
|
||||
var holder idExporterResults
|
||||
for _, hostname := range hostnames {
|
||||
// Pass the same list in each time, gorp will happily just append to the slice
|
||||
// Pass the same list in each time, borp will happily just append to the slice
|
||||
// instead of overwriting it each time
|
||||
// https://github.com/go-gorp/gorp/blob/2ae7d174a4cf270240c4561092402affba25da5e/select.go#L348-L355
|
||||
// https://github.com/letsencrypt/borp/blob/c87bd6443d59746a33aca77db34a60cfc344adb2/select.go#L349-L353
|
||||
_, err := c.dbMap.Select(
|
||||
ctx,
|
||||
&holder,
|
||||
`SELECT DISTINCT c.registrationID AS id
|
||||
FROM certificates AS c
|
||||
|
@ -279,15 +283,15 @@ func main() {
|
|||
hostnames, err := unmarshalHostnames(*hostnamesFile)
|
||||
cmd.FailOnError(err, "Problem unmarshalling hostnames")
|
||||
|
||||
results, err = exporter.findIDsForHostnames(hostnames)
|
||||
results, err = exporter.findIDsForHostnames(context.TODO(), hostnames)
|
||||
cmd.FailOnError(err, "Could not find IDs for hostnames")
|
||||
|
||||
} else if *withExampleHostnames {
|
||||
results, err = exporter.findIDsWithExampleHostnames()
|
||||
results, err = exporter.findIDsWithExampleHostnames(context.TODO())
|
||||
cmd.FailOnError(err, "Could not find IDs with hostnames")
|
||||
|
||||
} else {
|
||||
results, err = exporter.findIDs()
|
||||
results, err = exporter.findIDs(context.TODO())
|
||||
cmd.FailOnError(err, "Could not find IDs")
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,8 @@ const (
|
|||
)
|
||||
|
||||
func TestFindIDs(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
testCtx := setup(t)
|
||||
defer testCtx.cleanUp()
|
||||
|
||||
|
@ -49,7 +51,7 @@ func TestFindIDs(t *testing.T) {
|
|||
|
||||
// Run findIDs - since no certificates have been added corresponding to
|
||||
// the above registrations, no IDs should be found.
|
||||
results, err := testCtx.c.findIDs()
|
||||
results, err := testCtx.c.findIDs(ctx)
|
||||
test.AssertNotError(t, err, "findIDs() produced error")
|
||||
test.AssertEquals(t, len(results), 0)
|
||||
|
||||
|
@ -61,7 +63,7 @@ func TestFindIDs(t *testing.T) {
|
|||
// *not* be present since their certificate has already expired. Unlike
|
||||
// previous versions of this test RegD is not filtered out for having a `tel:`
|
||||
// contact field anymore - this is the duty of the notify-mailer.
|
||||
results, err = testCtx.c.findIDs()
|
||||
results, err = testCtx.c.findIDs(ctx)
|
||||
test.AssertNotError(t, err, "findIDs() produced error")
|
||||
test.AssertEquals(t, len(results), 3)
|
||||
for _, entry := range results {
|
||||
|
@ -76,7 +78,7 @@ func TestFindIDs(t *testing.T) {
|
|||
|
||||
// Allow a 1 year grace period
|
||||
testCtx.c.grace = 360 * 24 * time.Hour
|
||||
results, err = testCtx.c.findIDs()
|
||||
results, err = testCtx.c.findIDs(ctx)
|
||||
test.AssertNotError(t, err, "findIDs() produced error")
|
||||
// Now all four registration should be returned, including RegB since its
|
||||
// certificate expired within the grace period
|
||||
|
@ -93,6 +95,7 @@ func TestFindIDs(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestFindIDsWithExampleHostnames(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
testCtx := setup(t)
|
||||
defer testCtx.cleanUp()
|
||||
|
||||
|
@ -102,7 +105,7 @@ func TestFindIDsWithExampleHostnames(t *testing.T) {
|
|||
// Run findIDsWithExampleHostnames - since no certificates have been
|
||||
// added corresponding to the above registrations, no IDs should be
|
||||
// found.
|
||||
results, err := testCtx.c.findIDsWithExampleHostnames()
|
||||
results, err := testCtx.c.findIDsWithExampleHostnames(ctx)
|
||||
test.AssertNotError(t, err, "findIDs() produced error")
|
||||
test.AssertEquals(t, len(results), 0)
|
||||
|
||||
|
@ -113,7 +116,7 @@ func TestFindIDsWithExampleHostnames(t *testing.T) {
|
|||
// registrations with unexpired certs we should get exactly three
|
||||
// IDs back: RegA, RegC and RegD. RegB should *not* be present since
|
||||
// their certificate has already expired.
|
||||
results, err = testCtx.c.findIDsWithExampleHostnames()
|
||||
results, err = testCtx.c.findIDsWithExampleHostnames(ctx)
|
||||
test.AssertNotError(t, err, "findIDs() produced error")
|
||||
test.AssertEquals(t, len(results), 3)
|
||||
for _, entry := range results {
|
||||
|
@ -131,7 +134,7 @@ func TestFindIDsWithExampleHostnames(t *testing.T) {
|
|||
|
||||
// Allow a 1 year grace period
|
||||
testCtx.c.grace = 360 * 24 * time.Hour
|
||||
results, err = testCtx.c.findIDsWithExampleHostnames()
|
||||
results, err = testCtx.c.findIDsWithExampleHostnames(ctx)
|
||||
test.AssertNotError(t, err, "findIDs() produced error")
|
||||
|
||||
// Now all four registrations should be returned, including RegB
|
||||
|
@ -154,6 +157,8 @@ func TestFindIDsWithExampleHostnames(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestFindIDsForHostnames(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
testCtx := setup(t)
|
||||
defer testCtx.cleanUp()
|
||||
|
||||
|
@ -162,14 +167,14 @@ func TestFindIDsForHostnames(t *testing.T) {
|
|||
|
||||
// Run findIDsForHostnames - since no certificates have been added corresponding to
|
||||
// the above registrations, no IDs should be found.
|
||||
results, err := testCtx.c.findIDsForHostnames([]string{"example-a.com", "example-b.com", "example-c.com", "example-d.com"})
|
||||
results, err := testCtx.c.findIDsForHostnames(ctx, []string{"example-a.com", "example-b.com", "example-c.com", "example-d.com"})
|
||||
test.AssertNotError(t, err, "findIDs() produced error")
|
||||
test.AssertEquals(t, len(results), 0)
|
||||
|
||||
// Now add some certificates
|
||||
testCtx.addCertificates(t)
|
||||
|
||||
results, err = testCtx.c.findIDsForHostnames([]string{"example-a.com", "example-b.com", "example-c.com", "example-d.com"})
|
||||
results, err = testCtx.c.findIDsForHostnames(ctx, []string{"example-a.com", "example-b.com", "example-c.com", "example-d.com"})
|
||||
test.AssertNotError(t, err, "findIDsForHostnames() failed")
|
||||
test.AssertEquals(t, len(results), 3)
|
||||
for _, entry := range results {
|
||||
|
@ -314,6 +319,7 @@ func (tc testCtx) addRegistrations(t *testing.T) {
|
|||
}
|
||||
|
||||
func (tc testCtx) addCertificates(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
serial1 := big.NewInt(1336)
|
||||
serial1String := core.SerialToString(serial1)
|
||||
serial2 := big.NewInt(1337)
|
||||
|
@ -352,9 +358,10 @@ func (tc testCtx) addCertificates(t *testing.T) {
|
|||
Expires: rawCertA.NotAfter,
|
||||
DER: certDerA,
|
||||
}
|
||||
err := tc.c.dbMap.Insert(certA)
|
||||
err := tc.c.dbMap.Insert(ctx, certA)
|
||||
test.AssertNotError(t, err, "Couldn't add certA")
|
||||
_, err = tc.c.dbMap.Exec(
|
||||
_, err = tc.c.dbMap.ExecContext(
|
||||
ctx,
|
||||
"INSERT INTO issuedNames (reversedName, serial, notBefore) VALUES (?,?,0)",
|
||||
"com.example-a",
|
||||
serial1String,
|
||||
|
@ -377,9 +384,10 @@ func (tc testCtx) addCertificates(t *testing.T) {
|
|||
Expires: rawCertB.NotAfter,
|
||||
DER: certDerB,
|
||||
}
|
||||
err = tc.c.dbMap.Insert(certB)
|
||||
err = tc.c.dbMap.Insert(ctx, certB)
|
||||
test.AssertNotError(t, err, "Couldn't add certB")
|
||||
_, err = tc.c.dbMap.Exec(
|
||||
_, err = tc.c.dbMap.ExecContext(
|
||||
ctx,
|
||||
"INSERT INTO issuedNames (reversedName, serial, notBefore) VALUES (?,?,0)",
|
||||
"com.example-b",
|
||||
serial2String,
|
||||
|
@ -402,9 +410,10 @@ func (tc testCtx) addCertificates(t *testing.T) {
|
|||
Expires: rawCertC.NotAfter,
|
||||
DER: certDerC,
|
||||
}
|
||||
err = tc.c.dbMap.Insert(certC)
|
||||
err = tc.c.dbMap.Insert(ctx, certC)
|
||||
test.AssertNotError(t, err, "Couldn't add certC")
|
||||
_, err = tc.c.dbMap.Exec(
|
||||
_, err = tc.c.dbMap.ExecContext(
|
||||
ctx,
|
||||
"INSERT INTO issuedNames (reversedName, serial, notBefore) VALUES (?,?,0)",
|
||||
"com.example-c",
|
||||
serial3String,
|
||||
|
@ -427,9 +436,10 @@ func (tc testCtx) addCertificates(t *testing.T) {
|
|||
Expires: rawCertD.NotAfter,
|
||||
DER: certDerD,
|
||||
}
|
||||
err = tc.c.dbMap.Insert(certD)
|
||||
err = tc.c.dbMap.Insert(ctx, certD)
|
||||
test.AssertNotError(t, err, "Couldn't add certD")
|
||||
_, err = tc.c.dbMap.Exec(
|
||||
_, err = tc.c.dbMap.ExecContext(
|
||||
ctx,
|
||||
"INSERT INTO issuedNames (reversedName, serial, notBefore) VALUES (?,?,0)",
|
||||
"com.example-d",
|
||||
serial4String,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package notmain
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
@ -119,7 +120,7 @@ func (m *mailer) makeMessageBody(recipients []recipient) (string, error) {
|
|||
return messageBody.String(), nil
|
||||
}
|
||||
|
||||
func (m *mailer) run() error {
|
||||
func (m *mailer) run(ctx context.Context) error {
|
||||
err := m.ok()
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -128,7 +129,7 @@ func (m *mailer) run() error {
|
|||
totalRecipients := len(m.recipients)
|
||||
m.log.Infof("Resolving addresses for (%d) recipients", totalRecipients)
|
||||
|
||||
addressToRecipient, err := m.resolveAddresses()
|
||||
addressToRecipient, err := m.resolveAddresses(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -238,10 +239,10 @@ func (m *mailer) run() error {
|
|||
|
||||
// resolveAddresses creates a mapping of email addresses to (a list of)
|
||||
// `recipient`s that resolve to that email address.
|
||||
func (m *mailer) resolveAddresses() (addressToRecipientMap, error) {
|
||||
func (m *mailer) resolveAddresses(ctx context.Context) (addressToRecipientMap, error) {
|
||||
result := make(addressToRecipientMap, len(m.recipients))
|
||||
for _, recipient := range m.recipients {
|
||||
addresses, err := getAddressForID(recipient.id, m.dbMap)
|
||||
addresses, err := getAddressForID(ctx, recipient.id, m.dbMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -258,17 +259,17 @@ func (m *mailer) resolveAddresses() (addressToRecipientMap, error) {
|
|||
return result, nil
|
||||
}
|
||||
|
||||
// dbSelector abstracts over a subset of methods from `gorp.DbMap` objects to
|
||||
// dbSelector abstracts over a subset of methods from `borp.DbMap` objects to
|
||||
// facilitate mocking in unit tests.
|
||||
type dbSelector interface {
|
||||
SelectOne(holder interface{}, query string, args ...interface{}) error
|
||||
SelectOne(ctx context.Context, holder interface{}, query string, args ...interface{}) error
|
||||
}
|
||||
|
||||
// getAddressForID queries the database for the email address associated with
|
||||
// the provided registration ID.
|
||||
func getAddressForID(id int64, dbMap dbSelector) ([]string, error) {
|
||||
func getAddressForID(ctx context.Context, id int64, dbMap dbSelector) ([]string, error) {
|
||||
var result contactQueryResult
|
||||
err := dbMap.SelectOne(&result,
|
||||
err := dbMap.SelectOne(ctx, &result,
|
||||
`SELECT id,
|
||||
contact
|
||||
FROM registrations
|
||||
|
@ -606,7 +607,7 @@ func main() {
|
|||
parallelSends: *parallelSends,
|
||||
}
|
||||
|
||||
err = m.run()
|
||||
err = m.run(context.TODO())
|
||||
cmd.FailOnError(err, "Couldn't complete")
|
||||
|
||||
log.Info("Completed successfully")
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package notmain
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
@ -294,7 +295,7 @@ func TestSleepInterval(t *testing.T) {
|
|||
// Call run() - this should sleep `sleepLen` per destination address
|
||||
// After it returns, we expect (sleepLen * number of destinations) seconds has
|
||||
// elapsed
|
||||
err := m.run()
|
||||
err := m.run(context.Background())
|
||||
test.AssertNotError(t, err, "error calling mailer run()")
|
||||
expectedEnd := clock.NewFake()
|
||||
expectedEnd.Add(time.Second * time.Duration(sleepLen*len(recipients)))
|
||||
|
@ -314,7 +315,7 @@ func TestSleepInterval(t *testing.T) {
|
|||
|
||||
// Call run() - this should blast through all destinations without sleep
|
||||
// After it returns, we expect no clock time to have elapsed on the fake clock
|
||||
err = m.run()
|
||||
err = m.run(context.Background())
|
||||
test.AssertNotError(t, err, "error calling mailer run()")
|
||||
expectedEnd = clock.NewFake()
|
||||
test.AssertEquals(t, m.clk.Now(), expectedEnd.Now())
|
||||
|
@ -345,7 +346,7 @@ func TestMailIntervals(t *testing.T) {
|
|||
|
||||
// Run the mailer. It should produce an error about the interval start
|
||||
mc.Clear()
|
||||
err := m.run()
|
||||
err := m.run(context.Background())
|
||||
test.AssertError(t, err, "expected error")
|
||||
test.AssertEquals(t, len(mc.Messages), 0)
|
||||
|
||||
|
@ -364,7 +365,7 @@ func TestMailIntervals(t *testing.T) {
|
|||
|
||||
// Run the mailer. It should produce an error about the sleep interval
|
||||
mc.Clear()
|
||||
err = m.run()
|
||||
err = m.run(context.Background())
|
||||
test.AssertEquals(t, len(mc.Messages), 0)
|
||||
test.AssertEquals(t, err.Error(), "sleep interval (-10) is < 0")
|
||||
|
||||
|
@ -386,7 +387,7 @@ func TestMailIntervals(t *testing.T) {
|
|||
// test-example-updated@letsencrypt.org (beginning of the range),
|
||||
// and one to test-test-test@letsencrypt.org.
|
||||
mc.Clear()
|
||||
err = m.run()
|
||||
err = m.run(context.Background())
|
||||
test.AssertNotError(t, err, "run() produced an error")
|
||||
test.AssertEquals(t, len(mc.Messages), 2)
|
||||
test.AssertEquals(t, mocks.MailerMessage{
|
||||
|
@ -417,7 +418,7 @@ func TestMailIntervals(t *testing.T) {
|
|||
// Run the mailer. Two messages should have been produced, one to
|
||||
// example@letsencrypt.org (ID 1), one to example-example-example@example.com (ID 2)
|
||||
mc.Clear()
|
||||
err = m.run()
|
||||
err = m.run(context.Background())
|
||||
test.AssertNotError(t, err, "run() produced an error")
|
||||
test.AssertEquals(t, len(mc.Messages), 2)
|
||||
test.AssertEquals(t, mocks.MailerMessage{
|
||||
|
@ -456,7 +457,7 @@ func TestParallelism(t *testing.T) {
|
|||
}
|
||||
|
||||
mc.Clear()
|
||||
err := m.run()
|
||||
err := m.run(context.Background())
|
||||
test.AssertNotError(t, err, "run() produced an error")
|
||||
|
||||
// The fake clock should have advanced 9 seconds, one for each parallel
|
||||
|
@ -499,7 +500,7 @@ func TestMessageContentStatic(t *testing.T) {
|
|||
|
||||
// Run the mailer, one message should have been created with the content
|
||||
// expected
|
||||
err := m.run()
|
||||
err := m.run(context.Background())
|
||||
test.AssertNotError(t, err, "error calling mailer run()")
|
||||
test.AssertEquals(t, len(mc.Messages), 1)
|
||||
test.AssertEquals(t, mocks.MailerMessage{
|
||||
|
@ -536,7 +537,7 @@ func TestMessageContentInterpolated(t *testing.T) {
|
|||
|
||||
// Run the mailer, one message should have been created with the content
|
||||
// expected
|
||||
err := m.run()
|
||||
err := m.run(context.Background())
|
||||
test.AssertNotError(t, err, "error calling mailer run()")
|
||||
test.AssertEquals(t, len(mc.Messages), 1)
|
||||
test.AssertEquals(t, mocks.MailerMessage{
|
||||
|
@ -594,7 +595,7 @@ func TestMessageContentInterpolatedMultiple(t *testing.T) {
|
|||
|
||||
// Run the mailer, one message should have been created with the content
|
||||
// expected
|
||||
err := m.run()
|
||||
err := m.run(context.Background())
|
||||
test.AssertNotError(t, err, "error calling mailer run()")
|
||||
test.AssertEquals(t, len(mc.Messages), 1)
|
||||
test.AssertEquals(t, mocks.MailerMessage{
|
||||
|
@ -616,7 +617,7 @@ type mockEmailResolver struct{}
|
|||
|
||||
// the `mockEmailResolver` select method treats the requested reg ID as an index
|
||||
// into a list of anonymous structs
|
||||
func (bs mockEmailResolver) SelectOne(output interface{}, _ string, args ...interface{}) error {
|
||||
func (bs mockEmailResolver) SelectOne(ctx context.Context, output interface{}, _ string, args ...interface{}) error {
|
||||
// The "dbList" is just a list of contact records in memory
|
||||
dbList := []contactQueryResult{
|
||||
{
|
||||
|
@ -762,7 +763,7 @@ func TestResolveEmails(t *testing.T) {
|
|||
clk: clock.NewFake(),
|
||||
}
|
||||
|
||||
addressesToRecipients, err := m.resolveAddresses()
|
||||
addressesToRecipients, err := m.resolveAddresses(context.Background())
|
||||
test.AssertNotError(t, err, "failed to resolveEmailAddresses")
|
||||
|
||||
expected := []string{
|
||||
|
|
|
@ -44,7 +44,8 @@ func getStartingID(ctx context.Context, clk clock.Clock, db *db.WrappedMap) (int
|
|||
// certificates.
|
||||
startTime := clk.Now().Add(-24 * time.Hour)
|
||||
var minID *int64
|
||||
err := db.WithContext(ctx).QueryRow(
|
||||
err := db.QueryRowContext(
|
||||
ctx,
|
||||
"SELECT MIN(id) FROM certificateStatus WHERE notAfter >= ?",
|
||||
startTime,
|
||||
).Scan(&minID)
|
||||
|
@ -70,7 +71,8 @@ func (cl *client) loadFromDB(ctx context.Context, speed ProcessingSpeed, startFr
|
|||
// Find the current maximum id in certificateStatus. We do this because the table is always
|
||||
// growing. If we scanned until we saw a batch with no rows, we would scan forever.
|
||||
var maxID *int64
|
||||
err = cl.db.WithContext(ctx).QueryRow(
|
||||
err = cl.db.QueryRowContext(
|
||||
ctx,
|
||||
"SELECT MAX(id) FROM certificateStatus",
|
||||
).Scan(&maxID)
|
||||
if err != nil {
|
||||
|
@ -162,7 +164,7 @@ func (cl *client) scanFromDBOneBatch(ctx context.Context, prevID int64, frequenc
|
|||
return -1, fmt.Errorf("initializing db map: %w", err)
|
||||
}
|
||||
|
||||
rows, err := selector.Query(ctx, clauses, params...)
|
||||
rows, err := selector.QueryContext(ctx, clauses, params...)
|
||||
if err != nil {
|
||||
return -1, fmt.Errorf("scanning certificateStatus: %w", err)
|
||||
}
|
||||
|
|
|
@ -50,6 +50,8 @@ func makeClient() (*rocsp.RWClient, clock.Clock) {
|
|||
}
|
||||
|
||||
func TestGetStartingID(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
clk := clock.NewFake()
|
||||
dbMap, err := sa.DBMapForTest(vars.DBConnSAFullPerms)
|
||||
test.AssertNotError(t, err, "failed setting up db client")
|
||||
|
@ -60,7 +62,7 @@ func TestGetStartingID(t *testing.T) {
|
|||
Serial: "1337",
|
||||
NotAfter: clk.Now().Add(12 * time.Hour),
|
||||
}
|
||||
err = dbMap.Insert(&cs)
|
||||
err = dbMap.Insert(ctx, &cs)
|
||||
test.AssertNotError(t, err, "inserting certificate status")
|
||||
firstID := cs.ID
|
||||
|
||||
|
@ -68,7 +70,7 @@ func TestGetStartingID(t *testing.T) {
|
|||
Serial: "1338",
|
||||
NotAfter: clk.Now().Add(36 * time.Hour),
|
||||
}
|
||||
err = dbMap.Insert(&cs)
|
||||
err = dbMap.Insert(ctx, &cs)
|
||||
test.AssertNotError(t, err, "inserting certificate status")
|
||||
secondID := cs.ID
|
||||
t.Logf("first ID %d, second ID %d", firstID, secondID)
|
||||
|
@ -129,7 +131,7 @@ func TestLoadFromDB(t *testing.T) {
|
|||
defer test.ResetBoulderTestDatabase(t)
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
err = dbMap.Insert(&core.CertificateStatus{
|
||||
err = dbMap.Insert(context.Background(), &core.CertificateStatus{
|
||||
Serial: fmt.Sprintf("%036x", i),
|
||||
NotAfter: clk.Now().Add(200 * time.Hour),
|
||||
OCSPLastUpdated: clk.Now(),
|
||||
|
|
16
db/gorm.go
16
db/gorm.go
|
@ -51,7 +51,7 @@ func NewMappedSelector[T any](executor MappedExecutor) (MappedSelector[T], error
|
|||
// - Note that the reverse is not true: it's perfectly okay for there to be
|
||||
// database columns which do not correspond to fields in the struct; those
|
||||
// columns will be ignored.
|
||||
// TODO: In the future, when we replace gorp's TableMap with our own, this
|
||||
// TODO: In the future, when we replace borp's TableMap with our own, this
|
||||
// check should be performed at the time the mapping is declared.
|
||||
columns := make([]string, 0)
|
||||
seen := make(map[string]struct{})
|
||||
|
@ -80,21 +80,21 @@ type mappedSelector[T any] struct {
|
|||
columns []string
|
||||
}
|
||||
|
||||
// Query performs a SELECT on the appropriate table for T. It combines the best
|
||||
// features of gorp, the go stdlib, and generics, using the type parameter of
|
||||
// QueryContext performs a SELECT on the appropriate table for T. It combines the best
|
||||
// features of borp, the go stdlib, and generics, using the type parameter of
|
||||
// the typeSelector object to automatically look up the proper table name and
|
||||
// columns to select. It returns an iterable which yields fully-populated
|
||||
// objects of the parameterized type directly. The given clauses MUST be only
|
||||
// the bits of a sql query from "WHERE ..." onwards; if they contain any of the
|
||||
// "SELECT ... FROM ..." portion of the query it will result in an error. The
|
||||
// args take the same kinds of values as gorp's SELECT: either one argument per
|
||||
// args take the same kinds of values as borp's SELECT: either one argument per
|
||||
// positional placeholder, or a map of placeholder names to their arguments
|
||||
// (see https://pkg.go.dev/gopkg.in/gorp.v2#readme-ad-hoc-sql).
|
||||
// (see https://pkg.go.dev/github.com/letsencrypt/borp#readme-ad-hoc-sql).
|
||||
//
|
||||
// The caller is responsible for calling `Rows.Close()` when they are done with
|
||||
// the query. The caller is also responsible for ensuring that the clauses
|
||||
// argument does not contain any user-influenced input.
|
||||
func (ts mappedSelector[T]) Query(ctx context.Context, clauses string, args ...interface{}) (Rows[T], error) {
|
||||
func (ts mappedSelector[T]) QueryContext(ctx context.Context, clauses string, args ...interface{}) (Rows[T], error) {
|
||||
// Look up the table to use based on the type of this TypeSelector.
|
||||
var throwaway T
|
||||
tableMap, err := ts.wrapped.TableFor(reflect.TypeOf(throwaway), false)
|
||||
|
@ -106,7 +106,7 @@ func (ts mappedSelector[T]) Query(ctx context.Context, clauses string, args ...i
|
|||
}
|
||||
|
||||
// QueryFrom is the same as Query, but it additionally takes a table name to
|
||||
// select from, rather than automatically computing the table name from gorp's
|
||||
// select from, rather than automatically computing the table name from borp's
|
||||
// DbMap.
|
||||
//
|
||||
// The caller is responsible for calling `Rows.Close()` when they are done with
|
||||
|
@ -127,7 +127,7 @@ func (ts mappedSelector[T]) QueryFrom(ctx context.Context, tablename string, cla
|
|||
clauses,
|
||||
)
|
||||
|
||||
r, err := ts.wrapped.WithContext(ctx).Query(query, args...)
|
||||
r, err := ts.wrapped.QueryContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading db: %w", err)
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
"errors"
|
||||
"reflect"
|
||||
|
||||
"github.com/go-gorp/gorp/v3"
|
||||
"github.com/letsencrypt/borp"
|
||||
)
|
||||
|
||||
// These interfaces exist to aid in mocking database operations for unit tests.
|
||||
|
@ -18,26 +18,26 @@ import (
|
|||
|
||||
// A OneSelector is anything that provides a `SelectOne` function.
|
||||
type OneSelector interface {
|
||||
SelectOne(interface{}, string, ...interface{}) error
|
||||
SelectOne(context.Context, interface{}, string, ...interface{}) error
|
||||
}
|
||||
|
||||
// A Selector is anything that provides a `Select` function.
|
||||
type Selector interface {
|
||||
Select(interface{}, string, ...interface{}) ([]interface{}, error)
|
||||
Select(context.Context, interface{}, string, ...interface{}) ([]interface{}, error)
|
||||
}
|
||||
|
||||
// A Inserter is anything that provides an `Insert` function
|
||||
type Inserter interface {
|
||||
Insert(list ...interface{}) error
|
||||
Insert(context.Context, ...interface{}) error
|
||||
}
|
||||
|
||||
// A Execer is anything that provides an `Exec` function
|
||||
// A Execer is anything that provides an `ExecContext` function
|
||||
type Execer interface {
|
||||
Exec(string, ...interface{}) (sql.Result, error)
|
||||
ExecContext(context.Context, string, ...interface{}) (sql.Result, error)
|
||||
}
|
||||
|
||||
// SelectExecer offers a subset of gorp.SqlExecutor's methods: Select and
|
||||
// Exec.
|
||||
// SelectExecer offers a subset of borp.SqlExecutor's methods: Select and
|
||||
// ExecContext.
|
||||
type SelectExecer interface {
|
||||
Selector
|
||||
Execer
|
||||
|
@ -49,48 +49,46 @@ type DatabaseMap interface {
|
|||
OneSelector
|
||||
Inserter
|
||||
SelectExecer
|
||||
Begin() (Transaction, error)
|
||||
BeginTx(context.Context) (Transaction, error)
|
||||
}
|
||||
|
||||
// Executor offers the full combination of OneSelector, Inserter, SelectExecer
|
||||
// and adds a handful of other high level Gorp methods we use in Boulder.
|
||||
// and adds a handful of other high level borp methods we use in Boulder.
|
||||
type Executor interface {
|
||||
OneSelector
|
||||
Inserter
|
||||
SelectExecer
|
||||
Delete(...interface{}) (int64, error)
|
||||
Get(interface{}, ...interface{}) (interface{}, error)
|
||||
Update(...interface{}) (int64, error)
|
||||
Query(string, ...interface{}) (*sql.Rows, error)
|
||||
Queryer
|
||||
Delete(context.Context, ...interface{}) (int64, error)
|
||||
Get(context.Context, interface{}, ...interface{}) (interface{}, error)
|
||||
Update(context.Context, ...interface{}) (int64, error)
|
||||
}
|
||||
|
||||
// Queryer offers the Query method. Note that this is not read-only (i.e. not
|
||||
// Selector), since a Query can be `INSERT`, `UPDATE`, etc. The difference
|
||||
// between Query and Exec is that Query can return rows. So for instance it is
|
||||
// Queryer offers the QueryContext method. Note that this is not read-only (i.e. not
|
||||
// Selector), since a QueryContext can be `INSERT`, `UPDATE`, etc. The difference
|
||||
// between QueryContext and ExecContext is that QueryContext can return rows. So for instance it is
|
||||
// suitable for inserting rows and getting back ids.
|
||||
type Queryer interface {
|
||||
Query(string, ...interface{}) (*sql.Rows, error)
|
||||
QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
|
||||
}
|
||||
|
||||
// Transaction extends an Executor and adds Rollback, Commit, and WithContext.
|
||||
// Transaction extends an Executor and adds Rollback and Commit
|
||||
type Transaction interface {
|
||||
Executor
|
||||
Rollback() error
|
||||
Commit() error
|
||||
WithContext(ctx context.Context) gorp.SqlExecutor
|
||||
}
|
||||
|
||||
// MappedExecutor is anything that can map types to tables, and which can
|
||||
// produce a SqlExecutor bound to a context.
|
||||
// MappedExecutor is anything that can map types to tables
|
||||
type MappedExecutor interface {
|
||||
TableFor(reflect.Type, bool) (*gorp.TableMap, error)
|
||||
WithContext(ctx context.Context) gorp.SqlExecutor
|
||||
TableFor(reflect.Type, bool) (*borp.TableMap, error)
|
||||
QueryContext(ctx context.Context, clauses string, args ...interface{}) (*sql.Rows, error)
|
||||
}
|
||||
|
||||
// MappedSelector is anything that can execute various kinds of SQL statements
|
||||
// against a table automatically determined from the parameterized type.
|
||||
type MappedSelector[T any] interface {
|
||||
Query(ctx context.Context, clauses string, args ...interface{}) (Rows[T], error)
|
||||
QueryContext(ctx context.Context, clauses string, args ...interface{}) (Rows[T], error)
|
||||
QueryFrom(ctx context.Context, tablename string, clauses string, args ...interface{}) (Rows[T], error)
|
||||
}
|
||||
|
||||
|
@ -105,8 +103,8 @@ type Rows[T any] interface {
|
|||
|
||||
// MockSqlExecuter implement SqlExecutor by returning errors from every call.
|
||||
//
|
||||
// To mock out WithContext, we need to be able to return objects that satisfy
|
||||
// gorp.SqlExecutor. That's a pretty big interface, so we specify one no-op mock
|
||||
// TODO: To mock out WithContext, we needed to be able to return objects that satisfy
|
||||
// borp.SqlExecutor. That's a pretty big interface, so we specify one no-op mock
|
||||
// that we can embed everywhere we need to satisfy it.
|
||||
// Note: MockSqlExecutor does *not* implement WithContext. The expectation is
|
||||
// that structs that embed MockSqlExecutor will define their own WithContext
|
||||
|
@ -114,48 +112,48 @@ type Rows[T any] interface {
|
|||
// to override the specific methods they need to implement (e.g. SelectOne).
|
||||
type MockSqlExecutor struct{}
|
||||
|
||||
func (mse MockSqlExecutor) Get(i interface{}, keys ...interface{}) (interface{}, error) {
|
||||
func (mse MockSqlExecutor) Get(ctx context.Context, i interface{}, keys ...interface{}) (interface{}, error) {
|
||||
return nil, errors.New("unimplemented")
|
||||
}
|
||||
func (mse MockSqlExecutor) Insert(list ...interface{}) error {
|
||||
func (mse MockSqlExecutor) Insert(ctx context.Context, list ...interface{}) error {
|
||||
return errors.New("unimplemented")
|
||||
}
|
||||
func (mse MockSqlExecutor) Update(list ...interface{}) (int64, error) {
|
||||
func (mse MockSqlExecutor) Update(ctx context.Context, list ...interface{}) (int64, error) {
|
||||
return 0, errors.New("unimplemented")
|
||||
}
|
||||
func (mse MockSqlExecutor) Delete(list ...interface{}) (int64, error) {
|
||||
func (mse MockSqlExecutor) Delete(ctx context.Context, list ...interface{}) (int64, error) {
|
||||
return 0, errors.New("unimplemented")
|
||||
}
|
||||
func (mse MockSqlExecutor) Exec(query string, args ...interface{}) (sql.Result, error) {
|
||||
func (mse MockSqlExecutor) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) {
|
||||
return nil, errors.New("unimplemented")
|
||||
}
|
||||
func (mse MockSqlExecutor) Select(i interface{}, query string, args ...interface{}) ([]interface{}, error) {
|
||||
func (mse MockSqlExecutor) Select(ctx context.Context, i interface{}, query string, args ...interface{}) ([]interface{}, error) {
|
||||
return nil, errors.New("unimplemented")
|
||||
}
|
||||
func (mse MockSqlExecutor) SelectInt(query string, args ...interface{}) (int64, error) {
|
||||
func (mse MockSqlExecutor) SelectInt(ctx context.Context, query string, args ...interface{}) (int64, error) {
|
||||
return 0, errors.New("unimplemented")
|
||||
}
|
||||
func (mse MockSqlExecutor) SelectNullInt(query string, args ...interface{}) (sql.NullInt64, error) {
|
||||
func (mse MockSqlExecutor) SelectNullInt(ctx context.Context, query string, args ...interface{}) (sql.NullInt64, error) {
|
||||
return sql.NullInt64{}, errors.New("unimplemented")
|
||||
}
|
||||
func (mse MockSqlExecutor) SelectFloat(query string, args ...interface{}) (float64, error) {
|
||||
func (mse MockSqlExecutor) SelectFloat(ctx context.Context, query string, args ...interface{}) (float64, error) {
|
||||
return 0, errors.New("unimplemented")
|
||||
}
|
||||
func (mse MockSqlExecutor) SelectNullFloat(query string, args ...interface{}) (sql.NullFloat64, error) {
|
||||
func (mse MockSqlExecutor) SelectNullFloat(ctx context.Context, query string, args ...interface{}) (sql.NullFloat64, error) {
|
||||
return sql.NullFloat64{}, errors.New("unimplemented")
|
||||
}
|
||||
func (mse MockSqlExecutor) SelectStr(query string, args ...interface{}) (string, error) {
|
||||
func (mse MockSqlExecutor) SelectStr(ctx context.Context, query string, args ...interface{}) (string, error) {
|
||||
return "", errors.New("unimplemented")
|
||||
}
|
||||
func (mse MockSqlExecutor) SelectNullStr(query string, args ...interface{}) (sql.NullString, error) {
|
||||
func (mse MockSqlExecutor) SelectNullStr(ctx context.Context, query string, args ...interface{}) (sql.NullString, error) {
|
||||
return sql.NullString{}, errors.New("unimplemented")
|
||||
}
|
||||
func (mse MockSqlExecutor) SelectOne(holder interface{}, query string, args ...interface{}) error {
|
||||
func (mse MockSqlExecutor) SelectOne(ctx context.Context, holder interface{}, query string, args ...interface{}) error {
|
||||
return errors.New("unimplemented")
|
||||
}
|
||||
func (mse MockSqlExecutor) Query(query string, args ...interface{}) (*sql.Rows, error) {
|
||||
func (mse MockSqlExecutor) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) {
|
||||
return nil, errors.New("unimplemented")
|
||||
}
|
||||
func (mse MockSqlExecutor) QueryRow(query string, args ...interface{}) *sql.Row {
|
||||
func (mse MockSqlExecutor) QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row {
|
||||
return nil
|
||||
}
|
||||
|
|
181
db/map.go
181
db/map.go
|
@ -5,10 +5,11 @@ import (
|
|||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
|
||||
gorp "github.com/go-gorp/gorp/v3"
|
||||
"github.com/go-sql-driver/mysql"
|
||||
"github.com/letsencrypt/borp"
|
||||
)
|
||||
|
||||
// ErrDatabaseOp wraps an underlying err with a description of the operation
|
||||
|
@ -44,7 +45,7 @@ func (e ErrDatabaseOp) Unwrap() error {
|
|||
|
||||
// IsNoRows is a utility function for determining if an error wraps the go sql
|
||||
// package's ErrNoRows, which is returned when a Scan operation has no more
|
||||
// results to return, and as such is returned by many gorp methods.
|
||||
// results to return, and as such is returned by many borp methods.
|
||||
func IsNoRows(err error) bool {
|
||||
return errors.Is(err, sql.ErrNoRows)
|
||||
}
|
||||
|
@ -57,50 +58,74 @@ func IsDuplicate(err error) bool {
|
|||
return errors.As(err, &dbErr) && dbErr.Number == 1062
|
||||
}
|
||||
|
||||
// WrappedMap wraps a *gorp.DbMap such that its major functions wrap error
|
||||
// WrappedMap wraps a *borp.DbMap such that its major functions wrap error
|
||||
// results in ErrDatabaseOp instances before returning them to the caller.
|
||||
type WrappedMap struct {
|
||||
*gorp.DbMap
|
||||
dbMap *borp.DbMap
|
||||
}
|
||||
|
||||
func (m *WrappedMap) Get(holder interface{}, keys ...interface{}) (interface{}, error) {
|
||||
return WrappedExecutor{SqlExecutor: m.DbMap}.Get(holder, keys...)
|
||||
func NewWrappedMap(dbMap *borp.DbMap) *WrappedMap {
|
||||
return &WrappedMap{dbMap: dbMap}
|
||||
}
|
||||
|
||||
func (m *WrappedMap) Insert(list ...interface{}) error {
|
||||
return WrappedExecutor{SqlExecutor: m.DbMap}.Insert(list...)
|
||||
func (m *WrappedMap) SQLDb() *sql.DB {
|
||||
return m.dbMap.Db
|
||||
}
|
||||
|
||||
func (m *WrappedMap) Update(list ...interface{}) (int64, error) {
|
||||
return WrappedExecutor{SqlExecutor: m.DbMap}.Update(list...)
|
||||
func (m *WrappedMap) BorpDB() *borp.DbMap {
|
||||
return m.dbMap
|
||||
}
|
||||
|
||||
func (m *WrappedMap) Delete(list ...interface{}) (int64, error) {
|
||||
return WrappedExecutor{SqlExecutor: m.DbMap}.Delete(list...)
|
||||
func (m *WrappedMap) TableFor(t reflect.Type, checkPK bool) (*borp.TableMap, error) {
|
||||
return m.dbMap.TableFor(t, checkPK)
|
||||
}
|
||||
|
||||
func (m *WrappedMap) Select(holder interface{}, query string, args ...interface{}) ([]interface{}, error) {
|
||||
return WrappedExecutor{SqlExecutor: m.DbMap}.Select(holder, query, args...)
|
||||
func (m *WrappedMap) Get(ctx context.Context, holder interface{}, keys ...interface{}) (interface{}, error) {
|
||||
return WrappedExecutor{sqlExecutor: m.dbMap}.Get(ctx, holder, keys...)
|
||||
}
|
||||
|
||||
func (m *WrappedMap) SelectOne(holder interface{}, query string, args ...interface{}) error {
|
||||
return WrappedExecutor{SqlExecutor: m.DbMap}.SelectOne(holder, query, args...)
|
||||
func (m *WrappedMap) Insert(ctx context.Context, list ...interface{}) error {
|
||||
return WrappedExecutor{sqlExecutor: m.dbMap}.Insert(ctx, list...)
|
||||
}
|
||||
|
||||
func (m *WrappedMap) Query(query string, args ...interface{}) (*sql.Rows, error) {
|
||||
return WrappedExecutor{SqlExecutor: m.DbMap}.Query(query, args...)
|
||||
func (m *WrappedMap) Update(ctx context.Context, list ...interface{}) (int64, error) {
|
||||
return WrappedExecutor{sqlExecutor: m.dbMap}.Update(ctx, list...)
|
||||
}
|
||||
|
||||
func (m *WrappedMap) Exec(query string, args ...interface{}) (sql.Result, error) {
|
||||
return WrappedExecutor{SqlExecutor: m.DbMap}.Exec(query, args...)
|
||||
func (m *WrappedMap) Delete(ctx context.Context, list ...interface{}) (int64, error) {
|
||||
return WrappedExecutor{sqlExecutor: m.dbMap}.Delete(ctx, list...)
|
||||
}
|
||||
|
||||
func (m *WrappedMap) WithContext(ctx context.Context) gorp.SqlExecutor {
|
||||
return WrappedExecutor{SqlExecutor: m.DbMap.WithContext(ctx)}
|
||||
func (m *WrappedMap) Select(ctx context.Context, holder interface{}, query string, args ...interface{}) ([]interface{}, error) {
|
||||
return WrappedExecutor{sqlExecutor: m.dbMap}.Select(ctx, holder, query, args...)
|
||||
}
|
||||
|
||||
func (m *WrappedMap) Begin() (Transaction, error) {
|
||||
tx, err := m.DbMap.Begin()
|
||||
func (m *WrappedMap) SelectOne(ctx context.Context, holder interface{}, query string, args ...interface{}) error {
|
||||
return WrappedExecutor{sqlExecutor: m.dbMap}.SelectOne(ctx, holder, query, args...)
|
||||
}
|
||||
|
||||
func (m *WrappedMap) SelectNullInt(ctx context.Context, query string, args ...interface{}) (sql.NullInt64, error) {
|
||||
return WrappedExecutor{sqlExecutor: m.dbMap}.SelectNullInt(ctx, query, args...)
|
||||
}
|
||||
|
||||
func (m *WrappedMap) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) {
|
||||
return WrappedExecutor{sqlExecutor: m.dbMap}.QueryContext(ctx, query, args...)
|
||||
}
|
||||
|
||||
func (m *WrappedMap) QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row {
|
||||
return WrappedExecutor{sqlExecutor: m.dbMap}.QueryRowContext(ctx, query, args...)
|
||||
}
|
||||
|
||||
func (m *WrappedMap) SelectStr(ctx context.Context, query string, args ...interface{}) (string, error) {
|
||||
return WrappedExecutor{sqlExecutor: m.dbMap}.SelectStr(ctx, query, args...)
|
||||
}
|
||||
|
||||
func (m *WrappedMap) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) {
|
||||
return WrappedExecutor{sqlExecutor: m.dbMap}.ExecContext(ctx, query, args...)
|
||||
}
|
||||
|
||||
func (m *WrappedMap) BeginTx(ctx context.Context) (Transaction, error) {
|
||||
tx, err := m.dbMap.BeginTx(ctx)
|
||||
if err != nil {
|
||||
return tx, ErrDatabaseOp{
|
||||
Op: "begin transaction",
|
||||
|
@ -108,66 +133,62 @@ func (m *WrappedMap) Begin() (Transaction, error) {
|
|||
}
|
||||
}
|
||||
return WrappedTransaction{
|
||||
Transaction: tx,
|
||||
transaction: tx,
|
||||
}, err
|
||||
}
|
||||
|
||||
// WrappedTransaction wraps a *gorp.Transaction such that its major functions
|
||||
// WrappedTransaction wraps a *borp.Transaction such that its major functions
|
||||
// wrap error results in ErrDatabaseOp instances before returning them to the
|
||||
// caller.
|
||||
type WrappedTransaction struct {
|
||||
*gorp.Transaction
|
||||
}
|
||||
|
||||
func (tx WrappedTransaction) WithContext(ctx context.Context) gorp.SqlExecutor {
|
||||
return WrappedExecutor{SqlExecutor: tx.Transaction.WithContext(ctx)}
|
||||
transaction *borp.Transaction
|
||||
}
|
||||
|
||||
func (tx WrappedTransaction) Commit() error {
|
||||
return tx.Transaction.Commit()
|
||||
return tx.transaction.Commit()
|
||||
}
|
||||
|
||||
func (tx WrappedTransaction) Rollback() error {
|
||||
return tx.Transaction.Rollback()
|
||||
return tx.transaction.Rollback()
|
||||
}
|
||||
|
||||
func (tx WrappedTransaction) Get(holder interface{}, keys ...interface{}) (interface{}, error) {
|
||||
return (WrappedExecutor{SqlExecutor: tx.Transaction}).Get(holder, keys...)
|
||||
func (tx WrappedTransaction) Get(ctx context.Context, holder interface{}, keys ...interface{}) (interface{}, error) {
|
||||
return (WrappedExecutor{sqlExecutor: tx.transaction}).Get(ctx, holder, keys...)
|
||||
}
|
||||
|
||||
func (tx WrappedTransaction) Insert(list ...interface{}) error {
|
||||
return (WrappedExecutor{SqlExecutor: tx.Transaction}).Insert(list...)
|
||||
func (tx WrappedTransaction) Insert(ctx context.Context, list ...interface{}) error {
|
||||
return (WrappedExecutor{sqlExecutor: tx.transaction}).Insert(ctx, list...)
|
||||
}
|
||||
|
||||
func (tx WrappedTransaction) Update(list ...interface{}) (int64, error) {
|
||||
return (WrappedExecutor{SqlExecutor: tx.Transaction}).Update(list...)
|
||||
func (tx WrappedTransaction) Update(ctx context.Context, list ...interface{}) (int64, error) {
|
||||
return (WrappedExecutor{sqlExecutor: tx.transaction}).Update(ctx, list...)
|
||||
}
|
||||
|
||||
func (tx WrappedTransaction) Delete(list ...interface{}) (int64, error) {
|
||||
return (WrappedExecutor{SqlExecutor: tx.Transaction}).Delete(list...)
|
||||
func (tx WrappedTransaction) Delete(ctx context.Context, list ...interface{}) (int64, error) {
|
||||
return (WrappedExecutor{sqlExecutor: tx.transaction}).Delete(ctx, list...)
|
||||
}
|
||||
|
||||
func (tx WrappedTransaction) Select(holder interface{}, query string, args ...interface{}) ([]interface{}, error) {
|
||||
return (WrappedExecutor{SqlExecutor: tx.Transaction}).Select(holder, query, args...)
|
||||
func (tx WrappedTransaction) Select(ctx context.Context, holder interface{}, query string, args ...interface{}) ([]interface{}, error) {
|
||||
return (WrappedExecutor{sqlExecutor: tx.transaction}).Select(ctx, holder, query, args...)
|
||||
}
|
||||
|
||||
func (tx WrappedTransaction) SelectOne(holder interface{}, query string, args ...interface{}) error {
|
||||
return (WrappedExecutor{SqlExecutor: tx.Transaction}).SelectOne(holder, query, args...)
|
||||
func (tx WrappedTransaction) SelectOne(ctx context.Context, holder interface{}, query string, args ...interface{}) error {
|
||||
return (WrappedExecutor{sqlExecutor: tx.transaction}).SelectOne(ctx, holder, query, args...)
|
||||
}
|
||||
|
||||
func (tx WrappedTransaction) Query(query string, args ...interface{}) (*sql.Rows, error) {
|
||||
return (WrappedExecutor{SqlExecutor: tx.Transaction}).Query(query, args...)
|
||||
func (tx WrappedTransaction) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) {
|
||||
return (WrappedExecutor{sqlExecutor: tx.transaction}).QueryContext(ctx, query, args...)
|
||||
}
|
||||
|
||||
func (tx WrappedTransaction) Exec(query string, args ...interface{}) (sql.Result, error) {
|
||||
return (WrappedExecutor{SqlExecutor: tx.Transaction}).Exec(query, args...)
|
||||
func (tx WrappedTransaction) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) {
|
||||
return (WrappedExecutor{sqlExecutor: tx.transaction}).ExecContext(ctx, query, args...)
|
||||
}
|
||||
|
||||
// WrappedExecutor wraps a gorp.SqlExecutor such that its major functions
|
||||
// WrappedExecutor wraps a borp.SqlExecutor such that its major functions
|
||||
// wrap error results in ErrDatabaseOp instances before returning them to the
|
||||
// caller.
|
||||
type WrappedExecutor struct {
|
||||
gorp.SqlExecutor
|
||||
sqlExecutor borp.SqlExecutor
|
||||
}
|
||||
|
||||
func errForOp(operation string, err error, list []interface{}) ErrDatabaseOp {
|
||||
|
@ -203,56 +224,78 @@ func errForQuery(query, operation string, err error, list []interface{}) ErrData
|
|||
}
|
||||
}
|
||||
|
||||
func (we WrappedExecutor) Get(holder interface{}, keys ...interface{}) (interface{}, error) {
|
||||
res, err := we.SqlExecutor.Get(holder, keys...)
|
||||
func (we WrappedExecutor) Get(ctx context.Context, holder interface{}, keys ...interface{}) (interface{}, error) {
|
||||
res, err := we.sqlExecutor.Get(ctx, holder, keys...)
|
||||
if err != nil {
|
||||
return res, errForOp("get", err, []interface{}{holder})
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
func (we WrappedExecutor) Insert(list ...interface{}) error {
|
||||
err := we.SqlExecutor.Insert(list...)
|
||||
func (we WrappedExecutor) Insert(ctx context.Context, list ...interface{}) error {
|
||||
err := we.sqlExecutor.Insert(ctx, list...)
|
||||
if err != nil {
|
||||
return errForOp("insert", err, list)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (we WrappedExecutor) Update(list ...interface{}) (int64, error) {
|
||||
updatedRows, err := we.SqlExecutor.Update(list...)
|
||||
func (we WrappedExecutor) Update(ctx context.Context, list ...interface{}) (int64, error) {
|
||||
updatedRows, err := we.sqlExecutor.Update(ctx, list...)
|
||||
if err != nil {
|
||||
return updatedRows, errForOp("update", err, list)
|
||||
}
|
||||
return updatedRows, err
|
||||
}
|
||||
|
||||
func (we WrappedExecutor) Delete(list ...interface{}) (int64, error) {
|
||||
deletedRows, err := we.SqlExecutor.Delete(list...)
|
||||
func (we WrappedExecutor) Delete(ctx context.Context, list ...interface{}) (int64, error) {
|
||||
deletedRows, err := we.sqlExecutor.Delete(ctx, list...)
|
||||
if err != nil {
|
||||
return deletedRows, errForOp("delete", err, list)
|
||||
}
|
||||
return deletedRows, err
|
||||
}
|
||||
|
||||
func (we WrappedExecutor) Select(holder interface{}, query string, args ...interface{}) ([]interface{}, error) {
|
||||
result, err := we.SqlExecutor.Select(holder, query, args...)
|
||||
func (we WrappedExecutor) Select(ctx context.Context, holder interface{}, query string, args ...interface{}) ([]interface{}, error) {
|
||||
result, err := we.sqlExecutor.Select(ctx, holder, query, args...)
|
||||
if err != nil {
|
||||
return result, errForQuery(query, "select", err, []interface{}{holder})
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (we WrappedExecutor) SelectOne(holder interface{}, query string, args ...interface{}) error {
|
||||
err := we.SqlExecutor.SelectOne(holder, query, args...)
|
||||
func (we WrappedExecutor) SelectOne(ctx context.Context, holder interface{}, query string, args ...interface{}) error {
|
||||
err := we.sqlExecutor.SelectOne(ctx, holder, query, args...)
|
||||
if err != nil {
|
||||
return errForQuery(query, "select one", err, []interface{}{holder})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (we WrappedExecutor) Query(query string, args ...interface{}) (*sql.Rows, error) {
|
||||
rows, err := we.SqlExecutor.Query(query, args...)
|
||||
func (we WrappedExecutor) SelectNullInt(ctx context.Context, query string, args ...interface{}) (sql.NullInt64, error) {
|
||||
rows, err := we.sqlExecutor.SelectNullInt(ctx, query, args...)
|
||||
if err != nil {
|
||||
return sql.NullInt64{}, errForQuery(query, "select", err, nil)
|
||||
}
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
func (we WrappedExecutor) QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row {
|
||||
// Note: we can't do error wrapping here because the error is passed via the `*sql.Row`
|
||||
// object, and we can't produce a `*sql.Row` object with a custom error because it is unexported.
|
||||
return we.sqlExecutor.QueryRowContext(ctx, query, args...)
|
||||
}
|
||||
|
||||
func (we WrappedExecutor) SelectStr(ctx context.Context, query string, args ...interface{}) (string, error) {
|
||||
str, err := we.sqlExecutor.SelectStr(ctx, query, args...)
|
||||
if err != nil {
|
||||
return "", errForQuery(query, "select", err, nil)
|
||||
}
|
||||
return str, nil
|
||||
}
|
||||
|
||||
func (we WrappedExecutor) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) {
|
||||
rows, err := we.sqlExecutor.QueryContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return nil, errForQuery(query, "select", err, nil)
|
||||
}
|
||||
|
@ -271,10 +314,10 @@ var (
|
|||
|
||||
// tableRegexps is a list of regexps that tableFromQuery will try to use in
|
||||
// succession to find the table name for an SQL query. While tableFromQuery
|
||||
// isn't used by the higher level gorp Insert/Update/Select/etc functions we
|
||||
// isn't used by the higher level borp Insert/Update/Select/etc functions we
|
||||
// include regexps for matching inserts, updates, selects, etc because we want
|
||||
// to match the correct table when these types of queries are run through
|
||||
// Exec().
|
||||
// ExecContext().
|
||||
tableRegexps = []*regexp.Regexp{
|
||||
selectTableRegexp,
|
||||
insertTableRegexp,
|
||||
|
@ -295,8 +338,8 @@ func tableFromQuery(query string) string {
|
|||
return ""
|
||||
}
|
||||
|
||||
func (we WrappedExecutor) Exec(query string, args ...interface{}) (sql.Result, error) {
|
||||
res, err := we.SqlExecutor.Exec(query, args...)
|
||||
func (we WrappedExecutor) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) {
|
||||
res, err := we.sqlExecutor.ExecContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return res, errForQuery(query, "exec", err, args)
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
"fmt"
|
||||
"testing"
|
||||
|
||||
gorp "github.com/go-gorp/gorp/v3"
|
||||
"github.com/letsencrypt/borp"
|
||||
|
||||
"github.com/go-sql-driver/mysql"
|
||||
"github.com/letsencrypt/boulder/core"
|
||||
|
@ -236,25 +236,28 @@ func testDbMap(t *testing.T) *WrappedMap {
|
|||
dbConn, err := sql.Open("mysql", config.FormatDSN())
|
||||
test.AssertNotError(t, err, "opening DB connection")
|
||||
|
||||
dialect := gorp.MySQLDialect{Engine: "InnoDB", Encoding: "UTF8"}
|
||||
dialect := borp.MySQLDialect{Engine: "InnoDB", Encoding: "UTF8"}
|
||||
// NOTE(@cpu): We avoid giving a sa.BoulderTypeConverter to the DbMap field to
|
||||
// avoid the cyclic dep. We don't need to convert any types in the db tests.
|
||||
dbMap := &gorp.DbMap{Db: dbConn, Dialect: dialect, TypeConverter: nil}
|
||||
return &WrappedMap{DbMap: dbMap}
|
||||
dbMap := &borp.DbMap{Db: dbConn, Dialect: dialect, TypeConverter: nil}
|
||||
return &WrappedMap{dbMap: dbMap}
|
||||
}
|
||||
|
||||
func TestWrappedMap(t *testing.T) {
|
||||
mustDbErr := func(err error) ErrDatabaseOp {
|
||||
t.Helper()
|
||||
var dbOpErr ErrDatabaseOp
|
||||
test.AssertErrorWraps(t, err, &dbOpErr)
|
||||
return dbOpErr
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
testWrapper := func(dbMap Executor) {
|
||||
reg := &core.Registration{}
|
||||
|
||||
// Test wrapped Get
|
||||
_, err := dbMap.Get(reg)
|
||||
_, err := dbMap.Get(ctx, reg)
|
||||
test.AssertError(t, err, "expected err Getting Registration w/o type converter")
|
||||
dbOpErr := mustDbErr(err)
|
||||
test.AssertEquals(t, dbOpErr.Op, "get")
|
||||
|
@ -262,7 +265,7 @@ func TestWrappedMap(t *testing.T) {
|
|||
test.AssertError(t, dbOpErr.Err, "expected non-nil underlying err")
|
||||
|
||||
// Test wrapped Insert
|
||||
err = dbMap.Insert(reg)
|
||||
err = dbMap.Insert(ctx, reg)
|
||||
test.AssertError(t, err, "expected err Inserting Registration w/o type converter")
|
||||
dbOpErr = mustDbErr(err)
|
||||
test.AssertEquals(t, dbOpErr.Op, "insert")
|
||||
|
@ -270,7 +273,7 @@ func TestWrappedMap(t *testing.T) {
|
|||
test.AssertError(t, dbOpErr.Err, "expected non-nil underlying err")
|
||||
|
||||
// Test wrapped Update
|
||||
_, err = dbMap.Update(reg)
|
||||
_, err = dbMap.Update(ctx, reg)
|
||||
test.AssertError(t, err, "expected err Updating Registration w/o type converter")
|
||||
dbOpErr = mustDbErr(err)
|
||||
test.AssertEquals(t, dbOpErr.Op, "update")
|
||||
|
@ -278,7 +281,7 @@ func TestWrappedMap(t *testing.T) {
|
|||
test.AssertError(t, dbOpErr.Err, "expected non-nil underlying err")
|
||||
|
||||
// Test wrapped Delete
|
||||
_, err = dbMap.Delete(reg)
|
||||
_, err = dbMap.Delete(ctx, reg)
|
||||
test.AssertError(t, err, "expected err Deleting Registration w/o type converter")
|
||||
dbOpErr = mustDbErr(err)
|
||||
test.AssertEquals(t, dbOpErr.Op, "delete")
|
||||
|
@ -286,7 +289,7 @@ func TestWrappedMap(t *testing.T) {
|
|||
test.AssertError(t, dbOpErr.Err, "expected non-nil underlying err")
|
||||
|
||||
// Test wrapped Select with a bogus query
|
||||
_, err = dbMap.Select(reg, "blah")
|
||||
_, err = dbMap.Select(ctx, reg, "blah")
|
||||
test.AssertError(t, err, "expected err Selecting Registration w/o type converter")
|
||||
dbOpErr = mustDbErr(err)
|
||||
test.AssertEquals(t, dbOpErr.Op, "select")
|
||||
|
@ -294,7 +297,7 @@ func TestWrappedMap(t *testing.T) {
|
|||
test.AssertError(t, dbOpErr.Err, "expected non-nil underlying err")
|
||||
|
||||
// Test wrapped Select with a valid query
|
||||
_, err = dbMap.Select(reg, "SELECT id, contact FROM registrationzzz WHERE id > 1;")
|
||||
_, err = dbMap.Select(ctx, reg, "SELECT id, contact FROM registrationzzz WHERE id > 1;")
|
||||
test.AssertError(t, err, "expected err Selecting Registration w/o type converter")
|
||||
dbOpErr = mustDbErr(err)
|
||||
test.AssertEquals(t, dbOpErr.Op, "select")
|
||||
|
@ -302,7 +305,7 @@ func TestWrappedMap(t *testing.T) {
|
|||
test.AssertError(t, dbOpErr.Err, "expected non-nil underlying err")
|
||||
|
||||
// Test wrapped SelectOne with a bogus query
|
||||
err = dbMap.SelectOne(reg, "blah")
|
||||
err = dbMap.SelectOne(ctx, reg, "blah")
|
||||
test.AssertError(t, err, "expected err SelectOne-ing Registration w/o type converter")
|
||||
dbOpErr = mustDbErr(err)
|
||||
test.AssertEquals(t, dbOpErr.Op, "select one")
|
||||
|
@ -310,7 +313,7 @@ func TestWrappedMap(t *testing.T) {
|
|||
test.AssertError(t, dbOpErr.Err, "expected non-nil underlying err")
|
||||
|
||||
// Test wrapped SelectOne with a valid query
|
||||
err = dbMap.SelectOne(reg, "SELECT contact FROM doesNotExist WHERE id=1;")
|
||||
err = dbMap.SelectOne(ctx, reg, "SELECT contact FROM doesNotExist WHERE id=1;")
|
||||
test.AssertError(t, err, "expected err SelectOne-ing Registration w/o type converter")
|
||||
dbOpErr = mustDbErr(err)
|
||||
test.AssertEquals(t, dbOpErr.Op, "select one")
|
||||
|
@ -318,7 +321,7 @@ func TestWrappedMap(t *testing.T) {
|
|||
test.AssertError(t, dbOpErr.Err, "expected non-nil underlying err")
|
||||
|
||||
// Test wrapped Exec
|
||||
_, err = dbMap.Exec("INSERT INTO whatever (id) VALUES (?) WHERE id = ?", 10)
|
||||
_, err = dbMap.ExecContext(ctx, "INSERT INTO whatever (id) VALUES (?) WHERE id = ?", 10)
|
||||
test.AssertError(t, err, "expected err Exec-ing bad query")
|
||||
dbOpErr = mustDbErr(err)
|
||||
test.AssertEquals(t, dbOpErr.Op, "exec")
|
||||
|
@ -333,24 +336,10 @@ func TestWrappedMap(t *testing.T) {
|
|||
// database errors.
|
||||
testWrapper(dbMap)
|
||||
|
||||
// Using WithContext on the WrappedMap should return a map that continues to
|
||||
// operate in the expected fashion.
|
||||
dbMapWithCtx := dbMap.WithContext(context.Background())
|
||||
testWrapper(dbMapWithCtx)
|
||||
|
||||
// Using Begin to start a transaction with the dbMap should return a
|
||||
// transaction that continues to operate in the expected fashion.
|
||||
tx, err := dbMap.Begin()
|
||||
tx, err := dbMap.BeginTx(ctx)
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
test.AssertNotError(t, err, "unexpected error beginning transaction")
|
||||
testWrapper(tx)
|
||||
|
||||
// Using Begin to start a transaction with the dbMap and then using
|
||||
// WithContext should return a transaction that continues to operate in the
|
||||
// expected fashion.
|
||||
tx, err = dbMap.Begin()
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
test.AssertNotError(t, err, "unexpected error beginning transaction")
|
||||
txWithContext := tx.WithContext(context.Background())
|
||||
testWrapper(txWithContext)
|
||||
}
|
||||
|
|
12
db/multi.go
12
db/multi.go
|
@ -1,6 +1,7 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
@ -68,7 +69,7 @@ func (mi *MultiInserter) Add(row []interface{}) error {
|
|||
}
|
||||
|
||||
// query returns the formatted query string, and the slice of arguments for
|
||||
// for gorp to use in place of the query's question marks. Currently only
|
||||
// for borp to use in place of the query's question marks. Currently only
|
||||
// used by .Insert(), below.
|
||||
func (mi *MultiInserter) query() (string, []interface{}) {
|
||||
var questionsBuf strings.Builder
|
||||
|
@ -101,12 +102,11 @@ func (mi *MultiInserter) query() (string, []interface{}) {
|
|||
}
|
||||
|
||||
// Insert inserts all the collected rows into the database represented by
|
||||
// `queryer`. `queryer` is assumed to already have a context attached. If a
|
||||
// non-empty returningColumn was provided, then it returns the list of values
|
||||
// from that column returned by the query.
|
||||
func (mi *MultiInserter) Insert(queryer Queryer) ([]int64, error) {
|
||||
// `queryer`. If a non-empty returningColumn was provided, then it returns
|
||||
// the list of values from that column returned by the query.
|
||||
func (mi *MultiInserter) Insert(ctx context.Context, queryer Queryer) ([]int64, error) {
|
||||
query, queryArgs := mi.query()
|
||||
rows, err := queryer.Query(query, queryArgs...)
|
||||
rows, err := queryer.QueryContext(ctx, query, queryArgs...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
berrors "github.com/letsencrypt/boulder/errors"
|
||||
|
@ -8,9 +9,10 @@ import (
|
|||
)
|
||||
|
||||
func TestRollback(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
dbMap := testDbMap(t)
|
||||
|
||||
tx, _ := dbMap.Begin()
|
||||
tx, _ := dbMap.BeginTx(ctx)
|
||||
// Commit the transaction so that a subsequent rollback will always fail.
|
||||
_ = tx.Commit()
|
||||
|
||||
|
@ -28,7 +30,7 @@ func TestRollback(t *testing.T) {
|
|||
|
||||
// Create a new transaction and don't commit it this time. The rollback should
|
||||
// succeed.
|
||||
tx, _ = dbMap.Begin()
|
||||
tx, _ = dbMap.BeginTx(ctx)
|
||||
result = rollback(tx, innerErr)
|
||||
|
||||
// We expect that the err is returned unwrapped.
|
||||
|
|
|
@ -3,19 +3,18 @@ package db
|
|||
import "context"
|
||||
|
||||
// txFunc represents a function that does work in the context of a transaction.
|
||||
type txFunc func(txWithCtx Executor) (interface{}, error)
|
||||
type txFunc func(tx Executor) (interface{}, error)
|
||||
|
||||
// WithTransaction runs the given function in a transaction, rolling back if it
|
||||
// returns an error and committing if not. The provided context is also attached
|
||||
// to the transaction. WithTransaction also passes through a value returned by
|
||||
// `f`, if there is no error.
|
||||
func WithTransaction(ctx context.Context, dbMap DatabaseMap, f txFunc) (interface{}, error) {
|
||||
tx, err := dbMap.Begin()
|
||||
tx, err := dbMap.BeginTx(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
txWithCtx := tx.WithContext(ctx)
|
||||
result, err := f(txWithCtx)
|
||||
result, err := f(tx)
|
||||
if err != nil {
|
||||
return nil, rollback(tx, err)
|
||||
}
|
||||
|
|
|
@ -191,10 +191,10 @@ Specifically, that means that all of our `SELECT` statements should enumerate
|
|||
columns to select, and not use `*`. Also, generally speaking, we will need a
|
||||
separate model `struct` for serializing and deserializing data before and
|
||||
after the migration. This is because the ORM package we use,
|
||||
[`gorp`](https://github.com/go-gorp/gorp), expects every field in a struct to
|
||||
[`borp`](https://github.com/letsencrypt/borp), expects every field in a struct to
|
||||
map to a column in the table. If we add a new field to a model struct and
|
||||
Boulder attempts to write that struct to a table that doesn't yet have the
|
||||
corresponding column (case 1), gorp will fail with `Insert failed table posts
|
||||
corresponding column (case 1), borp will fail with `Insert failed table posts
|
||||
has no column named Foo`. There are examples of such models in sa/model.go,
|
||||
along with code to turn a model into a `struct` used internally.
|
||||
|
||||
|
@ -254,14 +254,14 @@ func (ssa *SQLStorageAuthority) GetPerson() (Person, error) {
|
|||
|
||||
func (ssa *SQLStorageAuthority) AddPerson(p Person) (error) {
|
||||
if features.Enabled(features.AllowWizards) { // Added!
|
||||
return ssa.dbMap.Insert(personModelv2{
|
||||
return ssa.dbMap.Insert(context.Background(), personModelv2{
|
||||
personModelv1: {
|
||||
HatSize: p.HatSize,
|
||||
},
|
||||
IsWizard: p.IsWizard,
|
||||
})
|
||||
} else {
|
||||
return ssa.dbMap.Insert(personModelv1{
|
||||
return ssa.dbMap.Insert(context.Background(), personModelv1{
|
||||
HatSize: p.HatSize,
|
||||
// p.IsWizard ignored
|
||||
})
|
||||
|
@ -270,14 +270,14 @@ func (ssa *SQLStorageAuthority) AddPerson(p Person) (error) {
|
|||
```
|
||||
|
||||
You will also need to update the `initTables` function from `sa/database.go` to
|
||||
tell Gorp which table to use for your versioned model structs. Make sure to
|
||||
tell borp which table to use for your versioned model structs. Make sure to
|
||||
consult the flag you defined so that only **one** of the table maps is added at
|
||||
any given time, otherwise Gorp will error. Depending on your table you may also
|
||||
any given time, otherwise borp will error. Depending on your table you may also
|
||||
need to add `SetKeys` and `SetVersionCol` entries for your versioned models.
|
||||
Example:
|
||||
|
||||
```go
|
||||
func initTables(dbMap *gorp.DbMap) {
|
||||
func initTables(dbMap *borp.DbMap) {
|
||||
// < unrelated lines snipped for brevity >
|
||||
|
||||
if features.Enabled(features.AllowWizards) {
|
||||
|
|
6
go.mod
6
go.mod
|
@ -9,7 +9,6 @@ require (
|
|||
github.com/aws/smithy-go v1.13.5
|
||||
github.com/beeker1121/goque v1.0.3-0.20191103205551-d618510128af
|
||||
github.com/eggsampler/acme/v3 v3.4.0
|
||||
github.com/go-gorp/gorp/v3 v3.1.0
|
||||
github.com/go-logr/stdr v1.2.2
|
||||
github.com/go-redis/redis/v8 v8.11.5
|
||||
github.com/go-sql-driver/mysql v1.7.1
|
||||
|
@ -18,6 +17,7 @@ require (
|
|||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
|
||||
github.com/hpcloud/tail v1.0.0
|
||||
github.com/jmhodges/clock v1.2.0
|
||||
github.com/letsencrypt/borp v0.0.0-20230707160741-6cc6ce580243
|
||||
github.com/letsencrypt/challtestsrv v1.2.1
|
||||
github.com/letsencrypt/pkcs11key/v4 v4.0.0
|
||||
github.com/letsencrypt/validator/v10 v10.0.0-20230215210743-a0c7dfc17158
|
||||
|
@ -73,11 +73,12 @@ require (
|
|||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/pelletier/go-toml v1.9.3 // indirect
|
||||
github.com/poy/onpar v1.1.2 // indirect
|
||||
github.com/prometheus/common v0.37.0 // indirect
|
||||
github.com/prometheus/procfs v0.8.0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.9.0 // indirect
|
||||
github.com/syndtr/goleveldb v1.0.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.15.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.15.0 // indirect
|
||||
|
@ -87,6 +88,7 @@ require (
|
|||
golang.org/x/sys v0.9.0 // indirect
|
||||
golang.org/x/tools v0.8.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
gopkg.in/fsnotify.v1 v1.4.7 // indirect
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||
k8s.io/klog/v2 v2.80.1 // indirect
|
||||
|
|
78
go.sum
78
go.sum
|
@ -39,12 +39,14 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
|
|||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g=
|
||||
github.com/a8m/expect v1.0.0/go.mod h1:4IwSCMumY49ScypDnjNbYEjgVeqy1/U2cEs3Lat96eA=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/aws/aws-sdk-go-v2 v1.17.7/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
|
||||
github.com/aws/aws-sdk-go-v2 v1.18.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
|
||||
github.com/aws/aws-sdk-go-v2 v1.18.1 h1:+tefE750oAb7ZQGzla6bLkOwfcQCEtC5y2RqoqCeqKo=
|
||||
|
@ -113,11 +115,20 @@ github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWH
|
|||
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/eggsampler/acme/v3 v3.4.0 h1:LHWnB3wShVshK1+umL6ObCjnc0MM+D7TE8JINjk8zGY=
|
||||
github.com/eggsampler/acme/v3 v3.4.0/go.mod h1:/qh0rKC/Dh7Jj+p4So7DbWmFNzC4dpcpK53r226Fhuo=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
|
@ -127,6 +138,7 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m
|
|||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
|
||||
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
|
||||
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
|
@ -135,8 +147,6 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME
|
|||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs=
|
||||
github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
|
@ -162,9 +172,11 @@ github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrt
|
|||
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ=
|
||||
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
|
@ -233,21 +245,27 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4
|
|||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2 h1:gDLXvp5S9izjldquuoAhDzccbskOL6tDC5jMSyx3zxE=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2/go.mod h1:7pdNwVWBBHGiCxa9lAszqCJMbfTISJ7oMftp8+UGV08=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
github.com/jmhodges/clock v1.2.0 h1:eq4kys+NI0PLngzaHEe7AmPT90XMGIEySD1JfV1PDIs=
|
||||
github.com/jmhodges/clock v1.2.0/go.mod h1:qKjhA7x7u/lQpPB1XAqX1b1lCI/w3/fNuYpI/ZjLynI=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
|
@ -257,23 +275,32 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1
|
|||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/letsencrypt/borp v0.0.0-20230707160741-6cc6ce580243 h1:xS2U6PQYRURk61YN4Y5xvyLbQVyAP/8fpE6hJZdwEWs=
|
||||
github.com/letsencrypt/borp v0.0.0-20230707160741-6cc6ce580243/go.mod h1:podMDq5wDu2ZO6JMKYQcjD3QdqOfNLWtP2RDSy8CHUU=
|
||||
github.com/letsencrypt/challtestsrv v1.2.1 h1:Lzv4jM+wSgVMCeO5a/F/IzSanhClstFMnX6SfrAJXjI=
|
||||
github.com/letsencrypt/challtestsrv v1.2.1/go.mod h1:Ur4e4FvELUXLGhkMztHOsPIsvGxD/kzSJninOrkM+zc=
|
||||
github.com/letsencrypt/pkcs11key/v4 v4.0.0 h1:qLc/OznH7xMr5ARJgkZCCWk+EomQkiNTOoOF5LAgagc=
|
||||
github.com/letsencrypt/pkcs11key/v4 v4.0.0/go.mod h1:EFUvBDay26dErnNb70Nd0/VW3tJiIbETBPTl9ATXQag=
|
||||
github.com/letsencrypt/validator/v10 v10.0.0-20230215210743-a0c7dfc17158 h1:HGFsIltYMUiB5eoFSowFzSoXkocM2k9ctmJ57QMGjys=
|
||||
github.com/letsencrypt/validator/v10 v10.0.0-20230215210743-a0c7dfc17158/go.mod h1:ZFNBS3H6OEsprCRjscty6GCBe5ZiX44x6qY4s7+bDX0=
|
||||
github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
|
||||
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
||||
|
@ -282,6 +309,8 @@ github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7Xn
|
|||
github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||
github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU=
|
||||
github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
|
@ -291,22 +320,29 @@ github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474/go.mod h1
|
|||
github.com/mreiferson/go-httpclient v0.0.0-20201222173833-5e475fde3a4d/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/nelsam/hel/v2 v2.3.2/go.mod h1:1ZTGfU2PFTOd5mx22i5O0Lc2GY933lQ2wb/ggy+rL3w=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ=
|
||||
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/poy/onpar v0.0.0-20200406201722-06f95a1c68e8/go.mod h1:nSbFQvMj97ZyhFRSJYtut+msi4sOY6zJDGCdSc+/rZU=
|
||||
github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY=
|
||||
github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjzg=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
||||
|
@ -319,6 +355,8 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:
|
|||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
|
||||
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
||||
|
@ -326,23 +364,35 @@ github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+
|
|||
github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE=
|
||||
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
|
||||
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
|
@ -350,17 +400,21 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
|
|||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
|
||||
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
||||
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C1wj2THlRK+oAhjeS/TRQwMfkIuet3w0=
|
||||
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399/go.mod h1:LdwHTNJT99C5fTAzDz0ud328OgXz+gierycbcIx2fRs=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||
github.com/weppos/publicsuffix-go v0.12.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k=
|
||||
github.com/weppos/publicsuffix-go v0.13.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k=
|
||||
github.com/weppos/publicsuffix-go v0.30.0/go.mod h1:kBi8zwYnR0zrbm8RcuN1o9Fzgpnnn+btVN8uWPMyXAY=
|
||||
github.com/weppos/publicsuffix-go v0.30.1-0.20230620154423-38c92ad2d5c6 h1:kNn7cjQYeNjKUflvFFCxFeyS7ENcDdfPmkhFpgd0G/A=
|
||||
github.com/weppos/publicsuffix-go v0.30.1-0.20230620154423-38c92ad2d5c6/go.mod h1:wdMq89hDN07Zqr0yqYAXIBTJXl4MEELx+HYHOZdf5gM=
|
||||
github.com/weppos/publicsuffix-go/publicsuffix/generator v0.0.0-20220927085643-dc0d00c92642/go.mod h1:GHfoeIdZLdZmLjMlzBftbTDntahTttUMWjxZwQJhULE=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
|
@ -377,6 +431,7 @@ github.com/zmap/zcrypto v0.0.0-20230310154051-c8b263fd8300/go.mod h1:mOd4yUMgn2f
|
|||
github.com/zmap/zlint/v3 v3.0.0/go.mod h1:paGwFySdHIBEMJ61YjoqT4h7Ge+fdYG4sUQhnTb1lJ8=
|
||||
github.com/zmap/zlint/v3 v3.5.0 h1:Eh2B5t6VKgVH0DFmTwOqE50POvyDhUaU9T2mJOe1vfQ=
|
||||
github.com/zmap/zlint/v3 v3.5.0/go.mod h1:JkNSrsDJ8F4VRtBZcYUQSvnWFL7utcjDIn+FE64mlBI=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
|
@ -403,7 +458,10 @@ go.opentelemetry.io/otel/trace v1.15.0/go.mod h1:CUsmE2Ht1CRkvE8OsMESvraoZrrcgD1
|
|||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw=
|
||||
go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
|
@ -457,12 +515,14 @@ golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73r
|
|||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
|
@ -523,8 +583,10 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -600,6 +662,7 @@ golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
|||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
|
@ -632,6 +695,7 @@ golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapK
|
|||
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200313205530-4303120df7d8/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
|
@ -708,6 +772,7 @@ google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg
|
|||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
|
@ -746,13 +811,16 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
|
|||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/go-jose/go-jose.v2 v2.6.1 h1:qEzJlIDmG9q5VO0M/o8tGS65QMHMS1w01TQJB1VPJ4U=
|
||||
gopkg.in/go-jose/go-jose.v2 v2.6.1/go.mod h1:zzZDPkNNw/c9IE7Z9jr11mBZQhKQTMzoEEIoEdZlFBI=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"reflect"
|
||||
"sync"
|
||||
|
||||
"github.com/go-gorp/gorp/v3"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"golang.org/x/crypto/ocsp"
|
||||
|
||||
|
@ -22,8 +21,7 @@ import (
|
|||
// dbSelector is a limited subset of the db.WrappedMap interface to allow for
|
||||
// easier mocking of mysql operations in tests.
|
||||
type dbSelector interface {
|
||||
SelectOne(holder interface{}, query string, args ...interface{}) error
|
||||
WithContext(ctx context.Context) gorp.SqlExecutor
|
||||
SelectOne(ctx context.Context, holder interface{}, query string, args ...interface{}) error
|
||||
}
|
||||
|
||||
// rocspSourceInterface expands on responder.Source by adding a private signAndSave method.
|
||||
|
@ -102,7 +100,7 @@ func (src *checkedRedisSource) Response(ctx context.Context, req *ocsp.Request)
|
|||
if src.sac != nil {
|
||||
dbStatus, dbErr = src.sac.GetRevocationStatus(ctx, &sapb.Serial{Serial: serialString})
|
||||
} else {
|
||||
dbStatus, dbErr = sa.SelectRevocationStatus(src.dbMap.WithContext(ctx), serialString)
|
||||
dbStatus, dbErr = sa.SelectRevocationStatus(ctx, src.dbMap, serialString)
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
|
|
|
@ -9,7 +9,6 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-gorp/gorp/v3"
|
||||
"golang.org/x/crypto/ocsp"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
@ -71,11 +70,7 @@ type echoSelector struct {
|
|||
status sa.RevocationStatusModel
|
||||
}
|
||||
|
||||
func (s echoSelector) WithContext(context.Context) gorp.SqlExecutor {
|
||||
return s
|
||||
}
|
||||
|
||||
func (s echoSelector) SelectOne(output interface{}, _ string, _ ...interface{}) error {
|
||||
func (s echoSelector) SelectOne(_ context.Context, output interface{}, _ string, _ ...interface{}) error {
|
||||
outputPtr, ok := output.(*sa.RevocationStatusModel)
|
||||
if !ok {
|
||||
return fmt.Errorf("incorrect output type %T", output)
|
||||
|
@ -89,27 +84,19 @@ type errorSelector struct {
|
|||
db.MockSqlExecutor
|
||||
}
|
||||
|
||||
func (s errorSelector) SelectOne(_ interface{}, _ string, _ ...interface{}) error {
|
||||
func (s errorSelector) SelectOne(_ context.Context, _ interface{}, _ string, _ ...interface{}) error {
|
||||
return errors.New("oops")
|
||||
}
|
||||
|
||||
func (s errorSelector) WithContext(context.Context) gorp.SqlExecutor {
|
||||
return s
|
||||
}
|
||||
|
||||
// notFoundSelector always returns an NoRows error.
|
||||
type notFoundSelector struct {
|
||||
db.MockSqlExecutor
|
||||
}
|
||||
|
||||
func (s notFoundSelector) SelectOne(_ interface{}, _ string, _ ...interface{}) error {
|
||||
func (s notFoundSelector) SelectOne(_ context.Context, _ interface{}, _ string, _ ...interface{}) error {
|
||||
return db.ErrDatabaseOp{Err: sql.ErrNoRows}
|
||||
}
|
||||
|
||||
func (s notFoundSelector) WithContext(context.Context) gorp.SqlExecutor {
|
||||
return s
|
||||
}
|
||||
|
||||
// echoSA always returns the given revocation status.
|
||||
type echoSA struct {
|
||||
mocks.StorageAuthorityReadOnly
|
||||
|
|
|
@ -5,8 +5,8 @@ import (
|
|||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/go-gorp/gorp/v3"
|
||||
"github.com/go-sql-driver/mysql"
|
||||
"github.com/letsencrypt/borp"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
|
||||
"github.com/letsencrypt/boulder/cmd"
|
||||
|
@ -45,7 +45,7 @@ type DbSettings struct {
|
|||
ConnMaxIdleTime time.Duration
|
||||
}
|
||||
|
||||
// InitWrappedDb constructs a wrapped gorp mapping object with the provided
|
||||
// InitWrappedDb constructs a wrapped borp mapping object with the provided
|
||||
// settings. If scope is non-nil, Prometheus metrics will be exported. If logger
|
||||
// is non-nil, SQL debug-level logging will be enabled. The only required parameter
|
||||
// is config.
|
||||
|
@ -82,7 +82,7 @@ func InitWrappedDb(config cmd.DBConfig, scope prometheus.Registerer, logger blog
|
|||
}
|
||||
|
||||
if scope != nil {
|
||||
err = initDBMetrics(dbMap.Db, scope, settings, addr, user)
|
||||
err = initDBMetrics(dbMap.SQLDb(), scope, settings, addr, user)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("while initializing metrics: %w", err)
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ func InitWrappedDb(config cmd.DBConfig, scope prometheus.Registerer, logger blog
|
|||
return dbMap, nil
|
||||
}
|
||||
|
||||
// DBMapForTest creates a wrapped root gorp mapping object. Create one of these for
|
||||
// DBMapForTest creates a wrapped root borp 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.
|
||||
|
@ -146,7 +146,7 @@ var setConnMaxIdleTime = func(db *sql.DB, connMaxIdleTime time.Duration) {
|
|||
//
|
||||
// This function also:
|
||||
// - pings the database (and errors if it's unreachable)
|
||||
// - wraps the connection in a gorp.DbMap so we can use the handy Get/Insert methods gorp provides
|
||||
// - wraps the connection in a borp.DbMap so we can use the handy Get/Insert methods borp provides
|
||||
// - wraps that in a db.WrappedMap to get more useful error messages
|
||||
func newDbMapFromMySQLConfig(config *mysql.Config, settings DbSettings) (*boulderDB.WrappedMap, error) {
|
||||
err := adjustMySQLConfig(config)
|
||||
|
@ -166,11 +166,11 @@ func newDbMapFromMySQLConfig(config *mysql.Config, settings DbSettings) (*boulde
|
|||
setConnMaxLifetime(db, settings.ConnMaxLifetime)
|
||||
setConnMaxIdleTime(db, settings.ConnMaxIdleTime)
|
||||
|
||||
dialect := gorp.MySQLDialect{Engine: "InnoDB", Encoding: "UTF8"}
|
||||
dbmap := &gorp.DbMap{Db: db, Dialect: dialect, TypeConverter: BoulderTypeConverter{}}
|
||||
dialect := borp.MySQLDialect{Engine: "InnoDB", Encoding: "UTF8"}
|
||||
dbmap := &borp.DbMap{Db: db, Dialect: dialect, TypeConverter: BoulderTypeConverter{}}
|
||||
|
||||
initTables(dbmap)
|
||||
return &boulderDB.WrappedMap{DbMap: dbmap}, nil
|
||||
return boulderDB.NewWrappedMap(dbmap), nil
|
||||
}
|
||||
|
||||
// adjustMySQLConfig sets certain flags that we want on every connection.
|
||||
|
@ -239,17 +239,17 @@ func adjustMySQLConfig(conf *mysql.Config) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// SetSQLDebug enables GORP SQL-level Debugging
|
||||
// SetSQLDebug enables borp SQL-level Debugging
|
||||
func SetSQLDebug(dbMap *boulderDB.WrappedMap, log blog.Logger) {
|
||||
dbMap.TraceOn("SQL: ", &SQLLogger{log})
|
||||
dbMap.BorpDB().TraceOn("SQL: ", &SQLLogger{log})
|
||||
}
|
||||
|
||||
// SQLLogger adapts the Boulder Logger to a format GORP can use.
|
||||
// SQLLogger adapts the Boulder Logger to a format borp can use.
|
||||
type SQLLogger struct {
|
||||
blog.Logger
|
||||
}
|
||||
|
||||
// Printf adapts the AuditLogger to GORP's interface
|
||||
// Printf adapts the AuditLogger to borp's interface
|
||||
func (log *SQLLogger) Printf(format string, v ...interface{}) {
|
||||
log.Debugf(format, v...)
|
||||
}
|
||||
|
@ -259,8 +259,8 @@ func (log *SQLLogger) Printf(format string, v ...interface{}) {
|
|||
// it is very important to declare them as a such here. It produces a side
|
||||
// effect in Insert() where the inserted object has its id field set to the
|
||||
// autoincremented value that resulted from the insert. See
|
||||
// https://godoc.org/github.com/coopernurse/gorp#DbMap.Insert
|
||||
func initTables(dbMap *gorp.DbMap) {
|
||||
// https://godoc.org/github.com/coopernurse/borp#DbMap.Insert
|
||||
func initTables(dbMap *borp.DbMap) {
|
||||
regTable := dbMap.AddTableWithName(regModel{}, "registrations").SetKeys(true, "ID")
|
||||
|
||||
regTable.SetVersionCol("LockCol")
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package sa
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"os"
|
||||
|
@ -134,7 +135,7 @@ func TestStrictness(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = dbMap.Exec(`insert into orderToAuthz2 set
|
||||
_, err = dbMap.ExecContext(ctx, `insert into orderToAuthz2 set
|
||||
orderID=999999999999999999999999999,
|
||||
authzID=999999999999999999999999999;`)
|
||||
if err == nil {
|
||||
|
@ -153,7 +154,7 @@ func TestTimeouts(t *testing.T) {
|
|||
// SLEEP is defined to return 1 if it was interrupted, but we want to actually
|
||||
// get an error to simulate what would happen with a slow query. So we wrap
|
||||
// the SLEEP in a subselect.
|
||||
_, err = dbMap.Exec(`SELECT 1 FROM (SELECT SLEEP(5)) as subselect;`)
|
||||
_, err = dbMap.ExecContext(ctx, `SELECT 1 FROM (SELECT SLEEP(5)) as subselect;`)
|
||||
if err == nil {
|
||||
t.Fatal("Expected error when running slow query, got none.")
|
||||
}
|
||||
|
@ -175,6 +176,7 @@ func TestAutoIncrementSchema(t *testing.T) {
|
|||
|
||||
var count int64
|
||||
err = dbMap.SelectOne(
|
||||
context.Background(),
|
||||
&count,
|
||||
`SELECT COUNT(*) FROM columns WHERE
|
||||
table_schema LIKE 'boulder%' AND
|
||||
|
|
75
sa/model.go
75
sa/model.go
|
@ -64,9 +64,9 @@ const regFields = "id, jwk, jwk_sha256, contact, agreement, initialIP, createdAt
|
|||
// ClearEmail removes the provided email address from one specified registration. If
|
||||
// there are multiple email addresses present, it does not modify other ones. If the email
|
||||
// address is not present, it does not modify the registration and will return a nil error.
|
||||
func ClearEmail(dbMap db.DatabaseMap, ctx context.Context, regID int64, email string) error {
|
||||
_, overallError := db.WithTransaction(ctx, dbMap, func(txWithCtx db.Executor) (interface{}, error) {
|
||||
curr, err := selectRegistration(txWithCtx, "id", regID)
|
||||
func ClearEmail(ctx context.Context, dbMap db.DatabaseMap, regID int64, email string) error {
|
||||
_, overallError := db.WithTransaction(ctx, dbMap, func(tx db.Executor) (interface{}, error) {
|
||||
curr, err := selectRegistration(ctx, tx, "id", regID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ func ClearEmail(dbMap db.DatabaseMap, ctx context.Context, regID int64, email st
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return txWithCtx.Update(newModel)
|
||||
return tx.Update(ctx, newModel)
|
||||
})
|
||||
if overallError != nil {
|
||||
return overallError
|
||||
|
@ -104,13 +104,14 @@ func ClearEmail(dbMap db.DatabaseMap, ctx context.Context, regID int64, email st
|
|||
}
|
||||
|
||||
// selectRegistration selects all fields of one registration model
|
||||
func selectRegistration(s db.OneSelector, whereCol string, args ...interface{}) (*regModel, error) {
|
||||
func selectRegistration(ctx context.Context, s db.OneSelector, whereCol string, args ...interface{}) (*regModel, error) {
|
||||
if whereCol != "id" && whereCol != "jwk_sha256" {
|
||||
return nil, fmt.Errorf("column name %q invalid for registrations table WHERE clause", whereCol)
|
||||
}
|
||||
|
||||
var model regModel
|
||||
err := s.SelectOne(
|
||||
ctx,
|
||||
&model,
|
||||
"SELECT "+regFields+" FROM registrations WHERE "+whereCol+" = ? LIMIT 1",
|
||||
args...,
|
||||
|
@ -123,9 +124,10 @@ const certFields = "registrationID, serial, digest, der, issued, expires"
|
|||
// SelectCertificate selects all fields of one certificate object identified by
|
||||
// a serial. If more than one row contains the same serial only the first is
|
||||
// returned.
|
||||
func SelectCertificate(s db.OneSelector, serial string) (core.Certificate, error) {
|
||||
func SelectCertificate(ctx context.Context, s db.OneSelector, serial string) (core.Certificate, error) {
|
||||
var model core.Certificate
|
||||
err := s.SelectOne(
|
||||
ctx,
|
||||
&model,
|
||||
"SELECT "+certFields+" FROM certificates WHERE serial = ? LIMIT 1",
|
||||
serial,
|
||||
|
@ -137,9 +139,10 @@ const precertFields = "registrationID, serial, der, issued, expires"
|
|||
|
||||
// SelectPrecertificate selects all fields of one precertificate object
|
||||
// identified by serial.
|
||||
func SelectPrecertificate(s db.OneSelector, serial string) (core.Certificate, error) {
|
||||
func SelectPrecertificate(ctx context.Context, s db.OneSelector, serial string) (core.Certificate, error) {
|
||||
var model precertificateModel
|
||||
err := s.SelectOne(
|
||||
ctx,
|
||||
&model,
|
||||
"SELECT "+precertFields+" FROM precertificates WHERE serial = ? LIMIT 1",
|
||||
serial)
|
||||
|
@ -158,18 +161,20 @@ type CertWithID struct {
|
|||
}
|
||||
|
||||
// SelectCertificates selects all fields of multiple certificate objects
|
||||
func SelectCertificates(s db.Selector, q string, args map[string]interface{}) ([]CertWithID, error) {
|
||||
func SelectCertificates(ctx context.Context, s db.Selector, q string, args map[string]interface{}) ([]CertWithID, error) {
|
||||
var models []CertWithID
|
||||
_, err := s.Select(
|
||||
ctx,
|
||||
&models,
|
||||
"SELECT id, "+certFields+" FROM certificates "+q, args)
|
||||
return models, err
|
||||
}
|
||||
|
||||
// SelectPrecertificates selects all fields of multiple precertificate objects.
|
||||
func SelectPrecertificates(s db.Selector, q string, args map[string]interface{}) ([]CertWithID, error) {
|
||||
func SelectPrecertificates(ctx context.Context, s db.Selector, q string, args map[string]interface{}) ([]CertWithID, error) {
|
||||
var models []CertWithID
|
||||
_, err := s.Select(
|
||||
ctx,
|
||||
&models,
|
||||
"SELECT id, "+precertFields+" FROM precertificates "+q, args)
|
||||
return models, err
|
||||
|
@ -192,9 +197,10 @@ const certStatusFields = "id, serial, status, ocspLastUpdated, revokedDate, revo
|
|||
|
||||
// SelectCertificateStatus selects all fields of one certificate status model
|
||||
// identified by serial
|
||||
func SelectCertificateStatus(s db.OneSelector, serial string) (core.CertificateStatus, error) {
|
||||
func SelectCertificateStatus(ctx context.Context, s db.OneSelector, serial string) (core.CertificateStatus, error) {
|
||||
var model core.CertificateStatus
|
||||
err := s.SelectOne(
|
||||
ctx,
|
||||
&model,
|
||||
"SELECT "+certStatusFields+" FROM certificateStatus WHERE serial = ? LIMIT 1",
|
||||
serial,
|
||||
|
@ -213,9 +219,10 @@ type RevocationStatusModel struct {
|
|||
|
||||
// SelectRevocationStatus returns the authoritative revocation information for
|
||||
// the certificate with the given serial.
|
||||
func SelectRevocationStatus(s db.OneSelector, serial string) (*sapb.RevocationStatus, error) {
|
||||
func SelectRevocationStatus(ctx context.Context, s db.OneSelector, serial string) (*sapb.RevocationStatus, error) {
|
||||
var model RevocationStatusModel
|
||||
err := s.SelectOne(
|
||||
ctx,
|
||||
&model,
|
||||
"SELECT status, revokedDate, revokedReason FROM certificateStatus WHERE serial = ? LIMIT 1",
|
||||
serial,
|
||||
|
@ -560,6 +567,7 @@ func rehydrateHostPort(vr *core.ValidationRecord) error {
|
|||
// the past. If the stored authz has a valid status, it is returned with a
|
||||
// valid status regardless of whether it is also expired.
|
||||
func SelectAuthzsMatchingIssuance(
|
||||
ctx context.Context,
|
||||
s db.Selector,
|
||||
regID int64,
|
||||
issued time.Time,
|
||||
|
@ -588,7 +596,7 @@ func SelectAuthzsMatchingIssuance(
|
|||
}
|
||||
|
||||
var authzModels []authzModel
|
||||
_, err := s.Select(&authzModels, query, args...)
|
||||
_, err := s.Select(ctx, &authzModels, query, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -885,8 +893,8 @@ type orderFQDNSet struct {
|
|||
Expires time.Time
|
||||
}
|
||||
|
||||
func addFQDNSet(db db.Inserter, names []string, serial string, issued time.Time, expires time.Time) error {
|
||||
return db.Insert(&core.FQDNSet{
|
||||
func addFQDNSet(ctx context.Context, db db.Inserter, names []string, serial string, issued time.Time, expires time.Time) error {
|
||||
return db.Insert(ctx, &core.FQDNSet{
|
||||
SetHash: HashNames(names),
|
||||
Serial: serial,
|
||||
Issued: issued,
|
||||
|
@ -899,12 +907,13 @@ func addFQDNSet(db db.Inserter, names []string, serial string, issued time.Time,
|
|||
// addition can take place within the order addition transaction. The caller is
|
||||
// required to rollback the transaction if an error is returned.
|
||||
func addOrderFQDNSet(
|
||||
ctx context.Context,
|
||||
db db.Inserter,
|
||||
names []string,
|
||||
orderID int64,
|
||||
regID int64,
|
||||
expires time.Time) error {
|
||||
return db.Insert(&orderFQDNSet{
|
||||
return db.Insert(ctx, &orderFQDNSet{
|
||||
SetHash: HashNames(names),
|
||||
OrderID: orderID,
|
||||
RegistrationID: regID,
|
||||
|
@ -917,10 +926,11 @@ func addOrderFQDNSet(
|
|||
// take place within the finalization transaction. The caller is required to
|
||||
// rollback the transaction if an error is returned.
|
||||
func deleteOrderFQDNSet(
|
||||
ctx context.Context,
|
||||
db db.Execer,
|
||||
orderID int64) error {
|
||||
|
||||
result, err := db.Exec(`
|
||||
result, err := db.ExecContext(ctx, `
|
||||
DELETE FROM orderFqdnSets
|
||||
WHERE orderID = ?`,
|
||||
orderID)
|
||||
|
@ -940,7 +950,7 @@ func deleteOrderFQDNSet(
|
|||
return nil
|
||||
}
|
||||
|
||||
func addIssuedNames(queryer db.Queryer, cert *x509.Certificate, isRenewal bool) error {
|
||||
func addIssuedNames(ctx context.Context, queryer db.Queryer, cert *x509.Certificate, isRenewal bool) error {
|
||||
if len(cert.DNSNames) == 0 {
|
||||
return berrors.InternalServerError("certificate has no DNSNames")
|
||||
}
|
||||
|
@ -960,11 +970,11 @@ func addIssuedNames(queryer db.Queryer, cert *x509.Certificate, isRenewal bool)
|
|||
return err
|
||||
}
|
||||
}
|
||||
_, err = multiInserter.Insert(queryer)
|
||||
_, err = multiInserter.Insert(ctx, queryer)
|
||||
return err
|
||||
}
|
||||
|
||||
func addKeyHash(db db.Inserter, cert *x509.Certificate) error {
|
||||
func addKeyHash(ctx context.Context, db db.Inserter, cert *x509.Certificate) error {
|
||||
if cert.RawSubjectPublicKeyInfo == nil {
|
||||
return errors.New("certificate has a nil RawSubjectPublicKeyInfo")
|
||||
}
|
||||
|
@ -974,7 +984,7 @@ func addKeyHash(db db.Inserter, cert *x509.Certificate) error {
|
|||
CertNotAfter: cert.NotAfter,
|
||||
CertSerial: core.SerialToString(cert.SerialNumber),
|
||||
}
|
||||
return db.Insert(khm)
|
||||
return db.Insert(ctx, khm)
|
||||
}
|
||||
|
||||
var blockedKeysColumns = "keyHash, added, source, comment"
|
||||
|
@ -992,9 +1002,8 @@ var blockedKeysColumns = "keyHash, added, source, comment"
|
|||
// - If all of the order's authorizations are valid, and we haven't begun
|
||||
// processing, then the order is status ready.
|
||||
//
|
||||
// An error is returned for any other case. It assumes that the provided
|
||||
// database selector already has a context associated with it.
|
||||
func statusForOrder(s db.Selector, order *corepb.Order, now time.Time) (string, error) {
|
||||
// An error is returned for any other case.
|
||||
func statusForOrder(ctx context.Context, s db.Selector, order *corepb.Order, now time.Time) (string, error) {
|
||||
// Without any further work we know an order with an error is invalid
|
||||
if order.Error != nil {
|
||||
return string(core.StatusInvalid), nil
|
||||
|
@ -1013,7 +1022,7 @@ func statusForOrder(s db.Selector, order *corepb.Order, now time.Time) (string,
|
|||
}
|
||||
|
||||
// Get the full Authorization objects for the order
|
||||
authzValidityInfo, err := getAuthorizationStatuses(s, order.V2Authorizations)
|
||||
authzValidityInfo, err := getAuthorizationStatuses(ctx, s, order.V2Authorizations)
|
||||
// If there was an error getting the authorizations, return it immediately
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
@ -1109,9 +1118,8 @@ type authzValidity struct {
|
|||
}
|
||||
|
||||
// getAuthorizationStatuses takes a sequence of authz IDs, and returns the
|
||||
// status and expiration date of each of them. It assumes that the provided
|
||||
// database selector already has a context associated with it.
|
||||
func getAuthorizationStatuses(s db.Selector, ids []int64) ([]authzValidity, error) {
|
||||
// status and expiration date of each of them.
|
||||
func getAuthorizationStatuses(ctx context.Context, s db.Selector, ids []int64) ([]authzValidity, error) {
|
||||
var params []interface{}
|
||||
for _, id := range ids {
|
||||
params = append(params, id)
|
||||
|
@ -1121,6 +1129,7 @@ func getAuthorizationStatuses(s db.Selector, ids []int64) ([]authzValidity, erro
|
|||
Expires time.Time
|
||||
}
|
||||
_, err := s.Select(
|
||||
ctx,
|
||||
&validityInfo,
|
||||
fmt.Sprintf("SELECT status, expires FROM authz2 WHERE id IN (%s)",
|
||||
db.QuestionMarks(len(ids))),
|
||||
|
@ -1140,11 +1149,11 @@ func getAuthorizationStatuses(s db.Selector, ids []int64) ([]authzValidity, erro
|
|||
return allAuthzValidity, nil
|
||||
}
|
||||
|
||||
// authzForOrder retrieves the authorization IDs for an order. It assumes that
|
||||
// the provided database selector already has a context associated with it.
|
||||
func authzForOrder(s db.Selector, orderID int64) ([]int64, error) {
|
||||
// authzForOrder retrieves the authorization IDs for an order.
|
||||
func authzForOrder(ctx context.Context, s db.Selector, orderID int64) ([]int64, error) {
|
||||
var v2IDs []int64
|
||||
_, err := s.Select(
|
||||
ctx,
|
||||
&v2IDs,
|
||||
"SELECT authzID FROM orderToAuthz2 WHERE orderID = ?",
|
||||
orderID,
|
||||
|
@ -1153,11 +1162,11 @@ func authzForOrder(s db.Selector, orderID int64) ([]int64, error) {
|
|||
}
|
||||
|
||||
// namesForOrder finds all of the requested names associated with an order. The
|
||||
// names are returned in their reversed form (see `sa.ReverseName`). It assumes
|
||||
// that the provided database selector already has a context associated with it.
|
||||
func namesForOrder(s db.Selector, orderID int64) ([]string, error) {
|
||||
// names are returned in their reversed form (see `sa.ReverseName`).
|
||||
func namesForOrder(ctx context.Context, s db.Selector, orderID int64) ([]string, error) {
|
||||
var reversedNames []string
|
||||
_, err := s.Select(
|
||||
ctx,
|
||||
&reversedNames,
|
||||
`SELECT reversedName
|
||||
FROM requestedNames
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package sa
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
|
@ -278,33 +279,35 @@ func TestPopulateAttemptedFieldsBadJSON(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCertificatesTableContainsDuplicateSerials(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
sa, fc, cleanUp := initSA(t)
|
||||
defer cleanUp()
|
||||
|
||||
serialString := core.SerialToString(big.NewInt(1337))
|
||||
|
||||
// Insert a certificate with a serial of `1337`.
|
||||
err := insertCertificate(sa.dbMap, fc, "1337.com", "leet", 1337, 1)
|
||||
err := insertCertificate(ctx, sa.dbMap, fc, "1337.com", "leet", 1337, 1)
|
||||
test.AssertNotError(t, err, "couldn't insert valid certificate")
|
||||
|
||||
// This should return the certificate that we just inserted.
|
||||
certA, err := SelectCertificate(sa.dbMap, serialString)
|
||||
certA, err := SelectCertificate(ctx, sa.dbMap, serialString)
|
||||
test.AssertNotError(t, err, "received an error for a valid query")
|
||||
|
||||
// Insert a certificate with a serial of `1337` but for a different
|
||||
// hostname.
|
||||
err = insertCertificate(sa.dbMap, fc, "1337.net", "leet", 1337, 1)
|
||||
err = insertCertificate(ctx, sa.dbMap, fc, "1337.net", "leet", 1337, 1)
|
||||
test.AssertNotError(t, err, "couldn't insert valid certificate")
|
||||
|
||||
// Despite a duplicate being present, this shouldn't error.
|
||||
certB, err := SelectCertificate(sa.dbMap, serialString)
|
||||
certB, err := SelectCertificate(ctx, sa.dbMap, serialString)
|
||||
test.AssertNotError(t, err, "received an error for a valid query")
|
||||
|
||||
// Ensure that `certA` and `certB` are the same.
|
||||
test.AssertByteEquals(t, certA.DER, certB.DER)
|
||||
}
|
||||
|
||||
func insertCertificate(dbMap *db.WrappedMap, fc clock.FakeClock, hostname, cn string, serial, regID int64) error {
|
||||
func insertCertificate(ctx context.Context, dbMap *db.WrappedMap, fc clock.FakeClock, hostname, cn string, serial, regID int64) error {
|
||||
serialBigInt := big.NewInt(serial)
|
||||
serialString := core.SerialToString(serialBigInt)
|
||||
|
||||
|
@ -325,7 +328,7 @@ func insertCertificate(dbMap *db.WrappedMap, fc clock.FakeClock, hostname, cn st
|
|||
Expires: template.NotAfter,
|
||||
DER: certDer,
|
||||
}
|
||||
err := dbMap.Insert(cert)
|
||||
err := dbMap.Insert(ctx, cert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -349,12 +352,14 @@ func makeKey() rsa.PrivateKey {
|
|||
}
|
||||
|
||||
func TestIncidentSerialModel(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
testIncidentsDbMap, err := DBMapForTest(vars.DBConnIncidentsFullPerms)
|
||||
test.AssertNotError(t, err, "Couldn't create test dbMap")
|
||||
defer test.ResetIncidentsTestDatabase(t)
|
||||
|
||||
// Inserting and retrieving a row with only the serial populated should work.
|
||||
_, err = testIncidentsDbMap.Exec(
|
||||
_, err = testIncidentsDbMap.ExecContext(ctx,
|
||||
"INSERT INTO incident_foo (serial) VALUES (?)",
|
||||
"1337",
|
||||
)
|
||||
|
@ -362,6 +367,7 @@ func TestIncidentSerialModel(t *testing.T) {
|
|||
|
||||
var res1 incidentSerialModel
|
||||
err = testIncidentsDbMap.SelectOne(
|
||||
ctx,
|
||||
&res1,
|
||||
"SELECT * FROM incident_foo WHERE serial = ?",
|
||||
"1337",
|
||||
|
@ -374,7 +380,7 @@ func TestIncidentSerialModel(t *testing.T) {
|
|||
test.AssertBoxedNil(t, res1.LastNoticeSent, "lastNoticeSent should be NULL")
|
||||
|
||||
// Inserting and retrieving a row with all columns populated should work.
|
||||
_, err = testIncidentsDbMap.Exec(
|
||||
_, err = testIncidentsDbMap.ExecContext(ctx,
|
||||
"INSERT INTO incident_foo (serial, registrationID, orderID, lastNoticeSent) VALUES (?, ?, ?, ?)",
|
||||
"1338",
|
||||
1,
|
||||
|
@ -385,6 +391,7 @@ func TestIncidentSerialModel(t *testing.T) {
|
|||
|
||||
var res2 incidentSerialModel
|
||||
err = testIncidentsDbMap.SelectOne(
|
||||
ctx,
|
||||
&res2,
|
||||
"SELECT * FROM incident_foo WHERE serial = ?",
|
||||
"1338",
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package sa
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -29,9 +30,8 @@ func baseDomain(name string) string {
|
|||
|
||||
// addCertificatesPerName adds 1 to the rate limit count for the provided
|
||||
// domains, in a specific time bucket. It must be executed in a transaction, and
|
||||
// the input timeToTheHour must be a time rounded to an hour. It assumes that
|
||||
// the given db already has a context associated with it.
|
||||
func (ssa *SQLStorageAuthority) addCertificatesPerName(db db.SelectExecer, names []string, timeToTheHour time.Time) error {
|
||||
// the input timeToTheHour must be a time rounded to an hour.
|
||||
func (ssa *SQLStorageAuthority) addCertificatesPerName(ctx context.Context, db db.SelectExecer, names []string, timeToTheHour time.Time) error {
|
||||
// De-duplicate the base domains.
|
||||
baseDomainsMap := make(map[string]bool)
|
||||
var qmarks []string
|
||||
|
@ -45,7 +45,7 @@ func (ssa *SQLStorageAuthority) addCertificatesPerName(db db.SelectExecer, names
|
|||
}
|
||||
}
|
||||
|
||||
_, err := db.Exec(`INSERT INTO certificatesPerName (eTLDPlusOne, time, count) VALUES `+
|
||||
_, err := db.ExecContext(ctx, `INSERT INTO certificatesPerName (eTLDPlusOne, time, count) VALUES `+
|
||||
strings.Join(qmarks, ", ")+` ON DUPLICATE KEY UPDATE count=count+1;`,
|
||||
values...)
|
||||
if err != nil {
|
||||
|
@ -56,15 +56,15 @@ func (ssa *SQLStorageAuthority) addCertificatesPerName(db db.SelectExecer, names
|
|||
}
|
||||
|
||||
// countCertificates returns the count of certificates issued for a domain's
|
||||
// eTLD+1 (aka base domain), during a given time range. It assumes that the
|
||||
// given db already has a context associated with it.
|
||||
func (ssa *SQLStorageAuthorityRO) countCertificates(dbMap db.Selector, domain string, timeRange *sapb.Range) (int64, time.Time, error) {
|
||||
// eTLD+1 (aka base domain), during a given time range.
|
||||
func (ssa *SQLStorageAuthorityRO) countCertificates(ctx context.Context, dbMap db.Selector, domain string, timeRange *sapb.Range) (int64, time.Time, error) {
|
||||
latest := time.Unix(0, timeRange.Latest)
|
||||
var results []struct {
|
||||
Count int64
|
||||
Time time.Time
|
||||
}
|
||||
_, err := dbMap.Select(
|
||||
ctx,
|
||||
&results,
|
||||
`SELECT count, time FROM certificatesPerName
|
||||
WHERE eTLDPlusOne = :baseDomain AND
|
||||
|
@ -100,10 +100,9 @@ func (ssa *SQLStorageAuthorityRO) countCertificates(dbMap db.Selector, domain st
|
|||
|
||||
// addNewOrdersRateLimit adds 1 to the rate limit count for the provided ID, in
|
||||
// a specific time bucket. It must be executed in a transaction, and the input
|
||||
// timeToTheMinute must be a time rounded to a minute. It assumes that the given
|
||||
// db already has a context associated with it.
|
||||
func addNewOrdersRateLimit(dbMap db.SelectExecer, regID int64, timeToTheMinute time.Time) error {
|
||||
_, err := dbMap.Exec(`INSERT INTO newOrdersRL
|
||||
// timeToTheMinute must be a time rounded to a minute.
|
||||
func addNewOrdersRateLimit(ctx context.Context, dbMap db.SelectExecer, regID int64, timeToTheMinute time.Time) error {
|
||||
_, err := dbMap.ExecContext(ctx, `INSERT INTO newOrdersRL
|
||||
(regID, time, count)
|
||||
VALUES (?, ?, 1)
|
||||
ON DUPLICATE KEY UPDATE count=count+1;`,
|
||||
|
@ -117,11 +116,11 @@ func addNewOrdersRateLimit(dbMap db.SelectExecer, regID int64, timeToTheMinute t
|
|||
}
|
||||
|
||||
// countNewOrders returns the count of orders created in the given time range
|
||||
// for the given registration ID. It assumes that the given db already has a
|
||||
// context associated with it.
|
||||
func countNewOrders(dbMap db.Selector, req *sapb.CountOrdersRequest) (*sapb.Count, error) {
|
||||
// for the given registration ID.
|
||||
func countNewOrders(ctx context.Context, dbMap db.Selector, req *sapb.CountOrdersRequest) (*sapb.Count, error) {
|
||||
var counts []int64
|
||||
_, err := dbMap.Select(
|
||||
ctx,
|
||||
&counts,
|
||||
`SELECT count FROM newOrdersRL
|
||||
WHERE regID = :regID AND
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package sa
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -10,6 +11,8 @@ import (
|
|||
)
|
||||
|
||||
func TestCertsPerNameRateLimitTable(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
sa, _, cleanUp := initSA(t)
|
||||
defer cleanUp()
|
||||
|
||||
|
@ -41,11 +44,11 @@ func TestCertsPerNameRateLimitTable(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, input := range inputs {
|
||||
tx, err := sa.dbMap.Begin()
|
||||
tx, err := sa.dbMap.BeginTx(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = sa.addCertificatesPerName(tx, input.names, input.time)
|
||||
err = sa.addCertificatesPerName(ctx, tx, input.names, input.time)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -78,7 +81,7 @@ func TestCertsPerNameRateLimitTable(t *testing.T) {
|
|||
Earliest: aprilFirst.Add(-1 * time.Second).UnixNano(),
|
||||
Latest: aprilFirst.Add(aWeek).UnixNano(),
|
||||
}
|
||||
count, earliest, err := sa.countCertificatesByName(sa.dbMap, tc.domainName, timeRange)
|
||||
count, earliest, err := sa.countCertificatesByName(ctx, sa.dbMap, tc.domainName, timeRange)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -110,27 +113,27 @@ func TestNewOrdersRateLimitTable(t *testing.T) {
|
|||
}
|
||||
|
||||
for i := 0; i <= 10; i++ {
|
||||
tx, err := sa.dbMap.Begin()
|
||||
tx, err := sa.dbMap.BeginTx(ctx)
|
||||
test.AssertNotError(t, err, "failed to open tx")
|
||||
for j := 0; j < i+1; j++ {
|
||||
err = addNewOrdersRateLimit(tx, manyCountRegID, start.Add(time.Minute*time.Duration(i)))
|
||||
err = addNewOrdersRateLimit(ctx, tx, manyCountRegID, start.Add(time.Minute*time.Duration(i)))
|
||||
}
|
||||
test.AssertNotError(t, err, "addNewOrdersRateLimit failed")
|
||||
test.AssertNotError(t, tx.Commit(), "failed to commit tx")
|
||||
}
|
||||
|
||||
count, err := countNewOrders(sa.dbMap, req)
|
||||
count, err := countNewOrders(ctx, sa.dbMap, req)
|
||||
test.AssertNotError(t, err, "countNewOrders failed")
|
||||
test.AssertEquals(t, count.Count, int64(0))
|
||||
|
||||
req.AccountID = manyCountRegID
|
||||
count, err = countNewOrders(sa.dbMap, req)
|
||||
count, err = countNewOrders(ctx, sa.dbMap, req)
|
||||
test.AssertNotError(t, err, "countNewOrders failed")
|
||||
test.AssertEquals(t, count.Count, int64(65))
|
||||
|
||||
req.Range.Earliest = start.Add(time.Minute * 5).UnixNano()
|
||||
req.Range.Latest = start.Add(time.Minute * 10).UnixNano()
|
||||
count, err = countNewOrders(sa.dbMap, req)
|
||||
count, err = countNewOrders(ctx, sa.dbMap, req)
|
||||
test.AssertNotError(t, err, "countNewOrders failed")
|
||||
test.AssertEquals(t, count.Count, int64(45))
|
||||
}
|
||||
|
|
102
sa/sa.go
102
sa/sa.go
|
@ -107,7 +107,7 @@ func (ssa *SQLStorageAuthority) NewRegistration(ctx context.Context, req *corepb
|
|||
|
||||
reg.CreatedAt = ssa.clk.Now()
|
||||
|
||||
err = ssa.dbMap.WithContext(ctx).Insert(reg)
|
||||
err = ssa.dbMap.Insert(ctx, reg)
|
||||
if err != nil {
|
||||
if db.IsDuplicate(err) {
|
||||
// duplicate entry error can only happen when jwk_sha256 collides, indicate
|
||||
|
@ -125,7 +125,7 @@ func (ssa *SQLStorageAuthority) UpdateRegistration(ctx context.Context, req *cor
|
|||
return nil, errIncompleteRequest
|
||||
}
|
||||
|
||||
curr, err := selectRegistration(ssa.dbMap.WithContext(ctx), "id", req.Id)
|
||||
curr, err := selectRegistration(ctx, ssa.dbMap, "id", req.Id)
|
||||
if err != nil {
|
||||
if db.IsNoRows(err) {
|
||||
return nil, berrors.NotFoundError("registration with ID '%d' not found", req.Id)
|
||||
|
@ -141,7 +141,7 @@ func (ssa *SQLStorageAuthority) UpdateRegistration(ctx context.Context, req *cor
|
|||
// Copy the existing registration model's LockCol to the new updated
|
||||
// registration model's LockCol
|
||||
update.LockCol = curr.LockCol
|
||||
n, err := ssa.dbMap.WithContext(ctx).Update(update)
|
||||
n, err := ssa.dbMap.Update(ctx, update)
|
||||
if err != nil {
|
||||
if db.IsDuplicate(err) {
|
||||
// duplicate entry error can only happen when jwk_sha256 collides, indicate
|
||||
|
@ -162,7 +162,7 @@ func (ssa *SQLStorageAuthority) AddSerial(ctx context.Context, req *sapb.AddSeri
|
|||
if req.Serial == "" || req.RegID == 0 || req.Created == 0 || req.Expires == 0 {
|
||||
return nil, errIncompleteRequest
|
||||
}
|
||||
err := ssa.dbMap.WithContext(ctx).Insert(&recordedSerialModel{
|
||||
err := ssa.dbMap.Insert(ctx, &recordedSerialModel{
|
||||
Serial: req.Serial,
|
||||
RegistrationID: req.RegID,
|
||||
Created: time.Unix(0, req.Created),
|
||||
|
@ -177,7 +177,7 @@ func (ssa *SQLStorageAuthority) AddSerial(ctx context.Context, req *sapb.AddSeri
|
|||
// SetCertificateStatusReady changes a serial's OCSP status from core.OCSPStatusNotReady to core.OCSPStatusGood.
|
||||
// Called when precertificate issuance succeeds. returns an error if the serial doesn't have status core.OCSPStatusNotReady.
|
||||
func (ssa *SQLStorageAuthority) SetCertificateStatusReady(ctx context.Context, req *sapb.Serial) (*emptypb.Empty, error) {
|
||||
res, err := ssa.dbMap.WithContext(ctx).Exec(
|
||||
res, err := ssa.dbMap.ExecContext(ctx,
|
||||
`UPDATE certificateStatus
|
||||
SET status = ?
|
||||
WHERE status = ? AND
|
||||
|
@ -222,12 +222,12 @@ func (ssa *SQLStorageAuthority) AddPrecertificate(ctx context.Context, req *sapb
|
|||
Expires: parsed.NotAfter,
|
||||
}
|
||||
|
||||
_, overallError := db.WithTransaction(ctx, ssa.dbMap, func(txWithCtx db.Executor) (interface{}, error) {
|
||||
_, overallError := db.WithTransaction(ctx, ssa.dbMap, func(tx db.Executor) (interface{}, error) {
|
||||
// Select to see if precert exists
|
||||
var row struct {
|
||||
Count int64
|
||||
}
|
||||
err := txWithCtx.SelectOne(&row, "SELECT COUNT(*) as count FROM precertificates WHERE serial=?", serialHex)
|
||||
err := tx.SelectOne(ctx, &row, "SELECT COUNT(*) as count FROM precertificates WHERE serial=?", serialHex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -235,7 +235,7 @@ func (ssa *SQLStorageAuthority) AddPrecertificate(ctx context.Context, req *sapb
|
|||
return nil, berrors.DuplicateError("cannot add a duplicate cert")
|
||||
}
|
||||
|
||||
err = txWithCtx.Insert(preCertModel)
|
||||
err = tx.Insert(ctx, preCertModel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -255,7 +255,7 @@ func (ssa *SQLStorageAuthority) AddPrecertificate(ctx context.Context, req *sapb
|
|||
IsExpired: false,
|
||||
IssuerNameID: req.IssuerNameID,
|
||||
}
|
||||
err = ssa.dbMap.WithContext(ctx).Insert(cs)
|
||||
err = ssa.dbMap.Insert(ctx, cs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -267,18 +267,19 @@ func (ssa *SQLStorageAuthority) AddPrecertificate(ctx context.Context, req *sapb
|
|||
// would be a misissuance and miscalculating whether the cert is a renewal or
|
||||
// not for the purpose of rate limiting is the least of our troubles.
|
||||
isRenewal, err := ssa.checkFQDNSetExists(
|
||||
txWithCtx.SelectOne,
|
||||
ctx,
|
||||
tx.SelectOne,
|
||||
parsed.DNSNames)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = addIssuedNames(txWithCtx, parsed, isRenewal)
|
||||
err = addIssuedNames(ctx, tx, parsed, isRenewal)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = addKeyHash(txWithCtx, parsed)
|
||||
err = addKeyHash(ctx, tx, parsed)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -314,12 +315,12 @@ func (ssa *SQLStorageAuthority) AddCertificate(ctx context.Context, req *sapb.Ad
|
|||
Expires: parsedCertificate.NotAfter,
|
||||
}
|
||||
|
||||
isRenewalRaw, overallError := db.WithTransaction(ctx, ssa.dbMap, func(txWithCtx db.Executor) (interface{}, error) {
|
||||
isRenewalRaw, overallError := db.WithTransaction(ctx, ssa.dbMap, func(tx db.Executor) (interface{}, error) {
|
||||
// Select to see if cert exists
|
||||
var row struct {
|
||||
Count int64
|
||||
}
|
||||
err := txWithCtx.SelectOne(&row, "SELECT COUNT(*) as count FROM certificates WHERE serial=?", serial)
|
||||
err := tx.SelectOne(ctx, &row, "SELECT COUNT(*) as count FROM certificates WHERE serial=?", serial)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -328,7 +329,7 @@ func (ssa *SQLStorageAuthority) AddCertificate(ctx context.Context, req *sapb.Ad
|
|||
}
|
||||
|
||||
// Save the final certificate
|
||||
err = txWithCtx.Insert(cert)
|
||||
err = tx.Insert(ctx, cert)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -340,7 +341,8 @@ func (ssa *SQLStorageAuthority) AddCertificate(ctx context.Context, req *sapb.Ad
|
|||
// would be a misissuance and miscalculating whether the cert is a renewal or
|
||||
// not for the purpose of rate limiting is the least of our troubles.
|
||||
isRenewal, err := ssa.checkFQDNSetExists(
|
||||
txWithCtx.SelectOne,
|
||||
ctx,
|
||||
tx.SelectOne,
|
||||
parsedCertificate.DNSNames)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -367,12 +369,12 @@ func (ssa *SQLStorageAuthority) AddCertificate(ctx context.Context, req *sapb.Ad
|
|||
// for rate limits. Since the effects of failing these writes is slight
|
||||
// miscalculation of rate limits we choose to not fail the AddCertificate
|
||||
// operation if the rate limit update transaction fails.
|
||||
_, rlTransactionErr := db.WithTransaction(ctx, ssa.dbMap, func(txWithCtx db.Executor) (interface{}, error) {
|
||||
_, rlTransactionErr := db.WithTransaction(ctx, ssa.dbMap, func(tx db.Executor) (interface{}, error) {
|
||||
// Add to the rate limit table, but only for new certificates. Renewals
|
||||
// don't count against the certificatesPerName limit.
|
||||
if !isRenewal {
|
||||
timeToTheHour := parsedCertificate.NotBefore.Round(time.Hour)
|
||||
err := ssa.addCertificatesPerName(txWithCtx, parsedCertificate.DNSNames, timeToTheHour)
|
||||
err := ssa.addCertificatesPerName(ctx, tx, parsedCertificate.DNSNames, timeToTheHour)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -381,7 +383,8 @@ func (ssa *SQLStorageAuthority) AddCertificate(ctx context.Context, req *sapb.Ad
|
|||
// Update the FQDN sets now that there is a final certificate to ensure rate
|
||||
// limits are calculated correctly.
|
||||
err = addFQDNSet(
|
||||
txWithCtx,
|
||||
ctx,
|
||||
tx,
|
||||
parsedCertificate.DNSNames,
|
||||
core.SerialToString(parsedCertificate.SerialNumber),
|
||||
parsedCertificate.NotBefore,
|
||||
|
@ -408,7 +411,7 @@ func (ssa *SQLStorageAuthority) DeactivateRegistration(ctx context.Context, req
|
|||
if req == nil || req.Id == 0 {
|
||||
return nil, errIncompleteRequest
|
||||
}
|
||||
_, err := ssa.dbMap.WithContext(ctx).Exec(
|
||||
_, err := ssa.dbMap.ExecContext(ctx,
|
||||
"UPDATE registrations SET status = ? WHERE status = ? AND id = ?",
|
||||
string(core.StatusDeactivated),
|
||||
string(core.StatusValid),
|
||||
|
@ -426,7 +429,7 @@ func (ssa *SQLStorageAuthority) DeactivateAuthorization2(ctx context.Context, re
|
|||
return nil, errIncompleteRequest
|
||||
}
|
||||
|
||||
_, err := ssa.dbMap.Exec(
|
||||
_, err := ssa.dbMap.ExecContext(ctx,
|
||||
`UPDATE authz2 SET status = :deactivated WHERE id = :id and status IN (:valid,:pending)`,
|
||||
map[string]interface{}{
|
||||
"deactivated": statusUint(core.StatusDeactivated),
|
||||
|
@ -451,7 +454,7 @@ func (ssa *SQLStorageAuthority) NewOrderAndAuthzs(ctx context.Context, req *sapb
|
|||
return nil, errIncompleteRequest
|
||||
}
|
||||
|
||||
output, err := db.WithTransaction(ctx, ssa.dbMap, func(txWithCtx db.Executor) (interface{}, error) {
|
||||
output, err := db.WithTransaction(ctx, ssa.dbMap, func(tx db.Executor) (interface{}, error) {
|
||||
// First, insert all of the new authorizations and record their IDs.
|
||||
newAuthzIDs := make([]int64, 0)
|
||||
if len(req.NewAuthzs) != 0 {
|
||||
|
@ -485,7 +488,7 @@ func (ssa *SQLStorageAuthority) NewOrderAndAuthzs(ctx context.Context, req *sapb
|
|||
return nil, err
|
||||
}
|
||||
}
|
||||
newAuthzIDs, err = inserter.Insert(txWithCtx)
|
||||
newAuthzIDs, err = inserter.Insert(ctx, tx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -497,7 +500,7 @@ func (ssa *SQLStorageAuthority) NewOrderAndAuthzs(ctx context.Context, req *sapb
|
|||
Expires: time.Unix(0, req.NewOrder.Expires),
|
||||
Created: ssa.clk.Now(),
|
||||
}
|
||||
err := txWithCtx.Insert(order)
|
||||
err := tx.Insert(ctx, order)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -519,7 +522,7 @@ func (ssa *SQLStorageAuthority) NewOrderAndAuthzs(ctx context.Context, req *sapb
|
|||
return nil, err
|
||||
}
|
||||
}
|
||||
_, err = inserter.Insert(txWithCtx)
|
||||
_, err = inserter.Insert(ctx, tx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -535,13 +538,13 @@ func (ssa *SQLStorageAuthority) NewOrderAndAuthzs(ctx context.Context, req *sapb
|
|||
return nil, err
|
||||
}
|
||||
}
|
||||
_, err = inserter.Insert(txWithCtx)
|
||||
_, err = inserter.Insert(ctx, tx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Fifth, insert the FQDNSet entry for the order.
|
||||
err = addOrderFQDNSet(txWithCtx, req.NewOrder.Names, order.ID, order.RegistrationID, order.Expires)
|
||||
err = addOrderFQDNSet(ctx, tx, req.NewOrder.Names, order.ID, order.RegistrationID, order.Expires)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -563,7 +566,7 @@ func (ssa *SQLStorageAuthority) NewOrderAndAuthzs(ctx context.Context, req *sapb
|
|||
|
||||
// Calculate the order status before returning it. Since it may have reused
|
||||
// all valid authorizations the order may be "born" in a ready status.
|
||||
status, err := statusForOrder(txWithCtx, res, ssa.clk.Now())
|
||||
status, err := statusForOrder(ctx, tx, res, ssa.clk.Now())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -581,7 +584,7 @@ func (ssa *SQLStorageAuthority) NewOrderAndAuthzs(ctx context.Context, req *sapb
|
|||
}
|
||||
|
||||
// Increment the order creation count
|
||||
err = addNewOrdersRateLimit(ssa.dbMap.WithContext(ctx), req.NewOrder.RegistrationID, ssa.clk.Now().Truncate(time.Minute))
|
||||
err = addNewOrdersRateLimit(ctx, ssa.dbMap, req.NewOrder.RegistrationID, ssa.clk.Now().Truncate(time.Minute))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -596,8 +599,8 @@ func (ssa *SQLStorageAuthority) SetOrderProcessing(ctx context.Context, req *sap
|
|||
if req.Id == 0 {
|
||||
return nil, errIncompleteRequest
|
||||
}
|
||||
_, overallError := db.WithTransaction(ctx, ssa.dbMap, func(txWithCtx db.Executor) (interface{}, error) {
|
||||
result, err := txWithCtx.Exec(`
|
||||
_, overallError := db.WithTransaction(ctx, ssa.dbMap, func(tx db.Executor) (interface{}, error) {
|
||||
result, err := tx.ExecContext(ctx, `
|
||||
UPDATE orders
|
||||
SET beganProcessing = ?
|
||||
WHERE id = ?
|
||||
|
@ -627,7 +630,7 @@ func (ssa *SQLStorageAuthority) SetOrderError(ctx context.Context, req *sapb.Set
|
|||
if req.Id == 0 || req.Error == nil {
|
||||
return nil, errIncompleteRequest
|
||||
}
|
||||
_, overallError := db.WithTransaction(ctx, ssa.dbMap, func(txWithCtx db.Executor) (interface{}, error) {
|
||||
_, overallError := db.WithTransaction(ctx, ssa.dbMap, func(tx db.Executor) (interface{}, error) {
|
||||
om, err := orderToModel(&corepb.Order{
|
||||
Id: req.Id,
|
||||
Error: req.Error,
|
||||
|
@ -636,7 +639,7 @@ func (ssa *SQLStorageAuthority) SetOrderError(ctx context.Context, req *sapb.Set
|
|||
return nil, err
|
||||
}
|
||||
|
||||
result, err := txWithCtx.Exec(`
|
||||
result, err := tx.ExecContext(ctx, `
|
||||
UPDATE orders
|
||||
SET error = ?
|
||||
WHERE id = ?`,
|
||||
|
@ -667,8 +670,8 @@ func (ssa *SQLStorageAuthority) FinalizeOrder(ctx context.Context, req *sapb.Fin
|
|||
if req.Id == 0 || req.CertificateSerial == "" {
|
||||
return nil, errIncompleteRequest
|
||||
}
|
||||
_, overallError := db.WithTransaction(ctx, ssa.dbMap, func(txWithCtx db.Executor) (interface{}, error) {
|
||||
result, err := txWithCtx.Exec(`
|
||||
_, overallError := db.WithTransaction(ctx, ssa.dbMap, func(tx db.Executor) (interface{}, error) {
|
||||
result, err := tx.ExecContext(ctx, `
|
||||
UPDATE orders
|
||||
SET certificateSerial = ?
|
||||
WHERE id = ? AND
|
||||
|
@ -686,7 +689,7 @@ func (ssa *SQLStorageAuthority) FinalizeOrder(ctx context.Context, req *sapb.Fin
|
|||
|
||||
// Delete the orderFQDNSet row for the order now that it has been finalized.
|
||||
// We use this table for order reuse and should not reuse a finalized order.
|
||||
err = deleteOrderFQDNSet(txWithCtx, req.Id)
|
||||
err = deleteOrderFQDNSet(ctx, tx, req.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -770,7 +773,7 @@ func (ssa *SQLStorageAuthority) FinalizeAuthorization2(ctx context.Context, req
|
|||
"validationError": veJSON,
|
||||
}
|
||||
|
||||
res, err := ssa.dbMap.Exec(query, params)
|
||||
res, err := ssa.dbMap.ExecContext(ctx, query, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -795,7 +798,7 @@ func (ssa *SQLStorageAuthority) RevokeCertificate(ctx context.Context, req *sapb
|
|||
|
||||
revokedDate := time.Unix(0, req.Date)
|
||||
|
||||
res, err := ssa.dbMap.Exec(
|
||||
res, err := ssa.dbMap.ExecContext(ctx,
|
||||
`UPDATE certificateStatus SET
|
||||
status = ?,
|
||||
revokedReason = ?,
|
||||
|
@ -838,7 +841,7 @@ func (ssa *SQLStorageAuthority) UpdateRevokedCertificate(ctx context.Context, re
|
|||
thisUpdate := time.Unix(0, req.Date)
|
||||
revokedDate := time.Unix(0, req.Backdate)
|
||||
|
||||
res, err := ssa.dbMap.Exec(
|
||||
res, err := ssa.dbMap.ExecContext(ctx,
|
||||
`UPDATE certificateStatus SET
|
||||
revokedReason = ?,
|
||||
ocspLastUpdated = ?
|
||||
|
@ -887,7 +890,7 @@ func (ssa *SQLStorageAuthority) AddBlockedKey(ctx context.Context, req *sapb.Add
|
|||
qs += ", ?"
|
||||
vals = append(vals, req.RevokedBy)
|
||||
}
|
||||
_, err := ssa.dbMap.Exec(
|
||||
_, err := ssa.dbMap.ExecContext(ctx,
|
||||
fmt.Sprintf("INSERT INTO blockedKeys (%s) VALUES (%s)", cols, qs),
|
||||
vals...,
|
||||
)
|
||||
|
@ -904,7 +907,7 @@ func (ssa *SQLStorageAuthority) AddBlockedKey(ctx context.Context, req *sapb.Add
|
|||
|
||||
// Health implements the grpc.checker interface.
|
||||
func (ssa *SQLStorageAuthority) Health(ctx context.Context) error {
|
||||
err := ssa.dbMap.WithContext(ctx).SelectOne(new(int), "SELECT 1")
|
||||
err := ssa.dbMap.SelectOne(ctx, new(int), "SELECT 1")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -939,9 +942,10 @@ func (ssa *SQLStorageAuthority) LeaseCRLShard(ctx context.Context, req *sapb.Lea
|
|||
// leased or are previously-unknown indices are considered older than any other
|
||||
// shard. It returns an error if all shards for the issuer are already leased.
|
||||
func (ssa *SQLStorageAuthority) leaseOldestCRLShard(ctx context.Context, req *sapb.LeaseCRLShardRequest) (*sapb.LeaseCRLShardResponse, error) {
|
||||
shardIdx, err := db.WithTransaction(ctx, ssa.dbMap, func(txWithCtx db.Executor) (interface{}, error) {
|
||||
shardIdx, err := db.WithTransaction(ctx, ssa.dbMap, func(tx db.Executor) (interface{}, error) {
|
||||
var shards []*crlShardModel
|
||||
_, err := txWithCtx.Select(
|
||||
_, err := tx.Select(
|
||||
ctx,
|
||||
&shards,
|
||||
`SELECT id, issuerID, idx, thisUpdate, nextUpdate, leasedUntil
|
||||
FROM crlShards
|
||||
|
@ -989,7 +993,7 @@ func (ssa *SQLStorageAuthority) leaseOldestCRLShard(ctx context.Context, req *sa
|
|||
}
|
||||
|
||||
if needToInsert {
|
||||
_, err = txWithCtx.Exec(
|
||||
_, err = tx.ExecContext(ctx,
|
||||
`INSERT INTO crlShards (issuerID, idx, leasedUntil)
|
||||
VALUES (?, ?, ?)`,
|
||||
req.IssuerNameID,
|
||||
|
@ -997,7 +1001,7 @@ func (ssa *SQLStorageAuthority) leaseOldestCRLShard(ctx context.Context, req *sa
|
|||
req.Until.AsTime(),
|
||||
)
|
||||
} else {
|
||||
_, err = txWithCtx.Exec(
|
||||
_, err = tx.ExecContext(ctx,
|
||||
`UPDATE crlShards
|
||||
SET leasedUntil = ?
|
||||
WHERE issuerID = ?
|
||||
|
@ -1032,8 +1036,8 @@ func (ssa *SQLStorageAuthority) leaseSpecificCRLShard(ctx context.Context, req *
|
|||
return nil, fmt.Errorf("request must identify a single shard index: %d != %d", req.MinShardIdx, req.MaxShardIdx)
|
||||
}
|
||||
|
||||
_, err := db.WithTransaction(ctx, ssa.dbMap, func(txWithCtx db.Executor) (interface{}, error) {
|
||||
res, err := txWithCtx.Exec(
|
||||
_, err := db.WithTransaction(ctx, ssa.dbMap, func(tx db.Executor) (interface{}, error) {
|
||||
res, err := tx.ExecContext(ctx,
|
||||
`UPDATE crlShards
|
||||
SET leasedUntil = ?
|
||||
WHERE issuerID = ?
|
||||
|
@ -1084,8 +1088,8 @@ func (ssa *SQLStorageAuthority) UpdateCRLShard(ctx context.Context, req *sapb.Up
|
|||
return nil, errIncompleteRequest
|
||||
}
|
||||
|
||||
_, err := db.WithTransaction(ctx, ssa.dbMap, func(txWithCtx db.Executor) (interface{}, error) {
|
||||
res, err := txWithCtx.Exec(
|
||||
_, err := db.WithTransaction(ctx, ssa.dbMap, func(tx db.Executor) (interface{}, error) {
|
||||
res, err := tx.ExecContext(ctx,
|
||||
`UPDATE crlShards
|
||||
SET thisUpdate = ?, nextUpdate = ?
|
||||
WHERE issuerID = ?
|
||||
|
|
|
@ -127,7 +127,7 @@ func createPendingAuthorization(t *testing.T, sa *SQLStorageAuthority, domain st
|
|||
Token: token,
|
||||
}
|
||||
|
||||
err = sa.dbMap.Insert(&am)
|
||||
err = sa.dbMap.Insert(context.Background(), &am)
|
||||
test.AssertNotError(t, err, "creating test authorization")
|
||||
|
||||
return am.ID
|
||||
|
@ -238,7 +238,6 @@ func TestSelectRegistration(t *testing.T) {
|
|||
sa, _, cleanUp := initSA(t)
|
||||
defer cleanUp()
|
||||
var ctx = context.Background()
|
||||
var ssaCtx = sa.dbMap.WithContext(ctx)
|
||||
jwk := goodTestJWK()
|
||||
jwkJSON, _ := jwk.MarshalJSON()
|
||||
sha, err := core.KeyDigestB64(jwk.Key)
|
||||
|
@ -253,11 +252,11 @@ func TestSelectRegistration(t *testing.T) {
|
|||
test.AssertNotError(t, err, fmt.Sprintf("couldn't create new registration: %s", err))
|
||||
test.Assert(t, reg.Id != 0, "ID shouldn't be 0")
|
||||
|
||||
_, err = selectRegistration(ssaCtx, "id", reg.Id)
|
||||
_, err = selectRegistration(ctx, sa.dbMap, "id", reg.Id)
|
||||
test.AssertNotError(t, err, "selecting by id should work")
|
||||
_, err = selectRegistration(ssaCtx, "jwk_sha256", sha)
|
||||
_, err = selectRegistration(ctx, sa.dbMap, "jwk_sha256", sha)
|
||||
test.AssertNotError(t, err, "selecting by jwk_sha256 should work")
|
||||
_, err = selectRegistration(ssaCtx, "initialIP", reg.Id)
|
||||
_, err = selectRegistration(ctx, sa.dbMap, "initialIP", reg.Id)
|
||||
test.AssertError(t, err, "selecting by any other column should not work")
|
||||
}
|
||||
|
||||
|
@ -305,9 +304,10 @@ func TestReplicationLagRetries(t *testing.T) {
|
|||
|
||||
// findIssuedName is a small helper test function to directly query the
|
||||
// issuedNames table for a given name to find a serial (or return an err).
|
||||
func findIssuedName(dbMap db.OneSelector, name string) (string, error) {
|
||||
func findIssuedName(ctx context.Context, dbMap db.OneSelector, name string) (string, error) {
|
||||
var issuedNamesSerial string
|
||||
err := dbMap.SelectOne(
|
||||
ctx,
|
||||
&issuedNamesSerial,
|
||||
`SELECT serial FROM issuedNames
|
||||
WHERE reversedName = ?
|
||||
|
@ -389,6 +389,7 @@ func TestGetSerialMetadata(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestAddPrecertificate(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
sa, clk, cleanUp := initSA(t)
|
||||
defer cleanUp()
|
||||
|
||||
|
@ -416,7 +417,7 @@ func TestAddPrecertificate(t *testing.T) {
|
|||
test.AssertEquals(t, clk.Now().UnixNano(), certStatus.OcspLastUpdated)
|
||||
|
||||
// It should show up in the issued names table
|
||||
issuedNamesSerial, err := findIssuedName(sa.dbMap, testCert.DNSNames[0])
|
||||
issuedNamesSerial, err := findIssuedName(ctx, sa.dbMap, testCert.DNSNames[0])
|
||||
test.AssertNotError(t, err, "expected no err querying issuedNames for precert")
|
||||
test.AssertEquals(t, issuedNamesSerial, serial)
|
||||
|
||||
|
@ -511,7 +512,7 @@ func TestAddPrecertificateKeyHash(t *testing.T) {
|
|||
test.AssertNotError(t, err, "failed to add precert")
|
||||
|
||||
var keyHashes []keyHashModel
|
||||
_, err = sa.dbMap.Select(&keyHashes, "SELECT * FROM keyHashToSerial")
|
||||
_, err = sa.dbMap.Select(context.Background(), &keyHashes, "SELECT * FROM keyHashToSerial")
|
||||
test.AssertNotError(t, err, "failed to retrieve rows from keyHashToSerial")
|
||||
test.AssertEquals(t, len(keyHashes), 1)
|
||||
test.AssertEquals(t, keyHashes[0].CertSerial, serial)
|
||||
|
@ -669,10 +670,10 @@ func TestCountCertificatesByNames(t *testing.T) {
|
|||
interlocker.Add(len(names))
|
||||
sa.parallelismPerRPC = len(names)
|
||||
oldCertCountFunc := sa.countCertificatesByName
|
||||
sa.countCertificatesByName = func(sel db.Selector, domain string, timeRange *sapb.Range) (int64, time.Time, error) {
|
||||
sa.countCertificatesByName = func(ctx context.Context, sel db.Selector, domain string, timeRange *sapb.Range) (int64, time.Time, error) {
|
||||
interlocker.Done()
|
||||
interlocker.Wait()
|
||||
return oldCertCountFunc(sel, domain, timeRange)
|
||||
return oldCertCountFunc(ctx, sel, domain, timeRange)
|
||||
}
|
||||
|
||||
certDER2, err := os.ReadFile("test-cert2.der")
|
||||
|
@ -849,15 +850,16 @@ func TestCountRegistrationsByIPRange(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestFQDNSets(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
sa, fc, cleanUp := initSA(t)
|
||||
defer cleanUp()
|
||||
|
||||
tx, err := sa.dbMap.Begin()
|
||||
tx, err := sa.dbMap.BeginTx(ctx)
|
||||
test.AssertNotError(t, err, "Failed to open transaction")
|
||||
names := []string{"a.example.com", "B.example.com"}
|
||||
expires := fc.Now().Add(time.Hour * 2).UTC()
|
||||
issued := fc.Now()
|
||||
err = addFQDNSet(tx, names, "serial", issued, expires)
|
||||
err = addFQDNSet(ctx, tx, names, "serial", issued, expires)
|
||||
test.AssertNotError(t, err, "Failed to add name set")
|
||||
test.AssertNotError(t, tx.Commit(), "Failed to commit transaction")
|
||||
|
||||
|
@ -878,9 +880,9 @@ func TestFQDNSets(t *testing.T) {
|
|||
test.AssertEquals(t, count.Count, int64(1))
|
||||
|
||||
// add another valid set
|
||||
tx, err = sa.dbMap.Begin()
|
||||
tx, err = sa.dbMap.BeginTx(ctx)
|
||||
test.AssertNotError(t, err, "Failed to open transaction")
|
||||
err = addFQDNSet(tx, names, "anotherSerial", issued, expires)
|
||||
err = addFQDNSet(ctx, tx, names, "anotherSerial", issued, expires)
|
||||
test.AssertNotError(t, err, "Failed to add name set")
|
||||
test.AssertNotError(t, tx.Commit(), "Failed to commit transaction")
|
||||
|
||||
|
@ -891,9 +893,10 @@ func TestFQDNSets(t *testing.T) {
|
|||
test.AssertEquals(t, count.Count, int64(2))
|
||||
|
||||
// add an expired set
|
||||
tx, err = sa.dbMap.Begin()
|
||||
tx, err = sa.dbMap.BeginTx(ctx)
|
||||
test.AssertNotError(t, err, "Failed to open transaction")
|
||||
err = addFQDNSet(
|
||||
ctx,
|
||||
tx,
|
||||
names,
|
||||
"yetAnotherSerial",
|
||||
|
@ -913,7 +916,7 @@ func TestFQDNSetTimestampsForWindow(t *testing.T) {
|
|||
sa, fc, cleanUp := initSA(t)
|
||||
defer cleanUp()
|
||||
|
||||
tx, err := sa.dbMap.Begin()
|
||||
tx, err := sa.dbMap.BeginTx(ctx)
|
||||
test.AssertNotError(t, err, "Failed to open transaction")
|
||||
|
||||
names := []string{"a.example.com", "B.example.com"}
|
||||
|
@ -931,7 +934,7 @@ func TestFQDNSetTimestampsForWindow(t *testing.T) {
|
|||
// Add an issuance for names inside the window.
|
||||
expires := fc.Now().Add(time.Hour * 2).UTC()
|
||||
firstIssued := fc.Now()
|
||||
err = addFQDNSet(tx, names, "serial", firstIssued, expires)
|
||||
err = addFQDNSet(ctx, tx, names, "serial", firstIssued, expires)
|
||||
test.AssertNotError(t, err, "Failed to add name set")
|
||||
test.AssertNotError(t, tx.Commit(), "Failed to commit transaction")
|
||||
|
||||
|
@ -949,9 +952,9 @@ func TestFQDNSetTimestampsForWindow(t *testing.T) {
|
|||
test.AssertEquals(t, firstIssued, time.Unix(0, resp.Timestamps[len(resp.Timestamps)-1]).UTC())
|
||||
|
||||
// Add another issuance for names inside the window.
|
||||
tx, err = sa.dbMap.Begin()
|
||||
tx, err = sa.dbMap.BeginTx(ctx)
|
||||
test.AssertNotError(t, err, "Failed to open transaction")
|
||||
err = addFQDNSet(tx, names, "anotherSerial", firstIssued, expires)
|
||||
err = addFQDNSet(ctx, tx, names, "anotherSerial", firstIssued, expires)
|
||||
test.AssertNotError(t, err, "Failed to add name set")
|
||||
test.AssertNotError(t, tx.Commit(), "Failed to commit transaction")
|
||||
|
||||
|
@ -963,9 +966,9 @@ func TestFQDNSetTimestampsForWindow(t *testing.T) {
|
|||
test.AssertEquals(t, firstIssued, time.Unix(0, resp.Timestamps[len(resp.Timestamps)-1]).UTC())
|
||||
|
||||
// Add another issuance for names but just outside the window.
|
||||
tx, err = sa.dbMap.Begin()
|
||||
tx, err = sa.dbMap.BeginTx(ctx)
|
||||
test.AssertNotError(t, err, "Failed to open transaction")
|
||||
err = addFQDNSet(tx, names, "yetAnotherSerial", firstIssued.Add(-window), expires)
|
||||
err = addFQDNSet(ctx, tx, names, "yetAnotherSerial", firstIssued.Add(-window), expires)
|
||||
test.AssertNotError(t, err, "Failed to add name set")
|
||||
test.AssertNotError(t, tx.Commit(), "Failed to commit transaction")
|
||||
|
||||
|
@ -985,11 +988,11 @@ func TestFQDNSetsExists(t *testing.T) {
|
|||
test.AssertNotError(t, err, "Failed to check FQDN set existence")
|
||||
test.Assert(t, !exists.Exists, "FQDN set shouldn't exist")
|
||||
|
||||
tx, err := sa.dbMap.Begin()
|
||||
tx, err := sa.dbMap.BeginTx(ctx)
|
||||
test.AssertNotError(t, err, "Failed to open transaction")
|
||||
expires := fc.Now().Add(time.Hour * 2).UTC()
|
||||
issued := fc.Now()
|
||||
err = addFQDNSet(tx, names, "serial", issued, expires)
|
||||
err = addFQDNSet(ctx, tx, names, "serial", issued, expires)
|
||||
test.AssertNotError(t, err, "Failed to add name set")
|
||||
test.AssertNotError(t, tx.Commit(), "Failed to commit transaction")
|
||||
|
||||
|
@ -1003,7 +1006,7 @@ type queryRecorder struct {
|
|||
args []interface{}
|
||||
}
|
||||
|
||||
func (e *queryRecorder) Query(query string, args ...interface{}) (*sql.Rows, error) {
|
||||
func (e *queryRecorder) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) {
|
||||
e.query = query
|
||||
e.args = args
|
||||
return nil, nil
|
||||
|
@ -1090,6 +1093,7 @@ func TestAddIssuedNames(t *testing.T) {
|
|||
t.Run(tc.Name, func(t *testing.T) {
|
||||
var e queryRecorder
|
||||
err := addIssuedNames(
|
||||
ctx,
|
||||
&e,
|
||||
&x509.Certificate{
|
||||
DNSNames: tc.IssuedNames,
|
||||
|
@ -1262,12 +1266,12 @@ func TestNewOrderAndAuthzs(t *testing.T) {
|
|||
test.AssertDeepEquals(t, order.V2Authorizations, []int64{1, 2, 3, 4})
|
||||
|
||||
var authzIDs []int64
|
||||
_, err = sa.dbMap.Select(&authzIDs, "SELECT authzID FROM orderToAuthz2 WHERE orderID = ?;", order.Id)
|
||||
_, err = sa.dbMap.Select(ctx, &authzIDs, "SELECT authzID FROM orderToAuthz2 WHERE orderID = ?;", order.Id)
|
||||
test.AssertNotError(t, err, "Failed to count orderToAuthz entries")
|
||||
test.AssertEquals(t, len(authzIDs), 4)
|
||||
test.AssertDeepEquals(t, authzIDs, []int64{1, 2, 3, 4})
|
||||
|
||||
names, err := namesForOrder(sa.dbReadOnlyMap, order.Id)
|
||||
names, err := namesForOrder(ctx, sa.dbReadOnlyMap, order.Id)
|
||||
test.AssertNotError(t, err, "namesForOrder errored")
|
||||
test.AssertEquals(t, len(names), 4)
|
||||
test.AssertDeepEquals(t, names, []string{"com.a", "com.b", "com.c", "com.d"})
|
||||
|
@ -1343,7 +1347,7 @@ func TestNewOrderAndAuthzs_NewAuthzExpectedFields(t *testing.T) {
|
|||
test.AssertNotError(t, err, "sa.NewOrderAndAuthzs failed")
|
||||
|
||||
// Safely get the authz for the order we created above.
|
||||
obj, err := sa.dbReadOnlyMap.Get(authzModel{}, order.V2Authorizations[0])
|
||||
obj, err := sa.dbReadOnlyMap.Get(ctx, authzModel{}, order.V2Authorizations[0])
|
||||
test.AssertNotError(t, err, fmt.Sprintf("authorization %d not found", order.V2Authorizations[0]))
|
||||
|
||||
// To access the data stored in obj at compile time, we type assert obj
|
||||
|
@ -1559,17 +1563,17 @@ func TestGetAuthorizations2(t *testing.T) {
|
|||
|
||||
// Associate authorizations with an order so that GetAuthorizations2 thinks
|
||||
// they are WFE2 authorizations.
|
||||
err := sa.dbMap.Insert(&orderToAuthzModel{
|
||||
err := sa.dbMap.Insert(ctx, &orderToAuthzModel{
|
||||
OrderID: 1,
|
||||
AuthzID: authzIDA,
|
||||
})
|
||||
test.AssertNotError(t, err, "sa.dbMap.Insert failed")
|
||||
err = sa.dbMap.Insert(&orderToAuthzModel{
|
||||
err = sa.dbMap.Insert(ctx, &orderToAuthzModel{
|
||||
OrderID: 1,
|
||||
AuthzID: authzIDB,
|
||||
})
|
||||
test.AssertNotError(t, err, "sa.dbMap.Insert failed")
|
||||
err = sa.dbMap.Insert(&orderToAuthzModel{
|
||||
err = sa.dbMap.Insert(ctx, &orderToAuthzModel{
|
||||
OrderID: 1,
|
||||
AuthzID: authzIDC,
|
||||
})
|
||||
|
@ -2176,9 +2180,9 @@ func TestAddCertificateRenewalBit(t *testing.T) {
|
|||
serial := "thrilla"
|
||||
|
||||
// Add a FQDN set for the names so that it will be considered a renewal
|
||||
tx, err := sa.dbMap.Begin()
|
||||
tx, err := sa.dbMap.BeginTx(ctx)
|
||||
test.AssertNotError(t, err, "Failed to open transaction")
|
||||
err = addFQDNSet(tx, names, serial, issued, expires)
|
||||
err = addFQDNSet(ctx, tx, names, serial, issued, expires)
|
||||
test.AssertNotError(t, err, "Failed to add name set")
|
||||
test.AssertNotError(t, tx.Commit(), "Failed to commit transaction")
|
||||
|
||||
|
@ -2201,6 +2205,7 @@ func TestAddCertificateRenewalBit(t *testing.T) {
|
|||
t.Helper()
|
||||
var count int
|
||||
err := sa.dbMap.SelectOne(
|
||||
ctx,
|
||||
&count,
|
||||
`SELECT COUNT(*) FROM issuedNames
|
||||
WHERE reversedName = ?
|
||||
|
@ -2929,7 +2934,7 @@ func TestIncidentsForSerial(t *testing.T) {
|
|||
weekAgo := sa.clk.Now().Add(-time.Hour * 24 * 7)
|
||||
|
||||
// Add a disabled incident.
|
||||
err = testSADbMap.Insert(&incidentModel{
|
||||
err = testSADbMap.Insert(ctx, &incidentModel{
|
||||
SerialTable: "incident_foo",
|
||||
URL: "https://example.com/foo-incident",
|
||||
RenewBy: sa.clk.Now().Add(time.Hour * 24 * 7),
|
||||
|
@ -2943,7 +2948,7 @@ func TestIncidentsForSerial(t *testing.T) {
|
|||
test.AssertEquals(t, len(result.Incidents), 0)
|
||||
|
||||
// Add an enabled incident.
|
||||
err = testSADbMap.Insert(&incidentModel{
|
||||
err = testSADbMap.Insert(ctx, &incidentModel{
|
||||
SerialTable: "incident_bar",
|
||||
URL: "https://example.com/test-incident",
|
||||
RenewBy: sa.clk.Now().Add(time.Hour * 24 * 7),
|
||||
|
@ -2959,7 +2964,7 @@ func TestIncidentsForSerial(t *testing.T) {
|
|||
OrderID: &one,
|
||||
LastNoticeSent: &weekAgo,
|
||||
}
|
||||
_, err = testIncidentsDbMap.Exec(
|
||||
_, err = testIncidentsDbMap.ExecContext(ctx,
|
||||
fmt.Sprintf("INSERT INTO incident_bar (%s) VALUES ('%s', %d, %d, '%s')",
|
||||
"serial, registrationID, orderID, lastNoticeSent",
|
||||
affectedCertA.Serial,
|
||||
|
@ -2983,7 +2988,7 @@ func TestIncidentsForSerial(t *testing.T) {
|
|||
OrderID: &two,
|
||||
LastNoticeSent: &weekAgo,
|
||||
}
|
||||
_, err = testIncidentsDbMap.Exec(
|
||||
_, err = testIncidentsDbMap.ExecContext(ctx,
|
||||
fmt.Sprintf("INSERT INTO incident_bar (%s) VALUES ('%s', %d, %d, '%s')",
|
||||
"serial, registrationID, orderID, lastNoticeSent",
|
||||
affectedCertB.Serial,
|
||||
|
@ -3089,7 +3094,7 @@ func TestSerialsForIncident(t *testing.T) {
|
|||
}
|
||||
for i := range expectedSerials {
|
||||
randInt := func() int64 { return mrand.Int63() }
|
||||
_, err := testIncidentsDbMap.Exec(
|
||||
_, err := testIncidentsDbMap.ExecContext(ctx,
|
||||
fmt.Sprintf("INSERT INTO incident_foo (%s) VALUES ('%s', %d, %d, '%s')",
|
||||
"serial, registrationID, orderID, lastNoticeSent",
|
||||
i,
|
||||
|
@ -3268,7 +3273,7 @@ func TestLeaseOldestCRLShard(t *testing.T) {
|
|||
|
||||
// Create 8 shards: 4 for each of 2 issuers. For each issuer, one shard is
|
||||
// currently leased, three are available, and one of those failed to update.
|
||||
_, err := sa.dbMap.Exec(
|
||||
_, err := sa.dbMap.ExecContext(ctx,
|
||||
`INSERT INTO crlShards (issuerID, idx, thisUpdate, nextUpdate, leasedUntil) VALUES
|
||||
(1, 0, ?, ?, ?),
|
||||
(1, 1, ?, ?, ?),
|
||||
|
@ -3321,6 +3326,7 @@ func TestLeaseOldestCRLShard(t *testing.T) {
|
|||
test.AssertEquals(t, res.ShardIdx, int64(3))
|
||||
|
||||
err = sa.dbMap.SelectOne(
|
||||
ctx,
|
||||
&untilModel,
|
||||
`SELECT leasedUntil FROM crlShards WHERE issuerID = ? AND idx = ? LIMIT 1`,
|
||||
res.IssuerNameID,
|
||||
|
@ -3344,6 +3350,7 @@ func TestLeaseOldestCRLShard(t *testing.T) {
|
|||
test.AssertEquals(t, res.ShardIdx, int64(1))
|
||||
|
||||
err = sa.dbMap.SelectOne(
|
||||
ctx,
|
||||
&untilModel,
|
||||
`SELECT leasedUntil FROM crlShards WHERE issuerID = ? AND idx = ? LIMIT 1`,
|
||||
res.IssuerNameID,
|
||||
|
@ -3369,6 +3376,7 @@ func TestLeaseOldestCRLShard(t *testing.T) {
|
|||
test.Assert(t, res.ShardIdx <= 7, "checking leased index")
|
||||
|
||||
err = sa.dbMap.SelectOne(
|
||||
ctx,
|
||||
&untilModel,
|
||||
`SELECT leasedUntil FROM crlShards WHERE issuerID = ? AND idx = ? LIMIT 1`,
|
||||
res.IssuerNameID,
|
||||
|
@ -3388,7 +3396,7 @@ func TestLeaseSpecificCRLShard(t *testing.T) {
|
|||
|
||||
// Create 8 shards: 4 for each of 2 issuers. For each issuer, one shard is
|
||||
// currently leased, three are available, and one of those failed to update.
|
||||
_, err := sa.dbMap.Exec(
|
||||
_, err := sa.dbMap.ExecContext(ctx,
|
||||
`INSERT INTO crlShards (issuerID, idx, thisUpdate, nextUpdate, leasedUntil) VALUES
|
||||
(1, 0, ?, ?, ?),
|
||||
(1, 1, ?, ?, ?),
|
||||
|
@ -3429,6 +3437,7 @@ func TestLeaseSpecificCRLShard(t *testing.T) {
|
|||
test.AssertEquals(t, res.ShardIdx, int64(1))
|
||||
|
||||
err = sa.dbMap.SelectOne(
|
||||
ctx,
|
||||
&untilModel,
|
||||
`SELECT leasedUntil FROM crlShards WHERE issuerID = ? AND idx = ? LIMIT 1`,
|
||||
res.IssuerNameID,
|
||||
|
@ -3452,6 +3461,7 @@ func TestLeaseSpecificCRLShard(t *testing.T) {
|
|||
test.AssertEquals(t, res.ShardIdx, int64(3))
|
||||
|
||||
err = sa.dbMap.SelectOne(
|
||||
ctx,
|
||||
&untilModel,
|
||||
`SELECT leasedUntil FROM crlShards WHERE issuerID = ? AND idx = ? LIMIT 1`,
|
||||
res.IssuerNameID,
|
||||
|
@ -3496,7 +3506,7 @@ func TestUpdateCRLShard(t *testing.T) {
|
|||
|
||||
// Create 8 shards: 4 for each of 2 issuers. For each issuer, one shard is
|
||||
// currently leased, three are available, and one of those failed to update.
|
||||
_, err := sa.dbMap.Exec(
|
||||
_, err := sa.dbMap.ExecContext(ctx,
|
||||
`INSERT INTO crlShards (issuerID, idx, thisUpdate, nextUpdate, leasedUntil) VALUES
|
||||
(1, 0, ?, ?, ?),
|
||||
(1, 1, ?, ?, ?),
|
||||
|
@ -3535,6 +3545,7 @@ func TestUpdateCRLShard(t *testing.T) {
|
|||
test.AssertNotError(t, err, "updating leased shard")
|
||||
|
||||
err = sa.dbMap.SelectOne(
|
||||
ctx,
|
||||
&thisUpdateModel,
|
||||
`SELECT thisUpdate FROM crlShards WHERE issuerID = ? AND idx = ? LIMIT 1`,
|
||||
1,
|
||||
|
@ -3556,6 +3567,7 @@ func TestUpdateCRLShard(t *testing.T) {
|
|||
test.AssertNotError(t, err, "updating unleased shard")
|
||||
|
||||
err = sa.dbMap.SelectOne(
|
||||
ctx,
|
||||
&thisUpdateModel,
|
||||
`SELECT thisUpdate FROM crlShards WHERE issuerID = ? AND idx = ? LIMIT 1`,
|
||||
1,
|
||||
|
|
94
sa/saro.go
94
sa/saro.go
|
@ -31,7 +31,7 @@ var (
|
|||
validIncidentTableRegexp = regexp.MustCompile(`^incident_[0-9a-zA-Z_]{1,100}$`)
|
||||
)
|
||||
|
||||
type certCountFunc func(db db.Selector, domain string, timeRange *sapb.Range) (int64, time.Time, error)
|
||||
type certCountFunc func(ctx context.Context, db db.Selector, domain string, timeRange *sapb.Range) (int64, time.Time, error)
|
||||
|
||||
// SQLStorageAuthorityRO defines a read-only subset of a Storage Authority
|
||||
type SQLStorageAuthorityRO struct {
|
||||
|
@ -70,7 +70,7 @@ type SQLStorageAuthorityRO struct {
|
|||
}
|
||||
|
||||
// NewSQLStorageAuthorityRO provides persistence using a SQL backend for
|
||||
// Boulder. It will modify the given gorp.DbMap by adding relevant tables.
|
||||
// Boulder. It will modify the given borp.DbMap by adding relevant tables.
|
||||
func NewSQLStorageAuthorityRO(
|
||||
dbReadOnlyMap *db.WrappedMap,
|
||||
dbIncidentsMap *db.WrappedMap,
|
||||
|
@ -107,13 +107,13 @@ func (ssa *SQLStorageAuthorityRO) GetRegistration(ctx context.Context, req *sapb
|
|||
return nil, errIncompleteRequest
|
||||
}
|
||||
|
||||
model, err := selectRegistration(ssa.dbReadOnlyMap.WithContext(ctx), "id", req.Id)
|
||||
model, err := selectRegistration(ctx, ssa.dbReadOnlyMap, "id", req.Id)
|
||||
if db.IsNoRows(err) && ssa.lagFactor != 0 {
|
||||
// GetRegistration is often called to validate a JWK belonging to a brand
|
||||
// new account whose registrations table row hasn't propagated to the read
|
||||
// replica yet. If we get a NoRows, wait a little bit and retry, once.
|
||||
ssa.clk.Sleep(ssa.lagFactor)
|
||||
model, err = selectRegistration(ssa.dbReadOnlyMap.WithContext(ctx), "id", req.Id)
|
||||
model, err = selectRegistration(ctx, ssa.dbReadOnlyMap, "id", req.Id)
|
||||
if err != nil {
|
||||
if db.IsNoRows(err) {
|
||||
ssa.lagFactorCounter.WithLabelValues("GetRegistration", "notfound").Inc()
|
||||
|
@ -154,7 +154,7 @@ func (ssa *SQLStorageAuthorityRO) GetRegistrationByKey(ctx context.Context, req
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
model, err := selectRegistration(ssa.dbReadOnlyMap.WithContext(ctx), "jwk_sha256", sha)
|
||||
model, err := selectRegistration(ctx, ssa.dbReadOnlyMap, "jwk_sha256", sha)
|
||||
if err != nil {
|
||||
if db.IsNoRows(err) {
|
||||
return nil, berrors.NotFoundError("no registrations with public key sha256 %q", sha)
|
||||
|
@ -223,7 +223,8 @@ func (ssa *SQLStorageAuthorityRO) CountRegistrationsByIP(ctx context.Context, re
|
|||
}
|
||||
|
||||
var count int64
|
||||
err := ssa.dbReadOnlyMap.WithContext(ctx).SelectOne(
|
||||
err := ssa.dbReadOnlyMap.SelectOne(
|
||||
ctx,
|
||||
&count,
|
||||
`SELECT COUNT(*) FROM registrations
|
||||
WHERE
|
||||
|
@ -256,7 +257,8 @@ func (ssa *SQLStorageAuthorityRO) CountRegistrationsByIPRange(ctx context.Contex
|
|||
|
||||
var count int64
|
||||
beginIP, endIP := ipRange(req.Ip)
|
||||
err := ssa.dbReadOnlyMap.WithContext(ctx).SelectOne(
|
||||
err := ssa.dbReadOnlyMap.SelectOne(
|
||||
ctx,
|
||||
&count,
|
||||
`SELECT COUNT(*) FROM registrations
|
||||
WHERE
|
||||
|
@ -321,7 +323,7 @@ func (ssa *SQLStorageAuthorityRO) CountCertificatesByNames(ctx context.Context,
|
|||
return
|
||||
default:
|
||||
}
|
||||
count, earliest, err := ssa.countCertificatesByName(ssa.dbReadOnlyMap.WithContext(ctx), domain, req.Range)
|
||||
count, earliest, err := ssa.countCertificatesByName(ctx, ssa.dbReadOnlyMap, domain, req.Range)
|
||||
if err != nil {
|
||||
results <- result{err: err}
|
||||
// Skip any further work
|
||||
|
@ -386,7 +388,8 @@ func (ssa *SQLStorageAuthorityRO) GetSerialMetadata(ctx context.Context, req *sa
|
|||
}
|
||||
|
||||
recordedSerial := recordedSerialModel{}
|
||||
err := ssa.dbReadOnlyMap.WithContext(ctx).SelectOne(
|
||||
err := ssa.dbReadOnlyMap.SelectOne(
|
||||
ctx,
|
||||
&recordedSerial,
|
||||
"SELECT * FROM serials WHERE serial = ?",
|
||||
req.Serial,
|
||||
|
@ -420,7 +423,7 @@ func (ssa *SQLStorageAuthorityRO) GetCertificate(ctx context.Context, req *sapb.
|
|||
return nil, fmt.Errorf("invalid certificate serial %s", req.Serial)
|
||||
}
|
||||
|
||||
cert, err := SelectCertificate(ssa.dbReadOnlyMap.WithContext(ctx), req.Serial)
|
||||
cert, err := SelectCertificate(ctx, ssa.dbReadOnlyMap, req.Serial)
|
||||
if db.IsNoRows(err) {
|
||||
return nil, berrors.NotFoundError("certificate with serial %q not found", req.Serial)
|
||||
}
|
||||
|
@ -446,7 +449,7 @@ func (ssa *SQLStorageAuthorityRO) GetCertificateStatus(ctx context.Context, req
|
|||
return nil, err
|
||||
}
|
||||
|
||||
certStatus, err := SelectCertificateStatus(ssa.dbReadOnlyMap.WithContext(ctx), req.Serial)
|
||||
certStatus, err := SelectCertificateStatus(ctx, ssa.dbReadOnlyMap, req.Serial)
|
||||
if db.IsNoRows(err) {
|
||||
return nil, berrors.NotFoundError("certificate status with serial %q not found", req.Serial)
|
||||
}
|
||||
|
@ -472,7 +475,7 @@ func (ssa *SQLStorageAuthorityRO) GetRevocationStatus(ctx context.Context, req *
|
|||
return nil, fmt.Errorf("invalid certificate serial %s", req.Serial)
|
||||
}
|
||||
|
||||
status, err := SelectRevocationStatus(ssa.dbReadOnlyMap.WithContext(ctx), req.Serial)
|
||||
status, err := SelectRevocationStatus(ctx, ssa.dbReadOnlyMap, req.Serial)
|
||||
if err != nil {
|
||||
if db.IsNoRows(err) {
|
||||
return nil, berrors.NotFoundError("certificate status with serial %q not found", req.Serial)
|
||||
|
@ -492,7 +495,7 @@ func (ssa *SQLStorageAuthorityRO) CountOrders(ctx context.Context, req *sapb.Cou
|
|||
return nil, errIncompleteRequest
|
||||
}
|
||||
|
||||
return countNewOrders(ssa.dbReadOnlyMap.WithContext(ctx), req)
|
||||
return countNewOrders(ctx, ssa.dbReadOnlyMap, req)
|
||||
}
|
||||
|
||||
func (ssa *SQLStorageAuthority) CountOrders(ctx context.Context, req *sapb.CountOrdersRequest) (*sapb.Count, error) {
|
||||
|
@ -507,7 +510,8 @@ func (ssa *SQLStorageAuthorityRO) CountFQDNSets(ctx context.Context, req *sapb.C
|
|||
}
|
||||
|
||||
var count int64
|
||||
err := ssa.dbReadOnlyMap.WithContext(ctx).SelectOne(
|
||||
err := ssa.dbReadOnlyMap.SelectOne(
|
||||
ctx,
|
||||
&count,
|
||||
`SELECT COUNT(*) FROM fqdnSets
|
||||
WHERE setHash = ?
|
||||
|
@ -533,7 +537,8 @@ func (ssa *SQLStorageAuthorityRO) FQDNSetTimestampsForWindow(ctx context.Context
|
|||
Issued time.Time
|
||||
}
|
||||
var rows []row
|
||||
_, err := ssa.dbReadOnlyMap.WithContext(ctx).Select(
|
||||
_, err := ssa.dbReadOnlyMap.Select(
|
||||
ctx,
|
||||
&rows,
|
||||
`SELECT issued FROM fqdnSets
|
||||
WHERE setHash = ?
|
||||
|
@ -563,7 +568,7 @@ func (ssa *SQLStorageAuthorityRO) FQDNSetExists(ctx context.Context, req *sapb.F
|
|||
if len(req.Domains) == 0 {
|
||||
return nil, errIncompleteRequest
|
||||
}
|
||||
exists, err := ssa.checkFQDNSetExists(ssa.dbReadOnlyMap.WithContext(ctx).SelectOne, req.Domains)
|
||||
exists, err := ssa.checkFQDNSetExists(ctx, ssa.dbReadOnlyMap.SelectOne, req.Domains)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -574,16 +579,17 @@ func (ssa *SQLStorageAuthority) FQDNSetExists(ctx context.Context, req *sapb.FQD
|
|||
return ssa.SQLStorageAuthorityRO.FQDNSetExists(ctx, req)
|
||||
}
|
||||
|
||||
// oneSelectorFunc is a func type that matches both gorp.Transaction.SelectOne
|
||||
// and gorp.DbMap.SelectOne.
|
||||
type oneSelectorFunc func(holder interface{}, query string, args ...interface{}) error
|
||||
// oneSelectorFunc is a func type that matches both borp.Transaction.SelectOne
|
||||
// and borp.DbMap.SelectOne.
|
||||
type oneSelectorFunc func(ctx context.Context, holder interface{}, query string, args ...interface{}) error
|
||||
|
||||
// checkFQDNSetExists uses the given oneSelectorFunc to check whether an fqdnSet
|
||||
// for the given names exists.
|
||||
func (ssa *SQLStorageAuthorityRO) checkFQDNSetExists(selector oneSelectorFunc, names []string) (bool, error) {
|
||||
func (ssa *SQLStorageAuthorityRO) checkFQDNSetExists(ctx context.Context, selector oneSelectorFunc, names []string) (bool, error) {
|
||||
namehash := HashNames(names)
|
||||
var exists bool
|
||||
err := selector(
|
||||
ctx,
|
||||
&exists,
|
||||
`SELECT EXISTS (SELECT id FROM fqdnSets WHERE setHash = ? LIMIT 1)`,
|
||||
namehash,
|
||||
|
@ -608,7 +614,8 @@ func (ssa *SQLStorageAuthorityRO) PreviousCertificateExists(ctx context.Context,
|
|||
|
||||
// Find the most recently issued certificate containing this domain name.
|
||||
var serial string
|
||||
err := ssa.dbReadOnlyMap.WithContext(ctx).SelectOne(
|
||||
err := ssa.dbReadOnlyMap.SelectOne(
|
||||
ctx,
|
||||
&serial,
|
||||
`SELECT serial FROM issuedNames
|
||||
WHERE reversedName = ?
|
||||
|
@ -625,7 +632,8 @@ func (ssa *SQLStorageAuthorityRO) PreviousCertificateExists(ctx context.Context,
|
|||
|
||||
// Check whether that certificate was issued to the specified account.
|
||||
var count int
|
||||
err = ssa.dbReadOnlyMap.WithContext(ctx).SelectOne(
|
||||
err = ssa.dbReadOnlyMap.SelectOne(
|
||||
ctx,
|
||||
&count,
|
||||
`SELECT COUNT(*) FROM certificates
|
||||
WHERE serial = ?
|
||||
|
@ -658,8 +666,8 @@ func (ssa *SQLStorageAuthorityRO) GetOrder(ctx context.Context, req *sapb.OrderR
|
|||
return nil, errIncompleteRequest
|
||||
}
|
||||
|
||||
txn := func(txWithCtx db.Executor) (interface{}, error) {
|
||||
omObj, err := txWithCtx.Get(orderModel{}, req.Id)
|
||||
txn := func(tx db.Executor) (interface{}, error) {
|
||||
omObj, err := tx.Get(ctx, orderModel{}, req.Id)
|
||||
if err != nil {
|
||||
if db.IsNoRows(err) {
|
||||
return nil, berrors.NotFoundError("no order found for ID %d", req.Id)
|
||||
|
@ -680,13 +688,13 @@ func (ssa *SQLStorageAuthorityRO) GetOrder(ctx context.Context, req *sapb.OrderR
|
|||
return nil, berrors.NotFoundError("no order found for ID %d", req.Id)
|
||||
}
|
||||
|
||||
v2AuthzIDs, err := authzForOrder(txWithCtx, order.Id)
|
||||
v2AuthzIDs, err := authzForOrder(ctx, tx, order.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
order.V2Authorizations = v2AuthzIDs
|
||||
|
||||
names, err := namesForOrder(txWithCtx, order.Id)
|
||||
names, err := namesForOrder(ctx, tx, order.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -700,7 +708,7 @@ func (ssa *SQLStorageAuthorityRO) GetOrder(ctx context.Context, req *sapb.OrderR
|
|||
order.Names = reversedNames
|
||||
|
||||
// Calculate the status for the order
|
||||
status, err := statusForOrder(txWithCtx, order, ssa.clk.Now())
|
||||
status, err := statusForOrder(ctx, tx, order, ssa.clk.Now())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -772,7 +780,7 @@ func (ssa *SQLStorageAuthorityRO) GetOrderForNames(ctx context.Context, req *sap
|
|||
RegistrationID int64
|
||||
}
|
||||
var err error
|
||||
err = ssa.dbReadOnlyMap.WithContext(ctx).SelectOne(&result, `
|
||||
err = ssa.dbReadOnlyMap.SelectOne(ctx, &result, `
|
||||
SELECT orderID, registrationID
|
||||
FROM orderFqdnSets
|
||||
WHERE setHash = ?
|
||||
|
@ -814,13 +822,13 @@ func (ssa *SQLStorageAuthorityRO) GetAuthorization2(ctx context.Context, req *sa
|
|||
if req.Id == 0 {
|
||||
return nil, errIncompleteRequest
|
||||
}
|
||||
obj, err := ssa.dbReadOnlyMap.Get(authzModel{}, req.Id)
|
||||
obj, err := ssa.dbReadOnlyMap.Get(ctx, authzModel{}, req.Id)
|
||||
if db.IsNoRows(err) && ssa.lagFactor != 0 {
|
||||
// GetAuthorization2 is often called shortly after a new order is created,
|
||||
// sometimes before the order's associated authz rows have propagated to the
|
||||
// read replica yet. If we get a NoRows, wait a little bit and retry, once.
|
||||
ssa.clk.Sleep(ssa.lagFactor)
|
||||
obj, err = ssa.dbReadOnlyMap.Get(authzModel{}, req.Id)
|
||||
obj, err = ssa.dbReadOnlyMap.Get(ctx, authzModel{}, req.Id)
|
||||
if err != nil {
|
||||
if db.IsNoRows(err) {
|
||||
ssa.lagFactorCounter.WithLabelValues("GetAuthorization2", "notfound").Inc()
|
||||
|
@ -890,6 +898,7 @@ func (ssa *SQLStorageAuthorityRO) GetAuthorizations2(ctx context.Context, req *s
|
|||
)
|
||||
|
||||
_, err := ssa.dbReadOnlyMap.Select(
|
||||
ctx,
|
||||
&authzModels,
|
||||
query,
|
||||
params...,
|
||||
|
@ -925,7 +934,8 @@ func (ssa *SQLStorageAuthorityRO) GetPendingAuthorization2(ctx context.Context,
|
|||
return nil, errIncompleteRequest
|
||||
}
|
||||
var am authzModel
|
||||
err := ssa.dbReadOnlyMap.WithContext(ctx).SelectOne(
|
||||
err := ssa.dbReadOnlyMap.SelectOne(
|
||||
ctx,
|
||||
&am,
|
||||
fmt.Sprintf(`SELECT %s FROM authz2 WHERE
|
||||
registrationID = :regID AND
|
||||
|
@ -964,7 +974,7 @@ func (ssa *SQLStorageAuthorityRO) CountPendingAuthorizations2(ctx context.Contex
|
|||
}
|
||||
|
||||
var count int64
|
||||
err := ssa.dbReadOnlyMap.WithContext(ctx).SelectOne(&count,
|
||||
err := ssa.dbReadOnlyMap.SelectOne(ctx, &count,
|
||||
`SELECT COUNT(*) FROM authz2 WHERE
|
||||
registrationID = :regID AND
|
||||
expires > :expires AND
|
||||
|
@ -1003,7 +1013,8 @@ func (ssa *SQLStorageAuthorityRO) GetValidOrderAuthorizations2(ctx context.Conte
|
|||
}
|
||||
|
||||
var ams []authzModel
|
||||
_, err := ssa.dbReadOnlyMap.WithContext(ctx).Select(
|
||||
_, err := ssa.dbReadOnlyMap.Select(
|
||||
ctx,
|
||||
&ams,
|
||||
fmt.Sprintf(`SELECT %s FROM authz2
|
||||
LEFT JOIN orderToAuthz2 ON authz2.ID = orderToAuthz2.authzID
|
||||
|
@ -1050,7 +1061,8 @@ func (ssa *SQLStorageAuthorityRO) CountInvalidAuthorizations2(ctx context.Contex
|
|||
}
|
||||
|
||||
var count int64
|
||||
err := ssa.dbReadOnlyMap.WithContext(ctx).SelectOne(
|
||||
err := ssa.dbReadOnlyMap.SelectOne(
|
||||
ctx,
|
||||
&count,
|
||||
`SELECT COUNT(*) FROM authz2 WHERE
|
||||
registrationID = :regID AND
|
||||
|
@ -1109,6 +1121,7 @@ func (ssa *SQLStorageAuthorityRO) GetValidAuthorizations2(ctx context.Context, r
|
|||
|
||||
var authzModels []authzModel
|
||||
_, err := ssa.dbReadOnlyMap.Select(
|
||||
ctx,
|
||||
&authzModels,
|
||||
query,
|
||||
params...,
|
||||
|
@ -1144,7 +1157,7 @@ func (ssa *SQLStorageAuthorityRO) KeyBlocked(ctx context.Context, req *sapb.KeyB
|
|||
}
|
||||
|
||||
var id int64
|
||||
err := ssa.dbReadOnlyMap.SelectOne(&id, `SELECT ID FROM blockedKeys WHERE keyHash = ?`, req.KeyHash)
|
||||
err := ssa.dbReadOnlyMap.SelectOne(ctx, &id, `SELECT ID FROM blockedKeys WHERE keyHash = ?`, req.KeyHash)
|
||||
if err != nil {
|
||||
if db.IsNoRows(err) {
|
||||
return &sapb.Exists{Exists: false}, nil
|
||||
|
@ -1167,7 +1180,7 @@ func (ssa *SQLStorageAuthorityRO) IncidentsForSerial(ctx context.Context, req *s
|
|||
}
|
||||
|
||||
var activeIncidents []incidentModel
|
||||
_, err := ssa.dbReadOnlyMap.Select(&activeIncidents, `SELECT * FROM incidents WHERE enabled = 1`)
|
||||
_, err := ssa.dbReadOnlyMap.Select(ctx, &activeIncidents, `SELECT * FROM incidents WHERE enabled = 1`)
|
||||
if err != nil {
|
||||
if db.IsNoRows(err) {
|
||||
return &sapb.Incidents{}, nil
|
||||
|
@ -1178,7 +1191,7 @@ func (ssa *SQLStorageAuthorityRO) IncidentsForSerial(ctx context.Context, req *s
|
|||
var incidentsForSerial []*sapb.Incident
|
||||
for _, i := range activeIncidents {
|
||||
var count int
|
||||
err := ssa.dbIncidentsMap.SelectOne(&count, fmt.Sprintf("SELECT COUNT(*) FROM %s WHERE serial = ?",
|
||||
err := ssa.dbIncidentsMap.SelectOne(ctx, &count, fmt.Sprintf("SELECT COUNT(*) FROM %s WHERE serial = ?",
|
||||
i.SerialTable), req.Serial)
|
||||
if err != nil {
|
||||
if db.IsNoRows(err) {
|
||||
|
@ -1293,7 +1306,7 @@ func (ssa *SQLStorageAuthorityRO) GetRevokedCerts(req *sapb.GetRevokedCertsReque
|
|||
return fmt.Errorf("initializing db map: %w", err)
|
||||
}
|
||||
|
||||
rows, err := selector.Query(stream.Context(), clauses, params...)
|
||||
rows, err := selector.QueryContext(stream.Context(), clauses, params...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("reading db: %w", err)
|
||||
}
|
||||
|
@ -1349,7 +1362,8 @@ func (ssa *SQLStorageAuthorityRO) GetMaxExpiration(ctx context.Context, req *emp
|
|||
var model struct {
|
||||
MaxNotAfter time.Time `db:"maxNotAfter"`
|
||||
}
|
||||
err := ssa.dbReadOnlyMap.WithContext(ctx).SelectOne(
|
||||
err := ssa.dbReadOnlyMap.SelectOne(
|
||||
ctx,
|
||||
&model,
|
||||
"SELECT MAX(notAfter) AS maxNotAfter FROM certificateStatus",
|
||||
)
|
||||
|
@ -1365,7 +1379,7 @@ func (ssa *SQLStorageAuthority) GetMaxExpiration(ctx context.Context, req *empty
|
|||
|
||||
// Health implements the grpc.checker interface.
|
||||
func (ssa *SQLStorageAuthorityRO) Health(ctx context.Context) error {
|
||||
err := ssa.dbReadOnlyMap.WithContext(ctx).SelectOne(new(int), "SELECT 1")
|
||||
err := ssa.dbReadOnlyMap.SelectOne(ctx, new(int), "SELECT 1")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -5,14 +5,14 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/go-gorp/gorp/v3"
|
||||
"github.com/letsencrypt/borp"
|
||||
"gopkg.in/go-jose/go-jose.v2"
|
||||
|
||||
"github.com/letsencrypt/boulder/core"
|
||||
"github.com/letsencrypt/boulder/identifier"
|
||||
)
|
||||
|
||||
// BoulderTypeConverter is used by Gorp for storing objects in DB.
|
||||
// BoulderTypeConverter is used by borp for storing objects in DB.
|
||||
type BoulderTypeConverter struct{}
|
||||
|
||||
// ToDb converts a Boulder object to one suitable for the DB representation.
|
||||
|
@ -40,7 +40,7 @@ func (tc BoulderTypeConverter) ToDb(val interface{}) (interface{}, error) {
|
|||
}
|
||||
|
||||
// FromDb converts a DB representation back into a Boulder object.
|
||||
func (tc BoulderTypeConverter) FromDb(target interface{}) (gorp.CustomScanner, bool) {
|
||||
func (tc BoulderTypeConverter) FromDb(target interface{}) (borp.CustomScanner, bool) {
|
||||
switch target.(type) {
|
||||
case *identifier.ACMEIdentifier, *[]core.Challenge, *[]string, *[][]int:
|
||||
binder := func(holder, target interface{}) error {
|
||||
|
@ -58,7 +58,7 @@ func (tc BoulderTypeConverter) FromDb(target interface{}) (gorp.CustomScanner, b
|
|||
}
|
||||
return nil
|
||||
}
|
||||
return gorp.CustomScanner{Holder: new(string), Target: target, Binder: binder}, true
|
||||
return borp.CustomScanner{Holder: new(string), Target: target, Binder: binder}, true
|
||||
case *jose.JSONWebKey:
|
||||
binder := func(holder, target interface{}) error {
|
||||
s, ok := holder.(*string)
|
||||
|
@ -82,7 +82,7 @@ func (tc BoulderTypeConverter) FromDb(target interface{}) (gorp.CustomScanner, b
|
|||
}
|
||||
return nil
|
||||
}
|
||||
return gorp.CustomScanner{Holder: new(string), Target: target, Binder: binder}, true
|
||||
return borp.CustomScanner{Holder: new(string), Target: target, Binder: binder}, true
|
||||
case *core.AcmeStatus:
|
||||
binder := func(holder, target interface{}) error {
|
||||
s, ok := holder.(*string)
|
||||
|
@ -97,7 +97,7 @@ func (tc BoulderTypeConverter) FromDb(target interface{}) (gorp.CustomScanner, b
|
|||
*st = core.AcmeStatus(*s)
|
||||
return nil
|
||||
}
|
||||
return gorp.CustomScanner{Holder: new(string), Target: target, Binder: binder}, true
|
||||
return borp.CustomScanner{Holder: new(string), Target: target, Binder: binder}, true
|
||||
case *core.OCSPStatus:
|
||||
binder := func(holder, target interface{}) error {
|
||||
s, ok := holder.(*string)
|
||||
|
@ -112,8 +112,8 @@ func (tc BoulderTypeConverter) FromDb(target interface{}) (gorp.CustomScanner, b
|
|||
*st = core.OCSPStatus(*s)
|
||||
return nil
|
||||
}
|
||||
return gorp.CustomScanner{Holder: new(string), Target: target, Binder: binder}, true
|
||||
return borp.CustomScanner{Holder: new(string), Target: target, Binder: binder}, true
|
||||
default:
|
||||
return gorp.CustomScanner{}, false
|
||||
return borp.CustomScanner{}, false
|
||||
}
|
||||
}
|
||||
|
|
35
test/db.go
35
test/db.go
|
@ -1,6 +1,7 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"io"
|
||||
|
@ -15,9 +16,9 @@ var (
|
|||
// rows in all tables in a database plus close the database
|
||||
// connection. It is satisfied by *sql.DB.
|
||||
type CleanUpDB interface {
|
||||
Begin() (*sql.Tx, error)
|
||||
Exec(query string, args ...interface{}) (sql.Result, error)
|
||||
Query(query string, args ...interface{}) (*sql.Rows, error)
|
||||
BeginTx(context.Context, *sql.TxOptions) (*sql.Tx, error)
|
||||
ExecContext(context.Context, string, ...interface{}) (sql.Result, error)
|
||||
QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
|
||||
|
||||
io.Closer
|
||||
}
|
||||
|
@ -27,7 +28,7 @@ type CleanUpDB interface {
|
|||
// table as this is used by sql-migrate (https://github.com/rubenv/sql-migrate)
|
||||
// to track migrations. If it encounters an error it fails the tests.
|
||||
func ResetBoulderTestDatabase(t testing.TB) func() {
|
||||
return resetTestDatabase(t, "boulder")
|
||||
return resetTestDatabase(t, context.Background(), "boulder")
|
||||
}
|
||||
|
||||
// ResetIncidentsTestDatabase returns a cleanup function which deletes all rows
|
||||
|
@ -36,20 +37,20 @@ func ResetBoulderTestDatabase(t testing.TB) func() {
|
|||
// (https://github.com/rubenv/sql-migrate) to track migrations. If it encounters
|
||||
// an error it fails the tests.
|
||||
func ResetIncidentsTestDatabase(t testing.TB) func() {
|
||||
return resetTestDatabase(t, "incidents")
|
||||
return resetTestDatabase(t, context.Background(), "incidents")
|
||||
}
|
||||
|
||||
func resetTestDatabase(t testing.TB, dbPrefix string) func() {
|
||||
func resetTestDatabase(t testing.TB, ctx context.Context, dbPrefix string) func() {
|
||||
db, err := sql.Open("mysql", fmt.Sprintf("test_setup@tcp(boulder-proxysql:6033)/%s_sa_test", dbPrefix))
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't create db: %s", err)
|
||||
}
|
||||
err = deleteEverythingInAllTables(db)
|
||||
err = deleteEverythingInAllTables(ctx, db)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to delete everything: %s", err)
|
||||
}
|
||||
return func() {
|
||||
err := deleteEverythingInAllTables(db)
|
||||
err := deleteEverythingInAllTables(ctx, db)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to truncate tables after the test: %s", err)
|
||||
}
|
||||
|
@ -61,8 +62,8 @@ func resetTestDatabase(t testing.TB, dbPrefix string) func() {
|
|||
// available to the CleanUpDB passed in and resets the autoincrement
|
||||
// counters. See allTableNamesInDB for what is meant by "all tables
|
||||
// available". To be used only in test code.
|
||||
func deleteEverythingInAllTables(db CleanUpDB) error {
|
||||
ts, err := allTableNamesInDB(db)
|
||||
func deleteEverythingInAllTables(ctx context.Context, db CleanUpDB) error {
|
||||
ts, err := allTableNamesInDB(ctx, db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -72,22 +73,22 @@ func deleteEverythingInAllTables(db CleanUpDB) error {
|
|||
// another connection to make the deletion on. Note that
|
||||
// `alter table` statements will silently cause transactions
|
||||
// to commit, so we do them outside of the transaction.
|
||||
tx, err := db.Begin()
|
||||
tx, err := db.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to start transaction to delete all rows from table %#v: %s", tn, err)
|
||||
}
|
||||
_, err = tx.Exec("set FOREIGN_KEY_CHECKS = 0")
|
||||
_, err = tx.ExecContext(ctx, "set FOREIGN_KEY_CHECKS = 0")
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to disable FOREIGN_KEY_CHECKS to delete all rows from table %#v: %s", tn, err)
|
||||
}
|
||||
// 1 = 1 here prevents the MariaDB i_am_a_dummy setting from
|
||||
// rejecting the DELETE for not having a WHERE clause.
|
||||
|
||||
_, err = tx.Exec("delete from `" + tn + "` where 1 = 1")
|
||||
_, err = tx.ExecContext(ctx, "delete from `"+tn+"` where 1 = 1")
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to delete all rows from table %#v: %s", tn, err)
|
||||
}
|
||||
_, err = tx.Exec("set FOREIGN_KEY_CHECKS = 1")
|
||||
_, err = tx.ExecContext(ctx, "set FOREIGN_KEY_CHECKS = 1")
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to re-enable FOREIGN_KEY_CHECKS to delete all rows from table %#v: %s", tn, err)
|
||||
}
|
||||
|
@ -96,7 +97,7 @@ func deleteEverythingInAllTables(db CleanUpDB) error {
|
|||
return fmt.Errorf("unable to commit transaction to delete all rows from table %#v: %s", tn, err)
|
||||
}
|
||||
|
||||
_, err = db.Exec("alter table `" + tn + "` AUTO_INCREMENT = 1")
|
||||
_, err = db.ExecContext(ctx, "alter table `"+tn+"` AUTO_INCREMENT = 1")
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to reset autoincrement on table %#v: %s", tn, err)
|
||||
}
|
||||
|
@ -107,8 +108,8 @@ func deleteEverythingInAllTables(db CleanUpDB) error {
|
|||
// allTableNamesInDB returns the names of the tables available to the passed
|
||||
// CleanUpDB. Omits the 'gorp_migrations' table as this is used by sql-migrate
|
||||
// (https://github.com/rubenv/sql-migrate) to track migrations.
|
||||
func allTableNamesInDB(db CleanUpDB) ([]string, error) {
|
||||
r, err := db.Query("select table_name from information_schema.tables t where t.table_schema = DATABASE() and t.table_name != 'gorp_migrations';")
|
||||
func allTableNamesInDB(ctx context.Context, db CleanUpDB) ([]string, error) {
|
||||
r, err := db.QueryContext(ctx, "select table_name from information_schema.tables t where t.table_schema = DATABASE() and t.table_name != 'gorp_migrations';")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
package integration
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
|
@ -92,16 +93,18 @@ func TestIssuanceCertStorageFailed(t *testing.T) {
|
|||
t.Parallel()
|
||||
os.Setenv("DIRECTORY", "http://boulder.service.consul:4001/directory")
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// This test is gated on the StoreLintingCertificateInsteadOfPrecertificate
|
||||
// feature flag.
|
||||
if os.Getenv("BOULDER_CONFIG_DIR") != "test/config-next" {
|
||||
t.Skip("Skipping test because it requires the StoreLintingCertificateInsteadOfPrecertificate feature flag")
|
||||
}
|
||||
|
||||
|
||||
db, err := sql.Open("mysql", vars.DBConnSAIntegrationFullPerms)
|
||||
test.AssertNotError(t, err, "failed to open db connection")
|
||||
|
||||
_, err = db.Exec(`DROP TRIGGER IF EXISTS fail_ready`)
|
||||
_, err = db.ExecContext(ctx, `DROP TRIGGER IF EXISTS fail_ready`)
|
||||
test.AssertNotError(t, err, "failed to drop trigger")
|
||||
|
||||
// Make a specific update to certificateStatus fail, for this test but not others.
|
||||
|
@ -114,7 +117,7 @@ func TestIssuanceCertStorageFailed(t *testing.T) {
|
|||
// NOTE: CREATE and DROP TRIGGER do not work in prepared statements. Go's
|
||||
// database/sql will automatically try to use a prepared statement if you pass
|
||||
// any arguments to Exec besides the query itself, so don't do that.
|
||||
_, err = db.Exec(`
|
||||
_, err = db.ExecContext(ctx, `
|
||||
CREATE TRIGGER fail_ready
|
||||
BEFORE UPDATE ON certificateStatus
|
||||
FOR EACH ROW BEGIN
|
||||
|
@ -130,8 +133,8 @@ func TestIssuanceCertStorageFailed(t *testing.T) {
|
|||
END
|
||||
`)
|
||||
test.AssertNotError(t, err, "failed to create trigger")
|
||||
|
||||
defer db.Exec(`DROP TRIGGER IF EXISTS fail_ready`)
|
||||
|
||||
defer db.ExecContext(ctx, `DROP TRIGGER IF EXISTS fail_ready`)
|
||||
|
||||
certKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
test.AssertNotError(t, err, "creating random cert key")
|
||||
|
@ -157,7 +160,7 @@ func TestIssuanceCertStorageFailed(t *testing.T) {
|
|||
test.AssertNotError(t, err, fmt.Sprintf("revoking via admin-revoker: %s", string(output)))
|
||||
|
||||
_, err = ocsp_helper.Req(cert,
|
||||
ocsp_helper.DefaultConfig.WithExpectStatus(ocsp.Revoked).WithExpectReason(ocsp.Unspecified))
|
||||
ocsp_helper.DefaultConfig.WithExpectStatus(ocsp.Revoked).WithExpectReason(ocsp.Unspecified))
|
||||
|
||||
// ---- Test revocation by key ----
|
||||
blockMyKeyDomain := "blockmykey.wantserror.com"
|
||||
|
@ -186,10 +189,10 @@ func TestIssuanceCertStorageFailed(t *testing.T) {
|
|||
1,
|
||||
)
|
||||
test.AssertNotError(t, err, "revoking second certificate")
|
||||
|
||||
|
||||
for i := 0; i < 300; i++ {
|
||||
_, err = ocsp_helper.Req(successfulCert,
|
||||
ocsp_helper.DefaultConfig.WithExpectStatus(ocsp.Revoked).WithExpectReason(ocsp.KeyCompromise))
|
||||
ocsp_helper.DefaultConfig.WithExpectStatus(ocsp.Revoked).WithExpectReason(ocsp.KeyCompromise))
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
language: go
|
||||
go:
|
||||
- "1.15.x"
|
||||
- "1.16.x"
|
||||
- tip
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- go: tip
|
||||
|
||||
services:
|
||||
- mysql
|
||||
- postgresql
|
||||
- sqlite3
|
||||
|
||||
env:
|
||||
global:
|
||||
- secure: RriLxF6+2yMl67hdVv8ImXlu0h62mhcpqjaOgYNU+IEbUQ7hx96CKY6gkpYubW3BgApvF5RH6j3+HKvh2kGp0XhDOYOQCODfBSaSipZ5Aa5RKjsEYLtuVIobvJ80awR9hUeql69+WXs0/s72WThG0qTbOUY4pqHWfteeY235hWM=
|
||||
|
||||
install:
|
||||
- go get -t -d
|
||||
- go get -t -d -tags integration
|
||||
|
||||
before_script:
|
||||
- mysql -e "CREATE DATABASE gorptest;"
|
||||
- mysql -u root -e "GRANT ALL ON gorptest.* TO gorptest@localhost IDENTIFIED BY 'gorptest'"
|
||||
- psql -c "CREATE DATABASE gorptest;" -U postgres
|
||||
- psql -c "CREATE USER "gorptest" WITH SUPERUSER PASSWORD 'gorptest';" -U postgres
|
||||
- go get github.com/lib/pq
|
||||
- go get github.com/mattn/go-sqlite3
|
||||
- go get github.com/ziutek/mymysql/godrv
|
||||
- go get github.com/go-sql-driver/mysql
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
- go get github.com/mattn/goveralls
|
||||
|
||||
script: ./test_all.sh
|
|
@ -1,139 +0,0 @@
|
|||
// Copyright 2012 James Cooper. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gorp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Implementation of Dialect for Oracle databases.
|
||||
type OracleDialect struct{}
|
||||
|
||||
func (d OracleDialect) QuerySuffix() string { return "" }
|
||||
|
||||
func (d OracleDialect) CreateIndexSuffix() string { return "" }
|
||||
|
||||
func (d OracleDialect) DropIndexSuffix() string { return "" }
|
||||
|
||||
func (d OracleDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string {
|
||||
switch val.Kind() {
|
||||
case reflect.Ptr:
|
||||
return d.ToSqlType(val.Elem(), maxsize, isAutoIncr)
|
||||
case reflect.Bool:
|
||||
return "boolean"
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32:
|
||||
if isAutoIncr {
|
||||
return "serial"
|
||||
}
|
||||
return "integer"
|
||||
case reflect.Int64, reflect.Uint64:
|
||||
if isAutoIncr {
|
||||
return "bigserial"
|
||||
}
|
||||
return "bigint"
|
||||
case reflect.Float64:
|
||||
return "double precision"
|
||||
case reflect.Float32:
|
||||
return "real"
|
||||
case reflect.Slice:
|
||||
if val.Elem().Kind() == reflect.Uint8 {
|
||||
return "bytea"
|
||||
}
|
||||
}
|
||||
|
||||
switch val.Name() {
|
||||
case "NullInt64":
|
||||
return "bigint"
|
||||
case "NullFloat64":
|
||||
return "double precision"
|
||||
case "NullBool":
|
||||
return "boolean"
|
||||
case "NullTime", "Time":
|
||||
return "timestamp with time zone"
|
||||
}
|
||||
|
||||
if maxsize > 0 {
|
||||
return fmt.Sprintf("varchar(%d)", maxsize)
|
||||
} else {
|
||||
return "text"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Returns empty string
|
||||
func (d OracleDialect) AutoIncrStr() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (d OracleDialect) AutoIncrBindValue() string {
|
||||
return "NULL"
|
||||
}
|
||||
|
||||
func (d OracleDialect) AutoIncrInsertSuffix(col *ColumnMap) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Returns suffix
|
||||
func (d OracleDialect) CreateTableSuffix() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (d OracleDialect) TruncateClause() string {
|
||||
return "truncate"
|
||||
}
|
||||
|
||||
// Returns "$(i+1)"
|
||||
func (d OracleDialect) BindVar(i int) string {
|
||||
return fmt.Sprintf(":%d", i+1)
|
||||
}
|
||||
|
||||
// After executing the insert uses the ColMap IdQuery to get the generated id
|
||||
func (d OracleDialect) InsertQueryToTarget(exec SqlExecutor, insertSql, idSql string, target interface{}, params ...interface{}) error {
|
||||
_, err := exec.Exec(insertSql, params...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
id, err := exec.SelectInt(idSql)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch target.(type) {
|
||||
case *int64:
|
||||
*(target.(*int64)) = id
|
||||
case *int32:
|
||||
*(target.(*int32)) = int32(id)
|
||||
case int:
|
||||
*(target.(*int)) = int(id)
|
||||
default:
|
||||
return fmt.Errorf("Id field can be int, int32 or int64")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d OracleDialect) QuoteField(f string) string {
|
||||
return `"` + strings.ToUpper(f) + `"`
|
||||
}
|
||||
|
||||
func (d OracleDialect) QuotedTableForQuery(schema string, table string) string {
|
||||
if strings.TrimSpace(schema) == "" {
|
||||
return d.QuoteField(table)
|
||||
}
|
||||
|
||||
return schema + "." + d.QuoteField(table)
|
||||
}
|
||||
|
||||
func (d OracleDialect) IfSchemaNotExists(command, schema string) string {
|
||||
return fmt.Sprintf("%s if not exists", command)
|
||||
}
|
||||
|
||||
func (d OracleDialect) IfTableExists(command, schema, table string) string {
|
||||
return fmt.Sprintf("%s if exists", command)
|
||||
}
|
||||
|
||||
func (d OracleDialect) IfTableNotExists(command, schema, table string) string {
|
||||
return fmt.Sprintf("%s if not exists", command)
|
||||
}
|
|
@ -1,149 +0,0 @@
|
|||
// Copyright 2012 James Cooper. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gorp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type PostgresDialect struct {
|
||||
suffix string
|
||||
LowercaseFields bool
|
||||
}
|
||||
|
||||
func (d PostgresDialect) QuerySuffix() string { return ";" }
|
||||
|
||||
func (d PostgresDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string {
|
||||
switch val.Kind() {
|
||||
case reflect.Ptr:
|
||||
return d.ToSqlType(val.Elem(), maxsize, isAutoIncr)
|
||||
case reflect.Bool:
|
||||
return "boolean"
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32:
|
||||
if isAutoIncr {
|
||||
return "serial"
|
||||
}
|
||||
return "integer"
|
||||
case reflect.Int64, reflect.Uint64:
|
||||
if isAutoIncr {
|
||||
return "bigserial"
|
||||
}
|
||||
return "bigint"
|
||||
case reflect.Float64:
|
||||
return "double precision"
|
||||
case reflect.Float32:
|
||||
return "real"
|
||||
case reflect.Slice:
|
||||
if val.Elem().Kind() == reflect.Uint8 {
|
||||
return "bytea"
|
||||
}
|
||||
}
|
||||
|
||||
switch val.Name() {
|
||||
case "NullInt64":
|
||||
return "bigint"
|
||||
case "NullFloat64":
|
||||
return "double precision"
|
||||
case "NullBool":
|
||||
return "boolean"
|
||||
case "Time", "NullTime":
|
||||
return "timestamp with time zone"
|
||||
}
|
||||
|
||||
if maxsize > 0 {
|
||||
return fmt.Sprintf("varchar(%d)", maxsize)
|
||||
} else {
|
||||
return "text"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Returns empty string
|
||||
func (d PostgresDialect) AutoIncrStr() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (d PostgresDialect) AutoIncrBindValue() string {
|
||||
return "default"
|
||||
}
|
||||
|
||||
func (d PostgresDialect) AutoIncrInsertSuffix(col *ColumnMap) string {
|
||||
return " returning " + d.QuoteField(col.ColumnName)
|
||||
}
|
||||
|
||||
// Returns suffix
|
||||
func (d PostgresDialect) CreateTableSuffix() string {
|
||||
return d.suffix
|
||||
}
|
||||
|
||||
func (d PostgresDialect) CreateIndexSuffix() string {
|
||||
return "using"
|
||||
}
|
||||
|
||||
func (d PostgresDialect) DropIndexSuffix() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (d PostgresDialect) TruncateClause() string {
|
||||
return "truncate"
|
||||
}
|
||||
|
||||
func (d PostgresDialect) SleepClause(s time.Duration) string {
|
||||
return fmt.Sprintf("pg_sleep(%f)", s.Seconds())
|
||||
}
|
||||
|
||||
// Returns "$(i+1)"
|
||||
func (d PostgresDialect) BindVar(i int) string {
|
||||
return fmt.Sprintf("$%d", i+1)
|
||||
}
|
||||
|
||||
func (d PostgresDialect) InsertAutoIncrToTarget(exec SqlExecutor, insertSql string, target interface{}, params ...interface{}) error {
|
||||
rows, err := exec.Query(insertSql, params...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
if !rows.Next() {
|
||||
return fmt.Errorf("No serial value returned for insert: %s Encountered error: %s", insertSql, rows.Err())
|
||||
}
|
||||
if err := rows.Scan(target); err != nil {
|
||||
return err
|
||||
}
|
||||
if rows.Next() {
|
||||
return fmt.Errorf("more than two serial value returned for insert: %s", insertSql)
|
||||
}
|
||||
return rows.Err()
|
||||
}
|
||||
|
||||
func (d PostgresDialect) QuoteField(f string) string {
|
||||
if d.LowercaseFields {
|
||||
return `"` + strings.ToLower(f) + `"`
|
||||
}
|
||||
return `"` + f + `"`
|
||||
}
|
||||
|
||||
func (d PostgresDialect) QuotedTableForQuery(schema string, table string) string {
|
||||
if strings.TrimSpace(schema) == "" {
|
||||
return d.QuoteField(table)
|
||||
}
|
||||
|
||||
return schema + "." + d.QuoteField(table)
|
||||
}
|
||||
|
||||
func (d PostgresDialect) IfSchemaNotExists(command, schema string) string {
|
||||
return fmt.Sprintf("%s if not exists", command)
|
||||
}
|
||||
|
||||
func (d PostgresDialect) IfTableExists(command, schema, table string) string {
|
||||
return fmt.Sprintf("%s if exists", command)
|
||||
}
|
||||
|
||||
func (d PostgresDialect) IfTableNotExists(command, schema, table string) string {
|
||||
return fmt.Sprintf("%s if not exists", command)
|
||||
}
|
|
@ -1,152 +0,0 @@
|
|||
// Copyright 2012 James Cooper. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gorp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type SnowflakeDialect struct {
|
||||
suffix string
|
||||
LowercaseFields bool
|
||||
}
|
||||
|
||||
func (d SnowflakeDialect) QuerySuffix() string { return ";" }
|
||||
|
||||
func (d SnowflakeDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string {
|
||||
switch val.Kind() {
|
||||
case reflect.Ptr:
|
||||
return d.ToSqlType(val.Elem(), maxsize, isAutoIncr)
|
||||
case reflect.Bool:
|
||||
return "boolean"
|
||||
case reflect.Int,
|
||||
reflect.Int8,
|
||||
reflect.Int16,
|
||||
reflect.Int32,
|
||||
reflect.Uint,
|
||||
reflect.Uint8,
|
||||
reflect.Uint16,
|
||||
reflect.Uint32:
|
||||
|
||||
if isAutoIncr {
|
||||
return "serial"
|
||||
}
|
||||
return "integer"
|
||||
case reflect.Int64, reflect.Uint64:
|
||||
if isAutoIncr {
|
||||
return "bigserial"
|
||||
}
|
||||
return "bigint"
|
||||
case reflect.Float64:
|
||||
return "double precision"
|
||||
case reflect.Float32:
|
||||
return "real"
|
||||
case reflect.Slice:
|
||||
if val.Elem().Kind() == reflect.Uint8 {
|
||||
return "binary"
|
||||
}
|
||||
}
|
||||
|
||||
switch val.Name() {
|
||||
case "NullInt64":
|
||||
return "bigint"
|
||||
case "NullFloat64":
|
||||
return "double precision"
|
||||
case "NullBool":
|
||||
return "boolean"
|
||||
case "Time", "NullTime":
|
||||
return "timestamp with time zone"
|
||||
}
|
||||
|
||||
if maxsize > 0 {
|
||||
return fmt.Sprintf("varchar(%d)", maxsize)
|
||||
} else {
|
||||
return "text"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Returns empty string
|
||||
func (d SnowflakeDialect) AutoIncrStr() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (d SnowflakeDialect) AutoIncrBindValue() string {
|
||||
return "default"
|
||||
}
|
||||
|
||||
func (d SnowflakeDialect) AutoIncrInsertSuffix(col *ColumnMap) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Returns suffix
|
||||
func (d SnowflakeDialect) CreateTableSuffix() string {
|
||||
return d.suffix
|
||||
}
|
||||
|
||||
func (d SnowflakeDialect) CreateIndexSuffix() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (d SnowflakeDialect) DropIndexSuffix() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (d SnowflakeDialect) TruncateClause() string {
|
||||
return "truncate"
|
||||
}
|
||||
|
||||
// Returns "$(i+1)"
|
||||
func (d SnowflakeDialect) BindVar(i int) string {
|
||||
return "?"
|
||||
}
|
||||
|
||||
func (d SnowflakeDialect) InsertAutoIncrToTarget(exec SqlExecutor, insertSql string, target interface{}, params ...interface{}) error {
|
||||
rows, err := exec.Query(insertSql, params...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
if !rows.Next() {
|
||||
return fmt.Errorf("No serial value returned for insert: %s Encountered error: %s", insertSql, rows.Err())
|
||||
}
|
||||
if err := rows.Scan(target); err != nil {
|
||||
return err
|
||||
}
|
||||
if rows.Next() {
|
||||
return fmt.Errorf("more than two serial value returned for insert: %s", insertSql)
|
||||
}
|
||||
return rows.Err()
|
||||
}
|
||||
|
||||
func (d SnowflakeDialect) QuoteField(f string) string {
|
||||
if d.LowercaseFields {
|
||||
return `"` + strings.ToLower(f) + `"`
|
||||
}
|
||||
return `"` + f + `"`
|
||||
}
|
||||
|
||||
func (d SnowflakeDialect) QuotedTableForQuery(schema string, table string) string {
|
||||
if strings.TrimSpace(schema) == "" {
|
||||
return d.QuoteField(table)
|
||||
}
|
||||
|
||||
return schema + "." + d.QuoteField(table)
|
||||
}
|
||||
|
||||
func (d SnowflakeDialect) IfSchemaNotExists(command, schema string) string {
|
||||
return fmt.Sprintf("%s if not exists", command)
|
||||
}
|
||||
|
||||
func (d SnowflakeDialect) IfTableExists(command, schema, table string) string {
|
||||
return fmt.Sprintf("%s if exists", command)
|
||||
}
|
||||
|
||||
func (d SnowflakeDialect) IfTableNotExists(command, schema, table string) string {
|
||||
return fmt.Sprintf("%s if not exists", command)
|
||||
}
|
|
@ -1,145 +0,0 @@
|
|||
// Copyright 2012 James Cooper. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gorp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Implementation of Dialect for Microsoft SQL Server databases.
|
||||
// Use gorp.SqlServerDialect{"2005"} for legacy datatypes.
|
||||
// Tested with driver: github.com/denisenkom/go-mssqldb
|
||||
|
||||
type SqlServerDialect struct {
|
||||
|
||||
// If set to "2005" legacy datatypes will be used
|
||||
Version string
|
||||
}
|
||||
|
||||
func (d SqlServerDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string {
|
||||
switch val.Kind() {
|
||||
case reflect.Ptr:
|
||||
return d.ToSqlType(val.Elem(), maxsize, isAutoIncr)
|
||||
case reflect.Bool:
|
||||
return "bit"
|
||||
case reflect.Int8:
|
||||
return "tinyint"
|
||||
case reflect.Uint8:
|
||||
return "smallint"
|
||||
case reflect.Int16:
|
||||
return "smallint"
|
||||
case reflect.Uint16:
|
||||
return "int"
|
||||
case reflect.Int, reflect.Int32:
|
||||
return "int"
|
||||
case reflect.Uint, reflect.Uint32:
|
||||
return "bigint"
|
||||
case reflect.Int64:
|
||||
return "bigint"
|
||||
case reflect.Uint64:
|
||||
return "numeric(20,0)"
|
||||
case reflect.Float32:
|
||||
return "float(24)"
|
||||
case reflect.Float64:
|
||||
return "float(53)"
|
||||
case reflect.Slice:
|
||||
if val.Elem().Kind() == reflect.Uint8 {
|
||||
return "varbinary"
|
||||
}
|
||||
}
|
||||
|
||||
switch val.Name() {
|
||||
case "NullInt64":
|
||||
return "bigint"
|
||||
case "NullFloat64":
|
||||
return "float(53)"
|
||||
case "NullBool":
|
||||
return "bit"
|
||||
case "NullTime", "Time":
|
||||
if d.Version == "2005" {
|
||||
return "datetime"
|
||||
}
|
||||
return "datetime2"
|
||||
}
|
||||
|
||||
if maxsize < 1 {
|
||||
if d.Version == "2005" {
|
||||
maxsize = 255
|
||||
} else {
|
||||
return fmt.Sprintf("nvarchar(max)")
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("nvarchar(%d)", maxsize)
|
||||
}
|
||||
|
||||
// Returns auto_increment
|
||||
func (d SqlServerDialect) AutoIncrStr() string {
|
||||
return "identity(0,1)"
|
||||
}
|
||||
|
||||
// Empty string removes autoincrement columns from the INSERT statements.
|
||||
func (d SqlServerDialect) AutoIncrBindValue() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (d SqlServerDialect) AutoIncrInsertSuffix(col *ColumnMap) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (d SqlServerDialect) CreateTableSuffix() string { return ";" }
|
||||
|
||||
func (d SqlServerDialect) TruncateClause() string {
|
||||
return "truncate table"
|
||||
}
|
||||
|
||||
// Returns "?"
|
||||
func (d SqlServerDialect) BindVar(i int) string {
|
||||
return "?"
|
||||
}
|
||||
|
||||
func (d SqlServerDialect) InsertAutoIncr(exec SqlExecutor, insertSql string, params ...interface{}) (int64, error) {
|
||||
return standardInsertAutoIncr(exec, insertSql, params...)
|
||||
}
|
||||
|
||||
func (d SqlServerDialect) QuoteField(f string) string {
|
||||
return "[" + strings.Replace(f, "]", "]]", -1) + "]"
|
||||
}
|
||||
|
||||
func (d SqlServerDialect) QuotedTableForQuery(schema string, table string) string {
|
||||
if strings.TrimSpace(schema) == "" {
|
||||
return d.QuoteField(table)
|
||||
}
|
||||
return d.QuoteField(schema) + "." + d.QuoteField(table)
|
||||
}
|
||||
|
||||
func (d SqlServerDialect) QuerySuffix() string { return ";" }
|
||||
|
||||
func (d SqlServerDialect) IfSchemaNotExists(command, schema string) string {
|
||||
s := fmt.Sprintf("if schema_id(N'%s') is null %s", schema, command)
|
||||
return s
|
||||
}
|
||||
|
||||
func (d SqlServerDialect) IfTableExists(command, schema, table string) string {
|
||||
var schema_clause string
|
||||
if strings.TrimSpace(schema) != "" {
|
||||
schema_clause = fmt.Sprintf("%s.", d.QuoteField(schema))
|
||||
}
|
||||
s := fmt.Sprintf("if object_id('%s%s') is not null %s", schema_clause, d.QuoteField(table), command)
|
||||
return s
|
||||
}
|
||||
|
||||
func (d SqlServerDialect) IfTableNotExists(command, schema, table string) string {
|
||||
var schema_clause string
|
||||
if strings.TrimSpace(schema) != "" {
|
||||
schema_clause = fmt.Sprintf("%s.", schema)
|
||||
}
|
||||
s := fmt.Sprintf("if object_id('%s%s') is null %s", schema_clause, table, command)
|
||||
return s
|
||||
}
|
||||
|
||||
func (d SqlServerDialect) CreateIndexSuffix() string { return "" }
|
||||
func (d SqlServerDialect) DropIndexSuffix() string { return "" }
|
|
@ -1,11 +0,0 @@
|
|||
// Copyright 2012 James Cooper. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package gorp provides a simple way to marshal Go structs to and from
|
||||
// SQL databases. It uses the database/sql package, and should work with any
|
||||
// compliant database/sql driver.
|
||||
//
|
||||
// Source code and project home:
|
||||
// https://github.com/go-gorp/gorp
|
||||
package gorp
|
|
@ -0,0 +1 @@
|
|||
* @letsencrypt/boulder-developers
|
240
vendor/github.com/go-gorp/gorp/v3/README.md → vendor/github.com/letsencrypt/borp/README.md
generated
vendored
240
vendor/github.com/go-gorp/gorp/v3/README.md → vendor/github.com/letsencrypt/borp/README.md
generated
vendored
|
@ -1,107 +1,16 @@
|
|||
# Go Relational Persistence
|
||||
# Boulder's Object Relational Persistence
|
||||
|
||||
[](https://github.com/go-gorp/gorp/actions)
|
||||
[](https://github.com/go-gorp/gorp/issues)
|
||||
[](https://pkg.go.dev/github.com/go-gorp/gorp/v3)
|
||||
Once upon a time, there was [gorp](https://github.com/go-gorp/gorp/). Gorp was
|
||||
good, and Let's Encrypt's adopted it for Boulder's database persistence layer.
|
||||
The maintainers became busy with other projects, and gorp stopped getting
|
||||
updated. However, it's still very useful to Boulder, and there are a few tweaks
|
||||
we'd like to make for our own purposes, so we've forked it.
|
||||
|
||||
### Update 2016-11-13: Future versions
|
||||
We maintain this primarily for Boulder's own use, and intend to make some API
|
||||
breaking changes during 2023. You are welcome to use our fork if you like it, but
|
||||
we do not expect to spend a lot of time maintaining it for non-Boulder uses.
|
||||
|
||||
As many of the maintainers have become busy with other projects,
|
||||
progress toward the ever-elusive v2 has slowed to the point that we're
|
||||
only occasionally making progress outside of merging pull requests.
|
||||
In the interest of continuing to release, I'd like to lean toward a
|
||||
more maintainable path forward.
|
||||
|
||||
For the moment, I am releasing a v2 tag with the current feature set
|
||||
from master, as some of those features have been actively used and
|
||||
relied on by more than one project. Our next goal is to continue
|
||||
cleaning up the code base with non-breaking changes as much as
|
||||
possible, but if/when a breaking change is needed, we'll just release
|
||||
new versions. This allows us to continue development at whatever pace
|
||||
we're capable of, without delaying the release of features or refusing
|
||||
PRs.
|
||||
|
||||
## Introduction
|
||||
|
||||
I hesitate to call gorp an ORM. Go doesn't really have objects, at
|
||||
least not in the classic Smalltalk/Java sense. There goes the "O".
|
||||
gorp doesn't know anything about the relationships between your
|
||||
structs (at least not yet). So the "R" is questionable too (but I use
|
||||
it in the name because, well, it seemed more clever).
|
||||
|
||||
The "M" is alive and well. Given some Go structs and a database, gorp
|
||||
should remove a fair amount of boilerplate busy-work from your code.
|
||||
|
||||
I hope that gorp saves you time, minimizes the drudgery of getting
|
||||
data in and out of your database, and helps your code focus on
|
||||
algorithms, not infrastructure.
|
||||
|
||||
* Bind struct fields to table columns via API or tag
|
||||
* Support for embedded structs
|
||||
* Support for transactions
|
||||
* Forward engineer db schema from structs (great for unit tests)
|
||||
* Pre/post insert/update/delete hooks
|
||||
* Automatically generate insert/update/delete statements for a struct
|
||||
* Automatic binding of auto increment PKs back to struct after insert
|
||||
* Delete by primary key(s)
|
||||
* Select by primary key(s)
|
||||
* Optional trace sql logging
|
||||
* Bind arbitrary SQL queries to a struct
|
||||
* Bind slice to SELECT query results without type assertions
|
||||
* Use positional or named bind parameters in custom SELECT queries
|
||||
* Optional optimistic locking using a version column (for
|
||||
update/deletes)
|
||||
|
||||
## Installation
|
||||
|
||||
Use `go get` or your favorite vendoring tool, using whichever import
|
||||
path you'd like.
|
||||
|
||||
## Versioning
|
||||
|
||||
We use semantic version tags. Feel free to import through `gopkg.in`
|
||||
(e.g. `gopkg.in/gorp.v2`) to get the latest tag for a major version,
|
||||
or check out the tag using your favorite vendoring tool.
|
||||
|
||||
Development is not very active right now, but we have plans to
|
||||
restructure `gorp` as we continue to move toward a more extensible
|
||||
system. Whenever a breaking change is needed, the major version will
|
||||
be bumped.
|
||||
|
||||
The `master` branch is where all development is done, and breaking
|
||||
changes may happen from time to time. That said, if you want to live
|
||||
on the bleeding edge and are comfortable updating your code when we
|
||||
make a breaking change, you may use `github.com/go-gorp/gorp` as your
|
||||
import path.
|
||||
|
||||
Check the version tags to see what's available. We'll make a good
|
||||
faith effort to add badges for new versions, but we make no
|
||||
guarantees.
|
||||
|
||||
## Supported Go versions
|
||||
|
||||
This package is guaranteed to be compatible with the latest 2 major
|
||||
versions of Go.
|
||||
|
||||
Any earlier versions are only supported on a best effort basis and can
|
||||
be dropped any time. Go has a great compatibility promise. Upgrading
|
||||
your program to a newer version of Go should never really be a
|
||||
problem.
|
||||
|
||||
## Migration guide
|
||||
|
||||
#### Pre-v2 to v2
|
||||
Automatic mapping of the version column used in optimistic locking has
|
||||
been removed as it could cause problems if the type was not int. The
|
||||
version column must now explicitly be set with
|
||||
`tablemap.SetVersionCol()`.
|
||||
|
||||
## Help/Support
|
||||
|
||||
Use our [`gitter` channel](https://gitter.im/go-gorp/gorp). We used
|
||||
to use IRC, but with most of us being pulled in many directions, we
|
||||
often need the email notifications from `gitter` to yell at us to sign
|
||||
in.
|
||||
In particular we maintain this to use with MariaDB 10.5+ and InnoDB.
|
||||
|
||||
## Quickstart
|
||||
|
||||
|
@ -110,7 +19,7 @@ package main
|
|||
|
||||
import (
|
||||
"database/sql"
|
||||
"gopkg.in/gorp.v1"
|
||||
"github.com/letsencrypt/borp"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
"log"
|
||||
"time"
|
||||
|
@ -146,9 +55,6 @@ func main() {
|
|||
|
||||
// fetch one row - note use of "post_id" instead of "Id" since column is aliased
|
||||
//
|
||||
// Postgres users should use $1 instead of ? placeholders
|
||||
// See 'Known Issues' below
|
||||
//
|
||||
err = dbmap.SelectOne(&p2, "select * from posts where post_id=?", p2.Id)
|
||||
checkErr(err, "SelectOne failed")
|
||||
log.Println("p2 row:", p2)
|
||||
|
@ -195,14 +101,14 @@ func newPost(title, body string) Post {
|
|||
}
|
||||
}
|
||||
|
||||
func initDb() *gorp.DbMap {
|
||||
func initDb() *borp.DbMap {
|
||||
// connect to db using standard Go database/sql API
|
||||
// use whatever database/sql driver you wish
|
||||
db, err := sql.Open("sqlite3", "/tmp/post_db.bin")
|
||||
checkErr(err, "sql.Open failed")
|
||||
|
||||
// construct a gorp DbMap
|
||||
dbmap := &gorp.DbMap{Db: db, Dialect: gorp.SqliteDialect{}}
|
||||
// construct a borp DbMap
|
||||
dbmap := &borp.DbMap{Db: db, Dialect: borp.SqliteDialect{}}
|
||||
|
||||
// add a table, setting the table name to 'posts' and
|
||||
// specifying that the Id property is an auto incrementing PK
|
||||
|
@ -249,7 +155,7 @@ type Person struct {
|
|||
// Example of using tags to alias fields to column names
|
||||
// The 'db' value is the column name
|
||||
//
|
||||
// A hyphen will cause gorp to skip this field, similar to the
|
||||
// A hyphen will cause borp to skip this field, similar to the
|
||||
// Go json package.
|
||||
//
|
||||
// This is equivalent to using the ColMap methods:
|
||||
|
@ -275,10 +181,10 @@ Then create a mapper, typically you'd do this one time at app startup:
|
|||
// use whatever database/sql driver you wish
|
||||
db, err := sql.Open("mymysql", "tcp:localhost:3306*mydb/myuser/mypassword")
|
||||
|
||||
// construct a gorp DbMap
|
||||
dbmap := &gorp.DbMap{Db: db, Dialect: gorp.MySQLDialect{"InnoDB", "UTF8"}}
|
||||
// construct a borp DbMap
|
||||
dbmap := &borp.DbMap{Db: db, Dialect: borp.MySQLDialect{"InnoDB", "UTF8"}}
|
||||
|
||||
// register the structs you wish to use with gorp
|
||||
// register the structs you wish to use with borp
|
||||
// you can also use the shorter dbmap.AddTable() if you
|
||||
// don't want to override the table name
|
||||
//
|
||||
|
@ -292,7 +198,7 @@ t3 := dbmap.AddTableWithName(Product{}, "product_test").SetKeys(true, "Id")
|
|||
|
||||
### Struct Embedding
|
||||
|
||||
gorp supports embedding structs. For example:
|
||||
borp supports embedding structs. For example:
|
||||
|
||||
```go
|
||||
type Names struct {
|
||||
|
@ -309,12 +215,12 @@ es := &WithEmbeddedStruct{-1, Names{FirstName: "Alice", LastName: "Smith"}}
|
|||
err := dbmap.Insert(es)
|
||||
```
|
||||
|
||||
See the `TestWithEmbeddedStruct` function in `gorp_test.go` for a full example.
|
||||
See the `TestWithEmbeddedStruct` function in `borp_test.go` for a full example.
|
||||
|
||||
### Create/Drop Tables ###
|
||||
|
||||
Automatically create / drop registered tables. This is useful for unit tests
|
||||
but is entirely optional. You can of course use gorp with tables created manually,
|
||||
but is entirely optional. You can of course use borp with tables created manually,
|
||||
or with a separate migration tool (like [sql-migrate](https://github.com/rubenv/sql-migrate), [goose](https://bitbucket.org/liamstask/goose) or [migrate](https://github.com/mattes/migrate)).
|
||||
|
||||
```go
|
||||
|
@ -333,9 +239,9 @@ dbmap.DropTables()
|
|||
|
||||
Optionally you can pass in a logger to trace all SQL statements.
|
||||
I recommend enabling this initially while you're getting the feel for what
|
||||
gorp is doing on your behalf.
|
||||
borp is doing on your behalf.
|
||||
|
||||
Gorp defines a `GorpLogger` interface that Go's built in `log.Logger` satisfies.
|
||||
Borp defines a `GorpLogger` interface that Go's built in `log.Logger` satisfies.
|
||||
However, you can write your own `GorpLogger` implementation, or use a package such
|
||||
as `glog` if you want more control over how statements are logged.
|
||||
|
||||
|
@ -415,7 +321,7 @@ var post Post
|
|||
err := dbmap.SelectOne(&post, "select * from post where id=?", id)
|
||||
```
|
||||
|
||||
Want to do joins? Just write the SQL and the struct. gorp will bind them:
|
||||
Want to do joins? Just write the SQL and the struct. Borp will bind them:
|
||||
|
||||
```go
|
||||
// Define a type for your join
|
||||
|
@ -459,10 +365,10 @@ if reflect.DeepEqual(list[0], expected) {
|
|||
|
||||
#### SELECT string or int64
|
||||
|
||||
gorp provides a few convenience methods for selecting a single string or int64.
|
||||
Borp provides a few convenience methods for selecting a single string or int64.
|
||||
|
||||
```go
|
||||
// select single int64 from db (use $1 instead of ? for postgresql)
|
||||
// select single int64 from db
|
||||
i64, err := dbmap.SelectInt("select count(*) from foo where blah=?", blahVal)
|
||||
|
||||
// select single string from db:
|
||||
|
@ -472,8 +378,9 @@ s, err := dbmap.SelectStr("select name from foo where blah=?", blahVal)
|
|||
|
||||
#### Named bind parameters
|
||||
|
||||
You may use a map or struct to bind parameters by name. This is currently
|
||||
only supported in SELECT queries.
|
||||
You may use a map or struct to bind parameters by name. This is supported in
|
||||
the Exec and Select* functions on DbMap and Transaction. It is not supported by
|
||||
Prepare, Query, or QueryRow.
|
||||
|
||||
```go
|
||||
_, err := dbm.Select(&dest, "select * from Foo where name = :name and age = :age", map[string]interface{}{
|
||||
|
@ -520,27 +427,26 @@ Use hooks to update data before/after saving to the db. Good for timestamps:
|
|||
|
||||
```go
|
||||
// implement the PreInsert and PreUpdate hooks
|
||||
func (i *Invoice) PreInsert(s gorp.SqlExecutor) error {
|
||||
func (i *Invoice) PreInsert(s borp.SqlExecutor) error {
|
||||
i.Created = time.Now().UnixNano()
|
||||
i.Updated = i.Created
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Invoice) PreUpdate(s gorp.SqlExecutor) error {
|
||||
func (i *Invoice) PreUpdate(s borp.SqlExecutor) error {
|
||||
i.Updated = time.Now().UnixNano()
|
||||
return nil
|
||||
}
|
||||
|
||||
// You can use the SqlExecutor to cascade additional SQL
|
||||
// Take care to avoid cycles. gorp won't prevent them.
|
||||
// Take care to avoid cycles. Borp won't prevent them.
|
||||
//
|
||||
// Here's an example of a cascading delete
|
||||
//
|
||||
func (p *Person) PreDelete(s gorp.SqlExecutor) error {
|
||||
func (p *Person) PreDelete(s borp.SqlExecutor) error {
|
||||
query := "delete from invoice_test where PersonId=?"
|
||||
|
||||
|
||||
_, err := s.Exec(query, p.Id)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -560,20 +466,20 @@ Full list of hooks that you can implement:
|
|||
|
||||
All have the same signature. for example:
|
||||
|
||||
func (p *MyStruct) PostUpdate(s gorp.SqlExecutor) error
|
||||
func (p *MyStruct) PostUpdate(s borp.SqlExecutor) error
|
||||
|
||||
### Optimistic Locking
|
||||
|
||||
#### Note that this behaviour has changed in v2. See [Migration Guide](#migration-guide).
|
||||
|
||||
gorp provides a simple optimistic locking feature, similar to Java's
|
||||
Borp provides a simple optimistic locking feature, similar to Java's
|
||||
JPA, that will raise an error if you try to update/delete a row whose
|
||||
`version` column has a value different than the one in memory. This
|
||||
provides a safe way to do "select then update" style operations
|
||||
without explicit read and write locks.
|
||||
|
||||
```go
|
||||
// Version is an auto-incremented number, managed by gorp
|
||||
// Version is an auto-incremented number, managed by borp
|
||||
// If this property is present on your struct, update
|
||||
// operations will be constrained
|
||||
//
|
||||
|
@ -606,7 +512,7 @@ p1.LName = "Howard"
|
|||
|
||||
// Raises error because p1.Version == 1, which is out of date
|
||||
count, err := dbmap.Update(p1)
|
||||
_, ok := err.(gorp.OptimisticLockError)
|
||||
_, ok := err.(borp.OptimisticLockError)
|
||||
if ok {
|
||||
// should reach this statement
|
||||
|
||||
|
@ -624,10 +530,6 @@ if ok {
|
|||
Indexes are frequently critical for performance. Here is how to add
|
||||
them to your tables.
|
||||
|
||||
NB: SqlServer and Oracle need testing and possible adjustment to the
|
||||
CreateIndexSuffix() and DropIndexSuffix() methods to make AddIndex()
|
||||
work for them.
|
||||
|
||||
In the example below we put an index both on the Id field, and on the
|
||||
AcctId field.
|
||||
|
||||
|
@ -659,7 +561,7 @@ MariaDB [test]> show create table Account;
|
|||
`AcctId` varchar(255) DEFAULT NULL,
|
||||
PRIMARY KEY (`Id`),
|
||||
UNIQUE KEY `AcctIdIndex` (`AcctId`) USING BTREE <<<--- yes! index added.
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8
|
||||
+---------+--------------------------+
|
||||
|
||||
```
|
||||
|
@ -667,32 +569,21 @@ MariaDB [test]> show create table Account;
|
|||
|
||||
## Database Drivers
|
||||
|
||||
gorp uses the Go 1 `database/sql` package. A full list of compliant
|
||||
borp uses the Go 1 `database/sql` package. A full list of compliant
|
||||
drivers is available here:
|
||||
|
||||
http://code.google.com/p/go-wiki/wiki/SQLDrivers
|
||||
|
||||
Sadly, SQL databases differ on various issues. gorp provides a Dialect
|
||||
Sadly, SQL databases differ on various issues. borp provides a Dialect
|
||||
interface that should be implemented per database vendor. Dialects
|
||||
are provided for:
|
||||
|
||||
* MySQL
|
||||
* PostgreSQL
|
||||
* sqlite3
|
||||
|
||||
Each of these three databases pass the test suite. See `gorp_test.go`
|
||||
Each of these three databases pass the test suite. See `borp_test.go`
|
||||
for example DSNs for these three databases.
|
||||
|
||||
Support is also provided for:
|
||||
|
||||
* Oracle (contributed by @klaidliadon)
|
||||
* SQL Server (contributed by @qrawl) - use driver:
|
||||
github.com/denisenkom/go-mssqldb
|
||||
|
||||
Note that these databases are not covered by CI and I (@coopernurse)
|
||||
have no good way to test them locally. So please try them and send
|
||||
patches as needed, but expect a bit more unpredicability.
|
||||
|
||||
## Sqlite3 Extensions
|
||||
|
||||
In order to use sqlite3 extensions you need to first register a custom driver:
|
||||
|
@ -721,43 +612,9 @@ func customDriver() (*sql.DB, error) {
|
|||
|
||||
## Known Issues
|
||||
|
||||
### SQL placeholder portability
|
||||
|
||||
Different databases use different strings to indicate variable
|
||||
placeholders in prepared SQL statements. Unlike some database
|
||||
abstraction layers (such as JDBC), Go's `database/sql` does not
|
||||
standardize this.
|
||||
|
||||
SQL generated by gorp in the `Insert`, `Update`, `Delete`, and `Get`
|
||||
methods delegates to a Dialect implementation for each database, and
|
||||
will generate portable SQL.
|
||||
|
||||
Raw SQL strings passed to `Exec`, `Select`, `SelectOne`, `SelectInt`,
|
||||
etc will not be parsed. Consequently you may have portability issues
|
||||
if you write a query like this:
|
||||
|
||||
```go
|
||||
// works on MySQL and Sqlite3, but not with Postgresql err :=
|
||||
dbmap.SelectOne(&val, "select * from foo where id = ?", 30)
|
||||
```
|
||||
|
||||
In `Select` and `SelectOne` you can use named parameters to work
|
||||
around this. The following is portable:
|
||||
|
||||
```go
|
||||
err := dbmap.SelectOne(&val, "select * from foo where id = :id",
|
||||
map[string]interface{} { "id": 30})
|
||||
```
|
||||
|
||||
Additionally, when using Postgres as your database, you should utilize
|
||||
`$1` instead of `?` placeholders as utilizing `?` placeholders when
|
||||
querying Postgres will result in `pq: operator does not exist`
|
||||
errors. Alternatively, use `dbMap.Dialect.BindVar(varIdx)` to get the
|
||||
proper variable binding for your dialect.
|
||||
|
||||
### time.Time and time zones
|
||||
|
||||
gorp will pass `time.Time` fields through to the `database/sql`
|
||||
borp will pass `time.Time` fields through to the `database/sql`
|
||||
driver, but note that the behavior of this type varies across database
|
||||
drivers.
|
||||
|
||||
|
@ -773,7 +630,7 @@ To avoid any potential issues with timezone/DST, consider:
|
|||
|
||||
## Running the tests
|
||||
|
||||
The included tests may be run against MySQL, Postgresql, or sqlite3.
|
||||
The included tests may be run against MySQL or sqlite3.
|
||||
You must set two environment variables so the test code knows which
|
||||
driver to use, and how to connect to your database.
|
||||
|
||||
|
@ -790,17 +647,10 @@ go test -bench="Bench" -benchtime 10
|
|||
```
|
||||
|
||||
Valid `GORP_TEST_DIALECT` values are: "mysql"(for mymysql),
|
||||
"gomysql"(for go-sql-driver), "postgres", "sqlite" See the
|
||||
"gomysql"(for go-sql-driver), or "sqlite" See the
|
||||
`test_all.sh` script for examples of all 3 databases. This is the
|
||||
script I run locally to test the library.
|
||||
|
||||
## Performance
|
||||
|
||||
gorp uses reflection to construct SQL queries and bind parameters.
|
||||
See the BenchmarkNativeCrud vs BenchmarkGorpCrud in gorp_test.go for a
|
||||
simple perf test. On my MacBook Pro gorp is about 2-3% slower than
|
||||
hand written SQL.
|
||||
|
||||
|
||||
## Contributors
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gorp
|
||||
package borp
|
||||
|
||||
import "reflect"
|
||||
|
236
vendor/github.com/go-gorp/gorp/v3/db.go → vendor/github.com/letsencrypt/borp/db.go
generated
vendored
236
vendor/github.com/go-gorp/gorp/v3/db.go → vendor/github.com/letsencrypt/borp/db.go
generated
vendored
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gorp
|
||||
package borp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
@ -24,12 +24,9 @@ import (
|
|||
//
|
||||
// Example:
|
||||
//
|
||||
// dialect := gorp.MySQLDialect{"InnoDB", "UTF8"}
|
||||
// dbmap := &gorp.DbMap{Db: db, Dialect: dialect}
|
||||
//
|
||||
// dialect := borp.MySQLDialect{"InnoDB", "UTF8"}
|
||||
// dbmap := &borp.DbMap{Db: db, Dialect: dialect}
|
||||
type DbMap struct {
|
||||
ctx context.Context
|
||||
|
||||
// Db handle to use with this map
|
||||
Db *sql.DB
|
||||
|
||||
|
@ -40,53 +37,7 @@ type DbMap struct {
|
|||
|
||||
// ExpandSlices when enabled will convert slice arguments in mappers into flat
|
||||
// values. It will modify the query, adding more placeholders, and the mapper,
|
||||
// adding each item of the slice as a new unique entry in the mapper. For
|
||||
// example, given the scenario bellow:
|
||||
//
|
||||
// dbmap.Select(&output, "SELECT 1 FROM example WHERE id IN (:IDs)", map[string]interface{}{
|
||||
// "IDs": []int64{1, 2, 3},
|
||||
// })
|
||||
//
|
||||
// The executed query would be:
|
||||
//
|
||||
// SELECT 1 FROM example WHERE id IN (:IDs0,:IDs1,:IDs2)
|
||||
//
|
||||
// With the mapper:
|
||||
//
|
||||
// map[string]interface{}{
|
||||
// "IDs": []int64{1, 2, 3},
|
||||
// "IDs0": int64(1),
|
||||
// "IDs1": int64(2),
|
||||
// "IDs2": int64(3),
|
||||
// }
|
||||
//
|
||||
// It is also flexible for custom slice types. The value just need to
|
||||
// implement stringer or numberer interfaces.
|
||||
//
|
||||
// type CustomValue string
|
||||
//
|
||||
// const (
|
||||
// CustomValueHey CustomValue = "hey"
|
||||
// CustomValueOh CustomValue = "oh"
|
||||
// )
|
||||
//
|
||||
// type CustomValues []CustomValue
|
||||
//
|
||||
// func (c CustomValues) ToStringSlice() []string {
|
||||
// values := make([]string, len(c))
|
||||
// for i := range c {
|
||||
// values[i] = string(c[i])
|
||||
// }
|
||||
// return values
|
||||
// }
|
||||
//
|
||||
// func query() {
|
||||
// // ...
|
||||
// result, err := dbmap.Select(&output, "SELECT 1 FROM example WHERE value IN (:Values)", map[string]interface{}{
|
||||
// "Values": CustomValues([]CustomValue{CustomValueHey}),
|
||||
// })
|
||||
// // ...
|
||||
// }
|
||||
// adding each item of the slice as a new unique entry in the mapper.
|
||||
ExpandSliceArgs bool
|
||||
|
||||
tables []*TableMap
|
||||
|
@ -117,19 +68,12 @@ func (m *DbMap) dynamicTableMap() map[string]*TableMap {
|
|||
return m.tablesDynamic
|
||||
}
|
||||
|
||||
func (m *DbMap) WithContext(ctx context.Context) SqlExecutor {
|
||||
copy := &DbMap{}
|
||||
*copy = *m
|
||||
copy.ctx = ctx
|
||||
return copy
|
||||
}
|
||||
|
||||
func (m *DbMap) CreateIndex() error {
|
||||
func (m *DbMap) CreateIndex(ctx context.Context) error {
|
||||
var err error
|
||||
dialect := reflect.TypeOf(m.Dialect)
|
||||
for _, table := range m.tables {
|
||||
for _, index := range table.indexes {
|
||||
err = m.createIndexImpl(dialect, table, index)
|
||||
err = m.createIndexImpl(ctx, dialect, table, index)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
@ -138,7 +82,7 @@ func (m *DbMap) CreateIndex() error {
|
|||
|
||||
for _, table := range m.dynamicTableMap() {
|
||||
for _, index := range table.indexes {
|
||||
err = m.createIndexImpl(dialect, table, index)
|
||||
err = m.createIndexImpl(ctx, dialect, table, index)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
@ -148,7 +92,7 @@ func (m *DbMap) CreateIndex() error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (m *DbMap) createIndexImpl(dialect reflect.Type,
|
||||
func (m *DbMap) createIndexImpl(ctx context.Context, dialect reflect.Type,
|
||||
table *TableMap,
|
||||
index *IndexMap) error {
|
||||
s := bytes.Buffer{}
|
||||
|
@ -158,9 +102,6 @@ func (m *DbMap) createIndexImpl(dialect reflect.Type,
|
|||
}
|
||||
s.WriteString(" index")
|
||||
s.WriteString(fmt.Sprintf(" %s on %s", index.IndexName, table.TableName))
|
||||
if dname := dialect.Name(); dname == "PostgresDialect" && index.IndexType != "" {
|
||||
s.WriteString(fmt.Sprintf(" %s %s", m.Dialect.CreateIndexSuffix(), index.IndexType))
|
||||
}
|
||||
s.WriteString(" (")
|
||||
for x, col := range index.columns {
|
||||
if x > 0 {
|
||||
|
@ -174,11 +115,11 @@ func (m *DbMap) createIndexImpl(dialect reflect.Type,
|
|||
s.WriteString(fmt.Sprintf(" %s %s", m.Dialect.CreateIndexSuffix(), index.IndexType))
|
||||
}
|
||||
s.WriteString(";")
|
||||
_, err := m.Exec(s.String())
|
||||
_, err := m.ExecContext(ctx, s.String())
|
||||
return err
|
||||
}
|
||||
|
||||
func (t *TableMap) DropIndex(name string) error {
|
||||
func (t *TableMap) DropIndex(ctx context.Context, name string) error {
|
||||
|
||||
var err error
|
||||
dialect := reflect.TypeOf(t.dbmap.Dialect)
|
||||
|
@ -191,7 +132,7 @@ func (t *TableMap) DropIndex(name string) error {
|
|||
s.WriteString(fmt.Sprintf(" %s %s", t.dbmap.Dialect.DropIndexSuffix(), t.TableName))
|
||||
}
|
||||
s.WriteString(";")
|
||||
_, e := t.dbmap.Exec(s.String())
|
||||
_, e := t.dbmap.ExecContext(ctx, s.String())
|
||||
if e != nil {
|
||||
err = e
|
||||
}
|
||||
|
@ -202,7 +143,7 @@ func (t *TableMap) DropIndex(name string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// AddTable registers the given interface type with gorp. The table name
|
||||
// AddTable registers the given interface type with borp. The table name
|
||||
// will be given the name of the TypeOf(i). You must call this function,
|
||||
// or AddTableWithName, for any struct type you wish to persist with
|
||||
// the given DbMap.
|
||||
|
@ -248,7 +189,7 @@ func (m *DbMap) AddTableWithNameAndSchema(i interface{}, schema string, name str
|
|||
return tmap
|
||||
}
|
||||
|
||||
// AddTableDynamic registers the given interface type with gorp.
|
||||
// AddTableDynamic registers the given interface type with borp.
|
||||
// The table name will be dynamically determined at runtime by
|
||||
// using the GetTableName method on DynamicTable interface
|
||||
func (m *DbMap) AddTableDynamic(inp DynamicTable, schema string) *TableMap {
|
||||
|
@ -418,23 +359,23 @@ func (m *DbMap) readStructColumns(t reflect.Type) (cols []*ColumnMap, primaryKey
|
|||
//
|
||||
// This is particularly useful in unit tests where you want to create
|
||||
// and destroy the schema automatically.
|
||||
func (m *DbMap) CreateTables() error {
|
||||
return m.createTables(false)
|
||||
func (m *DbMap) CreateTables(ctx context.Context) error {
|
||||
return m.createTables(ctx, false)
|
||||
}
|
||||
|
||||
// CreateTablesIfNotExists is similar to CreateTables, but starts
|
||||
// each statement with "create table if not exists" so that existing
|
||||
// tables do not raise errors
|
||||
func (m *DbMap) CreateTablesIfNotExists() error {
|
||||
return m.createTables(true)
|
||||
func (m *DbMap) CreateTablesIfNotExists(ctx context.Context) error {
|
||||
return m.createTables(ctx, true)
|
||||
}
|
||||
|
||||
func (m *DbMap) createTables(ifNotExists bool) error {
|
||||
func (m *DbMap) createTables(ctx context.Context, ifNotExists bool) error {
|
||||
var err error
|
||||
for i := range m.tables {
|
||||
table := m.tables[i]
|
||||
sql := table.SqlForCreate(ifNotExists)
|
||||
_, err = m.Exec(sql)
|
||||
_, err = m.ExecContext(ctx, sql)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -442,7 +383,7 @@ func (m *DbMap) createTables(ifNotExists bool) error {
|
|||
|
||||
for _, tbl := range m.dynamicTableMap() {
|
||||
sql := tbl.SqlForCreate(ifNotExists)
|
||||
_, err = m.Exec(sql)
|
||||
_, err = m.ExecContext(ctx, sql)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -453,7 +394,7 @@ func (m *DbMap) createTables(ifNotExists bool) error {
|
|||
|
||||
// DropTable drops an individual table.
|
||||
// Returns an error when the table does not exist.
|
||||
func (m *DbMap) DropTable(table interface{}) error {
|
||||
func (m *DbMap) DropTable(ctx context.Context, table interface{}) error {
|
||||
t := reflect.TypeOf(table)
|
||||
|
||||
tableName := ""
|
||||
|
@ -461,11 +402,11 @@ func (m *DbMap) DropTable(table interface{}) error {
|
|||
tableName = dyn.TableName()
|
||||
}
|
||||
|
||||
return m.dropTable(t, tableName, false)
|
||||
return m.dropTable(ctx, t, tableName, false)
|
||||
}
|
||||
|
||||
// DropTableIfExists drops an individual table when the table exists.
|
||||
func (m *DbMap) DropTableIfExists(table interface{}) error {
|
||||
func (m *DbMap) DropTableIfExists(ctx context.Context, table interface{}) error {
|
||||
t := reflect.TypeOf(table)
|
||||
|
||||
tableName := ""
|
||||
|
@ -473,34 +414,34 @@ func (m *DbMap) DropTableIfExists(table interface{}) error {
|
|||
tableName = dyn.TableName()
|
||||
}
|
||||
|
||||
return m.dropTable(t, tableName, true)
|
||||
return m.dropTable(ctx, t, tableName, true)
|
||||
}
|
||||
|
||||
// DropTables iterates through TableMaps registered to this DbMap and
|
||||
// executes "drop table" statements against the database for each.
|
||||
func (m *DbMap) DropTables() error {
|
||||
return m.dropTables(false)
|
||||
func (m *DbMap) DropTables(ctx context.Context) error {
|
||||
return m.dropTables(ctx, false)
|
||||
}
|
||||
|
||||
// DropTablesIfExists is the same as DropTables, but uses the "if exists" clause to
|
||||
// avoid errors for tables that do not exist.
|
||||
func (m *DbMap) DropTablesIfExists() error {
|
||||
return m.dropTables(true)
|
||||
func (m *DbMap) DropTablesIfExists(ctx context.Context) error {
|
||||
return m.dropTables(ctx, true)
|
||||
}
|
||||
|
||||
// Goes through all the registered tables, dropping them one by one.
|
||||
// If an error is encountered, then it is returned and the rest of
|
||||
// the tables are not dropped.
|
||||
func (m *DbMap) dropTables(addIfExists bool) (err error) {
|
||||
func (m *DbMap) dropTables(ctx context.Context, addIfExists bool) (err error) {
|
||||
for _, table := range m.tables {
|
||||
err = m.dropTableImpl(table, addIfExists)
|
||||
err = m.dropTableImpl(ctx, table, addIfExists)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, table := range m.dynamicTableMap() {
|
||||
err = m.dropTableImpl(table, addIfExists)
|
||||
err = m.dropTableImpl(ctx, table, addIfExists)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -510,21 +451,21 @@ func (m *DbMap) dropTables(addIfExists bool) (err error) {
|
|||
}
|
||||
|
||||
// Implementation of dropping a single table.
|
||||
func (m *DbMap) dropTable(t reflect.Type, name string, addIfExists bool) error {
|
||||
func (m *DbMap) dropTable(ctx context.Context, t reflect.Type, name string, addIfExists bool) error {
|
||||
table := tableOrNil(m, t, name)
|
||||
if table == nil {
|
||||
return fmt.Errorf("table %s was not registered", table.TableName)
|
||||
}
|
||||
|
||||
return m.dropTableImpl(table, addIfExists)
|
||||
return m.dropTableImpl(ctx, table, addIfExists)
|
||||
}
|
||||
|
||||
func (m *DbMap) dropTableImpl(table *TableMap, ifExists bool) (err error) {
|
||||
func (m *DbMap) dropTableImpl(ctx context.Context, table *TableMap, ifExists bool) (err error) {
|
||||
tableDrop := "drop table"
|
||||
if ifExists {
|
||||
tableDrop = m.Dialect.IfTableExists(tableDrop, table.SchemaName, table.TableName)
|
||||
}
|
||||
_, err = m.Exec(fmt.Sprintf("%s %s;", tableDrop, m.Dialect.QuotedTableForQuery(table.SchemaName, table.TableName)))
|
||||
_, err = m.ExecContext(ctx, fmt.Sprintf("%s %s;", tableDrop, m.Dialect.QuotedTableForQuery(table.SchemaName, table.TableName)))
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -532,18 +473,18 @@ func (m *DbMap) dropTableImpl(table *TableMap, ifExists bool) (err error) {
|
|||
// executes "truncate table" statements against the database for each, or in the case of
|
||||
// sqlite, a "delete from" with no "where" clause, which uses the truncate optimization
|
||||
// (http://www.sqlite.org/lang_delete.html)
|
||||
func (m *DbMap) TruncateTables() error {
|
||||
func (m *DbMap) TruncateTables(ctx context.Context) error {
|
||||
var err error
|
||||
for i := range m.tables {
|
||||
table := m.tables[i]
|
||||
_, e := m.Exec(fmt.Sprintf("%s %s;", m.Dialect.TruncateClause(), m.Dialect.QuotedTableForQuery(table.SchemaName, table.TableName)))
|
||||
_, e := m.ExecContext(ctx, fmt.Sprintf("%s %s;", m.Dialect.TruncateClause(), m.Dialect.QuotedTableForQuery(table.SchemaName, table.TableName)))
|
||||
if e != nil {
|
||||
err = e
|
||||
}
|
||||
}
|
||||
|
||||
for _, table := range m.dynamicTableMap() {
|
||||
_, e := m.Exec(fmt.Sprintf("%s %s;", m.Dialect.TruncateClause(), m.Dialect.QuotedTableForQuery(table.SchemaName, table.TableName)))
|
||||
_, e := m.ExecContext(ctx, fmt.Sprintf("%s %s;", m.Dialect.TruncateClause(), m.Dialect.QuotedTableForQuery(table.SchemaName, table.TableName)))
|
||||
if e != nil {
|
||||
err = e
|
||||
}
|
||||
|
@ -562,8 +503,8 @@ func (m *DbMap) TruncateTables() error {
|
|||
// before/after the INSERT statement if the interface defines them.
|
||||
//
|
||||
// Panics if any interface in the list has not been registered with AddTable
|
||||
func (m *DbMap) Insert(list ...interface{}) error {
|
||||
return insert(m, m, list...)
|
||||
func (m *DbMap) Insert(ctx context.Context, list ...interface{}) error {
|
||||
return insert(ctx, m, m, list...)
|
||||
}
|
||||
|
||||
// Update runs a SQL UPDATE statement for each element in list. List
|
||||
|
@ -576,8 +517,8 @@ func (m *DbMap) Insert(list ...interface{}) error {
|
|||
//
|
||||
// Returns an error if SetKeys has not been called on the TableMap
|
||||
// Panics if any interface in the list has not been registered with AddTable
|
||||
func (m *DbMap) Update(list ...interface{}) (int64, error) {
|
||||
return update(m, m, nil, list...)
|
||||
func (m *DbMap) Update(ctx context.Context, list ...interface{}) (int64, error) {
|
||||
return update(ctx, m, m, nil, list...)
|
||||
}
|
||||
|
||||
// UpdateColumns runs a SQL UPDATE statement for each element in list. List
|
||||
|
@ -592,8 +533,8 @@ func (m *DbMap) Update(list ...interface{}) (int64, error) {
|
|||
//
|
||||
// Returns an error if SetKeys has not been called on the TableMap
|
||||
// Panics if any interface in the list has not been registered with AddTable
|
||||
func (m *DbMap) UpdateColumns(filter ColumnFilter, list ...interface{}) (int64, error) {
|
||||
return update(m, m, filter, list...)
|
||||
func (m *DbMap) UpdateColumns(ctx context.Context, filter ColumnFilter, list ...interface{}) (int64, error) {
|
||||
return update(ctx, m, m, filter, list...)
|
||||
}
|
||||
|
||||
// Delete runs a SQL DELETE statement for each element in list. List
|
||||
|
@ -606,8 +547,8 @@ func (m *DbMap) UpdateColumns(filter ColumnFilter, list ...interface{}) (int64,
|
|||
//
|
||||
// Returns an error if SetKeys has not been called on the TableMap
|
||||
// Panics if any interface in the list has not been registered with AddTable
|
||||
func (m *DbMap) Delete(list ...interface{}) (int64, error) {
|
||||
return delete(m, m, list...)
|
||||
func (m *DbMap) Delete(ctx context.Context, list ...interface{}) (int64, error) {
|
||||
return delete(ctx, m, m, list...)
|
||||
}
|
||||
|
||||
// Get runs a SQL SELECT to fetch a single row from the table based on the
|
||||
|
@ -625,8 +566,8 @@ func (m *DbMap) Delete(list ...interface{}) (int64, error) {
|
|||
//
|
||||
// Returns an error if SetKeys has not been called on the TableMap
|
||||
// Panics if any interface in the list has not been registered with AddTable
|
||||
func (m *DbMap) Get(i interface{}, keys ...interface{}) (interface{}, error) {
|
||||
return get(m, m, i, keys...)
|
||||
func (m *DbMap) Get(ctx context.Context, i interface{}, keys ...interface{}) (interface{}, error) {
|
||||
return get(ctx, m, m, i, keys...)
|
||||
}
|
||||
|
||||
// Select runs an arbitrary SQL query, binding the columns in the result
|
||||
|
@ -648,17 +589,17 @@ func (m *DbMap) Get(i interface{}, keys ...interface{}) (interface{}, error) {
|
|||
// and nil returned.
|
||||
//
|
||||
// i does NOT need to be registered with AddTable()
|
||||
func (m *DbMap) Select(i interface{}, query string, args ...interface{}) ([]interface{}, error) {
|
||||
func (m *DbMap) Select(ctx context.Context, i interface{}, query string, args ...interface{}) ([]interface{}, error) {
|
||||
if m.ExpandSliceArgs {
|
||||
expandSliceArgs(&query, args...)
|
||||
}
|
||||
|
||||
return hookedselect(m, m, i, query, args...)
|
||||
return hookedselect(ctx, m, m, i, query, args...)
|
||||
}
|
||||
|
||||
// Exec runs an arbitrary SQL statement. args represent the bind parameters.
|
||||
// This is equivalent to running: Exec() using database/sql
|
||||
func (m *DbMap) Exec(query string, args ...interface{}) (sql.Result, error) {
|
||||
// This is equivalent to running: ExecContext() using database/sql
|
||||
func (m *DbMap) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) {
|
||||
if m.ExpandSliceArgs {
|
||||
expandSliceArgs(&query, args...)
|
||||
}
|
||||
|
@ -667,79 +608,84 @@ func (m *DbMap) Exec(query string, args ...interface{}) (sql.Result, error) {
|
|||
now := time.Now()
|
||||
defer m.trace(now, query, args...)
|
||||
}
|
||||
return maybeExpandNamedQueryAndExec(m, query, args...)
|
||||
return maybeExpandNamedQueryAndExec(ctx, m, query, args...)
|
||||
}
|
||||
|
||||
// SelectInt is a convenience wrapper around the gorp.SelectInt function
|
||||
func (m *DbMap) SelectInt(query string, args ...interface{}) (int64, error) {
|
||||
// SelectInt is a convenience wrapper around the borp.SelectInt function
|
||||
func (m *DbMap) SelectInt(ctx context.Context, query string, args ...interface{}) (int64, error) {
|
||||
if m.ExpandSliceArgs {
|
||||
expandSliceArgs(&query, args...)
|
||||
}
|
||||
|
||||
return SelectInt(m, query, args...)
|
||||
return SelectInt(ctx, m, query, args...)
|
||||
}
|
||||
|
||||
// SelectNullInt is a convenience wrapper around the gorp.SelectNullInt function
|
||||
func (m *DbMap) SelectNullInt(query string, args ...interface{}) (sql.NullInt64, error) {
|
||||
// SelectNullInt is a convenience wrapper around the borp.SelectNullInt function
|
||||
func (m *DbMap) SelectNullInt(ctx context.Context, query string, args ...interface{}) (sql.NullInt64, error) {
|
||||
if m.ExpandSliceArgs {
|
||||
expandSliceArgs(&query, args...)
|
||||
}
|
||||
|
||||
return SelectNullInt(m, query, args...)
|
||||
return SelectNullInt(ctx, m, query, args...)
|
||||
}
|
||||
|
||||
// SelectFloat is a convenience wrapper around the gorp.SelectFloat function
|
||||
func (m *DbMap) SelectFloat(query string, args ...interface{}) (float64, error) {
|
||||
// SelectFloat is a convenience wrapper around the borp.SelectFloat function
|
||||
func (m *DbMap) SelectFloat(ctx context.Context, query string, args ...interface{}) (float64, error) {
|
||||
if m.ExpandSliceArgs {
|
||||
expandSliceArgs(&query, args...)
|
||||
}
|
||||
|
||||
return SelectFloat(m, query, args...)
|
||||
return SelectFloat(ctx, m, query, args...)
|
||||
}
|
||||
|
||||
// SelectNullFloat is a convenience wrapper around the gorp.SelectNullFloat function
|
||||
func (m *DbMap) SelectNullFloat(query string, args ...interface{}) (sql.NullFloat64, error) {
|
||||
// SelectNullFloat is a convenience wrapper around the borp.SelectNullFloat function
|
||||
func (m *DbMap) SelectNullFloat(ctx context.Context, query string, args ...interface{}) (sql.NullFloat64, error) {
|
||||
if m.ExpandSliceArgs {
|
||||
expandSliceArgs(&query, args...)
|
||||
}
|
||||
|
||||
return SelectNullFloat(m, query, args...)
|
||||
return SelectNullFloat(ctx, m, query, args...)
|
||||
}
|
||||
|
||||
// SelectStr is a convenience wrapper around the gorp.SelectStr function
|
||||
func (m *DbMap) SelectStr(query string, args ...interface{}) (string, error) {
|
||||
// SelectStr is a convenience wrapper around the borp.SelectStr function
|
||||
func (m *DbMap) SelectStr(ctx context.Context, query string, args ...interface{}) (string, error) {
|
||||
if m.ExpandSliceArgs {
|
||||
expandSliceArgs(&query, args...)
|
||||
}
|
||||
|
||||
return SelectStr(m, query, args...)
|
||||
return SelectStr(ctx, m, query, args...)
|
||||
}
|
||||
|
||||
// SelectNullStr is a convenience wrapper around the gorp.SelectNullStr function
|
||||
func (m *DbMap) SelectNullStr(query string, args ...interface{}) (sql.NullString, error) {
|
||||
// SelectNullStr is a convenience wrapper around the borp.SelectNullStr function
|
||||
func (m *DbMap) SelectNullStr(ctx context.Context, query string, args ...interface{}) (sql.NullString, error) {
|
||||
if m.ExpandSliceArgs {
|
||||
expandSliceArgs(&query, args...)
|
||||
}
|
||||
|
||||
return SelectNullStr(m, query, args...)
|
||||
return SelectNullStr(ctx, m, query, args...)
|
||||
}
|
||||
|
||||
// SelectOne is a convenience wrapper around the gorp.SelectOne function
|
||||
func (m *DbMap) SelectOne(holder interface{}, query string, args ...interface{}) error {
|
||||
// SelectOne is a convenience wrapper around the borp.SelectOne function
|
||||
func (m *DbMap) SelectOne(ctx context.Context, holder interface{}, query string, args ...interface{}) error {
|
||||
if m.ExpandSliceArgs {
|
||||
expandSliceArgs(&query, args...)
|
||||
}
|
||||
|
||||
return SelectOne(m, m, holder, query, args...)
|
||||
return SelectOne(ctx, m, m, holder, query, args...)
|
||||
}
|
||||
|
||||
// Begin starts a gorp Transaction
|
||||
func (m *DbMap) Begin() (*Transaction, error) {
|
||||
// BeginTx starts a borp Transaction. It uses database/sql.DB.BeginTx under the hood so the same
|
||||
// guarantees apply. https://pkg.go.dev/database/sql#DB.BeginTx
|
||||
//
|
||||
// > The provided context is used until the transaction is committed or rolled back. If the context
|
||||
// > is canceled, the sql package will roll back the transaction. Tx.Commit will return an error if
|
||||
// > the context provided to BeginTx is canceled.
|
||||
func (m *DbMap) BeginTx(ctx context.Context) (*Transaction, error) {
|
||||
if m.logger != nil {
|
||||
now := time.Now()
|
||||
defer m.trace(now, "begin;")
|
||||
}
|
||||
tx, err := begin(m)
|
||||
tx, err := m.Db.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -789,13 +735,13 @@ func (m *DbMap) DynamicTableFor(tableName string, checkPK bool) (*TableMap, erro
|
|||
|
||||
// Prepare creates a prepared statement for later queries or executions.
|
||||
// Multiple queries or executions may be run concurrently from the returned statement.
|
||||
// This is equivalent to running: Prepare() using database/sql
|
||||
func (m *DbMap) Prepare(query string) (*sql.Stmt, error) {
|
||||
// This is equivalent to running: PrepareContext() using database/sql
|
||||
func (m *DbMap) PrepareContext(ctx context.Context, query string) (*sql.Stmt, error) {
|
||||
if m.logger != nil {
|
||||
now := time.Now()
|
||||
defer m.trace(now, query, nil)
|
||||
}
|
||||
return prepare(m, query)
|
||||
return m.Db.PrepareContext(ctx, query)
|
||||
}
|
||||
|
||||
func tableOrNil(m *DbMap, t reflect.Type, name string) *TableMap {
|
||||
|
@ -843,7 +789,8 @@ func (m *DbMap) tableForPointer(ptr interface{}, checkPK bool) (*TableMap, refle
|
|||
return t, elem, nil
|
||||
}
|
||||
|
||||
func (m *DbMap) QueryRow(query string, args ...interface{}) *sql.Row {
|
||||
// This is equivalent to running: QueryRowContext() using database/sql
|
||||
func (m *DbMap) QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row {
|
||||
if m.ExpandSliceArgs {
|
||||
expandSliceArgs(&query, args...)
|
||||
}
|
||||
|
@ -852,10 +799,11 @@ func (m *DbMap) QueryRow(query string, args ...interface{}) *sql.Row {
|
|||
now := time.Now()
|
||||
defer m.trace(now, query, args...)
|
||||
}
|
||||
return queryRow(m, query, args...)
|
||||
return m.Db.QueryRowContext(ctx, query, args...)
|
||||
}
|
||||
|
||||
func (m *DbMap) Query(q string, args ...interface{}) (*sql.Rows, error) {
|
||||
// This is equivalent to running: QueryContext() using database/sql
|
||||
func (m *DbMap) QueryContext(ctx context.Context, q string, args ...interface{}) (*sql.Rows, error) {
|
||||
if m.ExpandSliceArgs {
|
||||
expandSliceArgs(&q, args...)
|
||||
}
|
||||
|
@ -864,7 +812,7 @@ func (m *DbMap) Query(q string, args ...interface{}) (*sql.Rows, error) {
|
|||
now := time.Now()
|
||||
defer m.trace(now, q, args...)
|
||||
}
|
||||
return query(m, q, args...)
|
||||
return m.Db.QueryContext(ctx, q, args...)
|
||||
}
|
||||
|
||||
func (m *DbMap) trace(started time.Time, query string, args ...interface{}) {
|
||||
|
@ -874,7 +822,7 @@ func (m *DbMap) trace(started time.Time, query string, args ...interface{}) {
|
|||
|
||||
if m.logger != nil {
|
||||
var margs = argsString(args...)
|
||||
m.logger.Printf("%s%s [%s] (%v)", m.logPrefix, query, margs, (time.Now().Sub(started)))
|
||||
m.logger.Printf("%s%s [%s] (%v)", m.logPrefix, query, margs, (time.Since(started)))
|
||||
}
|
||||
}
|
||||
|
|
@ -2,9 +2,10 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gorp
|
||||
package borp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
|
@ -43,8 +44,8 @@ type Dialect interface {
|
|||
// string to truncate tables
|
||||
TruncateClause() string
|
||||
|
||||
// bind variable string to use when forming SQL statements
|
||||
// in many dbs it is "?", but Postgres appears to use $1
|
||||
// Bind variable string to use when forming SQL statements
|
||||
// in many dbs it is "?".
|
||||
//
|
||||
// i is a zero based index of the bind variable in this statement
|
||||
//
|
||||
|
@ -72,7 +73,7 @@ type Dialect interface {
|
|||
// the dialect can handle automatic assignment of more than just
|
||||
// integers, see TargetedAutoIncrInserter.
|
||||
type IntegerAutoIncrInserter interface {
|
||||
InsertAutoIncr(exec SqlExecutor, insertSql string, params ...interface{}) (int64, error)
|
||||
InsertAutoIncr(ctx context.Context, exec SqlExecutor, insertSql string, params ...interface{}) (int64, error)
|
||||
}
|
||||
|
||||
// TargetedAutoIncrInserter is implemented by dialects that can
|
||||
|
@ -83,7 +84,7 @@ type TargetedAutoIncrInserter interface {
|
|||
// automatically generated primary key directly to the passed in
|
||||
// target. The target should be a pointer to the primary key
|
||||
// field of the value being inserted.
|
||||
InsertAutoIncrToTarget(exec SqlExecutor, insertSql string, target interface{}, params ...interface{}) error
|
||||
InsertAutoIncrToTarget(ctx context.Context, xec SqlExecutor, insertSql string, target interface{}, params ...interface{}) error
|
||||
}
|
||||
|
||||
// TargetQueryInserter is implemented by dialects that can perform
|
||||
|
@ -93,11 +94,11 @@ type TargetQueryInserter interface {
|
|||
// TargetQueryInserter runs an insert operation and assigns the
|
||||
// automatically generated primary key retrived by the query
|
||||
// extracted from the GeneratedIdQuery field of the id column.
|
||||
InsertQueryToTarget(exec SqlExecutor, insertSql, idSql string, target interface{}, params ...interface{}) error
|
||||
InsertQueryToTarget(ctx context.Context, xec SqlExecutor, insertSql, idSql string, target interface{}, params ...interface{}) error
|
||||
}
|
||||
|
||||
func standardInsertAutoIncr(exec SqlExecutor, insertSql string, params ...interface{}) (int64, error) {
|
||||
res, err := exec.Exec(insertSql, params...)
|
||||
func standardInsertAutoIncr(ctx context.Context, exec SqlExecutor, insertSql string, params ...interface{}) (int64, error) {
|
||||
res, err := exec.ExecContext(ctx, insertSql, params...)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
|
@ -2,9 +2,10 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gorp
|
||||
package borp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
@ -140,8 +141,8 @@ func (d MySQLDialect) BindVar(i int) string {
|
|||
return "?"
|
||||
}
|
||||
|
||||
func (d MySQLDialect) InsertAutoIncr(exec SqlExecutor, insertSql string, params ...interface{}) (int64, error) {
|
||||
return standardInsertAutoIncr(exec, insertSql, params...)
|
||||
func (d MySQLDialect) InsertAutoIncr(ctx context.Context, exec SqlExecutor, insertSql string, params ...interface{}) (int64, error) {
|
||||
return standardInsertAutoIncr(ctx, exec, insertSql, params...)
|
||||
}
|
||||
|
||||
func (d MySQLDialect) QuoteField(f string) string {
|
|
@ -2,9 +2,10 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gorp
|
||||
package borp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
@ -86,8 +87,8 @@ func (d SqliteDialect) BindVar(i int) string {
|
|||
return "?"
|
||||
}
|
||||
|
||||
func (d SqliteDialect) InsertAutoIncr(exec SqlExecutor, insertSql string, params ...interface{}) (int64, error) {
|
||||
return standardInsertAutoIncr(exec, insertSql, params...)
|
||||
func (d SqliteDialect) InsertAutoIncr(ctx context.Context, exec SqlExecutor, insertSql string, params ...interface{}) (int64, error) {
|
||||
return standardInsertAutoIncr(ctx, exec, insertSql, params...)
|
||||
}
|
||||
|
||||
func (d SqliteDialect) QuoteField(f string) string {
|
|
@ -0,0 +1,87 @@
|
|||
// Copyright 2012 James Cooper. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package borp provides a simple way to marshal Go structs to and from
|
||||
// SQL databases. It uses the database/sql package, and should work with any
|
||||
// compliant database/sql driver.
|
||||
//
|
||||
// Source code, additional documentation, and examples:
|
||||
// https://github.com/letsencrypt/borp
|
||||
//
|
||||
// # Query Parameters
|
||||
//
|
||||
// Borp's Exec, Select*, Query, and QueryRow methods accept placeholder
|
||||
// parameters in the query, to be filled from the args parameters to these
|
||||
// functions. Borp supports some additional styles for placeholder parameters:
|
||||
//
|
||||
// # Named Bind Parameters
|
||||
//
|
||||
// For the Exec and Select* methods on DbMap and Transaction, Borp supports
|
||||
// named bind parameters. To use named bind parameters, instead of a list of
|
||||
// parameters, pass a single `map[string]interface{}` to these functions. And
|
||||
// instead of using ? in the query, use placeholder parameters of the form :word.
|
||||
// Before running the query, Borp will bind each named placeholder parameter to the
|
||||
// corresponding value found by looking up "word" in the map.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// _, err := dbm.Select(&dest, "select * from Foo where name = :name and age = :age",
|
||||
// map[string]interface{}{
|
||||
// "name": "Rob",
|
||||
// "age": 31,
|
||||
// })
|
||||
//
|
||||
// # Expanding Slices
|
||||
//
|
||||
// If you set the ExpandSlices field of DbMap to true, placeholders that bind to
|
||||
// slices will be handled specially. Borp will modify the query, adding more
|
||||
// placeholders to match the number of entries in the slice.
|
||||
//
|
||||
// For example, given the scenario bellow:
|
||||
//
|
||||
// dbmap.Select(&output, "SELECT 1 FROM example WHERE id IN (:IDs)", map[string]interface{}{
|
||||
// "IDs": []int64{1, 2, 3},
|
||||
// })
|
||||
//
|
||||
// The executed query would be:
|
||||
//
|
||||
// SELECT 1 FROM example WHERE id IN (:IDs0,:IDs1,:IDs2)
|
||||
//
|
||||
// With the mapper:
|
||||
//
|
||||
// map[string]interface{}{
|
||||
// "IDs": []int64{1, 2, 3},
|
||||
// "IDs0": int64(1),
|
||||
// "IDs1": int64(2),
|
||||
// "IDs2": int64(3),
|
||||
// }
|
||||
//
|
||||
// It is also flexible for custom slice types. The value just need to
|
||||
// implement stringer or numberer interfaces.
|
||||
//
|
||||
// type CustomValue string
|
||||
//
|
||||
// const (
|
||||
// CustomValueHey CustomValue = "hey"
|
||||
// CustomValueOh CustomValue = "oh"
|
||||
// )
|
||||
//
|
||||
// type CustomValues []CustomValue
|
||||
//
|
||||
// func (c CustomValues) ToStringSlice() []string {
|
||||
// values := make([]string, len(c))
|
||||
// for i := range c {
|
||||
// values[i] = string(c[i])
|
||||
// }
|
||||
// return values
|
||||
// }
|
||||
//
|
||||
// func query() {
|
||||
// // ...
|
||||
// result, err := dbmap.Select(&output, "SELECT 1 FROM example WHERE value IN (:Values)", map[string]interface{}{
|
||||
// "Values": CustomValues([]CustomValue{CustomValueHey}),
|
||||
// })
|
||||
// // ...
|
||||
// }
|
||||
package borp
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gorp
|
||||
package borp
|
||||
|
||||
import (
|
||||
"fmt"
|
178
vendor/github.com/go-gorp/gorp/v3/gorp.go → vendor/github.com/letsencrypt/borp/gorp.go
generated
vendored
178
vendor/github.com/go-gorp/gorp/v3/gorp.go → vendor/github.com/letsencrypt/borp/gorp.go
generated
vendored
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gorp
|
||||
package borp
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -15,30 +15,6 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
// OracleString (empty string is null)
|
||||
// TODO: move to dialect/oracle?, rename to String?
|
||||
type OracleString struct {
|
||||
sql.NullString
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (os *OracleString) Scan(value interface{}) error {
|
||||
if value == nil {
|
||||
os.String, os.Valid = "", false
|
||||
return nil
|
||||
}
|
||||
os.Valid = true
|
||||
return os.NullString.Scan(value)
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (os OracleString) Value() (driver.Value, error) {
|
||||
if !os.Valid || os.String == "" {
|
||||
return nil, nil
|
||||
}
|
||||
return os.String, nil
|
||||
}
|
||||
|
||||
// SqlTyper is a type that returns its database type. Most of the
|
||||
// time, the type can just use "database/sql/driver".Valuer; but when
|
||||
// it returns nil for its empty value, it needs to implement SqlTyper
|
||||
|
@ -61,7 +37,6 @@ func (nt *dummyField) Scan(value interface{}) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
var zeroVal reflect.Value
|
||||
var versFieldConst = "[gorp_ver_field]"
|
||||
|
||||
// The TypeConverter interface provides a way to map a value of one
|
||||
|
@ -90,22 +65,28 @@ type TypeConverter interface {
|
|||
// See the DbMap function docs for each of the functions below for more
|
||||
// information.
|
||||
type SqlExecutor interface {
|
||||
WithContext(ctx context.Context) SqlExecutor
|
||||
Get(i interface{}, keys ...interface{}) (interface{}, error)
|
||||
Insert(list ...interface{}) error
|
||||
Update(list ...interface{}) (int64, error)
|
||||
Delete(list ...interface{}) (int64, error)
|
||||
Exec(query string, args ...interface{}) (sql.Result, error)
|
||||
Select(i interface{}, query string, args ...interface{}) ([]interface{}, error)
|
||||
SelectInt(query string, args ...interface{}) (int64, error)
|
||||
SelectNullInt(query string, args ...interface{}) (sql.NullInt64, error)
|
||||
SelectFloat(query string, args ...interface{}) (float64, error)
|
||||
SelectNullFloat(query string, args ...interface{}) (sql.NullFloat64, error)
|
||||
SelectStr(query string, args ...interface{}) (string, error)
|
||||
SelectNullStr(query string, args ...interface{}) (sql.NullString, error)
|
||||
SelectOne(holder interface{}, query string, args ...interface{}) error
|
||||
Query(query string, args ...interface{}) (*sql.Rows, error)
|
||||
QueryRow(query string, args ...interface{}) *sql.Row
|
||||
Get(ctx context.Context, i interface{}, keys ...interface{}) (interface{}, error)
|
||||
Insert(ctx context.Context, list ...interface{}) error
|
||||
Update(ctx context.Context, list ...interface{}) (int64, error)
|
||||
Delete(ctx context.Context, list ...interface{}) (int64, error)
|
||||
Select(ctx context.Context, i interface{}, query string, args ...interface{}) ([]interface{}, error)
|
||||
SelectInt(ctx context.Context, query string, args ...interface{}) (int64, error)
|
||||
SelectNullInt(ctx context.Context, query string, args ...interface{}) (sql.NullInt64, error)
|
||||
SelectFloat(ctx context.Context, query string, args ...interface{}) (float64, error)
|
||||
SelectNullFloat(ctx context.Context, query string, args ...interface{}) (sql.NullFloat64, error)
|
||||
SelectStr(ctx context.Context, query string, args ...interface{}) (string, error)
|
||||
SelectNullStr(ctx context.Context, query string, args ...interface{}) (sql.NullString, error)
|
||||
SelectOne(ctx context.Context, holder interface{}, query string, args ...interface{}) error
|
||||
|
||||
// These method signatures are shared with *sql.DB.
|
||||
// Stylistically, `Context` in the name is redundant. It is the future, everything takes a context.
|
||||
// But since these three functions delegate to functions of the same name on *sql.DB, it would be
|
||||
// a little confusing if we had, e.g. `Exec` (taking a context), which delegates to
|
||||
// `sql.DB.ExecContext` (taking a context) as opposed to `sql.DB.Exec` (taking no context).
|
||||
// So we don't bother with `Context` in the name for Get, Insert, etc., but we do for these.
|
||||
ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error)
|
||||
QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error)
|
||||
QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row
|
||||
}
|
||||
|
||||
// DynamicTable allows the users of gorp to dynamically
|
||||
|
@ -156,14 +137,21 @@ func argsString(args ...interface{}) string {
|
|||
|
||||
// Calls the Exec function on the executor, but attempts to expand any eligible named
|
||||
// query arguments first.
|
||||
func maybeExpandNamedQueryAndExec(e SqlExecutor, query string, args ...interface{}) (sql.Result, error) {
|
||||
func maybeExpandNamedQueryAndExec(ctx context.Context, e SqlExecutor, query string, args ...interface{}) (sql.Result, error) {
|
||||
dbMap := extractDbMap(e)
|
||||
|
||||
if len(args) == 1 {
|
||||
query, args = maybeExpandNamedQuery(dbMap, query, args)
|
||||
}
|
||||
|
||||
return exec(e, query, args...)
|
||||
switch m := e.(type) {
|
||||
case *DbMap:
|
||||
return m.Db.ExecContext(ctx, query, args...)
|
||||
case *Transaction:
|
||||
return m.tx.ExecContext(ctx, query, args...)
|
||||
default:
|
||||
return nil, fmt.Errorf("gorp: unknown SqlExecutor type: %T", m)
|
||||
}
|
||||
}
|
||||
|
||||
func extractDbMap(e SqlExecutor) *DbMap {
|
||||
|
@ -176,29 +164,6 @@ func extractDbMap(e SqlExecutor) *DbMap {
|
|||
return nil
|
||||
}
|
||||
|
||||
// executor exposes the sql.DB and sql.Tx functions so that it can be used
|
||||
// on internal functions that need to be agnostic to the underlying object.
|
||||
type executor interface {
|
||||
Exec(query string, args ...interface{}) (sql.Result, error)
|
||||
Prepare(query string) (*sql.Stmt, error)
|
||||
QueryRow(query string, args ...interface{}) *sql.Row
|
||||
Query(query string, args ...interface{}) (*sql.Rows, error)
|
||||
ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error)
|
||||
PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)
|
||||
QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row
|
||||
QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error)
|
||||
}
|
||||
|
||||
func extractExecutorAndContext(e SqlExecutor) (executor, context.Context) {
|
||||
switch m := e.(type) {
|
||||
case *DbMap:
|
||||
return m.Db, m.ctx
|
||||
case *Transaction:
|
||||
return m.tx, m.ctx
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// maybeExpandNamedQuery checks the given arg to see if it's eligible to be used
|
||||
// as input to a named query. If so, it rewrites the query to use
|
||||
// dialect-dependent bindvars and instantiates the corresponding slice of
|
||||
|
@ -311,12 +276,13 @@ func fieldByName(val reflect.Value, fieldName string) *reflect.Value {
|
|||
// try to find field by exact match
|
||||
f := val.FieldByName(fieldName)
|
||||
|
||||
var zeroVal reflect.Value
|
||||
if f != zeroVal {
|
||||
return &f
|
||||
}
|
||||
|
||||
// try to find by case insensitive match - only the Postgres driver
|
||||
// seems to require this - in the case where columns are aliased in the sql
|
||||
// try to find by case insensitive match in the case where columns are
|
||||
// aliased in the sql
|
||||
fieldNameL := strings.ToLower(fieldName)
|
||||
fieldCount := val.NumField()
|
||||
t := val.Type()
|
||||
|
@ -387,7 +353,7 @@ func tableFor(m *DbMap, t reflect.Type, i interface{}) (*foundTable, error) {
|
|||
return &foundTable{table: table}, nil
|
||||
}
|
||||
|
||||
func get(m *DbMap, exec SqlExecutor, i interface{},
|
||||
func get(ctx context.Context, m *DbMap, exec SqlExecutor, i interface{},
|
||||
keys ...interface{}) (interface{}, error) {
|
||||
|
||||
t, err := toType(i)
|
||||
|
@ -427,7 +393,7 @@ func get(m *DbMap, exec SqlExecutor, i interface{},
|
|||
dest[x] = target
|
||||
}
|
||||
|
||||
row := exec.QueryRow(plan.query, keys...)
|
||||
row := exec.QueryRowContext(ctx, plan.query, keys...)
|
||||
err = row.Scan(dest...)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
|
@ -453,7 +419,7 @@ func get(m *DbMap, exec SqlExecutor, i interface{},
|
|||
return v.Interface(), nil
|
||||
}
|
||||
|
||||
func delete(m *DbMap, exec SqlExecutor, list ...interface{}) (int64, error) {
|
||||
func delete(ctx context.Context, m *DbMap, exec SqlExecutor, list ...interface{}) (int64, error) {
|
||||
count := int64(0)
|
||||
for _, ptr := range list {
|
||||
table, elem, err := m.tableForPointer(ptr, true)
|
||||
|
@ -474,7 +440,7 @@ func delete(m *DbMap, exec SqlExecutor, list ...interface{}) (int64, error) {
|
|||
return -1, err
|
||||
}
|
||||
|
||||
res, err := exec.Exec(bi.query, bi.args...)
|
||||
res, err := exec.ExecContext(ctx, bi.query, bi.args...)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
@ -484,7 +450,7 @@ func delete(m *DbMap, exec SqlExecutor, list ...interface{}) (int64, error) {
|
|||
}
|
||||
|
||||
if rows == 0 && bi.existingVersion > 0 {
|
||||
return lockError(m, exec, table.TableName,
|
||||
return lockError(ctx, m, exec, table.TableName,
|
||||
bi.existingVersion, elem, bi.keys...)
|
||||
}
|
||||
|
||||
|
@ -501,7 +467,7 @@ func delete(m *DbMap, exec SqlExecutor, list ...interface{}) (int64, error) {
|
|||
return count, nil
|
||||
}
|
||||
|
||||
func update(m *DbMap, exec SqlExecutor, colFilter ColumnFilter, list ...interface{}) (int64, error) {
|
||||
func update(ctx context.Context, m *DbMap, exec SqlExecutor, colFilter ColumnFilter, list ...interface{}) (int64, error) {
|
||||
count := int64(0)
|
||||
for _, ptr := range list {
|
||||
table, elem, err := m.tableForPointer(ptr, true)
|
||||
|
@ -522,7 +488,7 @@ func update(m *DbMap, exec SqlExecutor, colFilter ColumnFilter, list ...interfac
|
|||
return -1, err
|
||||
}
|
||||
|
||||
res, err := exec.Exec(bi.query, bi.args...)
|
||||
res, err := exec.ExecContext(ctx, bi.query, bi.args...)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
@ -533,7 +499,7 @@ func update(m *DbMap, exec SqlExecutor, colFilter ColumnFilter, list ...interfac
|
|||
}
|
||||
|
||||
if rows == 0 && bi.existingVersion > 0 {
|
||||
return lockError(m, exec, table.TableName,
|
||||
return lockError(ctx, m, exec, table.TableName,
|
||||
bi.existingVersion, elem, bi.keys...)
|
||||
}
|
||||
|
||||
|
@ -553,7 +519,7 @@ func update(m *DbMap, exec SqlExecutor, colFilter ColumnFilter, list ...interfac
|
|||
return count, nil
|
||||
}
|
||||
|
||||
func insert(m *DbMap, exec SqlExecutor, list ...interface{}) error {
|
||||
func insert(ctx context.Context, m *DbMap, exec SqlExecutor, list ...interface{}) error {
|
||||
for _, ptr := range list {
|
||||
table, elem, err := m.tableForPointer(ptr, false)
|
||||
if err != nil {
|
||||
|
@ -577,7 +543,7 @@ func insert(m *DbMap, exec SqlExecutor, list ...interface{}) error {
|
|||
f := elem.FieldByName(bi.autoIncrFieldName)
|
||||
switch inserter := m.Dialect.(type) {
|
||||
case IntegerAutoIncrInserter:
|
||||
id, err := inserter.InsertAutoIncr(exec, bi.query, bi.args...)
|
||||
id, err := inserter.InsertAutoIncr(ctx, exec, bi.query, bi.args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -590,7 +556,7 @@ func insert(m *DbMap, exec SqlExecutor, list ...interface{}) error {
|
|||
return fmt.Errorf("gorp: cannot set autoincrement value on non-Int field. SQL=%s autoIncrIdx=%d autoIncrFieldName=%s", bi.query, bi.autoIncrIdx, bi.autoIncrFieldName)
|
||||
}
|
||||
case TargetedAutoIncrInserter:
|
||||
err := inserter.InsertAutoIncrToTarget(exec, bi.query, f.Addr().Interface(), bi.args...)
|
||||
err := inserter.InsertAutoIncrToTarget(ctx, exec, bi.query, f.Addr().Interface(), bi.args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -599,7 +565,7 @@ func insert(m *DbMap, exec SqlExecutor, list ...interface{}) error {
|
|||
if idQuery == "" {
|
||||
return fmt.Errorf("gorp: cannot set %s value if its ColumnMap.GeneratedIdQuery is empty", bi.autoIncrFieldName)
|
||||
}
|
||||
err := inserter.InsertQueryToTarget(exec, bi.query, idQuery, f.Addr().Interface(), bi.args...)
|
||||
err := inserter.InsertQueryToTarget(ctx, exec, bi.query, idQuery, f.Addr().Interface(), bi.args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -607,7 +573,7 @@ func insert(m *DbMap, exec SqlExecutor, list ...interface{}) error {
|
|||
return fmt.Errorf("gorp: cannot use autoincrement fields on dialects that do not implement an autoincrementing interface")
|
||||
}
|
||||
} else {
|
||||
_, err := exec.Exec(bi.query, bi.args...)
|
||||
_, err := exec.ExecContext(ctx, bi.query, bi.args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -622,51 +588,3 @@ func insert(m *DbMap, exec SqlExecutor, list ...interface{}) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func exec(e SqlExecutor, query string, args ...interface{}) (sql.Result, error) {
|
||||
executor, ctx := extractExecutorAndContext(e)
|
||||
|
||||
if ctx != nil {
|
||||
return executor.ExecContext(ctx, query, args...)
|
||||
}
|
||||
|
||||
return executor.Exec(query, args...)
|
||||
}
|
||||
|
||||
func prepare(e SqlExecutor, query string) (*sql.Stmt, error) {
|
||||
executor, ctx := extractExecutorAndContext(e)
|
||||
|
||||
if ctx != nil {
|
||||
return executor.PrepareContext(ctx, query)
|
||||
}
|
||||
|
||||
return executor.Prepare(query)
|
||||
}
|
||||
|
||||
func queryRow(e SqlExecutor, query string, args ...interface{}) *sql.Row {
|
||||
executor, ctx := extractExecutorAndContext(e)
|
||||
|
||||
if ctx != nil {
|
||||
return executor.QueryRowContext(ctx, query, args...)
|
||||
}
|
||||
|
||||
return executor.QueryRow(query, args...)
|
||||
}
|
||||
|
||||
func query(e SqlExecutor, query string, args ...interface{}) (*sql.Rows, error) {
|
||||
executor, ctx := extractExecutorAndContext(e)
|
||||
|
||||
if ctx != nil {
|
||||
return executor.QueryContext(ctx, query, args...)
|
||||
}
|
||||
|
||||
return executor.Query(query, args...)
|
||||
}
|
||||
|
||||
func begin(m *DbMap) (*sql.Tx, error) {
|
||||
if m.ctx != nil {
|
||||
return m.Db.BeginTx(m.ctx, nil)
|
||||
}
|
||||
|
||||
return m.Db.Begin()
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gorp
|
||||
package borp
|
||||
|
||||
//++ TODO v2-phase3: HasPostGet => PostGetter, HasPostDelete => PostDeleter, etc.
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gorp
|
||||
package borp
|
||||
|
||||
// IndexMap represents a mapping between a Go struct field and a single
|
||||
// index in a table.
|
||||
|
@ -17,7 +17,6 @@ type IndexMap struct {
|
|||
Unique bool
|
||||
|
||||
// Index type supported by Dialect
|
||||
// Postgres: B-tree, Hash, GiST and GIN.
|
||||
// Mysql: Btree, Hash.
|
||||
// Sqlite: nil.
|
||||
IndexType string
|
||||
|
@ -29,7 +28,6 @@ type IndexMap struct {
|
|||
// Rename allows you to specify the index name in the table
|
||||
//
|
||||
// Example: table.IndMap("customer_test_idx").Rename("customer_idx")
|
||||
//
|
||||
func (idx *IndexMap) Rename(indname string) *IndexMap {
|
||||
idx.IndexName = indname
|
||||
return idx
|
|
@ -2,9 +2,10 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gorp
|
||||
package borp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
@ -39,11 +40,11 @@ func (e OptimisticLockError) Error() string {
|
|||
return fmt.Sprintf("gorp: OptimisticLockError no row found for table=%s keys=%v", e.TableName, e.Keys)
|
||||
}
|
||||
|
||||
func lockError(m *DbMap, exec SqlExecutor, tableName string,
|
||||
func lockError(ctx context.Context, m *DbMap, exec SqlExecutor, tableName string,
|
||||
existingVer int64, elem reflect.Value,
|
||||
keys ...interface{}) (int64, error) {
|
||||
|
||||
existing, err := get(m, exec, elem.Interface(), keys...)
|
||||
existing, err := get(ctx, m, exec, elem.Interface(), keys...)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gorp
|
||||
package borp
|
||||
|
||||
import "fmt"
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gorp
|
||||
package borp
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
|
@ -2,9 +2,10 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gorp
|
||||
package borp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
@ -13,9 +14,9 @@ import (
|
|||
// SelectInt executes the given query, which should be a SELECT statement for a single
|
||||
// integer column, and returns the value of the first row returned. If no rows are
|
||||
// found, zero is returned.
|
||||
func SelectInt(e SqlExecutor, query string, args ...interface{}) (int64, error) {
|
||||
func SelectInt(ctx context.Context, e SqlExecutor, query string, args ...interface{}) (int64, error) {
|
||||
var h int64
|
||||
err := selectVal(e, &h, query, args...)
|
||||
err := selectVal(ctx, e, &h, query, args...)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return 0, err
|
||||
}
|
||||
|
@ -25,9 +26,9 @@ func SelectInt(e SqlExecutor, query string, args ...interface{}) (int64, error)
|
|||
// SelectNullInt executes the given query, which should be a SELECT statement for a single
|
||||
// integer column, and returns the value of the first row returned. If no rows are
|
||||
// found, the empty sql.NullInt64 value is returned.
|
||||
func SelectNullInt(e SqlExecutor, query string, args ...interface{}) (sql.NullInt64, error) {
|
||||
func SelectNullInt(ctx context.Context, e SqlExecutor, query string, args ...interface{}) (sql.NullInt64, error) {
|
||||
var h sql.NullInt64
|
||||
err := selectVal(e, &h, query, args...)
|
||||
err := selectVal(ctx, e, &h, query, args...)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return h, err
|
||||
}
|
||||
|
@ -37,9 +38,9 @@ func SelectNullInt(e SqlExecutor, query string, args ...interface{}) (sql.NullIn
|
|||
// SelectFloat executes the given query, which should be a SELECT statement for a single
|
||||
// float column, and returns the value of the first row returned. If no rows are
|
||||
// found, zero is returned.
|
||||
func SelectFloat(e SqlExecutor, query string, args ...interface{}) (float64, error) {
|
||||
func SelectFloat(ctx context.Context, e SqlExecutor, query string, args ...interface{}) (float64, error) {
|
||||
var h float64
|
||||
err := selectVal(e, &h, query, args...)
|
||||
err := selectVal(ctx, e, &h, query, args...)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return 0, err
|
||||
}
|
||||
|
@ -49,9 +50,9 @@ func SelectFloat(e SqlExecutor, query string, args ...interface{}) (float64, err
|
|||
// SelectNullFloat executes the given query, which should be a SELECT statement for a single
|
||||
// float column, and returns the value of the first row returned. If no rows are
|
||||
// found, the empty sql.NullInt64 value is returned.
|
||||
func SelectNullFloat(e SqlExecutor, query string, args ...interface{}) (sql.NullFloat64, error) {
|
||||
func SelectNullFloat(ctx context.Context, e SqlExecutor, query string, args ...interface{}) (sql.NullFloat64, error) {
|
||||
var h sql.NullFloat64
|
||||
err := selectVal(e, &h, query, args...)
|
||||
err := selectVal(ctx, e, &h, query, args...)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return h, err
|
||||
}
|
||||
|
@ -61,9 +62,9 @@ func SelectNullFloat(e SqlExecutor, query string, args ...interface{}) (sql.Null
|
|||
// SelectStr executes the given query, which should be a SELECT statement for a single
|
||||
// char/varchar column, and returns the value of the first row returned. If no rows are
|
||||
// found, an empty string is returned.
|
||||
func SelectStr(e SqlExecutor, query string, args ...interface{}) (string, error) {
|
||||
func SelectStr(ctx context.Context, e SqlExecutor, query string, args ...interface{}) (string, error) {
|
||||
var h string
|
||||
err := selectVal(e, &h, query, args...)
|
||||
err := selectVal(ctx, e, &h, query, args...)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return "", err
|
||||
}
|
||||
|
@ -74,9 +75,9 @@ func SelectStr(e SqlExecutor, query string, args ...interface{}) (string, error)
|
|||
// statement for a single char/varchar column, and returns the value
|
||||
// of the first row returned. If no rows are found, the empty
|
||||
// sql.NullString is returned.
|
||||
func SelectNullStr(e SqlExecutor, query string, args ...interface{}) (sql.NullString, error) {
|
||||
func SelectNullStr(ctx context.Context, e SqlExecutor, query string, args ...interface{}) (sql.NullString, error) {
|
||||
var h sql.NullString
|
||||
err := selectVal(e, &h, query, args...)
|
||||
err := selectVal(ctx, e, &h, query, args...)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return h, err
|
||||
}
|
||||
|
@ -86,11 +87,10 @@ func SelectNullStr(e SqlExecutor, query string, args ...interface{}) (sql.NullSt
|
|||
// SelectOne executes the given query (which should be a SELECT statement)
|
||||
// and binds the result to holder, which must be a pointer.
|
||||
//
|
||||
// If no row is found, an error (sql.ErrNoRows specifically) will be returned
|
||||
// If no row is found, an error (sql.ErrNoRows specifically) will be returned.
|
||||
//
|
||||
// If more than one row is found, an error will be returned.
|
||||
//
|
||||
func SelectOne(m *DbMap, e SqlExecutor, holder interface{}, query string, args ...interface{}) error {
|
||||
func SelectOne(ctx context.Context, m *DbMap, e SqlExecutor, holder interface{}, query string, args ...interface{}) error {
|
||||
t := reflect.TypeOf(holder)
|
||||
if t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
|
@ -108,7 +108,7 @@ func SelectOne(m *DbMap, e SqlExecutor, holder interface{}, query string, args .
|
|||
if t.Kind() == reflect.Struct {
|
||||
var nonFatalErr error
|
||||
|
||||
list, err := hookedselect(m, e, holder, query, args...)
|
||||
list, err := hookedselect(ctx, m, e, holder, query, args...)
|
||||
if err != nil {
|
||||
if !NonFatalError(err) { // FIXME: double negative, rename NonFatalError to FatalError
|
||||
return err
|
||||
|
@ -143,10 +143,10 @@ func SelectOne(m *DbMap, e SqlExecutor, holder interface{}, query string, args .
|
|||
return nonFatalErr
|
||||
}
|
||||
|
||||
return selectVal(e, holder, query, args...)
|
||||
return selectVal(ctx, e, holder, query, args...)
|
||||
}
|
||||
|
||||
func selectVal(e SqlExecutor, holder interface{}, query string, args ...interface{}) error {
|
||||
func selectVal(ctx context.Context, e SqlExecutor, holder interface{}, query string, args ...interface{}) error {
|
||||
if len(args) == 1 {
|
||||
switch m := e.(type) {
|
||||
case *DbMap:
|
||||
|
@ -155,7 +155,7 @@ func selectVal(e SqlExecutor, holder interface{}, query string, args ...interfac
|
|||
query, args = maybeExpandNamedQuery(m.dbmap, query, args)
|
||||
}
|
||||
}
|
||||
rows, err := e.Query(query, args...)
|
||||
rows, err := e.QueryContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -168,15 +168,20 @@ func selectVal(e SqlExecutor, holder interface{}, query string, args ...interfac
|
|||
return sql.ErrNoRows
|
||||
}
|
||||
|
||||
return rows.Scan(holder)
|
||||
err = rows.Scan(holder)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return rows.Close()
|
||||
}
|
||||
|
||||
func hookedselect(m *DbMap, exec SqlExecutor, i interface{}, query string,
|
||||
func hookedselect(ctx context.Context, m *DbMap, exec SqlExecutor, i interface{}, query string,
|
||||
args ...interface{}) ([]interface{}, error) {
|
||||
|
||||
var nonFatalErr error
|
||||
|
||||
list, err := rawselect(m, exec, i, query, args...)
|
||||
list, err := rawselect(ctx, m, exec, i, query, args...)
|
||||
if err != nil {
|
||||
if !NonFatalError(err) {
|
||||
return nil, err
|
||||
|
@ -208,7 +213,7 @@ func hookedselect(m *DbMap, exec SqlExecutor, i interface{}, query string,
|
|||
return list, nonFatalErr
|
||||
}
|
||||
|
||||
func rawselect(m *DbMap, exec SqlExecutor, i interface{}, query string,
|
||||
func rawselect(ctx context.Context, m *DbMap, exec SqlExecutor, i interface{}, query string,
|
||||
args ...interface{}) ([]interface{}, error) {
|
||||
var (
|
||||
appendToSlice = false // Write results to i directly?
|
||||
|
@ -251,7 +256,7 @@ func rawselect(m *DbMap, exec SqlExecutor, i interface{}, query string,
|
|||
}
|
||||
|
||||
// Run the query
|
||||
rows, err := exec.Query(query, args...)
|
||||
rows, err := exec.QueryContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -351,6 +356,11 @@ func rawselect(m *DbMap, exec SqlExecutor, i interface{}, query string,
|
|||
}
|
||||
}
|
||||
|
||||
err = rows.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if appendToSlice && sliceValue.IsNil() {
|
||||
sliceValue.Set(reflect.MakeSlice(sliceValue.Type(), 0, 0))
|
||||
}
|
12
vendor/github.com/go-gorp/gorp/v3/table.go → vendor/github.com/letsencrypt/borp/table.go
generated
vendored
12
vendor/github.com/go-gorp/gorp/v3/table.go → vendor/github.com/letsencrypt/borp/table.go
generated
vendored
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gorp
|
||||
package borp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
@ -47,7 +47,6 @@ func (t *TableMap) ResetSql() {
|
|||
// Automatically calls ResetSql() to ensure SQL statements are regenerated.
|
||||
//
|
||||
// Panics if isAutoIncr is true, and fieldNames length != 1
|
||||
//
|
||||
func (t *TableMap) SetKeys(isAutoIncr bool, fieldNames ...string) *TableMap {
|
||||
if isAutoIncr && len(fieldNames) != 1 {
|
||||
panic(fmt.Sprintf(
|
||||
|
@ -73,17 +72,13 @@ func (t *TableMap) SetKeys(isAutoIncr bool, fieldNames ...string) *TableMap {
|
|||
// Automatically calls ResetSql() to ensure SQL statements are regenerated.
|
||||
//
|
||||
// Panics if fieldNames length < 2.
|
||||
//
|
||||
func (t *TableMap) SetUniqueTogether(fieldNames ...string) *TableMap {
|
||||
if len(fieldNames) < 2 {
|
||||
panic(fmt.Sprintf(
|
||||
"gorp: SetUniqueTogether: must provide at least two fieldNames to set uniqueness constraint."))
|
||||
panic("gorp: SetUniqueTogether: must provide at least two fieldNames to set uniqueness constraint.")
|
||||
}
|
||||
|
||||
columns := make([]string, 0, len(fieldNames))
|
||||
for _, name := range fieldNames {
|
||||
columns = append(columns, name)
|
||||
}
|
||||
columns = append(columns, fieldNames...)
|
||||
|
||||
for _, existingColumns := range t.uniqueTogether {
|
||||
if equal(existingColumns, columns) {
|
||||
|
@ -135,7 +130,6 @@ func (t *TableMap) IdxMap(field string) *IndexMap {
|
|||
// Function will panic if one of the given for index columns does not exists
|
||||
//
|
||||
// Automatically calls ResetSql() to ensure SQL statements are regenerated.
|
||||
//
|
||||
func (t *TableMap) AddIndex(name string, idxtype string, columns []string) *IndexMap {
|
||||
// check if we have a index with this name already
|
||||
for _, idx := range t.indexes {
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gorp
|
||||
package borp
|
||||
|
||||
import (
|
||||
"bytes"
|
|
@ -6,11 +6,6 @@
|
|||
echo "Running unit tests"
|
||||
go test -race
|
||||
|
||||
echo "Testing against postgres"
|
||||
export GORP_TEST_DSN="host=postgres user=gorptest password=gorptest dbname=gorptest sslmode=disable"
|
||||
export GORP_TEST_DIALECT=postgres
|
||||
go test -tags integration $GOBUILDFLAG $@ .
|
||||
|
||||
echo "Testing against sqlite"
|
||||
export GORP_TEST_DSN=/tmp/gorptest.bin
|
||||
export GORP_TEST_DIALECT=sqlite
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gorp
|
||||
package borp
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -15,55 +15,47 @@ import (
|
|||
// of that transaction. Transactions should be terminated with
|
||||
// a call to Commit() or Rollback()
|
||||
type Transaction struct {
|
||||
ctx context.Context
|
||||
dbmap *DbMap
|
||||
tx *sql.Tx
|
||||
closed bool
|
||||
}
|
||||
|
||||
func (t *Transaction) WithContext(ctx context.Context) SqlExecutor {
|
||||
copy := &Transaction{}
|
||||
*copy = *t
|
||||
copy.ctx = ctx
|
||||
return copy
|
||||
}
|
||||
|
||||
// Insert has the same behavior as DbMap.Insert(), but runs in a transaction.
|
||||
func (t *Transaction) Insert(list ...interface{}) error {
|
||||
return insert(t.dbmap, t, list...)
|
||||
func (t *Transaction) Insert(ctx context.Context, list ...interface{}) error {
|
||||
return insert(ctx, t.dbmap, t, list...)
|
||||
}
|
||||
|
||||
// Update had the same behavior as DbMap.Update(), but runs in a transaction.
|
||||
func (t *Transaction) Update(list ...interface{}) (int64, error) {
|
||||
return update(t.dbmap, t, nil, list...)
|
||||
func (t *Transaction) Update(ctx context.Context, list ...interface{}) (int64, error) {
|
||||
return update(ctx, t.dbmap, t, nil, list...)
|
||||
}
|
||||
|
||||
// UpdateColumns had the same behavior as DbMap.UpdateColumns(), but runs in a transaction.
|
||||
func (t *Transaction) UpdateColumns(filter ColumnFilter, list ...interface{}) (int64, error) {
|
||||
return update(t.dbmap, t, filter, list...)
|
||||
func (t *Transaction) UpdateColumns(ctx context.Context, filter ColumnFilter, list ...interface{}) (int64, error) {
|
||||
return update(ctx, t.dbmap, t, filter, list...)
|
||||
}
|
||||
|
||||
// Delete has the same behavior as DbMap.Delete(), but runs in a transaction.
|
||||
func (t *Transaction) Delete(list ...interface{}) (int64, error) {
|
||||
return delete(t.dbmap, t, list...)
|
||||
func (t *Transaction) Delete(ctx context.Context, list ...interface{}) (int64, error) {
|
||||
return delete(ctx, t.dbmap, t, list...)
|
||||
}
|
||||
|
||||
// Get has the same behavior as DbMap.Get(), but runs in a transaction.
|
||||
func (t *Transaction) Get(i interface{}, keys ...interface{}) (interface{}, error) {
|
||||
return get(t.dbmap, t, i, keys...)
|
||||
func (t *Transaction) Get(ctx context.Context, i interface{}, keys ...interface{}) (interface{}, error) {
|
||||
return get(ctx, t.dbmap, t, i, keys...)
|
||||
}
|
||||
|
||||
// Select has the same behavior as DbMap.Select(), but runs in a transaction.
|
||||
func (t *Transaction) Select(i interface{}, query string, args ...interface{}) ([]interface{}, error) {
|
||||
func (t *Transaction) Select(ctx context.Context, i interface{}, query string, args ...interface{}) ([]interface{}, error) {
|
||||
if t.dbmap.ExpandSliceArgs {
|
||||
expandSliceArgs(&query, args...)
|
||||
}
|
||||
|
||||
return hookedselect(t.dbmap, t, i, query, args...)
|
||||
return hookedselect(ctx, t.dbmap, t, i, query, args...)
|
||||
}
|
||||
|
||||
// Exec has the same behavior as DbMap.Exec(), but runs in a transaction.
|
||||
func (t *Transaction) Exec(query string, args ...interface{}) (sql.Result, error) {
|
||||
// ExecContext has the same behavior as DbMap.ExecContext(), but runs in a transaction.
|
||||
func (t *Transaction) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) {
|
||||
if t.dbmap.ExpandSliceArgs {
|
||||
expandSliceArgs(&query, args...)
|
||||
}
|
||||
|
@ -72,70 +64,70 @@ func (t *Transaction) Exec(query string, args ...interface{}) (sql.Result, error
|
|||
now := time.Now()
|
||||
defer t.dbmap.trace(now, query, args...)
|
||||
}
|
||||
return maybeExpandNamedQueryAndExec(t, query, args...)
|
||||
return maybeExpandNamedQueryAndExec(ctx, t, query, args...)
|
||||
}
|
||||
|
||||
// SelectInt is a convenience wrapper around the gorp.SelectInt function.
|
||||
func (t *Transaction) SelectInt(query string, args ...interface{}) (int64, error) {
|
||||
// SelectInt is a convenience wrapper around the borp.SelectInt function.
|
||||
func (t *Transaction) SelectInt(ctx context.Context, query string, args ...interface{}) (int64, error) {
|
||||
if t.dbmap.ExpandSliceArgs {
|
||||
expandSliceArgs(&query, args...)
|
||||
}
|
||||
|
||||
return SelectInt(t, query, args...)
|
||||
return SelectInt(ctx, t, query, args...)
|
||||
}
|
||||
|
||||
// SelectNullInt is a convenience wrapper around the gorp.SelectNullInt function.
|
||||
func (t *Transaction) SelectNullInt(query string, args ...interface{}) (sql.NullInt64, error) {
|
||||
// SelectNullInt is a convenience wrapper around the borp.SelectNullInt function.
|
||||
func (t *Transaction) SelectNullInt(ctx context.Context, query string, args ...interface{}) (sql.NullInt64, error) {
|
||||
if t.dbmap.ExpandSliceArgs {
|
||||
expandSliceArgs(&query, args...)
|
||||
}
|
||||
|
||||
return SelectNullInt(t, query, args...)
|
||||
return SelectNullInt(ctx, t, query, args...)
|
||||
}
|
||||
|
||||
// SelectFloat is a convenience wrapper around the gorp.SelectFloat function.
|
||||
func (t *Transaction) SelectFloat(query string, args ...interface{}) (float64, error) {
|
||||
// SelectFloat is a convenience wrapper around the borp.SelectFloat function.
|
||||
func (t *Transaction) SelectFloat(ctx context.Context, query string, args ...interface{}) (float64, error) {
|
||||
if t.dbmap.ExpandSliceArgs {
|
||||
expandSliceArgs(&query, args...)
|
||||
}
|
||||
|
||||
return SelectFloat(t, query, args...)
|
||||
return SelectFloat(ctx, t, query, args...)
|
||||
}
|
||||
|
||||
// SelectNullFloat is a convenience wrapper around the gorp.SelectNullFloat function.
|
||||
func (t *Transaction) SelectNullFloat(query string, args ...interface{}) (sql.NullFloat64, error) {
|
||||
// SelectNullFloat is a convenience wrapper around the borp.SelectNullFloat function.
|
||||
func (t *Transaction) SelectNullFloat(ctx context.Context, query string, args ...interface{}) (sql.NullFloat64, error) {
|
||||
if t.dbmap.ExpandSliceArgs {
|
||||
expandSliceArgs(&query, args...)
|
||||
}
|
||||
|
||||
return SelectNullFloat(t, query, args...)
|
||||
return SelectNullFloat(ctx, t, query, args...)
|
||||
}
|
||||
|
||||
// SelectStr is a convenience wrapper around the gorp.SelectStr function.
|
||||
func (t *Transaction) SelectStr(query string, args ...interface{}) (string, error) {
|
||||
// SelectStr is a convenience wrapper around the borp.SelectStr function.
|
||||
func (t *Transaction) SelectStr(ctx context.Context, query string, args ...interface{}) (string, error) {
|
||||
if t.dbmap.ExpandSliceArgs {
|
||||
expandSliceArgs(&query, args...)
|
||||
}
|
||||
|
||||
return SelectStr(t, query, args...)
|
||||
return SelectStr(ctx, t, query, args...)
|
||||
}
|
||||
|
||||
// SelectNullStr is a convenience wrapper around the gorp.SelectNullStr function.
|
||||
func (t *Transaction) SelectNullStr(query string, args ...interface{}) (sql.NullString, error) {
|
||||
// SelectNullStr is a convenience wrapper around the borp.SelectNullStr function.
|
||||
func (t *Transaction) SelectNullStr(ctx context.Context, query string, args ...interface{}) (sql.NullString, error) {
|
||||
if t.dbmap.ExpandSliceArgs {
|
||||
expandSliceArgs(&query, args...)
|
||||
}
|
||||
|
||||
return SelectNullStr(t, query, args...)
|
||||
return SelectNullStr(ctx, t, query, args...)
|
||||
}
|
||||
|
||||
// SelectOne is a convenience wrapper around the gorp.SelectOne function.
|
||||
func (t *Transaction) SelectOne(holder interface{}, query string, args ...interface{}) error {
|
||||
// SelectOne is a convenience wrapper around the borp.SelectOne function.
|
||||
func (t *Transaction) SelectOne(ctx context.Context, holder interface{}, query string, args ...interface{}) error {
|
||||
if t.dbmap.ExpandSliceArgs {
|
||||
expandSliceArgs(&query, args...)
|
||||
}
|
||||
|
||||
return SelectOne(t.dbmap, t, holder, query, args...)
|
||||
return SelectOne(ctx, t.dbmap, t, holder, query, args...)
|
||||
}
|
||||
|
||||
// Commit commits the underlying database transaction.
|
||||
|
@ -169,52 +161,52 @@ func (t *Transaction) Rollback() error {
|
|||
// Savepoint creates a savepoint with the given name. The name is interpolated
|
||||
// directly into the SQL SAVEPOINT statement, so you must sanitize it if it is
|
||||
// derived from user input.
|
||||
func (t *Transaction) Savepoint(name string) error {
|
||||
func (t *Transaction) Savepoint(ctx context.Context, name string) error {
|
||||
query := "savepoint " + t.dbmap.Dialect.QuoteField(name)
|
||||
if t.dbmap.logger != nil {
|
||||
now := time.Now()
|
||||
defer t.dbmap.trace(now, query, nil)
|
||||
}
|
||||
_, err := exec(t, query)
|
||||
_, err := t.ExecContext(ctx, query)
|
||||
return err
|
||||
}
|
||||
|
||||
// RollbackToSavepoint rolls back to the savepoint with the given name. The
|
||||
// name is interpolated directly into the SQL SAVEPOINT statement, so you must
|
||||
// sanitize it if it is derived from user input.
|
||||
func (t *Transaction) RollbackToSavepoint(savepoint string) error {
|
||||
func (t *Transaction) RollbackToSavepoint(ctx context.Context, savepoint string) error {
|
||||
query := "rollback to savepoint " + t.dbmap.Dialect.QuoteField(savepoint)
|
||||
if t.dbmap.logger != nil {
|
||||
now := time.Now()
|
||||
defer t.dbmap.trace(now, query, nil)
|
||||
}
|
||||
_, err := exec(t, query)
|
||||
_, err := t.ExecContext(ctx, query)
|
||||
return err
|
||||
}
|
||||
|
||||
// ReleaseSavepint releases the savepoint with the given name. The name is
|
||||
// interpolated directly into the SQL SAVEPOINT statement, so you must sanitize
|
||||
// it if it is derived from user input.
|
||||
func (t *Transaction) ReleaseSavepoint(savepoint string) error {
|
||||
func (t *Transaction) ReleaseSavepoint(ctx context.Context, savepoint string) error {
|
||||
query := "release savepoint " + t.dbmap.Dialect.QuoteField(savepoint)
|
||||
if t.dbmap.logger != nil {
|
||||
now := time.Now()
|
||||
defer t.dbmap.trace(now, query, nil)
|
||||
}
|
||||
_, err := exec(t, query)
|
||||
_, err := t.ExecContext(ctx, query)
|
||||
return err
|
||||
}
|
||||
|
||||
// Prepare has the same behavior as DbMap.Prepare(), but runs in a transaction.
|
||||
func (t *Transaction) Prepare(query string) (*sql.Stmt, error) {
|
||||
func (t *Transaction) PrepareContext(ctx context.Context, query string) (*sql.Stmt, error) {
|
||||
if t.dbmap.logger != nil {
|
||||
now := time.Now()
|
||||
defer t.dbmap.trace(now, query, nil)
|
||||
}
|
||||
return prepare(t, query)
|
||||
return t.tx.PrepareContext(ctx, query)
|
||||
}
|
||||
|
||||
func (t *Transaction) QueryRow(query string, args ...interface{}) *sql.Row {
|
||||
func (t *Transaction) QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row {
|
||||
if t.dbmap.ExpandSliceArgs {
|
||||
expandSliceArgs(&query, args...)
|
||||
}
|
||||
|
@ -223,10 +215,10 @@ func (t *Transaction) QueryRow(query string, args ...interface{}) *sql.Row {
|
|||
now := time.Now()
|
||||
defer t.dbmap.trace(now, query, args...)
|
||||
}
|
||||
return queryRow(t, query, args...)
|
||||
return t.tx.QueryRowContext(ctx, query, args...)
|
||||
}
|
||||
|
||||
func (t *Transaction) Query(q string, args ...interface{}) (*sql.Rows, error) {
|
||||
func (t *Transaction) QueryContext(ctx context.Context, q string, args ...interface{}) (*sql.Rows, error) {
|
||||
if t.dbmap.ExpandSliceArgs {
|
||||
expandSliceArgs(&q, args...)
|
||||
}
|
||||
|
@ -235,5 +227,5 @@ func (t *Transaction) Query(q string, args ...interface{}) (*sql.Rows, error) {
|
|||
now := time.Now()
|
||||
defer t.dbmap.trace(now, q, args...)
|
||||
}
|
||||
return query(t, q, args...)
|
||||
return t.tx.QueryContext(ctx, q, args...)
|
||||
}
|
|
@ -132,9 +132,6 @@ github.com/eggsampler/acme/v3
|
|||
# github.com/felixge/httpsnoop v1.0.3
|
||||
## explicit; go 1.13
|
||||
github.com/felixge/httpsnoop
|
||||
# github.com/go-gorp/gorp/v3 v3.1.0
|
||||
## explicit; go 1.18
|
||||
github.com/go-gorp/gorp/v3
|
||||
# github.com/go-logr/logr v1.2.4
|
||||
## explicit; go 1.16
|
||||
github.com/go-logr/logr
|
||||
|
@ -204,6 +201,11 @@ github.com/hpcloud/tail/winfile
|
|||
# github.com/jmhodges/clock v1.2.0
|
||||
## explicit; go 1.17
|
||||
github.com/jmhodges/clock
|
||||
# github.com/kr/pretty v0.3.1
|
||||
## explicit; go 1.12
|
||||
# github.com/letsencrypt/borp v0.0.0-20230707160741-6cc6ce580243
|
||||
## explicit; go 1.20
|
||||
github.com/letsencrypt/borp
|
||||
# github.com/letsencrypt/challtestsrv v1.2.1
|
||||
## explicit; go 1.13
|
||||
github.com/letsencrypt/challtestsrv
|
||||
|
@ -225,6 +227,8 @@ github.com/miekg/pkcs11
|
|||
# github.com/pelletier/go-toml v1.9.3
|
||||
## explicit; go 1.12
|
||||
github.com/pelletier/go-toml
|
||||
# github.com/poy/onpar v1.1.2
|
||||
## explicit; go 1.14
|
||||
# github.com/prometheus/client_golang v1.14.0
|
||||
## explicit; go 1.17
|
||||
github.com/prometheus/client_golang/prometheus
|
||||
|
@ -244,8 +248,6 @@ github.com/prometheus/common/model
|
|||
github.com/prometheus/procfs
|
||||
github.com/prometheus/procfs/internal/fs
|
||||
github.com/prometheus/procfs/internal/util
|
||||
# github.com/rogpeppe/go-internal v1.9.0
|
||||
## explicit; go 1.17
|
||||
# github.com/syndtr/goleveldb v1.0.0
|
||||
## explicit
|
||||
github.com/syndtr/goleveldb/leveldb
|
||||
|
@ -513,6 +515,8 @@ google.golang.org/protobuf/types/known/fieldmaskpb
|
|||
google.golang.org/protobuf/types/known/structpb
|
||||
google.golang.org/protobuf/types/known/timestamppb
|
||||
google.golang.org/protobuf/types/known/wrapperspb
|
||||
# gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c
|
||||
## explicit; go 1.11
|
||||
# gopkg.in/fsnotify.v1 v1.4.7
|
||||
## explicit
|
||||
gopkg.in/fsnotify.v1
|
||||
|
|
Loading…
Reference in New Issue