gorqlite

package module
v0.0.0-...-8d9f875 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 27, 2024 License: MIT Imports: 12 Imported by: 45

README

gorqlite - a Go client for rqlite

Circle CI

gorqlite is a Go client for rqlite that provides easy-to-use abstractions for working with the rqlite API.

It provides an idiomatic API specialized for rqlite and a database/sql driver (read below for more information on it). The main API provides similar semantics to database/sql, such as Open(), Query() and QueryOne(), Next()/Scan()/Map(), Write() and WriteOne(), etc.

Status

This client library is used in production by various groups, including Replicated. Check out their blog post on their use of rqlite.

Features

  • Abstracts the rqlite HTTP API interaction - the POSTs, JSON handling, etc. You submit your SQL and get back an iterator with familiar database/sql semantics (Next(), Scan(), etc.) or a map[column name as string]interface{}.
  • Timings and other metadata (e.g., num rows affected, last insert ID, etc.) are conveniently available and parsed into appropriate types.
  • A connection abstraction allows gorqlite to discover and remember the rqlite leader. gorqlite will automatically try other peers if the leader is lost, enabling fault-tolerant API operations.
  • Timeout can be set on a per-Connection basis to accommodate those with far-flung empires.
  • Use familiar database URL connection strings to connection, optionally including rqlite authentication and/or specific rqlite consistency levels.
  • Only a single node needs to be specified in the connection. By default gorqlite will talk to that node and figure out the rest of the cluster from its redirects and status API. This is known as Cluster Discovery.
  • Depending on your deployment, you may wish to disable Cluster Discovery. If you do disable it only the provided URL will be used to communicate with the API instead of discovering the leader and peers and retrying failed requests with different peers. To disable Cluster Discovery add disableClusterDiscovery=true as a URL Query Parameter when connecting to rqlite e.g. http://localhost:14001?disableClusterDiscovery=true.
    • This is helpful, for example, when using a Kubernetes service to handle the load balancing of the requests across healthy nodes. In such a setup Kubernetes takes care of finding a node to communicate with.
  • Support for several rqlite-specific operations:
    • Leader() and Peers() to examine the cluster.
    • SetConsistencyLevel() can be called at any time on a connection to change the consistency level for future operations.
    • Timing can be referenced on a per-result basis to retrieve the timings information for executed operations as float64, per the rqlite API.
  • Trace(io.Writer)/Trace(nil) can be used to turn on/off debugging information on everything gorqlite does to a io.Writer of your choice.
  • No external dependencies. Uses only standard library functions.

Install

go get github.com/rqlite/gorqlite

Examples

// These URLs are just generic database URLs, not rqlite API URLs,
// so you don't need to worry about the various rqlite paths ("/db/query"), etc.
// just supply the base URL and not "db" or anything after it.

// Yes, you need the http or https.

// No, you cannot specify a database name in the URL (this is sqlite, after all).

conn, err := gorqlite.Open("http://") // connects to localhost on 4001 without auth
conn, err := gorqlite.Open("https://") // same but with https
conn, err := gorqlite.Open("https://localhost:4001/") // same only explicitly

// With authentication:
conn, err := gorqlite.Open("https://mary:secret2@localhost:4001/")
// different server, setting the rqlite consistency level
conn, err := gorqlite.Open("https://mary:secret2@server1.example.com:4001/?level=none")
// same without auth, setting the rqlite consistency level
conn, err := gorqlite.Open("https://server2.example.com:4001/?level=weak")
// different port, setting the rqlite consistency level and timeout
conn, err := gorqlite.Open("https://localhost:2265/?level=strong&timeout=30")
// different port, disabling cluster discovery in the client
conn, err := gorqlite.Open("https://localhost:2265/?disableClusterDiscovery=true")

// Change our minds
conn.SetConsistencyLevel("none")
conn.SetConsistencyLevel("weak")
conn.SetConsistencyLevel("strong")

// Simulate database/sql Prepare()
statements := make ([]string,0)
pattern := "INSERT INTO secret_agents(id, hero_name, abbrev) VALUES (%d, '%s', '%3s')"
statements = append(statements,fmt.Sprintf(pattern,125718,"Speed Gibson","Speed"))
statements = append(statements,fmt.Sprintf(pattern,209166,"Clint Barlow","Clint"))
statements = append(statements,fmt.Sprintf(pattern,44107,"Barney Dunlap","Barney"))
results, err := conn.Write(statements)

// now we have an array of []WriteResult 

for n, v := range WriteResult {
	fmt.Printf("for result %d, %d rows were affected\n",n,v.RowsAffected)
	if v.Err != nil {
		fmt.Printf("   we have this error: %s\n",v.Err.Error())
	}
}

// or if we have an auto_increment column
res, err := conn.WriteOne("INSERT INTO foo (name) values ('bar')")
fmt.Printf("last insert id was %d\n",res.LastInsertID)

// just like database/sql, you're required to Next() before any Scan() or Map()

// note that rqlite is only going to send JSON types - see the encoding/json docs
// which means all numbers are float64s.  gorqlite will convert to int64s for you
// because it is convenient but other formats you will have to handle yourself

var id int64
var name string
rows, err := conn.QueryOne("select id, name from secret_agents where id > 500")
fmt.Printf("query returned %d rows\n",rows.NumRows)
for rows.Next() {
	err := rows.Scan(&id, &name)
	fmt.Printf("this is row number %d\n",rows.RowNumber)
	fmt.Printf("there are %d rows overall%d\n",rows.NumRows)
}

// just like WriteOne()/Write(), QueryOne() takes a single statement,
// while Query() takes a []string.  You'd only use Query() if you wanted
// to transactionally group a bunch of queries, and then you'd get back
// a []QueryResult

// alternatively, use Next()/Map()

for rows.Next() {
	m, err := rows.Map()
	// m is now a map[column name as string]interface{}
	id := m["name"].(float64) // the only json number type
	name := m["name"].(string)
}

// get rqlite cluster information
leader, err := conn.Leader()
// err could be set if the cluster wasn't answering, etc.
fmt.Println("current leader is"leader)
peers, err := conn.Peers()
for n, p := range peers {
	fmt.Printf("cluster peer %d: %s\n",n,p)
}

// turn on debug tracing to the io.Writer of your choice.
// gorqlite will verbosely write very granular debug information.
// this is similar to perl's DBI->Trace() facility.
// note that this is done at the package level, not the connection
// level, so you can debug Open() etc. if need be.

f, err := os.OpenFile("/tmp/deep_insights.log",OS_RDWR|os.O_CREATE|os.O_APPEND,0644)
gorqlite.TraceOn(f)

// change my mind and watch the trace
gorqlite.TraceOn(os.Stderr)

// turn off
gorqlite.TraceOff()

// using parameterized statements
wr, err := conn.WriteParameterized(
	[]gorqlite.ParameterizedStatement{
		{
			Query:     "INSERT INTO secret_agents(id, name, secret) VALUES(?, ?, ?)",
			Arguments: []interface{}{7, "James Bond", "not-a-secret"},
		},
	},
)
seq, err := conn.QueueParameterized(
	[]gorqlite.ParameterizedStatement{
		{
			Query:     "INSERT INTO secret_agents(id, name, secret) VALUES(?, ?, ?)",
			Arguments: []interface{}{7, "James Bond", "not-a-secret"},
		},
	},
)
qr, err := conn.QueryParameterized(
	[]gorqlite.ParameterizedStatement{
		{
			Query:     "SELECT id, name from secret_agents where id > ?",
			Arguments: []interface{}{3},
		},
	},
)

// alternatively
wr, err := conn.WriteOneParameterized(
	gorqlite.ParameterizedStatement{
		Query:     "INSERT INTO secret_agents(id, name, secret) VALUES(?, ?, ?)",
		Arguments: []interface{}{7, "James Bond", "not-a-secret"},
	},
)
seq, err := conn.QueueOneParameterized(
	gorqlite.ParameterizedStatement{
		Query:     "INSERT INTO secret_agents(id, name, secret) VALUES(?, ?, ?)",
		Arguments: []interface{}{7, "James Bond", "not-a-secret"},
	},
)
qr, err := conn.QueryOneParameterized(
	gorqlite.ParameterizedStatement{
		Query:     "SELECT id, name from secret_agents where id > ?",
		Arguments: []interface{}{3},
	},
)

// using nullable types
var name gorqlite.NullString
rows, err := conn.QueryOne("select name from secret_agents where id = 7")
for rows.Next() {
	err := rows.Scan(&name)
}
if name.Valid {
	// use name.String
} else {
	// NULL value
}

Queued Writes

The client does support Queued Writes. Instead of calling the Write() functions, call the queueing versions instead.

var seq int64
var err error

seq, err = conn.QueueOne("CREATE TABLE " + testTableName() + " (id integer, name text)")
seq, err = conn.Queue(...)

Important Notes

If you use access control, any user connecting will need the status permission in addition to any other needed permission. This is so gorqlite can query the cluster and try other peers if the the connection to the Leader is lost.

rqlite does not support iterative fetching from the DBMS, so your query will put all results into memory immediately. If you are working with large datasets on small systems, your experience may be sub-optimal.

Driver for database/sql

It is recommended that you use the main gorqlite-specific API when possible. However, if you need to use gorqlite with database/sql, you can import github.com/rqlite/gorqlite/stdlib. For example:

package main

import (
	"database/sql"

	_ "github.com/rqlite/gorqlite/stdlib"
)

func main() {
	db, err := sql.Open("rqlite", "http://")
	if err != nil {
		panic(err)
	}
	_, err := db.Exec("CREATE TABLE users (id INTEGER, name TEXT)")
	if err != nil {
		panic(err)
	}
}

The following limitations apply when using the rqlite database/sql driver:

  • rqlite supports transactions, but only in a single batch. You can group many statements into a single transaction, but you must submit them as a single unit. You cannot start a transaction, send some statements, come back later and submit some more, and then later commit.

  • As a consequence, there is no rollback.

  • The statement parsing/preparation API is not exposed at the SQL layer by sqlite, and hence it's not exposed by rqlite. What this means is that there's no way to prepare a statement ("INSERT INTO superheroes (?,?)") and then later bind executions to it. (In case you're wondering, yes, it would be possible for gorqlite to include a copy of sqlite3 and use its engine, but the sqlite C call to sqlite3_prepare_v2() will fail because a local sqlite3 won't know your DB's schemas and the sqlite3_prepare_v2() call validates the statement against the schema. We could open the local sqlite .db file maintained by rqlite and validate against that, but there is no way to make a consistency guarantee between time of preparation and execution, especially since the user can mix DDL and DML in a single transaction).

  • Therefore, Begin(), Rollback(), Commit(), and Prepare() are all no-ops that return no errors but don't do anything.

TODO

HTTPS has not been tested yet. In theory, HTTP should work just fine because it's just a URL to gorqlite, but it has not been tested.

Several features may be added in the future:

  • support for the backup API

  • support for expvars (debugvars)

  • perhaps deleting a node (via the remove API)

  • since connections are just config info, it should be possible to clone them, which would save startup time for new connections. This needs to be thread-safe, though, since a connection at any time could be updating its cluster info, etc.

  • gorqlite always talks to the Leader (unless it's searching for a Leader). In theory, you talk to a Follower in "none" consistency mode, but this adds a surprising amount of complexity. gorqlite has to take note of the URL you call it with, then try to match that to the cluster's list to mark it as the "default" URL. Then whenever it wants to do an operation, it has to carefully sort the peer list based on the consistency model, if the default URL has gone away, etc. And when cluster info is rebuilt, it has to track the default URL through that.

Other Design Notes

In standard database/sql drivers, Open() doesn't actually do anything. You get a "connection" that doesn't connect until you Ping() or send actual work. In gorqlite's case, it needs to connect to get cluster information, so this is done immediately and automatically open calling Open(). By the time Open() is returned, gorqlite has full cluster info.

Unlike database/sql connections, a gorqlite connection in the main gorqlite-specific API is not thread-safe. However, a gorqlite database/sql connection through package stdlib is thread-safe.

Close() will set a flag so if you try to use the connection afterwards, it will fail. But otherwise, you can merrily let your connections be garbage-collected with no harm, because they're just configuration tracking bundles and everything to the rqlite cluster is stateless. Indeed, the true reason that Close() exists is the author's feeling that if you open something, you should be able to close it. So why not GetConnection() then instead of Open()? Or GetClusterConfigurationTrackingObject()? I don't know. Fork me.

Leader() and Peers() will both cause gorqlite to reverify its cluster information before return. Note that if you call Leader() and then Peers() and something changes in between, it's possible to get inconsistent answers.

Since "weak" consistency is the default rqlite level, it is the default level for the client as well. The user can change this at will (either in the connection string or via SetConsistencyLevel(), and then the new level will apply to all future calls).

Tests

go test is used for testing. A running cluster is required.

By default, gorqlite uses this config for testing:

database URL : http://localhost:4001
table name   : gorqlite_test

Also, the tests in package stdlib use the same config but a table name of gorqlite_test_stdlib.

These can overridden using the environment variables:

GORQLITE_TEST_URL=https://somewhere.example.com:1234
GORQLITE_TEST_URL=https//user:password@somewhere.example.com:1234
etc.

GORQLITE_TEST_TABLE=some_other_table
GORQLITE_TEST_TABLE_STDLIB=some_other_table

Pronunciation

rqlite is supposed to be pronounced "ree qwell lite" (though many people pronounce is "R Q lite" too). So you could pronounce gorqlite as either "go ree kwell lite" or "gork lite". The Klingon in me prefers the latter. Really, isn't rqlite just the kind of battle-hardened, lean and mean system Klingons would use? Qapla'!

Documentation

Overview

Package gorqlite provieds a database/sql-like driver for rqlite, the distributed consistent sqlite.

Copyright (c)2016 andrew fabbro (andrew@fabbro.org)

See LICENSE.md for license. tl;dr: MIT. Conveniently, the same license as rqlite.

Project home page: https://github.com/raindo308/gorqlite

Learn more about rqlite at: https://github.com/rqlite/rqlite

Index

Constants

View Source
const (
	// ConsistencyLevelNone provides no consistency to other nodes.
	ConsistencyLevelNone consistencyLevel = iota
	// ConsistencyLevelWeak provides a weak consistency that guarantees the
	// queries are sent to the leader.
	ConsistencyLevelWeak
	// ConsistencyLevelStrong provides a strong consistency and guarantees
	// that queries are sent and received by other nodes.
	ConsistencyLevelStrong
)

Variables

View Source
var (
	// ErrClosed indicates that client connection was closed
	ErrClosed = errors.New("gorqlite: connection is closed")
)

Functions

func TraceOff

func TraceOff()

TraceOff turns off tracing output. Once you call TraceOff(), no further info is sent to the io.Writer, unless it is TraceOn'd again.

func TraceOn

func TraceOn(w io.Writer)

TraceOn turns on tracing output to the io.Writer of your choice.

Trace output is very detailed and verbose, as you might expect.

Normally, you should run with tracing off, as it makes absolutely no concession to performance and is intended for debugging/dev use.

Types

type Connection

type Connection struct {
	ID string //   generated in init()
	// contains filtered or unexported fields
}

Connection provides the connection abstraction. Note that since rqlite is stateless, there really is no "connection". However, this type holds information such as the current leader, peers, connection string to build URLs, etc.

Connections are assigned a "connection ID" which is a pseudo-UUID for connection identification in trace output only. This helps sort out what's going on if you have multiple connections going at once. It's generated using a non-standards-or-anything-else-compliant function that uses crypto/rand to generate 16 random bytes.

Note that the Connection objection holds info on all peers, gathered at time of Open() from the node specified.

func Open

func Open(connURL string) (*Connection, error)

Open creates and returns a "connection" to rqlite.

Since rqlite is stateless, there is no actual connection. Open() creates and initializes a gorqlite Connection type, which represents various config information.

The URL should be in a form like this:

http://localhost:4001

http://     default, no auth, localhost:4001
https://    default, no auth, localhost:4001, using https

http://localhost:1234
http://mary:secret2@localhost:1234

https://mary:secret2@somewhere.example.com:1234
https://mary:secret2@somewhere.example.com // will use 4001

func (*Connection) Close

func (conn *Connection) Close()

Close will mark the connection as closed. It is safe to be called multiple times.

func (*Connection) ConsistencyLevel

func (conn *Connection) ConsistencyLevel() (string, error)

ConsistencyLevel tells the current consistency level

func (*Connection) Leader

func (conn *Connection) Leader() (string, error)

Leader tells the current leader of the cluster

func (*Connection) Peers

func (conn *Connection) Peers() ([]string, error)

Peers tells the current peers of the cluster

func (*Connection) Query

func (conn *Connection) Query(sqlStatements []string) (results []QueryResult, err error)

Query is used to perform SELECT operations in the database. It takes an array of SQL statements and executes them in a single transaction, returning an array of QueryResult.

Query uses context.Background() internally; to specify the context, use QueryContext.

func (*Connection) QueryContext

func (conn *Connection) QueryContext(ctx context.Context, sqlStatements []string) (results []QueryResult, err error)

QueryContext is used to perform SELECT operations in the database. It takes an array of SQL statements and executes them in a single transaction, returning an array of QueryResult.

func (*Connection) QueryOne

func (conn *Connection) QueryOne(sqlStatement string) (qr QueryResult, err error)

QueryOne wraps Query into a single-statement method.

QueryOne uses context.Background() internally; to specify the context, use QueryOneContext.

func (*Connection) QueryOneContext

func (conn *Connection) QueryOneContext(ctx context.Context, sqlStatement string) (qr QueryResult, err error)

QueryOneContext wraps Query into a single-statement method.

func (*Connection) QueryOneParameterized

func (conn *Connection) QueryOneParameterized(statement ParameterizedStatement) (qr QueryResult, err error)

QueryOneParameterized wraps QueryParameterized into a single-statement method.

QueryOneParameterized uses context.Background() internally; to specify the context, use QueryOneParameterizedContext.

func (*Connection) QueryOneParameterizedContext

func (conn *Connection) QueryOneParameterizedContext(ctx context.Context, statement ParameterizedStatement) (qr QueryResult, err error)

QueryOneParameterizedContext wraps QueryParameterizedContext into a single-statement method.

func (*Connection) QueryParameterized

func (conn *Connection) QueryParameterized(sqlStatements []ParameterizedStatement) (results []QueryResult, err error)

QueryParameterized is used to perform SELECT operations in the database.

It takes an array of parameterized SQL statements and executes them in a single transaction, returning an array of QueryResult vars.

QueryParameterized uses context.Background() internally; to specify the context, use QueryParameterizedContext.

func (*Connection) QueryParameterizedContext

func (conn *Connection) QueryParameterizedContext(ctx context.Context, sqlStatements []ParameterizedStatement) (results []QueryResult, err error)

QueryParameterizedContext is used to perform SELECT operations in the database.

It takes an array of parameterized SQL statements and executes them in a single transaction, returning an array of QueryResult vars.

func (*Connection) Queue

func (conn *Connection) Queue(sqlStatements []string) (seq int64, err error)

Queue is used to perform asynchronous writes to the rqlite database as defined in the documentation: https://github.com/rqlite/rqlite/blob/master/DOC/QUEUED_WRITES.md

Queue uses context.Background() internally; to specify the context, use QueueContext. To use Queue with parameterized queries, use QueueParameterized.

func (*Connection) QueueContext

func (conn *Connection) QueueContext(ctx context.Context, sqlStatements []string) (seq int64, err error)

QueueContext is used to perform asynchronous writes to the rqlite database as defined in the documentation: https://github.com/rqlite/rqlite/blob/master/DOC/QUEUED_WRITES.md

To use QueueContext with parameterized queries, use QueueParameterizedContext.

func (*Connection) QueueOne

func (conn *Connection) QueueOne(sqlStatement string) (seq int64, err error)

QueueOne is a convenience method that wraps Queue into a single-statement.

QueueOne uses context.Background() internally; to specify the context, use QueueOneContext.

func (*Connection) QueueOneContext

func (conn *Connection) QueueOneContext(ctx context.Context, sqlStatement string) (seq int64, err error)

QueueOneContext is a convenience method that wraps QueueContext into a single-statement

func (*Connection) QueueOneParameterized

func (conn *Connection) QueueOneParameterized(statement ParameterizedStatement) (seq int64, err error)

QueueOneParameterized is a convenience method that wraps QueueParameterized into a single-statement method.

QueueOneParameterized uses context.Background() internally; to specify the context, use QueueOneParameterizedContext.

func (*Connection) QueueOneParameterizedContext

func (conn *Connection) QueueOneParameterizedContext(ctx context.Context, statement ParameterizedStatement) (seq int64, err error)

QueueOneParameterizedContext is a convenience method that wraps QueueParameterizedContext() into a single-statement method.

func (*Connection) QueueParameterized

func (conn *Connection) QueueParameterized(sqlStatements []ParameterizedStatement) (seq int64, err error)

QueueParameterized is used to perform asynchronous writes with parameterized queries to the rqlite database as defined in the documentation: https://github.com/rqlite/rqlite/blob/master/DOC/QUEUED_WRITES.md

QueueParameterized uses context.Background() internally; to specify the context, use QueueParameterizedContext.

func (*Connection) QueueParameterizedContext

func (conn *Connection) QueueParameterizedContext(ctx context.Context, sqlStatements []ParameterizedStatement) (seq int64, err error)

QueueParameterizedContext is used to perform asynchronous writes with parameterized queries to the rqlite database as defined in the documentation: https://github.com/rqlite/rqlite/blob/master/DOC/QUEUED_WRITES.md

func (*Connection) Request

func (conn *Connection) Request(sqlStatements []string) (results []RequestResult, err error)

Request is used to access Unified Endpoint to send read and writes requests in one operation.

func (*Connection) RequestContext

func (conn *Connection) RequestContext(ctx context.Context, sqlStatements []string) (results []RequestResult, err error)

RequestContext is used to access Unified Endpoint to send read and writes requests in one operation.

To use RequestContext with parameterized queries, use RequestParameterizedContext.

func (*Connection) RequestParameterized

func (conn *Connection) RequestParameterized(sqlStatements []ParameterizedStatement) (results []RequestResult, err error)

RequestParameterized returns an error if one is encountered during its operation. If it's something like a call to the rqlite API, then it'll return that error. If one statement out of several has an error, you can look at the individual statement's Err for more info.

RequestParameterized uses context.Background() internally; to specify the context, use RequestParameterizedContext.

func (*Connection) RequestParameterizedContext

func (conn *Connection) RequestParameterizedContext(ctx context.Context, sqlStatements []ParameterizedStatement) (results []RequestResult, err error)

RequestParameterizedContext returns an error if one is encountered during its operation. If it's something like a call to the rqlite API, then it'll return that error. If one statement out of several has an error, you can look at the individual statement's Err for more info.

func (*Connection) SetConsistencyLevel

func (conn *Connection) SetConsistencyLevel(levelDesired consistencyLevel) error

func (*Connection) SetExecutionWithTransaction

func (conn *Connection) SetExecutionWithTransaction(state bool) error

func (*Connection) Write

func (conn *Connection) Write(sqlStatements []string) (results []WriteResult, err error)

Write is used to perform DDL/DML in the database synchronously without parameters.

Write uses context.Background() internally; to specify the context, use WriteContext. To use Write with parameterized queries, use WriteParameterized.

func (*Connection) WriteContext

func (conn *Connection) WriteContext(ctx context.Context, sqlStatements []string) (results []WriteResult, err error)

WriteContext is used to perform DDL/DML in the database synchronously without parameters.

To use WriteContext with parameterized queries, use WriteParameterizedContext.

func (*Connection) WriteOne

func (conn *Connection) WriteOne(sqlStatement string) (wr WriteResult, err error)

WriteOne wraps Write() into a single-statement method.

WriteOne uses context.Background() internally; to specify the context, use WriteOneContext.

func (*Connection) WriteOneContext

func (conn *Connection) WriteOneContext(ctx context.Context, sqlStatement string) (wr WriteResult, err error)

WriteOneContext wraps WriteContext() into a single-statement

func (*Connection) WriteOneParameterized

func (conn *Connection) WriteOneParameterized(statement ParameterizedStatement) (wr WriteResult, err error)

WriteOneParameterized wraps WriteParameterized() into a single-statement method.

WriteOneParameterized uses context.Background() internally; to specify the context, use WriteOneParameterizedContext.

func (*Connection) WriteOneParameterizedContext

func (conn *Connection) WriteOneParameterizedContext(ctx context.Context, statement ParameterizedStatement) (wr WriteResult, err error)

WriteOneParameterizedContext wraps WriteParameterizedContext into a single-statement method.

func (*Connection) WriteParameterized

func (conn *Connection) WriteParameterized(sqlStatements []ParameterizedStatement) (results []WriteResult, err error)

WriteParameterized is used to perform DDL/DML in the database synchronously.

WriteParameterized takes an array of SQL statements, and returns an equal-sized array of WriteResults, each corresponding to the SQL statement that produced it.

All statements are executed as a single transaction.

WriteParameterized returns an error if one is encountered during its operation. If it's something like a call to the rqlite API, then it'll return that error. If one statement out of several has an error, you can look at the individual statement's Err for more info.

WriteParameterized uses context.Background() internally; to specify the context, use WriteParameterizedContext.

func (*Connection) WriteParameterizedContext

func (conn *Connection) WriteParameterizedContext(ctx context.Context, sqlStatements []ParameterizedStatement) (results []WriteResult, err error)

WriteParameterizedContext is used to perform DDL/DML in the database synchronously.

WriteParameterizedContext takes an array of SQL statements, and returns an equal-sized array of WriteResults, each corresponding to the SQL statement that produced it.

All statements are executed as a single transaction.

WriteParameterizedContext returns an error if one is encountered during its operation. If it's something like a call to the rqlite API, then it'll return that error. If one statement out of several has an error, you can look at the individual statement's Err for more info.

type NullBool

type NullBool struct {
	Bool  bool
	Valid bool // Valid is true if Bool is not NULL
}

NullBool represents a bool that may be null.

type NullFloat64

type NullFloat64 struct {
	Float64 float64
	Valid   bool // Valid is true if Float64 is not NULL
}

NullFloat64 represents a float64 that may be null.

type NullInt16

type NullInt16 struct {
	Int16 int16
	Valid bool // Valid is true if Int16 is not NULL
}

NullInt16 represents an int16 that may be null.

type NullInt32

type NullInt32 struct {
	Int32 int32
	Valid bool // Valid is true if Int32 is not NULL
}

NullInt32 represents an int32 that may be null.

type NullInt64

type NullInt64 struct {
	Int64 int64
	Valid bool // Valid is true if Int64 is not NULL
}

NullInt64 represents an int64 that may be null.

type NullString

type NullString struct {
	String string
	Valid  bool // Valid is true if String is not NULL
}

NullString represents a string that may be null.

type NullTime

type NullTime struct {
	Time  time.Time
	Valid bool // Valid is true if Time is not NULL
}

NullTime represents a time.Time that may be null.

type ParameterizedStatement

type ParameterizedStatement struct {
	Query     string
	Arguments []interface{}
}

type QueryResult

type QueryResult struct {
	Err error

	Timing float64
	// contains filtered or unexported fields
}

QueryResult holds the results of a call to Query(). You could think of it as a rowset.

So if you were to query:

SELECT id, name FROM some_table;

then a QueryResult would hold any errors from that query, a list of columns and types, and the actual row values.

Query() returns an array of QueryResult vars, while QueryOne() returns a single variable.

func (*QueryResult) Columns

func (qr *QueryResult) Columns() []string

Columns returns a list of the column names for this QueryResult.

func (*QueryResult) Map

func (qr *QueryResult) Map() (map[string]interface{}, error)

Map returns the current row (as advanced by Next()) as a map[string]interface{}.

The key is a string corresponding to a column name. The value is the corresponding column.

Note that only json values are supported, so you will need to type the interface{} accordingly.

func (*QueryResult) Next

func (qr *QueryResult) Next() bool

Next positions the QueryResult result pointer so that Scan() or Map() is ready.

You should call Next() first, but gorqlite will fix it if you call Map() or Scan() before the initial Next().

A common idiom:

rows := conn.Write(something)
for rows.Next() {
    // your Scan/Map and processing here.
}

func (*QueryResult) NumRows

func (qr *QueryResult) NumRows() int64

NumRows returns the number of rows returned by the query.

func (*QueryResult) RowNumber

func (qr *QueryResult) RowNumber() int64

RowNumber returns the current row number as Next() iterates through the result's rows.

func (*QueryResult) Scan

func (qr *QueryResult) Scan(dest ...interface{}) error

Scan takes a list of pointers and then updates them to reflect the current row's data.

Note that only the following data types are used, and they are a subset of the types JSON uses:

string, for JSON strings
float64, for JSON numbers
int64, as a convenient extension
nil for JSON null

booleans, JSON arrays, and JSON objects are not supported, since sqlite does not support them.

func (*QueryResult) Slice

func (qr *QueryResult) Slice() ([]interface{}, error)

Slice returns the current row (as advanced by Next()) as a []interface{}.

The slice is a shallow copy of the internal representation of the row data.

Note that only json values are supported, so you will need to type the interface{} accordingly.

func (*QueryResult) Types

func (qr *QueryResult) Types() []string

Types returns an array of the column's types.

Note that sqlite will repeat the type you tell it, but in many cases, it's ignored. So you can initialize a column as CHAR(3) but it's really TEXT. See https://www.sqlite.org/datatype3.html

This info may additionally conflict with the reality that your data is being JSON encoded/decoded.

type RequestResult

type RequestResult struct {
	Err   error
	Query *QueryResult
	Write *WriteResult
}

RequestResult holds the result of a single statement sent to Unified Endpoint.

If statement failed, Err contains the error, neither Query nor Write is set. If statement succeeded, either of Query or Write is set — depending on the type of the statement. Query.Err and Write.Err are never set.

type StatementErrors

type StatementErrors []error

func (StatementErrors) As

func (errs StatementErrors) As(target interface{}) bool

As returns true if the current error, or one of the statement errors can be assigned to the target error.

func (StatementErrors) Error

func (errs StatementErrors) Error() string

Error returns a string representation of the statement errors.

func (StatementErrors) Is

func (errs StatementErrors) Is(target error) bool

Is returns true if the current error, or one of the statement errors is equal to the target error.

func (StatementErrors) Unwrap

func (errs StatementErrors) Unwrap() []error

Unwrap returns the slice of statement errors.

type WriteResult

type WriteResult struct {
	Err          error // don't trust the rest if this isn't nil
	Timing       float64
	RowsAffected int64 // affected by the change
	LastInsertID int64 // if relevant, otherwise zero value
	// contains filtered or unexported fields
}

WriteResult holds the result of a single statement sent to Write().

Write() returns an array of WriteResult vars, while WriteOne() returns a single WriteResult.

Directories

Path Synopsis
Package stdlib provides a compatability layer from gorqlite to database/sql.
Package stdlib provides a compatability layer from gorqlite to database/sql.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL