github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/vent/sqldb/adapters/sqlite_adapter.go (about) 1 // sqlite3 is a CGO dependency - we cannot have it on board if we want to use pure Go (e.g. for cross-compiling and other things) 2 // +build sqlite 3 4 package adapters 5 6 import ( 7 "fmt" 8 "strings" 9 10 "github.com/hyperledger/burrow/logging" 11 "github.com/hyperledger/burrow/vent/types" 12 "github.com/jmoiron/sqlx" 13 sqlite3 "github.com/mattn/go-sqlite3" 14 "github.com/pkg/errors" 15 ) 16 17 var sqliteDataTypes = map[types.SQLColumnType]string{ 18 types.SQLColumnTypeBool: "BOOLEAN", 19 types.SQLColumnTypeByteA: "BLOB", 20 types.SQLColumnTypeInt: "INTEGER", 21 types.SQLColumnTypeSerial: "SERIAL", 22 types.SQLColumnTypeText: "TEXT", 23 types.SQLColumnTypeVarchar: "VARCHAR", 24 types.SQLColumnTypeTimeStamp: "TIMESTAMP", 25 types.SQLColumnTypeNumeric: "NUMERIC", 26 types.SQLColumnTypeJSON: "TEXT", 27 types.SQLColumnTypeBigInt: "BIGINT", 28 } 29 30 // SQLiteAdapter implements DBAdapter for SQLiteDB 31 type SQLiteAdapter struct { 32 types.SQLNames 33 Log *logging.Logger 34 } 35 36 var _ DBAdapter = &SQLiteAdapter{} 37 38 // NewSQLiteAdapter constructs a new db adapter 39 func NewSQLiteAdapter(sqlNames types.SQLNames, log *logging.Logger) *SQLiteAdapter { 40 return &SQLiteAdapter{ 41 SQLNames: sqlNames, 42 Log: log, 43 } 44 } 45 46 func (sla *SQLiteAdapter) Open(dbURL string) (*sqlx.DB, error) { 47 db, err := sqlx.Open("sqlite3", dbURL) 48 if err != nil { 49 sla.Log.InfoMsg("Error creating database connection", "err", err) 50 return nil, err 51 } 52 return db, nil 53 } 54 55 // TypeMapping convert generic dataTypes to database dependent dataTypes 56 func (sla *SQLiteAdapter) TypeMapping(sqlColumnType types.SQLColumnType) (string, error) { 57 if sqlDataType, ok := sqliteDataTypes[sqlColumnType]; ok { 58 return sqlDataType, nil 59 } 60 61 return "", fmt.Errorf("datatype %v not recognized", sqlColumnType) 62 } 63 64 // SecureColumnName return columns between appropriate security containers 65 func (sla *SQLiteAdapter) SecureName(name string) string { 66 return Cleanf("[%s]", name) 67 } 68 69 // CreateTableQuery builds query for creating a new table 70 func (sla *SQLiteAdapter) CreateTableQuery(tableName string, columns []*types.SQLTableColumn) (string, string) { 71 // build query 72 columnsDef := "" 73 primaryKey := "" 74 dictionaryValues := "" 75 hasSerial := false 76 77 for i, column := range columns { 78 secureColumn := sla.SecureName(column.Name) 79 sqlType, _ := sla.TypeMapping(column.Type) 80 pKey := 0 81 82 if columnsDef != "" { 83 columnsDef += ", " 84 dictionaryValues += ", " 85 } 86 87 if column.Type == types.SQLColumnTypeSerial { 88 // SQLITE AUTOINCREMENT LIMITATION 89 columnsDef += Cleanf("%s %s", secureColumn, "INTEGER PRIMARY KEY AUTOINCREMENT") 90 hasSerial = true 91 } else { 92 columnsDef += Cleanf("%s %s", secureColumn, sqlType) 93 } 94 95 if column.Length > 0 { 96 columnsDef += Cleanf("(%v)", column.Length) 97 } 98 99 if column.Primary { 100 pKey = 1 101 columnsDef += " NOT NULL" 102 if primaryKey != "" { 103 primaryKey += ", " 104 } 105 primaryKey += secureColumn 106 } 107 108 dictionaryValues += Cleanf("('%s','%s',%d,%d,%d,%d)", 109 tableName, 110 column.Name, 111 column.Type, 112 column.Length, 113 pKey, 114 i) 115 } 116 117 query := Cleanf("CREATE TABLE %s (%s", sla.SecureName(tableName), columnsDef) 118 if primaryKey != "" { 119 if hasSerial { 120 // SQLITE AUTOINCREMENT LIMITATION 121 query += "," + Cleanf("UNIQUE (%s)", primaryKey) 122 } else { 123 query += "," + Cleanf("CONSTRAINT %s_pkey PRIMARY KEY (%s)", tableName, primaryKey) 124 } 125 } 126 query += ");" 127 128 dictionaryQuery := Cleanf("INSERT INTO %s (%s,%s,%s,%s,%s,%s) VALUES %s;", 129 sla.Tables.Dictionary, 130 sla.Columns.TableName, sla.Columns.ColumnName, 131 sla.Columns.ColumnType, sla.Columns.ColumnLength, 132 sla.Columns.PrimaryKey, sla.Columns.ColumnOrder, 133 dictionaryValues) 134 135 return query, dictionaryQuery 136 } 137 138 // FindTableQuery returns a query that checks if a table exists 139 func (sla *SQLiteAdapter) FindTableQuery() string { 140 query := "SELECT COUNT(*) found FROM %s WHERE %s = $1;" 141 142 return Cleanf(query, 143 sla.Tables.Dictionary, // from 144 sla.Columns.TableName) // where 145 } 146 147 // TableDefinitionQuery returns a query with table structure 148 func (sla *SQLiteAdapter) TableDefinitionQuery() string { 149 query := ` 150 SELECT 151 %s,%s,%s,%s 152 FROM 153 %s 154 WHERE 155 %s = $1 156 ORDER BY 157 %s;` 158 159 return Cleanf(query, 160 sla.Columns.ColumnName, sla.Columns.ColumnType, // select 161 sla.Columns.ColumnLength, sla.Columns.PrimaryKey, // select 162 sla.Tables.Dictionary, // from 163 sla.Columns.TableName, // where 164 sla.Columns.ColumnOrder) // order by 165 } 166 167 // AlterColumnQuery returns a query for adding a new column to a table 168 func (sla *SQLiteAdapter) AlterColumnQuery(tableName, columnName string, sqlColumnType types.SQLColumnType, length, order int) (string, string) { 169 sqlType, _ := sla.TypeMapping(sqlColumnType) 170 if length > 0 { 171 sqlType = Cleanf("%s(%d)", sqlType, length) 172 } 173 174 query := Cleanf("ALTER TABLE %s ADD COLUMN %s %s;", 175 sla.SecureName(tableName), 176 sla.SecureName(columnName), 177 sqlType) 178 179 dictionaryQuery := Cleanf(` 180 INSERT INTO %s (%s,%s,%s,%s,%s,%s) 181 VALUES ('%s','%s',%d,%d,%d,%d);`, 182 183 sla.Tables.Dictionary, 184 185 sla.Columns.TableName, sla.Columns.ColumnName, 186 sla.Columns.ColumnType, sla.Columns.ColumnLength, 187 sla.Columns.PrimaryKey, sla.Columns.ColumnOrder, 188 189 tableName, columnName, sqlColumnType, length, 0, order) 190 191 return query, dictionaryQuery 192 } 193 194 // SelectRowQuery returns a query for selecting row values 195 func (sla *SQLiteAdapter) SelectRowQuery(tableName, fields, indexValue string) string { 196 return Cleanf("SELECT %s FROM %s WHERE %s = '%s';", fields, sla.SecureName(tableName), sla.Columns.Height, indexValue) 197 } 198 199 // SelectLogQuery returns a query for selecting all tables involved in a block trn 200 func (sla *SQLiteAdapter) SelectLogQuery() string { 201 query := ` 202 SELECT DISTINCT %s,%s FROM %s l WHERE %s = $1 AND %s = $2;` 203 204 return Cleanf(query, 205 sla.Columns.TableName, sla.Columns.EventName, // select 206 sla.Tables.Log, // from 207 sla.Columns.Height, // where 208 sla.Columns.ChainID) 209 } 210 211 // InsertLogQuery returns a query to insert a row in log table 212 func (sla *SQLiteAdapter) InsertLogQuery() string { 213 query := ` 214 INSERT INTO %s (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s) 215 VALUES (CURRENT_TIMESTAMP, $1, $2, $3, $4, $5, $6 ,$7, $8, $9, $10);` 216 217 return Cleanf(query, 218 sla.Tables.Log, // insert 219 //fields 220 sla.Columns.TimeStamp, 221 sla.Columns.ChainID, 222 sla.Columns.TableName, sla.Columns.EventName, sla.Columns.EventFilter, 223 sla.Columns.Height, sla.Columns.TxHash, sla.Columns.Action, sla.Columns.DataRow, 224 sla.Columns.SqlStmt, sla.Columns.SqlValues) 225 } 226 227 // ErrorEquals verify if an error is of a given SQL type 228 func (sla *SQLiteAdapter) ErrorEquals(err error, sqlErrorType types.SQLErrorType) bool { 229 slErr := new(sqlite3.Error) 230 if errors.As(err, slErr) { 231 errDescription := err.Error() 232 233 switch sqlErrorType { 234 case types.SQLErrorTypeGeneric: 235 return true 236 case types.SQLErrorTypeDuplicatedColumn: 237 return slErr.Code == 1 && strings.Contains(errDescription, "duplicate column") 238 case types.SQLErrorTypeDuplicatedTable: 239 return slErr.Code == 1 && strings.Contains(errDescription, "table") && strings.Contains(errDescription, "already exists") 240 case types.SQLErrorTypeUndefinedTable: 241 return slErr.Code == 1 && strings.Contains(errDescription, "no such table") 242 case types.SQLErrorTypeUndefinedColumn: 243 return slErr.Code == 1 && strings.Contains(errDescription, "table") && strings.Contains(errDescription, "has no column named") 244 case types.SQLErrorTypeInvalidType: 245 // NOT SUPPORTED 246 return false 247 } 248 } 249 250 return false 251 } 252 253 func (sla *SQLiteAdapter) UpsertQuery(table *types.SQLTable, row types.EventDataRow) (types.UpsertDeleteQuery, interface{}, error) { 254 pointers := make([]interface{}, 0) 255 columns := "" 256 insValues := "" 257 updValues := "" 258 pkColumns := "" 259 values := "" 260 var txHash interface{} = nil 261 262 i := 0 263 264 // for each column in table 265 for _, column := range table.Columns { 266 secureColumn := sla.SecureName(column.Name) 267 268 i++ 269 270 // INSERT INTO TABLE (*columns)......... 271 if columns != "" { 272 columns += ", " 273 insValues += ", " 274 values += ", " 275 } 276 columns += secureColumn 277 insValues += "$" + Cleanf("%d", i) 278 279 //find data for column 280 if value, ok := row.RowData[column.Name]; ok { 281 //load hash value 282 if column.Name == sla.Columns.TxHash { 283 txHash = value 284 } 285 286 // column found (not null) 287 // load values 288 pointers = append(pointers, &value) 289 values += fmt.Sprint(value) 290 291 if !column.Primary { 292 // column is no PK 293 // add to update list 294 // INSERT........... ON CONFLICT......DO UPDATE (*updValues) 295 if updValues != "" { 296 updValues += ", " 297 } 298 updValues += secureColumn + " = $" + Cleanf("%d", i) 299 } 300 } else if column.Primary { 301 // column NOT found (is null) and is PK 302 return types.UpsertDeleteQuery{}, nil, fmt.Errorf("error null primary key for column %s", secureColumn) 303 } else { 304 // column NOT found (is null) and is NOT PK 305 pointers = append(pointers, nil) 306 values += "null" 307 } 308 309 if column.Primary { 310 // ON CONFLICT (....values....) 311 if pkColumns != "" { 312 pkColumns += ", " 313 } 314 pkColumns += secureColumn 315 } 316 317 } 318 319 query := Cleanf("INSERT INTO %s (%s) VALUES (%s) ", sla.SecureName(table.Name), columns, insValues) 320 321 if pkColumns != "" { 322 if updValues != "" { 323 query += Cleanf("ON CONFLICT (%s) DO UPDATE SET %s", pkColumns, updValues) 324 } else { 325 query += Cleanf("ON CONFLICT (%s) DO NOTHING", pkColumns) 326 } 327 } 328 query += ";" 329 330 return types.UpsertDeleteQuery{Query: query, Values: values, Pointers: pointers}, txHash, nil 331 } 332 333 func (sla *SQLiteAdapter) DeleteQuery(table *types.SQLTable, row types.EventDataRow) (types.UpsertDeleteQuery, error) { 334 335 pointers := make([]interface{}, 0) 336 columns := "" 337 values := "" 338 i := 0 339 340 // for each column in table 341 for _, column := range table.Columns { 342 343 //only PK for delete 344 if column.Primary { 345 i++ 346 347 secureColumn := sla.SecureName(column.Name) 348 349 // WHERE .......... 350 if columns != "" { 351 columns += " AND " 352 values += ", " 353 } 354 355 columns += Cleanf("%s = $%d", secureColumn, i) 356 357 //find data for column 358 if value, ok := row.RowData[column.Name]; ok { 359 // column found (not null) 360 // load values 361 pointers = append(pointers, &value) 362 values += fmt.Sprint(value) 363 364 } else { 365 // column NOT found (is null) and is PK 366 return types.UpsertDeleteQuery{}, fmt.Errorf("error null primary key for column %s", secureColumn) 367 } 368 } 369 } 370 371 if columns == "" { 372 return types.UpsertDeleteQuery{}, fmt.Errorf("error primary key not found for deletion") 373 } 374 375 query := Cleanf("DELETE FROM %s WHERE %s;", sla.SecureName(table.Name), columns) 376 377 return types.UpsertDeleteQuery{Query: query, Values: values, Pointers: pointers}, nil 378 } 379 380 func (sla *SQLiteAdapter) RestoreDBQuery() string { 381 382 query := Cleanf("SELECT %s, %s, %s, %s, %s FROM %s", 383 sla.Columns.Id, sla.Columns.TableName, sla.Columns.Action, // select 384 sla.Columns.SqlStmt, sla.Columns.SqlValues, // select 385 sla.Tables.Log) 386 387 query += " WHERE strftime('%Y-%m-%d %H:%M:%S'," 388 389 query += Cleanf("%s) AND %s != '%s' AND %s != '%s' <=$1 ORDER BY %s;", 390 sla.Columns.TimeStamp, 391 sla.Columns.TableName, sla.Tables.Block, // where not _vent_block 392 sla.Columns.TableName, sla.Tables.Tx, // where not _vent_tx 393 sla.Columns.Id) 394 395 return query 396 397 } 398 399 func (sla *SQLiteAdapter) CleanDBQueries() types.SQLCleanDBQuery { 400 // Chain info 401 selectChainIDQry := Cleanf(` 402 SELECT 403 COUNT(*) REGISTERS, 404 COALESCE(MAX(%s),'') CHAINID, 405 COALESCE(MAX(%s),'') BVERSION 406 FROM %s;`, 407 sla.Columns.ChainID, sla.Columns.BurrowVersion, 408 sla.Tables.ChainInfo) 409 410 deleteChainIDQry := Cleanf(` 411 DELETE FROM %s;`, 412 sla.Tables.ChainInfo) 413 414 insertChainIDQry := Cleanf(` 415 INSERT INTO %s (%s,%s,%s) VALUES($1,$2,$3)`, 416 sla.Tables.ChainInfo, 417 sla.Columns.ChainID, sla.Columns.BurrowVersion, sla.Columns.Height) 418 419 // Dictionary 420 selectDictionaryQry := Cleanf(` 421 SELECT DISTINCT %s 422 FROM %s 423 WHERE %s 424 NOT IN ('%s','%s','%s');`, 425 sla.Columns.TableName, 426 sla.Tables.Dictionary, 427 sla.Columns.TableName, 428 sla.Tables.Log, sla.Tables.Dictionary, sla.Tables.ChainInfo) 429 430 deleteDictionaryQry := Cleanf(` 431 DELETE FROM %s 432 WHERE %s 433 NOT IN ('%s','%s','%s');`, 434 sla.Tables.Dictionary, 435 sla.Columns.TableName, 436 sla.Tables.Log, sla.Tables.Dictionary, sla.Tables.ChainInfo) 437 438 // log 439 deleteLogQry := Cleanf(` 440 DELETE FROM %s;`, 441 sla.Tables.Log) 442 443 return types.SQLCleanDBQuery{ 444 SelectChainIDQry: selectChainIDQry, 445 DeleteChainIDQry: deleteChainIDQry, 446 InsertChainIDQry: insertChainIDQry, 447 SelectDictionaryQry: selectDictionaryQry, 448 DeleteDictionaryQry: deleteDictionaryQry, 449 DeleteLogQry: deleteLogQry, 450 } 451 } 452 453 func (sla *SQLiteAdapter) DropTableQuery(tableName string) string { 454 // SQLite does not support DROP TABLE CASCADE so this will fail if there are dependent objects 455 return Cleanf(`DROP TABLE IF EXISTS %s;`, sla.SecureName(tableName)) 456 } 457 458 func (sla *SQLiteAdapter) SchemaName(tableName string) string { 459 return secureName(tableName) 460 }