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

     1  package sqldb
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"github.com/hyperledger/burrow/vent/sqldb/adapters"
    10  
    11  	"github.com/hyperledger/burrow/txs"
    12  
    13  	"encoding/json"
    14  
    15  	"github.com/hyperledger/burrow/vent/types"
    16  )
    17  
    18  // findTable checks if a table exists in the default schema
    19  func (db *SQLDB) findTable(tableName string) (bool, error) {
    20  
    21  	found := 0
    22  	safeTable := safe(tableName)
    23  	query := db.DBAdapter.FindTableQuery()
    24  
    25  	db.Log.Info("msg", "FIND TABLE", "query", query, "value", safeTable)
    26  	if err := db.DB.QueryRow(query, tableName).Scan(&found); err != nil {
    27  		db.Log.Info("msg", "Error finding table", "err", err)
    28  		return false, err
    29  	}
    30  
    31  	if found == 0 {
    32  		db.Log.Warn("msg", "Table not found", "value", safeTable)
    33  		return false, nil
    34  	}
    35  
    36  	return true, nil
    37  }
    38  
    39  // getSysTablesDefinition returns log, chain info & dictionary structures
    40  func (db *SQLDB) getSysTablesDefinition() types.EventTables {
    41  	return types.EventTables{
    42  		types.SQLLogTableName: {
    43  			Name: types.SQLLogTableName,
    44  			Columns: []*types.SQLTableColumn{
    45  				{
    46  					Name:    types.SQLColumnLabelId,
    47  					Type:    types.SQLColumnTypeSerial,
    48  					Primary: true,
    49  				},
    50  				{
    51  					Name:    types.SQLColumnLabelTimeStamp,
    52  					Type:    types.SQLColumnTypeTimeStamp,
    53  					Primary: false,
    54  				},
    55  				{
    56  					Name:    types.SQLColumnLabelTableName,
    57  					Type:    types.SQLColumnTypeVarchar,
    58  					Length:  100,
    59  					Primary: false,
    60  				},
    61  				{
    62  					Name:    types.SQLColumnLabelEventName,
    63  					Type:    types.SQLColumnTypeVarchar,
    64  					Length:  100,
    65  					Primary: false,
    66  				},
    67  				{
    68  					Name:    types.SQLColumnLabelEventFilter,
    69  					Type:    types.SQLColumnTypeVarchar,
    70  					Length:  100,
    71  					Primary: false,
    72  				},
    73  				// We use varchar for height - there is no uint64 type though numeric could have been used. We obtain the
    74  				// maximum height by maxing over the serial ID type
    75  				{
    76  					Name:    types.SQLColumnLabelHeight,
    77  					Type:    types.SQLColumnTypeVarchar,
    78  					Length:  100,
    79  					Primary: false,
    80  				},
    81  				{
    82  					Name:    types.SQLColumnLabelTxHash,
    83  					Type:    types.SQLColumnTypeVarchar,
    84  					Length:  txs.HashLengthHex,
    85  					Primary: false,
    86  				},
    87  				{
    88  					Name:    types.SQLColumnLabelAction,
    89  					Type:    types.SQLColumnTypeVarchar,
    90  					Length:  20,
    91  					Primary: false,
    92  				},
    93  				{
    94  					Name:    types.SQLColumnLabelDataRow,
    95  					Type:    types.SQLColumnTypeJSON,
    96  					Length:  0,
    97  					Primary: false,
    98  				},
    99  				{
   100  					Name:    types.SQLColumnLabelSqlStmt,
   101  					Type:    types.SQLColumnTypeText,
   102  					Length:  0,
   103  					Primary: false,
   104  				},
   105  				{
   106  					Name:    types.SQLColumnLabelSqlValues,
   107  					Type:    types.SQLColumnTypeText,
   108  					Length:  0,
   109  					Primary: false,
   110  				},
   111  			},
   112  			NotifyChannels: map[string][]string{types.BlockHeightLabel: {types.SQLColumnLabelHeight}},
   113  		},
   114  		types.SQLDictionaryTableName: {
   115  			Name: types.SQLDictionaryTableName,
   116  			Columns: []*types.SQLTableColumn{
   117  				{
   118  					Name:    types.SQLColumnLabelTableName,
   119  					Type:    types.SQLColumnTypeVarchar,
   120  					Length:  100,
   121  					Primary: true,
   122  				},
   123  				{
   124  					Name:    types.SQLColumnLabelColumnName,
   125  					Type:    types.SQLColumnTypeVarchar,
   126  					Length:  100,
   127  					Primary: true,
   128  				},
   129  				{
   130  					Name:    types.SQLColumnLabelColumnType,
   131  					Type:    types.SQLColumnTypeInt,
   132  					Length:  0,
   133  					Primary: false,
   134  				},
   135  				{
   136  					Name:    types.SQLColumnLabelColumnLength,
   137  					Type:    types.SQLColumnTypeInt,
   138  					Length:  0,
   139  					Primary: false,
   140  				},
   141  				{
   142  					Name:    types.SQLColumnLabelPrimaryKey,
   143  					Type:    types.SQLColumnTypeInt,
   144  					Length:  0,
   145  					Primary: false,
   146  				},
   147  				{
   148  					Name:    types.SQLColumnLabelColumnOrder,
   149  					Type:    types.SQLColumnTypeInt,
   150  					Length:  0,
   151  					Primary: false,
   152  				},
   153  			},
   154  		},
   155  		types.SQLChainInfoTableName: {
   156  			Name: types.SQLChainInfoTableName,
   157  			Columns: []*types.SQLTableColumn{
   158  				{
   159  					Name:    types.SQLColumnLabelChainID,
   160  					Type:    types.SQLColumnTypeVarchar,
   161  					Length:  100,
   162  					Primary: true,
   163  				},
   164  				{
   165  					Name:    types.SQLColumnLabelBurrowVer,
   166  					Type:    types.SQLColumnTypeVarchar,
   167  					Length:  100,
   168  					Primary: false,
   169  				},
   170  			},
   171  		},
   172  	}
   173  }
   174  
   175  // getTableDef returns the structure of a given SQL table
   176  func (db *SQLDB) getTableDef(tableName string) (*types.SQLTable, error) {
   177  	table := &types.SQLTable{
   178  		Name: safe(tableName),
   179  	}
   180  	found, err := db.findTable(table.Name)
   181  	if err != nil {
   182  		return nil, err
   183  	}
   184  
   185  	if !found {
   186  		db.Log.Info("msg", "Error table not found", "value", table.Name)
   187  		return nil, errors.New("Error table not found " + table.Name)
   188  	}
   189  
   190  	query := db.DBAdapter.TableDefinitionQuery()
   191  
   192  	db.Log.Info("msg", "QUERY STRUCTURE", "query", query, "value", table.Name)
   193  	rows, err := db.DB.Query(query, safe(tableName))
   194  	if err != nil {
   195  		db.Log.Info("msg", "Error querying table structure", "err", err)
   196  		return nil, err
   197  	}
   198  	defer rows.Close()
   199  
   200  	var columns []*types.SQLTableColumn
   201  
   202  	for rows.Next() {
   203  		var columnName string
   204  		var columnSQLType types.SQLColumnType
   205  		var columnIsPK int
   206  		var columnLength int
   207  
   208  		if err = rows.Scan(&columnName, &columnSQLType, &columnLength, &columnIsPK); err != nil {
   209  			db.Log.Info("msg", "Error scanning table structure", "err", err)
   210  			return nil, err
   211  		}
   212  
   213  		if _, err = db.DBAdapter.TypeMapping(columnSQLType); err != nil {
   214  			return nil, err
   215  		}
   216  
   217  		columns = append(columns, &types.SQLTableColumn{
   218  			Name:    columnName,
   219  			Type:    columnSQLType,
   220  			Length:  columnLength,
   221  			Primary: columnIsPK == 1,
   222  		})
   223  	}
   224  
   225  	if err = rows.Err(); err != nil {
   226  		db.Log.Info("msg", "Error during rows iteration", "err", err)
   227  		return nil, err
   228  	}
   229  
   230  	table.Columns = columns
   231  	return table, nil
   232  }
   233  
   234  // alterTable alters the structure of a SQL table & add info to the dictionary
   235  func (db *SQLDB) alterTable(table *types.SQLTable, eventName string) error {
   236  	db.Log.Info("msg", "Altering table", "value", table.Name)
   237  
   238  	// prepare log query
   239  	logQuery := db.DBAdapter.InsertLogQuery()
   240  
   241  	// current table structure
   242  	safeTable := safe(table.Name)
   243  	currentTable, err := db.getTableDef(safeTable)
   244  	if err != nil {
   245  		return err
   246  	}
   247  
   248  	sqlValues, _ := db.getJSON(nil)
   249  
   250  	// for each column in the new table structure
   251  	for order, newColumn := range table.Columns {
   252  		found := false
   253  
   254  		// check if exists in the current table structure
   255  		for _, currentColumn := range currentTable.Columns {
   256  			// if column exists
   257  			if currentColumn.Name == newColumn.Name {
   258  				found = true
   259  				break
   260  			}
   261  		}
   262  
   263  		if !found {
   264  			safeCol := safe(newColumn.Name)
   265  			query, dictionary := db.DBAdapter.AlterColumnQuery(safeTable, safeCol, newColumn.Type, newColumn.Length, order)
   266  
   267  			//alter column
   268  			db.Log.Info("msg", "ALTER TABLE", "query", safe(query))
   269  			_, err = db.DB.Exec(safe(query))
   270  
   271  			if err != nil {
   272  				if db.DBAdapter.ErrorEquals(err, types.SQLErrorTypeDuplicatedColumn) {
   273  					db.Log.Warn("msg", "Duplicate column", "value", safeCol)
   274  				} else {
   275  					db.Log.Info("msg", "Error altering table", "err", err)
   276  					return err
   277  				}
   278  			} else {
   279  				//store dictionary
   280  				db.Log.Info("msg", "STORE DICTIONARY", "query", dictionary)
   281  				_, err = db.DB.Exec(dictionary)
   282  				if err != nil {
   283  					db.Log.Info("msg", "Error storing  dictionary", "err", err)
   284  					return err
   285  				}
   286  
   287  				//insert log (if action is not database initialization)
   288  				if eventName != string(types.ActionInitialize) {
   289  					// Marshal the table into a JSON string.
   290  					var jsonData []byte
   291  					jsonData, err = db.getJSON(newColumn)
   292  					if err != nil {
   293  						db.Log.Info("msg", "error marshaling column", "err", err, "value", fmt.Sprintf("%v", newColumn))
   294  						return err
   295  					}
   296  					//insert log
   297  					_, err = db.DB.Exec(logQuery, table.Name, eventName, "", nil, nil, types.ActionAlterTable, jsonData, query, sqlValues)
   298  					if err != nil {
   299  						db.Log.Info("msg", "Error inserting log", "err", err)
   300  						return err
   301  					}
   302  				}
   303  			}
   304  		}
   305  	}
   306  
   307  	// Ensure triggers are defined
   308  	err = db.createTableTriggers(table)
   309  	if err != nil {
   310  		db.Log.Info("msg", "error creating notification triggers", "err", err, "value", fmt.Sprintf("%v", table))
   311  		return fmt.Errorf("could not create table notification triggers: %v", err)
   312  	}
   313  	return nil
   314  }
   315  
   316  // createTable creates a new table
   317  func (db *SQLDB) createTable(table *types.SQLTable, eventName string) error {
   318  	db.Log.Info("msg", "Creating Table", "value", table.Name)
   319  
   320  	// prepare log query
   321  	logQuery := db.DBAdapter.InsertLogQuery()
   322  
   323  	//get create table query
   324  	safeTable := safe(table.Name)
   325  	query, dictionary := db.DBAdapter.CreateTableQuery(safeTable, table.Columns)
   326  	if query == "" {
   327  		db.Log.Info("msg", "empty CREATE TABLE query")
   328  		return errors.New("empty CREATE TABLE query")
   329  	}
   330  
   331  	// create table
   332  	db.Log.Info("msg", "CREATE TABLE", "query", query)
   333  	_, err := db.DB.Exec(query)
   334  	if err != nil {
   335  		return err
   336  	}
   337  
   338  	//store dictionary
   339  	db.Log.Info("msg", "STORE DICTIONARY", "query", dictionary)
   340  	_, err = db.DB.Exec(dictionary)
   341  	if err != nil {
   342  		db.Log.Info("msg", "Error storing  dictionary", "err", err)
   343  		return err
   344  	}
   345  
   346  	err = db.createTableTriggers(table)
   347  	if err != nil {
   348  		db.Log.Info("msg", "error creating notification triggers", "err", err, "value", fmt.Sprintf("%v", table))
   349  		return fmt.Errorf("could not create table notification triggers: %v", err)
   350  	}
   351  
   352  	//insert log (if action is not database initialization)
   353  	if eventName != string(types.ActionInitialize) {
   354  		// Marshal the table into a JSON string.
   355  		var jsonData []byte
   356  		jsonData, err = db.getJSON(table)
   357  		if err != nil {
   358  			db.Log.Info("msg", "error marshaling table", "err", err, "value", fmt.Sprintf("%v", table))
   359  			return err
   360  		}
   361  		sqlValues, _ := db.getJSON(nil)
   362  
   363  		//insert log
   364  		_, err = db.DB.Exec(logQuery, table.Name, eventName, "", nil, nil, types.ActionCreateTable, jsonData, query, sqlValues)
   365  		if err != nil {
   366  			db.Log.Info("msg", "Error inserting log", "err", err)
   367  			return err
   368  		}
   369  	}
   370  	return nil
   371  }
   372  
   373  // Creates (or updates) table notification triggers and functions
   374  func (db *SQLDB) createTableTriggers(table *types.SQLTable) error {
   375  	// If the adapter supports notification triggers
   376  	dbNotify, ok := db.DBAdapter.(adapters.DBNotifyTriggerAdapter)
   377  	if ok {
   378  		for channel, columns := range table.NotifyChannels {
   379  			function := fmt.Sprintf("%s_%s_notify_function", table.Name, channel)
   380  
   381  			query := dbNotify.CreateNotifyFunctionQuery(function, channel, columns...)
   382  			db.Log.Info("msg", "CREATE NOTIFICATION FUNCTION", "query", query)
   383  			_, err := db.DB.Exec(query)
   384  			if err != nil {
   385  				return fmt.Errorf("could not create notification function: %v", err)
   386  			}
   387  
   388  			trigger := fmt.Sprintf("%s_%s_notify_trigger", table.Name, channel)
   389  			query = dbNotify.CreateTriggerQuery(trigger, table.Name, function)
   390  			db.Log.Info("msg", "CREATE NOTIFICATION TRIGGER", "query", query)
   391  			_, err = db.DB.Exec(query)
   392  			if err != nil {
   393  				return fmt.Errorf("could not create notification trigger: %v", err)
   394  			}
   395  		}
   396  	}
   397  	return nil
   398  }
   399  
   400  // getSelectQuery builds a select query for a specific SQL table and a given block
   401  func (db *SQLDB) getSelectQuery(table *types.SQLTable, height uint64) (string, error) {
   402  
   403  	fields := ""
   404  
   405  	for _, tableColumn := range table.Columns {
   406  		if fields != "" {
   407  			fields += ", "
   408  		}
   409  		fields += db.DBAdapter.SecureName(tableColumn.Name)
   410  	}
   411  
   412  	if fields == "" {
   413  		return "", errors.New("error table does not contain any fields")
   414  	}
   415  
   416  	query := db.DBAdapter.SelectRowQuery(table.Name, fields, strconv.FormatUint(height, 10))
   417  	return query, nil
   418  }
   419  
   420  // getBlockTables return all SQL tables that have been involved
   421  // in a given batch transaction for a specific block
   422  func (db *SQLDB) getBlockTables(height uint64) (types.EventTables, error) {
   423  	tables := make(types.EventTables)
   424  
   425  	query := db.DBAdapter.SelectLogQuery()
   426  	db.Log.Info("msg", "QUERY LOG", "query", query, "value", height)
   427  
   428  	rows, err := db.DB.Query(query, height)
   429  	if err != nil {
   430  		db.Log.Info("msg", "Error querying log", "err", err)
   431  		return tables, err
   432  	}
   433  	defer rows.Close()
   434  
   435  	for rows.Next() {
   436  		var eventName, tableName string
   437  		var table *types.SQLTable
   438  
   439  		err = rows.Scan(&tableName, &eventName)
   440  		if err != nil {
   441  			db.Log.Info("msg", "Error scanning table structure", "err", err)
   442  			return tables, err
   443  		}
   444  
   445  		err = rows.Err()
   446  		if err != nil {
   447  			db.Log.Info("msg", "Error scanning table structure", "err", err)
   448  			return tables, err
   449  		}
   450  
   451  		table, err = db.getTableDef(tableName)
   452  		if err != nil {
   453  			return tables, err
   454  		}
   455  
   456  		tables[eventName] = table
   457  	}
   458  
   459  	return tables, nil
   460  }
   461  
   462  // safe sanitizes a parameter
   463  func safe(parameter string) string {
   464  	replacer := strings.NewReplacer(";", "", ",", "")
   465  	return replacer.Replace(parameter)
   466  }
   467  
   468  //getJSON returns marshaled json from JSON single column
   469  func (db *SQLDB) getJSON(JSON interface{}) ([]byte, error) {
   470  	if JSON != nil {
   471  		return json.Marshal(JSON)
   472  	}
   473  	return json.Marshal("")
   474  }
   475  
   476  //getJSONFromValues returns marshaled json from query values
   477  func (db *SQLDB) getJSONFromValues(values []interface{}) ([]byte, error) {
   478  	if values != nil {
   479  		return json.Marshal(values)
   480  	}
   481  	return json.Marshal("")
   482  }
   483  
   484  //getValuesFromJSON returns query values from unmarshaled JSON column
   485  func (db *SQLDB) getValuesFromJSON(JSON string) ([]interface{}, error) {
   486  	pointers := make([]interface{}, 0)
   487  	bytes := []byte(JSON)
   488  	err := json.Unmarshal(bytes, &pointers)
   489  	return pointers, err
   490  }