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  }