...
Tawesoft Logo

Source file src/tawesoft.co.uk/go/sqlp/sqlite3/sqlite.go

Documentation: src/tawesoft.co.uk/go/sqlp/sqlite3/sqlite.go

     1  package sqlite3
     2  
     3  import (
     4      "database/sql"
     5      "fmt"
     6      "strings"
     7      
     8      "github.com/mattn/go-sqlite3"
     9      "tawesoft.co.uk/go/sqlp"
    10  )
    11  
    12  // Features implements tawesoft.co.uk/go/dev/sqlp features for SQLite3
    13  var Features = sqlp.Features{
    14      EscapeIdentifier: escapeIdentifier, // double quote
    15      EscapeString: escapeString, // single quote
    16      IsUniqueConstraintError: isUniqueConstraintError,
    17  }
    18  
    19  // Returns true iff err is a unique constraint error for SQLite
    20  func isUniqueConstraintError(err error) bool {
    21      if sqliteErr, ok := err.(sqlite3.Error); ok {
    22          return (sqliteErr.Code == sqlite3.ErrConstraint) &&
    23              (sqliteErr.ExtendedCode == sqlite3.ErrConstraintUnique)
    24      }
    25      return false
    26  }
    27  
    28  func escapeIdentifier(s string) (string, error) {
    29      s = strings.ReplaceAll(s, `"`, `""`)
    30      return `"`+s+`"`, nil
    31  }
    32  
    33  func escapeString(s string) (string, error) {
    34      s = strings.ReplaceAll(s, `'`, `''`)
    35      return `'`+s+`'`, nil
    36  }
    37  
    38  // A Collation is a names and a collation-aware string comparison function
    39  type Collation struct {
    40      Name string
    41      Cmp func(string, string) int
    42  }
    43  
    44  // Utf8Collation is a common Collation provided as a convenience
    45  var Utf8Collation = Collation{"utf8", strings.Compare}
    46  
    47  // JournalMode is a type of SQLite journal mode
    48  type JournalMode string
    49  const (
    50      JournalModeDelete   = JournalMode("DELETE")
    51      JournalModeTruncate = JournalMode("TRUNCATE")
    52      JournalModePersist  = JournalMode("PERSIST")
    53      JournalModeMemory   = JournalMode("MEMORY")
    54      JournalModeWAL      = JournalMode("WAL")
    55      JournalModeOff      = JournalMode("OFF")
    56  )
    57  
    58  // Config can be used to configure the SQLite connection
    59  type Config struct {
    60      // ForeignKeys enables/disables the SQLite foreign_keys pragma
    61      ForeignKeys bool
    62      
    63      // SecureDelete enables/disables the SQLite secure_delete pragma
    64      SecureDelete bool
    65      
    66      // JournalMode, defaults to JournalModeWAL
    67      JournalMode JournalMode
    68  }
    69  
    70  // Register creates a new Sqlite3 database driver named DriverName
    71  // (e.g. "sqlite3_withCollations") that implements the given Collations and
    72  // Extensions.
    73  //
    74  // Currently extensions is a placeholder argument and is not implemented.
    75  //
    76  // Must only be called once with a given driverName, otherwise Go will panic.
    77  func Register(driverName string, collations []Collation, extensions []interface{}) {
    78      // see https://godoc.org/github.com/mattn/go-sqlite3#hdr-Connection_Hook
    79      sql.Register(driverName,
    80          &sqlite3.SQLiteDriver{
    81              ConnectHook: func(conn *sqlite3.SQLiteConn) error {
    82                  for _, col := range collations {
    83                      err := conn.RegisterCollation(col.Name, col.Cmp)
    84                      if err != nil {
    85                          return fmt.Errorf("error registering %s database collation %s: %+v",
    86                              driverName, col.Name, err)
    87                      }
    88                  }
    89                  return nil
    90              },
    91      })
    92  }
    93  
    94  func onOff(x bool) string {
    95      if x { return "ON" }
    96      return "OFF"
    97  }
    98  
    99  // Opener returns an Open function with the config argument already applied
   100  func Opener(config Config) (func(string, string) (*sql.DB, error)) {
   101      return func(driverName string, dataSource string) (*sql.DB, error) {
   102          return Open(driverName, dataSource, config)
   103      }
   104  }
   105  
   106  // Open opens/creates an SQLite database with pragmas from config
   107  //
   108  // DriverName should match the name used in Register (or leave blank for default)
   109  //
   110  // DataSource is the sqlite connection string
   111  // e.g. "file:/var/sqlite/example.sql" or ":memory:"
   112  func Open(driverName string, dataSource string, config Config) (*sql.DB, error) {
   113      
   114      if driverName == "" {
   115          driverName = "sqlite3"
   116      }
   117      
   118      db, err := sql.Open(driverName, dataSource)
   119      if err != nil { return nil, err }
   120      
   121      var stmt = `
   122          PRAGMA foreign_keys = `+onOff(config.ForeignKeys)+`;
   123          PRAGMA secure_delete = `+onOff(config.SecureDelete)+`;
   124          PRAGMA journal_mode = `+string(config.JournalMode)+`;
   125      `
   126      _, err = db.Exec(stmt)
   127      if err != nil {
   128          db.Close();
   129          return nil, fmt.Errorf("error setting database PRAGMAs: %+v", err)
   130      }
   131      
   132      return db, nil
   133  }
   134  

View as plain text