254 lines
6.2 KiB
Go
254 lines
6.2 KiB
Go
|
// Copyright (c) 2013 Conformal Systems LLC.
|
||
|
// Use of this source code is governed by an ISC
|
||
|
// license that can be found in the LICENSE file.
|
||
|
|
||
|
package sqlite3
|
||
|
|
||
|
import (
|
||
|
"database/sql"
|
||
|
"github.com/conformal/btcdb"
|
||
|
"github.com/conformal/btcwire"
|
||
|
_ "github.com/mattn/go-sqlite3"
|
||
|
)
|
||
|
|
||
|
// InsertTx inserts a tx hash and its associated data into the database.
|
||
|
func (db *SqliteDb) InsertTx(txsha *btcwire.ShaHash, blockidx int64, txoff int, txlen int, usedbuf []byte) (err error) {
|
||
|
db.dbLock.Lock()
|
||
|
defer db.dbLock.Unlock()
|
||
|
|
||
|
return db.insertTx(txsha, blockidx, txoff, txlen, usedbuf)
|
||
|
}
|
||
|
|
||
|
// insertTx inserts a tx hash and its associated data into the database.
|
||
|
// Must be called with db lock held.
|
||
|
func (db *SqliteDb) insertTx(txsha *btcwire.ShaHash, blockidx int64, txoff int, txlen int, usedbuf []byte) (err error) {
|
||
|
|
||
|
tx := &db.txState
|
||
|
if tx.tx == nil {
|
||
|
err = db.startTx()
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
blockid := blockidx + 1
|
||
|
txd := tTxInsertData{txsha: txsha, blockid: blockid, txoff: txoff, txlen: txlen, usedbuf: usedbuf}
|
||
|
|
||
|
log.Tracef("inserting tx %v for block %v off %v len %v",
|
||
|
txsha, blockid, txoff, txlen)
|
||
|
|
||
|
rowBytes := txsha.String()
|
||
|
|
||
|
var op int // which table to insert data into.
|
||
|
if db.UseTempTX {
|
||
|
var tblockid int64
|
||
|
var ttxoff int
|
||
|
var ttxlen int
|
||
|
txop := db.txop(txFetchLocationByShaStmt)
|
||
|
row := txop.QueryRow(rowBytes)
|
||
|
err = row.Scan(&tblockid, &ttxoff, &ttxlen)
|
||
|
if err != sql.ErrNoRows {
|
||
|
// sha already present
|
||
|
err = btcdb.DuplicateSha
|
||
|
return
|
||
|
}
|
||
|
op = txtmpInsertStmt
|
||
|
} else {
|
||
|
op = txInsertStmt
|
||
|
}
|
||
|
|
||
|
txop := db.txop(op)
|
||
|
_, err = txop.Exec(rowBytes, blockid, txoff, txlen, usedbuf)
|
||
|
if err != nil {
|
||
|
log.Warnf("failed to insert %v %v %v", txsha, blockid, err)
|
||
|
return
|
||
|
}
|
||
|
if db.UseTempTX {
|
||
|
db.TempTblSz++
|
||
|
}
|
||
|
|
||
|
// put in insert list for replay
|
||
|
tx.txInsertList = append(tx.txInsertList, txd)
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// FetchLocationBySha looks up the Tx sha information by name.
|
||
|
func (db *SqliteDb) FetchLocationBySha(txsha *btcwire.ShaHash) (blockidx int64, txoff int, txlen int, err error) {
|
||
|
db.dbLock.Lock()
|
||
|
defer db.dbLock.Unlock()
|
||
|
|
||
|
return db.fetchLocationBySha(txsha)
|
||
|
}
|
||
|
|
||
|
// fetchLocationBySha look up the Tx sha information by name.
|
||
|
// Must be called with db lock held.
|
||
|
func (db *SqliteDb) fetchLocationBySha(txsha *btcwire.ShaHash) (blockidx int64, txoff int, txlen int, err error) {
|
||
|
var row *sql.Row
|
||
|
var blockid int64
|
||
|
var ttxoff int
|
||
|
var ttxlen int
|
||
|
|
||
|
rowBytes := txsha.String()
|
||
|
txop := db.txop(txFetchLocationByShaStmt)
|
||
|
row = txop.QueryRow(rowBytes)
|
||
|
|
||
|
err = row.Scan(&blockid, &ttxoff, &ttxlen)
|
||
|
if err == sql.ErrNoRows {
|
||
|
txop = db.txop(txtmpFetchLocationByShaStmt)
|
||
|
row = txop.QueryRow(rowBytes)
|
||
|
|
||
|
err = row.Scan(&blockid, &ttxoff, &ttxlen)
|
||
|
if err == sql.ErrNoRows {
|
||
|
err = btcdb.TxShaMissing
|
||
|
return
|
||
|
}
|
||
|
if err != nil {
|
||
|
log.Warnf("txtmp FetchLocationBySha: fail %v",
|
||
|
err)
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
if err != nil {
|
||
|
log.Warnf("FetchLocationBySha: fail %v", err)
|
||
|
return
|
||
|
}
|
||
|
blockidx = blockid - 1
|
||
|
txoff = ttxoff
|
||
|
txlen = ttxlen
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// FetchTxUsedBySha returns the used/spent buffer for a given transaction.
|
||
|
func (db *SqliteDb) FetchTxUsedBySha(txsha *btcwire.ShaHash) (spentbuf []byte, err error) {
|
||
|
var row *sql.Row
|
||
|
db.dbLock.Lock()
|
||
|
defer db.dbLock.Unlock()
|
||
|
|
||
|
rowBytes := txsha.String()
|
||
|
txop := db.txop(txFetchUsedByShaStmt)
|
||
|
row = txop.QueryRow(rowBytes)
|
||
|
|
||
|
var databytes []byte
|
||
|
err = row.Scan(&databytes)
|
||
|
if err == sql.ErrNoRows {
|
||
|
txop := db.txop(txtmpFetchUsedByShaStmt)
|
||
|
row = txop.QueryRow(rowBytes)
|
||
|
|
||
|
err = row.Scan(&databytes)
|
||
|
if err == sql.ErrNoRows {
|
||
|
err = btcdb.TxShaMissing
|
||
|
return
|
||
|
}
|
||
|
if err != nil {
|
||
|
log.Warnf("txtmp FetchLocationBySha: fail %v",
|
||
|
err)
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if err != nil {
|
||
|
log.Warnf("FetchUsedBySha: fail %v", err)
|
||
|
return
|
||
|
}
|
||
|
spentbuf = databytes
|
||
|
return
|
||
|
}
|
||
|
|
||
|
var vaccumDbNextMigrate bool
|
||
|
|
||
|
// migrateTmpTable functions to perform internal db optimization when
|
||
|
// performing large numbers of database inserts. When in Fast operation
|
||
|
// mode, it inserts into txtmp, then when that table reaches a certain
|
||
|
// size limit it moves all tx in the txtmp table into the primary tx
|
||
|
// table and recomputes the index on the primary tx table.
|
||
|
func (db *SqliteDb) migrateTmpTable() error {
|
||
|
db.endTx(true)
|
||
|
db.startTx() // ???
|
||
|
|
||
|
db.UseTempTX = false
|
||
|
db.TempTblSz = 0
|
||
|
|
||
|
var doVacuum bool
|
||
|
var nsteps int
|
||
|
if vaccumDbNextMigrate {
|
||
|
nsteps = 6
|
||
|
vaccumDbNextMigrate = false
|
||
|
doVacuum = true
|
||
|
} else {
|
||
|
nsteps = 5
|
||
|
vaccumDbNextMigrate = true
|
||
|
}
|
||
|
|
||
|
log.Infof("db compaction Stage 1/%v: Preparing", nsteps)
|
||
|
txop := db.txop(txMigratePrep)
|
||
|
_, err := txop.Exec()
|
||
|
if err != nil {
|
||
|
log.Warnf("Failed to prepare migrate - %v", err)
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
log.Infof("db compaction Stage 2/%v: Copying", nsteps)
|
||
|
txop = db.txop(txMigrateCopy)
|
||
|
_, err = txop.Exec()
|
||
|
if err != nil {
|
||
|
log.Warnf("Migrate read failed - %v", err)
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
log.Tracef("db compaction Stage 2a/%v: Enable db vacuum", nsteps)
|
||
|
txop = db.txop(txPragmaVacuumOn)
|
||
|
_, err = txop.Exec()
|
||
|
if err != nil {
|
||
|
log.Warnf("Migrate error trying to enable vacuum on "+
|
||
|
"temporary transaction table - %v", err)
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
log.Infof("db compaction Stage 3/%v: Clearing old data", nsteps)
|
||
|
txop = db.txop(txMigrateClear)
|
||
|
_, err = txop.Exec()
|
||
|
if err != nil {
|
||
|
log.Warnf("Migrate error trying to clear temporary "+
|
||
|
"transaction table - %v", err)
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
log.Tracef("db compaction Stage 3a/%v: Disable db vacuum", nsteps)
|
||
|
txop = db.txop(txPragmaVacuumOff)
|
||
|
_, err = txop.Exec()
|
||
|
if err != nil {
|
||
|
log.Warnf("Migrate error trying to disable vacuum on "+
|
||
|
"temporary transaction table - %v", err)
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
log.Infof("db compaction Stage 4/%v: Rebuilding index", nsteps)
|
||
|
txop = db.txop(txMigrateFinish)
|
||
|
_, err = txop.Exec()
|
||
|
if err != nil {
|
||
|
log.Warnf("Migrate error trying to clear temporary "+
|
||
|
"transaction table - %v", err)
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
log.Infof("db compaction Stage 5/%v: Finalizing transaction", nsteps)
|
||
|
db.endTx(true) // ???
|
||
|
|
||
|
if doVacuum {
|
||
|
log.Infof("db compaction Stage 6/%v: Optimizing database", nsteps)
|
||
|
txop = db.txop(txVacuum)
|
||
|
_, err = txop.Exec()
|
||
|
if err != nil {
|
||
|
log.Warnf("migrate error trying to clear txtmp tbl %v", err)
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
log.Infof("db compaction: Complete")
|
||
|
|
||
|
// TODO(drahn) - determine if this should be turned back on or not
|
||
|
db.UseTempTX = true
|
||
|
|
||
|
return nil
|
||
|
}
|