diff --git a/README.md b/README.md index 7fdbebe..564e318 100644 --- a/README.md +++ b/README.md @@ -117,8 +117,8 @@ import ( "context" "fmt" - _ "github.com/mattn/go-sqlite3" "github.com/vingarcia/ksql" + "github.com/vingarcia/ksql/adapters/ksqlite3" "github.com/vingarcia/ksql/nullable" ) @@ -152,7 +152,18 @@ var UsersTable = ksql.NewTable("users") func main() { ctx := context.Background() - db, err := ksql.New("sqlite3", "/tmp/hello.sqlite", ksql.Config{ + + // The available adapters are: + // - kpgx.New(ctx, connURL, ksql.Config{}) + // - kmysql.New(ctx, connURL, ksql.Config{}) + // - ksqlserver.New(ctx, connURL, ksql.Config{}) + // - ksqlite3.New(ctx, connURL, ksql.Config{}) + // + // For more detailed examples see: + // - `./examples/all_adapters/all_adapters.go` + // + // In this example we'll use sqlite3: + db, err := ksqlite3.New(ctx, "/tmp/hello.sqlite", ksql.Config{ MaxOpenConns: 1, }) if err != nil { diff --git a/adapters/kmysql/kmysql.go b/adapters/kmysql/kmysql.go new file mode 100644 index 0000000..c712701 --- /dev/null +++ b/adapters/kmysql/kmysql.go @@ -0,0 +1,33 @@ +package kmysql + +import ( + "context" + "database/sql" + + "github.com/vingarcia/ksql" + + // This is imported here so the user don't + // have to worry about it when he uses it. + _ "github.com/go-sql-driver/mysql" +) + +// New instantiates a new KissSQL client using the "mysql" driver +func New( + _ context.Context, + connectionString string, + config ksql.Config, +) (ksql.DB, error) { + config.SetDefaultValues() + + db, err := sql.Open("mysql", connectionString) + if err != nil { + return ksql.DB{}, err + } + if err = db.Ping(); err != nil { + return ksql.DB{}, err + } + + db.SetMaxOpenConns(config.MaxOpenConns) + + return ksql.NewWithAdapter(ksql.SQLAdapter{DB: db}, "mysql") +} diff --git a/kpgx/kpgx.go b/adapters/kpgx/kpgx.go similarity index 100% rename from kpgx/kpgx.go rename to adapters/kpgx/kpgx.go diff --git a/adapters/ksqlite3/ksqlite3.go b/adapters/ksqlite3/ksqlite3.go new file mode 100644 index 0000000..3d66558 --- /dev/null +++ b/adapters/ksqlite3/ksqlite3.go @@ -0,0 +1,33 @@ +package ksqlite3 + +import ( + "context" + "database/sql" + + "github.com/vingarcia/ksql" + + // This is imported here so the user don't + // have to worry about it when he uses it. + _ "github.com/mattn/go-sqlite3" +) + +// New instantiates a new KissSQL client using the "sqlite3" driver +func New( + _ context.Context, + connectionString string, + config ksql.Config, +) (ksql.DB, error) { + config.SetDefaultValues() + + db, err := sql.Open("sqlite3", connectionString) + if err != nil { + return ksql.DB{}, err + } + if err = db.Ping(); err != nil { + return ksql.DB{}, err + } + + db.SetMaxOpenConns(config.MaxOpenConns) + + return ksql.NewWithAdapter(ksql.SQLAdapter{DB: db}, "sqlite3") +} diff --git a/adapters/ksqlserver/ksqlserver.go b/adapters/ksqlserver/ksqlserver.go new file mode 100644 index 0000000..cb7ce66 --- /dev/null +++ b/adapters/ksqlserver/ksqlserver.go @@ -0,0 +1,33 @@ +package ksqlserver + +import ( + "context" + "database/sql" + + "github.com/vingarcia/ksql" + + // This is imported here so the user don't + // have to worry about it when he uses it. + _ "github.com/denisenkom/go-mssqldb" +) + +// New instantiates a new KissSQL client using the "sqlserver" driver +func New( + _ context.Context, + connectionString string, + config ksql.Config, +) (ksql.DB, error) { + config.SetDefaultValues() + + db, err := sql.Open("sqlserver", connectionString) + if err != nil { + return ksql.DB{}, err + } + if err = db.Ping(); err != nil { + return ksql.DB{}, err + } + + db.SetMaxOpenConns(config.MaxOpenConns) + + return ksql.NewWithAdapter(ksql.SQLAdapter{DB: db}, "sqlserver") +} diff --git a/benchmarks/benchmarks_test.go b/benchmarks/benchmarks_test.go index 681a3b6..28eb904 100644 --- a/benchmarks/benchmarks_test.go +++ b/benchmarks/benchmarks_test.go @@ -11,7 +11,7 @@ import ( "github.com/jmoiron/sqlx" _ "github.com/lib/pq" "github.com/vingarcia/ksql" - "github.com/vingarcia/ksql/kpgx" + "github.com/vingarcia/ksql/adapters/kpgx" "gorm.io/driver/postgres" "gorm.io/gorm" ) diff --git a/examples/all_adapters/all_adapters.go b/examples/all_adapters/all_adapters.go new file mode 100644 index 0000000..06b4782 --- /dev/null +++ b/examples/all_adapters/all_adapters.go @@ -0,0 +1,137 @@ +package main + +import ( + "context" + "fmt" + "log" + "os" + + "github.com/vingarcia/ksql" + "github.com/vingarcia/ksql/adapters/kmysql" + "github.com/vingarcia/ksql/adapters/kpgx" + "github.com/vingarcia/ksql/adapters/ksqlite3" + "github.com/vingarcia/ksql/adapters/ksqlserver" +) + +// User ... +type User struct { + ID int `ksql:"id"` + Name string `ksql:"name"` + Age int `ksql:"age"` + + // This field will be saved as JSON in the database + Address Address `ksql:"address,json"` +} + +// Address ... +type Address struct { + State string `json:"state"` + City string `json:"city"` +} + +// UsersTable informs ksql the name of the table and that it can +// use the default value for the primary key column name: "id" +var UsersTable = ksql.NewTable("users") + +func main() { + ctx := context.Background() + + var db ksql.Provider + var err error + + // This switch lists all the supported adapters, and how to use them: + switch os.Getenv("KSQL_ADAPTER") { + case "postgres", "pgx": + db, err = kpgx.New(ctx, "host=localhost port=5432 user=postgres password=postgres dbname=ksql sslmode=disable", ksql.Config{ + MaxOpenConns: 1, + }) + if err != nil { + log.Fatalf("unable to open database, reason: %s", err) + } + err = db.Exec(ctx, `CREATE TABLE users ( + id serial PRIMARY KEY, + age INT, + name VARCHAR(50), + address jsonb + )`) + if err != nil { + log.Fatalf("unable to create users table: %s", err) + } + case "mysql": + db, err = kmysql.New(ctx, "root:mysql@(127.0.0.1:3306)/ksql?timeout=30s", ksql.Config{ + MaxOpenConns: 1, + }) + if err != nil { + log.Fatalf("unable to open database: %s", err) + } + err = db.Exec(ctx, `CREATE TABLE users ( + id INT AUTO_INCREMENT PRIMARY KEY, + age INT, + name VARCHAR(50), + address JSON + )`) + if err != nil { + log.Fatalf("unable to create users table: %s", err) + } + case "sqlserver": + db, err = ksqlserver.New(ctx, "sqlserver://sa:Sqls3rv3r@127.0.0.1:1433?databaseName=ksql", ksql.Config{ + MaxOpenConns: 1, + }) + if err != nil { + log.Fatalf("unable to open database: %s", err) + } + + // In the example below NVARCHAR is the format + // we are using for storing JSON: + err = db.Exec(ctx, `CREATE TABLE users ( + id INT IDENTITY(1,1) PRIMARY KEY, + age INT, + name VARCHAR(50), + address NVARCHAR(4000) + )`) + if err != nil { + log.Fatalf("unable to create users table: %s", err) + } + case "", "sqlite3": + db, err = ksqlite3.New(ctx, "/tmp/ksql.sqlite", ksql.Config{ + MaxOpenConns: 1, + }) + if err != nil { + log.Fatalf("unable to open database: %s", err) + } + + // In the definition below, please note that BLOB is + // the only type we can use in sqlite for storing JSON. + err = db.Exec(ctx, `CREATE TABLE IF NOT EXISTS users ( + id INTEGER PRIMARY KEY, + age INTEGER, + name TEXT, + address BLOB + )`) + if err != nil { + log.Fatalf("unable to create users table: %s", err) + } + } + + insertUser := &User{ + Name: "Some Username", + Age: 22, + Address: Address{ + State: "MG", + }, + } + err = db.Insert(ctx, UsersTable, &insertUser) + if err != nil { + log.Fatalf("error inserting user: %s", err) + } + + var user User + err = db.QueryOne(ctx, &user, `SELECT name FROM users WHERE name = 'Some Username'`) + if err != nil { + log.Fatalf("error querying for user: %s", err) + } + + // Loaded from the database using LastInsertID or equivalent: + fmt.Printf("insertedUserID: %d\n", insertUser.ID) + fmt.Printf("ID loaded with QueryOne: %d\n", user.ID) +} diff --git a/examples/crud/crud.go b/examples/crud/crud.go index 6ef5df5..c87fad2 100644 --- a/examples/crud/crud.go +++ b/examples/crud/crud.go @@ -4,8 +4,8 @@ import ( "context" "fmt" - _ "github.com/mattn/go-sqlite3" "github.com/vingarcia/ksql" + "github.com/vingarcia/ksql/adapters/ksqlite3" "github.com/vingarcia/ksql/nullable" ) @@ -39,7 +39,18 @@ var UsersTable = ksql.NewTable("users") func main() { ctx := context.Background() - db, err := ksql.New("sqlite3", "/tmp/hello.sqlite", ksql.Config{ + + // The available adapters are: + // - kpgx.New(ctx, connURL, ksql.Config{}) + // - kmysql.New(ctx, connURL, ksql.Config{}) + // - ksqlserver.New(ctx, connURL, ksql.Config{}) + // - ksqlite3.New(ctx, connURL, ksql.Config{}) + // + // For more detailed examples see: + // - `./examples/all_adapters/all_adapters.go` + // + // In this example we'll use sqlite3: + db, err := ksqlite3.New(ctx, "/tmp/hello.sqlite", ksql.Config{ MaxOpenConns: 1, }) if err != nil { diff --git a/ksql.go b/ksql.go index 7ce7bdc..b7f4b45 100644 --- a/ksql.go +++ b/ksql.go @@ -76,6 +76,8 @@ type Config struct { MaxOpenConns int } +// SetDefaultValues should be called by all adapters +// to set the default config values if unset. func (c *Config) SetDefaultValues() { if c.MaxOpenConns == 0 { c.MaxOpenConns = 1