func (lo *SQLLimitOrderbook) UpdateBookCancel(cancel *match.CancelledOrder) (err error) {
	var tx *sql.Tx
	if tx, err = lo.DBHandler.Begin(); err != nil {
		err = fmt.Errorf("Error when beginning transaction for UpdateBookCancel: %s", err)
		return
	}
	defer func() {
		if err != nil {
			tx.Rollback()
			err = fmt.Errorf("Error with UpdateBookCancel: \n%s", err)
			return
		}
		err = tx.Commit()
	}()
	if _, err = tx.Exec("USE " + lo.orderSchema + ";"); err != nil {
		err = fmt.Errorf("Error using limit schema for UpdateBookCancel: %s", err)
		return
	}
	deleteOrderQuery := fmt.Sprintf("DELETE FROM %s WHERE orderID='%x';", lo.pair.String(), cancel.OrderID)
	var res sql.Result
	if res, err = tx.Exec(deleteOrderQuery); err != nil {
		err = fmt.Errorf("Error deleting order within tx for cancel: %s", err)
		return
	}
	var rowsAffected int64
	if rowsAffected, err = res.RowsAffected(); err != nil {
		err = fmt.Errorf("Error while getting rows affected for cancel: %s", err)
		return
	}
	if rowsAffected != 1 {
		err = fmt.Errorf("Error: Order cancel should only have affected one row. Instead, it affected %d", rowsAffected)
		return
	}
	return
}
UpdateBookCancel takes in an order cancellation and updates the orderbook.
package cxdbsql
import (
	"database/sql"
	"encoding/hex"
	"fmt"
	"net"
	"time"
	"github.com/mit-dci/lit/crypto/koblitz"
	"github.com/mit-dci/opencx/match"
)
type SQLLimitOrderbook struct {
	DBHandler *sql.DB
	dbUsername string
	dbPassword string
	dbAddr net.Addr
	orderSchema string
	pair *match.Pair
}
const (
	limitOrderbookSchema = "pubkey VARBINARY(66), orderID VARBINARY(64), side TEXT, price DOUBLE(30,2) UNSIGNED, amountHave BIGINT(64), amountWant BIGINT(64), time TIMESTAMP"
)
func CreateLimitOrderbook(pair *match.Pair) (book match.LimitOrderbook, err error) {
	conf := new(dbsqlConfig)
	*conf = *defaultConf
	dbConfigSetup(conf)
	var addr net.Addr
	if addr, err = net.ResolveTCPAddr("tcp", net.JoinHostPort(conf.DBHost, fmt.Sprintf("%d", conf.DBPort))); err != nil {
		err = fmt.Errorf("Couldn't resolve db address for CreateLimitEngine: %s", err)
		return
	}
	lo := &SQLLimitOrderbook{
		dbUsername:  conf.DBUsername,
		dbPassword:  conf.DBPassword,
		orderSchema: conf.ReadOnlyOrderSchemaName,
		dbAddr:      addr,
		pair:        pair,
	}
	if err = lo.setupLimitOrderbookTables(); err != nil {
		err = fmt.Errorf("Error setting up limit orderbook tables while creating engine: %s", err)
		return
	}
	openString := fmt.Sprintf("%s:%s@%s(%s)/", lo.dbUsername, lo.dbPassword, lo.dbAddr.Network(), lo.dbAddr.String())
	if lo.DBHandler, err = sql.Open("mysql", openString); err != nil {
		err = fmt.Errorf("Error opening database for CreateLimitEngine: %s", err)
		return
	}
	if err = lo.DBHandler.Ping(); err != nil {
		err = fmt.Errorf("Could not ping the database, is it running: %s", err)
		return
	}
	book = lo
	return
}
func (lo *SQLLimitOrderbook) setupLimitOrderbookTables() (err error) {
	openString := fmt.Sprintf("%s:%s@%s(%s)/", lo.dbUsername, lo.dbPassword, lo.dbAddr.Network(), lo.dbAddr.String())
	var rootHandler *sql.DB
	if rootHandler, err = sql.Open("mysql", openString); err != nil {
		err = fmt.Errorf("Error opening database for setup limit tables: %s", err)
		return
	}
	defer rootHandler.Close()
	if err = rootHandler.Ping(); err != nil {
		err = fmt.Errorf("Could not ping the database, is it running: %s", err)
		return
	}
	var tx *sql.Tx
	if tx, err = rootHandler.Begin(); err != nil {
		err = fmt.Errorf("Error when beginning transaction for setup limit tables: %s", err)
		return
	}
	defer func() {
		if err != nil {
			tx.Rollback()
			err = fmt.Errorf("Error while matching setup limit tables: \n%s", err)
			return
		}
		err = tx.Commit()
	}()
	if _, err = tx.Exec("CREATE SCHEMA IF NOT EXISTS " + lo.orderSchema + ";"); err != nil {
		err = fmt.Errorf("Error creating schema for setup limit order tables: %s", err)
		return
	}
	if _, err = tx.Exec("USE " + lo.orderSchema + ";"); err != nil {
		err = fmt.Errorf("Could not use %s schema: %s", lo.orderSchema, err)
		return
	}
	createTableQuery := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s (%s);", lo.pair.String(), limitEngineSchema)
	if _, err = tx.Exec(createTableQuery); err != nil {
		err = fmt.Errorf("Error creating limit orderbook table: %s", err)
		return
	}
	return
}
func (lo *SQLLimitOrderbook) UpdateBookExec(orderExec *match.OrderExecution) (err error) {
	var tx *sql.Tx
	if tx, err = lo.DBHandler.Begin(); err != nil {
		err = fmt.Errorf("Error when beginning transaction for UpdateBookExec: %s", err)
		return
	}
	defer func() {
		if err != nil {
			tx.Rollback()
			err = fmt.Errorf("Error while running UpdateBookExec: \n%s", err)
			return
		}
		err = tx.Commit()
	}()
	if _, err = tx.Exec("USE " + lo.orderSchema + ";"); err != nil {
		err = fmt.Errorf("Error using limit schema to UpdateBookExec: %s", err)
		return
	}
	if orderExec.Filled {
		deleteOrderQuery := fmt.Sprintf("DELETE FROM %s WHERE orderID='%x';", lo.pair.String(), orderExec.OrderID)
		if _, err = tx.Exec(deleteOrderQuery); err != nil {
			err = fmt.Errorf("Error deleting order within tx for UpdateBookExec: %s", err)
			return
		}
	} else {
		updateOrderQuery := fmt.Sprintf("UPDATE %s SET amountHave=%d, amountWant=%d WHERE orderID='%x';", lo.pair.String(), orderExec.NewAmountHave, orderExec.NewAmountWant, orderExec.OrderID)
		if _, err = tx.Exec(updateOrderQuery); err != nil {
			err = fmt.Errorf("Error updating order within tx for UpdateBookExec: %s", err)
			return
		}
	}
	return
}