├── .gitignore ├── README └── src ├── code.google.com └── p │ └── go-mysql-driver │ └── mysql │ ├── LICENSE │ ├── connection.go │ ├── const.go │ ├── driver.go │ ├── packets.go │ ├── result.go │ ├── rows.go │ ├── statement.go │ ├── transaction.go │ └── utils.go ├── github.com ├── jackc │ └── pgx │ │ ├── .gitignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── bench_test.go │ │ ├── conn.go │ │ ├── conn_config_test.go.example │ │ ├── conn_pool.go │ │ ├── conn_pool_test.go │ │ ├── conn_test.go │ │ ├── example_custom_type_test.go │ │ ├── examples │ │ ├── README.md │ │ └── url_shortener │ │ │ ├── README.md │ │ │ ├── main.go │ │ │ └── structure.sql │ │ ├── helper_test.go │ │ ├── messages.go │ │ ├── msg_reader.go │ │ ├── query.go │ │ ├── query_test.go │ │ ├── sql.go │ │ ├── sql_test.go │ │ ├── stdlib │ │ ├── sql.go │ │ └── sql_test.go │ │ ├── tx.go │ │ ├── tx_test.go │ │ ├── value_reader.go │ │ ├── values.go │ │ └── values_test.go ├── lib │ └── pq │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── CONTRIBUTING.md │ │ ├── LICENSE.md │ │ ├── README.md │ │ ├── bench_test.go │ │ ├── buf.go │ │ ├── conn.go │ │ ├── conn_test.go │ │ ├── conn_xact_test.go │ │ ├── copy.go │ │ ├── copy_test.go │ │ ├── doc.go │ │ ├── encode.go │ │ ├── encode_test.go │ │ ├── error.go │ │ ├── hstore │ │ ├── hstore.go │ │ └── hstore_test.go │ │ ├── listen_example │ │ └── doc.go │ │ ├── notify.go │ │ ├── notify_test.go │ │ ├── oid │ │ ├── doc.go │ │ ├── gen.go │ │ └── types.go │ │ ├── url.go │ │ ├── url_test.go │ │ ├── user_posix.go │ │ └── user_windows.go ├── mattn │ └── go-sqlite3 │ │ ├── Makefile │ │ ├── README.mkd │ │ ├── example │ │ ├── Makefile │ │ └── main.go │ │ ├── sqlite3.go │ │ └── sqlite3_test.go ├── tgulacsi │ └── goracle │ │ ├── LICENSE.txt │ │ ├── README.md │ │ ├── conntest │ │ └── main.go │ │ ├── godrv │ │ └── driver.go │ │ └── oracle │ │ ├── connection.go │ │ ├── connection_test.go │ │ ├── cursor.go │ │ ├── cursorvar.go │ │ ├── datatypes_test.go │ │ ├── datetimevar.go │ │ ├── debug_notrace.go │ │ ├── debug_trace.go │ │ ├── environment.go │ │ ├── error.go │ │ ├── externallobvar.go │ │ ├── lobvar.go │ │ ├── longvar.go │ │ ├── numbervar.go │ │ ├── stringvar.go │ │ └── variable.go └── ziutek │ └── mymysql │ ├── .gitignore │ ├── LICENSE │ ├── Makefile │ ├── README.md │ ├── autorc │ ├── LICENSE │ ├── Makefile │ ├── autorecon.go │ └── autorecon_test.go │ ├── godrv │ ├── Makefile │ ├── driver.go │ └── driver_test.go │ ├── make.bash │ ├── mysql │ ├── Makefile │ ├── errors.go │ ├── field.go │ ├── interface.go │ ├── row.go │ ├── types.go │ ├── types_test.go │ └── utils.go │ ├── native │ ├── LICENSE │ ├── Makefile │ ├── addons.go │ ├── bind_test.go │ ├── binding.go │ ├── codecs.go │ ├── command.go │ ├── common.go │ ├── consts.go │ ├── errors.go │ ├── init.go │ ├── mysql.go │ ├── native_test.go │ ├── packet.go │ ├── prepared.go │ ├── result.go │ └── unsafe.go │ └── thrsafe │ ├── LICENSE │ ├── Makefile │ ├── thrsafe.go │ └── thrsafe_test.go ├── gopkg.in └── inconshreveable │ └── log15.v2 │ ├── LICENSE │ ├── README.md │ ├── bench_test.go │ ├── doc.go │ ├── ext │ ├── ext_test.go │ ├── handler.go │ └── id.go │ ├── format.go │ ├── handler.go │ ├── log15_test.go │ ├── logger.go │ ├── root.go │ ├── syslog.go │ └── term │ ├── LICENSE │ ├── terminal_darwin.go │ ├── terminal_freebsd.go │ ├── terminal_linux.go │ ├── terminal_notwindows.go │ └── terminal_windows.go └── sqltest ├── drivers.go └── sql_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | \#* 3 | \.\#* 4 | *.out 5 | _test* 6 | _gotest* 7 | _obj 8 | _cgo* 9 | _go_.[568] 10 | *.cgo2.o 11 | pkg 12 | bin 13 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This project is an integration test, testing various Go database 2 | drivers (for the database/sql package). 3 | 4 | To run these tests, in this directory, run: 5 | 6 | $ export GOPATH=$PWD 7 | 8 | ... ignoring your existing GOPATH. (This project imports all the 3rd 9 | party drivers here, to make things easier to track, and to enable 10 | local fixes while waiting for upstream.) 11 | 12 | Then: 13 | 14 | $ cd src/sqltest 15 | $ go test -v 16 | 17 | or, most of the time, skipping the annoyingly long tests: 18 | 19 | $ go test -v -short 20 | 21 | 22 | **************************************************************************** 23 | For MySQL: 24 | **************************************************************************** 25 | mysqladmin -uroot -proot create gosqltest 26 | 27 | To set set your MySQL user / password run: 28 | 29 | $ export GOSQLTEST_MYSQL_USER=user 30 | $ export GOSQLTEST_MYSQL_PASS=password 31 | 32 | 33 | **************************************************************************** 34 | For Postgres: (replacing "bradfitz" with $USER) 35 | **************************************************************************** 36 | root@bradfitzlap:/home/bradfitz# su - postgres 37 | postgres@bradfitzlap:~$ psql 38 | postgres=# create database gosqltest; 39 | CREATE DATABASE 40 | postgres=# CREATE USER bradfitz WITH ENCRYPTED PASSWORD 'gosqltest'; 41 | CREATE ROLE 42 | postgres=# GRANT ALL PRIVILEGES ON DATABASE gosqltest to bradfitz; 43 | GRANT 44 | 45 | **************************************************************************** 46 | For Oracle: (replacing "bradfitz" with $USER) 47 | **************************************************************************** 48 | root@bradfitzlap:/home/bradfitz# service oracle-xe start 49 | Iff you don't have a nice test database, then i.e.: 50 | sqlplus /nolog < 0 { 50 | for i := 0; i < cap(dest); i++ { 51 | dest[i] = (*rows.content.rows[0])[i] 52 | } 53 | rows.content.rows = rows.content.rows[1:] 54 | } else { 55 | return io.EOF 56 | } 57 | return nil 58 | } 59 | -------------------------------------------------------------------------------- /src/code.google.com/p/go-mysql-driver/mysql/statement.go: -------------------------------------------------------------------------------- 1 | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package 2 | // 3 | // Copyright 2012 Julien Schmidt. All rights reserved. 4 | // http://www.julienschmidt.com 5 | // 6 | // This Source Code Form is subject to the terms of the Mozilla Public 7 | // License, v. 2.0. If a copy of the MPL was not distributed with this file, 8 | // You can obtain one at http://mozilla.org/MPL/2.0/. 9 | package mysql 10 | 11 | import ( 12 | "database/sql/driver" 13 | "errors" 14 | ) 15 | 16 | type stmtContent struct { 17 | mc *mysqlConn 18 | id uint32 19 | paramCount int 20 | params []mysqlField 21 | } 22 | 23 | type mysqlStmt struct { 24 | *stmtContent 25 | } 26 | 27 | func (stmt mysqlStmt) Close() error { 28 | e := stmt.mc.writeCommandPacket(COM_STMT_CLOSE, stmt.id) 29 | stmt.mc = nil 30 | return e 31 | } 32 | 33 | func (stmt mysqlStmt) NumInput() int { 34 | return stmt.paramCount 35 | } 36 | 37 | func (stmt mysqlStmt) Exec(args []driver.Value) (driver.Result, error) { 38 | if stmt.mc == nil { 39 | return nil, errors.New(`Invalid Statement`) 40 | } 41 | stmt.mc.affectedRows = 0 42 | stmt.mc.insertId = 0 43 | 44 | // Send command 45 | e := stmt.buildExecutePacket(&args) 46 | if e != nil { 47 | return nil, e 48 | } 49 | 50 | // Read Result 51 | var resLen int 52 | resLen, e = stmt.mc.readResultSetHeaderPacket() 53 | if e != nil { 54 | return nil, e 55 | } 56 | 57 | if resLen > 0 { 58 | // Columns 59 | _, e = stmt.mc.readUntilEOF() 60 | if e != nil { 61 | return nil, e 62 | } 63 | 64 | // Rows 65 | stmt.mc.affectedRows, e = stmt.mc.readUntilEOF() 66 | if e != nil { 67 | return nil, e 68 | } 69 | } 70 | if e != nil { 71 | return nil, e 72 | } 73 | 74 | if stmt.mc.affectedRows == 0 { 75 | return driver.ResultNoRows, nil 76 | } 77 | 78 | return mysqlResult{ 79 | affectedRows: int64(stmt.mc.affectedRows), 80 | insertId: int64(stmt.mc.insertId)}, 81 | nil 82 | } 83 | 84 | func (stmt mysqlStmt) Query(args []driver.Value) (dr driver.Rows, e error) { 85 | if stmt.mc == nil { 86 | return nil, errors.New(`Invalid Statement`) 87 | } 88 | 89 | // Send command 90 | e = stmt.buildExecutePacket(&args) 91 | if e != nil { 92 | return nil, e 93 | } 94 | 95 | // Get Result 96 | var resLen int 97 | rows := mysqlRows{new(rowsContent)} 98 | resLen, e = stmt.mc.readResultSetHeaderPacket() 99 | if e != nil { 100 | return nil, e 101 | } 102 | 103 | if resLen > 0 { 104 | // Columns 105 | rows.content.columns, e = stmt.mc.readColumns(resLen) 106 | if e != nil { 107 | return 108 | } 109 | 110 | // Rows 111 | e = stmt.mc.readBinaryRows(rows.content) 112 | if e != nil { 113 | return 114 | } 115 | } 116 | 117 | dr = rows 118 | return 119 | } 120 | 121 | // ColumnConverter returns a ValueConverter for the provided 122 | // column index. If the type of a specific column isn't known 123 | // or shouldn't be handled specially, DefaultValueConverter 124 | // can be returned. 125 | //func (stmt mysqlStmt) ColumnConverter(idx int) driver.ValueConverter { 126 | // debug(fmt.Sprintf("ColumnConverter(%d)", idx)) 127 | // return driver.DefaultParameterConverter 128 | //} 129 | -------------------------------------------------------------------------------- /src/code.google.com/p/go-mysql-driver/mysql/transaction.go: -------------------------------------------------------------------------------- 1 | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package 2 | // 3 | // Copyright 2012 Julien Schmidt. All rights reserved. 4 | // http://www.julienschmidt.com 5 | // 6 | // This Source Code Form is subject to the terms of the Mozilla Public 7 | // License, v. 2.0. If a copy of the MPL was not distributed with this file, 8 | // You can obtain one at http://mozilla.org/MPL/2.0/. 9 | package mysql 10 | 11 | type mysqlTx struct { 12 | mc *mysqlConn 13 | } 14 | 15 | func (tx *mysqlTx) Commit() (e error) { 16 | e = tx.mc.exec("COMMIT") 17 | tx.mc = nil 18 | return 19 | } 20 | 21 | func (tx *mysqlTx) Rollback() (e error) { 22 | e = tx.mc.exec("ROLLBACK") 23 | tx.mc = nil 24 | return 25 | } 26 | -------------------------------------------------------------------------------- /src/github.com/jackc/pgx/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | 24 | conn_config_test.go 25 | -------------------------------------------------------------------------------- /src/github.com/jackc/pgx/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Jack Christensen 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /src/github.com/jackc/pgx/bench_test.go: -------------------------------------------------------------------------------- 1 | package pgx_test 2 | 3 | import ( 4 | "github.com/jackc/pgx" 5 | "testing" 6 | ) 7 | 8 | func BenchmarkConnPool(b *testing.B) { 9 | config := pgx.ConnPoolConfig{ConnConfig: *defaultConnConfig, MaxConnections: 5} 10 | pool, err := pgx.NewConnPool(config) 11 | if err != nil { 12 | b.Fatalf("Unable to create connection pool: %v", err) 13 | } 14 | defer pool.Close() 15 | 16 | b.ResetTimer() 17 | for i := 0; i < b.N; i++ { 18 | var conn *pgx.Conn 19 | if conn, err = pool.Acquire(); err != nil { 20 | b.Fatalf("Unable to acquire connection: %v", err) 21 | } 22 | pool.Release(conn) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/github.com/jackc/pgx/conn_config_test.go.example: -------------------------------------------------------------------------------- 1 | package pgx_test 2 | 3 | import ( 4 | "github.com/jackc/pgx" 5 | ) 6 | 7 | var defaultConnConfig *pgx.ConnConfig = &pgx.ConnConfig{Host: "127.0.0.1", User: "pgx_md5", Password: "secret", Database: "pgx_test"} 8 | 9 | // To skip tests for specific connection / authentication types set that connection param to nil 10 | var tcpConnConfig *pgx.ConnConfig = nil 11 | var unixSocketConnConfig *pgx.ConnConfig = nil 12 | var md5ConnConfig *pgx.ConnConfig = nil 13 | var plainPasswordConnConfig *pgx.ConnConfig = nil 14 | var noPasswordConnConfig *pgx.ConnConfig = nil 15 | var invalidUserConnConfig *pgx.ConnConfig = nil 16 | 17 | // var tcpConnConfig *pgx.ConnConfig = &pgx.ConnConfig{Host: "127.0.0.1", User: "pgx_md5", Password: "secret", Database: "pgx_test"} 18 | // var unixSocketConnConfig *pgx.ConnConfig = &pgx.ConnConfig{Host: "/private/tmp", User: "pgx_none", Database: "pgx_test"} 19 | // var md5ConnConfig *pgx.ConnConfig = &pgx.ConnConfig{Host: "127.0.0.1", User: "pgx_md5", Password: "secret", Database: "pgx_test"} 20 | // var plainPasswordConnConfig *pgx.ConnConfig = &pgx.ConnConfig{Host: "127.0.0.1", User: "pgx_pw", Password: "secret", Database: "pgx_test"} 21 | // var noPasswordConnConfig *pgx.ConnConfig = &pgx.ConnConfig{Host: "127.0.0.1", User: "pgx_none", Database: "pgx_test"} 22 | // var invalidUserConnConfig *pgx.ConnConfig = &pgx.ConnConfig{Host: "127.0.0.1", User: "invalid", Database: "pgx_test"} 23 | -------------------------------------------------------------------------------- /src/github.com/jackc/pgx/example_custom_type_test.go: -------------------------------------------------------------------------------- 1 | package pgx_test 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/jackc/pgx" 7 | "regexp" 8 | "strconv" 9 | ) 10 | 11 | var pointRegexp *regexp.Regexp = regexp.MustCompile(`^\((.*),(.*)\)$`) 12 | 13 | // NullPoint represents a point that may be null. 14 | // 15 | // If Valid is false then the value is NULL. 16 | type NullPoint struct { 17 | X, Y float64 // Coordinates of point 18 | Valid bool // Valid is true if not NULL 19 | } 20 | 21 | const pointOid = 600 22 | 23 | func (p *NullPoint) Scan(vr *pgx.ValueReader) error { 24 | if vr.Type().DataType != pointOid { 25 | return pgx.SerializationError(fmt.Sprintf("NullPoint.Scan cannot decode OID %d", vr.Type().DataType)) 26 | } 27 | 28 | if vr.Len() == -1 { 29 | p.X, p.Y, p.Valid = 0, 0, false 30 | return nil 31 | } 32 | 33 | switch vr.Type().FormatCode { 34 | case pgx.TextFormatCode: 35 | s := vr.ReadString(vr.Len()) 36 | match := pointRegexp.FindStringSubmatch(s) 37 | if match == nil { 38 | return pgx.SerializationError(fmt.Sprintf("Received invalid point: %v", s)) 39 | } 40 | 41 | var err error 42 | p.X, err = strconv.ParseFloat(match[1], 64) 43 | if err != nil { 44 | return pgx.SerializationError(fmt.Sprintf("Received invalid point: %v", s)) 45 | } 46 | p.Y, err = strconv.ParseFloat(match[2], 64) 47 | if err != nil { 48 | return pgx.SerializationError(fmt.Sprintf("Received invalid point: %v", s)) 49 | } 50 | case pgx.BinaryFormatCode: 51 | return errors.New("binary format not implemented") 52 | default: 53 | return fmt.Errorf("unknown format %v", vr.Type().FormatCode) 54 | } 55 | 56 | p.Valid = true 57 | return vr.Err() 58 | } 59 | 60 | func (p NullPoint) EncodeText() (string, byte, error) { 61 | if p.Valid { 62 | return fmt.Sprintf("point(%v,%v)", p.X, p.Y), pgx.SafeText, nil 63 | } else { 64 | return "", pgx.NullText, nil 65 | } 66 | } 67 | 68 | func (p NullPoint) String() string { 69 | if p.Valid { 70 | return fmt.Sprintf("%v, %v", p.X, p.Y) 71 | } 72 | return "null point" 73 | } 74 | 75 | func Example_CustomType() { 76 | conn, err := pgx.Connect(*defaultConnConfig) 77 | if err != nil { 78 | fmt.Printf("Unable to establish connection: %v", err) 79 | return 80 | } 81 | 82 | var p NullPoint 83 | err = conn.QueryRow("select null::point").Scan(&p) 84 | if err != nil { 85 | fmt.Println(err) 86 | return 87 | } 88 | fmt.Println(p) 89 | 90 | err = conn.QueryRow("select point(1.5,2.5)").Scan(&p) 91 | if err != nil { 92 | fmt.Println(err) 93 | return 94 | } 95 | fmt.Println(p) 96 | // Output: 97 | // null point 98 | // 1.5, 2.5 99 | } 100 | -------------------------------------------------------------------------------- /src/github.com/jackc/pgx/examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | * url_shortener contains a simple example of using pgx in a web context. 4 | * (Tern)[https://github.com/jackc/tern] is a migration tool that uses pgx. 5 | * (The Pithy Reader)[https://github.com/jackc/tpr] is a RSS aggregator that uses pgx. 6 | -------------------------------------------------------------------------------- /src/github.com/jackc/pgx/examples/url_shortener/README.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | This is a sample REST URL shortener service implemented using pgx as the connector to a PostgreSQL data store. 4 | 5 | # Usage 6 | 7 | Create a PostgreSQL database and run structure.sql into it to create the necessary data schema. 8 | 9 | Edit connectionOptions in main.go with the location and credentials for your database. 10 | 11 | Run main.go: 12 | 13 | go run main.go 14 | 15 | ## Create or Update a Shortened URL 16 | 17 | curl -X PUT -d 'http://www.google.com' http://localhost:8080/google 18 | 19 | ## Get a Shortened URL 20 | 21 | curl http://localhost:8080/google 22 | 23 | ## Delete a Shortened URL 24 | 25 | curl -X DELETE http://localhost:8080/google 26 | -------------------------------------------------------------------------------- /src/github.com/jackc/pgx/examples/url_shortener/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/jackc/pgx" 5 | log "gopkg.in/inconshreveable/log15.v2" 6 | "io/ioutil" 7 | "net/http" 8 | "os" 9 | ) 10 | 11 | var pool *pgx.ConnPool 12 | 13 | // afterConnect creates the prepared statements that this application uses 14 | func afterConnect(conn *pgx.Conn) (err error) { 15 | _, err = conn.Prepare("getUrl", ` 16 | select url from shortened_urls where id=$1 17 | `) 18 | if err != nil { 19 | return 20 | } 21 | 22 | _, err = conn.Prepare("deleteUrl", ` 23 | delete from shortened_urls where id=$1 24 | `) 25 | if err != nil { 26 | return 27 | } 28 | 29 | // There technically is a small race condition in doing an upsert with a CTE 30 | // where one of two simultaneous requests to the shortened URL would fail 31 | // with a unique index violation. As the point of this demo is pgx usage and 32 | // not how to perfectly upsert in PostgreSQL it is deemed acceptable. 33 | _, err = conn.Prepare("putUrl", ` 34 | with upsert as ( 35 | update shortened_urls 36 | set url=$2 37 | where id=$1 38 | returning * 39 | ) 40 | insert into shortened_urls(id, url) 41 | select $1, $2 where not exists(select 1 from upsert) 42 | `) 43 | return 44 | } 45 | 46 | func getUrlHandler(w http.ResponseWriter, req *http.Request) { 47 | var url string 48 | err := pool.QueryRow("getUrl", req.URL.Path).Scan(&url) 49 | switch err { 50 | case nil: 51 | http.Redirect(w, req, url, http.StatusSeeOther) 52 | case pgx.ErrNoRows: 53 | http.NotFound(w, req) 54 | default: 55 | http.Error(w, "Internal server error", http.StatusInternalServerError) 56 | } 57 | } 58 | 59 | func putUrlHandler(w http.ResponseWriter, req *http.Request) { 60 | id := req.URL.Path 61 | var url string 62 | if body, err := ioutil.ReadAll(req.Body); err == nil { 63 | url = string(body) 64 | } else { 65 | http.Error(w, "Internal server error", http.StatusInternalServerError) 66 | return 67 | } 68 | 69 | if _, err := pool.Exec("putUrl", id, url); err == nil { 70 | w.WriteHeader(http.StatusOK) 71 | } else { 72 | http.Error(w, "Internal server error", http.StatusInternalServerError) 73 | } 74 | } 75 | 76 | func deleteUrlHandler(w http.ResponseWriter, req *http.Request) { 77 | if _, err := pool.Exec("deleteUrl", req.URL.Path); err == nil { 78 | w.WriteHeader(http.StatusOK) 79 | } else { 80 | http.Error(w, "Internal server error", http.StatusInternalServerError) 81 | } 82 | } 83 | 84 | func urlHandler(w http.ResponseWriter, req *http.Request) { 85 | switch req.Method { 86 | case "GET": 87 | getUrlHandler(w, req) 88 | 89 | case "PUT": 90 | putUrlHandler(w, req) 91 | 92 | case "DELETE": 93 | deleteUrlHandler(w, req) 94 | 95 | default: 96 | w.Header().Add("Allow", "GET, PUT, DELETE") 97 | w.WriteHeader(http.StatusMethodNotAllowed) 98 | } 99 | } 100 | 101 | func main() { 102 | var err error 103 | connPoolConfig := pgx.ConnPoolConfig{ 104 | ConnConfig: pgx.ConnConfig{ 105 | Host: "127.0.0.1", 106 | User: "jack", 107 | Password: "jack", 108 | Database: "url_shortener", 109 | Logger: log.New("module", "pgx"), 110 | }, 111 | MaxConnections: 5, 112 | AfterConnect: afterConnect, 113 | } 114 | pool, err = pgx.NewConnPool(connPoolConfig) 115 | if err != nil { 116 | log.Crit("Unable to create connection pool", "error", err) 117 | os.Exit(1) 118 | } 119 | 120 | http.HandleFunc("/", urlHandler) 121 | 122 | log.Info("Starting URL shortener on localhost:8080") 123 | err = http.ListenAndServe("localhost:8080", nil) 124 | if err != nil { 125 | log.Crit("Unable to start web server", "error", err) 126 | os.Exit(1) 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/github.com/jackc/pgx/examples/url_shortener/structure.sql: -------------------------------------------------------------------------------- 1 | create table shortened_urls ( 2 | id text primary key, 3 | url text not null 4 | ); -------------------------------------------------------------------------------- /src/github.com/jackc/pgx/helper_test.go: -------------------------------------------------------------------------------- 1 | package pgx_test 2 | 3 | import ( 4 | "github.com/jackc/pgx" 5 | "testing" 6 | ) 7 | 8 | func mustConnect(t testing.TB, config pgx.ConnConfig) *pgx.Conn { 9 | conn, err := pgx.Connect(config) 10 | if err != nil { 11 | t.Fatalf("Unable to establish connection: %v", err) 12 | } 13 | return conn 14 | } 15 | 16 | func closeConn(t testing.TB, conn *pgx.Conn) { 17 | err := conn.Close() 18 | if err != nil { 19 | t.Fatalf("conn.Close unexpectedly failed: %v", err) 20 | } 21 | } 22 | 23 | func mustPrepare(t testing.TB, conn *pgx.Conn, name, sql string) { 24 | if _, err := conn.Prepare(name, sql); err != nil { 25 | t.Fatalf("Could not prepare %v: %v", name, err) 26 | } 27 | } 28 | 29 | func mustExec(t testing.TB, conn *pgx.Conn, sql string, arguments ...interface{}) (commandTag pgx.CommandTag) { 30 | var err error 31 | if commandTag, err = conn.Exec(sql, arguments...); err != nil { 32 | t.Fatalf("Exec unexpectedly failed with %v: %v", sql, err) 33 | } 34 | return 35 | } 36 | 37 | // Do a simple query to ensure the connection is still usable 38 | func ensureConnValid(t *testing.T, conn *pgx.Conn) { 39 | var sum, rowCount int32 40 | 41 | rows, err := conn.Query("select generate_series(1,$1)", 10) 42 | if err != nil { 43 | t.Fatalf("conn.Query failed: ", err) 44 | } 45 | defer rows.Close() 46 | 47 | for rows.Next() { 48 | var n int32 49 | rows.Scan(&n) 50 | sum += n 51 | rowCount++ 52 | } 53 | 54 | if rows.Err() != nil { 55 | t.Fatalf("conn.Query failed: ", err) 56 | } 57 | 58 | if rowCount != 10 { 59 | t.Error("Select called onDataRow wrong number of times") 60 | } 61 | if sum != 55 { 62 | t.Error("Wrong values returned") 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/github.com/jackc/pgx/messages.go: -------------------------------------------------------------------------------- 1 | package pgx 2 | 3 | import ( 4 | "encoding/binary" 5 | ) 6 | 7 | const ( 8 | protocolVersionNumber = 196608 // 3.0 9 | ) 10 | 11 | const ( 12 | backendKeyData = 'K' 13 | authenticationX = 'R' 14 | readyForQuery = 'Z' 15 | rowDescription = 'T' 16 | dataRow = 'D' 17 | commandComplete = 'C' 18 | errorResponse = 'E' 19 | noticeResponse = 'N' 20 | parseComplete = '1' 21 | parameterDescription = 't' 22 | bindComplete = '2' 23 | notificationResponse = 'A' 24 | noData = 'n' 25 | ) 26 | 27 | type startupMessage struct { 28 | options map[string]string 29 | } 30 | 31 | func newStartupMessage() *startupMessage { 32 | return &startupMessage{map[string]string{}} 33 | } 34 | 35 | func (self *startupMessage) Bytes() (buf []byte) { 36 | buf = make([]byte, 8, 128) 37 | binary.BigEndian.PutUint32(buf[4:8], uint32(protocolVersionNumber)) 38 | for key, value := range self.options { 39 | buf = append(buf, key...) 40 | buf = append(buf, 0) 41 | buf = append(buf, value...) 42 | buf = append(buf, 0) 43 | } 44 | buf = append(buf, ("\000")...) 45 | binary.BigEndian.PutUint32(buf[0:4], uint32(len(buf))) 46 | return buf 47 | } 48 | 49 | type Oid int32 50 | 51 | type FieldDescription struct { 52 | Name string 53 | Table Oid 54 | AttributeNumber int16 55 | DataType Oid 56 | DataTypeSize int16 57 | Modifier int32 58 | FormatCode int16 59 | } 60 | 61 | type PgError struct { 62 | Severity string 63 | Code string 64 | Message string 65 | } 66 | 67 | func (self PgError) Error() string { 68 | return self.Severity + ": " + self.Message + " (SQLSTATE " + self.Code + ")" 69 | } 70 | 71 | func newWriteBuf(buf []byte, t byte) *WriteBuf { 72 | buf = append(buf, t, 0, 0, 0, 0) 73 | return &WriteBuf{buf: buf, sizeIdx: 1} 74 | } 75 | 76 | // WrifeBuf is used build messages to send to the PostgreSQL server. It is used 77 | // by the BinaryEncoder interface when implementing custom encoders. 78 | type WriteBuf struct { 79 | buf []byte 80 | sizeIdx int 81 | } 82 | 83 | func (wb *WriteBuf) startMsg(t byte) { 84 | wb.closeMsg() 85 | wb.buf = append(wb.buf, t, 0, 0, 0, 0) 86 | wb.sizeIdx = len(wb.buf) - 4 87 | } 88 | 89 | func (wb *WriteBuf) closeMsg() { 90 | binary.BigEndian.PutUint32(wb.buf[wb.sizeIdx:wb.sizeIdx+4], uint32(len(wb.buf)-wb.sizeIdx)) 91 | } 92 | 93 | func (wb *WriteBuf) WriteByte(b byte) { 94 | wb.buf = append(wb.buf, b) 95 | } 96 | 97 | func (wb *WriteBuf) WriteCString(s string) { 98 | wb.buf = append(wb.buf, []byte(s)...) 99 | wb.buf = append(wb.buf, 0) 100 | } 101 | 102 | func (wb *WriteBuf) WriteInt16(n int16) { 103 | b := make([]byte, 2) 104 | binary.BigEndian.PutUint16(b, uint16(n)) 105 | wb.buf = append(wb.buf, b...) 106 | } 107 | 108 | func (wb *WriteBuf) WriteInt32(n int32) { 109 | b := make([]byte, 4) 110 | binary.BigEndian.PutUint32(b, uint32(n)) 111 | wb.buf = append(wb.buf, b...) 112 | } 113 | 114 | func (wb *WriteBuf) WriteInt64(n int64) { 115 | b := make([]byte, 8) 116 | binary.BigEndian.PutUint64(b, uint64(n)) 117 | wb.buf = append(wb.buf, b...) 118 | } 119 | 120 | func (wb *WriteBuf) WriteBytes(b []byte) { 121 | wb.buf = append(wb.buf, b...) 122 | } 123 | -------------------------------------------------------------------------------- /src/github.com/jackc/pgx/msg_reader.go: -------------------------------------------------------------------------------- 1 | package pgx 2 | 3 | import ( 4 | "bufio" 5 | "encoding/binary" 6 | "errors" 7 | "io" 8 | "io/ioutil" 9 | ) 10 | 11 | // msgReader is a helper that reads values from a PostgreSQL message. 12 | type msgReader struct { 13 | reader *bufio.Reader 14 | buf [128]byte 15 | msgBytesRemaining int32 16 | err error 17 | } 18 | 19 | // Err returns any error that the msgReader has experienced 20 | func (r *msgReader) Err() error { 21 | return r.err 22 | } 23 | 24 | // fatal tells r that a Fatal error has occurred 25 | func (r *msgReader) fatal(err error) { 26 | r.err = err 27 | } 28 | 29 | // rxMsg reads the type and size of the next message. 30 | func (r *msgReader) rxMsg() (t byte, err error) { 31 | if r.err != nil { 32 | return 0, err 33 | } 34 | 35 | if r.msgBytesRemaining > 0 { 36 | io.CopyN(ioutil.Discard, r.reader, int64(r.msgBytesRemaining)) 37 | } 38 | 39 | t, err = r.reader.ReadByte() 40 | b := r.buf[0:4] 41 | _, err = io.ReadFull(r.reader, b) 42 | r.msgBytesRemaining = int32(binary.BigEndian.Uint32(b)) - 4 43 | return t, err 44 | } 45 | 46 | func (r *msgReader) readByte() byte { 47 | if r.err != nil { 48 | return 0 49 | } 50 | 51 | r.msgBytesRemaining -= 1 52 | if r.msgBytesRemaining < 0 { 53 | r.fatal(errors.New("read past end of message")) 54 | return 0 55 | } 56 | 57 | b, err := r.reader.ReadByte() 58 | if err != nil { 59 | r.fatal(err) 60 | return 0 61 | } 62 | 63 | return b 64 | } 65 | 66 | func (r *msgReader) readInt16() int16 { 67 | if r.err != nil { 68 | return 0 69 | } 70 | 71 | r.msgBytesRemaining -= 2 72 | if r.msgBytesRemaining < 0 { 73 | r.fatal(errors.New("read past end of message")) 74 | return 0 75 | } 76 | 77 | b := r.buf[0:2] 78 | _, err := io.ReadFull(r.reader, b) 79 | if err != nil { 80 | r.fatal(err) 81 | return 0 82 | } 83 | 84 | return int16(binary.BigEndian.Uint16(b)) 85 | } 86 | 87 | func (r *msgReader) readInt32() int32 { 88 | if r.err != nil { 89 | return 0 90 | } 91 | 92 | r.msgBytesRemaining -= 4 93 | if r.msgBytesRemaining < 0 { 94 | r.fatal(errors.New("read past end of message")) 95 | return 0 96 | } 97 | 98 | b := r.buf[0:4] 99 | _, err := io.ReadFull(r.reader, b) 100 | if err != nil { 101 | r.fatal(err) 102 | return 0 103 | } 104 | 105 | return int32(binary.BigEndian.Uint32(b)) 106 | } 107 | 108 | func (r *msgReader) readInt64() int64 { 109 | if r.err != nil { 110 | return 0 111 | } 112 | 113 | r.msgBytesRemaining -= 8 114 | if r.msgBytesRemaining < 0 { 115 | r.fatal(errors.New("read past end of message")) 116 | return 0 117 | } 118 | 119 | b := r.buf[0:8] 120 | _, err := io.ReadFull(r.reader, b) 121 | if err != nil { 122 | r.fatal(err) 123 | return 0 124 | } 125 | 126 | return int64(binary.BigEndian.Uint64(b)) 127 | } 128 | 129 | func (r *msgReader) readOid() Oid { 130 | return Oid(r.readInt32()) 131 | } 132 | 133 | // readCString reads a null terminated string 134 | func (r *msgReader) readCString() string { 135 | if r.err != nil { 136 | return "" 137 | } 138 | 139 | b, err := r.reader.ReadBytes(0) 140 | if err != nil { 141 | r.fatal(err) 142 | return "" 143 | } 144 | 145 | r.msgBytesRemaining -= int32(len(b)) 146 | if r.msgBytesRemaining < 0 { 147 | r.fatal(errors.New("read past end of message")) 148 | return "" 149 | } 150 | 151 | return string(b[0 : len(b)-1]) 152 | } 153 | 154 | // readString reads count bytes and returns as string 155 | func (r *msgReader) readString(count int32) string { 156 | if r.err != nil { 157 | return "" 158 | } 159 | 160 | r.msgBytesRemaining -= count 161 | if r.msgBytesRemaining < 0 { 162 | r.fatal(errors.New("read past end of message")) 163 | return "" 164 | } 165 | 166 | var b []byte 167 | if count <= int32(len(r.buf)) { 168 | b = r.buf[0:int(count)] 169 | } else { 170 | b = make([]byte, int(count)) 171 | } 172 | 173 | _, err := io.ReadFull(r.reader, b) 174 | if err != nil { 175 | r.fatal(err) 176 | return "" 177 | } 178 | 179 | return string(b) 180 | } 181 | 182 | // readBytes reads count bytes and returns as []byte 183 | func (r *msgReader) readBytes(count int32) []byte { 184 | if r.err != nil { 185 | return nil 186 | } 187 | 188 | r.msgBytesRemaining -= count 189 | if r.msgBytesRemaining < 0 { 190 | r.fatal(errors.New("read past end of message")) 191 | return nil 192 | } 193 | 194 | b := make([]byte, int(count)) 195 | 196 | _, err := io.ReadFull(r.reader, b) 197 | if err != nil { 198 | r.fatal(err) 199 | return nil 200 | } 201 | 202 | return b 203 | } 204 | -------------------------------------------------------------------------------- /src/github.com/jackc/pgx/sql.go: -------------------------------------------------------------------------------- 1 | package pgx 2 | 3 | import ( 4 | "strconv" 5 | ) 6 | 7 | // QueryArgs is a container for arguments to an SQL query. It is helpful when 8 | // building SQL statements where the number of arguments is variable. 9 | type QueryArgs []interface{} 10 | 11 | var placeholders []string 12 | 13 | func init() { 14 | placeholders = make([]string, 64) 15 | 16 | for i := 1; i < 64; i++ { 17 | placeholders[i] = "$" + strconv.FormatInt(int64(i), 10) 18 | } 19 | } 20 | 21 | // Append adds a value to qa and returns the placeholder value for the 22 | // argument. e.g. $1, $2, etc. 23 | func (qa *QueryArgs) Append(v interface{}) string { 24 | *qa = append(*qa, v) 25 | if len(*qa) < len(placeholders) { 26 | return placeholders[len(*qa)] 27 | } 28 | return "$" + strconv.FormatInt(int64(len(*qa)), 10) 29 | } 30 | -------------------------------------------------------------------------------- /src/github.com/jackc/pgx/sql_test.go: -------------------------------------------------------------------------------- 1 | package pgx_test 2 | 3 | import ( 4 | "github.com/jackc/pgx" 5 | "strconv" 6 | "testing" 7 | ) 8 | 9 | func TestQueryArgs(t *testing.T) { 10 | var qa pgx.QueryArgs 11 | 12 | for i := 1; i < 512; i++ { 13 | expectedPlaceholder := "$" + strconv.FormatInt(int64(i), 10) 14 | placeholder := qa.Append(i) 15 | if placeholder != expectedPlaceholder { 16 | t.Errorf(`Expected qa.Append to return "%s", but it returned "%s"`, expectedPlaceholder, placeholder) 17 | } 18 | } 19 | } 20 | 21 | func BenchmarkQueryArgs(b *testing.B) { 22 | for i := 0; i < b.N; i++ { 23 | qa := pgx.QueryArgs(make([]interface{}, 0, 16)) 24 | qa.Append("foo1") 25 | qa.Append("foo2") 26 | qa.Append("foo3") 27 | qa.Append("foo4") 28 | qa.Append("foo5") 29 | qa.Append("foo6") 30 | qa.Append("foo7") 31 | qa.Append("foo8") 32 | qa.Append("foo9") 33 | qa.Append("foo10") 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/github.com/jackc/pgx/tx.go: -------------------------------------------------------------------------------- 1 | package pgx 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | ) 7 | 8 | // Transaction isolation levels 9 | const ( 10 | Serializable = "serializable" 11 | RepeatableRead = "repeatable read" 12 | ReadCommitted = "read committed" 13 | ReadUncommitted = "read uncommitted" 14 | ) 15 | 16 | var ErrTxClosed = errors.New("tx is closed") 17 | 18 | // Begin starts a transaction with the default isolation level for the current 19 | // connection. To use a specific isolation level see BeginIso. 20 | func (c *Conn) Begin() (*Tx, error) { 21 | return c.begin("") 22 | } 23 | 24 | // BeginIso starts a transaction with isoLevel as the transaction isolation 25 | // level. 26 | // 27 | // Valid isolation levels (and their constants) are: 28 | // serializable (pgx.Serializable) 29 | // repeatable read (pgx.RepeatableRead) 30 | // read committed (pgx.ReadCommitted) 31 | // read uncommitted (pgx.ReadUncommitted) 32 | func (c *Conn) BeginIso(isoLevel string) (*Tx, error) { 33 | return c.begin(isoLevel) 34 | } 35 | 36 | func (c *Conn) begin(isoLevel string) (*Tx, error) { 37 | var beginSql string 38 | if isoLevel == "" { 39 | beginSql = "begin" 40 | } else { 41 | beginSql = fmt.Sprintf("begin isolation level %s", isoLevel) 42 | } 43 | 44 | _, err := c.Exec(beginSql) 45 | if err != nil { 46 | return nil, err 47 | } 48 | 49 | return &Tx{conn: c}, nil 50 | } 51 | 52 | // Tx represents a database transaction. 53 | // 54 | // All Tx methods return ErrTxClosed if Commit or Rollback has already been 55 | // called on the Tx. 56 | type Tx struct { 57 | pool *ConnPool 58 | conn *Conn 59 | closed bool 60 | } 61 | 62 | // Commit commits the transaction 63 | func (tx *Tx) Commit() error { 64 | if tx.closed { 65 | return ErrTxClosed 66 | } 67 | 68 | _, err := tx.conn.Exec("commit") 69 | tx.close() 70 | return err 71 | } 72 | 73 | // Rollback rolls back the transaction. Rollback will return ErrTxClosed if the 74 | // Tx is already closed, but is otherwise safe to call multiple times. Hence, a 75 | // defer tx.Rollback() is safe even if tx.Commit() will be called first in a 76 | // non-error condition. 77 | func (tx *Tx) Rollback() error { 78 | if tx.closed { 79 | return ErrTxClosed 80 | } 81 | 82 | _, err := tx.conn.Exec("rollback") 83 | tx.close() 84 | return err 85 | } 86 | 87 | func (tx *Tx) close() { 88 | if tx.pool != nil { 89 | tx.pool.Release(tx.conn) 90 | tx.pool = nil 91 | } 92 | tx.closed = true 93 | } 94 | 95 | // Exec delegates to the underlying *Conn 96 | func (tx *Tx) Exec(sql string, arguments ...interface{}) (commandTag CommandTag, err error) { 97 | if tx.closed { 98 | return CommandTag(""), ErrTxClosed 99 | } 100 | 101 | return tx.conn.Exec(sql, arguments...) 102 | } 103 | 104 | // Query delegates to the underlying *Conn 105 | func (tx *Tx) Query(sql string, args ...interface{}) (*Rows, error) { 106 | if tx.closed { 107 | // Because checking for errors can be deferred to the *Rows, build one with the error 108 | err := ErrTxClosed 109 | return &Rows{closed: true, err: err}, err 110 | } 111 | 112 | return tx.conn.Query(sql, args...) 113 | } 114 | 115 | // QueryRow delegates to the underlying *Conn 116 | func (tx *Tx) QueryRow(sql string, args ...interface{}) *Row { 117 | rows, _ := tx.Query(sql, args...) 118 | return (*Row)(rows) 119 | } 120 | -------------------------------------------------------------------------------- /src/github.com/jackc/pgx/tx_test.go: -------------------------------------------------------------------------------- 1 | package pgx_test 2 | 3 | import ( 4 | "github.com/jackc/pgx" 5 | "testing" 6 | ) 7 | 8 | func TestTransactionSuccessfulCommit(t *testing.T) { 9 | t.Parallel() 10 | 11 | conn := mustConnect(t, *defaultConnConfig) 12 | defer closeConn(t, conn) 13 | 14 | createSql := ` 15 | create temporary table foo( 16 | id integer, 17 | unique (id) initially deferred 18 | ); 19 | ` 20 | 21 | if _, err := conn.Exec(createSql); err != nil { 22 | t.Fatalf("Failed to create table: %v", err) 23 | } 24 | 25 | tx, err := conn.Begin() 26 | if err != nil { 27 | t.Fatalf("conn.Begin failed: %v", err) 28 | } 29 | 30 | _, err = tx.Exec("insert into foo(id) values (1)") 31 | if err != nil { 32 | t.Fatalf("tx.Exec failed: %v", err) 33 | } 34 | 35 | err = tx.Commit() 36 | if err != nil { 37 | t.Fatalf("tx.Commit failed: %v", err) 38 | } 39 | 40 | var n int64 41 | err = conn.QueryRow("select count(*) from foo").Scan(&n) 42 | if err != nil { 43 | t.Fatalf("QueryRow Scan failed: %v", err) 44 | } 45 | if n != 1 { 46 | t.Fatalf("Did not receive correct number of rows: %v", n) 47 | } 48 | } 49 | 50 | func TestTransactionSuccessfulRollback(t *testing.T) { 51 | t.Parallel() 52 | 53 | conn := mustConnect(t, *defaultConnConfig) 54 | defer closeConn(t, conn) 55 | 56 | createSql := ` 57 | create temporary table foo( 58 | id integer, 59 | unique (id) initially deferred 60 | ); 61 | ` 62 | 63 | if _, err := conn.Exec(createSql); err != nil { 64 | t.Fatalf("Failed to create table: %v", err) 65 | } 66 | 67 | tx, err := conn.Begin() 68 | if err != nil { 69 | t.Fatalf("conn.Begin failed: %v", err) 70 | } 71 | 72 | _, err = tx.Exec("insert into foo(id) values (1)") 73 | if err != nil { 74 | t.Fatalf("tx.Exec failed: %v", err) 75 | } 76 | 77 | err = tx.Rollback() 78 | if err != nil { 79 | t.Fatalf("tx.Rollback failed: %v", err) 80 | } 81 | 82 | var n int64 83 | err = conn.QueryRow("select count(*) from foo").Scan(&n) 84 | if err != nil { 85 | t.Fatalf("QueryRow Scan failed: %v", err) 86 | } 87 | if n != 0 { 88 | t.Fatalf("Did not receive correct number of rows: %v", n) 89 | } 90 | } 91 | 92 | func TestBeginIso(t *testing.T) { 93 | t.Parallel() 94 | 95 | conn := mustConnect(t, *defaultConnConfig) 96 | defer closeConn(t, conn) 97 | 98 | isoLevels := []string{pgx.Serializable, pgx.RepeatableRead, pgx.ReadCommitted, pgx.ReadUncommitted} 99 | for _, iso := range isoLevels { 100 | tx, err := conn.BeginIso(iso) 101 | if err != nil { 102 | t.Fatalf("conn.BeginIso failed: %v", err) 103 | } 104 | 105 | var level string 106 | conn.QueryRow("select current_setting('transaction_isolation')").Scan(&level) 107 | if level != iso { 108 | t.Errorf("Expected to be in isolation level %v but was %v", iso, level) 109 | } 110 | 111 | err = tx.Rollback() 112 | if err != nil { 113 | t.Fatalf("tx.Rollback failed: %v", err) 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/github.com/jackc/pgx/value_reader.go: -------------------------------------------------------------------------------- 1 | package pgx 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | // ValueReader is used by the Scanner interface to decode values. 8 | type ValueReader struct { 9 | mr *msgReader 10 | fd *FieldDescription 11 | valueBytesRemaining int32 12 | err error 13 | } 14 | 15 | // Err returns any error that the ValueReader has experienced 16 | func (r *ValueReader) Err() error { 17 | return r.err 18 | } 19 | 20 | // Fatal tells r that a Fatal error has occurred 21 | func (r *ValueReader) Fatal(err error) { 22 | r.err = err 23 | } 24 | 25 | // Len returns the number of unread bytes 26 | func (r *ValueReader) Len() int32 { 27 | return r.valueBytesRemaining 28 | } 29 | 30 | // Type returns the *FieldDescription of the value 31 | func (r *ValueReader) Type() *FieldDescription { 32 | return r.fd 33 | } 34 | 35 | func (r *ValueReader) ReadByte() byte { 36 | if r.err != nil { 37 | return 0 38 | } 39 | 40 | r.valueBytesRemaining -= 1 41 | if r.valueBytesRemaining < 0 { 42 | r.Fatal(errors.New("read past end of value")) 43 | return 0 44 | } 45 | 46 | return r.mr.readByte() 47 | } 48 | 49 | func (r *ValueReader) ReadInt16() int16 { 50 | if r.err != nil { 51 | return 0 52 | } 53 | 54 | r.valueBytesRemaining -= 2 55 | if r.valueBytesRemaining < 0 { 56 | r.Fatal(errors.New("read past end of value")) 57 | return 0 58 | } 59 | 60 | return r.mr.readInt16() 61 | } 62 | 63 | func (r *ValueReader) ReadInt32() int32 { 64 | if r.err != nil { 65 | return 0 66 | } 67 | 68 | r.valueBytesRemaining -= 4 69 | if r.valueBytesRemaining < 0 { 70 | r.Fatal(errors.New("read past end of value")) 71 | return 0 72 | } 73 | 74 | return r.mr.readInt32() 75 | } 76 | 77 | func (r *ValueReader) ReadInt64() int64 { 78 | if r.err != nil { 79 | return 0 80 | } 81 | 82 | r.valueBytesRemaining -= 8 83 | if r.valueBytesRemaining < 0 { 84 | r.Fatal(errors.New("read past end of value")) 85 | return 0 86 | } 87 | 88 | return r.mr.readInt64() 89 | } 90 | 91 | func (r *ValueReader) ReadOid() Oid { 92 | return Oid(r.ReadInt32()) 93 | } 94 | 95 | // ReadString reads count bytes and returns as string 96 | func (r *ValueReader) ReadString(count int32) string { 97 | if r.err != nil { 98 | return "" 99 | } 100 | 101 | r.valueBytesRemaining -= count 102 | if r.valueBytesRemaining < 0 { 103 | r.Fatal(errors.New("read past end of value")) 104 | return "" 105 | } 106 | 107 | return r.mr.readString(count) 108 | } 109 | 110 | // ReadBytes reads count bytes and returns as []byte 111 | func (r *ValueReader) ReadBytes(count int32) []byte { 112 | if r.err != nil { 113 | return nil 114 | } 115 | 116 | r.valueBytesRemaining -= count 117 | if r.valueBytesRemaining < 0 { 118 | r.Fatal(errors.New("read past end of value")) 119 | return nil 120 | } 121 | 122 | return r.mr.readBytes(count) 123 | } 124 | -------------------------------------------------------------------------------- /src/github.com/lib/pq/.gitignore: -------------------------------------------------------------------------------- 1 | .db 2 | *.test 3 | *~ 4 | *.swp 5 | -------------------------------------------------------------------------------- /src/github.com/lib/pq/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.0.2 5 | - 1.0.3 6 | - 1.1 7 | - 1.2 8 | - tip 9 | 10 | before_install: 11 | - psql --version 12 | - sudo /etc/init.d/postgresql stop 13 | - sudo apt-get -y --purge remove postgresql libpq-dev libpq5 postgresql-client-common postgresql-common 14 | - sudo rm -rf /var/lib/postgresql 15 | - wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add - 16 | - sudo sh -c "echo deb http://apt.postgresql.org/pub/repos/apt/ precise-pgdg main $PGVERSION >> /etc/apt/sources.list.d/postgresql.list" 17 | - sudo apt-get update -qq 18 | - sudo apt-get -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::="--force-confnew" install postgresql-$PGVERSION postgresql-server-dev-$PGVERSION postgresql-contrib-$PGVERSION 19 | - sudo chmod 777 /etc/postgresql/$PGVERSION/main/pg_hba.conf 20 | - sudo echo "local all postgres trust" > /etc/postgresql/$PGVERSION/main/pg_hba.conf 21 | - sudo echo "local all all trust" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf 22 | - sudo echo "host all all 127.0.0.1/32 trust" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf 23 | - sudo echo "host all all ::1/128 trust" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf 24 | - sudo /etc/init.d/postgresql restart 25 | 26 | env: 27 | matrix: 28 | - PGVERSION=9.3 29 | - PGVERSION=9.2 30 | - PGVERSION=9.1 31 | - PGVERSION=9.0 32 | - PGVERSION=8.4 33 | 34 | script: 35 | - env PGUSER=postgres go test -v ./... 36 | 37 | before_script: 38 | - psql -c 'create database pqgotest;' -U postgres 39 | -------------------------------------------------------------------------------- /src/github.com/lib/pq/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing to pq 2 | 3 | `pq` has a backlog of pull requests, but contributions are still very 4 | much welcome. You can help with patch review, submitting bug reports, 5 | or adding new functionality. There is no formal style guide, but 6 | please conform to the style of existing code and general Go formatting 7 | conventions when submitting patches. 8 | 9 | ### Patch review 10 | 11 | Help review existing open pull requests by commenting on the code or 12 | proposed functionality. 13 | 14 | ### Bug reports 15 | 16 | We appreciate any bug reports, but especially ones with self-contained 17 | (doesn't depend on code outside of pq), minimal (can't be simplified 18 | further) test cases. It's especially helpful if you can submit a pull 19 | request with just the failing test case (you'll probably want to 20 | pattern it after the tests in 21 | [conn_test.go](https://github.com/lib/pq/blob/master/conn_test.go). 22 | 23 | ### New functionality 24 | 25 | There are a number of pending patches for new functionality, so 26 | additional feature patches will take a while to merge. Still, patches 27 | are generally reviewed based on usefulness and complexity in addition 28 | to time-in-queue, so if you have a knockout idea, take a shot. Feel 29 | free to open an issue discussion your proposed patch beforehand. 30 | -------------------------------------------------------------------------------- /src/github.com/lib/pq/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011-2013, 'pq' Contributors 2 | Portions Copyright (C) 2011 Blake Mizerany 3 | 4 | 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: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | 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. 9 | -------------------------------------------------------------------------------- /src/github.com/lib/pq/README.md: -------------------------------------------------------------------------------- 1 | # pq - A pure Go postgres driver for Go's database/sql package 2 | 3 | [![Build Status](https://travis-ci.org/lib/pq.png?branch=master)](https://travis-ci.org/lib/pq) 4 | 5 | ## Install 6 | 7 | go get github.com/lib/pq 8 | 9 | ## Docs 10 | 11 | For detailed documentation and basic usage examples, please see the package 12 | documentation at . 13 | 14 | ## Tests 15 | 16 | `go test` is used for testing. A running PostgreSQL server is 17 | required, with the ability to log in. The default database to connect 18 | to test with is "pqgotest," but it can be overridden using environment 19 | variables. 20 | 21 | Example: 22 | 23 | PGHOST=/var/run/postgresql go test github.com/lib/pq 24 | 25 | Optionally, a benchmark suite can be run as part of the tests: 26 | 27 | PGHOST=/var/run/postgresql go test -bench . 28 | 29 | ## Features 30 | 31 | * SSL 32 | * Handles bad connections for `database/sql` 33 | * Scan `time.Time` correctly (i.e. `timestamp[tz]`, `time[tz]`, `date`) 34 | * Scan binary blobs correctly (i.e. `bytea`) 35 | * Package for `hstore` support 36 | * COPY FROM support 37 | * pq.ParseURL for converting urls to connection strings for sql.Open. 38 | * Many libpq compatible environment variables 39 | * Unix socket support 40 | * Notifications: `LISTEN`/`NOTIFY` 41 | 42 | ## Future / Things you can help with 43 | 44 | * Better COPY FROM / COPY TO (see discussion in #181) 45 | 46 | ## Thank you (alphabetical) 47 | 48 | Some of these contributors are from the original library `bmizerany/pq.go` whose 49 | code still exists in here. 50 | 51 | * Andy Balholm (andybalholm) 52 | * Ben Berkert (benburkert) 53 | * Bill Mill (llimllib) 54 | * Bjørn Madsen (aeons) 55 | * Blake Gentry (bgentry) 56 | * Brad Fitzpatrick (bradfitz) 57 | * Chris Walsh (cwds) 58 | * Daniel Farina (fdr) 59 | * Everyone at The Go Team 60 | * Evan Shaw (edsrzf) 61 | * Ewan Chou (coocood) 62 | * Federico Romero (federomero) 63 | * Gary Burd (garyburd) 64 | * Heroku (heroku) 65 | * Jason McVetta (jmcvetta) 66 | * Jeremy Jay (pbnjay) 67 | * Joakim Sernbrant (serbaut) 68 | * John Gallagher (jgallagher) 69 | * Kamil Kisiel (kisielk) 70 | * Kelly Dunn (kellydunn) 71 | * Keith Rarick (kr) 72 | * Kir Shatrov (kirs) 73 | * Maciek Sakrejda (deafbybeheading) 74 | * Marc Brinkmann (mbr) 75 | * Marko Tiikkaja (johto) 76 | * Matt Newberry (MattNewberry) 77 | * Matt Robenolt (mattrobenolt) 78 | * Martin Olsen (martinolsen) 79 | * Mike Lewis (mikelikespie) 80 | * Nicolas Patry (Narsil) 81 | * Oliver Tonnhofer (olt) 82 | * Patrick Hayes (phayes) 83 | * Paul Hammond (paulhammond) 84 | * Ryan Smith (ryandotsmith) 85 | * Samuel Stauffer (samuel) 86 | * Timothée Peignier (cyberdelia) 87 | * notedit (notedit) 88 | -------------------------------------------------------------------------------- /src/github.com/lib/pq/buf.go: -------------------------------------------------------------------------------- 1 | package pq 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "github.com/lib/pq/oid" 7 | ) 8 | 9 | type readBuf []byte 10 | 11 | func (b *readBuf) int32() (n int) { 12 | n = int(int32(binary.BigEndian.Uint32(*b))) 13 | *b = (*b)[4:] 14 | return 15 | } 16 | 17 | func (b *readBuf) oid() (n oid.Oid) { 18 | n = oid.Oid(binary.BigEndian.Uint32(*b)) 19 | *b = (*b)[4:] 20 | return 21 | } 22 | 23 | func (b *readBuf) int16() (n int) { 24 | n = int(binary.BigEndian.Uint16(*b)) 25 | *b = (*b)[2:] 26 | return 27 | } 28 | 29 | func (b *readBuf) string() string { 30 | i := bytes.IndexByte(*b, 0) 31 | if i < 0 { 32 | errorf("invalid message format; expected string terminator") 33 | } 34 | s := (*b)[:i] 35 | *b = (*b)[i+1:] 36 | return string(s) 37 | } 38 | 39 | func (b *readBuf) next(n int) (v []byte) { 40 | v = (*b)[:n] 41 | *b = (*b)[n:] 42 | return 43 | } 44 | 45 | func (b *readBuf) byte() byte { 46 | return b.next(1)[0] 47 | } 48 | 49 | type writeBuf []byte 50 | 51 | func (b *writeBuf) int32(n int) { 52 | x := make([]byte, 4) 53 | binary.BigEndian.PutUint32(x, uint32(n)) 54 | *b = append(*b, x...) 55 | } 56 | 57 | func (b *writeBuf) int16(n int) { 58 | x := make([]byte, 2) 59 | binary.BigEndian.PutUint16(x, uint16(n)) 60 | *b = append(*b, x...) 61 | } 62 | 63 | func (b *writeBuf) string(s string) { 64 | *b = append(*b, (s + "\000")...) 65 | } 66 | 67 | func (b *writeBuf) byte(c byte) { 68 | *b = append(*b, c) 69 | } 70 | 71 | func (b *writeBuf) bytes(v []byte) { 72 | *b = append(*b, v...) 73 | } 74 | -------------------------------------------------------------------------------- /src/github.com/lib/pq/conn_xact_test.go: -------------------------------------------------------------------------------- 1 | // +build go1.1 2 | 3 | package pq 4 | 5 | import ( 6 | "testing" 7 | ) 8 | 9 | func TestXactMultiStmt(t *testing.T) { 10 | // minified test case based on bug reports from 11 | // pico303@gmail.com and rangelspam@gmail.com 12 | t.Skip("Skipping failing test") 13 | db := openTestConn(t) 14 | defer db.Close() 15 | 16 | tx, err := db.Begin() 17 | if err != nil { 18 | t.Fatal(err) 19 | } 20 | defer tx.Commit() 21 | 22 | rows, err := tx.Query("select 1") 23 | if err != nil { 24 | t.Fatal(err) 25 | } 26 | 27 | if rows.Next() { 28 | var val int32 29 | if err = rows.Scan(&val); err != nil { 30 | t.Fatal(err) 31 | } 32 | } else { 33 | t.Fatal("Expected at least one row in first query in xact") 34 | } 35 | 36 | rows2, err := tx.Query("select 2") 37 | if err != nil { 38 | t.Fatal(err) 39 | } 40 | 41 | if rows2.Next() { 42 | var val2 int32 43 | if err := rows2.Scan(&val2); err != nil { 44 | t.Fatal(err) 45 | } 46 | } else { 47 | t.Fatal("Expected at least one row in second query in xact") 48 | } 49 | 50 | if err = rows.Err(); err != nil { 51 | t.Fatal(err) 52 | } 53 | 54 | if err = rows2.Err(); err != nil { 55 | t.Fatal(err) 56 | } 57 | 58 | if err = tx.Commit(); err != nil { 59 | t.Fatal(err) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/github.com/lib/pq/copy.go: -------------------------------------------------------------------------------- 1 | package pq 2 | 3 | import ( 4 | "database/sql/driver" 5 | "encoding/binary" 6 | "errors" 7 | "sync/atomic" 8 | ) 9 | 10 | var ( 11 | errCopyInClosed = errors.New("pq: copyin statement has already been closed") 12 | errBinaryCopyNotSupported = errors.New("pq: only text format supported for COPY") 13 | errCopyToNotSupported = errors.New("pq: COPY TO is not supported") 14 | errCopyNotSupportedOutsideTxn = errors.New("pq: COPY is only allowed inside a transaction") 15 | ) 16 | 17 | // CopyIn creates a COPY FROM statement which can be prepared with 18 | // Tx.Prepare(). The target table should be visible in search_path. 19 | func CopyIn(table string, columns ...string) string { 20 | stmt := "COPY " + QuoteIdentifier(table) + " (" 21 | for i, col := range columns { 22 | if i != 0 { 23 | stmt += ", " 24 | } 25 | stmt += QuoteIdentifier(col) 26 | } 27 | stmt += ") FROM STDIN" 28 | return stmt 29 | } 30 | 31 | // CopyInSchema creates a COPY FROM statement which can be prepared with 32 | // Tx.Prepare(). 33 | func CopyInSchema(schema, table string, columns ...string) string { 34 | stmt := "COPY " + QuoteIdentifier(schema) + "." + QuoteIdentifier(table) + " (" 35 | for i, col := range columns { 36 | if i != 0 { 37 | stmt += ", " 38 | } 39 | stmt += QuoteIdentifier(col) 40 | } 41 | stmt += ") FROM STDIN" 42 | return stmt 43 | } 44 | 45 | type copyin struct { 46 | cn *conn 47 | buffer []byte 48 | rowData chan []byte 49 | done chan bool 50 | 51 | closed bool 52 | err error 53 | errorset int32 54 | } 55 | 56 | const ciBufferSize = 64 * 1024 57 | 58 | // flush buffer before the buffer is filled up and needs reallocation 59 | const ciBufferFlushSize = 63 * 1024 60 | 61 | func (cn *conn) prepareCopyIn(q string) (_ driver.Stmt, err error) { 62 | defer errRecover(&err) 63 | 64 | if !cn.isInTransaction() { 65 | return nil, errCopyNotSupportedOutsideTxn 66 | } 67 | 68 | ci := ©in{ 69 | cn: cn, 70 | buffer: make([]byte, 0, ciBufferSize), 71 | rowData: make(chan []byte), 72 | done: make(chan bool), 73 | } 74 | // add CopyData identifier + 4 bytes for message length 75 | ci.buffer = append(ci.buffer, 'd', 0, 0, 0, 0) 76 | 77 | b := cn.writeBuf('Q') 78 | b.string(q) 79 | cn.send(b) 80 | 81 | awaitCopyInResponse: 82 | for { 83 | t, r := cn.recv1() 84 | switch t { 85 | case 'G': 86 | if r.byte() != 0 { 87 | err = errBinaryCopyNotSupported 88 | break awaitCopyInResponse 89 | } 90 | go ci.resploop() 91 | return ci, nil 92 | case 'H': 93 | err = errCopyToNotSupported 94 | break awaitCopyInResponse 95 | case 'E': 96 | err = parseError(r) 97 | case 'Z': 98 | if err == nil { 99 | errorf("unexpected ReadyForQuery in response to COPY") 100 | } 101 | cn.processReadyForQuery(r) 102 | return nil, err 103 | default: 104 | errorf("unknown response for copy query: %q", t) 105 | } 106 | } 107 | 108 | // something went wrong, abort COPY before we return 109 | b = cn.writeBuf('f') 110 | b.string(err.Error()) 111 | cn.send(b) 112 | 113 | for { 114 | t, r := cn.recv1() 115 | switch t { 116 | case 'c', 'C', 'E': 117 | case 'Z': 118 | // correctly aborted, we're done 119 | cn.processReadyForQuery(r) 120 | return nil, err 121 | default: 122 | errorf("unknown response for CopyFail: %q", t) 123 | } 124 | } 125 | 126 | panic("not reached") 127 | } 128 | 129 | func (ci *copyin) flush(buf []byte) { 130 | // set message length (without message identifier) 131 | binary.BigEndian.PutUint32(buf[1:], uint32(len(buf)-1)) 132 | 133 | _, err := ci.cn.c.Write(buf) 134 | if err != nil { 135 | panic(err) 136 | } 137 | } 138 | 139 | func (ci *copyin) resploop() { 140 | for { 141 | t, r := ci.cn.recv1() 142 | switch t { 143 | case 'C': 144 | // complete 145 | case 'Z': 146 | ci.cn.processReadyForQuery(r) 147 | ci.done <- true 148 | return 149 | case 'E': 150 | err := parseError(r) 151 | ci.setError(err) 152 | default: 153 | errorf("unknown response: %q", t) 154 | } 155 | } 156 | } 157 | 158 | func (ci *copyin) isErrorSet() bool { 159 | return atomic.LoadInt32(&ci.errorset) != 0 160 | } 161 | 162 | func (ci *copyin) setError(err error) { 163 | ci.err = err 164 | atomic.StoreInt32(&ci.errorset, 1) 165 | } 166 | 167 | func (ci *copyin) NumInput() int { 168 | return -1 169 | } 170 | 171 | func (ci *copyin) Query(v []driver.Value) (r driver.Rows, err error) { 172 | return nil, ErrNotSupported 173 | } 174 | 175 | // Exec inserts values into the COPY stream. The insert is asynchronous 176 | // and Exec can return errors from previous Exec calls to the same 177 | // COPY stmt. 178 | // 179 | // You need to call Exec(nil) to sync the COPY stream and to get any 180 | // errors from pending data, since Stmt.Close() doesn't return errors 181 | // to the user. 182 | func (ci *copyin) Exec(v []driver.Value) (r driver.Result, err error) { 183 | defer errRecover(&err) 184 | 185 | if ci.closed { 186 | return nil, errCopyInClosed 187 | } 188 | 189 | if ci.isErrorSet() { 190 | return nil, ci.err 191 | } 192 | 193 | if len(v) == 0 { 194 | err = ci.Close() 195 | ci.closed = true 196 | return nil, err 197 | } 198 | 199 | numValues := len(v) 200 | for i, value := range v { 201 | ci.buffer = appendEncodedText(&ci.cn.parameterStatus, ci.buffer, value) 202 | if i < numValues-1 { 203 | ci.buffer = append(ci.buffer, '\t') 204 | } 205 | } 206 | 207 | ci.buffer = append(ci.buffer, '\n') 208 | 209 | if len(ci.buffer) > ciBufferFlushSize { 210 | ci.flush(ci.buffer) 211 | // reset buffer, keep bytes for message identifier and length 212 | ci.buffer = ci.buffer[:5] 213 | } 214 | 215 | return driver.RowsAffected(0), nil 216 | } 217 | 218 | func (ci *copyin) Close() (err error) { 219 | defer errRecover(&err) 220 | 221 | if ci.closed { 222 | return errCopyInClosed 223 | } 224 | 225 | if len(ci.buffer) > 0 { 226 | ci.flush(ci.buffer) 227 | } 228 | // Avoid touching the scratch buffer as resploop could be using it. 229 | err = ci.cn.sendSimpleMessage('c') 230 | if err != nil { 231 | return err 232 | } 233 | 234 | <-ci.done 235 | 236 | if ci.isErrorSet() { 237 | err = ci.err 238 | return err 239 | } 240 | return nil 241 | } 242 | -------------------------------------------------------------------------------- /src/github.com/lib/pq/hstore/hstore.go: -------------------------------------------------------------------------------- 1 | package hstore 2 | 3 | import ( 4 | "database/sql" 5 | "database/sql/driver" 6 | "strings" 7 | ) 8 | 9 | // A wrapper for transferring Hstore values back and forth easily. 10 | type Hstore struct { 11 | Map map[string]sql.NullString 12 | } 13 | 14 | // escapes and quotes hstore keys/values 15 | // s should be a sql.NullString or string 16 | func hQuote(s interface{}) string { 17 | var str string 18 | switch v := s.(type) { 19 | case sql.NullString: 20 | if !v.Valid { 21 | return "NULL" 22 | } 23 | str = v.String 24 | case string: 25 | str = v 26 | default: 27 | panic("not a string or sql.NullString") 28 | } 29 | 30 | str = strings.Replace(str, "\\", "\\\\", -1) 31 | return `"` + strings.Replace(str, "\"", "\\\"", -1) + `"` 32 | } 33 | 34 | // Scan implements the Scanner interface. 35 | // 36 | // Note h.Map is reallocated before the scan to clear existing values. If the 37 | // hstore column's database value is NULL, then h.Map is set to nil instead. 38 | func (h *Hstore) Scan(value interface{}) error { 39 | if value == nil { 40 | h.Map = nil 41 | return nil 42 | } 43 | h.Map = make(map[string]sql.NullString) 44 | var b byte 45 | pair := [][]byte{{}, {}} 46 | pi := 0 47 | inQuote := false 48 | didQuote := false 49 | sawSlash := false 50 | bindex := 0 51 | for bindex, b = range value.([]byte) { 52 | if sawSlash { 53 | pair[pi] = append(pair[pi], b) 54 | sawSlash = false 55 | continue 56 | } 57 | 58 | switch b { 59 | case '\\': 60 | sawSlash = true 61 | continue 62 | case '"': 63 | inQuote = !inQuote 64 | if !didQuote { 65 | didQuote = true 66 | } 67 | continue 68 | default: 69 | if !inQuote { 70 | switch b { 71 | case ' ', '\t', '\n', '\r': 72 | continue 73 | case '=': 74 | continue 75 | case '>': 76 | pi = 1 77 | didQuote = false 78 | continue 79 | case ',': 80 | s := string(pair[1]) 81 | if !didQuote && len(s) == 4 && strings.ToLower(s) == "null" { 82 | h.Map[string(pair[0])] = sql.NullString{String: "", Valid: false} 83 | } else { 84 | h.Map[string(pair[0])] = sql.NullString{String: string(pair[1]), Valid: true} 85 | } 86 | pair[0] = []byte{} 87 | pair[1] = []byte{} 88 | pi = 0 89 | continue 90 | } 91 | } 92 | } 93 | pair[pi] = append(pair[pi], b) 94 | } 95 | if bindex > 0 { 96 | s := string(pair[1]) 97 | if !didQuote && len(s) == 4 && strings.ToLower(s) == "null" { 98 | h.Map[string(pair[0])] = sql.NullString{String: "", Valid: false} 99 | } else { 100 | h.Map[string(pair[0])] = sql.NullString{String: string(pair[1]), Valid: true} 101 | } 102 | } 103 | return nil 104 | } 105 | 106 | // Value implements the driver Valuer interface. Note if h.Map is nil, the 107 | // database column value will be set to NULL. 108 | func (h Hstore) Value() (driver.Value, error) { 109 | if h.Map == nil { 110 | return nil, nil 111 | } 112 | parts := []string{} 113 | for key, val := range h.Map { 114 | thispart := hQuote(key) + "=>" + hQuote(val) 115 | parts = append(parts, thispart) 116 | } 117 | return []byte(strings.Join(parts, ",")), nil 118 | } 119 | -------------------------------------------------------------------------------- /src/github.com/lib/pq/hstore/hstore_test.go: -------------------------------------------------------------------------------- 1 | package hstore 2 | 3 | import ( 4 | "database/sql" 5 | _ "github.com/lib/pq" 6 | "os" 7 | "testing" 8 | ) 9 | 10 | type Fatalistic interface { 11 | Fatal(args ...interface{}) 12 | } 13 | 14 | func openTestConn(t Fatalistic) *sql.DB { 15 | datname := os.Getenv("PGDATABASE") 16 | sslmode := os.Getenv("PGSSLMODE") 17 | 18 | if datname == "" { 19 | os.Setenv("PGDATABASE", "pqgotest") 20 | } 21 | 22 | if sslmode == "" { 23 | os.Setenv("PGSSLMODE", "disable") 24 | } 25 | 26 | conn, err := sql.Open("postgres", "") 27 | if err != nil { 28 | t.Fatal(err) 29 | } 30 | 31 | return conn 32 | } 33 | 34 | func TestHstore(t *testing.T) { 35 | db := openTestConn(t) 36 | defer db.Close() 37 | 38 | // quitely create hstore if it doesn't exist 39 | _, err := db.Exec("CREATE EXTENSION IF NOT EXISTS hstore") 40 | if err != nil { 41 | t.Log("Skipping hstore tests - hstore extension create failed. " + err.Error()) 42 | return 43 | } 44 | 45 | hs := Hstore{} 46 | 47 | // test for null-valued hstores 48 | err = db.QueryRow("SELECT NULL::hstore").Scan(&hs) 49 | if err != nil { 50 | t.Fatal(err) 51 | } 52 | if hs.Map != nil { 53 | t.Fatalf("expected null map") 54 | } 55 | 56 | err = db.QueryRow("SELECT $1::hstore", hs).Scan(&hs) 57 | if err != nil { 58 | t.Fatalf("re-query null map failed: %s", err.Error()) 59 | } 60 | if hs.Map != nil { 61 | t.Fatalf("expected null map") 62 | } 63 | 64 | // test for empty hstores 65 | err = db.QueryRow("SELECT ''::hstore").Scan(&hs) 66 | if err != nil { 67 | t.Fatal(err) 68 | } 69 | if hs.Map == nil { 70 | t.Fatalf("expected empty map, got null map") 71 | } 72 | if len(hs.Map) != 0 { 73 | t.Fatalf("expected empty map, got len(map)=%d", len(hs.Map)) 74 | } 75 | 76 | err = db.QueryRow("SELECT $1::hstore", hs).Scan(&hs) 77 | if err != nil { 78 | t.Fatalf("re-query empty map failed: %s", err.Error()) 79 | } 80 | if hs.Map == nil { 81 | t.Fatalf("expected empty map, got null map") 82 | } 83 | if len(hs.Map) != 0 { 84 | t.Fatalf("expected empty map, got len(map)=%d", len(hs.Map)) 85 | } 86 | 87 | // a few example maps to test out 88 | hsOnePair := Hstore{ 89 | Map: map[string]sql.NullString{ 90 | "key1": {"value1", true}, 91 | }, 92 | } 93 | 94 | hsThreePairs := Hstore{ 95 | Map: map[string]sql.NullString{ 96 | "key1": {"value1", true}, 97 | "key2": {"value2", true}, 98 | "key3": {"value3", true}, 99 | }, 100 | } 101 | 102 | hsSmorgasbord := Hstore{ 103 | Map: map[string]sql.NullString{ 104 | "nullstring": {"NULL", true}, 105 | "actuallynull": {"", false}, 106 | "NULL": {"NULL string key", true}, 107 | "withbracket": {"value>42", true}, 108 | "withequal": {"value=42", true}, 109 | `"withquotes1"`: {`this "should" be fine`, true}, 110 | `"withquotes"2"`: {`this "should\" also be fine`, true}, 111 | "embedded1": {"value1=>x1", true}, 112 | "embedded2": {`"value2"=>x2`, true}, 113 | "withnewlines": {"\n\nvalue\t=>2", true}, 114 | "<>": {`this, "should,\" also, => be fine`, true}, 115 | }, 116 | } 117 | 118 | // test encoding in query params, then decoding during Scan 119 | testBidirectional := func(h Hstore) { 120 | err = db.QueryRow("SELECT $1::hstore", h).Scan(&hs) 121 | if err != nil { 122 | t.Fatalf("re-query %d-pair map failed: %s", len(h.Map), err.Error()) 123 | } 124 | if hs.Map == nil { 125 | t.Fatalf("expected %d-pair map, got null map", len(h.Map)) 126 | } 127 | if len(hs.Map) != len(h.Map) { 128 | t.Fatalf("expected %d-pair map, got len(map)=%d", len(h.Map), len(hs.Map)) 129 | } 130 | 131 | for key, val := range hs.Map { 132 | otherval, found := h.Map[key] 133 | if !found { 134 | t.Fatalf(" key '%v' not found in %d-pair map", key, len(h.Map)) 135 | } 136 | if otherval.Valid != val.Valid { 137 | t.Fatalf(" value %v <> %v in %d-pair map", otherval, val, len(h.Map)) 138 | } 139 | if otherval.String != val.String { 140 | t.Fatalf(" value '%v' <> '%v' in %d-pair map", otherval.String, val.String, len(h.Map)) 141 | } 142 | } 143 | } 144 | 145 | testBidirectional(hsOnePair) 146 | testBidirectional(hsThreePairs) 147 | testBidirectional(hsSmorgasbord) 148 | } 149 | -------------------------------------------------------------------------------- /src/github.com/lib/pq/listen_example/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Below you will find a self-contained Go program which uses the LISTEN / NOTIFY 4 | mechanism to avoid polling the database while waiting for more work to arrive. 5 | 6 | // 7 | // You can see the program in action by defining a function similar to 8 | // the following: 9 | // 10 | // CREATE OR REPLACE FUNCTION public.get_work() 11 | // RETURNS bigint 12 | // LANGUAGE sql 13 | // AS $$ 14 | // SELECT CASE WHEN random() >= 0.2 THEN int8 '1' END 15 | // $$ 16 | // ; 17 | 18 | package main 19 | 20 | import ( 21 | "github.com/lib/pq" 22 | 23 | "database/sql" 24 | "fmt" 25 | "time" 26 | ) 27 | 28 | func doWork(db *sql.DB, work int64) { 29 | // work here 30 | } 31 | 32 | func getWork(db *sql.DB) { 33 | for { 34 | // get work from the database here 35 | var work sql.NullInt64 36 | err := db.QueryRow("SELECT get_work()").Scan(&work) 37 | if err != nil { 38 | fmt.Println("call to get_work() failed: ", err) 39 | time.Sleep(10 * time.Second) 40 | continue 41 | } 42 | if !work.Valid { 43 | // no more work to do 44 | fmt.Println("ran out of work") 45 | return 46 | } 47 | 48 | fmt.Println("starting work on ", work.Int64) 49 | go doWork(db, work.Int64) 50 | } 51 | } 52 | 53 | func waitForNotification(l *pq.Listener) { 54 | for { 55 | select { 56 | case <-l.Notify: 57 | fmt.Println("received notification, new work available") 58 | return 59 | case <-time.After(90 * time.Second): 60 | go func() { 61 | l.Ping() 62 | }() 63 | // Check if there's more work available, just in case it takes 64 | // a while for the Listener to notice connection loss and 65 | // reconnect. 66 | fmt.Println("received no work for 90 seconds, checking for new work") 67 | return 68 | } 69 | } 70 | } 71 | 72 | func main() { 73 | var conninfo string = "" 74 | 75 | db, err := sql.Open("postgres", conninfo) 76 | if err != nil { 77 | panic(err) 78 | } 79 | 80 | reportProblem := func(ev pq.ListenerEventType, err error) { 81 | if err != nil { 82 | fmt.Println(err.Error()) 83 | } 84 | } 85 | 86 | listener := pq.NewListener(conninfo, 10 * time.Second, time.Minute, reportProblem) 87 | err = listener.Listen("getwork") 88 | if err != nil { 89 | panic(err) 90 | } 91 | 92 | fmt.Println("entering main loop") 93 | for { 94 | // process all available work before waiting for notifications 95 | getWork(db) 96 | waitForNotification(listener) 97 | } 98 | } 99 | 100 | 101 | */ 102 | package listen_example 103 | -------------------------------------------------------------------------------- /src/github.com/lib/pq/oid/doc.go: -------------------------------------------------------------------------------- 1 | // Package oid contains OID constants 2 | // as defined by the Postgres server. 3 | package oid 4 | 5 | // Oid is a Postgres Object ID. 6 | type Oid uint32 7 | -------------------------------------------------------------------------------- /src/github.com/lib/pq/oid/gen.go: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | // Generate the table of OID values 4 | // Run with 'go run gen.go'. 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "log" 10 | "os" 11 | "os/exec" 12 | 13 | "database/sql" 14 | _ "github.com/lib/pq" 15 | ) 16 | 17 | func main() { 18 | datname := os.Getenv("PGDATABASE") 19 | sslmode := os.Getenv("PGSSLMODE") 20 | 21 | if datname == "" { 22 | os.Setenv("PGDATABASE", "pqgotest") 23 | } 24 | 25 | if sslmode == "" { 26 | os.Setenv("PGSSLMODE", "disable") 27 | } 28 | 29 | db, err := sql.Open("postgres", "") 30 | if err != nil { 31 | log.Fatal(err) 32 | } 33 | cmd := exec.Command("gofmt") 34 | cmd.Stderr = os.Stderr 35 | w, err := cmd.StdinPipe() 36 | if err != nil { 37 | log.Fatal(err) 38 | } 39 | f, err := os.Create("types.go") 40 | if err != nil { 41 | log.Fatal(err) 42 | } 43 | cmd.Stdout = f 44 | err = cmd.Start() 45 | if err != nil { 46 | log.Fatal(err) 47 | } 48 | fmt.Fprintln(w, "// generated by 'go run gen.go'; do not edit") 49 | fmt.Fprintln(w, "\npackage oid") 50 | fmt.Fprintln(w, "const (") 51 | rows, err := db.Query(` 52 | SELECT typname, oid 53 | FROM pg_type WHERE oid < 10000 54 | ORDER BY oid; 55 | `) 56 | if err != nil { 57 | log.Fatal(err) 58 | } 59 | var name string 60 | var oid int 61 | for rows.Next() { 62 | err = rows.Scan(&name, &oid) 63 | if err != nil { 64 | log.Fatal(err) 65 | } 66 | fmt.Fprintf(w, "T_%s Oid = %d\n", name, oid) 67 | } 68 | if err = rows.Err(); err != nil { 69 | log.Fatal(err) 70 | } 71 | fmt.Fprintln(w, ")") 72 | w.Close() 73 | cmd.Wait() 74 | } 75 | -------------------------------------------------------------------------------- /src/github.com/lib/pq/oid/types.go: -------------------------------------------------------------------------------- 1 | // generated by 'go run gen.go'; do not edit 2 | 3 | package oid 4 | 5 | const ( 6 | T_bool Oid = 16 7 | T_bytea Oid = 17 8 | T_char Oid = 18 9 | T_name Oid = 19 10 | T_int8 Oid = 20 11 | T_int2 Oid = 21 12 | T_int2vector Oid = 22 13 | T_int4 Oid = 23 14 | T_regproc Oid = 24 15 | T_text Oid = 25 16 | T_oid Oid = 26 17 | T_tid Oid = 27 18 | T_xid Oid = 28 19 | T_cid Oid = 29 20 | T_oidvector Oid = 30 21 | T_pg_type Oid = 71 22 | T_pg_attribute Oid = 75 23 | T_pg_proc Oid = 81 24 | T_pg_class Oid = 83 25 | T_json Oid = 114 26 | T_xml Oid = 142 27 | T__xml Oid = 143 28 | T_pg_node_tree Oid = 194 29 | T__json Oid = 199 30 | T_smgr Oid = 210 31 | T_point Oid = 600 32 | T_lseg Oid = 601 33 | T_path Oid = 602 34 | T_box Oid = 603 35 | T_polygon Oid = 604 36 | T_line Oid = 628 37 | T__line Oid = 629 38 | T_cidr Oid = 650 39 | T__cidr Oid = 651 40 | T_float4 Oid = 700 41 | T_float8 Oid = 701 42 | T_abstime Oid = 702 43 | T_reltime Oid = 703 44 | T_tinterval Oid = 704 45 | T_unknown Oid = 705 46 | T_circle Oid = 718 47 | T__circle Oid = 719 48 | T_money Oid = 790 49 | T__money Oid = 791 50 | T_macaddr Oid = 829 51 | T_inet Oid = 869 52 | T__bool Oid = 1000 53 | T__bytea Oid = 1001 54 | T__char Oid = 1002 55 | T__name Oid = 1003 56 | T__int2 Oid = 1005 57 | T__int2vector Oid = 1006 58 | T__int4 Oid = 1007 59 | T__regproc Oid = 1008 60 | T__text Oid = 1009 61 | T__tid Oid = 1010 62 | T__xid Oid = 1011 63 | T__cid Oid = 1012 64 | T__oidvector Oid = 1013 65 | T__bpchar Oid = 1014 66 | T__varchar Oid = 1015 67 | T__int8 Oid = 1016 68 | T__point Oid = 1017 69 | T__lseg Oid = 1018 70 | T__path Oid = 1019 71 | T__box Oid = 1020 72 | T__float4 Oid = 1021 73 | T__float8 Oid = 1022 74 | T__abstime Oid = 1023 75 | T__reltime Oid = 1024 76 | T__tinterval Oid = 1025 77 | T__polygon Oid = 1027 78 | T__oid Oid = 1028 79 | T_aclitem Oid = 1033 80 | T__aclitem Oid = 1034 81 | T__macaddr Oid = 1040 82 | T__inet Oid = 1041 83 | T_bpchar Oid = 1042 84 | T_varchar Oid = 1043 85 | T_date Oid = 1082 86 | T_time Oid = 1083 87 | T_timestamp Oid = 1114 88 | T__timestamp Oid = 1115 89 | T__date Oid = 1182 90 | T__time Oid = 1183 91 | T_timestamptz Oid = 1184 92 | T__timestamptz Oid = 1185 93 | T_interval Oid = 1186 94 | T__interval Oid = 1187 95 | T__numeric Oid = 1231 96 | T_pg_database Oid = 1248 97 | T__cstring Oid = 1263 98 | T_timetz Oid = 1266 99 | T__timetz Oid = 1270 100 | T_bit Oid = 1560 101 | T__bit Oid = 1561 102 | T_varbit Oid = 1562 103 | T__varbit Oid = 1563 104 | T_numeric Oid = 1700 105 | T_refcursor Oid = 1790 106 | T__refcursor Oid = 2201 107 | T_regprocedure Oid = 2202 108 | T_regoper Oid = 2203 109 | T_regoperator Oid = 2204 110 | T_regclass Oid = 2205 111 | T_regtype Oid = 2206 112 | T__regprocedure Oid = 2207 113 | T__regoper Oid = 2208 114 | T__regoperator Oid = 2209 115 | T__regclass Oid = 2210 116 | T__regtype Oid = 2211 117 | T_record Oid = 2249 118 | T_cstring Oid = 2275 119 | T_any Oid = 2276 120 | T_anyarray Oid = 2277 121 | T_void Oid = 2278 122 | T_trigger Oid = 2279 123 | T_language_handler Oid = 2280 124 | T_internal Oid = 2281 125 | T_opaque Oid = 2282 126 | T_anyelement Oid = 2283 127 | T__record Oid = 2287 128 | T_anynonarray Oid = 2776 129 | T_pg_authid Oid = 2842 130 | T_pg_auth_members Oid = 2843 131 | T__txid_snapshot Oid = 2949 132 | T_uuid Oid = 2950 133 | T__uuid Oid = 2951 134 | T_txid_snapshot Oid = 2970 135 | T_fdw_handler Oid = 3115 136 | T_anyenum Oid = 3500 137 | T_tsvector Oid = 3614 138 | T_tsquery Oid = 3615 139 | T_gtsvector Oid = 3642 140 | T__tsvector Oid = 3643 141 | T__gtsvector Oid = 3644 142 | T__tsquery Oid = 3645 143 | T_regconfig Oid = 3734 144 | T__regconfig Oid = 3735 145 | T_regdictionary Oid = 3769 146 | T__regdictionary Oid = 3770 147 | T_anyrange Oid = 3831 148 | T_event_trigger Oid = 3838 149 | T_int4range Oid = 3904 150 | T__int4range Oid = 3905 151 | T_numrange Oid = 3906 152 | T__numrange Oid = 3907 153 | T_tsrange Oid = 3908 154 | T__tsrange Oid = 3909 155 | T_tstzrange Oid = 3910 156 | T__tstzrange Oid = 3911 157 | T_daterange Oid = 3912 158 | T__daterange Oid = 3913 159 | T_int8range Oid = 3926 160 | T__int8range Oid = 3927 161 | ) 162 | -------------------------------------------------------------------------------- /src/github.com/lib/pq/url.go: -------------------------------------------------------------------------------- 1 | package pq 2 | 3 | import ( 4 | "fmt" 5 | nurl "net/url" 6 | "sort" 7 | "strings" 8 | ) 9 | 10 | // ParseURL no longer needs to be used by clients of this library since supplying a URL as a 11 | // connection string to sql.Open() is now supported: 12 | // 13 | // sql.Open("postgres", "postgres://bob:secret@1.2.3.4:5432/mydb?sslmode=verify-full") 14 | // 15 | // It remains exported here for backwards-compatibility. 16 | // 17 | // ParseURL converts a url to a connection string for driver.Open. 18 | // Example: 19 | // 20 | // "postgres://bob:secret@1.2.3.4:5432/mydb?sslmode=verify-full" 21 | // 22 | // converts to: 23 | // 24 | // "user=bob password=secret host=1.2.3.4 port=5432 dbname=mydb sslmode=verify-full" 25 | // 26 | // A minimal example: 27 | // 28 | // "postgres://" 29 | // 30 | // This will be blank, causing driver.Open to use all of the defaults 31 | func ParseURL(url string) (string, error) { 32 | u, err := nurl.Parse(url) 33 | if err != nil { 34 | return "", err 35 | } 36 | 37 | if u.Scheme != "postgres" { 38 | return "", fmt.Errorf("invalid connection protocol: %s", u.Scheme) 39 | } 40 | 41 | var kvs []string 42 | accrue := func(k, v string) { 43 | if v != "" { 44 | kvs = append(kvs, k+"="+v) 45 | } 46 | } 47 | 48 | if u.User != nil { 49 | v := u.User.Username() 50 | accrue("user", v) 51 | 52 | v, _ = u.User.Password() 53 | accrue("password", v) 54 | } 55 | 56 | i := strings.Index(u.Host, ":") 57 | if i < 0 { 58 | accrue("host", u.Host) 59 | } else { 60 | accrue("host", u.Host[:i]) 61 | accrue("port", u.Host[i+1:]) 62 | } 63 | 64 | if u.Path != "" { 65 | accrue("dbname", u.Path[1:]) 66 | } 67 | 68 | q := u.Query() 69 | for k := range q { 70 | accrue(k, q.Get(k)) 71 | } 72 | 73 | sort.Strings(kvs) // Makes testing easier (not a performance concern) 74 | return strings.Join(kvs, " "), nil 75 | } 76 | -------------------------------------------------------------------------------- /src/github.com/lib/pq/url_test.go: -------------------------------------------------------------------------------- 1 | package pq 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestSimpleParseURL(t *testing.T) { 8 | expected := "host=hostname.remote" 9 | str, err := ParseURL("postgres://hostname.remote") 10 | if err != nil { 11 | t.Fatal(err) 12 | } 13 | 14 | if str != expected { 15 | t.Fatalf("unexpected result from ParseURL:\n+ %v\n- %v", str, expected) 16 | } 17 | } 18 | 19 | func TestFullParseURL(t *testing.T) { 20 | expected := "dbname=database host=hostname.remote password=secret port=1234 user=username" 21 | str, err := ParseURL("postgres://username:secret@hostname.remote:1234/database") 22 | if err != nil { 23 | t.Fatal(err) 24 | } 25 | 26 | if str != expected { 27 | t.Fatalf("unexpected result from ParseURL:\n+ %s\n- %s", str, expected) 28 | } 29 | } 30 | 31 | func TestInvalidProtocolParseURL(t *testing.T) { 32 | _, err := ParseURL("http://hostname.remote") 33 | switch err { 34 | case nil: 35 | t.Fatal("Expected an error from parsing invalid protocol") 36 | default: 37 | msg := "invalid connection protocol: http" 38 | if err.Error() != msg { 39 | t.Fatalf("Unexpected error message:\n+ %s\n- %s", 40 | err.Error(), msg) 41 | } 42 | } 43 | } 44 | 45 | func TestMinimalURL(t *testing.T) { 46 | cs, err := ParseURL("postgres://") 47 | if err != nil { 48 | t.Fatal(err) 49 | } 50 | 51 | if cs != "" { 52 | t.Fatalf("expected blank connection string, got: %q", cs) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/github.com/lib/pq/user_posix.go: -------------------------------------------------------------------------------- 1 | // Package pq is a pure Go Postgres driver for the database/sql package. 2 | 3 | // +build darwin freebsd linux netbsd openbsd 4 | 5 | package pq 6 | 7 | import "os/user" 8 | 9 | func userCurrent() (string, error) { 10 | u, err := user.Current() 11 | if err != nil { 12 | return "", err 13 | } 14 | return u.Username, nil 15 | } 16 | -------------------------------------------------------------------------------- /src/github.com/lib/pq/user_windows.go: -------------------------------------------------------------------------------- 1 | // Package pq is a pure Go Postgres driver for the database/sql package. 2 | package pq 3 | 4 | import ( 5 | "path/filepath" 6 | "syscall" 7 | ) 8 | 9 | // Perform Windows user name lookup identically to libpq. 10 | // 11 | // The PostgreSQL code makes use of the legacy Win32 function 12 | // GetUserName, and that function has not been imported into stock Go. 13 | // GetUserNameEx is available though, the difference being that a 14 | // wider range of names are available. To get the output to be the 15 | // same as GetUserName, only the base (or last) component of the 16 | // result is returned. 17 | func userCurrent() (string, error) { 18 | pw_name := make([]uint16, 128) 19 | pwname_size := uint32(len(pw_name)) - 1 20 | err := syscall.GetUserNameEx(syscall.NameSamCompatible, &pw_name[0], &pwname_size) 21 | if err != nil { 22 | return "", err 23 | } 24 | s := syscall.UTF16ToString(pw_name) 25 | u := filepath.Base(s) 26 | return u, nil 27 | } 28 | -------------------------------------------------------------------------------- /src/github.com/mattn/go-sqlite3/Makefile: -------------------------------------------------------------------------------- 1 | include $(GOROOT)/src/Make.inc 2 | 3 | TARG = github.com/mattn/go-sqlite3 4 | CGOFILES = sqlite3.go 5 | 6 | include $(GOROOT)/src/Make.pkg 7 | -------------------------------------------------------------------------------- /src/github.com/mattn/go-sqlite3/README.mkd: -------------------------------------------------------------------------------- 1 | go-sqlite3 2 | ========== 3 | 4 | DESCRIPTION 5 | ----------- 6 | 7 | sqlite3 driver for go that using exp/sql 8 | 9 | LICENSE 10 | ------- 11 | 12 | MIT: http://mattn.mit-license.org/2011 13 | -------------------------------------------------------------------------------- /src/github.com/mattn/go-sqlite3/example/Makefile: -------------------------------------------------------------------------------- 1 | include $(GOROOT)/src/Make.inc 2 | 3 | TARG = main 4 | GOFILES = main.go 5 | 6 | include $(GOROOT)/src/Make.cmd 7 | -------------------------------------------------------------------------------- /src/github.com/mattn/go-sqlite3/example/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | _ "github.com/mattn/go-sqlite3" 7 | "os" 8 | ) 9 | 10 | func main() { 11 | os.Remove("./foo.db") 12 | 13 | db, err := sql.Open("sqlite3", "./foo.db") 14 | if err != nil { 15 | fmt.Println(err) 16 | return 17 | } 18 | 19 | sqls := []string{ 20 | "create table foo (id integer not null primary key, name text)", 21 | "delete from foo", 22 | } 23 | for _, sql := range sqls { 24 | _, err = db.Exec(sql) 25 | if err != nil { 26 | fmt.Printf("%q: %s\n", err, sql) 27 | return 28 | } 29 | } 30 | 31 | tx, err := db.Begin() 32 | if err != nil { 33 | fmt.Println(err) 34 | return 35 | } 36 | stmt, err := tx.Prepare("insert into foo(id, name) values(?, ?)") 37 | if err != nil { 38 | fmt.Println(err) 39 | return 40 | } 41 | defer stmt.Close() 42 | 43 | for i := 0; i < 100; i++ { 44 | _, err = stmt.Exec(i, fmt.Sprintf("こんにちわ世界%03d", i)) 45 | if err != nil { 46 | fmt.Println(err) 47 | return 48 | } 49 | } 50 | tx.Commit() 51 | 52 | rows, err := db.Query("select id, name from foo") 53 | if err != nil { 54 | fmt.Println(err) 55 | return 56 | } 57 | defer rows.Close() 58 | 59 | for rows.Next() { 60 | var id int 61 | var name string 62 | rows.Scan(&id, &name) 63 | println(id, name) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/github.com/tgulacsi/goracle/LICENSE.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bradfitz/go-sql-test/6b279fe18d201f5e443a388d02b31547cc0c4172/src/github.com/tgulacsi/goracle/LICENSE.txt -------------------------------------------------------------------------------- /src/github.com/tgulacsi/goracle/README.md: -------------------------------------------------------------------------------- 1 | # goracle # 2 | *goracle/oracle* is a package is a translated version of 3 | cx_Oracle (by Anthony Tuininga) converted from C (Python module) to Go. 4 | 5 | *goracle/godrv* is a package which is a database/sql/driver.Driver 6 | compliant wrapper for goracle/oracle - passes github.com/bradfitz/go-sql-test (as github.com/tgulacsi/go-sql-test). 7 | 8 | ## There ## 9 | CHAR, VARCHAR2, NUMBER, DATETIME simple AND array bind/define. 10 | 11 | ## Not working ## 12 | INTERVAL 13 | 14 | ## Not tested (yet) ## 15 | LONG, LONG RAW, CURSOR, LOB datatypes. -------------------------------------------------------------------------------- /src/github.com/tgulacsi/goracle/conntest/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "github.com/tgulacsi/goracle/oracle" 6 | "log" 7 | "os" 8 | "os/signal" 9 | "sync" 10 | "syscall" 11 | "testing" 12 | ) 13 | 14 | var ( 15 | dsn = flag.String("dsn", "", "Oracle DSN (user/passw@sid)") 16 | wait = flag.Bool("wait", false, "wait for USR1 signal?") 17 | ) 18 | 19 | func init() { 20 | flag.Parse() 21 | if *dsn == "" { 22 | *dsn = os.Getenv("DSN") 23 | } 24 | } 25 | 26 | func TestCursor(t *testing.T) { 27 | conn := getConnection(t) 28 | if !conn.IsConnected() { 29 | t.FailNow() 30 | } 31 | cur := conn.NewCursor() 32 | defer cur.Close() 33 | qry := "SELECT owner, object_name, object_id FROM all_objects WHERE ROWNUM < 20" 34 | log.Printf(`executing "%s"`, qry) 35 | if err := cur.Execute(qry, nil, nil); err != nil { 36 | t.Logf(`error with "%s": %s`, qry, err) 37 | t.Fail() 38 | } 39 | row, err := cur.FetchOne() 40 | if err != nil { 41 | t.Logf("error fetching: %s", err) 42 | t.Fail() 43 | } 44 | t.Logf("row: %+v", row) 45 | rows, err := cur.FetchMany(1000) 46 | if err != nil { 47 | t.Logf("error fetching many: %s", err) 48 | t.Fail() 49 | } 50 | for i, row := range rows { 51 | t.Logf("%03d: %v", i, row) 52 | } 53 | 54 | } 55 | 56 | var conn oracle.Connection 57 | 58 | func getConnection(t *testing.T) oracle.Connection { 59 | if conn.IsConnected() { 60 | return conn 61 | } 62 | 63 | if !(dsn != nil && *dsn != "") { 64 | t.Logf("cannot test connection without dsn!") 65 | return conn 66 | } 67 | user, passw, sid := oracle.SplitDsn(*dsn) 68 | var err error 69 | log.Printf("connecting to %s", *dsn) 70 | conn, err = oracle.NewConnection(user, passw, sid) 71 | if err != nil { 72 | t.Logf("error creating connection to %s: %s", *dsn, err) 73 | t.Fail() 74 | } 75 | if err = conn.Connect(0, false); err != nil { 76 | t.Logf("error connecting: %s", err) 77 | t.Fail() 78 | } 79 | return conn 80 | } 81 | 82 | func main() { 83 | t := new(testing.T) 84 | if *wait { 85 | c := make(chan os.Signal) 86 | var wg sync.WaitGroup 87 | wg.Add(1) 88 | go func() { 89 | log.Printf("waiting for signal...") 90 | sig := <-c 91 | log.Printf("got signal %s", sig) 92 | TestCursor(t) 93 | wg.Done() 94 | }() 95 | signal.Notify(c, syscall.SIGUSR1) 96 | wg.Wait() 97 | } else { 98 | TestCursor(t) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/github.com/tgulacsi/goracle/godrv/driver.go: -------------------------------------------------------------------------------- 1 | //Go Oracle driver 2 | package godrv 3 | 4 | import ( 5 | "database/sql" 6 | "database/sql/driver" 7 | "errors" 8 | // "fmt" 9 | "github.com/tgulacsi/goracle/oracle" 10 | // "io" 11 | // "math" 12 | // "net" 13 | "log" 14 | // "reflect" 15 | "strings" 16 | // "time" 17 | "unsafe" 18 | ) 19 | 20 | var ( 21 | NotImplemented = errors.New("Not implemented") 22 | IsDebug bool 23 | ) 24 | 25 | type conn struct { 26 | cx *oracle.Connection 27 | } 28 | 29 | type stmt struct { 30 | cu *oracle.Cursor //Stmt ? 31 | statement string 32 | } 33 | 34 | // Prepare the query for execution, return a prepared statement and error 35 | func (c conn) Prepare(query string) (driver.Stmt, error) { 36 | cu := c.cx.NewCursor() 37 | debug("%p.Prepare(%s)", cu, query) 38 | err := cu.Prepare(query, "") 39 | if err != nil { 40 | return nil, err 41 | } 42 | return stmt{cu: cu, statement: query}, nil 43 | } 44 | 45 | // closes the connection 46 | func (c conn) Close() error { 47 | err := c.cx.Close() 48 | c.cx = nil 49 | return err 50 | } 51 | 52 | type tx struct { 53 | cx *oracle.Connection //Transaction ? 54 | } 55 | 56 | // begins a transaction 57 | func (c conn) Begin() (driver.Tx, error) { 58 | if !c.cx.IsConnected() { 59 | if err := c.cx.Connect(0, false); err != nil { 60 | return tx{cx: nil}, err 61 | } 62 | } 63 | return tx{cx: c.cx}, nil 64 | } 65 | 66 | // commits currently opened transaction 67 | func (t tx) Commit() error { 68 | if t.cx != nil && t.cx.IsConnected() { 69 | return t.cx.NewCursor().Execute("COMMIT", nil, nil) 70 | } 71 | return nil 72 | } 73 | 74 | // rolls back current transaction 75 | func (t tx) Rollback() error { 76 | return t.cx.Rollback() 77 | } 78 | 79 | // closes statement 80 | func (s stmt) Close() error { 81 | if s.cu != nil { 82 | debug("CLOSEing statement %p (%s)", s.cu, s.statement) 83 | s.cu.Close() 84 | s.cu = nil 85 | } 86 | return nil 87 | } 88 | 89 | // number of input parameters 90 | func (s stmt) NumInput() int { 91 | names, err := s.cu.GetBindNames() 92 | if err != nil { 93 | log.Printf("error getting bind names of %p: %s", s.cu, err) 94 | return -1 95 | } 96 | return len(names) 97 | } 98 | 99 | type rowsRes struct { 100 | cu *oracle.Cursor 101 | cols []oracle.VariableDescription 102 | } 103 | 104 | // executes the statement 105 | func (s stmt) run(args []driver.Value) (*rowsRes, error) { 106 | //A driver Value is a value that drivers must be able to handle. 107 | //A Value is either nil or an instance of one of these types: 108 | //int64 109 | //float64 110 | //bool 111 | //[]byte 112 | //string [*] everywhere except from Rows.Next. 113 | //time.Time 114 | 115 | var err error 116 | a := (*[]interface{})(unsafe.Pointer(&args)) 117 | debug("%p.run(%s, %v)", s.cu, s.statement, *a) 118 | if err = s.cu.Execute(s.statement, *a, nil); err != nil { 119 | return nil, err 120 | } 121 | 122 | var cols []oracle.VariableDescription 123 | if !s.cu.IsDDL() { 124 | cols, err = s.cu.GetDescription() 125 | debug("cols: %+v err: %s", cols, err) 126 | if err != nil { 127 | return nil, err 128 | } 129 | } 130 | return &rowsRes{cu: s.cu, cols: cols}, nil 131 | } 132 | 133 | func (s stmt) Exec(args []driver.Value) (driver.Result, error) { 134 | return s.run(args) 135 | } 136 | 137 | func (s stmt) Query(args []driver.Value) (driver.Rows, error) { 138 | return s.run(args) 139 | } 140 | 141 | func (r rowsRes) LastInsertId() (int64, error) { 142 | return -1, NotImplemented 143 | } 144 | 145 | func (r rowsRes) RowsAffected() (int64, error) { 146 | return int64(r.cu.GetRowCount()), nil 147 | } 148 | 149 | // resultset column names 150 | func (r rowsRes) Columns() []string { 151 | cls := make([]string, len(r.cols)) 152 | for i, c := range r.cols { 153 | cls[i] = c.Name 154 | } 155 | return cls 156 | } 157 | 158 | // closes the resultset 159 | func (r rowsRes) Close() error { 160 | if r.cu != nil { 161 | debug("CLOSEing result %p", r.cu) 162 | // r.cu.Close() // FIXME 163 | r.cu = nil 164 | } 165 | return nil 166 | } 167 | 168 | // DATE, DATETIME, TIMESTAMP are treated as they are in Local time zone 169 | func (r rowsRes) Next(dest []driver.Value) error { 170 | row := (*[]interface{})(unsafe.Pointer(&dest)) 171 | // log.Printf("FetcOneInto(%p %+v len=%d) %T", row, *row, len(*row), *row) 172 | err := r.cu.FetchOneInto(*row...) 173 | debug("fetched row=%p %+v (len=%d) err=%s", row, *row, len(*row), err) 174 | return err 175 | } 176 | 177 | type Driver struct { 178 | // Defaults 179 | user, passwd, db string 180 | 181 | initCmds []string 182 | } 183 | 184 | // Open new connection. The uri need to have the following syntax: 185 | // 186 | // USER/PASSWD@SID 187 | // 188 | // SID (database identifier) can be a DSN (see goracle/oracle.MakeDSN) 189 | func (d *Driver) Open(uri string) (driver.Conn, error) { 190 | p := strings.Index(uri, "/") 191 | d.user = uri[:p] 192 | q := strings.Index(uri[p+1:], "@") 193 | if q < 0 { 194 | q = len(uri) - 1 195 | } else { 196 | q += p + 1 197 | } 198 | d.passwd = uri[p+1 : q] 199 | d.db = uri[q+1:] 200 | 201 | // Establish the connection 202 | cx, err := oracle.NewConnection(d.user, d.passwd, d.db) 203 | if err == nil { 204 | err = cx.Connect(0, false) 205 | } 206 | if err != nil { 207 | return nil, err 208 | } 209 | return &conn{cx: &cx}, nil 210 | } 211 | 212 | // use log.Printf for log messages if IsDebug 213 | func debug(fmt string, args ...interface{}) { 214 | if IsDebug { 215 | log.Printf(fmt, args...) 216 | } 217 | } 218 | 219 | // Driver automatically registered in database/sql 220 | var d = Driver{} 221 | 222 | func init() { 223 | sql.Register("goracle", &d) 224 | } 225 | -------------------------------------------------------------------------------- /src/github.com/tgulacsi/goracle/oracle/connection_test.go: -------------------------------------------------------------------------------- 1 | package oracle 2 | 3 | import ( 4 | "flag" 5 | "log" 6 | "testing" 7 | ) 8 | 9 | var dsn = flag.String("dsn", "", "Oracle DSN (user/passw@sid)") 10 | var dbg = flag.Bool("debug", false, "print debug messages?") 11 | 12 | func init() { 13 | flag.Parse() 14 | IsDebug = *dbg 15 | } 16 | 17 | func TestMakeDSN(t *testing.T) { 18 | dsn := MakeDSN("localhost", 1521, "sid", "") 19 | if dsn != ("(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=" + 20 | "(PROTOCOL=TCP)(HOST=localhost)(PORT=1521)))(CONNECT_DATA=(SID=sid)))") { 21 | t.Logf(dsn) 22 | t.Fail() 23 | } 24 | dsn = MakeDSN("localhost", 1522, "", "service") 25 | if dsn != ("(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=" + 26 | "(PROTOCOL=TCP)(HOST=localhost)(PORT=1522)))(CONNECT_DATA=" + 27 | "(SERVICE_NAME=service)))") { 28 | t.Logf(dsn) 29 | t.Fail() 30 | } 31 | } 32 | 33 | func TestClientVersion(t *testing.T) { 34 | t.Logf("ClientVersion=%+v", ClientVersion()) 35 | } 36 | 37 | func TestIsConnected(t *testing.T) { 38 | if (Connection{}).IsConnected() { 39 | t.Fail() 40 | } 41 | conn := getConnection(t) 42 | if !conn.IsConnected() { 43 | t.Fail() 44 | } 45 | if err := conn.Ping(); err != nil { 46 | t.Logf("error with Ping: %s", err) 47 | t.Fail() 48 | } 49 | } 50 | 51 | func TestCursor(t *testing.T) { 52 | conn := getConnection(t) 53 | if !conn.IsConnected() { 54 | t.FailNow() 55 | } 56 | cur := conn.NewCursor() 57 | defer cur.Close() 58 | qry := `SELECT owner||'.'||object_name, object_id, object_id/EXP(1) 59 | FROM all_objects 60 | WHERE ROWNUM < 20 61 | ORDER BY 3` 62 | if err := cur.Execute(qry, nil, nil); err != nil { 63 | t.Logf(`error with "%s": %s`, qry, err) 64 | t.Fail() 65 | } 66 | row, err := cur.FetchOne() 67 | if err != nil { 68 | t.Logf("error fetching: %s", err) 69 | t.Fail() 70 | } 71 | t.Logf("row: %+v", row) 72 | rows, err := cur.FetchMany(3) 73 | if err != nil { 74 | t.Logf("error fetching many: %s", err) 75 | t.Fail() 76 | } 77 | for i, row := range rows { 78 | t.Logf("%03d: %v", i, row) 79 | } 80 | rows, err = cur.FetchAll() 81 | if err != nil { 82 | t.Logf("error fetching remaining: %s", err) 83 | t.Fail() 84 | } 85 | for i, row := range rows { 86 | t.Logf("%03d: %v", i, row) 87 | } 88 | 89 | qry = `SELECT B.object_id, A.rn 90 | FROM all_objects B, (SELECT :1 rn FROM DUAL) A 91 | WHERE ROWNUM < GREATEST(2, A.rn)` 92 | params := []interface{}{2} 93 | if err = cur.Execute(qry, params, nil); err != nil { 94 | t.Logf(`error with "%s" %v: %s`, qry, params, err) 95 | t.Fail() 96 | } 97 | if rows, err = cur.FetchMany(3); err != nil { 98 | t.Logf("error fetching many: %s", err) 99 | t.Fail() 100 | } 101 | for i, row := range rows { 102 | t.Logf("%03d: %v", i, row) 103 | } 104 | 105 | qry = `SELECT TO_DATE('2006-01-02 15:04:05', 'YYYY-MM-DD HH24:MI:SS') FROM DUAL` 106 | if err = cur.Execute(qry, nil, nil); err != nil { 107 | t.Logf(`error with "%s": %s`, qry, err) 108 | t.Fail() 109 | } 110 | if row, err = cur.FetchOne(); err != nil { 111 | t.Logf("error fetching: %s", err) 112 | t.Fail() 113 | } 114 | t.Logf("%03d: %v", 0, row) 115 | 116 | if IntervalWorks { 117 | qry = `SELECT TO_DSINTERVAL('2 10:20:30.456') FROM DUAL` 118 | if err = cur.Execute(qry, nil, nil); err != nil { 119 | t.Logf(`error with "%s": %s`, qry, err) 120 | t.Fail() 121 | } 122 | if row, err = cur.FetchOne(); err != nil { 123 | t.Logf("error fetching: %s", err) 124 | t.Fail() 125 | } 126 | t.Logf("%03d: %v", 0, row) 127 | } 128 | 129 | if err = cur.Execute("CREATE GLOBAL TEMPORARY TABLE w (x LONG)", nil, nil); err != nil { 130 | t.Logf("cannot check LONG: %s", err) 131 | } else { 132 | cur.Execute("INSERT INTO w VALUES ('a')", nil, nil) 133 | qry = `SELECT x FROM w` 134 | if err = cur.Execute(qry, nil, nil); err != nil { 135 | t.Logf(`error with "%s": %s`, qry, err) 136 | t.Fail() 137 | } 138 | if row, err = cur.FetchOne(); err != nil { 139 | t.Logf("error fetching: %s", err) 140 | t.Fail() 141 | } 142 | t.Logf("row: %v", row) 143 | cur.Execute("DROP TABLE w", nil, nil) 144 | } 145 | } 146 | 147 | var conn Connection 148 | 149 | func getConnection(t *testing.T) Connection { 150 | if conn.handle != nil { 151 | return conn 152 | } 153 | 154 | if !(dsn != nil && *dsn != "") { 155 | t.Logf("cannot test connection without dsn!") 156 | return conn 157 | } 158 | user, passw, sid := SplitDsn(*dsn) 159 | var err error 160 | conn, err = NewConnection(user, passw, sid) 161 | if err != nil { 162 | log.Panicf("error creating connection to %s: %s", *dsn, err) 163 | } 164 | if err = conn.Connect(0, false); err != nil { 165 | log.Panicf("error connecting: %s", err) 166 | } 167 | return conn 168 | } 169 | -------------------------------------------------------------------------------- /src/github.com/tgulacsi/goracle/oracle/cursorvar.go: -------------------------------------------------------------------------------- 1 | package oracle 2 | 3 | /* 4 | #cgo CFLAGS: -I/usr/include/oracle/11.2/client64 5 | #cgo LDFLAGS: -lclntsh -L/usr/lib/oracle/11.2/client64/lib 6 | 7 | #include 8 | #include 9 | 10 | const unsigned int sof_OCIStmtp = sizeof(OCIStmt*); 11 | 12 | static void cursorVar_setHandle(void *data, OCIStmt *handle) { 13 | data = handle; 14 | } 15 | */ 16 | import "C" 17 | 18 | import ( 19 | "fmt" 20 | "unsafe" 21 | ) 22 | 23 | var CursorVarType *VariableType 24 | 25 | // Initialize the variable. 26 | func cursorVar_Initialize(v *Variable, cur *Cursor) error { 27 | var tempCursor *Cursor 28 | var err error 29 | 30 | v.connection = cur.connection 31 | v.cursors = make([]*Cursor, v.allocatedElements) 32 | for i := uint(0); i < v.allocatedElements; i++ { 33 | tempCursor = v.connection.NewCursor() 34 | if err = tempCursor.allocateHandle(); err != nil { 35 | return err 36 | } 37 | C.cursorVar_setHandle(unsafe.Pointer(&v.dataBytes[i]), tempCursor.handle) 38 | } 39 | 40 | return nil 41 | } 42 | 43 | // Prepare for variable destruction. 44 | func cursorVar_Finalize(v *Variable) error { 45 | v.connection = nil 46 | v.cursors = nil 47 | return nil 48 | } 49 | 50 | // Set the value of the variable. 51 | func cursorVar_SetValue(v *Variable, pos uint, value interface{}) error { 52 | x, ok := value.(*Cursor) 53 | if !ok { 54 | return fmt.Errorf("requires *Cursor, got %T", value) 55 | } 56 | 57 | var err error 58 | v.cursors[pos] = x 59 | if !x.isOwned { 60 | if err = x.freeHandle(); err != nil { 61 | return err 62 | } 63 | x.isOwned = true 64 | if err = x.allocateHandle(); err != nil { 65 | return err 66 | } 67 | } 68 | C.cursorVar_setHandle(unsafe.Pointer(&v.dataBytes[pos]), x.handle) 69 | x.statementType = -1 70 | return nil 71 | } 72 | 73 | // Set the value of the variable. 74 | func cursorVar_GetValue(v *Variable, pos uint) (interface{}, error) { 75 | cur := v.cursors[pos] 76 | cur.statementType = -1 77 | return cur, nil 78 | } 79 | 80 | func init() { 81 | CursorVarType = &VariableType{ 82 | initialize: cursorVar_Initialize, 83 | finalize: cursorVar_Finalize, 84 | setValue: cursorVar_SetValue, 85 | getValue: cursorVar_GetValue, 86 | oracleType: C.SQLT_RSET, // Oracle type 87 | charsetForm: C.SQLCS_IMPLICIT, // charset form 88 | size: uint(C.sof_OCIStmtp), // element length 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/github.com/tgulacsi/goracle/oracle/datetimevar.go: -------------------------------------------------------------------------------- 1 | package oracle 2 | 3 | /* 4 | #cgo CFLAGS: -I/usr/include/oracle/11.2/client64 5 | #cgo LDFLAGS: -lclntsh -L/usr/lib/oracle/11.2/client64/lib 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | const size_t sof_OCIIntervalp = sizeof(OCIInterval*); 13 | 14 | void getDateTime(const OCIDate *date, sb2 *year, ub1 *month, 15 | ub1 *day, ub1 *hour, ub1 *min, ub1 *sec) { 16 | *year = (date)->OCIDateYYYY; 17 | *month = (date)->OCIDateMM; 18 | *day = (date)->OCIDateDD; 19 | *hour = (date)->OCIDateTime.OCITimeHH; 20 | *min = (date)->OCIDateTime.OCITimeMI; 21 | *sec = (date)->OCIDateTime.OCITimeSS; 22 | } 23 | 24 | void setDateTime(OCIDate *date, sb2 year, ub1 month, ub1 day, 25 | ub1 hour, ub1 min, ub1 sec) { 26 | (date)->OCIDateYYYY = year; 27 | (date)->OCIDateMM = month; 28 | (date)->OCIDateDD = day; 29 | (date)->OCIDateTime.OCITimeHH = hour; 30 | (date)->OCIDateTime.OCITimeMI = min; 31 | (date)->OCIDateTime.OCITimeSS = sec; 32 | } 33 | */ 34 | import "C" 35 | 36 | import ( 37 | // "log" 38 | // "bytes" 39 | // "encoding/binary" 40 | "fmt" 41 | "time" 42 | "unsafe" 43 | ) 44 | 45 | var ( 46 | IntervalWorks bool = false 47 | DateTimeVarType, IntervalVarType *VariableType 48 | ) 49 | 50 | func (t *VariableType) IsDate() bool { 51 | if t == DateTimeVarType || t == IntervalVarType { 52 | return true 53 | } 54 | return false 55 | } 56 | 57 | func dateTimeVar_SetValue(v *Variable, pos uint, value interface{}) error { 58 | x, ok := value.(time.Time) 59 | if !ok { 60 | if a, ok := value.([]time.Time); !ok { 61 | return fmt.Errorf("awaited time.Time or []time.Time, got %T", value) 62 | } else { 63 | var err error 64 | for i, x := range a { 65 | if err = dateTimeVar_SetValue(v, pos+uint(i), x); err != nil { 66 | return err 67 | } 68 | } 69 | return nil 70 | } 71 | } 72 | /* 73 | if err := v.environment.CheckStatus( 74 | C.OCIDateSetDate(unsafe.Pointer(&v.dataBytes[pos*sizeof_OCIDate]), 75 | x.Year(), x.Month(), x.Date()), 76 | "OCIDateSetDate"); err != nil { 77 | return err 78 | } 79 | return v.environment.CheckStatus( 80 | C.OCIDateSetTime(unsafe.Pointer(&v.dataBytes[pos*sizeof_OCIDate]), 81 | x.Hour(), x.Minute(), x.Second()), 82 | "OCIDateSetTime") 83 | */ 84 | C.setDateTime((*C.OCIDate)(unsafe.Pointer(&v.dataBytes[pos*v.typ.size])), 85 | C.sb2(x.Year()), C.ub1(x.Month()), C.ub1(x.Day()), 86 | C.ub1(x.Hour()), C.ub1(x.Minute()), C.ub1(x.Second())) 87 | return nil 88 | } 89 | 90 | func dateTimeVar_GetValue(v *Variable, pos uint) (interface{}, error) { 91 | var ( 92 | year C.sb2 93 | month, day, hour, minute, second C.ub1 94 | ) 95 | /* 96 | err := v.environment.CheckStatus( 97 | C.OCIDateGetDate(&v.dataBytes[pos*sizeof_OCIDate], &year, &month, &day), 98 | "OCIDateGetDate") 99 | if err != nil { 100 | return nil, err 101 | } 102 | if err = v.environment.CheckStatus( 103 | C.OCIDateGetTime(&v.dataBytes[pos*sizeof_OCIDate], &hour, &minute, &second), 104 | "OCIDateGetTime"); err != nil { 105 | return nil, err 106 | } 107 | */ 108 | C.getDateTime((*C.OCIDate)(unsafe.Pointer(&v.dataBytes[pos*v.typ.size])), 109 | &year, &month, &day, &hour, &minute, &second) 110 | return time.Date(int(year), time.Month(month), int(day), 111 | int(hour), int(minute), int(second), 0, time.Local), nil 112 | } 113 | 114 | // Set the value of the variable. 115 | func internalVar_SetValue(v *Variable, pos uint, value interface{}) error { 116 | var days, hours, minutes, seconds, microseconds C.sb4 117 | 118 | x, ok := value.(time.Duration) 119 | if !ok { 120 | return fmt.Errorf("requires time.Duration, got %T", value) 121 | } 122 | 123 | days = C.sb4(x.Hours()) / 24 124 | hours = C.sb4(x.Hours()) - days*24 125 | minutes = C.sb4(x.Minutes() - x.Hours()*60) 126 | seconds = C.sb4(x.Seconds()-x.Minutes()) * 60 127 | microseconds = C.sb4(float64(x.Nanoseconds()/1000) - x.Seconds()*1000*1000) 128 | return v.environment.CheckStatus( 129 | C.OCIIntervalSetDaySecond(unsafe.Pointer(v.environment.handle), 130 | v.environment.errorHandle, 131 | days, hours, minutes, seconds, microseconds, 132 | (*C.OCIInterval)(unsafe.Pointer(&v.dataBytes[pos*v.typ.size]))), 133 | "IntervalSetDaySecond") 134 | } 135 | 136 | // Returns the value stored at the given array position. 137 | func internalVar_GetValue(v *Variable, pos uint) (interface{}, error) { 138 | var days, hours, minutes, seconds, microseconds C.sb4 139 | 140 | if err := v.environment.CheckStatus( 141 | C.OCIIntervalGetDaySecond(unsafe.Pointer(v.environment.handle), 142 | v.environment.errorHandle, 143 | &days, &hours, &minutes, &seconds, µseconds, 144 | (*C.OCIInterval)(unsafe.Pointer((&v.dataBytes[pos*v.typ.size])))), 145 | "internalVar_GetValue"); err != nil { 146 | return nil, err 147 | } 148 | return (time.Duration(days)*24*time.Hour + 149 | time.Duration(hours)*time.Hour + 150 | time.Duration(minutes)*time.Minute + 151 | time.Duration(seconds)*time.Second + 152 | time.Duration(microseconds)*time.Microsecond), nil 153 | } 154 | 155 | func init() { 156 | DateTimeVarType = &VariableType{ 157 | Name: "DateTime", 158 | setValue: dateTimeVar_SetValue, 159 | getValue: dateTimeVar_GetValue, 160 | oracleType: C.SQLT_ODT, // Oracle type 161 | charsetForm: C.SQLCS_IMPLICIT, // charset form 162 | size: C.sizeof_OCIDate, // element length 163 | isCharData: false, // is character data 164 | isVariableLength: false, // is variable length 165 | canBeCopied: true, // can be copied 166 | canBeInArray: true, // can be in array 167 | } 168 | IntervalVarType = &VariableType{ 169 | Name: "Interval", 170 | setValue: dateTimeVar_SetValue, 171 | getValue: dateTimeVar_GetValue, 172 | oracleType: C.SQLT_INTERVAL_DS, // Oracle type 173 | charsetForm: C.SQLCS_IMPLICIT, // charset form 174 | size: uint(C.sof_OCIIntervalp), // element length 175 | isCharData: false, // is character data 176 | isVariableLength: false, // is variable length 177 | canBeCopied: true, // can be copied 178 | canBeInArray: true, // can be in array 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/github.com/tgulacsi/goracle/oracle/debug_notrace.go: -------------------------------------------------------------------------------- 1 | // +build !trace 2 | 3 | package oracle 4 | 5 | const CTrace = false 6 | 7 | // no trace 8 | // prints with log.Printf the C-call trace 9 | func ctrace(name string, args ...interface{}) { 10 | //log.Printf("TRACE %s(%v)", name, args) 11 | } 12 | -------------------------------------------------------------------------------- /src/github.com/tgulacsi/goracle/oracle/debug_trace.go: -------------------------------------------------------------------------------- 1 | // +build trace 2 | 3 | package oracle 4 | 5 | import "log" 6 | 7 | const CTrace = true 8 | 9 | // prints with log.Printf the C-call trace 10 | func ctrace(name string, args ...interface{}) { 11 | log.Printf("CTRACE %s(%v)", name, args) 12 | } 13 | -------------------------------------------------------------------------------- /src/github.com/tgulacsi/goracle/oracle/error.go: -------------------------------------------------------------------------------- 1 | package oracle 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "strconv" 7 | ) 8 | 9 | type Error struct { 10 | Code int 11 | Message string 12 | At string 13 | Offset int 14 | } 15 | 16 | func NewError(code int, message string) *Error { 17 | return &Error{Code: code, Message: message} 18 | } 19 | 20 | func (err Error) Error() string { 21 | return err.String() 22 | } 23 | 24 | func (err Error) String() string { 25 | tail := strconv.Itoa(err.Code) + ": " + err.Message 26 | var head string 27 | if err.Offset != 0 { 28 | head = "row " + strconv.Itoa(err.Offset) + " " 29 | } 30 | if err.At != "" { 31 | return head + "@" + err.At + " " + tail 32 | } 33 | return head + tail 34 | } 35 | 36 | type mismatchElementNum int 37 | 38 | func (men mismatchElementNum) Error() string { 39 | return "Mismatch element number: found " + strconv.Itoa(int(men)) 40 | } 41 | 42 | func ProgrammingError(text string) error { 43 | return fmt.Errorf("Programming error: %s", text) 44 | } 45 | 46 | func setErrAt(err error, at string) { 47 | if x, ok := err.(*Error); ok { 48 | x.At = at 49 | } 50 | } 51 | 52 | // print debug messages? 53 | var IsDebug bool 54 | 55 | // print with log.Printf if IsDebug 56 | func debug(format string, args ...interface{}) { 57 | if IsDebug { 58 | log.Printf(format, args...) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/github.com/tgulacsi/goracle/oracle/longvar.go: -------------------------------------------------------------------------------- 1 | package oracle 2 | 3 | /* 4 | #cgo CFLAGS: -I/usr/include/oracle/11.2/client64 5 | #cgo LDFLAGS: -lclntsh -L/usr/lib/oracle/11.2/client64/lib 6 | 7 | #include 8 | #include 9 | */ 10 | import "C" 11 | 12 | import ( 13 | "bytes" 14 | "encoding/binary" 15 | "fmt" 16 | // "log" 17 | // "unsafe" 18 | ) 19 | 20 | var ( 21 | LongStringVarType, LongBinaryVarType *VariableType 22 | ) 23 | 24 | func init() { 25 | LongStringVarType = &VariableType{ 26 | setValue: longVar_SetValue, 27 | getValue: longVar_GetValue, 28 | getBufferSize: longVar_GetBufferSize, 29 | oracleType: C.SQLT_LVC, // Oracle type 30 | charsetForm: C.SQLCS_IMPLICIT, // charset form 31 | size: 128 * 1024, // element length (default) 32 | isCharData: true, // is character data 33 | isVariableLength: true, // is variable length 34 | canBeCopied: true, // can be copied 35 | canBeInArray: false, // can be in array 36 | } 37 | 38 | LongBinaryVarType = &VariableType{ 39 | setValue: longVar_SetValue, 40 | getValue: longVar_GetValue, 41 | getBufferSize: longVar_GetBufferSize, 42 | oracleType: C.SQLT_LVB, // Oracle type 43 | charsetForm: C.SQLCS_IMPLICIT, // charset form 44 | size: 128 * 1024, // element length (default) 45 | isCharData: false, // is character data 46 | isVariableLength: true, // is variable length 47 | canBeCopied: true, // can be copied 48 | canBeInArray: false, // can be in array 49 | } 50 | } 51 | 52 | // Set the value of the variable. 53 | func longVar_SetValue(v *Variable, pos uint, value interface{}) error { 54 | var x []byte 55 | if y, ok := value.(string); !ok { 56 | if y, ok := value.([]byte); !ok { 57 | return fmt.Errorf("awaited string or []byte, got %T", value) 58 | } else { 59 | x = y 60 | } 61 | } else { 62 | x = []byte(y) 63 | } 64 | // verify there is enough space to store the value 65 | length := uint(len(x) + 4) 66 | if uint(len(v.dataBytes)) < length { 67 | if err := v.resize(length); err != nil { 68 | return err 69 | } 70 | } 71 | 72 | p := v.bufferSize * pos 73 | if err := binary.Write(bytes.NewBuffer(v.dataBytes[p:p+4]), 74 | binary.LittleEndian, uint32(len(x))); err != nil { 75 | return err 76 | } 77 | 78 | // copy the string to the Oracle buffer 79 | copy(v.dataBytes[p+4:p+4+uint(len(x))], x) 80 | return nil 81 | } 82 | 83 | // Returns the value stored at the given array position. 84 | func longVar_GetValue(v *Variable, pos uint) (interface{}, error) { 85 | p := v.bufferSize * pos 86 | size := uint32(v.bufferSize) 87 | if err := binary.Read(bytes.NewReader(v.dataBytes[p:p+4]), 88 | binary.LittleEndian, &size); err != nil { 89 | return nil, err 90 | } 91 | data := v.dataBytes[p+4 : p+4+uint(size)] 92 | if v.typ == LongStringVarType { 93 | return string(data), nil 94 | } 95 | return data, nil 96 | } 97 | 98 | // Returns the size of the buffer to use for data of the given size. 99 | func longVar_GetBufferSize(v *Variable) uint { 100 | if v.typ.isCharData { 101 | return v.size + C.sizeof_ub4 102 | } 103 | return C.sizeof_ub4 + v.size*v.environment.MaxBytesPerCharacter 104 | } 105 | -------------------------------------------------------------------------------- /src/github.com/ziutek/mymysql/.gitignore: -------------------------------------------------------------------------------- 1 | *.6 2 | *.8 3 | *.a 4 | *.o 5 | *.so 6 | *.out 7 | *.go~ 8 | _obj 9 | _testmain.go 10 | _go_.6 11 | -------------------------------------------------------------------------------- /src/github.com/ziutek/mymysql/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010, Michal Derkacz 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 3. The name of the author may not be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /src/github.com/ziutek/mymysql/Makefile: -------------------------------------------------------------------------------- 1 | install: 2 | ./make.bash install 3 | 4 | clean: 5 | ./make.bash clean 6 | 7 | test: 8 | ./make.bash test 9 | 10 | all: 11 | ./make.bash clean install test 12 | 13 | .PHONY : install clean test all 14 | -------------------------------------------------------------------------------- /src/github.com/ziutek/mymysql/autorc/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010, Michal Derkacz 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 3. The name of the author may not be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /src/github.com/ziutek/mymysql/autorc/Makefile: -------------------------------------------------------------------------------- 1 | include $(GOROOT)/src/Make.inc 2 | 3 | TARG=github.com/ziutek/mymysql/autorc 4 | GOFILES=\ 5 | autorecon.go 6 | 7 | include $(GOROOT)/src/Make.pkg 8 | -------------------------------------------------------------------------------- /src/github.com/ziutek/mymysql/autorc/autorecon.go: -------------------------------------------------------------------------------- 1 | // Auto reconnect interface for MyMySQL 2 | package autorc 3 | 4 | import ( 5 | "github.com/ziutek/mymysql/mysql" 6 | "io" 7 | "log" 8 | "net" 9 | "time" 10 | ) 11 | 12 | // Return true if error is network error or UnexpectedEOF. 13 | func IsNetErr(err error) bool { 14 | if err == io.ErrUnexpectedEOF { 15 | return true 16 | } else if _, ok := err.(*net.OpError); ok { 17 | return true 18 | } 19 | return false 20 | } 21 | 22 | type Conn struct { 23 | Raw mysql.Conn 24 | // Maximum reconnect retries. 25 | // Default is 7 which means 1+2+3+4+5+6+7 = 28 seconds before return error. 26 | MaxRetries int 27 | 28 | // Debug logging. You may change it at any time. 29 | Debug bool 30 | } 31 | 32 | func New(proto, laddr, raddr, user, passwd string, db ...string) *Conn { 33 | return &Conn{mysql.New(proto, laddr, raddr, user, passwd, db...), 7, false} 34 | } 35 | 36 | func (c *Conn) reconnectIfNetErr(nn *int, err *error) { 37 | for *err != nil && IsNetErr(*err) && *nn <= c.MaxRetries { 38 | if c.Debug { 39 | log.Printf("Error: '%s' - reconnecting...", *err) 40 | } 41 | time.Sleep(1e9 * time.Duration(*nn)) 42 | *err = c.Raw.Reconnect() 43 | if c.Debug && *err != nil { 44 | log.Println("Can't reconnect:", *err) 45 | } 46 | *nn++ 47 | } 48 | } 49 | 50 | func (c *Conn) connectIfNotConnected() (err error) { 51 | if c.Raw.IsConnected() { 52 | return 53 | } 54 | err = c.Raw.Connect() 55 | nn := 0 56 | c.reconnectIfNetErr(&nn, &err) 57 | return 58 | } 59 | 60 | // Automatic connect/reconnect/repeat version of Use 61 | func (c *Conn) Use(dbname string) (err error) { 62 | if err = c.connectIfNotConnected(); err != nil { 63 | return 64 | } 65 | nn := 0 66 | for { 67 | if err = c.Raw.Use(dbname); err == nil { 68 | return 69 | } 70 | if c.reconnectIfNetErr(&nn, &err); err != nil { 71 | return 72 | } 73 | } 74 | panic(nil) 75 | } 76 | 77 | // Automatic connect/reconnect/repeat version of Query 78 | func (c *Conn) Query(sql string, params ...interface{}) (rows []mysql.Row, res mysql.Result, err error) { 79 | 80 | if err = c.connectIfNotConnected(); err != nil { 81 | return 82 | } 83 | nn := 0 84 | for { 85 | if rows, res, err = c.Raw.Query(sql, params...); err == nil { 86 | return 87 | } 88 | if c.reconnectIfNetErr(&nn, &err); err != nil { 89 | return 90 | } 91 | } 92 | panic(nil) 93 | } 94 | 95 | type Stmt struct { 96 | Raw mysql.Stmt 97 | con *Conn 98 | } 99 | 100 | // Automatic connect/reconnect/repeat version of Prepare 101 | func (c *Conn) Prepare(sql string) (*Stmt, error) { 102 | if err := c.connectIfNotConnected(); err != nil { 103 | return nil, err 104 | } 105 | nn := 0 106 | for { 107 | var ( 108 | err error 109 | s mysql.Stmt 110 | ) 111 | if s, err = c.Raw.Prepare(sql); err == nil { 112 | return &Stmt{s, c}, nil 113 | } 114 | if c.reconnectIfNetErr(&nn, &err); err != nil { 115 | return nil, err 116 | } 117 | } 118 | panic(nil) 119 | } 120 | 121 | // Automatic connect/reconnect/repeat version of Exec 122 | func (s *Stmt) Exec(params ...interface{}) (rows []mysql.Row, res mysql.Result, err error) { 123 | 124 | if err = s.con.connectIfNotConnected(); err != nil { 125 | return 126 | } 127 | nn := 0 128 | for { 129 | if rows, res, err = s.Raw.Exec(params...); err == nil { 130 | return 131 | } 132 | if s.con.reconnectIfNetErr(&nn, &err); err != nil { 133 | return 134 | } 135 | } 136 | panic(nil) 137 | } 138 | -------------------------------------------------------------------------------- /src/github.com/ziutek/mymysql/autorc/autorecon_test.go: -------------------------------------------------------------------------------- 1 | package autorc 2 | 3 | import ( 4 | _ "github.com/ziutek/mymysql/thrsafe" 5 | "testing" 6 | ) 7 | 8 | var ( 9 | conn = []string{"tcp", "", "127.0.0.1:3306"} 10 | user = "testuser" 11 | passwd = "TestPasswd9" 12 | dbname = "test" 13 | ) 14 | 15 | func checkErr(t *testing.T, err error, exp_err error) { 16 | if err != exp_err { 17 | if exp_err == nil { 18 | t.Fatalf("Error: %v", err) 19 | } else { 20 | t.Fatalf("Error: %v\nExpected error: %v", err, exp_err) 21 | } 22 | } 23 | } 24 | 25 | func TestAutoConnectReconnect(t *testing.T) { 26 | c := New(conn[0], conn[1], conn[2], user, passwd) 27 | c.Debug = false 28 | 29 | // Register initialisation commands 30 | c.Raw.Register("set names utf8") 31 | 32 | // my is in unconnected state 33 | checkErr(t, c.Use(dbname), nil) 34 | 35 | // Disconnect 36 | c.Raw.Close() 37 | 38 | // Drop test table if exists 39 | c.Query("drop table R") 40 | 41 | // Disconnect 42 | c.Raw.Close() 43 | 44 | // Create table 45 | _, _, err := c.Query( 46 | "create table R (id int primary key, name varchar(20))", 47 | ) 48 | checkErr(t, err, nil) 49 | 50 | // Kill the connection 51 | _, _, err = c.Query("kill %d", c.Raw.ThreadId()) 52 | checkErr(t, err, nil) 53 | 54 | // Prepare insert statement 55 | ins, err := c.Prepare("insert R values (?, ?)") 56 | checkErr(t, err, nil) 57 | 58 | // Kill the connection 59 | _, _, err = c.Query("kill %d", c.Raw.ThreadId()) 60 | checkErr(t, err, nil) 61 | 62 | // Bind insert parameters 63 | ins.Raw.Bind(1, "jeden") 64 | // Insert into table 65 | _, _, err = ins.Exec() 66 | checkErr(t, err, nil) 67 | 68 | // Kill the connection 69 | _, _, err = c.Query("kill %d", c.Raw.ThreadId()) 70 | checkErr(t, err, nil) 71 | 72 | // Bind insert parameters 73 | ins.Raw.Bind(2, "dwa") 74 | // Insert into table 75 | _, _, err = ins.Exec() 76 | checkErr(t, err, nil) 77 | 78 | // Kill the connection 79 | _, _, err = c.Query("kill %d", c.Raw.ThreadId()) 80 | checkErr(t, err, nil) 81 | 82 | // Select from table 83 | rows, res, err := c.Query("select * from R") 84 | checkErr(t, err, nil) 85 | id := res.Map("id") 86 | name := res.Map("name") 87 | if len(rows) != 2 || 88 | rows[0].Int(id) != 1 || rows[0].Str(name) != "jeden" || 89 | rows[1].Int(id) != 2 || rows[1].Str(name) != "dwa" { 90 | t.Fatal("Bad result") 91 | } 92 | 93 | // Kill the connection 94 | _, _, err = c.Query("kill %d", c.Raw.ThreadId()) 95 | checkErr(t, err, nil) 96 | 97 | // Drop table 98 | _, _, err = c.Query("drop table R") 99 | checkErr(t, err, nil) 100 | 101 | // Disconnect 102 | c.Raw.Close() 103 | } 104 | -------------------------------------------------------------------------------- /src/github.com/ziutek/mymysql/godrv/Makefile: -------------------------------------------------------------------------------- 1 | include $(GOROOT)/src/Make.inc 2 | 3 | TARG=github.com/ziutek/mymysql/godrv 4 | GOFILES=\ 5 | driver.go\ 6 | 7 | include $(GOROOT)/src/Make.pkg 8 | -------------------------------------------------------------------------------- /src/github.com/ziutek/mymysql/godrv/driver.go: -------------------------------------------------------------------------------- 1 | //MySQL driver for Go sql package 2 | package godrv 3 | 4 | import ( 5 | "database/sql" 6 | "database/sql/driver" 7 | "errors" 8 | "fmt" 9 | "github.com/ziutek/mymysql/mysql" 10 | "github.com/ziutek/mymysql/native" 11 | "io" 12 | "math" 13 | "reflect" 14 | "strings" 15 | "time" 16 | ) 17 | 18 | type conn struct { 19 | my mysql.Conn 20 | } 21 | 22 | func (c conn) Prepare(query string) (driver.Stmt, error) { 23 | st, err := c.my.Prepare(query) 24 | if err != nil { 25 | return nil, err 26 | } 27 | return stmt{st}, nil 28 | } 29 | 30 | func (c conn) Close() error { 31 | err := c.my.Close() 32 | c.my = nil 33 | return err 34 | } 35 | 36 | func (c conn) Begin() (driver.Tx, error) { 37 | t, err := c.my.Begin() 38 | if err != nil { 39 | return tx{nil}, err 40 | } 41 | return tx{t}, nil 42 | } 43 | 44 | type tx struct { 45 | my mysql.Transaction 46 | } 47 | 48 | func (t tx) Commit() error { 49 | return t.my.Commit() 50 | } 51 | 52 | func (t tx) Rollback() error { 53 | return t.my.Rollback() 54 | } 55 | 56 | type stmt struct { 57 | my mysql.Stmt 58 | } 59 | 60 | func (s stmt) Close() error { 61 | err := s.my.Delete() 62 | s.my = nil 63 | return err 64 | } 65 | 66 | func (s stmt) NumInput() int { 67 | return s.my.NumParam() 68 | } 69 | 70 | func (s stmt) run(vargs []driver.Value) (rowsRes, error) { 71 | args := make([]interface{}, len(vargs)) 72 | for i, a := range vargs { 73 | args[i] = a 74 | } 75 | res, err := s.my.Run(args...) 76 | if err != nil { 77 | return rowsRes{nil}, err 78 | } 79 | return rowsRes{res}, nil 80 | } 81 | 82 | func (s stmt) Exec(args []driver.Value) (driver.Result, error) { 83 | return s.run(args) 84 | } 85 | 86 | func (s stmt) Query(args []driver.Value) (driver.Rows, error) { 87 | return s.run(args) 88 | } 89 | 90 | type rowsRes struct { 91 | my mysql.Result 92 | } 93 | 94 | func (r rowsRes) LastInsertId() (int64, error) { 95 | return int64(r.my.InsertId()), nil 96 | } 97 | 98 | func (r rowsRes) RowsAffected() (int64, error) { 99 | return int64(r.my.AffectedRows()), nil 100 | } 101 | 102 | func (r rowsRes) Columns() []string { 103 | flds := r.my.Fields() 104 | cls := make([]string, len(flds)) 105 | for i, f := range flds { 106 | cls[i] = f.Name 107 | } 108 | return cls 109 | } 110 | 111 | func (r rowsRes) Close() error { 112 | err := r.my.End() 113 | r.my = nil 114 | if err != native.READ_AFTER_EOR_ERROR { 115 | return err 116 | } 117 | return nil 118 | } 119 | 120 | // DATE, DATETIME, TIMESTAMP are treated as they are in Local time zone 121 | func (r rowsRes) Next(dest []driver.Value) error { 122 | row, err := r.my.GetRow() 123 | if err != nil { 124 | return err 125 | } 126 | if row == nil { 127 | return io.EOF 128 | } 129 | for i, col := range row { 130 | if col == nil { 131 | dest[i] = nil 132 | continue 133 | } 134 | switch c := col.(type) { 135 | case time.Time: 136 | dest[i] = c 137 | continue 138 | case mysql.Timestamp: 139 | dest[i] = c.Time 140 | continue 141 | case mysql.Date: 142 | dest[i] = c.Localtime() 143 | continue 144 | } 145 | v := reflect.ValueOf(col) 146 | switch v.Kind() { 147 | case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 148 | // this contains time.Duration to 149 | dest[i] = v.Int() 150 | case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 151 | u := v.Uint() 152 | if u > math.MaxInt64 { 153 | panic("Value to large for int64 type") 154 | } 155 | dest[i] = int64(u) 156 | case reflect.Float32, reflect.Float64: 157 | dest[i] = v.Float() 158 | case reflect.Slice: 159 | if v.Type().Elem().Kind() == reflect.Uint8 { 160 | dest[i] = v.Interface().([]byte) 161 | break 162 | } 163 | fallthrough 164 | default: 165 | panic(fmt.Sprint("Unknown type of column: ", v.Type())) 166 | } 167 | } 168 | return nil 169 | } 170 | 171 | type Driver struct { 172 | // Defaults 173 | proto, laddr, raddr, user, passwd, db string 174 | } 175 | 176 | // Open new connection. The uri need to have the following syntax: 177 | // 178 | // [PROTOCOL_SPECFIIC*]DBNAME/USER/PASSWD 179 | // 180 | // where protocol spercific part may be empty (this means connection to 181 | // local server using default protocol). Currently possible forms: 182 | // DBNAME/USER/PASSWD 183 | // unix:SOCKPATH*DBNAME/USER/PASSWD 184 | // tcp:ADDR*DBNAME/USER/PASSWD 185 | func (d *Driver) Open(uri string) (driver.Conn, error) { 186 | pd := strings.SplitN(uri, "*", 2) 187 | if len(pd) == 2 { 188 | // Parse protocol part of URI 189 | p := strings.SplitN(pd[0], ":", 2) 190 | if len(p) != 2 { 191 | return nil, errors.New("Wrong protocol part of URI") 192 | } 193 | d.proto = p[0] 194 | d.raddr = p[1] 195 | // Remove protocol part 196 | pd = pd[1:] 197 | } 198 | // Parse database part of URI 199 | dup := strings.SplitN(pd[0], "/", 3) 200 | if len(dup) != 3 { 201 | return nil, errors.New("Wrong database part of URI") 202 | } 203 | d.db = dup[0] 204 | d.user = dup[1] 205 | d.passwd = dup[2] 206 | 207 | // Establish the connection 208 | c := conn{mysql.New(d.proto, d.laddr, d.raddr, d.user, d.passwd, d.db)} 209 | if err := c.my.Connect(); err != nil { 210 | return nil, err 211 | } 212 | return &c, nil 213 | } 214 | 215 | func init() { 216 | sql.Register("mymysql", &Driver{proto: "tcp", raddr: "127.0.0.1:3306"}) 217 | } 218 | -------------------------------------------------------------------------------- /src/github.com/ziutek/mymysql/godrv/driver_test.go: -------------------------------------------------------------------------------- 1 | package godrv 2 | 3 | import ( 4 | "database/sql" 5 | "testing" 6 | ) 7 | 8 | func checkErr(t *testing.T, err error) { 9 | if err != nil { 10 | t.Fatalf("Error: %v", err) 11 | } 12 | } 13 | func checkErrId(t *testing.T, err error, rid, eid int64) { 14 | checkErr(t, err) 15 | if rid != eid { 16 | t.Fatal("res.LastInsertId() ==", rid, "but should be", eid) 17 | } 18 | } 19 | 20 | func TestAll(t *testing.T) { 21 | data := []string{"jeden", "dwa"} 22 | 23 | db, err := sql.Open("mymysql", "test/testuser/TestPasswd9") 24 | 25 | db.Exec("DROP TABLE go") 26 | 27 | _, err = db.Exec( 28 | `CREATE TABLE go ( 29 | id INT PRIMARY KEY AUTO_INCREMENT, 30 | txt TEXT 31 | ) ENGINE=InnoDB`) 32 | checkErr(t, err) 33 | 34 | ins, err := db.Prepare("INSERT go SET txt=?") 35 | checkErr(t, err) 36 | 37 | tx, err := db.Begin() 38 | checkErr(t, err) 39 | 40 | res, err := ins.Exec(data[0]) 41 | checkErr(t, err) 42 | id, err := res.LastInsertId() 43 | checkErrId(t, err, id, 1) 44 | 45 | res, err = ins.Exec(data[1]) 46 | checkErr(t, err) 47 | id, err = res.LastInsertId() 48 | checkErrId(t, err, id, 2) 49 | 50 | checkErr(t, tx.Commit()) 51 | 52 | tx, err = db.Begin() 53 | checkErr(t, err) 54 | 55 | res, err = tx.Exec("INSERT go SET txt=?", "trzy") 56 | checkErr(t, err) 57 | id, err = res.LastInsertId() 58 | checkErrId(t, err, id, 3) 59 | 60 | checkErr(t, tx.Rollback()) 61 | 62 | rows, err := db.Query("SELECT * FROM go") 63 | checkErr(t, err) 64 | for rows.Next() { 65 | var id int 66 | var txt string 67 | checkErr(t, rows.Scan(&id, &txt)) 68 | if id > len(data) { 69 | t.Fatal("To many rows in table") 70 | } 71 | if data[id-1] != txt { 72 | t.Fatalf("txt[%d] == '%s' != '%s'", id, txt, data[id-1]) 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/github.com/ziutek/mymysql/make.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | for dir in mysql native thrsafe autorc godrv examples; do 4 | (cd $dir; make $@) 5 | done 6 | -------------------------------------------------------------------------------- /src/github.com/ziutek/mymysql/mysql/Makefile: -------------------------------------------------------------------------------- 1 | include $(GOROOT)/src/Make.inc 2 | 3 | TARG=github.com/ziutek/mymysql/mysql 4 | GOFILES=\ 5 | types.go\ 6 | field.go\ 7 | row.go\ 8 | interface.go\ 9 | utils.go\ 10 | errors.go\ 11 | 12 | include $(GOROOT)/src/Make.pkg 13 | -------------------------------------------------------------------------------- /src/github.com/ziutek/mymysql/mysql/field.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | type Field struct { 4 | Catalog string 5 | Db string 6 | Table string 7 | OrgTable string 8 | Name string 9 | OrgName string 10 | DispLen uint32 11 | // Charset uint16 12 | Flags uint16 13 | Type byte 14 | Scale byte 15 | } 16 | -------------------------------------------------------------------------------- /src/github.com/ziutek/mymysql/mysql/interface.go: -------------------------------------------------------------------------------- 1 | // MySQL Client API written entirely in Go without any external dependences. 2 | package mysql 3 | 4 | type ConnCommon interface { 5 | Start(sql string, params ...interface{}) (Result, error) 6 | Prepare(sql string) (Stmt, error) 7 | 8 | Ping() error 9 | ThreadId() uint32 10 | EscapeString(txt string) string 11 | 12 | Query(sql string, params ...interface{}) ([]Row, Result, error) 13 | } 14 | 15 | type Conn interface { 16 | ConnCommon 17 | 18 | Connect() error 19 | Close() error 20 | IsConnected() bool 21 | Reconnect() error 22 | Use(dbname string) error 23 | Register(sql string) 24 | SetMaxPktSize(new_size int) int 25 | 26 | Begin() (Transaction, error) 27 | } 28 | 29 | type Transaction interface { 30 | ConnCommon 31 | 32 | Commit() error 33 | Rollback() error 34 | Do(st Stmt) Stmt 35 | } 36 | 37 | type Stmt interface { 38 | Bind(params ...interface{}) 39 | ResetParams() 40 | Run(params ...interface{}) (Result, error) 41 | Delete() error 42 | Reset() error 43 | SendLongData(pnum int, data interface{}, pkt_size int) error 44 | 45 | Map(string) int 46 | NumField() int 47 | NumParam() int 48 | WarnCount() int 49 | 50 | Exec(params ...interface{}) ([]Row, Result, error) 51 | } 52 | 53 | type Result interface { 54 | GetRow() (Row, error) 55 | MoreResults() bool 56 | NextResult() (Result, error) 57 | 58 | Fields() []*Field 59 | Map(string) int 60 | Message() string 61 | AffectedRows() uint64 62 | InsertId() uint64 63 | WarnCount() int 64 | 65 | GetRows() ([]Row, error) 66 | End() error 67 | } 68 | 69 | var New func(proto, laddr, raddr, user, passwd string, db ...string) Conn 70 | -------------------------------------------------------------------------------- /src/github.com/ziutek/mymysql/mysql/types.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "strconv" 7 | "strings" 8 | "time" 9 | ) 10 | 11 | // For MySQL DATE type 12 | type Date struct { 13 | Year int16 14 | Month, Day byte 15 | } 16 | 17 | func (dd Date) String() string { 18 | return fmt.Sprintf("%04d-%02d-%02d", dd.Year, dd.Month, dd.Day) 19 | } 20 | 21 | // True if date is 0000-00-00 22 | func (dd Date) IsZero() bool { 23 | return dd.Day == 0 && dd.Month == 0 && dd.Year == 0 24 | } 25 | 26 | // Converts Date to time.Time using loc location. 27 | // Converts MySQL zero to time.Time zero. 28 | func (dd Date) Time(loc *time.Location) (t time.Time) { 29 | if !dd.IsZero() { 30 | t = time.Date( 31 | int(dd.Year), time.Month(dd.Month), int(dd.Day), 32 | 0, 0, 0, 0, 33 | loc, 34 | ) 35 | } 36 | return 37 | } 38 | 39 | // Converts Date to time.Time using Local location. 40 | // Converts MySQL zero to time.Time zero. 41 | func (dd Date) Localtime() time.Time { 42 | return dd.Time(time.Local) 43 | } 44 | 45 | // Convert string date in format YYYY-MM-DD to Date. 46 | // Leading and trailing spaces are ignored. If format is invalid returns zero. 47 | func ParseDate(str string) (dd Date, err error) { 48 | str = strings.TrimSpace(str) 49 | if str == "0000-00-00" { 50 | return 51 | } 52 | var ( 53 | y, m, d int 54 | ) 55 | if len(str) != 10 || str[4] != '-' || str[7] != '-' { 56 | goto invalid 57 | } 58 | if y, err = strconv.Atoi(str[0:4]); err != nil { 59 | return 60 | } 61 | if m, err = strconv.Atoi(str[5:7]); err != nil { 62 | return 63 | } 64 | if m < 1 || m > 12 { 65 | goto invalid 66 | } 67 | if d, err = strconv.Atoi(str[8:10]); err != nil { 68 | return 69 | } 70 | if d < 1 || d > 31 { 71 | goto invalid 72 | } 73 | dd.Year = int16(y) 74 | dd.Month = byte(m) 75 | dd.Day = byte(d) 76 | return 77 | 78 | invalid: 79 | err = errors.New("Invalid MySQL DATE string: " + str) 80 | return 81 | } 82 | 83 | // Sandard MySQL datetime format 84 | const TimeFormat = "2006-01-02 15:04:05.000000000" 85 | 86 | // Returns t as string in MySQL format Converts time.Time zero to MySQL zero. 87 | func TimeString(t time.Time) string { 88 | if t.IsZero() { 89 | return "0000-00-00 00:00:00" 90 | } 91 | if t.Nanosecond() == 0 { 92 | return t.Format(TimeFormat[:19]) 93 | } 94 | return t.Format(TimeFormat) 95 | } 96 | 97 | // Parses string datetime in TimeFormat using loc location. 98 | // Converts MySQL zero to time.Time zero. 99 | func ParseTime(str string, loc *time.Location) (t time.Time, err error) { 100 | str = strings.TrimSpace(str) 101 | format := TimeFormat[:19] 102 | switch len(str) { 103 | case 10: 104 | if str == "0000-00-00" { 105 | return 106 | } 107 | format = format[:10] 108 | case 19: 109 | if str == "0000-00-00 00:00:00" { 110 | return 111 | } 112 | } 113 | // Don't expect 0000-00-00 00:00:00.0+ 114 | t, err = time.Parse(format, str) 115 | if err == nil && loc != time.UTC { 116 | t = convertTime(t, loc) 117 | } 118 | return 119 | } 120 | 121 | // Convert time.Duration to string representation of mysql.TIME 122 | func DurationString(d time.Duration) string { 123 | sign := 1 124 | if d < 0 { 125 | sign = -1 126 | d = -d 127 | } 128 | ns := int(d % 1e9) 129 | d /= 1e9 130 | sec := int(d % 60) 131 | d /= 60 132 | min := int(d % 60) 133 | hour := int(d/60) * sign 134 | if ns == 0 { 135 | return fmt.Sprintf("%d:%02d:%02d", hour, min, sec) 136 | } 137 | return fmt.Sprintf("%d:%02d:%02d.%09d", hour, min, sec, ns) 138 | } 139 | 140 | // Parse duration from MySQL string format [+-]H+:MM:SS[.UUUUUUUUU]. 141 | // Leading and trailing spaces are ignored. If format is invalid returns nil. 142 | func ParseDuration(str string) (dur time.Duration, err error) { 143 | str = strings.TrimSpace(str) 144 | orig := str 145 | // Check sign 146 | sign := int64(1) 147 | switch str[0] { 148 | case '-': 149 | sign = -1 150 | fallthrough 151 | case '+': 152 | str = str[1:] 153 | } 154 | var i, d int64 155 | // Find houre 156 | if nn := strings.IndexRune(str, ':'); nn != -1 { 157 | if i, err = strconv.ParseInt(str[0:nn], 10, 64); err != nil { 158 | return 159 | } 160 | d = i * 3600 161 | str = str[nn+1:] 162 | } else { 163 | goto invalid 164 | } 165 | if len(str) != 5 && len(str) != 15 || str[2] != ':' { 166 | goto invalid 167 | } 168 | if i, err = strconv.ParseInt(str[0:2], 10, 64); err != nil { 169 | return 170 | } 171 | if i < 0 || i > 59 { 172 | goto invalid 173 | } 174 | d += i * 60 175 | if i, err = strconv.ParseInt(str[3:5], 10, 64); err != nil { 176 | return 177 | } 178 | if i < 0 || i > 59 { 179 | goto invalid 180 | } 181 | d += i 182 | d *= 1e9 183 | if len(str) == 15 { 184 | if str[5] != '.' { 185 | goto invalid 186 | } 187 | if i, err = strconv.ParseInt(str[6:15], 10, 64); err != nil { 188 | return 189 | } 190 | d += i 191 | } 192 | dur = time.Duration(d * sign) 193 | return 194 | 195 | invalid: 196 | err = errors.New("invalid MySQL TIME string: " + orig) 197 | return 198 | 199 | } 200 | 201 | type Blob []byte 202 | 203 | type Raw struct { 204 | Typ uint16 205 | Val *[]byte 206 | } 207 | 208 | type Timestamp struct { 209 | time.Time 210 | } 211 | 212 | func (t Timestamp) String() string { 213 | return TimeString(t.Time) 214 | } 215 | -------------------------------------------------------------------------------- /src/github.com/ziutek/mymysql/mysql/types_test.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | type sio struct { 9 | in, out string 10 | } 11 | 12 | func checkRow(t *testing.T, examples []sio, conv func(string) interface{}) { 13 | row := make(Row, 1) 14 | for _, ex := range examples { 15 | row[0] = conv(ex.in) 16 | str := row.Str(0) 17 | if str != ex.out { 18 | t.Fatalf("Wrong conversion: '%s' != '%s'", str, ex.out) 19 | } 20 | } 21 | } 22 | 23 | var dates = []sio{ 24 | sio{"2121-11-22", "2121-11-22"}, 25 | sio{"0000-00-00", "0000-00-00"}, 26 | sio{" 1234-12-18 ", "1234-12-18"}, 27 | sio{"\t1234-12-18 \r\n", "1234-12-18"}, 28 | } 29 | 30 | func TestConvDate(t *testing.T) { 31 | conv := func(str string) interface{} { 32 | d, err := ParseDate(str) 33 | if err != nil { 34 | return err 35 | } 36 | return d 37 | } 38 | checkRow(t, dates, conv) 39 | } 40 | 41 | var datetimes = []sio{ 42 | sio{"2121-11-22 11:22:32", "2121-11-22 11:22:32"}, 43 | sio{" 1234-12-18 22:11:22 ", "1234-12-18 22:11:22"}, 44 | sio{"\t 1234-12-18 22:11:22 \r\n", "1234-12-18 22:11:22"}, 45 | sio{"2000-11-11", "2000-11-11 00:00:00"}, 46 | sio{"0000-00-00 00:00:00", "0000-00-00 00:00:00"}, 47 | sio{"0000-00-00", "0000-00-00 00:00:00"}, 48 | sio{"2000-11-22 11:11:11.000111222", "2000-11-22 11:11:11.000111222"}, 49 | } 50 | 51 | func TestConvTime(t *testing.T) { 52 | conv := func(str string) interface{} { 53 | d, err := ParseTime(str, time.Local) 54 | if err != nil { 55 | return err 56 | } 57 | return d 58 | } 59 | checkRow(t, datetimes, conv) 60 | } 61 | 62 | var times = []sio{ 63 | sio{"1:23:45", "1:23:45"}, 64 | sio{"-112:23:45", "-112:23:45"}, 65 | sio{"+112:23:45", "112:23:45"}, 66 | sio{"1:60:00", "invalid MySQL TIME string: 1:60:00"}, 67 | sio{"1:00:60", "invalid MySQL TIME string: 1:00:60"}, 68 | sio{"1:23:45.000111333", "1:23:45.000111333"}, 69 | sio{"-1:23:45.000111333", "-1:23:45.000111333"}, 70 | } 71 | 72 | func TestConvDuration(t *testing.T) { 73 | conv := func(str string) interface{} { 74 | d, err := ParseDuration(str) 75 | if err != nil { 76 | return err 77 | } 78 | return d 79 | 80 | } 81 | checkRow(t, times, conv) 82 | } 83 | -------------------------------------------------------------------------------- /src/github.com/ziutek/mymysql/mysql/utils.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | // This call Start and next call GetRow as long as it reads all rows from the 4 | // result. Next it returns all readed rows as the slice of rows. 5 | func Query(c Conn, sql string, params ...interface{}) (rows []Row, res Result, err error) { 6 | res, err = c.Start(sql, params...) 7 | if err != nil { 8 | return 9 | } 10 | rows, err = GetRows(res) 11 | return 12 | } 13 | 14 | // This call Run and next call GetRow as long as it reads all rows from the 15 | // result. Next it returns all readed rows as the slice of rows. 16 | func Exec(s Stmt, params ...interface{}) (rows []Row, res Result, err error) { 17 | res, err = s.Run(params...) 18 | if err != nil { 19 | return 20 | } 21 | rows, err = GetRows(res) 22 | return 23 | } 24 | 25 | // Read all unreaded rows and discard them. This function is useful if you 26 | // don't want to use the remaining rows. It has an impact only on current 27 | // result. If there is multi result query, you must use NextResult method and 28 | // read/discard all rows in this result, before use other method that sends 29 | // data to the server. You can't use this function if last GetRow returned nil. 30 | func End(r Result) (err error) { 31 | var row Row 32 | for { 33 | row, err = r.GetRow() 34 | if err != nil || row == nil { 35 | break 36 | } 37 | } 38 | return 39 | } 40 | 41 | // Reads all rows from result and returns them as slice. 42 | func GetRows(r Result) (rows []Row, err error) { 43 | var row Row 44 | for { 45 | row, err = r.GetRow() 46 | if err != nil || row == nil { 47 | break 48 | } 49 | rows = append(rows, row) 50 | } 51 | return 52 | } 53 | -------------------------------------------------------------------------------- /src/github.com/ziutek/mymysql/native/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010, Michal Derkacz 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 3. The name of the author may not be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /src/github.com/ziutek/mymysql/native/Makefile: -------------------------------------------------------------------------------- 1 | include $(GOROOT)/src/Make.inc 2 | 3 | TARG=github.com/ziutek/mymysql/native 4 | GOFILES=\ 5 | mysql.go\ 6 | init.go\ 7 | common.go\ 8 | packet.go\ 9 | codecs.go\ 10 | errors.go\ 11 | consts.go\ 12 | command.go\ 13 | result.go\ 14 | addons.go\ 15 | prepared.go\ 16 | binding.go\ 17 | unsafe.go\ 18 | 19 | include $(GOROOT)/src/Make.pkg 20 | -------------------------------------------------------------------------------- /src/github.com/ziutek/mymysql/native/addons.go: -------------------------------------------------------------------------------- 1 | package native 2 | 3 | func NbinToNstr(nbin *[]byte) *string { 4 | if nbin == nil { 5 | return nil 6 | } 7 | str := string(*nbin) 8 | return &str 9 | } 10 | 11 | func NstrToNbin(nstr *string) *[]byte { 12 | if nstr == nil { 13 | return nil 14 | } 15 | bin := []byte(*nstr) 16 | return &bin 17 | } 18 | -------------------------------------------------------------------------------- /src/github.com/ziutek/mymysql/native/binding.go: -------------------------------------------------------------------------------- 1 | package native 2 | 3 | import ( 4 | "github.com/ziutek/mymysql/mysql" 5 | "reflect" 6 | "time" 7 | ) 8 | 9 | var ( 10 | timeType = reflect.TypeOf(time.Time{}) 11 | timestampType = reflect.TypeOf(mysql.Timestamp{}) 12 | dateType = reflect.TypeOf(mysql.Date{}) 13 | durationType = reflect.TypeOf(time.Duration(0)) 14 | blobType = reflect.TypeOf(mysql.Blob{}) 15 | rawType = reflect.TypeOf(mysql.Raw{}) 16 | ) 17 | 18 | // val should be an addressable value 19 | func bindValue(val reflect.Value) (out *paramValue) { 20 | if !val.IsValid() { 21 | return ¶mValue{typ: MYSQL_TYPE_NULL} 22 | } 23 | typ := val.Type() 24 | out = new(paramValue) 25 | if typ.Kind() == reflect.Ptr { 26 | // We have addressable pointer 27 | out.SetAddr(val.UnsafeAddr()) 28 | // Dereference pointer for next operation on its value 29 | typ = typ.Elem() 30 | val = val.Elem() 31 | } else { 32 | // We have addressable value. Create a pointer to it 33 | pv := val.Addr() 34 | // This pointer is unaddressable so copy it and return an address 35 | ppv := reflect.New(pv.Type()) 36 | ppv.Elem().Set(pv) 37 | out.SetAddr(ppv.Pointer()) 38 | } 39 | 40 | // Obtain value type 41 | switch typ.Kind() { 42 | case reflect.String: 43 | out.typ = MYSQL_TYPE_STRING 44 | out.length = -1 45 | return 46 | 47 | case reflect.Int: 48 | out.typ = _INT_TYPE 49 | out.length = _SIZE_OF_INT 50 | return 51 | 52 | case reflect.Int8: 53 | out.typ = MYSQL_TYPE_TINY 54 | out.length = 1 55 | return 56 | 57 | case reflect.Int16: 58 | out.typ = MYSQL_TYPE_SHORT 59 | out.length = 2 60 | return 61 | 62 | case reflect.Int32: 63 | out.typ = MYSQL_TYPE_LONG 64 | out.length = 4 65 | return 66 | 67 | case reflect.Int64: 68 | if typ == durationType { 69 | out.typ = MYSQL_TYPE_TIME 70 | out.length = -1 71 | return 72 | } 73 | out.typ = MYSQL_TYPE_LONGLONG 74 | out.length = 8 75 | return 76 | 77 | case reflect.Uint: 78 | out.typ = _INT_TYPE | MYSQL_UNSIGNED_MASK 79 | out.length = _SIZE_OF_INT 80 | return 81 | 82 | case reflect.Uint8: 83 | out.typ = MYSQL_TYPE_TINY | MYSQL_UNSIGNED_MASK 84 | out.length = 1 85 | return 86 | 87 | case reflect.Uint16: 88 | out.typ = MYSQL_TYPE_SHORT | MYSQL_UNSIGNED_MASK 89 | out.length = 2 90 | return 91 | 92 | case reflect.Uint32: 93 | out.typ = MYSQL_TYPE_LONG | MYSQL_UNSIGNED_MASK 94 | out.length = 4 95 | return 96 | 97 | case reflect.Uint64: 98 | out.typ = MYSQL_TYPE_LONGLONG | MYSQL_UNSIGNED_MASK 99 | out.length = 8 100 | return 101 | 102 | case reflect.Float32: 103 | out.typ = MYSQL_TYPE_FLOAT 104 | out.length = 4 105 | return 106 | 107 | case reflect.Float64: 108 | out.typ = MYSQL_TYPE_DOUBLE 109 | out.length = 8 110 | return 111 | 112 | case reflect.Slice: 113 | out.length = -1 114 | if typ == blobType { 115 | out.typ = MYSQL_TYPE_BLOB 116 | return 117 | } 118 | if typ.Elem().Kind() == reflect.Uint8 { 119 | out.typ = MYSQL_TYPE_VAR_STRING 120 | return 121 | } 122 | 123 | case reflect.Struct: 124 | out.length = -1 125 | if typ == timeType { 126 | out.typ = MYSQL_TYPE_DATETIME 127 | return 128 | } 129 | if typ == dateType { 130 | out.typ = MYSQL_TYPE_DATE 131 | return 132 | } 133 | if typ == timestampType { 134 | out.typ = MYSQL_TYPE_TIMESTAMP 135 | return 136 | } 137 | if typ == rawType { 138 | out.typ = val.FieldByName("Typ").Interface().(uint16) 139 | out.SetAddr(val.FieldByName("Val").Pointer()) 140 | out.raw = true 141 | return 142 | } 143 | 144 | case reflect.Bool: 145 | out.typ = MYSQL_TYPE_TINY 146 | // bool implementation isn't documented so we treat it in special way 147 | out.length = -1 148 | return 149 | } 150 | panic(BIND_UNK_TYPE) 151 | } 152 | -------------------------------------------------------------------------------- /src/github.com/ziutek/mymysql/native/command.go: -------------------------------------------------------------------------------- 1 | package native 2 | 3 | import "log" 4 | 5 | func (my *Conn) sendCmd(cmd byte, argv ...interface{}) { 6 | // Reset sequence number 7 | my.seq = 0 8 | // Write command 9 | switch cmd { 10 | case _COM_QUERY, _COM_INIT_DB, _COM_CREATE_DB, _COM_DROP_DB, 11 | _COM_STMT_PREPARE: 12 | pw := my.newPktWriter(1 + lenBS(argv[0])) 13 | writeByte(pw, cmd) 14 | writeBS(pw, argv[0]) 15 | 16 | case _COM_STMT_SEND_LONG_DATA: 17 | pw := my.newPktWriter(1 + 4 + 2 + lenBS(argv[2])) 18 | writeByte(pw, cmd) 19 | writeU32(pw, argv[0].(uint32)) // Statement ID 20 | writeU16(pw, argv[1].(uint16)) // Parameter number 21 | writeBS(pw, argv[2]) // payload 22 | 23 | case _COM_QUIT, _COM_STATISTICS, _COM_PROCESS_INFO, _COM_DEBUG, _COM_PING: 24 | pw := my.newPktWriter(1) 25 | writeByte(pw, cmd) 26 | 27 | case _COM_FIELD_LIST: 28 | pay_len := 1 + lenBS(argv[0]) + 1 29 | if len(argv) > 1 { 30 | pay_len += lenBS(argv[1]) 31 | } 32 | 33 | pw := my.newPktWriter(pay_len) 34 | writeByte(pw, cmd) 35 | writeNT(pw, argv[0]) 36 | if len(argv) > 1 { 37 | writeBS(pw, argv[1]) 38 | } 39 | 40 | case _COM_TABLE_DUMP: 41 | pw := my.newPktWriter(1 + lenLC(argv[0]) + lenLC(argv[1])) 42 | writeByte(pw, cmd) 43 | writeLC(pw, argv[0]) 44 | writeLC(pw, argv[1]) 45 | 46 | case _COM_REFRESH, _COM_SHUTDOWN: 47 | pw := my.newPktWriter(1 + 1) 48 | writeByte(pw, cmd) 49 | writeByte(pw, argv[0].(byte)) 50 | 51 | case _COM_STMT_FETCH: 52 | pw := my.newPktWriter(1 + 4 + 4) 53 | writeByte(pw, cmd) 54 | writeU32(pw, argv[0].(uint32)) 55 | writeU32(pw, argv[1].(uint32)) 56 | 57 | case _COM_PROCESS_KILL, _COM_STMT_CLOSE, _COM_STMT_RESET: 58 | pw := my.newPktWriter(1 + 4) 59 | writeByte(pw, cmd) 60 | writeU32(pw, argv[0].(uint32)) 61 | 62 | case _COM_SET_OPTION: 63 | pw := my.newPktWriter(1 + 2) 64 | writeByte(pw, cmd) 65 | writeU16(pw, argv[0].(uint16)) 66 | 67 | case _COM_CHANGE_USER: 68 | pw := my.newPktWriter( 69 | 1 + lenBS(argv[0]) + 1 + lenLC(argv[1]) + lenBS(argv[2]) + 1, 70 | ) 71 | writeByte(pw, cmd) 72 | writeNT(pw, argv[0]) // User name 73 | writeLC(pw, argv[1]) // Scrambled password 74 | writeNT(pw, argv[2]) // Database name 75 | //writeU16(pw, argv[3]) // Character set number (since 5.1.23?) 76 | 77 | case _COM_BINLOG_DUMP: 78 | pay_len := 1 + 4 + 2 + 4 79 | if len(argv) > 3 { 80 | pay_len += lenBS(argv[3]) 81 | } 82 | 83 | pw := my.newPktWriter(pay_len) 84 | writeByte(pw, cmd) 85 | writeU32(pw, argv[0].(uint32)) // Start position 86 | writeU16(pw, argv[1].(uint16)) // Flags 87 | writeU32(pw, argv[2].(uint32)) // Slave server id 88 | if len(argv) > 3 { 89 | writeBS(pw, argv[3]) 90 | } 91 | 92 | // TODO: case COM_REGISTER_SLAVE: 93 | 94 | default: 95 | panic("Unknown code for MySQL command") 96 | } 97 | 98 | if my.Debug { 99 | log.Printf("[%2d <-] Command packet: Cmd=0x%x", my.seq-1, cmd) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/github.com/ziutek/mymysql/native/common.go: -------------------------------------------------------------------------------- 1 | package native 2 | 3 | import ( 4 | "io" 5 | "runtime" 6 | ) 7 | 8 | var tab8s = " " 9 | 10 | func readFull(rd io.Reader, buf []byte) { 11 | for nn := 0; nn < len(buf); { 12 | kk, err := rd.Read(buf[nn:]) 13 | nn += kk 14 | if err != nil { 15 | if err == io.EOF { 16 | err = io.ErrUnexpectedEOF 17 | } 18 | panic(err) 19 | } 20 | } 21 | } 22 | 23 | func read(rd io.Reader, nn int) (buf []byte) { 24 | buf = make([]byte, nn) 25 | readFull(rd, buf) 26 | return 27 | } 28 | 29 | func readByte(rd io.Reader) byte { 30 | buf := make([]byte, 1) 31 | if _, err := rd.Read(buf); err != nil { 32 | if err == io.EOF { 33 | err = io.ErrUnexpectedEOF 34 | } 35 | panic(err) 36 | } 37 | return buf[0] 38 | } 39 | 40 | func write(wr io.Writer, buf []byte) { 41 | if _, err := wr.Write(buf); err != nil { 42 | panic(err) 43 | } 44 | } 45 | 46 | func writeByte(wr io.Writer, ch byte) { 47 | write(wr, []byte{ch}) 48 | } 49 | 50 | func writeString(wr io.Writer, str string) { 51 | write(wr, []byte(str)) 52 | } 53 | 54 | func writeBS(wr io.Writer, bs interface{}) { 55 | switch buf := bs.(type) { 56 | case string: 57 | writeString(wr, buf) 58 | case []byte: 59 | write(wr, buf) 60 | default: 61 | panic("Can't write: argument isn't a string nor []byte") 62 | } 63 | } 64 | 65 | func lenBS(bs interface{}) int { 66 | switch buf := bs.(type) { 67 | case string: 68 | return len(buf) 69 | case []byte: 70 | return len(buf) 71 | } 72 | panic("Can't get length: argument isn't a string nor []byte") 73 | } 74 | 75 | func catchError(err *error) { 76 | if pv := recover(); pv != nil { 77 | switch e := pv.(type) { 78 | case runtime.Error: 79 | panic(pv) 80 | case error: 81 | *err = e 82 | default: 83 | panic(pv) 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/github.com/ziutek/mymysql/native/consts.go: -------------------------------------------------------------------------------- 1 | package native 2 | 3 | import "strconv" 4 | 5 | // Client caps - borrowed from GoMySQL 6 | const ( 7 | _CLIENT_LONG_PASSWORD = 1 << iota 8 | _CLIENT_FOUND_ROWS 9 | _CLIENT_LONG_FLAG 10 | _CLIENT_CONNECT_WITH_DB 11 | _CLIENT_NO_SCHEMA 12 | _CLIENT_COMPRESS 13 | _CLIENT_ODBC 14 | _CLIENT_LOCAL_FILES 15 | _CLIENT_IGNORE_SPACE 16 | _CLIENT_PROTOCOL_41 17 | _CLIENT_INTERACTIVE 18 | _CLIENT_SSL 19 | _CLIENT_IGNORE_SIGPIPE 20 | _CLIENT_TRANSACTIONS 21 | _CLIENT_RESERVED 22 | _CLIENT_SECURE_CONN 23 | _CLIENT_MULTI_STATEMENTS 24 | _CLIENT_MULTI_RESULTS 25 | ) 26 | 27 | // Commands - borrowed from GoMySQL 28 | const ( 29 | _COM_QUIT = 0x01 30 | _COM_INIT_DB = 0x02 31 | _COM_QUERY = 0x03 32 | _COM_FIELD_LIST = 0x04 33 | _COM_CREATE_DB = 0x05 34 | _COM_DROP_DB = 0x06 35 | _COM_REFRESH = 0x07 36 | _COM_SHUTDOWN = 0x08 37 | _COM_STATISTICS = 0x09 38 | _COM_PROCESS_INFO = 0x0a 39 | _COM_CONNECT = 0x0b 40 | _COM_PROCESS_KILL = 0x0c 41 | _COM_DEBUG = 0x0d 42 | _COM_PING = 0x0e 43 | _COM_TIME = 0x0f 44 | _COM_DELAYED_INSERT = 0x10 45 | _COM_CHANGE_USER = 0x11 46 | _COM_BINLOG_DUMP = 0x12 47 | _COM_TABLE_DUMP = 0x13 48 | _COM_CONNECT_OUT = 0x14 49 | _COM_REGISTER_SLAVE = 0x15 50 | _COM_STMT_PREPARE = 0x16 51 | _COM_STMT_EXECUTE = 0x17 52 | _COM_STMT_SEND_LONG_DATA = 0x18 53 | _COM_STMT_CLOSE = 0x19 54 | _COM_STMT_RESET = 0x1a 55 | _COM_SET_OPTION = 0x1b 56 | _COM_STMT_FETCH = 0x1c 57 | ) 58 | 59 | // Server status 60 | const ( 61 | _SERVER_STATUS_IN_TRANS = 0x01 // Transaction has started 62 | _SERVER_STATUS_AUTOCOMMIT = 0x02 // Server in auto_commit mode 63 | _SERVER_STATUS_MORE_RESULTS = 0x04 64 | _SERVER_MORE_RESULTS_EXISTS = 0x08 // Multi query - next query exists 65 | _SERVER_QUERY_NO_GOOD_INDEX_USED = 0x10 66 | _SERVER_QUERY_NO_INDEX_USED = 0x20 67 | // Server opened a read-only non-scrollable cursor for a query 68 | _SERVER_STATUS_CURSOR_EXISTS = 0x40 69 | _SERVER_STATUS_LAST_ROW_SENT = 0x80 70 | 71 | _SERVER_STATUS_DB_DROPPED = 0x100 72 | _SERVER_STATUS_NO_BACKSLASH_ESCAPES = 0x200 73 | ) 74 | 75 | // MySQL protocol types. 76 | // 77 | // mymysql uses only some of them for send data to the MySQL server. Used 78 | // MySQL types are marked with a comment contains mymysql type that uses it. 79 | const ( 80 | MYSQL_TYPE_DECIMAL = 0x00 81 | MYSQL_TYPE_TINY = 0x01 // int8, uint8, bool 82 | MYSQL_TYPE_SHORT = 0x02 // int16, uint16 83 | MYSQL_TYPE_LONG = 0x03 // int32, uint32 84 | MYSQL_TYPE_FLOAT = 0x04 // float32 85 | MYSQL_TYPE_DOUBLE = 0x05 // float64 86 | MYSQL_TYPE_NULL = 0x06 // nil 87 | MYSQL_TYPE_TIMESTAMP = 0x07 // Timestamp 88 | MYSQL_TYPE_LONGLONG = 0x08 // int64, uint64 89 | MYSQL_TYPE_INT24 = 0x09 90 | MYSQL_TYPE_DATE = 0x0a // Date 91 | MYSQL_TYPE_TIME = 0x0b // Time 92 | MYSQL_TYPE_DATETIME = 0x0c // time.Time 93 | MYSQL_TYPE_YEAR = 0x0d 94 | MYSQL_TYPE_NEWDATE = 0x0e 95 | MYSQL_TYPE_VARCHAR = 0x0f 96 | MYSQL_TYPE_BIT = 0x10 97 | MYSQL_TYPE_NEWDECIMAL = 0xf6 98 | MYSQL_TYPE_ENUM = 0xf7 99 | MYSQL_TYPE_SET = 0xf8 100 | MYSQL_TYPE_TINY_BLOB = 0xf9 101 | MYSQL_TYPE_MEDIUM_BLOB = 0xfa 102 | MYSQL_TYPE_LONG_BLOB = 0xfb 103 | MYSQL_TYPE_BLOB = 0xfc // Blob 104 | MYSQL_TYPE_VAR_STRING = 0xfd // []byte 105 | MYSQL_TYPE_STRING = 0xfe // string 106 | MYSQL_TYPE_GEOMETRY = 0xff 107 | 108 | MYSQL_UNSIGNED_MASK = uint16(1 << 15) 109 | ) 110 | 111 | // Mapping of MySQL types to (prefered) protocol types. Use it if you create 112 | // your own Raw value. 113 | // 114 | // Comments contains corresponding types used by mymysql. string type may be 115 | // replaced by []byte type and vice versa. []byte type is native for sending 116 | // on a network, so any string is converted to it before sending. Than for 117 | // better preformance use []byte. 118 | const ( 119 | // Client send and receive, mymysql representation for send / receive 120 | TINYINT = MYSQL_TYPE_TINY // int8 / int8 121 | SMALLINT = MYSQL_TYPE_SHORT // int16 / int16 122 | INT = MYSQL_TYPE_LONG // int32 / int32 123 | BIGINT = MYSQL_TYPE_LONGLONG // int64 / int64 124 | FLOAT = MYSQL_TYPE_FLOAT // float32 / float32 125 | DOUBLE = MYSQL_TYPE_DOUBLE // float64 / float32 126 | TIME = MYSQL_TYPE_TIME // Time / Time 127 | DATE = MYSQL_TYPE_DATE // Date / Date 128 | DATETIME = MYSQL_TYPE_DATETIME // time.Time / time.Time 129 | TIMESTAMP = MYSQL_TYPE_TIMESTAMP // Timestamp / time.Time 130 | CHAR = MYSQL_TYPE_STRING // string / []byte 131 | BLOB = MYSQL_TYPE_BLOB // Blob / []byte 132 | NULL = MYSQL_TYPE_NULL // nil 133 | 134 | // Client send only, mymysql representation for send 135 | OUT_TEXT = MYSQL_TYPE_STRING // string 136 | OUT_VARCHAR = MYSQL_TYPE_STRING // string 137 | OUT_BINARY = MYSQL_TYPE_BLOB // Blob 138 | OUT_VARBINARY = MYSQL_TYPE_BLOB // Blob 139 | 140 | // Client receive only, mymysql representation for receive 141 | IN_MEDIUMINT = MYSQL_TYPE_INT24 // int32 142 | IN_YEAR = MYSQL_TYPE_SHORT // int16 143 | IN_BINARY = MYSQL_TYPE_STRING // []byte 144 | IN_VARCHAR = MYSQL_TYPE_VAR_STRING // []byte 145 | IN_VARBINARY = MYSQL_TYPE_VAR_STRING // []byte 146 | IN_TINYBLOB = MYSQL_TYPE_TINY_BLOB // []byte 147 | IN_TINYTEXT = MYSQL_TYPE_TINY_BLOB // []byte 148 | IN_TEXT = MYSQL_TYPE_BLOB // []byte 149 | IN_MEDIUMBLOB = MYSQL_TYPE_MEDIUM_BLOB // []byte 150 | IN_MEDIUMTEXT = MYSQL_TYPE_MEDIUM_BLOB // []byte 151 | IN_LONGBLOB = MYSQL_TYPE_LONG_BLOB // []byte 152 | IN_LONGTEXT = MYSQL_TYPE_LONG_BLOB // []byte 153 | 154 | // MySQL 5.x specific 155 | IN_DECIMAL = MYSQL_TYPE_NEWDECIMAL // TODO 156 | IN_BIT = MYSQL_TYPE_BIT // []byte 157 | ) 158 | 159 | // Flags - borrowed from GoMySQL 160 | const ( 161 | _FLAG_NOT_NULL = 1 << iota 162 | _FLAG_PRI_KEY 163 | _FLAG_UNIQUE_KEY 164 | _FLAG_MULTIPLE_KEY 165 | _FLAG_BLOB 166 | _FLAG_UNSIGNED 167 | _FLAG_ZEROFILL 168 | _FLAG_BINARY 169 | _FLAG_ENUM 170 | _FLAG_AUTO_INCREMENT 171 | _FLAG_TIMESTAMP 172 | _FLAG_SET 173 | _FLAG_NO_DEFAULT_VALUE 174 | ) 175 | 176 | var ( 177 | _SIZE_OF_INT int 178 | _INT_TYPE uint16 179 | ) 180 | 181 | func init() { 182 | switch strconv.IntSize { 183 | case 32: 184 | _INT_TYPE = MYSQL_TYPE_LONG 185 | _SIZE_OF_INT = 4 186 | case 64: 187 | _INT_TYPE = MYSQL_TYPE_LONGLONG 188 | _SIZE_OF_INT = 8 189 | default: 190 | panic("bad int size") 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /src/github.com/ziutek/mymysql/native/errors.go: -------------------------------------------------------------------------------- 1 | package native 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | var ( 8 | SEQ_ERROR = errors.New("packet sequence error") 9 | PKT_ERROR = errors.New("malformed packet") 10 | PKT_LONG_ERROR = errors.New("packet too long") 11 | UNEXP_NULL_LCS_ERROR = errors.New("unexpected NULL LCS") 12 | UNEXP_NULL_LCB_ERROR = errors.New("unexpected NULL LCB") 13 | UNEXP_NULL_DATE_ERROR = errors.New("unexpected NULL DATETIME") 14 | UNEXP_NULL_TIME_ERROR = errors.New("unexpected NULL TIME") 15 | UNK_RESULT_PKT_ERROR = errors.New("unexpected or unknown result packet") 16 | NOT_CONN_ERROR = errors.New("not connected") 17 | ALREDY_CONN_ERROR = errors.New("not connected") 18 | BAD_RESULT_ERROR = errors.New("unexpected result") 19 | UNREADED_REPLY_ERROR = errors.New("reply is not completely read") 20 | BIND_COUNT_ERROR = errors.New("wrong number of values for bind") 21 | BIND_UNK_TYPE = errors.New("unknown value type for bind") 22 | RESULT_COUNT_ERROR = errors.New("wrong number of result columns") 23 | BAD_COMMAND_ERROR = errors.New("comand isn't text SQL nor *Stmt") 24 | WRONG_DATE_LEN_ERROR = errors.New("wrong datetime/timestamp length") 25 | WRONG_TIME_LEN_ERROR = errors.New("wrong time length") 26 | UNK_MYSQL_TYPE_ERROR = errors.New("unknown MySQL type") 27 | WRONG_PARAM_NUM_ERROR = errors.New("wrong parameter number") 28 | UNK_DATA_TYPE_ERROR = errors.New("unknown data source type") 29 | SMALL_PKT_SIZE_ERROR = errors.New("specified packet size is to small") 30 | READ_AFTER_EOR_ERROR = errors.New("previous GetRow call returned nil row") 31 | ) 32 | -------------------------------------------------------------------------------- /src/github.com/ziutek/mymysql/native/init.go: -------------------------------------------------------------------------------- 1 | package native 2 | 3 | import ( 4 | "log" 5 | ) 6 | 7 | func (my *Conn) init() { 8 | my.seq = 0 // Reset sequence number, mainly for reconnect 9 | if my.Debug { 10 | log.Printf("[%2d ->] Init packet:", my.seq) 11 | } 12 | pr := my.newPktReader() 13 | my.info.scramble = make([]byte, 20) 14 | 15 | my.info.prot_ver = readByte(pr) 16 | my.info.serv_ver = readNTS(pr) 17 | my.info.thr_id = readU32(pr) 18 | readFull(pr, my.info.scramble[0:8]) 19 | read(pr, 1) 20 | my.info.caps = readU16(pr) 21 | my.info.lang = readByte(pr) 22 | my.status = readU16(pr) 23 | read(pr, 13) 24 | readFull(pr, my.info.scramble[8:]) 25 | // Skip other information 26 | pr.readAll() 27 | 28 | if my.Debug { 29 | log.Printf(tab8s+"ProtVer=%d, ServVer=\"%s\" Status=0x%x", 30 | my.info.prot_ver, my.info.serv_ver, my.status, 31 | ) 32 | } 33 | } 34 | 35 | func (my *Conn) auth() { 36 | if my.Debug { 37 | log.Printf("[%2d <-] Authentication packet", my.seq) 38 | } 39 | pay_len := 4 + 4 + 1 + 23 + len(my.user) + 1 + 1 + len(my.info.scramble) 40 | flags := uint32( 41 | _CLIENT_PROTOCOL_41 | 42 | _CLIENT_LONG_PASSWORD | 43 | _CLIENT_SECURE_CONN | 44 | _CLIENT_MULTI_STATEMENTS | 45 | _CLIENT_MULTI_RESULTS | 46 | _CLIENT_TRANSACTIONS, 47 | ) 48 | if len(my.dbname) > 0 { 49 | pay_len += len(my.dbname) + 1 50 | flags |= _CLIENT_CONNECT_WITH_DB 51 | } 52 | encr_passwd := my.encryptedPasswd() 53 | 54 | pw := my.newPktWriter(pay_len) 55 | writeU32(pw, flags) 56 | writeU32(pw, uint32(my.max_pkt_size)) 57 | writeByte(pw, my.info.lang) // Charset number 58 | write(pw, make([]byte, 23)) // Filler 59 | writeNTS(pw, my.user) // Username 60 | writeBin(pw, encr_passwd) // Encrypted password 61 | if len(my.dbname) > 0 { 62 | writeNTS(pw, my.dbname) 63 | } 64 | return 65 | } 66 | -------------------------------------------------------------------------------- /src/github.com/ziutek/mymysql/native/packet.go: -------------------------------------------------------------------------------- 1 | package native 2 | 3 | import ( 4 | "bufio" 5 | "errors" 6 | "io" 7 | ) 8 | 9 | type pktReader struct { 10 | rd *bufio.Reader 11 | seq *byte 12 | remain int 13 | last bool 14 | } 15 | 16 | func (my *Conn) newPktReader() *pktReader { 17 | return &pktReader{rd: my.rd, seq: &my.seq} 18 | } 19 | 20 | func (pr *pktReader) Read(buf []byte) (num int, err error) { 21 | if len(buf) == 0 { 22 | return 0, nil 23 | } 24 | defer catchError(&err) 25 | 26 | if pr.remain == 0 { 27 | // No data to read from current packet 28 | if pr.last { 29 | // No more packets 30 | return 0, io.EOF 31 | } 32 | // Read next packet header 33 | pr.remain = int(readU24(pr.rd)) 34 | seq := readByte(pr.rd) 35 | // Chceck sequence number 36 | if *pr.seq != seq { 37 | return 0, SEQ_ERROR 38 | } 39 | *pr.seq++ 40 | // Last packet? 41 | pr.last = (pr.remain != 0xffffff) 42 | } 43 | // Reading data 44 | if len(buf) <= pr.remain { 45 | num, err = pr.rd.Read(buf) 46 | } else { 47 | num, err = pr.rd.Read(buf[0:pr.remain]) 48 | } 49 | pr.remain -= num 50 | return 51 | } 52 | 53 | func (pr *pktReader) readAll() (buf []byte) { 54 | buf = make([]byte, pr.remain) 55 | nn := 0 56 | for { 57 | readFull(pr, buf[nn:]) 58 | if pr.last { 59 | break 60 | } 61 | // There is next packet to read 62 | new_buf := make([]byte, len(buf)+pr.remain) 63 | copy(new_buf[nn:], buf) 64 | nn += len(buf) 65 | buf = new_buf 66 | } 67 | return 68 | } 69 | 70 | func (pr *pktReader) unreadByte() { 71 | if err := pr.rd.UnreadByte(); err != nil { 72 | panic(err) 73 | } 74 | pr.remain++ 75 | } 76 | 77 | func (pr *pktReader) eof() bool { 78 | return pr.remain == 0 && pr.last 79 | } 80 | 81 | func (pr *pktReader) checkEof() { 82 | if !pr.eof() { 83 | panic(PKT_LONG_ERROR) 84 | } 85 | } 86 | 87 | type pktWriter struct { 88 | wr *bufio.Writer 89 | seq *byte 90 | remain int 91 | to_write int 92 | last bool 93 | } 94 | 95 | func (my *Conn) newPktWriter(to_write int) *pktWriter { 96 | return &pktWriter{wr: my.wr, seq: &my.seq, to_write: to_write} 97 | } 98 | 99 | /*func writePktHeader(wr io.Writer, seq byte, pay_len int) { 100 | writeU24(wr, uint32(pay_len)) 101 | writeByte(wr, seq) 102 | }*/ 103 | 104 | func (pw *pktWriter) Write(buf []byte) (num int, err error) { 105 | if len(buf) == 0 { 106 | return 107 | } 108 | defer catchError(&err) 109 | 110 | var nn int 111 | for len(buf) != 0 { 112 | if pw.remain == 0 { 113 | if pw.to_write == 0 { 114 | err = errors.New("too many data for write as packet") 115 | return 116 | } 117 | if pw.to_write >= 0xffffff { 118 | pw.remain = 0xffffff 119 | } else { 120 | pw.remain = pw.to_write 121 | pw.last = true 122 | } 123 | pw.to_write -= pw.remain 124 | // Write packet header 125 | writeU24(pw.wr, uint32(pw.remain)) 126 | writeByte(pw.wr, *pw.seq) 127 | // Update sequence number 128 | *pw.seq++ 129 | } 130 | nn = len(buf) 131 | if nn > pw.remain { 132 | nn = pw.remain 133 | } 134 | nn, err = pw.wr.Write(buf[0:nn]) 135 | num += nn 136 | pw.remain -= nn 137 | if err != nil { 138 | return 139 | } 140 | buf = buf[nn:] 141 | } 142 | if pw.remain+pw.to_write == 0 { 143 | if !pw.last { 144 | // Write header for empty packet 145 | writeU24(pw.wr, 0) 146 | writeByte(pw.wr, *pw.seq) 147 | // Update sequence number 148 | *pw.seq++ 149 | } 150 | // Flush bufio buffers 151 | err = pw.wr.Flush() 152 | } 153 | return 154 | } 155 | -------------------------------------------------------------------------------- /src/github.com/ziutek/mymysql/native/prepared.go: -------------------------------------------------------------------------------- 1 | package native 2 | 3 | import ( 4 | "github.com/ziutek/mymysql/mysql" 5 | "log" 6 | ) 7 | 8 | type Stmt struct { 9 | my *Conn 10 | 11 | id uint32 12 | sql string // For reprepare during reconnect 13 | 14 | params []*paramValue // Parameters binding 15 | rebind bool 16 | 17 | fields []*mysql.Field 18 | fc_map map[string]int // Maps field name to column number 19 | 20 | field_count int 21 | param_count int 22 | warning_count int 23 | status uint16 24 | } 25 | 26 | // Returns index for given name or -1 if field of that name doesn't exist 27 | func (res *Stmt) Map(field_name string) int { 28 | if fi, ok := res.fc_map[field_name]; ok { 29 | return fi 30 | } 31 | return -1 32 | } 33 | 34 | func (stmt *Stmt) NumField() int { 35 | return stmt.field_count 36 | } 37 | 38 | func (stmt *Stmt) NumParam() int { 39 | return stmt.param_count 40 | } 41 | 42 | func (stmt *Stmt) WarnCount() int { 43 | return stmt.warning_count 44 | } 45 | 46 | func (stmt *Stmt) sendCmdExec() { 47 | // Calculate packet length and NULL bitmap 48 | null_bitmap := make([]byte, (stmt.param_count+7)>>3) 49 | pkt_len := 1 + 4 + 1 + 4 + 1 + len(null_bitmap) 50 | for ii, param := range stmt.params { 51 | par_len := param.Len() 52 | pkt_len += par_len 53 | if par_len == 0 { 54 | null_byte := ii >> 3 55 | null_mask := byte(1) << uint(ii-(null_byte<<3)) 56 | null_bitmap[null_byte] |= null_mask 57 | } 58 | } 59 | if stmt.rebind { 60 | pkt_len += stmt.param_count * 2 61 | } 62 | // Reset sequence number 63 | stmt.my.seq = 0 64 | // Packet sending 65 | pw := stmt.my.newPktWriter(pkt_len) 66 | writeByte(pw, _COM_STMT_EXECUTE) 67 | writeU32(pw, stmt.id) 68 | writeByte(pw, 0) // flags = CURSOR_TYPE_NO_CURSOR 69 | writeU32(pw, 1) // iteration_count 70 | write(pw, null_bitmap) 71 | if stmt.rebind { 72 | writeByte(pw, 1) 73 | // Types 74 | for _, param := range stmt.params { 75 | writeU16(pw, param.typ) 76 | } 77 | } else { 78 | writeByte(pw, 0) 79 | } 80 | // Values 81 | for _, param := range stmt.params { 82 | writeValue(pw, param) 83 | } 84 | 85 | if stmt.my.Debug { 86 | log.Printf("[%2d <-] Exec command packet: len=%d, null_bitmap=%v, rebind=%t", 87 | stmt.my.seq-1, pkt_len, null_bitmap, stmt.rebind) 88 | } 89 | 90 | // Mark that we sended information about binded types 91 | stmt.rebind = false 92 | } 93 | 94 | func (my *Conn) getPrepareResult(stmt *Stmt) interface{} { 95 | loop: 96 | pr := my.newPktReader() // New reader for next packet 97 | pkt0 := readByte(pr) 98 | 99 | //log.Println("pkt0:", pkt0, "stmt:", stmt) 100 | 101 | if pkt0 == 255 { 102 | // Error packet 103 | my.getErrorPacket(pr) 104 | } 105 | 106 | if stmt == nil { 107 | if pkt0 == 0 { 108 | // OK packet 109 | return my.getPrepareOkPacket(pr) 110 | } 111 | } else { 112 | unreaded_params := (stmt.param_count < len(stmt.params)) 113 | switch { 114 | case pkt0 == 254: 115 | // EOF packet 116 | stmt.warning_count, stmt.status = my.getEofPacket(pr) 117 | stmt.my.status = stmt.status 118 | return stmt 119 | 120 | case pkt0 > 0 && pkt0 < 251 && (stmt.field_count < len(stmt.fields) || 121 | unreaded_params): 122 | // Field packet 123 | if unreaded_params { 124 | // Read and ignore parameter field. Sentence from MySQL source: 125 | /* skip parameters data: we don't support it yet */ 126 | my.getFieldPacket(pr) 127 | // Increment field count 128 | stmt.param_count++ 129 | } else { 130 | field := my.getFieldPacket(pr) 131 | stmt.fields[stmt.field_count] = field 132 | stmt.fc_map[field.Name] = stmt.field_count 133 | // Increment field count 134 | stmt.field_count++ 135 | } 136 | // Read next packet 137 | goto loop 138 | } 139 | } 140 | panic(UNK_RESULT_PKT_ERROR) 141 | } 142 | 143 | func (my *Conn) getPrepareOkPacket(pr *pktReader) (stmt *Stmt) { 144 | if my.Debug { 145 | log.Printf("[%2d ->] Perpared OK packet:", my.seq-1) 146 | } 147 | 148 | stmt = new(Stmt) 149 | stmt.my = my 150 | // First byte was readed by getPrepRes 151 | stmt.id = readU32(pr) 152 | stmt.fields = make([]*mysql.Field, int(readU16(pr))) // FieldCount 153 | stmt.params = make([]*paramValue, int(readU16(pr))) // ParamCount 154 | read(pr, 1) 155 | stmt.warning_count = int(readU16(pr)) 156 | pr.checkEof() 157 | 158 | // Make field map if fields exists. 159 | if len(stmt.fields) > 0 { 160 | stmt.fc_map = make(map[string]int) 161 | } 162 | if my.Debug { 163 | log.Printf(tab8s+"ID=0x%x ParamCount=%d FieldsCount=%d WarnCount=%d", 164 | stmt.id, len(stmt.params), len(stmt.fields), stmt.warning_count, 165 | ) 166 | } 167 | return 168 | } 169 | -------------------------------------------------------------------------------- /src/github.com/ziutek/mymysql/native/unsafe.go: -------------------------------------------------------------------------------- 1 | package native 2 | 3 | import ( 4 | "github.com/ziutek/mymysql/mysql" 5 | "io" 6 | "time" 7 | "unsafe" 8 | ) 9 | 10 | type paramValue struct { 11 | typ uint16 12 | addr unsafe.Pointer 13 | raw bool 14 | length int // >=0 - length of value, <0 - unknown length 15 | } 16 | 17 | func (pv *paramValue) SetAddr(addr uintptr) { 18 | pv.addr = unsafe.Pointer(addr) 19 | } 20 | 21 | func (val *paramValue) Len() int { 22 | if val.addr == nil { 23 | // Invalid Value was binded 24 | return 0 25 | } 26 | // val.addr always points to the pointer - lets dereference it 27 | ptr := *(*unsafe.Pointer)(val.addr) 28 | if ptr == nil { 29 | // Binded Ptr Value is nil 30 | return 0 31 | } 32 | 33 | if val.length >= 0 { 34 | return val.length 35 | } 36 | 37 | switch val.typ { 38 | case MYSQL_TYPE_STRING: 39 | return lenStr(*(*string)(ptr)) 40 | 41 | case MYSQL_TYPE_DATE: 42 | return lenDate(*(*mysql.Date)(ptr)) 43 | 44 | case MYSQL_TYPE_TIMESTAMP, MYSQL_TYPE_DATETIME: 45 | return lenTime(*(*time.Time)(ptr)) 46 | 47 | case MYSQL_TYPE_TIME: 48 | return lenDuration(*(*time.Duration)(ptr)) 49 | 50 | case MYSQL_TYPE_TINY: // val.length < 0 so this is bool 51 | return 1 52 | } 53 | // MYSQL_TYPE_VAR_STRING, MYSQL_TYPE_BLOB and type of Raw value 54 | return lenBin(*(*[]byte)(ptr)) 55 | } 56 | 57 | func writeValue(wr io.Writer, val *paramValue) { 58 | if val.addr == nil { 59 | // Invalid Value was binded 60 | return 61 | } 62 | // val.addr always points to the pointer - lets dereference it 63 | ptr := *(*unsafe.Pointer)(val.addr) 64 | if ptr == nil { 65 | // Binded Ptr Value is nil 66 | return 67 | } 68 | 69 | if val.raw || val.typ == MYSQL_TYPE_VAR_STRING || 70 | val.typ == MYSQL_TYPE_BLOB { 71 | writeBin(wr, *(*[]byte)(ptr)) 72 | return 73 | } 74 | // We don't need unsigned bit to check type 75 | switch val.typ & ^MYSQL_UNSIGNED_MASK { 76 | case MYSQL_TYPE_NULL: 77 | // Don't write null values 78 | 79 | case MYSQL_TYPE_STRING: 80 | writeStr(wr, *(*string)(ptr)) 81 | 82 | case MYSQL_TYPE_LONG, MYSQL_TYPE_FLOAT: 83 | writeU32(wr, *(*uint32)(ptr)) 84 | 85 | case MYSQL_TYPE_SHORT: 86 | writeU16(wr, *(*uint16)(ptr)) 87 | 88 | case MYSQL_TYPE_TINY: 89 | if val.length == -1 { 90 | // Translate bool value to MySQL tiny 91 | if *(*bool)(ptr) { 92 | writeByte(wr, 1) 93 | } else { 94 | writeByte(wr, 0) 95 | } 96 | } else { 97 | writeByte(wr, *(*byte)(ptr)) 98 | } 99 | 100 | case MYSQL_TYPE_LONGLONG, MYSQL_TYPE_DOUBLE: 101 | writeU64(wr, *(*uint64)(ptr)) 102 | 103 | case MYSQL_TYPE_DATE: 104 | writeDate(wr, *(*mysql.Date)(ptr)) 105 | 106 | case MYSQL_TYPE_TIMESTAMP, MYSQL_TYPE_DATETIME: 107 | writeTime(wr, *(*time.Time)(ptr)) 108 | 109 | case MYSQL_TYPE_TIME: 110 | writeDuration(wr, *(*time.Duration)(ptr)) 111 | 112 | default: 113 | panic(BIND_UNK_TYPE) 114 | } 115 | return 116 | } 117 | -------------------------------------------------------------------------------- /src/github.com/ziutek/mymysql/thrsafe/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010, Michal Derkacz 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 3. The name of the author may not be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /src/github.com/ziutek/mymysql/thrsafe/Makefile: -------------------------------------------------------------------------------- 1 | include $(GOROOT)/src/Make.inc 2 | 3 | TARG=github.com/ziutek/mymysql/thrsafe 4 | GOFILES=\ 5 | thrsafe.go\ 6 | 7 | include $(GOROOT)/src/Make.pkg 8 | -------------------------------------------------------------------------------- /src/github.com/ziutek/mymysql/thrsafe/thrsafe.go: -------------------------------------------------------------------------------- 1 | // Thread safe engine for MyMySQL 2 | 3 | // See documentation of mymysql/native for details/ 4 | package thrsafe 5 | 6 | import ( 7 | "sync" 8 | //"log" 9 | "github.com/ziutek/mymysql/mysql" 10 | _ "github.com/ziutek/mymysql/native" 11 | ) 12 | 13 | type Conn struct { 14 | mysql.Conn 15 | mutex *sync.Mutex 16 | } 17 | 18 | func (c *Conn) lock() { 19 | //log.Println(c, ":: lock @", c.mutex) 20 | c.mutex.Lock() 21 | } 22 | 23 | func (c *Conn) unlock() { 24 | //log.Println(c, ":: unlock @", c.mutex) 25 | c.mutex.Unlock() 26 | } 27 | 28 | type Result struct { 29 | mysql.Result 30 | conn *Conn 31 | } 32 | 33 | type Stmt struct { 34 | mysql.Stmt 35 | conn *Conn 36 | } 37 | 38 | type Transaction struct { 39 | *Conn 40 | conn *Conn 41 | } 42 | 43 | func New(proto, laddr, raddr, user, passwd string, db ...string) mysql.Conn { 44 | return &Conn{ 45 | Conn: orgNew(proto, laddr, raddr, user, passwd, db...), 46 | mutex: new(sync.Mutex), 47 | } 48 | } 49 | 50 | func (c *Conn) Connect() error { 51 | //log.Println("Connect") 52 | c.lock() 53 | defer c.unlock() 54 | return c.Conn.Connect() 55 | } 56 | 57 | func (c *Conn) Close() error { 58 | //log.Println("Close") 59 | c.lock() 60 | defer c.unlock() 61 | return c.Conn.Close() 62 | } 63 | 64 | func (c *Conn) Reconnect() error { 65 | //log.Println("Reconnect") 66 | c.lock() 67 | defer c.unlock() 68 | return c.Conn.Reconnect() 69 | } 70 | 71 | func (c *Conn) Use(dbname string) error { 72 | //log.Println("Use") 73 | c.lock() 74 | defer c.unlock() 75 | return c.Conn.Use(dbname) 76 | } 77 | 78 | func (c *Conn) Start(sql string, params ...interface{}) (mysql.Result, error) { 79 | //log.Println("Start") 80 | c.lock() 81 | res, err := c.Conn.Start(sql, params...) 82 | // Unlock if error or OK result (which doesn't provide any fields) 83 | if err != nil { 84 | c.unlock() 85 | return nil, err 86 | } 87 | if len(res.Fields()) == 0 { 88 | c.unlock() 89 | } 90 | return &Result{Result: res, conn: c}, err 91 | } 92 | 93 | func (res *Result) GetRow() (mysql.Row, error) { 94 | //log.Println("GetRow") 95 | if len(res.Result.Fields()) == 0 { 96 | // There is no fields in result (OK result) 97 | return nil, nil 98 | } 99 | row, err := res.Result.GetRow() 100 | if err != nil || row == nil && !res.MoreResults() { 101 | res.conn.unlock() 102 | } 103 | return row, err 104 | } 105 | 106 | func (res *Result) NextResult() (mysql.Result, error) { 107 | //log.Println("NextResult") 108 | next, err := res.Result.NextResult() 109 | if err != nil { 110 | return nil, err 111 | } 112 | return &Result{next, res.conn}, nil 113 | } 114 | 115 | func (c *Conn) Ping() error { 116 | c.lock() 117 | defer c.unlock() 118 | return c.Conn.Ping() 119 | } 120 | 121 | func (c *Conn) Prepare(sql string) (mysql.Stmt, error) { 122 | //log.Println("Prepare") 123 | c.lock() 124 | defer c.unlock() 125 | stmt, err := c.Conn.Prepare(sql) 126 | if err != nil { 127 | return nil, err 128 | } 129 | return &Stmt{Stmt: stmt, conn: c}, nil 130 | } 131 | 132 | func (stmt *Stmt) Run(params ...interface{}) (mysql.Result, error) { 133 | //log.Println("Run") 134 | stmt.conn.lock() 135 | res, err := stmt.Stmt.Run(params...) 136 | // Unlock if error or OK result (which doesn't provide any fields) 137 | if err != nil { 138 | stmt.conn.unlock() 139 | return nil, err 140 | } 141 | if len(res.Fields()) == 0 { 142 | stmt.conn.unlock() 143 | } 144 | return &Result{Result: res, conn: stmt.conn}, nil 145 | } 146 | 147 | func (stmt *Stmt) Delete() error { 148 | //log.Println("Delete") 149 | stmt.conn.lock() 150 | defer stmt.conn.unlock() 151 | return stmt.Stmt.Delete() 152 | } 153 | 154 | func (stmt *Stmt) Reset() error { 155 | //log.Println("Reset") 156 | stmt.conn.lock() 157 | defer stmt.conn.unlock() 158 | return stmt.Stmt.Reset() 159 | } 160 | 161 | func (stmt *Stmt) SendLongData(pnum int, data interface{}, pkt_size int) error { 162 | //log.Println("SendLongData") 163 | stmt.conn.lock() 164 | defer stmt.conn.unlock() 165 | return stmt.Stmt.SendLongData(pnum, data, pkt_size) 166 | } 167 | 168 | func (c *Conn) Query(sql string, params ...interface{}) ([]mysql.Row, mysql.Result, error) { 169 | return mysql.Query(c, sql, params...) 170 | } 171 | 172 | func (stmt *Stmt) Exec(params ...interface{}) ([]mysql.Row, mysql.Result, error) { 173 | return mysql.Exec(stmt, params...) 174 | } 175 | 176 | func (res *Result) End() error { 177 | return mysql.End(res) 178 | } 179 | 180 | func (res *Result) GetRows() ([]mysql.Row, error) { 181 | return mysql.GetRows(res) 182 | } 183 | 184 | // Begins a new transaction. No any other thread can send command on this 185 | // connection until Commit or Rollback will be called. 186 | func (c *Conn) Begin() (mysql.Transaction, error) { 187 | //log.Println("Begin") 188 | c.lock() 189 | tr := Transaction{ 190 | &Conn{c.Conn, new(sync.Mutex)}, 191 | c, 192 | } 193 | _, err := c.Conn.Start("START TRANSACTION") 194 | if err != nil { 195 | c.unlock() 196 | return nil, err 197 | } 198 | return &tr, nil 199 | } 200 | 201 | func (tr *Transaction) end(cr string) error { 202 | tr.lock() 203 | _, err := tr.conn.Conn.Start(cr) 204 | tr.conn.unlock() 205 | // Invalidate this transaction 206 | m := tr.Conn.mutex 207 | tr.Conn = nil 208 | tr.conn = nil 209 | m.Unlock() // One goorutine which still uses this transaction will panic 210 | return err 211 | } 212 | 213 | func (tr *Transaction) Commit() error { 214 | //log.Println("Commit") 215 | return tr.end("COMMIT") 216 | } 217 | 218 | func (tr *Transaction) Rollback() error { 219 | //log.Println("Rollback") 220 | return tr.end("ROLLBACK") 221 | } 222 | 223 | func (tr *Transaction) Do(st mysql.Stmt) mysql.Stmt { 224 | if s, ok := st.(*Stmt); ok && s.conn == tr.conn { 225 | // Returns new statement which uses statement mutexes 226 | return &Stmt{s.Stmt, tr.Conn} 227 | } 228 | panic("Transaction and statement doesn't belong to the same connection") 229 | } 230 | 231 | var orgNew func(proto, laddr, raddr, user, passwd string, db ...string) mysql.Conn 232 | 233 | func init() { 234 | orgNew = mysql.New 235 | mysql.New = New 236 | } 237 | -------------------------------------------------------------------------------- /src/github.com/ziutek/mymysql/thrsafe/thrsafe_test.go: -------------------------------------------------------------------------------- 1 | package thrsafe 2 | -------------------------------------------------------------------------------- /src/gopkg.in/inconshreveable/log15.v2/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2014 Alan Shreve 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /src/gopkg.in/inconshreveable/log15.v2/README.md: -------------------------------------------------------------------------------- 1 | ![obligatory xkcd](http://imgs.xkcd.com/comics/standards.png) 2 | 3 | # log15 4 | 5 | Package log15 provides an opinionated, simple toolkit for best-practice logging that is both human and machine readable. It is modeled after the standard library's io and net/http packages. 6 | 7 | ## Features 8 | - A simple, easy-to-understand API 9 | - Promotes structured logging by encouraging use of key/value pairs 10 | - Child loggers which inherit and add their own private context 11 | - Lazy evaluation of expensive operations 12 | - Simple Handler interface allowing for construction of flexible, custom logging configurations with a tiny API. 13 | - Color terminal support 14 | - Built-in support for logging to files, streams, syslog, and the network 15 | - Support for forking records to multiple handlers, buffering records for output, failing over from failed handler writes, + more 16 | 17 | ## Documentation 18 | 19 | The package documentation is extensive and complete. Browse on godoc: 20 | 21 | #### [log15 API Documentation](https://godoc.org/github.com/inconshreveable/log15) 22 | 23 | ## Versioning 24 | The API of the master branch of log15 should always be considered unstable. Using a stable version 25 | of the log15 package is supported by gopkg.in. Include your dependency like so: 26 | 27 | import log "gopkg.in/inconshreveable/log15.v2" 28 | 29 | You can also vendor log15 with a tool like Godep. 30 | 31 | ## Examples 32 | 33 | // all loggers can have key/value context 34 | srvlog := log.New("module", "app/server") 35 | 36 | // all log messages can have key/value context 37 | srvlog.Warn("abnormal conn rate", "rate", curRate, "low", lowRate, "high", highRate) 38 | 39 | // child loggers with inherited context 40 | connlog := srvlog.New("raddr", c.RemoteAddr()) 41 | connlog.Info("connection open") 42 | 43 | // lazy evaluation 44 | connlog.Debug("ping remote", "latency", log.Lazy(pingRemote)) 45 | 46 | // flexible configuration 47 | srvlog.SetHandler(log.MultiHandler( 48 | log.StreamHandler(os.Stderr, log.LogfmtFormat()), 49 | log.LvlFilterHandler( 50 | log.LvlError, 51 | log.Must.FileHandler("errors.json", log.JsonHandler()))) 52 | 53 | ## FAQ 54 | 55 | ### The varargs style is brittle and error prone! Can I have type saftey please? 56 | Yes. Use log.Ctx: 57 | 58 | srvlog := log.New(log.Ctx{"module": "app/server"}) 59 | srvlog.Warn("abnormal conn rate", log.Ctx{"rate": curRate, "low": lowRate, "high": highRate}) 60 | 61 | ## License 62 | Apache 63 | -------------------------------------------------------------------------------- /src/gopkg.in/inconshreveable/log15.v2/bench_test.go: -------------------------------------------------------------------------------- 1 | package log15 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func BenchmarkStreamNoCtx(b *testing.B) { 10 | lg := New() 11 | 12 | buf := bytes.Buffer{} 13 | lg.SetHandler(StreamHandler(&buf, LogfmtFormat())) 14 | 15 | for i := 0; i < b.N; i++ { 16 | lg.Info("test message") 17 | buf.Reset() 18 | } 19 | } 20 | 21 | func BenchmarkDiscard(b *testing.B) { 22 | lg := New() 23 | lg.SetHandler(DiscardHandler()) 24 | 25 | for i := 0; i < b.N; i++ { 26 | lg.Info("test message") 27 | } 28 | } 29 | 30 | func BenchmarkLogfmtNoCtx(b *testing.B) { 31 | r := Record{ 32 | Time: time.Now(), 33 | Lvl: LvlInfo, 34 | Msg: "test message", 35 | Ctx: []interface{}{}, 36 | } 37 | 38 | logfmt := LogfmtFormat() 39 | for i := 0; i < b.N; i++ { 40 | logfmt.Format(&r) 41 | } 42 | } 43 | 44 | func BenchmarkJsonNoCtx(b *testing.B) { 45 | r := Record{ 46 | Time: time.Now(), 47 | Lvl: LvlInfo, 48 | Msg: "test message", 49 | Ctx: []interface{}{}, 50 | } 51 | 52 | jsonfmt := JsonFormat() 53 | for i := 0; i < b.N; i++ { 54 | jsonfmt.Format(&r) 55 | } 56 | } 57 | 58 | func BenchmarkMultiLevelFilter(b *testing.B) { 59 | handler := MultiHandler( 60 | LvlFilterHandler(LvlDebug, DiscardHandler()), 61 | LvlFilterHandler(LvlError, DiscardHandler()), 62 | ) 63 | 64 | lg := New() 65 | lg.SetHandler(handler) 66 | for i := 0; i < b.N; i++ { 67 | lg.Info("test message") 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/gopkg.in/inconshreveable/log15.v2/ext/ext_test.go: -------------------------------------------------------------------------------- 1 | package ext 2 | 3 | import ( 4 | "errors" 5 | log "gopkg.in/inconshreveable/log15.v2" 6 | "math" 7 | "testing" 8 | ) 9 | 10 | func testHandler() (log.Handler, *log.Record) { 11 | rec := new(log.Record) 12 | return log.FuncHandler(func(r *log.Record) error { 13 | *rec = *r 14 | return nil 15 | }), rec 16 | } 17 | 18 | func TestHotSwapHandler(t *testing.T) { 19 | t.Parallel() 20 | 21 | h1, r1 := testHandler() 22 | 23 | l := log.New() 24 | h := HotSwapHandler(h1) 25 | l.SetHandler(h) 26 | 27 | l.Info("to h1") 28 | if r1.Msg != "to h1" { 29 | t.Fatalf("didn't get expected message to h1") 30 | } 31 | 32 | h2, r2 := testHandler() 33 | h.Swap(h2) 34 | l.Info("to h2") 35 | if r2.Msg != "to h2" { 36 | t.Fatalf("didn't get expected message to h2") 37 | } 38 | } 39 | 40 | func TestSpeculativeHandler(t *testing.T) { 41 | t.Parallel() 42 | 43 | // test with an even multiple of the buffer size, less than full buffer size 44 | // and not a multiple of the buffer size 45 | for _, count := range []int{10000, 50, 432} { 46 | recs := make(chan *log.Record) 47 | done := make(chan int) 48 | spec := SpeculativeHandler(100, log.ChannelHandler(recs)) 49 | 50 | go func() { 51 | defer close(done) 52 | expectedCount := int(math.Min(float64(count), float64(100))) 53 | expectedIdx := count - expectedCount 54 | for r := range recs { 55 | if r.Ctx[1] != expectedIdx { 56 | t.Errorf("Bad ctx 'i', got %d expected %d", r.Ctx[1], expectedIdx) 57 | return 58 | } 59 | expectedIdx++ 60 | expectedCount-- 61 | 62 | if expectedCount == 0 { 63 | // got everything we expected 64 | break 65 | } 66 | } 67 | 68 | select { 69 | case <-recs: 70 | t.Errorf("got an extra record we shouldn't have!") 71 | default: 72 | } 73 | }() 74 | 75 | lg := log.New() 76 | lg.SetHandler(spec) 77 | for i := 0; i < count; i++ { 78 | lg.Debug("test speculative", "i", i) 79 | } 80 | 81 | go spec.Flush() 82 | 83 | // wait for the go routine to finish 84 | <-done 85 | } 86 | } 87 | 88 | func TestErrorHandler(t *testing.T) { 89 | t.Parallel() 90 | 91 | h, r := testHandler() 92 | lg := log.New() 93 | lg.SetHandler(EscalateErrHandler( 94 | log.LvlFilterHandler(log.LvlError, h))) 95 | 96 | lg.Debug("some function result", "err", nil) 97 | if r.Msg != "" { 98 | t.Fatalf("Expected debug level message to be filtered") 99 | } 100 | 101 | lg.Debug("some function result", "err", errors.New("failed operation")) 102 | if r.Msg != "some function result" { 103 | t.Fatalf("Expected debug level message to be escalated and pass lvlfilter") 104 | } 105 | 106 | if r.Lvl != log.LvlError { 107 | t.Fatalf("Expected debug level message to be escalated to LvlError") 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/gopkg.in/inconshreveable/log15.v2/ext/handler.go: -------------------------------------------------------------------------------- 1 | package ext 2 | 3 | import ( 4 | log "gopkg.in/inconshreveable/log15.v2" 5 | "sync" 6 | ) 7 | 8 | // EscalateErrHandler wraps another handler and passes all records through 9 | // unchanged except if the logged context contains a non-nil error 10 | // value in its context. In that case, the record's level is raised 11 | // to LvlError unless it was already more serious (LvlCrit). 12 | // 13 | // This allows you to log the result of all functions for debugging 14 | // and still capture error conditions when in production with a single 15 | // log line. As an example, the following the log record will be written 16 | // out only if there was an error writing a value to redis: 17 | // 18 | // logger := logext.EscalateErrHandler( 19 | // log.LvlFilterHandler(log.LvlInfo, log.StdoutHandler)) 20 | // 21 | // reply, err := redisConn.Do("SET", "foo", "bar") 22 | // logger.Debug("Wrote value to redis", "reply", reply, "err", err) 23 | // if err != nil { 24 | // return err 25 | // } 26 | // 27 | func EscalateErrHandler(h log.Handler) log.Handler { 28 | return log.FuncHandler(func(r *log.Record) error { 29 | if r.Lvl > log.LvlError { 30 | for i := 1; i < len(r.Ctx); i++ { 31 | if v, ok := r.Ctx[i].(error); ok && v != nil { 32 | r.Lvl = log.LvlError 33 | break 34 | } 35 | } 36 | } 37 | return h.Log(r) 38 | }) 39 | } 40 | 41 | // SpeculativeHandler is a handler for speculative logging. It 42 | // keeps a ring buffer of the given size full of the last events 43 | // logged into it. When Flush is called, all buffered log records 44 | // are written to the wrapped handler. This is extremely for 45 | // continuosly capturing debug level output, but only flushing those 46 | // log records if an exceptional condition is encountered. 47 | func SpeculativeHandler(size int, h log.Handler) *Speculative { 48 | return &Speculative{ 49 | handler: h, 50 | recs: make([]*log.Record, size), 51 | } 52 | } 53 | 54 | type Speculative struct { 55 | mu sync.Mutex 56 | idx int 57 | recs []*log.Record 58 | handler log.Handler 59 | full bool 60 | } 61 | 62 | func (h *Speculative) Log(r *log.Record) error { 63 | h.mu.Lock() 64 | defer h.mu.Unlock() 65 | h.recs[h.idx] = r 66 | h.idx = (h.idx + 1) % len(h.recs) 67 | h.full = h.full || h.idx == 0 68 | return nil 69 | } 70 | 71 | func (h *Speculative) Flush() { 72 | recs := make([]*log.Record, 0) 73 | func() { 74 | h.mu.Lock() 75 | defer h.mu.Unlock() 76 | if h.full { 77 | recs = append(recs, h.recs[h.idx:]...) 78 | } 79 | recs = append(recs, h.recs[:h.idx]...) 80 | 81 | // reset state 82 | h.full = false 83 | h.idx = 0 84 | }() 85 | 86 | // don't hold the lock while we flush to the wrapped handler 87 | for _, r := range recs { 88 | h.handler.Log(r) 89 | } 90 | } 91 | 92 | // HotSwapHandler wraps another handler that may swapped out 93 | // dynamically at runtime in a thread-safe fashion. 94 | // HotSwapHandler is the same functionality 95 | // used to implement the SetHandler method for the default 96 | // implementation of Logger. 97 | func HotSwapHandler(h log.Handler) *HotSwap { 98 | return &HotSwap{handler: h} 99 | } 100 | 101 | type HotSwap struct { 102 | mu sync.RWMutex 103 | handler log.Handler 104 | } 105 | 106 | func (h *HotSwap) Log(r *log.Record) error { 107 | defer h.mu.RUnlock() 108 | h.mu.RLock() 109 | err := h.handler.Log(r) 110 | return err 111 | } 112 | 113 | func (h *HotSwap) Swap(newHandler log.Handler) { 114 | h.mu.Lock() 115 | defer h.mu.Unlock() 116 | h.handler = newHandler 117 | } 118 | -------------------------------------------------------------------------------- /src/gopkg.in/inconshreveable/log15.v2/ext/id.go: -------------------------------------------------------------------------------- 1 | package ext 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | var r *rand.Rand 10 | 11 | func init() { 12 | r = rand.New(rand.NewSource(time.Now().Unix())) 13 | } 14 | 15 | // RandId creates a random identifier of the requested length. 16 | // Useful for assigning mostly-unique identifiers for logging 17 | // and identification that are unlikely to collide because of 18 | // short lifespan or low set cardinality 19 | func RandId(idlen int) string { 20 | b := make([]byte, idlen) 21 | var randVal uint32 22 | for i := 0; i < idlen; i++ { 23 | byteIdx := i % 4 24 | if byteIdx == 0 { 25 | randVal = r.Uint32() 26 | } 27 | b[i] = byte((randVal >> (8 * uint(byteIdx))) & 0xFF) 28 | } 29 | return fmt.Sprintf("%x", b) 30 | } 31 | -------------------------------------------------------------------------------- /src/gopkg.in/inconshreveable/log15.v2/format.go: -------------------------------------------------------------------------------- 1 | package log15 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "strconv" 8 | "strings" 9 | "time" 10 | ) 11 | 12 | const ( 13 | timeFormat = "2006-01-02T15:04:05-0700" 14 | termTimeFormat = "01-02|15:04:05" 15 | floatFormat = 'f' 16 | termMsgJust = 40 17 | ) 18 | 19 | type Format interface { 20 | Format(r *Record) []byte 21 | } 22 | 23 | // FormatFunc returns a new Format object which uses 24 | // the given function to perform record formatting. 25 | func FormatFunc(f func(*Record) []byte) Format { 26 | return formatFunc(f) 27 | } 28 | 29 | type formatFunc func(*Record) []byte 30 | 31 | func (f formatFunc) Format(r *Record) []byte { 32 | return f(r) 33 | } 34 | 35 | // TerminalFormat formats log records optimized for human readability on 36 | // a terminal with color-coded level output and terser human friendly timestamp. 37 | // This format should only be used for interactive programs or while developing. 38 | // 39 | // [TIME] [LEVEL] MESAGE key=value key=value ... 40 | // 41 | // Example: 42 | // 43 | // [May 16 20:58:45] [DBUG] remove route ns=haproxy addr=127.0.0.1:50002 44 | // 45 | func TerminalFormat() Format { 46 | return FormatFunc(func(r *Record) []byte { 47 | var color = 0 48 | switch r.Lvl { 49 | case LvlCrit: 50 | color = 35 51 | case LvlError: 52 | color = 31 53 | case LvlWarn: 54 | color = 33 55 | case LvlInfo: 56 | color = 32 57 | case LvlDebug: 58 | color = 36 59 | } 60 | 61 | b := &bytes.Buffer{} 62 | lvl := strings.ToUpper(r.Lvl.String()) 63 | if color > 0 { 64 | fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %s ", color, lvl, r.Time.Format(termTimeFormat), r.Msg) 65 | } else { 66 | fmt.Fprintf(b, "[%s] [%s] %s ", lvl, r.Time.Format(termTimeFormat), r.Msg) 67 | } 68 | 69 | // try to justify the log output for short messages 70 | if len(r.Ctx) > 0 && len(r.Msg) < termMsgJust { 71 | b.Write(bytes.Repeat([]byte{' '}, termMsgJust-len(r.Msg))) 72 | } 73 | 74 | // print the keys logfmt style 75 | logfmt(b, r.Ctx, color) 76 | return b.Bytes() 77 | }) 78 | } 79 | 80 | // LogfmtFormat prints records in logfmt format, an easy machine-parseable but human-readable 81 | // format for key/value pairs. 82 | // 83 | // For more details see: http://godoc.org/github.com/kr/logfmt 84 | // 85 | func LogfmtFormat() Format { 86 | return FormatFunc(func(r *Record) []byte { 87 | common := []interface{}{"t", r.Time, "lvl", r.Lvl, "msg", r.Msg} 88 | buf := &bytes.Buffer{} 89 | logfmt(buf, append(common, r.Ctx...), 0) 90 | return buf.Bytes() 91 | }) 92 | } 93 | 94 | func logfmt(buf *bytes.Buffer, ctx []interface{}, color int) { 95 | for i := 0; i < len(ctx); i += 2 { 96 | if i != 0 { 97 | buf.WriteByte(' ') 98 | } 99 | 100 | k, ok := ctx[i].(string) 101 | v := formatLogfmtValue(ctx[i+1]) 102 | if !ok { 103 | k, v = errorKey, formatLogfmtValue(k) 104 | } 105 | 106 | // XXX: we should probably check that all of your key bytes aren't invalid 107 | if color > 0 { 108 | fmt.Fprintf(buf, "\x1b[%dm%s\x1b[0m=%s", color, k, v) 109 | } else { 110 | fmt.Fprintf(buf, "%s=%s", k, v) 111 | } 112 | } 113 | 114 | buf.WriteByte('\n') 115 | } 116 | 117 | // JsonFormat formats log records as JSON objects separated by newlines. 118 | // It is the equivalent of JsonFormatEx(false, true). 119 | func JsonFormat() Format { 120 | return JsonFormatEx(false, true) 121 | } 122 | 123 | // JsonFormatEx formats log records as JSON objects. If pretty is true, 124 | // records will be pretty-printed. If lineSeparated is true, records 125 | // will be logged with a new line between each record. 126 | func JsonFormatEx(pretty, lineSeparated bool) Format { 127 | jsonMarshal := json.Marshal 128 | if pretty { 129 | jsonMarshal = func(v interface{}) ([]byte, error) { 130 | return json.MarshalIndent(v, "", " ") 131 | } 132 | } 133 | 134 | return FormatFunc(func(r *Record) []byte { 135 | props := make(map[string]interface{}) 136 | 137 | props["t"] = r.Time 138 | props["lvl"] = r.Lvl 139 | props["msg"] = r.Msg 140 | 141 | for i := 0; i < len(r.Ctx); i += 2 { 142 | k, ok := r.Ctx[i].(string) 143 | if !ok { 144 | props[errorKey] = fmt.Sprintf("%+v is not a string key", r.Ctx[i]) 145 | } 146 | props[k] = formatJsonValue(r.Ctx[i+1]) 147 | } 148 | 149 | b, err := jsonMarshal(props) 150 | if err != nil { 151 | b, _ = jsonMarshal(map[string]string{ 152 | errorKey: err.Error(), 153 | }) 154 | return b 155 | } 156 | 157 | if lineSeparated { 158 | b = append(b, '\n') 159 | } 160 | 161 | return b 162 | }) 163 | } 164 | 165 | func formatShared(value interface{}) interface{} { 166 | switch v := value.(type) { 167 | case time.Time: 168 | return v.Format(timeFormat) 169 | 170 | case error: 171 | return v.Error() 172 | 173 | case fmt.Stringer: 174 | return v.String() 175 | 176 | default: 177 | return v 178 | } 179 | } 180 | 181 | func formatJsonValue(value interface{}) interface{} { 182 | value = formatShared(value) 183 | switch value.(type) { 184 | case int, int8, int16, int32, int64, float32, float64, uint, uint8, uint16, uint32, uint64, string: 185 | return value 186 | default: 187 | return fmt.Sprintf("%+v", value) 188 | } 189 | } 190 | 191 | // formatValue formats a value for serialization 192 | func formatLogfmtValue(value interface{}) string { 193 | if value == nil { 194 | return "nil" 195 | } 196 | 197 | value = formatShared(value) 198 | switch v := value.(type) { 199 | case bool: 200 | return strconv.FormatBool(v) 201 | case float32: 202 | return strconv.FormatFloat(float64(v), floatFormat, 3, 64) 203 | case float64: 204 | return strconv.FormatFloat(v, floatFormat, 3, 64) 205 | case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: 206 | return fmt.Sprintf("%d", value) 207 | case string: 208 | return escapeString(v) 209 | default: 210 | return escapeString(fmt.Sprintf("%+v", value)) 211 | } 212 | } 213 | 214 | func escapeString(s string) string { 215 | needQuotes := false 216 | e := bytes.Buffer{} 217 | e.WriteByte('"') 218 | for _, r := range s { 219 | if r <= ' ' || r == '=' || r == '"' { 220 | needQuotes = true 221 | } 222 | 223 | switch r { 224 | case '\\', '"': 225 | e.WriteByte('\\') 226 | e.WriteByte(byte(r)) 227 | case '\n': 228 | e.WriteByte('\\') 229 | e.WriteByte('n') 230 | case '\r': 231 | e.WriteByte('\\') 232 | e.WriteByte('r') 233 | case '\t': 234 | e.WriteByte('\\') 235 | e.WriteByte('t') 236 | default: 237 | e.WriteRune(r) 238 | } 239 | } 240 | e.WriteByte('"') 241 | start, stop := 0, e.Len() 242 | if !needQuotes { 243 | start, stop = 1, stop-1 244 | } 245 | return string(e.Bytes()[start:stop]) 246 | } 247 | -------------------------------------------------------------------------------- /src/gopkg.in/inconshreveable/log15.v2/logger.go: -------------------------------------------------------------------------------- 1 | package log15 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | const lvlKey = "lvl" 9 | const errorKey = "LOG15_ERROR" 10 | 11 | type Lvl int 12 | 13 | const ( 14 | LvlCrit Lvl = iota 15 | LvlError 16 | LvlWarn 17 | LvlInfo 18 | LvlDebug 19 | ) 20 | 21 | // Returns the name of a Lvl 22 | func (l Lvl) String() string { 23 | switch l { 24 | case LvlDebug: 25 | return "dbug" 26 | case LvlInfo: 27 | return "info" 28 | case LvlWarn: 29 | return "warn" 30 | case LvlError: 31 | return "eror" 32 | case LvlCrit: 33 | return "crit" 34 | default: 35 | panic("bad level") 36 | } 37 | } 38 | 39 | // Returns the appropriate Lvl from a string name. 40 | // Useful for parsing command line args and configuration files. 41 | func LvlFromString(lvlString string) (Lvl, error) { 42 | switch lvlString { 43 | case "debug", "dbug": 44 | return LvlDebug, nil 45 | case "info": 46 | return LvlInfo, nil 47 | case "warn": 48 | return LvlWarn, nil 49 | case "error", "eror": 50 | return LvlError, nil 51 | case "crit": 52 | return LvlCrit, nil 53 | default: 54 | return LvlDebug, fmt.Errorf("Unknown level: %v", lvlString) 55 | } 56 | } 57 | 58 | // A Record is what a Logger asks its handler to write 59 | type Record struct { 60 | Time time.Time 61 | Lvl Lvl 62 | Msg string 63 | Ctx []interface{} 64 | } 65 | 66 | // A Logger writes key/value pairs to a Handler 67 | type Logger interface { 68 | // New returns a new Logger that has this logger's context plus the given context 69 | New(ctx ...interface{}) Logger 70 | 71 | // SetHandler updates the logger to write records to the specified handler. 72 | SetHandler(h Handler) 73 | 74 | // Log a message at the given level with context key/value pairs 75 | Debug(msg string, ctx ...interface{}) 76 | Info(msg string, ctx ...interface{}) 77 | Warn(msg string, ctx ...interface{}) 78 | Error(msg string, ctx ...interface{}) 79 | Crit(msg string, ctx ...interface{}) 80 | } 81 | 82 | type logger struct { 83 | ctx []interface{} 84 | h *swapHandler 85 | } 86 | 87 | func (l *logger) write(msg string, lvl Lvl, ctx []interface{}) { 88 | l.h.Log(&Record{ 89 | Time: time.Now(), 90 | Lvl: lvl, 91 | Msg: msg, 92 | Ctx: append(l.ctx, normalize(ctx)...), 93 | }) 94 | } 95 | 96 | func (l *logger) New(ctx ...interface{}) Logger { 97 | return &logger{append(l.ctx, normalize(ctx)...), l.h} 98 | } 99 | 100 | func (l *logger) Debug(msg string, ctx ...interface{}) { 101 | l.write(msg, LvlDebug, ctx) 102 | } 103 | 104 | func (l *logger) Info(msg string, ctx ...interface{}) { 105 | l.write(msg, LvlInfo, ctx) 106 | } 107 | 108 | func (l *logger) Warn(msg string, ctx ...interface{}) { 109 | l.write(msg, LvlWarn, ctx) 110 | } 111 | 112 | func (l *logger) Error(msg string, ctx ...interface{}) { 113 | l.write(msg, LvlError, ctx) 114 | } 115 | 116 | func (l *logger) Crit(msg string, ctx ...interface{}) { 117 | l.write(msg, LvlCrit, ctx) 118 | } 119 | 120 | func (l *logger) SetHandler(h Handler) { 121 | l.h.Swap(h) 122 | } 123 | 124 | func normalize(ctx []interface{}) []interface{} { 125 | // if the caller passed a Ctx object, then expand it 126 | if len(ctx) == 1 { 127 | if ctxMap, ok := ctx[0].(Ctx); ok { 128 | ctx = ctxMap.toArray() 129 | } 130 | } 131 | 132 | // ctx needs to be even because it's a series of key/value pairs 133 | // no one wants to check for errors on logging functions, 134 | // so instead of erroring on bad input, we'll just make sure 135 | // that things are the right length and users can fix bugs 136 | // when they see the output looks wrong 137 | if len(ctx)%2 != 0 { 138 | ctx = append(ctx, nil, errorKey, "Normalized odd number of arguments by adding nil") 139 | } 140 | 141 | return ctx 142 | } 143 | 144 | // Lazy allows you to defer calculation of a logged value that is expensive 145 | // to compute until it is certain that it must be evaluated with the given filters. 146 | // 147 | // Lazy may also be used in conjunction with a Logger's New() function 148 | // to generate a child logger which always reports the current value of changing 149 | // state. 150 | // 151 | // You may wrap any function which takes no arguments to Lazy. It may return any 152 | // number of values of any type. 153 | type Lazy struct { 154 | Fn interface{} 155 | } 156 | 157 | // Ctx is a map of key/value pairs to pass as context to a log function 158 | // Use this only if you really need greater safety around the arguments you pass 159 | // to the logging functions. 160 | type Ctx map[string]interface{} 161 | 162 | func (c Ctx) toArray() []interface{} { 163 | arr := make([]interface{}, len(c)*2) 164 | 165 | i := 0 166 | for k, v := range c { 167 | arr[i] = k 168 | arr[i+1] = v 169 | i += 2 170 | } 171 | 172 | return arr 173 | } 174 | -------------------------------------------------------------------------------- /src/gopkg.in/inconshreveable/log15.v2/root.go: -------------------------------------------------------------------------------- 1 | package log15 2 | 3 | import ( 4 | "gopkg.in/inconshreveable/log15.v2/term" 5 | "os" 6 | ) 7 | 8 | var ( 9 | root Logger 10 | StdoutHandler = StreamHandler(os.Stdout, LogfmtFormat()) 11 | StderrHandler = StreamHandler(os.Stderr, LogfmtFormat()) 12 | ) 13 | 14 | func init() { 15 | if term.IsTty(os.Stdout.Fd()) { 16 | StdoutHandler = StreamHandler(os.Stdout, TerminalFormat()) 17 | } 18 | 19 | if term.IsTty(os.Stderr.Fd()) { 20 | StderrHandler = StreamHandler(os.Stderr, TerminalFormat()) 21 | } 22 | 23 | root = &logger{[]interface{}{}, &swapHandler{handler: StdoutHandler}} 24 | } 25 | 26 | // New returns a new logger with the given context. 27 | // New is a convenient alias for Root().New 28 | func New(ctx ...interface{}) Logger { 29 | return root.New(ctx...) 30 | } 31 | 32 | // Root returns the root logger 33 | func Root() Logger { 34 | return root 35 | } 36 | 37 | // Debug is a convenient alias for Root().Debug 38 | func Debug(msg string, ctx ...interface{}) { 39 | root.Debug(msg, ctx...) 40 | } 41 | 42 | // Info is a convenient alias for Root().Info 43 | func Info(msg string, ctx ...interface{}) { 44 | root.Info(msg, ctx...) 45 | } 46 | 47 | // Warn is a convenient alias for Root().Warn 48 | func Warn(msg string, ctx ...interface{}) { 49 | root.Warn(msg, ctx...) 50 | } 51 | 52 | // Error is a convenient alias for Root().Error 53 | func Error(msg string, ctx ...interface{}) { 54 | root.Error(msg, ctx...) 55 | } 56 | 57 | // Crit is a convenient alias for Root().Crit 58 | func Crit(msg string, ctx ...interface{}) { 59 | root.Crit(msg, ctx...) 60 | } 61 | -------------------------------------------------------------------------------- /src/gopkg.in/inconshreveable/log15.v2/syslog.go: -------------------------------------------------------------------------------- 1 | // +build !windows,!plan9 2 | 3 | package log15 4 | 5 | import ( 6 | "log/syslog" 7 | "strings" 8 | ) 9 | 10 | // SyslogHandler opens a connection to the system syslog daemon by calling 11 | // syslog.New and writes all records to it. 12 | func SyslogHandler(tag string, fmtr Format) (Handler, error) { 13 | wr, err := syslog.New(syslog.LOG_INFO, tag) 14 | return sharedSyslog(fmtr, wr, err) 15 | } 16 | 17 | // SyslogHandler opens a connection to a log daemon over the network and writes 18 | // all log records to it. 19 | func SyslogNetHandler(net, addr string, tag string, fmtr Format) (Handler, error) { 20 | wr, err := syslog.Dial(net, addr, syslog.LOG_INFO, tag) 21 | return sharedSyslog(fmtr, wr, err) 22 | } 23 | 24 | func sharedSyslog(fmtr Format, sysWr *syslog.Writer, err error) (Handler, error) { 25 | if err != nil { 26 | return nil, err 27 | } 28 | h := FuncHandler(func(r *Record) error { 29 | var syslogFn = sysWr.Info 30 | switch r.Lvl { 31 | case LvlCrit: 32 | syslogFn = sysWr.Crit 33 | case LvlError: 34 | syslogFn = sysWr.Err 35 | case LvlWarn: 36 | syslogFn = sysWr.Warning 37 | case LvlInfo: 38 | syslogFn = sysWr.Info 39 | case LvlDebug: 40 | syslogFn = sysWr.Debug 41 | } 42 | 43 | s := strings.TrimSpace(string(fmtr.Format(r))) 44 | return syslogFn(s) 45 | }) 46 | return LazyHandler(&closingHandler{sysWr, h}), nil 47 | } 48 | 49 | func (m muster) SyslogHandler(tag string, fmtr Format) Handler { 50 | return must(SyslogHandler(tag, fmtr)) 51 | } 52 | 53 | func (m muster) SyslogNetHandler(net, addr string, tag string, fmtr Format) Handler { 54 | return must(SyslogNetHandler(net, addr, tag, fmtr)) 55 | } 56 | -------------------------------------------------------------------------------- /src/gopkg.in/inconshreveable/log15.v2/term/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Simon Eskildsen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/gopkg.in/inconshreveable/log15.v2/term/terminal_darwin.go: -------------------------------------------------------------------------------- 1 | // Based on ssh/terminal: 2 | // Copyright 2013 The Go Authors. All rights reserved. 3 | // Use of this source code is governed by a BSD-style 4 | // license that can be found in the LICENSE file. 5 | 6 | package term 7 | 8 | import "syscall" 9 | 10 | const ioctlReadTermios = syscall.TIOCGETA 11 | 12 | type Termios syscall.Termios 13 | -------------------------------------------------------------------------------- /src/gopkg.in/inconshreveable/log15.v2/term/terminal_freebsd.go: -------------------------------------------------------------------------------- 1 | package term 2 | 3 | import ( 4 | "syscall" 5 | ) 6 | 7 | const ioctlReadTermios = syscall.TIOCGETA 8 | 9 | // Go 1.2 doesn't include Termios for FreeBSD. This should be added in 1.3 and this could be merged with terminal_darwin. 10 | type Termios struct { 11 | Iflag uint32 12 | Oflag uint32 13 | Cflag uint32 14 | Lflag uint32 15 | Cc [20]uint8 16 | Ispeed uint32 17 | Ospeed uint32 18 | } 19 | -------------------------------------------------------------------------------- /src/gopkg.in/inconshreveable/log15.v2/term/terminal_linux.go: -------------------------------------------------------------------------------- 1 | // Based on ssh/terminal: 2 | // Copyright 2013 The Go Authors. All rights reserved. 3 | // Use of this source code is governed by a BSD-style 4 | // license that can be found in the LICENSE file. 5 | 6 | package term 7 | 8 | import "syscall" 9 | 10 | const ioctlReadTermios = syscall.TCGETS 11 | 12 | type Termios syscall.Termios 13 | -------------------------------------------------------------------------------- /src/gopkg.in/inconshreveable/log15.v2/term/terminal_notwindows.go: -------------------------------------------------------------------------------- 1 | // Based on ssh/terminal: 2 | // Copyright 2011 The Go Authors. All rights reserved. 3 | // Use of this source code is governed by a BSD-style 4 | // license that can be found in the LICENSE file. 5 | 6 | // +build linux,!appengine darwin freebsd 7 | 8 | package term 9 | 10 | import ( 11 | "syscall" 12 | "unsafe" 13 | ) 14 | 15 | // IsTty returns true if the given file descriptor is a terminal. 16 | func IsTty(fd uintptr) bool { 17 | var termios Termios 18 | _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) 19 | return err == 0 20 | } 21 | -------------------------------------------------------------------------------- /src/gopkg.in/inconshreveable/log15.v2/term/terminal_windows.go: -------------------------------------------------------------------------------- 1 | // Based on ssh/terminal: 2 | // Copyright 2011 The Go Authors. All rights reserved. 3 | // Use of this source code is governed by a BSD-style 4 | // license that can be found in the LICENSE file. 5 | 6 | // +build windows 7 | 8 | package term 9 | 10 | import ( 11 | "syscall" 12 | "unsafe" 13 | ) 14 | 15 | var kernel32 = syscall.NewLazyDLL("kernel32.dll") 16 | 17 | var ( 18 | procGetConsoleMode = kernel32.NewProc("GetConsoleMode") 19 | ) 20 | 21 | // IsTty returns true if the given file descriptor is a terminal. 22 | func IsTty(fd uintptr) bool { 23 | var st uint32 24 | r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0) 25 | return r != 0 && e == 0 26 | } 27 | -------------------------------------------------------------------------------- /src/sqltest/drivers.go: -------------------------------------------------------------------------------- 1 | package sqltest 2 | 3 | import ( 4 | _ "code.google.com/p/go-mysql-driver/mysql" 5 | _ "github.com/jackc/pgx/stdlib" 6 | _ "github.com/lib/pq" 7 | _ "github.com/mattn/go-sqlite3" 8 | _ "github.com/tgulacsi/goracle/godrv" 9 | _ "github.com/ziutek/mymysql/godrv" 10 | ) 11 | --------------------------------------------------------------------------------