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 }