github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/vent/sqldb/sqldb.go (about)

     1  package sqldb
     2  
     3  import (
     4  	"database/sql"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"math"
     9  	"strconv"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/hyperledger/burrow/logging"
    14  	"github.com/hyperledger/burrow/vent/sqldb/adapters"
    15  	"github.com/hyperledger/burrow/vent/types"
    16  	"github.com/jmoiron/sqlx"
    17  )
    18  
    19  const maxUint64 uint64 = (1 << 64) - 1
    20  
    21  var tables = types.DefaultSQLTableNames
    22  var columns = types.DefaultSQLColumnNames
    23  
    24  // SQLDB implements the access to a sql database
    25  type SQLDB struct {
    26  	DB *sqlx.DB
    27  	adapters.DBAdapter
    28  	Schema  string
    29  	Queries Queries
    30  	types.SQLNames
    31  	Log *logging.Logger
    32  }
    33  
    34  // NewSQLDB delegates work to a specific database adapter implementation,
    35  // opens database connection and create log tables
    36  func NewSQLDB(connection types.SQLConnection) (*SQLDB, error) {
    37  	db := &SQLDB{
    38  		Schema:   connection.DBSchema,
    39  		SQLNames: types.DefaultSQLNames,
    40  		Log:      connection.Log,
    41  	}
    42  
    43  	switch connection.DBAdapter {
    44  	case types.PostgresDB:
    45  		db.DBAdapter = adapters.NewPostgresAdapter(safe(connection.DBSchema), db.SQLNames, connection.Log)
    46  
    47  	case types.SQLiteDB:
    48  		db.DBAdapter = adapters.NewSQLiteAdapter(db.SQLNames, connection.Log)
    49  	default:
    50  		return nil, errors.New("invalid database adapter")
    51  	}
    52  
    53  	var err error
    54  	db.DB, err = db.DBAdapter.Open(connection.DBURL)
    55  	if err != nil {
    56  		db.Log.InfoMsg("Error opening database connection", "err", err)
    57  		return nil, err
    58  	}
    59  
    60  	if err = db.Ping(); err != nil {
    61  		db.Log.InfoMsg("Error database not available", "err", err)
    62  		return nil, err
    63  	}
    64  
    65  	return db, nil
    66  }
    67  
    68  // Initialise the system and chain tables in case this is the first run - is idempotent though will drop tables
    69  // if ChainID has changed
    70  func (db *SQLDB) Init(chainID, burrowVersion string) error {
    71  	db.Log.InfoMsg("Initializing DB")
    72  
    73  	// Create dictionary and log tables
    74  	sysTables := db.systemTablesDefinition()
    75  
    76  	// IMPORTANT: DO NOT CHANGE TABLE CREATION ORDER (1)
    77  	if err := db.createTable(chainID, sysTables[db.Tables.Dictionary], true); err != nil {
    78  		if !db.DBAdapter.ErrorEquals(err, types.SQLErrorTypeDuplicatedTable) {
    79  			db.Log.InfoMsg("Error creating Dictionary table", "err", err)
    80  			return err
    81  		}
    82  	}
    83  
    84  	// IMPORTANT: DO NOT CHANGE TABLE CREATION ORDER (2)
    85  	if err := db.createTable(chainID, sysTables[db.Tables.Log], true); err != nil {
    86  		if !db.DBAdapter.ErrorEquals(err, types.SQLErrorTypeDuplicatedTable) {
    87  			db.Log.InfoMsg("Error creating Log table", "err", err)
    88  			return err
    89  		}
    90  	}
    91  
    92  	// IMPORTANT: DO NOT CHANGE TABLE CREATION ORDER (3)
    93  	if err := db.createTable(chainID, sysTables[db.Tables.ChainInfo], true); err != nil {
    94  		if !db.DBAdapter.ErrorEquals(err, types.SQLErrorTypeDuplicatedTable) {
    95  			db.Log.InfoMsg("Error creating Chain Info table", "err", err)
    96  			return err
    97  		}
    98  	}
    99  
   100  	chainIDChanged, err := db.InitChain(chainID, burrowVersion)
   101  	if err != nil {
   102  		return fmt.Errorf("could not initialise chain in database: %v", err)
   103  	}
   104  
   105  	if chainIDChanged {
   106  		// If the chain has changed - drop existing data
   107  		err = db.CleanTables(chainID, burrowVersion)
   108  		if err != nil {
   109  			return fmt.Errorf("could not clean tables after ChainID change: %v", err)
   110  		}
   111  	}
   112  
   113  	db.Queries, err = db.prepareQueries()
   114  	if err != nil {
   115  		db.Log.InfoMsg("Could not prepare queries", "err", err)
   116  		return err
   117  	}
   118  
   119  	return nil
   120  }
   121  
   122  func (db *SQLDB) prepareQueries() (Queries, error) {
   123  	err := new(error)
   124  	//language=SQL
   125  	return Queries{
   126  		LastBlockHeight: db.prepare(err, fmt.Sprintf("SELECT %s FROM %s WHERE %s=:chainid",
   127  			db.Columns.Height, // select
   128  			db.DBAdapter.SchemaName(db.Tables.ChainInfo), // from
   129  			db.DBAdapter.SecureName(db.Columns.ChainID),  // where
   130  		)),
   131  		SetBlockHeight: fmt.Sprintf("UPDATE %s SET %s = :height WHERE %s = :chainid",
   132  			db.DBAdapter.SchemaName(db.Tables.ChainInfo), // update
   133  			db.Columns.Height,  // set
   134  			db.Columns.ChainID, // where
   135  		),
   136  	}, *err
   137  }
   138  
   139  func (db *SQLDB) InitChain(chainID, burrowVersion string) (chainIDChanged bool, _ error) {
   140  	cleanQueries := db.DBAdapter.CleanDBQueries()
   141  
   142  	var savedChainID, savedBurrowVersion, query string
   143  	savedRows := 0
   144  
   145  	// Read chainID
   146  	query = cleanQueries.SelectChainIDQry
   147  	if err := db.DB.QueryRow(query).Scan(&savedRows, &savedChainID, &savedBurrowVersion); err != nil {
   148  		db.Log.InfoMsg("Error selecting CHAIN ID", "err", err, "query", query)
   149  		return false, err
   150  	}
   151  
   152  	if savedRows == 1 {
   153  		return savedChainID != chainID, nil
   154  	}
   155  
   156  	if savedRows > 1 {
   157  		return false, fmt.Errorf("error multiple chains defined returned")
   158  	}
   159  
   160  	// First database access
   161  	// Save new values and exit
   162  	query = cleanQueries.InsertChainIDQry
   163  	_, err := db.DB.Exec(query, chainID, burrowVersion, 0)
   164  
   165  	if err != nil {
   166  		db.Log.InfoMsg("Error inserting CHAIN ID", "err", err, "query", query)
   167  	}
   168  	return false, err
   169  
   170  }
   171  
   172  // CleanTables drop tables if stored chainID is different from the given one & store new chainID
   173  // if the chainID is the same, do nothing
   174  func (db *SQLDB) CleanTables(chainID, burrowVersion string) error {
   175  	var tx *sql.Tx
   176  	var err error
   177  	var tableName string
   178  	tables := make([]string, 0)
   179  	cleanQueries := db.DBAdapter.CleanDBQueries()
   180  
   181  	// Begin tx
   182  	if tx, err = db.DB.Begin(); err != nil {
   183  		db.Log.InfoMsg("Error beginning transaction", "err", err)
   184  		return err
   185  	}
   186  	defer tx.Rollback()
   187  
   188  	// Delete chainID
   189  	query := cleanQueries.DeleteChainIDQry
   190  	if _, err = tx.Exec(query); err != nil {
   191  		db.Log.InfoMsg("Error deleting CHAIN ID", "err", err, "query", query)
   192  		return err
   193  	}
   194  
   195  	// Insert chainID
   196  	query = cleanQueries.InsertChainIDQry
   197  	if _, err := tx.Exec(query, chainID, burrowVersion, 0); err != nil {
   198  		db.Log.InfoMsg("Error inserting CHAIN ID", "err", err, "query", query)
   199  		return err
   200  	}
   201  
   202  	// Load Tables
   203  	query = cleanQueries.SelectDictionaryQry
   204  	rows, err := tx.Query(query)
   205  	if err != nil {
   206  		db.Log.InfoMsg("error querying dictionary", "err", err, "query", query)
   207  		return err
   208  	}
   209  	defer rows.Close()
   210  
   211  	for rows.Next() {
   212  		if err = rows.Scan(&tableName); err != nil {
   213  			db.Log.InfoMsg("error scanning table structure", "err", err)
   214  			return err
   215  		}
   216  
   217  		if err = rows.Err(); err != nil {
   218  			db.Log.InfoMsg("error scanning table structure", "err", err)
   219  			return err
   220  		}
   221  		tables = append(tables, tableName)
   222  	}
   223  
   224  	// Delete Dictionary
   225  	query = cleanQueries.DeleteDictionaryQry
   226  	if _, err = tx.Exec(query); err != nil {
   227  		db.Log.InfoMsg("Error deleting dictionary", "err", err, "query", query)
   228  		return err
   229  	}
   230  
   231  	// Delete Log
   232  	query = cleanQueries.DeleteLogQry
   233  	if _, err = tx.Exec(query); err != nil {
   234  		db.Log.InfoMsg("Error deleting log", "err", err, "query", query)
   235  		return err
   236  	}
   237  	// Drop database tables
   238  	for _, tableName = range tables {
   239  		query = db.DBAdapter.DropTableQuery(tableName)
   240  		if _, err = tx.Exec(query); err != nil {
   241  			// if error == table does not exists, continue
   242  			if !db.DBAdapter.ErrorEquals(err, types.SQLErrorTypeUndefinedTable) {
   243  				db.Log.InfoMsg("error dropping tables", "err", err, "value", tableName, "query", query)
   244  				return err
   245  			}
   246  		}
   247  	}
   248  
   249  	// Commit
   250  	if err = tx.Commit(); err != nil {
   251  		db.Log.InfoMsg("Error commiting transaction", "err", err)
   252  		return err
   253  	}
   254  
   255  	return nil
   256  }
   257  
   258  // Close database connection
   259  func (db *SQLDB) Close() {
   260  	if err := db.DB.Close(); err != nil {
   261  		db.Log.InfoMsg("Error closing database", "err", err)
   262  	}
   263  }
   264  
   265  // Ping database
   266  func (db *SQLDB) Ping() error {
   267  	if err := db.DB.Ping(); err != nil {
   268  		db.Log.InfoMsg("Error database not available", "err", err)
   269  		return err
   270  	}
   271  
   272  	return nil
   273  }
   274  
   275  // SynchronizeDB synchronize db tables structures from given tables specifications
   276  func (db *SQLDB) SynchronizeDB(chainID string, eventTables types.EventTables) error {
   277  	db.Log.InfoMsg("Synchronizing DB")
   278  
   279  	for _, table := range eventTables {
   280  		found, err := db.findTable(table.Name)
   281  		if err != nil {
   282  			return err
   283  		}
   284  
   285  		if found {
   286  			err = db.alterTable(chainID, table)
   287  		} else {
   288  			err = db.createTable(chainID, table, false)
   289  		}
   290  		if err != nil {
   291  			return err
   292  		}
   293  	}
   294  
   295  	return nil
   296  }
   297  
   298  // SetBlock inserts or updates multiple rows and stores log info in SQL tables
   299  func (db *SQLDB) SetBlock(chainID string, eventTables types.EventTables, eventData types.EventData) error {
   300  	db.Log.InfoMsg("Synchronize Block", "action", "SYNC")
   301  
   302  	// Begin tx
   303  	tx, err := db.DB.Beginx()
   304  	if err != nil {
   305  		db.Log.InfoMsg("Error beginning transaction", "err", err)
   306  		return err
   307  	}
   308  	defer tx.Rollback()
   309  
   310  	// Prepare log statement
   311  	logQuery := db.DBAdapter.InsertLogQuery()
   312  	logStmt, err := tx.Prepare(logQuery)
   313  	if err != nil {
   314  		db.Log.InfoMsg("Error preparing log stmt", "err", err)
   315  		return err
   316  	}
   317  	defer logStmt.Close()
   318  
   319  	var tableName string
   320  loop:
   321  	// for each table in the block
   322  	for _, table := range eventTables {
   323  		tableName = safe(table.Name)
   324  		dataRows := eventData.Tables[table.Name]
   325  		// for Each Row
   326  		for _, row := range dataRows {
   327  			var queryVal types.UpsertDeleteQuery
   328  			var txHash interface{}
   329  			var errQuery error
   330  
   331  			switch row.Action {
   332  			case types.ActionUpsert:
   333  				//Prepare Upsert
   334  				if queryVal, txHash, errQuery = db.DBAdapter.UpsertQuery(table, row); errQuery != nil {
   335  					db.Log.InfoMsg("Error building upsert query", "err", errQuery, "value", fmt.Sprintf("%v %v", table, row))
   336  					break loop // exits from all loops -> continue in close log stmt
   337  				}
   338  
   339  			case types.ActionDelete:
   340  				//Prepare Delete
   341  				if queryVal, errQuery = db.DBAdapter.DeleteQuery(table, row); errQuery != nil {
   342  					db.Log.InfoMsg("Error building delete query", "err", errQuery, "value", fmt.Sprintf("%v %v", table, row))
   343  					break loop // exits from all loops -> continue in close log stmt
   344  				}
   345  			default:
   346  				//Invalid Action
   347  				db.Log.InfoMsg("invalid action", "value", row.Action)
   348  				err = fmt.Errorf("invalid row action %s", row.Action)
   349  				break loop // exits from all loops -> continue in close log stmt
   350  			}
   351  
   352  			sqlQuery := queryVal.Query
   353  
   354  			// Perform row action
   355  			db.Log.InfoMsg("msg", "action", row.Action, "query", sqlQuery, "value", queryVal.Values)
   356  			_, err = tx.Exec(sqlQuery, queryVal.Pointers...)
   357  			if err != nil {
   358  				err = fmt.Errorf("could not execute query '%s': %w", sqlQuery, err)
   359  				db.Log.InfoMsg(fmt.Sprintf("error performing %s on row", row.Action), "err", err, "value", queryVal.Values)
   360  				break loop // exits from all loops -> continue in close log stmt
   361  			}
   362  
   363  			// Marshal the rowData map
   364  			rowData, err := getJSON(row.RowData)
   365  			if err != nil {
   366  				db.Log.InfoMsg("error marshaling rowData", "err", err, "value", fmt.Sprintf("%v", row.RowData))
   367  				break loop // exits from all loops -> continue in close log stmt
   368  			}
   369  
   370  			// Marshal sql values
   371  			sqlValues, err := getJSONFromValues(queryVal.Pointers)
   372  			if err != nil {
   373  				db.Log.InfoMsg("error marshaling rowdata", "err", err, "value", fmt.Sprintf("%v", row.RowData))
   374  				break loop // exits from all loops -> continue in close log stmt
   375  			}
   376  
   377  			eventName, _ := row.RowData[db.Columns.EventName].(string)
   378  			// Insert in log
   379  			db.Log.InfoMsg("INSERT LOG",
   380  				"log_query", logQuery,
   381  				"chain_id", chainID,
   382  				"table_name", tableName,
   383  				"event_name", eventName,
   384  				"event_filter", row.EventClass.GetFilter(),
   385  				"block_height", eventData.BlockHeight,
   386  				"tx_hash", txHash,
   387  				"row_action", row.Action,
   388  				"row_data", string(rowData),
   389  				"sql_query", sqlQuery,
   390  				"sql_values", string(sqlValues),
   391  			)
   392  
   393  			if _, err = logStmt.Exec(chainID, tableName, eventName, row.EventClass.GetFilter(), eventData.BlockHeight,
   394  				txHash, row.Action, rowData, sqlQuery, sqlValues); err != nil {
   395  				db.Log.InfoMsg("Error inserting into log", "err", err)
   396  				break loop // exits from all loops -> continue in close log stmt
   397  			}
   398  		}
   399  	}
   400  
   401  	// Close log statement
   402  	if err == nil {
   403  		if err = logStmt.Close(); err != nil {
   404  			db.Log.InfoMsg("Error closing log stmt", "err", err)
   405  		}
   406  	}
   407  
   408  	// Error handling
   409  	if err != nil {
   410  		// Rollback error
   411  		if errRb := tx.Rollback(); errRb != nil {
   412  			db.Log.InfoMsg("Error on rollback", "err", errRb)
   413  			return errRb
   414  		}
   415  
   416  		//Is a SQL error
   417  		if db.DBAdapter.ErrorEquals(err, types.SQLErrorTypeGeneric) {
   418  
   419  			// Table does not exists
   420  			if db.DBAdapter.ErrorEquals(err, types.SQLErrorTypeUndefinedTable) {
   421  				db.Log.InfoMsg("Table not found", "value", tableName)
   422  				//Synchronize DB
   423  				if err = db.SynchronizeDB(chainID, eventTables); err != nil {
   424  					return err
   425  				}
   426  				//Retry
   427  				return db.SetBlock(chainID, eventTables, eventData)
   428  			}
   429  
   430  			// Columns do not match
   431  			if db.DBAdapter.ErrorEquals(err, types.SQLErrorTypeUndefinedColumn) {
   432  				db.Log.InfoMsg("Column not found", "value", tableName)
   433  				//Synchronize DB
   434  				if err = db.SynchronizeDB(chainID, eventTables); err != nil {
   435  					return err
   436  				}
   437  				//Retry
   438  				return db.SetBlock(chainID, eventTables, eventData)
   439  			}
   440  			return err
   441  		}
   442  		return err
   443  	}
   444  
   445  	db.Log.InfoMsg("COMMIT", "action", "COMMIT")
   446  
   447  	err = db.SetBlockHeight(tx, chainID, eventData.BlockHeight)
   448  	if err != nil {
   449  		db.Log.InfoMsg("Could not commit block height", "err", err)
   450  		return err
   451  	}
   452  
   453  	err = tx.Commit()
   454  	if err != nil {
   455  		db.Log.InfoMsg("Error on commit", "err", err)
   456  		return err
   457  	}
   458  
   459  	return nil
   460  }
   461  
   462  // GetBlock returns all tables structures and row data for given block
   463  func (db *SQLDB) GetBlock(chainID string, height uint64) (types.EventData, error) {
   464  	var data types.EventData
   465  	data.BlockHeight = height
   466  	data.Tables = make(map[string]types.EventDataTable)
   467  
   468  	// get all table structures involved in the block
   469  	tables, err := db.getBlockTables(chainID, height)
   470  	if err != nil {
   471  		return data, err
   472  	}
   473  
   474  	query := ""
   475  
   476  	// for each table
   477  	for _, table := range tables {
   478  		// get query for table
   479  		query, err = db.getSelectQuery(table, height)
   480  		if err != nil {
   481  			db.Log.InfoMsg("Error building table query", "err", err)
   482  			return data, err
   483  		}
   484  		db.Log.InfoMsg("Query table data", "query", query)
   485  		rows, err := db.DB.Query(query)
   486  		if err != nil {
   487  			db.Log.InfoMsg("Error querying table data", "err", err)
   488  			return data, err
   489  		}
   490  		defer rows.Close()
   491  
   492  		cols, err := rows.Columns()
   493  		if err != nil {
   494  			db.Log.InfoMsg("Error getting row columns", "err", err)
   495  			return data, err
   496  		}
   497  
   498  		// builds pointers
   499  		length := len(cols)
   500  		pointers := make([]interface{}, length)
   501  		containers := make([]sql.NullString, length)
   502  
   503  		for i := range pointers {
   504  			pointers[i] = &containers[i]
   505  		}
   506  
   507  		// for each row in table
   508  		var dataRows []types.EventDataRow
   509  
   510  		for rows.Next() {
   511  
   512  			row := make(map[string]interface{})
   513  
   514  			if err = rows.Scan(pointers...); err != nil {
   515  				db.Log.InfoMsg("Error scanning data", "err", err)
   516  				return data, err
   517  			}
   518  			db.Log.InfoMsg("Query resultset", "value", fmt.Sprintf("%+v", containers))
   519  
   520  			// for each column in row
   521  			for i, col := range cols {
   522  				// add value if not null
   523  				if containers[i].Valid {
   524  					row[col] = containers[i].String
   525  				}
   526  			}
   527  			dataRows = append(dataRows, types.EventDataRow{Action: types.ActionRead, RowData: row})
   528  		}
   529  
   530  		if err = rows.Err(); err != nil {
   531  			db.Log.InfoMsg("Error during rows iteration", "err", err)
   532  			return data, err
   533  		}
   534  		data.Tables[table.Name] = dataRows
   535  	}
   536  	return data, nil
   537  }
   538  
   539  func (db *SQLDB) LastBlockHeight(chainID string) (uint64, error) {
   540  	const errHeader = "LastBlockHeight()"
   541  	type arg struct {
   542  		ChainID string
   543  	}
   544  	height := new(uint64)
   545  	err := db.Queries.LastBlockHeight.Get(height, arg{ChainID: chainID})
   546  	if err != nil {
   547  		return 0, fmt.Errorf("%s: %v", errHeader, err)
   548  	}
   549  	return *height, nil
   550  }
   551  
   552  func (db *SQLDB) SetBlockHeight(tx sqlx.Ext, chainID string, height uint64) error {
   553  	const errHeader = "SetBlockHeight()"
   554  	type arg struct {
   555  		ChainID string
   556  		Height  uint64
   557  	}
   558  	result, err := sqlx.NamedExec(tx, db.Queries.SetBlockHeight, arg{ChainID: chainID, Height: height})
   559  	if err != nil {
   560  		return err
   561  	}
   562  	rows, err := result.RowsAffected()
   563  	if err != nil {
   564  		return fmt.Errorf("%s: could not get rows affected: %v", errHeader, err)
   565  	}
   566  	if rows != 1 {
   567  		return fmt.Errorf("%s: should update the height in  exactly one row for ChainID '%s' but %d rows were affected",
   568  			errHeader, chainID, rows)
   569  	}
   570  	return nil
   571  }
   572  
   573  // RestoreDB restores the DB to a given moment in time. If prefix is provided restores the table state to a new set of
   574  // tables as <prefix>_<table name>. Drops destination tables before recreating them. If zero time passed restores
   575  // all values
   576  func (db *SQLDB) RestoreDB(restoreTime time.Time, prefix string) error {
   577  	const year = time.Hour * 24 * 365
   578  	const yymmddhhmmss = "2006-01-02 15:04:05"
   579  
   580  	var pointers []interface{}
   581  
   582  	// Get Restore DB query
   583  	query := db.DBAdapter.RestoreDBQuery()
   584  	if restoreTime.IsZero() {
   585  		// We'll assume a sufficiently small clock skew...
   586  		restoreTime = time.Now().Add(100 * year)
   587  	}
   588  	strTime := restoreTime.Format(yymmddhhmmss)
   589  
   590  	db.Log.InfoMsg("RESTORING DB..................................")
   591  
   592  	db.Log.InfoMsg("open log", "query", query)
   593  	// Postgres does not work is run within same tx as updates, see: https://github.com/lib/pq/issues/81
   594  	rows, err := db.DB.Query(query, strTime)
   595  	if err != nil {
   596  		db.Log.InfoMsg("error querying log", "err", err)
   597  		return err
   598  	}
   599  	defer rows.Close()
   600  
   601  	tx, err := db.DB.Begin()
   602  	if err != nil {
   603  		db.Log.InfoMsg("could not open transaction for restore", "err", err)
   604  		return err
   605  	}
   606  	defer tx.Rollback()
   607  
   608  	// For each row returned
   609  	for rows.Next() {
   610  		var id int64
   611  		var tableName, sqlSmt, sqlValues string
   612  		var action types.DBAction
   613  
   614  		err = rows.Err()
   615  		if err != nil {
   616  			db.Log.InfoMsg("error scanning table structure", "err", err)
   617  			return err
   618  		}
   619  
   620  		err = rows.Scan(&id, &tableName, &action, &sqlSmt, &sqlValues)
   621  		if err != nil {
   622  			db.Log.InfoMsg("error scanning table structure", "err", err)
   623  			return err
   624  		}
   625  
   626  		restoreTable := tableName
   627  		if prefix != "" {
   628  			restoreTable = fmt.Sprintf("%s_%s", prefix, tableName)
   629  		}
   630  
   631  		switch action {
   632  		case types.ActionUpsert, types.ActionDelete:
   633  			// get row values
   634  			if pointers, err = getValuesFromJSON(sqlValues); err != nil {
   635  				db.Log.InfoMsg("error unmarshaling json", "err", err, "value", sqlValues)
   636  				return err
   637  			}
   638  
   639  			// Prepare Upsert/delete
   640  			query = sqlSmt
   641  			if prefix != "" {
   642  				// TODO: [Silas] ugh this is a little fragile
   643  				query = strings.Replace(sqlSmt, tableName, restoreTable, -1)
   644  			}
   645  
   646  			db.Log.InfoMsg("SQL COMMAND", "sql", query, "log_id", id)
   647  			if _, err = tx.Exec(query, pointers...); err != nil {
   648  				db.Log.InfoMsg("Error executing upsert/delete ", "err", err, "value", sqlSmt, "data", sqlValues)
   649  				return err
   650  			}
   651  
   652  		case types.ActionAlterTable, types.ActionCreateTable:
   653  			if action == types.ActionCreateTable {
   654  				dropQuery := db.DBAdapter.DropTableQuery(restoreTable)
   655  				_, err := tx.Exec(dropQuery)
   656  				if err != nil && !db.DBAdapter.ErrorEquals(err, types.SQLErrorTypeUndefinedTable) {
   657  					return fmt.Errorf("could not drop target restore table %s: %v", restoreTable, err)
   658  				}
   659  			}
   660  			// Prepare Alter/Create Table
   661  			query = strings.Replace(sqlSmt, tableName, restoreTable, -1)
   662  
   663  			db.Log.InfoMsg("SQL COMMAND", "sql", query)
   664  			_, err = tx.Exec(query)
   665  			if err != nil {
   666  				db.Log.InfoMsg("Error executing alter/create table command ", "err", err, "value", sqlSmt)
   667  				return err
   668  			}
   669  		default:
   670  			// Invalid Action
   671  			db.Log.InfoMsg("invalid action", "value", action)
   672  			return fmt.Errorf("invalid row action %s", action)
   673  		}
   674  	}
   675  	err = tx.Commit()
   676  	if err != nil {
   677  		db.Log.InfoMsg("could not commit restore tx", "err", err)
   678  		return err
   679  	}
   680  	return nil
   681  }
   682  
   683  func (db *SQLDB) prepare(perr *error, query string) *sqlx.NamedStmt {
   684  	stmt, err := db.DB.PrepareNamed(query)
   685  	if err != nil && *perr == nil {
   686  		*perr = fmt.Errorf("could not prepare query '%s': %v", query, err)
   687  	}
   688  	return stmt
   689  }
   690  
   691  // findTable checks if a table exists in the default schema
   692  func (db *SQLDB) findTable(tableName string) (bool, error) {
   693  
   694  	found := 0
   695  	safeTable := safe(tableName)
   696  	query := db.DBAdapter.FindTableQuery()
   697  
   698  	db.Log.InfoMsg("FIND TABLE", "query", query, "value", safeTable)
   699  	if err := db.DB.QueryRow(query, tableName).Scan(&found); err != nil {
   700  		db.Log.InfoMsg("Error finding table", "err", err)
   701  		return false, err
   702  	}
   703  
   704  	if found == 0 {
   705  		db.Log.InfoMsg("Table not found", "value", safeTable)
   706  		return false, nil
   707  	}
   708  
   709  	return true, nil
   710  }
   711  
   712  // getTableDef returns the structure of a given SQL table
   713  func (db *SQLDB) getTableDef(tableName string) (*types.SQLTable, error) {
   714  	table := &types.SQLTable{
   715  		Name: safe(tableName),
   716  	}
   717  	found, err := db.findTable(table.Name)
   718  	if err != nil {
   719  		return nil, err
   720  	}
   721  
   722  	if !found {
   723  		db.Log.InfoMsg("Error table not found", "value", table.Name)
   724  		return nil, errors.New("Error table not found " + table.Name)
   725  	}
   726  
   727  	query := db.DBAdapter.TableDefinitionQuery()
   728  
   729  	db.Log.InfoMsg("QUERY STRUCTURE", "query", query, "value", table.Name)
   730  	rows, err := db.DB.Query(query, safe(tableName))
   731  	if err != nil {
   732  		db.Log.InfoMsg("Error querying table structure", "err", err)
   733  		return nil, err
   734  	}
   735  	defer rows.Close()
   736  
   737  	var columns []*types.SQLTableColumn
   738  
   739  	for rows.Next() {
   740  		var columnName string
   741  		var columnSQLType types.SQLColumnType
   742  		var columnIsPK int
   743  		var columnLength int
   744  
   745  		if err = rows.Scan(&columnName, &columnSQLType, &columnLength, &columnIsPK); err != nil {
   746  			db.Log.InfoMsg("Error scanning table structure", "err", err)
   747  			return nil, err
   748  		}
   749  
   750  		if _, err = db.DBAdapter.TypeMapping(columnSQLType); err != nil {
   751  			return nil, err
   752  		}
   753  
   754  		columns = append(columns, &types.SQLTableColumn{
   755  			Name:    columnName,
   756  			Type:    columnSQLType,
   757  			Length:  columnLength,
   758  			Primary: columnIsPK == 1,
   759  		})
   760  	}
   761  
   762  	if err = rows.Err(); err != nil {
   763  		db.Log.InfoMsg("Error during rows iteration", "err", err)
   764  		return nil, err
   765  	}
   766  
   767  	table.Columns = columns
   768  	return table, nil
   769  }
   770  
   771  // alterTable alters the structure of a SQL table & add info to the dictionary
   772  func (db *SQLDB) alterTable(chainID string, table *types.SQLTable) error {
   773  	db.Log.InfoMsg("Altering table", "value", table.Name)
   774  
   775  	// prepare log query
   776  	logQuery := db.DBAdapter.InsertLogQuery()
   777  
   778  	// current table structure
   779  	safeTable := safe(table.Name)
   780  	currentTable, err := db.getTableDef(safeTable)
   781  	if err != nil {
   782  		return err
   783  	}
   784  
   785  	sqlValues, _ := getJSON(nil)
   786  
   787  	// for each column in the new table structure
   788  	for order, newColumn := range table.Columns {
   789  		found := false
   790  
   791  		// check if exists in the current table structure
   792  		for _, currentColumn := range currentTable.Columns {
   793  			// if column exists
   794  			if currentColumn.Name == newColumn.Name {
   795  				found = true
   796  				break
   797  			}
   798  		}
   799  
   800  		if !found {
   801  			safeCol := safe(newColumn.Name)
   802  			query, dictionary := db.DBAdapter.AlterColumnQuery(safeTable, safeCol, newColumn.Type, newColumn.Length, order)
   803  
   804  			//alter column
   805  			db.Log.InfoMsg("ALTER TABLE", "query", safe(query))
   806  			_, err = db.DB.Exec(safe(query))
   807  
   808  			if err != nil {
   809  				if db.DBAdapter.ErrorEquals(err, types.SQLErrorTypeDuplicatedColumn) {
   810  					db.Log.InfoMsg("Duplicate column", "value", safeCol)
   811  				} else {
   812  					db.Log.InfoMsg("Error altering table", "err", err)
   813  					return err
   814  				}
   815  			} else {
   816  				//store dictionary
   817  				db.Log.InfoMsg("STORE DICTIONARY", "query", dictionary)
   818  				_, err = db.DB.Exec(dictionary)
   819  				if err != nil {
   820  					db.Log.InfoMsg("Error storing  dictionary", "err", err)
   821  					return err
   822  				}
   823  
   824  				// Marshal the table into a JSON string.
   825  				var jsonData []byte
   826  				jsonData, err = getJSON(newColumn)
   827  				if err != nil {
   828  					db.Log.InfoMsg("error marshaling column", "err", err, "value", fmt.Sprintf("%v", newColumn))
   829  					return err
   830  				}
   831  				//insert log
   832  				_, err = db.DB.Exec(logQuery, chainID, table.Name, "", "", nil, nil, types.ActionAlterTable, jsonData, query, sqlValues)
   833  				if err != nil {
   834  					db.Log.InfoMsg("Error inserting log", "err", err)
   835  					return err
   836  				}
   837  			}
   838  		}
   839  	}
   840  
   841  	// Ensure triggers are defined
   842  	err = db.createTableTriggers(table)
   843  	if err != nil {
   844  		db.Log.InfoMsg("error creating notification triggers", "err", err, "value", fmt.Sprintf("%v", table))
   845  		return fmt.Errorf("could not create table notification triggers: %v", err)
   846  	}
   847  	return nil
   848  }
   849  
   850  // createTable creates a new table
   851  func (db *SQLDB) createTable(chainID string, table *types.SQLTable, isInitialise bool) error {
   852  	db.Log.InfoMsg("Creating Table", "value", table.Name)
   853  
   854  	// prepare log query
   855  	logQuery := db.DBAdapter.InsertLogQuery()
   856  
   857  	//get create table query
   858  	safeTable := safe(table.Name)
   859  	query, dictionary := db.DBAdapter.CreateTableQuery(safeTable, table.Columns)
   860  	if query == "" {
   861  		db.Log.InfoMsg("empty CREATE TABLE query")
   862  		return errors.New("empty CREATE TABLE query")
   863  	}
   864  
   865  	// create table
   866  	db.Log.InfoMsg("CREATE TABLE", "query", query)
   867  	_, err := db.DB.Exec(query)
   868  	if err != nil {
   869  		return err
   870  	}
   871  
   872  	//store dictionary
   873  	db.Log.InfoMsg("STORE DICTIONARY", "query", dictionary)
   874  	_, err = db.DB.Exec(dictionary)
   875  	if err != nil {
   876  		db.Log.InfoMsg("Error storing  dictionary", "err", err)
   877  		return err
   878  	}
   879  
   880  	err = db.createTableTriggers(table)
   881  	if err != nil {
   882  		db.Log.InfoMsg("error creating notification triggers", "err", err, "value", fmt.Sprintf("%v", table))
   883  		return fmt.Errorf("could not create table notification triggers: %v", err)
   884  	}
   885  
   886  	//insert log (if action is not database initialization)
   887  	if !isInitialise {
   888  		// Marshal the table into a JSON string.
   889  		var jsonData []byte
   890  		jsonData, err = getJSON(table)
   891  		if err != nil {
   892  			db.Log.InfoMsg("error marshaling table", "err", err, "value", fmt.Sprintf("%v", table))
   893  			return err
   894  		}
   895  		sqlValues, _ := getJSON(nil)
   896  
   897  		//insert log
   898  		_, err = db.DB.Exec(logQuery, chainID, table.Name, "", "", nil, nil, types.ActionCreateTable, jsonData, query, sqlValues)
   899  		if err != nil {
   900  			db.Log.InfoMsg("Error inserting log", "err", err)
   901  			return err
   902  		}
   903  	}
   904  	return nil
   905  }
   906  
   907  // Creates (or updates) table notification triggers and functions
   908  func (db *SQLDB) createTableTriggers(table *types.SQLTable) error {
   909  	// If the adapter supports notification triggers
   910  	dbNotify, ok := db.DBAdapter.(adapters.DBNotifyTriggerAdapter)
   911  	if ok {
   912  		for channel, columns := range table.NotifyChannels {
   913  			function := fmt.Sprintf("%s_%s_notify_function", table.Name, channel)
   914  
   915  			query := dbNotify.CreateNotifyFunctionQuery(function, channel, columns...)
   916  			db.Log.InfoMsg("CREATE NOTIFICATION FUNCTION", "query", query)
   917  			_, err := db.DB.Exec(query)
   918  			if err != nil {
   919  				return fmt.Errorf("could not create notification function: %v", err)
   920  			}
   921  
   922  			trigger := fmt.Sprintf("%s_%s_notify_trigger", table.Name, channel)
   923  			query = dbNotify.CreateTriggerQuery(trigger, table.Name, function)
   924  			db.Log.InfoMsg("CREATE NOTIFICATION TRIGGER", "query", query)
   925  			_, err = db.DB.Exec(query)
   926  			if err != nil {
   927  				return fmt.Errorf("could not create notification trigger: %v", err)
   928  			}
   929  		}
   930  	}
   931  	return nil
   932  }
   933  
   934  // getSelectQuery builds a select query for a specific SQL table and a given block
   935  func (db *SQLDB) getSelectQuery(table *types.SQLTable, height uint64) (string, error) {
   936  
   937  	fields := ""
   938  
   939  	for _, tableColumn := range table.Columns {
   940  		if fields != "" {
   941  			fields += ", "
   942  		}
   943  		fields += db.DBAdapter.SecureName(tableColumn.Name)
   944  	}
   945  
   946  	if fields == "" {
   947  		return "", errors.New("error table does not contain any fields")
   948  	}
   949  
   950  	query := db.DBAdapter.SelectRowQuery(table.Name, fields, strconv.FormatUint(height, 10))
   951  	return query, nil
   952  }
   953  
   954  // getBlockTables return all SQL tables that have been involved
   955  // in a given batch transaction for a specific block
   956  func (db *SQLDB) getBlockTables(chainid string, height uint64) (types.EventTables, error) {
   957  	tables := make(types.EventTables)
   958  
   959  	query := db.DBAdapter.SelectLogQuery()
   960  	db.Log.InfoMsg("QUERY LOG", "query", query, "height", height, "chainid", chainid)
   961  
   962  	rows, err := db.DB.Query(query, height, chainid)
   963  	if err != nil {
   964  		db.Log.InfoMsg("Error querying log", "err", err)
   965  		return tables, err
   966  	}
   967  
   968  	defer rows.Close()
   969  
   970  	for rows.Next() {
   971  		var eventName, tableName string
   972  		var table *types.SQLTable
   973  
   974  		err = rows.Scan(&tableName, &eventName)
   975  		if err != nil {
   976  			db.Log.InfoMsg("Error scanning table structure", "err", err)
   977  			return tables, err
   978  		}
   979  
   980  		err = rows.Err()
   981  		if err != nil {
   982  			db.Log.InfoMsg("Error scanning table structure", "err", err)
   983  			return tables, err
   984  		}
   985  
   986  		table, err = db.getTableDef(tableName)
   987  		if err != nil {
   988  			return tables, err
   989  		}
   990  
   991  		tables[tableName] = table
   992  	}
   993  
   994  	return tables, nil
   995  }
   996  
   997  // safe sanitizes a parameter
   998  func safe(parameter string) string {
   999  	replacer := strings.NewReplacer(";", "", ",", "")
  1000  	return replacer.Replace(parameter)
  1001  }
  1002  
  1003  //getJSON returns marshaled json from JSON single column
  1004  func getJSON(JSON interface{}) ([]byte, error) {
  1005  	if JSON != nil {
  1006  		return json.Marshal(JSON)
  1007  	}
  1008  	return json.Marshal("")
  1009  }
  1010  
  1011  //getJSONFromValues returns marshaled json from query values
  1012  func getJSONFromValues(values []interface{}) ([]byte, error) {
  1013  	if values != nil {
  1014  		return json.Marshal(values)
  1015  	}
  1016  	return json.Marshal("")
  1017  }
  1018  
  1019  //getValuesFromJSON returns query values from unmarshaled JSON column
  1020  func getValuesFromJSON(JSON string) ([]interface{}, error) {
  1021  	pointers := make([]interface{}, 0)
  1022  	bytes := []byte(JSON)
  1023  	err := json.Unmarshal(bytes, &pointers)
  1024  	if err != nil {
  1025  		return nil, err
  1026  	}
  1027  	for i, ptr := range pointers {
  1028  		switch v := ptr.(type) {
  1029  		// Normalise integral floats
  1030  		case float64:
  1031  			i64 := int64(v)
  1032  			if float64(i64) == v {
  1033  				pointers[i] = i64
  1034  			}
  1035  		}
  1036  	}
  1037  	return pointers, nil
  1038  }
  1039  
  1040  func digits(x uint64) int {
  1041  	if x == 0 {
  1042  		return 1
  1043  	}
  1044  	return int(math.Log10(float64(x))) + 1
  1045  }