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 }