converted to using pg
Change-Id: Ia3124ee979df4267841ac63051328f90ad65cb97
This commit is contained in:
parent
9d3ce56e26
commit
8bf4595c67
118
api_test.go
118
api_test.go
@ -12,16 +12,18 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/pborman/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
const window = 5 * time.Minute
|
const window = 5 * time.Minute
|
||||||
|
|
||||||
func TestAdd(t *testing.T) {
|
func TestAdd(t *testing.T) {
|
||||||
db, done := testDB(t)
|
db, err := NewTestDB(uuid.New())
|
||||||
if db == nil {
|
if err != nil {
|
||||||
t.Fatalf("could not create temp db")
|
t.Errorf("couldn't connect to fresh db : %v", err)
|
||||||
}
|
}
|
||||||
defer done()
|
defer db.Close()
|
||||||
|
|
||||||
sm := http.NewServeMux()
|
sm := http.NewServeMux()
|
||||||
NewServer(sm, db, nil, "", window, false)
|
NewServer(sm, db, nil, "", window, false)
|
||||||
@ -163,11 +165,11 @@ func TestAdd(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidPath(t *testing.T) {
|
func TestInvalidPath(t *testing.T) {
|
||||||
db, done := testDB(t)
|
db, err := NewTestDB(uuid.New())
|
||||||
if db == nil {
|
if err != nil {
|
||||||
t.Fatalf("could not create temp db")
|
t.Errorf("couldn't connect to fresh db : %v", err)
|
||||||
}
|
}
|
||||||
defer done()
|
defer db.Close()
|
||||||
|
|
||||||
sm := http.NewServeMux()
|
sm := http.NewServeMux()
|
||||||
NewServer(sm, db, nil, "", window, false)
|
NewServer(sm, db, nil, "", window, false)
|
||||||
@ -195,11 +197,11 @@ func TestInvalidPath(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCannotDuplicateExistingPath(t *testing.T) {
|
func TestCannotDuplicateExistingPath(t *testing.T) {
|
||||||
db, done := testDB(t)
|
db, err := NewTestDB(uuid.New())
|
||||||
if db == nil {
|
if err != nil {
|
||||||
t.Fatalf("could not create temp db")
|
t.Errorf("couldn't connect to fresh db : %v", err)
|
||||||
}
|
}
|
||||||
defer done()
|
defer db.Close()
|
||||||
|
|
||||||
sm := http.NewServeMux()
|
sm := http.NewServeMux()
|
||||||
NewServer(sm, db, nil, "", window, false)
|
NewServer(sm, db, nil, "", window, false)
|
||||||
@ -241,11 +243,11 @@ func TestCannotDuplicateExistingPath(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCannotAddExistingSubPath(t *testing.T) {
|
func TestCannotAddExistingSubPath(t *testing.T) {
|
||||||
db, done := testDB(t)
|
db, err := NewTestDB(uuid.New())
|
||||||
if db == nil {
|
if err != nil {
|
||||||
t.Fatalf("could not create temp db")
|
t.Errorf("couldn't connect to fresh db : %v", err)
|
||||||
}
|
}
|
||||||
defer done()
|
defer db.Close()
|
||||||
|
|
||||||
sm := http.NewServeMux()
|
sm := http.NewServeMux()
|
||||||
NewServer(sm, db, nil, "", window, false)
|
NewServer(sm, db, nil, "", window, false)
|
||||||
@ -289,11 +291,11 @@ func TestCannotAddExistingSubPath(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMissingRepo(t *testing.T) {
|
func TestMissingRepo(t *testing.T) {
|
||||||
db, done := testDB(t)
|
db, err := NewTestDB(uuid.New())
|
||||||
if db == nil {
|
if err != nil {
|
||||||
t.Fatalf("could not create temp db")
|
t.Errorf("couldn't connect to fresh db : %v", err)
|
||||||
}
|
}
|
||||||
defer done()
|
defer db.Close()
|
||||||
|
|
||||||
sm := http.NewServeMux()
|
sm := http.NewServeMux()
|
||||||
NewServer(sm, db, nil, "", window, false)
|
NewServer(sm, db, nil, "", window, false)
|
||||||
@ -322,11 +324,11 @@ func TestMissingRepo(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestBadJson(t *testing.T) {
|
func TestBadJson(t *testing.T) {
|
||||||
db, done := testDB(t)
|
db, err := NewTestDB(uuid.New())
|
||||||
if db == nil {
|
if err != nil {
|
||||||
t.Fatalf("could not create temp db")
|
t.Errorf("couldn't connect to fresh db : %v", err)
|
||||||
}
|
}
|
||||||
defer done()
|
defer db.Close()
|
||||||
|
|
||||||
sm := http.NewServeMux()
|
sm := http.NewServeMux()
|
||||||
NewServer(sm, db, nil, "", window, false)
|
NewServer(sm, db, nil, "", window, false)
|
||||||
@ -355,11 +357,11 @@ func TestBadJson(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestNoAuth(t *testing.T) {
|
func TestNoAuth(t *testing.T) {
|
||||||
db, done := testDB(t)
|
db, err := NewTestDB(uuid.New())
|
||||||
if db == nil {
|
if err != nil {
|
||||||
t.Fatalf("could not create temp db")
|
t.Errorf("couldn't connect to fresh db : %v", err)
|
||||||
}
|
}
|
||||||
defer done()
|
defer db.Close()
|
||||||
|
|
||||||
sm := http.NewServeMux()
|
sm := http.NewServeMux()
|
||||||
NewServer(sm, db, nil, "", window, false)
|
NewServer(sm, db, nil, "", window, false)
|
||||||
@ -384,11 +386,11 @@ func TestNoAuth(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestBadVcs(t *testing.T) {
|
func TestBadVcs(t *testing.T) {
|
||||||
db, done := testDB(t)
|
db, err := NewTestDB(uuid.New())
|
||||||
if db == nil {
|
if err != nil {
|
||||||
t.Fatalf("could not create temp db")
|
t.Errorf("couldn't connect to fresh db : %v", err)
|
||||||
}
|
}
|
||||||
defer done()
|
defer db.Close()
|
||||||
|
|
||||||
sm := http.NewServeMux()
|
sm := http.NewServeMux()
|
||||||
NewServer(sm, db, nil, "", window, false)
|
NewServer(sm, db, nil, "", window, false)
|
||||||
@ -415,11 +417,11 @@ func TestBadVcs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestUnsupportedMethod(t *testing.T) {
|
func TestUnsupportedMethod(t *testing.T) {
|
||||||
db, done := testDB(t)
|
db, err := NewTestDB(uuid.New())
|
||||||
if db == nil {
|
if err != nil {
|
||||||
t.Fatalf("could not create temp db")
|
t.Errorf("couldn't connect to fresh db : %v", err)
|
||||||
}
|
}
|
||||||
defer done()
|
defer db.Close()
|
||||||
|
|
||||||
sm := http.NewServeMux()
|
sm := http.NewServeMux()
|
||||||
NewServer(sm, db, nil, "", window, false)
|
NewServer(sm, db, nil, "", window, false)
|
||||||
@ -447,11 +449,11 @@ func TestUnsupportedMethod(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDelete(t *testing.T) {
|
func TestDelete(t *testing.T) {
|
||||||
db, done := testDB(t)
|
db, err := NewTestDB(uuid.New())
|
||||||
if db == nil {
|
if err != nil {
|
||||||
t.Fatalf("could not create temp db")
|
t.Errorf("couldn't connect to fresh db : %v", err)
|
||||||
}
|
}
|
||||||
defer done()
|
defer db.Close()
|
||||||
|
|
||||||
sm := http.NewServeMux()
|
sm := http.NewServeMux()
|
||||||
NewServer(sm, db, nil, "", window, false)
|
NewServer(sm, db, nil, "", window, false)
|
||||||
@ -511,17 +513,17 @@ func TestDelete(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestSingleGet(t *testing.T) {
|
func TestSingleGet(t *testing.T) {
|
||||||
db, done := testDB(t)
|
db, err := NewTestDB(uuid.New())
|
||||||
if db == nil {
|
if err != nil {
|
||||||
t.Fatalf("could not create temp db")
|
t.Errorf("couldn't connect to fresh db : %v", err)
|
||||||
}
|
}
|
||||||
defer done()
|
defer db.Close()
|
||||||
|
|
||||||
sm := http.NewServeMux()
|
sm := http.NewServeMux()
|
||||||
NewServer(sm, db, nil, "", window, true)
|
NewServer(sm, db, nil, "", window, true)
|
||||||
ts := httptest.NewServer(sm)
|
ts := httptest.NewServer(sm)
|
||||||
|
email := "sm@example.org"
|
||||||
tok, err := db.addUser("sm@example.org")
|
tok, err := db.addUser(email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failure to add user: %v", err)
|
t.Fatalf("failure to add user: %v", err)
|
||||||
}
|
}
|
||||||
@ -565,11 +567,11 @@ func TestSingleGet(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestRegister(t *testing.T) {
|
func TestRegister(t *testing.T) {
|
||||||
db, done := testDB(t)
|
db, err := NewTestDB(uuid.New())
|
||||||
if db == nil {
|
if err != nil {
|
||||||
t.Fatalf("could not create temp db")
|
t.Errorf("couldn't connect to fresh db : %v", err)
|
||||||
}
|
}
|
||||||
defer done()
|
defer db.Close()
|
||||||
|
|
||||||
sm := http.NewServeMux()
|
sm := http.NewServeMux()
|
||||||
mm := &mockMail{}
|
mm := &mockMail{}
|
||||||
@ -618,11 +620,11 @@ func TestRegister(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestRoundTrip(t *testing.T) {
|
func TestRoundTrip(t *testing.T) {
|
||||||
db, done := testDB(t)
|
db, err := NewTestDB(uuid.New())
|
||||||
if db == nil {
|
if err != nil {
|
||||||
t.Fatalf("could not create temp db")
|
t.Errorf("couldn't connect to fresh db : %v", err)
|
||||||
}
|
}
|
||||||
defer done()
|
defer db.Close()
|
||||||
|
|
||||||
sm := http.NewServeMux()
|
sm := http.NewServeMux()
|
||||||
mm := &mockMail{}
|
mm := &mockMail{}
|
||||||
@ -686,11 +688,11 @@ func TestRoundTrip(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestForgot(t *testing.T) {
|
func TestForgot(t *testing.T) {
|
||||||
db, done := testDB(t)
|
db, err := NewTestDB(uuid.New())
|
||||||
if db == nil {
|
if err != nil {
|
||||||
t.Fatalf("could not create temp db")
|
t.Errorf("couldn't connect to fresh db : %v", err)
|
||||||
}
|
}
|
||||||
defer done()
|
defer db.Close()
|
||||||
|
|
||||||
sm := http.NewServeMux()
|
sm := http.NewServeMux()
|
||||||
mm := &mockMail{}
|
mm := &mockMail{}
|
||||||
|
@ -50,6 +50,7 @@ import (
|
|||||||
"mcquay.me/vain"
|
"mcquay.me/vain"
|
||||||
|
|
||||||
"github.com/kelseyhightower/envconfig"
|
"github.com/kelseyhightower/envconfig"
|
||||||
|
_ "github.com/lib/pq"
|
||||||
)
|
)
|
||||||
|
|
||||||
const usage = "vaind [init] <dbname>"
|
const usage = "vaind [init] <dbname>"
|
||||||
@ -83,7 +84,7 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
db, err := vain.NewDB(os.Args[2])
|
db, err := vain.NewPGDB("localhost", os.Args[2], 32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "couldn't open db: %v\n", err)
|
fmt.Fprintf(os.Stderr, "couldn't open db: %v\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
44
db.go
44
db.go
@ -8,8 +8,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
// for side effects
|
|
||||||
_ "github.com/mattn/go-sqlite3"
|
|
||||||
|
|
||||||
verrors "mcquay.me/vain/errors"
|
verrors "mcquay.me/vain/errors"
|
||||||
vsql "mcquay.me/vain/sql"
|
vsql "mcquay.me/vain/sql"
|
||||||
@ -30,6 +28,22 @@ func NewDB(path string) (*DB, error) {
|
|||||||
return &DB{conn}, err
|
return &DB{conn}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewPGDB returns a populated DB and verifies the connection is usable.
|
||||||
|
func NewPGDB(dbhost string, dbname string, maxConn int) (*DB, error) {
|
||||||
|
dsn := fmt.Sprintf("host=%s dbname=%s sslmode=disable", dbhost, dbname)
|
||||||
|
|
||||||
|
conn, err := sqlx.Open("postgres", dsn)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := conn.Ping(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.SetMaxOpenConns(maxConn)
|
||||||
|
return &DB{conn: conn}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Init runs the embedded sql to initialize tables.
|
// Init runs the embedded sql to initialize tables.
|
||||||
func (db *DB) Init() error {
|
func (db *DB) Init() error {
|
||||||
content, err := vsql.Asset("sql/init.sql")
|
content, err := vsql.Asset("sql/init.sql")
|
||||||
@ -56,7 +70,7 @@ func (db *DB) AddPackage(p Package) error {
|
|||||||
|
|
||||||
// RemovePackage removes package with given path
|
// RemovePackage removes package with given path
|
||||||
func (db *DB) RemovePackage(path string) error {
|
func (db *DB) RemovePackage(path string) error {
|
||||||
_, err := db.conn.Exec("DELETE FROM packages WHERE path = ?", path)
|
_, err := db.conn.Exec("DELETE FROM packages WHERE path = $1", path)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,7 +97,7 @@ func (db *DB) Pkgs() []Package {
|
|||||||
// PackageExists tells if a package with path is in the database.
|
// PackageExists tells if a package with path is in the database.
|
||||||
func (db *DB) PackageExists(path string) bool {
|
func (db *DB) PackageExists(path string) bool {
|
||||||
var count int
|
var count int
|
||||||
if err := db.conn.Get(&count, "SELECT COUNT(*) FROM packages WHERE path = ?", path); err != nil {
|
if err := db.conn.Get(&count, "SELECT COUNT(*) FROM packages WHERE path = $1", path); err != nil {
|
||||||
log.Printf("%+v", err)
|
log.Printf("%+v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,7 +114,7 @@ func (db *DB) PackageExists(path string) bool {
|
|||||||
// Package fetches the package associated with path.
|
// Package fetches the package associated with path.
|
||||||
func (db *DB) Package(path string) (Package, error) {
|
func (db *DB) Package(path string) (Package, error) {
|
||||||
r := Package{}
|
r := Package{}
|
||||||
err := db.conn.Get(&r, "SELECT * FROM packages WHERE path = ?", path)
|
err := db.conn.Get(&r, "SELECT * FROM packages WHERE path = $1", path)
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
return r, verrors.HTTP{
|
return r, verrors.HTTP{
|
||||||
Message: fmt.Sprintf("couldn't find package %q", path),
|
Message: fmt.Sprintf("couldn't find package %q", path),
|
||||||
@ -129,7 +143,7 @@ func (db *DB) NSForToken(ns string, tok string) error {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
var count int
|
var count int
|
||||||
if err = txn.Get(&count, "SELECT COUNT(*) FROM namespaces WHERE namespaces.ns = ?", ns); err != nil {
|
if err = txn.Get(&count, "SELECT COUNT(*) FROM namespaces WHERE namespaces.ns = $1", ns); err != nil {
|
||||||
return verrors.HTTP{
|
return verrors.HTTP{
|
||||||
Message: fmt.Sprintf("problem matching fetching namespaces matching %q", ns),
|
Message: fmt.Sprintf("problem matching fetching namespaces matching %q", ns),
|
||||||
Code: http.StatusInternalServerError,
|
Code: http.StatusInternalServerError,
|
||||||
@ -157,7 +171,7 @@ func (db *DB) NSForToken(ns string, tok string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = txn.Get(&count, "SELECT COUNT(*) FROM namespaces JOIN users ON namespaces.email = users.email WHERE users.token = ? AND namespaces.ns = ?", tok, ns); err != nil {
|
if err = txn.Get(&count, "SELECT COUNT(*) FROM namespaces JOIN users ON namespaces.email = users.email WHERE users.token = $1 AND namespaces.ns = $2", tok, ns); err != nil {
|
||||||
return verrors.HTTP{
|
return verrors.HTTP{
|
||||||
Message: fmt.Sprintf("ns: %q, tok: %q; %v", ns, tok, err),
|
Message: fmt.Sprintf("ns: %q, tok: %q; %v", ns, tok, err),
|
||||||
Code: http.StatusInternalServerError,
|
Code: http.StatusInternalServerError,
|
||||||
@ -200,7 +214,7 @@ func (db *DB) Register(email string) (string, error) {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
var count int
|
var count int
|
||||||
if err = txn.Get(&count, "SELECT COUNT(*) FROM users WHERE email = ?", email); err != nil {
|
if err = txn.Get(&count, "SELECT COUNT(*) FROM users WHERE email = $1", email); err != nil {
|
||||||
return "", verrors.HTTP{
|
return "", verrors.HTTP{
|
||||||
Message: fmt.Sprintf("could not search for email %q in db: %v", email, err),
|
Message: fmt.Sprintf("could not search for email %q in db: %v", email, err),
|
||||||
Code: http.StatusInternalServerError,
|
Code: http.StatusInternalServerError,
|
||||||
@ -216,7 +230,7 @@ func (db *DB) Register(email string) (string, error) {
|
|||||||
|
|
||||||
tok := FreshToken()
|
tok := FreshToken()
|
||||||
_, err = txn.Exec(
|
_, err = txn.Exec(
|
||||||
"INSERT INTO users(email, token, requested) VALUES (?, ?, ?)",
|
"INSERT INTO users(email, token, requested) VALUES ($1, $2, $3)",
|
||||||
email,
|
email,
|
||||||
tok,
|
tok,
|
||||||
time.Now(),
|
time.Now(),
|
||||||
@ -243,7 +257,7 @@ func (db *DB) Confirm(token string) (string, error) {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
var count int
|
var count int
|
||||||
if err = txn.Get(&count, "SELECT COUNT(*) FROM users WHERE token = ?", token); err != nil {
|
if err = txn.Get(&count, "SELECT COUNT(*) FROM users WHERE token = $1", token); err != nil {
|
||||||
return "", verrors.HTTP{
|
return "", verrors.HTTP{
|
||||||
Message: fmt.Sprintf("could not perform search for user with token %q in db: %v", token, err),
|
Message: fmt.Sprintf("could not perform search for user with token %q in db: %v", token, err),
|
||||||
Code: http.StatusInternalServerError,
|
Code: http.StatusInternalServerError,
|
||||||
@ -260,7 +274,7 @@ func (db *DB) Confirm(token string) (string, error) {
|
|||||||
newToken := FreshToken()
|
newToken := FreshToken()
|
||||||
|
|
||||||
_, err = txn.Exec(
|
_, err = txn.Exec(
|
||||||
"UPDATE users SET token = ?, registered = 1 WHERE token = ?",
|
"UPDATE users SET token = $1, registered = true WHERE token = $2",
|
||||||
newToken,
|
newToken,
|
||||||
token,
|
token,
|
||||||
)
|
)
|
||||||
@ -293,7 +307,7 @@ func (db *DB) forgot(email string, window time.Duration) (string, error) {
|
|||||||
Token string
|
Token string
|
||||||
Requested time.Time
|
Requested time.Time
|
||||||
}{}
|
}{}
|
||||||
if err = txn.Get(&out, "SELECT token, requested FROM users WHERE email = ?", email); err != nil {
|
if err = txn.Get(&out, "SELECT token, requested FROM users WHERE email = $1", email); err != nil {
|
||||||
return "", verrors.HTTP{
|
return "", verrors.HTTP{
|
||||||
Message: fmt.Sprintf("could not find email %q in db", email),
|
Message: fmt.Sprintf("could not find email %q in db", email),
|
||||||
Code: http.StatusNotFound,
|
Code: http.StatusNotFound,
|
||||||
@ -306,7 +320,7 @@ func (db *DB) forgot(email string, window time.Duration) (string, error) {
|
|||||||
Code: http.StatusTooManyRequests,
|
Code: http.StatusTooManyRequests,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_, err = txn.Exec("UPDATE users SET requested = ? WHERE email = ?", time.Now().Add(window), email)
|
_, err = txn.Exec("UPDATE users SET requested = $1 WHERE email = $2", time.Now().Add(window), email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", verrors.HTTP{
|
return "", verrors.HTTP{
|
||||||
Message: fmt.Sprintf("could not update last requested time for %q: %v", email, err),
|
Message: fmt.Sprintf("could not update last requested time for %q: %v", email, err),
|
||||||
@ -319,7 +333,7 @@ func (db *DB) forgot(email string, window time.Duration) (string, error) {
|
|||||||
func (db *DB) addUser(email string) (string, error) {
|
func (db *DB) addUser(email string) (string, error) {
|
||||||
tok := FreshToken()
|
tok := FreshToken()
|
||||||
_, err := db.conn.Exec(
|
_, err := db.conn.Exec(
|
||||||
"INSERT INTO users(email, token, requested) VALUES (?, ?, ?)",
|
"INSERT INTO users(email, token, requested) VALUES ($1, $2, $3)",
|
||||||
email,
|
email,
|
||||||
tok,
|
tok,
|
||||||
time.Now(),
|
time.Now(),
|
||||||
@ -331,7 +345,7 @@ func (db *DB) user(email string) (User, error) {
|
|||||||
u := User{}
|
u := User{}
|
||||||
err := db.conn.Get(
|
err := db.conn.Get(
|
||||||
&u,
|
&u,
|
||||||
"SELECT email, token, registered, requested FROM users WHERE email = ?",
|
"SELECT email, token, registered, requested FROM users WHERE email = $1",
|
||||||
email,
|
email,
|
||||||
)
|
)
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
|
77
db_test.go
Normal file
77
db_test.go
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
package vain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/lib/pq"
|
||||||
|
"github.com/pborman/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TestDB struct {
|
||||||
|
DB
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTestDB(name string) (*TestDB, error) {
|
||||||
|
pgdb, err := NewPGDB("localhost", "postgres", 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("problem connecting to admin database: %v", err)
|
||||||
|
}
|
||||||
|
if _, err := pgdb.conn.Exec(fmt.Sprintf("CREATE DATABASE %s", pq.QuoteIdentifier(name))); err != nil {
|
||||||
|
return nil, fmt.Errorf("problem creating test database:: %v", err)
|
||||||
|
}
|
||||||
|
pgdb.conn.Close()
|
||||||
|
|
||||||
|
db, err := NewPGDB("localhost", name, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("couldn't connect to fresh db %q: %v", name, err)
|
||||||
|
}
|
||||||
|
if err := db.Init(); err != nil {
|
||||||
|
return nil, fmt.Errorf("couldn't initialize db: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r := &TestDB{
|
||||||
|
name: name,
|
||||||
|
DB: DB{
|
||||||
|
conn: db.conn,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tdb *TestDB) Close() error {
|
||||||
|
errs := []string{}
|
||||||
|
if err := tdb.conn.Close(); err != nil {
|
||||||
|
errs = append(errs, fmt.Sprintf("problem closing connection to temp db: %v", err))
|
||||||
|
}
|
||||||
|
if err := tdb.Drop(); err != nil {
|
||||||
|
errs = append(errs, fmt.Sprintf("problem cleaning up temporary db %q: %v", tdb.name, err))
|
||||||
|
}
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return errors.New(strings.Join(errs, " & "))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tdb *TestDB) Drop() error {
|
||||||
|
pgdb, err := NewPGDB("localhost", "postgres", 64)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("problem connecting to admin database: %v", err)
|
||||||
|
}
|
||||||
|
defer pgdb.conn.Close()
|
||||||
|
if _, err := pgdb.conn.Exec(fmt.Sprintf("DROP DATABASE %s", pq.QuoteIdentifier(tdb.name))); err != nil {
|
||||||
|
return fmt.Errorf("problem dropping test database:: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFoo(t *testing.T) {
|
||||||
|
db, err := NewTestDB(uuid.New())
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("couldn't connect to fresh db : %v", err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
}
|
@ -1,8 +1,8 @@
|
|||||||
CREATE TABLE users (
|
CREATE TABLE users (
|
||||||
email TEXT PRIMARY KEY,
|
email TEXT PRIMARY KEY,
|
||||||
token TEXT UNIQUE,
|
token TEXT UNIQUE,
|
||||||
registered boolean DEFAULT 0,
|
registered boolean DEFAULT false,
|
||||||
requested DATETIME
|
requested TIMESTAMP NOT NULL DEFAULT NOW()
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE namespaces (
|
CREATE TABLE namespaces (
|
||||||
|
@ -68,7 +68,7 @@ func (fi bindataFileInfo) Sys() interface{} {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var _sqlInitSql = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x74\x8f\xc1\x4e\x84\x30\x14\x45\xf7\xfd\x8a\xb7\x64\x12\x17\xee\x5d\xd5\xf2\x26\x21\x32\xa8\x9d\x92\xc8\xb2\xc2\x0b\x12\xa0\x45\x0a\x7e\xbf\x0d\x4d\x15\xe3\xd0\x5d\x7b\x6f\x7a\xce\x15\x12\xb9\x42\x50\xfc\x31\x47\x58\x1d\xcd\x0e\x12\x06\xfe\xd0\xa8\xbb\x01\x14\xbe\x29\x78\x91\xd9\x85\xcb\x0a\x9e\xb0\xba\xdb\xb2\xc5\xf6\x64\x42\x56\x16\xd9\x6b\x89\xe1\x79\xa6\xb6\x73\x0b\xcd\xd4\xc0\xbb\xb5\x03\x69\x03\x29\x9e\x79\x99\x2b\xb8\x8f\x8d\xcf\x95\x7c\xa5\x81\xd4\x53\x55\x76\x41\x76\x7a\x60\xec\x8f\x84\xd1\x23\xb9\x49\xd7\x14\x4d\x8c\x3b\xd0\xd8\x29\x4a\x3c\xa3\xc4\x42\xe0\x35\x8c\x48\xb6\xec\x04\xcf\x85\x57\xc8\xd1\x7f\x2e\xf8\x55\xf0\xf4\x06\xcf\xa3\x7a\xdd\xfe\xd0\xbe\xea\x80\x8b\xc2\x93\xdd\x5d\x27\xbd\x7c\xfc\xdf\x1d\x05\x77\x12\xbf\x23\x12\xe3\x0e\x34\xbe\x03\x00\x00\xff\xff\xc7\xbb\x93\xa7\x7b\x01\x00\x00")
|
var _sqlInitSql = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\x74\x90\xc1\x6e\x83\x30\x10\x44\xef\x7c\xc5\x1c\x41\xea\x1f\xf4\xe4\xc2\x46\x42\x35\x26\x35\x8b\xda\x1c\x5d\xb2\x4d\xa3\x10\x43\x31\xe9\xf7\x57\x0a\x22\x25\x8a\x38\xda\x33\xd2\xbc\xb7\xa9\x25\xc5\x04\x56\x2f\x9a\x70\x09\x32\x04\xc4\x11\x00\xc8\xd9\x1d\x5b\x30\x7d\x30\xb6\x36\x2f\x94\xdd\xe1\x95\x76\x4f\xd7\x6c\xec\x4e\xe2\xa7\xac\x36\xf9\x5b\x4d\xd3\xf7\x20\x87\x63\x18\x65\x90\x3d\x3e\xbb\xae\x15\xe7\x91\xd1\x46\xd5\x9a\xf1\xe5\xda\x20\x73\xeb\xe7\x22\x61\x94\x3d\x38\x2f\xa8\x62\x55\x6c\x61\x4a\x86\xa9\xb5\xbe\xf5\x4d\xf9\x1e\x27\x51\xf2\x1c\x45\x77\x80\xde\x9d\x25\xf4\xae\x91\x99\xd2\x87\x15\xc4\x05\xbe\xa5\x0d\x59\x32\x29\x55\x93\x60\x7c\xcd\x12\x94\x06\x19\x69\x62\x42\xaa\xaa\x54\x65\xf4\xb8\xd7\xbb\xe6\xe4\x0e\xb7\xb5\xdf\x66\x9a\x9b\x45\xfa\x6e\xf1\xec\xdd\xf8\xfd\x78\x93\x19\x70\x01\xf1\x2f\x11\xfb\xb0\x82\xf1\x17\x00\x00\xff\xff\x04\x74\x3c\xff\x97\x01\x00\x00")
|
||||||
|
|
||||||
func sqlInitSqlBytes() ([]byte, error) {
|
func sqlInitSqlBytes() ([]byte, error) {
|
||||||
return bindataRead(
|
return bindataRead(
|
||||||
@ -83,7 +83,7 @@ func sqlInitSql() (*asset, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "sql/init.sql", size: 379, mode: os.FileMode(436), modTime: time.Unix(1461818129, 0)}
|
info := bindataFileInfo{name: "sql/init.sql", size: 407, mode: os.FileMode(436), modTime: time.Unix(1466486246, 0)}
|
||||||
a := &asset{bytes: bytes, info: info}
|
a := &asset{bytes: bytes, info: info}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
29
vendor/github.com/lib/pq/CONTRIBUTING.md
generated
vendored
Normal file
29
vendor/github.com/lib/pq/CONTRIBUTING.md
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
## Contributing to pq
|
||||||
|
|
||||||
|
`pq` has a backlog of pull requests, but contributions are still very
|
||||||
|
much welcome. You can help with patch review, submitting bug reports,
|
||||||
|
or adding new functionality. There is no formal style guide, but
|
||||||
|
please conform to the style of existing code and general Go formatting
|
||||||
|
conventions when submitting patches.
|
||||||
|
|
||||||
|
### Patch review
|
||||||
|
|
||||||
|
Help review existing open pull requests by commenting on the code or
|
||||||
|
proposed functionality.
|
||||||
|
|
||||||
|
### Bug reports
|
||||||
|
|
||||||
|
We appreciate any bug reports, but especially ones with self-contained
|
||||||
|
(doesn't depend on code outside of pq), minimal (can't be simplified
|
||||||
|
further) test cases. It's especially helpful if you can submit a pull
|
||||||
|
request with just the failing test case (you'll probably want to
|
||||||
|
pattern it after the tests in
|
||||||
|
[conn_test.go](https://github.com/lib/pq/blob/master/conn_test.go).
|
||||||
|
|
||||||
|
### New functionality
|
||||||
|
|
||||||
|
There are a number of pending patches for new functionality, so
|
||||||
|
additional feature patches will take a while to merge. Still, patches
|
||||||
|
are generally reviewed based on usefulness and complexity in addition
|
||||||
|
to time-in-queue, so if you have a knockout idea, take a shot. Feel
|
||||||
|
free to open an issue discussion your proposed patch beforehand.
|
8
vendor/github.com/lib/pq/LICENSE.md
generated
vendored
Normal file
8
vendor/github.com/lib/pq/LICENSE.md
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
Copyright (c) 2011-2013, 'pq' Contributors
|
||||||
|
Portions Copyright (C) 2011 Blake Mizerany
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
105
vendor/github.com/lib/pq/README.md
generated
vendored
Normal file
105
vendor/github.com/lib/pq/README.md
generated
vendored
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
# pq - A pure Go postgres driver for Go's database/sql package
|
||||||
|
|
||||||
|
[![Build Status](https://travis-ci.org/lib/pq.png?branch=master)](https://travis-ci.org/lib/pq)
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
go get github.com/lib/pq
|
||||||
|
|
||||||
|
## Docs
|
||||||
|
|
||||||
|
For detailed documentation and basic usage examples, please see the package
|
||||||
|
documentation at <http://godoc.org/github.com/lib/pq>.
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
`go test` is used for testing. A running PostgreSQL server is
|
||||||
|
required, with the ability to log in. The default database to connect
|
||||||
|
to test with is "pqgotest," but it can be overridden using environment
|
||||||
|
variables.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
PGHOST=/run/postgresql go test github.com/lib/pq
|
||||||
|
|
||||||
|
Optionally, a benchmark suite can be run as part of the tests:
|
||||||
|
|
||||||
|
PGHOST=/run/postgresql go test -bench .
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
* SSL
|
||||||
|
* Handles bad connections for `database/sql`
|
||||||
|
* Scan `time.Time` correctly (i.e. `timestamp[tz]`, `time[tz]`, `date`)
|
||||||
|
* Scan binary blobs correctly (i.e. `bytea`)
|
||||||
|
* Package for `hstore` support
|
||||||
|
* COPY FROM support
|
||||||
|
* pq.ParseURL for converting urls to connection strings for sql.Open.
|
||||||
|
* Many libpq compatible environment variables
|
||||||
|
* Unix socket support
|
||||||
|
* Notifications: `LISTEN`/`NOTIFY`
|
||||||
|
* pgpass support
|
||||||
|
|
||||||
|
## Future / Things you can help with
|
||||||
|
|
||||||
|
* Better COPY FROM / COPY TO (see discussion in #181)
|
||||||
|
|
||||||
|
## Thank you (alphabetical)
|
||||||
|
|
||||||
|
Some of these contributors are from the original library `bmizerany/pq.go` whose
|
||||||
|
code still exists in here.
|
||||||
|
|
||||||
|
* Andy Balholm (andybalholm)
|
||||||
|
* Ben Berkert (benburkert)
|
||||||
|
* Benjamin Heatwole (bheatwole)
|
||||||
|
* Bill Mill (llimllib)
|
||||||
|
* Bjørn Madsen (aeons)
|
||||||
|
* Blake Gentry (bgentry)
|
||||||
|
* Brad Fitzpatrick (bradfitz)
|
||||||
|
* Charlie Melbye (cmelbye)
|
||||||
|
* Chris Bandy (cbandy)
|
||||||
|
* Chris Gilling (cgilling)
|
||||||
|
* Chris Walsh (cwds)
|
||||||
|
* Dan Sosedoff (sosedoff)
|
||||||
|
* Daniel Farina (fdr)
|
||||||
|
* Eric Chlebek (echlebek)
|
||||||
|
* Eric Garrido (minusnine)
|
||||||
|
* Eric Urban (hydrogen18)
|
||||||
|
* Everyone at The Go Team
|
||||||
|
* Evan Shaw (edsrzf)
|
||||||
|
* Ewan Chou (coocood)
|
||||||
|
* Fazal Majid (fazalmajid)
|
||||||
|
* Federico Romero (federomero)
|
||||||
|
* Fumin (fumin)
|
||||||
|
* Gary Burd (garyburd)
|
||||||
|
* Heroku (heroku)
|
||||||
|
* James Pozdena (jpoz)
|
||||||
|
* Jason McVetta (jmcvetta)
|
||||||
|
* Jeremy Jay (pbnjay)
|
||||||
|
* Joakim Sernbrant (serbaut)
|
||||||
|
* John Gallagher (jgallagher)
|
||||||
|
* Jonathan Rudenberg (titanous)
|
||||||
|
* Joël Stemmer (jstemmer)
|
||||||
|
* Kamil Kisiel (kisielk)
|
||||||
|
* Kelly Dunn (kellydunn)
|
||||||
|
* Keith Rarick (kr)
|
||||||
|
* Kir Shatrov (kirs)
|
||||||
|
* Lann Martin (lann)
|
||||||
|
* Maciek Sakrejda (deafbybeheading)
|
||||||
|
* Marc Brinkmann (mbr)
|
||||||
|
* Marko Tiikkaja (johto)
|
||||||
|
* Matt Newberry (MattNewberry)
|
||||||
|
* Matt Robenolt (mattrobenolt)
|
||||||
|
* Martin Olsen (martinolsen)
|
||||||
|
* Mike Lewis (mikelikespie)
|
||||||
|
* Nicolas Patry (Narsil)
|
||||||
|
* Oliver Tonnhofer (olt)
|
||||||
|
* Patrick Hayes (phayes)
|
||||||
|
* Paul Hammond (paulhammond)
|
||||||
|
* Ryan Smith (ryandotsmith)
|
||||||
|
* Samuel Stauffer (samuel)
|
||||||
|
* Timothée Peignier (cyberdelia)
|
||||||
|
* Travis Cline (tmc)
|
||||||
|
* TruongSinh Tran-Nguyen (truongsinh)
|
||||||
|
* Yaismel Miranda (ympons)
|
||||||
|
* notedit (notedit)
|
435
vendor/github.com/lib/pq/bench_test.go
generated
vendored
Normal file
435
vendor/github.com/lib/pq/bench_test.go
generated
vendored
Normal file
@ -0,0 +1,435 @@
|
|||||||
|
// +build go1.1
|
||||||
|
|
||||||
|
package pq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"database/sql"
|
||||||
|
"database/sql/driver"
|
||||||
|
"io"
|
||||||
|
"math/rand"
|
||||||
|
"net"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/lib/pq/oid"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
selectStringQuery = "SELECT '" + strings.Repeat("0123456789", 10) + "'"
|
||||||
|
selectSeriesQuery = "SELECT generate_series(1, 100)"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BenchmarkSelectString(b *testing.B) {
|
||||||
|
var result string
|
||||||
|
benchQuery(b, selectStringQuery, &result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkSelectSeries(b *testing.B) {
|
||||||
|
var result int
|
||||||
|
benchQuery(b, selectSeriesQuery, &result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func benchQuery(b *testing.B, query string, result interface{}) {
|
||||||
|
b.StopTimer()
|
||||||
|
db := openTestConn(b)
|
||||||
|
defer db.Close()
|
||||||
|
b.StartTimer()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
benchQueryLoop(b, db, query, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func benchQueryLoop(b *testing.B, db *sql.DB, query string, result interface{}) {
|
||||||
|
rows, err := db.Query(query)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
for rows.Next() {
|
||||||
|
err = rows.Scan(result)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal("failed to scan", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// reading from circularConn yields content[:prefixLen] once, followed by
|
||||||
|
// content[prefixLen:] over and over again. It never returns EOF.
|
||||||
|
type circularConn struct {
|
||||||
|
content string
|
||||||
|
prefixLen int
|
||||||
|
pos int
|
||||||
|
net.Conn // for all other net.Conn methods that will never be called
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *circularConn) Read(b []byte) (n int, err error) {
|
||||||
|
n = copy(b, r.content[r.pos:])
|
||||||
|
r.pos += n
|
||||||
|
if r.pos >= len(r.content) {
|
||||||
|
r.pos = r.prefixLen
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *circularConn) Write(b []byte) (n int, err error) { return len(b), nil }
|
||||||
|
|
||||||
|
func (r *circularConn) Close() error { return nil }
|
||||||
|
|
||||||
|
func fakeConn(content string, prefixLen int) *conn {
|
||||||
|
c := &circularConn{content: content, prefixLen: prefixLen}
|
||||||
|
return &conn{buf: bufio.NewReader(c), c: c}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This benchmark is meant to be the same as BenchmarkSelectString, but takes
|
||||||
|
// out some of the factors this package can't control. The numbers are less noisy,
|
||||||
|
// but also the costs of network communication aren't accurately represented.
|
||||||
|
func BenchmarkMockSelectString(b *testing.B) {
|
||||||
|
b.StopTimer()
|
||||||
|
// taken from a recorded run of BenchmarkSelectString
|
||||||
|
// See: http://www.postgresql.org/docs/current/static/protocol-message-formats.html
|
||||||
|
const response = "1\x00\x00\x00\x04" +
|
||||||
|
"t\x00\x00\x00\x06\x00\x00" +
|
||||||
|
"T\x00\x00\x00!\x00\x01?column?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xc1\xff\xfe\xff\xff\xff\xff\x00\x00" +
|
||||||
|
"Z\x00\x00\x00\x05I" +
|
||||||
|
"2\x00\x00\x00\x04" +
|
||||||
|
"D\x00\x00\x00n\x00\x01\x00\x00\x00d0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
|
||||||
|
"C\x00\x00\x00\rSELECT 1\x00" +
|
||||||
|
"Z\x00\x00\x00\x05I" +
|
||||||
|
"3\x00\x00\x00\x04" +
|
||||||
|
"Z\x00\x00\x00\x05I"
|
||||||
|
c := fakeConn(response, 0)
|
||||||
|
b.StartTimer()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
benchMockQuery(b, c, selectStringQuery)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var seriesRowData = func() string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
for i := 1; i <= 100; i++ {
|
||||||
|
digits := byte(2)
|
||||||
|
if i >= 100 {
|
||||||
|
digits = 3
|
||||||
|
} else if i < 10 {
|
||||||
|
digits = 1
|
||||||
|
}
|
||||||
|
buf.WriteString("D\x00\x00\x00")
|
||||||
|
buf.WriteByte(10 + digits)
|
||||||
|
buf.WriteString("\x00\x01\x00\x00\x00")
|
||||||
|
buf.WriteByte(digits)
|
||||||
|
buf.WriteString(strconv.Itoa(i))
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}()
|
||||||
|
|
||||||
|
func BenchmarkMockSelectSeries(b *testing.B) {
|
||||||
|
b.StopTimer()
|
||||||
|
var response = "1\x00\x00\x00\x04" +
|
||||||
|
"t\x00\x00\x00\x06\x00\x00" +
|
||||||
|
"T\x00\x00\x00!\x00\x01?column?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xc1\xff\xfe\xff\xff\xff\xff\x00\x00" +
|
||||||
|
"Z\x00\x00\x00\x05I" +
|
||||||
|
"2\x00\x00\x00\x04" +
|
||||||
|
seriesRowData +
|
||||||
|
"C\x00\x00\x00\x0fSELECT 100\x00" +
|
||||||
|
"Z\x00\x00\x00\x05I" +
|
||||||
|
"3\x00\x00\x00\x04" +
|
||||||
|
"Z\x00\x00\x00\x05I"
|
||||||
|
c := fakeConn(response, 0)
|
||||||
|
b.StartTimer()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
benchMockQuery(b, c, selectSeriesQuery)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func benchMockQuery(b *testing.B, c *conn, query string) {
|
||||||
|
stmt, err := c.Prepare(query)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
rows, err := stmt.Query(nil)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var dest [1]driver.Value
|
||||||
|
for {
|
||||||
|
if err := rows.Next(dest[:]); err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkPreparedSelectString(b *testing.B) {
|
||||||
|
var result string
|
||||||
|
benchPreparedQuery(b, selectStringQuery, &result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkPreparedSelectSeries(b *testing.B) {
|
||||||
|
var result int
|
||||||
|
benchPreparedQuery(b, selectSeriesQuery, &result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func benchPreparedQuery(b *testing.B, query string, result interface{}) {
|
||||||
|
b.StopTimer()
|
||||||
|
db := openTestConn(b)
|
||||||
|
defer db.Close()
|
||||||
|
stmt, err := db.Prepare(query)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
b.StartTimer()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
benchPreparedQueryLoop(b, db, stmt, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func benchPreparedQueryLoop(b *testing.B, db *sql.DB, stmt *sql.Stmt, result interface{}) {
|
||||||
|
rows, err := stmt.Query()
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
if !rows.Next() {
|
||||||
|
rows.Close()
|
||||||
|
b.Fatal("no rows")
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
for rows.Next() {
|
||||||
|
err = rows.Scan(&result)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal("failed to scan")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// See the comment for BenchmarkMockSelectString.
|
||||||
|
func BenchmarkMockPreparedSelectString(b *testing.B) {
|
||||||
|
b.StopTimer()
|
||||||
|
const parseResponse = "1\x00\x00\x00\x04" +
|
||||||
|
"t\x00\x00\x00\x06\x00\x00" +
|
||||||
|
"T\x00\x00\x00!\x00\x01?column?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xc1\xff\xfe\xff\xff\xff\xff\x00\x00" +
|
||||||
|
"Z\x00\x00\x00\x05I"
|
||||||
|
const responses = parseResponse +
|
||||||
|
"2\x00\x00\x00\x04" +
|
||||||
|
"D\x00\x00\x00n\x00\x01\x00\x00\x00d0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
|
||||||
|
"C\x00\x00\x00\rSELECT 1\x00" +
|
||||||
|
"Z\x00\x00\x00\x05I"
|
||||||
|
c := fakeConn(responses, len(parseResponse))
|
||||||
|
|
||||||
|
stmt, err := c.Prepare(selectStringQuery)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
b.StartTimer()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
benchPreparedMockQuery(b, c, stmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkMockPreparedSelectSeries(b *testing.B) {
|
||||||
|
b.StopTimer()
|
||||||
|
const parseResponse = "1\x00\x00\x00\x04" +
|
||||||
|
"t\x00\x00\x00\x06\x00\x00" +
|
||||||
|
"T\x00\x00\x00!\x00\x01?column?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xc1\xff\xfe\xff\xff\xff\xff\x00\x00" +
|
||||||
|
"Z\x00\x00\x00\x05I"
|
||||||
|
var responses = parseResponse +
|
||||||
|
"2\x00\x00\x00\x04" +
|
||||||
|
seriesRowData +
|
||||||
|
"C\x00\x00\x00\x0fSELECT 100\x00" +
|
||||||
|
"Z\x00\x00\x00\x05I"
|
||||||
|
c := fakeConn(responses, len(parseResponse))
|
||||||
|
|
||||||
|
stmt, err := c.Prepare(selectSeriesQuery)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
b.StartTimer()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
benchPreparedMockQuery(b, c, stmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func benchPreparedMockQuery(b *testing.B, c *conn, stmt driver.Stmt) {
|
||||||
|
rows, err := stmt.Query(nil)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var dest [1]driver.Value
|
||||||
|
for {
|
||||||
|
if err := rows.Next(dest[:]); err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkEncodeInt64(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
encode(¶meterStatus{}, int64(1234), oid.T_int8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkEncodeFloat64(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
encode(¶meterStatus{}, 3.14159, oid.T_float8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var testByteString = []byte("abcdefghijklmnopqrstuvwxyz")
|
||||||
|
|
||||||
|
func BenchmarkEncodeByteaHex(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
encode(¶meterStatus{serverVersion: 90000}, testByteString, oid.T_bytea)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func BenchmarkEncodeByteaEscape(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
encode(¶meterStatus{serverVersion: 84000}, testByteString, oid.T_bytea)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkEncodeBool(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
encode(¶meterStatus{}, true, oid.T_bool)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var testTimestamptz = time.Date(2001, time.January, 1, 0, 0, 0, 0, time.Local)
|
||||||
|
|
||||||
|
func BenchmarkEncodeTimestamptz(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
encode(¶meterStatus{}, testTimestamptz, oid.T_timestamptz)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var testIntBytes = []byte("1234")
|
||||||
|
|
||||||
|
func BenchmarkDecodeInt64(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
decode(¶meterStatus{}, testIntBytes, oid.T_int8, formatText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var testFloatBytes = []byte("3.14159")
|
||||||
|
|
||||||
|
func BenchmarkDecodeFloat64(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
decode(¶meterStatus{}, testFloatBytes, oid.T_float8, formatText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var testBoolBytes = []byte{'t'}
|
||||||
|
|
||||||
|
func BenchmarkDecodeBool(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
decode(¶meterStatus{}, testBoolBytes, oid.T_bool, formatText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecodeBool(t *testing.T) {
|
||||||
|
db := openTestConn(t)
|
||||||
|
rows, err := db.Query("select true")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
rows.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
var testTimestamptzBytes = []byte("2013-09-17 22:15:32.360754-07")
|
||||||
|
|
||||||
|
func BenchmarkDecodeTimestamptz(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
decode(¶meterStatus{}, testTimestamptzBytes, oid.T_timestamptz, formatText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkDecodeTimestamptzMultiThread(b *testing.B) {
|
||||||
|
oldProcs := runtime.GOMAXPROCS(0)
|
||||||
|
defer runtime.GOMAXPROCS(oldProcs)
|
||||||
|
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||||
|
globalLocationCache = newLocationCache()
|
||||||
|
|
||||||
|
f := func(wg *sync.WaitGroup, loops int) {
|
||||||
|
defer wg.Done()
|
||||||
|
for i := 0; i < loops; i++ {
|
||||||
|
decode(¶meterStatus{}, testTimestamptzBytes, oid.T_timestamptz, formatText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wg := &sync.WaitGroup{}
|
||||||
|
b.ResetTimer()
|
||||||
|
for j := 0; j < 10; j++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go f(wg, b.N/10)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkLocationCache(b *testing.B) {
|
||||||
|
globalLocationCache = newLocationCache()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
globalLocationCache.getLocation(rand.Intn(10000))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkLocationCacheMultiThread(b *testing.B) {
|
||||||
|
oldProcs := runtime.GOMAXPROCS(0)
|
||||||
|
defer runtime.GOMAXPROCS(oldProcs)
|
||||||
|
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||||
|
globalLocationCache = newLocationCache()
|
||||||
|
|
||||||
|
f := func(wg *sync.WaitGroup, loops int) {
|
||||||
|
defer wg.Done()
|
||||||
|
for i := 0; i < loops; i++ {
|
||||||
|
globalLocationCache.getLocation(rand.Intn(10000))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wg := &sync.WaitGroup{}
|
||||||
|
b.ResetTimer()
|
||||||
|
for j := 0; j < 10; j++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go f(wg, b.N/10)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stress test the performance of parsing results from the wire.
|
||||||
|
func BenchmarkResultParsing(b *testing.B) {
|
||||||
|
b.StopTimer()
|
||||||
|
|
||||||
|
db := openTestConn(b)
|
||||||
|
defer db.Close()
|
||||||
|
_, err := db.Exec("BEGIN")
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
b.StartTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
res, err := db.Query("SELECT generate_series(1, 50000)")
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
res.Close()
|
||||||
|
}
|
||||||
|
}
|
91
vendor/github.com/lib/pq/buf.go
generated
vendored
Normal file
91
vendor/github.com/lib/pq/buf.go
generated
vendored
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
package pq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
|
||||||
|
"github.com/lib/pq/oid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type readBuf []byte
|
||||||
|
|
||||||
|
func (b *readBuf) int32() (n int) {
|
||||||
|
n = int(int32(binary.BigEndian.Uint32(*b)))
|
||||||
|
*b = (*b)[4:]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *readBuf) oid() (n oid.Oid) {
|
||||||
|
n = oid.Oid(binary.BigEndian.Uint32(*b))
|
||||||
|
*b = (*b)[4:]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// N.B: this is actually an unsigned 16-bit integer, unlike int32
|
||||||
|
func (b *readBuf) int16() (n int) {
|
||||||
|
n = int(binary.BigEndian.Uint16(*b))
|
||||||
|
*b = (*b)[2:]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *readBuf) string() string {
|
||||||
|
i := bytes.IndexByte(*b, 0)
|
||||||
|
if i < 0 {
|
||||||
|
errorf("invalid message format; expected string terminator")
|
||||||
|
}
|
||||||
|
s := (*b)[:i]
|
||||||
|
*b = (*b)[i+1:]
|
||||||
|
return string(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *readBuf) next(n int) (v []byte) {
|
||||||
|
v = (*b)[:n]
|
||||||
|
*b = (*b)[n:]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *readBuf) byte() byte {
|
||||||
|
return b.next(1)[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
type writeBuf struct {
|
||||||
|
buf []byte
|
||||||
|
pos int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *writeBuf) int32(n int) {
|
||||||
|
x := make([]byte, 4)
|
||||||
|
binary.BigEndian.PutUint32(x, uint32(n))
|
||||||
|
b.buf = append(b.buf, x...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *writeBuf) int16(n int) {
|
||||||
|
x := make([]byte, 2)
|
||||||
|
binary.BigEndian.PutUint16(x, uint16(n))
|
||||||
|
b.buf = append(b.buf, x...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *writeBuf) string(s string) {
|
||||||
|
b.buf = append(b.buf, (s + "\000")...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *writeBuf) byte(c byte) {
|
||||||
|
b.buf = append(b.buf, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *writeBuf) bytes(v []byte) {
|
||||||
|
b.buf = append(b.buf, v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *writeBuf) wrap() []byte {
|
||||||
|
p := b.buf[b.pos:]
|
||||||
|
binary.BigEndian.PutUint32(p, uint32(len(p)))
|
||||||
|
return b.buf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *writeBuf) next(c byte) {
|
||||||
|
p := b.buf[b.pos:]
|
||||||
|
binary.BigEndian.PutUint32(p, uint32(len(p)))
|
||||||
|
b.pos = len(b.buf) + 1
|
||||||
|
b.buf = append(b.buf, c, 0, 0, 0, 0)
|
||||||
|
}
|
1862
vendor/github.com/lib/pq/conn.go
generated
vendored
Normal file
1862
vendor/github.com/lib/pq/conn.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1434
vendor/github.com/lib/pq/conn_test.go
generated
vendored
Normal file
1434
vendor/github.com/lib/pq/conn_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
267
vendor/github.com/lib/pq/copy.go
generated
vendored
Normal file
267
vendor/github.com/lib/pq/copy.go
generated
vendored
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
package pq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql/driver"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errCopyInClosed = errors.New("pq: copyin statement has already been closed")
|
||||||
|
errBinaryCopyNotSupported = errors.New("pq: only text format supported for COPY")
|
||||||
|
errCopyToNotSupported = errors.New("pq: COPY TO is not supported")
|
||||||
|
errCopyNotSupportedOutsideTxn = errors.New("pq: COPY is only allowed inside a transaction")
|
||||||
|
)
|
||||||
|
|
||||||
|
// CopyIn creates a COPY FROM statement which can be prepared with
|
||||||
|
// Tx.Prepare(). The target table should be visible in search_path.
|
||||||
|
func CopyIn(table string, columns ...string) string {
|
||||||
|
stmt := "COPY " + QuoteIdentifier(table) + " ("
|
||||||
|
for i, col := range columns {
|
||||||
|
if i != 0 {
|
||||||
|
stmt += ", "
|
||||||
|
}
|
||||||
|
stmt += QuoteIdentifier(col)
|
||||||
|
}
|
||||||
|
stmt += ") FROM STDIN"
|
||||||
|
return stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
// CopyInSchema creates a COPY FROM statement which can be prepared with
|
||||||
|
// Tx.Prepare().
|
||||||
|
func CopyInSchema(schema, table string, columns ...string) string {
|
||||||
|
stmt := "COPY " + QuoteIdentifier(schema) + "." + QuoteIdentifier(table) + " ("
|
||||||
|
for i, col := range columns {
|
||||||
|
if i != 0 {
|
||||||
|
stmt += ", "
|
||||||
|
}
|
||||||
|
stmt += QuoteIdentifier(col)
|
||||||
|
}
|
||||||
|
stmt += ") FROM STDIN"
|
||||||
|
return stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
type copyin struct {
|
||||||
|
cn *conn
|
||||||
|
buffer []byte
|
||||||
|
rowData chan []byte
|
||||||
|
done chan bool
|
||||||
|
|
||||||
|
closed bool
|
||||||
|
|
||||||
|
sync.Mutex // guards err
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
const ciBufferSize = 64 * 1024
|
||||||
|
|
||||||
|
// flush buffer before the buffer is filled up and needs reallocation
|
||||||
|
const ciBufferFlushSize = 63 * 1024
|
||||||
|
|
||||||
|
func (cn *conn) prepareCopyIn(q string) (_ driver.Stmt, err error) {
|
||||||
|
if !cn.isInTransaction() {
|
||||||
|
return nil, errCopyNotSupportedOutsideTxn
|
||||||
|
}
|
||||||
|
|
||||||
|
ci := ©in{
|
||||||
|
cn: cn,
|
||||||
|
buffer: make([]byte, 0, ciBufferSize),
|
||||||
|
rowData: make(chan []byte),
|
||||||
|
done: make(chan bool, 1),
|
||||||
|
}
|
||||||
|
// add CopyData identifier + 4 bytes for message length
|
||||||
|
ci.buffer = append(ci.buffer, 'd', 0, 0, 0, 0)
|
||||||
|
|
||||||
|
b := cn.writeBuf('Q')
|
||||||
|
b.string(q)
|
||||||
|
cn.send(b)
|
||||||
|
|
||||||
|
awaitCopyInResponse:
|
||||||
|
for {
|
||||||
|
t, r := cn.recv1()
|
||||||
|
switch t {
|
||||||
|
case 'G':
|
||||||
|
if r.byte() != 0 {
|
||||||
|
err = errBinaryCopyNotSupported
|
||||||
|
break awaitCopyInResponse
|
||||||
|
}
|
||||||
|
go ci.resploop()
|
||||||
|
return ci, nil
|
||||||
|
case 'H':
|
||||||
|
err = errCopyToNotSupported
|
||||||
|
break awaitCopyInResponse
|
||||||
|
case 'E':
|
||||||
|
err = parseError(r)
|
||||||
|
case 'Z':
|
||||||
|
if err == nil {
|
||||||
|
cn.bad = true
|
||||||
|
errorf("unexpected ReadyForQuery in response to COPY")
|
||||||
|
}
|
||||||
|
cn.processReadyForQuery(r)
|
||||||
|
return nil, err
|
||||||
|
default:
|
||||||
|
cn.bad = true
|
||||||
|
errorf("unknown response for copy query: %q", t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// something went wrong, abort COPY before we return
|
||||||
|
b = cn.writeBuf('f')
|
||||||
|
b.string(err.Error())
|
||||||
|
cn.send(b)
|
||||||
|
|
||||||
|
for {
|
||||||
|
t, r := cn.recv1()
|
||||||
|
switch t {
|
||||||
|
case 'c', 'C', 'E':
|
||||||
|
case 'Z':
|
||||||
|
// correctly aborted, we're done
|
||||||
|
cn.processReadyForQuery(r)
|
||||||
|
return nil, err
|
||||||
|
default:
|
||||||
|
cn.bad = true
|
||||||
|
errorf("unknown response for CopyFail: %q", t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ci *copyin) flush(buf []byte) {
|
||||||
|
// set message length (without message identifier)
|
||||||
|
binary.BigEndian.PutUint32(buf[1:], uint32(len(buf)-1))
|
||||||
|
|
||||||
|
_, err := ci.cn.c.Write(buf)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ci *copyin) resploop() {
|
||||||
|
for {
|
||||||
|
var r readBuf
|
||||||
|
t, err := ci.cn.recvMessage(&r)
|
||||||
|
if err != nil {
|
||||||
|
ci.cn.bad = true
|
||||||
|
ci.setError(err)
|
||||||
|
ci.done <- true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch t {
|
||||||
|
case 'C':
|
||||||
|
// complete
|
||||||
|
case 'N':
|
||||||
|
// NoticeResponse
|
||||||
|
case 'Z':
|
||||||
|
ci.cn.processReadyForQuery(&r)
|
||||||
|
ci.done <- true
|
||||||
|
return
|
||||||
|
case 'E':
|
||||||
|
err := parseError(&r)
|
||||||
|
ci.setError(err)
|
||||||
|
default:
|
||||||
|
ci.cn.bad = true
|
||||||
|
ci.setError(fmt.Errorf("unknown response during CopyIn: %q", t))
|
||||||
|
ci.done <- true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ci *copyin) isErrorSet() bool {
|
||||||
|
ci.Lock()
|
||||||
|
isSet := (ci.err != nil)
|
||||||
|
ci.Unlock()
|
||||||
|
return isSet
|
||||||
|
}
|
||||||
|
|
||||||
|
// setError() sets ci.err if one has not been set already. Caller must not be
|
||||||
|
// holding ci.Mutex.
|
||||||
|
func (ci *copyin) setError(err error) {
|
||||||
|
ci.Lock()
|
||||||
|
if ci.err == nil {
|
||||||
|
ci.err = err
|
||||||
|
}
|
||||||
|
ci.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ci *copyin) NumInput() int {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ci *copyin) Query(v []driver.Value) (r driver.Rows, err error) {
|
||||||
|
return nil, ErrNotSupported
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exec inserts values into the COPY stream. The insert is asynchronous
|
||||||
|
// and Exec can return errors from previous Exec calls to the same
|
||||||
|
// COPY stmt.
|
||||||
|
//
|
||||||
|
// You need to call Exec(nil) to sync the COPY stream and to get any
|
||||||
|
// errors from pending data, since Stmt.Close() doesn't return errors
|
||||||
|
// to the user.
|
||||||
|
func (ci *copyin) Exec(v []driver.Value) (r driver.Result, err error) {
|
||||||
|
if ci.closed {
|
||||||
|
return nil, errCopyInClosed
|
||||||
|
}
|
||||||
|
|
||||||
|
if ci.cn.bad {
|
||||||
|
return nil, driver.ErrBadConn
|
||||||
|
}
|
||||||
|
defer ci.cn.errRecover(&err)
|
||||||
|
|
||||||
|
if ci.isErrorSet() {
|
||||||
|
return nil, ci.err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(v) == 0 {
|
||||||
|
return nil, ci.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
numValues := len(v)
|
||||||
|
for i, value := range v {
|
||||||
|
ci.buffer = appendEncodedText(&ci.cn.parameterStatus, ci.buffer, value)
|
||||||
|
if i < numValues-1 {
|
||||||
|
ci.buffer = append(ci.buffer, '\t')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ci.buffer = append(ci.buffer, '\n')
|
||||||
|
|
||||||
|
if len(ci.buffer) > ciBufferFlushSize {
|
||||||
|
ci.flush(ci.buffer)
|
||||||
|
// reset buffer, keep bytes for message identifier and length
|
||||||
|
ci.buffer = ci.buffer[:5]
|
||||||
|
}
|
||||||
|
|
||||||
|
return driver.RowsAffected(0), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ci *copyin) Close() (err error) {
|
||||||
|
if ci.closed { // Don't do anything, we're already closed
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ci.closed = true
|
||||||
|
|
||||||
|
if ci.cn.bad {
|
||||||
|
return driver.ErrBadConn
|
||||||
|
}
|
||||||
|
defer ci.cn.errRecover(&err)
|
||||||
|
|
||||||
|
if len(ci.buffer) > 0 {
|
||||||
|
ci.flush(ci.buffer)
|
||||||
|
}
|
||||||
|
// Avoid touching the scratch buffer as resploop could be using it.
|
||||||
|
err = ci.cn.sendSimpleMessage('c')
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
<-ci.done
|
||||||
|
|
||||||
|
if ci.isErrorSet() {
|
||||||
|
err = ci.err
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
465
vendor/github.com/lib/pq/copy_test.go
generated
vendored
Normal file
465
vendor/github.com/lib/pq/copy_test.go
generated
vendored
Normal file
@ -0,0 +1,465 @@
|
|||||||
|
package pq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"database/sql"
|
||||||
|
"database/sql/driver"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCopyInStmt(t *testing.T) {
|
||||||
|
var stmt string
|
||||||
|
stmt = CopyIn("table name")
|
||||||
|
if stmt != `COPY "table name" () FROM STDIN` {
|
||||||
|
t.Fatal(stmt)
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt = CopyIn("table name", "column 1", "column 2")
|
||||||
|
if stmt != `COPY "table name" ("column 1", "column 2") FROM STDIN` {
|
||||||
|
t.Fatal(stmt)
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt = CopyIn(`table " name """`, `co"lumn""`)
|
||||||
|
if stmt != `COPY "table "" name """"""" ("co""lumn""""") FROM STDIN` {
|
||||||
|
t.Fatal(stmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCopyInSchemaStmt(t *testing.T) {
|
||||||
|
var stmt string
|
||||||
|
stmt = CopyInSchema("schema name", "table name")
|
||||||
|
if stmt != `COPY "schema name"."table name" () FROM STDIN` {
|
||||||
|
t.Fatal(stmt)
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt = CopyInSchema("schema name", "table name", "column 1", "column 2")
|
||||||
|
if stmt != `COPY "schema name"."table name" ("column 1", "column 2") FROM STDIN` {
|
||||||
|
t.Fatal(stmt)
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt = CopyInSchema(`schema " name """`, `table " name """`, `co"lumn""`)
|
||||||
|
if stmt != `COPY "schema "" name """"""".`+
|
||||||
|
`"table "" name """"""" ("co""lumn""""") FROM STDIN` {
|
||||||
|
t.Fatal(stmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCopyInMultipleValues(t *testing.T) {
|
||||||
|
db := openTestConn(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
txn, err := db.Begin()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer txn.Rollback()
|
||||||
|
|
||||||
|
_, err = txn.Exec("CREATE TEMP TABLE temp (a int, b varchar)")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt, err := txn.Prepare(CopyIn("temp", "a", "b"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
longString := strings.Repeat("#", 500)
|
||||||
|
|
||||||
|
for i := 0; i < 500; i++ {
|
||||||
|
_, err = stmt.Exec(int64(i), longString)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = stmt.Exec()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = stmt.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var num int
|
||||||
|
err = txn.QueryRow("SELECT COUNT(*) FROM temp").Scan(&num)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if num != 500 {
|
||||||
|
t.Fatalf("expected 500 items, not %d", num)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCopyInRaiseStmtTrigger(t *testing.T) {
|
||||||
|
db := openTestConn(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
if getServerVersion(t, db) < 90000 {
|
||||||
|
var exists int
|
||||||
|
err := db.QueryRow("SELECT 1 FROM pg_language WHERE lanname = 'plpgsql'").Scan(&exists)
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
t.Skip("language PL/PgSQL does not exist; skipping TestCopyInRaiseStmtTrigger")
|
||||||
|
} else if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
txn, err := db.Begin()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer txn.Rollback()
|
||||||
|
|
||||||
|
_, err = txn.Exec("CREATE TEMP TABLE temp (a int, b varchar)")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = txn.Exec(`
|
||||||
|
CREATE OR REPLACE FUNCTION pg_temp.temptest()
|
||||||
|
RETURNS trigger AS
|
||||||
|
$BODY$ begin
|
||||||
|
raise notice 'Hello world';
|
||||||
|
return new;
|
||||||
|
end $BODY$
|
||||||
|
LANGUAGE plpgsql`)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = txn.Exec(`
|
||||||
|
CREATE TRIGGER temptest_trigger
|
||||||
|
BEFORE INSERT
|
||||||
|
ON temp
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE PROCEDURE pg_temp.temptest()`)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt, err := txn.Prepare(CopyIn("temp", "a", "b"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
longString := strings.Repeat("#", 500)
|
||||||
|
|
||||||
|
_, err = stmt.Exec(int64(1), longString)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = stmt.Exec()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = stmt.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var num int
|
||||||
|
err = txn.QueryRow("SELECT COUNT(*) FROM temp").Scan(&num)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if num != 1 {
|
||||||
|
t.Fatalf("expected 1 items, not %d", num)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCopyInTypes(t *testing.T) {
|
||||||
|
db := openTestConn(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
txn, err := db.Begin()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer txn.Rollback()
|
||||||
|
|
||||||
|
_, err = txn.Exec("CREATE TEMP TABLE temp (num INTEGER, text VARCHAR, blob BYTEA, nothing VARCHAR)")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt, err := txn.Prepare(CopyIn("temp", "num", "text", "blob", "nothing"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = stmt.Exec(int64(1234567890), "Héllö\n ☃!\r\t\\", []byte{0, 255, 9, 10, 13}, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = stmt.Exec()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = stmt.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var num int
|
||||||
|
var text string
|
||||||
|
var blob []byte
|
||||||
|
var nothing sql.NullString
|
||||||
|
|
||||||
|
err = txn.QueryRow("SELECT * FROM temp").Scan(&num, &text, &blob, ¬hing)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if num != 1234567890 {
|
||||||
|
t.Fatal("unexpected result", num)
|
||||||
|
}
|
||||||
|
if text != "Héllö\n ☃!\r\t\\" {
|
||||||
|
t.Fatal("unexpected result", text)
|
||||||
|
}
|
||||||
|
if bytes.Compare(blob, []byte{0, 255, 9, 10, 13}) != 0 {
|
||||||
|
t.Fatal("unexpected result", blob)
|
||||||
|
}
|
||||||
|
if nothing.Valid {
|
||||||
|
t.Fatal("unexpected result", nothing.String)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCopyInWrongType(t *testing.T) {
|
||||||
|
db := openTestConn(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
txn, err := db.Begin()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer txn.Rollback()
|
||||||
|
|
||||||
|
_, err = txn.Exec("CREATE TEMP TABLE temp (num INTEGER)")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt, err := txn.Prepare(CopyIn("temp", "num"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
|
||||||
|
_, err = stmt.Exec("Héllö\n ☃!\r\t\\")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = stmt.Exec()
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error")
|
||||||
|
}
|
||||||
|
if pge := err.(*Error); pge.Code.Name() != "invalid_text_representation" {
|
||||||
|
t.Fatalf("expected 'invalid input syntax for integer' error, got %s (%+v)", pge.Code.Name(), pge)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCopyOutsideOfTxnError(t *testing.T) {
|
||||||
|
db := openTestConn(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
_, err := db.Prepare(CopyIn("temp", "num"))
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("COPY outside of transaction did not return an error")
|
||||||
|
}
|
||||||
|
if err != errCopyNotSupportedOutsideTxn {
|
||||||
|
t.Fatalf("expected %s, got %s", err, err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCopyInBinaryError(t *testing.T) {
|
||||||
|
db := openTestConn(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
txn, err := db.Begin()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer txn.Rollback()
|
||||||
|
|
||||||
|
_, err = txn.Exec("CREATE TEMP TABLE temp (num INTEGER)")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
_, err = txn.Prepare("COPY temp (num) FROM STDIN WITH binary")
|
||||||
|
if err != errBinaryCopyNotSupported {
|
||||||
|
t.Fatalf("expected %s, got %+v", errBinaryCopyNotSupported, err)
|
||||||
|
}
|
||||||
|
// check that the protocol is in a valid state
|
||||||
|
err = txn.Rollback()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCopyFromError(t *testing.T) {
|
||||||
|
db := openTestConn(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
txn, err := db.Begin()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer txn.Rollback()
|
||||||
|
|
||||||
|
_, err = txn.Exec("CREATE TEMP TABLE temp (num INTEGER)")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
_, err = txn.Prepare("COPY temp (num) TO STDOUT")
|
||||||
|
if err != errCopyToNotSupported {
|
||||||
|
t.Fatalf("expected %s, got %+v", errCopyToNotSupported, err)
|
||||||
|
}
|
||||||
|
// check that the protocol is in a valid state
|
||||||
|
err = txn.Rollback()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCopySyntaxError(t *testing.T) {
|
||||||
|
db := openTestConn(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
txn, err := db.Begin()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer txn.Rollback()
|
||||||
|
|
||||||
|
_, err = txn.Prepare("COPY ")
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error")
|
||||||
|
}
|
||||||
|
if pge := err.(*Error); pge.Code.Name() != "syntax_error" {
|
||||||
|
t.Fatalf("expected syntax error, got %s (%+v)", pge.Code.Name(), pge)
|
||||||
|
}
|
||||||
|
// check that the protocol is in a valid state
|
||||||
|
err = txn.Rollback()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests for connection errors in copyin.resploop()
|
||||||
|
func TestCopyRespLoopConnectionError(t *testing.T) {
|
||||||
|
db := openTestConn(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
txn, err := db.Begin()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer txn.Rollback()
|
||||||
|
|
||||||
|
var pid int
|
||||||
|
err = txn.QueryRow("SELECT pg_backend_pid()").Scan(&pid)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = txn.Exec("CREATE TEMP TABLE temp (a int)")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt, err := txn.Prepare(CopyIn("temp", "a"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
|
||||||
|
_, err = db.Exec("SELECT pg_terminate_backend($1)", pid)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if getServerVersion(t, db) < 90500 {
|
||||||
|
// We have to try and send something over, since postgres before
|
||||||
|
// version 9.5 won't process SIGTERMs while it's waiting for
|
||||||
|
// CopyData/CopyEnd messages; see tcop/postgres.c.
|
||||||
|
_, err = stmt.Exec(1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, err = stmt.Exec()
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected error")
|
||||||
|
}
|
||||||
|
pge, ok := err.(*Error)
|
||||||
|
if !ok {
|
||||||
|
if err == driver.ErrBadConn {
|
||||||
|
// likely an EPIPE
|
||||||
|
} else {
|
||||||
|
t.Fatalf("expected *pq.Error or driver.ErrBadConn, got %+#v", err)
|
||||||
|
}
|
||||||
|
} else if pge.Code.Name() != "admin_shutdown" {
|
||||||
|
t.Fatalf("expected admin_shutdown, got %s", pge.Code.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = stmt.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkCopyIn(b *testing.B) {
|
||||||
|
db := openTestConn(b)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
txn, err := db.Begin()
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
defer txn.Rollback()
|
||||||
|
|
||||||
|
_, err = txn.Exec("CREATE TEMP TABLE temp (a int, b varchar)")
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt, err := txn.Prepare(CopyIn("temp", "a", "b"))
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_, err = stmt.Exec(int64(i), "hello world!")
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = stmt.Exec()
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = stmt.Close()
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var num int
|
||||||
|
err = txn.QueryRow("SELECT COUNT(*) FROM temp").Scan(&num)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if num != b.N {
|
||||||
|
b.Fatalf("expected %d items, not %d", b.N, num)
|
||||||
|
}
|
||||||
|
}
|
212
vendor/github.com/lib/pq/doc.go
generated
vendored
Normal file
212
vendor/github.com/lib/pq/doc.go
generated
vendored
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
/*
|
||||||
|
Package pq is a pure Go Postgres driver for the database/sql package.
|
||||||
|
|
||||||
|
In most cases clients will use the database/sql package instead of
|
||||||
|
using this package directly. For example:
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
|
_ "github.com/lib/pq"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
db, err := sql.Open("postgres", "user=pqgotest dbname=pqgotest sslmode=verify-full")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
age := 21
|
||||||
|
rows, err := db.Query("SELECT name FROM users WHERE age = $1", age)
|
||||||
|
…
|
||||||
|
}
|
||||||
|
|
||||||
|
You can also connect to a database using a URL. For example:
|
||||||
|
|
||||||
|
db, err := sql.Open("postgres", "postgres://pqgotest:password@localhost/pqgotest?sslmode=verify-full")
|
||||||
|
|
||||||
|
|
||||||
|
Connection String Parameters
|
||||||
|
|
||||||
|
|
||||||
|
Similarly to libpq, when establishing a connection using pq you are expected to
|
||||||
|
supply a connection string containing zero or more parameters.
|
||||||
|
A subset of the connection parameters supported by libpq are also supported by pq.
|
||||||
|
Additionally, pq also lets you specify run-time parameters (such as search_path or work_mem)
|
||||||
|
directly in the connection string. This is different from libpq, which does not allow
|
||||||
|
run-time parameters in the connection string, instead requiring you to supply
|
||||||
|
them in the options parameter.
|
||||||
|
|
||||||
|
For compatibility with libpq, the following special connection parameters are
|
||||||
|
supported:
|
||||||
|
|
||||||
|
* dbname - The name of the database to connect to
|
||||||
|
* user - The user to sign in as
|
||||||
|
* password - The user's password
|
||||||
|
* host - The host to connect to. Values that start with / are for unix domain sockets. (default is localhost)
|
||||||
|
* port - The port to bind to. (default is 5432)
|
||||||
|
* sslmode - Whether or not to use SSL (default is require, this is not the default for libpq)
|
||||||
|
* fallback_application_name - An application_name to fall back to if one isn't provided.
|
||||||
|
* connect_timeout - Maximum wait for connection, in seconds. Zero or not specified means wait indefinitely.
|
||||||
|
* sslcert - Cert file location. The file must contain PEM encoded data.
|
||||||
|
* sslkey - Key file location. The file must contain PEM encoded data.
|
||||||
|
* sslrootcert - The location of the root certificate file. The file must contain PEM encoded data.
|
||||||
|
|
||||||
|
Valid values for sslmode are:
|
||||||
|
|
||||||
|
* disable - No SSL
|
||||||
|
* require - Always SSL (skip verification)
|
||||||
|
* verify-ca - Always SSL (verify that the certificate presented by the server was signed by a trusted CA)
|
||||||
|
* verify-full - Always SSL (verify that the certification presented by the server was signed by a trusted CA and the server host name matches the one in the certificate)
|
||||||
|
|
||||||
|
See http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING
|
||||||
|
for more information about connection string parameters.
|
||||||
|
|
||||||
|
Use single quotes for values that contain whitespace:
|
||||||
|
|
||||||
|
"user=pqgotest password='with spaces'"
|
||||||
|
|
||||||
|
A backslash will escape the next character in values:
|
||||||
|
|
||||||
|
"user=space\ man password='it\'s valid'
|
||||||
|
|
||||||
|
Note that the connection parameter client_encoding (which sets the
|
||||||
|
text encoding for the connection) may be set but must be "UTF8",
|
||||||
|
matching with the same rules as Postgres. It is an error to provide
|
||||||
|
any other value.
|
||||||
|
|
||||||
|
In addition to the parameters listed above, any run-time parameter that can be
|
||||||
|
set at backend start time can be set in the connection string. For more
|
||||||
|
information, see
|
||||||
|
http://www.postgresql.org/docs/current/static/runtime-config.html.
|
||||||
|
|
||||||
|
Most environment variables as specified at http://www.postgresql.org/docs/current/static/libpq-envars.html
|
||||||
|
supported by libpq are also supported by pq. If any of the environment
|
||||||
|
variables not supported by pq are set, pq will panic during connection
|
||||||
|
establishment. Environment variables have a lower precedence than explicitly
|
||||||
|
provided connection parameters.
|
||||||
|
|
||||||
|
The pgpass mechanism as described in http://www.postgresql.org/docs/current/static/libpq-pgpass.html
|
||||||
|
is supported, but on Windows PGPASSFILE must be specified explicitly.
|
||||||
|
|
||||||
|
Queries
|
||||||
|
|
||||||
|
database/sql does not dictate any specific format for parameter
|
||||||
|
markers in query strings, and pq uses the Postgres-native ordinal markers,
|
||||||
|
as shown above. The same marker can be reused for the same parameter:
|
||||||
|
|
||||||
|
rows, err := db.Query(`SELECT name FROM users WHERE favorite_fruit = $1
|
||||||
|
OR age BETWEEN $2 AND $2 + 3`, "orange", 64)
|
||||||
|
|
||||||
|
pq does not support the LastInsertId() method of the Result type in database/sql.
|
||||||
|
To return the identifier of an INSERT (or UPDATE or DELETE), use the Postgres
|
||||||
|
RETURNING clause with a standard Query or QueryRow call:
|
||||||
|
|
||||||
|
var userid int
|
||||||
|
err := db.QueryRow(`INSERT INTO users(name, favorite_fruit, age)
|
||||||
|
VALUES('beatrice', 'starfruit', 93) RETURNING id`).Scan(&userid)
|
||||||
|
|
||||||
|
For more details on RETURNING, see the Postgres documentation:
|
||||||
|
|
||||||
|
http://www.postgresql.org/docs/current/static/sql-insert.html
|
||||||
|
http://www.postgresql.org/docs/current/static/sql-update.html
|
||||||
|
http://www.postgresql.org/docs/current/static/sql-delete.html
|
||||||
|
|
||||||
|
For additional instructions on querying see the documentation for the database/sql package.
|
||||||
|
|
||||||
|
Errors
|
||||||
|
|
||||||
|
pq may return errors of type *pq.Error which can be interrogated for error details:
|
||||||
|
|
||||||
|
if err, ok := err.(*pq.Error); ok {
|
||||||
|
fmt.Println("pq error:", err.Code.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
See the pq.Error type for details.
|
||||||
|
|
||||||
|
|
||||||
|
Bulk imports
|
||||||
|
|
||||||
|
You can perform bulk imports by preparing a statement returned by pq.CopyIn (or
|
||||||
|
pq.CopyInSchema) in an explicit transaction (sql.Tx). The returned statement
|
||||||
|
handle can then be repeatedly "executed" to copy data into the target table.
|
||||||
|
After all data has been processed you should call Exec() once with no arguments
|
||||||
|
to flush all buffered data. Any call to Exec() might return an error which
|
||||||
|
should be handled appropriately, but because of the internal buffering an error
|
||||||
|
returned by Exec() might not be related to the data passed in the call that
|
||||||
|
failed.
|
||||||
|
|
||||||
|
CopyIn uses COPY FROM internally. It is not possible to COPY outside of an
|
||||||
|
explicit transaction in pq.
|
||||||
|
|
||||||
|
Usage example:
|
||||||
|
|
||||||
|
txn, err := db.Begin()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt, err := txn.Prepare(pq.CopyIn("users", "name", "age"))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, user := range users {
|
||||||
|
_, err = stmt.Exec(user.Name, int64(user.Age))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = stmt.Exec()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = stmt.Close()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = txn.Commit()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Notifications
|
||||||
|
|
||||||
|
|
||||||
|
PostgreSQL supports a simple publish/subscribe model over database
|
||||||
|
connections. See http://www.postgresql.org/docs/current/static/sql-notify.html
|
||||||
|
for more information about the general mechanism.
|
||||||
|
|
||||||
|
To start listening for notifications, you first have to open a new connection
|
||||||
|
to the database by calling NewListener. This connection can not be used for
|
||||||
|
anything other than LISTEN / NOTIFY. Calling Listen will open a "notification
|
||||||
|
channel"; once a notification channel is open, a notification generated on that
|
||||||
|
channel will effect a send on the Listener.Notify channel. A notification
|
||||||
|
channel will remain open until Unlisten is called, though connection loss might
|
||||||
|
result in some notifications being lost. To solve this problem, Listener sends
|
||||||
|
a nil pointer over the Notify channel any time the connection is re-established
|
||||||
|
following a connection loss. The application can get information about the
|
||||||
|
state of the underlying connection by setting an event callback in the call to
|
||||||
|
NewListener.
|
||||||
|
|
||||||
|
A single Listener can safely be used from concurrent goroutines, which means
|
||||||
|
that there is often no need to create more than one Listener in your
|
||||||
|
application. However, a Listener is always connected to a single database, so
|
||||||
|
you will need to create a new Listener instance for every database you want to
|
||||||
|
receive notifications in.
|
||||||
|
|
||||||
|
The channel name in both Listen and Unlisten is case sensitive, and can contain
|
||||||
|
any characters legal in an identifier (see
|
||||||
|
http://www.postgresql.org/docs/current/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
|
||||||
|
for more information). Note that the channel name will be truncated to 63
|
||||||
|
bytes by the PostgreSQL server.
|
||||||
|
|
||||||
|
You can find a complete, working example of Listener usage at
|
||||||
|
http://godoc.org/github.com/lib/pq/listen_example.
|
||||||
|
|
||||||
|
*/
|
||||||
|
package pq
|
579
vendor/github.com/lib/pq/encode.go
generated
vendored
Normal file
579
vendor/github.com/lib/pq/encode.go
generated
vendored
Normal file
@ -0,0 +1,579 @@
|
|||||||
|
package pq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"database/sql/driver"
|
||||||
|
"encoding/binary"
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/lib/pq/oid"
|
||||||
|
)
|
||||||
|
|
||||||
|
func binaryEncode(parameterStatus *parameterStatus, x interface{}) []byte {
|
||||||
|
switch v := x.(type) {
|
||||||
|
case []byte:
|
||||||
|
return v
|
||||||
|
default:
|
||||||
|
return encode(parameterStatus, x, oid.T_unknown)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func encode(parameterStatus *parameterStatus, x interface{}, pgtypOid oid.Oid) []byte {
|
||||||
|
switch v := x.(type) {
|
||||||
|
case int64:
|
||||||
|
return strconv.AppendInt(nil, v, 10)
|
||||||
|
case float64:
|
||||||
|
return strconv.AppendFloat(nil, v, 'f', -1, 64)
|
||||||
|
case []byte:
|
||||||
|
if pgtypOid == oid.T_bytea {
|
||||||
|
return encodeBytea(parameterStatus.serverVersion, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return v
|
||||||
|
case string:
|
||||||
|
if pgtypOid == oid.T_bytea {
|
||||||
|
return encodeBytea(parameterStatus.serverVersion, []byte(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
return []byte(v)
|
||||||
|
case bool:
|
||||||
|
return strconv.AppendBool(nil, v)
|
||||||
|
case time.Time:
|
||||||
|
return formatTs(v)
|
||||||
|
|
||||||
|
default:
|
||||||
|
errorf("encode: unknown type for %T", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
panic("not reached")
|
||||||
|
}
|
||||||
|
|
||||||
|
func decode(parameterStatus *parameterStatus, s []byte, typ oid.Oid, f format) interface{} {
|
||||||
|
if f == formatBinary {
|
||||||
|
return binaryDecode(parameterStatus, s, typ)
|
||||||
|
} else {
|
||||||
|
return textDecode(parameterStatus, s, typ)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func binaryDecode(parameterStatus *parameterStatus, s []byte, typ oid.Oid) interface{} {
|
||||||
|
switch typ {
|
||||||
|
case oid.T_bytea:
|
||||||
|
return s
|
||||||
|
case oid.T_int8:
|
||||||
|
return int64(binary.BigEndian.Uint64(s))
|
||||||
|
case oid.T_int4:
|
||||||
|
return int64(int32(binary.BigEndian.Uint32(s)))
|
||||||
|
case oid.T_int2:
|
||||||
|
return int64(int16(binary.BigEndian.Uint16(s)))
|
||||||
|
|
||||||
|
default:
|
||||||
|
errorf("don't know how to decode binary parameter of type %d", uint32(typ))
|
||||||
|
}
|
||||||
|
|
||||||
|
panic("not reached")
|
||||||
|
}
|
||||||
|
|
||||||
|
func textDecode(parameterStatus *parameterStatus, s []byte, typ oid.Oid) interface{} {
|
||||||
|
switch typ {
|
||||||
|
case oid.T_bytea:
|
||||||
|
return parseBytea(s)
|
||||||
|
case oid.T_timestamptz:
|
||||||
|
return parseTs(parameterStatus.currentLocation, string(s))
|
||||||
|
case oid.T_timestamp, oid.T_date:
|
||||||
|
return parseTs(nil, string(s))
|
||||||
|
case oid.T_time:
|
||||||
|
return mustParse("15:04:05", typ, s)
|
||||||
|
case oid.T_timetz:
|
||||||
|
return mustParse("15:04:05-07", typ, s)
|
||||||
|
case oid.T_bool:
|
||||||
|
return s[0] == 't'
|
||||||
|
case oid.T_int8, oid.T_int4, oid.T_int2:
|
||||||
|
i, err := strconv.ParseInt(string(s), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
errorf("%s", err)
|
||||||
|
}
|
||||||
|
return i
|
||||||
|
case oid.T_float4, oid.T_float8:
|
||||||
|
bits := 64
|
||||||
|
if typ == oid.T_float4 {
|
||||||
|
bits = 32
|
||||||
|
}
|
||||||
|
f, err := strconv.ParseFloat(string(s), bits)
|
||||||
|
if err != nil {
|
||||||
|
errorf("%s", err)
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// appendEncodedText encodes item in text format as required by COPY
|
||||||
|
// and appends to buf
|
||||||
|
func appendEncodedText(parameterStatus *parameterStatus, buf []byte, x interface{}) []byte {
|
||||||
|
switch v := x.(type) {
|
||||||
|
case int64:
|
||||||
|
return strconv.AppendInt(buf, v, 10)
|
||||||
|
case float64:
|
||||||
|
return strconv.AppendFloat(buf, v, 'f', -1, 64)
|
||||||
|
case []byte:
|
||||||
|
encodedBytea := encodeBytea(parameterStatus.serverVersion, v)
|
||||||
|
return appendEscapedText(buf, string(encodedBytea))
|
||||||
|
case string:
|
||||||
|
return appendEscapedText(buf, v)
|
||||||
|
case bool:
|
||||||
|
return strconv.AppendBool(buf, v)
|
||||||
|
case time.Time:
|
||||||
|
return append(buf, formatTs(v)...)
|
||||||
|
case nil:
|
||||||
|
return append(buf, "\\N"...)
|
||||||
|
default:
|
||||||
|
errorf("encode: unknown type for %T", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
panic("not reached")
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendEscapedText(buf []byte, text string) []byte {
|
||||||
|
escapeNeeded := false
|
||||||
|
startPos := 0
|
||||||
|
var c byte
|
||||||
|
|
||||||
|
// check if we need to escape
|
||||||
|
for i := 0; i < len(text); i++ {
|
||||||
|
c = text[i]
|
||||||
|
if c == '\\' || c == '\n' || c == '\r' || c == '\t' {
|
||||||
|
escapeNeeded = true
|
||||||
|
startPos = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !escapeNeeded {
|
||||||
|
return append(buf, text...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy till first char to escape, iterate the rest
|
||||||
|
result := append(buf, text[:startPos]...)
|
||||||
|
for i := startPos; i < len(text); i++ {
|
||||||
|
c = text[i]
|
||||||
|
switch c {
|
||||||
|
case '\\':
|
||||||
|
result = append(result, '\\', '\\')
|
||||||
|
case '\n':
|
||||||
|
result = append(result, '\\', 'n')
|
||||||
|
case '\r':
|
||||||
|
result = append(result, '\\', 'r')
|
||||||
|
case '\t':
|
||||||
|
result = append(result, '\\', 't')
|
||||||
|
default:
|
||||||
|
result = append(result, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustParse(f string, typ oid.Oid, s []byte) time.Time {
|
||||||
|
str := string(s)
|
||||||
|
|
||||||
|
// check for a 30-minute-offset timezone
|
||||||
|
if (typ == oid.T_timestamptz || typ == oid.T_timetz) &&
|
||||||
|
str[len(str)-3] == ':' {
|
||||||
|
f += ":00"
|
||||||
|
}
|
||||||
|
t, err := time.Parse(f, str)
|
||||||
|
if err != nil {
|
||||||
|
errorf("decode: %s", err)
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
var invalidTimestampErr = errors.New("invalid timestamp")
|
||||||
|
|
||||||
|
type timestampParser struct {
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *timestampParser) expect(str, char string, pos int) {
|
||||||
|
if p.err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if pos+1 > len(str) {
|
||||||
|
p.err = invalidTimestampErr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if c := str[pos : pos+1]; c != char && p.err == nil {
|
||||||
|
p.err = fmt.Errorf("expected '%v' at position %v; got '%v'", char, pos, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *timestampParser) mustAtoi(str string, begin int, end int) int {
|
||||||
|
if p.err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if begin < 0 || end < 0 || begin > end || end > len(str) {
|
||||||
|
p.err = invalidTimestampErr
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
result, err := strconv.Atoi(str[begin:end])
|
||||||
|
if err != nil {
|
||||||
|
if p.err == nil {
|
||||||
|
p.err = fmt.Errorf("expected number; got '%v'", str)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// The location cache caches the time zones typically used by the client.
|
||||||
|
type locationCache struct {
|
||||||
|
cache map[int]*time.Location
|
||||||
|
lock sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// All connections share the same list of timezones. Benchmarking shows that
|
||||||
|
// about 5% speed could be gained by putting the cache in the connection and
|
||||||
|
// losing the mutex, at the cost of a small amount of memory and a somewhat
|
||||||
|
// significant increase in code complexity.
|
||||||
|
var globalLocationCache *locationCache = newLocationCache()
|
||||||
|
|
||||||
|
func newLocationCache() *locationCache {
|
||||||
|
return &locationCache{cache: make(map[int]*time.Location)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the cached timezone for the specified offset, creating and caching
|
||||||
|
// it if necessary.
|
||||||
|
func (c *locationCache) getLocation(offset int) *time.Location {
|
||||||
|
c.lock.Lock()
|
||||||
|
defer c.lock.Unlock()
|
||||||
|
|
||||||
|
location, ok := c.cache[offset]
|
||||||
|
if !ok {
|
||||||
|
location = time.FixedZone("", offset)
|
||||||
|
c.cache[offset] = location
|
||||||
|
}
|
||||||
|
|
||||||
|
return location
|
||||||
|
}
|
||||||
|
|
||||||
|
var infinityTsEnabled = false
|
||||||
|
var infinityTsNegative time.Time
|
||||||
|
var infinityTsPositive time.Time
|
||||||
|
|
||||||
|
const (
|
||||||
|
infinityTsEnabledAlready = "pq: infinity timestamp enabled already"
|
||||||
|
infinityTsNegativeMustBeSmaller = "pq: infinity timestamp: negative value must be smaller (before) than positive"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If EnableInfinityTs is not called, "-infinity" and "infinity" will return
|
||||||
|
* []byte("-infinity") and []byte("infinity") respectively, and potentially
|
||||||
|
* cause error "sql: Scan error on column index 0: unsupported driver -> Scan pair: []uint8 -> *time.Time",
|
||||||
|
* when scanning into a time.Time value.
|
||||||
|
*
|
||||||
|
* Once EnableInfinityTs has been called, all connections created using this
|
||||||
|
* driver will decode Postgres' "-infinity" and "infinity" for "timestamp",
|
||||||
|
* "timestamp with time zone" and "date" types to the predefined minimum and
|
||||||
|
* maximum times, respectively. When encoding time.Time values, any time which
|
||||||
|
* equals or precedes the predefined minimum time will be encoded to
|
||||||
|
* "-infinity". Any values at or past the maximum time will similarly be
|
||||||
|
* encoded to "infinity".
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* If EnableInfinityTs is called with negative >= positive, it will panic.
|
||||||
|
* Calling EnableInfinityTs after a connection has been established results in
|
||||||
|
* undefined behavior. If EnableInfinityTs is called more than once, it will
|
||||||
|
* panic.
|
||||||
|
*/
|
||||||
|
func EnableInfinityTs(negative time.Time, positive time.Time) {
|
||||||
|
if infinityTsEnabled {
|
||||||
|
panic(infinityTsEnabledAlready)
|
||||||
|
}
|
||||||
|
if !negative.Before(positive) {
|
||||||
|
panic(infinityTsNegativeMustBeSmaller)
|
||||||
|
}
|
||||||
|
infinityTsEnabled = true
|
||||||
|
infinityTsNegative = negative
|
||||||
|
infinityTsPositive = positive
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Testing might want to toggle infinityTsEnabled
|
||||||
|
*/
|
||||||
|
func disableInfinityTs() {
|
||||||
|
infinityTsEnabled = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a time function specific to the Postgres default DateStyle
|
||||||
|
// setting ("ISO, MDY"), the only one we currently support. This
|
||||||
|
// accounts for the discrepancies between the parsing available with
|
||||||
|
// time.Parse and the Postgres date formatting quirks.
|
||||||
|
func parseTs(currentLocation *time.Location, str string) interface{} {
|
||||||
|
switch str {
|
||||||
|
case "-infinity":
|
||||||
|
if infinityTsEnabled {
|
||||||
|
return infinityTsNegative
|
||||||
|
}
|
||||||
|
return []byte(str)
|
||||||
|
case "infinity":
|
||||||
|
if infinityTsEnabled {
|
||||||
|
return infinityTsPositive
|
||||||
|
}
|
||||||
|
return []byte(str)
|
||||||
|
}
|
||||||
|
t, err := ParseTimestamp(currentLocation, str)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseTimestamp parses Postgres' text format. It returns a time.Time in
|
||||||
|
// currentLocation iff that time's offset agrees with the offset sent from the
|
||||||
|
// Postgres server. Otherwise, ParseTimestamp returns a time.Time with the
|
||||||
|
// fixed offset offset provided by the Postgres server.
|
||||||
|
func ParseTimestamp(currentLocation *time.Location, str string) (time.Time, error) {
|
||||||
|
p := timestampParser{}
|
||||||
|
|
||||||
|
monSep := strings.IndexRune(str, '-')
|
||||||
|
// this is Gregorian year, not ISO Year
|
||||||
|
// In Gregorian system, the year 1 BC is followed by AD 1
|
||||||
|
year := p.mustAtoi(str, 0, monSep)
|
||||||
|
daySep := monSep + 3
|
||||||
|
month := p.mustAtoi(str, monSep+1, daySep)
|
||||||
|
p.expect(str, "-", daySep)
|
||||||
|
timeSep := daySep + 3
|
||||||
|
day := p.mustAtoi(str, daySep+1, timeSep)
|
||||||
|
|
||||||
|
var hour, minute, second int
|
||||||
|
if len(str) > monSep+len("01-01")+1 {
|
||||||
|
p.expect(str, " ", timeSep)
|
||||||
|
minSep := timeSep + 3
|
||||||
|
p.expect(str, ":", minSep)
|
||||||
|
hour = p.mustAtoi(str, timeSep+1, minSep)
|
||||||
|
secSep := minSep + 3
|
||||||
|
p.expect(str, ":", secSep)
|
||||||
|
minute = p.mustAtoi(str, minSep+1, secSep)
|
||||||
|
secEnd := secSep + 3
|
||||||
|
second = p.mustAtoi(str, secSep+1, secEnd)
|
||||||
|
}
|
||||||
|
remainderIdx := monSep + len("01-01 00:00:00") + 1
|
||||||
|
// Three optional (but ordered) sections follow: the
|
||||||
|
// fractional seconds, the time zone offset, and the BC
|
||||||
|
// designation. We set them up here and adjust the other
|
||||||
|
// offsets if the preceding sections exist.
|
||||||
|
|
||||||
|
nanoSec := 0
|
||||||
|
tzOff := 0
|
||||||
|
|
||||||
|
if remainderIdx+1 <= len(str) && str[remainderIdx:remainderIdx+1] == "." {
|
||||||
|
fracStart := remainderIdx + 1
|
||||||
|
fracOff := strings.IndexAny(str[fracStart:], "-+ ")
|
||||||
|
if fracOff < 0 {
|
||||||
|
fracOff = len(str) - fracStart
|
||||||
|
}
|
||||||
|
fracSec := p.mustAtoi(str, fracStart, fracStart+fracOff)
|
||||||
|
nanoSec = fracSec * (1000000000 / int(math.Pow(10, float64(fracOff))))
|
||||||
|
|
||||||
|
remainderIdx += fracOff + 1
|
||||||
|
}
|
||||||
|
if tzStart := remainderIdx; tzStart+1 <= len(str) && (str[tzStart:tzStart+1] == "-" || str[tzStart:tzStart+1] == "+") {
|
||||||
|
// time zone separator is always '-' or '+' (UTC is +00)
|
||||||
|
var tzSign int
|
||||||
|
if c := str[tzStart : tzStart+1]; c == "-" {
|
||||||
|
tzSign = -1
|
||||||
|
} else if c == "+" {
|
||||||
|
tzSign = +1
|
||||||
|
} else {
|
||||||
|
return time.Time{}, fmt.Errorf("expected '-' or '+' at position %v; got %v", tzStart, c)
|
||||||
|
}
|
||||||
|
tzHours := p.mustAtoi(str, tzStart+1, tzStart+3)
|
||||||
|
remainderIdx += 3
|
||||||
|
var tzMin, tzSec int
|
||||||
|
if tzStart+4 <= len(str) && str[tzStart+3:tzStart+4] == ":" {
|
||||||
|
tzMin = p.mustAtoi(str, tzStart+4, tzStart+6)
|
||||||
|
remainderIdx += 3
|
||||||
|
}
|
||||||
|
if tzStart+7 <= len(str) && str[tzStart+6:tzStart+7] == ":" {
|
||||||
|
tzSec = p.mustAtoi(str, tzStart+7, tzStart+9)
|
||||||
|
remainderIdx += 3
|
||||||
|
}
|
||||||
|
tzOff = tzSign * ((tzHours * 60 * 60) + (tzMin * 60) + tzSec)
|
||||||
|
}
|
||||||
|
var isoYear int
|
||||||
|
if remainderIdx+3 <= len(str) && str[remainderIdx:remainderIdx+3] == " BC" {
|
||||||
|
isoYear = 1 - year
|
||||||
|
remainderIdx += 3
|
||||||
|
} else {
|
||||||
|
isoYear = year
|
||||||
|
}
|
||||||
|
if remainderIdx < len(str) {
|
||||||
|
return time.Time{}, fmt.Errorf("expected end of input, got %v", str[remainderIdx:])
|
||||||
|
}
|
||||||
|
t := time.Date(isoYear, time.Month(month), day,
|
||||||
|
hour, minute, second, nanoSec,
|
||||||
|
globalLocationCache.getLocation(tzOff))
|
||||||
|
|
||||||
|
if currentLocation != nil {
|
||||||
|
// Set the location of the returned Time based on the session's
|
||||||
|
// TimeZone value, but only if the local time zone database agrees with
|
||||||
|
// the remote database on the offset.
|
||||||
|
lt := t.In(currentLocation)
|
||||||
|
_, newOff := lt.Zone()
|
||||||
|
if newOff == tzOff {
|
||||||
|
t = lt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return t, p.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// formatTs formats t into a format postgres understands.
|
||||||
|
func formatTs(t time.Time) []byte {
|
||||||
|
if infinityTsEnabled {
|
||||||
|
// t <= -infinity : ! (t > -infinity)
|
||||||
|
if !t.After(infinityTsNegative) {
|
||||||
|
return []byte("-infinity")
|
||||||
|
}
|
||||||
|
// t >= infinity : ! (!t < infinity)
|
||||||
|
if !t.Before(infinityTsPositive) {
|
||||||
|
return []byte("infinity")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FormatTimestamp(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormatTimestamp formats t into Postgres' text format for timestamps.
|
||||||
|
func FormatTimestamp(t time.Time) []byte {
|
||||||
|
// Need to send dates before 0001 A.D. with " BC" suffix, instead of the
|
||||||
|
// minus sign preferred by Go.
|
||||||
|
// Beware, "0000" in ISO is "1 BC", "-0001" is "2 BC" and so on
|
||||||
|
bc := false
|
||||||
|
if t.Year() <= 0 {
|
||||||
|
// flip year sign, and add 1, e.g: "0" will be "1", and "-10" will be "11"
|
||||||
|
t = t.AddDate((-t.Year())*2+1, 0, 0)
|
||||||
|
bc = true
|
||||||
|
}
|
||||||
|
b := []byte(t.Format(time.RFC3339Nano))
|
||||||
|
|
||||||
|
_, offset := t.Zone()
|
||||||
|
offset = offset % 60
|
||||||
|
if offset != 0 {
|
||||||
|
// RFC3339Nano already printed the minus sign
|
||||||
|
if offset < 0 {
|
||||||
|
offset = -offset
|
||||||
|
}
|
||||||
|
|
||||||
|
b = append(b, ':')
|
||||||
|
if offset < 10 {
|
||||||
|
b = append(b, '0')
|
||||||
|
}
|
||||||
|
b = strconv.AppendInt(b, int64(offset), 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
if bc {
|
||||||
|
b = append(b, " BC"...)
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse a bytea value received from the server. Both "hex" and the legacy
|
||||||
|
// "escape" format are supported.
|
||||||
|
func parseBytea(s []byte) (result []byte) {
|
||||||
|
if len(s) >= 2 && bytes.Equal(s[:2], []byte("\\x")) {
|
||||||
|
// bytea_output = hex
|
||||||
|
s = s[2:] // trim off leading "\\x"
|
||||||
|
result = make([]byte, hex.DecodedLen(len(s)))
|
||||||
|
_, err := hex.Decode(result, s)
|
||||||
|
if err != nil {
|
||||||
|
errorf("%s", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// bytea_output = escape
|
||||||
|
for len(s) > 0 {
|
||||||
|
if s[0] == '\\' {
|
||||||
|
// escaped '\\'
|
||||||
|
if len(s) >= 2 && s[1] == '\\' {
|
||||||
|
result = append(result, '\\')
|
||||||
|
s = s[2:]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// '\\' followed by an octal number
|
||||||
|
if len(s) < 4 {
|
||||||
|
errorf("invalid bytea sequence %v", s)
|
||||||
|
}
|
||||||
|
r, err := strconv.ParseInt(string(s[1:4]), 8, 9)
|
||||||
|
if err != nil {
|
||||||
|
errorf("could not parse bytea value: %s", err.Error())
|
||||||
|
}
|
||||||
|
result = append(result, byte(r))
|
||||||
|
s = s[4:]
|
||||||
|
} else {
|
||||||
|
// We hit an unescaped, raw byte. Try to read in as many as
|
||||||
|
// possible in one go.
|
||||||
|
i := bytes.IndexByte(s, '\\')
|
||||||
|
if i == -1 {
|
||||||
|
result = append(result, s...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
result = append(result, s[:i]...)
|
||||||
|
s = s[i:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeBytea(serverVersion int, v []byte) (result []byte) {
|
||||||
|
if serverVersion >= 90000 {
|
||||||
|
// Use the hex format if we know that the server supports it
|
||||||
|
result = make([]byte, 2+hex.EncodedLen(len(v)))
|
||||||
|
result[0] = '\\'
|
||||||
|
result[1] = 'x'
|
||||||
|
hex.Encode(result[2:], v)
|
||||||
|
} else {
|
||||||
|
// .. or resort to "escape"
|
||||||
|
for _, b := range v {
|
||||||
|
if b == '\\' {
|
||||||
|
result = append(result, '\\', '\\')
|
||||||
|
} else if b < 0x20 || b > 0x7e {
|
||||||
|
result = append(result, []byte(fmt.Sprintf("\\%03o", b))...)
|
||||||
|
} else {
|
||||||
|
result = append(result, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// NullTime represents a time.Time that may be null. NullTime implements the
|
||||||
|
// sql.Scanner interface so it can be used as a scan destination, similar to
|
||||||
|
// sql.NullString.
|
||||||
|
type NullTime struct {
|
||||||
|
Time time.Time
|
||||||
|
Valid bool // Valid is true if Time is not NULL
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan implements the Scanner interface.
|
||||||
|
func (nt *NullTime) Scan(value interface{}) error {
|
||||||
|
nt.Time, nt.Valid = value.(time.Time)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value implements the driver Valuer interface.
|
||||||
|
func (nt NullTime) Value() (driver.Value, error) {
|
||||||
|
if !nt.Valid {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return nt.Time, nil
|
||||||
|
}
|
727
vendor/github.com/lib/pq/encode_test.go
generated
vendored
Normal file
727
vendor/github.com/lib/pq/encode_test.go
generated
vendored
Normal file
@ -0,0 +1,727 @@
|
|||||||
|
package pq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/lib/pq/oid"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestScanTimestamp(t *testing.T) {
|
||||||
|
var nt NullTime
|
||||||
|
tn := time.Now()
|
||||||
|
nt.Scan(tn)
|
||||||
|
if !nt.Valid {
|
||||||
|
t.Errorf("Expected Valid=false")
|
||||||
|
}
|
||||||
|
if nt.Time != tn {
|
||||||
|
t.Errorf("Time value mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestScanNilTimestamp(t *testing.T) {
|
||||||
|
var nt NullTime
|
||||||
|
nt.Scan(nil)
|
||||||
|
if nt.Valid {
|
||||||
|
t.Errorf("Expected Valid=false")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var timeTests = []struct {
|
||||||
|
str string
|
||||||
|
timeval time.Time
|
||||||
|
}{
|
||||||
|
{"22001-02-03", time.Date(22001, time.February, 3, 0, 0, 0, 0, time.FixedZone("", 0))},
|
||||||
|
{"2001-02-03", time.Date(2001, time.February, 3, 0, 0, 0, 0, time.FixedZone("", 0))},
|
||||||
|
{"2001-02-03 04:05:06", time.Date(2001, time.February, 3, 4, 5, 6, 0, time.FixedZone("", 0))},
|
||||||
|
{"2001-02-03 04:05:06.000001", time.Date(2001, time.February, 3, 4, 5, 6, 1000, time.FixedZone("", 0))},
|
||||||
|
{"2001-02-03 04:05:06.00001", time.Date(2001, time.February, 3, 4, 5, 6, 10000, time.FixedZone("", 0))},
|
||||||
|
{"2001-02-03 04:05:06.0001", time.Date(2001, time.February, 3, 4, 5, 6, 100000, time.FixedZone("", 0))},
|
||||||
|
{"2001-02-03 04:05:06.001", time.Date(2001, time.February, 3, 4, 5, 6, 1000000, time.FixedZone("", 0))},
|
||||||
|
{"2001-02-03 04:05:06.01", time.Date(2001, time.February, 3, 4, 5, 6, 10000000, time.FixedZone("", 0))},
|
||||||
|
{"2001-02-03 04:05:06.1", time.Date(2001, time.February, 3, 4, 5, 6, 100000000, time.FixedZone("", 0))},
|
||||||
|
{"2001-02-03 04:05:06.12", time.Date(2001, time.February, 3, 4, 5, 6, 120000000, time.FixedZone("", 0))},
|
||||||
|
{"2001-02-03 04:05:06.123", time.Date(2001, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))},
|
||||||
|
{"2001-02-03 04:05:06.1234", time.Date(2001, time.February, 3, 4, 5, 6, 123400000, time.FixedZone("", 0))},
|
||||||
|
{"2001-02-03 04:05:06.12345", time.Date(2001, time.February, 3, 4, 5, 6, 123450000, time.FixedZone("", 0))},
|
||||||
|
{"2001-02-03 04:05:06.123456", time.Date(2001, time.February, 3, 4, 5, 6, 123456000, time.FixedZone("", 0))},
|
||||||
|
{"2001-02-03 04:05:06.123-07", time.Date(2001, time.February, 3, 4, 5, 6, 123000000,
|
||||||
|
time.FixedZone("", -7*60*60))},
|
||||||
|
{"2001-02-03 04:05:06-07", time.Date(2001, time.February, 3, 4, 5, 6, 0,
|
||||||
|
time.FixedZone("", -7*60*60))},
|
||||||
|
{"2001-02-03 04:05:06-07:42", time.Date(2001, time.February, 3, 4, 5, 6, 0,
|
||||||
|
time.FixedZone("", -(7*60*60+42*60)))},
|
||||||
|
{"2001-02-03 04:05:06-07:30:09", time.Date(2001, time.February, 3, 4, 5, 6, 0,
|
||||||
|
time.FixedZone("", -(7*60*60+30*60+9)))},
|
||||||
|
{"2001-02-03 04:05:06+07", time.Date(2001, time.February, 3, 4, 5, 6, 0,
|
||||||
|
time.FixedZone("", 7*60*60))},
|
||||||
|
{"0011-02-03 04:05:06 BC", time.Date(-10, time.February, 3, 4, 5, 6, 0, time.FixedZone("", 0))},
|
||||||
|
{"0011-02-03 04:05:06.123 BC", time.Date(-10, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))},
|
||||||
|
{"0011-02-03 04:05:06.123-07 BC", time.Date(-10, time.February, 3, 4, 5, 6, 123000000,
|
||||||
|
time.FixedZone("", -7*60*60))},
|
||||||
|
{"0001-02-03 04:05:06.123", time.Date(1, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))},
|
||||||
|
{"0001-02-03 04:05:06.123 BC", time.Date(1, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0)).AddDate(-1, 0, 0)},
|
||||||
|
{"0001-02-03 04:05:06.123 BC", time.Date(0, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))},
|
||||||
|
{"0002-02-03 04:05:06.123 BC", time.Date(0, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0)).AddDate(-1, 0, 0)},
|
||||||
|
{"0002-02-03 04:05:06.123 BC", time.Date(-1, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))},
|
||||||
|
{"12345-02-03 04:05:06.1", time.Date(12345, time.February, 3, 4, 5, 6, 100000000, time.FixedZone("", 0))},
|
||||||
|
{"123456-02-03 04:05:06.1", time.Date(123456, time.February, 3, 4, 5, 6, 100000000, time.FixedZone("", 0))},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that parsing the string results in the expected value.
|
||||||
|
func TestParseTs(t *testing.T) {
|
||||||
|
for i, tt := range timeTests {
|
||||||
|
val, err := ParseTimestamp(nil, tt.str)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%d: got error: %v", i, err)
|
||||||
|
} else if val.String() != tt.timeval.String() {
|
||||||
|
t.Errorf("%d: expected to parse %q into %q; got %q",
|
||||||
|
i, tt.str, tt.timeval, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var timeErrorTests = []string{
|
||||||
|
"2001",
|
||||||
|
"2001-2-03",
|
||||||
|
"2001-02-3",
|
||||||
|
"2001-02-03 ",
|
||||||
|
"2001-02-03 04",
|
||||||
|
"2001-02-03 04:",
|
||||||
|
"2001-02-03 04:05",
|
||||||
|
"2001-02-03 04:05:",
|
||||||
|
"2001-02-03 04:05:6",
|
||||||
|
"2001-02-03 04:05:06.123 B",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that parsing the string results in an error.
|
||||||
|
func TestParseTsErrors(t *testing.T) {
|
||||||
|
for i, tt := range timeErrorTests {
|
||||||
|
_, err := ParseTimestamp(nil, tt)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("%d: expected an error from parsing: %v", i, tt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now test that sending the value into the database and parsing it back
|
||||||
|
// returns the same time.Time value.
|
||||||
|
func TestEncodeAndParseTs(t *testing.T) {
|
||||||
|
db, err := openTestConnConninfo("timezone='Etc/UTC'")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
for i, tt := range timeTests {
|
||||||
|
var dbstr string
|
||||||
|
err = db.QueryRow("SELECT ($1::timestamptz)::text", tt.timeval).Scan(&dbstr)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%d: could not send value %q to the database: %s", i, tt.timeval, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
val, err := ParseTimestamp(nil, dbstr)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%d: could not parse value %q: %s", i, dbstr, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
val = val.In(tt.timeval.Location())
|
||||||
|
if val.String() != tt.timeval.String() {
|
||||||
|
t.Errorf("%d: expected to parse %q into %q; got %q", i, dbstr, tt.timeval, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var formatTimeTests = []struct {
|
||||||
|
time time.Time
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{time.Time{}, "0001-01-01T00:00:00Z"},
|
||||||
|
{time.Date(2001, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 0)), "2001-02-03T04:05:06.123456789Z"},
|
||||||
|
{time.Date(2001, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 2*60*60)), "2001-02-03T04:05:06.123456789+02:00"},
|
||||||
|
{time.Date(2001, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", -6*60*60)), "2001-02-03T04:05:06.123456789-06:00"},
|
||||||
|
{time.Date(2001, time.February, 3, 4, 5, 6, 0, time.FixedZone("", -(7*60*60+30*60+9))), "2001-02-03T04:05:06-07:30:09"},
|
||||||
|
|
||||||
|
{time.Date(1, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 0)), "0001-02-03T04:05:06.123456789Z"},
|
||||||
|
{time.Date(1, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 2*60*60)), "0001-02-03T04:05:06.123456789+02:00"},
|
||||||
|
{time.Date(1, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", -6*60*60)), "0001-02-03T04:05:06.123456789-06:00"},
|
||||||
|
|
||||||
|
{time.Date(0, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 0)), "0001-02-03T04:05:06.123456789Z BC"},
|
||||||
|
{time.Date(0, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 2*60*60)), "0001-02-03T04:05:06.123456789+02:00 BC"},
|
||||||
|
{time.Date(0, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", -6*60*60)), "0001-02-03T04:05:06.123456789-06:00 BC"},
|
||||||
|
|
||||||
|
{time.Date(1, time.February, 3, 4, 5, 6, 0, time.FixedZone("", -(7*60*60+30*60+9))), "0001-02-03T04:05:06-07:30:09"},
|
||||||
|
{time.Date(0, time.February, 3, 4, 5, 6, 0, time.FixedZone("", -(7*60*60+30*60+9))), "0001-02-03T04:05:06-07:30:09 BC"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormatTs(t *testing.T) {
|
||||||
|
for i, tt := range formatTimeTests {
|
||||||
|
val := string(formatTs(tt.time))
|
||||||
|
if val != tt.expected {
|
||||||
|
t.Errorf("%d: incorrect time format %q, want %q", i, val, tt.expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTimestampWithTimeZone(t *testing.T) {
|
||||||
|
db := openTestConn(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
tx, err := db.Begin()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer tx.Rollback()
|
||||||
|
|
||||||
|
// try several different locations, all included in Go's zoneinfo.zip
|
||||||
|
for _, locName := range []string{
|
||||||
|
"UTC",
|
||||||
|
"America/Chicago",
|
||||||
|
"America/New_York",
|
||||||
|
"Australia/Darwin",
|
||||||
|
"Australia/Perth",
|
||||||
|
} {
|
||||||
|
loc, err := time.LoadLocation(locName)
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("Could not load time zone %s - skipping", locName)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Postgres timestamps have a resolution of 1 microsecond, so don't
|
||||||
|
// use the full range of the Nanosecond argument
|
||||||
|
refTime := time.Date(2012, 11, 6, 10, 23, 42, 123456000, loc)
|
||||||
|
|
||||||
|
for _, pgTimeZone := range []string{"US/Eastern", "Australia/Darwin"} {
|
||||||
|
// Switch Postgres's timezone to test different output timestamp formats
|
||||||
|
_, err = tx.Exec(fmt.Sprintf("set time zone '%s'", pgTimeZone))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var gotTime time.Time
|
||||||
|
row := tx.QueryRow("select $1::timestamp with time zone", refTime)
|
||||||
|
err = row.Scan(&gotTime)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !refTime.Equal(gotTime) {
|
||||||
|
t.Errorf("timestamps not equal: %s != %s", refTime, gotTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that the time zone is set correctly based on TimeZone
|
||||||
|
pgLoc, err := time.LoadLocation(pgTimeZone)
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("Could not load time zone %s - skipping", pgLoc)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
translated := refTime.In(pgLoc)
|
||||||
|
if translated.String() != gotTime.String() {
|
||||||
|
t.Errorf("timestamps not equal: %s != %s", translated, gotTime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTimestampWithOutTimezone(t *testing.T) {
|
||||||
|
db := openTestConn(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
test := func(ts, pgts string) {
|
||||||
|
r, err := db.Query("SELECT $1::timestamp", pgts)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Could not run query: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
n := r.Next()
|
||||||
|
|
||||||
|
if n != true {
|
||||||
|
t.Fatal("Expected at least one row")
|
||||||
|
}
|
||||||
|
|
||||||
|
var result time.Time
|
||||||
|
err = r.Scan(&result)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Did not expect error scanning row: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected, err := time.Parse(time.RFC3339, ts)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Could not parse test time literal: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !result.Equal(expected) {
|
||||||
|
t.Fatalf("Expected time to match %v: got mismatch %v",
|
||||||
|
expected, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
n = r.Next()
|
||||||
|
if n != false {
|
||||||
|
t.Fatal("Expected only one row")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test("2000-01-01T00:00:00Z", "2000-01-01T00:00:00")
|
||||||
|
|
||||||
|
// Test higher precision time
|
||||||
|
test("2013-01-04T20:14:58.80033Z", "2013-01-04 20:14:58.80033")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInfinityTimestamp(t *testing.T) {
|
||||||
|
db := openTestConn(t)
|
||||||
|
defer db.Close()
|
||||||
|
var err error
|
||||||
|
var resultT time.Time
|
||||||
|
|
||||||
|
expectedErrorStrPrefix := `sql: Scan error on column index 0: unsupported`
|
||||||
|
type testCases []struct {
|
||||||
|
Query string
|
||||||
|
Param string
|
||||||
|
ExpectedErrStrPrefix string
|
||||||
|
ExpectedVal interface{}
|
||||||
|
}
|
||||||
|
tc := testCases{
|
||||||
|
{"SELECT $1::timestamp", "-infinity", expectedErrorStrPrefix, "-infinity"},
|
||||||
|
{"SELECT $1::timestamptz", "-infinity", expectedErrorStrPrefix, "-infinity"},
|
||||||
|
{"SELECT $1::timestamp", "infinity", expectedErrorStrPrefix, "infinity"},
|
||||||
|
{"SELECT $1::timestamptz", "infinity", expectedErrorStrPrefix, "infinity"},
|
||||||
|
}
|
||||||
|
// try to assert []byte to time.Time
|
||||||
|
for _, q := range tc {
|
||||||
|
err = db.QueryRow(q.Query, q.Param).Scan(&resultT)
|
||||||
|
if !strings.HasPrefix(err.Error(), q.ExpectedErrStrPrefix) {
|
||||||
|
t.Errorf("Scanning -/+infinity, expected error to have prefix %q, got %q", q.ExpectedErrStrPrefix, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// yield []byte
|
||||||
|
for _, q := range tc {
|
||||||
|
var resultI interface{}
|
||||||
|
err = db.QueryRow(q.Query, q.Param).Scan(&resultI)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Scanning -/+infinity, expected no error, got %q", err)
|
||||||
|
}
|
||||||
|
result, ok := resultI.([]byte)
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("Scanning -/+infinity, expected []byte, got %#v", resultI)
|
||||||
|
}
|
||||||
|
if string(result) != q.ExpectedVal {
|
||||||
|
t.Errorf("Scanning -/+infinity, expected %q, got %q", q.ExpectedVal, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
y1500 := time.Date(1500, time.January, 1, 0, 0, 0, 0, time.UTC)
|
||||||
|
y2500 := time.Date(2500, time.January, 1, 0, 0, 0, 0, time.UTC)
|
||||||
|
EnableInfinityTs(y1500, y2500)
|
||||||
|
|
||||||
|
err = db.QueryRow("SELECT $1::timestamp", "infinity").Scan(&resultT)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Scanning infinity, expected no error, got %q", err)
|
||||||
|
}
|
||||||
|
if !resultT.Equal(y2500) {
|
||||||
|
t.Errorf("Scanning infinity, expected %q, got %q", y2500, resultT)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.QueryRow("SELECT $1::timestamptz", "infinity").Scan(&resultT)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Scanning infinity, expected no error, got %q", err)
|
||||||
|
}
|
||||||
|
if !resultT.Equal(y2500) {
|
||||||
|
t.Errorf("Scanning Infinity, expected time %q, got %q", y2500, resultT.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.QueryRow("SELECT $1::timestamp", "-infinity").Scan(&resultT)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Scanning -infinity, expected no error, got %q", err)
|
||||||
|
}
|
||||||
|
if !resultT.Equal(y1500) {
|
||||||
|
t.Errorf("Scanning -infinity, expected time %q, got %q", y1500, resultT.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.QueryRow("SELECT $1::timestamptz", "-infinity").Scan(&resultT)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Scanning -infinity, expected no error, got %q", err)
|
||||||
|
}
|
||||||
|
if !resultT.Equal(y1500) {
|
||||||
|
t.Errorf("Scanning -infinity, expected time %q, got %q", y1500, resultT.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
y_1500 := time.Date(-1500, time.January, 1, 0, 0, 0, 0, time.UTC)
|
||||||
|
y11500 := time.Date(11500, time.January, 1, 0, 0, 0, 0, time.UTC)
|
||||||
|
var s string
|
||||||
|
err = db.QueryRow("SELECT $1::timestamp::text", y_1500).Scan(&s)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Encoding -infinity, expected no error, got %q", err)
|
||||||
|
}
|
||||||
|
if s != "-infinity" {
|
||||||
|
t.Errorf("Encoding -infinity, expected %q, got %q", "-infinity", s)
|
||||||
|
}
|
||||||
|
err = db.QueryRow("SELECT $1::timestamptz::text", y_1500).Scan(&s)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Encoding -infinity, expected no error, got %q", err)
|
||||||
|
}
|
||||||
|
if s != "-infinity" {
|
||||||
|
t.Errorf("Encoding -infinity, expected %q, got %q", "-infinity", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.QueryRow("SELECT $1::timestamp::text", y11500).Scan(&s)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Encoding infinity, expected no error, got %q", err)
|
||||||
|
}
|
||||||
|
if s != "infinity" {
|
||||||
|
t.Errorf("Encoding infinity, expected %q, got %q", "infinity", s)
|
||||||
|
}
|
||||||
|
err = db.QueryRow("SELECT $1::timestamptz::text", y11500).Scan(&s)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Encoding infinity, expected no error, got %q", err)
|
||||||
|
}
|
||||||
|
if s != "infinity" {
|
||||||
|
t.Errorf("Encoding infinity, expected %q, got %q", "infinity", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
disableInfinityTs()
|
||||||
|
|
||||||
|
var panicErrorString string
|
||||||
|
func() {
|
||||||
|
defer func() {
|
||||||
|
panicErrorString, _ = recover().(string)
|
||||||
|
}()
|
||||||
|
EnableInfinityTs(y2500, y1500)
|
||||||
|
}()
|
||||||
|
if panicErrorString != infinityTsNegativeMustBeSmaller {
|
||||||
|
t.Errorf("Expected error, %q, got %q", infinityTsNegativeMustBeSmaller, panicErrorString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStringWithNul(t *testing.T) {
|
||||||
|
db := openTestConn(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
hello0world := string("hello\x00world")
|
||||||
|
_, err := db.Query("SELECT $1::text", &hello0world)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Postgres accepts a string with nul in it; " +
|
||||||
|
"injection attacks may be plausible")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestByteSliceToText(t *testing.T) {
|
||||||
|
db := openTestConn(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
b := []byte("hello world")
|
||||||
|
row := db.QueryRow("SELECT $1::text", b)
|
||||||
|
|
||||||
|
var result []byte
|
||||||
|
err := row.Scan(&result)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(result) != string(b) {
|
||||||
|
t.Fatalf("expected %v but got %v", b, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStringToBytea(t *testing.T) {
|
||||||
|
db := openTestConn(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
b := "hello world"
|
||||||
|
row := db.QueryRow("SELECT $1::bytea", b)
|
||||||
|
|
||||||
|
var result []byte
|
||||||
|
err := row.Scan(&result)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(result, []byte(b)) {
|
||||||
|
t.Fatalf("expected %v but got %v", b, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTextByteSliceToUUID(t *testing.T) {
|
||||||
|
db := openTestConn(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
b := []byte("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11")
|
||||||
|
row := db.QueryRow("SELECT $1::uuid", b)
|
||||||
|
|
||||||
|
var result string
|
||||||
|
err := row.Scan(&result)
|
||||||
|
if forceBinaryParameters() {
|
||||||
|
pqErr := err.(*Error)
|
||||||
|
if pqErr == nil {
|
||||||
|
t.Errorf("Expected to get error")
|
||||||
|
} else if pqErr.Code != "22P03" {
|
||||||
|
t.Fatalf("Expected to get invalid binary encoding error (22P03), got %s", pqErr.Code)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if result != string(b) {
|
||||||
|
t.Fatalf("expected %v but got %v", b, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBinaryByteSlicetoUUID(t *testing.T) {
|
||||||
|
db := openTestConn(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
b := []byte{'\xa0', '\xee', '\xbc', '\x99',
|
||||||
|
'\x9c', '\x0b',
|
||||||
|
'\x4e', '\xf8',
|
||||||
|
'\xbb', '\x00', '\x6b',
|
||||||
|
'\xb9', '\xbd', '\x38', '\x0a', '\x11'}
|
||||||
|
row := db.QueryRow("SELECT $1::uuid", b)
|
||||||
|
|
||||||
|
var result string
|
||||||
|
err := row.Scan(&result)
|
||||||
|
if forceBinaryParameters() {
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if result != string("a0eebc99-9c0b-4ef8-bb00-6bb9bd380a11") {
|
||||||
|
t.Fatalf("expected %v but got %v", b, result)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pqErr := err.(*Error)
|
||||||
|
if pqErr == nil {
|
||||||
|
t.Errorf("Expected to get error")
|
||||||
|
} else if pqErr.Code != "22021" {
|
||||||
|
t.Fatalf("Expected to get invalid byte sequence for encoding error (22021), got %s", pqErr.Code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStringToUUID(t *testing.T) {
|
||||||
|
db := openTestConn(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
s := "a0eebc99-9c0b-4ef8-bb00-6bb9bd380a11"
|
||||||
|
row := db.QueryRow("SELECT $1::uuid", s)
|
||||||
|
|
||||||
|
var result string
|
||||||
|
err := row.Scan(&result)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if result != s {
|
||||||
|
t.Fatalf("expected %v but got %v", s, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTextByteSliceToInt(t *testing.T) {
|
||||||
|
db := openTestConn(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
expected := 12345678
|
||||||
|
b := []byte(fmt.Sprintf("%d", expected))
|
||||||
|
row := db.QueryRow("SELECT $1::int", b)
|
||||||
|
|
||||||
|
var result int
|
||||||
|
err := row.Scan(&result)
|
||||||
|
if forceBinaryParameters() {
|
||||||
|
pqErr := err.(*Error)
|
||||||
|
if pqErr == nil {
|
||||||
|
t.Errorf("Expected to get error")
|
||||||
|
} else if pqErr.Code != "22P03" {
|
||||||
|
t.Fatalf("Expected to get invalid binary encoding error (22P03), got %s", pqErr.Code)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if result != expected {
|
||||||
|
t.Fatalf("expected %v but got %v", expected, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBinaryByteSliceToInt(t *testing.T) {
|
||||||
|
db := openTestConn(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
expected := 12345678
|
||||||
|
b := []byte{'\x00', '\xbc', '\x61', '\x4e'}
|
||||||
|
row := db.QueryRow("SELECT $1::int", b)
|
||||||
|
|
||||||
|
var result int
|
||||||
|
err := row.Scan(&result)
|
||||||
|
if forceBinaryParameters() {
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if result != expected {
|
||||||
|
t.Fatalf("expected %v but got %v", expected, result)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pqErr := err.(*Error)
|
||||||
|
if pqErr == nil {
|
||||||
|
t.Errorf("Expected to get error")
|
||||||
|
} else if pqErr.Code != "22021" {
|
||||||
|
t.Fatalf("Expected to get invalid byte sequence for encoding error (22021), got %s", pqErr.Code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestByteaOutputFormatEncoding(t *testing.T) {
|
||||||
|
input := []byte("\\x\x00\x01\x02\xFF\xFEabcdefg0123")
|
||||||
|
want := []byte("\\x5c78000102fffe6162636465666730313233")
|
||||||
|
got := encode(¶meterStatus{serverVersion: 90000}, input, oid.T_bytea)
|
||||||
|
if !bytes.Equal(want, got) {
|
||||||
|
t.Errorf("invalid hex bytea output, got %v but expected %v", got, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
want = []byte("\\\\x\\000\\001\\002\\377\\376abcdefg0123")
|
||||||
|
got = encode(¶meterStatus{serverVersion: 84000}, input, oid.T_bytea)
|
||||||
|
if !bytes.Equal(want, got) {
|
||||||
|
t.Errorf("invalid escape bytea output, got %v but expected %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestByteaOutputFormats(t *testing.T) {
|
||||||
|
db := openTestConn(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
if getServerVersion(t, db) < 90000 {
|
||||||
|
// skip
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
testByteaOutputFormat := func(f string, usePrepared bool) {
|
||||||
|
expectedData := []byte("\x5c\x78\x00\xff\x61\x62\x63\x01\x08")
|
||||||
|
sqlQuery := "SELECT decode('5c7800ff6162630108', 'hex')"
|
||||||
|
|
||||||
|
var data []byte
|
||||||
|
|
||||||
|
// use a txn to avoid relying on getting the same connection
|
||||||
|
txn, err := db.Begin()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer txn.Rollback()
|
||||||
|
|
||||||
|
_, err = txn.Exec("SET LOCAL bytea_output TO " + f)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
var rows *sql.Rows
|
||||||
|
var stmt *sql.Stmt
|
||||||
|
if usePrepared {
|
||||||
|
stmt, err = txn.Prepare(sqlQuery)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
rows, err = stmt.Query()
|
||||||
|
} else {
|
||||||
|
// use Query; QueryRow would hide the actual error
|
||||||
|
rows, err = txn.Query(sqlQuery)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !rows.Next() {
|
||||||
|
if rows.Err() != nil {
|
||||||
|
t.Fatal(rows.Err())
|
||||||
|
}
|
||||||
|
t.Fatal("shouldn't happen")
|
||||||
|
}
|
||||||
|
err = rows.Scan(&data)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
err = rows.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if stmt != nil {
|
||||||
|
err = stmt.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !bytes.Equal(data, expectedData) {
|
||||||
|
t.Errorf("unexpected bytea value %v for format %s; expected %v", data, f, expectedData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testByteaOutputFormat("hex", false)
|
||||||
|
testByteaOutputFormat("escape", false)
|
||||||
|
testByteaOutputFormat("hex", true)
|
||||||
|
testByteaOutputFormat("escape", true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAppendEncodedText(t *testing.T) {
|
||||||
|
var buf []byte
|
||||||
|
|
||||||
|
buf = appendEncodedText(¶meterStatus{serverVersion: 90000}, buf, int64(10))
|
||||||
|
buf = append(buf, '\t')
|
||||||
|
buf = appendEncodedText(¶meterStatus{serverVersion: 90000}, buf, 42.0000000001)
|
||||||
|
buf = append(buf, '\t')
|
||||||
|
buf = appendEncodedText(¶meterStatus{serverVersion: 90000}, buf, "hello\tworld")
|
||||||
|
buf = append(buf, '\t')
|
||||||
|
buf = appendEncodedText(¶meterStatus{serverVersion: 90000}, buf, []byte{0, 128, 255})
|
||||||
|
|
||||||
|
if string(buf) != "10\t42.0000000001\thello\\tworld\t\\\\x0080ff" {
|
||||||
|
t.Fatal(string(buf))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAppendEscapedText(t *testing.T) {
|
||||||
|
if esc := appendEscapedText(nil, "hallo\tescape"); string(esc) != "hallo\\tescape" {
|
||||||
|
t.Fatal(string(esc))
|
||||||
|
}
|
||||||
|
if esc := appendEscapedText(nil, "hallo\\tescape\n"); string(esc) != "hallo\\\\tescape\\n" {
|
||||||
|
t.Fatal(string(esc))
|
||||||
|
}
|
||||||
|
if esc := appendEscapedText(nil, "\n\r\t\f"); string(esc) != "\\n\\r\\t\f" {
|
||||||
|
t.Fatal(string(esc))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAppendEscapedTextExistingBuffer(t *testing.T) {
|
||||||
|
var buf []byte
|
||||||
|
buf = []byte("123\t")
|
||||||
|
if esc := appendEscapedText(buf, "hallo\tescape"); string(esc) != "123\thallo\\tescape" {
|
||||||
|
t.Fatal(string(esc))
|
||||||
|
}
|
||||||
|
buf = []byte("123\t")
|
||||||
|
if esc := appendEscapedText(buf, "hallo\\tescape\n"); string(esc) != "123\thallo\\\\tescape\\n" {
|
||||||
|
t.Fatal(string(esc))
|
||||||
|
}
|
||||||
|
buf = []byte("123\t")
|
||||||
|
if esc := appendEscapedText(buf, "\n\r\t\f"); string(esc) != "123\t\\n\\r\\t\f" {
|
||||||
|
t.Fatal(string(esc))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkAppendEscapedText(b *testing.B) {
|
||||||
|
longString := ""
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
longString += "123456789\n"
|
||||||
|
}
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
appendEscapedText(nil, longString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkAppendEscapedTextNoEscape(b *testing.B) {
|
||||||
|
longString := ""
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
longString += "1234567890"
|
||||||
|
}
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
appendEscapedText(nil, longString)
|
||||||
|
}
|
||||||
|
}
|
508
vendor/github.com/lib/pq/error.go
generated
vendored
Normal file
508
vendor/github.com/lib/pq/error.go
generated
vendored
Normal file
@ -0,0 +1,508 @@
|
|||||||
|
package pq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql/driver"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Error severities
|
||||||
|
const (
|
||||||
|
Efatal = "FATAL"
|
||||||
|
Epanic = "PANIC"
|
||||||
|
Ewarning = "WARNING"
|
||||||
|
Enotice = "NOTICE"
|
||||||
|
Edebug = "DEBUG"
|
||||||
|
Einfo = "INFO"
|
||||||
|
Elog = "LOG"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Error represents an error communicating with the server.
|
||||||
|
//
|
||||||
|
// See http://www.postgresql.org/docs/current/static/protocol-error-fields.html for details of the fields
|
||||||
|
type Error struct {
|
||||||
|
Severity string
|
||||||
|
Code ErrorCode
|
||||||
|
Message string
|
||||||
|
Detail string
|
||||||
|
Hint string
|
||||||
|
Position string
|
||||||
|
InternalPosition string
|
||||||
|
InternalQuery string
|
||||||
|
Where string
|
||||||
|
Schema string
|
||||||
|
Table string
|
||||||
|
Column string
|
||||||
|
DataTypeName string
|
||||||
|
Constraint string
|
||||||
|
File string
|
||||||
|
Line string
|
||||||
|
Routine string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorCode is a five-character error code.
|
||||||
|
type ErrorCode string
|
||||||
|
|
||||||
|
// Name returns a more human friendly rendering of the error code, namely the
|
||||||
|
// "condition name".
|
||||||
|
//
|
||||||
|
// See http://www.postgresql.org/docs/9.3/static/errcodes-appendix.html for
|
||||||
|
// details.
|
||||||
|
func (ec ErrorCode) Name() string {
|
||||||
|
return errorCodeNames[ec]
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorClass is only the class part of an error code.
|
||||||
|
type ErrorClass string
|
||||||
|
|
||||||
|
// Name returns the condition name of an error class. It is equivalent to the
|
||||||
|
// condition name of the "standard" error code (i.e. the one having the last
|
||||||
|
// three characters "000").
|
||||||
|
func (ec ErrorClass) Name() string {
|
||||||
|
return errorCodeNames[ErrorCode(ec+"000")]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Class returns the error class, e.g. "28".
|
||||||
|
//
|
||||||
|
// See http://www.postgresql.org/docs/9.3/static/errcodes-appendix.html for
|
||||||
|
// details.
|
||||||
|
func (ec ErrorCode) Class() ErrorClass {
|
||||||
|
return ErrorClass(ec[0:2])
|
||||||
|
}
|
||||||
|
|
||||||
|
// errorCodeNames is a mapping between the five-character error codes and the
|
||||||
|
// human readable "condition names". It is derived from the list at
|
||||||
|
// http://www.postgresql.org/docs/9.3/static/errcodes-appendix.html
|
||||||
|
var errorCodeNames = map[ErrorCode]string{
|
||||||
|
// Class 00 - Successful Completion
|
||||||
|
"00000": "successful_completion",
|
||||||
|
// Class 01 - Warning
|
||||||
|
"01000": "warning",
|
||||||
|
"0100C": "dynamic_result_sets_returned",
|
||||||
|
"01008": "implicit_zero_bit_padding",
|
||||||
|
"01003": "null_value_eliminated_in_set_function",
|
||||||
|
"01007": "privilege_not_granted",
|
||||||
|
"01006": "privilege_not_revoked",
|
||||||
|
"01004": "string_data_right_truncation",
|
||||||
|
"01P01": "deprecated_feature",
|
||||||
|
// Class 02 - No Data (this is also a warning class per the SQL standard)
|
||||||
|
"02000": "no_data",
|
||||||
|
"02001": "no_additional_dynamic_result_sets_returned",
|
||||||
|
// Class 03 - SQL Statement Not Yet Complete
|
||||||
|
"03000": "sql_statement_not_yet_complete",
|
||||||
|
// Class 08 - Connection Exception
|
||||||
|
"08000": "connection_exception",
|
||||||
|
"08003": "connection_does_not_exist",
|
||||||
|
"08006": "connection_failure",
|
||||||
|
"08001": "sqlclient_unable_to_establish_sqlconnection",
|
||||||
|
"08004": "sqlserver_rejected_establishment_of_sqlconnection",
|
||||||
|
"08007": "transaction_resolution_unknown",
|
||||||
|
"08P01": "protocol_violation",
|
||||||
|
// Class 09 - Triggered Action Exception
|
||||||
|
"09000": "triggered_action_exception",
|
||||||
|
// Class 0A - Feature Not Supported
|
||||||
|
"0A000": "feature_not_supported",
|
||||||
|
// Class 0B - Invalid Transaction Initiation
|
||||||
|
"0B000": "invalid_transaction_initiation",
|
||||||
|
// Class 0F - Locator Exception
|
||||||
|
"0F000": "locator_exception",
|
||||||
|
"0F001": "invalid_locator_specification",
|
||||||
|
// Class 0L - Invalid Grantor
|
||||||
|
"0L000": "invalid_grantor",
|
||||||
|
"0LP01": "invalid_grant_operation",
|
||||||
|
// Class 0P - Invalid Role Specification
|
||||||
|
"0P000": "invalid_role_specification",
|
||||||
|
// Class 0Z - Diagnostics Exception
|
||||||
|
"0Z000": "diagnostics_exception",
|
||||||
|
"0Z002": "stacked_diagnostics_accessed_without_active_handler",
|
||||||
|
// Class 20 - Case Not Found
|
||||||
|
"20000": "case_not_found",
|
||||||
|
// Class 21 - Cardinality Violation
|
||||||
|
"21000": "cardinality_violation",
|
||||||
|
// Class 22 - Data Exception
|
||||||
|
"22000": "data_exception",
|
||||||
|
"2202E": "array_subscript_error",
|
||||||
|
"22021": "character_not_in_repertoire",
|
||||||
|
"22008": "datetime_field_overflow",
|
||||||
|
"22012": "division_by_zero",
|
||||||
|
"22005": "error_in_assignment",
|
||||||
|
"2200B": "escape_character_conflict",
|
||||||
|
"22022": "indicator_overflow",
|
||||||
|
"22015": "interval_field_overflow",
|
||||||
|
"2201E": "invalid_argument_for_logarithm",
|
||||||
|
"22014": "invalid_argument_for_ntile_function",
|
||||||
|
"22016": "invalid_argument_for_nth_value_function",
|
||||||
|
"2201F": "invalid_argument_for_power_function",
|
||||||
|
"2201G": "invalid_argument_for_width_bucket_function",
|
||||||
|
"22018": "invalid_character_value_for_cast",
|
||||||
|
"22007": "invalid_datetime_format",
|
||||||
|
"22019": "invalid_escape_character",
|
||||||
|
"2200D": "invalid_escape_octet",
|
||||||
|
"22025": "invalid_escape_sequence",
|
||||||
|
"22P06": "nonstandard_use_of_escape_character",
|
||||||
|
"22010": "invalid_indicator_parameter_value",
|
||||||
|
"22023": "invalid_parameter_value",
|
||||||
|
"2201B": "invalid_regular_expression",
|
||||||
|
"2201W": "invalid_row_count_in_limit_clause",
|
||||||
|
"2201X": "invalid_row_count_in_result_offset_clause",
|
||||||
|
"22009": "invalid_time_zone_displacement_value",
|
||||||
|
"2200C": "invalid_use_of_escape_character",
|
||||||
|
"2200G": "most_specific_type_mismatch",
|
||||||
|
"22004": "null_value_not_allowed",
|
||||||
|
"22002": "null_value_no_indicator_parameter",
|
||||||
|
"22003": "numeric_value_out_of_range",
|
||||||
|
"22026": "string_data_length_mismatch",
|
||||||
|
"22001": "string_data_right_truncation",
|
||||||
|
"22011": "substring_error",
|
||||||
|
"22027": "trim_error",
|
||||||
|
"22024": "unterminated_c_string",
|
||||||
|
"2200F": "zero_length_character_string",
|
||||||
|
"22P01": "floating_point_exception",
|
||||||
|
"22P02": "invalid_text_representation",
|
||||||
|
"22P03": "invalid_binary_representation",
|
||||||
|
"22P04": "bad_copy_file_format",
|
||||||
|
"22P05": "untranslatable_character",
|
||||||
|
"2200L": "not_an_xml_document",
|
||||||
|
"2200M": "invalid_xml_document",
|
||||||
|
"2200N": "invalid_xml_content",
|
||||||
|
"2200S": "invalid_xml_comment",
|
||||||
|
"2200T": "invalid_xml_processing_instruction",
|
||||||
|
// Class 23 - Integrity Constraint Violation
|
||||||
|
"23000": "integrity_constraint_violation",
|
||||||
|
"23001": "restrict_violation",
|
||||||
|
"23502": "not_null_violation",
|
||||||
|
"23503": "foreign_key_violation",
|
||||||
|
"23505": "unique_violation",
|
||||||
|
"23514": "check_violation",
|
||||||
|
"23P01": "exclusion_violation",
|
||||||
|
// Class 24 - Invalid Cursor State
|
||||||
|
"24000": "invalid_cursor_state",
|
||||||
|
// Class 25 - Invalid Transaction State
|
||||||
|
"25000": "invalid_transaction_state",
|
||||||
|
"25001": "active_sql_transaction",
|
||||||
|
"25002": "branch_transaction_already_active",
|
||||||
|
"25008": "held_cursor_requires_same_isolation_level",
|
||||||
|
"25003": "inappropriate_access_mode_for_branch_transaction",
|
||||||
|
"25004": "inappropriate_isolation_level_for_branch_transaction",
|
||||||
|
"25005": "no_active_sql_transaction_for_branch_transaction",
|
||||||
|
"25006": "read_only_sql_transaction",
|
||||||
|
"25007": "schema_and_data_statement_mixing_not_supported",
|
||||||
|
"25P01": "no_active_sql_transaction",
|
||||||
|
"25P02": "in_failed_sql_transaction",
|
||||||
|
// Class 26 - Invalid SQL Statement Name
|
||||||
|
"26000": "invalid_sql_statement_name",
|
||||||
|
// Class 27 - Triggered Data Change Violation
|
||||||
|
"27000": "triggered_data_change_violation",
|
||||||
|
// Class 28 - Invalid Authorization Specification
|
||||||
|
"28000": "invalid_authorization_specification",
|
||||||
|
"28P01": "invalid_password",
|
||||||
|
// Class 2B - Dependent Privilege Descriptors Still Exist
|
||||||
|
"2B000": "dependent_privilege_descriptors_still_exist",
|
||||||
|
"2BP01": "dependent_objects_still_exist",
|
||||||
|
// Class 2D - Invalid Transaction Termination
|
||||||
|
"2D000": "invalid_transaction_termination",
|
||||||
|
// Class 2F - SQL Routine Exception
|
||||||
|
"2F000": "sql_routine_exception",
|
||||||
|
"2F005": "function_executed_no_return_statement",
|
||||||
|
"2F002": "modifying_sql_data_not_permitted",
|
||||||
|
"2F003": "prohibited_sql_statement_attempted",
|
||||||
|
"2F004": "reading_sql_data_not_permitted",
|
||||||
|
// Class 34 - Invalid Cursor Name
|
||||||
|
"34000": "invalid_cursor_name",
|
||||||
|
// Class 38 - External Routine Exception
|
||||||
|
"38000": "external_routine_exception",
|
||||||
|
"38001": "containing_sql_not_permitted",
|
||||||
|
"38002": "modifying_sql_data_not_permitted",
|
||||||
|
"38003": "prohibited_sql_statement_attempted",
|
||||||
|
"38004": "reading_sql_data_not_permitted",
|
||||||
|
// Class 39 - External Routine Invocation Exception
|
||||||
|
"39000": "external_routine_invocation_exception",
|
||||||
|
"39001": "invalid_sqlstate_returned",
|
||||||
|
"39004": "null_value_not_allowed",
|
||||||
|
"39P01": "trigger_protocol_violated",
|
||||||
|
"39P02": "srf_protocol_violated",
|
||||||
|
// Class 3B - Savepoint Exception
|
||||||
|
"3B000": "savepoint_exception",
|
||||||
|
"3B001": "invalid_savepoint_specification",
|
||||||
|
// Class 3D - Invalid Catalog Name
|
||||||
|
"3D000": "invalid_catalog_name",
|
||||||
|
// Class 3F - Invalid Schema Name
|
||||||
|
"3F000": "invalid_schema_name",
|
||||||
|
// Class 40 - Transaction Rollback
|
||||||
|
"40000": "transaction_rollback",
|
||||||
|
"40002": "transaction_integrity_constraint_violation",
|
||||||
|
"40001": "serialization_failure",
|
||||||
|
"40003": "statement_completion_unknown",
|
||||||
|
"40P01": "deadlock_detected",
|
||||||
|
// Class 42 - Syntax Error or Access Rule Violation
|
||||||
|
"42000": "syntax_error_or_access_rule_violation",
|
||||||
|
"42601": "syntax_error",
|
||||||
|
"42501": "insufficient_privilege",
|
||||||
|
"42846": "cannot_coerce",
|
||||||
|
"42803": "grouping_error",
|
||||||
|
"42P20": "windowing_error",
|
||||||
|
"42P19": "invalid_recursion",
|
||||||
|
"42830": "invalid_foreign_key",
|
||||||
|
"42602": "invalid_name",
|
||||||
|
"42622": "name_too_long",
|
||||||
|
"42939": "reserved_name",
|
||||||
|
"42804": "datatype_mismatch",
|
||||||
|
"42P18": "indeterminate_datatype",
|
||||||
|
"42P21": "collation_mismatch",
|
||||||
|
"42P22": "indeterminate_collation",
|
||||||
|
"42809": "wrong_object_type",
|
||||||
|
"42703": "undefined_column",
|
||||||
|
"42883": "undefined_function",
|
||||||
|
"42P01": "undefined_table",
|
||||||
|
"42P02": "undefined_parameter",
|
||||||
|
"42704": "undefined_object",
|
||||||
|
"42701": "duplicate_column",
|
||||||
|
"42P03": "duplicate_cursor",
|
||||||
|
"42P04": "duplicate_database",
|
||||||
|
"42723": "duplicate_function",
|
||||||
|
"42P05": "duplicate_prepared_statement",
|
||||||
|
"42P06": "duplicate_schema",
|
||||||
|
"42P07": "duplicate_table",
|
||||||
|
"42712": "duplicate_alias",
|
||||||
|
"42710": "duplicate_object",
|
||||||
|
"42702": "ambiguous_column",
|
||||||
|
"42725": "ambiguous_function",
|
||||||
|
"42P08": "ambiguous_parameter",
|
||||||
|
"42P09": "ambiguous_alias",
|
||||||
|
"42P10": "invalid_column_reference",
|
||||||
|
"42611": "invalid_column_definition",
|
||||||
|
"42P11": "invalid_cursor_definition",
|
||||||
|
"42P12": "invalid_database_definition",
|
||||||
|
"42P13": "invalid_function_definition",
|
||||||
|
"42P14": "invalid_prepared_statement_definition",
|
||||||
|
"42P15": "invalid_schema_definition",
|
||||||
|
"42P16": "invalid_table_definition",
|
||||||
|
"42P17": "invalid_object_definition",
|
||||||
|
// Class 44 - WITH CHECK OPTION Violation
|
||||||
|
"44000": "with_check_option_violation",
|
||||||
|
// Class 53 - Insufficient Resources
|
||||||
|
"53000": "insufficient_resources",
|
||||||
|
"53100": "disk_full",
|
||||||
|
"53200": "out_of_memory",
|
||||||
|
"53300": "too_many_connections",
|
||||||
|
"53400": "configuration_limit_exceeded",
|
||||||
|
// Class 54 - Program Limit Exceeded
|
||||||
|
"54000": "program_limit_exceeded",
|
||||||
|
"54001": "statement_too_complex",
|
||||||
|
"54011": "too_many_columns",
|
||||||
|
"54023": "too_many_arguments",
|
||||||
|
// Class 55 - Object Not In Prerequisite State
|
||||||
|
"55000": "object_not_in_prerequisite_state",
|
||||||
|
"55006": "object_in_use",
|
||||||
|
"55P02": "cant_change_runtime_param",
|
||||||
|
"55P03": "lock_not_available",
|
||||||
|
// Class 57 - Operator Intervention
|
||||||
|
"57000": "operator_intervention",
|
||||||
|
"57014": "query_canceled",
|
||||||
|
"57P01": "admin_shutdown",
|
||||||
|
"57P02": "crash_shutdown",
|
||||||
|
"57P03": "cannot_connect_now",
|
||||||
|
"57P04": "database_dropped",
|
||||||
|
// Class 58 - System Error (errors external to PostgreSQL itself)
|
||||||
|
"58000": "system_error",
|
||||||
|
"58030": "io_error",
|
||||||
|
"58P01": "undefined_file",
|
||||||
|
"58P02": "duplicate_file",
|
||||||
|
// Class F0 - Configuration File Error
|
||||||
|
"F0000": "config_file_error",
|
||||||
|
"F0001": "lock_file_exists",
|
||||||
|
// Class HV - Foreign Data Wrapper Error (SQL/MED)
|
||||||
|
"HV000": "fdw_error",
|
||||||
|
"HV005": "fdw_column_name_not_found",
|
||||||
|
"HV002": "fdw_dynamic_parameter_value_needed",
|
||||||
|
"HV010": "fdw_function_sequence_error",
|
||||||
|
"HV021": "fdw_inconsistent_descriptor_information",
|
||||||
|
"HV024": "fdw_invalid_attribute_value",
|
||||||
|
"HV007": "fdw_invalid_column_name",
|
||||||
|
"HV008": "fdw_invalid_column_number",
|
||||||
|
"HV004": "fdw_invalid_data_type",
|
||||||
|
"HV006": "fdw_invalid_data_type_descriptors",
|
||||||
|
"HV091": "fdw_invalid_descriptor_field_identifier",
|
||||||
|
"HV00B": "fdw_invalid_handle",
|
||||||
|
"HV00C": "fdw_invalid_option_index",
|
||||||
|
"HV00D": "fdw_invalid_option_name",
|
||||||
|
"HV090": "fdw_invalid_string_length_or_buffer_length",
|
||||||
|
"HV00A": "fdw_invalid_string_format",
|
||||||
|
"HV009": "fdw_invalid_use_of_null_pointer",
|
||||||
|
"HV014": "fdw_too_many_handles",
|
||||||
|
"HV001": "fdw_out_of_memory",
|
||||||
|
"HV00P": "fdw_no_schemas",
|
||||||
|
"HV00J": "fdw_option_name_not_found",
|
||||||
|
"HV00K": "fdw_reply_handle",
|
||||||
|
"HV00Q": "fdw_schema_not_found",
|
||||||
|
"HV00R": "fdw_table_not_found",
|
||||||
|
"HV00L": "fdw_unable_to_create_execution",
|
||||||
|
"HV00M": "fdw_unable_to_create_reply",
|
||||||
|
"HV00N": "fdw_unable_to_establish_connection",
|
||||||
|
// Class P0 - PL/pgSQL Error
|
||||||
|
"P0000": "plpgsql_error",
|
||||||
|
"P0001": "raise_exception",
|
||||||
|
"P0002": "no_data_found",
|
||||||
|
"P0003": "too_many_rows",
|
||||||
|
// Class XX - Internal Error
|
||||||
|
"XX000": "internal_error",
|
||||||
|
"XX001": "data_corrupted",
|
||||||
|
"XX002": "index_corrupted",
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseError(r *readBuf) *Error {
|
||||||
|
err := new(Error)
|
||||||
|
for t := r.byte(); t != 0; t = r.byte() {
|
||||||
|
msg := r.string()
|
||||||
|
switch t {
|
||||||
|
case 'S':
|
||||||
|
err.Severity = msg
|
||||||
|
case 'C':
|
||||||
|
err.Code = ErrorCode(msg)
|
||||||
|
case 'M':
|
||||||
|
err.Message = msg
|
||||||
|
case 'D':
|
||||||
|
err.Detail = msg
|
||||||
|
case 'H':
|
||||||
|
err.Hint = msg
|
||||||
|
case 'P':
|
||||||
|
err.Position = msg
|
||||||
|
case 'p':
|
||||||
|
err.InternalPosition = msg
|
||||||
|
case 'q':
|
||||||
|
err.InternalQuery = msg
|
||||||
|
case 'W':
|
||||||
|
err.Where = msg
|
||||||
|
case 's':
|
||||||
|
err.Schema = msg
|
||||||
|
case 't':
|
||||||
|
err.Table = msg
|
||||||
|
case 'c':
|
||||||
|
err.Column = msg
|
||||||
|
case 'd':
|
||||||
|
err.DataTypeName = msg
|
||||||
|
case 'n':
|
||||||
|
err.Constraint = msg
|
||||||
|
case 'F':
|
||||||
|
err.File = msg
|
||||||
|
case 'L':
|
||||||
|
err.Line = msg
|
||||||
|
case 'R':
|
||||||
|
err.Routine = msg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fatal returns true if the Error Severity is fatal.
|
||||||
|
func (err *Error) Fatal() bool {
|
||||||
|
return err.Severity == Efatal
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get implements the legacy PGError interface. New code should use the fields
|
||||||
|
// of the Error struct directly.
|
||||||
|
func (err *Error) Get(k byte) (v string) {
|
||||||
|
switch k {
|
||||||
|
case 'S':
|
||||||
|
return err.Severity
|
||||||
|
case 'C':
|
||||||
|
return string(err.Code)
|
||||||
|
case 'M':
|
||||||
|
return err.Message
|
||||||
|
case 'D':
|
||||||
|
return err.Detail
|
||||||
|
case 'H':
|
||||||
|
return err.Hint
|
||||||
|
case 'P':
|
||||||
|
return err.Position
|
||||||
|
case 'p':
|
||||||
|
return err.InternalPosition
|
||||||
|
case 'q':
|
||||||
|
return err.InternalQuery
|
||||||
|
case 'W':
|
||||||
|
return err.Where
|
||||||
|
case 's':
|
||||||
|
return err.Schema
|
||||||
|
case 't':
|
||||||
|
return err.Table
|
||||||
|
case 'c':
|
||||||
|
return err.Column
|
||||||
|
case 'd':
|
||||||
|
return err.DataTypeName
|
||||||
|
case 'n':
|
||||||
|
return err.Constraint
|
||||||
|
case 'F':
|
||||||
|
return err.File
|
||||||
|
case 'L':
|
||||||
|
return err.Line
|
||||||
|
case 'R':
|
||||||
|
return err.Routine
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err Error) Error() string {
|
||||||
|
return "pq: " + err.Message
|
||||||
|
}
|
||||||
|
|
||||||
|
// PGError is an interface used by previous versions of pq. It is provided
|
||||||
|
// only to support legacy code. New code should use the Error type.
|
||||||
|
type PGError interface {
|
||||||
|
Error() string
|
||||||
|
Fatal() bool
|
||||||
|
Get(k byte) (v string)
|
||||||
|
}
|
||||||
|
|
||||||
|
func errorf(s string, args ...interface{}) {
|
||||||
|
panic(fmt.Errorf("pq: %s", fmt.Sprintf(s, args...)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func errRecoverNoErrBadConn(err *error) {
|
||||||
|
e := recover()
|
||||||
|
if e == nil {
|
||||||
|
// Do nothing
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var ok bool
|
||||||
|
*err, ok = e.(error)
|
||||||
|
if !ok {
|
||||||
|
*err = fmt.Errorf("pq: unexpected error: %#v", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *conn) errRecover(err *error) {
|
||||||
|
e := recover()
|
||||||
|
switch v := e.(type) {
|
||||||
|
case nil:
|
||||||
|
// Do nothing
|
||||||
|
case runtime.Error:
|
||||||
|
c.bad = true
|
||||||
|
panic(v)
|
||||||
|
case *Error:
|
||||||
|
if v.Fatal() {
|
||||||
|
*err = driver.ErrBadConn
|
||||||
|
} else {
|
||||||
|
*err = v
|
||||||
|
}
|
||||||
|
case *net.OpError:
|
||||||
|
*err = driver.ErrBadConn
|
||||||
|
case error:
|
||||||
|
if v == io.EOF || v.(error).Error() == "remote error: handshake failure" {
|
||||||
|
*err = driver.ErrBadConn
|
||||||
|
} else {
|
||||||
|
*err = v
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
c.bad = true
|
||||||
|
panic(fmt.Sprintf("unknown error: %#v", e))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Any time we return ErrBadConn, we need to remember it since *Tx doesn't
|
||||||
|
// mark the connection bad in database/sql.
|
||||||
|
if *err == driver.ErrBadConn {
|
||||||
|
c.bad = true
|
||||||
|
}
|
||||||
|
}
|
782
vendor/github.com/lib/pq/notify.go
generated
vendored
Normal file
782
vendor/github.com/lib/pq/notify.go
generated
vendored
Normal file
@ -0,0 +1,782 @@
|
|||||||
|
package pq
|
||||||
|
|
||||||
|
// Package pq is a pure Go Postgres driver for the database/sql package.
|
||||||
|
// This module contains support for Postgres LISTEN/NOTIFY.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Notification represents a single notification from the database.
|
||||||
|
type Notification struct {
|
||||||
|
// Process ID (PID) of the notifying postgres backend.
|
||||||
|
BePid int
|
||||||
|
// Name of the channel the notification was sent on.
|
||||||
|
Channel string
|
||||||
|
// Payload, or the empty string if unspecified.
|
||||||
|
Extra string
|
||||||
|
}
|
||||||
|
|
||||||
|
func recvNotification(r *readBuf) *Notification {
|
||||||
|
bePid := r.int32()
|
||||||
|
channel := r.string()
|
||||||
|
extra := r.string()
|
||||||
|
|
||||||
|
return &Notification{bePid, channel, extra}
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
connStateIdle int32 = iota
|
||||||
|
connStateExpectResponse
|
||||||
|
connStateExpectReadyForQuery
|
||||||
|
)
|
||||||
|
|
||||||
|
type message struct {
|
||||||
|
typ byte
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
var errListenerConnClosed = errors.New("pq: ListenerConn has been closed")
|
||||||
|
|
||||||
|
// ListenerConn is a low-level interface for waiting for notifications. You
|
||||||
|
// should use Listener instead.
|
||||||
|
type ListenerConn struct {
|
||||||
|
// guards cn and err
|
||||||
|
connectionLock sync.Mutex
|
||||||
|
cn *conn
|
||||||
|
err error
|
||||||
|
|
||||||
|
connState int32
|
||||||
|
|
||||||
|
// the sending goroutine will be holding this lock
|
||||||
|
senderLock sync.Mutex
|
||||||
|
|
||||||
|
notificationChan chan<- *Notification
|
||||||
|
|
||||||
|
replyChan chan message
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a new ListenerConn. Use NewListener instead.
|
||||||
|
func NewListenerConn(name string, notificationChan chan<- *Notification) (*ListenerConn, error) {
|
||||||
|
return newDialListenerConn(defaultDialer{}, name, notificationChan)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDialListenerConn(d Dialer, name string, c chan<- *Notification) (*ListenerConn, error) {
|
||||||
|
cn, err := DialOpen(d, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
l := &ListenerConn{
|
||||||
|
cn: cn.(*conn),
|
||||||
|
notificationChan: c,
|
||||||
|
connState: connStateIdle,
|
||||||
|
replyChan: make(chan message, 2),
|
||||||
|
}
|
||||||
|
|
||||||
|
go l.listenerConnMain()
|
||||||
|
|
||||||
|
return l, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can only allow one goroutine at a time to be running a query on the
|
||||||
|
// connection for various reasons, so the goroutine sending on the connection
|
||||||
|
// must be holding senderLock.
|
||||||
|
//
|
||||||
|
// Returns an error if an unrecoverable error has occurred and the ListenerConn
|
||||||
|
// should be abandoned.
|
||||||
|
func (l *ListenerConn) acquireSenderLock() error {
|
||||||
|
// we must acquire senderLock first to avoid deadlocks; see ExecSimpleQuery
|
||||||
|
l.senderLock.Lock()
|
||||||
|
|
||||||
|
l.connectionLock.Lock()
|
||||||
|
err := l.err
|
||||||
|
l.connectionLock.Unlock()
|
||||||
|
if err != nil {
|
||||||
|
l.senderLock.Unlock()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *ListenerConn) releaseSenderLock() {
|
||||||
|
l.senderLock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// setState advances the protocol state to newState. Returns false if moving
|
||||||
|
// to that state from the current state is not allowed.
|
||||||
|
func (l *ListenerConn) setState(newState int32) bool {
|
||||||
|
var expectedState int32
|
||||||
|
|
||||||
|
switch newState {
|
||||||
|
case connStateIdle:
|
||||||
|
expectedState = connStateExpectReadyForQuery
|
||||||
|
case connStateExpectResponse:
|
||||||
|
expectedState = connStateIdle
|
||||||
|
case connStateExpectReadyForQuery:
|
||||||
|
expectedState = connStateExpectResponse
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unexpected listenerConnState %d", newState))
|
||||||
|
}
|
||||||
|
|
||||||
|
return atomic.CompareAndSwapInt32(&l.connState, expectedState, newState)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main logic is here: receive messages from the postgres backend, forward
|
||||||
|
// notifications and query replies and keep the internal state in sync with the
|
||||||
|
// protocol state. Returns when the connection has been lost, is about to go
|
||||||
|
// away or should be discarded because we couldn't agree on the state with the
|
||||||
|
// server backend.
|
||||||
|
func (l *ListenerConn) listenerConnLoop() (err error) {
|
||||||
|
defer errRecoverNoErrBadConn(&err)
|
||||||
|
|
||||||
|
r := &readBuf{}
|
||||||
|
for {
|
||||||
|
t, err := l.cn.recvMessage(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch t {
|
||||||
|
case 'A':
|
||||||
|
// recvNotification copies all the data so we don't need to worry
|
||||||
|
// about the scratch buffer being overwritten.
|
||||||
|
l.notificationChan <- recvNotification(r)
|
||||||
|
|
||||||
|
case 'T', 'D':
|
||||||
|
// only used by tests; ignore
|
||||||
|
|
||||||
|
case 'E':
|
||||||
|
// We might receive an ErrorResponse even when not in a query; it
|
||||||
|
// is expected that the server will close the connection after
|
||||||
|
// that, but we should make sure that the error we display is the
|
||||||
|
// one from the stray ErrorResponse, not io.ErrUnexpectedEOF.
|
||||||
|
if !l.setState(connStateExpectReadyForQuery) {
|
||||||
|
return parseError(r)
|
||||||
|
}
|
||||||
|
l.replyChan <- message{t, parseError(r)}
|
||||||
|
|
||||||
|
case 'C', 'I':
|
||||||
|
if !l.setState(connStateExpectReadyForQuery) {
|
||||||
|
// protocol out of sync
|
||||||
|
return fmt.Errorf("unexpected CommandComplete")
|
||||||
|
}
|
||||||
|
// ExecSimpleQuery doesn't need to know about this message
|
||||||
|
|
||||||
|
case 'Z':
|
||||||
|
if !l.setState(connStateIdle) {
|
||||||
|
// protocol out of sync
|
||||||
|
return fmt.Errorf("unexpected ReadyForQuery")
|
||||||
|
}
|
||||||
|
l.replyChan <- message{t, nil}
|
||||||
|
|
||||||
|
case 'N', 'S':
|
||||||
|
// ignore
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unexpected message %q from server in listenerConnLoop", t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the main routine for the goroutine receiving on the database
|
||||||
|
// connection. Most of the main logic is in listenerConnLoop.
|
||||||
|
func (l *ListenerConn) listenerConnMain() {
|
||||||
|
err := l.listenerConnLoop()
|
||||||
|
|
||||||
|
// listenerConnLoop terminated; we're done, but we still have to clean up.
|
||||||
|
// Make sure nobody tries to start any new queries by making sure the err
|
||||||
|
// pointer is set. It is important that we do not overwrite its value; a
|
||||||
|
// connection could be closed by either this goroutine or one sending on
|
||||||
|
// the connection -- whoever closes the connection is assumed to have the
|
||||||
|
// more meaningful error message (as the other one will probably get
|
||||||
|
// net.errClosed), so that goroutine sets the error we expose while the
|
||||||
|
// other error is discarded. If the connection is lost while two
|
||||||
|
// goroutines are operating on the socket, it probably doesn't matter which
|
||||||
|
// error we expose so we don't try to do anything more complex.
|
||||||
|
l.connectionLock.Lock()
|
||||||
|
if l.err == nil {
|
||||||
|
l.err = err
|
||||||
|
}
|
||||||
|
l.cn.Close()
|
||||||
|
l.connectionLock.Unlock()
|
||||||
|
|
||||||
|
// There might be a query in-flight; make sure nobody's waiting for a
|
||||||
|
// response to it, since there's not going to be one.
|
||||||
|
close(l.replyChan)
|
||||||
|
|
||||||
|
// let the listener know we're done
|
||||||
|
close(l.notificationChan)
|
||||||
|
|
||||||
|
// this ListenerConn is done
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send a LISTEN query to the server. See ExecSimpleQuery.
|
||||||
|
func (l *ListenerConn) Listen(channel string) (bool, error) {
|
||||||
|
return l.ExecSimpleQuery("LISTEN " + QuoteIdentifier(channel))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send an UNLISTEN query to the server. See ExecSimpleQuery.
|
||||||
|
func (l *ListenerConn) Unlisten(channel string) (bool, error) {
|
||||||
|
return l.ExecSimpleQuery("UNLISTEN " + QuoteIdentifier(channel))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send `UNLISTEN *` to the server. See ExecSimpleQuery.
|
||||||
|
func (l *ListenerConn) UnlistenAll() (bool, error) {
|
||||||
|
return l.ExecSimpleQuery("UNLISTEN *")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ping the remote server to make sure it's alive. Non-nil error means the
|
||||||
|
// connection has failed and should be abandoned.
|
||||||
|
func (l *ListenerConn) Ping() error {
|
||||||
|
sent, err := l.ExecSimpleQuery("")
|
||||||
|
if !sent {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
// shouldn't happen
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to send a query on the connection. Returns an error if sending the
|
||||||
|
// query failed, and the caller should initiate closure of this connection.
|
||||||
|
// The caller must be holding senderLock (see acquireSenderLock and
|
||||||
|
// releaseSenderLock).
|
||||||
|
func (l *ListenerConn) sendSimpleQuery(q string) (err error) {
|
||||||
|
defer errRecoverNoErrBadConn(&err)
|
||||||
|
|
||||||
|
// must set connection state before sending the query
|
||||||
|
if !l.setState(connStateExpectResponse) {
|
||||||
|
panic("two queries running at the same time")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can't use l.cn.writeBuf here because it uses the scratch buffer which
|
||||||
|
// might get overwritten by listenerConnLoop.
|
||||||
|
b := &writeBuf{
|
||||||
|
buf: []byte("Q\x00\x00\x00\x00"),
|
||||||
|
pos: 1,
|
||||||
|
}
|
||||||
|
b.string(q)
|
||||||
|
l.cn.send(b)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute a "simple query" (i.e. one with no bindable parameters) on the
|
||||||
|
// connection. The possible return values are:
|
||||||
|
// 1) "executed" is true; the query was executed to completion on the
|
||||||
|
// database server. If the query failed, err will be set to the error
|
||||||
|
// returned by the database, otherwise err will be nil.
|
||||||
|
// 2) If "executed" is false, the query could not be executed on the remote
|
||||||
|
// server. err will be non-nil.
|
||||||
|
//
|
||||||
|
// After a call to ExecSimpleQuery has returned an executed=false value, the
|
||||||
|
// connection has either been closed or will be closed shortly thereafter, and
|
||||||
|
// all subsequently executed queries will return an error.
|
||||||
|
func (l *ListenerConn) ExecSimpleQuery(q string) (executed bool, err error) {
|
||||||
|
if err = l.acquireSenderLock(); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
defer l.releaseSenderLock()
|
||||||
|
|
||||||
|
err = l.sendSimpleQuery(q)
|
||||||
|
if err != nil {
|
||||||
|
// We can't know what state the protocol is in, so we need to abandon
|
||||||
|
// this connection.
|
||||||
|
l.connectionLock.Lock()
|
||||||
|
// Set the error pointer if it hasn't been set already; see
|
||||||
|
// listenerConnMain.
|
||||||
|
if l.err == nil {
|
||||||
|
l.err = err
|
||||||
|
}
|
||||||
|
l.connectionLock.Unlock()
|
||||||
|
l.cn.c.Close()
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// now we just wait for a reply..
|
||||||
|
for {
|
||||||
|
m, ok := <-l.replyChan
|
||||||
|
if !ok {
|
||||||
|
// We lost the connection to server, don't bother waiting for a
|
||||||
|
// a response. err should have been set already.
|
||||||
|
l.connectionLock.Lock()
|
||||||
|
err := l.err
|
||||||
|
l.connectionLock.Unlock()
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
switch m.typ {
|
||||||
|
case 'Z':
|
||||||
|
// sanity check
|
||||||
|
if m.err != nil {
|
||||||
|
panic("m.err != nil")
|
||||||
|
}
|
||||||
|
// done; err might or might not be set
|
||||||
|
return true, err
|
||||||
|
|
||||||
|
case 'E':
|
||||||
|
// sanity check
|
||||||
|
if m.err == nil {
|
||||||
|
panic("m.err == nil")
|
||||||
|
}
|
||||||
|
// server responded with an error; ReadyForQuery to follow
|
||||||
|
err = m.err
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false, fmt.Errorf("unknown response for simple query: %q", m.typ)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *ListenerConn) Close() error {
|
||||||
|
l.connectionLock.Lock()
|
||||||
|
if l.err != nil {
|
||||||
|
l.connectionLock.Unlock()
|
||||||
|
return errListenerConnClosed
|
||||||
|
}
|
||||||
|
l.err = errListenerConnClosed
|
||||||
|
l.connectionLock.Unlock()
|
||||||
|
// We can't send anything on the connection without holding senderLock.
|
||||||
|
// Simply close the net.Conn to wake up everyone operating on it.
|
||||||
|
return l.cn.c.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Err() returns the reason the connection was closed. It is not safe to call
|
||||||
|
// this function until l.Notify has been closed.
|
||||||
|
func (l *ListenerConn) Err() error {
|
||||||
|
return l.err
|
||||||
|
}
|
||||||
|
|
||||||
|
var errListenerClosed = errors.New("pq: Listener has been closed")
|
||||||
|
|
||||||
|
var ErrChannelAlreadyOpen = errors.New("pq: channel is already open")
|
||||||
|
var ErrChannelNotOpen = errors.New("pq: channel is not open")
|
||||||
|
|
||||||
|
type ListenerEventType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Emitted only when the database connection has been initially
|
||||||
|
// initialized. err will always be nil.
|
||||||
|
ListenerEventConnected ListenerEventType = iota
|
||||||
|
|
||||||
|
// Emitted after a database connection has been lost, either because of an
|
||||||
|
// error or because Close has been called. err will be set to the reason
|
||||||
|
// the database connection was lost.
|
||||||
|
ListenerEventDisconnected
|
||||||
|
|
||||||
|
// Emitted after a database connection has been re-established after
|
||||||
|
// connection loss. err will always be nil. After this event has been
|
||||||
|
// emitted, a nil pq.Notification is sent on the Listener.Notify channel.
|
||||||
|
ListenerEventReconnected
|
||||||
|
|
||||||
|
// Emitted after a connection to the database was attempted, but failed.
|
||||||
|
// err will be set to an error describing why the connection attempt did
|
||||||
|
// not succeed.
|
||||||
|
ListenerEventConnectionAttemptFailed
|
||||||
|
)
|
||||||
|
|
||||||
|
type EventCallbackType func(event ListenerEventType, err error)
|
||||||
|
|
||||||
|
// Listener provides an interface for listening to notifications from a
|
||||||
|
// PostgreSQL database. For general usage information, see section
|
||||||
|
// "Notifications".
|
||||||
|
//
|
||||||
|
// Listener can safely be used from concurrently running goroutines.
|
||||||
|
type Listener struct {
|
||||||
|
// Channel for receiving notifications from the database. In some cases a
|
||||||
|
// nil value will be sent. See section "Notifications" above.
|
||||||
|
Notify chan *Notification
|
||||||
|
|
||||||
|
name string
|
||||||
|
minReconnectInterval time.Duration
|
||||||
|
maxReconnectInterval time.Duration
|
||||||
|
dialer Dialer
|
||||||
|
eventCallback EventCallbackType
|
||||||
|
|
||||||
|
lock sync.Mutex
|
||||||
|
isClosed bool
|
||||||
|
reconnectCond *sync.Cond
|
||||||
|
cn *ListenerConn
|
||||||
|
connNotificationChan <-chan *Notification
|
||||||
|
channels map[string]struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewListener creates a new database connection dedicated to LISTEN / NOTIFY.
|
||||||
|
//
|
||||||
|
// name should be set to a connection string to be used to establish the
|
||||||
|
// database connection (see section "Connection String Parameters" above).
|
||||||
|
//
|
||||||
|
// minReconnectInterval controls the duration to wait before trying to
|
||||||
|
// re-establish the database connection after connection loss. After each
|
||||||
|
// consecutive failure this interval is doubled, until maxReconnectInterval is
|
||||||
|
// reached. Successfully completing the connection establishment procedure
|
||||||
|
// resets the interval back to minReconnectInterval.
|
||||||
|
//
|
||||||
|
// The last parameter eventCallback can be set to a function which will be
|
||||||
|
// called by the Listener when the state of the underlying database connection
|
||||||
|
// changes. This callback will be called by the goroutine which dispatches the
|
||||||
|
// notifications over the Notify channel, so you should try to avoid doing
|
||||||
|
// potentially time-consuming operations from the callback.
|
||||||
|
func NewListener(name string,
|
||||||
|
minReconnectInterval time.Duration,
|
||||||
|
maxReconnectInterval time.Duration,
|
||||||
|
eventCallback EventCallbackType) *Listener {
|
||||||
|
return NewDialListener(defaultDialer{}, name, minReconnectInterval, maxReconnectInterval, eventCallback)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDialListener is like NewListener but it takes a Dialer.
|
||||||
|
func NewDialListener(d Dialer,
|
||||||
|
name string,
|
||||||
|
minReconnectInterval time.Duration,
|
||||||
|
maxReconnectInterval time.Duration,
|
||||||
|
eventCallback EventCallbackType) *Listener {
|
||||||
|
|
||||||
|
l := &Listener{
|
||||||
|
name: name,
|
||||||
|
minReconnectInterval: minReconnectInterval,
|
||||||
|
maxReconnectInterval: maxReconnectInterval,
|
||||||
|
dialer: d,
|
||||||
|
eventCallback: eventCallback,
|
||||||
|
|
||||||
|
channels: make(map[string]struct{}),
|
||||||
|
|
||||||
|
Notify: make(chan *Notification, 32),
|
||||||
|
}
|
||||||
|
l.reconnectCond = sync.NewCond(&l.lock)
|
||||||
|
|
||||||
|
go l.listenerMain()
|
||||||
|
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the notification channel for this listener. This is the same
|
||||||
|
// channel as Notify, and will not be recreated during the life time of the
|
||||||
|
// Listener.
|
||||||
|
func (l *Listener) NotificationChannel() <-chan *Notification {
|
||||||
|
return l.Notify
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen starts listening for notifications on a channel. Calls to this
|
||||||
|
// function will block until an acknowledgement has been received from the
|
||||||
|
// server. Note that Listener automatically re-establishes the connection
|
||||||
|
// after connection loss, so this function may block indefinitely if the
|
||||||
|
// connection can not be re-established.
|
||||||
|
//
|
||||||
|
// Listen will only fail in three conditions:
|
||||||
|
// 1) The channel is already open. The returned error will be
|
||||||
|
// ErrChannelAlreadyOpen.
|
||||||
|
// 2) The query was executed on the remote server, but PostgreSQL returned an
|
||||||
|
// error message in response to the query. The returned error will be a
|
||||||
|
// pq.Error containing the information the server supplied.
|
||||||
|
// 3) Close is called on the Listener before the request could be completed.
|
||||||
|
//
|
||||||
|
// The channel name is case-sensitive.
|
||||||
|
func (l *Listener) Listen(channel string) error {
|
||||||
|
l.lock.Lock()
|
||||||
|
defer l.lock.Unlock()
|
||||||
|
|
||||||
|
if l.isClosed {
|
||||||
|
return errListenerClosed
|
||||||
|
}
|
||||||
|
|
||||||
|
// The server allows you to issue a LISTEN on a channel which is already
|
||||||
|
// open, but it seems useful to be able to detect this case to spot for
|
||||||
|
// mistakes in application logic. If the application genuinely does't
|
||||||
|
// care, it can check the exported error and ignore it.
|
||||||
|
_, exists := l.channels[channel]
|
||||||
|
if exists {
|
||||||
|
return ErrChannelAlreadyOpen
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.cn != nil {
|
||||||
|
// If gotResponse is true but error is set, the query was executed on
|
||||||
|
// the remote server, but resulted in an error. This should be
|
||||||
|
// relatively rare, so it's fine if we just pass the error to our
|
||||||
|
// caller. However, if gotResponse is false, we could not complete the
|
||||||
|
// query on the remote server and our underlying connection is about
|
||||||
|
// to go away, so we only add relname to l.channels, and wait for
|
||||||
|
// resync() to take care of the rest.
|
||||||
|
gotResponse, err := l.cn.Listen(channel)
|
||||||
|
if gotResponse && err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
l.channels[channel] = struct{}{}
|
||||||
|
for l.cn == nil {
|
||||||
|
l.reconnectCond.Wait()
|
||||||
|
// we let go of the mutex for a while
|
||||||
|
if l.isClosed {
|
||||||
|
return errListenerClosed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlisten removes a channel from the Listener's channel list. Returns
|
||||||
|
// ErrChannelNotOpen if the Listener is not listening on the specified channel.
|
||||||
|
// Returns immediately with no error if there is no connection. Note that you
|
||||||
|
// might still get notifications for this channel even after Unlisten has
|
||||||
|
// returned.
|
||||||
|
//
|
||||||
|
// The channel name is case-sensitive.
|
||||||
|
func (l *Listener) Unlisten(channel string) error {
|
||||||
|
l.lock.Lock()
|
||||||
|
defer l.lock.Unlock()
|
||||||
|
|
||||||
|
if l.isClosed {
|
||||||
|
return errListenerClosed
|
||||||
|
}
|
||||||
|
|
||||||
|
// Similarly to LISTEN, this is not an error in Postgres, but it seems
|
||||||
|
// useful to distinguish from the normal conditions.
|
||||||
|
_, exists := l.channels[channel]
|
||||||
|
if !exists {
|
||||||
|
return ErrChannelNotOpen
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.cn != nil {
|
||||||
|
// Similarly to Listen (see comment in that function), the caller
|
||||||
|
// should only be bothered with an error if it came from the backend as
|
||||||
|
// a response to our query.
|
||||||
|
gotResponse, err := l.cn.Unlisten(channel)
|
||||||
|
if gotResponse && err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't bother waiting for resync if there's no connection.
|
||||||
|
delete(l.channels, channel)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnlistenAll removes all channels from the Listener's channel list. Returns
|
||||||
|
// immediately with no error if there is no connection. Note that you might
|
||||||
|
// still get notifications for any of the deleted channels even after
|
||||||
|
// UnlistenAll has returned.
|
||||||
|
func (l *Listener) UnlistenAll() error {
|
||||||
|
l.lock.Lock()
|
||||||
|
defer l.lock.Unlock()
|
||||||
|
|
||||||
|
if l.isClosed {
|
||||||
|
return errListenerClosed
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.cn != nil {
|
||||||
|
// Similarly to Listen (see comment in that function), the caller
|
||||||
|
// should only be bothered with an error if it came from the backend as
|
||||||
|
// a response to our query.
|
||||||
|
gotResponse, err := l.cn.UnlistenAll()
|
||||||
|
if gotResponse && err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't bother waiting for resync if there's no connection.
|
||||||
|
l.channels = make(map[string]struct{})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ping the remote server to make sure it's alive. Non-nil return value means
|
||||||
|
// that there is no active connection.
|
||||||
|
func (l *Listener) Ping() error {
|
||||||
|
l.lock.Lock()
|
||||||
|
defer l.lock.Unlock()
|
||||||
|
|
||||||
|
if l.isClosed {
|
||||||
|
return errListenerClosed
|
||||||
|
}
|
||||||
|
if l.cn == nil {
|
||||||
|
return errors.New("no connection")
|
||||||
|
}
|
||||||
|
|
||||||
|
return l.cn.Ping()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up after losing the server connection. Returns l.cn.Err(), which
|
||||||
|
// should have the reason the connection was lost.
|
||||||
|
func (l *Listener) disconnectCleanup() error {
|
||||||
|
l.lock.Lock()
|
||||||
|
defer l.lock.Unlock()
|
||||||
|
|
||||||
|
// sanity check; can't look at Err() until the channel has been closed
|
||||||
|
select {
|
||||||
|
case _, ok := <-l.connNotificationChan:
|
||||||
|
if ok {
|
||||||
|
panic("connNotificationChan not closed")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic("connNotificationChan not closed")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := l.cn.Err()
|
||||||
|
l.cn.Close()
|
||||||
|
l.cn = nil
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Synchronize the list of channels we want to be listening on with the server
|
||||||
|
// after the connection has been established.
|
||||||
|
func (l *Listener) resync(cn *ListenerConn, notificationChan <-chan *Notification) error {
|
||||||
|
doneChan := make(chan error)
|
||||||
|
go func() {
|
||||||
|
for channel := range l.channels {
|
||||||
|
// If we got a response, return that error to our caller as it's
|
||||||
|
// going to be more descriptive than cn.Err().
|
||||||
|
gotResponse, err := cn.Listen(channel)
|
||||||
|
if gotResponse && err != nil {
|
||||||
|
doneChan <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we couldn't reach the server, wait for notificationChan to
|
||||||
|
// close and then return the error message from the connection, as
|
||||||
|
// per ListenerConn's interface.
|
||||||
|
if err != nil {
|
||||||
|
for _ = range notificationChan {
|
||||||
|
}
|
||||||
|
doneChan <- cn.Err()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
doneChan <- nil
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Ignore notifications while synchronization is going on to avoid
|
||||||
|
// deadlocks. We have to send a nil notification over Notify anyway as
|
||||||
|
// we can't possibly know which notifications (if any) were lost while
|
||||||
|
// the connection was down, so there's no reason to try and process
|
||||||
|
// these messages at all.
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case _, ok := <-notificationChan:
|
||||||
|
if !ok {
|
||||||
|
notificationChan = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
case err := <-doneChan:
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// caller should NOT be holding l.lock
|
||||||
|
func (l *Listener) closed() bool {
|
||||||
|
l.lock.Lock()
|
||||||
|
defer l.lock.Unlock()
|
||||||
|
|
||||||
|
return l.isClosed
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Listener) connect() error {
|
||||||
|
notificationChan := make(chan *Notification, 32)
|
||||||
|
cn, err := newDialListenerConn(l.dialer, l.name, notificationChan)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
l.lock.Lock()
|
||||||
|
defer l.lock.Unlock()
|
||||||
|
|
||||||
|
err = l.resync(cn, notificationChan)
|
||||||
|
if err != nil {
|
||||||
|
cn.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
l.cn = cn
|
||||||
|
l.connNotificationChan = notificationChan
|
||||||
|
l.reconnectCond.Broadcast()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close disconnects the Listener from the database and shuts it down.
|
||||||
|
// Subsequent calls to its methods will return an error. Close returns an
|
||||||
|
// error if the connection has already been closed.
|
||||||
|
func (l *Listener) Close() error {
|
||||||
|
l.lock.Lock()
|
||||||
|
defer l.lock.Unlock()
|
||||||
|
|
||||||
|
if l.isClosed {
|
||||||
|
return errListenerClosed
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.cn != nil {
|
||||||
|
l.cn.Close()
|
||||||
|
}
|
||||||
|
l.isClosed = true
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Listener) emitEvent(event ListenerEventType, err error) {
|
||||||
|
if l.eventCallback != nil {
|
||||||
|
l.eventCallback(event, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main logic here: maintain a connection to the server when possible, wait
|
||||||
|
// for notifications and emit events.
|
||||||
|
func (l *Listener) listenerConnLoop() {
|
||||||
|
var nextReconnect time.Time
|
||||||
|
|
||||||
|
reconnectInterval := l.minReconnectInterval
|
||||||
|
for {
|
||||||
|
for {
|
||||||
|
err := l.connect()
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.closed() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
l.emitEvent(ListenerEventConnectionAttemptFailed, err)
|
||||||
|
|
||||||
|
time.Sleep(reconnectInterval)
|
||||||
|
reconnectInterval *= 2
|
||||||
|
if reconnectInterval > l.maxReconnectInterval {
|
||||||
|
reconnectInterval = l.maxReconnectInterval
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if nextReconnect.IsZero() {
|
||||||
|
l.emitEvent(ListenerEventConnected, nil)
|
||||||
|
} else {
|
||||||
|
l.emitEvent(ListenerEventReconnected, nil)
|
||||||
|
l.Notify <- nil
|
||||||
|
}
|
||||||
|
|
||||||
|
reconnectInterval = l.minReconnectInterval
|
||||||
|
nextReconnect = time.Now().Add(reconnectInterval)
|
||||||
|
|
||||||
|
for {
|
||||||
|
notification, ok := <-l.connNotificationChan
|
||||||
|
if !ok {
|
||||||
|
// lost connection, loop again
|
||||||
|
break
|
||||||
|
}
|
||||||
|
l.Notify <- notification
|
||||||
|
}
|
||||||
|
|
||||||
|
err := l.disconnectCleanup()
|
||||||
|
if l.closed() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
l.emitEvent(ListenerEventDisconnected, err)
|
||||||
|
|
||||||
|
time.Sleep(nextReconnect.Sub(time.Now()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Listener) listenerMain() {
|
||||||
|
l.listenerConnLoop()
|
||||||
|
close(l.Notify)
|
||||||
|
}
|
574
vendor/github.com/lib/pq/notify_test.go
generated
vendored
Normal file
574
vendor/github.com/lib/pq/notify_test.go
generated
vendored
Normal file
@ -0,0 +1,574 @@
|
|||||||
|
package pq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errNilNotification = errors.New("nil notification")
|
||||||
|
|
||||||
|
func expectNotification(t *testing.T, ch <-chan *Notification, relname string, extra string) error {
|
||||||
|
select {
|
||||||
|
case n := <-ch:
|
||||||
|
if n == nil {
|
||||||
|
return errNilNotification
|
||||||
|
}
|
||||||
|
if n.Channel != relname || n.Extra != extra {
|
||||||
|
return fmt.Errorf("unexpected notification %v", n)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
case <-time.After(1500 * time.Millisecond):
|
||||||
|
return fmt.Errorf("timeout")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func expectNoNotification(t *testing.T, ch <-chan *Notification) error {
|
||||||
|
select {
|
||||||
|
case n := <-ch:
|
||||||
|
return fmt.Errorf("unexpected notification %v", n)
|
||||||
|
case <-time.After(100 * time.Millisecond):
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func expectEvent(t *testing.T, eventch <-chan ListenerEventType, et ListenerEventType) error {
|
||||||
|
select {
|
||||||
|
case e := <-eventch:
|
||||||
|
if e != et {
|
||||||
|
return fmt.Errorf("unexpected event %v", e)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
case <-time.After(1500 * time.Millisecond):
|
||||||
|
panic("expectEvent timeout")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func expectNoEvent(t *testing.T, eventch <-chan ListenerEventType) error {
|
||||||
|
select {
|
||||||
|
case e := <-eventch:
|
||||||
|
return fmt.Errorf("unexpected event %v", e)
|
||||||
|
case <-time.After(100 * time.Millisecond):
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestListenerConn(t *testing.T) (*ListenerConn, <-chan *Notification) {
|
||||||
|
datname := os.Getenv("PGDATABASE")
|
||||||
|
sslmode := os.Getenv("PGSSLMODE")
|
||||||
|
|
||||||
|
if datname == "" {
|
||||||
|
os.Setenv("PGDATABASE", "pqgotest")
|
||||||
|
}
|
||||||
|
|
||||||
|
if sslmode == "" {
|
||||||
|
os.Setenv("PGSSLMODE", "disable")
|
||||||
|
}
|
||||||
|
|
||||||
|
notificationChan := make(chan *Notification)
|
||||||
|
l, err := NewListenerConn("", notificationChan)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return l, notificationChan
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewListenerConn(t *testing.T) {
|
||||||
|
l, _ := newTestListenerConn(t)
|
||||||
|
|
||||||
|
defer l.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConnListen(t *testing.T) {
|
||||||
|
l, channel := newTestListenerConn(t)
|
||||||
|
|
||||||
|
defer l.Close()
|
||||||
|
|
||||||
|
db := openTestConn(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
ok, err := l.Listen("notify_test")
|
||||||
|
if !ok || err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.Exec("NOTIFY notify_test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = expectNotification(t, channel, "notify_test", "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConnUnlisten(t *testing.T) {
|
||||||
|
l, channel := newTestListenerConn(t)
|
||||||
|
|
||||||
|
defer l.Close()
|
||||||
|
|
||||||
|
db := openTestConn(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
ok, err := l.Listen("notify_test")
|
||||||
|
if !ok || err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.Exec("NOTIFY notify_test")
|
||||||
|
|
||||||
|
err = expectNotification(t, channel, "notify_test", "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, err = l.Unlisten("notify_test")
|
||||||
|
if !ok || err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.Exec("NOTIFY notify_test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = expectNoNotification(t, channel)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConnUnlistenAll(t *testing.T) {
|
||||||
|
l, channel := newTestListenerConn(t)
|
||||||
|
|
||||||
|
defer l.Close()
|
||||||
|
|
||||||
|
db := openTestConn(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
ok, err := l.Listen("notify_test")
|
||||||
|
if !ok || err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.Exec("NOTIFY notify_test")
|
||||||
|
|
||||||
|
err = expectNotification(t, channel, "notify_test", "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, err = l.UnlistenAll()
|
||||||
|
if !ok || err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.Exec("NOTIFY notify_test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = expectNoNotification(t, channel)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConnClose(t *testing.T) {
|
||||||
|
l, _ := newTestListenerConn(t)
|
||||||
|
defer l.Close()
|
||||||
|
|
||||||
|
err := l.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
err = l.Close()
|
||||||
|
if err != errListenerConnClosed {
|
||||||
|
t.Fatalf("expected errListenerConnClosed; got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConnPing(t *testing.T) {
|
||||||
|
l, _ := newTestListenerConn(t)
|
||||||
|
defer l.Close()
|
||||||
|
err := l.Ping()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
err = l.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
err = l.Ping()
|
||||||
|
if err != errListenerConnClosed {
|
||||||
|
t.Fatalf("expected errListenerConnClosed; got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test for deadlock where a query fails while another one is queued
|
||||||
|
func TestConnExecDeadlock(t *testing.T) {
|
||||||
|
l, _ := newTestListenerConn(t)
|
||||||
|
defer l.Close()
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(2)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
l.ExecSimpleQuery("SELECT pg_sleep(60)")
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
runtime.Gosched()
|
||||||
|
go func() {
|
||||||
|
l.ExecSimpleQuery("SELECT 1")
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
// give the two goroutines some time to get into position
|
||||||
|
runtime.Gosched()
|
||||||
|
// calls Close on the net.Conn; equivalent to a network failure
|
||||||
|
l.Close()
|
||||||
|
|
||||||
|
var done int32 = 0
|
||||||
|
go func() {
|
||||||
|
time.Sleep(10 * time.Second)
|
||||||
|
if atomic.LoadInt32(&done) != 1 {
|
||||||
|
panic("timed out")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
wg.Wait()
|
||||||
|
atomic.StoreInt32(&done, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test for ListenerConn being closed while a slow query is executing
|
||||||
|
func TestListenerConnCloseWhileQueryIsExecuting(t *testing.T) {
|
||||||
|
l, _ := newTestListenerConn(t)
|
||||||
|
defer l.Close()
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(1)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
sent, err := l.ExecSimpleQuery("SELECT pg_sleep(60)")
|
||||||
|
if sent {
|
||||||
|
panic("expected sent=false")
|
||||||
|
}
|
||||||
|
// could be any of a number of errors
|
||||||
|
if err == nil {
|
||||||
|
panic("expected error")
|
||||||
|
}
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
// give the above goroutine some time to get into position
|
||||||
|
runtime.Gosched()
|
||||||
|
err := l.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
var done int32 = 0
|
||||||
|
go func() {
|
||||||
|
time.Sleep(10 * time.Second)
|
||||||
|
if atomic.LoadInt32(&done) != 1 {
|
||||||
|
panic("timed out")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
wg.Wait()
|
||||||
|
atomic.StoreInt32(&done, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNotifyExtra(t *testing.T) {
|
||||||
|
db := openTestConn(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
if getServerVersion(t, db) < 90000 {
|
||||||
|
t.Skip("skipping NOTIFY payload test since the server does not appear to support it")
|
||||||
|
}
|
||||||
|
|
||||||
|
l, channel := newTestListenerConn(t)
|
||||||
|
defer l.Close()
|
||||||
|
|
||||||
|
ok, err := l.Listen("notify_test")
|
||||||
|
if !ok || err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.Exec("NOTIFY notify_test, 'something'")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = expectNotification(t, channel, "notify_test", "something")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a new test listener and also set the timeouts
|
||||||
|
func newTestListenerTimeout(t *testing.T, min time.Duration, max time.Duration) (*Listener, <-chan ListenerEventType) {
|
||||||
|
datname := os.Getenv("PGDATABASE")
|
||||||
|
sslmode := os.Getenv("PGSSLMODE")
|
||||||
|
|
||||||
|
if datname == "" {
|
||||||
|
os.Setenv("PGDATABASE", "pqgotest")
|
||||||
|
}
|
||||||
|
|
||||||
|
if sslmode == "" {
|
||||||
|
os.Setenv("PGSSLMODE", "disable")
|
||||||
|
}
|
||||||
|
|
||||||
|
eventch := make(chan ListenerEventType, 16)
|
||||||
|
l := NewListener("", min, max, func(t ListenerEventType, err error) { eventch <- t })
|
||||||
|
err := expectEvent(t, eventch, ListenerEventConnected)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return l, eventch
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestListener(t *testing.T) (*Listener, <-chan ListenerEventType) {
|
||||||
|
return newTestListenerTimeout(t, time.Hour, time.Hour)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListenerListen(t *testing.T) {
|
||||||
|
l, _ := newTestListener(t)
|
||||||
|
defer l.Close()
|
||||||
|
|
||||||
|
db := openTestConn(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
err := l.Listen("notify_listen_test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.Exec("NOTIFY notify_listen_test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = expectNotification(t, l.Notify, "notify_listen_test", "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListenerUnlisten(t *testing.T) {
|
||||||
|
l, _ := newTestListener(t)
|
||||||
|
defer l.Close()
|
||||||
|
|
||||||
|
db := openTestConn(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
err := l.Listen("notify_listen_test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.Exec("NOTIFY notify_listen_test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = l.Unlisten("notify_listen_test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = expectNotification(t, l.Notify, "notify_listen_test", "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.Exec("NOTIFY notify_listen_test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = expectNoNotification(t, l.Notify)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListenerUnlistenAll(t *testing.T) {
|
||||||
|
l, _ := newTestListener(t)
|
||||||
|
defer l.Close()
|
||||||
|
|
||||||
|
db := openTestConn(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
err := l.Listen("notify_listen_test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.Exec("NOTIFY notify_listen_test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = l.UnlistenAll()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = expectNotification(t, l.Notify, "notify_listen_test", "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.Exec("NOTIFY notify_listen_test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = expectNoNotification(t, l.Notify)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListenerFailedQuery(t *testing.T) {
|
||||||
|
l, eventch := newTestListener(t)
|
||||||
|
defer l.Close()
|
||||||
|
|
||||||
|
db := openTestConn(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
err := l.Listen("notify_listen_test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.Exec("NOTIFY notify_listen_test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = expectNotification(t, l.Notify, "notify_listen_test", "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// shouldn't cause a disconnect
|
||||||
|
ok, err := l.cn.ExecSimpleQuery("SELECT error")
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("could not send query to server: %v", err)
|
||||||
|
}
|
||||||
|
_, ok = err.(PGError)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("unexpected error %v", err)
|
||||||
|
}
|
||||||
|
err = expectNoEvent(t, eventch)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// should still work
|
||||||
|
_, err = db.Exec("NOTIFY notify_listen_test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = expectNotification(t, l.Notify, "notify_listen_test", "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListenerReconnect(t *testing.T) {
|
||||||
|
l, eventch := newTestListenerTimeout(t, 20*time.Millisecond, time.Hour)
|
||||||
|
defer l.Close()
|
||||||
|
|
||||||
|
db := openTestConn(t)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
err := l.Listen("notify_listen_test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.Exec("NOTIFY notify_listen_test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = expectNotification(t, l.Notify, "notify_listen_test", "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// kill the connection and make sure it comes back up
|
||||||
|
ok, err := l.cn.ExecSimpleQuery("SELECT pg_terminate_backend(pg_backend_pid())")
|
||||||
|
if ok {
|
||||||
|
t.Fatalf("could not kill the connection: %v", err)
|
||||||
|
}
|
||||||
|
if err != io.EOF {
|
||||||
|
t.Fatalf("unexpected error %v", err)
|
||||||
|
}
|
||||||
|
err = expectEvent(t, eventch, ListenerEventDisconnected)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
err = expectEvent(t, eventch, ListenerEventReconnected)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// should still work
|
||||||
|
_, err = db.Exec("NOTIFY notify_listen_test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// should get nil after Reconnected
|
||||||
|
err = expectNotification(t, l.Notify, "", "")
|
||||||
|
if err != errNilNotification {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = expectNotification(t, l.Notify, "notify_listen_test", "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListenerClose(t *testing.T) {
|
||||||
|
l, _ := newTestListenerTimeout(t, 20*time.Millisecond, time.Hour)
|
||||||
|
defer l.Close()
|
||||||
|
|
||||||
|
err := l.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
err = l.Close()
|
||||||
|
if err != errListenerClosed {
|
||||||
|
t.Fatalf("expected errListenerClosed; got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListenerPing(t *testing.T) {
|
||||||
|
l, _ := newTestListenerTimeout(t, 20*time.Millisecond, time.Hour)
|
||||||
|
defer l.Close()
|
||||||
|
|
||||||
|
err := l.Ping()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = l.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = l.Ping()
|
||||||
|
if err != errListenerClosed {
|
||||||
|
t.Fatalf("expected errListenerClosed; got %v", err)
|
||||||
|
}
|
||||||
|
}
|
6
vendor/github.com/lib/pq/oid/doc.go
generated
vendored
Normal file
6
vendor/github.com/lib/pq/oid/doc.go
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
// Package oid contains OID constants
|
||||||
|
// as defined by the Postgres server.
|
||||||
|
package oid
|
||||||
|
|
||||||
|
// Oid is a Postgres Object ID.
|
||||||
|
type Oid uint32
|
74
vendor/github.com/lib/pq/oid/gen.go
generated
vendored
Normal file
74
vendor/github.com/lib/pq/oid/gen.go
generated
vendored
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
// +build ignore
|
||||||
|
|
||||||
|
// Generate the table of OID values
|
||||||
|
// Run with 'go run gen.go'.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
|
||||||
|
_ "github.com/lib/pq"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
datname := os.Getenv("PGDATABASE")
|
||||||
|
sslmode := os.Getenv("PGSSLMODE")
|
||||||
|
|
||||||
|
if datname == "" {
|
||||||
|
os.Setenv("PGDATABASE", "pqgotest")
|
||||||
|
}
|
||||||
|
|
||||||
|
if sslmode == "" {
|
||||||
|
os.Setenv("PGSSLMODE", "disable")
|
||||||
|
}
|
||||||
|
|
||||||
|
db, err := sql.Open("postgres", "")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
cmd := exec.Command("gofmt")
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
w, err := cmd.StdinPipe()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
f, err := os.Create("types.go")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
cmd.Stdout = f
|
||||||
|
err = cmd.Start()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
fmt.Fprintln(w, "// generated by 'go run gen.go'; do not edit")
|
||||||
|
fmt.Fprintln(w, "\npackage oid")
|
||||||
|
fmt.Fprintln(w, "const (")
|
||||||
|
rows, err := db.Query(`
|
||||||
|
SELECT typname, oid
|
||||||
|
FROM pg_type WHERE oid < 10000
|
||||||
|
ORDER BY oid;
|
||||||
|
`)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
var name string
|
||||||
|
var oid int
|
||||||
|
for rows.Next() {
|
||||||
|
err = rows.Scan(&name, &oid)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, "T_%s Oid = %d\n", name, oid)
|
||||||
|
}
|
||||||
|
if err = rows.Err(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
fmt.Fprintln(w, ")")
|
||||||
|
w.Close()
|
||||||
|
cmd.Wait()
|
||||||
|
}
|
161
vendor/github.com/lib/pq/oid/types.go
generated
vendored
Normal file
161
vendor/github.com/lib/pq/oid/types.go
generated
vendored
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
// generated by 'go run gen.go'; do not edit
|
||||||
|
|
||||||
|
package oid
|
||||||
|
|
||||||
|
const (
|
||||||
|
T_bool Oid = 16
|
||||||
|
T_bytea Oid = 17
|
||||||
|
T_char Oid = 18
|
||||||
|
T_name Oid = 19
|
||||||
|
T_int8 Oid = 20
|
||||||
|
T_int2 Oid = 21
|
||||||
|
T_int2vector Oid = 22
|
||||||
|
T_int4 Oid = 23
|
||||||
|
T_regproc Oid = 24
|
||||||
|
T_text Oid = 25
|
||||||
|
T_oid Oid = 26
|
||||||
|
T_tid Oid = 27
|
||||||
|
T_xid Oid = 28
|
||||||
|
T_cid Oid = 29
|
||||||
|
T_oidvector Oid = 30
|
||||||
|
T_pg_type Oid = 71
|
||||||
|
T_pg_attribute Oid = 75
|
||||||
|
T_pg_proc Oid = 81
|
||||||
|
T_pg_class Oid = 83
|
||||||
|
T_json Oid = 114
|
||||||
|
T_xml Oid = 142
|
||||||
|
T__xml Oid = 143
|
||||||
|
T_pg_node_tree Oid = 194
|
||||||
|
T__json Oid = 199
|
||||||
|
T_smgr Oid = 210
|
||||||
|
T_point Oid = 600
|
||||||
|
T_lseg Oid = 601
|
||||||
|
T_path Oid = 602
|
||||||
|
T_box Oid = 603
|
||||||
|
T_polygon Oid = 604
|
||||||
|
T_line Oid = 628
|
||||||
|
T__line Oid = 629
|
||||||
|
T_cidr Oid = 650
|
||||||
|
T__cidr Oid = 651
|
||||||
|
T_float4 Oid = 700
|
||||||
|
T_float8 Oid = 701
|
||||||
|
T_abstime Oid = 702
|
||||||
|
T_reltime Oid = 703
|
||||||
|
T_tinterval Oid = 704
|
||||||
|
T_unknown Oid = 705
|
||||||
|
T_circle Oid = 718
|
||||||
|
T__circle Oid = 719
|
||||||
|
T_money Oid = 790
|
||||||
|
T__money Oid = 791
|
||||||
|
T_macaddr Oid = 829
|
||||||
|
T_inet Oid = 869
|
||||||
|
T__bool Oid = 1000
|
||||||
|
T__bytea Oid = 1001
|
||||||
|
T__char Oid = 1002
|
||||||
|
T__name Oid = 1003
|
||||||
|
T__int2 Oid = 1005
|
||||||
|
T__int2vector Oid = 1006
|
||||||
|
T__int4 Oid = 1007
|
||||||
|
T__regproc Oid = 1008
|
||||||
|
T__text Oid = 1009
|
||||||
|
T__tid Oid = 1010
|
||||||
|
T__xid Oid = 1011
|
||||||
|
T__cid Oid = 1012
|
||||||
|
T__oidvector Oid = 1013
|
||||||
|
T__bpchar Oid = 1014
|
||||||
|
T__varchar Oid = 1015
|
||||||
|
T__int8 Oid = 1016
|
||||||
|
T__point Oid = 1017
|
||||||
|
T__lseg Oid = 1018
|
||||||
|
T__path Oid = 1019
|
||||||
|
T__box Oid = 1020
|
||||||
|
T__float4 Oid = 1021
|
||||||
|
T__float8 Oid = 1022
|
||||||
|
T__abstime Oid = 1023
|
||||||
|
T__reltime Oid = 1024
|
||||||
|
T__tinterval Oid = 1025
|
||||||
|
T__polygon Oid = 1027
|
||||||
|
T__oid Oid = 1028
|
||||||
|
T_aclitem Oid = 1033
|
||||||
|
T__aclitem Oid = 1034
|
||||||
|
T__macaddr Oid = 1040
|
||||||
|
T__inet Oid = 1041
|
||||||
|
T_bpchar Oid = 1042
|
||||||
|
T_varchar Oid = 1043
|
||||||
|
T_date Oid = 1082
|
||||||
|
T_time Oid = 1083
|
||||||
|
T_timestamp Oid = 1114
|
||||||
|
T__timestamp Oid = 1115
|
||||||
|
T__date Oid = 1182
|
||||||
|
T__time Oid = 1183
|
||||||
|
T_timestamptz Oid = 1184
|
||||||
|
T__timestamptz Oid = 1185
|
||||||
|
T_interval Oid = 1186
|
||||||
|
T__interval Oid = 1187
|
||||||
|
T__numeric Oid = 1231
|
||||||
|
T_pg_database Oid = 1248
|
||||||
|
T__cstring Oid = 1263
|
||||||
|
T_timetz Oid = 1266
|
||||||
|
T__timetz Oid = 1270
|
||||||
|
T_bit Oid = 1560
|
||||||
|
T__bit Oid = 1561
|
||||||
|
T_varbit Oid = 1562
|
||||||
|
T__varbit Oid = 1563
|
||||||
|
T_numeric Oid = 1700
|
||||||
|
T_refcursor Oid = 1790
|
||||||
|
T__refcursor Oid = 2201
|
||||||
|
T_regprocedure Oid = 2202
|
||||||
|
T_regoper Oid = 2203
|
||||||
|
T_regoperator Oid = 2204
|
||||||
|
T_regclass Oid = 2205
|
||||||
|
T_regtype Oid = 2206
|
||||||
|
T__regprocedure Oid = 2207
|
||||||
|
T__regoper Oid = 2208
|
||||||
|
T__regoperator Oid = 2209
|
||||||
|
T__regclass Oid = 2210
|
||||||
|
T__regtype Oid = 2211
|
||||||
|
T_record Oid = 2249
|
||||||
|
T_cstring Oid = 2275
|
||||||
|
T_any Oid = 2276
|
||||||
|
T_anyarray Oid = 2277
|
||||||
|
T_void Oid = 2278
|
||||||
|
T_trigger Oid = 2279
|
||||||
|
T_language_handler Oid = 2280
|
||||||
|
T_internal Oid = 2281
|
||||||
|
T_opaque Oid = 2282
|
||||||
|
T_anyelement Oid = 2283
|
||||||
|
T__record Oid = 2287
|
||||||
|
T_anynonarray Oid = 2776
|
||||||
|
T_pg_authid Oid = 2842
|
||||||
|
T_pg_auth_members Oid = 2843
|
||||||
|
T__txid_snapshot Oid = 2949
|
||||||
|
T_uuid Oid = 2950
|
||||||
|
T__uuid Oid = 2951
|
||||||
|
T_txid_snapshot Oid = 2970
|
||||||
|
T_fdw_handler Oid = 3115
|
||||||
|
T_anyenum Oid = 3500
|
||||||
|
T_tsvector Oid = 3614
|
||||||
|
T_tsquery Oid = 3615
|
||||||
|
T_gtsvector Oid = 3642
|
||||||
|
T__tsvector Oid = 3643
|
||||||
|
T__gtsvector Oid = 3644
|
||||||
|
T__tsquery Oid = 3645
|
||||||
|
T_regconfig Oid = 3734
|
||||||
|
T__regconfig Oid = 3735
|
||||||
|
T_regdictionary Oid = 3769
|
||||||
|
T__regdictionary Oid = 3770
|
||||||
|
T_anyrange Oid = 3831
|
||||||
|
T_event_trigger Oid = 3838
|
||||||
|
T_int4range Oid = 3904
|
||||||
|
T__int4range Oid = 3905
|
||||||
|
T_numrange Oid = 3906
|
||||||
|
T__numrange Oid = 3907
|
||||||
|
T_tsrange Oid = 3908
|
||||||
|
T__tsrange Oid = 3909
|
||||||
|
T_tstzrange Oid = 3910
|
||||||
|
T__tstzrange Oid = 3911
|
||||||
|
T_daterange Oid = 3912
|
||||||
|
T__daterange Oid = 3913
|
||||||
|
T_int8range Oid = 3926
|
||||||
|
T__int8range Oid = 3927
|
||||||
|
)
|
269
vendor/github.com/lib/pq/ssl_test.go
generated
vendored
Normal file
269
vendor/github.com/lib/pq/ssl_test.go
generated
vendored
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
package pq
|
||||||
|
|
||||||
|
// This file contains SSL tests
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "crypto/sha256"
|
||||||
|
"crypto/x509"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func maybeSkipSSLTests(t *testing.T) {
|
||||||
|
// Require some special variables for testing certificates
|
||||||
|
if os.Getenv("PQSSLCERTTEST_PATH") == "" {
|
||||||
|
t.Skip("PQSSLCERTTEST_PATH not set, skipping SSL tests")
|
||||||
|
}
|
||||||
|
|
||||||
|
value := os.Getenv("PQGOSSLTESTS")
|
||||||
|
if value == "" || value == "0" {
|
||||||
|
t.Skip("PQGOSSLTESTS not enabled, skipping SSL tests")
|
||||||
|
} else if value != "1" {
|
||||||
|
t.Fatalf("unexpected value %q for PQGOSSLTESTS", value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func openSSLConn(t *testing.T, conninfo string) (*sql.DB, error) {
|
||||||
|
db, err := openTestConnConninfo(conninfo)
|
||||||
|
if err != nil {
|
||||||
|
// should never fail
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// Do something with the connection to see whether it's working or not.
|
||||||
|
tx, err := db.Begin()
|
||||||
|
if err == nil {
|
||||||
|
return db, tx.Rollback()
|
||||||
|
}
|
||||||
|
_ = db.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkSSLSetup(t *testing.T, conninfo string) {
|
||||||
|
db, err := openSSLConn(t, conninfo)
|
||||||
|
if err == nil {
|
||||||
|
db.Close()
|
||||||
|
t.Fatalf("expected error with conninfo=%q", conninfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect over SSL and run a simple query to test the basics
|
||||||
|
func TestSSLConnection(t *testing.T) {
|
||||||
|
maybeSkipSSLTests(t)
|
||||||
|
// Environment sanity check: should fail without SSL
|
||||||
|
checkSSLSetup(t, "sslmode=disable user=pqgossltest")
|
||||||
|
|
||||||
|
db, err := openSSLConn(t, "sslmode=require user=pqgossltest")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
rows, err := db.Query("SELECT 1")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
rows.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test sslmode=verify-full
|
||||||
|
func TestSSLVerifyFull(t *testing.T) {
|
||||||
|
maybeSkipSSLTests(t)
|
||||||
|
// Environment sanity check: should fail without SSL
|
||||||
|
checkSSLSetup(t, "sslmode=disable user=pqgossltest")
|
||||||
|
|
||||||
|
// Not OK according to the system CA
|
||||||
|
_, err := openSSLConn(t, "host=postgres sslmode=verify-full user=pqgossltest")
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error")
|
||||||
|
}
|
||||||
|
_, ok := err.(x509.UnknownAuthorityError)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("expected x509.UnknownAuthorityError, got %#+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "root.crt")
|
||||||
|
rootCert := "sslrootcert=" + rootCertPath + " "
|
||||||
|
// No match on Common Name
|
||||||
|
_, err = openSSLConn(t, rootCert+"host=127.0.0.1 sslmode=verify-full user=pqgossltest")
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error")
|
||||||
|
}
|
||||||
|
_, ok = err.(x509.HostnameError)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("expected x509.HostnameError, got %#+v", err)
|
||||||
|
}
|
||||||
|
// OK
|
||||||
|
_, err = openSSLConn(t, rootCert+"host=postgres sslmode=verify-full user=pqgossltest")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test sslmode=require sslrootcert=rootCertPath
|
||||||
|
func TestSSLRequireWithRootCert(t *testing.T) {
|
||||||
|
maybeSkipSSLTests(t)
|
||||||
|
// Environment sanity check: should fail without SSL
|
||||||
|
checkSSLSetup(t, "sslmode=disable user=pqgossltest")
|
||||||
|
|
||||||
|
bogusRootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "bogus_root.crt")
|
||||||
|
bogusRootCert := "sslrootcert=" + bogusRootCertPath + " "
|
||||||
|
|
||||||
|
// Not OK according to the bogus CA
|
||||||
|
_, err := openSSLConn(t, bogusRootCert+"host=postgres sslmode=require user=pqgossltest")
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error")
|
||||||
|
}
|
||||||
|
_, ok := err.(x509.UnknownAuthorityError)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("expected x509.UnknownAuthorityError, got %s, %#+v", err, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
nonExistentCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "non_existent.crt")
|
||||||
|
nonExistentCert := "sslrootcert=" + nonExistentCertPath + " "
|
||||||
|
|
||||||
|
// No match on Common Name, but that's OK because we're not validating anything.
|
||||||
|
_, err = openSSLConn(t, nonExistentCert+"host=127.0.0.1 sslmode=require user=pqgossltest")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "root.crt")
|
||||||
|
rootCert := "sslrootcert=" + rootCertPath + " "
|
||||||
|
|
||||||
|
// No match on Common Name, but that's OK because we're not validating the CN.
|
||||||
|
_, err = openSSLConn(t, rootCert+"host=127.0.0.1 sslmode=require user=pqgossltest")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// Everything OK
|
||||||
|
_, err = openSSLConn(t, rootCert+"host=postgres sslmode=require user=pqgossltest")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test sslmode=verify-ca
|
||||||
|
func TestSSLVerifyCA(t *testing.T) {
|
||||||
|
maybeSkipSSLTests(t)
|
||||||
|
// Environment sanity check: should fail without SSL
|
||||||
|
checkSSLSetup(t, "sslmode=disable user=pqgossltest")
|
||||||
|
|
||||||
|
// Not OK according to the system CA
|
||||||
|
_, err := openSSLConn(t, "host=postgres sslmode=verify-ca user=pqgossltest")
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error")
|
||||||
|
}
|
||||||
|
_, ok := err.(x509.UnknownAuthorityError)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("expected x509.UnknownAuthorityError, got %#+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "root.crt")
|
||||||
|
rootCert := "sslrootcert=" + rootCertPath + " "
|
||||||
|
// No match on Common Name, but that's OK
|
||||||
|
_, err = openSSLConn(t, rootCert+"host=127.0.0.1 sslmode=verify-ca user=pqgossltest")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// Everything OK
|
||||||
|
_, err = openSSLConn(t, rootCert+"host=postgres sslmode=verify-ca user=pqgossltest")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCertConninfo(t *testing.T, source string) string {
|
||||||
|
var sslkey string
|
||||||
|
var sslcert string
|
||||||
|
|
||||||
|
certpath := os.Getenv("PQSSLCERTTEST_PATH")
|
||||||
|
|
||||||
|
switch source {
|
||||||
|
case "missingkey":
|
||||||
|
sslkey = "/tmp/filedoesnotexist"
|
||||||
|
sslcert = filepath.Join(certpath, "postgresql.crt")
|
||||||
|
case "missingcert":
|
||||||
|
sslkey = filepath.Join(certpath, "postgresql.key")
|
||||||
|
sslcert = "/tmp/filedoesnotexist"
|
||||||
|
case "certtwice":
|
||||||
|
sslkey = filepath.Join(certpath, "postgresql.crt")
|
||||||
|
sslcert = filepath.Join(certpath, "postgresql.crt")
|
||||||
|
case "valid":
|
||||||
|
sslkey = filepath.Join(certpath, "postgresql.key")
|
||||||
|
sslcert = filepath.Join(certpath, "postgresql.crt")
|
||||||
|
default:
|
||||||
|
t.Fatalf("invalid source %q", source)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("sslmode=require user=pqgosslcert sslkey=%s sslcert=%s", sslkey, sslcert)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Authenticate over SSL using client certificates
|
||||||
|
func TestSSLClientCertificates(t *testing.T) {
|
||||||
|
maybeSkipSSLTests(t)
|
||||||
|
// Environment sanity check: should fail without SSL
|
||||||
|
checkSSLSetup(t, "sslmode=disable user=pqgossltest")
|
||||||
|
|
||||||
|
// Should also fail without a valid certificate
|
||||||
|
db, err := openSSLConn(t, "sslmode=require user=pqgosslcert")
|
||||||
|
if err == nil {
|
||||||
|
db.Close()
|
||||||
|
t.Fatal("expected error")
|
||||||
|
}
|
||||||
|
pge, ok := err.(*Error)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("expected pq.Error")
|
||||||
|
}
|
||||||
|
if pge.Code.Name() != "invalid_authorization_specification" {
|
||||||
|
t.Fatalf("unexpected error code %q", pge.Code.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should work
|
||||||
|
db, err = openSSLConn(t, getCertConninfo(t, "valid"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
rows, err := db.Query("SELECT 1")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
rows.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test errors with ssl certificates
|
||||||
|
func TestSSLClientCertificatesMissingFiles(t *testing.T) {
|
||||||
|
maybeSkipSSLTests(t)
|
||||||
|
// Environment sanity check: should fail without SSL
|
||||||
|
checkSSLSetup(t, "sslmode=disable user=pqgossltest")
|
||||||
|
|
||||||
|
// Key missing, should fail
|
||||||
|
_, err := openSSLConn(t, getCertConninfo(t, "missingkey"))
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error")
|
||||||
|
}
|
||||||
|
// should be a PathError
|
||||||
|
_, ok := err.(*os.PathError)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("expected PathError, got %#+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cert missing, should fail
|
||||||
|
_, err = openSSLConn(t, getCertConninfo(t, "missingcert"))
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error")
|
||||||
|
}
|
||||||
|
// should be a PathError
|
||||||
|
_, ok = err.(*os.PathError)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("expected PathError, got %#+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key has wrong permissions, should fail
|
||||||
|
_, err = openSSLConn(t, getCertConninfo(t, "certtwice"))
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error")
|
||||||
|
}
|
||||||
|
if err != ErrSSLKeyHasWorldPermissions {
|
||||||
|
t.Fatalf("expected ErrSSLKeyHasWorldPermissions, got %#+v", err)
|
||||||
|
}
|
||||||
|
}
|
76
vendor/github.com/lib/pq/url.go
generated
vendored
Normal file
76
vendor/github.com/lib/pq/url.go
generated
vendored
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
package pq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
nurl "net/url"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ParseURL no longer needs to be used by clients of this library since supplying a URL as a
|
||||||
|
// connection string to sql.Open() is now supported:
|
||||||
|
//
|
||||||
|
// sql.Open("postgres", "postgres://bob:secret@1.2.3.4:5432/mydb?sslmode=verify-full")
|
||||||
|
//
|
||||||
|
// It remains exported here for backwards-compatibility.
|
||||||
|
//
|
||||||
|
// ParseURL converts a url to a connection string for driver.Open.
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// "postgres://bob:secret@1.2.3.4:5432/mydb?sslmode=verify-full"
|
||||||
|
//
|
||||||
|
// converts to:
|
||||||
|
//
|
||||||
|
// "user=bob password=secret host=1.2.3.4 port=5432 dbname=mydb sslmode=verify-full"
|
||||||
|
//
|
||||||
|
// A minimal example:
|
||||||
|
//
|
||||||
|
// "postgres://"
|
||||||
|
//
|
||||||
|
// This will be blank, causing driver.Open to use all of the defaults
|
||||||
|
func ParseURL(url string) (string, error) {
|
||||||
|
u, err := nurl.Parse(url)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.Scheme != "postgres" && u.Scheme != "postgresql" {
|
||||||
|
return "", fmt.Errorf("invalid connection protocol: %s", u.Scheme)
|
||||||
|
}
|
||||||
|
|
||||||
|
var kvs []string
|
||||||
|
escaper := strings.NewReplacer(` `, `\ `, `'`, `\'`, `\`, `\\`)
|
||||||
|
accrue := func(k, v string) {
|
||||||
|
if v != "" {
|
||||||
|
kvs = append(kvs, k+"="+escaper.Replace(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.User != nil {
|
||||||
|
v := u.User.Username()
|
||||||
|
accrue("user", v)
|
||||||
|
|
||||||
|
v, _ = u.User.Password()
|
||||||
|
accrue("password", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if host, port, err := net.SplitHostPort(u.Host); err != nil {
|
||||||
|
accrue("host", u.Host)
|
||||||
|
} else {
|
||||||
|
accrue("host", host)
|
||||||
|
accrue("port", port)
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.Path != "" {
|
||||||
|
accrue("dbname", u.Path[1:])
|
||||||
|
}
|
||||||
|
|
||||||
|
q := u.Query()
|
||||||
|
for k := range q {
|
||||||
|
accrue(k, q.Get(k))
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(kvs) // Makes testing easier (not a performance concern)
|
||||||
|
return strings.Join(kvs, " "), nil
|
||||||
|
}
|
66
vendor/github.com/lib/pq/url_test.go
generated
vendored
Normal file
66
vendor/github.com/lib/pq/url_test.go
generated
vendored
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
package pq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSimpleParseURL(t *testing.T) {
|
||||||
|
expected := "host=hostname.remote"
|
||||||
|
str, err := ParseURL("postgres://hostname.remote")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if str != expected {
|
||||||
|
t.Fatalf("unexpected result from ParseURL:\n+ %v\n- %v", str, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIPv6LoopbackParseURL(t *testing.T) {
|
||||||
|
expected := "host=::1 port=1234"
|
||||||
|
str, err := ParseURL("postgres://[::1]:1234")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if str != expected {
|
||||||
|
t.Fatalf("unexpected result from ParseURL:\n+ %v\n- %v", str, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFullParseURL(t *testing.T) {
|
||||||
|
expected := `dbname=database host=hostname.remote password=top\ secret port=1234 user=username`
|
||||||
|
str, err := ParseURL("postgres://username:top%20secret@hostname.remote:1234/database")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if str != expected {
|
||||||
|
t.Fatalf("unexpected result from ParseURL:\n+ %s\n- %s", str, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInvalidProtocolParseURL(t *testing.T) {
|
||||||
|
_, err := ParseURL("http://hostname.remote")
|
||||||
|
switch err {
|
||||||
|
case nil:
|
||||||
|
t.Fatal("Expected an error from parsing invalid protocol")
|
||||||
|
default:
|
||||||
|
msg := "invalid connection protocol: http"
|
||||||
|
if err.Error() != msg {
|
||||||
|
t.Fatalf("Unexpected error message:\n+ %s\n- %s",
|
||||||
|
err.Error(), msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMinimalURL(t *testing.T) {
|
||||||
|
cs, err := ParseURL("postgres://")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cs != "" {
|
||||||
|
t.Fatalf("expected blank connection string, got: %q", cs)
|
||||||
|
}
|
||||||
|
}
|
24
vendor/github.com/lib/pq/user_posix.go
generated
vendored
Normal file
24
vendor/github.com/lib/pq/user_posix.go
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// Package pq is a pure Go Postgres driver for the database/sql package.
|
||||||
|
|
||||||
|
// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris rumprun
|
||||||
|
|
||||||
|
package pq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/user"
|
||||||
|
)
|
||||||
|
|
||||||
|
func userCurrent() (string, error) {
|
||||||
|
u, err := user.Current()
|
||||||
|
if err == nil {
|
||||||
|
return u.Username, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
name := os.Getenv("USER")
|
||||||
|
if name != "" {
|
||||||
|
return name, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", ErrCouldNotDetectUsername
|
||||||
|
}
|
27
vendor/github.com/lib/pq/user_windows.go
generated
vendored
Normal file
27
vendor/github.com/lib/pq/user_windows.go
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// Package pq is a pure Go Postgres driver for the database/sql package.
|
||||||
|
package pq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Perform Windows user name lookup identically to libpq.
|
||||||
|
//
|
||||||
|
// The PostgreSQL code makes use of the legacy Win32 function
|
||||||
|
// GetUserName, and that function has not been imported into stock Go.
|
||||||
|
// GetUserNameEx is available though, the difference being that a
|
||||||
|
// wider range of names are available. To get the output to be the
|
||||||
|
// same as GetUserName, only the base (or last) component of the
|
||||||
|
// result is returned.
|
||||||
|
func userCurrent() (string, error) {
|
||||||
|
pw_name := make([]uint16, 128)
|
||||||
|
pwname_size := uint32(len(pw_name)) - 1
|
||||||
|
err := syscall.GetUserNameEx(syscall.NameSamCompatible, &pw_name[0], &pwname_size)
|
||||||
|
if err != nil {
|
||||||
|
return "", ErrCouldNotDetectUsername
|
||||||
|
}
|
||||||
|
s := syscall.UTF16ToString(pw_name)
|
||||||
|
u := filepath.Base(s)
|
||||||
|
return u, nil
|
||||||
|
}
|
3
vendor/github.com/mattn/go-sqlite3/.gitignore
generated
vendored
3
vendor/github.com/mattn/go-sqlite3/.gitignore
generated
vendored
@ -1,3 +0,0 @@
|
|||||||
*.db
|
|
||||||
*.exe
|
|
||||||
*.dll
|
|
13
vendor/github.com/mattn/go-sqlite3/.travis.yml
generated
vendored
13
vendor/github.com/mattn/go-sqlite3/.travis.yml
generated
vendored
@ -1,13 +0,0 @@
|
|||||||
language: go
|
|
||||||
sudo: required
|
|
||||||
dist: trusty
|
|
||||||
go:
|
|
||||||
- 1.5
|
|
||||||
- 1.6
|
|
||||||
- tip
|
|
||||||
before_install:
|
|
||||||
- go get github.com/mattn/goveralls
|
|
||||||
- go get golang.org/x/tools/cmd/cover
|
|
||||||
script:
|
|
||||||
- $HOME/gopath/bin/goveralls -repotoken 3qJVUE0iQwqnCbmNcDsjYu1nh4J4KIFXx
|
|
||||||
- go test -v . -tags "libsqlite3"
|
|
21
vendor/github.com/mattn/go-sqlite3/LICENSE
generated
vendored
21
vendor/github.com/mattn/go-sqlite3/LICENSE
generated
vendored
@ -1,21 +0,0 @@
|
|||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2014 Yasuhiro Matsumoto
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
81
vendor/github.com/mattn/go-sqlite3/README.md
generated
vendored
81
vendor/github.com/mattn/go-sqlite3/README.md
generated
vendored
@ -1,81 +0,0 @@
|
|||||||
go-sqlite3
|
|
||||||
==========
|
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.org/mattn/go-sqlite3.svg?branch=master)](https://travis-ci.org/mattn/go-sqlite3)
|
|
||||||
[![Coverage Status](https://coveralls.io/repos/mattn/go-sqlite3/badge.svg?branch=master)](https://coveralls.io/r/mattn/go-sqlite3?branch=master)
|
|
||||||
[![GoDoc](https://godoc.org/github.com/mattn/go-sqlite3?status.svg)](http://godoc.org/github.com/mattn/go-sqlite3)
|
|
||||||
|
|
||||||
Description
|
|
||||||
-----------
|
|
||||||
|
|
||||||
sqlite3 driver conforming to the built-in database/sql interface
|
|
||||||
|
|
||||||
Installation
|
|
||||||
------------
|
|
||||||
|
|
||||||
This package can be installed with the go get command:
|
|
||||||
|
|
||||||
go get github.com/mattn/go-sqlite3
|
|
||||||
|
|
||||||
_go-sqlite3_ is *cgo* package.
|
|
||||||
If you want to build your app using go-sqlite3, you need gcc.
|
|
||||||
However, if you install _go-sqlite3_ with `go install github.com/mattn/go-sqlite3`, you don't need gcc to build your app anymore.
|
|
||||||
|
|
||||||
Documentation
|
|
||||||
-------------
|
|
||||||
|
|
||||||
API documentation can be found here: http://godoc.org/github.com/mattn/go-sqlite3
|
|
||||||
|
|
||||||
Examples can be found under the `./_example` directory
|
|
||||||
|
|
||||||
FAQ
|
|
||||||
---
|
|
||||||
|
|
||||||
* Want to build go-sqlite3 with libsqlite3 on my linux.
|
|
||||||
|
|
||||||
Use `go build --tags "libsqlite3 linux"`
|
|
||||||
|
|
||||||
* Want to build go-sqlite3 with libsqlite3 on OS X.
|
|
||||||
|
|
||||||
Install sqlite3 from homebrew: `brew install sqlite3`
|
|
||||||
|
|
||||||
Use `go build --tags "libsqlite3 darwin"`
|
|
||||||
|
|
||||||
* Want to build go-sqlite3 with icu extension.
|
|
||||||
|
|
||||||
Use `go build --tags "icu"`
|
|
||||||
|
|
||||||
* Can't build go-sqlite3 on windows 64bit.
|
|
||||||
|
|
||||||
> Probably, you are using go 1.0, go1.0 has a problem when it comes to compiling/linking on windows 64bit.
|
|
||||||
> See: https://github.com/mattn/go-sqlite3/issues/27
|
|
||||||
|
|
||||||
* Getting insert error while query is opened.
|
|
||||||
|
|
||||||
> You can pass some arguments into the connection string, for example, a URI.
|
|
||||||
> See: https://github.com/mattn/go-sqlite3/issues/39
|
|
||||||
|
|
||||||
* Do you want to cross compile? mingw on Linux or Mac?
|
|
||||||
|
|
||||||
> See: https://github.com/mattn/go-sqlite3/issues/106
|
|
||||||
> See also: http://www.limitlessfx.com/cross-compile-golang-app-for-windows-from-linux.html
|
|
||||||
|
|
||||||
* Want to get time.Time with current locale
|
|
||||||
|
|
||||||
Use `loc=auto` in SQLite3 filename schema like `file:foo.db?loc=auto`.
|
|
||||||
|
|
||||||
License
|
|
||||||
-------
|
|
||||||
|
|
||||||
MIT: http://mattn.mit-license.org/2012
|
|
||||||
|
|
||||||
sqlite3-binding.c, sqlite3-binding.h, sqlite3ext.h
|
|
||||||
|
|
||||||
The -binding suffix was added to avoid build failures under gccgo.
|
|
||||||
|
|
||||||
In this repository, those files are an amalgamation of code that was copied from SQLite3. The license of that code is the same as the license of SQLite3.
|
|
||||||
|
|
||||||
Author
|
|
||||||
------
|
|
||||||
|
|
||||||
Yasuhiro Matsumoto (a.k.a mattn)
|
|
133
vendor/github.com/mattn/go-sqlite3/_example/custom_func/main.go
generated
vendored
133
vendor/github.com/mattn/go-sqlite3/_example/custom_func/main.go
generated
vendored
@ -1,133 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"math"
|
|
||||||
"math/rand"
|
|
||||||
|
|
||||||
sqlite "github.com/mattn/go-sqlite3"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Computes x^y
|
|
||||||
func pow(x, y int64) int64 {
|
|
||||||
return int64(math.Pow(float64(x), float64(y)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Computes the bitwise exclusive-or of all its arguments
|
|
||||||
func xor(xs ...int64) int64 {
|
|
||||||
var ret int64
|
|
||||||
for _, x := range xs {
|
|
||||||
ret ^= x
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns a random number. It's actually deterministic here because
|
|
||||||
// we don't seed the RNG, but it's an example of a non-pure function
|
|
||||||
// from SQLite's POV.
|
|
||||||
func getrand() int64 {
|
|
||||||
return rand.Int63()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Computes the standard deviation of a GROUPed BY set of values
|
|
||||||
type stddev struct {
|
|
||||||
xs []int64
|
|
||||||
// Running average calculation
|
|
||||||
sum int64
|
|
||||||
n int64
|
|
||||||
}
|
|
||||||
|
|
||||||
func newStddev() *stddev { return &stddev{} }
|
|
||||||
|
|
||||||
func (s *stddev) Step(x int64) {
|
|
||||||
s.xs = append(s.xs, x)
|
|
||||||
s.sum += x
|
|
||||||
s.n++
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stddev) Done() float64 {
|
|
||||||
mean := float64(s.sum) / float64(s.n)
|
|
||||||
var sqDiff []float64
|
|
||||||
for _, x := range s.xs {
|
|
||||||
sqDiff = append(sqDiff, math.Pow(float64(x)-mean, 2))
|
|
||||||
}
|
|
||||||
var dev float64
|
|
||||||
for _, x := range sqDiff {
|
|
||||||
dev += x
|
|
||||||
}
|
|
||||||
dev /= float64(len(sqDiff))
|
|
||||||
return math.Sqrt(dev)
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
sql.Register("sqlite3_custom", &sqlite.SQLiteDriver{
|
|
||||||
ConnectHook: func(conn *sqlite.SQLiteConn) error {
|
|
||||||
if err := conn.RegisterFunc("pow", pow, true); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := conn.RegisterFunc("xor", xor, true); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := conn.RegisterFunc("rand", getrand, false); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := conn.RegisterAggregator("stddev", newStddev, true); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
db, err := sql.Open("sqlite3_custom", ":memory:")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Failed to open database:", err)
|
|
||||||
}
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
var i int64
|
|
||||||
err = db.QueryRow("SELECT pow(2,3)").Scan(&i)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("POW query error:", err)
|
|
||||||
}
|
|
||||||
fmt.Println("pow(2,3) =", i) // 8
|
|
||||||
|
|
||||||
err = db.QueryRow("SELECT xor(1,2,3,4,5,6)").Scan(&i)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("XOR query error:", err)
|
|
||||||
}
|
|
||||||
fmt.Println("xor(1,2,3,4,5) =", i) // 7
|
|
||||||
|
|
||||||
err = db.QueryRow("SELECT rand()").Scan(&i)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("RAND query error:", err)
|
|
||||||
}
|
|
||||||
fmt.Println("rand() =", i) // pseudorandom
|
|
||||||
|
|
||||||
_, err = db.Exec("create table foo (department integer, profits integer)")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Failed to create table:", err)
|
|
||||||
}
|
|
||||||
_, err = db.Exec("insert into foo values (1, 10), (1, 20), (1, 45), (2, 42), (2, 115)")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Failed to insert records:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
rows, err := db.Query("select department, stddev(profits) from foo group by department")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("STDDEV query error:", err)
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
for rows.Next() {
|
|
||||||
var dept int64
|
|
||||||
var dev float64
|
|
||||||
if err := rows.Scan(&dept, &dev); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
fmt.Printf("dept=%d stddev=%f\n", dept, dev)
|
|
||||||
}
|
|
||||||
if err := rows.Err(); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
71
vendor/github.com/mattn/go-sqlite3/_example/hook/hook.go
generated
vendored
71
vendor/github.com/mattn/go-sqlite3/_example/hook/hook.go
generated
vendored
@ -1,71 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"github.com/mattn/go-sqlite3"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
sqlite3conn := []*sqlite3.SQLiteConn{}
|
|
||||||
sql.Register("sqlite3_with_hook_example",
|
|
||||||
&sqlite3.SQLiteDriver{
|
|
||||||
ConnectHook: func(conn *sqlite3.SQLiteConn) error {
|
|
||||||
sqlite3conn = append(sqlite3conn, conn)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
})
|
|
||||||
os.Remove("./foo.db")
|
|
||||||
os.Remove("./bar.db")
|
|
||||||
|
|
||||||
destDb, err := sql.Open("sqlite3_with_hook_example", "./foo.db")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer destDb.Close()
|
|
||||||
destDb.Ping()
|
|
||||||
|
|
||||||
_, err = destDb.Exec("create table foo(id int, value text)")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
_, err = destDb.Exec("insert into foo values(1, 'foo')")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
_, err = destDb.Exec("insert into foo values(2, 'bar')")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
_, err = destDb.Query("select * from foo")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
srcDb, err := sql.Open("sqlite3_with_hook_example", "./bar.db")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer srcDb.Close()
|
|
||||||
srcDb.Ping()
|
|
||||||
|
|
||||||
bk, err := sqlite3conn[1].Backup("main", sqlite3conn[0], "main")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = bk.Step(-1)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
_, err = destDb.Query("select * from foo")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
_, err = destDb.Exec("insert into foo values(3, 'bar')")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
bk.Finish()
|
|
||||||
}
|
|
22
vendor/github.com/mattn/go-sqlite3/_example/mod_regexp/Makefile
generated
vendored
22
vendor/github.com/mattn/go-sqlite3/_example/mod_regexp/Makefile
generated
vendored
@ -1,22 +0,0 @@
|
|||||||
ifeq ($(OS),Windows_NT)
|
|
||||||
EXE=extension.exe
|
|
||||||
EXT=sqlite3_mod_regexp.dll
|
|
||||||
RM=cmd /c del
|
|
||||||
LDFLAG=
|
|
||||||
else
|
|
||||||
EXE=extension
|
|
||||||
EXT=sqlite3_mod_regexp.so
|
|
||||||
RM=rm
|
|
||||||
LDFLAG=-fPIC
|
|
||||||
endif
|
|
||||||
|
|
||||||
all : $(EXE) $(EXT)
|
|
||||||
|
|
||||||
$(EXE) : extension.go
|
|
||||||
go build $<
|
|
||||||
|
|
||||||
$(EXT) : sqlite3_mod_regexp.c
|
|
||||||
gcc $(LDFLAG) -shared -o $@ $< -lsqlite3 -lpcre
|
|
||||||
|
|
||||||
clean :
|
|
||||||
@-$(RM) $(EXE) $(EXT)
|
|
43
vendor/github.com/mattn/go-sqlite3/_example/mod_regexp/extension.go
generated
vendored
43
vendor/github.com/mattn/go-sqlite3/_example/mod_regexp/extension.go
generated
vendored
@ -1,43 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"fmt"
|
|
||||||
"github.com/mattn/go-sqlite3"
|
|
||||||
"log"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
sql.Register("sqlite3_with_extensions",
|
|
||||||
&sqlite3.SQLiteDriver{
|
|
||||||
Extensions: []string{
|
|
||||||
"sqlite3_mod_regexp",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
db, err := sql.Open("sqlite3_with_extensions", ":memory:")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
// Force db to make a new connection in pool
|
|
||||||
// by putting the original in a transaction
|
|
||||||
tx, err := db.Begin()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer tx.Commit()
|
|
||||||
|
|
||||||
// New connection works (hopefully!)
|
|
||||||
rows, err := db.Query("select 'hello world' where 'hello world' regexp '^hello.*d$'")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
for rows.Next() {
|
|
||||||
var helloworld string
|
|
||||||
rows.Scan(&helloworld)
|
|
||||||
fmt.Println(helloworld)
|
|
||||||
}
|
|
||||||
}
|
|
31
vendor/github.com/mattn/go-sqlite3/_example/mod_regexp/sqlite3_mod_regexp.c
generated
vendored
31
vendor/github.com/mattn/go-sqlite3/_example/mod_regexp/sqlite3_mod_regexp.c
generated
vendored
@ -1,31 +0,0 @@
|
|||||||
#include <pcre.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <sqlite3ext.h>
|
|
||||||
|
|
||||||
SQLITE_EXTENSION_INIT1
|
|
||||||
static void regexp_func(sqlite3_context *context, int argc, sqlite3_value **argv) {
|
|
||||||
if (argc >= 2) {
|
|
||||||
const char *target = (const char *)sqlite3_value_text(argv[1]);
|
|
||||||
const char *pattern = (const char *)sqlite3_value_text(argv[0]);
|
|
||||||
const char* errstr = NULL;
|
|
||||||
int erroff = 0;
|
|
||||||
int vec[500];
|
|
||||||
int n, rc;
|
|
||||||
pcre* re = pcre_compile(pattern, 0, &errstr, &erroff, NULL);
|
|
||||||
rc = pcre_exec(re, NULL, target, strlen(target), 0, 0, vec, 500);
|
|
||||||
if (rc <= 0) {
|
|
||||||
sqlite3_result_error(context, errstr, 0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
sqlite3_result_int(context, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
__declspec(dllexport)
|
|
||||||
#endif
|
|
||||||
int sqlite3_extension_init(sqlite3 *db, char **errmsg, const sqlite3_api_routines *api) {
|
|
||||||
SQLITE_EXTENSION_INIT2(api);
|
|
||||||
return sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8, (void*)db, regexp_func, NULL, NULL);
|
|
||||||
}
|
|
24
vendor/github.com/mattn/go-sqlite3/_example/mod_vtable/Makefile
generated
vendored
24
vendor/github.com/mattn/go-sqlite3/_example/mod_vtable/Makefile
generated
vendored
@ -1,24 +0,0 @@
|
|||||||
ifeq ($(OS),Windows_NT)
|
|
||||||
EXE=extension.exe
|
|
||||||
EXT=sqlite3_mod_vtable.dll
|
|
||||||
RM=cmd /c del
|
|
||||||
LIBCURL=-lcurldll
|
|
||||||
LDFLAG=
|
|
||||||
else
|
|
||||||
EXE=extension
|
|
||||||
EXT=sqlite3_mod_vtable.so
|
|
||||||
RM=rm
|
|
||||||
LDFLAG=-fPIC
|
|
||||||
LIBCURL=-lcurl
|
|
||||||
endif
|
|
||||||
|
|
||||||
all : $(EXE) $(EXT)
|
|
||||||
|
|
||||||
$(EXE) : extension.go
|
|
||||||
go build $<
|
|
||||||
|
|
||||||
$(EXT) : sqlite3_mod_vtable.cc
|
|
||||||
g++ $(LDFLAG) -shared -o $@ $< -lsqlite3 $(LIBCURL)
|
|
||||||
|
|
||||||
clean :
|
|
||||||
@-$(RM) $(EXE) $(EXT)
|
|
36
vendor/github.com/mattn/go-sqlite3/_example/mod_vtable/extension.go
generated
vendored
36
vendor/github.com/mattn/go-sqlite3/_example/mod_vtable/extension.go
generated
vendored
@ -1,36 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"fmt"
|
|
||||||
"github.com/mattn/go-sqlite3"
|
|
||||||
"log"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
sql.Register("sqlite3_with_extensions",
|
|
||||||
&sqlite3.SQLiteDriver{
|
|
||||||
Extensions: []string{
|
|
||||||
"sqlite3_mod_vtable",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
db, err := sql.Open("sqlite3_with_extensions", ":memory:")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
db.Exec("create virtual table repo using github(id, full_name, description, html_url)")
|
|
||||||
|
|
||||||
rows, err := db.Query("select id, full_name, description, html_url from repo")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
for rows.Next() {
|
|
||||||
var id, full_name, description, html_url string
|
|
||||||
rows.Scan(&id, &full_name, &description, &html_url)
|
|
||||||
fmt.Printf("%s: %s\n\t%s\n\t%s\n\n", id, full_name, description, html_url)
|
|
||||||
}
|
|
||||||
}
|
|
1040
vendor/github.com/mattn/go-sqlite3/_example/mod_vtable/picojson.h
generated
vendored
1040
vendor/github.com/mattn/go-sqlite3/_example/mod_vtable/picojson.h
generated
vendored
File diff suppressed because it is too large
Load Diff
238
vendor/github.com/mattn/go-sqlite3/_example/mod_vtable/sqlite3_mod_vtable.cc
generated
vendored
238
vendor/github.com/mattn/go-sqlite3/_example/mod_vtable/sqlite3_mod_vtable.cc
generated
vendored
@ -1,238 +0,0 @@
|
|||||||
#include <string>
|
|
||||||
#include <sstream>
|
|
||||||
#include <sqlite3-binding.h>
|
|
||||||
#include <sqlite3ext.h>
|
|
||||||
#include <curl/curl.h>
|
|
||||||
#include "picojson.h"
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
# define EXPORT __declspec(dllexport)
|
|
||||||
#else
|
|
||||||
# define EXPORT
|
|
||||||
#endif
|
|
||||||
|
|
||||||
SQLITE_EXTENSION_INIT1;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
char* data; // response data from server
|
|
||||||
size_t size; // response size of data
|
|
||||||
} MEMFILE;
|
|
||||||
|
|
||||||
MEMFILE*
|
|
||||||
memfopen() {
|
|
||||||
MEMFILE* mf = (MEMFILE*) malloc(sizeof(MEMFILE));
|
|
||||||
if (mf) {
|
|
||||||
mf->data = NULL;
|
|
||||||
mf->size = 0;
|
|
||||||
}
|
|
||||||
return mf;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
memfclose(MEMFILE* mf) {
|
|
||||||
if (mf->data) free(mf->data);
|
|
||||||
free(mf);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t
|
|
||||||
memfwrite(char* ptr, size_t size, size_t nmemb, void* stream) {
|
|
||||||
MEMFILE* mf = (MEMFILE*) stream;
|
|
||||||
int block = size * nmemb;
|
|
||||||
if (!mf) return block; // through
|
|
||||||
if (!mf->data)
|
|
||||||
mf->data = (char*) malloc(block);
|
|
||||||
else
|
|
||||||
mf->data = (char*) realloc(mf->data, mf->size + block);
|
|
||||||
if (mf->data) {
|
|
||||||
memcpy(mf->data + mf->size, ptr, block);
|
|
||||||
mf->size += block;
|
|
||||||
}
|
|
||||||
return block;
|
|
||||||
}
|
|
||||||
|
|
||||||
char*
|
|
||||||
memfstrdup(MEMFILE* mf) {
|
|
||||||
char* buf;
|
|
||||||
if (mf->size == 0) return NULL;
|
|
||||||
buf = (char*) malloc(mf->size + 1);
|
|
||||||
memcpy(buf, mf->data, mf->size);
|
|
||||||
buf[mf->size] = 0;
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
my_connect(sqlite3 *db, void *pAux, int argc, const char * const *argv, sqlite3_vtab **ppVTab, char **c) {
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << "CREATE TABLE " << argv[0]
|
|
||||||
<< "(id int, full_name text, description text, html_url text)";
|
|
||||||
int rc = sqlite3_declare_vtab(db, ss.str().c_str());
|
|
||||||
*ppVTab = (sqlite3_vtab *) sqlite3_malloc(sizeof(sqlite3_vtab));
|
|
||||||
memset(*ppVTab, 0, sizeof(sqlite3_vtab));
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
my_create(sqlite3 *db, void *pAux, int argc, const char * const * argv, sqlite3_vtab **ppVTab, char **c) {
|
|
||||||
return my_connect(db, pAux, argc, argv, ppVTab, c);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int my_disconnect(sqlite3_vtab *pVTab) {
|
|
||||||
sqlite3_free(pVTab);
|
|
||||||
return SQLITE_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
my_destroy(sqlite3_vtab *pVTab) {
|
|
||||||
sqlite3_free(pVTab);
|
|
||||||
return SQLITE_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
sqlite3_vtab_cursor base;
|
|
||||||
int index;
|
|
||||||
picojson::value* rows;
|
|
||||||
} cursor;
|
|
||||||
|
|
||||||
static int
|
|
||||||
my_open(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor) {
|
|
||||||
MEMFILE* mf;
|
|
||||||
CURL* curl;
|
|
||||||
char* json;
|
|
||||||
CURLcode res = CURLE_OK;
|
|
||||||
char error[CURL_ERROR_SIZE] = {0};
|
|
||||||
char* cert_file = getenv("SSL_CERT_FILE");
|
|
||||||
|
|
||||||
mf = memfopen();
|
|
||||||
curl = curl_easy_init();
|
|
||||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, "curl/7.29.0");
|
|
||||||
curl_easy_setopt(curl, CURLOPT_URL, "https://api.github.com/repositories");
|
|
||||||
if (cert_file)
|
|
||||||
curl_easy_setopt(curl, CURLOPT_CAINFO, cert_file);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, mf);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, memfwrite);
|
|
||||||
res = curl_easy_perform(curl);
|
|
||||||
curl_easy_cleanup(curl);
|
|
||||||
if (res != CURLE_OK) {
|
|
||||||
std::cerr << error << std::endl;
|
|
||||||
return SQLITE_FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
picojson::value* v = new picojson::value;
|
|
||||||
std::string err;
|
|
||||||
picojson::parse(*v, mf->data, mf->data + mf->size, &err);
|
|
||||||
memfclose(mf);
|
|
||||||
|
|
||||||
if (!err.empty()) {
|
|
||||||
delete v;
|
|
||||||
std::cerr << err << std::endl;
|
|
||||||
return SQLITE_FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
cursor *c = (cursor *)sqlite3_malloc(sizeof(cursor));
|
|
||||||
c->rows = v;
|
|
||||||
c->index = 0;
|
|
||||||
*ppCursor = &c->base;
|
|
||||||
return SQLITE_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
my_close(cursor *c) {
|
|
||||||
delete c->rows;
|
|
||||||
sqlite3_free(c);
|
|
||||||
return SQLITE_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
my_filter(cursor *c, int idxNum, const char *idxStr, int argc, sqlite3_value **argv) {
|
|
||||||
c->index = 0;
|
|
||||||
return SQLITE_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
my_next(cursor *c) {
|
|
||||||
c->index++;
|
|
||||||
return SQLITE_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
my_eof(cursor *c) {
|
|
||||||
return c->index >= c->rows->get<picojson::array>().size() ? 1 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
my_column(cursor *c, sqlite3_context *ctxt, int i) {
|
|
||||||
picojson::value v = c->rows->get<picojson::array>()[c->index];
|
|
||||||
picojson::object row = v.get<picojson::object>();
|
|
||||||
const char* p = NULL;
|
|
||||||
switch (i) {
|
|
||||||
case 0:
|
|
||||||
p = row["id"].to_str().c_str();
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
p = row["full_name"].to_str().c_str();
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
p = row["description"].to_str().c_str();
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
p = row["html_url"].to_str().c_str();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
sqlite3_result_text(ctxt, strdup(p), strlen(p), free);
|
|
||||||
return SQLITE_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
my_rowid(cursor *c, sqlite3_int64 *pRowid) {
|
|
||||||
*pRowid = c->index;
|
|
||||||
return SQLITE_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
my_bestindex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo) {
|
|
||||||
return SQLITE_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const sqlite3_module module = {
|
|
||||||
0,
|
|
||||||
my_create,
|
|
||||||
my_connect,
|
|
||||||
my_bestindex,
|
|
||||||
my_disconnect,
|
|
||||||
my_destroy,
|
|
||||||
my_open,
|
|
||||||
(int (*)(sqlite3_vtab_cursor *)) my_close,
|
|
||||||
(int (*)(sqlite3_vtab_cursor *, int, char const *, int, sqlite3_value **)) my_filter,
|
|
||||||
(int (*)(sqlite3_vtab_cursor *)) my_next,
|
|
||||||
(int (*)(sqlite3_vtab_cursor *)) my_eof,
|
|
||||||
(int (*)(sqlite3_vtab_cursor *, sqlite3_context *, int)) my_column,
|
|
||||||
(int (*)(sqlite3_vtab_cursor *, sqlite3_int64 *)) my_rowid,
|
|
||||||
NULL, // my_update
|
|
||||||
NULL, // my_begin
|
|
||||||
NULL, // my_sync
|
|
||||||
NULL, // my_commit
|
|
||||||
NULL, // my_rollback
|
|
||||||
NULL, // my_findfunction
|
|
||||||
NULL, // my_rename
|
|
||||||
};
|
|
||||||
|
|
||||||
static void
|
|
||||||
destructor(void *arg) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
|
|
||||||
EXPORT int
|
|
||||||
sqlite3_extension_init(sqlite3 *db, char **errmsg, const sqlite3_api_routines *api) {
|
|
||||||
SQLITE_EXTENSION_INIT2(api);
|
|
||||||
sqlite3_create_module_v2(db, "github", &module, NULL, destructor);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
106
vendor/github.com/mattn/go-sqlite3/_example/simple/simple.go
generated
vendored
106
vendor/github.com/mattn/go-sqlite3/_example/simple/simple.go
generated
vendored
@ -1,106 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"fmt"
|
|
||||||
_ "github.com/mattn/go-sqlite3"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
os.Remove("./foo.db")
|
|
||||||
|
|
||||||
db, err := sql.Open("sqlite3", "./foo.db")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
sqlStmt := `
|
|
||||||
create table foo (id integer not null primary key, name text);
|
|
||||||
delete from foo;
|
|
||||||
`
|
|
||||||
_, err = db.Exec(sqlStmt)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("%q: %s\n", err, sqlStmt)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
tx, err := db.Begin()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
stmt, err := tx.Prepare("insert into foo(id, name) values(?, ?)")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer stmt.Close()
|
|
||||||
for i := 0; i < 100; i++ {
|
|
||||||
_, err = stmt.Exec(i, fmt.Sprintf("こんにちわ世界%03d", i))
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tx.Commit()
|
|
||||||
|
|
||||||
rows, err := db.Query("select id, name from foo")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
for rows.Next() {
|
|
||||||
var id int
|
|
||||||
var name string
|
|
||||||
err = rows.Scan(&id, &name)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
fmt.Println(id, name)
|
|
||||||
}
|
|
||||||
err = rows.Err()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt, err = db.Prepare("select name from foo where id = ?")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer stmt.Close()
|
|
||||||
var name string
|
|
||||||
err = stmt.QueryRow("3").Scan(&name)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
fmt.Println(name)
|
|
||||||
|
|
||||||
_, err = db.Exec("delete from foo")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = db.Exec("insert into foo(id, name) values(1, 'foo'), (2, 'bar'), (3, 'baz')")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
rows, err = db.Query("select id, name from foo")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
for rows.Next() {
|
|
||||||
var id int
|
|
||||||
var name string
|
|
||||||
err = rows.Scan(&id, &name)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
fmt.Println(id, name)
|
|
||||||
}
|
|
||||||
err = rows.Err()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
74
vendor/github.com/mattn/go-sqlite3/backup.go
generated
vendored
74
vendor/github.com/mattn/go-sqlite3/backup.go
generated
vendored
@ -1,74 +0,0 @@
|
|||||||
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
|
|
||||||
//
|
|
||||||
// Use of this source code is governed by an MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package sqlite3
|
|
||||||
|
|
||||||
/*
|
|
||||||
#ifndef USE_LIBSQLITE3
|
|
||||||
#include <sqlite3-binding.h>
|
|
||||||
#else
|
|
||||||
#include <sqlite3.h>
|
|
||||||
#endif
|
|
||||||
#include <stdlib.h>
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
import (
|
|
||||||
"runtime"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
type SQLiteBackup struct {
|
|
||||||
b *C.sqlite3_backup
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *SQLiteConn) Backup(dest string, conn *SQLiteConn, src string) (*SQLiteBackup, error) {
|
|
||||||
destptr := C.CString(dest)
|
|
||||||
defer C.free(unsafe.Pointer(destptr))
|
|
||||||
srcptr := C.CString(src)
|
|
||||||
defer C.free(unsafe.Pointer(srcptr))
|
|
||||||
|
|
||||||
if b := C.sqlite3_backup_init(c.db, destptr, conn.db, srcptr); b != nil {
|
|
||||||
bb := &SQLiteBackup{b: b}
|
|
||||||
runtime.SetFinalizer(bb, (*SQLiteBackup).Finish)
|
|
||||||
return bb, nil
|
|
||||||
}
|
|
||||||
return nil, c.lastError()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Backs up for one step. Calls the underlying `sqlite3_backup_step` function.
|
|
||||||
// This function returns a boolean indicating if the backup is done and
|
|
||||||
// an error signalling any other error. Done is returned if the underlying C
|
|
||||||
// function returns SQLITE_DONE (Code 101)
|
|
||||||
func (b *SQLiteBackup) Step(p int) (bool, error) {
|
|
||||||
ret := C.sqlite3_backup_step(b.b, C.int(p))
|
|
||||||
if ret == C.SQLITE_DONE {
|
|
||||||
return true, nil
|
|
||||||
} else if ret != 0 && ret != C.SQLITE_LOCKED && ret != C.SQLITE_BUSY {
|
|
||||||
return false, Error{Code: ErrNo(ret)}
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *SQLiteBackup) Remaining() int {
|
|
||||||
return int(C.sqlite3_backup_remaining(b.b))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *SQLiteBackup) PageCount() int {
|
|
||||||
return int(C.sqlite3_backup_pagecount(b.b))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *SQLiteBackup) Finish() error {
|
|
||||||
return b.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *SQLiteBackup) Close() error {
|
|
||||||
ret := C.sqlite3_backup_finish(b.b)
|
|
||||||
if ret != 0 {
|
|
||||||
return Error{Code: ErrNo(ret)}
|
|
||||||
}
|
|
||||||
b.b = nil
|
|
||||||
runtime.SetFinalizer(b, nil)
|
|
||||||
return nil
|
|
||||||
}
|
|
336
vendor/github.com/mattn/go-sqlite3/callback.go
generated
vendored
336
vendor/github.com/mattn/go-sqlite3/callback.go
generated
vendored
@ -1,336 +0,0 @@
|
|||||||
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
|
|
||||||
//
|
|
||||||
// Use of this source code is governed by an MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package sqlite3
|
|
||||||
|
|
||||||
// You can't export a Go function to C and have definitions in the C
|
|
||||||
// preamble in the same file, so we have to have callbackTrampoline in
|
|
||||||
// its own file. Because we need a separate file anyway, the support
|
|
||||||
// code for SQLite custom functions is in here.
|
|
||||||
|
|
||||||
/*
|
|
||||||
#include <sqlite3-binding.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
void _sqlite3_result_text(sqlite3_context* ctx, const char* s);
|
|
||||||
void _sqlite3_result_blob(sqlite3_context* ctx, const void* b, int l);
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"math"
|
|
||||||
"reflect"
|
|
||||||
"sync"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
//export callbackTrampoline
|
|
||||||
func callbackTrampoline(ctx *C.sqlite3_context, argc int, argv **C.sqlite3_value) {
|
|
||||||
args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:argc:argc]
|
|
||||||
fi := lookupHandle(uintptr(C.sqlite3_user_data(ctx))).(*functionInfo)
|
|
||||||
fi.Call(ctx, args)
|
|
||||||
}
|
|
||||||
|
|
||||||
//export stepTrampoline
|
|
||||||
func stepTrampoline(ctx *C.sqlite3_context, argc int, argv **C.sqlite3_value) {
|
|
||||||
args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:argc:argc]
|
|
||||||
ai := lookupHandle(uintptr(C.sqlite3_user_data(ctx))).(*aggInfo)
|
|
||||||
ai.Step(ctx, args)
|
|
||||||
}
|
|
||||||
|
|
||||||
//export doneTrampoline
|
|
||||||
func doneTrampoline(ctx *C.sqlite3_context) {
|
|
||||||
handle := uintptr(C.sqlite3_user_data(ctx))
|
|
||||||
ai := lookupHandle(handle).(*aggInfo)
|
|
||||||
ai.Done(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use handles to avoid passing Go pointers to C.
|
|
||||||
|
|
||||||
type handleVal struct {
|
|
||||||
db *SQLiteConn
|
|
||||||
val interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
var handleLock sync.Mutex
|
|
||||||
var handleVals = make(map[uintptr]handleVal)
|
|
||||||
var handleIndex uintptr = 100
|
|
||||||
|
|
||||||
func newHandle(db *SQLiteConn, v interface{}) uintptr {
|
|
||||||
handleLock.Lock()
|
|
||||||
defer handleLock.Unlock()
|
|
||||||
i := handleIndex
|
|
||||||
handleIndex++
|
|
||||||
handleVals[i] = handleVal{db, v}
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
func lookupHandle(handle uintptr) interface{} {
|
|
||||||
handleLock.Lock()
|
|
||||||
defer handleLock.Unlock()
|
|
||||||
r, ok := handleVals[handle]
|
|
||||||
if !ok {
|
|
||||||
if handle >= 100 && handle < handleIndex {
|
|
||||||
panic("deleted handle")
|
|
||||||
} else {
|
|
||||||
panic("invalid handle")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return r.val
|
|
||||||
}
|
|
||||||
|
|
||||||
func deleteHandles(db *SQLiteConn) {
|
|
||||||
handleLock.Lock()
|
|
||||||
defer handleLock.Unlock()
|
|
||||||
for handle, val := range handleVals {
|
|
||||||
if val.db == db {
|
|
||||||
delete(handleVals, handle)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is only here so that tests can refer to it.
|
|
||||||
type callbackArgRaw C.sqlite3_value
|
|
||||||
|
|
||||||
type callbackArgConverter func(*C.sqlite3_value) (reflect.Value, error)
|
|
||||||
|
|
||||||
type callbackArgCast struct {
|
|
||||||
f callbackArgConverter
|
|
||||||
typ reflect.Type
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c callbackArgCast) Run(v *C.sqlite3_value) (reflect.Value, error) {
|
|
||||||
val, err := c.f(v)
|
|
||||||
if err != nil {
|
|
||||||
return reflect.Value{}, err
|
|
||||||
}
|
|
||||||
if !val.Type().ConvertibleTo(c.typ) {
|
|
||||||
return reflect.Value{}, fmt.Errorf("cannot convert %s to %s", val.Type(), c.typ)
|
|
||||||
}
|
|
||||||
return val.Convert(c.typ), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func callbackArgInt64(v *C.sqlite3_value) (reflect.Value, error) {
|
|
||||||
if C.sqlite3_value_type(v) != C.SQLITE_INTEGER {
|
|
||||||
return reflect.Value{}, fmt.Errorf("argument must be an INTEGER")
|
|
||||||
}
|
|
||||||
return reflect.ValueOf(int64(C.sqlite3_value_int64(v))), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func callbackArgBool(v *C.sqlite3_value) (reflect.Value, error) {
|
|
||||||
if C.sqlite3_value_type(v) != C.SQLITE_INTEGER {
|
|
||||||
return reflect.Value{}, fmt.Errorf("argument must be an INTEGER")
|
|
||||||
}
|
|
||||||
i := int64(C.sqlite3_value_int64(v))
|
|
||||||
val := false
|
|
||||||
if i != 0 {
|
|
||||||
val = true
|
|
||||||
}
|
|
||||||
return reflect.ValueOf(val), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func callbackArgFloat64(v *C.sqlite3_value) (reflect.Value, error) {
|
|
||||||
if C.sqlite3_value_type(v) != C.SQLITE_FLOAT {
|
|
||||||
return reflect.Value{}, fmt.Errorf("argument must be a FLOAT")
|
|
||||||
}
|
|
||||||
return reflect.ValueOf(float64(C.sqlite3_value_double(v))), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func callbackArgBytes(v *C.sqlite3_value) (reflect.Value, error) {
|
|
||||||
switch C.sqlite3_value_type(v) {
|
|
||||||
case C.SQLITE_BLOB:
|
|
||||||
l := C.sqlite3_value_bytes(v)
|
|
||||||
p := C.sqlite3_value_blob(v)
|
|
||||||
return reflect.ValueOf(C.GoBytes(p, l)), nil
|
|
||||||
case C.SQLITE_TEXT:
|
|
||||||
l := C.sqlite3_value_bytes(v)
|
|
||||||
c := unsafe.Pointer(C.sqlite3_value_text(v))
|
|
||||||
return reflect.ValueOf(C.GoBytes(c, l)), nil
|
|
||||||
default:
|
|
||||||
return reflect.Value{}, fmt.Errorf("argument must be BLOB or TEXT")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func callbackArgString(v *C.sqlite3_value) (reflect.Value, error) {
|
|
||||||
switch C.sqlite3_value_type(v) {
|
|
||||||
case C.SQLITE_BLOB:
|
|
||||||
l := C.sqlite3_value_bytes(v)
|
|
||||||
p := (*C.char)(C.sqlite3_value_blob(v))
|
|
||||||
return reflect.ValueOf(C.GoStringN(p, l)), nil
|
|
||||||
case C.SQLITE_TEXT:
|
|
||||||
c := (*C.char)(unsafe.Pointer(C.sqlite3_value_text(v)))
|
|
||||||
return reflect.ValueOf(C.GoString(c)), nil
|
|
||||||
default:
|
|
||||||
return reflect.Value{}, fmt.Errorf("argument must be BLOB or TEXT")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func callbackArgGeneric(v *C.sqlite3_value) (reflect.Value, error) {
|
|
||||||
switch C.sqlite3_value_type(v) {
|
|
||||||
case C.SQLITE_INTEGER:
|
|
||||||
return callbackArgInt64(v)
|
|
||||||
case C.SQLITE_FLOAT:
|
|
||||||
return callbackArgFloat64(v)
|
|
||||||
case C.SQLITE_TEXT:
|
|
||||||
return callbackArgString(v)
|
|
||||||
case C.SQLITE_BLOB:
|
|
||||||
return callbackArgBytes(v)
|
|
||||||
case C.SQLITE_NULL:
|
|
||||||
// Interpret NULL as a nil byte slice.
|
|
||||||
var ret []byte
|
|
||||||
return reflect.ValueOf(ret), nil
|
|
||||||
default:
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func callbackArg(typ reflect.Type) (callbackArgConverter, error) {
|
|
||||||
switch typ.Kind() {
|
|
||||||
case reflect.Interface:
|
|
||||||
if typ.NumMethod() != 0 {
|
|
||||||
return nil, errors.New("the only supported interface type is interface{}")
|
|
||||||
}
|
|
||||||
return callbackArgGeneric, nil
|
|
||||||
case reflect.Slice:
|
|
||||||
if typ.Elem().Kind() != reflect.Uint8 {
|
|
||||||
return nil, errors.New("the only supported slice type is []byte")
|
|
||||||
}
|
|
||||||
return callbackArgBytes, nil
|
|
||||||
case reflect.String:
|
|
||||||
return callbackArgString, nil
|
|
||||||
case reflect.Bool:
|
|
||||||
return callbackArgBool, nil
|
|
||||||
case reflect.Int64:
|
|
||||||
return callbackArgInt64, nil
|
|
||||||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Int, reflect.Uint:
|
|
||||||
c := callbackArgCast{callbackArgInt64, typ}
|
|
||||||
return c.Run, nil
|
|
||||||
case reflect.Float64:
|
|
||||||
return callbackArgFloat64, nil
|
|
||||||
case reflect.Float32:
|
|
||||||
c := callbackArgCast{callbackArgFloat64, typ}
|
|
||||||
return c.Run, nil
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("don't know how to convert to %s", typ)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func callbackConvertArgs(argv []*C.sqlite3_value, converters []callbackArgConverter, variadic callbackArgConverter) ([]reflect.Value, error) {
|
|
||||||
var args []reflect.Value
|
|
||||||
|
|
||||||
if len(argv) < len(converters) {
|
|
||||||
return nil, fmt.Errorf("function requires at least %d arguments", len(converters))
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, arg := range argv[:len(converters)] {
|
|
||||||
v, err := converters[i](arg)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
args = append(args, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
if variadic != nil {
|
|
||||||
for _, arg := range argv[len(converters):] {
|
|
||||||
v, err := variadic(arg)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
args = append(args, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return args, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type callbackRetConverter func(*C.sqlite3_context, reflect.Value) error
|
|
||||||
|
|
||||||
func callbackRetInteger(ctx *C.sqlite3_context, v reflect.Value) error {
|
|
||||||
switch v.Type().Kind() {
|
|
||||||
case reflect.Int64:
|
|
||||||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Int, reflect.Uint:
|
|
||||||
v = v.Convert(reflect.TypeOf(int64(0)))
|
|
||||||
case reflect.Bool:
|
|
||||||
b := v.Interface().(bool)
|
|
||||||
if b {
|
|
||||||
v = reflect.ValueOf(int64(1))
|
|
||||||
} else {
|
|
||||||
v = reflect.ValueOf(int64(0))
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("cannot convert %s to INTEGER", v.Type())
|
|
||||||
}
|
|
||||||
|
|
||||||
C.sqlite3_result_int64(ctx, C.sqlite3_int64(v.Interface().(int64)))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func callbackRetFloat(ctx *C.sqlite3_context, v reflect.Value) error {
|
|
||||||
switch v.Type().Kind() {
|
|
||||||
case reflect.Float64:
|
|
||||||
case reflect.Float32:
|
|
||||||
v = v.Convert(reflect.TypeOf(float64(0)))
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("cannot convert %s to FLOAT", v.Type())
|
|
||||||
}
|
|
||||||
|
|
||||||
C.sqlite3_result_double(ctx, C.double(v.Interface().(float64)))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func callbackRetBlob(ctx *C.sqlite3_context, v reflect.Value) error {
|
|
||||||
if v.Type().Kind() != reflect.Slice || v.Type().Elem().Kind() != reflect.Uint8 {
|
|
||||||
return fmt.Errorf("cannot convert %s to BLOB", v.Type())
|
|
||||||
}
|
|
||||||
i := v.Interface()
|
|
||||||
if i == nil || len(i.([]byte)) == 0 {
|
|
||||||
C.sqlite3_result_null(ctx)
|
|
||||||
} else {
|
|
||||||
bs := i.([]byte)
|
|
||||||
C._sqlite3_result_blob(ctx, unsafe.Pointer(&bs[0]), C.int(len(bs)))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func callbackRetText(ctx *C.sqlite3_context, v reflect.Value) error {
|
|
||||||
if v.Type().Kind() != reflect.String {
|
|
||||||
return fmt.Errorf("cannot convert %s to TEXT", v.Type())
|
|
||||||
}
|
|
||||||
C._sqlite3_result_text(ctx, C.CString(v.Interface().(string)))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func callbackRet(typ reflect.Type) (callbackRetConverter, error) {
|
|
||||||
switch typ.Kind() {
|
|
||||||
case reflect.Slice:
|
|
||||||
if typ.Elem().Kind() != reflect.Uint8 {
|
|
||||||
return nil, errors.New("the only supported slice type is []byte")
|
|
||||||
}
|
|
||||||
return callbackRetBlob, nil
|
|
||||||
case reflect.String:
|
|
||||||
return callbackRetText, nil
|
|
||||||
case reflect.Bool, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Int, reflect.Uint:
|
|
||||||
return callbackRetInteger, nil
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
return callbackRetFloat, nil
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("don't know how to convert to %s", typ)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func callbackError(ctx *C.sqlite3_context, err error) {
|
|
||||||
cstr := C.CString(err.Error())
|
|
||||||
defer C.free(unsafe.Pointer(cstr))
|
|
||||||
C.sqlite3_result_error(ctx, cstr, -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test support code. Tests are not allowed to import "C", so we can't
|
|
||||||
// declare any functions that use C.sqlite3_value.
|
|
||||||
func callbackSyntheticForTests(v reflect.Value, err error) callbackArgConverter {
|
|
||||||
return func(*C.sqlite3_value) (reflect.Value, error) {
|
|
||||||
return v, err
|
|
||||||
}
|
|
||||||
}
|
|
97
vendor/github.com/mattn/go-sqlite3/callback_test.go
generated
vendored
97
vendor/github.com/mattn/go-sqlite3/callback_test.go
generated
vendored
@ -1,97 +0,0 @@
|
|||||||
package sqlite3
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"math"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCallbackArgCast(t *testing.T) {
|
|
||||||
intConv := callbackSyntheticForTests(reflect.ValueOf(int64(math.MaxInt64)), nil)
|
|
||||||
floatConv := callbackSyntheticForTests(reflect.ValueOf(float64(math.MaxFloat64)), nil)
|
|
||||||
errConv := callbackSyntheticForTests(reflect.Value{}, errors.New("test"))
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
f callbackArgConverter
|
|
||||||
o reflect.Value
|
|
||||||
}{
|
|
||||||
{intConv, reflect.ValueOf(int8(-1))},
|
|
||||||
{intConv, reflect.ValueOf(int16(-1))},
|
|
||||||
{intConv, reflect.ValueOf(int32(-1))},
|
|
||||||
{intConv, reflect.ValueOf(uint8(math.MaxUint8))},
|
|
||||||
{intConv, reflect.ValueOf(uint16(math.MaxUint16))},
|
|
||||||
{intConv, reflect.ValueOf(uint32(math.MaxUint32))},
|
|
||||||
// Special case, int64->uint64 is only 1<<63 - 1, not 1<<64 - 1
|
|
||||||
{intConv, reflect.ValueOf(uint64(math.MaxInt64))},
|
|
||||||
{floatConv, reflect.ValueOf(float32(math.Inf(1)))},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
conv := callbackArgCast{test.f, test.o.Type()}
|
|
||||||
val, err := conv.Run(nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Couldn't convert to %s: %s", test.o.Type(), err)
|
|
||||||
} else if !reflect.DeepEqual(val.Interface(), test.o.Interface()) {
|
|
||||||
t.Errorf("Unexpected result from converting to %s: got %v, want %v", test.o.Type(), val.Interface(), test.o.Interface())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
conv := callbackArgCast{errConv, reflect.TypeOf(int8(0))}
|
|
||||||
_, err := conv.Run(nil)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("Expected error during callbackArgCast, but got none")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCallbackConverters(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
v interface{}
|
|
||||||
err bool
|
|
||||||
}{
|
|
||||||
// Unfortunately, we can't tell which converter was returned,
|
|
||||||
// but we can at least check which types can be converted.
|
|
||||||
{[]byte{0}, false},
|
|
||||||
{"text", false},
|
|
||||||
{true, false},
|
|
||||||
{int8(0), false},
|
|
||||||
{int16(0), false},
|
|
||||||
{int32(0), false},
|
|
||||||
{int64(0), false},
|
|
||||||
{uint8(0), false},
|
|
||||||
{uint16(0), false},
|
|
||||||
{uint32(0), false},
|
|
||||||
{uint64(0), false},
|
|
||||||
{int(0), false},
|
|
||||||
{uint(0), false},
|
|
||||||
{float64(0), false},
|
|
||||||
{float32(0), false},
|
|
||||||
|
|
||||||
{func() {}, true},
|
|
||||||
{complex64(complex(0, 0)), true},
|
|
||||||
{complex128(complex(0, 0)), true},
|
|
||||||
{struct{}{}, true},
|
|
||||||
{map[string]string{}, true},
|
|
||||||
{[]string{}, true},
|
|
||||||
{(*int8)(nil), true},
|
|
||||||
{make(chan int), true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
_, err := callbackArg(reflect.TypeOf(test.v))
|
|
||||||
if test.err && err == nil {
|
|
||||||
t.Errorf("Expected an error when converting %s, got no error", reflect.TypeOf(test.v))
|
|
||||||
} else if !test.err && err != nil {
|
|
||||||
t.Errorf("Expected converter when converting %s, got error: %s", reflect.TypeOf(test.v), err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
_, err := callbackRet(reflect.TypeOf(test.v))
|
|
||||||
if test.err && err == nil {
|
|
||||||
t.Errorf("Expected an error when converting %s, got no error", reflect.TypeOf(test.v))
|
|
||||||
} else if !test.err && err != nil {
|
|
||||||
t.Errorf("Expected converter when converting %s, got error: %s", reflect.TypeOf(test.v), err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
112
vendor/github.com/mattn/go-sqlite3/doc.go
generated
vendored
112
vendor/github.com/mattn/go-sqlite3/doc.go
generated
vendored
@ -1,112 +0,0 @@
|
|||||||
/*
|
|
||||||
Package sqlite3 provides interface to SQLite3 databases.
|
|
||||||
|
|
||||||
This works as a driver for database/sql.
|
|
||||||
|
|
||||||
Installation
|
|
||||||
|
|
||||||
go get github.com/mattn/go-sqlite3
|
|
||||||
|
|
||||||
Supported Types
|
|
||||||
|
|
||||||
Currently, go-sqlite3 supports the following data types.
|
|
||||||
|
|
||||||
+------------------------------+
|
|
||||||
|go | sqlite3 |
|
|
||||||
|----------|-------------------|
|
|
||||||
|nil | null |
|
|
||||||
|int | integer |
|
|
||||||
|int64 | integer |
|
|
||||||
|float64 | float |
|
|
||||||
|bool | integer |
|
|
||||||
|[]byte | blob |
|
|
||||||
|string | text |
|
|
||||||
|time.Time | timestamp/datetime|
|
|
||||||
+------------------------------+
|
|
||||||
|
|
||||||
SQLite3 Extension
|
|
||||||
|
|
||||||
You can write your own extension module for sqlite3. For example, below is an
|
|
||||||
extension for a Regexp matcher operation.
|
|
||||||
|
|
||||||
#include <pcre.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <sqlite3ext.h>
|
|
||||||
|
|
||||||
SQLITE_EXTENSION_INIT1
|
|
||||||
static void regexp_func(sqlite3_context *context, int argc, sqlite3_value **argv) {
|
|
||||||
if (argc >= 2) {
|
|
||||||
const char *target = (const char *)sqlite3_value_text(argv[1]);
|
|
||||||
const char *pattern = (const char *)sqlite3_value_text(argv[0]);
|
|
||||||
const char* errstr = NULL;
|
|
||||||
int erroff = 0;
|
|
||||||
int vec[500];
|
|
||||||
int n, rc;
|
|
||||||
pcre* re = pcre_compile(pattern, 0, &errstr, &erroff, NULL);
|
|
||||||
rc = pcre_exec(re, NULL, target, strlen(target), 0, 0, vec, 500);
|
|
||||||
if (rc <= 0) {
|
|
||||||
sqlite3_result_error(context, errstr, 0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
sqlite3_result_int(context, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
__declspec(dllexport)
|
|
||||||
#endif
|
|
||||||
int sqlite3_extension_init(sqlite3 *db, char **errmsg,
|
|
||||||
const sqlite3_api_routines *api) {
|
|
||||||
SQLITE_EXTENSION_INIT2(api);
|
|
||||||
return sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8,
|
|
||||||
(void*)db, regexp_func, NULL, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
It needs to be built as a so/dll shared library. And you need to register
|
|
||||||
the extension module like below.
|
|
||||||
|
|
||||||
sql.Register("sqlite3_with_extensions",
|
|
||||||
&sqlite3.SQLiteDriver{
|
|
||||||
Extensions: []string{
|
|
||||||
"sqlite3_mod_regexp",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
Then, you can use this extension.
|
|
||||||
|
|
||||||
rows, err := db.Query("select text from mytable where name regexp '^golang'")
|
|
||||||
|
|
||||||
Connection Hook
|
|
||||||
|
|
||||||
You can hook and inject your code when the connection is established. database/sql
|
|
||||||
doesn't provide a way to get native go-sqlite3 interfaces. So if you want,
|
|
||||||
you need to set ConnectHook and get the SQLiteConn.
|
|
||||||
|
|
||||||
sql.Register("sqlite3_with_hook_example",
|
|
||||||
&sqlite3.SQLiteDriver{
|
|
||||||
ConnectHook: func(conn *sqlite3.SQLiteConn) error {
|
|
||||||
sqlite3conn = append(sqlite3conn, conn)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
Go SQlite3 Extensions
|
|
||||||
|
|
||||||
If you want to register Go functions as SQLite extension functions,
|
|
||||||
call RegisterFunction from ConnectHook.
|
|
||||||
|
|
||||||
regex = func(re, s string) (bool, error) {
|
|
||||||
return regexp.MatchString(re, s)
|
|
||||||
}
|
|
||||||
sql.Register("sqlite3_with_go_func",
|
|
||||||
&sqlite3.SQLiteDriver{
|
|
||||||
ConnectHook: func(conn *sqlite3.SQLiteConn) error {
|
|
||||||
return conn.RegisterFunc("regexp", regex, true)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
See the documentation of RegisterFunc for more details.
|
|
||||||
|
|
||||||
*/
|
|
||||||
package sqlite3
|
|
128
vendor/github.com/mattn/go-sqlite3/error.go
generated
vendored
128
vendor/github.com/mattn/go-sqlite3/error.go
generated
vendored
@ -1,128 +0,0 @@
|
|||||||
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
|
|
||||||
//
|
|
||||||
// Use of this source code is governed by an MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package sqlite3
|
|
||||||
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
type ErrNo int
|
|
||||||
|
|
||||||
const ErrNoMask C.int = 0xff
|
|
||||||
|
|
||||||
type ErrNoExtended int
|
|
||||||
|
|
||||||
type Error struct {
|
|
||||||
Code ErrNo /* The error code returned by SQLite */
|
|
||||||
ExtendedCode ErrNoExtended /* The extended error code returned by SQLite */
|
|
||||||
err string /* The error string returned by sqlite3_errmsg(),
|
|
||||||
this usually contains more specific details. */
|
|
||||||
}
|
|
||||||
|
|
||||||
// result codes from http://www.sqlite.org/c3ref/c_abort.html
|
|
||||||
var (
|
|
||||||
ErrError = ErrNo(1) /* SQL error or missing database */
|
|
||||||
ErrInternal = ErrNo(2) /* Internal logic error in SQLite */
|
|
||||||
ErrPerm = ErrNo(3) /* Access permission denied */
|
|
||||||
ErrAbort = ErrNo(4) /* Callback routine requested an abort */
|
|
||||||
ErrBusy = ErrNo(5) /* The database file is locked */
|
|
||||||
ErrLocked = ErrNo(6) /* A table in the database is locked */
|
|
||||||
ErrNomem = ErrNo(7) /* A malloc() failed */
|
|
||||||
ErrReadonly = ErrNo(8) /* Attempt to write a readonly database */
|
|
||||||
ErrInterrupt = ErrNo(9) /* Operation terminated by sqlite3_interrupt() */
|
|
||||||
ErrIoErr = ErrNo(10) /* Some kind of disk I/O error occurred */
|
|
||||||
ErrCorrupt = ErrNo(11) /* The database disk image is malformed */
|
|
||||||
ErrNotFound = ErrNo(12) /* Unknown opcode in sqlite3_file_control() */
|
|
||||||
ErrFull = ErrNo(13) /* Insertion failed because database is full */
|
|
||||||
ErrCantOpen = ErrNo(14) /* Unable to open the database file */
|
|
||||||
ErrProtocol = ErrNo(15) /* Database lock protocol error */
|
|
||||||
ErrEmpty = ErrNo(16) /* Database is empty */
|
|
||||||
ErrSchema = ErrNo(17) /* The database schema changed */
|
|
||||||
ErrTooBig = ErrNo(18) /* String or BLOB exceeds size limit */
|
|
||||||
ErrConstraint = ErrNo(19) /* Abort due to constraint violation */
|
|
||||||
ErrMismatch = ErrNo(20) /* Data type mismatch */
|
|
||||||
ErrMisuse = ErrNo(21) /* Library used incorrectly */
|
|
||||||
ErrNoLFS = ErrNo(22) /* Uses OS features not supported on host */
|
|
||||||
ErrAuth = ErrNo(23) /* Authorization denied */
|
|
||||||
ErrFormat = ErrNo(24) /* Auxiliary database format error */
|
|
||||||
ErrRange = ErrNo(25) /* 2nd parameter to sqlite3_bind out of range */
|
|
||||||
ErrNotADB = ErrNo(26) /* File opened that is not a database file */
|
|
||||||
ErrNotice = ErrNo(27) /* Notifications from sqlite3_log() */
|
|
||||||
ErrWarning = ErrNo(28) /* Warnings from sqlite3_log() */
|
|
||||||
)
|
|
||||||
|
|
||||||
func (err ErrNo) Error() string {
|
|
||||||
return Error{Code: err}.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrNo) Extend(by int) ErrNoExtended {
|
|
||||||
return ErrNoExtended(int(err) | (by << 8))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrNoExtended) Error() string {
|
|
||||||
return Error{Code: ErrNo(C.int(err) & ErrNoMask), ExtendedCode: err}.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err Error) Error() string {
|
|
||||||
if err.err != "" {
|
|
||||||
return err.err
|
|
||||||
}
|
|
||||||
return errorString(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// result codes from http://www.sqlite.org/c3ref/c_abort_rollback.html
|
|
||||||
var (
|
|
||||||
ErrIoErrRead = ErrIoErr.Extend(1)
|
|
||||||
ErrIoErrShortRead = ErrIoErr.Extend(2)
|
|
||||||
ErrIoErrWrite = ErrIoErr.Extend(3)
|
|
||||||
ErrIoErrFsync = ErrIoErr.Extend(4)
|
|
||||||
ErrIoErrDirFsync = ErrIoErr.Extend(5)
|
|
||||||
ErrIoErrTruncate = ErrIoErr.Extend(6)
|
|
||||||
ErrIoErrFstat = ErrIoErr.Extend(7)
|
|
||||||
ErrIoErrUnlock = ErrIoErr.Extend(8)
|
|
||||||
ErrIoErrRDlock = ErrIoErr.Extend(9)
|
|
||||||
ErrIoErrDelete = ErrIoErr.Extend(10)
|
|
||||||
ErrIoErrBlocked = ErrIoErr.Extend(11)
|
|
||||||
ErrIoErrNoMem = ErrIoErr.Extend(12)
|
|
||||||
ErrIoErrAccess = ErrIoErr.Extend(13)
|
|
||||||
ErrIoErrCheckReservedLock = ErrIoErr.Extend(14)
|
|
||||||
ErrIoErrLock = ErrIoErr.Extend(15)
|
|
||||||
ErrIoErrClose = ErrIoErr.Extend(16)
|
|
||||||
ErrIoErrDirClose = ErrIoErr.Extend(17)
|
|
||||||
ErrIoErrSHMOpen = ErrIoErr.Extend(18)
|
|
||||||
ErrIoErrSHMSize = ErrIoErr.Extend(19)
|
|
||||||
ErrIoErrSHMLock = ErrIoErr.Extend(20)
|
|
||||||
ErrIoErrSHMMap = ErrIoErr.Extend(21)
|
|
||||||
ErrIoErrSeek = ErrIoErr.Extend(22)
|
|
||||||
ErrIoErrDeleteNoent = ErrIoErr.Extend(23)
|
|
||||||
ErrIoErrMMap = ErrIoErr.Extend(24)
|
|
||||||
ErrIoErrGetTempPath = ErrIoErr.Extend(25)
|
|
||||||
ErrIoErrConvPath = ErrIoErr.Extend(26)
|
|
||||||
ErrLockedSharedCache = ErrLocked.Extend(1)
|
|
||||||
ErrBusyRecovery = ErrBusy.Extend(1)
|
|
||||||
ErrBusySnapshot = ErrBusy.Extend(2)
|
|
||||||
ErrCantOpenNoTempDir = ErrCantOpen.Extend(1)
|
|
||||||
ErrCantOpenIsDir = ErrCantOpen.Extend(2)
|
|
||||||
ErrCantOpenFullPath = ErrCantOpen.Extend(3)
|
|
||||||
ErrCantOpenConvPath = ErrCantOpen.Extend(4)
|
|
||||||
ErrCorruptVTab = ErrCorrupt.Extend(1)
|
|
||||||
ErrReadonlyRecovery = ErrReadonly.Extend(1)
|
|
||||||
ErrReadonlyCantLock = ErrReadonly.Extend(2)
|
|
||||||
ErrReadonlyRollback = ErrReadonly.Extend(3)
|
|
||||||
ErrReadonlyDbMoved = ErrReadonly.Extend(4)
|
|
||||||
ErrAbortRollback = ErrAbort.Extend(2)
|
|
||||||
ErrConstraintCheck = ErrConstraint.Extend(1)
|
|
||||||
ErrConstraintCommitHook = ErrConstraint.Extend(2)
|
|
||||||
ErrConstraintForeignKey = ErrConstraint.Extend(3)
|
|
||||||
ErrConstraintFunction = ErrConstraint.Extend(4)
|
|
||||||
ErrConstraintNotNull = ErrConstraint.Extend(5)
|
|
||||||
ErrConstraintPrimaryKey = ErrConstraint.Extend(6)
|
|
||||||
ErrConstraintTrigger = ErrConstraint.Extend(7)
|
|
||||||
ErrConstraintUnique = ErrConstraint.Extend(8)
|
|
||||||
ErrConstraintVTab = ErrConstraint.Extend(9)
|
|
||||||
ErrConstraintRowId = ErrConstraint.Extend(10)
|
|
||||||
ErrNoticeRecoverWAL = ErrNotice.Extend(1)
|
|
||||||
ErrNoticeRecoverRollback = ErrNotice.Extend(2)
|
|
||||||
ErrWarningAutoIndex = ErrWarning.Extend(1)
|
|
||||||
)
|
|
242
vendor/github.com/mattn/go-sqlite3/error_test.go
generated
vendored
242
vendor/github.com/mattn/go-sqlite3/error_test.go
generated
vendored
@ -1,242 +0,0 @@
|
|||||||
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
|
|
||||||
//
|
|
||||||
// Use of this source code is governed by an MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package sqlite3
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSimpleError(t *testing.T) {
|
|
||||||
e := ErrError.Error()
|
|
||||||
if e != "SQL logic error or missing database" {
|
|
||||||
t.Error("wrong error code:" + e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCorruptDbErrors(t *testing.T) {
|
|
||||||
dirName, err := ioutil.TempDir("", "sqlite3")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(dirName)
|
|
||||||
|
|
||||||
dbFileName := path.Join(dirName, "test.db")
|
|
||||||
f, err := os.Create(dbFileName)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
f.Write([]byte{1, 2, 3, 4, 5})
|
|
||||||
f.Close()
|
|
||||||
|
|
||||||
db, err := sql.Open("sqlite3", dbFileName)
|
|
||||||
if err == nil {
|
|
||||||
_, err = db.Exec("drop table foo")
|
|
||||||
}
|
|
||||||
|
|
||||||
sqliteErr := err.(Error)
|
|
||||||
if sqliteErr.Code != ErrNotADB {
|
|
||||||
t.Error("wrong error code for corrupted DB")
|
|
||||||
}
|
|
||||||
if err.Error() == "" {
|
|
||||||
t.Error("wrong error string for corrupted DB")
|
|
||||||
}
|
|
||||||
db.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSqlLogicErrors(t *testing.T) {
|
|
||||||
dirName, err := ioutil.TempDir("", "sqlite3")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(dirName)
|
|
||||||
|
|
||||||
dbFileName := path.Join(dirName, "test.db")
|
|
||||||
db, err := sql.Open("sqlite3", dbFileName)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
_, err = db.Exec("CREATE TABLE Foo (id INTEGER PRIMARY KEY)")
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
const expectedErr = "table Foo already exists"
|
|
||||||
_, err = db.Exec("CREATE TABLE Foo (id INTEGER PRIMARY KEY)")
|
|
||||||
if err.Error() != expectedErr {
|
|
||||||
t.Errorf("Unexpected error: %s, expected %s", err.Error(), expectedErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestExtendedErrorCodes_ForeignKey(t *testing.T) {
|
|
||||||
dirName, err := ioutil.TempDir("", "sqlite3-err")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(dirName)
|
|
||||||
|
|
||||||
dbFileName := path.Join(dirName, "test.db")
|
|
||||||
db, err := sql.Open("sqlite3", dbFileName)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
_, err = db.Exec("PRAGMA foreign_keys=ON;")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("PRAGMA foreign_keys=ON: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = db.Exec(`CREATE TABLE Foo (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
value INTEGER NOT NULL,
|
|
||||||
ref INTEGER NULL REFERENCES Foo (id),
|
|
||||||
UNIQUE(value)
|
|
||||||
);`)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = db.Exec("INSERT INTO Foo (ref, value) VALUES (100, 100);")
|
|
||||||
if err == nil {
|
|
||||||
t.Error("No error!")
|
|
||||||
} else {
|
|
||||||
sqliteErr := err.(Error)
|
|
||||||
if sqliteErr.Code != ErrConstraint {
|
|
||||||
t.Errorf("Wrong basic error code: %d != %d",
|
|
||||||
sqliteErr.Code, ErrConstraint)
|
|
||||||
}
|
|
||||||
if sqliteErr.ExtendedCode != ErrConstraintForeignKey {
|
|
||||||
t.Errorf("Wrong extended error code: %d != %d",
|
|
||||||
sqliteErr.ExtendedCode, ErrConstraintForeignKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestExtendedErrorCodes_NotNull(t *testing.T) {
|
|
||||||
dirName, err := ioutil.TempDir("", "sqlite3-err")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(dirName)
|
|
||||||
|
|
||||||
dbFileName := path.Join(dirName, "test.db")
|
|
||||||
db, err := sql.Open("sqlite3", dbFileName)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
_, err = db.Exec("PRAGMA foreign_keys=ON;")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("PRAGMA foreign_keys=ON: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = db.Exec(`CREATE TABLE Foo (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
value INTEGER NOT NULL,
|
|
||||||
ref INTEGER NULL REFERENCES Foo (id),
|
|
||||||
UNIQUE(value)
|
|
||||||
);`)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := db.Exec("INSERT INTO Foo (value) VALUES (100);")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Creating first row: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
id, err := res.LastInsertId()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Retrieving last insert id: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = db.Exec("INSERT INTO Foo (ref) VALUES (?);", id)
|
|
||||||
if err == nil {
|
|
||||||
t.Error("No error!")
|
|
||||||
} else {
|
|
||||||
sqliteErr := err.(Error)
|
|
||||||
if sqliteErr.Code != ErrConstraint {
|
|
||||||
t.Errorf("Wrong basic error code: %d != %d",
|
|
||||||
sqliteErr.Code, ErrConstraint)
|
|
||||||
}
|
|
||||||
if sqliteErr.ExtendedCode != ErrConstraintNotNull {
|
|
||||||
t.Errorf("Wrong extended error code: %d != %d",
|
|
||||||
sqliteErr.ExtendedCode, ErrConstraintNotNull)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestExtendedErrorCodes_Unique(t *testing.T) {
|
|
||||||
dirName, err := ioutil.TempDir("", "sqlite3-err")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(dirName)
|
|
||||||
|
|
||||||
dbFileName := path.Join(dirName, "test.db")
|
|
||||||
db, err := sql.Open("sqlite3", dbFileName)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
_, err = db.Exec("PRAGMA foreign_keys=ON;")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("PRAGMA foreign_keys=ON: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = db.Exec(`CREATE TABLE Foo (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
value INTEGER NOT NULL,
|
|
||||||
ref INTEGER NULL REFERENCES Foo (id),
|
|
||||||
UNIQUE(value)
|
|
||||||
);`)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := db.Exec("INSERT INTO Foo (value) VALUES (100);")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Creating first row: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
id, err := res.LastInsertId()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Retrieving last insert id: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = db.Exec("INSERT INTO Foo (ref, value) VALUES (?, 100);", id)
|
|
||||||
if err == nil {
|
|
||||||
t.Error("No error!")
|
|
||||||
} else {
|
|
||||||
sqliteErr := err.(Error)
|
|
||||||
if sqliteErr.Code != ErrConstraint {
|
|
||||||
t.Errorf("Wrong basic error code: %d != %d",
|
|
||||||
sqliteErr.Code, ErrConstraint)
|
|
||||||
}
|
|
||||||
if sqliteErr.ExtendedCode != ErrConstraintUnique {
|
|
||||||
t.Errorf("Wrong extended error code: %d != %d",
|
|
||||||
sqliteErr.ExtendedCode, ErrConstraintUnique)
|
|
||||||
}
|
|
||||||
extended := sqliteErr.Code.Extend(3).Error()
|
|
||||||
expected := "constraint failed"
|
|
||||||
if extended != expected {
|
|
||||||
t.Errorf("Wrong basic error code: %q != %q",
|
|
||||||
extended, expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
189319
vendor/github.com/mattn/go-sqlite3/sqlite3-binding.c
generated
vendored
189319
vendor/github.com/mattn/go-sqlite3/sqlite3-binding.c
generated
vendored
File diff suppressed because it is too large
Load Diff
8733
vendor/github.com/mattn/go-sqlite3/sqlite3-binding.h
generated
vendored
8733
vendor/github.com/mattn/go-sqlite3/sqlite3-binding.h
generated
vendored
File diff suppressed because it is too large
Load Diff
1006
vendor/github.com/mattn/go-sqlite3/sqlite3.go
generated
vendored
1006
vendor/github.com/mattn/go-sqlite3/sqlite3.go
generated
vendored
File diff suppressed because it is too large
Load Diff
130
vendor/github.com/mattn/go-sqlite3/sqlite3_fts3_test.go
generated
vendored
130
vendor/github.com/mattn/go-sqlite3/sqlite3_fts3_test.go
generated
vendored
@ -1,130 +0,0 @@
|
|||||||
// Copyright (C) 2015 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
|
|
||||||
//
|
|
||||||
// Use of this source code is governed by an MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package sqlite3
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestFTS3(t *testing.T) {
|
|
||||||
tempFilename := TempFilename(t)
|
|
||||||
defer os.Remove(tempFilename)
|
|
||||||
db, err := sql.Open("sqlite3", tempFilename)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Failed to open database:", err)
|
|
||||||
}
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
_, err = db.Exec("DROP TABLE foo")
|
|
||||||
_, err = db.Exec("CREATE VIRTUAL TABLE foo USING fts3(id INTEGER PRIMARY KEY, value TEXT)")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Failed to create table:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = db.Exec("INSERT INTO foo(id, value) VALUES(?, ?)", 1, `今日の 晩御飯は 天麩羅よ`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Failed to insert value:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = db.Exec("INSERT INTO foo(id, value) VALUES(?, ?)", 2, `今日は いい 天気だ`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Failed to insert value:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
rows, err := db.Query("SELECT id, value FROM foo WHERE value MATCH '今日* 天*'")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Unable to query foo table:", err)
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
for rows.Next() {
|
|
||||||
var id int
|
|
||||||
var value string
|
|
||||||
|
|
||||||
if err := rows.Scan(&id, &value); err != nil {
|
|
||||||
t.Error("Unable to scan results:", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if id == 1 && value != `今日の 晩御飯は 天麩羅よ` {
|
|
||||||
t.Error("Value for id 1 should be `今日の 晩御飯は 天麩羅よ`, but:", value)
|
|
||||||
} else if id == 2 && value != `今日は いい 天気だ` {
|
|
||||||
t.Error("Value for id 2 should be `今日は いい 天気だ`, but:", value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rows, err = db.Query("SELECT value FROM foo WHERE value MATCH '今日* 天麩羅*'")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Unable to query foo table:", err)
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
var value string
|
|
||||||
if !rows.Next() {
|
|
||||||
t.Fatal("Result should be only one")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := rows.Scan(&value); err != nil {
|
|
||||||
t.Fatal("Unable to scan results:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if value != `今日の 晩御飯は 天麩羅よ` {
|
|
||||||
t.Fatal("Value should be `今日の 晩御飯は 天麩羅よ`, but:", value)
|
|
||||||
}
|
|
||||||
|
|
||||||
if rows.Next() {
|
|
||||||
t.Fatal("Result should be only one")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFTS4(t *testing.T) {
|
|
||||||
tempFilename := TempFilename(t)
|
|
||||||
defer os.Remove(tempFilename)
|
|
||||||
db, err := sql.Open("sqlite3", tempFilename)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Failed to open database:", err)
|
|
||||||
}
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
_, err = db.Exec("DROP TABLE foo")
|
|
||||||
_, err = db.Exec("CREATE VIRTUAL TABLE foo USING fts4(tokenize=unicode61, id INTEGER PRIMARY KEY, value TEXT)")
|
|
||||||
switch {
|
|
||||||
case err != nil && err.Error() == "unknown tokenizer: unicode61":
|
|
||||||
t.Skip("FTS4 not supported")
|
|
||||||
case err != nil:
|
|
||||||
t.Fatal("Failed to create table:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = db.Exec("INSERT INTO foo(id, value) VALUES(?, ?)", 1, `février`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Failed to insert value:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
rows, err := db.Query("SELECT value FROM foo WHERE value MATCH 'fevrier'")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Unable to query foo table:", err)
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
var value string
|
|
||||||
if !rows.Next() {
|
|
||||||
t.Fatal("Result should be only one")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := rows.Scan(&value); err != nil {
|
|
||||||
t.Fatal("Unable to scan results:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if value != `février` {
|
|
||||||
t.Fatal("Value should be `février`, but:", value)
|
|
||||||
}
|
|
||||||
|
|
||||||
if rows.Next() {
|
|
||||||
t.Fatal("Result should be only one")
|
|
||||||
}
|
|
||||||
}
|
|
13
vendor/github.com/mattn/go-sqlite3/sqlite3_fts5.go
generated
vendored
13
vendor/github.com/mattn/go-sqlite3/sqlite3_fts5.go
generated
vendored
@ -1,13 +0,0 @@
|
|||||||
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
|
|
||||||
//
|
|
||||||
// Use of this source code is governed by an MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
// +build fts5
|
|
||||||
|
|
||||||
package sqlite3
|
|
||||||
|
|
||||||
/*
|
|
||||||
#cgo CFLAGS: -DSQLITE_ENABLE_FTS5
|
|
||||||
#cgo LDFLAGS: -lm
|
|
||||||
*/
|
|
||||||
import "C"
|
|
13
vendor/github.com/mattn/go-sqlite3/sqlite3_icu.go
generated
vendored
13
vendor/github.com/mattn/go-sqlite3/sqlite3_icu.go
generated
vendored
@ -1,13 +0,0 @@
|
|||||||
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
|
|
||||||
//
|
|
||||||
// Use of this source code is governed by an MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
// +build icu
|
|
||||||
|
|
||||||
package sqlite3
|
|
||||||
|
|
||||||
/*
|
|
||||||
#cgo LDFLAGS: -licuuc -licui18n
|
|
||||||
#cgo CFLAGS: -DSQLITE_ENABLE_ICU
|
|
||||||
*/
|
|
||||||
import "C"
|
|
12
vendor/github.com/mattn/go-sqlite3/sqlite3_json1.go
generated
vendored
12
vendor/github.com/mattn/go-sqlite3/sqlite3_json1.go
generated
vendored
@ -1,12 +0,0 @@
|
|||||||
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
|
|
||||||
//
|
|
||||||
// Use of this source code is governed by an MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
// +build json1
|
|
||||||
|
|
||||||
package sqlite3
|
|
||||||
|
|
||||||
/*
|
|
||||||
#cgo CFLAGS: -DSQLITE_ENABLE_JSON1
|
|
||||||
*/
|
|
||||||
import "C"
|
|
14
vendor/github.com/mattn/go-sqlite3/sqlite3_libsqlite3.go
generated
vendored
14
vendor/github.com/mattn/go-sqlite3/sqlite3_libsqlite3.go
generated
vendored
@ -1,14 +0,0 @@
|
|||||||
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
|
|
||||||
//
|
|
||||||
// Use of this source code is governed by an MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
// +build libsqlite3
|
|
||||||
|
|
||||||
package sqlite3
|
|
||||||
|
|
||||||
/*
|
|
||||||
#cgo CFLAGS: -DUSE_LIBSQLITE3
|
|
||||||
#cgo linux LDFLAGS: -lsqlite3
|
|
||||||
#cgo darwin LDFLAGS: -L/usr/local/opt/sqlite/lib -lsqlite3
|
|
||||||
*/
|
|
||||||
import "C"
|
|
63
vendor/github.com/mattn/go-sqlite3/sqlite3_load_extension.go
generated
vendored
63
vendor/github.com/mattn/go-sqlite3/sqlite3_load_extension.go
generated
vendored
@ -1,63 +0,0 @@
|
|||||||
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
|
|
||||||
//
|
|
||||||
// Use of this source code is governed by an MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
// +build !sqlite_omit_load_extension
|
|
||||||
|
|
||||||
package sqlite3
|
|
||||||
|
|
||||||
/*
|
|
||||||
#include <sqlite3-binding.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *SQLiteConn) loadExtensions(extensions []string) error {
|
|
||||||
rv := C.sqlite3_enable_load_extension(c.db, 1)
|
|
||||||
if rv != C.SQLITE_OK {
|
|
||||||
return errors.New(C.GoString(C.sqlite3_errmsg(c.db)))
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, extension := range extensions {
|
|
||||||
cext := C.CString(extension)
|
|
||||||
defer C.free(unsafe.Pointer(cext))
|
|
||||||
rv = C.sqlite3_load_extension(c.db, cext, nil, nil)
|
|
||||||
if rv != C.SQLITE_OK {
|
|
||||||
return errors.New(C.GoString(C.sqlite3_errmsg(c.db)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = C.sqlite3_enable_load_extension(c.db, 0)
|
|
||||||
if rv != C.SQLITE_OK {
|
|
||||||
return errors.New(C.GoString(C.sqlite3_errmsg(c.db)))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *SQLiteConn) LoadExtension(lib string, entry string) error {
|
|
||||||
rv := C.sqlite3_enable_load_extension(c.db, 1)
|
|
||||||
if rv != C.SQLITE_OK {
|
|
||||||
return errors.New(C.GoString(C.sqlite3_errmsg(c.db)))
|
|
||||||
}
|
|
||||||
|
|
||||||
clib := C.CString(lib)
|
|
||||||
defer C.free(unsafe.Pointer(clib))
|
|
||||||
centry := C.CString(entry)
|
|
||||||
defer C.free(unsafe.Pointer(centry))
|
|
||||||
|
|
||||||
rv = C.sqlite3_load_extension(c.db, clib, centry, nil)
|
|
||||||
if rv != C.SQLITE_OK {
|
|
||||||
return errors.New(C.GoString(C.sqlite3_errmsg(c.db)))
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = C.sqlite3_enable_load_extension(c.db, 0)
|
|
||||||
if rv != C.SQLITE_OK {
|
|
||||||
return errors.New(C.GoString(C.sqlite3_errmsg(c.db)))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
23
vendor/github.com/mattn/go-sqlite3/sqlite3_omit_load_extension.go
generated
vendored
23
vendor/github.com/mattn/go-sqlite3/sqlite3_omit_load_extension.go
generated
vendored
@ -1,23 +0,0 @@
|
|||||||
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
|
|
||||||
//
|
|
||||||
// Use of this source code is governed by an MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
// +build sqlite_omit_load_extension
|
|
||||||
|
|
||||||
package sqlite3
|
|
||||||
|
|
||||||
/*
|
|
||||||
#cgo CFLAGS: -DSQLITE_OMIT_LOAD_EXTENSION
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *SQLiteConn) loadExtensions(extensions []string) error {
|
|
||||||
return errors.New("Extensions have been disabled for static builds")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *SQLiteConn) LoadExtension(lib string, entry string) error {
|
|
||||||
return errors.New("Extensions have been disabled for static builds")
|
|
||||||
}
|
|
13
vendor/github.com/mattn/go-sqlite3/sqlite3_other.go
generated
vendored
13
vendor/github.com/mattn/go-sqlite3/sqlite3_other.go
generated
vendored
@ -1,13 +0,0 @@
|
|||||||
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
|
|
||||||
//
|
|
||||||
// Use of this source code is governed by an MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
// +build !windows
|
|
||||||
|
|
||||||
package sqlite3
|
|
||||||
|
|
||||||
/*
|
|
||||||
#cgo CFLAGS: -I.
|
|
||||||
#cgo linux LDFLAGS: -ldl
|
|
||||||
*/
|
|
||||||
import "C"
|
|
1350
vendor/github.com/mattn/go-sqlite3/sqlite3_test.go
generated
vendored
1350
vendor/github.com/mattn/go-sqlite3/sqlite3_test.go
generated
vendored
File diff suppressed because it is too large
Load Diff
409
vendor/github.com/mattn/go-sqlite3/sqlite3_test/sqltest.go
generated
vendored
409
vendor/github.com/mattn/go-sqlite3/sqlite3_test/sqltest.go
generated
vendored
@ -1,409 +0,0 @@
|
|||||||
package sqlite3_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"fmt"
|
|
||||||
"math/rand"
|
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"sync"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Dialect int
|
|
||||||
|
|
||||||
const (
|
|
||||||
SQLITE Dialect = iota
|
|
||||||
POSTGRESQL
|
|
||||||
MYSQL
|
|
||||||
)
|
|
||||||
|
|
||||||
type DB struct {
|
|
||||||
*testing.T
|
|
||||||
*sql.DB
|
|
||||||
dialect Dialect
|
|
||||||
once sync.Once
|
|
||||||
}
|
|
||||||
|
|
||||||
var db *DB
|
|
||||||
|
|
||||||
// the following tables will be created and dropped during the test
|
|
||||||
var testTables = []string{"foo", "bar", "t", "bench"}
|
|
||||||
|
|
||||||
var tests = []testing.InternalTest{
|
|
||||||
{"TestBlobs", TestBlobs},
|
|
||||||
{"TestManyQueryRow", TestManyQueryRow},
|
|
||||||
{"TestTxQuery", TestTxQuery},
|
|
||||||
{"TestPreparedStmt", TestPreparedStmt},
|
|
||||||
}
|
|
||||||
|
|
||||||
var benchmarks = []testing.InternalBenchmark{
|
|
||||||
{"BenchmarkExec", BenchmarkExec},
|
|
||||||
{"BenchmarkQuery", BenchmarkQuery},
|
|
||||||
{"BenchmarkParams", BenchmarkParams},
|
|
||||||
{"BenchmarkStmt", BenchmarkStmt},
|
|
||||||
{"BenchmarkRows", BenchmarkRows},
|
|
||||||
{"BenchmarkStmtRows", BenchmarkStmtRows},
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunTests runs the SQL test suite
|
|
||||||
func RunTests(t *testing.T, d *sql.DB, dialect Dialect) {
|
|
||||||
db = &DB{t, d, dialect, sync.Once{}}
|
|
||||||
testing.RunTests(func(string, string) (bool, error) { return true, nil }, tests)
|
|
||||||
|
|
||||||
if !testing.Short() {
|
|
||||||
for _, b := range benchmarks {
|
|
||||||
fmt.Printf("%-20s", b.Name)
|
|
||||||
r := testing.Benchmark(b.F)
|
|
||||||
fmt.Printf("%10d %10.0f req/s\n", r.N, float64(r.N)/r.T.Seconds())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
db.tearDown()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *DB) mustExec(sql string, args ...interface{}) sql.Result {
|
|
||||||
res, err := db.Exec(sql, args...)
|
|
||||||
if err != nil {
|
|
||||||
db.Fatalf("Error running %q: %v", sql, err)
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *DB) tearDown() {
|
|
||||||
for _, tbl := range testTables {
|
|
||||||
switch db.dialect {
|
|
||||||
case SQLITE:
|
|
||||||
db.mustExec("drop table if exists " + tbl)
|
|
||||||
case MYSQL, POSTGRESQL:
|
|
||||||
db.mustExec("drop table if exists " + tbl)
|
|
||||||
default:
|
|
||||||
db.Fatal("unkown dialect")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// q replaces ? parameters if needed
|
|
||||||
func (db *DB) q(sql string) string {
|
|
||||||
switch db.dialect {
|
|
||||||
case POSTGRESQL: // repace with $1, $2, ..
|
|
||||||
qrx := regexp.MustCompile(`\?`)
|
|
||||||
n := 0
|
|
||||||
return qrx.ReplaceAllStringFunc(sql, func(string) string {
|
|
||||||
n++
|
|
||||||
return "$" + strconv.Itoa(n)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return sql
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *DB) blobType(size int) string {
|
|
||||||
switch db.dialect {
|
|
||||||
case SQLITE:
|
|
||||||
return fmt.Sprintf("blob[%d]", size)
|
|
||||||
case POSTGRESQL:
|
|
||||||
return "bytea"
|
|
||||||
case MYSQL:
|
|
||||||
return fmt.Sprintf("VARBINARY(%d)", size)
|
|
||||||
}
|
|
||||||
panic("unkown dialect")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *DB) serialPK() string {
|
|
||||||
switch db.dialect {
|
|
||||||
case SQLITE:
|
|
||||||
return "integer primary key autoincrement"
|
|
||||||
case POSTGRESQL:
|
|
||||||
return "serial primary key"
|
|
||||||
case MYSQL:
|
|
||||||
return "integer primary key auto_increment"
|
|
||||||
}
|
|
||||||
panic("unkown dialect")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *DB) now() string {
|
|
||||||
switch db.dialect {
|
|
||||||
case SQLITE:
|
|
||||||
return "datetime('now')"
|
|
||||||
case POSTGRESQL:
|
|
||||||
return "now()"
|
|
||||||
case MYSQL:
|
|
||||||
return "now()"
|
|
||||||
}
|
|
||||||
panic("unkown dialect")
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeBench() {
|
|
||||||
if _, err := db.Exec("create table bench (n varchar(32), i integer, d double, s varchar(32), t datetime)"); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
st, err := db.Prepare("insert into bench values (?, ?, ?, ?, ?)")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
defer st.Close()
|
|
||||||
for i := 0; i < 100; i++ {
|
|
||||||
if _, err = st.Exec(nil, i, float64(i), fmt.Sprintf("%d", i), time.Now()); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestResult(t *testing.T) {
|
|
||||||
db.tearDown()
|
|
||||||
db.mustExec("create temporary table test (id " + db.serialPK() + ", name varchar(10))")
|
|
||||||
|
|
||||||
for i := 1; i < 3; i++ {
|
|
||||||
r := db.mustExec(db.q("insert into test (name) values (?)"), fmt.Sprintf("row %d", i))
|
|
||||||
n, err := r.RowsAffected()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if n != 1 {
|
|
||||||
t.Errorf("got %v, want %v", n, 1)
|
|
||||||
}
|
|
||||||
n, err = r.LastInsertId()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if n != int64(i) {
|
|
||||||
t.Errorf("got %v, want %v", n, i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if _, err := db.Exec("error!"); err == nil {
|
|
||||||
t.Fatalf("expected error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBlobs(t *testing.T) {
|
|
||||||
db.tearDown()
|
|
||||||
var blob = []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
|
|
||||||
db.mustExec("create table foo (id integer primary key, bar " + db.blobType(16) + ")")
|
|
||||||
db.mustExec(db.q("insert into foo (id, bar) values(?,?)"), 0, blob)
|
|
||||||
|
|
||||||
want := fmt.Sprintf("%x", blob)
|
|
||||||
|
|
||||||
b := make([]byte, 16)
|
|
||||||
err := db.QueryRow(db.q("select bar from foo where id = ?"), 0).Scan(&b)
|
|
||||||
got := fmt.Sprintf("%x", b)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("[]byte scan: %v", err)
|
|
||||||
} else if got != want {
|
|
||||||
t.Errorf("for []byte, got %q; want %q", got, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = db.QueryRow(db.q("select bar from foo where id = ?"), 0).Scan(&got)
|
|
||||||
want = string(blob)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("string scan: %v", err)
|
|
||||||
} else if got != want {
|
|
||||||
t.Errorf("for string, got %q; want %q", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestManyQueryRow(t *testing.T) {
|
|
||||||
if testing.Short() {
|
|
||||||
t.Log("skipping in short mode")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
db.tearDown()
|
|
||||||
db.mustExec("create table foo (id integer primary key, name varchar(50))")
|
|
||||||
db.mustExec(db.q("insert into foo (id, name) values(?,?)"), 1, "bob")
|
|
||||||
var name string
|
|
||||||
for i := 0; i < 10000; i++ {
|
|
||||||
err := db.QueryRow(db.q("select name from foo where id = ?"), 1).Scan(&name)
|
|
||||||
if err != nil || name != "bob" {
|
|
||||||
t.Fatalf("on query %d: err=%v, name=%q", i, err, name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTxQuery(t *testing.T) {
|
|
||||||
db.tearDown()
|
|
||||||
tx, err := db.Begin()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer tx.Rollback()
|
|
||||||
|
|
||||||
_, err = tx.Exec("create table foo (id integer primary key, name varchar(50))")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = tx.Exec(db.q("insert into foo (id, name) values(?,?)"), 1, "bob")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
r, err := tx.Query(db.q("select name from foo where id = ?"), 1)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer r.Close()
|
|
||||||
|
|
||||||
if !r.Next() {
|
|
||||||
if r.Err() != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
t.Fatal("expected one rows")
|
|
||||||
}
|
|
||||||
|
|
||||||
var name string
|
|
||||||
err = r.Scan(&name)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPreparedStmt(t *testing.T) {
|
|
||||||
db.tearDown()
|
|
||||||
db.mustExec("CREATE TABLE t (count INT)")
|
|
||||||
sel, err := db.Prepare("SELECT count FROM t ORDER BY count DESC")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("prepare 1: %v", err)
|
|
||||||
}
|
|
||||||
ins, err := db.Prepare(db.q("INSERT INTO t (count) VALUES (?)"))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("prepare 2: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for n := 1; n <= 3; n++ {
|
|
||||||
if _, err := ins.Exec(n); err != nil {
|
|
||||||
t.Fatalf("insert(%d) = %v", n, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const nRuns = 10
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
for i := 0; i < nRuns; i++ {
|
|
||||||
wg.Add(1)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
for j := 0; j < 10; j++ {
|
|
||||||
count := 0
|
|
||||||
if err := sel.QueryRow().Scan(&count); err != nil && err != sql.ErrNoRows {
|
|
||||||
t.Errorf("Query: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if _, err := ins.Exec(rand.Intn(100)); err != nil {
|
|
||||||
t.Errorf("Insert: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Benchmarks need to use panic() since b.Error errors are lost when
|
|
||||||
// running via testing.Benchmark() I would like to run these via go
|
|
||||||
// test -bench but calling Benchmark() from a benchmark test
|
|
||||||
// currently hangs go.
|
|
||||||
|
|
||||||
func BenchmarkExec(b *testing.B) {
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
if _, err := db.Exec("select 1"); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkQuery(b *testing.B) {
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
var n sql.NullString
|
|
||||||
var i int
|
|
||||||
var f float64
|
|
||||||
var s string
|
|
||||||
// var t time.Time
|
|
||||||
if err := db.QueryRow("select null, 1, 1.1, 'foo'").Scan(&n, &i, &f, &s); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkParams(b *testing.B) {
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
var n sql.NullString
|
|
||||||
var i int
|
|
||||||
var f float64
|
|
||||||
var s string
|
|
||||||
// var t time.Time
|
|
||||||
if err := db.QueryRow("select ?, ?, ?, ?", nil, 1, 1.1, "foo").Scan(&n, &i, &f, &s); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkStmt(b *testing.B) {
|
|
||||||
st, err := db.Prepare("select ?, ?, ?, ?")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
defer st.Close()
|
|
||||||
|
|
||||||
for n := 0; n < b.N; n++ {
|
|
||||||
var n sql.NullString
|
|
||||||
var i int
|
|
||||||
var f float64
|
|
||||||
var s string
|
|
||||||
// var t time.Time
|
|
||||||
if err := st.QueryRow(nil, 1, 1.1, "foo").Scan(&n, &i, &f, &s); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkRows(b *testing.B) {
|
|
||||||
db.once.Do(makeBench)
|
|
||||||
|
|
||||||
for n := 0; n < b.N; n++ {
|
|
||||||
var n sql.NullString
|
|
||||||
var i int
|
|
||||||
var f float64
|
|
||||||
var s string
|
|
||||||
var t time.Time
|
|
||||||
r, err := db.Query("select * from bench")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
for r.Next() {
|
|
||||||
if err = r.Scan(&n, &i, &f, &s, &t); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err = r.Err(); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkStmtRows(b *testing.B) {
|
|
||||||
db.once.Do(makeBench)
|
|
||||||
|
|
||||||
st, err := db.Prepare("select * from bench")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
defer st.Close()
|
|
||||||
|
|
||||||
for n := 0; n < b.N; n++ {
|
|
||||||
var n sql.NullString
|
|
||||||
var i int
|
|
||||||
var f float64
|
|
||||||
var s string
|
|
||||||
var t time.Time
|
|
||||||
r, err := st.Query()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
for r.Next() {
|
|
||||||
if err = r.Scan(&n, &i, &f, &s, &t); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err = r.Err(); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
14
vendor/github.com/mattn/go-sqlite3/sqlite3_windows.go
generated
vendored
14
vendor/github.com/mattn/go-sqlite3/sqlite3_windows.go
generated
vendored
@ -1,14 +0,0 @@
|
|||||||
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
|
|
||||||
//
|
|
||||||
// Use of this source code is governed by an MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
// +build windows
|
|
||||||
|
|
||||||
package sqlite3
|
|
||||||
|
|
||||||
/*
|
|
||||||
#cgo CFLAGS: -I. -fno-stack-check -fno-stack-protector -mno-stack-arg-probe
|
|
||||||
#cgo windows,386 CFLAGS: -D_USE_32BIT_TIME_T
|
|
||||||
#cgo LDFLAGS: -lmingwex -lmingw32
|
|
||||||
*/
|
|
||||||
import "C"
|
|
546
vendor/github.com/mattn/go-sqlite3/sqlite3ext.h
generated
vendored
546
vendor/github.com/mattn/go-sqlite3/sqlite3ext.h
generated
vendored
@ -1,546 +0,0 @@
|
|||||||
/*
|
|
||||||
** 2006 June 7
|
|
||||||
**
|
|
||||||
** The author disclaims copyright to this source code. In place of
|
|
||||||
** a legal notice, here is a blessing:
|
|
||||||
**
|
|
||||||
** May you do good and not evil.
|
|
||||||
** May you find forgiveness for yourself and forgive others.
|
|
||||||
** May you share freely, never taking more than you give.
|
|
||||||
**
|
|
||||||
*************************************************************************
|
|
||||||
** This header file defines the SQLite interface for use by
|
|
||||||
** shared libraries that want to be imported as extensions into
|
|
||||||
** an SQLite instance. Shared libraries that intend to be loaded
|
|
||||||
** as extensions by SQLite should #include this file instead of
|
|
||||||
** sqlite3.h.
|
|
||||||
*/
|
|
||||||
#ifndef _SQLITE3EXT_H_
|
|
||||||
#define _SQLITE3EXT_H_
|
|
||||||
#include "sqlite3-binding.h"
|
|
||||||
|
|
||||||
typedef struct sqlite3_api_routines sqlite3_api_routines;
|
|
||||||
|
|
||||||
/*
|
|
||||||
** The following structure holds pointers to all of the SQLite API
|
|
||||||
** routines.
|
|
||||||
**
|
|
||||||
** WARNING: In order to maintain backwards compatibility, add new
|
|
||||||
** interfaces to the end of this structure only. If you insert new
|
|
||||||
** interfaces in the middle of this structure, then older different
|
|
||||||
** versions of SQLite will not be able to load each other's shared
|
|
||||||
** libraries!
|
|
||||||
*/
|
|
||||||
struct sqlite3_api_routines {
|
|
||||||
void * (*aggregate_context)(sqlite3_context*,int nBytes);
|
|
||||||
int (*aggregate_count)(sqlite3_context*);
|
|
||||||
int (*bind_blob)(sqlite3_stmt*,int,const void*,int n,void(*)(void*));
|
|
||||||
int (*bind_double)(sqlite3_stmt*,int,double);
|
|
||||||
int (*bind_int)(sqlite3_stmt*,int,int);
|
|
||||||
int (*bind_int64)(sqlite3_stmt*,int,sqlite_int64);
|
|
||||||
int (*bind_null)(sqlite3_stmt*,int);
|
|
||||||
int (*bind_parameter_count)(sqlite3_stmt*);
|
|
||||||
int (*bind_parameter_index)(sqlite3_stmt*,const char*zName);
|
|
||||||
const char * (*bind_parameter_name)(sqlite3_stmt*,int);
|
|
||||||
int (*bind_text)(sqlite3_stmt*,int,const char*,int n,void(*)(void*));
|
|
||||||
int (*bind_text16)(sqlite3_stmt*,int,const void*,int,void(*)(void*));
|
|
||||||
int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*);
|
|
||||||
int (*busy_handler)(sqlite3*,int(*)(void*,int),void*);
|
|
||||||
int (*busy_timeout)(sqlite3*,int ms);
|
|
||||||
int (*changes)(sqlite3*);
|
|
||||||
int (*close)(sqlite3*);
|
|
||||||
int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*,
|
|
||||||
int eTextRep,const char*));
|
|
||||||
int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*,
|
|
||||||
int eTextRep,const void*));
|
|
||||||
const void * (*column_blob)(sqlite3_stmt*,int iCol);
|
|
||||||
int (*column_bytes)(sqlite3_stmt*,int iCol);
|
|
||||||
int (*column_bytes16)(sqlite3_stmt*,int iCol);
|
|
||||||
int (*column_count)(sqlite3_stmt*pStmt);
|
|
||||||
const char * (*column_database_name)(sqlite3_stmt*,int);
|
|
||||||
const void * (*column_database_name16)(sqlite3_stmt*,int);
|
|
||||||
const char * (*column_decltype)(sqlite3_stmt*,int i);
|
|
||||||
const void * (*column_decltype16)(sqlite3_stmt*,int);
|
|
||||||
double (*column_double)(sqlite3_stmt*,int iCol);
|
|
||||||
int (*column_int)(sqlite3_stmt*,int iCol);
|
|
||||||
sqlite_int64 (*column_int64)(sqlite3_stmt*,int iCol);
|
|
||||||
const char * (*column_name)(sqlite3_stmt*,int);
|
|
||||||
const void * (*column_name16)(sqlite3_stmt*,int);
|
|
||||||
const char * (*column_origin_name)(sqlite3_stmt*,int);
|
|
||||||
const void * (*column_origin_name16)(sqlite3_stmt*,int);
|
|
||||||
const char * (*column_table_name)(sqlite3_stmt*,int);
|
|
||||||
const void * (*column_table_name16)(sqlite3_stmt*,int);
|
|
||||||
const unsigned char * (*column_text)(sqlite3_stmt*,int iCol);
|
|
||||||
const void * (*column_text16)(sqlite3_stmt*,int iCol);
|
|
||||||
int (*column_type)(sqlite3_stmt*,int iCol);
|
|
||||||
sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol);
|
|
||||||
void * (*commit_hook)(sqlite3*,int(*)(void*),void*);
|
|
||||||
int (*complete)(const char*sql);
|
|
||||||
int (*complete16)(const void*sql);
|
|
||||||
int (*create_collation)(sqlite3*,const char*,int,void*,
|
|
||||||
int(*)(void*,int,const void*,int,const void*));
|
|
||||||
int (*create_collation16)(sqlite3*,const void*,int,void*,
|
|
||||||
int(*)(void*,int,const void*,int,const void*));
|
|
||||||
int (*create_function)(sqlite3*,const char*,int,int,void*,
|
|
||||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
|
|
||||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
|
||||||
void (*xFinal)(sqlite3_context*));
|
|
||||||
int (*create_function16)(sqlite3*,const void*,int,int,void*,
|
|
||||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
|
|
||||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
|
||||||
void (*xFinal)(sqlite3_context*));
|
|
||||||
int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*);
|
|
||||||
int (*data_count)(sqlite3_stmt*pStmt);
|
|
||||||
sqlite3 * (*db_handle)(sqlite3_stmt*);
|
|
||||||
int (*declare_vtab)(sqlite3*,const char*);
|
|
||||||
int (*enable_shared_cache)(int);
|
|
||||||
int (*errcode)(sqlite3*db);
|
|
||||||
const char * (*errmsg)(sqlite3*);
|
|
||||||
const void * (*errmsg16)(sqlite3*);
|
|
||||||
int (*exec)(sqlite3*,const char*,sqlite3_callback,void*,char**);
|
|
||||||
int (*expired)(sqlite3_stmt*);
|
|
||||||
int (*finalize)(sqlite3_stmt*pStmt);
|
|
||||||
void (*free)(void*);
|
|
||||||
void (*free_table)(char**result);
|
|
||||||
int (*get_autocommit)(sqlite3*);
|
|
||||||
void * (*get_auxdata)(sqlite3_context*,int);
|
|
||||||
int (*get_table)(sqlite3*,const char*,char***,int*,int*,char**);
|
|
||||||
int (*global_recover)(void);
|
|
||||||
void (*interruptx)(sqlite3*);
|
|
||||||
sqlite_int64 (*last_insert_rowid)(sqlite3*);
|
|
||||||
const char * (*libversion)(void);
|
|
||||||
int (*libversion_number)(void);
|
|
||||||
void *(*malloc)(int);
|
|
||||||
char * (*mprintf)(const char*,...);
|
|
||||||
int (*open)(const char*,sqlite3**);
|
|
||||||
int (*open16)(const void*,sqlite3**);
|
|
||||||
int (*prepare)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
|
|
||||||
int (*prepare16)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
|
|
||||||
void * (*profile)(sqlite3*,void(*)(void*,const char*,sqlite_uint64),void*);
|
|
||||||
void (*progress_handler)(sqlite3*,int,int(*)(void*),void*);
|
|
||||||
void *(*realloc)(void*,int);
|
|
||||||
int (*reset)(sqlite3_stmt*pStmt);
|
|
||||||
void (*result_blob)(sqlite3_context*,const void*,int,void(*)(void*));
|
|
||||||
void (*result_double)(sqlite3_context*,double);
|
|
||||||
void (*result_error)(sqlite3_context*,const char*,int);
|
|
||||||
void (*result_error16)(sqlite3_context*,const void*,int);
|
|
||||||
void (*result_int)(sqlite3_context*,int);
|
|
||||||
void (*result_int64)(sqlite3_context*,sqlite_int64);
|
|
||||||
void (*result_null)(sqlite3_context*);
|
|
||||||
void (*result_text)(sqlite3_context*,const char*,int,void(*)(void*));
|
|
||||||
void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*));
|
|
||||||
void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*));
|
|
||||||
void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*));
|
|
||||||
void (*result_value)(sqlite3_context*,sqlite3_value*);
|
|
||||||
void * (*rollback_hook)(sqlite3*,void(*)(void*),void*);
|
|
||||||
int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,
|
|
||||||
const char*,const char*),void*);
|
|
||||||
void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*));
|
|
||||||
char * (*snprintf)(int,char*,const char*,...);
|
|
||||||
int (*step)(sqlite3_stmt*);
|
|
||||||
int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,
|
|
||||||
char const**,char const**,int*,int*,int*);
|
|
||||||
void (*thread_cleanup)(void);
|
|
||||||
int (*total_changes)(sqlite3*);
|
|
||||||
void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*);
|
|
||||||
int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*);
|
|
||||||
void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*,
|
|
||||||
sqlite_int64),void*);
|
|
||||||
void * (*user_data)(sqlite3_context*);
|
|
||||||
const void * (*value_blob)(sqlite3_value*);
|
|
||||||
int (*value_bytes)(sqlite3_value*);
|
|
||||||
int (*value_bytes16)(sqlite3_value*);
|
|
||||||
double (*value_double)(sqlite3_value*);
|
|
||||||
int (*value_int)(sqlite3_value*);
|
|
||||||
sqlite_int64 (*value_int64)(sqlite3_value*);
|
|
||||||
int (*value_numeric_type)(sqlite3_value*);
|
|
||||||
const unsigned char * (*value_text)(sqlite3_value*);
|
|
||||||
const void * (*value_text16)(sqlite3_value*);
|
|
||||||
const void * (*value_text16be)(sqlite3_value*);
|
|
||||||
const void * (*value_text16le)(sqlite3_value*);
|
|
||||||
int (*value_type)(sqlite3_value*);
|
|
||||||
char *(*vmprintf)(const char*,va_list);
|
|
||||||
/* Added ??? */
|
|
||||||
int (*overload_function)(sqlite3*, const char *zFuncName, int nArg);
|
|
||||||
/* Added by 3.3.13 */
|
|
||||||
int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
|
|
||||||
int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
|
|
||||||
int (*clear_bindings)(sqlite3_stmt*);
|
|
||||||
/* Added by 3.4.1 */
|
|
||||||
int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*,
|
|
||||||
void (*xDestroy)(void *));
|
|
||||||
/* Added by 3.5.0 */
|
|
||||||
int (*bind_zeroblob)(sqlite3_stmt*,int,int);
|
|
||||||
int (*blob_bytes)(sqlite3_blob*);
|
|
||||||
int (*blob_close)(sqlite3_blob*);
|
|
||||||
int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64,
|
|
||||||
int,sqlite3_blob**);
|
|
||||||
int (*blob_read)(sqlite3_blob*,void*,int,int);
|
|
||||||
int (*blob_write)(sqlite3_blob*,const void*,int,int);
|
|
||||||
int (*create_collation_v2)(sqlite3*,const char*,int,void*,
|
|
||||||
int(*)(void*,int,const void*,int,const void*),
|
|
||||||
void(*)(void*));
|
|
||||||
int (*file_control)(sqlite3*,const char*,int,void*);
|
|
||||||
sqlite3_int64 (*memory_highwater)(int);
|
|
||||||
sqlite3_int64 (*memory_used)(void);
|
|
||||||
sqlite3_mutex *(*mutex_alloc)(int);
|
|
||||||
void (*mutex_enter)(sqlite3_mutex*);
|
|
||||||
void (*mutex_free)(sqlite3_mutex*);
|
|
||||||
void (*mutex_leave)(sqlite3_mutex*);
|
|
||||||
int (*mutex_try)(sqlite3_mutex*);
|
|
||||||
int (*open_v2)(const char*,sqlite3**,int,const char*);
|
|
||||||
int (*release_memory)(int);
|
|
||||||
void (*result_error_nomem)(sqlite3_context*);
|
|
||||||
void (*result_error_toobig)(sqlite3_context*);
|
|
||||||
int (*sleep)(int);
|
|
||||||
void (*soft_heap_limit)(int);
|
|
||||||
sqlite3_vfs *(*vfs_find)(const char*);
|
|
||||||
int (*vfs_register)(sqlite3_vfs*,int);
|
|
||||||
int (*vfs_unregister)(sqlite3_vfs*);
|
|
||||||
int (*xthreadsafe)(void);
|
|
||||||
void (*result_zeroblob)(sqlite3_context*,int);
|
|
||||||
void (*result_error_code)(sqlite3_context*,int);
|
|
||||||
int (*test_control)(int, ...);
|
|
||||||
void (*randomness)(int,void*);
|
|
||||||
sqlite3 *(*context_db_handle)(sqlite3_context*);
|
|
||||||
int (*extended_result_codes)(sqlite3*,int);
|
|
||||||
int (*limit)(sqlite3*,int,int);
|
|
||||||
sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*);
|
|
||||||
const char *(*sql)(sqlite3_stmt*);
|
|
||||||
int (*status)(int,int*,int*,int);
|
|
||||||
int (*backup_finish)(sqlite3_backup*);
|
|
||||||
sqlite3_backup *(*backup_init)(sqlite3*,const char*,sqlite3*,const char*);
|
|
||||||
int (*backup_pagecount)(sqlite3_backup*);
|
|
||||||
int (*backup_remaining)(sqlite3_backup*);
|
|
||||||
int (*backup_step)(sqlite3_backup*,int);
|
|
||||||
const char *(*compileoption_get)(int);
|
|
||||||
int (*compileoption_used)(const char*);
|
|
||||||
int (*create_function_v2)(sqlite3*,const char*,int,int,void*,
|
|
||||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
|
|
||||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
|
||||||
void (*xFinal)(sqlite3_context*),
|
|
||||||
void(*xDestroy)(void*));
|
|
||||||
int (*db_config)(sqlite3*,int,...);
|
|
||||||
sqlite3_mutex *(*db_mutex)(sqlite3*);
|
|
||||||
int (*db_status)(sqlite3*,int,int*,int*,int);
|
|
||||||
int (*extended_errcode)(sqlite3*);
|
|
||||||
void (*log)(int,const char*,...);
|
|
||||||
sqlite3_int64 (*soft_heap_limit64)(sqlite3_int64);
|
|
||||||
const char *(*sourceid)(void);
|
|
||||||
int (*stmt_status)(sqlite3_stmt*,int,int);
|
|
||||||
int (*strnicmp)(const char*,const char*,int);
|
|
||||||
int (*unlock_notify)(sqlite3*,void(*)(void**,int),void*);
|
|
||||||
int (*wal_autocheckpoint)(sqlite3*,int);
|
|
||||||
int (*wal_checkpoint)(sqlite3*,const char*);
|
|
||||||
void *(*wal_hook)(sqlite3*,int(*)(void*,sqlite3*,const char*,int),void*);
|
|
||||||
int (*blob_reopen)(sqlite3_blob*,sqlite3_int64);
|
|
||||||
int (*vtab_config)(sqlite3*,int op,...);
|
|
||||||
int (*vtab_on_conflict)(sqlite3*);
|
|
||||||
/* Version 3.7.16 and later */
|
|
||||||
int (*close_v2)(sqlite3*);
|
|
||||||
const char *(*db_filename)(sqlite3*,const char*);
|
|
||||||
int (*db_readonly)(sqlite3*,const char*);
|
|
||||||
int (*db_release_memory)(sqlite3*);
|
|
||||||
const char *(*errstr)(int);
|
|
||||||
int (*stmt_busy)(sqlite3_stmt*);
|
|
||||||
int (*stmt_readonly)(sqlite3_stmt*);
|
|
||||||
int (*stricmp)(const char*,const char*);
|
|
||||||
int (*uri_boolean)(const char*,const char*,int);
|
|
||||||
sqlite3_int64 (*uri_int64)(const char*,const char*,sqlite3_int64);
|
|
||||||
const char *(*uri_parameter)(const char*,const char*);
|
|
||||||
char *(*vsnprintf)(int,char*,const char*,va_list);
|
|
||||||
int (*wal_checkpoint_v2)(sqlite3*,const char*,int,int*,int*);
|
|
||||||
/* Version 3.8.7 and later */
|
|
||||||
int (*auto_extension)(void(*)(void));
|
|
||||||
int (*bind_blob64)(sqlite3_stmt*,int,const void*,sqlite3_uint64,
|
|
||||||
void(*)(void*));
|
|
||||||
int (*bind_text64)(sqlite3_stmt*,int,const char*,sqlite3_uint64,
|
|
||||||
void(*)(void*),unsigned char);
|
|
||||||
int (*cancel_auto_extension)(void(*)(void));
|
|
||||||
int (*load_extension)(sqlite3*,const char*,const char*,char**);
|
|
||||||
void *(*malloc64)(sqlite3_uint64);
|
|
||||||
sqlite3_uint64 (*msize)(void*);
|
|
||||||
void *(*realloc64)(void*,sqlite3_uint64);
|
|
||||||
void (*reset_auto_extension)(void);
|
|
||||||
void (*result_blob64)(sqlite3_context*,const void*,sqlite3_uint64,
|
|
||||||
void(*)(void*));
|
|
||||||
void (*result_text64)(sqlite3_context*,const char*,sqlite3_uint64,
|
|
||||||
void(*)(void*), unsigned char);
|
|
||||||
int (*strglob)(const char*,const char*);
|
|
||||||
/* Version 3.8.11 and later */
|
|
||||||
sqlite3_value *(*value_dup)(const sqlite3_value*);
|
|
||||||
void (*value_free)(sqlite3_value*);
|
|
||||||
int (*result_zeroblob64)(sqlite3_context*,sqlite3_uint64);
|
|
||||||
int (*bind_zeroblob64)(sqlite3_stmt*, int, sqlite3_uint64);
|
|
||||||
/* Version 3.9.0 and later */
|
|
||||||
unsigned int (*value_subtype)(sqlite3_value*);
|
|
||||||
void (*result_subtype)(sqlite3_context*,unsigned int);
|
|
||||||
/* Version 3.10.0 and later */
|
|
||||||
int (*status64)(int,sqlite3_int64*,sqlite3_int64*,int);
|
|
||||||
int (*strlike)(const char*,const char*,unsigned int);
|
|
||||||
int (*db_cacheflush)(sqlite3*);
|
|
||||||
/* Version 3.12.0 and later */
|
|
||||||
int (*system_errno)(sqlite3*);
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
** The following macros redefine the API routines so that they are
|
|
||||||
** redirected through the global sqlite3_api structure.
|
|
||||||
**
|
|
||||||
** This header file is also used by the loadext.c source file
|
|
||||||
** (part of the main SQLite library - not an extension) so that
|
|
||||||
** it can get access to the sqlite3_api_routines structure
|
|
||||||
** definition. But the main library does not want to redefine
|
|
||||||
** the API. So the redefinition macros are only valid if the
|
|
||||||
** SQLITE_CORE macros is undefined.
|
|
||||||
*/
|
|
||||||
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
|
||||||
#define sqlite3_aggregate_context sqlite3_api->aggregate_context
|
|
||||||
#ifndef SQLITE_OMIT_DEPRECATED
|
|
||||||
#define sqlite3_aggregate_count sqlite3_api->aggregate_count
|
|
||||||
#endif
|
|
||||||
#define sqlite3_bind_blob sqlite3_api->bind_blob
|
|
||||||
#define sqlite3_bind_double sqlite3_api->bind_double
|
|
||||||
#define sqlite3_bind_int sqlite3_api->bind_int
|
|
||||||
#define sqlite3_bind_int64 sqlite3_api->bind_int64
|
|
||||||
#define sqlite3_bind_null sqlite3_api->bind_null
|
|
||||||
#define sqlite3_bind_parameter_count sqlite3_api->bind_parameter_count
|
|
||||||
#define sqlite3_bind_parameter_index sqlite3_api->bind_parameter_index
|
|
||||||
#define sqlite3_bind_parameter_name sqlite3_api->bind_parameter_name
|
|
||||||
#define sqlite3_bind_text sqlite3_api->bind_text
|
|
||||||
#define sqlite3_bind_text16 sqlite3_api->bind_text16
|
|
||||||
#define sqlite3_bind_value sqlite3_api->bind_value
|
|
||||||
#define sqlite3_busy_handler sqlite3_api->busy_handler
|
|
||||||
#define sqlite3_busy_timeout sqlite3_api->busy_timeout
|
|
||||||
#define sqlite3_changes sqlite3_api->changes
|
|
||||||
#define sqlite3_close sqlite3_api->close
|
|
||||||
#define sqlite3_collation_needed sqlite3_api->collation_needed
|
|
||||||
#define sqlite3_collation_needed16 sqlite3_api->collation_needed16
|
|
||||||
#define sqlite3_column_blob sqlite3_api->column_blob
|
|
||||||
#define sqlite3_column_bytes sqlite3_api->column_bytes
|
|
||||||
#define sqlite3_column_bytes16 sqlite3_api->column_bytes16
|
|
||||||
#define sqlite3_column_count sqlite3_api->column_count
|
|
||||||
#define sqlite3_column_database_name sqlite3_api->column_database_name
|
|
||||||
#define sqlite3_column_database_name16 sqlite3_api->column_database_name16
|
|
||||||
#define sqlite3_column_decltype sqlite3_api->column_decltype
|
|
||||||
#define sqlite3_column_decltype16 sqlite3_api->column_decltype16
|
|
||||||
#define sqlite3_column_double sqlite3_api->column_double
|
|
||||||
#define sqlite3_column_int sqlite3_api->column_int
|
|
||||||
#define sqlite3_column_int64 sqlite3_api->column_int64
|
|
||||||
#define sqlite3_column_name sqlite3_api->column_name
|
|
||||||
#define sqlite3_column_name16 sqlite3_api->column_name16
|
|
||||||
#define sqlite3_column_origin_name sqlite3_api->column_origin_name
|
|
||||||
#define sqlite3_column_origin_name16 sqlite3_api->column_origin_name16
|
|
||||||
#define sqlite3_column_table_name sqlite3_api->column_table_name
|
|
||||||
#define sqlite3_column_table_name16 sqlite3_api->column_table_name16
|
|
||||||
#define sqlite3_column_text sqlite3_api->column_text
|
|
||||||
#define sqlite3_column_text16 sqlite3_api->column_text16
|
|
||||||
#define sqlite3_column_type sqlite3_api->column_type
|
|
||||||
#define sqlite3_column_value sqlite3_api->column_value
|
|
||||||
#define sqlite3_commit_hook sqlite3_api->commit_hook
|
|
||||||
#define sqlite3_complete sqlite3_api->complete
|
|
||||||
#define sqlite3_complete16 sqlite3_api->complete16
|
|
||||||
#define sqlite3_create_collation sqlite3_api->create_collation
|
|
||||||
#define sqlite3_create_collation16 sqlite3_api->create_collation16
|
|
||||||
#define sqlite3_create_function sqlite3_api->create_function
|
|
||||||
#define sqlite3_create_function16 sqlite3_api->create_function16
|
|
||||||
#define sqlite3_create_module sqlite3_api->create_module
|
|
||||||
#define sqlite3_create_module_v2 sqlite3_api->create_module_v2
|
|
||||||
#define sqlite3_data_count sqlite3_api->data_count
|
|
||||||
#define sqlite3_db_handle sqlite3_api->db_handle
|
|
||||||
#define sqlite3_declare_vtab sqlite3_api->declare_vtab
|
|
||||||
#define sqlite3_enable_shared_cache sqlite3_api->enable_shared_cache
|
|
||||||
#define sqlite3_errcode sqlite3_api->errcode
|
|
||||||
#define sqlite3_errmsg sqlite3_api->errmsg
|
|
||||||
#define sqlite3_errmsg16 sqlite3_api->errmsg16
|
|
||||||
#define sqlite3_exec sqlite3_api->exec
|
|
||||||
#ifndef SQLITE_OMIT_DEPRECATED
|
|
||||||
#define sqlite3_expired sqlite3_api->expired
|
|
||||||
#endif
|
|
||||||
#define sqlite3_finalize sqlite3_api->finalize
|
|
||||||
#define sqlite3_free sqlite3_api->free
|
|
||||||
#define sqlite3_free_table sqlite3_api->free_table
|
|
||||||
#define sqlite3_get_autocommit sqlite3_api->get_autocommit
|
|
||||||
#define sqlite3_get_auxdata sqlite3_api->get_auxdata
|
|
||||||
#define sqlite3_get_table sqlite3_api->get_table
|
|
||||||
#ifndef SQLITE_OMIT_DEPRECATED
|
|
||||||
#define sqlite3_global_recover sqlite3_api->global_recover
|
|
||||||
#endif
|
|
||||||
#define sqlite3_interrupt sqlite3_api->interruptx
|
|
||||||
#define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid
|
|
||||||
#define sqlite3_libversion sqlite3_api->libversion
|
|
||||||
#define sqlite3_libversion_number sqlite3_api->libversion_number
|
|
||||||
#define sqlite3_malloc sqlite3_api->malloc
|
|
||||||
#define sqlite3_mprintf sqlite3_api->mprintf
|
|
||||||
#define sqlite3_open sqlite3_api->open
|
|
||||||
#define sqlite3_open16 sqlite3_api->open16
|
|
||||||
#define sqlite3_prepare sqlite3_api->prepare
|
|
||||||
#define sqlite3_prepare16 sqlite3_api->prepare16
|
|
||||||
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
|
|
||||||
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
|
|
||||||
#define sqlite3_profile sqlite3_api->profile
|
|
||||||
#define sqlite3_progress_handler sqlite3_api->progress_handler
|
|
||||||
#define sqlite3_realloc sqlite3_api->realloc
|
|
||||||
#define sqlite3_reset sqlite3_api->reset
|
|
||||||
#define sqlite3_result_blob sqlite3_api->result_blob
|
|
||||||
#define sqlite3_result_double sqlite3_api->result_double
|
|
||||||
#define sqlite3_result_error sqlite3_api->result_error
|
|
||||||
#define sqlite3_result_error16 sqlite3_api->result_error16
|
|
||||||
#define sqlite3_result_int sqlite3_api->result_int
|
|
||||||
#define sqlite3_result_int64 sqlite3_api->result_int64
|
|
||||||
#define sqlite3_result_null sqlite3_api->result_null
|
|
||||||
#define sqlite3_result_text sqlite3_api->result_text
|
|
||||||
#define sqlite3_result_text16 sqlite3_api->result_text16
|
|
||||||
#define sqlite3_result_text16be sqlite3_api->result_text16be
|
|
||||||
#define sqlite3_result_text16le sqlite3_api->result_text16le
|
|
||||||
#define sqlite3_result_value sqlite3_api->result_value
|
|
||||||
#define sqlite3_rollback_hook sqlite3_api->rollback_hook
|
|
||||||
#define sqlite3_set_authorizer sqlite3_api->set_authorizer
|
|
||||||
#define sqlite3_set_auxdata sqlite3_api->set_auxdata
|
|
||||||
#define sqlite3_snprintf sqlite3_api->snprintf
|
|
||||||
#define sqlite3_step sqlite3_api->step
|
|
||||||
#define sqlite3_table_column_metadata sqlite3_api->table_column_metadata
|
|
||||||
#define sqlite3_thread_cleanup sqlite3_api->thread_cleanup
|
|
||||||
#define sqlite3_total_changes sqlite3_api->total_changes
|
|
||||||
#define sqlite3_trace sqlite3_api->trace
|
|
||||||
#ifndef SQLITE_OMIT_DEPRECATED
|
|
||||||
#define sqlite3_transfer_bindings sqlite3_api->transfer_bindings
|
|
||||||
#endif
|
|
||||||
#define sqlite3_update_hook sqlite3_api->update_hook
|
|
||||||
#define sqlite3_user_data sqlite3_api->user_data
|
|
||||||
#define sqlite3_value_blob sqlite3_api->value_blob
|
|
||||||
#define sqlite3_value_bytes sqlite3_api->value_bytes
|
|
||||||
#define sqlite3_value_bytes16 sqlite3_api->value_bytes16
|
|
||||||
#define sqlite3_value_double sqlite3_api->value_double
|
|
||||||
#define sqlite3_value_int sqlite3_api->value_int
|
|
||||||
#define sqlite3_value_int64 sqlite3_api->value_int64
|
|
||||||
#define sqlite3_value_numeric_type sqlite3_api->value_numeric_type
|
|
||||||
#define sqlite3_value_text sqlite3_api->value_text
|
|
||||||
#define sqlite3_value_text16 sqlite3_api->value_text16
|
|
||||||
#define sqlite3_value_text16be sqlite3_api->value_text16be
|
|
||||||
#define sqlite3_value_text16le sqlite3_api->value_text16le
|
|
||||||
#define sqlite3_value_type sqlite3_api->value_type
|
|
||||||
#define sqlite3_vmprintf sqlite3_api->vmprintf
|
|
||||||
#define sqlite3_vsnprintf sqlite3_api->vsnprintf
|
|
||||||
#define sqlite3_overload_function sqlite3_api->overload_function
|
|
||||||
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
|
|
||||||
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
|
|
||||||
#define sqlite3_clear_bindings sqlite3_api->clear_bindings
|
|
||||||
#define sqlite3_bind_zeroblob sqlite3_api->bind_zeroblob
|
|
||||||
#define sqlite3_blob_bytes sqlite3_api->blob_bytes
|
|
||||||
#define sqlite3_blob_close sqlite3_api->blob_close
|
|
||||||
#define sqlite3_blob_open sqlite3_api->blob_open
|
|
||||||
#define sqlite3_blob_read sqlite3_api->blob_read
|
|
||||||
#define sqlite3_blob_write sqlite3_api->blob_write
|
|
||||||
#define sqlite3_create_collation_v2 sqlite3_api->create_collation_v2
|
|
||||||
#define sqlite3_file_control sqlite3_api->file_control
|
|
||||||
#define sqlite3_memory_highwater sqlite3_api->memory_highwater
|
|
||||||
#define sqlite3_memory_used sqlite3_api->memory_used
|
|
||||||
#define sqlite3_mutex_alloc sqlite3_api->mutex_alloc
|
|
||||||
#define sqlite3_mutex_enter sqlite3_api->mutex_enter
|
|
||||||
#define sqlite3_mutex_free sqlite3_api->mutex_free
|
|
||||||
#define sqlite3_mutex_leave sqlite3_api->mutex_leave
|
|
||||||
#define sqlite3_mutex_try sqlite3_api->mutex_try
|
|
||||||
#define sqlite3_open_v2 sqlite3_api->open_v2
|
|
||||||
#define sqlite3_release_memory sqlite3_api->release_memory
|
|
||||||
#define sqlite3_result_error_nomem sqlite3_api->result_error_nomem
|
|
||||||
#define sqlite3_result_error_toobig sqlite3_api->result_error_toobig
|
|
||||||
#define sqlite3_sleep sqlite3_api->sleep
|
|
||||||
#define sqlite3_soft_heap_limit sqlite3_api->soft_heap_limit
|
|
||||||
#define sqlite3_vfs_find sqlite3_api->vfs_find
|
|
||||||
#define sqlite3_vfs_register sqlite3_api->vfs_register
|
|
||||||
#define sqlite3_vfs_unregister sqlite3_api->vfs_unregister
|
|
||||||
#define sqlite3_threadsafe sqlite3_api->xthreadsafe
|
|
||||||
#define sqlite3_result_zeroblob sqlite3_api->result_zeroblob
|
|
||||||
#define sqlite3_result_error_code sqlite3_api->result_error_code
|
|
||||||
#define sqlite3_test_control sqlite3_api->test_control
|
|
||||||
#define sqlite3_randomness sqlite3_api->randomness
|
|
||||||
#define sqlite3_context_db_handle sqlite3_api->context_db_handle
|
|
||||||
#define sqlite3_extended_result_codes sqlite3_api->extended_result_codes
|
|
||||||
#define sqlite3_limit sqlite3_api->limit
|
|
||||||
#define sqlite3_next_stmt sqlite3_api->next_stmt
|
|
||||||
#define sqlite3_sql sqlite3_api->sql
|
|
||||||
#define sqlite3_status sqlite3_api->status
|
|
||||||
#define sqlite3_backup_finish sqlite3_api->backup_finish
|
|
||||||
#define sqlite3_backup_init sqlite3_api->backup_init
|
|
||||||
#define sqlite3_backup_pagecount sqlite3_api->backup_pagecount
|
|
||||||
#define sqlite3_backup_remaining sqlite3_api->backup_remaining
|
|
||||||
#define sqlite3_backup_step sqlite3_api->backup_step
|
|
||||||
#define sqlite3_compileoption_get sqlite3_api->compileoption_get
|
|
||||||
#define sqlite3_compileoption_used sqlite3_api->compileoption_used
|
|
||||||
#define sqlite3_create_function_v2 sqlite3_api->create_function_v2
|
|
||||||
#define sqlite3_db_config sqlite3_api->db_config
|
|
||||||
#define sqlite3_db_mutex sqlite3_api->db_mutex
|
|
||||||
#define sqlite3_db_status sqlite3_api->db_status
|
|
||||||
#define sqlite3_extended_errcode sqlite3_api->extended_errcode
|
|
||||||
#define sqlite3_log sqlite3_api->log
|
|
||||||
#define sqlite3_soft_heap_limit64 sqlite3_api->soft_heap_limit64
|
|
||||||
#define sqlite3_sourceid sqlite3_api->sourceid
|
|
||||||
#define sqlite3_stmt_status sqlite3_api->stmt_status
|
|
||||||
#define sqlite3_strnicmp sqlite3_api->strnicmp
|
|
||||||
#define sqlite3_unlock_notify sqlite3_api->unlock_notify
|
|
||||||
#define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint
|
|
||||||
#define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint
|
|
||||||
#define sqlite3_wal_hook sqlite3_api->wal_hook
|
|
||||||
#define sqlite3_blob_reopen sqlite3_api->blob_reopen
|
|
||||||
#define sqlite3_vtab_config sqlite3_api->vtab_config
|
|
||||||
#define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict
|
|
||||||
/* Version 3.7.16 and later */
|
|
||||||
#define sqlite3_close_v2 sqlite3_api->close_v2
|
|
||||||
#define sqlite3_db_filename sqlite3_api->db_filename
|
|
||||||
#define sqlite3_db_readonly sqlite3_api->db_readonly
|
|
||||||
#define sqlite3_db_release_memory sqlite3_api->db_release_memory
|
|
||||||
#define sqlite3_errstr sqlite3_api->errstr
|
|
||||||
#define sqlite3_stmt_busy sqlite3_api->stmt_busy
|
|
||||||
#define sqlite3_stmt_readonly sqlite3_api->stmt_readonly
|
|
||||||
#define sqlite3_stricmp sqlite3_api->stricmp
|
|
||||||
#define sqlite3_uri_boolean sqlite3_api->uri_boolean
|
|
||||||
#define sqlite3_uri_int64 sqlite3_api->uri_int64
|
|
||||||
#define sqlite3_uri_parameter sqlite3_api->uri_parameter
|
|
||||||
#define sqlite3_uri_vsnprintf sqlite3_api->vsnprintf
|
|
||||||
#define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2
|
|
||||||
/* Version 3.8.7 and later */
|
|
||||||
#define sqlite3_auto_extension sqlite3_api->auto_extension
|
|
||||||
#define sqlite3_bind_blob64 sqlite3_api->bind_blob64
|
|
||||||
#define sqlite3_bind_text64 sqlite3_api->bind_text64
|
|
||||||
#define sqlite3_cancel_auto_extension sqlite3_api->cancel_auto_extension
|
|
||||||
#define sqlite3_load_extension sqlite3_api->load_extension
|
|
||||||
#define sqlite3_malloc64 sqlite3_api->malloc64
|
|
||||||
#define sqlite3_msize sqlite3_api->msize
|
|
||||||
#define sqlite3_realloc64 sqlite3_api->realloc64
|
|
||||||
#define sqlite3_reset_auto_extension sqlite3_api->reset_auto_extension
|
|
||||||
#define sqlite3_result_blob64 sqlite3_api->result_blob64
|
|
||||||
#define sqlite3_result_text64 sqlite3_api->result_text64
|
|
||||||
#define sqlite3_strglob sqlite3_api->strglob
|
|
||||||
/* Version 3.8.11 and later */
|
|
||||||
#define sqlite3_value_dup sqlite3_api->value_dup
|
|
||||||
#define sqlite3_value_free sqlite3_api->value_free
|
|
||||||
#define sqlite3_result_zeroblob64 sqlite3_api->result_zeroblob64
|
|
||||||
#define sqlite3_bind_zeroblob64 sqlite3_api->bind_zeroblob64
|
|
||||||
/* Version 3.9.0 and later */
|
|
||||||
#define sqlite3_value_subtype sqlite3_api->value_subtype
|
|
||||||
#define sqlite3_result_subtype sqlite3_api->result_subtype
|
|
||||||
/* Version 3.10.0 and later */
|
|
||||||
#define sqlite3_status64 sqlite3_api->status64
|
|
||||||
#define sqlite3_strlike sqlite3_api->strlike
|
|
||||||
#define sqlite3_db_cacheflush sqlite3_api->db_cacheflush
|
|
||||||
/* Version 3.12.0 and later */
|
|
||||||
#define sqlite3_system_errno sqlite3_api->system_errno
|
|
||||||
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
|
|
||||||
|
|
||||||
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
|
||||||
/* This case when the file really is being compiled as a loadable
|
|
||||||
** extension */
|
|
||||||
# define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0;
|
|
||||||
# define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v;
|
|
||||||
# define SQLITE_EXTENSION_INIT3 \
|
|
||||||
extern const sqlite3_api_routines *sqlite3_api;
|
|
||||||
#else
|
|
||||||
/* This case when the file is being statically linked into the
|
|
||||||
** application */
|
|
||||||
# define SQLITE_EXTENSION_INIT1 /*no-op*/
|
|
||||||
# define SQLITE_EXTENSION_INIT2(v) (void)v; /* unused parameter */
|
|
||||||
# define SQLITE_EXTENSION_INIT3 /*no-op*/
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* _SQLITE3EXT_H_ */
|
|
10
vendor/github.com/pborman/uuid/CONTRIBUTING.md
generated
vendored
Normal file
10
vendor/github.com/pborman/uuid/CONTRIBUTING.md
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# How to contribute
|
||||||
|
|
||||||
|
We definitely welcome patches and contribution to this project!
|
||||||
|
|
||||||
|
### Legal requirements
|
||||||
|
|
||||||
|
In order to protect both you and ourselves, you will need to sign the
|
||||||
|
[Contributor License Agreement](https://cla.developers.google.com/clas).
|
||||||
|
|
||||||
|
You may have already signed it for other Google projects.
|
1
vendor/github.com/pborman/uuid/CONTRIBUTORS
generated
vendored
Normal file
1
vendor/github.com/pborman/uuid/CONTRIBUTORS
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
Paul Borman <borman@google.com>
|
27
vendor/github.com/pborman/uuid/LICENSE
generated
vendored
Normal file
27
vendor/github.com/pborman/uuid/LICENSE
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
Copyright (c) 2009,2014 Google Inc. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
13
vendor/github.com/pborman/uuid/README.md
generated
vendored
Normal file
13
vendor/github.com/pborman/uuid/README.md
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
This project was automatically exported from code.google.com/p/go-uuid
|
||||||
|
|
||||||
|
# uuid ![build status](https://travis-ci.org/pborman/uuid.svg?branch=master)
|
||||||
|
The uuid package generates and inspects UUIDs based on [RFC 412](http://tools.ietf.org/html/rfc4122) and DCE 1.1: Authentication and Security Services.
|
||||||
|
|
||||||
|
###### Install
|
||||||
|
`go get github.com/pborman/uuid`
|
||||||
|
|
||||||
|
###### Documentation
|
||||||
|
[![GoDoc](https://godoc.org/github.com/pborman/uuid?status.svg)](http://godoc.org/github.com/pborman/uuid)
|
||||||
|
|
||||||
|
Full `go doc` style documentation for the package can be viewed online without installing this package by using the GoDoc site here:
|
||||||
|
http://godoc.org/github.com/pborman/uuid
|
84
vendor/github.com/pborman/uuid/dce.go
generated
vendored
Executable file
84
vendor/github.com/pborman/uuid/dce.go
generated
vendored
Executable file
@ -0,0 +1,84 @@
|
|||||||
|
// Copyright 2011 Google Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package uuid
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Domain represents a Version 2 domain
|
||||||
|
type Domain byte
|
||||||
|
|
||||||
|
// Domain constants for DCE Security (Version 2) UUIDs.
|
||||||
|
const (
|
||||||
|
Person = Domain(0)
|
||||||
|
Group = Domain(1)
|
||||||
|
Org = Domain(2)
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewDCESecurity returns a DCE Security (Version 2) UUID.
|
||||||
|
//
|
||||||
|
// The domain should be one of Person, Group or Org.
|
||||||
|
// On a POSIX system the id should be the users UID for the Person
|
||||||
|
// domain and the users GID for the Group. The meaning of id for
|
||||||
|
// the domain Org or on non-POSIX systems is site defined.
|
||||||
|
//
|
||||||
|
// For a given domain/id pair the same token may be returned for up to
|
||||||
|
// 7 minutes and 10 seconds.
|
||||||
|
func NewDCESecurity(domain Domain, id uint32) UUID {
|
||||||
|
uuid := NewUUID()
|
||||||
|
if uuid != nil {
|
||||||
|
uuid[6] = (uuid[6] & 0x0f) | 0x20 // Version 2
|
||||||
|
uuid[9] = byte(domain)
|
||||||
|
binary.BigEndian.PutUint32(uuid[0:], id)
|
||||||
|
}
|
||||||
|
return uuid
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDCEPerson returns a DCE Security (Version 2) UUID in the person
|
||||||
|
// domain with the id returned by os.Getuid.
|
||||||
|
//
|
||||||
|
// NewDCEPerson(Person, uint32(os.Getuid()))
|
||||||
|
func NewDCEPerson() UUID {
|
||||||
|
return NewDCESecurity(Person, uint32(os.Getuid()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDCEGroup returns a DCE Security (Version 2) UUID in the group
|
||||||
|
// domain with the id returned by os.Getgid.
|
||||||
|
//
|
||||||
|
// NewDCEGroup(Group, uint32(os.Getgid()))
|
||||||
|
func NewDCEGroup() UUID {
|
||||||
|
return NewDCESecurity(Group, uint32(os.Getgid()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Domain returns the domain for a Version 2 UUID or false.
|
||||||
|
func (uuid UUID) Domain() (Domain, bool) {
|
||||||
|
if v, _ := uuid.Version(); v != 2 {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
return Domain(uuid[9]), true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Id returns the id for a Version 2 UUID or false.
|
||||||
|
func (uuid UUID) Id() (uint32, bool) {
|
||||||
|
if v, _ := uuid.Version(); v != 2 {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
return binary.BigEndian.Uint32(uuid[0:4]), true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Domain) String() string {
|
||||||
|
switch d {
|
||||||
|
case Person:
|
||||||
|
return "Person"
|
||||||
|
case Group:
|
||||||
|
return "Group"
|
||||||
|
case Org:
|
||||||
|
return "Org"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("Domain%d", int(d))
|
||||||
|
}
|
8
vendor/github.com/pborman/uuid/doc.go
generated
vendored
Executable file
8
vendor/github.com/pborman/uuid/doc.go
generated
vendored
Executable file
@ -0,0 +1,8 @@
|
|||||||
|
// Copyright 2011 Google Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// The uuid package generates and inspects UUIDs.
|
||||||
|
//
|
||||||
|
// UUIDs are based on RFC 4122 and DCE 1.1: Authentication and Security Services.
|
||||||
|
package uuid
|
53
vendor/github.com/pborman/uuid/hash.go
generated
vendored
Normal file
53
vendor/github.com/pborman/uuid/hash.go
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// Copyright 2011 Google Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package uuid
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"crypto/sha1"
|
||||||
|
"hash"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Well known Name Space IDs and UUIDs
|
||||||
|
var (
|
||||||
|
NameSpace_DNS = Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
|
||||||
|
NameSpace_URL = Parse("6ba7b811-9dad-11d1-80b4-00c04fd430c8")
|
||||||
|
NameSpace_OID = Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8")
|
||||||
|
NameSpace_X500 = Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8")
|
||||||
|
NIL = Parse("00000000-0000-0000-0000-000000000000")
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewHash returns a new UUID derived from the hash of space concatenated with
|
||||||
|
// data generated by h. The hash should be at least 16 byte in length. The
|
||||||
|
// first 16 bytes of the hash are used to form the UUID. The version of the
|
||||||
|
// UUID will be the lower 4 bits of version. NewHash is used to implement
|
||||||
|
// NewMD5 and NewSHA1.
|
||||||
|
func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID {
|
||||||
|
h.Reset()
|
||||||
|
h.Write(space)
|
||||||
|
h.Write([]byte(data))
|
||||||
|
s := h.Sum(nil)
|
||||||
|
uuid := make([]byte, 16)
|
||||||
|
copy(uuid, s)
|
||||||
|
uuid[6] = (uuid[6] & 0x0f) | uint8((version&0xf)<<4)
|
||||||
|
uuid[8] = (uuid[8] & 0x3f) | 0x80 // RFC 4122 variant
|
||||||
|
return uuid
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMD5 returns a new MD5 (Version 3) UUID based on the
|
||||||
|
// supplied name space and data.
|
||||||
|
//
|
||||||
|
// NewHash(md5.New(), space, data, 3)
|
||||||
|
func NewMD5(space UUID, data []byte) UUID {
|
||||||
|
return NewHash(md5.New(), space, data, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSHA1 returns a new SHA1 (Version 5) UUID based on the
|
||||||
|
// supplied name space and data.
|
||||||
|
//
|
||||||
|
// NewHash(sha1.New(), space, data, 5)
|
||||||
|
func NewSHA1(space UUID, data []byte) UUID {
|
||||||
|
return NewHash(sha1.New(), space, data, 5)
|
||||||
|
}
|
34
vendor/github.com/pborman/uuid/json.go
generated
vendored
Normal file
34
vendor/github.com/pborman/uuid/json.go
generated
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package uuid
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
func (u UUID) MarshalJSON() ([]byte, error) {
|
||||||
|
if len(u) != 16 {
|
||||||
|
return []byte(`""`), nil
|
||||||
|
}
|
||||||
|
var js [38]byte
|
||||||
|
js[0] = '"'
|
||||||
|
encodeHex(js[1:], u)
|
||||||
|
js[37] = '"'
|
||||||
|
return js[:], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UUID) UnmarshalJSON(data []byte) error {
|
||||||
|
if string(data) == `""` {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if data[0] != '"' {
|
||||||
|
return errors.New("invalid UUID format")
|
||||||
|
}
|
||||||
|
data = data[1 : len(data)-1]
|
||||||
|
uu := Parse(string(data))
|
||||||
|
if uu == nil {
|
||||||
|
return errors.New("invalid UUID format")
|
||||||
|
}
|
||||||
|
*u = uu
|
||||||
|
return nil
|
||||||
|
}
|
61
vendor/github.com/pborman/uuid/json_test.go
generated
vendored
Normal file
61
vendor/github.com/pborman/uuid/json_test.go
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package uuid
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var testUUID = Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479")
|
||||||
|
|
||||||
|
func TestJSON(t *testing.T) {
|
||||||
|
type S struct {
|
||||||
|
ID1 UUID
|
||||||
|
ID2 UUID
|
||||||
|
}
|
||||||
|
s1 := S{ID1: testUUID}
|
||||||
|
data, err := json.Marshal(&s1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
var s2 S
|
||||||
|
if err := json.Unmarshal(data, &s2); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(&s1, &s2) {
|
||||||
|
t.Errorf("got %#v, want %#v", s2, s1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkUUID_MarshalJSON(b *testing.B) {
|
||||||
|
x := &struct {
|
||||||
|
UUID UUID `json:"uuid"`
|
||||||
|
}{}
|
||||||
|
x.UUID = Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479")
|
||||||
|
if x.UUID == nil {
|
||||||
|
b.Fatal("invalid uuid")
|
||||||
|
}
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
js, err := json.Marshal(x)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("marshal json: %#v (%v)", js, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkUUID_UnmarshalJSON(b *testing.B) {
|
||||||
|
js := []byte(`{"uuid":"f47ac10b-58cc-0372-8567-0e02b2c3d479"}`)
|
||||||
|
var x *struct {
|
||||||
|
UUID UUID `json:"uuid"`
|
||||||
|
}
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
err := json.Unmarshal(js, &x)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("marshal json: %#v (%v)", js, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
117
vendor/github.com/pborman/uuid/node.go
generated
vendored
Executable file
117
vendor/github.com/pborman/uuid/node.go
generated
vendored
Executable file
@ -0,0 +1,117 @@
|
|||||||
|
// Copyright 2011 Google Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package uuid
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
nodeMu sync.Mutex
|
||||||
|
interfaces []net.Interface // cached list of interfaces
|
||||||
|
ifname string // name of interface being used
|
||||||
|
nodeID []byte // hardware for version 1 UUIDs
|
||||||
|
)
|
||||||
|
|
||||||
|
// NodeInterface returns the name of the interface from which the NodeID was
|
||||||
|
// derived. The interface "user" is returned if the NodeID was set by
|
||||||
|
// SetNodeID.
|
||||||
|
func NodeInterface() string {
|
||||||
|
defer nodeMu.Unlock()
|
||||||
|
nodeMu.Lock()
|
||||||
|
return ifname
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNodeInterface selects the hardware address to be used for Version 1 UUIDs.
|
||||||
|
// If name is "" then the first usable interface found will be used or a random
|
||||||
|
// Node ID will be generated. If a named interface cannot be found then false
|
||||||
|
// is returned.
|
||||||
|
//
|
||||||
|
// SetNodeInterface never fails when name is "".
|
||||||
|
func SetNodeInterface(name string) bool {
|
||||||
|
defer nodeMu.Unlock()
|
||||||
|
nodeMu.Lock()
|
||||||
|
return setNodeInterface(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setNodeInterface(name string) bool {
|
||||||
|
if interfaces == nil {
|
||||||
|
var err error
|
||||||
|
interfaces, err = net.Interfaces()
|
||||||
|
if err != nil && name != "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ifs := range interfaces {
|
||||||
|
if len(ifs.HardwareAddr) >= 6 && (name == "" || name == ifs.Name) {
|
||||||
|
if setNodeID(ifs.HardwareAddr) {
|
||||||
|
ifname = ifs.Name
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We found no interfaces with a valid hardware address. If name
|
||||||
|
// does not specify a specific interface generate a random Node ID
|
||||||
|
// (section 4.1.6)
|
||||||
|
if name == "" {
|
||||||
|
if nodeID == nil {
|
||||||
|
nodeID = make([]byte, 6)
|
||||||
|
}
|
||||||
|
randomBits(nodeID)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeID returns a slice of a copy of the current Node ID, setting the Node ID
|
||||||
|
// if not already set.
|
||||||
|
func NodeID() []byte {
|
||||||
|
defer nodeMu.Unlock()
|
||||||
|
nodeMu.Lock()
|
||||||
|
if nodeID == nil {
|
||||||
|
setNodeInterface("")
|
||||||
|
}
|
||||||
|
nid := make([]byte, 6)
|
||||||
|
copy(nid, nodeID)
|
||||||
|
return nid
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNodeID sets the Node ID to be used for Version 1 UUIDs. The first 6 bytes
|
||||||
|
// of id are used. If id is less than 6 bytes then false is returned and the
|
||||||
|
// Node ID is not set.
|
||||||
|
func SetNodeID(id []byte) bool {
|
||||||
|
defer nodeMu.Unlock()
|
||||||
|
nodeMu.Lock()
|
||||||
|
if setNodeID(id) {
|
||||||
|
ifname = "user"
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func setNodeID(id []byte) bool {
|
||||||
|
if len(id) < 6 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if nodeID == nil {
|
||||||
|
nodeID = make([]byte, 6)
|
||||||
|
}
|
||||||
|
copy(nodeID, id)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeID returns the 6 byte node id encoded in uuid. It returns nil if uuid is
|
||||||
|
// not valid. The NodeID is only well defined for version 1 and 2 UUIDs.
|
||||||
|
func (uuid UUID) NodeID() []byte {
|
||||||
|
if len(uuid) != 16 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
node := make([]byte, 6)
|
||||||
|
copy(node, uuid[10:])
|
||||||
|
return node
|
||||||
|
}
|
66
vendor/github.com/pborman/uuid/seq_test.go
generated
vendored
Normal file
66
vendor/github.com/pborman/uuid/seq_test.go
generated
vendored
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
// Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package uuid
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This test is only run when --regressions is passed on the go test line.
|
||||||
|
var regressions = flag.Bool("regressions", false, "run uuid regression tests")
|
||||||
|
|
||||||
|
// TestClockSeqRace tests for a particular race condition of returning two
|
||||||
|
// identical Version1 UUIDs. The duration of 1 minute was chosen as the race
|
||||||
|
// condition, before being fixed, nearly always occured in under 30 seconds.
|
||||||
|
func TestClockSeqRace(t *testing.T) {
|
||||||
|
if !*regressions {
|
||||||
|
t.Skip("skipping regression tests")
|
||||||
|
}
|
||||||
|
duration := time.Minute
|
||||||
|
|
||||||
|
done := make(chan struct{})
|
||||||
|
defer close(done)
|
||||||
|
|
||||||
|
ch := make(chan UUID, 10000)
|
||||||
|
ncpu := runtime.NumCPU()
|
||||||
|
switch ncpu {
|
||||||
|
case 0, 1:
|
||||||
|
// We can't run the test effectively.
|
||||||
|
t.Skip("skipping race test, only one CPU detected")
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
runtime.GOMAXPROCS(ncpu)
|
||||||
|
}
|
||||||
|
for i := 0; i < ncpu; i++ {
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-done:
|
||||||
|
return
|
||||||
|
case ch <- NewUUID():
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
uuids := make(map[string]bool)
|
||||||
|
cnt := 0
|
||||||
|
start := time.Now()
|
||||||
|
for u := range ch {
|
||||||
|
s := u.String()
|
||||||
|
if uuids[s] {
|
||||||
|
t.Errorf("duplicate uuid after %d in %v: %s", cnt, time.Since(start), s)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
uuids[s] = true
|
||||||
|
if time.Since(start) > duration {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cnt++
|
||||||
|
}
|
||||||
|
}
|
66
vendor/github.com/pborman/uuid/sql.go
generated
vendored
Normal file
66
vendor/github.com/pborman/uuid/sql.go
generated
vendored
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
// Copyright 2015 Google Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package uuid
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql/driver"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Scan implements sql.Scanner so UUIDs can be read from databases transparently
|
||||||
|
// Currently, database types that map to string and []byte are supported. Please
|
||||||
|
// consult database-specific driver documentation for matching types.
|
||||||
|
func (uuid *UUID) Scan(src interface{}) error {
|
||||||
|
switch src.(type) {
|
||||||
|
case string:
|
||||||
|
// if an empty UUID comes from a table, we return a null UUID
|
||||||
|
if src.(string) == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// see uuid.Parse for required string format
|
||||||
|
parsed := Parse(src.(string))
|
||||||
|
|
||||||
|
if parsed == nil {
|
||||||
|
return errors.New("Scan: invalid UUID format")
|
||||||
|
}
|
||||||
|
|
||||||
|
*uuid = parsed
|
||||||
|
case []byte:
|
||||||
|
b := src.([]byte)
|
||||||
|
|
||||||
|
// if an empty UUID comes from a table, we return a null UUID
|
||||||
|
if len(b) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// assumes a simple slice of bytes if 16 bytes
|
||||||
|
// otherwise attempts to parse
|
||||||
|
if len(b) == 16 {
|
||||||
|
*uuid = UUID(b)
|
||||||
|
} else {
|
||||||
|
u := Parse(string(b))
|
||||||
|
|
||||||
|
if u == nil {
|
||||||
|
return errors.New("Scan: invalid UUID format")
|
||||||
|
}
|
||||||
|
|
||||||
|
*uuid = u
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("Scan: unable to scan type %T into UUID", src)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value implements sql.Valuer so that UUIDs can be written to databases
|
||||||
|
// transparently. Currently, UUIDs map to strings. Please consult
|
||||||
|
// database-specific driver documentation for matching types.
|
||||||
|
func (uuid UUID) Value() (driver.Value, error) {
|
||||||
|
return uuid.String(), nil
|
||||||
|
}
|
96
vendor/github.com/pborman/uuid/sql_test.go
generated
vendored
Normal file
96
vendor/github.com/pborman/uuid/sql_test.go
generated
vendored
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
// Copyright 2015 Google Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package uuid
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestScan(t *testing.T) {
|
||||||
|
var stringTest string = "f47ac10b-58cc-0372-8567-0e02b2c3d479"
|
||||||
|
var byteTest []byte = Parse(stringTest)
|
||||||
|
var badTypeTest int = 6
|
||||||
|
var invalidTest string = "f47ac10b-58cc-0372-8567-0e02b2c3d4"
|
||||||
|
|
||||||
|
// sunny day tests
|
||||||
|
|
||||||
|
var uuid UUID
|
||||||
|
err := (&uuid).Scan(stringTest)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = (&uuid).Scan([]byte(stringTest))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = (&uuid).Scan(byteTest)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// bad type tests
|
||||||
|
|
||||||
|
err = (&uuid).Scan(badTypeTest)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("int correctly parsed and shouldn't have")
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), "unable to scan type") {
|
||||||
|
t.Error("attempting to parse an int returned an incorrect error message")
|
||||||
|
}
|
||||||
|
|
||||||
|
// invalid/incomplete uuids
|
||||||
|
|
||||||
|
err = (&uuid).Scan(invalidTest)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("invalid uuid was parsed without error")
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), "invalid UUID") {
|
||||||
|
t.Error("attempting to parse an invalid UUID returned an incorrect error message")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = (&uuid).Scan(byteTest[:len(byteTest)-2])
|
||||||
|
if err == nil {
|
||||||
|
t.Error("invalid byte uuid was parsed without error")
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), "invalid UUID") {
|
||||||
|
t.Error("attempting to parse an invalid byte UUID returned an incorrect error message")
|
||||||
|
}
|
||||||
|
|
||||||
|
// empty tests
|
||||||
|
|
||||||
|
uuid = nil
|
||||||
|
var emptySlice []byte
|
||||||
|
err = (&uuid).Scan(emptySlice)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if uuid != nil {
|
||||||
|
t.Error("UUID was not nil after scanning empty byte slice")
|
||||||
|
}
|
||||||
|
|
||||||
|
uuid = nil
|
||||||
|
var emptyString string
|
||||||
|
err = (&uuid).Scan(emptyString)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if uuid != nil {
|
||||||
|
t.Error("UUID was not nil after scanning empty string")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValue(t *testing.T) {
|
||||||
|
stringTest := "f47ac10b-58cc-0372-8567-0e02b2c3d479"
|
||||||
|
uuid := Parse(stringTest)
|
||||||
|
val, _ := uuid.Value()
|
||||||
|
if val != stringTest {
|
||||||
|
t.Error("Value() did not return expected string")
|
||||||
|
}
|
||||||
|
}
|
132
vendor/github.com/pborman/uuid/time.go
generated
vendored
Executable file
132
vendor/github.com/pborman/uuid/time.go
generated
vendored
Executable file
@ -0,0 +1,132 @@
|
|||||||
|
// Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package uuid
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Time represents a time as the number of 100's of nanoseconds since 15 Oct
|
||||||
|
// 1582.
|
||||||
|
type Time int64
|
||||||
|
|
||||||
|
const (
|
||||||
|
lillian = 2299160 // Julian day of 15 Oct 1582
|
||||||
|
unix = 2440587 // Julian day of 1 Jan 1970
|
||||||
|
epoch = unix - lillian // Days between epochs
|
||||||
|
g1582 = epoch * 86400 // seconds between epochs
|
||||||
|
g1582ns100 = g1582 * 10000000 // 100s of a nanoseconds between epochs
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
timeMu sync.Mutex
|
||||||
|
lasttime uint64 // last time we returned
|
||||||
|
clock_seq uint16 // clock sequence for this run
|
||||||
|
|
||||||
|
timeNow = time.Now // for testing
|
||||||
|
)
|
||||||
|
|
||||||
|
// UnixTime converts t the number of seconds and nanoseconds using the Unix
|
||||||
|
// epoch of 1 Jan 1970.
|
||||||
|
func (t Time) UnixTime() (sec, nsec int64) {
|
||||||
|
sec = int64(t - g1582ns100)
|
||||||
|
nsec = (sec % 10000000) * 100
|
||||||
|
sec /= 10000000
|
||||||
|
return sec, nsec
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTime returns the current Time (100s of nanoseconds since 15 Oct 1582) and
|
||||||
|
// clock sequence as well as adjusting the clock sequence as needed. An error
|
||||||
|
// is returned if the current time cannot be determined.
|
||||||
|
func GetTime() (Time, uint16, error) {
|
||||||
|
defer timeMu.Unlock()
|
||||||
|
timeMu.Lock()
|
||||||
|
return getTime()
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTime() (Time, uint16, error) {
|
||||||
|
t := timeNow()
|
||||||
|
|
||||||
|
// If we don't have a clock sequence already, set one.
|
||||||
|
if clock_seq == 0 {
|
||||||
|
setClockSequence(-1)
|
||||||
|
}
|
||||||
|
now := uint64(t.UnixNano()/100) + g1582ns100
|
||||||
|
|
||||||
|
// If time has gone backwards with this clock sequence then we
|
||||||
|
// increment the clock sequence
|
||||||
|
if now <= lasttime {
|
||||||
|
clock_seq = ((clock_seq + 1) & 0x3fff) | 0x8000
|
||||||
|
}
|
||||||
|
lasttime = now
|
||||||
|
return Time(now), clock_seq, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClockSequence returns the current clock sequence, generating one if not
|
||||||
|
// already set. The clock sequence is only used for Version 1 UUIDs.
|
||||||
|
//
|
||||||
|
// The uuid package does not use global static storage for the clock sequence or
|
||||||
|
// the last time a UUID was generated. Unless SetClockSequence a new random
|
||||||
|
// clock sequence is generated the first time a clock sequence is requested by
|
||||||
|
// ClockSequence, GetTime, or NewUUID. (section 4.2.1.1) sequence is generated
|
||||||
|
// for
|
||||||
|
func ClockSequence() int {
|
||||||
|
defer timeMu.Unlock()
|
||||||
|
timeMu.Lock()
|
||||||
|
return clockSequence()
|
||||||
|
}
|
||||||
|
|
||||||
|
func clockSequence() int {
|
||||||
|
if clock_seq == 0 {
|
||||||
|
setClockSequence(-1)
|
||||||
|
}
|
||||||
|
return int(clock_seq & 0x3fff)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetClockSeq sets the clock sequence to the lower 14 bits of seq. Setting to
|
||||||
|
// -1 causes a new sequence to be generated.
|
||||||
|
func SetClockSequence(seq int) {
|
||||||
|
defer timeMu.Unlock()
|
||||||
|
timeMu.Lock()
|
||||||
|
setClockSequence(seq)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setClockSequence(seq int) {
|
||||||
|
if seq == -1 {
|
||||||
|
var b [2]byte
|
||||||
|
randomBits(b[:]) // clock sequence
|
||||||
|
seq = int(b[0])<<8 | int(b[1])
|
||||||
|
}
|
||||||
|
old_seq := clock_seq
|
||||||
|
clock_seq = uint16(seq&0x3fff) | 0x8000 // Set our variant
|
||||||
|
if old_seq != clock_seq {
|
||||||
|
lasttime = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in
|
||||||
|
// uuid. It returns false if uuid is not valid. The time is only well defined
|
||||||
|
// for version 1 and 2 UUIDs.
|
||||||
|
func (uuid UUID) Time() (Time, bool) {
|
||||||
|
if len(uuid) != 16 {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
time := int64(binary.BigEndian.Uint32(uuid[0:4]))
|
||||||
|
time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32
|
||||||
|
time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48
|
||||||
|
return Time(time), true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClockSequence returns the clock sequence encoded in uuid. It returns false
|
||||||
|
// if uuid is not valid. The clock sequence is only well defined for version 1
|
||||||
|
// and 2 UUIDs.
|
||||||
|
func (uuid UUID) ClockSequence() (int, bool) {
|
||||||
|
if len(uuid) != 16 {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
return int(binary.BigEndian.Uint16(uuid[8:10])) & 0x3fff, true
|
||||||
|
}
|
43
vendor/github.com/pborman/uuid/util.go
generated
vendored
Normal file
43
vendor/github.com/pborman/uuid/util.go
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// Copyright 2011 Google Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package uuid
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// randomBits completely fills slice b with random data.
|
||||||
|
func randomBits(b []byte) {
|
||||||
|
if _, err := io.ReadFull(rander, b); err != nil {
|
||||||
|
panic(err.Error()) // rand should never fail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// xvalues returns the value of a byte as a hexadecimal digit or 255.
|
||||||
|
var xvalues = [256]byte{
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
}
|
||||||
|
|
||||||
|
// xtob converts the the first two hex bytes of x into a byte.
|
||||||
|
func xtob(x string) (byte, bool) {
|
||||||
|
b1 := xvalues[x[0]]
|
||||||
|
b2 := xvalues[x[1]]
|
||||||
|
return (b1 << 4) | b2, b1 != 255 && b2 != 255
|
||||||
|
}
|
201
vendor/github.com/pborman/uuid/uuid.go
generated
vendored
Normal file
201
vendor/github.com/pborman/uuid/uuid.go
generated
vendored
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
// Copyright 2011 Google Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package uuid
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Array is a pass-by-value UUID that can be used as an effecient key in a map.
|
||||||
|
type Array [16]byte
|
||||||
|
|
||||||
|
// UUID converts uuid into a slice.
|
||||||
|
func (uuid Array) UUID() UUID {
|
||||||
|
return uuid[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the string representation of uuid,
|
||||||
|
// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.
|
||||||
|
func (uuid Array) String() string {
|
||||||
|
return uuid.UUID().String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC
|
||||||
|
// 4122.
|
||||||
|
type UUID []byte
|
||||||
|
|
||||||
|
// A Version represents a UUIDs version.
|
||||||
|
type Version byte
|
||||||
|
|
||||||
|
// A Variant represents a UUIDs variant.
|
||||||
|
type Variant byte
|
||||||
|
|
||||||
|
// Constants returned by Variant.
|
||||||
|
const (
|
||||||
|
Invalid = Variant(iota) // Invalid UUID
|
||||||
|
RFC4122 // The variant specified in RFC4122
|
||||||
|
Reserved // Reserved, NCS backward compatibility.
|
||||||
|
Microsoft // Reserved, Microsoft Corporation backward compatibility.
|
||||||
|
Future // Reserved for future definition.
|
||||||
|
)
|
||||||
|
|
||||||
|
var rander = rand.Reader // random function
|
||||||
|
|
||||||
|
// New returns a new random (version 4) UUID as a string. It is a convenience
|
||||||
|
// function for NewRandom().String().
|
||||||
|
func New() string {
|
||||||
|
return NewRandom().String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse decodes s into a UUID or returns nil. Both the UUID form of
|
||||||
|
// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and
|
||||||
|
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded.
|
||||||
|
func Parse(s string) UUID {
|
||||||
|
if len(s) == 36+9 {
|
||||||
|
if strings.ToLower(s[:9]) != "urn:uuid:" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
s = s[9:]
|
||||||
|
} else if len(s) != 36 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var uuid [16]byte
|
||||||
|
for i, x := range [16]int{
|
||||||
|
0, 2, 4, 6,
|
||||||
|
9, 11,
|
||||||
|
14, 16,
|
||||||
|
19, 21,
|
||||||
|
24, 26, 28, 30, 32, 34} {
|
||||||
|
if v, ok := xtob(s[x:]); !ok {
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
uuid[i] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return uuid[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal returns true if uuid1 and uuid2 are equal.
|
||||||
|
func Equal(uuid1, uuid2 UUID) bool {
|
||||||
|
return bytes.Equal(uuid1, uuid2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Array returns an array representation of uuid that can be used as a map key.
|
||||||
|
// Array panics if uuid is not valid.
|
||||||
|
func (uuid UUID) Array() Array {
|
||||||
|
if len(uuid) != 16 {
|
||||||
|
panic("invalid uuid")
|
||||||
|
}
|
||||||
|
var a Array
|
||||||
|
copy(a[:], uuid)
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||||
|
// , or "" if uuid is invalid.
|
||||||
|
func (uuid UUID) String() string {
|
||||||
|
if len(uuid) != 16 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
var buf [36]byte
|
||||||
|
encodeHex(buf[:], uuid)
|
||||||
|
return string(buf[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// URN returns the RFC 2141 URN form of uuid,
|
||||||
|
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, or "" if uuid is invalid.
|
||||||
|
func (uuid UUID) URN() string {
|
||||||
|
if len(uuid) != 16 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
var buf [36 + 9]byte
|
||||||
|
copy(buf[:], "urn:uuid:")
|
||||||
|
encodeHex(buf[9:], uuid)
|
||||||
|
return string(buf[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeHex(dst []byte, uuid UUID) {
|
||||||
|
hex.Encode(dst[:], uuid[:4])
|
||||||
|
dst[8] = '-'
|
||||||
|
hex.Encode(dst[9:13], uuid[4:6])
|
||||||
|
dst[13] = '-'
|
||||||
|
hex.Encode(dst[14:18], uuid[6:8])
|
||||||
|
dst[18] = '-'
|
||||||
|
hex.Encode(dst[19:23], uuid[8:10])
|
||||||
|
dst[23] = '-'
|
||||||
|
hex.Encode(dst[24:], uuid[10:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variant returns the variant encoded in uuid. It returns Invalid if
|
||||||
|
// uuid is invalid.
|
||||||
|
func (uuid UUID) Variant() Variant {
|
||||||
|
if len(uuid) != 16 {
|
||||||
|
return Invalid
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case (uuid[8] & 0xc0) == 0x80:
|
||||||
|
return RFC4122
|
||||||
|
case (uuid[8] & 0xe0) == 0xc0:
|
||||||
|
return Microsoft
|
||||||
|
case (uuid[8] & 0xe0) == 0xe0:
|
||||||
|
return Future
|
||||||
|
default:
|
||||||
|
return Reserved
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version returns the version of uuid. It returns false if uuid is not
|
||||||
|
// valid.
|
||||||
|
func (uuid UUID) Version() (Version, bool) {
|
||||||
|
if len(uuid) != 16 {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
return Version(uuid[6] >> 4), true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v Version) String() string {
|
||||||
|
if v > 15 {
|
||||||
|
return fmt.Sprintf("BAD_VERSION_%d", v)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("VERSION_%d", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v Variant) String() string {
|
||||||
|
switch v {
|
||||||
|
case RFC4122:
|
||||||
|
return "RFC4122"
|
||||||
|
case Reserved:
|
||||||
|
return "Reserved"
|
||||||
|
case Microsoft:
|
||||||
|
return "Microsoft"
|
||||||
|
case Future:
|
||||||
|
return "Future"
|
||||||
|
case Invalid:
|
||||||
|
return "Invalid"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("BadVariant%d", int(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRand sets the random number generator to r, which implents io.Reader.
|
||||||
|
// If r.Read returns an error when the package requests random data then
|
||||||
|
// a panic will be issued.
|
||||||
|
//
|
||||||
|
// Calling SetRand with nil sets the random number generator to the default
|
||||||
|
// generator.
|
||||||
|
func SetRand(r io.Reader) {
|
||||||
|
if r == nil {
|
||||||
|
rander = rand.Reader
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rander = r
|
||||||
|
}
|
543
vendor/github.com/pborman/uuid/uuid_test.go
generated
vendored
Normal file
543
vendor/github.com/pborman/uuid/uuid_test.go
generated
vendored
Normal file
@ -0,0 +1,543 @@
|
|||||||
|
// Copyright 2011 Google Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package uuid
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type test struct {
|
||||||
|
in string
|
||||||
|
version Version
|
||||||
|
variant Variant
|
||||||
|
isuuid bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var tests = []test{
|
||||||
|
{"f47ac10b-58cc-0372-8567-0e02b2c3d479", 0, RFC4122, true},
|
||||||
|
{"f47ac10b-58cc-1372-8567-0e02b2c3d479", 1, RFC4122, true},
|
||||||
|
{"f47ac10b-58cc-2372-8567-0e02b2c3d479", 2, RFC4122, true},
|
||||||
|
{"f47ac10b-58cc-3372-8567-0e02b2c3d479", 3, RFC4122, true},
|
||||||
|
{"f47ac10b-58cc-4372-8567-0e02b2c3d479", 4, RFC4122, true},
|
||||||
|
{"f47ac10b-58cc-5372-8567-0e02b2c3d479", 5, RFC4122, true},
|
||||||
|
{"f47ac10b-58cc-6372-8567-0e02b2c3d479", 6, RFC4122, true},
|
||||||
|
{"f47ac10b-58cc-7372-8567-0e02b2c3d479", 7, RFC4122, true},
|
||||||
|
{"f47ac10b-58cc-8372-8567-0e02b2c3d479", 8, RFC4122, true},
|
||||||
|
{"f47ac10b-58cc-9372-8567-0e02b2c3d479", 9, RFC4122, true},
|
||||||
|
{"f47ac10b-58cc-a372-8567-0e02b2c3d479", 10, RFC4122, true},
|
||||||
|
{"f47ac10b-58cc-b372-8567-0e02b2c3d479", 11, RFC4122, true},
|
||||||
|
{"f47ac10b-58cc-c372-8567-0e02b2c3d479", 12, RFC4122, true},
|
||||||
|
{"f47ac10b-58cc-d372-8567-0e02b2c3d479", 13, RFC4122, true},
|
||||||
|
{"f47ac10b-58cc-e372-8567-0e02b2c3d479", 14, RFC4122, true},
|
||||||
|
{"f47ac10b-58cc-f372-8567-0e02b2c3d479", 15, RFC4122, true},
|
||||||
|
|
||||||
|
{"urn:uuid:f47ac10b-58cc-4372-0567-0e02b2c3d479", 4, Reserved, true},
|
||||||
|
{"URN:UUID:f47ac10b-58cc-4372-0567-0e02b2c3d479", 4, Reserved, true},
|
||||||
|
{"f47ac10b-58cc-4372-0567-0e02b2c3d479", 4, Reserved, true},
|
||||||
|
{"f47ac10b-58cc-4372-1567-0e02b2c3d479", 4, Reserved, true},
|
||||||
|
{"f47ac10b-58cc-4372-2567-0e02b2c3d479", 4, Reserved, true},
|
||||||
|
{"f47ac10b-58cc-4372-3567-0e02b2c3d479", 4, Reserved, true},
|
||||||
|
{"f47ac10b-58cc-4372-4567-0e02b2c3d479", 4, Reserved, true},
|
||||||
|
{"f47ac10b-58cc-4372-5567-0e02b2c3d479", 4, Reserved, true},
|
||||||
|
{"f47ac10b-58cc-4372-6567-0e02b2c3d479", 4, Reserved, true},
|
||||||
|
{"f47ac10b-58cc-4372-7567-0e02b2c3d479", 4, Reserved, true},
|
||||||
|
{"f47ac10b-58cc-4372-8567-0e02b2c3d479", 4, RFC4122, true},
|
||||||
|
{"f47ac10b-58cc-4372-9567-0e02b2c3d479", 4, RFC4122, true},
|
||||||
|
{"f47ac10b-58cc-4372-a567-0e02b2c3d479", 4, RFC4122, true},
|
||||||
|
{"f47ac10b-58cc-4372-b567-0e02b2c3d479", 4, RFC4122, true},
|
||||||
|
{"f47ac10b-58cc-4372-c567-0e02b2c3d479", 4, Microsoft, true},
|
||||||
|
{"f47ac10b-58cc-4372-d567-0e02b2c3d479", 4, Microsoft, true},
|
||||||
|
{"f47ac10b-58cc-4372-e567-0e02b2c3d479", 4, Future, true},
|
||||||
|
{"f47ac10b-58cc-4372-f567-0e02b2c3d479", 4, Future, true},
|
||||||
|
|
||||||
|
{"f47ac10b158cc-5372-a567-0e02b2c3d479", 0, Invalid, false},
|
||||||
|
{"f47ac10b-58cc25372-a567-0e02b2c3d479", 0, Invalid, false},
|
||||||
|
{"f47ac10b-58cc-53723a567-0e02b2c3d479", 0, Invalid, false},
|
||||||
|
{"f47ac10b-58cc-5372-a56740e02b2c3d479", 0, Invalid, false},
|
||||||
|
{"f47ac10b-58cc-5372-a567-0e02-2c3d479", 0, Invalid, false},
|
||||||
|
{"g47ac10b-58cc-4372-a567-0e02b2c3d479", 0, Invalid, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
var constants = []struct {
|
||||||
|
c interface{}
|
||||||
|
name string
|
||||||
|
}{
|
||||||
|
{Person, "Person"},
|
||||||
|
{Group, "Group"},
|
||||||
|
{Org, "Org"},
|
||||||
|
{Invalid, "Invalid"},
|
||||||
|
{RFC4122, "RFC4122"},
|
||||||
|
{Reserved, "Reserved"},
|
||||||
|
{Microsoft, "Microsoft"},
|
||||||
|
{Future, "Future"},
|
||||||
|
{Domain(17), "Domain17"},
|
||||||
|
{Variant(42), "BadVariant42"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTest(t *testing.T, in string, tt test) {
|
||||||
|
uuid := Parse(in)
|
||||||
|
if ok := (uuid != nil); ok != tt.isuuid {
|
||||||
|
t.Errorf("Parse(%s) got %v expected %v\b", in, ok, tt.isuuid)
|
||||||
|
}
|
||||||
|
if uuid == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if v := uuid.Variant(); v != tt.variant {
|
||||||
|
t.Errorf("Variant(%s) got %d expected %d\b", in, v, tt.variant)
|
||||||
|
}
|
||||||
|
if v, _ := uuid.Version(); v != tt.version {
|
||||||
|
t.Errorf("Version(%s) got %d expected %d\b", in, v, tt.version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUUID(t *testing.T) {
|
||||||
|
for _, tt := range tests {
|
||||||
|
testTest(t, tt.in, tt)
|
||||||
|
testTest(t, strings.ToUpper(tt.in), tt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConstants(t *testing.T) {
|
||||||
|
for x, tt := range constants {
|
||||||
|
v, ok := tt.c.(fmt.Stringer)
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("%x: %v: not a stringer", x, v)
|
||||||
|
} else if s := v.String(); s != tt.name {
|
||||||
|
v, _ := tt.c.(int)
|
||||||
|
t.Errorf("%x: Constant %T:%d gives %q, expected %q", x, tt.c, v, s, tt.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRandomUUID(t *testing.T) {
|
||||||
|
m := make(map[string]bool)
|
||||||
|
for x := 1; x < 32; x++ {
|
||||||
|
uuid := NewRandom()
|
||||||
|
s := uuid.String()
|
||||||
|
if m[s] {
|
||||||
|
t.Errorf("NewRandom returned duplicated UUID %s", s)
|
||||||
|
}
|
||||||
|
m[s] = true
|
||||||
|
if v, _ := uuid.Version(); v != 4 {
|
||||||
|
t.Errorf("Random UUID of version %s", v)
|
||||||
|
}
|
||||||
|
if uuid.Variant() != RFC4122 {
|
||||||
|
t.Errorf("Random UUID is variant %d", uuid.Variant())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNew(t *testing.T) {
|
||||||
|
m := make(map[string]bool)
|
||||||
|
for x := 1; x < 32; x++ {
|
||||||
|
s := New()
|
||||||
|
if m[s] {
|
||||||
|
t.Errorf("New returned duplicated UUID %s", s)
|
||||||
|
}
|
||||||
|
m[s] = true
|
||||||
|
uuid := Parse(s)
|
||||||
|
if uuid == nil {
|
||||||
|
t.Errorf("New returned %q which does not decode", s)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if v, _ := uuid.Version(); v != 4 {
|
||||||
|
t.Errorf("Random UUID of version %s", v)
|
||||||
|
}
|
||||||
|
if uuid.Variant() != RFC4122 {
|
||||||
|
t.Errorf("Random UUID is variant %d", uuid.Variant())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func clockSeq(t *testing.T, uuid UUID) int {
|
||||||
|
seq, ok := uuid.ClockSequence()
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("%s: invalid clock sequence", uuid)
|
||||||
|
}
|
||||||
|
return seq
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClockSeq(t *testing.T) {
|
||||||
|
// Fake time.Now for this test to return a monotonically advancing time; restore it at end.
|
||||||
|
defer func(orig func() time.Time) { timeNow = orig }(timeNow)
|
||||||
|
monTime := time.Now()
|
||||||
|
timeNow = func() time.Time {
|
||||||
|
monTime = monTime.Add(1 * time.Second)
|
||||||
|
return monTime
|
||||||
|
}
|
||||||
|
|
||||||
|
SetClockSequence(-1)
|
||||||
|
uuid1 := NewUUID()
|
||||||
|
uuid2 := NewUUID()
|
||||||
|
|
||||||
|
if clockSeq(t, uuid1) != clockSeq(t, uuid2) {
|
||||||
|
t.Errorf("clock sequence %d != %d", clockSeq(t, uuid1), clockSeq(t, uuid2))
|
||||||
|
}
|
||||||
|
|
||||||
|
SetClockSequence(-1)
|
||||||
|
uuid2 = NewUUID()
|
||||||
|
|
||||||
|
// Just on the very off chance we generated the same sequence
|
||||||
|
// two times we try again.
|
||||||
|
if clockSeq(t, uuid1) == clockSeq(t, uuid2) {
|
||||||
|
SetClockSequence(-1)
|
||||||
|
uuid2 = NewUUID()
|
||||||
|
}
|
||||||
|
if clockSeq(t, uuid1) == clockSeq(t, uuid2) {
|
||||||
|
t.Errorf("Duplicate clock sequence %d", clockSeq(t, uuid1))
|
||||||
|
}
|
||||||
|
|
||||||
|
SetClockSequence(0x1234)
|
||||||
|
uuid1 = NewUUID()
|
||||||
|
if seq := clockSeq(t, uuid1); seq != 0x1234 {
|
||||||
|
t.Errorf("%s: expected seq 0x1234 got 0x%04x", uuid1, seq)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCoding(t *testing.T) {
|
||||||
|
text := "7d444840-9dc0-11d1-b245-5ffdce74fad2"
|
||||||
|
urn := "urn:uuid:7d444840-9dc0-11d1-b245-5ffdce74fad2"
|
||||||
|
data := UUID{
|
||||||
|
0x7d, 0x44, 0x48, 0x40,
|
||||||
|
0x9d, 0xc0,
|
||||||
|
0x11, 0xd1,
|
||||||
|
0xb2, 0x45,
|
||||||
|
0x5f, 0xfd, 0xce, 0x74, 0xfa, 0xd2,
|
||||||
|
}
|
||||||
|
if v := data.String(); v != text {
|
||||||
|
t.Errorf("%x: encoded to %s, expected %s", data, v, text)
|
||||||
|
}
|
||||||
|
if v := data.URN(); v != urn {
|
||||||
|
t.Errorf("%x: urn is %s, expected %s", data, v, urn)
|
||||||
|
}
|
||||||
|
|
||||||
|
uuid := Parse(text)
|
||||||
|
if !Equal(uuid, data) {
|
||||||
|
t.Errorf("%s: decoded to %s, expected %s", text, uuid, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVersion1(t *testing.T) {
|
||||||
|
uuid1 := NewUUID()
|
||||||
|
uuid2 := NewUUID()
|
||||||
|
|
||||||
|
if Equal(uuid1, uuid2) {
|
||||||
|
t.Errorf("%s:duplicate uuid", uuid1)
|
||||||
|
}
|
||||||
|
if v, _ := uuid1.Version(); v != 1 {
|
||||||
|
t.Errorf("%s: version %s expected 1", uuid1, v)
|
||||||
|
}
|
||||||
|
if v, _ := uuid2.Version(); v != 1 {
|
||||||
|
t.Errorf("%s: version %s expected 1", uuid2, v)
|
||||||
|
}
|
||||||
|
n1 := uuid1.NodeID()
|
||||||
|
n2 := uuid2.NodeID()
|
||||||
|
if !bytes.Equal(n1, n2) {
|
||||||
|
t.Errorf("Different nodes %x != %x", n1, n2)
|
||||||
|
}
|
||||||
|
t1, ok := uuid1.Time()
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("%s: invalid time", uuid1)
|
||||||
|
}
|
||||||
|
t2, ok := uuid2.Time()
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("%s: invalid time", uuid2)
|
||||||
|
}
|
||||||
|
q1, ok := uuid1.ClockSequence()
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("%s: invalid clock sequence", uuid1)
|
||||||
|
}
|
||||||
|
q2, ok := uuid2.ClockSequence()
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("%s: invalid clock sequence", uuid2)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case t1 == t2 && q1 == q2:
|
||||||
|
t.Error("time stopped")
|
||||||
|
case t1 > t2 && q1 == q2:
|
||||||
|
t.Error("time reversed")
|
||||||
|
case t1 < t2 && q1 != q2:
|
||||||
|
t.Error("clock sequence chaned unexpectedly")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNode(t *testing.T) {
|
||||||
|
// This test is mostly to make sure we don't leave nodeMu locked.
|
||||||
|
ifname = ""
|
||||||
|
if ni := NodeInterface(); ni != "" {
|
||||||
|
t.Errorf("NodeInterface got %q, want %q", ni, "")
|
||||||
|
}
|
||||||
|
if SetNodeInterface("xyzzy") {
|
||||||
|
t.Error("SetNodeInterface succeeded on a bad interface name")
|
||||||
|
}
|
||||||
|
if !SetNodeInterface("") {
|
||||||
|
t.Error("SetNodeInterface failed")
|
||||||
|
}
|
||||||
|
if ni := NodeInterface(); ni == "" {
|
||||||
|
t.Error("NodeInterface returned an empty string")
|
||||||
|
}
|
||||||
|
|
||||||
|
ni := NodeID()
|
||||||
|
if len(ni) != 6 {
|
||||||
|
t.Errorf("ni got %d bytes, want 6", len(ni))
|
||||||
|
}
|
||||||
|
hasData := false
|
||||||
|
for _, b := range ni {
|
||||||
|
if b != 0 {
|
||||||
|
hasData = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !hasData {
|
||||||
|
t.Error("nodeid is all zeros")
|
||||||
|
}
|
||||||
|
|
||||||
|
id := []byte{1, 2, 3, 4, 5, 6, 7, 8}
|
||||||
|
SetNodeID(id)
|
||||||
|
ni = NodeID()
|
||||||
|
if !bytes.Equal(ni, id[:6]) {
|
||||||
|
t.Errorf("got nodeid %v, want %v", ni, id[:6])
|
||||||
|
}
|
||||||
|
|
||||||
|
if ni := NodeInterface(); ni != "user" {
|
||||||
|
t.Errorf("got inteface %q, want %q", ni, "user")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNodeAndTime(t *testing.T) {
|
||||||
|
// Time is February 5, 1998 12:30:23.136364800 AM GMT
|
||||||
|
|
||||||
|
uuid := Parse("7d444840-9dc0-11d1-b245-5ffdce74fad2")
|
||||||
|
node := []byte{0x5f, 0xfd, 0xce, 0x74, 0xfa, 0xd2}
|
||||||
|
|
||||||
|
ts, ok := uuid.Time()
|
||||||
|
if ok {
|
||||||
|
c := time.Unix(ts.UnixTime())
|
||||||
|
want := time.Date(1998, 2, 5, 0, 30, 23, 136364800, time.UTC)
|
||||||
|
if !c.Equal(want) {
|
||||||
|
t.Errorf("Got time %v, want %v", c, want)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.Errorf("%s: bad time", uuid)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(node, uuid.NodeID()) {
|
||||||
|
t.Errorf("Expected node %v got %v", node, uuid.NodeID())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMD5(t *testing.T) {
|
||||||
|
uuid := NewMD5(NameSpace_DNS, []byte("python.org")).String()
|
||||||
|
want := "6fa459ea-ee8a-3ca4-894e-db77e160355e"
|
||||||
|
if uuid != want {
|
||||||
|
t.Errorf("MD5: got %q expected %q", uuid, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSHA1(t *testing.T) {
|
||||||
|
uuid := NewSHA1(NameSpace_DNS, []byte("python.org")).String()
|
||||||
|
want := "886313e1-3b8a-5372-9b90-0c9aee199e5d"
|
||||||
|
if uuid != want {
|
||||||
|
t.Errorf("SHA1: got %q expected %q", uuid, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNodeID(t *testing.T) {
|
||||||
|
nid := []byte{1, 2, 3, 4, 5, 6}
|
||||||
|
SetNodeInterface("")
|
||||||
|
s := NodeInterface()
|
||||||
|
if s == "" || s == "user" {
|
||||||
|
t.Errorf("NodeInterface %q after SetInteface", s)
|
||||||
|
}
|
||||||
|
node1 := NodeID()
|
||||||
|
if node1 == nil {
|
||||||
|
t.Error("NodeID nil after SetNodeInterface", s)
|
||||||
|
}
|
||||||
|
SetNodeID(nid)
|
||||||
|
s = NodeInterface()
|
||||||
|
if s != "user" {
|
||||||
|
t.Errorf("Expected NodeInterface %q got %q", "user", s)
|
||||||
|
}
|
||||||
|
node2 := NodeID()
|
||||||
|
if node2 == nil {
|
||||||
|
t.Error("NodeID nil after SetNodeID", s)
|
||||||
|
}
|
||||||
|
if bytes.Equal(node1, node2) {
|
||||||
|
t.Error("NodeID not changed after SetNodeID", s)
|
||||||
|
} else if !bytes.Equal(nid, node2) {
|
||||||
|
t.Errorf("NodeID is %x, expected %x", node2, nid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDCE(t *testing.T, name string, uuid UUID, domain Domain, id uint32) {
|
||||||
|
if uuid == nil {
|
||||||
|
t.Errorf("%s failed", name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if v, _ := uuid.Version(); v != 2 {
|
||||||
|
t.Errorf("%s: %s: expected version 2, got %s", name, uuid, v)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if v, ok := uuid.Domain(); !ok || v != domain {
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("%s: %d: Domain failed", name, uuid)
|
||||||
|
} else {
|
||||||
|
t.Errorf("%s: %s: expected domain %d, got %d", name, uuid, domain, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if v, ok := uuid.Id(); !ok || v != id {
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("%s: %d: Id failed", name, uuid)
|
||||||
|
} else {
|
||||||
|
t.Errorf("%s: %s: expected id %d, got %d", name, uuid, id, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDCE(t *testing.T) {
|
||||||
|
testDCE(t, "NewDCESecurity", NewDCESecurity(42, 12345678), 42, 12345678)
|
||||||
|
testDCE(t, "NewDCEPerson", NewDCEPerson(), Person, uint32(os.Getuid()))
|
||||||
|
testDCE(t, "NewDCEGroup", NewDCEGroup(), Group, uint32(os.Getgid()))
|
||||||
|
}
|
||||||
|
|
||||||
|
type badRand struct{}
|
||||||
|
|
||||||
|
func (r badRand) Read(buf []byte) (int, error) {
|
||||||
|
for i, _ := range buf {
|
||||||
|
buf[i] = byte(i)
|
||||||
|
}
|
||||||
|
return len(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBadRand(t *testing.T) {
|
||||||
|
SetRand(badRand{})
|
||||||
|
uuid1 := New()
|
||||||
|
uuid2 := New()
|
||||||
|
if uuid1 != uuid2 {
|
||||||
|
t.Errorf("execpted duplicates, got %q and %q", uuid1, uuid2)
|
||||||
|
}
|
||||||
|
SetRand(nil)
|
||||||
|
uuid1 = New()
|
||||||
|
uuid2 = New()
|
||||||
|
if uuid1 == uuid2 {
|
||||||
|
t.Errorf("unexecpted duplicates, got %q", uuid1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUUID_Array(t *testing.T) {
|
||||||
|
expect := Array{
|
||||||
|
0xf4, 0x7a, 0xc1, 0x0b,
|
||||||
|
0x58, 0xcc,
|
||||||
|
0x03, 0x72,
|
||||||
|
0x85, 0x67,
|
||||||
|
0x0e, 0x02, 0xb2, 0xc3, 0xd4, 0x79,
|
||||||
|
}
|
||||||
|
uuid := Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479")
|
||||||
|
if uuid == nil {
|
||||||
|
t.Fatal("invalid uuid")
|
||||||
|
}
|
||||||
|
if uuid.Array() != expect {
|
||||||
|
t.Fatal("invalid array")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestArray_UUID(t *testing.T) {
|
||||||
|
array := Array{
|
||||||
|
0xf4, 0x7a, 0xc1, 0x0b,
|
||||||
|
0x58, 0xcc,
|
||||||
|
0x03, 0x72,
|
||||||
|
0x85, 0x67,
|
||||||
|
0x0e, 0x02, 0xb2, 0xc3, 0xd4, 0x79,
|
||||||
|
}
|
||||||
|
expect := Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479")
|
||||||
|
if expect == nil {
|
||||||
|
t.Fatal("invalid uuid")
|
||||||
|
}
|
||||||
|
if !bytes.Equal(array.UUID(), expect) {
|
||||||
|
t.Fatal("invalid uuid")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkParse(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
uuid := Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479")
|
||||||
|
if uuid == nil {
|
||||||
|
b.Fatal("invalid uuid")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkNew(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
New()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkUUID_String(b *testing.B) {
|
||||||
|
uuid := Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479")
|
||||||
|
if uuid == nil {
|
||||||
|
b.Fatal("invalid uuid")
|
||||||
|
}
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
if uuid.String() == "" {
|
||||||
|
b.Fatal("invalid uuid")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkUUID_URN(b *testing.B) {
|
||||||
|
uuid := Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479")
|
||||||
|
if uuid == nil {
|
||||||
|
b.Fatal("invalid uuid")
|
||||||
|
}
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
if uuid.URN() == "" {
|
||||||
|
b.Fatal("invalid uuid")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkUUID_Array(b *testing.B) {
|
||||||
|
expect := Array{
|
||||||
|
0xf4, 0x7a, 0xc1, 0x0b,
|
||||||
|
0x58, 0xcc,
|
||||||
|
0x03, 0x72,
|
||||||
|
0x85, 0x67,
|
||||||
|
0x0e, 0x02, 0xb2, 0xc3, 0xd4, 0x79,
|
||||||
|
}
|
||||||
|
uuid := Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479")
|
||||||
|
if uuid == nil {
|
||||||
|
b.Fatal("invalid uuid")
|
||||||
|
}
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
if uuid.Array() != expect {
|
||||||
|
b.Fatal("invalid array")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkArray_UUID(b *testing.B) {
|
||||||
|
array := Array{
|
||||||
|
0xf4, 0x7a, 0xc1, 0x0b,
|
||||||
|
0x58, 0xcc,
|
||||||
|
0x03, 0x72,
|
||||||
|
0x85, 0x67,
|
||||||
|
0x0e, 0x02, 0xb2, 0xc3, 0xd4, 0x79,
|
||||||
|
}
|
||||||
|
expect := Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479")
|
||||||
|
if expect == nil {
|
||||||
|
b.Fatal("invalid uuid")
|
||||||
|
}
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
if !bytes.Equal(array.UUID(), expect) {
|
||||||
|
b.Fatal("invalid uuid")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
41
vendor/github.com/pborman/uuid/version1.go
generated
vendored
Normal file
41
vendor/github.com/pborman/uuid/version1.go
generated
vendored
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// Copyright 2011 Google Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package uuid
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewUUID returns a Version 1 UUID based on the current NodeID and clock
|
||||||
|
// sequence, and the current time. If the NodeID has not been set by SetNodeID
|
||||||
|
// or SetNodeInterface then it will be set automatically. If the NodeID cannot
|
||||||
|
// be set NewUUID returns nil. If clock sequence has not been set by
|
||||||
|
// SetClockSequence then it will be set automatically. If GetTime fails to
|
||||||
|
// return the current NewUUID returns nil.
|
||||||
|
func NewUUID() UUID {
|
||||||
|
if nodeID == nil {
|
||||||
|
SetNodeInterface("")
|
||||||
|
}
|
||||||
|
|
||||||
|
now, seq, err := GetTime()
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
uuid := make([]byte, 16)
|
||||||
|
|
||||||
|
time_low := uint32(now & 0xffffffff)
|
||||||
|
time_mid := uint16((now >> 32) & 0xffff)
|
||||||
|
time_hi := uint16((now >> 48) & 0x0fff)
|
||||||
|
time_hi |= 0x1000 // Version 1
|
||||||
|
|
||||||
|
binary.BigEndian.PutUint32(uuid[0:], time_low)
|
||||||
|
binary.BigEndian.PutUint16(uuid[4:], time_mid)
|
||||||
|
binary.BigEndian.PutUint16(uuid[6:], time_hi)
|
||||||
|
binary.BigEndian.PutUint16(uuid[8:], seq)
|
||||||
|
copy(uuid[10:], nodeID)
|
||||||
|
|
||||||
|
return uuid
|
||||||
|
}
|
25
vendor/github.com/pborman/uuid/version4.go
generated
vendored
Normal file
25
vendor/github.com/pborman/uuid/version4.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// Copyright 2011 Google Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package uuid
|
||||||
|
|
||||||
|
// Random returns a Random (Version 4) UUID or panics.
|
||||||
|
//
|
||||||
|
// The strength of the UUIDs is based on the strength of the crypto/rand
|
||||||
|
// package.
|
||||||
|
//
|
||||||
|
// A note about uniqueness derived from from the UUID Wikipedia entry:
|
||||||
|
//
|
||||||
|
// Randomly generated UUIDs have 122 random bits. One's annual risk of being
|
||||||
|
// hit by a meteorite is estimated to be one chance in 17 billion, that
|
||||||
|
// means the probability is about 0.00000000006 (6 × 10−11),
|
||||||
|
// equivalent to the odds of creating a few tens of trillions of UUIDs in a
|
||||||
|
// year and having one duplicate.
|
||||||
|
func NewRandom() UUID {
|
||||||
|
uuid := make([]byte, 16)
|
||||||
|
randomBits([]byte(uuid))
|
||||||
|
uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4
|
||||||
|
uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10
|
||||||
|
return uuid
|
||||||
|
}
|
25
vendor/vendor.json
vendored
Normal file
25
vendor/vendor.json
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"comment": "",
|
||||||
|
"ignore": "",
|
||||||
|
"package": [
|
||||||
|
{
|
||||||
|
"checksumSHA1": "KYCKbkGtO+RcwJf6gUijuop2cNQ=",
|
||||||
|
"path": "github.com/lib/pq",
|
||||||
|
"revision": "55196ec83d60d789e27b2627549bf00974a70084",
|
||||||
|
"revisionTime": "2016-03-28T15:34:59Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"checksumSHA1": "xppHi82MLqVx1eyQmbhTesAEjx8=",
|
||||||
|
"path": "github.com/lib/pq/oid",
|
||||||
|
"revision": "55196ec83d60d789e27b2627549bf00974a70084",
|
||||||
|
"revisionTime": "2016-03-28T15:34:59Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"checksumSHA1": "jam7OmzX4/mUJkR3iOPW5/+2IQ4=",
|
||||||
|
"path": "github.com/pborman/uuid",
|
||||||
|
"revision": "c55201b036063326c5b1b89ccfe45a184973d073",
|
||||||
|
"revisionTime": "2016-02-16T16:37:10Z"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"rootPath": "mcquay.me/vain"
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user