-
Notifications
You must be signed in to change notification settings - Fork 6
Go Client
go get github.com/tezc/resql/goresql/
Resql provides minimal API for clients. A quick example:
package main
import (
"fmt"
resql "github.com/tezc/resql/goresql"
)
func main() {
s, _ := resql.Create(&resql.Config{})
s.PutStatement("SELECT 'Hello World!';")
rs, _ := s.Execute(true)
var col resql.NullString
rs.Row().Read(&col)
fmt.Println(col.String)
s.Shutdown()
}
ClientName
: Unique client name, if not provided a random string will be assigned.
ClusterName
: Cluster name must match with configured cluster name on servers. This is just a simple security check to prevent clients from connecting to the wrong cluster.
TimeoutMillis
: Timeout for any operation of the client. This timeout applies to connection/reconnection time and query execution time.
Urls
: Server urls. It is okay to give just one URL. Client will get other nodes' urls when it's connected to one node.
OutgoingAddr
: Set outgoing address of the client, null for automatic address selection.
OutgoingPort
: Set outgoing port of the client, null for automatic port selection.
package main
import (
"fmt"
resql "github.com/tezc/resql/goresql"
"os"
)
func main() {
s, err := resql.Create(&resql.Config{
ClusterName: "cluster",
ClientName: "goclient",
TimeoutMillis: 5000,
Urls: []string{"tcp://127.0.0.1:7600"},
})
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
s.PutStatement("SELECT 'Hello World!';")
rs, err := s.Execute(false)
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
var col resql.NullString
rs.Row().Read(&col)
fmt.Println(col.String)
err = s.Shutdown()
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
}
Client has one method to define operations: put()
. This method will put your operations into a buffer, you may call it more than once. put() will batch your operations, execute() will send it to the server and wait for a response.
package main
import (
"fmt"
resql "github.com/tezc/resql/goresql"
"os"
)
func main() {
s, err := resql.Create(&resql.Config{})
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
s.PutStatement("CREATE TABLE test (key TEXT, value TEXT);")
_, err = s.Execute(false)
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
// Cleanup
s.PutStatement("DROP TABLE test;")
_, err = s.Execute(false)
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
err = s.Shutdown()
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
}
As you can see, execute has a boolean parameter: Execute(readonly bool)
If your operation is read-only, e.g SELECT, set this parameter to true. This is an optimization. Readonly operations don't change the state of the database, so no need to write these operations to WAL file. If you set read-only to true even your operation is not, the server will reject your operation and you'll catch that error while developing your application.
package main
import (
"fmt"
resql "github.com/tezc/resql/goresql"
"os"
)
func main() {
s, err := resql.Create(&resql.Config{})
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
s.PutStatement("CREATE TABLE test (key TEXT, value TEXT);")
_, err = s.Execute(false)
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
// Option - 1, with parameter name
s.PutStatement("INSERT INTO test VALUES(:name,:lastname);")
s.BindParam(":name", "jane")
s.BindParam(":lastname", "doe")
_, err = s.Execute(false)
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
// Option - 2, with index
s.PutStatement("INSERT INTO test VALUES(?,?);")
s.BindIndex(0, "jane")
s.BindIndex(1, "doe")
_, err = s.Execute(false)
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
// Cleanup
s.PutStatement("DROP TABLE test;")
_, _ = s.Execute(false)
err = s.Shutdown()
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
}
package main
import (
"fmt"
resql "github.com/tezc/resql/goresql"
"os"
)
func main() {
s, err := resql.Create(&resql.Config{})
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
s.PutStatement("DROP TABLE IF EXISTS test;")
s.PutStatement("CREATE TABLE test (key TEXT, value TEXT);")
_, err = s.Execute(false)
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
s.PutStatement("INSERT INTO test VALUES('jane','doe');")
s.PutStatement("INSERT INTO test VALUES('jack','doe');")
s.PutStatement("INSERT INTO test VALUES('joe','doe');")
_, err = s.Execute(false)
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
s.PutStatement("SELECT * FROM test")
rs, err := s.Execute(true)
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
for r := rs.Row(); r != nil; r = rs.Row() {
var name, lastname resql.NullString
err = r.Read(&name, &lastname)
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
fmt.Println(name.String, lastname.String)
}
// Cleanup
s.PutStatement("DROP TABLE test;")
_, _ = s.Execute(false)
err = s.Shutdown()
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
}
If you're going to execute a statement many times, prepare the statement and use it for better performance. Prepared statements are kept on servers. They can be deleted by calling Delete()
method of the client or they will be freed automatically when the client closes the connection gracefully.
package main
import (
"fmt"
resql "github.com/tezc/resql/goresql"
"os"
)
func main() {
s, err := resql.Create(&resql.Config{})
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
s.PutStatement("DROP TABLE IF EXISTS test;")
s.PutStatement("CREATE TABLE test (key TEXT, value TEXT);")
_, err = s.Execute(false)
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
// Option-1, using indexes
p, err := s.Prepare("INSERT INTO test VALUES(?,?)");
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
s.PutPrepared(p)
s.BindIndex(0, "jane")
s.BindIndex(1, "doe")
_, err = s.Execute(false)
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
// Clean-up if you're not going to use prepared statement again.
err = s.Delete(p)
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
// Option-2, using parameter names
p, err = s.Prepare("INSERT INTO test VALUES(:name,:lastname)");
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
s.PutPrepared(p)
s.BindParam(":name", "jane")
s.BindParam(":lastname", "doe")
_, err = s.Execute(false)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
// Clean-up if you're not going to use prepared statement again.
err = s.Delete(p)
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
// Cleanup
s.PutStatement("DROP TABLE test;")
_, _ = s.Execute(false)
err = s.Shutdown()
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
}
If you Put()
multiple operations and call Execute()
, these operations will be processed atomically. Either all will succeed or fail. You can even combine INSERT's with SELECT's.
package main
import (
"fmt"
resql "github.com/tezc/resql/goresql"
"os"
)
func main() {
s, err := resql.Create(&resql.Config{})
if err != nil {
fmt.Println(err)
os.Exit(1)
}
s.PutStatement("DROP TABLE IF EXISTS test;")
s.PutStatement("CREATE TABLE test (key TEXT, value TEXT);")
s.PutStatement("INSERT INTO test VALUES('mykey', 1000);")
_, err = s.Execute(false)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
// Demo for getAndIncrement atomically.
s.PutStatement("SELECT * FROM test WHERE key = 'mykey';");
s.PutStatement("UPDATE test SET value = value + 1 WHERE key = 'mykey'");
s.PutStatement("SELECT * FROM test WHERE key = 'mykey';");
rs, err := s.Execute(false)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
// rs has three result sets, each corresponds to operations
// that we added into the batch.
// First operation was SELECT
for r := rs.Row(); r != nil; r = rs.Row() {
val, err := r.GetColumn("value")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Println("Value was :", val)
}
// Advance to the next result set which is for UPDATE.
rs.NextResultSet()
fmt.Println("Changes :", rs.LinesChanged())
// Advance to the next result set which is for SELECT again.
rs.NextResultSet()
for r := rs.Row(); r != nil; r = rs.Row() {
val, err := r.GetColumn("value")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Println("Value is :", val)
}
// Cleanup
s.PutStatement("DROP TABLE test;")
_, _ = s.Execute(false)
err = s.Shutdown()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
- Get Started
- Administration
- Clients