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