github.com/Tri-stone/burrow@v0.25.0/vent/sqldb/sqldb.go (about)

     1  package sqldb
     2  
     3  import (
     4  	"database/sql"
     5  	"errors"
     6  	"fmt"
     7  	"strconv"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/hyperledger/burrow/vent/logger"
    12  	"github.com/hyperledger/burrow/vent/sqldb/adapters"
    13  	"github.com/hyperledger/burrow/vent/types"
    14  )
    15  
    16  // SQLDB implements the access to a sql database
    17  type SQLDB struct {
    18  	DB        *sql.DB
    19  	DBAdapter adapters.DBAdapter
    20  	Schema    string
    21  	Log       *logger.Logger
    22  }
    23  
    24  // NewSQLDB delegates work to a specific database adapter implementation,
    25  // opens database connection and create log tables
    26  func NewSQLDB(connection types.SQLConnection) (*SQLDB, error) {
    27  	db := &SQLDB{
    28  		Schema: connection.DBSchema,
    29  		Log:    connection.Log,
    30  	}
    31  
    32  	var url string
    33  
    34  	switch connection.DBAdapter {
    35  	case types.PostgresDB:
    36  		db.DBAdapter = adapters.NewPostgresAdapter(safe(connection.DBSchema), connection.Log)
    37  		url = connection.DBURL
    38  
    39  	case types.SQLiteDB:
    40  		db.DBAdapter = adapters.NewSQLiteAdapter(connection.Log)
    41  		// "?_journal_mode=WAL" parameter is necessary to prevent database locking
    42  		url = connection.DBURL + "?_journal_mode=WAL"
    43  
    44  	default:
    45  		return nil, errors.New("invalid database adapter")
    46  	}
    47  
    48  	var err error
    49  	db.DB, err = db.DBAdapter.Open(url)
    50  	if err != nil {
    51  		db.Log.Info("msg", "Error opening database connection", "err", err)
    52  		return nil, err
    53  	}
    54  
    55  	if err = db.Ping(); err != nil {
    56  		db.Log.Info("msg", "Error database not available", "err", err)
    57  		return nil, err
    58  	}
    59  
    60  	db.Log.Info("msg", "Initializing DB")
    61  
    62  	// Create dictionary and log tables
    63  	sysTables := db.getSysTablesDefinition()
    64  
    65  	// IMPORTANT: DO NOT CHANGE TABLE CREATION ORDER (1)
    66  	if err = db.createTable(sysTables[types.SQLDictionaryTableName], string(types.ActionInitialize)); err != nil {
    67  		if !db.DBAdapter.ErrorEquals(err, types.SQLErrorTypeDuplicatedTable) {
    68  			db.Log.Info("msg", "Error creating Dictionary table", "err", err)
    69  			return nil, err
    70  		}
    71  	}
    72  
    73  	// IMPORTANT: DO NOT CHANGE TABLE CREATION ORDER (2)
    74  	if err = db.createTable(sysTables[types.SQLLogTableName], string(types.ActionInitialize)); err != nil {
    75  		if !db.DBAdapter.ErrorEquals(err, types.SQLErrorTypeDuplicatedTable) {
    76  			db.Log.Info("msg", "Error creating Log table", "err", err)
    77  			return nil, err
    78  		}
    79  	}
    80  
    81  	// IMPORTANT: DO NOT CHANGE TABLE CREATION ORDER (3)
    82  	if err = db.createTable(sysTables[types.SQLChainInfoTableName], string(types.ActionInitialize)); err != nil {
    83  		if !db.DBAdapter.ErrorEquals(err, types.SQLErrorTypeDuplicatedTable) {
    84  			db.Log.Info("msg", "Error creating Chain Info table", "err", err)
    85  			return nil, err
    86  		}
    87  	}
    88  
    89  	if err = db.CleanTables(connection.ChainID, connection.BurrowVersion); err != nil {
    90  		db.Log.Info("msg", "Error cleaning tables", "err", err)
    91  		return nil, err
    92  	}
    93  	return db, nil
    94  }
    95  
    96  // CleanTables, drop tables if stored chainID is different from the given one & store new chainID
    97  // if the chainID is the same, do nothing
    98  func (db *SQLDB) CleanTables(chainID, burrowVersion string) error {
    99  
   100  	if chainID == "" {
   101  		return fmt.Errorf("error CHAIN ID cannot by empty")
   102  	}
   103  
   104  	cleanQueries := db.DBAdapter.CleanDBQueries()
   105  
   106  	var savedChainID, savedBurrowVersion, query string
   107  	savedRows := 0
   108  
   109  	// Read chainID
   110  	query = cleanQueries.SelectChainIDQry
   111  	if err := db.DB.QueryRow(query).Scan(&savedRows, &savedChainID, &savedBurrowVersion); err != nil {
   112  		db.Log.Info("msg", "Error selecting CHAIN ID", "err", err, "query", query)
   113  		return err
   114  	}
   115  
   116  	switch {
   117  	// Must be empty or one row
   118  	case savedRows != 0 && savedRows != 1:
   119  		return fmt.Errorf("error multiple CHAIN ID returned")
   120  
   121  	// First database access
   122  	case savedRows == 0:
   123  		// Save new values and exit
   124  		query = cleanQueries.InsertChainIDQry
   125  		if _, err := db.DB.Exec(query, chainID, burrowVersion); err != nil {
   126  			db.Log.Info("msg", "Error inserting CHAIN ID", "err", err, "query", query)
   127  			return err
   128  		}
   129  		return nil
   130  
   131  	// if data equals previous version exit
   132  	case savedChainID == chainID:
   133  		return nil
   134  
   135  	// clean database
   136  	default:
   137  		var tx *sql.Tx
   138  		var err error
   139  		var tableName string
   140  		tables := make([]string, 0)
   141  
   142  		// Begin tx
   143  		if tx, err = db.DB.Begin(); err != nil {
   144  			db.Log.Info("msg", "Error beginning transaction", "err", err)
   145  			return err
   146  		}
   147  		defer tx.Rollback()
   148  
   149  		// Delete chainID
   150  		query := cleanQueries.DeleteChainIDQry
   151  		if _, err = tx.Exec(query); err != nil {
   152  			db.Log.Info("msg", "Error deleting CHAIN ID", "err", err, "query", query)
   153  			return err
   154  		}
   155  
   156  		// Insert chainID
   157  		query = cleanQueries.InsertChainIDQry
   158  		if _, err := tx.Exec(query, chainID, burrowVersion); err != nil {
   159  			db.Log.Info("msg", "Error inserting CHAIN ID", "err", err, "query", query)
   160  			return err
   161  		}
   162  
   163  		// Load Tables
   164  		query = cleanQueries.SelectDictionaryQry
   165  		rows, err := tx.Query(query)
   166  		if err != nil {
   167  			db.Log.Info("msg", "error querying dictionary", "err", err, "query", query)
   168  			return err
   169  		}
   170  		defer rows.Close()
   171  
   172  		for rows.Next() {
   173  
   174  			if err = rows.Scan(&tableName); err != nil {
   175  				db.Log.Info("msg", "error scanning table structure", "err", err)
   176  				return err
   177  			}
   178  
   179  			if err = rows.Err(); err != nil {
   180  				db.Log.Info("msg", "error scanning table structure", "err", err)
   181  				return err
   182  			}
   183  			tables = append(tables, tableName)
   184  		}
   185  
   186  		// Delete Dictionary
   187  		query = cleanQueries.DeleteDictionaryQry
   188  		if _, err = tx.Exec(query); err != nil {
   189  			db.Log.Info("msg", "Error deleting dictionary", "err", err, "query", query)
   190  			return err
   191  		}
   192  
   193  		// Delete Log
   194  		query = cleanQueries.DeleteLogQry
   195  		if _, err = tx.Exec(query); err != nil {
   196  			db.Log.Info("msg", "Error deleting log", "err", err, "query", query)
   197  			return err
   198  		}
   199  		// Drop database tables
   200  		for _, tableName = range tables {
   201  			query = db.DBAdapter.DropTableQuery(tableName)
   202  			if _, err = tx.Exec(query); err != nil {
   203  				// if error == table does not exists, continue
   204  				if !db.DBAdapter.ErrorEquals(err, types.SQLErrorTypeUndefinedTable) {
   205  					db.Log.Info("msg", "error dropping tables", "err", err, "value", tableName, "query", query)
   206  					return err
   207  				}
   208  			}
   209  		}
   210  
   211  		// Commit
   212  		if err = tx.Commit(); err != nil {
   213  			db.Log.Info("msg", "Error commiting transaction", "err", err)
   214  			return err
   215  		}
   216  
   217  		return nil
   218  	}
   219  }
   220  
   221  // Close database connection
   222  func (db *SQLDB) Close() {
   223  	if err := db.DB.Close(); err != nil {
   224  		db.Log.Error("msg", "Error closing database", "err", err)
   225  	}
   226  }
   227  
   228  // Ping database
   229  func (db *SQLDB) Ping() error {
   230  	if err := db.DB.Ping(); err != nil {
   231  		db.Log.Info("msg", "Error database not available", "err", err)
   232  		return err
   233  	}
   234  
   235  	return nil
   236  }
   237  
   238  // GetLastBlockID returns last inserted blockId from log table
   239  func (db *SQLDB) GetLastBlockHeight() (uint64, error) {
   240  	query := db.DBAdapter.LastBlockIDQuery()
   241  	id := ""
   242  
   243  	db.Log.Info("msg", "MAX ID", "query", query)
   244  
   245  	if err := db.DB.QueryRow(query).Scan(&id); err != nil {
   246  		db.Log.Info("msg", "Error selecting last block id", "err", err)
   247  		return 0, err
   248  	}
   249  	height, err := strconv.ParseUint(id, 10, 64)
   250  	if err != nil {
   251  		return 0, fmt.Errorf("could not parse height from block ID: %v", err)
   252  	}
   253  	return height, nil
   254  }
   255  
   256  // SynchronizeDB synchronize db tables structures from given tables specifications
   257  func (db *SQLDB) SynchronizeDB(eventTables types.EventTables) error {
   258  	db.Log.Info("msg", "Synchronizing DB")
   259  
   260  	for eventName, table := range eventTables {
   261  		found, err := db.findTable(table.Name)
   262  		if err != nil {
   263  			return err
   264  		}
   265  
   266  		if found {
   267  			err = db.alterTable(table, eventName)
   268  		} else {
   269  			err = db.createTable(table, eventName)
   270  		}
   271  		if err != nil {
   272  			return err
   273  		}
   274  	}
   275  
   276  	return nil
   277  }
   278  
   279  // SetBlock inserts or updates multiple rows and stores log info in SQL tables
   280  func (db *SQLDB) SetBlock(eventTables types.EventTables, eventData types.EventData) error {
   281  	db.Log.Info("msg", "Synchronize Block..........")
   282  
   283  	//Declarations
   284  	var logStmt *sql.Stmt
   285  	var tx *sql.Tx
   286  	var safeTable string
   287  	var query string
   288  	var queryVal types.UpsertDeleteQuery
   289  	var err error
   290  	var errQuery error
   291  	var txHash interface{}
   292  	var jsonData []byte
   293  	var sqlValues []byte
   294  
   295  	// Begin tx
   296  	if tx, err = db.DB.Begin(); err != nil {
   297  		db.Log.Info("msg", "Error beginning transaction", "err", err)
   298  		return err
   299  	}
   300  	defer tx.Rollback()
   301  
   302  	// Prepare log statement
   303  	logQuery := db.DBAdapter.InsertLogQuery()
   304  	if logStmt, err = tx.Prepare(logQuery); err != nil {
   305  		db.Log.Info("msg", "Error preparing log stmt", "err", err)
   306  		return err
   307  	}
   308  
   309  loop:
   310  	// for each table in the block
   311  	for eventName, table := range eventTables {
   312  		safeTable = safe(table.Name)
   313  		dataRows := eventData.Tables[table.Name]
   314  		// for Each Row
   315  		for _, row := range dataRows {
   316  
   317  			switch row.Action {
   318  			case types.ActionUpsert:
   319  				//Prepare Upsert
   320  				if queryVal, txHash, errQuery = db.DBAdapter.UpsertQuery(table, row); errQuery != nil {
   321  					db.Log.Info("msg", "Error building upsert query", "err", errQuery, "value", fmt.Sprintf("%v %v", table, row))
   322  					break loop // exits from all loops -> continue in close log stmt
   323  				}
   324  
   325  			case types.ActionDelete:
   326  				//Prepare Delete
   327  				txHash = nil
   328  				if queryVal, errQuery = db.DBAdapter.DeleteQuery(table, row); errQuery != nil {
   329  					db.Log.Info("msg", "Error building delete query", "err", errQuery, "value", fmt.Sprintf("%v %v", table, row))
   330  					break loop // exits from all loops -> continue in close log stmt
   331  				}
   332  			default:
   333  				//Invalid Action
   334  				db.Log.Info("msg", "invalid action", "value", row.Action)
   335  				err = fmt.Errorf("invalid row action %s", row.Action)
   336  				break loop // exits from all loops -> continue in close log stmt
   337  			}
   338  
   339  			query = queryVal.Query
   340  
   341  			// Perform row action
   342  			db.Log.Info("msg", row.Action, "query", query, "value", queryVal.Values)
   343  			if _, err = tx.Exec(query, queryVal.Pointers...); err != nil {
   344  				db.Log.Info("msg", fmt.Sprintf("error performing %s on row", row.Action), "err", err, "value", queryVal.Values)
   345  				break loop // exits from all loops -> continue in close log stmt
   346  			}
   347  
   348  			// Marshal the rowData map
   349  			if jsonData, err = db.getJSON(row.RowData); err != nil {
   350  				db.Log.Info("msg", "error marshaling rowData", "err", err, "value", fmt.Sprintf("%v", row.RowData))
   351  				break loop // exits from all loops -> continue in close log stmt
   352  			}
   353  
   354  			// Marshal sql values
   355  			if sqlValues, err = db.getJSONFromValues(queryVal.Pointers); err != nil {
   356  				db.Log.Info("msg", "error marshaling rowdata", "err", err, "value", fmt.Sprintf("%v", row.RowData))
   357  				break loop // exits from all loops -> continue in close log stmt
   358  			}
   359  
   360  			// Insert in log
   361  			db.Log.Info("msg", "INSERT LOG", "query", logQuery, "value",
   362  				fmt.Sprintf("tableName = %s eventName = %s block = %d", safeTable, eventName, eventData.BlockHeight))
   363  
   364  			if _, err = logStmt.Exec(safeTable, eventName, row.EventClass.GetFilter(), eventData.BlockHeight, txHash,
   365  				row.Action, jsonData, query, sqlValues); err != nil {
   366  				db.Log.Info("msg", "Error inserting into log", "err", err)
   367  				break loop // exits from all loops -> continue in close log stmt
   368  			}
   369  		}
   370  	}
   371  
   372  	// Close log statement
   373  	if err == nil {
   374  		if err = logStmt.Close(); err != nil {
   375  			db.Log.Info("msg", "Error closing log stmt", "err", err)
   376  		}
   377  	}
   378  
   379  	// Error handling
   380  	if err != nil {
   381  		// Rollback error
   382  		if errRb := tx.Rollback(); errRb != nil {
   383  			db.Log.Info("msg", "Error on rollback", "err", errRb)
   384  			return errRb
   385  		}
   386  
   387  		//Is a SQL error
   388  		if db.DBAdapter.ErrorEquals(err, types.SQLErrorTypeGeneric) {
   389  
   390  			// Table does not exists
   391  			if db.DBAdapter.ErrorEquals(err, types.SQLErrorTypeUndefinedTable) {
   392  				db.Log.Warn("msg", "Table not found", "value", safeTable)
   393  				//Synchronize DB
   394  				if err = db.SynchronizeDB(eventTables); err != nil {
   395  					return err
   396  				}
   397  				//Retry
   398  				return db.SetBlock(eventTables, eventData)
   399  			}
   400  
   401  			// Columns do not match
   402  			if db.DBAdapter.ErrorEquals(err, types.SQLErrorTypeUndefinedColumn) {
   403  				db.Log.Warn("msg", "Column not found", "value", safeTable)
   404  				//Synchronize DB
   405  				if err = db.SynchronizeDB(eventTables); err != nil {
   406  					return err
   407  				}
   408  				//Retry
   409  				return db.SetBlock(eventTables, eventData)
   410  			}
   411  			return err
   412  		}
   413  		return err
   414  	}
   415  
   416  	db.Log.Info("msg", "COMMIT")
   417  
   418  	if err := tx.Commit(); err != nil {
   419  		db.Log.Info("msg", "Error on commit", "err", err)
   420  		return err
   421  	}
   422  
   423  	return nil
   424  }
   425  
   426  // GetBlock returns all tables structures and row data for given block
   427  func (db *SQLDB) GetBlock(height uint64) (types.EventData, error) {
   428  	var data types.EventData
   429  	data.BlockHeight = height
   430  	data.Tables = make(map[string]types.EventDataTable)
   431  
   432  	// get all table structures involved in the block
   433  	tables, err := db.getBlockTables(height)
   434  	if err != nil {
   435  		return data, err
   436  	}
   437  
   438  	query := ""
   439  
   440  	// for each table
   441  	for _, table := range tables {
   442  		// get query for table
   443  		query, err = db.getSelectQuery(table, height)
   444  		if err != nil {
   445  			db.Log.Info("msg", "Error building table query", "err", err)
   446  			return data, err
   447  		}
   448  		db.Log.Info("msg", "Query table data", "query", query)
   449  		rows, err := db.DB.Query(query)
   450  		if err != nil {
   451  			db.Log.Info("msg", "Error querying table data", "err", err)
   452  			return data, err
   453  		}
   454  		defer rows.Close()
   455  
   456  		cols, err := rows.Columns()
   457  		if err != nil {
   458  			db.Log.Info("msg", "Error getting row columns", "err", err)
   459  			return data, err
   460  		}
   461  
   462  		// builds pointers
   463  		length := len(cols)
   464  		pointers := make([]interface{}, length)
   465  		containers := make([]sql.NullString, length)
   466  
   467  		for i := range pointers {
   468  			pointers[i] = &containers[i]
   469  		}
   470  
   471  		// for each row in table
   472  		var dataRows []types.EventDataRow
   473  
   474  		for rows.Next() {
   475  
   476  			row := make(map[string]interface{})
   477  
   478  			if err = rows.Scan(pointers...); err != nil {
   479  				db.Log.Info("msg", "Error scanning data", "err", err)
   480  				return data, err
   481  			}
   482  			db.Log.Info("msg", "Query resultset", "value", fmt.Sprintf("%+v", containers))
   483  
   484  			// for each column in row
   485  			for i, col := range cols {
   486  				// add value if not null
   487  				if containers[i].Valid {
   488  					row[col] = containers[i].String
   489  				}
   490  			}
   491  			dataRows = append(dataRows, types.EventDataRow{Action: types.ActionRead, RowData: row})
   492  		}
   493  
   494  		if err = rows.Err(); err != nil {
   495  			db.Log.Info("msg", "Error during rows iteration", "err", err)
   496  			return data, err
   497  		}
   498  		data.Tables[table.Name] = dataRows
   499  	}
   500  	return data, nil
   501  }
   502  
   503  // RestoreDB restores the DB to a given moment in time
   504  func (db *SQLDB) RestoreDB(time time.Time, prefix string) error {
   505  
   506  	const yymmddhhmmss = "2006-01-02 15:04:05"
   507  
   508  	var pointers []interface{}
   509  
   510  	if prefix == "" {
   511  		return fmt.Errorf("error prefix mus not be empty")
   512  	}
   513  
   514  	// Get Restore DB query
   515  	query := db.DBAdapter.RestoreDBQuery()
   516  	strTime := time.Format(yymmddhhmmss)
   517  
   518  	db.Log.Info("msg", "RESTORING DB..................................")
   519  
   520  	// Open rows
   521  	db.Log.Info("msg", "open log", "query", query)
   522  	rows, err := db.DB.Query(query, strTime)
   523  	if err != nil {
   524  		db.Log.Info("msg", "error querying log", "err", err)
   525  		return err
   526  	}
   527  	defer rows.Close()
   528  
   529  	// For each row returned
   530  	for rows.Next() {
   531  		var tableName, sqlSmt, sqlValues string
   532  		var action types.DBAction
   533  
   534  		if err = rows.Scan(&tableName, &action, &sqlSmt, &sqlValues); err != nil {
   535  			db.Log.Info("msg", "error scanning table structure", "err", err)
   536  			return err
   537  		}
   538  
   539  		if err = rows.Err(); err != nil {
   540  			db.Log.Info("msg", "error scanning table structure", "err", err)
   541  			return err
   542  		}
   543  
   544  		restoreTable := fmt.Sprintf("%s_%s", prefix, tableName)
   545  
   546  		switch action {
   547  		case types.ActionUpsert, types.ActionDelete:
   548  			// get row values
   549  			if pointers, err = db.getValuesFromJSON(sqlValues); err != nil {
   550  				db.Log.Info("msg", "error unmarshaling json", "err", err, "value", sqlValues)
   551  				return err
   552  			}
   553  
   554  			// Prepare Upsert/delete
   555  			query = strings.Replace(sqlSmt, tableName, restoreTable, -1)
   556  
   557  			db.Log.Info("msg", "SQL COMMAND", "sql", query)
   558  			if _, err = db.DB.Exec(query, pointers...); err != nil {
   559  				db.Log.Info("msg", "Error executing upsert/delete ", "err", err, "value", sqlSmt, "data", sqlValues)
   560  				return err
   561  			}
   562  
   563  		case types.ActionAlterTable, types.ActionCreateTable:
   564  			// Prepare Alter/Create Table
   565  			query = strings.Replace(sqlSmt, tableName, restoreTable, -1)
   566  
   567  			db.Log.Info("msg", "SQL COMMAND", "sql", query)
   568  			if _, err = db.DB.Exec(query); err != nil {
   569  				db.Log.Info("msg", "Error executing alter/create table command ", "err", err, "value", sqlSmt)
   570  				return err
   571  			}
   572  		default:
   573  			// Invalid Action
   574  			db.Log.Info("msg", "invalid action", "value", action)
   575  			return fmt.Errorf("invalid row action %s", action)
   576  		}
   577  	}
   578  	return nil
   579  }