Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

init

  • Loading branch information...
commit df1f1317967381f224717825633bb31354d8767c 0 parents
Blake Mizerany authored
2  .gitignore
... ... @@ -0,0 +1,2 @@
  1 +.db
  2 +*.test
66 README.md
Source Rendered
... ... @@ -0,0 +1,66 @@
  1 +# pq - A pure Go postgres driver for Go's database/sql package
  2 +
  3 +## Install
  4 +
  5 + go get github.com/bmizerany/pq
  6 +
  7 +## Use
  8 +
  9 + package main
  10 +
  11 + import (
  12 + _ "github.com/bmizerany/pq"
  13 + "database/sql"
  14 + )
  15 +
  16 + func main() {
  17 + db, err := sql.Open("postgres", "user=pqgotest dbname=pqgotest sslmode=verify-full")
  18 + // ...
  19 + }
  20 +
  21 +**Connection String Parameters**
  22 +
  23 +These are a subset of the libpq connection parameters.
  24 +See http://www.postgresql.org/docs/9.0/static/libpq-connect.html
  25 +
  26 +* `dbname` - The name of the database to connect to
  27 +* `user` - The user to sign in as
  28 +* `password` - The user's password
  29 +* `host` - The host to connect to. Values that start with `/` are for unix domain sockets. (default is `localhost`)
  30 +* `port` - The port to bind to. (default is `5432`)
  31 +* `sslmode` - Whether or not to use SSL (default is `require`, this is not the default for libpq)
  32 + Valid values are:
  33 + * `disable` - No SSL
  34 + * `require` - Always SSL (skip verification)
  35 + * `verify-full` - Always SSL (require verification)
  36 +
  37 +See http://tip.golang.org/pkg/database/sql to learn how to use with `pq` through the `database/sql` package.
  38 +
  39 +## Features
  40 +
  41 +* SSL
  42 +* Handles bad connections for `database/sql`
  43 +* Scan `time.Time` correctly (i.e. `timestamp[tz]`, `time[tz]`, `date`)
  44 +* Scan binary blobs correctly (i.e. `bytea`)
  45 +
  46 +## Future / Things you can help with
  47 +
  48 +* Notifications: `LISTEN`/`NOTIFY`
  49 +* `hstore` sugar (i.e. handling hstore in `rows.Scan`)
  50 +
  51 +## Thank you (alphabetical)
  52 +
  53 +Some of these contributors are from the original library `bmizerany/pq.go` whose
  54 +code still exists in here.
  55 +
  56 +* Blake Gentry (bgentry)
  57 +* Brad Fitzpatrick (bradfitz)
  58 +* Daniel Farina (fdr)
  59 +* Everyone at The Go Team
  60 +* Federico Romero (federomero)
  61 +* Heroku (heroku)
  62 +* Keith Rarick (kr)
  63 +* Mike Lewis (mikelikespie)
  64 +* Ryan Smith (ryandotsmith)
  65 +* Samuel Stauffer (samuel)
  66 +* notedit (notedit)
74 buf.go
... ... @@ -0,0 +1,74 @@
  1 +package pq
  2 +
  3 +import (
  4 + "bytes"
  5 + "encoding/binary"
  6 +)
  7 +
  8 +type readBuf []byte
  9 +
  10 +func (b *readBuf) int32() (n int) {
  11 + n = int(binary.BigEndian.Uint32(*b))
  12 + *b = (*b)[4:]
  13 + return
  14 +}
  15 +
  16 +func (b *readBuf) int16() (n int) {
  17 + n = int(binary.BigEndian.Uint16(*b))
  18 + *b = (*b)[2:]
  19 + return
  20 +}
  21 +
  22 +var stringTerm = []byte{0}
  23 +
  24 +func (b *readBuf) string() string {
  25 + i := bytes.Index(*b, stringTerm)
  26 + if i < 0 {
  27 + errorf("invalid message format; expected string terminator")
  28 + }
  29 + s := (*b)[:i]
  30 + *b = (*b)[i+1:]
  31 + return string(s)
  32 +}
  33 +
  34 +func (b *readBuf) next(n int) (v []byte) {
  35 + v = (*b)[:n]
  36 + *b = (*b)[n:]
  37 + return
  38 +}
  39 +
  40 +func (b *readBuf) byte() byte {
  41 + return b.next(1)[0]
  42 +}
  43 +
  44 +type writeBuf []byte
  45 +
  46 +func newWriteBuf(c byte) *writeBuf {
  47 + b := make(writeBuf, 5)
  48 + b[0] = c
  49 + return &b
  50 +}
  51 +
  52 +func (b *writeBuf) int32(n int) {
  53 + x := make([]byte, 4)
  54 + binary.BigEndian.PutUint32(x, uint32(n))
  55 + *b = append(*b, x...)
  56 +}
  57 +
  58 +func (b *writeBuf) int16(n int) {
  59 + x := make([]byte, 2)
  60 + binary.BigEndian.PutUint16(x, uint16(n))
  61 + *b = append(*b, x...)
  62 +}
  63 +
  64 +func (b *writeBuf) string(s string) {
  65 + *b = append(*b, (s + "\000")...)
  66 +}
  67 +
  68 +func (b *writeBuf) byte(c byte) {
  69 + *b = append(*b, c)
  70 +}
  71 +
  72 +func (b *writeBuf) bytes(v []byte) {
  73 + *b = append(*b, v...)
  74 +}
486 conn.go
... ... @@ -0,0 +1,486 @@
  1 +package pq
  2 +
  3 +import (
  4 + "crypto/md5"
  5 + "crypto/tls"
  6 + "database/sql"
  7 + "database/sql/driver"
  8 + "encoding/binary"
  9 + "errors"
  10 + "fmt"
  11 + "io"
  12 + "net"
  13 + "strconv"
  14 + "strings"
  15 +)
  16 +
  17 +var (
  18 + ErrSSLNotSupported = errors.New("pq: SSL is not enabled on the server")
  19 + ErrNotSupported = errors.New("pq: this is postgres, a real database, this isn't a valid command")
  20 +)
  21 +
  22 +type drv struct{}
  23 +
  24 +func (d *drv) Open(name string) (driver.Conn, error) {
  25 + return Open(name)
  26 +}
  27 +
  28 +func init() {
  29 + sql.Register("postgres", &drv{})
  30 +}
  31 +
  32 +type conn struct {
  33 + c net.Conn
  34 + namei int
  35 +}
  36 +
  37 +func Open(name string) (_ driver.Conn, err error) {
  38 + defer errRecover(&err)
  39 +
  40 + o := make(Values)
  41 + o.Set("host", "localhost")
  42 + o.Set("port", "5432")
  43 + parseOpts(name, o)
  44 +
  45 + c, err := net.Dial(network(o))
  46 + if err != nil {
  47 + return nil, err
  48 + }
  49 +
  50 + cn := &conn{c: c}
  51 + cn.ssl(o)
  52 + cn.startup(o)
  53 + return cn, nil
  54 +}
  55 +
  56 +func network(o Values) (string, string) {
  57 + host := o.Get("host")
  58 +
  59 + if strings.HasPrefix(host, "/") {
  60 + return "unix", host
  61 + }
  62 +
  63 + return "tcp", host + ":" + o.Get("port")
  64 +}
  65 +
  66 +type Values map[string]string
  67 +
  68 +func (vs Values) Set(k, v string) {
  69 + vs[k] = v
  70 +}
  71 +
  72 +func (vs Values) Get(k string) (v string) {
  73 + v, _ = vs[k]
  74 + return
  75 +}
  76 +
  77 +func parseOpts(name string, o Values) {
  78 + if len(name) == 0 {
  79 + return
  80 + }
  81 +
  82 + ps := strings.Split(name, " ")
  83 + for _, p := range ps {
  84 + kv := strings.Split(p, "=")
  85 + if len(kv) < 2 {
  86 + errorf("invalid option: %q", p)
  87 + }
  88 + o.Set(kv[0], kv[1])
  89 + }
  90 +}
  91 +
  92 +func (cn *conn) Begin() (driver.Tx, error) {
  93 + st, err := cn.Prepare("BEGIN")
  94 + if err != nil {
  95 + return nil, err
  96 + }
  97 +
  98 + _, err = st.Exec(nil)
  99 + return cn, err
  100 +}
  101 +
  102 +func (cn *conn) Commit() error {
  103 + st, err := cn.Prepare("COMMIT")
  104 + if err != nil {
  105 + return err
  106 + }
  107 +
  108 + _, err = st.Exec(nil)
  109 + return err
  110 +}
  111 +
  112 +func (cn *conn) Rollback() error {
  113 + st, err := cn.Prepare("ROLLBACK")
  114 + if err != nil {
  115 + return err
  116 + }
  117 +
  118 + _, err = st.Exec(nil)
  119 + return err
  120 +}
  121 +
  122 +func (cn *conn) gname() string {
  123 + cn.namei++
  124 + return strconv.FormatInt(int64(cn.namei), 10)
  125 +}
  126 +
  127 +func (cn *conn) Prepare(q string) (_ driver.Stmt, err error) {
  128 + defer errRecover(&err)
  129 +
  130 + st := &stmt{cn: cn, name: cn.gname()}
  131 +
  132 + b := newWriteBuf('P')
  133 + b.string(st.name)
  134 + b.string(q)
  135 + b.int16(0)
  136 + cn.send(b)
  137 +
  138 + b = newWriteBuf('D')
  139 + b.byte('S')
  140 + b.string(st.name)
  141 + cn.send(b)
  142 +
  143 + cn.send(newWriteBuf('H'))
  144 +
  145 + t, r := cn.recv()
  146 + if t != '1' {
  147 + errorf("unexpected parse response: %q", t)
  148 + }
  149 +
  150 + t, r = cn.recv()
  151 + if t != 't' {
  152 + errorf("unexpected describe params response: %q", t)
  153 + }
  154 + st.nparams = int(r.int16())
  155 +
  156 + t, r = cn.recv()
  157 + switch t {
  158 + case 'T':
  159 + n := r.int16()
  160 + st.cols = make([]string, n)
  161 + st.ooid = make([]int, n)
  162 + for i := range st.cols {
  163 + st.cols[i] = r.string()
  164 + r.next(6)
  165 + st.ooid[i] = r.int32()
  166 + r.next(8)
  167 + }
  168 + case 'n':
  169 + // no data
  170 + default:
  171 + errorf("unexpected describe rows response: %q", t)
  172 + }
  173 +
  174 + return st, nil
  175 +}
  176 +
  177 +func (cn *conn) Close() error {
  178 + return cn.c.Close()
  179 +}
  180 +
  181 +// Assumes len(*m) is > 5
  182 +func (cn *conn) send(m *writeBuf) {
  183 + b := (*m)[1:]
  184 + binary.BigEndian.PutUint32(b, uint32(len(b)))
  185 +
  186 + if (*m)[0] == 0 {
  187 + *m = b
  188 + }
  189 +
  190 + _, err := cn.c.Write(*m)
  191 + if err != nil {
  192 + panic(err)
  193 + }
  194 +}
  195 +
  196 +func (cn *conn) recv() (t byte, r *readBuf) {
  197 + for {
  198 + t, r = cn.recv1()
  199 + switch t {
  200 + case 'E':
  201 + panic(parseError(r))
  202 + case 'N':
  203 + // TODO(bmizerany): log notices?
  204 + default:
  205 + return
  206 + }
  207 + }
  208 +
  209 + panic("not reached")
  210 +}
  211 +
  212 +func (cn *conn) recv1() (byte, *readBuf) {
  213 + x := make([]byte, 5)
  214 + _, err := cn.c.Read(x)
  215 + if err != nil {
  216 + panic(err)
  217 + }
  218 +
  219 + b := readBuf(x[1:])
  220 + y := make([]byte, b.int32()-4)
  221 + _, err = io.ReadFull(cn.c, y)
  222 + if err != nil {
  223 + panic(err)
  224 + }
  225 +
  226 + return x[0], (*readBuf)(&y)
  227 +}
  228 +
  229 +func (cn *conn) ssl(o Values) {
  230 + tlsConf := tls.Config{}
  231 + switch mode := o.Get("sslmode"); mode {
  232 + case "require", "":
  233 + tlsConf.InsecureSkipVerify = true
  234 + case "verify-full":
  235 + // fall out
  236 + case "disable":
  237 + return
  238 + default:
  239 + errorf(`unsupported sslmode %q; only "require" (default), "verify-full", and "disable" supported`, mode)
  240 + }
  241 +
  242 + w := newWriteBuf(0)
  243 + w.int32(80877103)
  244 + cn.send(w)
  245 +
  246 + b := make([]byte, 1)
  247 + _, err := io.ReadFull(cn.c, b)
  248 + if err != nil {
  249 + panic(err)
  250 + }
  251 +
  252 + if b[0] != 'S' {
  253 + panic(ErrSSLNotSupported)
  254 + }
  255 +
  256 + cn.c = tls.Client(cn.c, &tlsConf)
  257 +}
  258 +
  259 +func (cn *conn) startup(o Values) {
  260 + w := newWriteBuf(0)
  261 + w.int32(196608)
  262 + w.string("user")
  263 + w.string(o.Get("user"))
  264 + w.string("database")
  265 + w.string(o.Get("dbname"))
  266 + w.string("")
  267 + cn.send(w)
  268 +
  269 + for {
  270 + t, r := cn.recv()
  271 + switch t {
  272 + case 'K', 'S':
  273 + case 'R':
  274 + cn.auth(r)
  275 + case 'Z':
  276 + return
  277 + default:
  278 + errorf("unknown response for startup: %q", t)
  279 + }
  280 + }
  281 +}
  282 +
  283 +func (cn *conn) auth(r *readBuf) {
  284 + switch code := r.int32(); code {
  285 + case 0:
  286 + // OK
  287 + case 5:
  288 + s := string(r.next(4))
  289 + w := newWriteBuf('p')
  290 + w.string("md5" + md5s(md5s("foo"+"pqgotest")+s))
  291 + cn.send(w)
  292 +
  293 + t, r := cn.recv()
  294 + if t != 'R' {
  295 + errorf("unexpected password response: %q", t)
  296 + }
  297 +
  298 + if r.int32() != 0 {
  299 + errorf("unexpected authentication resoonse: %q", t)
  300 + }
  301 + default:
  302 + errorf("unknown authentication response: %d", code)
  303 + }
  304 +}
  305 +
  306 +type stmt struct {
  307 + cn *conn
  308 + name string
  309 + cols []string
  310 + nparams int
  311 + ooid []int
  312 + closed bool
  313 +}
  314 +
  315 +func (st *stmt) Close() (err error) {
  316 + if st.closed {
  317 + return nil
  318 + }
  319 +
  320 + defer errRecover(&err)
  321 +
  322 + w := newWriteBuf('C')
  323 + w.byte('S')
  324 + w.string(st.name)
  325 + st.cn.send(w)
  326 +
  327 + st.cn.send(newWriteBuf('S'))
  328 +
  329 + t, _ := st.cn.recv()
  330 + if t != '3' {
  331 + errorf("unexpected close response: %q", t)
  332 + }
  333 + st.closed = true
  334 +
  335 + t, _ = st.cn.recv()
  336 + if t != 'Z' {
  337 + errorf("expected ready for query, but got: %q", t)
  338 + }
  339 +
  340 + return nil
  341 +}
  342 +
  343 +func (st *stmt) Query(v []driver.Value) (_ driver.Rows, err error) {
  344 + defer errRecover(&err)
  345 +
  346 + st.exec(v)
  347 +
  348 + return &rows{st: st}, nil
  349 +}
  350 +
  351 +func (st *stmt) Exec(v []driver.Value) (res driver.Result, err error) {
  352 + defer errRecover(&err)
  353 + st.exec(v)
  354 +
  355 + for {
  356 + t, r := st.cn.recv()
  357 + switch t {
  358 + case 'C':
  359 + res = parseComplete(r.string())
  360 + case 'Z':
  361 + // done
  362 + return
  363 + case 'D':
  364 + errorf("unexpected data row returned in Exec; check your query")
  365 + case 'S':
  366 + // Ignore
  367 + default:
  368 + errorf("unknown exec response: %q", t)
  369 + }
  370 + }
  371 +
  372 + panic("not reached")
  373 +}
  374 +
  375 +func (st *stmt) exec(v []driver.Value) {
  376 + w := newWriteBuf('B')
  377 + w.string("")
  378 + w.string(st.name)
  379 + w.int16(0)
  380 + w.int16(len(v))
  381 + for _, x := range v {
  382 + if x == nil {
  383 + w.int32(-1)
  384 + } else {
  385 + b := encode(x)
  386 + w.int32(len(b))
  387 + w.bytes(b)
  388 + }
  389 + }
  390 + w.int16(0)
  391 + st.cn.send(w)
  392 +
  393 + w = newWriteBuf('E')
  394 + w.string("")
  395 + w.int32(0)
  396 + st.cn.send(w)
  397 +
  398 + st.cn.send(newWriteBuf('S'))
  399 +
  400 + t, _ := st.cn.recv()
  401 + if t != '2' {
  402 + errorf("unexpected bind response: %q", t)
  403 + }
  404 +}
  405 +
  406 +func (st *stmt) NumInput() int {
  407 + return st.nparams
  408 +}
  409 +
  410 +type result int64
  411 +
  412 +func (i result) RowsAffected() (int64, error) {
  413 + return int64(i), nil
  414 +}
  415 +
  416 +func (i result) LastInsertId() (int64, error) {
  417 + return 0, ErrNotSupported
  418 +}
  419 +
  420 +func parseComplete(s string) driver.Result {
  421 + parts := strings.Split(s, " ")
  422 + n, _ := strconv.ParseInt(parts[len(parts)-1], 10, 64)
  423 + return result(n)
  424 +}
  425 +
  426 +type rows struct {
  427 + st *stmt
  428 + done bool
  429 +}
  430 +
  431 +func (rs *rows) Close() error {
  432 + for {
  433 + err := rs.Next(nil)
  434 + switch err {
  435 + case nil:
  436 + case io.EOF:
  437 + return nil
  438 + default:
  439 + return err
  440 + }
  441 + }
  442 + panic("not reached")
  443 +}
  444 +
  445 +func (rs *rows) Columns() []string {
  446 + return rs.st.cols
  447 +}
  448 +
  449 +func (rs *rows) Next(dest []driver.Value) (err error) {
  450 + if rs.done {
  451 + return io.EOF
  452 + }
  453 +
  454 + defer errRecover(&err)
  455 +
  456 + for {
  457 + t, r := rs.st.cn.recv()
  458 + switch t {
  459 + case 'C', 'S':
  460 + continue
  461 + case 'Z':
  462 + rs.done = true
  463 + return io.EOF
  464 + case 'D':
  465 + n := r.int16()
  466 + for i := 0; i < len(dest) && i < n; i++ {
  467 + l := r.int32()
  468 + if l == -1 {
  469 + continue
  470 + }
  471 + dest[i] = decode(r.next(l), rs.st.ooid[i])
  472 + }
  473 + return
  474 + default:
  475 + errorf("unexpected message after execute: %q", t)
  476 + }
  477 + }
  478 +
  479 + panic("not reached")
  480 +}
  481 +
  482 +func md5s(s string) string {
  483 + h := md5.New()
  484 + h.Write([]byte(s))
  485 + return fmt.Sprintf("%x", h.Sum(nil))
  486 +}
247 conn_test.go
... ... @@ -0,0 +1,247 @@
  1 +package pq
  2 +
  3 +import (
  4 + "database/sql"
  5 + "database/sql/driver"
  6 + "io"
  7 + "reflect"
  8 + "testing"
  9 + "time"
  10 +)
  11 +
  12 +var cs = "user=pqgotest sslmode=disable"
  13 +
  14 +func TestExec(t *testing.T) {
  15 + db, err := sql.Open("postgres", cs)
  16 + if err != nil {
  17 + t.Fatal(err)
  18 + }
  19 + defer db.Close()
  20 +
  21 + db.Exec("DELETE FROM temp")
  22 +
  23 + r, err := db.Exec("INSERT INTO temp VALUES (1)")
  24 + if err != nil {
  25 + t.Fatal(err)
  26 + }
  27 +
  28 + if n, _ := r.RowsAffected(); n != 1 {
  29 + t.Fatalf("expected 1 row affected, not %d", n)
  30 + }
  31 +}
  32 +
  33 +func TestStatment(t *testing.T) {
  34 + db, err := sql.Open("postgres", cs)
  35 + if err != nil {
  36 + t.Fatal(err)
  37 + }
  38 + defer db.Close()
  39 +
  40 + st, err := db.Prepare("SELECT 1")
  41 + if err != nil {
  42 + t.Fatal(err)
  43 + }
  44 +
  45 + st1, err := db.Prepare("SELECT 2")
  46 + if err != nil {
  47 + t.Fatal(err)
  48 + }
  49 +
  50 + r, err := st.Query()
  51 + if err != nil {
  52 + t.Fatal(err)
  53 + }
  54 +
  55 + if !r.Next() {
  56 + t.Fatal("expected row")
  57 + }
  58 +
  59 + var i int
  60 + err = r.Scan(&i)
  61 + if err != nil {
  62 + t.Fatal(err)
  63 + }
  64 +
  65 + if i != 1 {
  66 + t.Fatalf("expected 1, got %d", i)
  67 + }
  68 +
  69 + // st1
  70 +
  71 + r, err = st1.Query()
  72 + if err != nil {
  73 + t.Fatal(err)
  74 + }
  75 +
  76 + if !r.Next() {
  77 + if r.Err() != nil {
  78 + t.Fatal(r.Err())
  79 + }
  80 + t.Fatal("expected row")
  81 + }
  82 +
  83 + err = r.Scan(&i)
  84 + if err != nil {
  85 + t.Fatal(err)
  86 + }
  87 +
  88 + if i != 2 {
  89 + t.Fatalf("expected 2, got %d", i)
  90 + }
  91 +}
  92 +
  93 +func TestRowsCloseBeforeDone(t *testing.T) {
  94 + db, err := sql.Open("postgres", cs)
  95 + if err != nil {
  96 + t.Fatal(err)
  97 + }
  98 +
  99 + r, err := db.Query("SELECT 1")
  100 + if err != nil {
  101 + t.Fatal(err)
  102 + }
  103 +
  104 + err = r.Close()
  105 + if err != nil {
  106 + t.Fatal(err)
  107 + }
  108 +
  109 + if r.Next() {
  110 + t.Fatal("unexpected row")
  111 + }
  112 +
  113 + if r.Err() != nil {
  114 + t.Fatal(r.Err())
  115 + }
  116 +}
  117 +
  118 +func TestEncodeDecode(t *testing.T) {
  119 + db, err := sql.Open("postgres", cs)
  120 + if err != nil {
  121 + t.Fatal(err)
  122 + }
  123 + defer db.Close()
  124 +
  125 + q := `
  126 + SELECT
  127 + '\x000102'::bytea,
  128 + 'foobar'::text,
  129 + NULL::integer,
  130 + '2000-1-1 01:02:03.04-7'::timestamptz
  131 + WHERE
  132 + '\x000102'::bytea = $1
  133 + AND 'foobar'::text = $2
  134 + AND $3::integer is NULL
  135 + `
  136 + // AND '2000-1-1 12:00:00.000000-7'::timestamp = $3
  137 +
  138 + exp1 := []byte{0, 1, 2}
  139 + exp2 := "foobar"
  140 +
  141 + r, err := db.Query(q, exp1, exp2, nil)
  142 + if err != nil {
  143 + t.Fatal(err)
  144 + }
  145 + defer r.Close()
  146 +
  147 + if !r.Next() {
  148 + if r.Err() != nil {
  149 + t.Fatal(r.Err())
  150 + }
  151 + t.Fatal("expected row")
  152 + }
  153 +
  154 + var got1 []byte
  155 + var got2 string
  156 + var got3 = sql.NullInt64{Valid: true}
  157 + var got4 time.Time
  158 +
  159 + err = r.Scan(&got1, &got2, &got3, &got4)
  160 + if err != nil {
  161 + t.Fatal(err)
  162 + }
  163 +
  164 + if !reflect.DeepEqual(exp1, got1) {
  165 + t.Errorf("expected %q byte: %q", exp1, got1)
  166 + }
  167 +
  168 + if !reflect.DeepEqual(exp2, got2) {
  169 + t.Errorf("expected %q byte: %q", exp2, got2)
  170 + }
  171 +
  172 + if got3.Valid {
  173 + t.Fatal("expected invalid")
  174 + }
  175 +
  176 + if got4.Year() != 2000 {
  177 + t.Fatal("wrong year")
  178 + }
  179 +}
  180 +
  181 +func TestNoData(t *testing.T) {
  182 + db, err := sql.Open("postgres", cs)
  183 + if err != nil {
  184 + t.Fatal(err)
  185 + }
  186 + defer db.Close()
  187 +
  188 + st, err := db.Prepare("SELECT 1 WHERE true = false")
  189 + if err != nil {
  190 + t.Fatal(err)
  191 + }
  192 + defer st.Close()
  193 +
  194 + r, err := st.Query()
  195 + if err != nil {
  196 + t.Fatal(err)
  197 + }
  198 + defer r.Close()
  199 +
  200 + if r.Next() {
  201 + if r.Err() != nil {
  202 + t.Fatal(r.Err())
  203 + }
  204 + t.Fatal("unexpected row")
  205 + }
  206 +}
  207 +
  208 +func TestPGError(t *testing.T) {
  209 + db, err := sql.Open("postgres", "user=asdf")
  210 + if err != nil {
  211 + t.Fatal(err)
  212 + }
  213 + defer db.Close()
  214 +
  215 + _, err = db.Begin()
  216 + if err == nil {
  217 + t.Fatal("expected error")
  218 + }
  219 +
  220 + if err != driver.ErrBadConn {
  221 + t.Fatalf("expected a PGError, got: %v", err)
  222 + }
  223 +}
  224 +
  225 +func TestBadConn(t *testing.T) {
  226 + var err error
  227 +
  228 + func() {
  229 + defer errRecover(&err)
  230 + panic(io.EOF)
  231 + }()
  232 +
  233 + if err != driver.ErrBadConn {
  234 + t.Fatalf("expected driver.ErrBadConn, got: %#v", err)
  235 + }
  236 +
  237 + func() {
  238 + defer errRecover(&err)
  239 + e := &PGError{c: make(map[byte]string)}
  240 + e.c['S'] = Efatal
  241 + panic(e)
  242 + }()
  243 +
  244 + if err != driver.ErrBadConn {
  245 + t.Fatalf("expected driver.ErrBadConn, got: %#v", err)
  246 + }
  247 +}
63 encode.go
... ... @@ -0,0 +1,63 @@
  1 +package pq
  2 +
  3 +import (
  4 + "encoding/hex"
  5 + "fmt"
  6 + "time"
  7 +)
  8 +
  9 +func encode(x interface{}) []byte {
  10 + const timeFormat = "2006-01-02 15:04:05.0000-07"
  11 +
  12 + switch v := x.(type) {
  13 + case int64:
  14 + return []byte(fmt.Sprintf("%d", v))
  15 + case float32, float64:
  16 + return []byte(fmt.Sprintf("%f", v))
  17 + case []byte:
  18 + return []byte(fmt.Sprintf("\\x%x", v))
  19 + case string:
  20 + return []byte(v)
  21 + case bool:
  22 + return []byte(fmt.Sprintf("%t", v))
  23 + case time.Time:
  24 + return []byte(v.Format(timeFormat))
  25 + default:
  26 + errorf("encode: unknown type for %T", v)
  27 + }
  28 +
  29 + panic("not reached")
  30 +}
  31 +
  32 +func decode(s []byte, typ int) interface{} {
  33 + switch typ {
  34 + case t_bytea:
  35 + s = s[2:] // trim off "\\x"
  36 + d := make([]byte, hex.DecodedLen(len(s)))
  37 + _, err := hex.Decode(d, s)
  38 + if err != nil {
  39 + errorf("%s", err)
  40 + }
  41 + return d
  42 + case t_timestamptz:
  43 + return mustParse("2006-01-02 15:04:05-07", s)
  44 + case t_timestamp:
  45 + return mustParse("2006-01-02 15:04:05", s)
  46 + case t_time:
  47 + return mustParse("15:04:05", s)
  48 + case t_timetz:
  49 + return mustParse("15:04:05-07", s)
  50 + case t_date:
  51 + return mustParse("2006-01-02", s)
  52 + }
  53 +
  54 + return s
  55 +}
  56 +
  57 +func mustParse(f string, s []byte) time.Time {
  58 + t, err := time.Parse(f, string(s))
  59 + if err != nil {
  60 + errorf("decode: %s", err)
  61 + }
  62 + return t
  63 +}
78 error.go
... ... @@ -0,0 +1,78 @@
  1 +package pq
  2 +
  3 +import (
  4 + "database/sql/driver"
  5 + "fmt"
  6 + "io"
  7 + "runtime"
  8 +)
  9 +
  10 +const (
  11 + Efatal = "FATAL"
  12 + Epanic = "PANIC"
  13 + Ewarning = "WARNING"
  14 + Enotice = "NOTICE"
  15 + Edebug = "DEBUG"
  16 + Einfo = "INFO"
  17 + Elog = "LOG"
  18 +)
  19 +
  20 +type Error error
  21 +
  22 +type PGError struct {
  23 + c map[byte]string
  24 +}
  25 +
  26 +func parseError(r *readBuf) *PGError {
  27 + err := &PGError{make(map[byte]string)}
  28 + for t := r.byte(); t != 0; t = r.byte() {
  29 + err.c[t] = r.string()
  30 + }
  31 + return err
  32 +}
  33 +
  34 +func (err *PGError) Get(k byte) (v string) {
  35 + v, _ = err.c[k]
  36 + return
  37 +}
  38 +
  39 +func (err *PGError) Fatal() bool {
  40 + return err.Get('S') == Efatal
  41 +}
  42 +
  43 +func (err *PGError) Error() string {
  44 + var s string
  45 + for k, v := range err.c {
  46 + s += fmt.Sprintf(" %c:%q", k, v)
  47 + }
  48 + return "pq: " + s[1:]
  49 +}
  50 +
  51 +func errorf(s string, args ...interface{}) {
  52 + panic(Error(fmt.Errorf("pq: %s", fmt.Sprintf(s, args...))))
  53 +}
  54 +
  55 +func errRecover(err *error) {
  56 + e := recover()
  57 + switch v := e.(type) {
  58 + case nil:
  59 + // Do nothing
  60 + case runtime.Error:
  61 + panic(v)
  62 + case *PGError:
  63 + if v.Fatal() {
  64 + *err = driver.ErrBadConn
  65 + } else {
  66 + *err = v
  67 + }
  68 + case error:
  69 + if v == io.EOF {
  70 + *err = driver.ErrBadConn
  71 + } else {
  72 + *err = v
  73 + }
  74 +
  75 + default:
  76 + panic(fmt.Sprintf("unknown error: %#v", e))
  77 + }
  78 +}
317 types.go
... ... @@ -0,0 +1,317 @@
  1 +package pq
  2 +
  3 +const (
  4 + t_bool = 16
  5 + t_bytea = 17
  6 + t_char = 18
  7 + t_name = 19
  8 + t_int8 = 20
  9 + t_int2 = 21
  10 + t_int2vector = 22
  11 + t_int4 = 23
  12 + t_regproc = 24
  13 + t_text = 25
  14 + t_oid = 26
  15 + t_tid = 27
  16 + t_xid = 28
  17 + t_cid = 29
  18 + t_oidvector = 30
  19 + t_pg_type = 71
  20 + t_pg_attribute = 75
  21 + t_pg_proc = 81
  22 + t_pg_class = 83
  23 + t_xml = 142
  24 + t__xml = 143
  25 + t_pg_node_tree = 194
  26 + t_smgr = 210
  27 + t_point = 600
  28 + t_lseg = 601
  29 + t_path = 602
  30 + t_box = 603
  31 + t_polygon = 604
  32 + t_line = 628
  33 + t__line = 629
  34 + t_float4 = 700
  35 + t_float8 = 701
  36 + t_abstime = 702
  37 + t_reltime = 703
  38 + t_tinterval = 704
  39 + t_unknown = 705
  40 + t_circle = 718
  41 + t__circle = 719
  42 + t_money = 790
  43 + t__money = 791
  44 + t_macaddr = 829
  45 + t_inet = 869
  46 + t_cidr = 650
  47 + t__bool = 1000
  48 + t__bytea = 1001
  49 + t__char = 1002
  50 + t__name = 1003
  51 + t__int2 = 1005
  52 + t__int2vector = 1006
  53 + t__int4 = 1007
  54 + t__regproc = 1008
  55 + t__text = 1009
  56 + t__oid = 1028
  57 + t__tid = 1010
  58 + t__xid = 1011
  59 + t__cid = 1012
  60 + t__oidvector = 1013
  61 + t__bpchar = 1014
  62 + t__varchar = 1015
  63 + t__int8 = 1016
  64 + t__point = 1017
  65 + t__lseg = 1018
  66 + t__path = 1019
  67 + t__box = 1020
  68 + t__float4 = 1021
  69 + t__float8 = 1022
  70 + t__abstime = 1023
  71 + t__reltime = 1024
  72 + t__tinterval = 1025
  73 + t__polygon = 1027
  74 + t_aclitem = 1033
  75 + t__aclitem = 1034
  76 + t__macaddr = 1040
  77 + t__inet = 1041
  78 + t__cidr = 651
  79 + t__cstring = 1263
  80 + t_bpchar = 1042
  81 + t_varchar = 1043
  82 + t_date = 1082
  83 + t_time = 1083
  84 + t_timestamp = 1114
  85 + t__timestamp = 1115
  86 + t__date = 1182
  87 + t__time = 1183
  88 + t_timestamptz = 1184
  89 + t__timestamptz = 1185
  90 + t_interval = 1186
  91 + t__interval = 1187
  92 + t__numeric = 1231
  93 + t_timetz = 1266
  94 + t__timetz = 1270
  95 + t_bit = 1560
  96 + t__bit = 1561
  97 + t_varbit = 1562
  98 + t__varbit = 1563
  99 + t_numeric = 1700
  100 + t_refcursor = 1790
  101 + t__refcursor = 2201
  102 + t_regprocedure = 2202
  103 + t_regoper = 2203
  104 + t_regoperator = 2204
  105 + t_regclass = 2205
  106 + t_regtype = 2206
  107 + t__regprocedure = 2207
  108 + t__regoper = 2208
  109 + t__regoperator = 2209
  110 + t__regclass = 2210
  111 + t__regtype = 2211
  112 + t_uuid = 2950
  113 + t__uuid = 2951
  114 + t_tsvector = 3614
  115 + t_gtsvector = 3642
  116 + t_tsquery = 3615
  117 + t_regconfig = 3734
  118 + t_regdictionary = 3769
  119 + t__tsvector = 3643
  120 + t__gtsvector = 3644
  121 + t__tsquery = 3645
  122 + t__regconfig = 3735
  123 + t__regdictionary = 3770
  124 + t_txid_snapshot = 2970
  125 + t__txid_snapshot = 2949
  126 + t_record = 2249
  127 + t__record = 2287
  128 + t_cstring = 2275
  129 + t_any = 2276
  130 + t_anyarray = 2277
  131 + t_void = 2278
  132 + t_trigger = 2279
  133 + t_language_handler = 2280
  134 + t_internal = 2281
  135 + t_opaque = 2282
  136 + t_anyelement = 2283
  137 + t_anynonarray = 2776
  138 + t_anyenum = 3500
  139 + t_fdw_handler = 3115
  140 + t_pg_attrdef = 10000
  141 + t_pg_constraint = 10001
  142 + t_pg_inherits = 10002
  143 + t_pg_index = 10003
  144 + t_pg_operator = 10004
  145 + t_pg_opfamily = 10005
  146 + t_pg_opclass = 10006
  147 + t_pg_am = 10117
  148 + t_pg_amop = 10118
  149 + t_pg_amproc = 10478
  150 + t_pg_language = 10731
  151 + t_pg_largeobject_metadata = 10732
  152 + t_pg_largeobject = 10733
  153 + t_pg_aggregate = 10734
  154 + t_pg_statistic = 10735
  155 + t_pg_rewrite = 10736
  156 + t_pg_trigger = 10737
  157 + t_pg_description = 10738
  158 + t_pg_cast = 10739
  159 + t_pg_enum = 10936
  160 + t_pg_namespace = 10937
  161 + t_pg_conversion = 10938
  162 + t_pg_depend = 10939
  163 + t_pg_database = 1248
  164 + t_pg_db_role_setting = 10940
  165 + t_pg_tablespace = 10941
  166 + t_pg_pltemplate = 10942
  167 + t_pg_authid = 2842
  168 + t_pg_auth_members = 2843
  169 + t_pg_shdepend = 10943
  170 + t_pg_shdescription = 10944
  171 + t_pg_ts_config = 10945
  172 + t_pg_ts_config_map = 10946
  173 + t_pg_ts_dict = 10947
  174 + t_pg_ts_parser = 10948
  175 + t_pg_ts_template = 10949
  176 + t_pg_extension = 10950
  177 + t_pg_foreign_data_wrapper = 10951
  178 + t_pg_foreign_server = 10952
  179 + t_pg_user_mapping = 10953
  180 + t_pg_foreign_table = 10954
  181 + t_pg_default_acl = 10955
  182 + t_pg_seclabel = 10956
  183 + t_pg_collation = 10957
  184 + t_pg_toast_2604 = 10958
  185 + t_pg_toast_2606 = 10959
  186 + t_pg_toast_2609 = 10960
  187 + t_pg_toast_1255 = 10961
  188 + t_pg_toast_2618 = 10962
  189 + t_pg_toast_3596 = 10963
  190 + t_pg_toast_2619 = 10964
  191 + t_pg_toast_2620 = 10965
  192 + t_pg_toast_1262 = 10966
  193 + t_pg_toast_2396 = 10967
  194 + t_pg_toast_2964 = 10968
  195 + t_pg_roles = 10970
  196 + t_pg_shadow = 10973
  197 + t_pg_group = 10976
  198 + t_pg_user = 10979
  199 + t_pg_rules = 10982
  200 + t_pg_views = 10986
  201 + t_pg_tables = 10989
  202 + t_pg_indexes = 10993
  203 + t_pg_stats = 10997
  204 + t_pg_locks = 11001
  205 + t_pg_cursors = 11004
  206 + t_pg_available_extensions = 11007
  207 + t_pg_available_extension_versions = 11010
  208 + t_pg_prepared_xacts = 11013
  209 + t_pg_prepared_statements = 11017
  210 + t_pg_seclabels = 11020
  211 + t_pg_settings = 11024
  212 + t_pg_timezone_abbrevs = 11029
  213 + t_pg_timezone_names = 11032
  214 + t_pg_stat_all_tables = 11035
  215 + t_pg_stat_xact_all_tables = 11039
  216 + t_pg_stat_sys_tables = 11043
  217 + t_pg_stat_xact_sys_tables = 11047
  218 + t_pg_stat_user_tables = 11050
  219 + t_pg_stat_xact_user_tables = 11054
  220 + t_pg_statio_all_tables = 11057
  221 + t_pg_statio_sys_tables = 11061
  222 + t_pg_statio_user_tables = 11064
  223 + t_pg_stat_all_indexes = 11067
  224 + t_pg_stat_sys_indexes = 11071
  225 + t_pg_stat_user_indexes = 11074
  226 + t_pg_statio_all_indexes = 11077
  227 + t_pg_statio_sys_indexes = 11081
  228 + t_pg_statio_user_indexes = 11084
  229 + t_pg_statio_all_sequences = 11087
  230 + t_pg_statio_sys_sequences = 11090
  231 + t_pg_statio_user_sequences = 11093
  232 + t_pg_stat_activity = 11096
  233 + t_pg_stat_replication = 11099
  234 + t_pg_stat_database = 11102
  235 + t_pg_stat_database_conflicts = 11105
  236 + t_pg_stat_user_functions = 11108
  237 + t_pg_stat_xact_user_functions = 11112
  238 + t_pg_stat_bgwriter = 11116
  239 + t_pg_user_mappings = 11119
  240 + t_cardinal_number = 11669
  241 + t_character_data = 11671
  242 + t_sql_identifier = 11672
  243 + t_information_schema_catalog_name = 11674
  244 + t_time_stamp = 11676
  245 + t_yes_or_no = 11677
  246 + t_applicable_roles = 11680
  247 + t_administrable_role_authorizations = 11684
  248 + t_attributes = 11687
  249 + t_character_sets = 11691
  250 + t_check_constraint_routine_usage = 11695
  251 + t_check_constraints = 11699
  252 + t_collations = 11703
  253 + t_collation_character_set_applicability = 11706
  254 + t_column_domain_usage = 11709
  255 + t_column_privileges = 11713
  256 + t_column_udt_usage = 11717
  257 + t_columns = 11721
  258 + t_constraint_column_usage = 11725
  259 + t_constraint_table_usage = 11729
  260 + t_domain_constraints = 11733
  261 + t_domain_udt_usage = 11737
  262 + t_domains = 11740
  263 + t_enabled_roles = 11744
  264 + t_key_column_usage = 11747
  265 + t_parameters = 11751
  266 + t_referential_constraints = 11755
  267 + t_role_column_grants = 11759
  268 + t_routine_privileges = 11762
  269 + t_role_routine_grants = 11766
  270 + t_routines = 11769
  271 + t_schemata = 11773